mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-06-19 21:31:37 +00:00
This PR introduces a node scheme abstraction. The interface is only implemented by `hashScheme` at the moment, but will be extended by `pathScheme` very soon. Apart from that, a few changes are also included which is worth mentioning: - port the changes in the stacktrie, tracking the path prefix of nodes during commit - use ethdb.Database for constructing trie.Database. This is not necessary right now, but it is required for path-based used to open reverse diff freezer Co-authored-by: rjl493456442 <garyrong0905@gmail.com>
This commit is contained in:
parent
a4fbda2924
commit
07328dcec4
22 changed files with 402 additions and 208 deletions
|
|
@ -39,6 +39,7 @@ import (
|
|||
xdc_genesis "github.com/XinFinOrg/XDPoSChain/genesis"
|
||||
"github.com/XinFinOrg/XDPoSChain/log"
|
||||
"github.com/XinFinOrg/XDPoSChain/node"
|
||||
"github.com/XinFinOrg/XDPoSChain/trie"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
|
|
@ -48,7 +49,10 @@ var (
|
|||
Name: "init",
|
||||
Usage: "Bootstrap and initialize a new genesis block",
|
||||
ArgsUsage: "<genesisPath>",
|
||||
Flags: slices.Concat(utils.NetworkFlags, utils.DatabaseFlags),
|
||||
Flags: slices.Concat(
|
||||
[]cli.Flag{utils.CachePreimagesFlag},
|
||||
utils.NetworkFlags,
|
||||
utils.DatabaseFlags),
|
||||
Description: `
|
||||
The init command initializes a new genesis block and definition for the network.
|
||||
This is a destructive action and changes the network in which you will be
|
||||
|
|
@ -424,7 +428,10 @@ func dump(ctx *cli.Context) error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
state, err := state.New(root, state.NewDatabase(db))
|
||||
config := &trie.Config{
|
||||
Preimages: true, // always enable preimage lookup
|
||||
}
|
||||
state, err := state.New(root, state.NewDatabaseWithConfig(db, config))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -167,10 +167,12 @@ type BlockChain struct {
|
|||
chainConfig *params.ChainConfig // Chain & network configuration
|
||||
cacheConfig *CacheConfig // Cache configuration for pruning
|
||||
|
||||
db ethdb.Database // Low level persistent database to store final content in
|
||||
XDCxDb ethdb.XDCxDatabase
|
||||
triegc *prque.Prque[int64, common.Hash] // Priority queue mapping block numbers to tries to gc
|
||||
gcproc time.Duration // Accumulates canonical block processing for trie dumping
|
||||
db ethdb.Database // Low level persistent database to store final content in
|
||||
XDCxDb ethdb.XDCxDatabase // XDCx database
|
||||
triegc *prque.Prque[int64, common.Hash] // Priority queue mapping block numbers to tries to gc
|
||||
gcproc time.Duration // Accumulates canonical block processing for trie dumping
|
||||
triedb *trie.Database // The database handler for maintaining trie nodes.
|
||||
stateCache state.Database // State database to reuse between imports (contains state cache)
|
||||
|
||||
hc *HeaderChain
|
||||
rmLogsFeed event.Feed
|
||||
|
|
@ -188,8 +190,6 @@ type BlockChain struct {
|
|||
currentBlock atomic.Value // Current head of the block chain
|
||||
currentFastBlock atomic.Value // Current head of the fast-sync chain (may be above the block chain!)
|
||||
|
||||
stateCache state.Database // State database to reuse between imports (contains state cache)
|
||||
|
||||
bodyCache *lru.Cache[common.Hash, *types.Body] // Cache for the most recent block bodies
|
||||
bodyRLPCache *lru.Cache[common.Hash, rlp.RawValue] // Cache for the most recent block bodies in RLP encoded format
|
||||
receiptsCache *lru.Cache[common.Hash, types.Receipts] // Cache for the most recent block receipts
|
||||
|
|
@ -239,10 +239,16 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, chainConfig *par
|
|||
}
|
||||
}
|
||||
|
||||
// Open trie database with provided config
|
||||
triedb := trie.NewDatabaseWithConfig(db, &trie.Config{
|
||||
Cache: cacheConfig.TrieCleanLimit,
|
||||
Preimages: cacheConfig.Preimages,
|
||||
})
|
||||
bc := &BlockChain{
|
||||
chainConfig: chainConfig,
|
||||
cacheConfig: cacheConfig,
|
||||
db: db,
|
||||
triedb: triedb,
|
||||
triegc: prque.New[int64, common.Hash](nil),
|
||||
stateCache: state.NewDatabaseWithConfig(db, &trie.Config{
|
||||
Cache: cacheConfig.TrieCleanLimit,
|
||||
|
|
@ -268,6 +274,7 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, chainConfig *par
|
|||
rejectedLendingItem: lru.NewCache[common.Hash, interface{}](tradingstate.OrderCacheLimit),
|
||||
finalizedTrade: lru.NewCache[common.Hash, interface{}](tradingstate.OrderCacheLimit),
|
||||
}
|
||||
bc.stateCache = state.NewDatabaseWithNodeDB(bc.db, bc.triedb)
|
||||
bc.validator = NewBlockValidator(chainConfig, bc, engine)
|
||||
bc.prefetcher = newStatePrefetcher(chainConfig, bc, engine)
|
||||
bc.processor = NewStateProcessor(chainConfig, bc, engine)
|
||||
|
|
@ -373,8 +380,7 @@ func (bc *BlockChain) loadLastState() error {
|
|||
}
|
||||
// Make sure the state associated with the block is available
|
||||
repair := false
|
||||
_, err := state.New(currentBlock.Root(), bc.stateCache)
|
||||
if err != nil {
|
||||
if !bc.HasState(currentBlock.Root()) {
|
||||
repair = true
|
||||
} else {
|
||||
engine, ok := bc.Engine().(*XDPoS.XDPoS)
|
||||
|
|
@ -487,7 +493,7 @@ func (bc *BlockChain) setHeadBeyondRoot(head uint64) error {
|
|||
if newHeadBlock == nil {
|
||||
newHeadBlock = bc.genesisBlock
|
||||
} else {
|
||||
if _, err := state.New(newHeadBlock.Root(), bc.stateCache); err != nil {
|
||||
if !bc.HasState(newHeadBlock.Root()) {
|
||||
// Rewound state missing, rolled back to before pivot, reset to genesis
|
||||
newHeadBlock = bc.genesisBlock
|
||||
}
|
||||
|
|
@ -712,7 +718,7 @@ func (bc *BlockChain) repair(head **types.Block) error {
|
|||
for {
|
||||
// Abort if we've rewound to a head block that does have associated state
|
||||
if (common.RollbackNumber == 0) || ((*head).Number().Uint64() < common.RollbackNumber) {
|
||||
if _, err := state.New((*head).Root(), bc.stateCache); err == nil {
|
||||
if bc.HasState((*head).Root()) {
|
||||
log.Info("Rewound blockchain to past state", "number", (*head).Number(), "hash", (*head).Hash())
|
||||
engine, ok := bc.Engine().(*XDPoS.XDPoS)
|
||||
if ok {
|
||||
|
|
@ -1082,10 +1088,10 @@ func (bc *BlockChain) saveData() {
|
|||
if !bc.cacheConfig.TrieDirtyDisabled {
|
||||
var tradingTriedb *trie.Database
|
||||
var lendingTriedb *trie.Database
|
||||
engine, _ := bc.Engine().(*XDPoS.XDPoS)
|
||||
triedb := bc.stateCache.TrieDB()
|
||||
var tradingService utils.TradingService
|
||||
var lendingService utils.LendingService
|
||||
triedb := bc.triedb
|
||||
engine, _ := bc.Engine().(*XDPoS.XDPoS)
|
||||
if bc.Config().IsTIPXDCX(bc.CurrentBlock().Number()) && bc.chainConfig.XDPoS != nil && bc.CurrentBlock().NumberU64() > bc.chainConfig.XDPoS.Epoch && engine != nil {
|
||||
tradingService = engine.GetXDCXService()
|
||||
if tradingService != nil && tradingService.GetStateCache() != nil {
|
||||
|
|
@ -1439,7 +1445,6 @@ func (bc *BlockChain) writeBlockWithState(block *types.Block, receipts []*types.
|
|||
if err != nil {
|
||||
return NonStatTy, err
|
||||
}
|
||||
triedb := bc.stateCache.TrieDB()
|
||||
|
||||
tradingRoot := common.Hash{}
|
||||
if tradingState != nil {
|
||||
|
|
@ -1474,7 +1479,7 @@ func (bc *BlockChain) writeBlockWithState(block *types.Block, receipts []*types.
|
|||
|
||||
// If we're running an archive node, always flush
|
||||
if bc.cacheConfig.TrieDirtyDisabled {
|
||||
if err := triedb.Commit(root, false); err != nil {
|
||||
if err := bc.triedb.Commit(root, false); err != nil {
|
||||
return NonStatTy, err
|
||||
}
|
||||
if tradingTrieDb != nil {
|
||||
|
|
@ -1489,7 +1494,7 @@ func (bc *BlockChain) writeBlockWithState(block *types.Block, receipts []*types.
|
|||
}
|
||||
} else {
|
||||
// Full but not archive node, do proper garbage collection
|
||||
triedb.Reference(root, common.Hash{}) // metadata reference to keep trie alive
|
||||
bc.triedb.Reference(root, common.Hash{}) // metadata reference to keep trie alive
|
||||
bc.triegc.Push(root, -int64(block.NumberU64()))
|
||||
if tradingTrieDb != nil {
|
||||
tradingTrieDb.Reference(tradingRoot, common.Hash{})
|
||||
|
|
@ -1516,11 +1521,11 @@ func (bc *BlockChain) writeBlockWithState(block *types.Block, receipts []*types.
|
|||
// size = size + lendingTrieDb.Size()
|
||||
//}
|
||||
var (
|
||||
nodes, imgs = triedb.Size()
|
||||
nodes, imgs = bc.triedb.Size()
|
||||
limit = common.StorageSize(bc.cacheConfig.TrieDirtyLimit) * 1024 * 1024
|
||||
)
|
||||
if nodes > limit || imgs > 4*1024*1024 {
|
||||
triedb.Cap(limit - ethdb.IdealBatchSize)
|
||||
bc.triedb.Cap(limit - ethdb.IdealBatchSize)
|
||||
}
|
||||
if bc.gcproc > bc.cacheConfig.TrieTimeLimit || chosen > lastWrite+triesInMemory {
|
||||
// If the header is missing (canonical chain behind), we're reorging a low
|
||||
|
|
@ -1535,7 +1540,7 @@ func (bc *BlockChain) writeBlockWithState(block *types.Block, receipts []*types.
|
|||
log.Info("State in memory for too long, committing", "time", bc.gcproc, "allowance", bc.cacheConfig.TrieTimeLimit, "optimum", float64(chosen-lastWrite)/triesInMemory)
|
||||
}
|
||||
// Flush an entire trie and restart the counters
|
||||
triedb.Commit(header.Root, true)
|
||||
bc.triedb.Commit(header.Root, true)
|
||||
lastWrite = chosen
|
||||
bc.gcproc = 0
|
||||
if tradingTrieDb != nil && lendingTrieDb != nil {
|
||||
|
|
@ -1555,7 +1560,7 @@ func (bc *BlockChain) writeBlockWithState(block *types.Block, receipts []*types.
|
|||
bc.triegc.Push(root, number)
|
||||
break
|
||||
}
|
||||
triedb.Dereference(root)
|
||||
bc.triedb.Dereference(root)
|
||||
}
|
||||
if tradingService != nil {
|
||||
for !tradingService.GetTriegc().Empty() {
|
||||
|
|
@ -1831,7 +1836,7 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals bool) (int, []
|
|||
bc.UpdateBlocksHashCache(block)
|
||||
}
|
||||
|
||||
dirty, _ := bc.stateCache.TrieDB().Size()
|
||||
dirty, _ := bc.triedb.Size()
|
||||
stats.report(chain, it.index, dirty)
|
||||
if bc.chainConfig.XDPoS != nil {
|
||||
engine, _ := bc.Engine().(*XDPoS.XDPoS)
|
||||
|
|
@ -2287,7 +2292,7 @@ func (bc *BlockChain) insertBlock(block *types.Block) ([]interface{}, []*types.L
|
|||
}
|
||||
stats.processed++
|
||||
stats.usedGas += result.usedGas
|
||||
dirty, _ := bc.stateCache.TrieDB().Size()
|
||||
dirty, _ := bc.triedb.Size()
|
||||
stats.report(types.Blocks{block}, 0, dirty)
|
||||
if bc.chainConfig.XDPoS != nil {
|
||||
// epoch block
|
||||
|
|
|
|||
26
core/blockchain_reader.go
Normal file
26
core/blockchain_reader.go
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
// 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 core
|
||||
|
||||
import (
|
||||
"github.com/XinFinOrg/XDPoSChain/trie"
|
||||
)
|
||||
|
||||
// TrieDB retrieves the low level trie database used for data storage.
|
||||
func (bc *BlockChain) TrieDB() *trie.Database {
|
||||
return bc.triedb
|
||||
}
|
||||
|
|
@ -130,23 +130,33 @@ func NewDatabase(db ethdb.Database) Database {
|
|||
// large memory cache.
|
||||
func NewDatabaseWithConfig(db ethdb.Database, config *trie.Config) Database {
|
||||
return &cachingDB{
|
||||
db: trie.NewDatabaseWithConfig(db, config),
|
||||
disk: db,
|
||||
codeCache: lru.NewSizeConstrainedCache[common.Hash, []byte](codeCacheSize),
|
||||
codeSizeCache: lru.NewCache[common.Hash, int](codeSizeCacheSize),
|
||||
codeCache: lru.NewSizeConstrainedCache[common.Hash, []byte](codeCacheSize),
|
||||
triedb: trie.NewDatabaseWithConfig(db, config),
|
||||
}
|
||||
}
|
||||
|
||||
// NewDatabaseWithNodeDB creates a state database with an already initialized node database.
|
||||
func NewDatabaseWithNodeDB(db ethdb.Database, triedb *trie.Database) Database {
|
||||
return &cachingDB{
|
||||
disk: db,
|
||||
codeSizeCache: lru.NewCache[common.Hash, int](codeSizeCacheSize),
|
||||
codeCache: lru.NewSizeConstrainedCache[common.Hash, []byte](codeCacheSize),
|
||||
triedb: triedb,
|
||||
}
|
||||
}
|
||||
|
||||
type cachingDB struct {
|
||||
db *trie.Database
|
||||
disk ethdb.KeyValueStore
|
||||
codeCache *lru.SizeConstrainedCache[common.Hash, []byte]
|
||||
codeSizeCache *lru.Cache[common.Hash, int]
|
||||
codeCache *lru.SizeConstrainedCache[common.Hash, []byte]
|
||||
triedb *trie.Database
|
||||
}
|
||||
|
||||
// OpenTrie opens the main account trie at a specific root hash.
|
||||
func (db *cachingDB) OpenTrie(root common.Hash) (Trie, error) {
|
||||
tr, err := trie.NewStateTrie(trie.StateTrieID(root), db.db)
|
||||
tr, err := trie.NewStateTrie(trie.StateTrieID(root), db.triedb)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -155,7 +165,7 @@ func (db *cachingDB) OpenTrie(root common.Hash) (Trie, error) {
|
|||
|
||||
// OpenStorageTrie opens the storage trie of an account.
|
||||
func (db *cachingDB) OpenStorageTrie(stateRoot common.Hash, addrHash, root common.Hash) (Trie, error) {
|
||||
tr, err := trie.NewStateTrie(trie.StorageTrieID(stateRoot, addrHash, root), db.db)
|
||||
tr, err := trie.NewStateTrie(trie.StorageTrieID(stateRoot, addrHash, root), db.triedb)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -218,5 +228,5 @@ func (db *cachingDB) DiskDB() ethdb.KeyValueStore {
|
|||
|
||||
// TrieDB retrieves any intermediate trie-node caching layer.
|
||||
func (db *cachingDB) TrieDB() *trie.Database {
|
||||
return db.db
|
||||
return db.triedb
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,15 +21,15 @@ import (
|
|||
"testing"
|
||||
|
||||
"github.com/XinFinOrg/XDPoSChain/common"
|
||||
"github.com/XinFinOrg/XDPoSChain/ethdb"
|
||||
)
|
||||
|
||||
// Tests that the node iterator indeed walks over the entire database contents.
|
||||
func TestNodeIteratorCoverage(t *testing.T) {
|
||||
// Create some arbitrary test state to iterate
|
||||
db, root, _ := makeTestState()
|
||||
db, sdb, root, _ := makeTestState()
|
||||
sdb.TrieDB().Commit(root, false)
|
||||
|
||||
state, err := New(root, db)
|
||||
state, err := New(root, sdb)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create state trie at %x: %v", root, err)
|
||||
}
|
||||
|
|
@ -42,19 +42,19 @@ func TestNodeIteratorCoverage(t *testing.T) {
|
|||
}
|
||||
// Cross check the iterated hashes and the database/nodepool content
|
||||
for hash := range hashes {
|
||||
if _, err = db.TrieDB().Node(hash); err != nil {
|
||||
_, err = db.ContractCode(common.Hash{}, hash)
|
||||
if _, err = sdb.TrieDB().Node(hash); err != nil {
|
||||
_, err = sdb.ContractCode(common.Hash{}, hash)
|
||||
}
|
||||
if err != nil {
|
||||
t.Errorf("failed to retrieve reported node %x", hash)
|
||||
}
|
||||
}
|
||||
for _, hash := range db.TrieDB().Nodes() {
|
||||
for _, hash := range sdb.TrieDB().Nodes() {
|
||||
if _, ok := hashes[hash]; !ok {
|
||||
t.Errorf("state entry not reported %x", hash)
|
||||
}
|
||||
}
|
||||
it := db.DiskDB().(ethdb.Database).NewIterator(nil, nil)
|
||||
it := db.NewIterator(nil, nil)
|
||||
for it.Next() {
|
||||
key := it.Key()
|
||||
if bytes.HasPrefix(key, []byte("secure-key-")) {
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ import (
|
|||
)
|
||||
|
||||
// NewStateSync create a new state trie download scheduler.
|
||||
func NewStateSync(root common.Hash, database ethdb.KeyValueReader, onLeaf func(keys [][]byte, leaf []byte) error) *trie.Sync {
|
||||
func NewStateSync(root common.Hash, database ethdb.KeyValueReader, onLeaf func(keys [][]byte, leaf []byte) error, scheme trie.NodeScheme) *trie.Sync {
|
||||
// Register the storage slot callback if the external callback is specified.
|
||||
var onSlot func(keys [][]byte, path []byte, leaf []byte, parent common.Hash, parentPath []byte) error
|
||||
if onLeaf != nil {
|
||||
|
|
@ -52,6 +52,6 @@ func NewStateSync(root common.Hash, database ethdb.KeyValueReader, onLeaf func(k
|
|||
syncer.AddCodeEntry(common.BytesToHash(obj.CodeHash), path, parent, parentPath)
|
||||
return nil
|
||||
}
|
||||
syncer = trie.NewSync(root, database, onAccount)
|
||||
syncer = trie.NewSync(root, database, onAccount, scheme)
|
||||
return syncer
|
||||
}
|
||||
|
|
|
|||
|
|
@ -40,10 +40,11 @@ type testAccount struct {
|
|||
}
|
||||
|
||||
// makeTestState create a sample test state to test node-wise reconstruction.
|
||||
func makeTestState() (Database, common.Hash, []*testAccount) {
|
||||
func makeTestState() (ethdb.Database, Database, common.Hash, []*testAccount) {
|
||||
// Create an empty state
|
||||
db := NewDatabase(rawdb.NewMemoryDatabase())
|
||||
state, _ := New(types.EmptyRootHash, db)
|
||||
db := rawdb.NewMemoryDatabase()
|
||||
sdb := NewDatabase(db)
|
||||
state, _ := New(types.EmptyRootHash, sdb)
|
||||
|
||||
// Fill it with some arbitrary data
|
||||
var accounts []*testAccount
|
||||
|
|
@ -64,7 +65,7 @@ func makeTestState() (Database, common.Hash, []*testAccount) {
|
|||
if i%5 == 0 {
|
||||
for j := byte(0); j < 5; j++ {
|
||||
hash := crypto.Keccak256Hash([]byte{i, i, i, i, i, j, j})
|
||||
obj.SetState(db, hash, hash)
|
||||
obj.SetState(sdb, hash, hash)
|
||||
}
|
||||
}
|
||||
state.updateStateObject(obj)
|
||||
|
|
@ -73,7 +74,7 @@ func makeTestState() (Database, common.Hash, []*testAccount) {
|
|||
root, _ := state.Commit(false)
|
||||
|
||||
// Return the generated state
|
||||
return db, root, accounts
|
||||
return db, sdb, root, accounts
|
||||
}
|
||||
|
||||
// checkStateAccounts cross references a reconstructed state with an expected
|
||||
|
|
@ -101,7 +102,7 @@ func checkStateAccounts(t *testing.T, db ethdb.Database, root common.Hash, accou
|
|||
}
|
||||
|
||||
// checkTrieConsistency checks that all nodes in a (sub-)trie are indeed present.
|
||||
func checkTrieConsistency(db ethdb.KeyValueStore, root common.Hash) error {
|
||||
func checkTrieConsistency(db ethdb.Database, root common.Hash) error {
|
||||
if v, _ := db.Get(root[:]); v == nil {
|
||||
return nil // Consider a non existent state consistent.
|
||||
}
|
||||
|
|
@ -133,7 +134,9 @@ func checkStateConsistency(db ethdb.Database, root common.Hash) error {
|
|||
|
||||
// Tests that an empty state is not scheduled for syncing.
|
||||
func TestEmptyStateSync(t *testing.T) {
|
||||
sync := NewStateSync(types.EmptyRootHash, rawdb.NewMemoryDatabase(), nil)
|
||||
db := trie.NewDatabase(rawdb.NewMemoryDatabase())
|
||||
empty := common.HexToHash("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421")
|
||||
sync := NewStateSync(empty, rawdb.NewMemoryDatabase(), nil, db.Scheme())
|
||||
if paths, nodes, codes := sync.Missing(1); len(paths) != 0 || len(nodes) != 0 || len(codes) != 0 {
|
||||
t.Errorf("content requested for empty state: %v, %v, %v", nodes, paths, codes)
|
||||
}
|
||||
|
|
@ -170,7 +173,7 @@ type stateElement struct {
|
|||
|
||||
func testIterativeStateSync(t *testing.T, count int, commit bool, bypath bool) {
|
||||
// Create a random state to copy
|
||||
srcDb, srcRoot, srcAccounts := makeTestState()
|
||||
_, srcDb, srcRoot, srcAccounts := makeTestState()
|
||||
if commit {
|
||||
srcDb.TrieDB().Commit(srcRoot, false)
|
||||
}
|
||||
|
|
@ -178,7 +181,7 @@ func testIterativeStateSync(t *testing.T, count int, commit bool, bypath bool) {
|
|||
|
||||
// Create a destination state and sync with the scheduler
|
||||
dstDb := rawdb.NewMemoryDatabase()
|
||||
sched := NewStateSync(srcRoot, dstDb, nil)
|
||||
sched := NewStateSync(srcRoot, dstDb, nil, srcDb.TrieDB().Scheme())
|
||||
|
||||
var (
|
||||
nodeElements []stateElement
|
||||
|
|
@ -281,11 +284,11 @@ func testIterativeStateSync(t *testing.T, count int, commit bool, bypath bool) {
|
|||
// partial results are returned, and the others sent only later.
|
||||
func TestIterativeDelayedStateSync(t *testing.T) {
|
||||
// Create a random state to copy
|
||||
srcDb, srcRoot, srcAccounts := makeTestState()
|
||||
_, srcDb, srcRoot, srcAccounts := makeTestState()
|
||||
|
||||
// Create a destination state and sync with the scheduler
|
||||
dstDb := rawdb.NewMemoryDatabase()
|
||||
sched := NewStateSync(srcRoot, dstDb, nil)
|
||||
sched := NewStateSync(srcRoot, dstDb, nil, srcDb.TrieDB().Scheme())
|
||||
|
||||
var (
|
||||
nodeElements []stateElement
|
||||
|
|
@ -374,11 +377,11 @@ func TestIterativeRandomStateSyncBatched(t *testing.T) { testIterativeRandomS
|
|||
|
||||
func testIterativeRandomStateSync(t *testing.T, count int) {
|
||||
// Create a random state to copy
|
||||
srcDb, srcRoot, srcAccounts := makeTestState()
|
||||
_, srcDb, srcRoot, srcAccounts := makeTestState()
|
||||
|
||||
// Create a destination state and sync with the scheduler
|
||||
dstDb := rawdb.NewMemoryDatabase()
|
||||
sched := NewStateSync(srcRoot, dstDb, nil)
|
||||
sched := NewStateSync(srcRoot, dstDb, nil, srcDb.TrieDB().Scheme())
|
||||
|
||||
nodeQueue := make(map[string]stateElement)
|
||||
codeQueue := make(map[common.Hash]struct{})
|
||||
|
|
@ -454,11 +457,11 @@ func testIterativeRandomStateSync(t *testing.T, count int) {
|
|||
// partial results are returned (Even those randomly), others sent only later.
|
||||
func TestIterativeRandomDelayedStateSync(t *testing.T) {
|
||||
// Create a random state to copy
|
||||
srcDb, srcRoot, srcAccounts := makeTestState()
|
||||
_, srcDb, srcRoot, srcAccounts := makeTestState()
|
||||
|
||||
// Create a destination state and sync with the scheduler
|
||||
dstDb := rawdb.NewMemoryDatabase()
|
||||
sched := NewStateSync(srcRoot, dstDb, nil)
|
||||
sched := NewStateSync(srcRoot, dstDb, nil, srcDb.TrieDB().Scheme())
|
||||
|
||||
nodeQueue := make(map[string]stateElement)
|
||||
codeQueue := make(map[common.Hash]struct{})
|
||||
|
|
@ -544,7 +547,7 @@ func TestIterativeRandomDelayedStateSync(t *testing.T) {
|
|||
// the database.
|
||||
func TestIncompleteStateSync(t *testing.T) {
|
||||
// Create a random state to copy
|
||||
srcDb, srcRoot, srcAccounts := makeTestState()
|
||||
db, srcDb, srcRoot, srcAccounts := makeTestState()
|
||||
|
||||
/// isCodeLookup to save some hashing
|
||||
var isCode = make(map[common.Hash]struct{})
|
||||
|
|
@ -554,15 +557,16 @@ func TestIncompleteStateSync(t *testing.T) {
|
|||
}
|
||||
}
|
||||
isCode[types.EmptyCodeHash] = struct{}{}
|
||||
checkTrieConsistency(srcDb.DiskDB(), srcRoot)
|
||||
checkTrieConsistency(db, srcRoot)
|
||||
|
||||
// Create a destination state and sync with the scheduler
|
||||
dstDb := rawdb.NewMemoryDatabase()
|
||||
sched := NewStateSync(srcRoot, dstDb, nil)
|
||||
sched := NewStateSync(srcRoot, dstDb, nil, srcDb.TrieDB().Scheme())
|
||||
|
||||
var (
|
||||
addedCodes []common.Hash
|
||||
addedNodes []common.Hash
|
||||
addedCodes []common.Hash
|
||||
addedPaths []string
|
||||
addedHashes []common.Hash
|
||||
)
|
||||
nodeQueue := make(map[string]stateElement)
|
||||
codeQueue := make(map[common.Hash]struct{})
|
||||
|
|
@ -599,15 +603,16 @@ func TestIncompleteStateSync(t *testing.T) {
|
|||
var nodehashes []common.Hash
|
||||
if len(nodeQueue) > 0 {
|
||||
results := make([]trie.NodeSyncResult, 0, len(nodeQueue))
|
||||
for key, element := range nodeQueue {
|
||||
for path, element := range nodeQueue {
|
||||
data, err := srcDb.TrieDB().Node(element.hash)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to retrieve node data for %x", element.hash)
|
||||
}
|
||||
results = append(results, trie.NodeSyncResult{Path: key, Data: data})
|
||||
results = append(results, trie.NodeSyncResult{Path: path, Data: data})
|
||||
|
||||
if element.hash != srcRoot {
|
||||
addedNodes = append(addedNodes, element.hash)
|
||||
addedPaths = append(addedPaths, element.path)
|
||||
addedHashes = append(addedHashes, element.hash)
|
||||
}
|
||||
nodehashes = append(nodehashes, element.hash)
|
||||
}
|
||||
|
|
@ -655,12 +660,18 @@ func TestIncompleteStateSync(t *testing.T) {
|
|||
}
|
||||
rawdb.WriteCode(dstDb, node, val)
|
||||
}
|
||||
for _, node := range addedNodes {
|
||||
val := rawdb.ReadTrieNode(dstDb, node)
|
||||
rawdb.DeleteTrieNode(dstDb, node)
|
||||
if err := checkStateConsistency(dstDb, srcRoot); err == nil {
|
||||
t.Errorf("trie inconsistency not caught, missing: %v", node.Hex())
|
||||
scheme := srcDb.TrieDB().Scheme()
|
||||
for i, path := range addedPaths {
|
||||
owner, inner := trie.ResolvePath([]byte(path))
|
||||
hash := addedHashes[i]
|
||||
val := scheme.ReadTrieNode(dstDb, owner, inner, hash)
|
||||
if val == nil {
|
||||
t.Error("missing trie node")
|
||||
}
|
||||
rawdb.WriteTrieNode(dstDb, node, val)
|
||||
scheme.DeleteTrieNode(dstDb, owner, inner, hash)
|
||||
if err := checkStateConsistency(dstDb, srcRoot); err == nil {
|
||||
t.Errorf("trie inconsistency not caught, missing: %v", path)
|
||||
}
|
||||
scheme.WriteTrieNode(dstDb, owner, inner, hash, val)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@ import (
|
|||
"github.com/XinFinOrg/XDPoSChain/log"
|
||||
"github.com/XinFinOrg/XDPoSChain/metrics"
|
||||
"github.com/XinFinOrg/XDPoSChain/params"
|
||||
"github.com/XinFinOrg/XDPoSChain/trie"
|
||||
)
|
||||
|
||||
// proposeBlockHandlerFn is a callback type to handle a block by the consensus
|
||||
|
|
@ -205,6 +206,10 @@ type BlockChain interface {
|
|||
|
||||
// InsertReceiptChain inserts a batch of receipts into the local chain.
|
||||
InsertReceiptChain(types.Blocks, []types.Receipts) (int, error)
|
||||
|
||||
// TrieDB retrieves the low level trie database used for interacting
|
||||
// with trie nodes.
|
||||
TrieDB() *trie.Database
|
||||
}
|
||||
|
||||
// New creates a new downloader to fetch hashes and blocks from remote peers.
|
||||
|
|
|
|||
|
|
@ -44,8 +44,10 @@ func init() {
|
|||
}
|
||||
|
||||
// downloadTester is a test simulator for mocking out local block chain.
|
||||
// TODO(daniel): remove field triedb
|
||||
type downloadTester struct {
|
||||
downloader *Downloader
|
||||
triedb *trie.Database
|
||||
|
||||
genesis *types.Block // Genesis blocks used by the tester and peers
|
||||
stateDb ethdb.Database // Database used by the tester for syncing from peers
|
||||
|
|
@ -74,11 +76,16 @@ func newTester() *downloadTester {
|
|||
ownChainTd: map[common.Hash]*big.Int{testGenesis.Hash(): testGenesis.Difficulty()},
|
||||
}
|
||||
tester.stateDb = rawdb.NewMemoryDatabase()
|
||||
tester.triedb = trie.NewDatabase(tester.stateDb)
|
||||
tester.stateDb.Put(testGenesis.Root().Bytes(), []byte{0x00})
|
||||
tester.downloader = New(tester.stateDb, new(event.TypeMux), tester, nil, tester.dropPeer, tester.handleProposedBlock)
|
||||
return tester
|
||||
}
|
||||
|
||||
func (db *downloadTester) TrieDB() *trie.Database {
|
||||
return db.triedb
|
||||
}
|
||||
|
||||
// terminate aborts any operations on the embedded downloader and releases all
|
||||
// held resources.
|
||||
func (dl *downloadTester) terminate() {
|
||||
|
|
|
|||
|
|
@ -291,18 +291,19 @@ type codeTask struct {
|
|||
// newStateSync creates a new state trie download scheduler. This method does not
|
||||
// yet start the sync. The user needs to call run to initiate.
|
||||
// only use fast sync but XDC only run full sync
|
||||
// TODO(daniel): remove field sched
|
||||
func newStateSync(d *Downloader, root common.Hash) *stateSync {
|
||||
return &stateSync{
|
||||
d: d,
|
||||
sched: state.NewStateSync(root, d.stateDB, nil),
|
||||
root: root,
|
||||
cancel: make(chan struct{}),
|
||||
done: make(chan struct{}),
|
||||
started: make(chan struct{}),
|
||||
sched: state.NewStateSync(root, d.stateDB, nil, d.blockchain.TrieDB().Scheme()),
|
||||
keccak: sha3.NewLegacyKeccak256(),
|
||||
trieTasks: make(map[string]*trieTask),
|
||||
codeTasks: make(map[common.Hash]*codeTask),
|
||||
deliver: make(chan *stateReq),
|
||||
cancel: make(chan struct{}),
|
||||
done: make(chan struct{}),
|
||||
started: make(chan struct{}),
|
||||
root: root,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -103,10 +103,8 @@ func (t *BlockTest) Run() error {
|
|||
|
||||
// import pre accounts & construct test genesis block & state root
|
||||
db := rawdb.NewMemoryDatabase()
|
||||
gblock, err := t.genesis(config).Commit(db)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
gspec := t.genesis(config)
|
||||
gblock := gspec.MustCommit(db)
|
||||
if gblock.Hash() != t.json.Genesis.Hash {
|
||||
return fmt.Errorf("genesis block hash doesn't match test: computed=%x, test=%x", gblock.Hash().Bytes(), t.json.Genesis.Hash)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -67,7 +67,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
|
||||
|
|
@ -271,14 +271,14 @@ 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 {
|
||||
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 {
|
||||
cleans = fastcache.New(config.Cache * 1024 * 1024)
|
||||
|
|
@ -891,3 +891,8 @@ func (db *Database) CommitPreimages() error {
|
|||
}
|
||||
return db.preimages.commit(true)
|
||||
}
|
||||
|
||||
// Scheme returns the node scheme used in the database.
|
||||
func (db *Database) Scheme() NodeScheme {
|
||||
return &hashScheme{}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,13 +20,13 @@ import (
|
|||
"testing"
|
||||
|
||||
"github.com/XinFinOrg/XDPoSChain/common"
|
||||
"github.com/XinFinOrg/XDPoSChain/ethdb/memorydb"
|
||||
"github.com/XinFinOrg/XDPoSChain/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")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -327,7 +327,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)
|
||||
|
|
@ -418,8 +418,8 @@ 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()
|
||||
// Commit test trie to db, then remove the node containing "bars".
|
||||
diskdb := rawdb.NewMemoryDatabase()
|
||||
triedb := NewDatabase(diskdb)
|
||||
|
||||
ctr := NewEmpty(triedb)
|
||||
|
|
@ -528,7 +528,7 @@ func (l *loggingDb) Close() error {
|
|||
func makeLargeTestTrie() (*Database, *StateTrie, *loggingDb) {
|
||||
// Create an empty trie
|
||||
logDb := &loggingDb{0, memorydb.New()}
|
||||
triedb := NewDatabase(logDb)
|
||||
triedb := NewDatabase(rawdb.NewDatabase(logDb))
|
||||
trie, _ := NewStateTrie(TrieID(common.Hash{}), triedb)
|
||||
|
||||
// Fill it with some arbitrary data
|
||||
|
|
@ -563,7 +563,7 @@ func TestNodeIteratorLargeTrie(t *testing.T) {
|
|||
|
||||
func TestIteratorNodeBlob(t *testing.T) {
|
||||
var (
|
||||
db = memorydb.New()
|
||||
db = rawdb.NewMemoryDatabase()
|
||||
triedb = NewDatabase(db)
|
||||
trie = NewEmpty(triedb)
|
||||
)
|
||||
|
|
|
|||
96
trie/schema.go
Normal file
96
trie/schema.go
Normal file
|
|
@ -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/XinFinOrg/XDPoSChain/common"
|
||||
"github.com/XinFinOrg/XDPoSChain/core/rawdb"
|
||||
"github.com/XinFinOrg/XDPoSChain/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
|
||||
)
|
||||
|
||||
// NodeScheme describes 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
|
||||
}
|
||||
|
|
@ -24,19 +24,19 @@ import (
|
|||
"testing"
|
||||
|
||||
"github.com/XinFinOrg/XDPoSChain/common"
|
||||
"github.com/XinFinOrg/XDPoSChain/core/rawdb"
|
||||
"github.com/XinFinOrg/XDPoSChain/crypto"
|
||||
"github.com/XinFinOrg/XDPoSChain/ethdb/memorydb"
|
||||
)
|
||||
|
||||
func newEmptySecure() *StateTrie {
|
||||
trie, _ := NewStateTrie(TrieID(common.Hash{}), NewDatabase(memorydb.New()))
|
||||
trie, _ := NewStateTrie(TrieID(common.Hash{}), NewDatabase(rawdb.NewMemoryDatabase()))
|
||||
return trie
|
||||
}
|
||||
|
||||
// makeTestStateTrie creates a large enough secure trie for testing.
|
||||
func makeTestStateTrie() (*Database, *StateTrie, map[string][]byte) {
|
||||
// Create an empty trie
|
||||
triedb := NewDatabase(memorydb.New())
|
||||
triedb := NewDatabase(rawdb.NewMemoryDatabase())
|
||||
trie, _ := NewStateTrie(TrieID(common.Hash{}), triedb)
|
||||
|
||||
// Fill it with some arbitrary data
|
||||
|
|
|
|||
|
|
@ -26,7 +26,6 @@ import (
|
|||
|
||||
"github.com/XinFinOrg/XDPoSChain/common"
|
||||
"github.com/XinFinOrg/XDPoSChain/core/types"
|
||||
"github.com/XinFinOrg/XDPoSChain/ethdb"
|
||||
"github.com/XinFinOrg/XDPoSChain/log"
|
||||
)
|
||||
|
||||
|
|
@ -38,10 +37,14 @@ var stPool = sync.Pool{
|
|||
},
|
||||
}
|
||||
|
||||
func stackTrieFromPool(db ethdb.KeyValueWriter, owner common.Hash) *StackTrie {
|
||||
// NodeWriteFunc is used to provide all information of a dirty node for committing
|
||||
// so that callers can flush nodes into database with desired scheme.
|
||||
type NodeWriteFunc = func(owner common.Hash, path []byte, hash common.Hash, blob []byte)
|
||||
|
||||
func stackTrieFromPool(writeFn NodeWriteFunc, owner common.Hash) *StackTrie {
|
||||
st := stPool.Get().(*StackTrie)
|
||||
st.db = db
|
||||
st.owner = owner
|
||||
st.writeFn = writeFn
|
||||
return st
|
||||
}
|
||||
|
||||
|
|
@ -54,41 +57,41 @@ func returnToPool(st *StackTrie) {
|
|||
// in order. Once it determines that a subtree will no longer be inserted
|
||||
// into, it will hash it and free up the memory it uses.
|
||||
type StackTrie struct {
|
||||
owner common.Hash // the owner of the trie
|
||||
nodeType uint8 // node type (as in branch, ext, leaf)
|
||||
val []byte // value contained by this node if it's a leaf
|
||||
key []byte // key chunk covered by this (leaf|ext) node
|
||||
children [16]*StackTrie // list of children (for branch and exts)
|
||||
db ethdb.KeyValueWriter // Pointer to the commit db, can be nil
|
||||
owner common.Hash // the owner of the trie
|
||||
nodeType uint8 // node type (as in branch, ext, leaf)
|
||||
val []byte // value contained by this node if it's a leaf
|
||||
key []byte // key chunk covered by this (leaf|ext) node
|
||||
children [16]*StackTrie // list of children (for branch and exts)
|
||||
writeFn NodeWriteFunc // function for committing nodes, can be nil
|
||||
}
|
||||
|
||||
// NewStackTrie allocates and initializes an empty trie.
|
||||
func NewStackTrie(db ethdb.KeyValueWriter) *StackTrie {
|
||||
func NewStackTrie(writeFn NodeWriteFunc) *StackTrie {
|
||||
return &StackTrie{
|
||||
nodeType: emptyNode,
|
||||
db: db,
|
||||
writeFn: writeFn,
|
||||
}
|
||||
}
|
||||
|
||||
// NewStackTrieWithOwner allocates and initializes an empty trie, but with
|
||||
// the additional owner field.
|
||||
func NewStackTrieWithOwner(db ethdb.KeyValueWriter, owner common.Hash) *StackTrie {
|
||||
func NewStackTrieWithOwner(writeFn NodeWriteFunc, owner common.Hash) *StackTrie {
|
||||
return &StackTrie{
|
||||
owner: owner,
|
||||
nodeType: emptyNode,
|
||||
db: db,
|
||||
writeFn: writeFn,
|
||||
}
|
||||
}
|
||||
|
||||
// NewFromBinary initialises a serialized stacktrie with the given db.
|
||||
func NewFromBinary(data []byte, db ethdb.KeyValueWriter) (*StackTrie, error) {
|
||||
func NewFromBinary(data []byte, writeFn NodeWriteFunc) (*StackTrie, error) {
|
||||
var st StackTrie
|
||||
if err := st.UnmarshalBinary(data); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// If a database is used, we need to recursively add it to every child
|
||||
if db != nil {
|
||||
st.setDb(db)
|
||||
if writeFn != nil {
|
||||
st.setWriter(writeFn)
|
||||
}
|
||||
return &st, nil
|
||||
}
|
||||
|
|
@ -161,25 +164,25 @@ func (st *StackTrie) unmarshalBinary(r io.Reader) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (st *StackTrie) setDb(db ethdb.KeyValueWriter) {
|
||||
st.db = db
|
||||
func (st *StackTrie) setWriter(writeFn NodeWriteFunc) {
|
||||
st.writeFn = writeFn
|
||||
for _, child := range st.children {
|
||||
if child != nil {
|
||||
child.setDb(db)
|
||||
child.setWriter(writeFn)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func newLeaf(owner common.Hash, key, val []byte, db ethdb.KeyValueWriter) *StackTrie {
|
||||
st := stackTrieFromPool(db, owner)
|
||||
func newLeaf(owner common.Hash, key, val []byte, writeFn NodeWriteFunc) *StackTrie {
|
||||
st := stackTrieFromPool(writeFn, owner)
|
||||
st.nodeType = leafNode
|
||||
st.key = append(st.key, key...)
|
||||
st.val = val
|
||||
return st
|
||||
}
|
||||
|
||||
func newExt(owner common.Hash, key []byte, child *StackTrie, db ethdb.KeyValueWriter) *StackTrie {
|
||||
st := stackTrieFromPool(db, owner)
|
||||
func newExt(owner common.Hash, key []byte, child *StackTrie, writeFn NodeWriteFunc) *StackTrie {
|
||||
st := stackTrieFromPool(writeFn, owner)
|
||||
st.nodeType = extNode
|
||||
st.key = append(st.key, key...)
|
||||
st.children[0] = child
|
||||
|
|
@ -201,7 +204,7 @@ func (st *StackTrie) TryUpdate(key, value []byte) error {
|
|||
if len(value) == 0 {
|
||||
panic("deletion not supported")
|
||||
}
|
||||
st.insert(k[:len(k)-1], value)
|
||||
st.insert(k[:len(k)-1], value, nil)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -213,7 +216,7 @@ func (st *StackTrie) Update(key, value []byte) {
|
|||
|
||||
func (st *StackTrie) Reset() {
|
||||
st.owner = common.Hash{}
|
||||
st.db = nil
|
||||
st.writeFn = nil
|
||||
st.key = st.key[:0]
|
||||
st.val = nil
|
||||
for i := range st.children {
|
||||
|
|
@ -236,7 +239,7 @@ func (st *StackTrie) getDiffIndex(key []byte) int {
|
|||
|
||||
// Helper function to that inserts a (key, value) pair into
|
||||
// the trie.
|
||||
func (st *StackTrie) insert(key, value []byte) {
|
||||
func (st *StackTrie) insert(key, value []byte, prefix []byte) {
|
||||
switch st.nodeType {
|
||||
case branchNode: /* Branch */
|
||||
idx := int(key[0])
|
||||
|
|
@ -245,7 +248,7 @@ func (st *StackTrie) insert(key, value []byte) {
|
|||
for i := idx - 1; i >= 0; i-- {
|
||||
if st.children[i] != nil {
|
||||
if st.children[i].nodeType != hashedNode {
|
||||
st.children[i].hash()
|
||||
st.children[i].hash(append(prefix, byte(i)))
|
||||
}
|
||||
break
|
||||
}
|
||||
|
|
@ -253,9 +256,9 @@ func (st *StackTrie) insert(key, value []byte) {
|
|||
|
||||
// Add new child
|
||||
if st.children[idx] == nil {
|
||||
st.children[idx] = newLeaf(st.owner, key[1:], value, st.db)
|
||||
st.children[idx] = newLeaf(st.owner, key[1:], value, st.writeFn)
|
||||
} else {
|
||||
st.children[idx].insert(key[1:], value)
|
||||
st.children[idx].insert(key[1:], value, append(prefix, key[0]))
|
||||
}
|
||||
|
||||
case extNode: /* Ext */
|
||||
|
|
@ -270,7 +273,7 @@ func (st *StackTrie) insert(key, value []byte) {
|
|||
if diffidx == len(st.key) {
|
||||
// Ext key and key segment are identical, recurse into
|
||||
// the child node.
|
||||
st.children[0].insert(key[diffidx:], value)
|
||||
st.children[0].insert(key[diffidx:], value, append(prefix, key[:diffidx]...))
|
||||
return
|
||||
}
|
||||
// Save the original part. Depending if the break is
|
||||
|
|
@ -279,14 +282,19 @@ func (st *StackTrie) insert(key, value []byte) {
|
|||
// node directly.
|
||||
var n *StackTrie
|
||||
if diffidx < len(st.key)-1 {
|
||||
n = newExt(st.owner, st.key[diffidx+1:], st.children[0], st.db)
|
||||
// Break on the non-last byte, insert an intermediate
|
||||
// extension. The path prefix of the newly-inserted
|
||||
// extension should also contain the different byte.
|
||||
n = newExt(st.owner, st.key[diffidx+1:], st.children[0], st.writeFn)
|
||||
n.hash(append(prefix, st.key[:diffidx+1]...))
|
||||
} else {
|
||||
// Break on the last byte, no need to insert
|
||||
// an extension node: reuse the current node
|
||||
// an extension node: reuse the current node.
|
||||
// The path prefix of the original part should
|
||||
// still be same.
|
||||
n = st.children[0]
|
||||
n.hash(append(prefix, st.key...))
|
||||
}
|
||||
// Convert to hash
|
||||
n.hash()
|
||||
var p *StackTrie
|
||||
if diffidx == 0 {
|
||||
// the break is on the first byte, so
|
||||
|
|
@ -299,12 +307,12 @@ func (st *StackTrie) insert(key, value []byte) {
|
|||
// the common prefix is at least one byte
|
||||
// long, insert a new intermediate branch
|
||||
// node.
|
||||
st.children[0] = stackTrieFromPool(st.db, st.owner)
|
||||
st.children[0] = stackTrieFromPool(st.writeFn, st.owner)
|
||||
st.children[0].nodeType = branchNode
|
||||
p = st.children[0]
|
||||
}
|
||||
// Create a leaf for the inserted part
|
||||
o := newLeaf(st.owner, key[diffidx+1:], value, st.db)
|
||||
o := newLeaf(st.owner, key[diffidx+1:], value, st.writeFn)
|
||||
|
||||
// Insert both child leaves where they belong:
|
||||
origIdx := st.key[diffidx]
|
||||
|
|
@ -340,7 +348,7 @@ func (st *StackTrie) insert(key, value []byte) {
|
|||
// Convert current node into an ext,
|
||||
// and insert a child branch node.
|
||||
st.nodeType = extNode
|
||||
st.children[0] = NewStackTrieWithOwner(st.db, st.owner)
|
||||
st.children[0] = NewStackTrieWithOwner(st.writeFn, st.owner)
|
||||
st.children[0].nodeType = branchNode
|
||||
p = st.children[0]
|
||||
}
|
||||
|
|
@ -349,11 +357,11 @@ func (st *StackTrie) insert(key, value []byte) {
|
|||
// value and another containing the new value. The child leaf
|
||||
// is hashed directly in order to free up some memory.
|
||||
origIdx := st.key[diffidx]
|
||||
p.children[origIdx] = newLeaf(st.owner, st.key[diffidx+1:], st.val, st.db)
|
||||
p.children[origIdx].hash()
|
||||
p.children[origIdx] = newLeaf(st.owner, st.key[diffidx+1:], st.val, st.writeFn)
|
||||
p.children[origIdx].hash(append(prefix, st.key[:diffidx+1]...))
|
||||
|
||||
newIdx := key[diffidx]
|
||||
p.children[newIdx] = newLeaf(st.owner, key[diffidx+1:], value, st.db)
|
||||
p.children[newIdx] = newLeaf(st.owner, key[diffidx+1:], value, st.writeFn)
|
||||
|
||||
// Finally, cut off the key part that has been passed
|
||||
// over to the children.
|
||||
|
|
@ -384,14 +392,14 @@ func (st *StackTrie) insert(key, value []byte) {
|
|||
// - And the 'st.type' will be 'hashedNode' AGAIN
|
||||
//
|
||||
// This method also sets 'st.type' to hashedNode, and clears 'st.key'.
|
||||
func (st *StackTrie) hash() {
|
||||
func (st *StackTrie) hash(path []byte) {
|
||||
h := newHasher(false)
|
||||
defer returnHasherToPool(h)
|
||||
|
||||
st.hashRec(h)
|
||||
st.hashRec(h, path)
|
||||
}
|
||||
|
||||
func (st *StackTrie) hashRec(hasher *hasher) {
|
||||
func (st *StackTrie) hashRec(hasher *hasher, path []byte) {
|
||||
// The switch below sets this to the RLP-encoding of this node.
|
||||
var encodedNode []byte
|
||||
|
||||
|
|
@ -412,8 +420,7 @@ func (st *StackTrie) hashRec(hasher *hasher) {
|
|||
nodes[i] = nilValueNode
|
||||
continue
|
||||
}
|
||||
|
||||
child.hashRec(hasher)
|
||||
child.hashRec(hasher, append(path, byte(i)))
|
||||
if len(child.val) < 32 {
|
||||
nodes[i] = rawNode(child.val)
|
||||
} else {
|
||||
|
|
@ -429,10 +436,9 @@ func (st *StackTrie) hashRec(hasher *hasher) {
|
|||
encodedNode = hasher.encodedBytes()
|
||||
|
||||
case extNode:
|
||||
st.children[0].hashRec(hasher)
|
||||
st.children[0].hashRec(hasher, append(path, st.key...))
|
||||
|
||||
sz := hexToCompactInPlace(st.key)
|
||||
n := rawShortNode{Key: st.key[:sz]}
|
||||
n := rawShortNode{Key: hexToCompact(st.key)}
|
||||
if len(st.children[0].val) < 32 {
|
||||
n.Val = rawNode(st.children[0].val)
|
||||
} else {
|
||||
|
|
@ -448,8 +454,7 @@ func (st *StackTrie) hashRec(hasher *hasher) {
|
|||
|
||||
case leafNode:
|
||||
st.key = append(st.key, byte(16))
|
||||
sz := hexToCompactInPlace(st.key)
|
||||
n := rawShortNode{Key: st.key[:sz], Val: valueNode(st.val)}
|
||||
n := rawShortNode{Key: hexToCompact(st.key), Val: valueNode(st.val)}
|
||||
|
||||
n.encode(hasher.encbuf)
|
||||
encodedNode = hasher.encodedBytes()
|
||||
|
|
@ -468,10 +473,8 @@ func (st *StackTrie) hashRec(hasher *hasher) {
|
|||
// Write the hash to the 'val'. We allocate a new val here to not mutate
|
||||
// input values
|
||||
st.val = hasher.hashData(encodedNode)
|
||||
if st.db != nil {
|
||||
// TODO! Is it safe to Put the slice here?
|
||||
// Do all db implementations copy the value provided?
|
||||
st.db.Put(st.val, encodedNode)
|
||||
if st.writeFn != nil {
|
||||
st.writeFn(st.owner, path, common.BytesToHash(st.val), encodedNode)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -480,12 +483,11 @@ func (st *StackTrie) Hash() (h common.Hash) {
|
|||
hasher := newHasher(false)
|
||||
defer returnHasherToPool(hasher)
|
||||
|
||||
st.hashRec(hasher)
|
||||
st.hashRec(hasher, nil)
|
||||
if len(st.val) == 32 {
|
||||
copy(h[:], st.val)
|
||||
return h
|
||||
}
|
||||
|
||||
// If the node's RLP isn't 32 bytes long, the node will not
|
||||
// be hashed, and instead contain the rlp-encoding of the
|
||||
// node. For the top level node, we need to force the hashing.
|
||||
|
|
@ -503,25 +505,24 @@ func (st *StackTrie) Hash() (h common.Hash) {
|
|||
// The associated database is expected, otherwise the whole commit
|
||||
// functionality should be disabled.
|
||||
func (st *StackTrie) Commit() (h common.Hash, err error) {
|
||||
if st.db == nil {
|
||||
if st.writeFn == nil {
|
||||
return common.Hash{}, ErrCommitDisabled
|
||||
}
|
||||
|
||||
hasher := newHasher(false)
|
||||
defer returnHasherToPool(hasher)
|
||||
|
||||
st.hashRec(hasher)
|
||||
st.hashRec(hasher, nil)
|
||||
if len(st.val) == 32 {
|
||||
copy(h[:], st.val)
|
||||
return h, nil
|
||||
}
|
||||
|
||||
// If the node's RLP isn't 32 bytes long, the node will not
|
||||
// be hashed (and committed), and instead contain the rlp-encoding of the
|
||||
// be hashed (and committed), and instead contain the rlp-encoding of the
|
||||
// node. For the top level node, we need to force the hashing+commit.
|
||||
hasher.sha.Reset()
|
||||
hasher.sha.Write(st.val)
|
||||
hasher.sha.Read(h[:])
|
||||
st.db.Put(h[:], st.val)
|
||||
|
||||
st.writeFn(st.owner, nil, h, st.val)
|
||||
return h, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,8 +22,8 @@ import (
|
|||
"testing"
|
||||
|
||||
"github.com/XinFinOrg/XDPoSChain/common"
|
||||
"github.com/XinFinOrg/XDPoSChain/core/rawdb"
|
||||
"github.com/XinFinOrg/XDPoSChain/crypto"
|
||||
"github.com/XinFinOrg/XDPoSChain/ethdb/memorydb"
|
||||
)
|
||||
|
||||
func TestStackTrieInsertAndHash(t *testing.T) {
|
||||
|
|
@ -188,7 +188,7 @@ 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")
|
||||
|
|
@ -203,7 +203,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")
|
||||
|
|
@ -229,7 +229,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")
|
||||
|
|
@ -254,7 +254,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
|
||||
|
|
@ -283,7 +283,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
|
||||
|
|
@ -353,7 +353,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
|
||||
|
|
|
|||
48
trie/sync.go
48
trie/sync.go
|
|
@ -65,7 +65,7 @@ type SyncPath [][]byte
|
|||
// version that can be sent over the network.
|
||||
func NewSyncPath(path []byte) SyncPath {
|
||||
// If the hash is from the account trie, append a single item, if it
|
||||
// is from the a storage trie, append a tuple. Note, the length 64 is
|
||||
// is from a storage trie, append a tuple. Note, the length 64 is
|
||||
// clashing between account leaf and storage root. It's fine though
|
||||
// because having a trie node at 64 depth means a hash collision was
|
||||
// found and we're long dead.
|
||||
|
|
@ -75,6 +75,22 @@ func NewSyncPath(path []byte) SyncPath {
|
|||
return SyncPath{hexToKeybytes(path[:64]), hexToCompact(path[64:])}
|
||||
}
|
||||
|
||||
// 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
|
||||
|
||||
// nodeRequest represents a scheduled or already in-flight trie node retrieval request.
|
||||
type nodeRequest struct {
|
||||
hash common.Hash // Hash of the trie node to retrieve
|
||||
|
|
@ -139,6 +155,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
|
||||
|
|
@ -148,8 +165,9 @@ type Sync struct {
|
|||
}
|
||||
|
||||
// NewSync creates a new trie data download scheduler.
|
||||
func NewSync(root common.Hash, database ethdb.KeyValueReader, callback LeafCallback) *Sync {
|
||||
func NewSync(root common.Hash, database ethdb.KeyValueReader, callback LeafCallback, scheme NodeScheme) *Sync {
|
||||
ts := &Sync{
|
||||
scheme: scheme,
|
||||
database: database,
|
||||
membatch: newSyncMemBatch(),
|
||||
nodeReqs: make(map[string]*nodeRequest),
|
||||
|
|
@ -172,7 +190,8 @@ func (s *Sync) AddSubTrie(root common.Hash, path []byte, parent common.Hash, par
|
|||
if s.membatch.hasNode(path) {
|
||||
return
|
||||
}
|
||||
if rawdb.HasTrieNode(s.database, root) {
|
||||
owner, inner := ResolvePath(path)
|
||||
if s.scheme.HasTrieNode(s.database, owner, inner, root) {
|
||||
return
|
||||
}
|
||||
// Assemble the new sub-trie sync request
|
||||
|
|
@ -205,7 +224,7 @@ func (s *Sync) AddCodeEntry(hash common.Hash, path []byte, parent common.Hash, p
|
|||
return
|
||||
}
|
||||
// If database says duplicate, the blob is present for sure.
|
||||
// Note we only check the existence with new code scheme, fast
|
||||
// Note we only check the existence with new code scheme, snap
|
||||
// sync is expected to run with a fresh new node. Even there
|
||||
// exists the code with legacy format, fetch and store with
|
||||
// new scheme anyway.
|
||||
|
|
@ -329,7 +348,8 @@ func (s *Sync) ProcessNode(result NodeSyncResult) error {
|
|||
func (s *Sync) Commit(dbw ethdb.Batch) error {
|
||||
// Dump the membatch into a database dbw
|
||||
for path, value := range s.membatch.nodes {
|
||||
rawdb.WriteTrieNode(dbw, s.membatch.hashes[path], value)
|
||||
owner, inner := ResolvePath([]byte(path))
|
||||
s.scheme.WriteTrieNode(dbw, owner, inner, s.membatch.hashes[path], value)
|
||||
}
|
||||
for hash, value := range s.membatch.codes {
|
||||
rawdb.WriteCode(dbw, hash, value)
|
||||
|
|
@ -445,8 +465,11 @@ func (s *Sync) children(req *nodeRequest, object node) ([]*nodeRequest, error) {
|
|||
|
||||
// If database says duplicate, then at least the trie node is present
|
||||
// and we hold the assumption that it's NOT legacy contract code.
|
||||
chash := common.BytesToHash(node)
|
||||
if rawdb.HasTrieNode(s.database, chash) {
|
||||
var (
|
||||
chash = common.BytesToHash(node)
|
||||
owner, inner = ResolvePath(child.path)
|
||||
)
|
||||
if s.scheme.HasTrieNode(s.database, owner, inner, chash) {
|
||||
return
|
||||
}
|
||||
// Locally unknown node, schedule for retrieval
|
||||
|
|
@ -516,3 +539,14 @@ func (s *Sync) commitCodeRequest(req *codeRequest) error {
|
|||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ResolvePath resolves the provided composite node path by separating the
|
||||
// path in account trie if it's existent.
|
||||
func ResolvePath(path []byte) (common.Hash, []byte) {
|
||||
var owner common.Hash
|
||||
if len(path) >= 2*common.HashLength {
|
||||
owner = common.BytesToHash(hexToKeybytes(path[:2*common.HashLength]))
|
||||
path = path[2*common.HashLength:]
|
||||
}
|
||||
return owner, path
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ import (
|
|||
"testing"
|
||||
|
||||
"github.com/XinFinOrg/XDPoSChain/common"
|
||||
"github.com/XinFinOrg/XDPoSChain/core/rawdb"
|
||||
"github.com/XinFinOrg/XDPoSChain/core/types"
|
||||
"github.com/XinFinOrg/XDPoSChain/crypto"
|
||||
"github.com/XinFinOrg/XDPoSChain/ethdb/memorydb"
|
||||
|
|
@ -30,7 +31,7 @@ import (
|
|||
// makeTestTrie create a sample test trie to test node-wise reconstruction.
|
||||
func makeTestTrie() (*Database, *StateTrie, map[string][]byte) {
|
||||
// Create an empty trie
|
||||
triedb := NewDatabase(memorydb.New())
|
||||
triedb := NewDatabase(rawdb.NewMemoryDatabase())
|
||||
trie, _ := NewStateTrie(TrieID(common.Hash{}), triedb)
|
||||
|
||||
// Fill it with some arbitrary data
|
||||
|
|
@ -104,13 +105,13 @@ type trieElement struct {
|
|||
|
||||
// Tests that an empty trie is not scheduled for syncing.
|
||||
func TestEmptySync(t *testing.T) {
|
||||
dbA := NewDatabase(memorydb.New())
|
||||
dbB := NewDatabase(memorydb.New())
|
||||
dbA := NewDatabase(rawdb.NewMemoryDatabase())
|
||||
dbB := NewDatabase(rawdb.NewMemoryDatabase())
|
||||
emptyA, _ := New(TrieID(common.Hash{}), dbA)
|
||||
emptyB, _ := New(TrieID(types.EmptyRootHash), dbB)
|
||||
|
||||
for i, trie := range []*Trie{emptyA, emptyB} {
|
||||
sync := NewSync(trie.Hash(), memorydb.New(), nil)
|
||||
sync := NewSync(trie.Hash(), memorydb.New(), nil, []*Database{dbA, dbB}[i].Scheme())
|
||||
if paths, nodes, codes := sync.Missing(1); len(paths) != 0 || len(nodes) != 0 || len(codes) != 0 {
|
||||
t.Errorf("test %d: content requested for empty trie: %v, %v, %v", i, paths, nodes, codes)
|
||||
}
|
||||
|
|
@ -129,9 +130,9 @@ func testIterativeSync(t *testing.T, count int, bypath bool) {
|
|||
srcDb, srcTrie, srcData := makeTestTrie()
|
||||
|
||||
// Create a destination trie and sync with the scheduler
|
||||
diskdb := memorydb.New()
|
||||
diskdb := rawdb.NewMemoryDatabase()
|
||||
triedb := NewDatabase(diskdb)
|
||||
sched := NewSync(srcTrie.Hash(), diskdb, nil)
|
||||
sched := NewSync(srcTrie.Hash(), diskdb, nil, srcDb.Scheme())
|
||||
|
||||
// The code requests are ignored here since there is no code
|
||||
// at the testing trie.
|
||||
|
|
@ -195,9 +196,9 @@ func TestIterativeDelayedSync(t *testing.T) {
|
|||
srcDb, srcTrie, srcData := makeTestTrie()
|
||||
|
||||
// Create a destination trie and sync with the scheduler
|
||||
diskdb := memorydb.New()
|
||||
diskdb := rawdb.NewMemoryDatabase()
|
||||
triedb := NewDatabase(diskdb)
|
||||
sched := NewSync(srcTrie.Hash(), diskdb, nil)
|
||||
sched := NewSync(srcTrie.Hash(), diskdb, nil, srcDb.Scheme())
|
||||
|
||||
// The code requests are ignored here since there is no code
|
||||
// at the testing trie.
|
||||
|
|
@ -256,9 +257,9 @@ func testIterativeRandomSync(t *testing.T, count int) {
|
|||
srcDb, srcTrie, srcData := makeTestTrie()
|
||||
|
||||
// Create a destination trie and sync with the scheduler
|
||||
diskdb := memorydb.New()
|
||||
diskdb := rawdb.NewMemoryDatabase()
|
||||
triedb := NewDatabase(diskdb)
|
||||
sched := NewSync(srcTrie.Hash(), diskdb, nil)
|
||||
sched := NewSync(srcTrie.Hash(), diskdb, nil, srcDb.Scheme())
|
||||
|
||||
// The code requests are ignored here since there is no code
|
||||
// at the testing trie.
|
||||
|
|
@ -314,9 +315,9 @@ func TestIterativeRandomDelayedSync(t *testing.T) {
|
|||
srcDb, srcTrie, srcData := makeTestTrie()
|
||||
|
||||
// Create a destination trie and sync with the scheduler
|
||||
diskdb := memorydb.New()
|
||||
diskdb := rawdb.NewMemoryDatabase()
|
||||
triedb := NewDatabase(diskdb)
|
||||
sched := NewSync(srcTrie.Hash(), diskdb, nil)
|
||||
sched := NewSync(srcTrie.Hash(), diskdb, nil, srcDb.Scheme())
|
||||
|
||||
// The code requests are ignored here since there is no code
|
||||
// at the testing trie.
|
||||
|
|
@ -377,9 +378,9 @@ func TestDuplicateAvoidanceSync(t *testing.T) {
|
|||
srcDb, srcTrie, srcData := makeTestTrie()
|
||||
|
||||
// Create a destination trie and sync with the scheduler
|
||||
diskdb := memorydb.New()
|
||||
diskdb := rawdb.NewMemoryDatabase()
|
||||
triedb := NewDatabase(diskdb)
|
||||
sched := NewSync(srcTrie.Hash(), diskdb, nil)
|
||||
sched := NewSync(srcTrie.Hash(), diskdb, nil, srcDb.Scheme())
|
||||
|
||||
// The code requests are ignored here since there is no code
|
||||
// at the testing trie.
|
||||
|
|
@ -440,9 +441,9 @@ func TestIncompleteSync(t *testing.T) {
|
|||
srcDb, srcTrie, _ := makeTestTrie()
|
||||
|
||||
// Create a destination trie and sync with the scheduler
|
||||
diskdb := memorydb.New()
|
||||
diskdb := rawdb.NewMemoryDatabase()
|
||||
triedb := NewDatabase(diskdb)
|
||||
sched := NewSync(srcTrie.Hash(), diskdb, nil)
|
||||
sched := NewSync(srcTrie.Hash(), diskdb, nil, srcDb.Scheme())
|
||||
|
||||
// The code requests are ignored here since there is no code
|
||||
// at the testing trie.
|
||||
|
|
@ -520,9 +521,9 @@ func TestSyncOrdering(t *testing.T) {
|
|||
srcDb, srcTrie, srcData := makeTestTrie()
|
||||
|
||||
// Create a destination trie and sync with the scheduler, tracking the requests
|
||||
diskdb := memorydb.New()
|
||||
diskdb := rawdb.NewMemoryDatabase()
|
||||
triedb := NewDatabase(diskdb)
|
||||
sched := NewSync(srcTrie.Hash(), diskdb, nil)
|
||||
sched := NewSync(srcTrie.Hash(), diskdb, nil, srcDb.Scheme())
|
||||
|
||||
// The code requests are ignored here since there is no code
|
||||
// at the testing trie.
|
||||
|
|
|
|||
16
trie/trie.go
16
trie/trie.go
|
|
@ -27,22 +27,6 @@ import (
|
|||
"github.com/XinFinOrg/XDPoSChain/log"
|
||||
)
|
||||
|
||||
// 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
|
||||
|
||||
// Trie is a Merkle Patricia Trie. Use New to create a trie that sits on
|
||||
// top of a database. Whenever trie performs a commit operation, the generated
|
||||
// nodes will be gathered and returned in a set. Once the trie is committed,
|
||||
|
|
|
|||
|
|
@ -35,7 +35,6 @@ import (
|
|||
"github.com/XinFinOrg/XDPoSChain/core/types"
|
||||
"github.com/XinFinOrg/XDPoSChain/crypto"
|
||||
"github.com/XinFinOrg/XDPoSChain/ethdb"
|
||||
"github.com/XinFinOrg/XDPoSChain/ethdb/memorydb"
|
||||
"github.com/XinFinOrg/XDPoSChain/rlp"
|
||||
"github.com/davecgh/go-spew/spew"
|
||||
"golang.org/x/crypto/sha3"
|
||||
|
|
@ -67,7 +66,7 @@ func TestNull(t *testing.T) {
|
|||
|
||||
func TestMissingRoot(t *testing.T) {
|
||||
root := common.HexToHash("0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33")
|
||||
trie, err := New(TrieID(root), NewDatabase(memorydb.New()))
|
||||
trie, err := New(TrieID(root), NewDatabase(rawdb.NewMemoryDatabase()))
|
||||
if trie != nil {
|
||||
t.Error("New returned non-nil trie for invalid root")
|
||||
}
|
||||
|
|
@ -80,7 +79,7 @@ func TestMissingNodeDisk(t *testing.T) { testMissingNode(t, false) }
|
|||
func TestMissingNodeMemonly(t *testing.T) { testMissingNode(t, true) }
|
||||
|
||||
func testMissingNode(t *testing.T, memonly bool) {
|
||||
diskdb := memorydb.New()
|
||||
diskdb := rawdb.NewMemoryDatabase()
|
||||
triedb := NewDatabase(diskdb)
|
||||
|
||||
trie := NewEmpty(triedb)
|
||||
|
|
@ -435,7 +434,7 @@ func runRandTestBool(rt randTest) bool {
|
|||
|
||||
func runRandTest(rt randTest) error {
|
||||
var (
|
||||
triedb = NewDatabase(memorydb.New())
|
||||
triedb = NewDatabase(rawdb.NewMemoryDatabase())
|
||||
tr = NewEmpty(triedb)
|
||||
values = make(map[string]string) // tracks content of the trie
|
||||
origTrie = NewEmpty(triedb)
|
||||
|
|
@ -815,11 +814,13 @@ func TestCommitSequenceStackTrie(t *testing.T) {
|
|||
prng := rand.New(rand.NewSource(int64(count)))
|
||||
// This spongeDb is used to check the sequence of disk-db-writes
|
||||
s := &spongeDb{sponge: sha3.NewLegacyKeccak256(), id: "a"}
|
||||
db := NewDatabase(s)
|
||||
db := NewDatabase(rawdb.NewDatabase(s))
|
||||
trie := NewEmpty(db)
|
||||
// Another sponge is used for the stacktrie commits
|
||||
stackTrieSponge := &spongeDb{sponge: sha3.NewLegacyKeccak256(), id: "b"}
|
||||
stTrie := NewStackTrie(stackTrieSponge)
|
||||
stTrie := NewStackTrie(func(owner common.Hash, path []byte, hash common.Hash, blob []byte) {
|
||||
db.Scheme().WriteTrieNode(stackTrieSponge, owner, path, hash, blob)
|
||||
})
|
||||
// Fill the trie with elements
|
||||
for i := 0; i < count; i++ {
|
||||
// For the stack trie, we need to do inserts in proper order
|
||||
|
|
@ -872,11 +873,13 @@ func TestCommitSequenceStackTrie(t *testing.T) {
|
|||
// not fit into 32 bytes, rlp-encoded. However, it's still the correct thing to do.
|
||||
func TestCommitSequenceSmallRoot(t *testing.T) {
|
||||
s := &spongeDb{sponge: sha3.NewLegacyKeccak256(), id: "a"}
|
||||
db := NewDatabase(s)
|
||||
db := NewDatabase(rawdb.NewDatabase(s))
|
||||
trie := NewEmpty(db)
|
||||
// Another sponge is used for the stacktrie commits
|
||||
stackTrieSponge := &spongeDb{sponge: sha3.NewLegacyKeccak256(), id: "b"}
|
||||
stTrie := NewStackTrie(stackTrieSponge)
|
||||
stTrie := NewStackTrie(func(owner common.Hash, path []byte, hash common.Hash, blob []byte) {
|
||||
db.Scheme().WriteTrieNode(stackTrieSponge, owner, path, hash, blob)
|
||||
})
|
||||
// Add a single small-element to the trie(s)
|
||||
key := make([]byte, 5)
|
||||
key[0] = 1
|
||||
|
|
|
|||
Loading…
Reference in a new issue