go-ethereum icon indicating copy to clipboard operation
go-ethereum copied to clipboard

core/state: changes to journalling, part 1

Open holiman opened this issue 1 year ago • 4 comments

This is a follow-up to #29520, and a preparatory PR to a more thorough change in the journalling system.

API methods instead of append operations

This PR hides the journal-implementation details away, so that the statedb invokes methods like JournalCreate, instead of explicitly appending journal-events in a list. This means that it's up to the journal whether to implement it as a sequence of events or aggregate/merge events.

Snapshot-management inside the journal

This PR also makes it so that management of valid snapshots is moved inside the journal, exposed via the methods Snapshot() int and RevertToSnapshot(revid int, s *StateDB).

SetCode

JournalSetCode journals the setting of code: it is implicit that the previous values were "no code" and emptyCodeHash. Therefore, we can simplify the setCode journal.

Selfdestruct

The self-destruct journalling is a bit strange: we allow the selfdestruct operation to be journalled several times. This makes it so that we also are forced to store whether the account was already destructed.

What we can do instead, is to only journal the first destruction, and after that only journal balance-changes, but not journal the selfdestruct itself.

This simplifies the journalling, so that internals about state management does not leak into the journal-API.

Preimages

Preimages were, for some reason, integrated into the journal management, despite not being a consensus-critical data structure. This PR undoes that.

holiman avatar Jan 26 '24 14:01 holiman

(content moved to PR description)

holiman avatar Jan 29 '24 10:01 holiman

Where these changes are leading to is the following:

type scopedJournal struct {
	accountChanges map[common.Address][]byte
	refund         int64
	logs           []types.Log
        .... 
}

The accountChanges are a representation of an account (probably rlp-data for types.SlimAccount). It is the entry-type which replaces individual balanceChange, nonceChange, creationChange, touchChange and selfdestructChange. We only ever need it once, per scope. Example:

1. add 1 to X
2. add 2 to X
3. set nonce=500 for X
4. X is selfdestructed
5. x is set to zero balance
6. revert

Instead of collecting this as 6 separate entries, it's sufficient if the very first add 1 to X stores the pre-state of X. After that, we can omit expanding the journal for this scope.

Therefore, it is good if we can remove implementation-flags such as destructed from entering the journal. And it's the same with code, if we can prevent code from entering into the journal, that change too can be encompassed by this system.

Example of how it can look:

// journalAccountChange is the common shared implementation for all account-changes.
// These changes all fall back to this method:
// - balance change
// - nonce change
// - creation change (in this case, the account is nil)
func (j *scopedJournal) journaAccountChange(address common.Address, account types.StateAccount) {
	// Unless the account has already been journalled, journal it now
	if _, ok := accountChanges[address]; !ok {
		if account != nil {
			accountChanges[address] = types.SlimAccountRLP(account)
		} else {
			accountChanges[address] = nil
		}
		j.dirties[address]++
	}
}
func (j *scopedJournal) JournalNonceChange(addr common.Address, account types.StateAccount) {
	j.journaAccountChange(addr, account)
}

func (j *scopedJournal) JournalBalanceChange(addr common.Address, account types.StateAccount) {
	j.journaAccountChange(address, account)
}

func (j *scopedJournal) JournalCreate(addr common.Address) {
	j.journaAccountChange(addr, nil)
}

holiman avatar Jan 29 '24 10:01 holiman

The effect of the changes -- not on this branch, but no the even more experimental journal_changes_pt2 The burntpix benchmark (the execution time differs pretty wildly between 7-11 seconds on both versions).

With master:

EVM gas used:    1194877856
execution time:  8.303515894s
allocations:     497_562
allocated bytes: 107_153_152

With set-based journal:

EVM gas used:    1194877856
execution time:  9.63126912s
allocations:     30_046
allocated bytes: 28_804_928

holiman avatar Jan 30 '24 12:01 holiman

Now rebased to follow on top of https://github.com/ethereum/go-ethereum/pull/29627

holiman avatar Apr 25 '24 06:04 holiman

Rebased

holiman avatar Aug 08 '24 07:08 holiman