core/state: simplify journal api, remove externally visible snapshot ids
cmd/evm: post-rebase fixup
This commit is contained in:
parent
10fc900287
commit
81fa3e6b76
|
@ -253,16 +253,16 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
|
||||||
statedb.SetTxContext(tx.Hash(), txIndex)
|
statedb.SetTxContext(tx.Hash(), txIndex)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
snapshot = statedb.Snapshot()
|
prevGas = gaspool.Gas()
|
||||||
prevGas = gaspool.Gas()
|
|
||||||
)
|
)
|
||||||
|
statedb.Snapshot()
|
||||||
if tracer != nil && tracer.OnTxStart != nil {
|
if tracer != nil && tracer.OnTxStart != nil {
|
||||||
tracer.OnTxStart(evm.GetVMContext(), tx, msg.From)
|
tracer.OnTxStart(evm.GetVMContext(), tx, msg.From)
|
||||||
}
|
}
|
||||||
// (ret []byte, usedGas uint64, failed bool, err error)
|
// (ret []byte, usedGas uint64, failed bool, err error)
|
||||||
msgResult, err := core.ApplyMessage(evm, msg, gaspool)
|
msgResult, err := core.ApplyMessage(evm, msg, gaspool)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
statedb.RevertToSnapshot(snapshot)
|
statedb.RevertSnapshot()
|
||||||
log.Info("rejected tx", "index", i, "hash", tx.Hash(), "from", msg.From, "error", err)
|
log.Info("rejected tx", "index", i, "hash", tx.Hash(), "from", msg.From, "error", err)
|
||||||
rejectedTxs = append(rejectedTxs, &rejectedTx{i, err.Error()})
|
rejectedTxs = append(rejectedTxs, &rejectedTx{i, err.Error()})
|
||||||
gaspool.SetGas(prevGas)
|
gaspool.SetGas(prevGas)
|
||||||
|
@ -276,7 +276,7 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
|
||||||
}
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
statedb.DiscardSnapshot(snapshot)
|
statedb.DiscardSnapshot()
|
||||||
includedTxs = append(includedTxs, tx)
|
includedTxs = append(includedTxs, tx)
|
||||||
if hashError != nil {
|
if hashError != nil {
|
||||||
return nil, nil, nil, NewError(ErrorMissingBlockhash, hashError)
|
return nil, nil, nil, NewError(ErrorMissingBlockhash, hashError)
|
||||||
|
|
|
@ -230,8 +230,6 @@ func runCmd(ctx *cli.Context) error {
|
||||||
sdb := state.NewDatabase(triedb, nil)
|
sdb := state.NewDatabase(triedb, nil)
|
||||||
prestate, _ = state.New(genesis.Root(), sdb)
|
prestate, _ = state.New(genesis.Root(), sdb)
|
||||||
chainConfig = genesisConfig.Config
|
chainConfig = genesisConfig.Config
|
||||||
id := statedb.Snapshot()
|
|
||||||
defer statedb.DiscardSnapshot(id)
|
|
||||||
if ctx.String(SenderFlag.Name) != "" {
|
if ctx.String(SenderFlag.Name) != "" {
|
||||||
sender = common.HexToAddress(ctx.String(SenderFlag.Name))
|
sender = common.HexToAddress(ctx.String(SenderFlag.Name))
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,7 +22,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type journal interface {
|
type journal interface {
|
||||||
// snapshot returns an identifier for the current revision of the state.
|
// snapshot starts a new journal scope which can be reverted or discarded.
|
||||||
// The lifeycle of journalling is as follows:
|
// The lifeycle of journalling is as follows:
|
||||||
// - snapshot() starts a 'scope'.
|
// - snapshot() starts a 'scope'.
|
||||||
// - The method snapshot() may be called any number of times.
|
// - The method snapshot() may be called any number of times.
|
||||||
|
@ -30,15 +30,15 @@ type journal interface {
|
||||||
// the scope via either of:
|
// the scope via either of:
|
||||||
// - revertToSnapshot, which undoes the changes in the scope, or
|
// - revertToSnapshot, which undoes the changes in the scope, or
|
||||||
// - discardSnapshot, which discards the ability to revert the changes in the scope.
|
// - discardSnapshot, which discards the ability to revert the changes in the scope.
|
||||||
snapshot() int
|
snapshot()
|
||||||
|
|
||||||
// revertToSnapshot reverts all state changes made since the given revision.
|
// revertSnapshot reverts all state changes made since the last call to snapshot().
|
||||||
revertToSnapshot(revid int, s *StateDB)
|
revertSnapshot(s *StateDB)
|
||||||
|
|
||||||
// discardSnapshot removes the snapshot with the given id; after calling this
|
// discardSnapshot removes the latest snapshot; after calling this
|
||||||
// method, it is no longer possible to revert to that particular snapshot, the
|
// method, it is no longer possible to revert to that particular snapshot, the
|
||||||
// changes are considered part of the parent scope.
|
// changes are considered part of the parent scope.
|
||||||
discardSnapshot(revid int)
|
discardSnapshot()
|
||||||
|
|
||||||
// reset clears the journal so it can be reused.
|
// reset clears the journal so it can be reused.
|
||||||
reset()
|
reset()
|
||||||
|
|
|
@ -17,10 +17,8 @@
|
||||||
package state
|
package state
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"maps"
|
"maps"
|
||||||
"slices"
|
"slices"
|
||||||
"sort"
|
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
|
@ -28,11 +26,6 @@ import (
|
||||||
"github.com/holiman/uint256"
|
"github.com/holiman/uint256"
|
||||||
)
|
)
|
||||||
|
|
||||||
type revision struct {
|
|
||||||
id int
|
|
||||||
journalIndex int
|
|
||||||
}
|
|
||||||
|
|
||||||
// journalEntry is a modification entry in the state change linear journal that can be
|
// journalEntry is a modification entry in the state change linear journal that can be
|
||||||
// reverted on demand.
|
// reverted on demand.
|
||||||
type journalEntry interface {
|
type journalEntry interface {
|
||||||
|
@ -53,8 +46,7 @@ type linearJournal struct {
|
||||||
entries []journalEntry // Current changes tracked by the linearJournal
|
entries []journalEntry // Current changes tracked by the linearJournal
|
||||||
dirties map[common.Address]int // Dirty accounts and the number of changes
|
dirties map[common.Address]int // Dirty accounts and the number of changes
|
||||||
|
|
||||||
validRevisions []revision
|
revisions []int // sequence of indexes to points in time designating snapshots
|
||||||
nextRevisionId int
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// compile-time interface check
|
// compile-time interface check
|
||||||
|
@ -72,9 +64,8 @@ func newLinearJournal() *linearJournal {
|
||||||
// can be reused.
|
// can be reused.
|
||||||
func (j *linearJournal) reset() {
|
func (j *linearJournal) reset() {
|
||||||
j.entries = j.entries[:0]
|
j.entries = j.entries[:0]
|
||||||
j.validRevisions = j.validRevisions[:0]
|
j.revisions = j.revisions[:0]
|
||||||
clear(j.dirties)
|
clear(j.dirties)
|
||||||
j.nextRevisionId = 0
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (j linearJournal) dirtyAccounts() []common.Address {
|
func (j linearJournal) dirtyAccounts() []common.Address {
|
||||||
|
@ -86,33 +77,33 @@ func (j linearJournal) dirtyAccounts() []common.Address {
|
||||||
return dirty
|
return dirty
|
||||||
}
|
}
|
||||||
|
|
||||||
// snapshot returns an identifier for the current revision of the state.
|
// snapshot starts a new journal scope which can be reverted or discarded.
|
||||||
func (j *linearJournal) snapshot() int {
|
func (j *linearJournal) snapshot() {
|
||||||
id := j.nextRevisionId
|
j.revisions = append(j.revisions, len(j.entries))
|
||||||
j.nextRevisionId++
|
|
||||||
j.validRevisions = append(j.validRevisions, revision{id, j.length()})
|
|
||||||
return id
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (j *linearJournal) revertToSnapshot(revid int, s *StateDB) {
|
// revertSnapshot reverts all state changes made since the last call to snapshot().
|
||||||
// Find the snapshot in the stack of valid snapshots.
|
func (j *linearJournal) revertSnapshot(s *StateDB) {
|
||||||
idx := sort.Search(len(j.validRevisions), func(i int) bool {
|
id := len(j.revisions) - 1
|
||||||
return j.validRevisions[i].id >= revid
|
revision := j.revisions[id]
|
||||||
})
|
|
||||||
if idx == len(j.validRevisions) || j.validRevisions[idx].id != revid {
|
|
||||||
panic(fmt.Errorf("revision id %v cannot be reverted (valid revisions: %d)", revid, len(j.validRevisions)))
|
|
||||||
}
|
|
||||||
snapshot := j.validRevisions[idx].journalIndex
|
|
||||||
|
|
||||||
// Replay the linearJournal to undo changes and remove invalidated snapshots
|
// Replay the linearJournal to undo changes and remove invalidated snapshots
|
||||||
j.revert(s, snapshot)
|
j.revertTo(s, revision)
|
||||||
j.validRevisions = j.validRevisions[:idx]
|
j.revisions = j.revisions[:id]
|
||||||
}
|
}
|
||||||
|
|
||||||
// discardSnapshot removes the snapshot with the given id; after calling this
|
// discardSnapshot removes the latest snapshot; after calling this
|
||||||
// method, it is no longer possible to revert to that particular snapshot, the
|
// method, it is no longer possible to revert to that particular snapshot, the
|
||||||
// changes are considered part of the parent scope.
|
// changes are considered part of the parent scope.
|
||||||
func (j *linearJournal) discardSnapshot(id int) {
|
func (j *linearJournal) discardSnapshot() {
|
||||||
|
id := len(j.revisions) - 1
|
||||||
|
if id == 0 {
|
||||||
|
// If a transaction is applied successfully, the statedb.Finalize will
|
||||||
|
// end by clearing and resetting the journal. Invoking a discardSnapshot
|
||||||
|
// afterwards will land here: calling discard on an empty journal.
|
||||||
|
// This is fine
|
||||||
|
return
|
||||||
|
}
|
||||||
|
j.revisions = j.revisions[:id]
|
||||||
}
|
}
|
||||||
|
|
||||||
// append inserts a new modification entry to the end of the change linearJournal.
|
// append inserts a new modification entry to the end of the change linearJournal.
|
||||||
|
@ -125,7 +116,7 @@ func (j *linearJournal) append(entry journalEntry) {
|
||||||
|
|
||||||
// revert undoes a batch of journalled modifications along with any reverted
|
// revert undoes a batch of journalled modifications along with any reverted
|
||||||
// dirty handling too.
|
// dirty handling too.
|
||||||
func (j *linearJournal) revert(statedb *StateDB, snapshot int) {
|
func (j *linearJournal) revertTo(statedb *StateDB, snapshot int) {
|
||||||
for i := len(j.entries) - 1; i >= snapshot; i-- {
|
for i := len(j.entries) - 1; i >= snapshot; i-- {
|
||||||
// Undo the changes made by the operation
|
// Undo the changes made by the operation
|
||||||
j.entries[i].revert(statedb)
|
j.entries[i].revert(statedb)
|
||||||
|
@ -159,10 +150,9 @@ func (j *linearJournal) copy() journal {
|
||||||
entries = append(entries, j.entries[i].copy())
|
entries = append(entries, j.entries[i].copy())
|
||||||
}
|
}
|
||||||
return &linearJournal{
|
return &linearJournal{
|
||||||
entries: entries,
|
entries: entries,
|
||||||
dirties: maps.Clone(j.dirties),
|
dirties: maps.Clone(j.dirties),
|
||||||
validRevisions: slices.Clone(j.validRevisions),
|
revisions: slices.Clone(j.revisions),
|
||||||
nextRevisionId: j.nextRevisionId,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -18,13 +18,11 @@ package state
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"fmt"
|
|
||||||
"maps"
|
"maps"
|
||||||
"slices"
|
"slices"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/log"
|
|
||||||
"github.com/holiman/uint256"
|
"github.com/holiman/uint256"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -349,50 +347,33 @@ func (j *sparseJournal) copy() journal {
|
||||||
return cp
|
return cp
|
||||||
}
|
}
|
||||||
|
|
||||||
// snapshot returns an identifier for the current revision of the state.
|
// snapshot starts a new journal scope which can be reverted or discarded.
|
||||||
// OBS: A call to Snapshot is _required_ in order to initialize the journalling,
|
// OBS: A call to Snapshot is _required_ in order to initialize the journalling,
|
||||||
// invoking the journal-methods without having invoked Snapshot will lead to
|
// invoking the journal-methods without having invoked Snapshot will lead to
|
||||||
// panic.
|
// panic.
|
||||||
func (j *sparseJournal) snapshot() int {
|
func (j *sparseJournal) snapshot() {
|
||||||
id := len(j.entries)
|
|
||||||
j.entries = append(j.entries, newScopedJournal())
|
j.entries = append(j.entries, newScopedJournal())
|
||||||
return id
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// revertToSnapshot reverts all state changes made since the given revision.
|
// revertSnapshot reverts all state changes made since the last call to snapshot().
|
||||||
func (j *sparseJournal) revertToSnapshot(id int, s *StateDB) {
|
func (j *sparseJournal) revertSnapshot(s *StateDB) {
|
||||||
if id >= len(j.entries) {
|
id := len(j.entries) - 1
|
||||||
panic(fmt.Errorf("revision id %v cannot be reverted", id))
|
j.entries[id].revert(s)
|
||||||
}
|
|
||||||
// Revert the entries sequentially
|
|
||||||
for i := len(j.entries) - 1; i >= id; i-- {
|
|
||||||
entry := j.entries[i]
|
|
||||||
entry.revert(s)
|
|
||||||
}
|
|
||||||
j.entries = j.entries[:id]
|
j.entries = j.entries[:id]
|
||||||
}
|
}
|
||||||
|
|
||||||
// discardSnapshot removes the snapshot with the given id; after calling this
|
// discardSnapshot removes the latest snapshot; after calling this
|
||||||
// method, it is no longer possible to revert to that particular snapshot, the
|
// method, it is no longer possible to revert to that particular snapshot, the
|
||||||
// changes are considered part of the parent scope.
|
// changes are considered part of the parent scope.
|
||||||
func (j *sparseJournal) discardSnapshot(id int) {
|
func (j *sparseJournal) discardSnapshot() {
|
||||||
if id == 0 {
|
id := len(j.entries) - 1
|
||||||
return
|
|
||||||
}
|
|
||||||
// here we must merge the 'id' with it's parent.
|
// here we must merge the 'id' with it's parent.
|
||||||
want := len(j.entries) - 1
|
if id == 0 {
|
||||||
have := id
|
// If a transaction is applied successfully, the statedb.Finalize will
|
||||||
if want != have {
|
// end by clearing and resetting the journal. Invoking a discardSnapshot
|
||||||
if want == 0 && id == 1 {
|
// afterwards will land here: calling discard on an empty journal.
|
||||||
// If a transcation is applied successfully, the statedb.Finalize will
|
// This is fine
|
||||||
// end by clearing and resetting the journal. Invoking a discardSnapshot
|
return
|
||||||
// afterwards will lead us here.
|
|
||||||
// Let's not panic, but it's ok to complain a bit
|
|
||||||
log.Error("Extraneous invocation to discard snapshot")
|
|
||||||
return
|
|
||||||
} else {
|
|
||||||
panic(fmt.Sprintf("journalling error, want discard(%d), have discard(%d)", want, have))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
entry := j.entries[id]
|
entry := j.entries[id]
|
||||||
parent := j.entries[id-1]
|
parent := j.entries[id-1]
|
||||||
|
|
|
@ -75,23 +75,23 @@ func testJournalAccessList(t *testing.T, j journal) {
|
||||||
statedb.accessList = newAccessList()
|
statedb.accessList = newAccessList()
|
||||||
statedb.journal = j
|
statedb.journal = j
|
||||||
|
|
||||||
|
j.snapshot()
|
||||||
{
|
{
|
||||||
// If the journal performs the rollback in the wrong order, this
|
// If the journal performs the rollback in the wrong order, this
|
||||||
// will cause a panic.
|
// will cause a panic.
|
||||||
id := j.snapshot()
|
|
||||||
statedb.AddSlotToAccessList(common.Address{0x1}, common.Hash{0x4})
|
statedb.AddSlotToAccessList(common.Address{0x1}, common.Hash{0x4})
|
||||||
statedb.AddSlotToAccessList(common.Address{0x3}, common.Hash{0x4})
|
statedb.AddSlotToAccessList(common.Address{0x3}, common.Hash{0x4})
|
||||||
statedb.RevertToSnapshot(id)
|
|
||||||
}
|
}
|
||||||
|
statedb.RevertSnapshot()
|
||||||
|
j.snapshot()
|
||||||
{
|
{
|
||||||
id := j.snapshot()
|
|
||||||
statedb.AddAddressToAccessList(common.Address{0x2})
|
statedb.AddAddressToAccessList(common.Address{0x2})
|
||||||
statedb.AddAddressToAccessList(common.Address{0x3})
|
statedb.AddAddressToAccessList(common.Address{0x3})
|
||||||
statedb.AddAddressToAccessList(common.Address{0x4})
|
statedb.AddAddressToAccessList(common.Address{0x4})
|
||||||
statedb.RevertToSnapshot(id)
|
}
|
||||||
if statedb.accessList.ContainsAddress(common.Address{0x2}) {
|
statedb.RevertSnapshot()
|
||||||
t.Fatal("should be missing")
|
if statedb.accessList.ContainsAddress(common.Address{0x2}) {
|
||||||
}
|
t.Fatal("should be missing")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -107,25 +107,27 @@ func testJournalRefunds(t *testing.T, j journal) {
|
||||||
var statedb = &StateDB{}
|
var statedb = &StateDB{}
|
||||||
statedb.accessList = newAccessList()
|
statedb.accessList = newAccessList()
|
||||||
statedb.journal = j
|
statedb.journal = j
|
||||||
zero := j.snapshot()
|
j.snapshot()
|
||||||
j.refundChange(0)
|
|
||||||
j.refundChange(1)
|
|
||||||
{
|
{
|
||||||
id := j.snapshot()
|
j.refundChange(0)
|
||||||
j.refundChange(2)
|
j.refundChange(1)
|
||||||
j.refundChange(3)
|
j.snapshot()
|
||||||
j.revertToSnapshot(id, statedb)
|
{
|
||||||
|
j.refundChange(2)
|
||||||
|
j.refundChange(3)
|
||||||
|
}
|
||||||
|
j.revertSnapshot(statedb)
|
||||||
if have, want := statedb.refund, uint64(2); have != want {
|
if have, want := statedb.refund, uint64(2); have != want {
|
||||||
t.Fatalf("have %d want %d", have, want)
|
t.Fatalf("have %d want %d", have, want)
|
||||||
}
|
}
|
||||||
|
j.snapshot()
|
||||||
|
{
|
||||||
|
j.refundChange(2)
|
||||||
|
j.refundChange(3)
|
||||||
|
}
|
||||||
|
j.discardSnapshot()
|
||||||
}
|
}
|
||||||
{
|
j.revertSnapshot(statedb)
|
||||||
id := j.snapshot()
|
|
||||||
j.refundChange(2)
|
|
||||||
j.refundChange(3)
|
|
||||||
j.discardSnapshot(id)
|
|
||||||
}
|
|
||||||
j.revertToSnapshot(zero, statedb)
|
|
||||||
if have, want := statedb.refund, uint64(0); have != want {
|
if have, want := statedb.refund, uint64(0); have != want {
|
||||||
t.Fatalf("have %d want %d", have, want)
|
t.Fatalf("have %d want %d", have, want)
|
||||||
}
|
}
|
||||||
|
|
|
@ -160,25 +160,26 @@ func TestSnapshot(t *testing.T) {
|
||||||
s := newStateEnv()
|
s := newStateEnv()
|
||||||
|
|
||||||
// snapshot the genesis state
|
// snapshot the genesis state
|
||||||
genesis := s.state.Snapshot()
|
s.state.Snapshot()
|
||||||
|
{
|
||||||
|
// set initial state object value
|
||||||
|
s.state.SetState(stateobjaddr, storageaddr, data1)
|
||||||
|
s.state.Snapshot()
|
||||||
|
{
|
||||||
|
// set a new state object value, revert it and ensure correct content
|
||||||
|
s.state.SetState(stateobjaddr, storageaddr, data2)
|
||||||
|
}
|
||||||
|
s.state.RevertSnapshot()
|
||||||
|
|
||||||
// set initial state object value
|
if v := s.state.GetState(stateobjaddr, storageaddr); v != data1 {
|
||||||
s.state.SetState(stateobjaddr, storageaddr, data1)
|
t.Errorf("wrong storage value %v, want %v", v, data1)
|
||||||
snapshot := s.state.Snapshot()
|
}
|
||||||
|
if v := s.state.GetCommittedState(stateobjaddr, storageaddr); v != (common.Hash{}) {
|
||||||
// set a new state object value, revert it and ensure correct content
|
t.Errorf("wrong committed storage value %v, want %v", v, common.Hash{})
|
||||||
s.state.SetState(stateobjaddr, storageaddr, data2)
|
}
|
||||||
s.state.RevertToSnapshot(snapshot)
|
|
||||||
|
|
||||||
if v := s.state.GetState(stateobjaddr, storageaddr); v != data1 {
|
|
||||||
t.Errorf("wrong storage value %v, want %v", v, data1)
|
|
||||||
}
|
}
|
||||||
if v := s.state.GetCommittedState(stateobjaddr, storageaddr); v != (common.Hash{}) {
|
|
||||||
t.Errorf("wrong committed storage value %v, want %v", v, common.Hash{})
|
|
||||||
}
|
|
||||||
|
|
||||||
// revert up to the genesis state and ensure correct content
|
// revert up to the genesis state and ensure correct content
|
||||||
s.state.RevertToSnapshot(genesis)
|
s.state.RevertSnapshot()
|
||||||
if v := s.state.GetState(stateobjaddr, storageaddr); v != (common.Hash{}) {
|
if v := s.state.GetState(stateobjaddr, storageaddr); v != (common.Hash{}) {
|
||||||
t.Errorf("wrong storage value %v, want %v", v, common.Hash{})
|
t.Errorf("wrong storage value %v, want %v", v, common.Hash{})
|
||||||
}
|
}
|
||||||
|
@ -189,22 +190,23 @@ func TestSnapshot(t *testing.T) {
|
||||||
|
|
||||||
func TestSnapshotEmpty(t *testing.T) {
|
func TestSnapshotEmpty(t *testing.T) {
|
||||||
s := newStateEnv()
|
s := newStateEnv()
|
||||||
s.state.RevertToSnapshot(s.state.Snapshot())
|
s.state.Snapshot()
|
||||||
|
s.state.RevertSnapshot()
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCreateObjectRevert(t *testing.T) {
|
func TestCreateObjectRevert(t *testing.T) {
|
||||||
state, _ := New(types.EmptyRootHash, NewDatabaseForTesting())
|
state, _ := New(types.EmptyRootHash, NewDatabaseForTesting())
|
||||||
addr := common.BytesToAddress([]byte("so0"))
|
addr := common.BytesToAddress([]byte("so0"))
|
||||||
snap := state.Snapshot()
|
state.Snapshot()
|
||||||
|
{
|
||||||
state.CreateAccount(addr)
|
state.CreateAccount(addr)
|
||||||
so0 := state.getStateObject(addr)
|
so0 := state.getStateObject(addr)
|
||||||
so0.SetBalance(uint256.NewInt(42))
|
so0.SetBalance(uint256.NewInt(42))
|
||||||
so0.SetNonce(43)
|
so0.SetNonce(43)
|
||||||
so0.SetCode(crypto.Keccak256Hash([]byte{'c', 'a', 'f', 'e'}), []byte{'c', 'a', 'f', 'e'})
|
so0.SetCode(crypto.Keccak256Hash([]byte{'c', 'a', 'f', 'e'}), []byte{'c', 'a', 'f', 'e'})
|
||||||
state.setStateObject(so0)
|
state.setStateObject(so0)
|
||||||
|
}
|
||||||
state.RevertToSnapshot(snap)
|
state.RevertSnapshot()
|
||||||
if state.Exist(addr) {
|
if state.Exist(addr) {
|
||||||
t.Error("Unexpected account after revert")
|
t.Error("Unexpected account after revert")
|
||||||
}
|
}
|
||||||
|
|
|
@ -132,7 +132,7 @@ type StateDB struct {
|
||||||
transientStorage transientStorage
|
transientStorage transientStorage
|
||||||
|
|
||||||
// Journal of state modifications. This is the backbone of
|
// Journal of state modifications. This is the backbone of
|
||||||
// Snapshot and RevertToSnapshot.
|
// Snapshot and RevertSnapshot.
|
||||||
journal journal
|
journal journal
|
||||||
|
|
||||||
// State witness if cross validation is needed
|
// State witness if cross validation is needed
|
||||||
|
@ -708,21 +708,21 @@ func (s *StateDB) Copy() *StateDB {
|
||||||
return state
|
return state
|
||||||
}
|
}
|
||||||
|
|
||||||
// Snapshot returns an identifier for the current revision of the state.
|
// Snapshot starts a new journalled scope.
|
||||||
func (s *StateDB) Snapshot() int {
|
func (s *StateDB) Snapshot() {
|
||||||
return s.journal.snapshot()
|
s.journal.snapshot()
|
||||||
}
|
}
|
||||||
|
|
||||||
// DiscardSnapshot removes the snapshot with the given id; after calling this
|
// DiscardSnapshot removes the ability to roll back the changes in the most
|
||||||
// method, it is no longer possible to revert to that particular snapshot, the
|
// recent journalled scope. After calling this method, the changes are considered
|
||||||
// changes are considered part of the parent scope.
|
// part of the parent scope.
|
||||||
func (s *StateDB) DiscardSnapshot(id int) {
|
func (s *StateDB) DiscardSnapshot() {
|
||||||
s.journal.discardSnapshot(id)
|
s.journal.discardSnapshot()
|
||||||
}
|
}
|
||||||
|
|
||||||
// RevertToSnapshot reverts all state changes made since the given revision.
|
// RevertSnapshot reverts all state changes made in the most recent journalled scope.
|
||||||
func (s *StateDB) RevertToSnapshot(revid int) {
|
func (s *StateDB) RevertSnapshot() {
|
||||||
s.journal.revertToSnapshot(revid, s)
|
s.journal.revertSnapshot(s)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetRefund returns the current value of the refund counter.
|
// GetRefund returns the current value of the refund counter.
|
||||||
|
|
|
@ -141,16 +141,16 @@ func (s *hookedStateDB) Prepare(rules params.Rules, sender, coinbase common.Addr
|
||||||
s.inner.Prepare(rules, sender, coinbase, dest, precompiles, txAccesses)
|
s.inner.Prepare(rules, sender, coinbase, dest, precompiles, txAccesses)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *hookedStateDB) DiscardSnapshot(id int) {
|
func (s *hookedStateDB) DiscardSnapshot() {
|
||||||
s.inner.DiscardSnapshot(id)
|
s.inner.DiscardSnapshot()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *hookedStateDB) RevertToSnapshot(i int) {
|
func (s *hookedStateDB) RevertSnapshot() {
|
||||||
s.inner.RevertToSnapshot(i)
|
s.inner.RevertSnapshot()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *hookedStateDB) Snapshot() int {
|
func (s *hookedStateDB) Snapshot() {
|
||||||
return s.inner.Snapshot()
|
s.inner.Snapshot()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *hookedStateDB) AddPreimage(hash common.Hash, bytes []byte) {
|
func (s *hookedStateDB) AddPreimage(hash common.Hash, bytes []byte) {
|
||||||
|
|
|
@ -563,14 +563,13 @@ func (test *snapshotTest) String() string {
|
||||||
func (test *snapshotTest) run() bool {
|
func (test *snapshotTest) run() bool {
|
||||||
// Run all actions and create snapshots.
|
// Run all actions and create snapshots.
|
||||||
var (
|
var (
|
||||||
state, _ = New(types.EmptyRootHash, NewDatabaseForTesting())
|
state, _ = New(types.EmptyRootHash, NewDatabaseForTesting())
|
||||||
snapshotRevs = make([]int, len(test.snapshots))
|
sindex = 0
|
||||||
sindex = 0
|
checkstates = make([]*StateDB, len(test.snapshots))
|
||||||
checkstates = make([]*StateDB, len(test.snapshots))
|
|
||||||
)
|
)
|
||||||
for i, action := range test.actions {
|
for i, action := range test.actions {
|
||||||
if len(test.snapshots) > sindex && i == test.snapshots[sindex] {
|
if len(test.snapshots) > sindex && i == test.snapshots[sindex] {
|
||||||
snapshotRevs[sindex] = state.Snapshot()
|
state.Snapshot()
|
||||||
checkstates[sindex] = state.Copy()
|
checkstates[sindex] = state.Copy()
|
||||||
sindex++
|
sindex++
|
||||||
}
|
}
|
||||||
|
@ -579,7 +578,7 @@ func (test *snapshotTest) run() bool {
|
||||||
// Revert all snapshots in reverse order. Each revert must yield a state
|
// Revert all snapshots in reverse order. Each revert must yield a state
|
||||||
// that is equivalent to fresh state with all actions up the snapshot applied.
|
// that is equivalent to fresh state with all actions up the snapshot applied.
|
||||||
for sindex--; sindex >= 0; sindex-- {
|
for sindex--; sindex >= 0; sindex-- {
|
||||||
state.RevertToSnapshot(snapshotRevs[sindex])
|
state.RevertSnapshot()
|
||||||
if err := test.checkEqual(state, checkstates[sindex]); err != nil {
|
if err := test.checkEqual(state, checkstates[sindex]); err != nil {
|
||||||
test.err = fmt.Errorf("state mismatch after revert to snapshot %d\n%v", sindex, err)
|
test.err = fmt.Errorf("state mismatch after revert to snapshot %d\n%v", sindex, err)
|
||||||
return false
|
return false
|
||||||
|
@ -734,13 +733,13 @@ func TestTouchDelete(t *testing.T) {
|
||||||
root, _ := s.state.Commit(0, false)
|
root, _ := s.state.Commit(0, false)
|
||||||
s.state, _ = New(root, s.state.db)
|
s.state, _ = New(root, s.state.db)
|
||||||
|
|
||||||
snapshot := s.state.Snapshot()
|
s.state.Snapshot()
|
||||||
s.state.AddBalance(common.Address{}, new(uint256.Int), tracing.BalanceChangeUnspecified)
|
s.state.AddBalance(common.Address{}, new(uint256.Int), tracing.BalanceChangeUnspecified)
|
||||||
|
|
||||||
if len(s.state.journal.dirtyAccounts()) != 1 {
|
if len(s.state.journal.dirtyAccounts()) != 1 {
|
||||||
t.Fatal("expected one dirty state object")
|
t.Fatal("expected one dirty state object")
|
||||||
}
|
}
|
||||||
s.state.RevertToSnapshot(snapshot)
|
s.state.RevertSnapshot()
|
||||||
if len(s.state.journal.dirtyAccounts()) != 0 {
|
if len(s.state.journal.dirtyAccounts()) != 0 {
|
||||||
t.Fatal("expected no dirty state object")
|
t.Fatal("expected no dirty state object")
|
||||||
}
|
}
|
||||||
|
@ -985,9 +984,11 @@ func TestDeleteCreateRevert(t *testing.T) {
|
||||||
state.SelfDestruct(addr)
|
state.SelfDestruct(addr)
|
||||||
state.Finalise(true)
|
state.Finalise(true)
|
||||||
|
|
||||||
id := state.Snapshot()
|
state.Snapshot()
|
||||||
state.SetBalance(addr, uint256.NewInt(2), tracing.BalanceChangeUnspecified)
|
{
|
||||||
state.RevertToSnapshot(id)
|
state.SetBalance(addr, uint256.NewInt(2), tracing.BalanceChangeUnspecified)
|
||||||
|
}
|
||||||
|
state.RevertSnapshot()
|
||||||
|
|
||||||
// Commit the entire state and make sure we don't crash and have the correct state
|
// Commit the entire state and make sure we don't crash and have the correct state
|
||||||
root, _ = state.Commit(0, true)
|
root, _ = state.Commit(0, true)
|
||||||
|
@ -1126,25 +1127,15 @@ func TestStateDBAccessList(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var ids []int
|
state.journal.snapshot() // journal id 0
|
||||||
push := func(id int) {
|
|
||||||
ids = append(ids, id)
|
|
||||||
}
|
|
||||||
pop := func() int {
|
|
||||||
id := ids[len(ids)-1]
|
|
||||||
ids = ids[:len(ids)-1]
|
|
||||||
return id
|
|
||||||
}
|
|
||||||
|
|
||||||
push(state.journal.snapshot()) // journal id 0
|
|
||||||
state.AddAddressToAccessList(addr("aa")) // 1
|
state.AddAddressToAccessList(addr("aa")) // 1
|
||||||
push(state.journal.snapshot()) // journal id 1
|
state.journal.snapshot() // journal id 1
|
||||||
state.AddAddressToAccessList(addr("bb")) // 2
|
state.AddAddressToAccessList(addr("bb")) // 2
|
||||||
push(state.journal.snapshot()) // journal id 2
|
state.journal.snapshot() // journal id 2
|
||||||
state.AddSlotToAccessList(addr("bb"), slot("01")) // 3
|
state.AddSlotToAccessList(addr("bb"), slot("01")) // 3
|
||||||
push(state.journal.snapshot()) // journal id 3
|
state.journal.snapshot() // journal id 3
|
||||||
state.AddSlotToAccessList(addr("bb"), slot("02")) // 4
|
state.AddSlotToAccessList(addr("bb"), slot("02")) // 4
|
||||||
push(state.journal.snapshot()) // journal id 4
|
state.journal.snapshot() // journal id 4
|
||||||
verifyAddrs("aa", "bb")
|
verifyAddrs("aa", "bb")
|
||||||
verifySlots("bb", "01", "02")
|
verifySlots("bb", "01", "02")
|
||||||
|
|
||||||
|
@ -1158,11 +1149,11 @@ func TestStateDBAccessList(t *testing.T) {
|
||||||
|
|
||||||
// some new ones
|
// some new ones
|
||||||
state.AddSlotToAccessList(addr("bb"), slot("03")) // 5
|
state.AddSlotToAccessList(addr("bb"), slot("03")) // 5
|
||||||
push(state.journal.snapshot()) // journal id 5
|
state.journal.snapshot() // journal id 5
|
||||||
state.AddSlotToAccessList(addr("aa"), slot("01")) // 6
|
state.AddSlotToAccessList(addr("aa"), slot("01")) // 6
|
||||||
push(state.journal.snapshot()) // journal id 6
|
state.journal.snapshot() // journal id 6
|
||||||
state.AddAddressToAccessList(addr("cc")) // 7
|
state.AddAddressToAccessList(addr("cc")) // 7
|
||||||
push(state.journal.snapshot()) // journal id 7
|
state.journal.snapshot() // journal id 7
|
||||||
state.AddSlotToAccessList(addr("cc"), slot("01")) // 8
|
state.AddSlotToAccessList(addr("cc"), slot("01")) // 8
|
||||||
|
|
||||||
verifyAddrs("aa", "bb", "cc")
|
verifyAddrs("aa", "bb", "cc")
|
||||||
|
@ -1171,7 +1162,7 @@ func TestStateDBAccessList(t *testing.T) {
|
||||||
verifySlots("cc", "01")
|
verifySlots("cc", "01")
|
||||||
|
|
||||||
// now start rolling back changes
|
// now start rolling back changes
|
||||||
state.journal.revertToSnapshot(pop(), state) // revert to 6
|
state.journal.revertSnapshot(state) // revert to 6
|
||||||
if _, ok := state.SlotInAccessList(addr("cc"), slot("01")); ok {
|
if _, ok := state.SlotInAccessList(addr("cc"), slot("01")); ok {
|
||||||
t.Fatalf("slot present, expected missing")
|
t.Fatalf("slot present, expected missing")
|
||||||
}
|
}
|
||||||
|
@ -1179,7 +1170,7 @@ func TestStateDBAccessList(t *testing.T) {
|
||||||
verifySlots("aa", "01")
|
verifySlots("aa", "01")
|
||||||
verifySlots("bb", "01", "02", "03")
|
verifySlots("bb", "01", "02", "03")
|
||||||
|
|
||||||
state.journal.revertToSnapshot(pop(), state) // revert to 5
|
state.journal.revertSnapshot(state) // revert to 5
|
||||||
if state.AddressInAccessList(addr("cc")) {
|
if state.AddressInAccessList(addr("cc")) {
|
||||||
t.Fatalf("addr present, expected missing")
|
t.Fatalf("addr present, expected missing")
|
||||||
}
|
}
|
||||||
|
@ -1187,40 +1178,40 @@ func TestStateDBAccessList(t *testing.T) {
|
||||||
verifySlots("aa", "01")
|
verifySlots("aa", "01")
|
||||||
verifySlots("bb", "01", "02", "03")
|
verifySlots("bb", "01", "02", "03")
|
||||||
|
|
||||||
state.journal.revertToSnapshot(pop(), state) // revert to 4
|
state.journal.revertSnapshot(state) // revert to 4
|
||||||
if _, ok := state.SlotInAccessList(addr("aa"), slot("01")); ok {
|
if _, ok := state.SlotInAccessList(addr("aa"), slot("01")); ok {
|
||||||
t.Fatalf("slot present, expected missing")
|
t.Fatalf("slot present, expected missing")
|
||||||
}
|
}
|
||||||
verifyAddrs("aa", "bb")
|
verifyAddrs("aa", "bb")
|
||||||
verifySlots("bb", "01", "02", "03")
|
verifySlots("bb", "01", "02", "03")
|
||||||
|
|
||||||
state.journal.revertToSnapshot(pop(), state) // revert to 3
|
state.journal.revertSnapshot(state) // revert to 3
|
||||||
if _, ok := state.SlotInAccessList(addr("bb"), slot("03")); ok {
|
if _, ok := state.SlotInAccessList(addr("bb"), slot("03")); ok {
|
||||||
t.Fatalf("slot present, expected missing")
|
t.Fatalf("slot present, expected missing")
|
||||||
}
|
}
|
||||||
verifyAddrs("aa", "bb")
|
verifyAddrs("aa", "bb")
|
||||||
verifySlots("bb", "01", "02")
|
verifySlots("bb", "01", "02")
|
||||||
|
|
||||||
state.journal.revertToSnapshot(pop(), state) // revert to 2
|
state.journal.revertSnapshot(state) // revert to 2
|
||||||
if _, ok := state.SlotInAccessList(addr("bb"), slot("02")); ok {
|
if _, ok := state.SlotInAccessList(addr("bb"), slot("02")); ok {
|
||||||
t.Fatalf("slot present, expected missing")
|
t.Fatalf("slot present, expected missing")
|
||||||
}
|
}
|
||||||
verifyAddrs("aa", "bb")
|
verifyAddrs("aa", "bb")
|
||||||
verifySlots("bb", "01")
|
verifySlots("bb", "01")
|
||||||
|
|
||||||
state.journal.revertToSnapshot(pop(), state) // revert to 1
|
state.journal.revertSnapshot(state) // revert to 1
|
||||||
if _, ok := state.SlotInAccessList(addr("bb"), slot("01")); ok {
|
if _, ok := state.SlotInAccessList(addr("bb"), slot("01")); ok {
|
||||||
t.Fatalf("slot present, expected missing")
|
t.Fatalf("slot present, expected missing")
|
||||||
}
|
}
|
||||||
verifyAddrs("aa", "bb")
|
verifyAddrs("aa", "bb")
|
||||||
|
|
||||||
state.journal.revertToSnapshot(pop(), state) // revert to 0
|
state.journal.revertSnapshot(state) // revert to 0
|
||||||
if state.AddressInAccessList(addr("bb")) {
|
if state.AddressInAccessList(addr("bb")) {
|
||||||
t.Fatalf("addr present, expected missing")
|
t.Fatalf("addr present, expected missing")
|
||||||
}
|
}
|
||||||
verifyAddrs("aa")
|
verifyAddrs("aa")
|
||||||
|
|
||||||
state.journal.revertToSnapshot(0, state)
|
state.journal.revertSnapshot(state)
|
||||||
if state.AddressInAccessList(addr("aa")) {
|
if state.AddressInAccessList(addr("aa")) {
|
||||||
t.Fatalf("addr present, expected missing")
|
t.Fatalf("addr present, expected missing")
|
||||||
}
|
}
|
||||||
|
@ -1291,7 +1282,7 @@ func TestStateDBTransientStorage(t *testing.T) {
|
||||||
key := common.Hash{0x01}
|
key := common.Hash{0x01}
|
||||||
value := common.Hash{0x02}
|
value := common.Hash{0x02}
|
||||||
addr := common.Address{}
|
addr := common.Address{}
|
||||||
revision := state.journal.snapshot()
|
state.journal.snapshot()
|
||||||
state.SetTransientState(addr, key, value)
|
state.SetTransientState(addr, key, value)
|
||||||
|
|
||||||
// the retrieved value should equal what was set
|
// the retrieved value should equal what was set
|
||||||
|
@ -1301,7 +1292,7 @@ func TestStateDBTransientStorage(t *testing.T) {
|
||||||
|
|
||||||
// revert the transient state being set and then check that the
|
// revert the transient state being set and then check that the
|
||||||
// value is now the empty hash
|
// value is now the empty hash
|
||||||
state.journal.revertToSnapshot(revision, state)
|
state.journal.revertSnapshot(state)
|
||||||
if got, exp := state.GetTransientState(addr, key), (common.Hash{}); exp != got {
|
if got, exp := state.GetTransientState(addr, key), (common.Hash{}); exp != got {
|
||||||
t.Fatalf("transient storage mismatch: have %x, want %x", got, exp)
|
t.Fatalf("transient storage mismatch: have %x, want %x", got, exp)
|
||||||
}
|
}
|
||||||
|
@ -1395,22 +1386,24 @@ func TestStorageDirtiness(t *testing.T) {
|
||||||
checkDirty(common.Hash{0x1}, common.Hash{}, false)
|
checkDirty(common.Hash{0x1}, common.Hash{}, false)
|
||||||
|
|
||||||
// the storage change is valid, dirty marker is expected
|
// the storage change is valid, dirty marker is expected
|
||||||
snap := state.Snapshot()
|
state.Snapshot()
|
||||||
state.SetState(addr, common.Hash{0x1}, common.Hash{0x1})
|
{
|
||||||
checkDirty(common.Hash{0x1}, common.Hash{0x1}, true)
|
state.SetState(addr, common.Hash{0x1}, common.Hash{0x1})
|
||||||
|
checkDirty(common.Hash{0x1}, common.Hash{0x1}, true)
|
||||||
|
}
|
||||||
// the storage change is reverted, dirtiness should be revoked
|
// the storage change is reverted, dirtiness should be revoked
|
||||||
state.RevertToSnapshot(snap)
|
state.RevertSnapshot()
|
||||||
checkDirty(common.Hash{0x1}, common.Hash{}, false)
|
checkDirty(common.Hash{0x1}, common.Hash{}, false)
|
||||||
|
|
||||||
// the storage is reset back to its original value, dirtiness should be revoked
|
// the storage is reset back to its original value, dirtiness should be revoked
|
||||||
state.SetState(addr, common.Hash{0x1}, common.Hash{0x1})
|
state.SetState(addr, common.Hash{0x1}, common.Hash{0x1})
|
||||||
snap = state.Snapshot()
|
state.Snapshot()
|
||||||
state.SetState(addr, common.Hash{0x1}, common.Hash{})
|
{
|
||||||
checkDirty(common.Hash{0x1}, common.Hash{}, false)
|
state.SetState(addr, common.Hash{0x1}, common.Hash{})
|
||||||
|
checkDirty(common.Hash{0x1}, common.Hash{}, false)
|
||||||
|
}
|
||||||
// the storage change is reverted, dirty value should be set back
|
// the storage change is reverted, dirty value should be set back
|
||||||
state.RevertToSnapshot(snap)
|
state.RevertSnapshot()
|
||||||
checkDirty(common.Hash{0x1}, common.Hash{0x1}, true)
|
checkDirty(common.Hash{0x1}, common.Hash{0x1}, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1455,10 +1448,12 @@ func TestStorageDirtiness2(t *testing.T) {
|
||||||
checkDirty(common.Hash{0x1}, common.Hash{0xa}, false)
|
checkDirty(common.Hash{0x1}, common.Hash{0xa}, false)
|
||||||
|
|
||||||
// Enter new scope
|
// Enter new scope
|
||||||
snap := state.Snapshot()
|
state.Snapshot()
|
||||||
state.SetState(addr, common.Hash{0x1}, common.Hash{0xb}) // SLOT(1) = 0xB
|
{
|
||||||
checkDirty(common.Hash{0x1}, common.Hash{0xb}, true) // Should be flagged dirty
|
state.SetState(addr, common.Hash{0x1}, common.Hash{0xb}) // SLOT(1) = 0xB
|
||||||
state.RevertToSnapshot(snap) // Revert scope
|
checkDirty(common.Hash{0x1}, common.Hash{0xb}, true) // Should be flagged dirty
|
||||||
|
}
|
||||||
|
state.RevertSnapshot() // Revert scope
|
||||||
|
|
||||||
// the storage change has been set back to original, dirtiness should be revoked
|
// the storage change has been set back to original, dirtiness should be revoked
|
||||||
checkDirty(common.Hash{0x1}, common.Hash{0x1}, false)
|
checkDirty(common.Hash{0x1}, common.Hash{0x1}, false)
|
||||||
|
|
|
@ -190,7 +190,7 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas
|
||||||
if !value.IsZero() && !evm.Context.CanTransfer(evm.StateDB, caller.Address(), value) {
|
if !value.IsZero() && !evm.Context.CanTransfer(evm.StateDB, caller.Address(), value) {
|
||||||
return nil, gas, ErrInsufficientBalance
|
return nil, gas, ErrInsufficientBalance
|
||||||
}
|
}
|
||||||
snapshot := evm.StateDB.Snapshot()
|
evm.StateDB.Snapshot()
|
||||||
p, isPrecompile := evm.precompile(addr)
|
p, isPrecompile := evm.precompile(addr)
|
||||||
|
|
||||||
if !evm.StateDB.Exist(addr) {
|
if !evm.StateDB.Exist(addr) {
|
||||||
|
@ -198,7 +198,7 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas
|
||||||
// add proof of absence to witness
|
// add proof of absence to witness
|
||||||
wgas := evm.AccessEvents.AddAccount(addr, false)
|
wgas := evm.AccessEvents.AddAccount(addr, false)
|
||||||
if gas < wgas {
|
if gas < wgas {
|
||||||
evm.StateDB.RevertToSnapshot(snapshot)
|
evm.StateDB.RevertSnapshot()
|
||||||
return nil, 0, ErrOutOfGas
|
return nil, 0, ErrOutOfGas
|
||||||
}
|
}
|
||||||
gas -= wgas
|
gas -= wgas
|
||||||
|
@ -206,7 +206,7 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas
|
||||||
|
|
||||||
if !isPrecompile && evm.chainRules.IsEIP158 && value.IsZero() {
|
if !isPrecompile && evm.chainRules.IsEIP158 && value.IsZero() {
|
||||||
// Calling a non-existing account, don't do anything.
|
// Calling a non-existing account, don't do anything.
|
||||||
evm.StateDB.DiscardSnapshot(snapshot)
|
evm.StateDB.DiscardSnapshot()
|
||||||
return nil, gas, nil
|
return nil, gas, nil
|
||||||
}
|
}
|
||||||
evm.StateDB.CreateAccount(addr)
|
evm.StateDB.CreateAccount(addr)
|
||||||
|
@ -235,7 +235,7 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas
|
||||||
// above we revert to the snapshot and consume any gas remaining. Additionally,
|
// above we revert to the snapshot and consume any gas remaining. Additionally,
|
||||||
// when we're in homestead this also counts for code storage gas errors.
|
// when we're in homestead this also counts for code storage gas errors.
|
||||||
if err != nil {
|
if err != nil {
|
||||||
evm.StateDB.RevertToSnapshot(snapshot)
|
evm.StateDB.RevertSnapshot()
|
||||||
if err != ErrExecutionReverted {
|
if err != ErrExecutionReverted {
|
||||||
if evm.Config.Tracer != nil && evm.Config.Tracer.OnGasChange != nil {
|
if evm.Config.Tracer != nil && evm.Config.Tracer.OnGasChange != nil {
|
||||||
evm.Config.Tracer.OnGasChange(gas, 0, tracing.GasChangeCallFailedExecution)
|
evm.Config.Tracer.OnGasChange(gas, 0, tracing.GasChangeCallFailedExecution)
|
||||||
|
@ -244,7 +244,7 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas
|
||||||
gas = 0
|
gas = 0
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
evm.StateDB.DiscardSnapshot(snapshot)
|
evm.StateDB.DiscardSnapshot()
|
||||||
}
|
}
|
||||||
return ret, gas, err
|
return ret, gas, err
|
||||||
}
|
}
|
||||||
|
@ -275,7 +275,7 @@ func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte,
|
||||||
if !evm.Context.CanTransfer(evm.StateDB, caller.Address(), value) {
|
if !evm.Context.CanTransfer(evm.StateDB, caller.Address(), value) {
|
||||||
return nil, gas, ErrInsufficientBalance
|
return nil, gas, ErrInsufficientBalance
|
||||||
}
|
}
|
||||||
var snapshot = evm.StateDB.Snapshot()
|
evm.StateDB.Snapshot()
|
||||||
|
|
||||||
// It is allowed to call precompiles, even via delegatecall
|
// It is allowed to call precompiles, even via delegatecall
|
||||||
if p, isPrecompile := evm.precompile(addr); isPrecompile {
|
if p, isPrecompile := evm.precompile(addr); isPrecompile {
|
||||||
|
@ -290,7 +290,7 @@ func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte,
|
||||||
gas = contract.Gas
|
gas = contract.Gas
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
evm.StateDB.RevertToSnapshot(snapshot)
|
evm.StateDB.RevertSnapshot()
|
||||||
if err != ErrExecutionReverted {
|
if err != ErrExecutionReverted {
|
||||||
if evm.Config.Tracer != nil && evm.Config.Tracer.OnGasChange != nil {
|
if evm.Config.Tracer != nil && evm.Config.Tracer.OnGasChange != nil {
|
||||||
evm.Config.Tracer.OnGasChange(gas, 0, tracing.GasChangeCallFailedExecution)
|
evm.Config.Tracer.OnGasChange(gas, 0, tracing.GasChangeCallFailedExecution)
|
||||||
|
@ -299,7 +299,7 @@ func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte,
|
||||||
gas = 0
|
gas = 0
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
evm.StateDB.DiscardSnapshot(snapshot)
|
evm.StateDB.DiscardSnapshot()
|
||||||
}
|
}
|
||||||
return ret, gas, err
|
return ret, gas, err
|
||||||
}
|
}
|
||||||
|
@ -325,7 +325,7 @@ func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []by
|
||||||
if evm.depth > int(params.CallCreateDepth) {
|
if evm.depth > int(params.CallCreateDepth) {
|
||||||
return nil, gas, ErrDepth
|
return nil, gas, ErrDepth
|
||||||
}
|
}
|
||||||
var snapshot = evm.StateDB.Snapshot()
|
evm.StateDB.Snapshot()
|
||||||
|
|
||||||
// It is allowed to call precompiles, even via delegatecall
|
// It is allowed to call precompiles, even via delegatecall
|
||||||
if p, isPrecompile := evm.precompile(addr); isPrecompile {
|
if p, isPrecompile := evm.precompile(addr); isPrecompile {
|
||||||
|
@ -339,7 +339,7 @@ func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []by
|
||||||
gas = contract.Gas
|
gas = contract.Gas
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
evm.StateDB.RevertToSnapshot(snapshot)
|
evm.StateDB.RevertSnapshot()
|
||||||
if err != ErrExecutionReverted {
|
if err != ErrExecutionReverted {
|
||||||
if evm.Config.Tracer != nil && evm.Config.Tracer.OnGasChange != nil {
|
if evm.Config.Tracer != nil && evm.Config.Tracer.OnGasChange != nil {
|
||||||
evm.Config.Tracer.OnGasChange(gas, 0, tracing.GasChangeCallFailedExecution)
|
evm.Config.Tracer.OnGasChange(gas, 0, tracing.GasChangeCallFailedExecution)
|
||||||
|
@ -347,7 +347,7 @@ func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []by
|
||||||
gas = 0
|
gas = 0
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
evm.StateDB.DiscardSnapshot(snapshot)
|
evm.StateDB.DiscardSnapshot()
|
||||||
}
|
}
|
||||||
return ret, gas, err
|
return ret, gas, err
|
||||||
}
|
}
|
||||||
|
@ -373,7 +373,7 @@ func (evm *EVM) StaticCall(caller ContractRef, addr common.Address, input []byte
|
||||||
// after all empty accounts were deleted, so this is not required. However, if we omit this,
|
// after all empty accounts were deleted, so this is not required. However, if we omit this,
|
||||||
// then certain tests start failing; stRevertTest/RevertPrecompiledTouchExactOOG.json.
|
// then certain tests start failing; stRevertTest/RevertPrecompiledTouchExactOOG.json.
|
||||||
// We could change this, but for now it's left for legacy reasons
|
// We could change this, but for now it's left for legacy reasons
|
||||||
var snapshot = evm.StateDB.Snapshot()
|
evm.StateDB.Snapshot()
|
||||||
|
|
||||||
// We do an AddBalance of zero here, just in order to trigger a touch.
|
// We do an AddBalance of zero here, just in order to trigger a touch.
|
||||||
// This doesn't matter on Mainnet, where all empties are gone at the time of Byzantium,
|
// This doesn't matter on Mainnet, where all empties are gone at the time of Byzantium,
|
||||||
|
@ -399,7 +399,7 @@ func (evm *EVM) StaticCall(caller ContractRef, addr common.Address, input []byte
|
||||||
gas = contract.Gas
|
gas = contract.Gas
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
evm.StateDB.RevertToSnapshot(snapshot)
|
evm.StateDB.RevertSnapshot()
|
||||||
if err != ErrExecutionReverted {
|
if err != ErrExecutionReverted {
|
||||||
if evm.Config.Tracer != nil && evm.Config.Tracer.OnGasChange != nil {
|
if evm.Config.Tracer != nil && evm.Config.Tracer.OnGasChange != nil {
|
||||||
evm.Config.Tracer.OnGasChange(gas, 0, tracing.GasChangeCallFailedExecution)
|
evm.Config.Tracer.OnGasChange(gas, 0, tracing.GasChangeCallFailedExecution)
|
||||||
|
@ -408,7 +408,7 @@ func (evm *EVM) StaticCall(caller ContractRef, addr common.Address, input []byte
|
||||||
gas = 0
|
gas = 0
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
evm.StateDB.DiscardSnapshot(snapshot)
|
evm.StateDB.DiscardSnapshot()
|
||||||
}
|
}
|
||||||
return ret, gas, err
|
return ret, gas, err
|
||||||
}
|
}
|
||||||
|
@ -482,7 +482,7 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64,
|
||||||
// Create a new account on the state only if the object was not present.
|
// Create a new account on the state only if the object was not present.
|
||||||
// It might be possible the contract code is deployed to a pre-existent
|
// It might be possible the contract code is deployed to a pre-existent
|
||||||
// account with non-zero balance.
|
// account with non-zero balance.
|
||||||
snapshot := evm.StateDB.Snapshot()
|
evm.StateDB.Snapshot()
|
||||||
if !evm.StateDB.Exist(address) {
|
if !evm.StateDB.Exist(address) {
|
||||||
evm.StateDB.CreateAccount(address)
|
evm.StateDB.CreateAccount(address)
|
||||||
}
|
}
|
||||||
|
@ -516,12 +516,12 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64,
|
||||||
|
|
||||||
ret, err = evm.initNewContract(contract, address, value)
|
ret, err = evm.initNewContract(contract, address, value)
|
||||||
if err != nil && (evm.chainRules.IsHomestead || err != ErrCodeStoreOutOfGas) {
|
if err != nil && (evm.chainRules.IsHomestead || err != ErrCodeStoreOutOfGas) {
|
||||||
evm.StateDB.RevertToSnapshot(snapshot)
|
evm.StateDB.RevertSnapshot()
|
||||||
if err != ErrExecutionReverted {
|
if err != ErrExecutionReverted {
|
||||||
contract.UseGas(contract.Gas, evm.Config.Tracer, tracing.GasChangeCallFailedExecution)
|
contract.UseGas(contract.Gas, evm.Config.Tracer, tracing.GasChangeCallFailedExecution)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
evm.StateDB.DiscardSnapshot(snapshot)
|
evm.StateDB.DiscardSnapshot()
|
||||||
}
|
}
|
||||||
return ret, address, contract.Gas, err
|
return ret, address, contract.Gas, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -90,15 +90,14 @@ type StateDB interface {
|
||||||
|
|
||||||
Prepare(rules params.Rules, sender, coinbase common.Address, dest *common.Address, precompiles []common.Address, txAccesses types.AccessList)
|
Prepare(rules params.Rules, sender, coinbase common.Address, dest *common.Address, precompiles []common.Address, txAccesses types.AccessList)
|
||||||
|
|
||||||
// RevertToSnapshot reverts all state changes made since the given revision.
|
// Snapshot starts a new journalled scope.
|
||||||
RevertToSnapshot(int)
|
Snapshot()
|
||||||
|
// RevertSnapshot reverts all state changes made in the most recent journalled scope.
|
||||||
// DiscardSnapshot removes the snapshot with the given id; after calling this
|
RevertSnapshot()
|
||||||
// method, it is no longer possible to revert to that particular snapshot, the
|
// DiscardSnapshot removes the ability to roll back the changes in the most
|
||||||
// changes are considered part of the parent scope.
|
// recent journalled scope. After calling this method, the changes are considered
|
||||||
DiscardSnapshot(int)
|
// part of the parent scope.
|
||||||
// Snapshot returns an identifier for the current scope of the state.
|
DiscardSnapshot()
|
||||||
Snapshot() int
|
|
||||||
|
|
||||||
AddLog(*types.Log)
|
AddLog(*types.Log)
|
||||||
AddPreimage(common.Hash, []byte)
|
AddPreimage(common.Hash, []byte)
|
||||||
|
|
|
@ -219,7 +219,7 @@ func benchTracer(tracerName string, test *callTracerTest, b *testing.B) {
|
||||||
evm := vm.NewEVM(context, state.StateDB, test.Genesis.Config, vm.Config{})
|
evm := vm.NewEVM(context, state.StateDB, test.Genesis.Config, vm.Config{})
|
||||||
|
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
snap := state.StateDB.Snapshot()
|
state.StateDB.Snapshot()
|
||||||
tracer, err := tracers.DefaultDirectory.New(tracerName, new(tracers.Context), nil, test.Genesis.Config)
|
tracer, err := tracers.DefaultDirectory.New(tracerName, new(tracers.Context), nil, test.Genesis.Config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
b.Fatalf("failed to create call tracer: %v", err)
|
b.Fatalf("failed to create call tracer: %v", err)
|
||||||
|
@ -238,7 +238,7 @@ func benchTracer(tracerName string, test *callTracerTest, b *testing.B) {
|
||||||
if _, err = tracer.GetResult(); err != nil {
|
if _, err = tracer.GetResult(); err != nil {
|
||||||
b.Fatal(err)
|
b.Fatal(err)
|
||||||
}
|
}
|
||||||
state.StateDB.RevertToSnapshot(snap)
|
state.StateDB.RevertSnapshot()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -92,11 +92,11 @@ func BenchmarkTransactionTraceV2(b *testing.B) {
|
||||||
tracer.OnTxStart(evm.GetVMContext(), tx, msg.From)
|
tracer.OnTxStart(evm.GetVMContext(), tx, msg.From)
|
||||||
evm.Config.Tracer = tracer
|
evm.Config.Tracer = tracer
|
||||||
|
|
||||||
snap := state.StateDB.Snapshot()
|
state.StateDB.Snapshot()
|
||||||
_, err := core.ApplyMessage(evm, msg, new(core.GasPool).AddGas(tx.Gas()))
|
_, err := core.ApplyMessage(evm, msg, new(core.GasPool).AddGas(tx.Gas()))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
b.Fatal(err)
|
b.Fatal(err)
|
||||||
}
|
}
|
||||||
state.StateDB.RevertToSnapshot(snap)
|
state.StateDB.RevertSnapshot()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -302,13 +302,11 @@ func (miner *Miner) commitBlobTransaction(env *environment, tx *types.Transactio
|
||||||
|
|
||||||
// applyTransaction runs the transaction. If execution fails, state and gas pool are reverted.
|
// applyTransaction runs the transaction. If execution fails, state and gas pool are reverted.
|
||||||
func (miner *Miner) applyTransaction(env *environment, tx *types.Transaction) (*types.Receipt, error) {
|
func (miner *Miner) applyTransaction(env *environment, tx *types.Transaction) (*types.Receipt, error) {
|
||||||
var (
|
gp := env.gasPool.Gas()
|
||||||
snap = env.state.Snapshot()
|
env.state.Snapshot()
|
||||||
gp = env.gasPool.Gas()
|
|
||||||
)
|
|
||||||
receipt, err := core.ApplyTransaction(env.evm, env.gasPool, env.state, env.header, tx, &env.header.GasUsed)
|
receipt, err := core.ApplyTransaction(env.evm, env.gasPool, env.state, env.header, tx, &env.header.GasUsed)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
env.state.RevertToSnapshot(snap)
|
env.state.RevertSnapshot()
|
||||||
env.gasPool.SetGas(gp)
|
env.gasPool.SetGas(gp)
|
||||||
}
|
}
|
||||||
return receipt, err
|
return receipt, err
|
||||||
|
|
|
@ -316,7 +316,7 @@ func runBenchmark(b *testing.B, t *StateTest) {
|
||||||
)
|
)
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
for n := 0; n < b.N; n++ {
|
for n := 0; n < b.N; n++ {
|
||||||
snapshot := state.StateDB.Snapshot()
|
state.StateDB.Snapshot()
|
||||||
state.StateDB.Prepare(rules, msg.From, context.Coinbase, msg.To, vm.ActivePrecompiles(rules), msg.AccessList)
|
state.StateDB.Prepare(rules, msg.From, context.Coinbase, msg.To, vm.ActivePrecompiles(rules), msg.AccessList)
|
||||||
b.StartTimer()
|
b.StartTimer()
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
|
@ -333,7 +333,7 @@ func runBenchmark(b *testing.B, t *StateTest) {
|
||||||
refund += state.StateDB.GetRefund()
|
refund += state.StateDB.GetRefund()
|
||||||
gasUsed += msg.GasLimit - leftOverGas
|
gasUsed += msg.GasLimit - leftOverGas
|
||||||
|
|
||||||
state.StateDB.RevertToSnapshot(snapshot)
|
state.StateDB.RevertSnapshot()
|
||||||
}
|
}
|
||||||
if elapsed < 1 {
|
if elapsed < 1 {
|
||||||
elapsed = 1
|
elapsed = 1
|
||||||
|
|
|
@ -320,18 +320,18 @@ func (t *StateTest) RunNoVerify(subtest StateSubtest, vmconfig vm.Config, snapsh
|
||||||
tracer.OnTxStart(evm.GetVMContext(), nil, msg.From)
|
tracer.OnTxStart(evm.GetVMContext(), nil, msg.From)
|
||||||
}
|
}
|
||||||
// Execute the message.
|
// Execute the message.
|
||||||
snapshot := st.StateDB.Snapshot()
|
st.StateDB.Snapshot()
|
||||||
gaspool := new(core.GasPool)
|
gaspool := new(core.GasPool)
|
||||||
gaspool.AddGas(block.GasLimit())
|
gaspool.AddGas(block.GasLimit())
|
||||||
vmRet, err := core.ApplyMessage(evm, msg, gaspool)
|
vmRet, err := core.ApplyMessage(evm, msg, gaspool)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
st.StateDB.RevertToSnapshot(snapshot)
|
st.StateDB.RevertSnapshot()
|
||||||
if tracer := evm.Config.Tracer; tracer != nil && tracer.OnTxEnd != nil {
|
if tracer := evm.Config.Tracer; tracer != nil && tracer.OnTxEnd != nil {
|
||||||
evm.Config.Tracer.OnTxEnd(nil, err)
|
evm.Config.Tracer.OnTxEnd(nil, err)
|
||||||
}
|
}
|
||||||
return st, common.Hash{}, 0, err
|
return st, common.Hash{}, 0, err
|
||||||
}
|
}
|
||||||
st.StateDB.DiscardSnapshot(snapshot)
|
st.StateDB.DiscardSnapshot()
|
||||||
// Add 0-value mining reward. This only makes a difference in the cases
|
// Add 0-value mining reward. This only makes a difference in the cases
|
||||||
// where
|
// where
|
||||||
// - the coinbase self-destructed, or
|
// - the coinbase self-destructed, or
|
||||||
|
|
Loading…
Reference in New Issue