@@ -53,10 +53,12 @@ var (
5353 _ triedb.DBOverride = (* Database )(nil )
5454)
5555
56- // A Database is an implementation of [triedb.DBOverride].
56+ // Database is a triedb.DBOverride implementation backed by Firewood.
57+ // It acts as a HashDB for backwards compatibility with most of the blockchain code.
5758type Database struct {
5859 // The underlying Firewood database, used for storing proposals and revisions.
59- // This must be exported so other packages (e.g. state sync) can access firewood-specific methods.
60+ // This is exported as read-only, with knowledge that the consumer will not close it
61+ // and the latest state can be modified at any time.
6062 Firewood * ffi.Database
6163
6264 proposals
@@ -71,7 +73,7 @@ type proposals struct {
7173 // in the case of duplicate state roots.
7274 // The root of the tree is stored here, and represents the top-most layer on disk.
7375 tree * proposal
74- // possibleProposals temporarily holds proposals created during a trie update.
76+ // possible temporarily holds proposals created during a trie update.
7577 // This is cleared after the update is complete and the proposals have been sent to the database.
7678 possible []* proposal
7779}
@@ -95,22 +97,35 @@ type proposalMeta struct {
9597 height uint64
9698}
9799
100+ // Config provides necessary parameters for creating a Firewood database.
98101type Config struct {
99- ChainDir string
100- CleanCacheSize int
102+ DatabasePath string
103+ CacheSizeBytes uint
101104 FreeListCacheEntries uint
102- Revisions uint
103- ReadCacheStrategy ffi.CacheStrategy
105+ MaxRevisions uint
106+ CacheStrategy ffi.CacheStrategy
104107}
105108
106- // Note that `FilePath` is not specified, and must always be set by the user.
107- var Defaults = Config {
108- CleanCacheSize : 1024 * 1024 , // 1MB
109- FreeListCacheEntries : 40_000 ,
110- Revisions : 100 ,
111- ReadCacheStrategy : ffi .CacheAllReads ,
109+ // DefaultConfig returns a default Config with the given directory.
110+ // The default config is:
111+ // - CacheSizeBytes: 1MB
112+ // - FreeListCacheEntries: 40,000
113+ // - MaxRevisions: 100
114+ // - CacheStrategy: [ffi.CacheAllReads]
115+ func DefaultConfig (dir string ) Config {
116+ return Config {
117+ DatabasePath : dir ,
118+ CacheSizeBytes : 1024 * 1024 , // 1MB
119+ FreeListCacheEntries : 40_000 ,
120+ MaxRevisions : 100 ,
121+ CacheStrategy : ffi .CacheAllReads ,
122+ }
112123}
113124
125+ // BackendConstructor implements the [triedb.DBConstructor] interface.
126+ // It creates a new Firewood database with the given configuration.
127+ // Since Firewood uses its own on-disk format, the provided ethdb.Database is ignored.
128+ // Any error during creation will cause the program to exit.
114129func (c Config ) BackendConstructor (ethdb.Database ) triedb.DBOverride {
115130 db , err := New (c )
116131 if err != nil {
@@ -119,26 +134,26 @@ func (c Config) BackendConstructor(ethdb.Database) triedb.DBOverride {
119134 return db
120135}
121136
122- // New creates a new Firewood database with the given disk database and configuration.
123- // Any error during creation will cause the program to exit .
137+ // New creates a new Firewood database with the given configuration.
138+ // The database will not be opened on error .
124139func New (config Config ) (* Database , error ) {
125- if err := validateDir (config .ChainDir ); err != nil {
140+ if err := validateDir (config .DatabasePath ); err != nil {
126141 return nil , err
127142 }
128143
129- logPath := filepath .Join (config .ChainDir , logFileName )
144+ logPath := filepath .Join (config .DatabasePath , logFileName )
130145 if err := ffi .StartLogs (& ffi.LogConfig {Path : logPath }); err != nil {
131146 // This shouldn't be a fatal error, as this can only be called once per thread.
132147 // Specifically, this will return an error in unit tests.
133148 log .Warn ("firewood: error starting logs" , "error" , err )
134149 }
135150
136- dbPath := filepath .Join (config .ChainDir , dbFileName )
151+ dbPath := filepath .Join (config .DatabasePath , dbFileName )
137152 fw , err := ffi .New (dbPath , & ffi.Config {
138- NodeCacheEntries : uint ( config .CleanCacheSize ) / 256 , // TODO(alarso16): 256 bytes may not be accurate
153+ NodeCacheEntries : config .CacheSizeBytes / 256 , // TODO(alarso16): 256 bytes per node may not be accurate
139154 FreeListCacheEntries : config .FreeListCacheEntries ,
140- Revisions : config .Revisions ,
141- ReadCacheStrategy : config .ReadCacheStrategy ,
155+ Revisions : config .MaxRevisions ,
156+ ReadCacheStrategy : config .CacheStrategy ,
142157 })
143158 if err != nil {
144159 return nil , err
@@ -176,7 +191,7 @@ func validateDir(dir string) error {
176191 switch info , err := os .Stat (dir ); {
177192 case os .IsNotExist (err ):
178193 log .Info ("Database directory not found, creating" , "path" , dir )
179- if err := os .MkdirAll (dir , 0o755 ); err != nil {
194+ if err := os .MkdirAll (dir , 0o750 ); err != nil {
180195 return fmt .Errorf ("creating database directory: %v" , err )
181196 }
182197 return nil
@@ -222,6 +237,8 @@ func (*Database) Size() (common.StorageSize, common.StorageSize) {
222237 return 0 , 0
223238}
224239
240+ // Reference is no-op because proposals are only referenced when created.
241+ // Additionally, internal nodes do not need tracked by consumers.
225242func (* Database ) Reference (common.Hash , common.Hash ) {}
226243
227244// Dereference is no-op because unused proposals are dereferenced when no longer valid.
@@ -238,6 +255,11 @@ func (*Database) Cap(common.StorageSize) error {
238255 return nil
239256}
240257
258+ // Close closes the database, freeing all associated resources.
259+ // This may hang for a short period while waiting for finalizers to complete.
260+ // If it does not close as expected, this indicates that there are still references
261+ // to proposals or revisions in memory, and an error will be returned.
262+ // The database should not be used after calling Close.
241263func (db * Database ) Close () error {
242264 p := & db .proposals
243265 p .Lock ()
@@ -253,6 +275,11 @@ func (db *Database) Close() error {
253275 return db .Firewood .Close (context .Background ())
254276}
255277
278+ // Update updates the database to the given root at the given height.
279+ // The parent block hash and block hash must be provided in the options.
280+ // A proposal must have already been created from [AccountTrie.Commit] with the same root,
281+ // parent root, and height.
282+ // If no such proposal exists, an error will be returned.
256283func (db * Database ) Update (root , parent common.Hash , height uint64 , _ * trienode.MergedNodeSet , _ * triestate.Set , opts ... stateconf.TrieDBUpdateOption ) error {
257284 // We require block hashes to be provided for all blocks in production.
258285 // However, many tests cannot reasonably provide a block blockHash for genesis, so we allow it to be omitted.
@@ -360,7 +387,7 @@ func find[S ~[]E, E comparable](s S, fn func(E) bool) (E, bool) {
360387// Any time this is called, we expect either:
361388// 1. The root is the same as the current root of the database (empty block during bootstrapping)
362389// 2. We have created a valid propsal with that root, and it is of height +1 above the proposal tree root.
363- // Additionally, this should be unique.
390+ // Additionally, this will be unique.
364391//
365392// Afterward, we know that no other proposal at this height can be committed, so we can dereference all
366393// children in the the other branches of the proposal tree.
0 commit comments