mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-18 10:50:44 +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"
|
xdc_genesis "github.com/XinFinOrg/XDPoSChain/genesis"
|
||||||
"github.com/XinFinOrg/XDPoSChain/log"
|
"github.com/XinFinOrg/XDPoSChain/log"
|
||||||
"github.com/XinFinOrg/XDPoSChain/node"
|
"github.com/XinFinOrg/XDPoSChain/node"
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/trie"
|
||||||
"github.com/urfave/cli/v2"
|
"github.com/urfave/cli/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -48,7 +49,10 @@ var (
|
||||||
Name: "init",
|
Name: "init",
|
||||||
Usage: "Bootstrap and initialize a new genesis block",
|
Usage: "Bootstrap and initialize a new genesis block",
|
||||||
ArgsUsage: "<genesisPath>",
|
ArgsUsage: "<genesisPath>",
|
||||||
Flags: slices.Concat(utils.NetworkFlags, utils.DatabaseFlags),
|
Flags: slices.Concat(
|
||||||
|
[]cli.Flag{utils.CachePreimagesFlag},
|
||||||
|
utils.NetworkFlags,
|
||||||
|
utils.DatabaseFlags),
|
||||||
Description: `
|
Description: `
|
||||||
The init command initializes a new genesis block and definition for the network.
|
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
|
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 {
|
if err != nil {
|
||||||
return err
|
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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -168,9 +168,11 @@ type BlockChain struct {
|
||||||
cacheConfig *CacheConfig // Cache configuration for pruning
|
cacheConfig *CacheConfig // Cache configuration for pruning
|
||||||
|
|
||||||
db ethdb.Database // Low level persistent database to store final content in
|
db ethdb.Database // Low level persistent database to store final content in
|
||||||
XDCxDb ethdb.XDCxDatabase
|
XDCxDb ethdb.XDCxDatabase // XDCx database
|
||||||
triegc *prque.Prque[int64, common.Hash] // Priority queue mapping block numbers to tries to gc
|
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
|
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
|
hc *HeaderChain
|
||||||
rmLogsFeed event.Feed
|
rmLogsFeed event.Feed
|
||||||
|
|
@ -188,8 +190,6 @@ type BlockChain struct {
|
||||||
currentBlock atomic.Value // Current head of the block chain
|
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!)
|
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
|
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
|
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
|
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{
|
bc := &BlockChain{
|
||||||
chainConfig: chainConfig,
|
chainConfig: chainConfig,
|
||||||
cacheConfig: cacheConfig,
|
cacheConfig: cacheConfig,
|
||||||
db: db,
|
db: db,
|
||||||
|
triedb: triedb,
|
||||||
triegc: prque.New[int64, common.Hash](nil),
|
triegc: prque.New[int64, common.Hash](nil),
|
||||||
stateCache: state.NewDatabaseWithConfig(db, &trie.Config{
|
stateCache: state.NewDatabaseWithConfig(db, &trie.Config{
|
||||||
Cache: cacheConfig.TrieCleanLimit,
|
Cache: cacheConfig.TrieCleanLimit,
|
||||||
|
|
@ -268,6 +274,7 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, chainConfig *par
|
||||||
rejectedLendingItem: lru.NewCache[common.Hash, interface{}](tradingstate.OrderCacheLimit),
|
rejectedLendingItem: lru.NewCache[common.Hash, interface{}](tradingstate.OrderCacheLimit),
|
||||||
finalizedTrade: 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.validator = NewBlockValidator(chainConfig, bc, engine)
|
||||||
bc.prefetcher = newStatePrefetcher(chainConfig, bc, engine)
|
bc.prefetcher = newStatePrefetcher(chainConfig, bc, engine)
|
||||||
bc.processor = NewStateProcessor(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
|
// Make sure the state associated with the block is available
|
||||||
repair := false
|
repair := false
|
||||||
_, err := state.New(currentBlock.Root(), bc.stateCache)
|
if !bc.HasState(currentBlock.Root()) {
|
||||||
if err != nil {
|
|
||||||
repair = true
|
repair = true
|
||||||
} else {
|
} else {
|
||||||
engine, ok := bc.Engine().(*XDPoS.XDPoS)
|
engine, ok := bc.Engine().(*XDPoS.XDPoS)
|
||||||
|
|
@ -487,7 +493,7 @@ func (bc *BlockChain) setHeadBeyondRoot(head uint64) error {
|
||||||
if newHeadBlock == nil {
|
if newHeadBlock == nil {
|
||||||
newHeadBlock = bc.genesisBlock
|
newHeadBlock = bc.genesisBlock
|
||||||
} else {
|
} 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
|
// Rewound state missing, rolled back to before pivot, reset to genesis
|
||||||
newHeadBlock = bc.genesisBlock
|
newHeadBlock = bc.genesisBlock
|
||||||
}
|
}
|
||||||
|
|
@ -712,7 +718,7 @@ func (bc *BlockChain) repair(head **types.Block) error {
|
||||||
for {
|
for {
|
||||||
// Abort if we've rewound to a head block that does have associated state
|
// Abort if we've rewound to a head block that does have associated state
|
||||||
if (common.RollbackNumber == 0) || ((*head).Number().Uint64() < common.RollbackNumber) {
|
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())
|
log.Info("Rewound blockchain to past state", "number", (*head).Number(), "hash", (*head).Hash())
|
||||||
engine, ok := bc.Engine().(*XDPoS.XDPoS)
|
engine, ok := bc.Engine().(*XDPoS.XDPoS)
|
||||||
if ok {
|
if ok {
|
||||||
|
|
@ -1082,10 +1088,10 @@ func (bc *BlockChain) saveData() {
|
||||||
if !bc.cacheConfig.TrieDirtyDisabled {
|
if !bc.cacheConfig.TrieDirtyDisabled {
|
||||||
var tradingTriedb *trie.Database
|
var tradingTriedb *trie.Database
|
||||||
var lendingTriedb *trie.Database
|
var lendingTriedb *trie.Database
|
||||||
engine, _ := bc.Engine().(*XDPoS.XDPoS)
|
|
||||||
triedb := bc.stateCache.TrieDB()
|
|
||||||
var tradingService utils.TradingService
|
var tradingService utils.TradingService
|
||||||
var lendingService utils.LendingService
|
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 {
|
if bc.Config().IsTIPXDCX(bc.CurrentBlock().Number()) && bc.chainConfig.XDPoS != nil && bc.CurrentBlock().NumberU64() > bc.chainConfig.XDPoS.Epoch && engine != nil {
|
||||||
tradingService = engine.GetXDCXService()
|
tradingService = engine.GetXDCXService()
|
||||||
if tradingService != nil && tradingService.GetStateCache() != nil {
|
if tradingService != nil && tradingService.GetStateCache() != nil {
|
||||||
|
|
@ -1439,7 +1445,6 @@ func (bc *BlockChain) writeBlockWithState(block *types.Block, receipts []*types.
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return NonStatTy, err
|
return NonStatTy, err
|
||||||
}
|
}
|
||||||
triedb := bc.stateCache.TrieDB()
|
|
||||||
|
|
||||||
tradingRoot := common.Hash{}
|
tradingRoot := common.Hash{}
|
||||||
if tradingState != nil {
|
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 we're running an archive node, always flush
|
||||||
if bc.cacheConfig.TrieDirtyDisabled {
|
if bc.cacheConfig.TrieDirtyDisabled {
|
||||||
if err := triedb.Commit(root, false); err != nil {
|
if err := bc.triedb.Commit(root, false); err != nil {
|
||||||
return NonStatTy, err
|
return NonStatTy, err
|
||||||
}
|
}
|
||||||
if tradingTrieDb != nil {
|
if tradingTrieDb != nil {
|
||||||
|
|
@ -1489,7 +1494,7 @@ func (bc *BlockChain) writeBlockWithState(block *types.Block, receipts []*types.
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Full but not archive node, do proper garbage collection
|
// 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()))
|
bc.triegc.Push(root, -int64(block.NumberU64()))
|
||||||
if tradingTrieDb != nil {
|
if tradingTrieDb != nil {
|
||||||
tradingTrieDb.Reference(tradingRoot, common.Hash{})
|
tradingTrieDb.Reference(tradingRoot, common.Hash{})
|
||||||
|
|
@ -1516,11 +1521,11 @@ func (bc *BlockChain) writeBlockWithState(block *types.Block, receipts []*types.
|
||||||
// size = size + lendingTrieDb.Size()
|
// size = size + lendingTrieDb.Size()
|
||||||
//}
|
//}
|
||||||
var (
|
var (
|
||||||
nodes, imgs = triedb.Size()
|
nodes, imgs = bc.triedb.Size()
|
||||||
limit = common.StorageSize(bc.cacheConfig.TrieDirtyLimit) * 1024 * 1024
|
limit = common.StorageSize(bc.cacheConfig.TrieDirtyLimit) * 1024 * 1024
|
||||||
)
|
)
|
||||||
if nodes > limit || imgs > 4*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 bc.gcproc > bc.cacheConfig.TrieTimeLimit || chosen > lastWrite+triesInMemory {
|
||||||
// If the header is missing (canonical chain behind), we're reorging a low
|
// 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)
|
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
|
// Flush an entire trie and restart the counters
|
||||||
triedb.Commit(header.Root, true)
|
bc.triedb.Commit(header.Root, true)
|
||||||
lastWrite = chosen
|
lastWrite = chosen
|
||||||
bc.gcproc = 0
|
bc.gcproc = 0
|
||||||
if tradingTrieDb != nil && lendingTrieDb != nil {
|
if tradingTrieDb != nil && lendingTrieDb != nil {
|
||||||
|
|
@ -1555,7 +1560,7 @@ func (bc *BlockChain) writeBlockWithState(block *types.Block, receipts []*types.
|
||||||
bc.triegc.Push(root, number)
|
bc.triegc.Push(root, number)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
triedb.Dereference(root)
|
bc.triedb.Dereference(root)
|
||||||
}
|
}
|
||||||
if tradingService != nil {
|
if tradingService != nil {
|
||||||
for !tradingService.GetTriegc().Empty() {
|
for !tradingService.GetTriegc().Empty() {
|
||||||
|
|
@ -1831,7 +1836,7 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals bool) (int, []
|
||||||
bc.UpdateBlocksHashCache(block)
|
bc.UpdateBlocksHashCache(block)
|
||||||
}
|
}
|
||||||
|
|
||||||
dirty, _ := bc.stateCache.TrieDB().Size()
|
dirty, _ := bc.triedb.Size()
|
||||||
stats.report(chain, it.index, dirty)
|
stats.report(chain, it.index, dirty)
|
||||||
if bc.chainConfig.XDPoS != nil {
|
if bc.chainConfig.XDPoS != nil {
|
||||||
engine, _ := bc.Engine().(*XDPoS.XDPoS)
|
engine, _ := bc.Engine().(*XDPoS.XDPoS)
|
||||||
|
|
@ -2287,7 +2292,7 @@ func (bc *BlockChain) insertBlock(block *types.Block) ([]interface{}, []*types.L
|
||||||
}
|
}
|
||||||
stats.processed++
|
stats.processed++
|
||||||
stats.usedGas += result.usedGas
|
stats.usedGas += result.usedGas
|
||||||
dirty, _ := bc.stateCache.TrieDB().Size()
|
dirty, _ := bc.triedb.Size()
|
||||||
stats.report(types.Blocks{block}, 0, dirty)
|
stats.report(types.Blocks{block}, 0, dirty)
|
||||||
if bc.chainConfig.XDPoS != nil {
|
if bc.chainConfig.XDPoS != nil {
|
||||||
// epoch block
|
// 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.
|
// large memory cache.
|
||||||
func NewDatabaseWithConfig(db ethdb.Database, config *trie.Config) Database {
|
func NewDatabaseWithConfig(db ethdb.Database, config *trie.Config) Database {
|
||||||
return &cachingDB{
|
return &cachingDB{
|
||||||
db: trie.NewDatabaseWithConfig(db, config),
|
|
||||||
disk: db,
|
disk: db,
|
||||||
codeCache: lru.NewSizeConstrainedCache[common.Hash, []byte](codeCacheSize),
|
|
||||||
codeSizeCache: lru.NewCache[common.Hash, int](codeSizeCacheSize),
|
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 {
|
type cachingDB struct {
|
||||||
db *trie.Database
|
|
||||||
disk ethdb.KeyValueStore
|
disk ethdb.KeyValueStore
|
||||||
codeCache *lru.SizeConstrainedCache[common.Hash, []byte]
|
|
||||||
codeSizeCache *lru.Cache[common.Hash, int]
|
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.
|
// OpenTrie opens the main account trie at a specific root hash.
|
||||||
func (db *cachingDB) OpenTrie(root common.Hash) (Trie, error) {
|
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 {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -155,7 +165,7 @@ func (db *cachingDB) OpenTrie(root common.Hash) (Trie, error) {
|
||||||
|
|
||||||
// OpenStorageTrie opens the storage trie of an account.
|
// OpenStorageTrie opens the storage trie of an account.
|
||||||
func (db *cachingDB) OpenStorageTrie(stateRoot common.Hash, addrHash, root common.Hash) (Trie, error) {
|
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 {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -218,5 +228,5 @@ func (db *cachingDB) DiskDB() ethdb.KeyValueStore {
|
||||||
|
|
||||||
// TrieDB retrieves any intermediate trie-node caching layer.
|
// TrieDB retrieves any intermediate trie-node caching layer.
|
||||||
func (db *cachingDB) TrieDB() *trie.Database {
|
func (db *cachingDB) TrieDB() *trie.Database {
|
||||||
return db.db
|
return db.triedb
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -21,15 +21,15 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/XinFinOrg/XDPoSChain/common"
|
"github.com/XinFinOrg/XDPoSChain/common"
|
||||||
"github.com/XinFinOrg/XDPoSChain/ethdb"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Tests that the node iterator indeed walks over the entire database contents.
|
// Tests that the node iterator indeed walks over the entire database contents.
|
||||||
func TestNodeIteratorCoverage(t *testing.T) {
|
func TestNodeIteratorCoverage(t *testing.T) {
|
||||||
// Create some arbitrary test state to iterate
|
// 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 {
|
if err != nil {
|
||||||
t.Fatalf("failed to create state trie at %x: %v", root, err)
|
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
|
// Cross check the iterated hashes and the database/nodepool content
|
||||||
for hash := range hashes {
|
for hash := range hashes {
|
||||||
if _, err = db.TrieDB().Node(hash); err != nil {
|
if _, err = sdb.TrieDB().Node(hash); err != nil {
|
||||||
_, err = db.ContractCode(common.Hash{}, hash)
|
_, err = sdb.ContractCode(common.Hash{}, hash)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("failed to retrieve reported node %x", hash)
|
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 {
|
if _, ok := hashes[hash]; !ok {
|
||||||
t.Errorf("state entry not reported %x", hash)
|
t.Errorf("state entry not reported %x", hash)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
it := db.DiskDB().(ethdb.Database).NewIterator(nil, nil)
|
it := db.NewIterator(nil, nil)
|
||||||
for it.Next() {
|
for it.Next() {
|
||||||
key := it.Key()
|
key := it.Key()
|
||||||
if bytes.HasPrefix(key, []byte("secure-key-")) {
|
if bytes.HasPrefix(key, []byte("secure-key-")) {
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// NewStateSync create a new state trie download scheduler.
|
// 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.
|
// 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
|
var onSlot func(keys [][]byte, path []byte, leaf []byte, parent common.Hash, parentPath []byte) error
|
||||||
if onLeaf != nil {
|
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)
|
syncer.AddCodeEntry(common.BytesToHash(obj.CodeHash), path, parent, parentPath)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
syncer = trie.NewSync(root, database, onAccount)
|
syncer = trie.NewSync(root, database, onAccount, scheme)
|
||||||
return syncer
|
return syncer
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -40,10 +40,11 @@ type testAccount struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// makeTestState create a sample test state to test node-wise reconstruction.
|
// 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
|
// Create an empty state
|
||||||
db := NewDatabase(rawdb.NewMemoryDatabase())
|
db := rawdb.NewMemoryDatabase()
|
||||||
state, _ := New(types.EmptyRootHash, db)
|
sdb := NewDatabase(db)
|
||||||
|
state, _ := New(types.EmptyRootHash, sdb)
|
||||||
|
|
||||||
// Fill it with some arbitrary data
|
// Fill it with some arbitrary data
|
||||||
var accounts []*testAccount
|
var accounts []*testAccount
|
||||||
|
|
@ -64,7 +65,7 @@ func makeTestState() (Database, common.Hash, []*testAccount) {
|
||||||
if i%5 == 0 {
|
if i%5 == 0 {
|
||||||
for j := byte(0); j < 5; j++ {
|
for j := byte(0); j < 5; j++ {
|
||||||
hash := crypto.Keccak256Hash([]byte{i, i, i, i, i, j, 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)
|
state.updateStateObject(obj)
|
||||||
|
|
@ -73,7 +74,7 @@ func makeTestState() (Database, common.Hash, []*testAccount) {
|
||||||
root, _ := state.Commit(false)
|
root, _ := state.Commit(false)
|
||||||
|
|
||||||
// Return the generated state
|
// Return the generated state
|
||||||
return db, root, accounts
|
return db, sdb, root, accounts
|
||||||
}
|
}
|
||||||
|
|
||||||
// checkStateAccounts cross references a reconstructed state with an expected
|
// 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.
|
// 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 {
|
if v, _ := db.Get(root[:]); v == nil {
|
||||||
return nil // Consider a non existent state consistent.
|
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.
|
// Tests that an empty state is not scheduled for syncing.
|
||||||
func TestEmptyStateSync(t *testing.T) {
|
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 {
|
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)
|
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) {
|
func testIterativeStateSync(t *testing.T, count int, commit bool, bypath bool) {
|
||||||
// Create a random state to copy
|
// Create a random state to copy
|
||||||
srcDb, srcRoot, srcAccounts := makeTestState()
|
_, srcDb, srcRoot, srcAccounts := makeTestState()
|
||||||
if commit {
|
if commit {
|
||||||
srcDb.TrieDB().Commit(srcRoot, false)
|
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
|
// Create a destination state and sync with the scheduler
|
||||||
dstDb := rawdb.NewMemoryDatabase()
|
dstDb := rawdb.NewMemoryDatabase()
|
||||||
sched := NewStateSync(srcRoot, dstDb, nil)
|
sched := NewStateSync(srcRoot, dstDb, nil, srcDb.TrieDB().Scheme())
|
||||||
|
|
||||||
var (
|
var (
|
||||||
nodeElements []stateElement
|
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.
|
// partial results are returned, and the others sent only later.
|
||||||
func TestIterativeDelayedStateSync(t *testing.T) {
|
func TestIterativeDelayedStateSync(t *testing.T) {
|
||||||
// Create a random state to copy
|
// Create a random state to copy
|
||||||
srcDb, srcRoot, srcAccounts := makeTestState()
|
_, srcDb, srcRoot, srcAccounts := makeTestState()
|
||||||
|
|
||||||
// Create a destination state and sync with the scheduler
|
// Create a destination state and sync with the scheduler
|
||||||
dstDb := rawdb.NewMemoryDatabase()
|
dstDb := rawdb.NewMemoryDatabase()
|
||||||
sched := NewStateSync(srcRoot, dstDb, nil)
|
sched := NewStateSync(srcRoot, dstDb, nil, srcDb.TrieDB().Scheme())
|
||||||
|
|
||||||
var (
|
var (
|
||||||
nodeElements []stateElement
|
nodeElements []stateElement
|
||||||
|
|
@ -374,11 +377,11 @@ func TestIterativeRandomStateSyncBatched(t *testing.T) { testIterativeRandomS
|
||||||
|
|
||||||
func testIterativeRandomStateSync(t *testing.T, count int) {
|
func testIterativeRandomStateSync(t *testing.T, count int) {
|
||||||
// Create a random state to copy
|
// Create a random state to copy
|
||||||
srcDb, srcRoot, srcAccounts := makeTestState()
|
_, srcDb, srcRoot, srcAccounts := makeTestState()
|
||||||
|
|
||||||
// Create a destination state and sync with the scheduler
|
// Create a destination state and sync with the scheduler
|
||||||
dstDb := rawdb.NewMemoryDatabase()
|
dstDb := rawdb.NewMemoryDatabase()
|
||||||
sched := NewStateSync(srcRoot, dstDb, nil)
|
sched := NewStateSync(srcRoot, dstDb, nil, srcDb.TrieDB().Scheme())
|
||||||
|
|
||||||
nodeQueue := make(map[string]stateElement)
|
nodeQueue := make(map[string]stateElement)
|
||||||
codeQueue := make(map[common.Hash]struct{})
|
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.
|
// partial results are returned (Even those randomly), others sent only later.
|
||||||
func TestIterativeRandomDelayedStateSync(t *testing.T) {
|
func TestIterativeRandomDelayedStateSync(t *testing.T) {
|
||||||
// Create a random state to copy
|
// Create a random state to copy
|
||||||
srcDb, srcRoot, srcAccounts := makeTestState()
|
_, srcDb, srcRoot, srcAccounts := makeTestState()
|
||||||
|
|
||||||
// Create a destination state and sync with the scheduler
|
// Create a destination state and sync with the scheduler
|
||||||
dstDb := rawdb.NewMemoryDatabase()
|
dstDb := rawdb.NewMemoryDatabase()
|
||||||
sched := NewStateSync(srcRoot, dstDb, nil)
|
sched := NewStateSync(srcRoot, dstDb, nil, srcDb.TrieDB().Scheme())
|
||||||
|
|
||||||
nodeQueue := make(map[string]stateElement)
|
nodeQueue := make(map[string]stateElement)
|
||||||
codeQueue := make(map[common.Hash]struct{})
|
codeQueue := make(map[common.Hash]struct{})
|
||||||
|
|
@ -544,7 +547,7 @@ func TestIterativeRandomDelayedStateSync(t *testing.T) {
|
||||||
// the database.
|
// the database.
|
||||||
func TestIncompleteStateSync(t *testing.T) {
|
func TestIncompleteStateSync(t *testing.T) {
|
||||||
// Create a random state to copy
|
// Create a random state to copy
|
||||||
srcDb, srcRoot, srcAccounts := makeTestState()
|
db, srcDb, srcRoot, srcAccounts := makeTestState()
|
||||||
|
|
||||||
/// isCodeLookup to save some hashing
|
/// isCodeLookup to save some hashing
|
||||||
var isCode = make(map[common.Hash]struct{})
|
var isCode = make(map[common.Hash]struct{})
|
||||||
|
|
@ -554,15 +557,16 @@ func TestIncompleteStateSync(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
isCode[types.EmptyCodeHash] = struct{}{}
|
isCode[types.EmptyCodeHash] = struct{}{}
|
||||||
checkTrieConsistency(srcDb.DiskDB(), srcRoot)
|
checkTrieConsistency(db, srcRoot)
|
||||||
|
|
||||||
// Create a destination state and sync with the scheduler
|
// Create a destination state and sync with the scheduler
|
||||||
dstDb := rawdb.NewMemoryDatabase()
|
dstDb := rawdb.NewMemoryDatabase()
|
||||||
sched := NewStateSync(srcRoot, dstDb, nil)
|
sched := NewStateSync(srcRoot, dstDb, nil, srcDb.TrieDB().Scheme())
|
||||||
|
|
||||||
var (
|
var (
|
||||||
addedCodes []common.Hash
|
addedCodes []common.Hash
|
||||||
addedNodes []common.Hash
|
addedPaths []string
|
||||||
|
addedHashes []common.Hash
|
||||||
)
|
)
|
||||||
nodeQueue := make(map[string]stateElement)
|
nodeQueue := make(map[string]stateElement)
|
||||||
codeQueue := make(map[common.Hash]struct{})
|
codeQueue := make(map[common.Hash]struct{})
|
||||||
|
|
@ -599,15 +603,16 @@ func TestIncompleteStateSync(t *testing.T) {
|
||||||
var nodehashes []common.Hash
|
var nodehashes []common.Hash
|
||||||
if len(nodeQueue) > 0 {
|
if len(nodeQueue) > 0 {
|
||||||
results := make([]trie.NodeSyncResult, 0, len(nodeQueue))
|
results := make([]trie.NodeSyncResult, 0, len(nodeQueue))
|
||||||
for key, element := range nodeQueue {
|
for path, element := range nodeQueue {
|
||||||
data, err := srcDb.TrieDB().Node(element.hash)
|
data, err := srcDb.TrieDB().Node(element.hash)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to retrieve node data for %x", element.hash)
|
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 {
|
if element.hash != srcRoot {
|
||||||
addedNodes = append(addedNodes, element.hash)
|
addedPaths = append(addedPaths, element.path)
|
||||||
|
addedHashes = append(addedHashes, element.hash)
|
||||||
}
|
}
|
||||||
nodehashes = append(nodehashes, element.hash)
|
nodehashes = append(nodehashes, element.hash)
|
||||||
}
|
}
|
||||||
|
|
@ -655,12 +660,18 @@ func TestIncompleteStateSync(t *testing.T) {
|
||||||
}
|
}
|
||||||
rawdb.WriteCode(dstDb, node, val)
|
rawdb.WriteCode(dstDb, node, val)
|
||||||
}
|
}
|
||||||
for _, node := range addedNodes {
|
scheme := srcDb.TrieDB().Scheme()
|
||||||
val := rawdb.ReadTrieNode(dstDb, node)
|
for i, path := range addedPaths {
|
||||||
rawdb.DeleteTrieNode(dstDb, node)
|
owner, inner := trie.ResolvePath([]byte(path))
|
||||||
|
hash := addedHashes[i]
|
||||||
|
val := scheme.ReadTrieNode(dstDb, owner, inner, hash)
|
||||||
|
if val == nil {
|
||||||
|
t.Error("missing trie node")
|
||||||
|
}
|
||||||
|
scheme.DeleteTrieNode(dstDb, owner, inner, hash)
|
||||||
if err := checkStateConsistency(dstDb, srcRoot); err == nil {
|
if err := checkStateConsistency(dstDb, srcRoot); err == nil {
|
||||||
t.Errorf("trie inconsistency not caught, missing: %v", node.Hex())
|
t.Errorf("trie inconsistency not caught, missing: %v", path)
|
||||||
}
|
}
|
||||||
rawdb.WriteTrieNode(dstDb, node, val)
|
scheme.WriteTrieNode(dstDb, owner, inner, hash, val)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -34,6 +34,7 @@ import (
|
||||||
"github.com/XinFinOrg/XDPoSChain/log"
|
"github.com/XinFinOrg/XDPoSChain/log"
|
||||||
"github.com/XinFinOrg/XDPoSChain/metrics"
|
"github.com/XinFinOrg/XDPoSChain/metrics"
|
||||||
"github.com/XinFinOrg/XDPoSChain/params"
|
"github.com/XinFinOrg/XDPoSChain/params"
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/trie"
|
||||||
)
|
)
|
||||||
|
|
||||||
// proposeBlockHandlerFn is a callback type to handle a block by the consensus
|
// 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 inserts a batch of receipts into the local chain.
|
||||||
InsertReceiptChain(types.Blocks, []types.Receipts) (int, error)
|
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.
|
// 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.
|
// downloadTester is a test simulator for mocking out local block chain.
|
||||||
|
// TODO(daniel): remove field triedb
|
||||||
type downloadTester struct {
|
type downloadTester struct {
|
||||||
downloader *Downloader
|
downloader *Downloader
|
||||||
|
triedb *trie.Database
|
||||||
|
|
||||||
genesis *types.Block // Genesis blocks used by the tester and peers
|
genesis *types.Block // Genesis blocks used by the tester and peers
|
||||||
stateDb ethdb.Database // Database used by the tester for syncing from 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()},
|
ownChainTd: map[common.Hash]*big.Int{testGenesis.Hash(): testGenesis.Difficulty()},
|
||||||
}
|
}
|
||||||
tester.stateDb = rawdb.NewMemoryDatabase()
|
tester.stateDb = rawdb.NewMemoryDatabase()
|
||||||
|
tester.triedb = trie.NewDatabase(tester.stateDb)
|
||||||
tester.stateDb.Put(testGenesis.Root().Bytes(), []byte{0x00})
|
tester.stateDb.Put(testGenesis.Root().Bytes(), []byte{0x00})
|
||||||
tester.downloader = New(tester.stateDb, new(event.TypeMux), tester, nil, tester.dropPeer, tester.handleProposedBlock)
|
tester.downloader = New(tester.stateDb, new(event.TypeMux), tester, nil, tester.dropPeer, tester.handleProposedBlock)
|
||||||
return tester
|
return tester
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (db *downloadTester) TrieDB() *trie.Database {
|
||||||
|
return db.triedb
|
||||||
|
}
|
||||||
|
|
||||||
// terminate aborts any operations on the embedded downloader and releases all
|
// terminate aborts any operations on the embedded downloader and releases all
|
||||||
// held resources.
|
// held resources.
|
||||||
func (dl *downloadTester) terminate() {
|
func (dl *downloadTester) terminate() {
|
||||||
|
|
|
||||||
|
|
@ -291,18 +291,19 @@ type codeTask struct {
|
||||||
// newStateSync creates a new state trie download scheduler. This method does not
|
// newStateSync creates a new state trie download scheduler. This method does not
|
||||||
// yet start the sync. The user needs to call run to initiate.
|
// yet start the sync. The user needs to call run to initiate.
|
||||||
// only use fast sync but XDC only run full sync
|
// only use fast sync but XDC only run full sync
|
||||||
|
// TODO(daniel): remove field sched
|
||||||
func newStateSync(d *Downloader, root common.Hash) *stateSync {
|
func newStateSync(d *Downloader, root common.Hash) *stateSync {
|
||||||
return &stateSync{
|
return &stateSync{
|
||||||
d: d,
|
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(),
|
keccak: sha3.NewLegacyKeccak256(),
|
||||||
trieTasks: make(map[string]*trieTask),
|
trieTasks: make(map[string]*trieTask),
|
||||||
codeTasks: make(map[common.Hash]*codeTask),
|
codeTasks: make(map[common.Hash]*codeTask),
|
||||||
deliver: make(chan *stateReq),
|
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
|
// import pre accounts & construct test genesis block & state root
|
||||||
db := rawdb.NewMemoryDatabase()
|
db := rawdb.NewMemoryDatabase()
|
||||||
gblock, err := t.genesis(config).Commit(db)
|
gspec := t.genesis(config)
|
||||||
if err != nil {
|
gblock := gspec.MustCommit(db)
|
||||||
return err
|
|
||||||
}
|
|
||||||
if gblock.Hash() != t.json.Genesis.Hash {
|
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)
|
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
|
// behind this split design is to provide read access to RPC handlers and sync
|
||||||
// servers even while the trie is executing expensive garbage collection.
|
// servers even while the trie is executing expensive garbage collection.
|
||||||
type Database struct {
|
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
|
cleans *fastcache.Cache // GC friendly memory Cache of clean Node RLPs
|
||||||
dirties map[common.Hash]*cachedNode // Data and references relationships of dirty trie nodes
|
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
|
// 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
|
// its written out to disk or garbage collected. No read Cache is created, so all
|
||||||
// data retrievals will hit the underlying disk database.
|
// data retrievals will hit the underlying disk database.
|
||||||
func NewDatabase(diskdb ethdb.KeyValueStore) *Database {
|
func NewDatabase(diskdb ethdb.Database) *Database {
|
||||||
return NewDatabaseWithConfig(diskdb, nil)
|
return NewDatabaseWithConfig(diskdb, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewDatabaseWithConfig creates a new trie database to store ephemeral trie content
|
// 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
|
// before its written out to disk or garbage collected. It also acts as a read cache
|
||||||
// for nodes loaded from disk.
|
// 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
|
var cleans *fastcache.Cache
|
||||||
if config != nil && config.Cache > 0 {
|
if config != nil && config.Cache > 0 {
|
||||||
cleans = fastcache.New(config.Cache * 1024 * 1024)
|
cleans = fastcache.New(config.Cache * 1024 * 1024)
|
||||||
|
|
@ -891,3 +891,8 @@ func (db *Database) CommitPreimages() error {
|
||||||
}
|
}
|
||||||
return db.preimages.commit(true)
|
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"
|
"testing"
|
||||||
|
|
||||||
"github.com/XinFinOrg/XDPoSChain/common"
|
"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
|
// Tests that the trie database returns a missing trie Node error if attempting
|
||||||
// to retrieve the meta root.
|
// to retrieve the meta root.
|
||||||
func TestDatabaseMetarootFetch(t *testing.T) {
|
func TestDatabaseMetarootFetch(t *testing.T) {
|
||||||
db := NewDatabase(memorydb.New())
|
db := NewDatabase(rawdb.NewMemoryDatabase())
|
||||||
if _, err := db.Node(common.Hash{}); err == nil {
|
if _, err := db.Node(common.Hash{}); err == nil {
|
||||||
t.Fatalf("metaroot retrieval succeeded")
|
t.Fatalf("metaroot retrieval succeeded")
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -327,7 +327,7 @@ func TestIteratorContinueAfterErrorDisk(t *testing.T) { testIteratorContinueA
|
||||||
func TestIteratorContinueAfterErrorMemonly(t *testing.T) { testIteratorContinueAfterError(t, true) }
|
func TestIteratorContinueAfterErrorMemonly(t *testing.T) { testIteratorContinueAfterError(t, true) }
|
||||||
|
|
||||||
func testIteratorContinueAfterError(t *testing.T, memonly bool) {
|
func testIteratorContinueAfterError(t *testing.T, memonly bool) {
|
||||||
diskdb := memorydb.New()
|
diskdb := rawdb.NewMemoryDatabase()
|
||||||
triedb := NewDatabase(diskdb)
|
triedb := NewDatabase(diskdb)
|
||||||
|
|
||||||
tr := NewEmpty(triedb)
|
tr := NewEmpty(triedb)
|
||||||
|
|
@ -418,8 +418,8 @@ func TestIteratorContinueAfterSeekErrorMemonly(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func testIteratorContinueAfterSeekError(t *testing.T, memonly bool) {
|
func testIteratorContinueAfterSeekError(t *testing.T, memonly bool) {
|
||||||
// Commit test trie to Db, then remove the Node containing "bars".
|
// Commit test trie to db, then remove the node containing "bars".
|
||||||
diskdb := memorydb.New()
|
diskdb := rawdb.NewMemoryDatabase()
|
||||||
triedb := NewDatabase(diskdb)
|
triedb := NewDatabase(diskdb)
|
||||||
|
|
||||||
ctr := NewEmpty(triedb)
|
ctr := NewEmpty(triedb)
|
||||||
|
|
@ -528,7 +528,7 @@ func (l *loggingDb) Close() error {
|
||||||
func makeLargeTestTrie() (*Database, *StateTrie, *loggingDb) {
|
func makeLargeTestTrie() (*Database, *StateTrie, *loggingDb) {
|
||||||
// Create an empty trie
|
// Create an empty trie
|
||||||
logDb := &loggingDb{0, memorydb.New()}
|
logDb := &loggingDb{0, memorydb.New()}
|
||||||
triedb := NewDatabase(logDb)
|
triedb := NewDatabase(rawdb.NewDatabase(logDb))
|
||||||
trie, _ := NewStateTrie(TrieID(common.Hash{}), triedb)
|
trie, _ := NewStateTrie(TrieID(common.Hash{}), triedb)
|
||||||
|
|
||||||
// Fill it with some arbitrary data
|
// Fill it with some arbitrary data
|
||||||
|
|
@ -563,7 +563,7 @@ func TestNodeIteratorLargeTrie(t *testing.T) {
|
||||||
|
|
||||||
func TestIteratorNodeBlob(t *testing.T) {
|
func TestIteratorNodeBlob(t *testing.T) {
|
||||||
var (
|
var (
|
||||||
db = memorydb.New()
|
db = rawdb.NewMemoryDatabase()
|
||||||
triedb = NewDatabase(db)
|
triedb = NewDatabase(db)
|
||||||
trie = NewEmpty(triedb)
|
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"
|
"testing"
|
||||||
|
|
||||||
"github.com/XinFinOrg/XDPoSChain/common"
|
"github.com/XinFinOrg/XDPoSChain/common"
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/core/rawdb"
|
||||||
"github.com/XinFinOrg/XDPoSChain/crypto"
|
"github.com/XinFinOrg/XDPoSChain/crypto"
|
||||||
"github.com/XinFinOrg/XDPoSChain/ethdb/memorydb"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func newEmptySecure() *StateTrie {
|
func newEmptySecure() *StateTrie {
|
||||||
trie, _ := NewStateTrie(TrieID(common.Hash{}), NewDatabase(memorydb.New()))
|
trie, _ := NewStateTrie(TrieID(common.Hash{}), NewDatabase(rawdb.NewMemoryDatabase()))
|
||||||
return trie
|
return trie
|
||||||
}
|
}
|
||||||
|
|
||||||
// makeTestStateTrie creates a large enough secure trie for testing.
|
// makeTestStateTrie creates a large enough secure trie for testing.
|
||||||
func makeTestStateTrie() (*Database, *StateTrie, map[string][]byte) {
|
func makeTestStateTrie() (*Database, *StateTrie, map[string][]byte) {
|
||||||
// Create an empty trie
|
// Create an empty trie
|
||||||
triedb := NewDatabase(memorydb.New())
|
triedb := NewDatabase(rawdb.NewMemoryDatabase())
|
||||||
trie, _ := NewStateTrie(TrieID(common.Hash{}), triedb)
|
trie, _ := NewStateTrie(TrieID(common.Hash{}), triedb)
|
||||||
|
|
||||||
// Fill it with some arbitrary data
|
// Fill it with some arbitrary data
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,6 @@ import (
|
||||||
|
|
||||||
"github.com/XinFinOrg/XDPoSChain/common"
|
"github.com/XinFinOrg/XDPoSChain/common"
|
||||||
"github.com/XinFinOrg/XDPoSChain/core/types"
|
"github.com/XinFinOrg/XDPoSChain/core/types"
|
||||||
"github.com/XinFinOrg/XDPoSChain/ethdb"
|
|
||||||
"github.com/XinFinOrg/XDPoSChain/log"
|
"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 := stPool.Get().(*StackTrie)
|
||||||
st.db = db
|
|
||||||
st.owner = owner
|
st.owner = owner
|
||||||
|
st.writeFn = writeFn
|
||||||
return st
|
return st
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -59,36 +62,36 @@ type StackTrie struct {
|
||||||
val []byte // value contained by this node if it's a leaf
|
val []byte // value contained by this node if it's a leaf
|
||||||
key []byte // key chunk covered by this (leaf|ext) node
|
key []byte // key chunk covered by this (leaf|ext) node
|
||||||
children [16]*StackTrie // list of children (for branch and exts)
|
children [16]*StackTrie // list of children (for branch and exts)
|
||||||
db ethdb.KeyValueWriter // Pointer to the commit db, can be nil
|
writeFn NodeWriteFunc // function for committing nodes, can be nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewStackTrie allocates and initializes an empty trie.
|
// NewStackTrie allocates and initializes an empty trie.
|
||||||
func NewStackTrie(db ethdb.KeyValueWriter) *StackTrie {
|
func NewStackTrie(writeFn NodeWriteFunc) *StackTrie {
|
||||||
return &StackTrie{
|
return &StackTrie{
|
||||||
nodeType: emptyNode,
|
nodeType: emptyNode,
|
||||||
db: db,
|
writeFn: writeFn,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewStackTrieWithOwner allocates and initializes an empty trie, but with
|
// NewStackTrieWithOwner allocates and initializes an empty trie, but with
|
||||||
// the additional owner field.
|
// the additional owner field.
|
||||||
func NewStackTrieWithOwner(db ethdb.KeyValueWriter, owner common.Hash) *StackTrie {
|
func NewStackTrieWithOwner(writeFn NodeWriteFunc, owner common.Hash) *StackTrie {
|
||||||
return &StackTrie{
|
return &StackTrie{
|
||||||
owner: owner,
|
owner: owner,
|
||||||
nodeType: emptyNode,
|
nodeType: emptyNode,
|
||||||
db: db,
|
writeFn: writeFn,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewFromBinary initialises a serialized stacktrie with the given db.
|
// 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
|
var st StackTrie
|
||||||
if err := st.UnmarshalBinary(data); err != nil {
|
if err := st.UnmarshalBinary(data); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
// If a database is used, we need to recursively add it to every child
|
// If a database is used, we need to recursively add it to every child
|
||||||
if db != nil {
|
if writeFn != nil {
|
||||||
st.setDb(db)
|
st.setWriter(writeFn)
|
||||||
}
|
}
|
||||||
return &st, nil
|
return &st, nil
|
||||||
}
|
}
|
||||||
|
|
@ -161,25 +164,25 @@ func (st *StackTrie) unmarshalBinary(r io.Reader) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (st *StackTrie) setDb(db ethdb.KeyValueWriter) {
|
func (st *StackTrie) setWriter(writeFn NodeWriteFunc) {
|
||||||
st.db = db
|
st.writeFn = writeFn
|
||||||
for _, child := range st.children {
|
for _, child := range st.children {
|
||||||
if child != nil {
|
if child != nil {
|
||||||
child.setDb(db)
|
child.setWriter(writeFn)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func newLeaf(owner common.Hash, key, val []byte, db ethdb.KeyValueWriter) *StackTrie {
|
func newLeaf(owner common.Hash, key, val []byte, writeFn NodeWriteFunc) *StackTrie {
|
||||||
st := stackTrieFromPool(db, owner)
|
st := stackTrieFromPool(writeFn, owner)
|
||||||
st.nodeType = leafNode
|
st.nodeType = leafNode
|
||||||
st.key = append(st.key, key...)
|
st.key = append(st.key, key...)
|
||||||
st.val = val
|
st.val = val
|
||||||
return st
|
return st
|
||||||
}
|
}
|
||||||
|
|
||||||
func newExt(owner common.Hash, key []byte, child *StackTrie, db ethdb.KeyValueWriter) *StackTrie {
|
func newExt(owner common.Hash, key []byte, child *StackTrie, writeFn NodeWriteFunc) *StackTrie {
|
||||||
st := stackTrieFromPool(db, owner)
|
st := stackTrieFromPool(writeFn, owner)
|
||||||
st.nodeType = extNode
|
st.nodeType = extNode
|
||||||
st.key = append(st.key, key...)
|
st.key = append(st.key, key...)
|
||||||
st.children[0] = child
|
st.children[0] = child
|
||||||
|
|
@ -201,7 +204,7 @@ func (st *StackTrie) TryUpdate(key, value []byte) error {
|
||||||
if len(value) == 0 {
|
if len(value) == 0 {
|
||||||
panic("deletion not supported")
|
panic("deletion not supported")
|
||||||
}
|
}
|
||||||
st.insert(k[:len(k)-1], value)
|
st.insert(k[:len(k)-1], value, nil)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -213,7 +216,7 @@ func (st *StackTrie) Update(key, value []byte) {
|
||||||
|
|
||||||
func (st *StackTrie) Reset() {
|
func (st *StackTrie) Reset() {
|
||||||
st.owner = common.Hash{}
|
st.owner = common.Hash{}
|
||||||
st.db = nil
|
st.writeFn = nil
|
||||||
st.key = st.key[:0]
|
st.key = st.key[:0]
|
||||||
st.val = nil
|
st.val = nil
|
||||||
for i := range st.children {
|
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
|
// Helper function to that inserts a (key, value) pair into
|
||||||
// the trie.
|
// the trie.
|
||||||
func (st *StackTrie) insert(key, value []byte) {
|
func (st *StackTrie) insert(key, value []byte, prefix []byte) {
|
||||||
switch st.nodeType {
|
switch st.nodeType {
|
||||||
case branchNode: /* Branch */
|
case branchNode: /* Branch */
|
||||||
idx := int(key[0])
|
idx := int(key[0])
|
||||||
|
|
@ -245,7 +248,7 @@ func (st *StackTrie) insert(key, value []byte) {
|
||||||
for i := idx - 1; i >= 0; i-- {
|
for i := idx - 1; i >= 0; i-- {
|
||||||
if st.children[i] != nil {
|
if st.children[i] != nil {
|
||||||
if st.children[i].nodeType != hashedNode {
|
if st.children[i].nodeType != hashedNode {
|
||||||
st.children[i].hash()
|
st.children[i].hash(append(prefix, byte(i)))
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
@ -253,9 +256,9 @@ func (st *StackTrie) insert(key, value []byte) {
|
||||||
|
|
||||||
// Add new child
|
// Add new child
|
||||||
if st.children[idx] == nil {
|
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 {
|
} else {
|
||||||
st.children[idx].insert(key[1:], value)
|
st.children[idx].insert(key[1:], value, append(prefix, key[0]))
|
||||||
}
|
}
|
||||||
|
|
||||||
case extNode: /* Ext */
|
case extNode: /* Ext */
|
||||||
|
|
@ -270,7 +273,7 @@ func (st *StackTrie) insert(key, value []byte) {
|
||||||
if diffidx == len(st.key) {
|
if diffidx == len(st.key) {
|
||||||
// Ext key and key segment are identical, recurse into
|
// Ext key and key segment are identical, recurse into
|
||||||
// the child node.
|
// the child node.
|
||||||
st.children[0].insert(key[diffidx:], value)
|
st.children[0].insert(key[diffidx:], value, append(prefix, key[:diffidx]...))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// Save the original part. Depending if the break is
|
// Save the original part. Depending if the break is
|
||||||
|
|
@ -279,14 +282,19 @@ func (st *StackTrie) insert(key, value []byte) {
|
||||||
// node directly.
|
// node directly.
|
||||||
var n *StackTrie
|
var n *StackTrie
|
||||||
if diffidx < len(st.key)-1 {
|
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 {
|
} else {
|
||||||
// Break on the last byte, no need to insert
|
// 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 = st.children[0]
|
||||||
|
n.hash(append(prefix, st.key...))
|
||||||
}
|
}
|
||||||
// Convert to hash
|
|
||||||
n.hash()
|
|
||||||
var p *StackTrie
|
var p *StackTrie
|
||||||
if diffidx == 0 {
|
if diffidx == 0 {
|
||||||
// the break is on the first byte, so
|
// 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
|
// the common prefix is at least one byte
|
||||||
// long, insert a new intermediate branch
|
// long, insert a new intermediate branch
|
||||||
// node.
|
// node.
|
||||||
st.children[0] = stackTrieFromPool(st.db, st.owner)
|
st.children[0] = stackTrieFromPool(st.writeFn, st.owner)
|
||||||
st.children[0].nodeType = branchNode
|
st.children[0].nodeType = branchNode
|
||||||
p = st.children[0]
|
p = st.children[0]
|
||||||
}
|
}
|
||||||
// Create a leaf for the inserted part
|
// 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:
|
// Insert both child leaves where they belong:
|
||||||
origIdx := st.key[diffidx]
|
origIdx := st.key[diffidx]
|
||||||
|
|
@ -340,7 +348,7 @@ func (st *StackTrie) insert(key, value []byte) {
|
||||||
// Convert current node into an ext,
|
// Convert current node into an ext,
|
||||||
// and insert a child branch node.
|
// and insert a child branch node.
|
||||||
st.nodeType = extNode
|
st.nodeType = extNode
|
||||||
st.children[0] = NewStackTrieWithOwner(st.db, st.owner)
|
st.children[0] = NewStackTrieWithOwner(st.writeFn, st.owner)
|
||||||
st.children[0].nodeType = branchNode
|
st.children[0].nodeType = branchNode
|
||||||
p = st.children[0]
|
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
|
// value and another containing the new value. The child leaf
|
||||||
// is hashed directly in order to free up some memory.
|
// is hashed directly in order to free up some memory.
|
||||||
origIdx := st.key[diffidx]
|
origIdx := st.key[diffidx]
|
||||||
p.children[origIdx] = newLeaf(st.owner, st.key[diffidx+1:], st.val, st.db)
|
p.children[origIdx] = newLeaf(st.owner, st.key[diffidx+1:], st.val, st.writeFn)
|
||||||
p.children[origIdx].hash()
|
p.children[origIdx].hash(append(prefix, st.key[:diffidx+1]...))
|
||||||
|
|
||||||
newIdx := key[diffidx]
|
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
|
// Finally, cut off the key part that has been passed
|
||||||
// over to the children.
|
// over to the children.
|
||||||
|
|
@ -384,14 +392,14 @@ func (st *StackTrie) insert(key, value []byte) {
|
||||||
// - And the 'st.type' will be 'hashedNode' AGAIN
|
// - And the 'st.type' will be 'hashedNode' AGAIN
|
||||||
//
|
//
|
||||||
// This method also sets 'st.type' to hashedNode, and clears 'st.key'.
|
// 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)
|
h := newHasher(false)
|
||||||
defer returnHasherToPool(h)
|
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.
|
// The switch below sets this to the RLP-encoding of this node.
|
||||||
var encodedNode []byte
|
var encodedNode []byte
|
||||||
|
|
||||||
|
|
@ -412,8 +420,7 @@ func (st *StackTrie) hashRec(hasher *hasher) {
|
||||||
nodes[i] = nilValueNode
|
nodes[i] = nilValueNode
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
child.hashRec(hasher, append(path, byte(i)))
|
||||||
child.hashRec(hasher)
|
|
||||||
if len(child.val) < 32 {
|
if len(child.val) < 32 {
|
||||||
nodes[i] = rawNode(child.val)
|
nodes[i] = rawNode(child.val)
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -429,10 +436,9 @@ func (st *StackTrie) hashRec(hasher *hasher) {
|
||||||
encodedNode = hasher.encodedBytes()
|
encodedNode = hasher.encodedBytes()
|
||||||
|
|
||||||
case extNode:
|
case extNode:
|
||||||
st.children[0].hashRec(hasher)
|
st.children[0].hashRec(hasher, append(path, st.key...))
|
||||||
|
|
||||||
sz := hexToCompactInPlace(st.key)
|
n := rawShortNode{Key: hexToCompact(st.key)}
|
||||||
n := rawShortNode{Key: st.key[:sz]}
|
|
||||||
if len(st.children[0].val) < 32 {
|
if len(st.children[0].val) < 32 {
|
||||||
n.Val = rawNode(st.children[0].val)
|
n.Val = rawNode(st.children[0].val)
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -448,8 +454,7 @@ func (st *StackTrie) hashRec(hasher *hasher) {
|
||||||
|
|
||||||
case leafNode:
|
case leafNode:
|
||||||
st.key = append(st.key, byte(16))
|
st.key = append(st.key, byte(16))
|
||||||
sz := hexToCompactInPlace(st.key)
|
n := rawShortNode{Key: hexToCompact(st.key), Val: valueNode(st.val)}
|
||||||
n := rawShortNode{Key: st.key[:sz], Val: valueNode(st.val)}
|
|
||||||
|
|
||||||
n.encode(hasher.encbuf)
|
n.encode(hasher.encbuf)
|
||||||
encodedNode = hasher.encodedBytes()
|
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
|
// Write the hash to the 'val'. We allocate a new val here to not mutate
|
||||||
// input values
|
// input values
|
||||||
st.val = hasher.hashData(encodedNode)
|
st.val = hasher.hashData(encodedNode)
|
||||||
if st.db != nil {
|
if st.writeFn != nil {
|
||||||
// TODO! Is it safe to Put the slice here?
|
st.writeFn(st.owner, path, common.BytesToHash(st.val), encodedNode)
|
||||||
// Do all db implementations copy the value provided?
|
|
||||||
st.db.Put(st.val, encodedNode)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -480,12 +483,11 @@ func (st *StackTrie) Hash() (h common.Hash) {
|
||||||
hasher := newHasher(false)
|
hasher := newHasher(false)
|
||||||
defer returnHasherToPool(hasher)
|
defer returnHasherToPool(hasher)
|
||||||
|
|
||||||
st.hashRec(hasher)
|
st.hashRec(hasher, nil)
|
||||||
if len(st.val) == 32 {
|
if len(st.val) == 32 {
|
||||||
copy(h[:], st.val)
|
copy(h[:], st.val)
|
||||||
return h
|
return h
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the node's RLP isn't 32 bytes long, the node will not
|
// If the node's RLP isn't 32 bytes long, the node will not
|
||||||
// be hashed, and instead contain the rlp-encoding of the
|
// be hashed, and instead contain the rlp-encoding of the
|
||||||
// node. For the top level node, we need to force the hashing.
|
// 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
|
// The associated database is expected, otherwise the whole commit
|
||||||
// functionality should be disabled.
|
// functionality should be disabled.
|
||||||
func (st *StackTrie) Commit() (h common.Hash, err error) {
|
func (st *StackTrie) Commit() (h common.Hash, err error) {
|
||||||
if st.db == nil {
|
if st.writeFn == nil {
|
||||||
return common.Hash{}, ErrCommitDisabled
|
return common.Hash{}, ErrCommitDisabled
|
||||||
}
|
}
|
||||||
|
|
||||||
hasher := newHasher(false)
|
hasher := newHasher(false)
|
||||||
defer returnHasherToPool(hasher)
|
defer returnHasherToPool(hasher)
|
||||||
|
|
||||||
st.hashRec(hasher)
|
st.hashRec(hasher, nil)
|
||||||
if len(st.val) == 32 {
|
if len(st.val) == 32 {
|
||||||
copy(h[:], st.val)
|
copy(h[:], st.val)
|
||||||
return h, nil
|
return h, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the node's RLP isn't 32 bytes long, the node will not
|
// 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.
|
// node. For the top level node, we need to force the hashing+commit.
|
||||||
hasher.sha.Reset()
|
hasher.sha.Reset()
|
||||||
hasher.sha.Write(st.val)
|
hasher.sha.Write(st.val)
|
||||||
hasher.sha.Read(h[:])
|
hasher.sha.Read(h[:])
|
||||||
st.db.Put(h[:], st.val)
|
|
||||||
|
st.writeFn(st.owner, nil, h, st.val)
|
||||||
return h, nil
|
return h, nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -22,8 +22,8 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/XinFinOrg/XDPoSChain/common"
|
"github.com/XinFinOrg/XDPoSChain/common"
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/core/rawdb"
|
||||||
"github.com/XinFinOrg/XDPoSChain/crypto"
|
"github.com/XinFinOrg/XDPoSChain/crypto"
|
||||||
"github.com/XinFinOrg/XDPoSChain/ethdb/memorydb"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestStackTrieInsertAndHash(t *testing.T) {
|
func TestStackTrieInsertAndHash(t *testing.T) {
|
||||||
|
|
@ -188,7 +188,7 @@ func TestStackTrieInsertAndHash(t *testing.T) {
|
||||||
|
|
||||||
func TestSizeBug(t *testing.T) {
|
func TestSizeBug(t *testing.T) {
|
||||||
st := NewStackTrie(nil)
|
st := NewStackTrie(nil)
|
||||||
nt := NewEmpty(NewDatabase(memorydb.New()))
|
nt := NewEmpty(NewDatabase(rawdb.NewMemoryDatabase()))
|
||||||
|
|
||||||
leaf := common.FromHex("290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563")
|
leaf := common.FromHex("290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563")
|
||||||
value := common.FromHex("94cf40d0d2b44f2b66e07cace1372ca42b73cf21a3")
|
value := common.FromHex("94cf40d0d2b44f2b66e07cace1372ca42b73cf21a3")
|
||||||
|
|
@ -203,7 +203,7 @@ func TestSizeBug(t *testing.T) {
|
||||||
|
|
||||||
func TestEmptyBug(t *testing.T) {
|
func TestEmptyBug(t *testing.T) {
|
||||||
st := NewStackTrie(nil)
|
st := NewStackTrie(nil)
|
||||||
nt := NewEmpty(NewDatabase(memorydb.New()))
|
nt := NewEmpty(NewDatabase(rawdb.NewMemoryDatabase()))
|
||||||
|
|
||||||
//leaf := common.FromHex("290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563")
|
//leaf := common.FromHex("290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563")
|
||||||
//value := common.FromHex("94cf40d0d2b44f2b66e07cace1372ca42b73cf21a3")
|
//value := common.FromHex("94cf40d0d2b44f2b66e07cace1372ca42b73cf21a3")
|
||||||
|
|
@ -229,7 +229,7 @@ func TestEmptyBug(t *testing.T) {
|
||||||
|
|
||||||
func TestValLength56(t *testing.T) {
|
func TestValLength56(t *testing.T) {
|
||||||
st := NewStackTrie(nil)
|
st := NewStackTrie(nil)
|
||||||
nt := NewEmpty(NewDatabase(memorydb.New()))
|
nt := NewEmpty(NewDatabase(rawdb.NewMemoryDatabase()))
|
||||||
|
|
||||||
//leaf := common.FromHex("290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563")
|
//leaf := common.FromHex("290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563")
|
||||||
//value := common.FromHex("94cf40d0d2b44f2b66e07cace1372ca42b73cf21a3")
|
//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.
|
// which causes a lot of node-within-node. This case was found via fuzzing.
|
||||||
func TestUpdateSmallNodes(t *testing.T) {
|
func TestUpdateSmallNodes(t *testing.T) {
|
||||||
st := NewStackTrie(nil)
|
st := NewStackTrie(nil)
|
||||||
nt := NewEmpty(NewDatabase(memorydb.New()))
|
nt := NewEmpty(NewDatabase(rawdb.NewMemoryDatabase()))
|
||||||
|
|
||||||
kvs := []struct {
|
kvs := []struct {
|
||||||
K string
|
K string
|
||||||
|
|
@ -283,7 +283,7 @@ func TestUpdateSmallNodes(t *testing.T) {
|
||||||
func TestUpdateVariableKeys(t *testing.T) {
|
func TestUpdateVariableKeys(t *testing.T) {
|
||||||
t.SkipNow()
|
t.SkipNow()
|
||||||
st := NewStackTrie(nil)
|
st := NewStackTrie(nil)
|
||||||
nt := NewEmpty(NewDatabase(memorydb.New()))
|
nt := NewEmpty(NewDatabase(rawdb.NewMemoryDatabase()))
|
||||||
|
|
||||||
kvs := []struct {
|
kvs := []struct {
|
||||||
K string
|
K string
|
||||||
|
|
@ -353,7 +353,7 @@ func TestStacktrieNotModifyValues(t *testing.T) {
|
||||||
func TestStacktrieSerialization(t *testing.T) {
|
func TestStacktrieSerialization(t *testing.T) {
|
||||||
var (
|
var (
|
||||||
st = NewStackTrie(nil)
|
st = NewStackTrie(nil)
|
||||||
nt = NewEmpty(NewDatabase(memorydb.New()))
|
nt = NewEmpty(NewDatabase(rawdb.NewMemoryDatabase()))
|
||||||
keyB = big.NewInt(1)
|
keyB = big.NewInt(1)
|
||||||
keyDelta = big.NewInt(1)
|
keyDelta = big.NewInt(1)
|
||||||
vals [][]byte
|
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.
|
// version that can be sent over the network.
|
||||||
func NewSyncPath(path []byte) SyncPath {
|
func NewSyncPath(path []byte) SyncPath {
|
||||||
// If the hash is from the account trie, append a single item, if it
|
// 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
|
// clashing between account leaf and storage root. It's fine though
|
||||||
// because having a trie node at 64 depth means a hash collision was
|
// because having a trie node at 64 depth means a hash collision was
|
||||||
// found and we're long dead.
|
// found and we're long dead.
|
||||||
|
|
@ -75,6 +75,22 @@ func NewSyncPath(path []byte) SyncPath {
|
||||||
return SyncPath{hexToKeybytes(path[:64]), hexToCompact(path[64:])}
|
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.
|
// nodeRequest represents a scheduled or already in-flight trie node retrieval request.
|
||||||
type nodeRequest struct {
|
type nodeRequest struct {
|
||||||
hash common.Hash // Hash of the trie node to retrieve
|
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
|
// unknown trie hashes to retrieve, accepts Node data associated with said hashes
|
||||||
// and reconstructs the trie step by step until all is done.
|
// and reconstructs the trie step by step until all is done.
|
||||||
type Sync struct {
|
type Sync struct {
|
||||||
|
scheme NodeScheme // Node scheme descriptor used in database.
|
||||||
database ethdb.KeyValueReader // Persistent database to check for existing entries
|
database ethdb.KeyValueReader // Persistent database to check for existing entries
|
||||||
membatch *syncMemBatch // Memory buffer to avoid frequent database writes
|
membatch *syncMemBatch // Memory buffer to avoid frequent database writes
|
||||||
nodeReqs map[string]*nodeRequest // Pending requests pertaining to a trie node path
|
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.
|
// 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{
|
ts := &Sync{
|
||||||
|
scheme: scheme,
|
||||||
database: database,
|
database: database,
|
||||||
membatch: newSyncMemBatch(),
|
membatch: newSyncMemBatch(),
|
||||||
nodeReqs: make(map[string]*nodeRequest),
|
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) {
|
if s.membatch.hasNode(path) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if rawdb.HasTrieNode(s.database, root) {
|
owner, inner := ResolvePath(path)
|
||||||
|
if s.scheme.HasTrieNode(s.database, owner, inner, root) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// Assemble the new sub-trie sync request
|
// 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
|
return
|
||||||
}
|
}
|
||||||
// If database says duplicate, the blob is present for sure.
|
// 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
|
// sync is expected to run with a fresh new node. Even there
|
||||||
// exists the code with legacy format, fetch and store with
|
// exists the code with legacy format, fetch and store with
|
||||||
// new scheme anyway.
|
// new scheme anyway.
|
||||||
|
|
@ -329,7 +348,8 @@ func (s *Sync) ProcessNode(result NodeSyncResult) error {
|
||||||
func (s *Sync) Commit(dbw ethdb.Batch) error {
|
func (s *Sync) Commit(dbw ethdb.Batch) error {
|
||||||
// Dump the membatch into a database dbw
|
// Dump the membatch into a database dbw
|
||||||
for path, value := range s.membatch.nodes {
|
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 {
|
for hash, value := range s.membatch.codes {
|
||||||
rawdb.WriteCode(dbw, hash, value)
|
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
|
// If database says duplicate, then at least the trie node is present
|
||||||
// and we hold the assumption that it's NOT legacy contract code.
|
// and we hold the assumption that it's NOT legacy contract code.
|
||||||
chash := common.BytesToHash(node)
|
var (
|
||||||
if rawdb.HasTrieNode(s.database, chash) {
|
chash = common.BytesToHash(node)
|
||||||
|
owner, inner = ResolvePath(child.path)
|
||||||
|
)
|
||||||
|
if s.scheme.HasTrieNode(s.database, owner, inner, chash) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// Locally unknown node, schedule for retrieval
|
// Locally unknown node, schedule for retrieval
|
||||||
|
|
@ -516,3 +539,14 @@ func (s *Sync) commitCodeRequest(req *codeRequest) error {
|
||||||
}
|
}
|
||||||
return nil
|
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"
|
"testing"
|
||||||
|
|
||||||
"github.com/XinFinOrg/XDPoSChain/common"
|
"github.com/XinFinOrg/XDPoSChain/common"
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/core/rawdb"
|
||||||
"github.com/XinFinOrg/XDPoSChain/core/types"
|
"github.com/XinFinOrg/XDPoSChain/core/types"
|
||||||
"github.com/XinFinOrg/XDPoSChain/crypto"
|
"github.com/XinFinOrg/XDPoSChain/crypto"
|
||||||
"github.com/XinFinOrg/XDPoSChain/ethdb/memorydb"
|
"github.com/XinFinOrg/XDPoSChain/ethdb/memorydb"
|
||||||
|
|
@ -30,7 +31,7 @@ import (
|
||||||
// makeTestTrie create a sample test trie to test node-wise reconstruction.
|
// makeTestTrie create a sample test trie to test node-wise reconstruction.
|
||||||
func makeTestTrie() (*Database, *StateTrie, map[string][]byte) {
|
func makeTestTrie() (*Database, *StateTrie, map[string][]byte) {
|
||||||
// Create an empty trie
|
// Create an empty trie
|
||||||
triedb := NewDatabase(memorydb.New())
|
triedb := NewDatabase(rawdb.NewMemoryDatabase())
|
||||||
trie, _ := NewStateTrie(TrieID(common.Hash{}), triedb)
|
trie, _ := NewStateTrie(TrieID(common.Hash{}), triedb)
|
||||||
|
|
||||||
// Fill it with some arbitrary data
|
// Fill it with some arbitrary data
|
||||||
|
|
@ -104,13 +105,13 @@ type trieElement struct {
|
||||||
|
|
||||||
// Tests that an empty trie is not scheduled for syncing.
|
// Tests that an empty trie is not scheduled for syncing.
|
||||||
func TestEmptySync(t *testing.T) {
|
func TestEmptySync(t *testing.T) {
|
||||||
dbA := NewDatabase(memorydb.New())
|
dbA := NewDatabase(rawdb.NewMemoryDatabase())
|
||||||
dbB := NewDatabase(memorydb.New())
|
dbB := NewDatabase(rawdb.NewMemoryDatabase())
|
||||||
emptyA, _ := New(TrieID(common.Hash{}), dbA)
|
emptyA, _ := New(TrieID(common.Hash{}), dbA)
|
||||||
emptyB, _ := New(TrieID(types.EmptyRootHash), dbB)
|
emptyB, _ := New(TrieID(types.EmptyRootHash), dbB)
|
||||||
|
|
||||||
for i, trie := range []*Trie{emptyA, emptyB} {
|
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 {
|
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)
|
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()
|
srcDb, srcTrie, srcData := makeTestTrie()
|
||||||
|
|
||||||
// Create a destination trie and sync with the scheduler
|
// Create a destination trie and sync with the scheduler
|
||||||
diskdb := memorydb.New()
|
diskdb := rawdb.NewMemoryDatabase()
|
||||||
triedb := NewDatabase(diskdb)
|
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
|
// The code requests are ignored here since there is no code
|
||||||
// at the testing trie.
|
// at the testing trie.
|
||||||
|
|
@ -195,9 +196,9 @@ func TestIterativeDelayedSync(t *testing.T) {
|
||||||
srcDb, srcTrie, srcData := makeTestTrie()
|
srcDb, srcTrie, srcData := makeTestTrie()
|
||||||
|
|
||||||
// Create a destination trie and sync with the scheduler
|
// Create a destination trie and sync with the scheduler
|
||||||
diskdb := memorydb.New()
|
diskdb := rawdb.NewMemoryDatabase()
|
||||||
triedb := NewDatabase(diskdb)
|
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
|
// The code requests are ignored here since there is no code
|
||||||
// at the testing trie.
|
// at the testing trie.
|
||||||
|
|
@ -256,9 +257,9 @@ func testIterativeRandomSync(t *testing.T, count int) {
|
||||||
srcDb, srcTrie, srcData := makeTestTrie()
|
srcDb, srcTrie, srcData := makeTestTrie()
|
||||||
|
|
||||||
// Create a destination trie and sync with the scheduler
|
// Create a destination trie and sync with the scheduler
|
||||||
diskdb := memorydb.New()
|
diskdb := rawdb.NewMemoryDatabase()
|
||||||
triedb := NewDatabase(diskdb)
|
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
|
// The code requests are ignored here since there is no code
|
||||||
// at the testing trie.
|
// at the testing trie.
|
||||||
|
|
@ -314,9 +315,9 @@ func TestIterativeRandomDelayedSync(t *testing.T) {
|
||||||
srcDb, srcTrie, srcData := makeTestTrie()
|
srcDb, srcTrie, srcData := makeTestTrie()
|
||||||
|
|
||||||
// Create a destination trie and sync with the scheduler
|
// Create a destination trie and sync with the scheduler
|
||||||
diskdb := memorydb.New()
|
diskdb := rawdb.NewMemoryDatabase()
|
||||||
triedb := NewDatabase(diskdb)
|
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
|
// The code requests are ignored here since there is no code
|
||||||
// at the testing trie.
|
// at the testing trie.
|
||||||
|
|
@ -377,9 +378,9 @@ func TestDuplicateAvoidanceSync(t *testing.T) {
|
||||||
srcDb, srcTrie, srcData := makeTestTrie()
|
srcDb, srcTrie, srcData := makeTestTrie()
|
||||||
|
|
||||||
// Create a destination trie and sync with the scheduler
|
// Create a destination trie and sync with the scheduler
|
||||||
diskdb := memorydb.New()
|
diskdb := rawdb.NewMemoryDatabase()
|
||||||
triedb := NewDatabase(diskdb)
|
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
|
// The code requests are ignored here since there is no code
|
||||||
// at the testing trie.
|
// at the testing trie.
|
||||||
|
|
@ -440,9 +441,9 @@ func TestIncompleteSync(t *testing.T) {
|
||||||
srcDb, srcTrie, _ := makeTestTrie()
|
srcDb, srcTrie, _ := makeTestTrie()
|
||||||
|
|
||||||
// Create a destination trie and sync with the scheduler
|
// Create a destination trie and sync with the scheduler
|
||||||
diskdb := memorydb.New()
|
diskdb := rawdb.NewMemoryDatabase()
|
||||||
triedb := NewDatabase(diskdb)
|
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
|
// The code requests are ignored here since there is no code
|
||||||
// at the testing trie.
|
// at the testing trie.
|
||||||
|
|
@ -520,9 +521,9 @@ func TestSyncOrdering(t *testing.T) {
|
||||||
srcDb, srcTrie, srcData := makeTestTrie()
|
srcDb, srcTrie, srcData := makeTestTrie()
|
||||||
|
|
||||||
// Create a destination trie and sync with the scheduler, tracking the requests
|
// Create a destination trie and sync with the scheduler, tracking the requests
|
||||||
diskdb := memorydb.New()
|
diskdb := rawdb.NewMemoryDatabase()
|
||||||
triedb := NewDatabase(diskdb)
|
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
|
// The code requests are ignored here since there is no code
|
||||||
// at the testing trie.
|
// at the testing trie.
|
||||||
|
|
|
||||||
16
trie/trie.go
16
trie/trie.go
|
|
@ -27,22 +27,6 @@ import (
|
||||||
"github.com/XinFinOrg/XDPoSChain/log"
|
"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
|
// 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
|
// 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,
|
// 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/core/types"
|
||||||
"github.com/XinFinOrg/XDPoSChain/crypto"
|
"github.com/XinFinOrg/XDPoSChain/crypto"
|
||||||
"github.com/XinFinOrg/XDPoSChain/ethdb"
|
"github.com/XinFinOrg/XDPoSChain/ethdb"
|
||||||
"github.com/XinFinOrg/XDPoSChain/ethdb/memorydb"
|
|
||||||
"github.com/XinFinOrg/XDPoSChain/rlp"
|
"github.com/XinFinOrg/XDPoSChain/rlp"
|
||||||
"github.com/davecgh/go-spew/spew"
|
"github.com/davecgh/go-spew/spew"
|
||||||
"golang.org/x/crypto/sha3"
|
"golang.org/x/crypto/sha3"
|
||||||
|
|
@ -67,7 +66,7 @@ func TestNull(t *testing.T) {
|
||||||
|
|
||||||
func TestMissingRoot(t *testing.T) {
|
func TestMissingRoot(t *testing.T) {
|
||||||
root := common.HexToHash("0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33")
|
root := common.HexToHash("0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33")
|
||||||
trie, err := New(TrieID(root), NewDatabase(memorydb.New()))
|
trie, err := New(TrieID(root), NewDatabase(rawdb.NewMemoryDatabase()))
|
||||||
if trie != nil {
|
if trie != nil {
|
||||||
t.Error("New returned non-nil trie for invalid root")
|
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 TestMissingNodeMemonly(t *testing.T) { testMissingNode(t, true) }
|
||||||
|
|
||||||
func testMissingNode(t *testing.T, memonly bool) {
|
func testMissingNode(t *testing.T, memonly bool) {
|
||||||
diskdb := memorydb.New()
|
diskdb := rawdb.NewMemoryDatabase()
|
||||||
triedb := NewDatabase(diskdb)
|
triedb := NewDatabase(diskdb)
|
||||||
|
|
||||||
trie := NewEmpty(triedb)
|
trie := NewEmpty(triedb)
|
||||||
|
|
@ -435,7 +434,7 @@ func runRandTestBool(rt randTest) bool {
|
||||||
|
|
||||||
func runRandTest(rt randTest) error {
|
func runRandTest(rt randTest) error {
|
||||||
var (
|
var (
|
||||||
triedb = NewDatabase(memorydb.New())
|
triedb = NewDatabase(rawdb.NewMemoryDatabase())
|
||||||
tr = NewEmpty(triedb)
|
tr = NewEmpty(triedb)
|
||||||
values = make(map[string]string) // tracks content of the trie
|
values = make(map[string]string) // tracks content of the trie
|
||||||
origTrie = NewEmpty(triedb)
|
origTrie = NewEmpty(triedb)
|
||||||
|
|
@ -815,11 +814,13 @@ func TestCommitSequenceStackTrie(t *testing.T) {
|
||||||
prng := rand.New(rand.NewSource(int64(count)))
|
prng := rand.New(rand.NewSource(int64(count)))
|
||||||
// This spongeDb is used to check the sequence of disk-db-writes
|
// This spongeDb is used to check the sequence of disk-db-writes
|
||||||
s := &spongeDb{sponge: sha3.NewLegacyKeccak256(), id: "a"}
|
s := &spongeDb{sponge: sha3.NewLegacyKeccak256(), id: "a"}
|
||||||
db := NewDatabase(s)
|
db := NewDatabase(rawdb.NewDatabase(s))
|
||||||
trie := NewEmpty(db)
|
trie := NewEmpty(db)
|
||||||
// Another sponge is used for the stacktrie commits
|
// Another sponge is used for the stacktrie commits
|
||||||
stackTrieSponge := &spongeDb{sponge: sha3.NewLegacyKeccak256(), id: "b"}
|
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
|
// Fill the trie with elements
|
||||||
for i := 0; i < count; i++ {
|
for i := 0; i < count; i++ {
|
||||||
// For the stack trie, we need to do inserts in proper order
|
// 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.
|
// not fit into 32 bytes, rlp-encoded. However, it's still the correct thing to do.
|
||||||
func TestCommitSequenceSmallRoot(t *testing.T) {
|
func TestCommitSequenceSmallRoot(t *testing.T) {
|
||||||
s := &spongeDb{sponge: sha3.NewLegacyKeccak256(), id: "a"}
|
s := &spongeDb{sponge: sha3.NewLegacyKeccak256(), id: "a"}
|
||||||
db := NewDatabase(s)
|
db := NewDatabase(rawdb.NewDatabase(s))
|
||||||
trie := NewEmpty(db)
|
trie := NewEmpty(db)
|
||||||
// Another sponge is used for the stacktrie commits
|
// Another sponge is used for the stacktrie commits
|
||||||
stackTrieSponge := &spongeDb{sponge: sha3.NewLegacyKeccak256(), id: "b"}
|
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)
|
// Add a single small-element to the trie(s)
|
||||||
key := make([]byte, 5)
|
key := make([]byte, 5)
|
||||||
key[0] = 1
|
key[0] = 1
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue