Skip to content

Commit

Permalink
core,eth,tests,trie: abstract node scheme, and contruct database
Browse files Browse the repository at this point in the history
interface instead of keyvalue for supporting storing diff reverse data
in ancient
  • Loading branch information
huyngopt1994 committed Sep 18, 2024
1 parent be08ccd commit a25b351
Show file tree
Hide file tree
Showing 12 changed files with 175 additions and 66 deletions.
6 changes: 6 additions & 0 deletions core/rawdb/accessors_state.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,12 @@ func WriteCode(db ethdb.KeyValueWriter, hash common.Hash, code []byte) {
}
}

// HasTrieNode checks if the trie node with the provided hash is present in db.
func HasTrieNode(db ethdb.KeyValueReader, hash common.Hash) bool {
ok, _ := db.Has(hash.Bytes())
return ok
}

// DeleteCode deletes the specified contract code from the database.
func DeleteCode(db ethdb.KeyValueWriter, hash common.Hash) {
if err := db.Delete(codeKey(hash)); err != nil {
Expand Down
5 changes: 2 additions & 3 deletions tests/fuzzers/trie/trie-fuzzer.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import (
"fmt"

"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/ethdb/memorydb"
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/trie"
)

Expand Down Expand Up @@ -143,8 +143,7 @@ func Fuzz(input []byte) int {
}

func runRandTest(rt randTest) error {

triedb := trie.NewDatabase(memorydb.New())
triedb := trie.NewDatabase(rawdb.NewMemoryDatabase())

tr := trie.NewEmpty(triedb)
values := make(map[string]string) // tracks content of the trie
Expand Down
12 changes: 9 additions & 3 deletions trie/database.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ var (
// behind this split design is to provide read access to RPC handlers and sync
// servers even while the trie is executing expensive garbage collection.
type Database struct {
diskdb ethdb.KeyValueStore // Persistent storage for matured trie nodes
diskdb ethdb.Database // Persistent storage for matured trie nodes

cleans *fastcache.Cache // GC friendly memory cache of clean node RLPs
dirties map[common.Hash]*cachedNode // Data and references relationships of dirty trie nodes
Expand Down Expand Up @@ -280,14 +280,15 @@ type Config struct {
// NewDatabase creates a new trie database to store ephemeral trie content before
// its written out to disk or garbage collected. No read cache is created, so all
// data retrievals will hit the underlying disk database.
func NewDatabase(diskdb ethdb.KeyValueStore) *Database {
// Using ethdb.Database which covers KeyValueStore and Freezer Interfaces.
func NewDatabase(diskdb ethdb.Database) *Database {
return NewDatabaseWithConfig(diskdb, nil)
}

// NewDatabaseWithConfig creates a new trie database to store ephemeral trie content
// before its written out to disk or garbage collected. It also acts as a read cache
// for nodes loaded from disk.
func NewDatabaseWithConfig(diskdb ethdb.KeyValueStore, config *Config) *Database {
func NewDatabaseWithConfig(diskdb ethdb.Database, config *Config) *Database {
var cleans *fastcache.Cache
if config != nil && config.Cache > 0 {
if config.Journal == "" {
Expand Down Expand Up @@ -864,3 +865,8 @@ func (db *Database) SaveCachePeriodically(dir string, interval time.Duration, st
}
}
}

// Scheme returns the node scheme used in the database. Right now, we only support hash scheme.
func (db *Database) Scheme() NodeScheme {
return &hashScheme{}
}
4 changes: 2 additions & 2 deletions trie/database_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,13 @@ import (
"testing"

"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/ethdb/memorydb"
"github.com/ethereum/go-ethereum/core/rawdb"
)

// Tests that the trie database returns a missing trie node error if attempting
// to retrieve the meta root.
func TestDatabaseMetarootFetch(t *testing.T) {
db := NewDatabase(memorydb.New())
db := NewDatabase(rawdb.NewMemoryDatabase())
if _, err := db.Node(common.Hash{}); err == nil {
t.Fatalf("metaroot retrieval succeeded")
}
Expand Down
6 changes: 3 additions & 3 deletions trie/iterator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -326,7 +326,7 @@ func TestIteratorContinueAfterErrorDisk(t *testing.T) { testIteratorContinueA
func TestIteratorContinueAfterErrorMemonly(t *testing.T) { testIteratorContinueAfterError(t, true) }

func testIteratorContinueAfterError(t *testing.T, memonly bool) {
diskdb := memorydb.New()
diskdb := rawdb.NewMemoryDatabase()
triedb := NewDatabase(diskdb)

tr := NewEmpty(triedb)
Expand Down Expand Up @@ -418,7 +418,7 @@ func TestIteratorContinueAfterSeekErrorMemonly(t *testing.T) {

func testIteratorContinueAfterSeekError(t *testing.T, memonly bool) {
// Commit test trie to db, then remove the node containing "bars".
diskdb := memorydb.New()
diskdb := rawdb.NewMemoryDatabase()
triedb := NewDatabase(diskdb)

ctr := NewEmpty(triedb)
Expand Down Expand Up @@ -531,7 +531,7 @@ func (l *loggingDb) Close() error {
func makeLargeTestTrie() (*Database, *SecureTrie, *loggingDb) {
// Create an empty trie
logDb := &loggingDb{0, memorydb.New()}
triedb := NewDatabase(logDb)
triedb := NewDatabase(rawdb.NewDatabase(logDb))
trie, _ := NewSecure(common.Hash{}, common.Hash{}, triedb)

// Fill it with some arbitrary data
Expand Down
96 changes: 96 additions & 0 deletions trie/schema.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
// Copyright 2021 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.

package trie

import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/ethdb"
)

const (
HashScheme = "hashScheme" // Identifier of hash based node scheme

// Path-based scheme will be introduced in the following PRs.
// PathScheme = "pathScheme" // Identifier of path based node scheme
)

// NodeShceme desribes the scheme for interacting nodes in disk.
type NodeScheme interface {
// Name returns the identifier of node scheme.
Name() string

// HasTrieNode checks the trie node presence with the provided node info and
// the associated node hash.
HasTrieNode(db ethdb.KeyValueReader, owner common.Hash, path []byte, hash common.Hash) bool

// ReadTrieNode retrieves the trie node from database with the provided node
// info and the associated node hash.
ReadTrieNode(db ethdb.KeyValueReader, owner common.Hash, path []byte, hash common.Hash) []byte

// WriteTrieNode writes the trie node into database with the provided node
// info and associated node hash.
WriteTrieNode(db ethdb.KeyValueWriter, owner common.Hash, path []byte, hash common.Hash, node []byte)

// DeleteTrieNode deletes the trie node from database with the provided node
// info and associated node hash.
DeleteTrieNode(db ethdb.KeyValueWriter, owner common.Hash, path []byte, hash common.Hash)

// IsTrieNode returns an indicator if the given database key is the key of
// trie node according to the scheme.
IsTrieNode(key []byte) (bool, []byte)
}

type hashScheme struct{}

// Name returns the identifier of hash based scheme.
func (scheme *hashScheme) Name() string {
return HashScheme
}

// HasTrieNode checks the trie node presence with the provided node info and
// the associated node hash.
func (scheme *hashScheme) HasTrieNode(db ethdb.KeyValueReader, owner common.Hash, path []byte, hash common.Hash) bool {
return rawdb.HasTrieNode(db, hash)
}

// ReadTrieNode retrieves the trie node from database with the provided node info
// and associated node hash.
func (scheme *hashScheme) ReadTrieNode(db ethdb.KeyValueReader, owner common.Hash, path []byte, hash common.Hash) []byte {
return rawdb.ReadTrieNode(db, hash)
}

// WriteTrieNode writes the trie node into database with the provided node info
// and associated node hash.
func (scheme *hashScheme) WriteTrieNode(db ethdb.KeyValueWriter, owner common.Hash, path []byte, hash common.Hash, node []byte) {
rawdb.WriteTrieNode(db, hash, node)
}

// DeleteTrieNode deletes the trie node from database with the provided node info
// and associated node hash.
func (scheme *hashScheme) DeleteTrieNode(db ethdb.KeyValueWriter, owner common.Hash, path []byte, hash common.Hash) {
rawdb.DeleteTrieNode(db, hash)
}

// IsTrieNode returns an indicator if the given database key is the key of trie
// node according to the scheme.
func (scheme *hashScheme) IsTrieNode(key []byte) (bool, []byte) {
if len(key) == common.HashLength {
return true, key
}
return false, nil
}
6 changes: 3 additions & 3 deletions trie/secure_trie_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,19 +23,19 @@ import (
"testing"

"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethdb/memorydb"
)

func newEmptySecure() *SecureTrie {
trie, _ := NewSecure(common.Hash{}, common.Hash{}, NewDatabase(memorydb.New()))
trie, _ := NewSecure(common.Hash{}, common.Hash{}, NewDatabase(rawdb.NewMemoryDatabase()))
return trie
}

// makeTestSecureTrie creates a large enough secure trie for testing.
func makeTestSecureTrie() (*Database, *SecureTrie, map[string][]byte) {
// Create an empty trie
triedb := NewDatabase(memorydb.New())
triedb := NewDatabase(rawdb.NewMemoryDatabase())
trie, _ := NewSecure(common.Hash{}, common.Hash{}, triedb)

// Fill it with some arbitrary data
Expand Down
15 changes: 8 additions & 7 deletions trie/stacktrie_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ import (
"testing"

"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethdb/memorydb"
)

func TestStackTrieInsertAndHash(t *testing.T) {
Expand Down Expand Up @@ -188,7 +188,8 @@ func TestStackTrieInsertAndHash(t *testing.T) {

func TestSizeBug(t *testing.T) {
st := NewStackTrie(nil)
nt := NewEmpty(NewDatabase(memorydb.New()))

nt := NewEmpty(NewDatabase(rawdb.NewMemoryDatabase()))

leaf := common.FromHex("290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563")
value := common.FromHex("94cf40d0d2b44f2b66e07cace1372ca42b73cf21a3")
Expand All @@ -203,7 +204,7 @@ func TestSizeBug(t *testing.T) {

func TestEmptyBug(t *testing.T) {
st := NewStackTrie(nil)
nt := NewEmpty(NewDatabase(memorydb.New()))
nt := NewEmpty(NewDatabase(rawdb.NewMemoryDatabase()))

//leaf := common.FromHex("290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563")
//value := common.FromHex("94cf40d0d2b44f2b66e07cace1372ca42b73cf21a3")
Expand All @@ -229,7 +230,7 @@ func TestEmptyBug(t *testing.T) {

func TestValLength56(t *testing.T) {
st := NewStackTrie(nil)
nt := NewEmpty(NewDatabase(memorydb.New()))
nt := NewEmpty(NewDatabase(rawdb.NewMemoryDatabase()))

//leaf := common.FromHex("290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563")
//value := common.FromHex("94cf40d0d2b44f2b66e07cace1372ca42b73cf21a3")
Expand All @@ -254,7 +255,7 @@ func TestValLength56(t *testing.T) {
// which causes a lot of node-within-node. This case was found via fuzzing.
func TestUpdateSmallNodes(t *testing.T) {
st := NewStackTrie(nil)
nt := NewEmpty(NewDatabase(memorydb.New()))
nt := NewEmpty(NewDatabase(rawdb.NewMemoryDatabase()))

kvs := []struct {
K string
Expand Down Expand Up @@ -283,7 +284,7 @@ func TestUpdateSmallNodes(t *testing.T) {
func TestUpdateVariableKeys(t *testing.T) {
t.SkipNow()
st := NewStackTrie(nil)
nt := NewEmpty(NewDatabase(memorydb.New()))
nt := NewEmpty(NewDatabase(rawdb.NewMemoryDatabase()))

kvs := []struct {
K string
Expand Down Expand Up @@ -354,7 +355,7 @@ func TestStacktrieNotModifyValues(t *testing.T) {
func TestStacktrieSerialization(t *testing.T) {
var (
st = NewStackTrie(nil)
nt = NewEmpty(NewDatabase(memorydb.New()))
nt = NewEmpty(NewDatabase(rawdb.NewMemoryDatabase()))
keyB = big.NewInt(1)
keyDelta = big.NewInt(1)
vals [][]byte
Expand Down
19 changes: 18 additions & 1 deletion trie/sync.go
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@ func (batch *syncMemBatch) hasCode(hash common.Hash) bool {
// unknown trie hashes to retrieve, accepts node data associated with said hashes
// and reconstructs the trie step by step until all is done.
type Sync struct {
scheme NodeScheme // Node scheme descriptor used in database.
database ethdb.KeyValueReader // Persistent database to check for existing entries
membatch *syncMemBatch // Memory buffer to avoid frequent database writes
nodeReqs map[string]*nodeRequest // Pending requests pertaining to a trie node path
Expand All @@ -146,8 +147,24 @@ type Sync struct {
bloom *SyncBloom // Bloom filter for fast state existence checks
}

// LeafCallback is a callback type invoked when a trie operation reaches a leaf
// node.
//
// The keys is a path tuple identifying a particular trie node either in a single
// trie (account) or a layered trie (account -> storage). Each key in the tuple
// is in the raw format(32 bytes).
//
// The path is a composite hexary path identifying the trie node. All the key
// bytes are converted to the hexary nibbles and composited with the parent path
// if the trie node is in a layered trie.
//
// It's used by state sync and commit to allow handling external references
// between account and storage tries. And also it's used in the state healing
// for extracting the raw states(leaf nodes) with corresponding paths.
type LeafCallback func(keys [][]byte, path []byte, leaf []byte, parent common.Hash, parentPath []byte) error

// NewSync creates a new trie data download scheduler.
func NewSync(root common.Hash, database ethdb.KeyValueReader, callback LeafCallback, bloom *SyncBloom) *Sync {
func NewSync(root common.Hash, database ethdb.KeyValueReader, callback LeafCallback, bloom *SyncBloom, scheme NodeScheme) *Sync {
ts := &Sync{
database: database,
membatch: newSyncMemBatch(),
Expand Down
Loading

0 comments on commit a25b351

Please sign in to comment.