Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions core/state/statedb.go
Original file line number Diff line number Diff line change
Expand Up @@ -1152,7 +1152,7 @@ func (s *StateDB) handleDestruction(nodes *trienode.MergedNodeSet) (map[common.A
//
// The associated block number of the state transition is also provided
// for more chain context.
func (s *StateDB) Commit(block uint64, deleteEmptyObjects bool, opts ...stateconf.SnapshotUpdateOption) (common.Hash, error) {
func (s *StateDB) Commit(block uint64, deleteEmptyObjects bool, opts ...stateconf.StateDBCommitOption) (common.Hash, error) {
// Short circuit in case any database failure occurred earlier.
if s.dbErr != nil {
return common.Hash{}, fmt.Errorf("commit aborted due to earlier error: %v", s.dbErr)
Expand Down Expand Up @@ -1242,7 +1242,7 @@ func (s *StateDB) Commit(block uint64, deleteEmptyObjects bool, opts ...statecon
start := time.Now()
// Only update if there's a state transition (skip empty Clique blocks)
if parent := s.snap.Root(); parent != root {
if err := s.snaps.Update(root, parent, s.convertAccountSet(s.stateObjectsDestruct), s.accounts, s.storages, opts...); err != nil {
if err := s.snaps.Update(root, parent, s.convertAccountSet(s.stateObjectsDestruct), s.accounts, s.storages, stateconf.ExtractSnapshotUpdateOpts(opts...)...); err != nil {
log.Warn("Failed to update snapshot tree", "from", parent, "to", root, "err", err)
}
// Keep 128 diff layers in the memory, persistent layer is 129th.
Expand All @@ -1268,7 +1268,7 @@ func (s *StateDB) Commit(block uint64, deleteEmptyObjects bool, opts ...statecon
if root != origin {
start := time.Now()
set := triestate.New(s.accountsOrigin, s.storagesOrigin, incomplete)
if err := s.db.TrieDB().Update(root, origin, block, nodes, set); err != nil {
if err := s.db.TrieDB().Update(root, origin, block, nodes, set, stateconf.ExtractTrieDBUpdateOpts(opts...)...); err != nil {
return common.Hash{}, err
}
s.originalRoot = root
Expand Down
64 changes: 56 additions & 8 deletions core/state/statedb.libevm_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,23 +26,48 @@ import (
"github.com/ava-labs/libevm/core/rawdb"
"github.com/ava-labs/libevm/core/state/snapshot"
"github.com/ava-labs/libevm/core/types"
"github.com/ava-labs/libevm/ethdb"
"github.com/ava-labs/libevm/libevm/stateconf"
"github.com/ava-labs/libevm/trie"
"github.com/ava-labs/libevm/trie/trienode"
"github.com/ava-labs/libevm/trie/triestate"
"github.com/ava-labs/libevm/triedb"
"github.com/ava-labs/libevm/triedb/database"
"github.com/ava-labs/libevm/triedb/hashdb"
)

func TestStateDBCommitPropagatesOptions(t *testing.T) {
var rec snapTreeRecorder
sdb, err := New(types.EmptyRootHash, NewDatabase(rawdb.NewMemoryDatabase()), &rec)
memdb := rawdb.NewMemoryDatabase()
trieRec := &triedbRecorder{Database: hashdb.New(memdb, nil, &trie.MerkleResolver{})}
triedb := triedb.NewDatabase(
memdb,
&triedb.Config{
DBOverride: func(_ ethdb.Database) triedb.DBOverride {
return trieRec
},
},
)
var snapRec snapTreeRecorder
sdb, err := New(types.EmptyRootHash, NewDatabaseWithNodeDB(memdb, triedb), &snapRec)
require.NoError(t, err, "New()")

// Ensures that rec.Update() will be called.
sdb.SetNonce(common.Address{}, 42)

const payload = "hello world"
opt := stateconf.WithUpdatePayload(payload)
_, err = sdb.Commit(0, false, opt)
require.NoErrorf(t, err, "%T.Commit(..., %T)", sdb, opt)
const snapshotPayload = "hello world"
var (
parentHash = common.HexToHash("0x0102030405060708090a0b0c0d0e0f1011121314151617181920212223242526")
currentHash = common.HexToHash("0x1234567890123456789012345678901234567890123456789012345678901234")
)
snapshotOpt := stateconf.WithSnapshotUpdatePayload(snapshotPayload)
triedbOpt := stateconf.WithTrieDBUpdatePayload(parentHash, currentHash)
_, err = sdb.Commit(0, false, stateconf.WithSnapshotUpdateOpts(snapshotOpt), stateconf.WithTrieDBUpdateOpts(triedbOpt))

assert.Equalf(t, payload, rec.gotPayload, "%T payload propagated via %T.Commit() to %T.Update()", opt, sdb, rec)
require.NoErrorf(t, err, "%T.Commit(..., %T, %T)", sdb, snapshotOpt, triedbOpt)
assert.Equalf(t, snapshotPayload, snapRec.gotPayload, "%T payload propagated via %T.Commit() to %T.Update()", snapshotOpt, sdb, snapRec)
assert.Truef(t, trieRec.exists, "%T exists propagated via %T.Commit() to %T.Update()", triedbOpt, sdb, trieRec)
assert.Equalf(t, parentHash, trieRec.parentBlockHash, "%T parentHash propagated via %T.Commit() to %T.Update()", triedbOpt, sdb, trieRec)
assert.Equalf(t, currentHash, trieRec.currentBlockHash, "%T currentHash propagated via %T.Commit() to %T.Update()", triedbOpt, sdb, trieRec)
}

type snapTreeRecorder struct {
Expand All @@ -59,7 +84,7 @@ func (r *snapTreeRecorder) Update(
_ map[common.Hash]struct{}, _ map[common.Hash][]byte, _ map[common.Hash]map[common.Hash][]byte,
opts ...stateconf.SnapshotUpdateOption,
) error {
r.gotPayload = stateconf.ExtractUpdatePayload(opts...)
r.gotPayload = stateconf.ExtractSnapshotUpdatePayload(opts...)
return nil
}

Expand All @@ -78,3 +103,26 @@ func (snapshotStub) Account(common.Hash) (*types.SlimAccount, error) {
func (snapshotStub) Root() common.Hash {
return common.Hash{}
}

type triedbRecorder struct {
*hashdb.Database
parentBlockHash common.Hash
currentBlockHash common.Hash
exists bool
}

func (r *triedbRecorder) Update(
root common.Hash,
parent common.Hash,
block uint64,
nodes *trienode.MergedNodeSet,
states *triestate.Set,
opts ...stateconf.TrieDBUpdateOption,
) error {
r.parentBlockHash, r.currentBlockHash, r.exists = stateconf.ExtractTrieDBUpdatePayload(opts...)
return r.Database.Update(root, parent, block, nodes, states)
}

func (r *triedbRecorder) Reader(_ common.Hash) (database.Reader, error) {
return r.Database.Reader(common.Hash{})
}
80 changes: 75 additions & 5 deletions libevm/stateconf/conf.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,48 @@
// Package stateconf configures state management.
package stateconf

import "github.com/ava-labs/libevm/libevm/options"
import (
"github.com/ava-labs/libevm/common"
"github.com/ava-labs/libevm/libevm/options"
)

// A StateDBCommitOption configures the behaviour of state.StateDB.Commit()
type StateDBCommitOption = options.Option[stateDBCommitConfig]

type stateDBCommitConfig struct {
snapshotOpts []SnapshotUpdateOption
triedbOpts []TrieDBUpdateOption
}

// WithSnapshotUpdateOpts returns a StateDBCommitOption carrying a list of
// SnapshotUpdateOptions.
// If multiple such options are used, only the last will be applied as they overwrite each other.
func WithSnapshotUpdateOpts(opts ...SnapshotUpdateOption) StateDBCommitOption {
return options.Func[stateDBCommitConfig](func(c *stateDBCommitConfig) {
c.snapshotOpts = opts
})
}

// ExtractSnapshotUpdateOpts returns the list of SnapshotUpdateOptions carried
// by the provided slice of StateDBCommitOption.
func ExtractSnapshotUpdateOpts(opts ...StateDBCommitOption) []SnapshotUpdateOption {
return options.As(opts...).snapshotOpts
}

// WithTrieDBUpdateOpts returns a StateDBCommitOption carrying a list of
// TrieDBUpdateOptions. If multiple such options are used, only the last will be
// applied as they overwrite each other.
func WithTrieDBUpdateOpts(opts ...TrieDBUpdateOption) StateDBCommitOption {
return options.Func[stateDBCommitConfig](func(c *stateDBCommitConfig) {
c.triedbOpts = opts
})
}

// ExtractTrieDBUpdateOpts returns the list of TrieDBUpdateOptions carried by
// the provided slice of StateDBCommitOption.
func ExtractTrieDBUpdateOpts(opts ...StateDBCommitOption) []TrieDBUpdateOption {
return options.As(opts...).triedbOpts
}

// A SnapshotUpdateOption configures the behaviour of
// state.SnapshotTree.Update() implementations. This will be removed along with
Expand All @@ -28,18 +69,47 @@ type snapshotUpdateConfig struct {
payload any
}

// WithUpdatePayload returns a SnapshotUpdateOption carrying an arbitrary
// WithSnapshotUpdatePayload returns a SnapshotUpdateOption carrying an arbitrary
// payload. It acts only as a carrier to exploit existing function plumbing and
// the effect on behaviour is left to the implementation receiving it.
func WithUpdatePayload(p any) SnapshotUpdateOption {
func WithSnapshotUpdatePayload(p any) SnapshotUpdateOption {
return options.Func[snapshotUpdateConfig](func(c *snapshotUpdateConfig) {
c.payload = p
})
}

// ExtractUpdatePayload returns the payload carried by a [WithUpdatePayload]
// ExtractSnapshotUpdatePayload returns the payload carried by a [WithSnapshotUpdatePayload]
// option. Only one such option can be used at once; behaviour is otherwise
// undefined.
func ExtractUpdatePayload(opts ...SnapshotUpdateOption) any {
func ExtractSnapshotUpdatePayload(opts ...SnapshotUpdateOption) any {
return options.As(opts...).payload
}

// A TrieDBUpdateOption configures the behaviour of triedb.Database.Update() implementations.
type TrieDBUpdateOption = options.Option[triedbUpdateConfig]

type triedbUpdateConfig struct {
parentBlockHash *common.Hash
currentBlockHash *common.Hash
}

// WithTrieDBUpdatePayload returns a TrieDBUpdateOption carrying two block hashes.
// It acts only as a carrier to exploit existing function plumbing and
// the effect on behaviour is left to the implementation receiving it.
func WithTrieDBUpdatePayload(parent common.Hash, current common.Hash) TrieDBUpdateOption {
return options.Func[triedbUpdateConfig](func(c *triedbUpdateConfig) {
c.parentBlockHash = &parent
c.currentBlockHash = &current
})
}

// ExtractTrieDBUpdatePayload returns the payload carried by a [WithTrieDBUpdatePayload]
// option. Only one such option can be used at once; behaviour is otherwise
// undefined.
func ExtractTrieDBUpdatePayload(opts ...TrieDBUpdateOption) (common.Hash, common.Hash, bool) {
conf := options.As(opts...)
if conf.parentBlockHash == nil && conf.currentBlockHash == nil {
return common.Hash{}, common.Hash{}, false
}
return *conf.parentBlockHash, *conf.currentBlockHash, true
}
7 changes: 4 additions & 3 deletions triedb/database.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (

"github.com/ava-labs/libevm/common"
"github.com/ava-labs/libevm/ethdb"
"github.com/ava-labs/libevm/libevm/stateconf"
"github.com/ava-labs/libevm/log"
"github.com/ava-labs/libevm/trie"
"github.com/ava-labs/libevm/trie/trienode"
Expand Down Expand Up @@ -70,7 +71,7 @@ type backend interface {
//
// The passed in maps(nodes, states) will be retained to avoid copying
// everything. Therefore, these maps must not be changed afterwards.
Update(root common.Hash, parent common.Hash, block uint64, nodes *trienode.MergedNodeSet, states *triestate.Set) error
Update(root common.Hash, parent common.Hash, block uint64, nodes *trienode.MergedNodeSet, states *triestate.Set, opts ...stateconf.TrieDBUpdateOption) error

// Commit writes all relevant trie nodes belonging to the specified state
// to disk. Report specifies whether logs will be displayed in info level.
Expand Down Expand Up @@ -148,11 +149,11 @@ func (db *Database) Reader(blockRoot common.Hash) (database.Reader, error) {
//
// The passed in maps(nodes, states) will be retained to avoid copying everything.
// Therefore, these maps must not be changed afterwards.
func (db *Database) Update(root common.Hash, parent common.Hash, block uint64, nodes *trienode.MergedNodeSet, states *triestate.Set) error {
func (db *Database) Update(root common.Hash, parent common.Hash, block uint64, nodes *trienode.MergedNodeSet, states *triestate.Set, opts ...stateconf.TrieDBUpdateOption) error {
if db.preimages != nil {
db.preimages.commit(false)
}
return db.backend.Update(root, parent, block, nodes, states)
return db.backend.Update(root, parent, block, nodes, states, opts...)
}

// Commit iterates over all the children of a particular node, writes them out
Expand Down
3 changes: 2 additions & 1 deletion triedb/hashdb/database.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import (
"github.com/ava-labs/libevm/core/rawdb"
"github.com/ava-labs/libevm/core/types"
"github.com/ava-labs/libevm/ethdb"
"github.com/ava-labs/libevm/libevm/stateconf"
"github.com/ava-labs/libevm/log"
"github.com/ava-labs/libevm/metrics"
"github.com/ava-labs/libevm/rlp"
Expand Down Expand Up @@ -548,7 +549,7 @@ func (db *Database) Initialized(genesisRoot common.Hash) bool {

// Update inserts the dirty nodes in provided nodeset into database and link the
// account trie with multiple storage tries if necessary.
func (db *Database) Update(root common.Hash, parent common.Hash, block uint64, nodes *trienode.MergedNodeSet, states *triestate.Set) error {
func (db *Database) Update(root common.Hash, parent common.Hash, block uint64, nodes *trienode.MergedNodeSet, states *triestate.Set, _ ...stateconf.TrieDBUpdateOption) error {
// Ensure the parent state is present and signal a warning if not.
if parent != types.EmptyRootHash {
if blob, _ := db.node(parent); len(blob) == 0 {
Expand Down
3 changes: 2 additions & 1 deletion triedb/pathdb/database.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import (
"github.com/ava-labs/libevm/core/rawdb"
"github.com/ava-labs/libevm/core/types"
"github.com/ava-labs/libevm/ethdb"
"github.com/ava-labs/libevm/libevm/stateconf"
"github.com/ava-labs/libevm/log"
"github.com/ava-labs/libevm/params"
"github.com/ava-labs/libevm/trie/trienode"
Expand Down Expand Up @@ -223,7 +224,7 @@ func (db *Database) Reader(root common.Hash) (layer, error) {
//
// The passed in maps(nodes, states) will be retained to avoid copying everything.
// Therefore, these maps must not be changed afterwards.
func (db *Database) Update(root common.Hash, parentRoot common.Hash, block uint64, nodes *trienode.MergedNodeSet, states *triestate.Set) error {
func (db *Database) Update(root common.Hash, parentRoot common.Hash, block uint64, nodes *trienode.MergedNodeSet, states *triestate.Set, _ ...stateconf.TrieDBUpdateOption) error {
// Hold the lock to prevent concurrent mutations.
db.lock.Lock()
defer db.lock.Unlock()
Expand Down