Skip to content

Conversation

@alarso16
Copy link

@alarso16 alarso16 commented Nov 6, 2025

Why this should be merged

This is the first step in integrating Firewood into libevm for shared use in coreth and subnet-evm. This PR includes the triedb.DBOverride implementation, as well as the state.Trie implementations necessary to inject into state.Database. This is essentially a re-write of the current implementation in coreth, so feel free to suggest any structural changes. Let's get it right this time.

Note this cannot be merged until the next Firewood release. The automatic proposal cleanup and some bug fixes are necessary.

The most notable difference from coreth is a performance difference (see ava-labs/coreth#1238)

How this works

All the logic is essentially the same as before, with the exception of proposal creation and cleanup. Proposals are now created at hash time, and then rather than being dropped, are passed to the triedb for potential commit.

How this was tested

Sorry, there's no unit tests. I'm thinking PR 2 will be creating the state.Database injections and fixing the fuzz test in coreth. However, you can test this by checking out alarso16/firewood-libevm in coreth, using some go mod edit statements, and building firewood locally. It passes all coreth tests.

@alarso16 alarso16 self-assigned this Nov 6, 2025
@alarso16 alarso16 added Status: 🔴 DO NOT MERGE This PR is not meant to be merged in its current state Category: New feature 🆕 labels Nov 6, 2025
@alarso16 alarso16 linked an issue Nov 6, 2025 that may be closed by this pull request
@alarso16 alarso16 requested a review from ARR4N November 6, 2025 17:12
parentRoot common.Hash
root common.Hash
reader database.Reader
dirtyKeys map[string][]byte // Store dirty changes

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could use sync.Map as a direct replacement or add a sync.RWMutex to guard access to the map.

Also consider if sharing AccountTrie instances across goroutines is necessary, or if each goroutine should have its own copy.

}

// This function only reads from existing tracked proposals, so we can use a read lock.
db.proposalLock.RLock()
Copy link

@powerslider powerslider Nov 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need a write lock to clean up old proposals and store new ones:

	db.proposalLock.Lock()
	defer db.proposalLock.Unlock()

    db.possibleProposals = nil

Clean up should be mandatory, because (I suppose) this releases references to Rust memory held via FFI.

Maybe you can do a clean up function that can do this operation atomically, because I think there are lots of places where you would need to do this.

// Edge case: first set of proposals before `Commit`, or empty genesis block
// Neither `Update` nor `Commit` is called for genesis, so we can accept a proposal with parentHash of empty.
for _, possible := range db.possibleProposals {
if possible.info.parent.hashes[common.Hash{}] == struct{}{} {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since hashes is map[common.Hash]struct{}, accessing a non-existent key returns the zero value struct{}{}, making this comparison always true when the key doesn't exist. This is a logic bug - it should check if the key exists.

Suggested change
if possible.info.parent.hashes[common.Hash{}] == struct{}{} {
if _, ok := possible.info.parent.hashes[common.Hash{}]; ok {

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Category: New feature 🆕 Status: 🔴 DO NOT MERGE This PR is not meant to be merged in its current state

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Uplift triedb/firewood and testing

3 participants