diff --git a/graft/coreth/core/blockchain.go b/graft/coreth/core/blockchain.go index 875e5ca35b05..1d3443bc8d48 100644 --- a/graft/coreth/core/blockchain.go +++ b/graft/coreth/core/blockchain.go @@ -230,13 +230,13 @@ func (c *CacheConfig) triedbConfig() *triedb.Config { log.Crit("Chain data directory must be specified for Firewood") } - config.DBOverride = firewood.Config{ - ChainDataDir: c.ChainDataDir, - CleanCacheSize: c.TrieCleanLimit * 1024 * 1024, - FreeListCacheEntries: firewood.Defaults.FreeListCacheEntries, - Revisions: uint(c.StateHistory), // must be at least 2 - ReadCacheStrategy: ffi.CacheAllReads, - ArchiveMode: !c.Pruning, + config.DBOverride = firewood.TrieDBConfig{ + DatabaseDir: c.ChainDataDir, + CacheSizeBytes: uint(c.TrieCleanLimit * 1024 * 1024), + FreeListCacheEntries: 40_000, // Firewood default + RevisionsInMemory: uint(c.StateHistory), // must be at least 2 + CacheStrategy: ffi.CacheAllReads, + Archive: !c.Pruning, }.BackendConstructor } return config diff --git a/graft/coreth/core/genesis_test.go b/graft/coreth/core/genesis_test.go index 75b112602054..ffaecc301d4a 100644 --- a/graft/coreth/core/genesis_test.go +++ b/graft/coreth/core/genesis_test.go @@ -304,9 +304,7 @@ func newDbConfig(t *testing.T, scheme string) *triedb.Config { case rawdb.PathScheme: return &triedb.Config{DBOverride: pathdb.Defaults.BackendConstructor} case customrawdb.FirewoodScheme: - fwCfg := firewood.Defaults - // Create a unique temporary directory for each test - fwCfg.ChainDataDir = t.TempDir() + fwCfg := firewood.DefaultConfig(t.TempDir()) return &triedb.Config{DBOverride: fwCfg.BackendConstructor} default: t.Fatalf("unknown scheme %s", scheme) diff --git a/graft/coreth/tests/state_test_util.go b/graft/coreth/tests/state_test_util.go index 502446f001f4..b54cb4426cd8 100644 --- a/graft/coreth/tests/state_test_util.go +++ b/graft/coreth/tests/state_test_util.go @@ -68,8 +68,7 @@ func MakePreState(db ethdb.Database, accounts types.GenesisAlloc, snapshotter bo case rawdb.PathScheme: tconf.DBOverride = pathdb.Defaults.BackendConstructor case customrawdb.FirewoodScheme: - cfg := firewood.Defaults - cfg.ChainDataDir = tempdir + cfg := firewood.DefaultConfig(tempdir) tconf.DBOverride = cfg.BackendConstructor default: panic("unknown trie database scheme" + scheme) diff --git a/graft/evm/firewood/hash_test.go b/graft/evm/firewood/hash_test.go index 184b307c6b59..5b676a4fdf11 100644 --- a/graft/evm/firewood/hash_test.go +++ b/graft/evm/firewood/hash_test.go @@ -77,8 +77,7 @@ func newFuzzState(t *testing.T) *fuzzState { }) firewoodMemdb := rawdb.NewMemoryDatabase() - fwCfg := Defaults // copy the defaults - fwCfg.ChainDataDir = t.TempDir() // Use a temporary directory for the Firewood + fwCfg := DefaultConfig(t.TempDir()) firewoodState := state.NewDatabaseWithConfig( firewoodMemdb, &triedb.Config{ diff --git a/graft/evm/firewood/triedb.go b/graft/evm/firewood/triedb.go index c495433042b4..4a5fe90e2962 100644 --- a/graft/evm/firewood/triedb.go +++ b/graft/evm/firewood/triedb.go @@ -63,24 +63,32 @@ type ProposalContext struct { Children []*ProposalContext } -type Config struct { - ChainDataDir string - CleanCacheSize int // Size of the clean cache in bytes - FreeListCacheEntries uint // Number of free list entries to cache - Revisions uint // Number of revisions to keep in memory (must be >= 2) - ReadCacheStrategy ffi.CacheStrategy - ArchiveMode bool +type TrieDBConfig struct { + DatabaseDir string + CacheSizeBytes uint + FreeListCacheEntries uint + RevisionsInMemory uint // must be >= 2 + CacheStrategy ffi.CacheStrategy + Archive bool } -// Note that `FilePath` is not specified, and must always be set by the user. -var Defaults = Config{ - CleanCacheSize: 1024 * 1024, // 1MB - FreeListCacheEntries: 40_000, - Revisions: 100, - ReadCacheStrategy: ffi.CacheAllReads, +// DefaultConfig returns a sensible TrieDBConfig with the given directory. +// The default config is: +// - CacheSizeBytes: 1MB +// - FreeListCacheEntries: 40,000 +// - RevisionsInMemory: 100 +// - CacheStrategy: [ffi.CacheAllReads] +func DefaultConfig(dir string) TrieDBConfig { + return TrieDBConfig{ + DatabaseDir: dir, + CacheSizeBytes: 1024 * 1024, // 1MB + FreeListCacheEntries: 40_000, + RevisionsInMemory: 100, + CacheStrategy: ffi.CacheAllReads, + } } -func (c Config) BackendConstructor(ethdb.Database) triedb.DBOverride { +func (c TrieDBConfig) BackendConstructor(ethdb.Database) triedb.DBOverride { db, err := New(c) if err != nil { log.Crit("firewood: error creating database", "error", err) @@ -89,7 +97,12 @@ func (c Config) BackendConstructor(ethdb.Database) triedb.DBOverride { } type TrieDB struct { - fwDisk *ffi.Database // The underlying Firewood database, used for storing proposals and revisions. + // The underlying Firewood database, used for storing proposals and revisions. + // This is exported with the knowledge that consumer will not close it and the latest state can be modified + // at any time via block execution. The consumer should only use for read operations, + // or ensure that writes occur outside of block execution. + Firewood *ffi.Database + proposalLock sync.RWMutex // proposalMap provides O(1) access by state root to all proposals stored in the proposalTree proposalMap map[common.Hash][]*ProposalContext @@ -102,19 +115,19 @@ type TrieDB struct { // New creates a new Firewood database with the given disk database and configuration. // Any error during creation will cause the program to exit. -func New(config Config) (*TrieDB, error) { - path := filepath.Join(config.ChainDataDir, firewoodDir) +func New(config TrieDBConfig) (*TrieDB, error) { + path := filepath.Join(config.DatabaseDir, firewoodDir) if err := validatePath(path); err != nil { return nil, err } options := []ffi.Option{ - ffi.WithNodeCacheEntries(uint(config.CleanCacheSize / 256)), // TODO(#4750): is 256 bytes per node a good estimate? + ffi.WithNodeCacheEntries(config.CacheSizeBytes / 256), // TODO(#4750): is 256 bytes per node a good estimate? ffi.WithFreeListCacheEntries(config.FreeListCacheEntries), - ffi.WithRevisions(config.Revisions), - ffi.WithReadCacheStrategy(config.ReadCacheStrategy), + ffi.WithRevisions(config.RevisionsInMemory), + ffi.WithReadCacheStrategy(config.CacheStrategy), } - if config.ArchiveMode { + if config.Archive { options = append(options, ffi.WithRootStore()) } @@ -129,7 +142,7 @@ func New(config Config) (*TrieDB, error) { } return &TrieDB{ - fwDisk: fw, + Firewood: fw, proposalMap: make(map[common.Hash][]*ProposalContext), proposalTree: &ProposalContext{ Root: common.Hash(currentRoot), @@ -173,7 +186,7 @@ func (*TrieDB) Scheme() string { // Initialized checks whether a non-empty genesis block has been written. func (t *TrieDB) Initialized(common.Hash) bool { - root, err := t.fwDisk.Root() + root, err := t.Firewood.Root() if err != nil { log.Error("firewood: error getting current root", "error", err) return false @@ -269,7 +282,7 @@ func (t *TrieDB) propose(root common.Hash, parentRoot common.Hash, hash common.H } log.Debug("firewood: proposing from database root", "root", root.Hex(), "height", block) - p, err := createProposal(t.fwDisk, root, keys, values) + p, err := createProposal(t.Firewood, root, keys, values) if err != nil { return err } @@ -329,7 +342,7 @@ func (t *TrieDB) Commit(root common.Hash, report bool) error { defer t.cleanupCommittedProposal(pCtx) // Assert that the root of the database matches the committed proposal root. - currentRoot, err := t.fwDisk.Root() + currentRoot, err := t.Firewood.Root() if err != nil { return fmt.Errorf("firewood: error getting current root after commit: %w", err) } @@ -382,7 +395,7 @@ func (t *TrieDB) Close() error { // Close the database // This may block momentarily while finalizers for Firewood objects run. - return t.fwDisk.Close(context.Background()) + return t.Firewood.Close(context.Background()) } // createProposal creates a new proposal from the given layer @@ -486,7 +499,7 @@ func (t *TrieDB) removeProposalFromMap(pCtx *ProposalContext) { // Reader retrieves a node reader belonging to the given state root. // An error will be returned if the requested state is not available. func (t *TrieDB) Reader(root common.Hash) (database.Reader, error) { - revision, err := t.fwDisk.Revision(ffi.Hash(root)) + revision, err := t.Firewood.Revision(ffi.Hash(root)) if err != nil { return nil, fmt.Errorf("firewood: unable to retrieve from root %s: %w", root.Hex(), err) } @@ -526,7 +539,7 @@ func (t *TrieDB) getProposalHash(parentRoot common.Hash, keys, values [][]byte) start := time.Now() if t.proposalTree.Root == parentRoot { // Propose from the database root. - p, err = t.fwDisk.Propose(keys, values) + p, err = t.Firewood.Propose(keys, values) if err != nil { return common.Hash{}, fmt.Errorf("firewood: error proposing from root %s: %w", parentRoot.Hex(), err) } diff --git a/graft/subnet-evm/core/blockchain.go b/graft/subnet-evm/core/blockchain.go index 026865b7002a..87e14cc0a6a8 100644 --- a/graft/subnet-evm/core/blockchain.go +++ b/graft/subnet-evm/core/blockchain.go @@ -242,13 +242,13 @@ func (c *CacheConfig) triedbConfig() *triedb.Config { log.Crit("Chain data directory must be specified for Firewood") } - config.DBOverride = firewood.Config{ - ChainDataDir: c.ChainDataDir, - CleanCacheSize: c.TrieCleanLimit * 1024 * 1024, - FreeListCacheEntries: firewood.Defaults.FreeListCacheEntries, - Revisions: uint(c.StateHistory), // must be at least 2 - ReadCacheStrategy: ffi.CacheAllReads, - ArchiveMode: !c.Pruning, + config.DBOverride = firewood.TrieDBConfig{ + DatabaseDir: c.ChainDataDir, + CacheSizeBytes: uint(c.TrieCleanLimit * 1024 * 1024), + FreeListCacheEntries: 40_000, // Firewood default + RevisionsInMemory: uint(c.StateHistory), // must be at least 2 + CacheStrategy: ffi.CacheAllReads, + Archive: !c.Pruning, }.BackendConstructor } return config diff --git a/graft/subnet-evm/core/genesis_test.go b/graft/subnet-evm/core/genesis_test.go index 119364956dc9..422c2db3cf4c 100644 --- a/graft/subnet-evm/core/genesis_test.go +++ b/graft/subnet-evm/core/genesis_test.go @@ -370,9 +370,7 @@ func newDbConfig(t *testing.T, scheme string) *triedb.Config { case rawdb.PathScheme: return &triedb.Config{DBOverride: pathdb.Defaults.BackendConstructor} case customrawdb.FirewoodScheme: - fwCfg := firewood.Defaults - // Create a unique temporary directory for each test - fwCfg.ChainDataDir = t.TempDir() + fwCfg := firewood.DefaultConfig(t.TempDir()) return &triedb.Config{DBOverride: fwCfg.BackendConstructor} default: t.Fatalf("unknown scheme %s", scheme) diff --git a/graft/subnet-evm/tests/state_test_util.go b/graft/subnet-evm/tests/state_test_util.go index 0e1f64bb085c..a47a097950ae 100644 --- a/graft/subnet-evm/tests/state_test_util.go +++ b/graft/subnet-evm/tests/state_test_util.go @@ -68,8 +68,7 @@ func MakePreState(db ethdb.Database, accounts types.GenesisAlloc, snapshotter bo case rawdb.PathScheme: tconf.DBOverride = pathdb.Defaults.BackendConstructor case customrawdb.FirewoodScheme: - cfg := firewood.Defaults - cfg.ChainDataDir = tempdir + cfg := firewood.DefaultConfig(tempdir) tconf.DBOverride = cfg.BackendConstructor default: panic("unknown trie database scheme" + scheme)