package ffi

import "github.com/ava-labs/firewood/ffi"

Package ffi provides a Go wrapper around the Firewood database.

Index

Constants

const RootLength = C.sizeof_HashKey

RootLength is the hash length for all Firewood hashes.

Variables

var (
	// ErrDroppedRevision wraps [ErrDropped].
	ErrDroppedRevision       = fmt.Errorf("revision %w", ErrDropped)
	ErrRevisionNotFound      = errors.New("revision not found")
	ErrEndRevisionNotFound   = errors.New("end revision not found")
	ErrStartRevisionNotFound = errors.New("start revision not found")
)
var ErrDropped = errors.New("already dropped")

ErrDropped is the shared sentinel wrapped by every dropped-handle error in this package: ErrDroppedRevision, ErrDroppedReconstructed, and the unexported proposal and iterator equivalents. Callers that don't care which handle type was invalidated — for example after WithForceCloseHandles drops every outstanding handle — can match all of them with errors.Is(err, ErrDropped).

var ErrDroppedReconstructed = fmt.Errorf("reconstructed view %w", ErrDropped)

ErrDroppedReconstructed wraps ErrDropped.

var ErrEthProofNotSupported = errors.New("eth_getProof requires ethereum hash mode")

ErrEthProofNotSupported is returned by Revision.EthGetProof and Reconstructed.EthGetProof when the database is not running in Ethereum hash mode and therefore cannot produce an eth_getProof-compatible proof.

Functions

func FlushBlockReplay

func FlushBlockReplay() error

FlushBlockReplay flushes buffered block replay operations to disk.

This function is only meaningful when the FFI library was compiled with the `block-replay` feature and the `FIREWOOD_BLOCK_REPLAY_PATH` environment variable is set. Otherwise, it is a no-op.

Note: the recording and flushing is not handled per db, and this will flush all logs, if multiple databases are open.

func GatherRenderedMetrics

func GatherRenderedMetrics() ([]*dto.MetricFamily, error)

GatherRenderedMetrics collects structured metrics from the global recorder and returns them as prometheus protobuf metric families. Returns an error if the global recorder is not initialized. This method must be called after StartMetrics.

The result merges Rust-side metrics (via the native recorder) with Go-side metrics (proof serialization timing) from an independent Go prometheus registry.

func StartLogs

func StartLogs(config *LogConfig) error

StartLogs initialized the log factor in the Rust library with the provided configuration. This function only needs to be called once. An error is returned if this method is called a second time.

func StartMetrics

func StartMetrics() error

StartMetrics starts the global recorder for metrics. This function only needs to be called once. This is best used in conjunction with the Gatherer type to collect metrics.

Types

type BatchOp

type BatchOp struct {
	// contains filtered or unexported fields
}

BatchOp represents a single batch operation to be applied to the database. Create BatchOp values using the Put, Delete, and PrefixDelete functions.

BatchOp uses an explicit tagged representation so that the intent of each operation is unambiguous. In particular, a Put with an empty (zero-length) value stores an empty value — it does NOT delete the key. Use Delete or PrefixDelete to remove keys.

func Delete
func Delete(key []byte) BatchOp

Delete creates a BatchOp that deletes a specific key.

func PrefixDelete
func PrefixDelete(prefix []byte) BatchOp

PrefixDelete creates a BatchOp that deletes all keys with the given prefix.

func Put
func Put(key, value []byte) BatchOp

Put creates a BatchOp that inserts or updates a key with a value. The value may be empty (zero-length) to store an empty value.

type Borrower

type Borrower interface {
	// BorrowedBytes returns a slice of bytes that borrows the data from the
	// Borrower's internal memory.
	//
	// The returned slice is valid only as long as the Borrower is valid.
	// If the Borrower is freed, the slice will become invalid.
	BorrowedBytes() []byte

	// CopiedBytes returns a slice of bytes that is a copy of the Borrower's
	// internal memory.
	//
	// This is fully independent of the borrowed data and is valid even after
	// the Borrower is freed.
	CopiedBytes() []byte

	// Free releases the memory associated with the Borrower's data.
	//
	// It is safe to call this method multiple times. Subsequent calls will
	// do nothing if the data has already been freed (or was never set).
	//
	// However, it is not safe to call this method concurrently from multiple
	// goroutines. It is also not safe to call this method while there are
	// outstanding references to the slice returned by BorrowedBytes. Any
	// existing slices will become invalid and may cause undefined behavior
	// if used after the Free call.
	Free() error
}

Borrower is an interface for types that can borrow or copy bytes returned from FFI methods.

type CacheStrategy

type CacheStrategy uint8

A CacheStrategy represents the caching strategy used by a Database.

const (
	// OnlyCacheWrites caches only writes.
	OnlyCacheWrites CacheStrategy = iota
	// CacheBranchReads caches intermediate reads and writes.
	CacheBranchReads
	// CacheAllReads caches all reads and writes.
	CacheAllReads
)

type ChangeProof

type ChangeProof struct {
	// contains filtered or unexported fields
}

ChangeProof represents a proof of changes between two roots for a range of keys.

func (*ChangeProof) CodeHashes
func (p *ChangeProof) CodeHashes() ([]Hash, error)

CodeHashes returns the contract code hashes referenced by the account values this proof puts, on the same terms as RangeProof.CodeHashes. Delete and DeleteRange entries are skipped.

Only Ethereum-mode proofs have account values; for a merkledb-mode proof this returns an error.

func (ChangeProof) Drop
func (h ChangeProof) Drop() error

Drop releases the C-side resource and releases this handle's lease on the parent database.

Release is unconditional: even if the underlying free call returns an error, the lease is still released. The C pointer is cleared before the free call, so a subsequent Drop is a no-op rather than a retry against a possibly-corrupt pointer. Any free error is surfaced to the caller, which is then responsible for deciding what to do — but the caller does not need to worry about the database hanging on shutdown because a free errored.

Idempotent: subsequent calls after the first return nil.

func (*ChangeProof) FindNextKey
func (proof *ChangeProof) FindNextKey(endKey Maybe[[]byte]) (*NextKeyRange, error)

FindNextKey returns the next key range to fetch for a change proof, or nil if there are no more keys to fetch. The proof is not consumed.

func (*ChangeProof) Marshal
func (p *ChangeProof) Marshal() ([]byte, error)

Marshal returns a serialized representation of this ChangeProof.

The format is unspecified and opaque to firewood.

type CloseOption

type CloseOption func(*closeConfig)

CloseOption configures the behavior of Database.Close.

func WithForceCloseHandles
func WithForceCloseHandles() CloseOption

WithForceCloseHandles makes Database.Close forcibly drop every outstanding Proposal, Revision, Reconstructed, Iterator, RangeProof, and ChangeProof before closing the database, instead of waiting for the caller to release them.

Each handle is dropped once even if its underlying free errors, so a failing free cannot stall the close. Dropping a handle can still wait for an in-flight operation on it to finish, so a slow reader can delay the close past the deadline. Drop errors are joined and returned, and the database close is still attempted on a best-effort basis.

While force-close is in progress, attempts to construct derived handles (for example via Proposal.Propose or Revision.Iter) fail with an error indicating the database is closed: a child constructor that would otherwise race the close is rejected rather than leaking past it.

Methods on a force-dropped handle behave the same as if the caller had explicitly invoked Drop.

type Database

type Database struct {
	// contains filtered or unexported fields
}

Database is an FFI wrapper for the Rust Firewood database. All functions rely on CGO to call into the underlying Rust implementation. Instances are created via New and must be closed with Database.Close when no longer needed.

A Database can have outstanding Revision and Proposal, which access the database's memory. These must be released before closing the database. See Database.Close for more details.

Database supports two hashing modes: Firewood hashing and Ethereum-compatible hashing. Ethereum-compatible hashing is distributed, but you can use the more efficient Firewood hashing by compiling from source. See the Firewood repository for more details.

For concurrent use cases, see each type and method's documentation for thread-safety.

func New
func New(dbDir string, nodeHashAlgorithm NodeHashAlgorithm, opts ...Option) (*Database, error)

New opens or creates a new Firewood database with the given node hashing algorithm and database options. The database directory will be created at the provided path if it does not already exist.

The [nodeHashAlgorithm] is required and selects the database's hashing mode at runtime, on a per-database basis:

A single binary supports both modes. When opening an existing database, the algorithm must match the one the database was created with.

If no Option is provided, sensible defaults will be used. See the With* functions for details about each configuration parameter and its default value.

It is the caller's responsibility to call Database.Close when the database is no longer needed. No other Database in this process should be opened with the same file path until the database is closed.

func (*Database) ChangeProof
func (db *Database) ChangeProof(
	startRoot, endRoot Hash,
	startKey, endKey Maybe[[]byte],
	maxLength uint32,
) (*ChangeProof, error)

ChangeProof returns a proof that the changes between [startRoot] and [endRoot] for keys in the range [startKey, endKey]. The proof may be truncated to at most [maxLength] entries, if non-zero. If either [startKey] or [endKey] is Nothing, the range is unbounded in that direction.

func (*Database) Close
func (db *Database) Close(ctx context.Context, opts ...CloseOption) error

Close releases the memory associated with the Database and stops the background persistence thread.

By default Close blocks until all outstanding keep-alive handles are disowned or the context.Context is cancelled. That is, until every Proposal, Revision, Reconstructed, Iterator, RangeProof, and ChangeProof created from this Database is either unreachable or has been explicitly released via Proposal.Commit or its Drop method. Committing a proof does not release it. Unreachable objects are released by their GC cleanups before Close returns. If the context expires first, ErrActiveKeepAliveHandles is returned and [C.fwd_close_db] is not called.

Pass WithForceCloseHandles to forcibly drop every outstanding handle instead of waiting for the caller to release them. See WithForceCloseHandles for details on its semantics, including how ctx cancellation is reported.

Safe to call multiple times; subsequent calls after the first are no-ops and return nil.

func (*Database) Dump
func (db *Database) Dump() (string, error)

Dump returns a DOT (Graphviz) format representation of the trie structure of the latest revision for debugging purposes.

Returns an error if the database is closed or if there was an error dumping the trie.

func (*Database) Get
func (db *Database) Get(key []byte) ([]byte, error)

Get retrieves the value for the given key from the most recent revision. If the key is not found, the return value will be nil.

This function conflicts with all other calls that access the latest state of the database, and will lock for the duration of this function. If you need to perform concurrent reads, consider using Database.Revision or Database.LatestRevision to get a Revision and calling Revision.Get on it.

func (*Database) LatestRevision
func (db *Database) LatestRevision() (*Revision, error)

LatestRevision returns a Revision representing the latest state of the database. If the latest revision has root EmptyRoot, it returns an error. The Revision must be released with Revision.Drop before closing the database.

This function conflicts with all other calls that access the latest state of the database, and will lock for the duration of this function.

func (*Database) Propose
func (db *Database) Propose(batch []BatchOp) (*Proposal, error)

Propose creates a new proposal with the given batch operations. The proposal is not committed until Proposal.Commit is called. See Database.Close regarding freeing proposals. All proposals should be freed before closing the database.

Use Put, Delete, and PrefixDelete to create batch operations. A Put with an empty value stores an empty value; use Delete or PrefixDelete to remove keys.

This function conflicts with all other calls that access the latest state of the database, and will lock for the duration of this function.

func (*Database) RangeProof
func (db *Database) RangeProof(
	rootHash Hash,
	startKey, endKey Maybe[[]byte],
	maxLength uint32,
) (*RangeProof, error)

RangeProof returns a proof that the values in the range [startKey, endKey] are included in the tree with the current root. The proof may be truncated to at most [maxLength] entries, if non-zero. If either [startKey] or [endKey] is Nothing, the range is unbounded in that direction. If [rootHash] is Nothing, the current root of the database is used.

func (*Database) Revision
func (db *Database) Revision(root Hash) (*Revision, error)

Revision returns a revision of the database. If the provided root does not exist (or is the EmptyRoot), it returns an error. The Revision must be released with Revision.Drop before closing the database.

This function is thread-safe with all other operations.

func (*Database) Root
func (db *Database) Root() Hash

Root returns the current root hash of the trie. With Firewood hashing, the empty trie must return EmptyRoot. If the database is already closed, it returns EmptyRoot.

This function conflicts with all other calls that access the latest state of the database, and will lock for the duration of this function.

func (*Database) UnmarshalChangeProof
func (db *Database) UnmarshalChangeProof(data []byte) (*ChangeProof, error)

UnmarshalChangeProof parses a proof produced by ChangeProof.Marshal for use with db. A proof encoded for the other hash mode is rejected here, before any verification.

func (*Database) UnmarshalRangeProof
func (db *Database) UnmarshalRangeProof(data []byte) (*RangeProof, error)

UnmarshalRangeProof parses a proof produced by RangeProof.Marshal for use with db. A proof encoded for the other hash mode is rejected here, before any verification. The returned proof holds no lease on db until it is verified.

func (*Database) Update
func (db *Database) Update(batch []BatchOp) (Hash, error)

Update applies a batch of operations to the database, returning the hash of the root node after the batch is applied. This is equivalent to creating a proposal with Database.Propose, then committing it with Proposal.Commit.

Use Put, Delete, and PrefixDelete to create batch operations. A Put with an empty value stores an empty value; use Delete or PrefixDelete to remove keys.

This function conflicts with all other calls that access the latest state of the database, and will lock for the duration of this function.

func (*Database) VerifyAndCommitChangeProof
func (db *Database) VerifyAndCommitChangeProof(
	proof *ChangeProof,
	endRoot Hash,
	startKey, endKey Maybe[[]byte],
	maxLength uint32,
) (Hash, error)

VerifyAndCommitChangeProof verifies the change proof and commits it in a single call. The proof is not consumed — it remains available for ChangeProof.FindNextKey or serialization afterward.

func (*Database) VerifyAndCommitRangeProof
func (db *Database) VerifyAndCommitRangeProof(
	proof *RangeProof,
	startKey, endKey Maybe[[]byte],
	rootHash Hash,
	maxLength uint32,
) (Hash, error)

VerifyAndCommitRangeProof verifies the provided range [proof] proves the values in the range [startKey, endKey] are included in the tree with the given [rootHash]. If the proof is valid, it is committed to the database and the new root hash is returned. The resulting root hash may not equal the provided root hash if the proof was truncated due to [maxLength].

The commit replaces state across the proven range on the same terms as *Database.VerifyRangeProof.

func (*Database) VerifyChangeProof
func (db *Database) VerifyChangeProof(
	proof *ChangeProof,
	endRoot Hash,
	startKey, endKey Maybe[[]byte],
	maxLength uint32,
) (*Proposal, error)

VerifyChangeProof verifies the change proof and creates a standard Proposal. The proof is not consumed — it can still be used for ChangeProof.FindNextKey or serialization.

func (*Database) VerifyRangeProof
func (db *Database) VerifyRangeProof(
	proof *RangeProof,
	startKey, endKey Maybe[[]byte],
	rootHash Hash,
	maxLength uint32,
) error

VerifyRangeProof verifies the provided range [proof] proves the changes between [startRoot] and [endRoot] for keys in the range [startKey, endKey]. If the proof is valid, a proposal containing the changes is prepared. The call to *Database.VerifyAndCommitRangeProof will skip verification and commit the prepared proposal.

The proposal replaces state across the range the proof proves: any existing key in that range that the proof does not carry is deleted. The range runs from [startKey] to the proof's right edge, which is [endKey] when the responder covered the whole request and a smaller key when it truncated. Keys past that edge are left as they are; *RangeProof.FindNextKey reports where to resume.

The prepared proposal borrows the database, so a successful call binds the proof to db: db cannot be closed gracefully until the proof is dropped, and other databases reject the proof with an error. A successful *Database.VerifyAndCommitRangeProof binds the proof the same way.

type EthAccountProof

type EthAccountProof struct {
	// Nonce is the account transaction count.
	Nonce uint64
	// Balance is the account balance, zero-padded big-endian.
	Balance [32]byte
	// CodeHash is the Keccak-256 of the account's contract code (the empty-code
	// hash for accounts with no code).
	CodeHash [32]byte
	// StorageHash is the storage trie root embedded in the account leaf (the
	// empty-trie root for absent accounts).
	StorageHash [32]byte
	// AccountProof holds the RLP-encoded account-trie nodes, root-to-leaf order.
	AccountProof [][]byte
	// StorageProof holds one proof per requested slot key, in input order.
	StorageProof []EthStorageProof
}

EthAccountProof is the result of Revision.EthGetProof and Reconstructed.EthGetProof: the four account scalars plus the RLP-encoded account-trie nodes proving them, and one EthStorageProof per requested storage slot.

The proof byte slices are canonical RLP-encoded Ethereum MPT nodes, suitable for verification with go-ethereum's trie.VerifyProof against the revision root (account proof) or against StorageHash (storage proofs). JSON marshalling that matches the eth_getProof JSON-RPC response is left to the caller.

type EthStorageProof

type EthStorageProof struct {
	// Key is the requested 32-byte slot trie key, echoed back from the input.
	Key [32]byte
	// Value is the stored slot value, or nil for an exclusion proof.
	Value []byte
	// Proof holds the RLP-encoded storage-trie nodes, root-to-leaf order. It is
	// empty when the account has no storage at all.
	Proof [][]byte
}

EthStorageProof is a single per-slot entry within an EthAccountProof.

type Gatherer

type Gatherer struct{}

Gatherer is a prometheus.Gatherer that collects metrics from the firewood library.

Metrics carry a `db_tag` label identifying the database that recorded them (see WithMetricsTag), except proof-marshal timing, which runs without a database in scope.

func (Gatherer) Gather
func (Gatherer) Gather() ([]*dto.MetricFamily, error)

type Hash

type Hash [RootLength]byte

Hash is the type used for all firewood hashes.

var (
	// EmptyRoot is the zero value for [Hash]
	EmptyRoot Hash
	// ErrActiveKeepAliveHandles is returned by [Database.Close] when the
	// supplied context is cancelled before every outstanding handle that
	// holds a keep-alive lease on the database has been released. That
	// set includes [Proposal], [Revision], [Reconstructed], [Iterator] and
	// [RangeProof] and instances. Pass [WithForceCloseHandles] to drop
	// them automatically.
	ErrActiveKeepAliveHandles = errors.New("cannot close database with active keep-alive handles")
)

type Iterator

type Iterator struct {
	// contains filtered or unexported fields
}

Iterator provides sequential access to key-value pairs within a Revision or Proposal. An iterator traverses the trie in lexicographic key order, starting from a specified key.

Instances are created via Revision.Iter or Proposal.Iter, and must be released with Iterator.Drop when no longer needed.

An Iterator holds a reference to the underlying view, so it can safely outlive the Revision or Proposal it was created from. The underlying state will not be released until the Iterator is released. The Iterator additionally keeps the Database alive: Database.Close will block on outstanding iterators (or, with WithForceCloseHandles, drop them).

Iterator supports two modes of accessing key-value pairs. Iterator.Next copies the key and value into Go-managed memory. Iterator.NextBorrowed returns slices that borrow Rust-owned memory, which is faster but the slices are only valid until the next call to Next, NextBorrowed, or Iterator.Drop.

An Iterator is single-reader: Next, NextBorrowed, Key, Value, and Err must not be called concurrently. Drop is the exception and is safe from another goroutine, including force-close.

func (Iterator) Drop
func (ih Iterator) Drop() error

Drop releases the resources associated with the iterator. This must be called when the iterator is no longer needed to avoid memory leaks. As a safety net, runtime.AddCleanup also drops the iterator if its wrapper becomes unreachable without an explicit Drop.

It is safe to call Drop multiple times and from multiple goroutines; subsequent calls after the first are no-ops.

func (*Iterator) Err
func (it *Iterator) Err() error

Err returns the error from the last call to Iterator.Next or Iterator.NextBorrowed, or nil if no error occurred.

func (*Iterator) Key
func (it *Iterator) Key() []byte

Key returns the key of the current key-value pair. If the iterator has not been advanced or is exhausted, it returns nil.

If the iterator was advanced with Iterator.NextBorrowed, the returned slice borrows Rust memory and is only valid until the next call to Iterator.Next, Iterator.NextBorrowed, or Iterator.Drop — including a Drop initiated by another goroutine, such as Database.Close with WithForceCloseHandles. Key deliberately takes no lock: the caller consumes the slice after Key returns, so a lock here could not extend the slice's validity.

func (*Iterator) Next
func (it *Iterator) Next() bool

Next advances the iterator to the next key-value pair and returns true if a pair is available. The key and value can be retrieved with Iterator.Key and Iterator.Value.

Next copies the key and value into Go-managed memory, making them safe to retain after subsequent calls. For better performance, use Iterator.NextBorrowed.

It returns false when the iterator is exhausted or an error occurs. Check Iterator.Err after iteration completes to distinguish between the two. It is safe to call Next after it returns false; it will continue to return false.

func (*Iterator) NextBorrowed
func (it *Iterator) NextBorrowed() bool

NextBorrowed advances the iterator like Iterator.Next, but the slices returned by Iterator.Key and Iterator.Value borrow Rust-owned memory instead of copying. This is faster than Next but the slices are only valid until the next call to Iterator.Next, Iterator.NextBorrowed, or Iterator.Drop.

WARNING: Do not retain, store, or modify the slices. Doing so results in undefined behavior.

It returns false when the iterator is exhausted or an error occurs, same as Next.

func (*Iterator) SetBatchSize
func (it *Iterator) SetBatchSize(batchSize int)

SetBatchSize sets the number of key-value pairs to retrieve per FFI call. A batch size greater than 1 reduces FFI overhead when iterating over many items. A batch size of 0 or 1 disables batching. The default is 0.

func (*Iterator) Value
func (it *Iterator) Value() []byte

Value returns the value of the current key-value pair. If the iterator has not been advanced or is exhausted, it returns nil.

If the iterator was advanced with Iterator.NextBorrowed, the returned slice borrows Rust memory and is only valid until the next call to Iterator.Next, Iterator.NextBorrowed, or Iterator.Drop — including a Drop initiated by another goroutine, such as Database.Close with WithForceCloseHandles. Value deliberately takes no lock: the caller consumes the slice after Value returns, so a lock here could not extend the slice's validity.

type LogConfig

type LogConfig struct {
	Path        string
	FilterLevel string
}

LogConfig configures logs for this process.

type Maybe

type Maybe[T any] interface {
	// HasValue returns true if the Maybe contains a value.
	HasValue() bool
	// Value returns the value contained in the Maybe.
	//
	// Implementations may panic if the Maybe contains no value but can also
	// return the zero value of T.
	Value() T
}

Maybe is an interface that represents an optional value of type T.

Maybe is a drop-in replacement for the Maybe type defined in avalanchego/utils/maybe. This interface is used to avoid importing avalanchego packages into the ffi package, which would create a circular dependency.

<https://github.com/ava-labs/avalanchego/blob/66ca7dc0ce354ff4c4ebdc8b377e099ba91f2e2b/utils/maybe/maybe.go#L40-L48>

The avalanchego implementation of Maybe implements this interface.

func Nothing
func Nothing[T any]() Maybe[T]

Nothing returns a Maybe holding no value.

func Some
func Some[T any](value T) Maybe[T]

Some returns a Maybe holding value.

type NextKeyRange

type NextKeyRange struct {
	StartKey []byte
	EndKey   Maybe[[]byte]
}

NextKeyRange is a range of keys still to fetch, `[StartKey, EndKey]`, both inclusive. StartKey is the smallest key above the last one already synchronized, so passing it as the next request's start key resumes without fetching that key again. If EndKey has no value, the range is unbounded above.

The keys are Go-owned copies; a NextKeyRange needs no release.

type NodeHashAlgorithm

type NodeHashAlgorithm C.enum_NodeHashAlgorithm

NodeHashAlgorithm represents the node hashing algorithm used by the database. It is a per-database runtime choice: a single binary can open databases of either algorithm. When opening an existing database, the algorithm must match the one the database was created with.

Currently, there are only two variants but more may be added in the future.

const (
	// MerkleDBNodeHashing uses MerkleDB-compatible SHA-256 based hashing
	MerkleDBNodeHashing NodeHashAlgorithm = C.NodeHashAlgorithm_MerkleDB
	// EthereumNodeHashing uses Ethereum-compatible Keccak-256 based hashing
	EthereumNodeHashing NodeHashAlgorithm = C.NodeHashAlgorithm_Ethereum
)

type Option

type Option func(*config)

Option is a function that configures a Database.

func WithExpensiveMetrics
func WithExpensiveMetrics() Option

WithExpensiveMetrics enables expensive metrics recording for this database. Expensive metrics are disabled by default since they can introduce overhead. Default: false

func WithFreeListMemoryLimitKB
func WithFreeListMemoryLimitKB(memoryLimitKB uint) Option

WithFreeListMemoryLimitKB sets the memory limit for the freelist cache in kibibytes. The freelist cache manages available disk space for reuse. Nothing is preallocated, so this is an upper bound on the memory the cache may use. Must be non-zero. Default: 4096 (4 MB)

func WithMaxPersistenceGap
func WithMaxPersistenceGap(maxPersistenceGap uint64) Option

WithMaxPersistenceGap sets the maximum number of committed revisions that can be ahead of the last persisted revision. A commit that would exceed this distance blocks until persistence advances the checkpoint.

Consequently, a persisted revision is guaranteed to be found by looking back at most maxPersistenceGap revisions from any committed revision.

Defaults to 1. Must be positive and less than the value set by WithRevisions.

func WithMetricsTag
func WithMetricsTag(tag string) Option

WithMetricsTag sets a tag to attach to metrics and logs emitted for this database. Default: empty (no tag; metrics are recorded with the fallback db_tag="untagged" label)

func WithNodeCacheSizeInBytes
func WithNodeCacheSizeInBytes(sizeInBytes uint) Option

WithNodeCacheSizeInBytes sets the node cache memory limit in bytes. Must be non-zero. Default: 128 MB

func WithReadCacheStrategy
func WithReadCacheStrategy(strategy CacheStrategy) Option

WithReadCacheStrategy sets the caching strategy used for the node cache. Default: OnlyCacheWrites

func WithRevisions
func WithRevisions(revisions uint) Option

WithRevisions sets the maximum number of historical revisions to keep in memory. If RootStoreDir is set, then any revisions removed from memory will still be kept on disk. Otherwise, any revisions removed from memory will no longer be kept on disk. Must be >= 2 and > WithMaxPersistenceGap. Default: 100

func WithRootStore
func WithRootStore() Option

WithRootStore defines whether to enable storing all historical revisions on disk. When set, historical revisions will be persisted to disk even after being removed from memory (based on the Revisions limit). Default: false

func WithTruncate
func WithTruncate(truncate bool) Option

WithTruncate sets whether to clear the database file if it already exists. Default: false

type Proposal

type Proposal struct {
	// contains filtered or unexported fields
}

Proposal represents a set of proposed changes to be committed to the database. Proposals are created via Database.Propose or Proposal.Propose, and must be either committed with Proposal.Commit or released with Proposal.Drop.

Proposals must be committed or released before the associated database is closed. Release is done by calling Proposal.Drop. A finalizer is set on each Proposal to ensure Drop is called when the Proposal is garbage collected, but relying on finalizers is not recommended. Failing to commit or release a proposal before the database is closed will cause it to block or fail.

All read operations on a Proposal are thread-safe with respect to each other, and can be performed regardless of the state of the associated database. However, proposals cannot be committed concurrently with other operations that access the latest state of the database, such as Database.Get and Database.Propose,

func (*Proposal) Commit
func (p *Proposal) Commit() error

Commit commits the proposal and returns any errors.

The underlying data is no longer available during/after this call, but the root hash can still be retrieved using Proposal.Root. This is safe to call only once; subsequent calls will return an error.

During this function, the latest state of the database is locked, so other operations that access it (such as Database.Get and Database.Propose) will block until this function returns.

func (*Proposal) CommitWithRebase
func (p *Proposal) CommitWithRebase() (Hash, error)

CommitWithRebase commits the proposal, automatically rebasing if the proposal's parent is no longer the latest revision. Returns the root hash of the committed revision.

Only this proposal is rebased. Child proposals created via Proposal.Propose are not automatically reparented after a rebase and should not be used.

This is safe to call only once; subsequent calls will return an error.

During this function, the latest state of the database is locked, so other operations that access it (such as Database.Get and Database.Propose) will block until this function returns.

func (Proposal) Drop
func (h Proposal) Drop() error

Drop releases the C-side resource and releases this handle's lease on the parent database.

Release is unconditional: even if the underlying free call returns an error, the lease is still released. The C pointer is cleared before the free call, so a subsequent Drop is a no-op rather than a retry against a possibly-corrupt pointer. Any free error is surfaced to the caller, which is then responsible for deciding what to do — but the caller does not need to worry about the database hanging on shutdown because a free errored.

Idempotent: subsequent calls after the first return nil.

func (*Proposal) Dump
func (p *Proposal) Dump() (string, error)

Dump returns a DOT (Graphviz) format representation of the trie structure of this proposal for debugging purposes.

Returns errDroppedProposal if this Proposal has already been committed or released.

func (*Proposal) Get
func (p *Proposal) Get(key []byte) ([]byte, error)

Get retrieves the value for the given key. If the key does not exist, it returns nil.

func (*Proposal) Iter
func (p *Proposal) Iter(key []byte) (*Iterator, error)

Iter creates an Iterator over the key-value pairs in this proposal, starting at the first key greater than or equal to the provided key. Pass nil or an empty slice to iterate from the beginning.

The Iterator must be released with Iterator.Drop when no longer needed, otherwise the underlying proposal will never be properly freed.

It returns an error if this Proposal has already been committed or released.

func (*Proposal) Propose
func (p *Proposal) Propose(batch []BatchOp) (*Proposal, error)

Propose is equivalent to Database.Propose except that the new proposal is based on `p`. The returned proposal cannot be committed until the parent proposal `p` has been committed. Additionally, it must be committed or released before the Database is closed.

Use Put, Delete, and PrefixDelete to create batch operations. A Put with an empty value stores an empty value; use Delete or PrefixDelete to remove keys.

func (*Proposal) Root
func (p *Proposal) Root() Hash

Root retrieves the root hash of the proposal.

type RangeProof

type RangeProof struct {
	// contains filtered or unexported fields
}

RangeProof represents a proof that a range of keys and their values are included in a trie with a given root hash.

func (*RangeProof) CodeHashes
func (p *RangeProof) CodeHashes() ([]Hash, error)

CodeHashes returns the contract code hashes referenced by the account values in this proof, in no particular order and possibly with duplicates.

Only Ethereum-mode proofs have account values; for a merkledb-mode proof this returns an error. This method can be called any time after the proof is created.

func (RangeProof) Drop
func (h RangeProof) Drop() error

Drop releases the C-side resource and releases this handle's lease on the parent database.

Release is unconditional: even if the underlying free call returns an error, the lease is still released. The C pointer is cleared before the free call, so a subsequent Drop is a no-op rather than a retry against a possibly-corrupt pointer. Any free error is surfaced to the caller, which is then responsible for deciding what to do — but the caller does not need to worry about the database hanging on shutdown because a free errored.

Idempotent: subsequent calls after the first return nil.

func (*RangeProof) FindNextKey
func (p *RangeProof) FindNextKey() (*NextKeyRange, error)

FindNextKey returns the next key range to fetch for this proof, if any. If the proof has been fully processed, nil is returned. If an error occurs while determining the next key range, that error is returned.

FindNextKey can only be called after a successful call to *Database.VerifyRangeProof or *Database.VerifyAndCommitRangeProof.

The next key range indicates the next `[StartKey, EndKey]` range of keys that should be synchronized to complete the requested range; see NextKeyRange. ava-labs/firewood#352 tracks tightening the start key beyond the last proven key.

func (*RangeProof) Marshal
func (p *RangeProof) Marshal() ([]byte, error)

Marshal returns a serialized representation of this RangeProof.

The format is unspecified and opaque to firewood.

func (*RangeProof) Verify
func (p *RangeProof) Verify(
	rootHash Hash,
	startKey, endKey Maybe[[]byte],
	maxLength uint32,
) error

Verify verifies the provided range [proof] proves the values in the range [startKey, endKey] are included in the tree with the given [rootHash]. If the proof is valid, nil is returned; otherwise an error describing why the proof is invalid is returned.

type Reconstructed

type Reconstructed struct {
	// contains filtered or unexported fields
}

Reconstructed is a linear, read-only reconstructed view over a historical revision.

Unlike Proposal, a Reconstructed view cannot be committed and does not participate in proposal branching semantics. Calling Reconstructed.Reconstruct updates this instance in place.

Reconstructed handles must be released before the associated database is closed by calling Reconstructed.Drop. A cleanup function is registered to call Drop automatically if needed, but explicit calls are recommended.

func (*Reconstructed) Clone
func (r *Reconstructed) Clone() (*Reconstructed, error)

Clone returns an independent Reconstructed handle that shares the underlying view with the receiver. The two handles can be used, reconstructed, and freed independently.

func (Reconstructed) Drop
func (h Reconstructed) Drop() error

Drop releases the C-side resource and releases this handle's lease on the parent database.

Release is unconditional: even if the underlying free call returns an error, the lease is still released. The C pointer is cleared before the free call, so a subsequent Drop is a no-op rather than a retry against a possibly-corrupt pointer. Any free error is surfaced to the caller, which is then responsible for deciding what to do — but the caller does not need to worry about the database hanging on shutdown because a free errored.

Idempotent: subsequent calls after the first return nil.

func (*Reconstructed) Dump
func (r *Reconstructed) Dump() (string, error)

Dump returns a DOT (Graphviz) representation of this reconstructed view.

func (*Reconstructed) EthGetProof
func (r *Reconstructed) EthGetProof(accountKey []byte, slotKeys [][]byte) (*EthAccountProof, error)

EthGetProof is Revision.EthGetProof evaluated against this reconstructed view. It returns ErrEthProofNotSupported if the database is not in Ethereum hash mode, and ErrDroppedReconstructed if the view has been released.

func (*Reconstructed) Get
func (r *Reconstructed) Get(key []byte) ([]byte, error)

Get retrieves the value for the given key in this reconstructed view.

func (*Reconstructed) Iter
func (r *Reconstructed) Iter(key []byte) (*Iterator, error)

Iter creates an iterator over the reconstructed view.

func (*Reconstructed) Reconstruct
func (r *Reconstructed) Reconstruct(batch []BatchOp) error

Reconstruct applies a new batch on top of this reconstructed view.

On success, the receiver is updated to point at the newly reconstructed view. On error, the receiver is no longer usable and its resources are fully released; calling Reconstructed.Drop is not required (but safe as a no-op).

func (*Reconstructed) Root
func (r *Reconstructed) Root() Hash

Root returns the root hash of the reconstructed view. Unlike other methods, Root remains usable after Reconstructed.Drop and returns the last cached root (or EmptyRoot if the root was never computed).

type Revision

type Revision struct {
	// contains filtered or unexported fields
}

Revision is an immutable view over the state at a specific root hash. Instances are created via Database.Revision, provide read-only access to the revision, and must be released with Revision.Drop when no longer needed.

Revisions must be released before the associated database is closed by calling Revision.Drop. A finalizer is set on each Revision to ensure Drop is called when the Revision is garbage collected, but relying on finalizers is not recommended. Failing to release a revision before the database is closed will cause it to block or fail.

Additionally, Revisions should be released when no longer needed to allow the database to free any associated resources. Firewood ensures that the state associated with a Revision is retained until all Revisions based on that state have been released.

All operations on a Revision are thread-safe with respect to each other.

func (Revision) Drop
func (h Revision) Drop() error

Drop releases the C-side resource and releases this handle's lease on the parent database.

Release is unconditional: even if the underlying free call returns an error, the lease is still released. The C pointer is cleared before the free call, so a subsequent Drop is a no-op rather than a retry against a possibly-corrupt pointer. Any free error is surfaced to the caller, which is then responsible for deciding what to do — but the caller does not need to worry about the database hanging on shutdown because a free errored.

Idempotent: subsequent calls after the first return nil.

func (*Revision) Dump
func (r *Revision) Dump() (string, error)

Dump returns a DOT (Graphviz) format representation of the trie structure of this revision for debugging purposes.

Returns ErrDroppedRevision if this revision has already been released.

func (*Revision) EthGetProof
func (r *Revision) EthGetProof(accountKey []byte, slotKeys [][]byte) (*EthAccountProof, error)

EthGetProof produces an eth_getProof-compatible proof for the account at accountKey and each storage slot in slotKeys, evaluated against this revision.

Both accountKey and every entry of slotKeys must be 32-byte trie keys: the caller is responsible for keccak-hashing addresses and storage slots into their trie-key forms (firewood stores accounts at keccak256(address) and slots at keccak256(address) ++ keccak256(slot)).

Absent accounts come back with zero account scalars plus the empty-code and empty-trie hashes; the proof bytes themselves distinguish inclusion from exclusion to a verifier.

It returns ErrEthProofNotSupported if the database is not in Ethereum hash mode, and ErrDroppedRevision if this revision has already been released.

func (*Revision) Get
func (r *Revision) Get(key []byte) ([]byte, error)

Get reads the value stored at the provided key within the revision. If the key does not exist, it returns nil.

It returns ErrDroppedRevision if this revision has already been released.

func (*Revision) Iter
func (r *Revision) Iter(key []byte) (*Iterator, error)

Iter creates an Iterator over the key-value pairs in this revision, starting at the first key greater than or equal to the provided key. Pass nil or an empty slice to iterate from the beginning.

The Iterator must be released with Iterator.Drop when no longer needed.

It returns ErrDroppedRevision if this revision has already been released.

func (*Revision) Reconstruct
func (r *Revision) Reconstruct(batch []BatchOp) (*Reconstructed, error)

Reconstruct applies a batch of operations on top of this historical revision, returning a Reconstructed view.

The returned view is not committed to the database and is not visible via Database.Revision or Database.Root.

func (*Revision) Root
func (r *Revision) Root() Hash

Root returns the root hash of the revision.

Directories

gen/update-cgo-ldflags
gen/update-cgo-pragmas go generate script