Skip to content

Commit

Permalink
Builder Efficient reverts (#90)
Browse files Browse the repository at this point in the history
* Initial commit for efficient revert experimentation

* Add additional unit tests and fix unit test errors, reduce duplicate code, add support for sbundles, expose CLI flag and environment variable to enable multi-tx-snapshots

* Update godoc, remove unused getter for access list, add CLI flag to builder flag list, update log level for multi-tx-snap error

* Add retry logic for multi-tx-snapshot block build algorithm

* Update unit tests to test EnableMultiTxSnap

* Change account touch tracer to access list tracer for env changes

* Update greedy builder to use passed in algorithm configuration rather than default

* Add new multi-transaction snapshot stack to support more than one active snapshot, useful for cases like nested bundle applying and rollback, optional bundle discard, and bundle merging

* Clean up code, add comprehensive stack tests with fuzzing, fix edge cases where merge operation for stack commit was not properly updated

* Add refund support to efficient revert so state returns to correct refund value on discard

Address PR feedback, separate block build function initializing to de-dupe logic, split types into separate definitions

Add panic if copy is performed with non-empty stack of snapshots

Update env changes method name

Fix unit test and update log to trace

Remove refund in case it causes bad state

* Add unit tests for state comparison, potential fix for gas and root mismatch through snapshot revert and removal of tx rollback

* Debug commit

* DRY profit logic

* Revert commit

This rolls back to commit before fc32a84.


* Add fuzz state using state smart contract, add Copy method for multi-tx, pass gas limit for test setup, add abigen bindings and abi for compiled state fuzz test smart contract

* fix a bug with state reverts of accounts that are not touched according to the journal (#102)

* Simplify test

* Use different builders instead of configuration switch since major refactor required to handle more dynamic configurations.

* Update env changes to reduce redundancy and make control flow easier to follow

* Remove debug validation

* Update comments, add touch change to state fuzz test smart contract

* Add fuzz tests for transient storage and account touch operations

* Remove unused code

---------

Co-authored-by: Vitaly Drogan <[email protected]>
  • Loading branch information
Wazzymandias and dvush authored Sep 27, 2023
1 parent c5985be commit cd74c75
Show file tree
Hide file tree
Showing 24 changed files with 5,137 additions and 627 deletions.
692 changes: 692 additions & 0 deletions core/state/multi_tx_snapshot.go

Large diffs are not rendered by default.

929 changes: 929 additions & 0 deletions core/state/multi_tx_snapshot_test.go

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions core/state/state_object.go
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,9 @@ func (s *stateObject) setState(key, value common.Hash) {
func (s *stateObject) finalise(prefetch bool) {
slotsToPrefetch := make([][]byte, 0, len(s.dirtyStorage))
for key, value := range s.dirtyStorage {
prev, ok := s.pendingStorage[key]
s.db.multiTxSnapshotStack.UpdatePendingStorage(s.address, key, prev, ok)

s.pendingStorage[key] = value
if value != s.originStorage[key] {
slotsToPrefetch = append(slotsToPrefetch, common.CopyBytes(key[:])) // Copy needed for closure
Expand Down
40 changes: 40 additions & 0 deletions core/state/statedb.go
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,9 @@ type StateDB struct {
validRevisions []revision
nextRevisionId int

// Multi-Transaction Snapshot Stack
multiTxSnapshotStack *MultiTxSnapshotStack

// Measurements gathered during execution for debugging purposes
AccountReads time.Duration
AccountHashes time.Duration
Expand Down Expand Up @@ -151,6 +154,8 @@ func New(root common.Hash, db Database, snaps *snapshot.Tree) (*StateDB, error)
transientStorage: newTransientStorage(),
hasher: crypto.NewKeccakState(),
}

sdb.multiTxSnapshotStack = NewMultiTxSnapshotStack(sdb)
if sdb.snaps != nil {
if sdb.snap = sdb.snaps.Snapshot(root); sdb.snap != nil {
sdb.snapAccounts = make(map[common.Hash][]byte)
Expand Down Expand Up @@ -712,6 +717,8 @@ func (s *StateDB) Copy() *StateDB {
journal: newJournal(),
hasher: crypto.NewKeccakState(),
}
// Initialize copy of multi-transaction snapshot stack for the copied state
state.multiTxSnapshotStack = s.multiTxSnapshotStack.Copy(state)
// Copy the dirty states, logs, and preimages
for addr := range s.journal.dirties {
// As documented [here](https://github.com/ethereum/go-ethereum/pull/16485#issuecomment-380438527),
Expand Down Expand Up @@ -842,6 +849,8 @@ func (s *StateDB) GetRefund() uint64 {
// the journal as well as the refunds. Finalise, however, will not push any updates
// into the tries just yet. Only IntermediateRoot or Commit will do that.
func (s *StateDB) Finalise(deleteEmptyObjects bool) {
s.multiTxSnapshotStack.UpdateFromJournal(s.journal)

addressesToPrefetch := make([][]byte, 0, len(s.journal.dirties))
for addr := range s.journal.dirties {
obj, exist := s.stateObjects[addr]
Expand All @@ -855,6 +864,8 @@ func (s *StateDB) Finalise(deleteEmptyObjects bool) {
continue
}
if obj.suicided || (deleteEmptyObjects && obj.empty()) {
s.multiTxSnapshotStack.UpdateObjectDeleted(obj.address, obj.deleted)

obj.deleted = true

// We need to maintain account deletions explicitly (will remain
Expand All @@ -872,6 +883,12 @@ func (s *StateDB) Finalise(deleteEmptyObjects bool) {
} else {
obj.finalise(true) // Prefetch slots in the background
}

if s.multiTxSnapshotStack.Size() > 0 {
_, wasPending := s.stateObjectsPending[addr]
_, wasDirty := s.stateObjectsDirty[addr]
s.multiTxSnapshotStack.UpdatePendingStatus(addr, wasPending, wasDirty)
}
s.stateObjectsPending[addr] = struct{}{}
s.stateObjectsDirty[addr] = struct{}{}

Expand All @@ -894,6 +911,10 @@ func (s *StateDB) IntermediateRoot(deleteEmptyObjects bool) common.Hash {
// Finalise all the dirty storage states and write them into the tries
s.Finalise(deleteEmptyObjects)

// Intermediate root writes updates to the trie, which will cause
// in memory multi-transaction snapshot to be incompatible with the committed state, so we invalidate.
s.multiTxSnapshotStack.Invalidate()

// If there was a trie prefetcher operating, it gets aborted and irrevocably
// modified after we start retrieving tries. Remove it from the statedb after
// this round of use.
Expand Down Expand Up @@ -1181,3 +1202,22 @@ func (s *StateDB) convertAccountSet(set map[common.Address]struct{}) map[common.
}
return ret
}

func (s *StateDB) NewMultiTxSnapshot() (err error) {
_, err = s.multiTxSnapshotStack.NewSnapshot()
return
}

func (s *StateDB) MultiTxSnapshotRevert() (err error) {
_, err = s.multiTxSnapshotStack.Revert()
return
}

func (s *StateDB) MultiTxSnapshotCommit() (err error) {
_, err = s.multiTxSnapshotStack.Commit()
return
}

func (s *StateDB) MultiTxSnapshotStackSize() int {
return s.multiTxSnapshotStack.Size()
}
Loading

0 comments on commit cd74c75

Please sign in to comment.