core, trie: thread-safe triedb commit, configurable caches

This commit is contained in:
Péter Szilágyi 2018-01-17 17:04:40 +02:00
parent 34d766a243
commit 80eb5be015
No known key found for this signature in database
GPG key ID: E9AE538CEDF8293D
33 changed files with 295 additions and 197 deletions

View file

@ -69,7 +69,7 @@ func NewSimulatedBackend(alloc core.GenesisAlloc) *SimulatedBackend {
database, _ := ethdb.NewMemDatabase() database, _ := ethdb.NewMemDatabase()
genesis := core.Genesis{Config: params.AllEthashProtocolChanges, Alloc: alloc} genesis := core.Genesis{Config: params.AllEthashProtocolChanges, Alloc: alloc}
genesis.MustCommit(database) genesis.MustCommit(database)
blockchain, _ := core.NewBlockChain(database, genesis.Config, ethash.NewFaker(), vm.Config{}) blockchain, _ := core.NewBlockChain(database, nil, genesis.Config, ethash.NewFaker(), vm.Config{})
backend := &SimulatedBackend{ backend := &SimulatedBackend{
database: database, database: database,

View file

@ -97,7 +97,9 @@ func runCmd(ctx *cli.Context) error {
} }
if ctx.GlobalString(GenesisFlag.Name) != "" { if ctx.GlobalString(GenesisFlag.Name) != "" {
gen := readGenesis(ctx.GlobalString(GenesisFlag.Name)) gen := readGenesis(ctx.GlobalString(GenesisFlag.Name))
_, statedb, _ = gen.ToBlock() db, _ := ethdb.NewMemDatabase()
genesis := gen.ToBlock(db)
statedb, _ = state.New(genesis.Root(), state.NewDatabase(trie.NewDatabase(db)))
chainConfig = gen.Config chainConfig = gen.Config
} else { } else {
db, _ := ethdb.NewMemDatabase() db, _ := ethdb.NewMemDatabase()

View file

@ -89,6 +89,9 @@ var (
utils.LightPeersFlag, utils.LightPeersFlag,
utils.LightKDFFlag, utils.LightKDFFlag,
utils.CacheFlag, utils.CacheFlag,
utils.CacheDatabaseFlag,
utils.CacheGCFlag,
utils.CacheGCTimeoutFlag,
utils.TrieCacheGenFlag, utils.TrieCacheGenFlag,
utils.ListenPortFlag, utils.ListenPortFlag,
utils.MaxPeersFlag, utils.MaxPeersFlag,

View file

@ -22,10 +22,11 @@ import (
"io" "io"
"sort" "sort"
"strings"
"github.com/ethereum/go-ethereum/cmd/utils" "github.com/ethereum/go-ethereum/cmd/utils"
"github.com/ethereum/go-ethereum/internal/debug" "github.com/ethereum/go-ethereum/internal/debug"
"gopkg.in/urfave/cli.v1" "gopkg.in/urfave/cli.v1"
"strings"
) )
// AppHelpTemplate is the test template for the default, global app help topic. // AppHelpTemplate is the test template for the default, global app help topic.
@ -127,6 +128,9 @@ var AppHelpFlagGroups = []flagGroup{
Name: "PERFORMANCE TUNING", Name: "PERFORMANCE TUNING",
Flags: []cli.Flag{ Flags: []cli.Flag{
utils.CacheFlag, utils.CacheFlag,
utils.CacheDatabaseFlag,
utils.CacheGCFlag,
utils.CacheGCTimeoutFlag,
utils.TrieCacheGenFlag, utils.TrieCacheGenFlag,
}, },
}, },

View file

@ -27,6 +27,7 @@ import (
"runtime" "runtime"
"strconv" "strconv"
"strings" "strings"
"time"
"github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/accounts/keystore" "github.com/ethereum/go-ethereum/accounts/keystore"
@ -293,8 +294,23 @@ var (
// Performance tuning settings // Performance tuning settings
CacheFlag = cli.IntFlag{ CacheFlag = cli.IntFlag{
Name: "cache", Name: "cache",
Usage: "Megabytes of memory allocated to internal caching (min 16MB / database forced)", Usage: "Megabytes of memory allocated to internal caching",
Value: 128, Value: 1024,
}
CacheDatabaseFlag = cli.IntFlag{
Name: "cache.database",
Usage: "Percentage of cache memory allowance to use for database io",
Value: 75,
}
CacheGCFlag = cli.IntFlag{
Name: "cache.gc",
Usage: "Percentage of cache memory allowance to use for trie pruning",
Value: 25,
}
CacheGCTimeoutFlag = cli.DurationFlag{
Name: "cache.gc.timeout",
Usage: "Maximum time after which to flush memory-only tries to disk",
Value: 5 * time.Minute,
} }
TrieCacheGenFlag = cli.IntFlag{ TrieCacheGenFlag = cli.IntFlag{
Name: "trie-cache-gens", Name: "trie-cache-gens",
@ -1021,11 +1037,17 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *eth.Config) {
cfg.NetworkId = ctx.GlobalUint64(NetworkIdFlag.Name) cfg.NetworkId = ctx.GlobalUint64(NetworkIdFlag.Name)
} }
if ctx.GlobalIsSet(CacheFlag.Name) { if ctx.GlobalIsSet(CacheFlag.Name) || ctx.GlobalIsSet(CacheDatabaseFlag.Name) {
cfg.DatabaseCache = ctx.GlobalInt(CacheFlag.Name) cfg.DatabaseCache = ctx.GlobalInt(CacheFlag.Name) * ctx.GlobalInt(CacheDatabaseFlag.Name) / 100
} }
cfg.DatabaseHandles = makeDatabaseHandles() cfg.DatabaseHandles = makeDatabaseHandles()
if ctx.GlobalIsSet(CacheFlag.Name) || ctx.GlobalIsSet(CacheGCFlag.Name) {
cfg.TrieCache = ctx.GlobalInt(CacheFlag.Name) * ctx.GlobalInt(CacheGCFlag.Name) / 100
}
if ctx.GlobalIsSet(CacheGCTimeoutFlag.Name) {
cfg.TrieTimeout = ctx.GlobalDuration(CacheGCTimeoutFlag.Name)
}
if ctx.GlobalIsSet(MinerThreadsFlag.Name) { if ctx.GlobalIsSet(MinerThreadsFlag.Name) {
cfg.MinerThreads = ctx.GlobalInt(MinerThreadsFlag.Name) cfg.MinerThreads = ctx.GlobalInt(MinerThreadsFlag.Name)
} }
@ -1157,7 +1179,7 @@ func SetupNetwork(ctx *cli.Context) {
// MakeChainDatabase open an LevelDB using the flags passed to the client and will hard crash if it fails. // MakeChainDatabase open an LevelDB using the flags passed to the client and will hard crash if it fails.
func MakeChainDatabase(ctx *cli.Context, stack *node.Node) ethdb.Database { func MakeChainDatabase(ctx *cli.Context, stack *node.Node) ethdb.Database {
var ( var (
cache = ctx.GlobalInt(CacheFlag.Name) cache = ctx.GlobalInt(CacheFlag.Name) * ctx.GlobalInt(CacheDatabaseFlag.Name) / 100
handles = makeDatabaseHandles() handles = makeDatabaseHandles()
) )
name := "chaindata" name := "chaindata"
@ -1209,8 +1231,18 @@ func MakeChain(ctx *cli.Context, stack *node.Node) (chain *core.BlockChain, chai
}) })
} }
} }
cache := &core.CacheConfig{
TrieNodeLimit: eth.DefaultConfig.TrieCache,
TrieTimeLimit: eth.DefaultConfig.TrieTimeout,
}
if ctx.GlobalIsSet(CacheFlag.Name) || ctx.GlobalIsSet(CacheGCFlag.Name) {
cache.TrieNodeLimit = ctx.GlobalInt(CacheFlag.Name) * ctx.GlobalInt(CacheGCFlag.Name) / 100
}
if ctx.GlobalIsSet(CacheGCTimeoutFlag.Name) {
cache.TrieTimeLimit = ctx.GlobalDuration(CacheGCTimeoutFlag.Name)
}
vmcfg := vm.Config{EnablePreimageRecording: ctx.GlobalBool(VMEnableDebugFlag.Name)} vmcfg := vm.Config{EnablePreimageRecording: ctx.GlobalBool(VMEnableDebugFlag.Name)}
chain, err = core.NewBlockChain(chainDb, config, engine, vmcfg) chain, err = core.NewBlockChain(chainDb, cache, config, engine, vmcfg)
if err != nil { if err != nil {
Fatalf("Can't create BlockChain: %v", err) Fatalf("Can't create BlockChain: %v", err)
} }

View file

@ -173,7 +173,7 @@ func benchInsertChain(b *testing.B, disk bool, gen func(int, *BlockGen)) {
// Time the insertion of the new chain. // Time the insertion of the new chain.
// State and blocks are stored in the same DB. // State and blocks are stored in the same DB.
chainman, _ := NewBlockChain(db, gspec.Config, ethash.NewFaker(), vm.Config{}) chainman, _ := NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{})
defer chainman.Stop() defer chainman.Stop()
b.ReportAllocs() b.ReportAllocs()
b.ResetTimer() b.ResetTimer()
@ -283,7 +283,7 @@ func benchReadChain(b *testing.B, full bool, count uint64) {
if err != nil { if err != nil {
b.Fatalf("error opening database at %v: %v", dir, err) b.Fatalf("error opening database at %v: %v", dir, err)
} }
chain, err := NewBlockChain(db, params.TestChainConfig, ethash.NewFaker(), vm.Config{}) chain, err := NewBlockChain(db, nil, params.TestChainConfig, ethash.NewFaker(), vm.Config{})
if err != nil { if err != nil {
b.Fatalf("error creating chain: %v", err) b.Fatalf("error creating chain: %v", err)
} }

View file

@ -42,7 +42,7 @@ func TestHeaderVerification(t *testing.T) {
headers[i] = block.Header() headers[i] = block.Header()
} }
// Run the header checker for blocks one-by-one, checking for both valid and invalid nonces // Run the header checker for blocks one-by-one, checking for both valid and invalid nonces
chain, _ := NewBlockChain(testdb, params.TestChainConfig, ethash.NewFaker(), vm.Config{}) chain, _ := NewBlockChain(testdb, nil, params.TestChainConfig, ethash.NewFaker(), vm.Config{})
defer chain.Stop() defer chain.Stop()
for i := 0; i < len(blocks); i++ { for i := 0; i < len(blocks); i++ {
@ -106,11 +106,11 @@ func testHeaderConcurrentVerification(t *testing.T, threads int) {
var results <-chan error var results <-chan error
if valid { if valid {
chain, _ := NewBlockChain(testdb, params.TestChainConfig, ethash.NewFaker(), vm.Config{}) chain, _ := NewBlockChain(testdb, nil, params.TestChainConfig, ethash.NewFaker(), vm.Config{})
_, results = chain.engine.VerifyHeaders(chain, headers, seals) _, results = chain.engine.VerifyHeaders(chain, headers, seals)
chain.Stop() chain.Stop()
} else { } else {
chain, _ := NewBlockChain(testdb, params.TestChainConfig, ethash.NewFakeFailer(uint64(len(headers)-1)), vm.Config{}) chain, _ := NewBlockChain(testdb, nil, params.TestChainConfig, ethash.NewFakeFailer(uint64(len(headers)-1)), vm.Config{})
_, results = chain.engine.VerifyHeaders(chain, headers, seals) _, results = chain.engine.VerifyHeaders(chain, headers, seals)
chain.Stop() chain.Stop()
} }
@ -173,7 +173,7 @@ func testHeaderConcurrentAbortion(t *testing.T, threads int) {
defer runtime.GOMAXPROCS(old) defer runtime.GOMAXPROCS(old)
// Start the verifications and immediately abort // Start the verifications and immediately abort
chain, _ := NewBlockChain(testdb, params.TestChainConfig, ethash.NewFakeDelayer(time.Millisecond), vm.Config{}) chain, _ := NewBlockChain(testdb, nil, params.TestChainConfig, ethash.NewFakeDelayer(time.Millisecond), vm.Config{})
defer chain.Stop() defer chain.Stop()
abort, results := chain.engine.VerifyHeaders(chain, headers, seals) abort, results := chain.engine.VerifyHeaders(chain, headers, seals)

View file

@ -56,11 +56,19 @@ const (
maxFutureBlocks = 256 maxFutureBlocks = 256
maxTimeFutureBlocks = 30 maxTimeFutureBlocks = 30
badBlockLimit = 10 badBlockLimit = 10
triesInMemory = 128
// BlockChainVersion ensures that an incompatible database forces a resync from scratch. // BlockChainVersion ensures that an incompatible database forces a resync from scratch.
BlockChainVersion = 3 BlockChainVersion = 3
) )
// CacheConfig contains the configuration values for the trie caching/pruning
// that's resident in a blockchain.
type CacheConfig struct {
TrieNodeLimit int // Memory limit (MB) at which to flush the current in-memory trie to disk
TrieTimeLimit time.Duration // Time limit after which to flush the current in-memory trie to disk
}
// BlockChain represents the canonical chain given a database with a genesis // BlockChain represents the canonical chain given a database with a genesis
// block. The Blockchain manages chain imports, reverts, chain reorganisations. // block. The Blockchain manages chain imports, reverts, chain reorganisations.
// //
@ -76,10 +84,13 @@ const (
// included in the canonical one where as GetBlockByNumber always represents the // included in the canonical one where as GetBlockByNumber always represents the
// canonical chain. // canonical chain.
type BlockChain struct { type BlockChain struct {
config *params.ChainConfig // chain & network configuration chainConfig *params.ChainConfig // Chain & network configuration
cacheConfig *CacheConfig // Cache configuration for pruning
diskdb ethdb.Database // Low level persistent database to store final content in
triedb *trie.Database // High level ephemeral database to store non-final tries in
hc *HeaderChain hc *HeaderChain
chainDb ethdb.Database
rmLogsFeed event.Feed rmLogsFeed event.Feed
chainFeed event.Feed chainFeed event.Feed
chainSideFeed event.Feed chainSideFeed event.Feed
@ -121,17 +132,27 @@ type BlockChain struct {
// NewBlockChain returns a fully initialised block chain using information // NewBlockChain returns a fully initialised block chain using information
// available in the database. It initialises the default Ethereum Validator and // available in the database. It initialises the default Ethereum Validator and
// Processor. // Processor.
func NewBlockChain(chainDb ethdb.Database, config *params.ChainConfig, engine consensus.Engine, vmConfig vm.Config) (*BlockChain, error) { func NewBlockChain(diskdb ethdb.Database, cacheConfig *CacheConfig, chainConfig *params.ChainConfig, engine consensus.Engine, vmConfig vm.Config) (*BlockChain, error) {
if cacheConfig == nil {
cacheConfig = &CacheConfig{
TrieNodeLimit: 256 * 1024 * 1024,
TrieTimeLimit: 5 * time.Minute,
}
}
bodyCache, _ := lru.New(bodyCacheLimit) bodyCache, _ := lru.New(bodyCacheLimit)
bodyRLPCache, _ := lru.New(bodyCacheLimit) bodyRLPCache, _ := lru.New(bodyCacheLimit)
blockCache, _ := lru.New(blockCacheLimit) blockCache, _ := lru.New(blockCacheLimit)
futureBlocks, _ := lru.New(maxFutureBlocks) futureBlocks, _ := lru.New(maxFutureBlocks)
badBlocks, _ := lru.New(badBlockLimit) badBlocks, _ := lru.New(badBlockLimit)
triedb := trie.NewDatabase(diskdb)
bc := &BlockChain{ bc := &BlockChain{
config: config, chainConfig: chainConfig,
chainDb: chainDb, cacheConfig: cacheConfig,
stateCache: state.NewDatabase(trie.NewDatabase(chainDb)), diskdb: diskdb,
triedb: triedb,
stateCache: state.NewDatabase(triedb),
quit: make(chan struct{}), quit: make(chan struct{}),
bodyCache: bodyCache, bodyCache: bodyCache,
bodyRLPCache: bodyRLPCache, bodyRLPCache: bodyRLPCache,
@ -141,11 +162,11 @@ func NewBlockChain(chainDb ethdb.Database, config *params.ChainConfig, engine co
vmConfig: vmConfig, vmConfig: vmConfig,
badBlocks: badBlocks, badBlocks: badBlocks,
} }
bc.SetValidator(NewBlockValidator(config, bc, engine)) bc.SetValidator(NewBlockValidator(chainConfig, bc, engine))
bc.SetProcessor(NewStateProcessor(config, bc, engine)) bc.SetProcessor(NewStateProcessor(chainConfig, bc, engine))
var err error var err error
bc.hc, err = NewHeaderChain(chainDb, config, engine, bc.getProcInterrupt) bc.hc, err = NewHeaderChain(diskdb, chainConfig, engine, bc.getProcInterrupt)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -182,7 +203,7 @@ func (bc *BlockChain) getProcInterrupt() bool {
// assumes that the chain manager mutex is held. // assumes that the chain manager mutex is held.
func (bc *BlockChain) loadLastState() error { func (bc *BlockChain) loadLastState() error {
// Restore the last known head block // Restore the last known head block
head := GetHeadBlockHash(bc.chainDb) head := GetHeadBlockHash(bc.diskdb)
if head == (common.Hash{}) { if head == (common.Hash{}) {
// Corrupt or empty database, init from scratch // Corrupt or empty database, init from scratch
log.Warn("Empty database, resetting chain") log.Warn("Empty database, resetting chain")
@ -206,7 +227,7 @@ func (bc *BlockChain) loadLastState() error {
// Restore the last known head header // Restore the last known head header
currentHeader := bc.currentBlock.Header() currentHeader := bc.currentBlock.Header()
if head := GetHeadHeaderHash(bc.chainDb); head != (common.Hash{}) { if head := GetHeadHeaderHash(bc.diskdb); head != (common.Hash{}) {
if header := bc.GetHeaderByHash(head); header != nil { if header := bc.GetHeaderByHash(head); header != nil {
currentHeader = header currentHeader = header
} }
@ -215,7 +236,7 @@ func (bc *BlockChain) loadLastState() error {
// Restore the last known head fast block // Restore the last known head fast block
bc.currentFastBlock = bc.currentBlock bc.currentFastBlock = bc.currentBlock
if head := GetHeadFastBlockHash(bc.chainDb); head != (common.Hash{}) { if head := GetHeadFastBlockHash(bc.diskdb); head != (common.Hash{}) {
if block := bc.GetBlockByHash(head); block != nil { if block := bc.GetBlockByHash(head); block != nil {
bc.currentFastBlock = block bc.currentFastBlock = block
} }
@ -245,7 +266,7 @@ func (bc *BlockChain) SetHead(head uint64) error {
// Rewind the header chain, deleting all block bodies until then // Rewind the header chain, deleting all block bodies until then
delFn := func(hash common.Hash, num uint64) { delFn := func(hash common.Hash, num uint64) {
DeleteBody(bc.chainDb, hash, num) DeleteBody(bc.diskdb, hash, num)
} }
bc.hc.SetHead(head, delFn) bc.hc.SetHead(head, delFn)
currentHeader := bc.hc.CurrentHeader() currentHeader := bc.hc.CurrentHeader()
@ -277,10 +298,10 @@ func (bc *BlockChain) SetHead(head uint64) error {
if bc.currentFastBlock == nil { if bc.currentFastBlock == nil {
bc.currentFastBlock = bc.genesisBlock bc.currentFastBlock = bc.genesisBlock
} }
if err := WriteHeadBlockHash(bc.chainDb, bc.currentBlock.Hash()); err != nil { if err := WriteHeadBlockHash(bc.diskdb, bc.currentBlock.Hash()); err != nil {
log.Crit("Failed to reset head full block", "err", err) log.Crit("Failed to reset head full block", "err", err)
} }
if err := WriteHeadFastBlockHash(bc.chainDb, bc.currentFastBlock.Hash()); err != nil { if err := WriteHeadFastBlockHash(bc.diskdb, bc.currentFastBlock.Hash()); err != nil {
log.Crit("Failed to reset head fast block", "err", err) log.Crit("Failed to reset head fast block", "err", err)
} }
return bc.loadLastState() return bc.loadLastState()
@ -294,7 +315,7 @@ func (bc *BlockChain) FastSyncCommitHead(hash common.Hash) error {
if block == nil { if block == nil {
return fmt.Errorf("non existent block [%x…]", hash[:4]) return fmt.Errorf("non existent block [%x…]", hash[:4])
} }
if _, err := trie.NewSecure(block.Root(), bc.stateCache.TrieDB(), 0); err != nil { if _, err := trie.NewSecure(block.Root(), bc.triedb, 0); err != nil {
return err return err
} }
// If all checks out, manually set the head block // If all checks out, manually set the head block
@ -389,7 +410,7 @@ func (bc *BlockChain) ResetWithGenesisBlock(genesis *types.Block) error {
if err := bc.hc.WriteTd(genesis.Hash(), genesis.NumberU64(), genesis.Difficulty()); err != nil { if err := bc.hc.WriteTd(genesis.Hash(), genesis.NumberU64(), genesis.Difficulty()); err != nil {
log.Crit("Failed to write genesis block TD", "err", err) log.Crit("Failed to write genesis block TD", "err", err)
} }
if err := WriteBlock(bc.chainDb, genesis); err != nil { if err := WriteBlock(bc.diskdb, genesis); err != nil {
log.Crit("Failed to write genesis block", "err", err) log.Crit("Failed to write genesis block", "err", err)
} }
bc.genesisBlock = genesis bc.genesisBlock = genesis
@ -439,13 +460,13 @@ func (bc *BlockChain) ExportN(w io.Writer, first uint64, last uint64) error {
// Note, this function assumes that the `mu` mutex is held! // Note, this function assumes that the `mu` mutex is held!
func (bc *BlockChain) insert(block *types.Block) { func (bc *BlockChain) insert(block *types.Block) {
// If the block is on a side chain or an unknown one, force other heads onto it too // If the block is on a side chain or an unknown one, force other heads onto it too
updateHeads := GetCanonicalHash(bc.chainDb, block.NumberU64()) != block.Hash() updateHeads := GetCanonicalHash(bc.diskdb, block.NumberU64()) != block.Hash()
// Add the block to the canonical chain number scheme and mark as the head // Add the block to the canonical chain number scheme and mark as the head
if err := WriteCanonicalHash(bc.chainDb, block.Hash(), block.NumberU64()); err != nil { if err := WriteCanonicalHash(bc.diskdb, block.Hash(), block.NumberU64()); err != nil {
log.Crit("Failed to insert block number", "err", err) log.Crit("Failed to insert block number", "err", err)
} }
if err := WriteHeadBlockHash(bc.chainDb, block.Hash()); err != nil { if err := WriteHeadBlockHash(bc.diskdb, block.Hash()); err != nil {
log.Crit("Failed to insert head block hash", "err", err) log.Crit("Failed to insert head block hash", "err", err)
} }
bc.currentBlock = block bc.currentBlock = block
@ -454,7 +475,7 @@ func (bc *BlockChain) insert(block *types.Block) {
if updateHeads { if updateHeads {
bc.hc.SetCurrentHeader(block.Header()) bc.hc.SetCurrentHeader(block.Header())
if err := WriteHeadFastBlockHash(bc.chainDb, block.Hash()); err != nil { if err := WriteHeadFastBlockHash(bc.diskdb, block.Hash()); err != nil {
log.Crit("Failed to insert head fast block hash", "err", err) log.Crit("Failed to insert head fast block hash", "err", err)
} }
bc.currentFastBlock = block bc.currentFastBlock = block
@ -474,7 +495,7 @@ func (bc *BlockChain) GetBody(hash common.Hash) *types.Body {
body := cached.(*types.Body) body := cached.(*types.Body)
return body return body
} }
body := GetBody(bc.chainDb, hash, bc.hc.GetBlockNumber(hash)) body := GetBody(bc.diskdb, hash, bc.hc.GetBlockNumber(hash))
if body == nil { if body == nil {
return nil return nil
} }
@ -490,7 +511,7 @@ func (bc *BlockChain) GetBodyRLP(hash common.Hash) rlp.RawValue {
if cached, ok := bc.bodyRLPCache.Get(hash); ok { if cached, ok := bc.bodyRLPCache.Get(hash); ok {
return cached.(rlp.RawValue) return cached.(rlp.RawValue)
} }
body := GetBodyRLP(bc.chainDb, hash, bc.hc.GetBlockNumber(hash)) body := GetBodyRLP(bc.diskdb, hash, bc.hc.GetBlockNumber(hash))
if len(body) == 0 { if len(body) == 0 {
return nil return nil
} }
@ -504,7 +525,7 @@ func (bc *BlockChain) HasBlock(hash common.Hash, number uint64) bool {
if bc.blockCache.Contains(hash) { if bc.blockCache.Contains(hash) {
return true return true
} }
ok, _ := bc.chainDb.Has(blockBodyKey(hash, number)) ok, _ := bc.diskdb.Has(blockBodyKey(hash, number))
return ok return ok
} }
@ -528,7 +549,7 @@ func (bc *BlockChain) GetBlock(hash common.Hash, number uint64) *types.Block {
if block, ok := bc.blockCache.Get(hash); ok { if block, ok := bc.blockCache.Get(hash); ok {
return block.(*types.Block) return block.(*types.Block)
} }
block := GetBlock(bc.chainDb, hash, number) block := GetBlock(bc.diskdb, hash, number)
if block == nil { if block == nil {
return nil return nil
} }
@ -545,13 +566,18 @@ func (bc *BlockChain) GetBlockByHash(hash common.Hash) *types.Block {
// GetBlockByNumber retrieves a block from the database by number, caching it // GetBlockByNumber retrieves a block from the database by number, caching it
// (associated with its hash) if found. // (associated with its hash) if found.
func (bc *BlockChain) GetBlockByNumber(number uint64) *types.Block { func (bc *BlockChain) GetBlockByNumber(number uint64) *types.Block {
hash := GetCanonicalHash(bc.chainDb, number) hash := GetCanonicalHash(bc.diskdb, number)
if hash == (common.Hash{}) { if hash == (common.Hash{}) {
return nil return nil
} }
return bc.GetBlock(hash, number) return bc.GetBlock(hash, number)
} }
// GetReceiptsByHash retrieves the receipts for all transactions in a given block.
func (bc *BlockChain) GetReceiptsByHash(hash common.Hash) types.Receipts {
return GetBlockReceipts(bc.diskdb, hash, GetBlockNumber(bc.diskdb, hash))
}
// GetBlocksFromHash returns the block corresponding to hash and up to n-1 ancestors. // GetBlocksFromHash returns the block corresponding to hash and up to n-1 ancestors.
// [deprecated by eth/62] // [deprecated by eth/62]
func (bc *BlockChain) GetBlocksFromHash(hash common.Hash, n int) (blocks []*types.Block) { func (bc *BlockChain) GetBlocksFromHash(hash common.Hash, n int) (blocks []*types.Block) {
@ -579,6 +605,12 @@ func (bc *BlockChain) GetUnclesInChain(block *types.Block, length int) []*types.
return uncles return uncles
} }
// TrieNode retrieves a blob of data associated with a trie node (or code hash)
// either from ephemeral in-memory cache, or from persistent storage.
func (bc *BlockChain) TrieNode(hash common.Hash) ([]byte, error) {
return bc.triedb.Node(hash)
}
// Stop stops the blockchain service. If any imports are currently in progress // Stop stops the blockchain service. If any imports are currently in progress
// it will abort them using the procInterrupt. // it will abort them using the procInterrupt.
func (bc *BlockChain) Stop() { func (bc *BlockChain) Stop() {
@ -595,13 +627,9 @@ func (bc *BlockChain) Stop() {
// Ensure the state of the latest block is also stored to disk // Ensure the state of the latest block is also stored to disk
root := bc.CurrentHeader().Root root := bc.CurrentHeader().Root
batch := bc.chainDb.NewBatch() if err := bc.triedb.Commit(root); err != nil {
if err := bc.stateCache.TrieDB().Commit(root, batch); err != nil {
log.Error("Failed to commit latest state trie", "err", err) log.Error("Failed to commit latest state trie", "err", err)
} }
if err := batch.Write(); err != nil {
log.Error("Failed to write latest state trie", "err", err)
}
log.Info("Blockchain manager stopped") log.Info("Blockchain manager stopped")
} }
@ -646,11 +674,11 @@ func (bc *BlockChain) Rollback(chain []common.Hash) {
} }
if bc.currentFastBlock.Hash() == hash { if bc.currentFastBlock.Hash() == hash {
bc.currentFastBlock = bc.GetBlock(bc.currentFastBlock.ParentHash(), bc.currentFastBlock.NumberU64()-1) bc.currentFastBlock = bc.GetBlock(bc.currentFastBlock.ParentHash(), bc.currentFastBlock.NumberU64()-1)
WriteHeadFastBlockHash(bc.chainDb, bc.currentFastBlock.Hash()) WriteHeadFastBlockHash(bc.diskdb, bc.currentFastBlock.Hash())
} }
if bc.currentBlock.Hash() == hash { if bc.currentBlock.Hash() == hash {
bc.currentBlock = bc.GetBlock(bc.currentBlock.ParentHash(), bc.currentBlock.NumberU64()-1) bc.currentBlock = bc.GetBlock(bc.currentBlock.ParentHash(), bc.currentBlock.NumberU64()-1)
WriteHeadBlockHash(bc.chainDb, bc.currentBlock.Hash()) WriteHeadBlockHash(bc.diskdb, bc.currentBlock.Hash())
} }
} }
} }
@ -709,7 +737,7 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [
stats = struct{ processed, ignored int32 }{} stats = struct{ processed, ignored int32 }{}
start = time.Now() start = time.Now()
bytes = 0 bytes = 0
batch = bc.chainDb.NewBatch() batch = bc.diskdb.NewBatch()
) )
for i, block := range blockChain { for i, block := range blockChain {
receipts := receiptChain[i] receipts := receiptChain[i]
@ -727,7 +755,7 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [
continue continue
} }
// Compute all the non-consensus fields of the receipts // Compute all the non-consensus fields of the receipts
SetReceiptsData(bc.config, block, receipts) SetReceiptsData(bc.chainConfig, block, receipts)
// Write all the data out into the database // Write all the data out into the database
if err := WriteBody(batch, block.Hash(), block.NumberU64(), block.Body()); err != nil { if err := WriteBody(batch, block.Hash(), block.NumberU64(), block.Body()); err != nil {
return i, fmt.Errorf("failed to write block body: %v", err) return i, fmt.Errorf("failed to write block body: %v", err)
@ -760,7 +788,7 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [
head := blockChain[len(blockChain)-1] head := blockChain[len(blockChain)-1]
if td := bc.GetTd(head.Hash(), head.NumberU64()); td != nil { // Rewind may have occurred, skip in that case if td := bc.GetTd(head.Hash(), head.NumberU64()); td != nil { // Rewind may have occurred, skip in that case
if bc.GetTd(bc.currentFastBlock.Hash(), bc.currentFastBlock.NumberU64()).Cmp(td) < 0 { if bc.GetTd(bc.currentFastBlock.Hash(), bc.currentFastBlock.NumberU64()).Cmp(td) < 0 {
if err := WriteHeadFastBlockHash(bc.chainDb, head.Hash()); err != nil { if err := WriteHeadFastBlockHash(bc.diskdb, head.Hash()); err != nil {
log.Crit("Failed to update head fast block hash", "err", err) log.Crit("Failed to update head fast block hash", "err", err)
} }
bc.currentFastBlock = head bc.currentFastBlock = head
@ -802,46 +830,38 @@ func (bc *BlockChain) WriteBlockAndState(block *types.Block, receipts []*types.R
return NonStatTy, err return NonStatTy, err
} }
// Write other block data using a batch. // Write other block data using a batch.
batch := bc.chainDb.NewBatch() batch := bc.diskdb.NewBatch()
if err := WriteBlock(batch, block); err != nil { if err := WriteBlock(batch, block); err != nil {
return NonStatTy, err return NonStatTy, err
} }
root, err := state.Commit(bc.config.IsEIP158(block.Number())) root, err := state.Commit(bc.chainConfig.IsEIP158(block.Number()))
if err != nil { if err != nil {
return NonStatTy, err return NonStatTy, err
} }
db := bc.stateCache.TrieDB()
var ( bc.triedb.Reference(root, common.Hash{}) // metadata reference to keep trie alive
writeRetention = uint64(128) // Number of trie nodes we need to retain in memory if current := block.NumberU64(); current > triesInMemory {
memoryAllowance = common.StorageSize(64 * 1024 * 1024) // Memory allowance below which to avoid writing to disk
timeAllowance = 3 * time.Minute // Time allowance below which to avoid writing to disk
)
db.Reference(root, common.Hash{}) // metadata reference to keep trie alive
if current := block.NumberU64(); current > writeRetention {
// Find the next state trie we need to get rid of or commit // Find the next state trie we need to get rid of or commit
header := bc.GetHeaderByNumber(current - writeRetention) header := bc.GetHeaderByNumber(current - triesInMemory)
chosen := header.Number.Uint64() chosen := header.Number.Uint64()
// Only write to disk if we exceeded our memory allowance *and* also have at // Only write to disk if we exceeded our memory allowance *and* also have at
// least a given number of tries gapped. // least a given number of tries gapped.
if (db.Size() > memoryAllowance || bc.procTime > timeAllowance) && chosen >= lastWrite+writeRetention { if (bc.triedb.Size()/(1024*1024) > common.StorageSize(bc.cacheConfig.TrieNodeLimit) || bc.procTime > bc.cacheConfig.TrieTimeLimit) && chosen >= lastWrite+triesInMemory {
db.Commit(header.Root, batch) bc.triedb.Commit(header.Root)
lastWrite = chosen lastWrite = chosen
bc.procTime = 0 bc.procTime = 0
} }
// Garbage collect anything below our required write retention // Garbage collect anything below our required write retention
db.Dereference(header.Root, common.Hash{}) bc.triedb.Dereference(header.Root, common.Hash{})
if current%10000 == 0 { if current%10000 == 0 {
log.Warn("Current trie pruning state", "size", db.Size(), "elapsed", bc.procTime) log.Debug("Current trie pruning state", "size", bc.triedb.Size(), "elapsed", bc.procTime)
} }
} }
if err := WriteBlockReceipts(batch, block.Hash(), block.NumberU64(), receipts); err != nil { if err := WriteBlockReceipts(batch, block.Hash(), block.NumberU64(), receipts); err != nil {
return NonStatTy, err return NonStatTy, err
} }
// If the total difficulty is higher than our known, add it to the canonical chain // If the total difficulty is higher than our known, add it to the canonical chain
// Second clause in the if statement reduces the vulnerability to selfish mining. // Second clause in the if statement reduces the vulnerability to selfish mining.
// Please refer to http://www.cs.cornell.edu/~ie53/publications/btcProcFC.pdf // Please refer to http://www.cs.cornell.edu/~ie53/publications/btcProcFC.pdf
@ -862,7 +882,7 @@ func (bc *BlockChain) WriteBlockAndState(block *types.Block, receipts []*types.R
return NonStatTy, err return NonStatTy, err
} }
// Write hash preimages // Write hash preimages
if err := WritePreimages(bc.chainDb, block.NumberU64(), state.Preimages()); err != nil { if err := WritePreimages(bc.diskdb, block.NumberU64(), state.Preimages()); err != nil {
return NonStatTy, err return NonStatTy, err
} }
status = CanonStatTy status = CanonStatTy
@ -1106,7 +1126,7 @@ func (bc *BlockChain) reorg(oldBlock, newBlock *types.Block) error {
// These logs are later announced as deleted. // These logs are later announced as deleted.
collectLogs = func(h common.Hash) { collectLogs = func(h common.Hash) {
// Coalesce logs and set 'Removed'. // Coalesce logs and set 'Removed'.
receipts := GetBlockReceipts(bc.chainDb, h, bc.hc.GetBlockNumber(h)) receipts := GetBlockReceipts(bc.diskdb, h, bc.hc.GetBlockNumber(h))
for _, receipt := range receipts { for _, receipt := range receipts {
for _, log := range receipt.Logs { for _, log := range receipt.Logs {
del := *log del := *log
@ -1175,7 +1195,7 @@ func (bc *BlockChain) reorg(oldBlock, newBlock *types.Block) error {
// insert the block in the canonical way, re-writing history // insert the block in the canonical way, re-writing history
bc.insert(newChain[i]) bc.insert(newChain[i])
// write lookup entries for hash based transaction/receipt searches // write lookup entries for hash based transaction/receipt searches
if err := WriteTxLookupEntries(bc.chainDb, newChain[i]); err != nil { if err := WriteTxLookupEntries(bc.diskdb, newChain[i]); err != nil {
return err return err
} }
addedTxs = append(addedTxs, newChain[i].Transactions()...) addedTxs = append(addedTxs, newChain[i].Transactions()...)
@ -1185,7 +1205,7 @@ func (bc *BlockChain) reorg(oldBlock, newBlock *types.Block) error {
// When transactions get deleted from the database that means the // When transactions get deleted from the database that means the
// receipts that were created in the fork must also be deleted // receipts that were created in the fork must also be deleted
for _, tx := range diff { for _, tx := range diff {
DeleteTxLookupEntry(bc.chainDb, tx.Hash()) DeleteTxLookupEntry(bc.diskdb, tx.Hash())
} }
if len(deletedLogs) > 0 { if len(deletedLogs) > 0 {
go bc.rmLogsFeed.Send(RemovedLogsEvent{deletedLogs}) go bc.rmLogsFeed.Send(RemovedLogsEvent{deletedLogs})
@ -1277,7 +1297,7 @@ Hash: 0x%x
Error: %v Error: %v
############################## ##############################
`, bc.config, block.Number(), block.Hash(), receiptString, err)) `, bc.chainConfig, block.Number(), block.Hash(), receiptString, err))
} }
// InsertHeaderChain attempts to insert the given header chain in to the local // InsertHeaderChain attempts to insert the given header chain in to the local
@ -1384,7 +1404,7 @@ func (bc *BlockChain) GetHeaderByNumber(number uint64) *types.Header {
} }
// Config retrieves the blockchain's chain configuration. // Config retrieves the blockchain's chain configuration.
func (bc *BlockChain) Config() *params.ChainConfig { return bc.config } func (bc *BlockChain) Config() *params.ChainConfig { return bc.chainConfig }
// Engine retrieves the blockchain's consensus engine. // Engine retrieves the blockchain's consensus engine.
func (bc *BlockChain) Engine() consensus.Engine { return bc.engine } func (bc *BlockChain) Engine() consensus.Engine { return bc.engine }

View file

@ -46,7 +46,7 @@ func newTestBlockChain(fake bool) *BlockChain {
if !fake { if !fake {
engine = ethash.NewTester() engine = ethash.NewTester()
} }
blockchain, err := NewBlockChain(db, gspec.Config, engine, vm.Config{}) blockchain, err := NewBlockChain(db, nil, gspec.Config, engine, vm.Config{})
if err != nil { if err != nil {
panic(err) panic(err)
} }
@ -148,8 +148,8 @@ func testBlockChainImport(chain types.Blocks, blockchain *BlockChain) error {
return err return err
} }
blockchain.mu.Lock() blockchain.mu.Lock()
WriteTd(blockchain.chainDb, block.Hash(), block.NumberU64(), new(big.Int).Add(block.Difficulty(), blockchain.GetTdByHash(block.ParentHash()))) WriteTd(blockchain.diskdb, block.Hash(), block.NumberU64(), new(big.Int).Add(block.Difficulty(), blockchain.GetTdByHash(block.ParentHash())))
WriteBlock(blockchain.chainDb, block) WriteBlock(blockchain.diskdb, block)
statedb.Commit(false) statedb.Commit(false)
blockchain.mu.Unlock() blockchain.mu.Unlock()
} }
@ -166,8 +166,8 @@ func testHeaderChainImport(chain []*types.Header, blockchain *BlockChain) error
} }
// Manually insert the header into the database, but don't reorganise (allows subsequent testing) // Manually insert the header into the database, but don't reorganise (allows subsequent testing)
blockchain.mu.Lock() blockchain.mu.Lock()
WriteTd(blockchain.chainDb, header.Hash(), header.Number.Uint64(), new(big.Int).Add(header.Difficulty, blockchain.GetTdByHash(header.ParentHash))) WriteTd(blockchain.diskdb, header.Hash(), header.Number.Uint64(), new(big.Int).Add(header.Difficulty, blockchain.GetTdByHash(header.ParentHash)))
WriteHeader(blockchain.chainDb, header) WriteHeader(blockchain.diskdb, header)
blockchain.mu.Unlock() blockchain.mu.Unlock()
} }
return nil return nil
@ -186,9 +186,9 @@ func TestLastBlock(t *testing.T) {
bchain := newTestBlockChain(false) bchain := newTestBlockChain(false)
defer bchain.Stop() defer bchain.Stop()
block := makeBlockChain(bchain.CurrentBlock(), 1, ethash.NewFaker(), bchain.chainDb, 0)[0] block := makeBlockChain(bchain.CurrentBlock(), 1, ethash.NewFaker(), bchain.diskdb, 0)[0]
bchain.insert(block) bchain.insert(block)
if block.Hash() != GetHeadBlockHash(bchain.chainDb) { if block.Hash() != GetHeadBlockHash(bchain.diskdb) {
t.Errorf("Write/Get HeadBlockHash failed") t.Errorf("Write/Get HeadBlockHash failed")
} }
} }
@ -496,7 +496,7 @@ func testReorgBadHashes(t *testing.T, full bool) {
} }
// Create a new BlockChain and check that it rolled back the state. // Create a new BlockChain and check that it rolled back the state.
ncm, err := NewBlockChain(bc.chainDb, bc.config, ethash.NewFaker(), vm.Config{}) ncm, err := NewBlockChain(bc.diskdb, nil, bc.chainConfig, ethash.NewFaker(), vm.Config{})
if err != nil { if err != nil {
t.Fatalf("failed to create new chain manager: %v", err) t.Fatalf("failed to create new chain manager: %v", err)
} }
@ -609,7 +609,7 @@ func TestFastVsFullChains(t *testing.T) {
// Import the chain as an archive node for the comparison baseline // Import the chain as an archive node for the comparison baseline
archiveDb, _ := ethdb.NewMemDatabase() archiveDb, _ := ethdb.NewMemDatabase()
gspec.MustCommit(archiveDb) gspec.MustCommit(archiveDb)
archive, _ := NewBlockChain(archiveDb, gspec.Config, ethash.NewFaker(), vm.Config{}) archive, _ := NewBlockChain(archiveDb, nil, gspec.Config, ethash.NewFaker(), vm.Config{})
defer archive.Stop() defer archive.Stop()
if n, err := archive.InsertChain(blocks); err != nil { if n, err := archive.InsertChain(blocks); err != nil {
@ -618,7 +618,7 @@ func TestFastVsFullChains(t *testing.T) {
// Fast import the chain as a non-archive node to test // Fast import the chain as a non-archive node to test
fastDb, _ := ethdb.NewMemDatabase() fastDb, _ := ethdb.NewMemDatabase()
gspec.MustCommit(fastDb) gspec.MustCommit(fastDb)
fast, _ := NewBlockChain(fastDb, gspec.Config, ethash.NewFaker(), vm.Config{}) fast, _ := NewBlockChain(fastDb, nil, gspec.Config, ethash.NewFaker(), vm.Config{})
defer fast.Stop() defer fast.Stop()
headers := make([]*types.Header, len(blocks)) headers := make([]*types.Header, len(blocks))
@ -696,7 +696,7 @@ func TestLightVsFastVsFullChainHeads(t *testing.T) {
archiveDb, _ := ethdb.NewMemDatabase() archiveDb, _ := ethdb.NewMemDatabase()
gspec.MustCommit(archiveDb) gspec.MustCommit(archiveDb)
archive, _ := NewBlockChain(archiveDb, gspec.Config, ethash.NewFaker(), vm.Config{}) archive, _ := NewBlockChain(archiveDb, nil, gspec.Config, ethash.NewFaker(), vm.Config{})
if n, err := archive.InsertChain(blocks); err != nil { if n, err := archive.InsertChain(blocks); err != nil {
t.Fatalf("failed to process block %d: %v", n, err) t.Fatalf("failed to process block %d: %v", n, err)
} }
@ -709,7 +709,7 @@ func TestLightVsFastVsFullChainHeads(t *testing.T) {
// Import the chain as a non-archive node and ensure all pointers are updated // Import the chain as a non-archive node and ensure all pointers are updated
fastDb, _ := ethdb.NewMemDatabase() fastDb, _ := ethdb.NewMemDatabase()
gspec.MustCommit(fastDb) gspec.MustCommit(fastDb)
fast, _ := NewBlockChain(fastDb, gspec.Config, ethash.NewFaker(), vm.Config{}) fast, _ := NewBlockChain(fastDb, nil, gspec.Config, ethash.NewFaker(), vm.Config{})
defer fast.Stop() defer fast.Stop()
headers := make([]*types.Header, len(blocks)) headers := make([]*types.Header, len(blocks))
@ -730,7 +730,7 @@ func TestLightVsFastVsFullChainHeads(t *testing.T) {
lightDb, _ := ethdb.NewMemDatabase() lightDb, _ := ethdb.NewMemDatabase()
gspec.MustCommit(lightDb) gspec.MustCommit(lightDb)
light, _ := NewBlockChain(lightDb, gspec.Config, ethash.NewFaker(), vm.Config{}) light, _ := NewBlockChain(lightDb, nil, gspec.Config, ethash.NewFaker(), vm.Config{})
if n, err := light.InsertHeaderChain(headers, 1); err != nil { if n, err := light.InsertHeaderChain(headers, 1); err != nil {
t.Fatalf("failed to insert header %d: %v", n, err) t.Fatalf("failed to insert header %d: %v", n, err)
} }
@ -799,7 +799,7 @@ func TestChainTxReorgs(t *testing.T) {
} }
}) })
// Import the chain. This runs all block validation rules. // Import the chain. This runs all block validation rules.
blockchain, _ := NewBlockChain(db, gspec.Config, ethash.NewFaker(), vm.Config{}) blockchain, _ := NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{})
if i, err := blockchain.InsertChain(chain); err != nil { if i, err := blockchain.InsertChain(chain); err != nil {
t.Fatalf("failed to insert original chain[%d]: %v", i, err) t.Fatalf("failed to insert original chain[%d]: %v", i, err)
} }
@ -870,7 +870,7 @@ func TestLogReorgs(t *testing.T) {
signer = types.NewEIP155Signer(gspec.Config.ChainId) signer = types.NewEIP155Signer(gspec.Config.ChainId)
) )
blockchain, _ := NewBlockChain(db, gspec.Config, ethash.NewFaker(), vm.Config{}) blockchain, _ := NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{})
defer blockchain.Stop() defer blockchain.Stop()
rmLogsCh := make(chan RemovedLogsEvent) rmLogsCh := make(chan RemovedLogsEvent)
@ -917,7 +917,7 @@ func TestReorgSideEvent(t *testing.T) {
signer = types.NewEIP155Signer(gspec.Config.ChainId) signer = types.NewEIP155Signer(gspec.Config.ChainId)
) )
blockchain, _ := NewBlockChain(db, gspec.Config, ethash.NewFaker(), vm.Config{}) blockchain, _ := NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{})
defer blockchain.Stop() defer blockchain.Stop()
chain, _ := GenerateChain(gspec.Config, genesis, ethash.NewFaker(), db, 3, func(i int, gen *BlockGen) {}) chain, _ := GenerateChain(gspec.Config, genesis, ethash.NewFaker(), db, 3, func(i int, gen *BlockGen) {})
@ -992,7 +992,7 @@ func TestCanonicalBlockRetrieval(t *testing.T) {
bc := newTestBlockChain(true) bc := newTestBlockChain(true)
defer bc.Stop() defer bc.Stop()
chain, _ := GenerateChain(bc.config, bc.genesisBlock, ethash.NewFaker(), bc.chainDb, 10, func(i int, gen *BlockGen) {}) chain, _ := GenerateChain(bc.chainConfig, bc.genesisBlock, ethash.NewFaker(), bc.diskdb, 10, func(i int, gen *BlockGen) {})
var pend sync.WaitGroup var pend sync.WaitGroup
pend.Add(len(chain)) pend.Add(len(chain))
@ -1003,14 +1003,14 @@ func TestCanonicalBlockRetrieval(t *testing.T) {
// try to retrieve a block by its canonical hash and see if the block data can be retrieved. // try to retrieve a block by its canonical hash and see if the block data can be retrieved.
for { for {
ch := GetCanonicalHash(bc.chainDb, block.NumberU64()) ch := GetCanonicalHash(bc.diskdb, block.NumberU64())
if ch == (common.Hash{}) { if ch == (common.Hash{}) {
continue // busy wait for canonical hash to be written continue // busy wait for canonical hash to be written
} }
if ch != block.Hash() { if ch != block.Hash() {
t.Fatalf("unknown canonical hash, want %s, got %s", block.Hash().Hex(), ch.Hex()) t.Fatalf("unknown canonical hash, want %s, got %s", block.Hash().Hex(), ch.Hex())
} }
fb := GetBlock(bc.chainDb, ch, block.NumberU64()) fb := GetBlock(bc.diskdb, ch, block.NumberU64())
if fb == nil { if fb == nil {
t.Fatalf("unable to retrieve block %d for canonical hash: %s", block.NumberU64(), ch.Hex()) t.Fatalf("unable to retrieve block %d for canonical hash: %s", block.NumberU64(), ch.Hex())
} }
@ -1043,7 +1043,7 @@ func TestEIP155Transition(t *testing.T) {
genesis = gspec.MustCommit(db) genesis = gspec.MustCommit(db)
) )
blockchain, _ := NewBlockChain(db, gspec.Config, ethash.NewFaker(), vm.Config{}) blockchain, _ := NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{})
defer blockchain.Stop() defer blockchain.Stop()
blocks, _ := GenerateChain(gspec.Config, genesis, ethash.NewFaker(), db, 4, func(i int, block *BlockGen) { blocks, _ := GenerateChain(gspec.Config, genesis, ethash.NewFaker(), db, 4, func(i int, block *BlockGen) {
@ -1151,7 +1151,7 @@ func TestEIP161AccountRemoval(t *testing.T) {
} }
genesis = gspec.MustCommit(db) genesis = gspec.MustCommit(db)
) )
blockchain, _ := NewBlockChain(db, gspec.Config, ethash.NewFaker(), vm.Config{}) blockchain, _ := NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{})
defer blockchain.Stop() defer blockchain.Stop()
blocks, _ := GenerateChain(gspec.Config, genesis, ethash.NewFaker(), db, 3, func(i int, block *BlockGen) { blocks, _ := GenerateChain(gspec.Config, genesis, ethash.NewFaker(), db, 3, func(i int, block *BlockGen) {
@ -1226,7 +1226,7 @@ func TestBlockchainHeaderchainReorgConsistency(t *testing.T) {
diskdb, _ := ethdb.NewMemDatabase() diskdb, _ := ethdb.NewMemDatabase()
new(Genesis).MustCommit(diskdb) new(Genesis).MustCommit(diskdb)
chain, err := NewBlockChain(diskdb, params.TestChainConfig, engine, vm.Config{}) chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{})
if err != nil { if err != nil {
t.Fatalf("failed to create tester chain: %v", err) t.Fatalf("failed to create tester chain: %v", err)
} }

View file

@ -169,7 +169,7 @@ func GenerateChain(config *params.ChainConfig, parent *types.Block, engine conse
genblock := func(i int, parent *types.Block, statedb *state.StateDB) (*types.Block, types.Receipts) { genblock := func(i int, parent *types.Block, statedb *state.StateDB) (*types.Block, types.Receipts) {
// TODO(karalabe): This is needed for clique, which depends on multiple blocks. // TODO(karalabe): This is needed for clique, which depends on multiple blocks.
// It's nonetheless ugly to spin up a blockchain here. Get rid of this somehow. // It's nonetheless ugly to spin up a blockchain here. Get rid of this somehow.
blockchain, _ := NewBlockChain(db, config, engine, vm.Config{}) blockchain, _ := NewBlockChain(db, nil, config, engine, vm.Config{})
defer blockchain.Stop() defer blockchain.Stop()
b := &BlockGen{i: i, parent: parent, chain: blocks, chainReader: blockchain, statedb: statedb, config: config, engine: engine} b := &BlockGen{i: i, parent: parent, chain: blocks, chainReader: blockchain, statedb: statedb, config: config, engine: engine}
@ -199,7 +199,7 @@ func GenerateChain(config *params.ChainConfig, parent *types.Block, engine conse
if err != nil { if err != nil {
panic(fmt.Sprintf("state write error: %v", err)) panic(fmt.Sprintf("state write error: %v", err))
} }
if err := triedb.Commit(root, db); err != nil { if err := triedb.Commit(root); err != nil {
panic(fmt.Sprintf("trie write error: %v", err)) panic(fmt.Sprintf("trie write error: %v", err))
} }
return block, b.receipts return block, b.receipts
@ -252,7 +252,7 @@ func newCanonical(engine consensus.Engine, n int, full bool) (ethdb.Database, *B
db, _ := ethdb.NewMemDatabase() db, _ := ethdb.NewMemDatabase()
genesis := gspec.MustCommit(db) genesis := gspec.MustCommit(db)
blockchain, _ := NewBlockChain(db, params.AllEthashProtocolChanges, engine, vm.Config{}) blockchain, _ := NewBlockChain(db, nil, params.AllEthashProtocolChanges, engine, vm.Config{})
// Create and inject the requested chain // Create and inject the requested chain
if n == 0 { if n == 0 {
return db, blockchain, nil return db, blockchain, nil

View file

@ -79,7 +79,7 @@ func ExampleGenerateChain() {
}) })
// Import the chain. This runs all block validation rules. // Import the chain. This runs all block validation rules.
blockchain, _ := NewBlockChain(db, gspec.Config, ethash.NewFaker(), vm.Config{}) blockchain, _ := NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{})
defer blockchain.Stop() defer blockchain.Stop()
if i, err := blockchain.InsertChain(chain); err != nil { if i, err := blockchain.InsertChain(chain); err != nil {

View file

@ -45,7 +45,7 @@ func TestDAOForkRangeExtradata(t *testing.T) {
proConf.DAOForkBlock = forkBlock proConf.DAOForkBlock = forkBlock
proConf.DAOForkSupport = true proConf.DAOForkSupport = true
proBc, _ := NewBlockChain(proDb, &proConf, ethash.NewFaker(), vm.Config{}) proBc, _ := NewBlockChain(proDb, nil, &proConf, ethash.NewFaker(), vm.Config{})
defer proBc.Stop() defer proBc.Stop()
conDb, _ := ethdb.NewMemDatabase() conDb, _ := ethdb.NewMemDatabase()
@ -55,7 +55,7 @@ func TestDAOForkRangeExtradata(t *testing.T) {
conConf.DAOForkBlock = forkBlock conConf.DAOForkBlock = forkBlock
conConf.DAOForkSupport = false conConf.DAOForkSupport = false
conBc, _ := NewBlockChain(conDb, &conConf, ethash.NewFaker(), vm.Config{}) conBc, _ := NewBlockChain(conDb, nil, &conConf, ethash.NewFaker(), vm.Config{})
defer conBc.Stop() defer conBc.Stop()
if _, err := proBc.InsertChain(prefix); err != nil { if _, err := proBc.InsertChain(prefix); err != nil {
@ -69,7 +69,7 @@ func TestDAOForkRangeExtradata(t *testing.T) {
// Create a pro-fork block, and try to feed into the no-fork chain // Create a pro-fork block, and try to feed into the no-fork chain
db, _ = ethdb.NewMemDatabase() db, _ = ethdb.NewMemDatabase()
gspec.MustCommit(db) gspec.MustCommit(db)
bc, _ := NewBlockChain(db, &conConf, ethash.NewFaker(), vm.Config{}) bc, _ := NewBlockChain(db, nil, &conConf, ethash.NewFaker(), vm.Config{})
defer bc.Stop() defer bc.Stop()
blocks := conBc.GetBlocksFromHash(conBc.CurrentBlock().Hash(), int(conBc.CurrentBlock().NumberU64())) blocks := conBc.GetBlocksFromHash(conBc.CurrentBlock().Hash(), int(conBc.CurrentBlock().NumberU64()))
@ -79,7 +79,7 @@ func TestDAOForkRangeExtradata(t *testing.T) {
if _, err := bc.InsertChain(blocks); err != nil { if _, err := bc.InsertChain(blocks); err != nil {
t.Fatalf("failed to import contra-fork chain for expansion: %v", err) t.Fatalf("failed to import contra-fork chain for expansion: %v", err)
} }
if err := bc.stateCache.TrieDB().Commit(bc.CurrentHeader().Root, db); err != nil { if err := bc.stateCache.TrieDB().Commit(bc.CurrentHeader().Root); err != nil {
t.Fatalf("failed to commit contra-fork head for expansion: %v", err) t.Fatalf("failed to commit contra-fork head for expansion: %v", err)
} }
blocks, _ = GenerateChain(&proConf, conBc.CurrentBlock(), ethash.NewFaker(), db, 1, func(i int, gen *BlockGen) {}) blocks, _ = GenerateChain(&proConf, conBc.CurrentBlock(), ethash.NewFaker(), db, 1, func(i int, gen *BlockGen) {})
@ -94,7 +94,7 @@ func TestDAOForkRangeExtradata(t *testing.T) {
// Create a no-fork block, and try to feed into the pro-fork chain // Create a no-fork block, and try to feed into the pro-fork chain
db, _ = ethdb.NewMemDatabase() db, _ = ethdb.NewMemDatabase()
gspec.MustCommit(db) gspec.MustCommit(db)
bc, _ = NewBlockChain(db, &proConf, ethash.NewFaker(), vm.Config{}) bc, _ = NewBlockChain(db, nil, &proConf, ethash.NewFaker(), vm.Config{})
defer bc.Stop() defer bc.Stop()
blocks = proBc.GetBlocksFromHash(proBc.CurrentBlock().Hash(), int(proBc.CurrentBlock().NumberU64())) blocks = proBc.GetBlocksFromHash(proBc.CurrentBlock().Hash(), int(proBc.CurrentBlock().NumberU64()))
@ -104,7 +104,7 @@ func TestDAOForkRangeExtradata(t *testing.T) {
if _, err := bc.InsertChain(blocks); err != nil { if _, err := bc.InsertChain(blocks); err != nil {
t.Fatalf("failed to import pro-fork chain for expansion: %v", err) t.Fatalf("failed to import pro-fork chain for expansion: %v", err)
} }
if err := bc.stateCache.TrieDB().Commit(bc.CurrentHeader().Root, db); err != nil { if err := bc.stateCache.TrieDB().Commit(bc.CurrentHeader().Root); err != nil {
t.Fatalf("failed to commit pro-fork head for expansion: %v", err) t.Fatalf("failed to commit pro-fork head for expansion: %v", err)
} }
blocks, _ = GenerateChain(&conConf, proBc.CurrentBlock(), ethash.NewFaker(), db, 1, func(i int, gen *BlockGen) {}) blocks, _ = GenerateChain(&conConf, proBc.CurrentBlock(), ethash.NewFaker(), db, 1, func(i int, gen *BlockGen) {})
@ -120,7 +120,7 @@ func TestDAOForkRangeExtradata(t *testing.T) {
// Verify that contra-forkers accept pro-fork extra-datas after forking finishes // Verify that contra-forkers accept pro-fork extra-datas after forking finishes
db, _ = ethdb.NewMemDatabase() db, _ = ethdb.NewMemDatabase()
gspec.MustCommit(db) gspec.MustCommit(db)
bc, _ := NewBlockChain(db, &conConf, ethash.NewFaker(), vm.Config{}) bc, _ := NewBlockChain(db, nil, &conConf, ethash.NewFaker(), vm.Config{})
defer bc.Stop() defer bc.Stop()
blocks := conBc.GetBlocksFromHash(conBc.CurrentBlock().Hash(), int(conBc.CurrentBlock().NumberU64())) blocks := conBc.GetBlocksFromHash(conBc.CurrentBlock().Hash(), int(conBc.CurrentBlock().NumberU64()))
@ -130,7 +130,7 @@ func TestDAOForkRangeExtradata(t *testing.T) {
if _, err := bc.InsertChain(blocks); err != nil { if _, err := bc.InsertChain(blocks); err != nil {
t.Fatalf("failed to import contra-fork chain for expansion: %v", err) t.Fatalf("failed to import contra-fork chain for expansion: %v", err)
} }
if err := bc.stateCache.TrieDB().Commit(bc.CurrentHeader().Root, db); err != nil { if err := bc.stateCache.TrieDB().Commit(bc.CurrentHeader().Root); err != nil {
t.Fatalf("failed to commit contra-fork head for expansion: %v", err) t.Fatalf("failed to commit contra-fork head for expansion: %v", err)
} }
blocks, _ = GenerateChain(&proConf, conBc.CurrentBlock(), ethash.NewFaker(), db, 1, func(i int, gen *BlockGen) {}) blocks, _ = GenerateChain(&proConf, conBc.CurrentBlock(), ethash.NewFaker(), db, 1, func(i int, gen *BlockGen) {})
@ -140,7 +140,7 @@ func TestDAOForkRangeExtradata(t *testing.T) {
// Verify that pro-forkers accept contra-fork extra-datas after forking finishes // Verify that pro-forkers accept contra-fork extra-datas after forking finishes
db, _ = ethdb.NewMemDatabase() db, _ = ethdb.NewMemDatabase()
gspec.MustCommit(db) gspec.MustCommit(db)
bc, _ = NewBlockChain(db, &proConf, ethash.NewFaker(), vm.Config{}) bc, _ = NewBlockChain(db, nil, &proConf, ethash.NewFaker(), vm.Config{})
defer bc.Stop() defer bc.Stop()
blocks = proBc.GetBlocksFromHash(proBc.CurrentBlock().Hash(), int(proBc.CurrentBlock().NumberU64())) blocks = proBc.GetBlocksFromHash(proBc.CurrentBlock().Hash(), int(proBc.CurrentBlock().NumberU64()))
@ -150,7 +150,7 @@ func TestDAOForkRangeExtradata(t *testing.T) {
if _, err := bc.InsertChain(blocks); err != nil { if _, err := bc.InsertChain(blocks); err != nil {
t.Fatalf("failed to import pro-fork chain for expansion: %v", err) t.Fatalf("failed to import pro-fork chain for expansion: %v", err)
} }
if err := bc.stateCache.TrieDB().Commit(bc.CurrentHeader().Root, db); err != nil { if err := bc.stateCache.TrieDB().Commit(bc.CurrentHeader().Root); err != nil {
t.Fatalf("failed to commit pro-fork head for expansion: %v", err) t.Fatalf("failed to commit pro-fork head for expansion: %v", err)
} }
blocks, _ = GenerateChain(&conConf, proBc.CurrentBlock(), ethash.NewFaker(), db, 1, func(i int, gen *BlockGen) {}) blocks, _ = GenerateChain(&conConf, proBc.CurrentBlock(), ethash.NewFaker(), db, 1, func(i int, gen *BlockGen) {})

View file

@ -170,10 +170,9 @@ func SetupGenesisBlock(db ethdb.Database, genesis *Genesis) (*params.ChainConfig
// Check whether the genesis block is already written. // Check whether the genesis block is already written.
if genesis != nil { if genesis != nil {
block, _, _ := genesis.ToBlock() hash := genesis.ToBlock(nil).Hash()
hash := block.Hash()
if hash != stored { if hash != stored {
return genesis.Config, block.Hash(), &GenesisMismatchError{stored, hash} return genesis.Config, hash, &GenesisMismatchError{stored, hash}
} }
} }
@ -221,10 +220,13 @@ func (g *Genesis) configOrDefault(ghash common.Hash) *params.ChainConfig {
} }
} }
// ToBlock creates the block and state of a genesis specification. // ToBlock creates the genesis block and writes state of a genesis specification
func (g *Genesis) ToBlock() (*types.Block, *state.StateDB, *trie.Database) { // to the given database (or discards it if nil).
diskdb, _ := ethdb.NewMemDatabase() func (g *Genesis) ToBlock(db ethdb.Database) *types.Block {
triedb := trie.NewDatabase(diskdb) if db == nil {
db, _ = ethdb.NewMemDatabase()
}
triedb := trie.NewDatabase(db)
statedb, _ := state.New(common.Hash{}, state.NewDatabase(triedb)) statedb, _ := state.New(common.Hash{}, state.NewDatabase(triedb))
for addr, account := range g.Alloc { for addr, account := range g.Alloc {
statedb.AddBalance(addr, account.Balance) statedb.AddBalance(addr, account.Balance)
@ -254,22 +256,19 @@ func (g *Genesis) ToBlock() (*types.Block, *state.StateDB, *trie.Database) {
if g.Difficulty == nil { if g.Difficulty == nil {
head.Difficulty = params.GenesisDifficulty head.Difficulty = params.GenesisDifficulty
} }
return types.NewBlock(head, nil, nil, nil), statedb, triedb statedb.Commit(false)
triedb.Commit(root)
return types.NewBlock(head, nil, nil, nil)
} }
// Commit writes the block and state of a genesis specification to the database. // Commit writes the block and state of a genesis specification to the database.
// The block is committed as the canonical head block. // The block is committed as the canonical head block.
func (g *Genesis) Commit(db ethdb.Database) (*types.Block, error) { func (g *Genesis) Commit(db ethdb.Database) (*types.Block, error) {
block, statedb, triedb := g.ToBlock() block := g.ToBlock(db)
if block.Number().Sign() != 0 { if block.Number().Sign() != 0 {
return nil, fmt.Errorf("can't commit genesis block with number > 0") return nil, fmt.Errorf("can't commit genesis block with number > 0")
} }
if _, err := statedb.Commit(false); err != nil {
return nil, fmt.Errorf("cannot write state: %v", err)
}
if err := triedb.Commit(block.Root(), db); err != nil {
return nil, err
}
if err := WriteTd(db, block.Hash(), block.NumberU64(), g.Difficulty); err != nil { if err := WriteTd(db, block.Hash(), block.NumberU64(), g.Difficulty); err != nil {
return nil, err return nil, err
} }

View file

@ -30,11 +30,11 @@ import (
) )
func TestDefaultGenesisBlock(t *testing.T) { func TestDefaultGenesisBlock(t *testing.T) {
block, _, _ := DefaultGenesisBlock().ToBlock() block := DefaultGenesisBlock().ToBlock(nil)
if block.Hash() != params.MainnetGenesisHash { if block.Hash() != params.MainnetGenesisHash {
t.Errorf("wrong mainnet genesis hash, got %v, want %v", block.Hash(), params.MainnetGenesisHash) t.Errorf("wrong mainnet genesis hash, got %v, want %v", block.Hash(), params.MainnetGenesisHash)
} }
block, _, _ = DefaultTestnetGenesisBlock().ToBlock() block = DefaultTestnetGenesisBlock().ToBlock(nil)
if block.Hash() != params.TestnetGenesisHash { if block.Hash() != params.TestnetGenesisHash {
t.Errorf("wrong testnet genesis hash, got %v, want %v", block.Hash(), params.TestnetGenesisHash) t.Errorf("wrong testnet genesis hash, got %v, want %v", block.Hash(), params.TestnetGenesisHash)
} }
@ -118,7 +118,7 @@ func TestSetupGenesis(t *testing.T) {
// Commit the 'old' genesis block with Homestead transition at #2. // Commit the 'old' genesis block with Homestead transition at #2.
// Advance to block #4, past the homestead transition block of customg. // Advance to block #4, past the homestead transition block of customg.
genesis := oldcustomg.MustCommit(db) genesis := oldcustomg.MustCommit(db)
bc, _ := NewBlockChain(db, oldcustomg.Config, ethash.NewFullFaker(), vm.Config{}) bc, _ := NewBlockChain(db, nil, oldcustomg.Config, ethash.NewFullFaker(), vm.Config{})
defer bc.Stop() defer bc.Stop()
bc.SetValidator(bproc{}) bc.SetValidator(bproc{})
bc.InsertChain(makeBlockChainWithDiff(genesis, []int{2, 3, 4, 5}, 0)) bc.InsertChain(makeBlockChainWithDiff(genesis, []int{2, 3, 4, 5}, 0))

View file

@ -144,9 +144,11 @@ func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) {
} }
core.WriteBlockChainVersion(chainDb, core.BlockChainVersion) core.WriteBlockChainVersion(chainDb, core.BlockChainVersion)
} }
var (
vmConfig := vm.Config{EnablePreimageRecording: config.EnablePreimageRecording} vmConfig = vm.Config{EnablePreimageRecording: config.EnablePreimageRecording}
eth.blockchain, err = core.NewBlockChain(chainDb, eth.chainConfig, eth.engine, vmConfig) cacheConfig = &core.CacheConfig{TrieNodeLimit: config.TrieCache, TrieTimeLimit: config.TrieTimeout}
)
eth.blockchain, err = core.NewBlockChain(chainDb, cacheConfig, eth.chainConfig, eth.engine, vmConfig)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View file

@ -22,6 +22,7 @@ import (
"os/user" "os/user"
"path/filepath" "path/filepath"
"runtime" "runtime"
"time"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/common/hexutil"
@ -44,7 +45,9 @@ var DefaultConfig = Config{
}, },
NetworkId: 1, NetworkId: 1,
LightPeers: 100, LightPeers: 100,
DatabaseCache: 128, DatabaseCache: 768,
TrieCache: 256,
TrieTimeout: 5 * time.Minute,
GasPrice: big.NewInt(18 * params.Shannon), GasPrice: big.NewInt(18 * params.Shannon),
TxPool: core.DefaultTxPoolConfig, TxPool: core.DefaultTxPoolConfig,
@ -87,6 +90,8 @@ type Config struct {
SkipBcVersionCheck bool `toml:"-"` SkipBcVersionCheck bool `toml:"-"`
DatabaseHandles int `toml:"-"` DatabaseHandles int `toml:"-"`
DatabaseCache int DatabaseCache int
TrieCache int
TrieTimeout time.Duration
// Mining-related options // Mining-related options
Etherbase common.Address `toml:",omitempty"` Etherbase common.Address `toml:",omitempty"`

View file

@ -71,7 +71,6 @@ type ProtocolManager struct {
txpool txPool txpool txPool
blockchain *core.BlockChain blockchain *core.BlockChain
chaindb ethdb.Database
chainconfig *params.ChainConfig chainconfig *params.ChainConfig
maxPeers int maxPeers int
@ -106,7 +105,6 @@ func NewProtocolManager(config *params.ChainConfig, mode downloader.SyncMode, ne
eventMux: mux, eventMux: mux,
txpool: txpool, txpool: txpool,
blockchain: blockchain, blockchain: blockchain,
chaindb: chaindb,
chainconfig: config, chainconfig: config,
peers: newPeerSet(), peers: newPeerSet(),
newPeerCh: make(chan *peer), newPeerCh: make(chan *peer),
@ -538,7 +536,7 @@ func (pm *ProtocolManager) handleMsg(p *peer) error {
return errResp(ErrDecode, "msg %v: %v", msg, err) return errResp(ErrDecode, "msg %v: %v", msg, err)
} }
// Retrieve the requested state entry, stopping if enough was found // Retrieve the requested state entry, stopping if enough was found
if entry, err := pm.chaindb.Get(hash.Bytes()); err == nil { if entry, err := pm.blockchain.TrieNode(hash); err == nil {
data = append(data, entry) data = append(data, entry)
bytes += len(entry) bytes += len(entry)
} }
@ -576,7 +574,7 @@ func (pm *ProtocolManager) handleMsg(p *peer) error {
return errResp(ErrDecode, "msg %v: %v", msg, err) return errResp(ErrDecode, "msg %v: %v", msg, err)
} }
// Retrieve the requested block's receipts, skipping if unknown to us // Retrieve the requested block's receipts, skipping if unknown to us
results := core.GetBlockReceipts(pm.chaindb, hash, core.GetBlockNumber(pm.chaindb, hash)) results := pm.blockchain.GetReceiptsByHash(hash)
if results == nil { if results == nil {
if header := pm.blockchain.GetHeaderByHash(hash); header == nil || header.ReceiptHash != types.EmptyRootHash { if header := pm.blockchain.GetHeaderByHash(hash); header == nil || header.ReceiptHash != types.EmptyRootHash {
continue continue

View file

@ -57,7 +57,7 @@ func TestProtocolCompatibility(t *testing.T) {
for i, tt := range tests { for i, tt := range tests {
ProtocolVersions = []uint{tt.version} ProtocolVersions = []uint{tt.version}
pm, err := newTestProtocolManager(tt.mode, 0, nil, nil) pm, _, err := newTestProtocolManager(tt.mode, 0, nil, nil)
if pm != nil { if pm != nil {
defer pm.Stop() defer pm.Stop()
} }
@ -72,7 +72,7 @@ func TestGetBlockHeaders62(t *testing.T) { testGetBlockHeaders(t, 62) }
func TestGetBlockHeaders63(t *testing.T) { testGetBlockHeaders(t, 63) } func TestGetBlockHeaders63(t *testing.T) { testGetBlockHeaders(t, 63) }
func testGetBlockHeaders(t *testing.T, protocol int) { func testGetBlockHeaders(t *testing.T, protocol int) {
pm := newTestProtocolManagerMust(t, downloader.FullSync, downloader.MaxHashFetch+15, nil, nil) pm, _ := newTestProtocolManagerMust(t, downloader.FullSync, downloader.MaxHashFetch+15, nil, nil)
peer, _ := newTestPeer("peer", protocol, pm, true) peer, _ := newTestPeer("peer", protocol, pm, true)
defer peer.close() defer peer.close()
@ -231,7 +231,7 @@ func TestGetBlockBodies62(t *testing.T) { testGetBlockBodies(t, 62) }
func TestGetBlockBodies63(t *testing.T) { testGetBlockBodies(t, 63) } func TestGetBlockBodies63(t *testing.T) { testGetBlockBodies(t, 63) }
func testGetBlockBodies(t *testing.T, protocol int) { func testGetBlockBodies(t *testing.T, protocol int) {
pm := newTestProtocolManagerMust(t, downloader.FullSync, downloader.MaxBlockFetch+15, nil, nil) pm, _ := newTestProtocolManagerMust(t, downloader.FullSync, downloader.MaxBlockFetch+15, nil, nil)
peer, _ := newTestPeer("peer", protocol, pm, true) peer, _ := newTestPeer("peer", protocol, pm, true)
defer peer.close() defer peer.close()
@ -338,13 +338,13 @@ func testGetNodeData(t *testing.T, protocol int) {
} }
} }
// Assemble the test environment // Assemble the test environment
pm := newTestProtocolManagerMust(t, downloader.FullSync, 4, generator, nil) pm, db := newTestProtocolManagerMust(t, downloader.FullSync, 4, generator, nil)
peer, _ := newTestPeer("peer", protocol, pm, true) peer, _ := newTestPeer("peer", protocol, pm, true)
defer peer.close() defer peer.close()
// Fetch for now the entire chain db // Fetch for now the entire chain db
hashes := []common.Hash{} hashes := []common.Hash{}
for _, key := range pm.chaindb.(*ethdb.MemDatabase).Keys() { for _, key := range db.Keys() {
if len(key) == len(common.Hash{}) { if len(key) == len(common.Hash{}) {
hashes = append(hashes, common.BytesToHash(key)) hashes = append(hashes, common.BytesToHash(key))
} }
@ -430,7 +430,7 @@ func testGetReceipt(t *testing.T, protocol int) {
} }
} }
// Assemble the test environment // Assemble the test environment
pm := newTestProtocolManagerMust(t, downloader.FullSync, 4, generator, nil) pm, _ := newTestProtocolManagerMust(t, downloader.FullSync, 4, generator, nil)
peer, _ := newTestPeer("peer", protocol, pm, true) peer, _ := newTestPeer("peer", protocol, pm, true)
defer peer.close() defer peer.close()
@ -440,7 +440,7 @@ func testGetReceipt(t *testing.T, protocol int) {
block := pm.blockchain.GetBlockByNumber(i) block := pm.blockchain.GetBlockByNumber(i)
hashes = append(hashes, block.Hash()) hashes = append(hashes, block.Hash())
receipts = append(receipts, core.GetBlockReceipts(pm.chaindb, block.Hash(), block.NumberU64())) receipts = append(receipts, pm.blockchain.GetReceiptsByHash(block.Hash()))
} }
// Send the hash request and verify the response // Send the hash request and verify the response
p2p.Send(peer.app, 0x0f, hashes) p2p.Send(peer.app, 0x0f, hashes)
@ -473,7 +473,7 @@ func testDAOChallenge(t *testing.T, localForked, remoteForked bool, timeout bool
config = &params.ChainConfig{DAOForkBlock: big.NewInt(1), DAOForkSupport: localForked} config = &params.ChainConfig{DAOForkBlock: big.NewInt(1), DAOForkSupport: localForked}
gspec = &core.Genesis{Config: config} gspec = &core.Genesis{Config: config}
genesis = gspec.MustCommit(db) genesis = gspec.MustCommit(db)
blockchain, _ = core.NewBlockChain(db, config, pow, vm.Config{}) blockchain, _ = core.NewBlockChain(db, nil, config, pow, vm.Config{})
) )
pm, err := NewProtocolManager(config, downloader.FullSync, DefaultConfig.NetworkId, evmux, new(testTxPool), pow, blockchain, db) pm, err := NewProtocolManager(config, downloader.FullSync, DefaultConfig.NetworkId, evmux, new(testTxPool), pow, blockchain, db)
if err != nil { if err != nil {

View file

@ -49,7 +49,7 @@ var (
// newTestProtocolManager creates a new protocol manager for testing purposes, // newTestProtocolManager creates a new protocol manager for testing purposes,
// with the given number of blocks already known, and potential notification // with the given number of blocks already known, and potential notification
// channels for different events. // channels for different events.
func newTestProtocolManager(mode downloader.SyncMode, blocks int, generator func(int, *core.BlockGen), newtx chan<- []*types.Transaction) (*ProtocolManager, error) { func newTestProtocolManager(mode downloader.SyncMode, blocks int, generator func(int, *core.BlockGen), newtx chan<- []*types.Transaction) (*ProtocolManager, *ethdb.MemDatabase, error) {
var ( var (
evmux = new(event.TypeMux) evmux = new(event.TypeMux)
engine = ethash.NewFaker() engine = ethash.NewFaker()
@ -59,7 +59,7 @@ func newTestProtocolManager(mode downloader.SyncMode, blocks int, generator func
Alloc: core.GenesisAlloc{testBank: {Balance: big.NewInt(1000000)}}, Alloc: core.GenesisAlloc{testBank: {Balance: big.NewInt(1000000)}},
} }
genesis = gspec.MustCommit(db) genesis = gspec.MustCommit(db)
blockchain, _ = core.NewBlockChain(db, gspec.Config, engine, vm.Config{}) blockchain, _ = core.NewBlockChain(db, nil, gspec.Config, engine, vm.Config{})
) )
chain, _ := core.GenerateChain(gspec.Config, genesis, ethash.NewFaker(), db, blocks, generator) chain, _ := core.GenerateChain(gspec.Config, genesis, ethash.NewFaker(), db, blocks, generator)
if _, err := blockchain.InsertChain(chain); err != nil { if _, err := blockchain.InsertChain(chain); err != nil {
@ -68,22 +68,22 @@ func newTestProtocolManager(mode downloader.SyncMode, blocks int, generator func
pm, err := NewProtocolManager(gspec.Config, mode, DefaultConfig.NetworkId, evmux, &testTxPool{added: newtx}, engine, blockchain, db) pm, err := NewProtocolManager(gspec.Config, mode, DefaultConfig.NetworkId, evmux, &testTxPool{added: newtx}, engine, blockchain, db)
if err != nil { if err != nil {
return nil, err return nil, nil, err
} }
pm.Start(1000) pm.Start(1000)
return pm, nil return pm, db, nil
} }
// newTestProtocolManagerMust creates a new protocol manager for testing purposes, // newTestProtocolManagerMust creates a new protocol manager for testing purposes,
// with the given number of blocks already known, and potential notification // with the given number of blocks already known, and potential notification
// channels for different events. In case of an error, the constructor force- // channels for different events. In case of an error, the constructor force-
// fails the test. // fails the test.
func newTestProtocolManagerMust(t *testing.T, mode downloader.SyncMode, blocks int, generator func(int, *core.BlockGen), newtx chan<- []*types.Transaction) *ProtocolManager { func newTestProtocolManagerMust(t *testing.T, mode downloader.SyncMode, blocks int, generator func(int, *core.BlockGen), newtx chan<- []*types.Transaction) (*ProtocolManager, *ethdb.MemDatabase) {
pm, err := newTestProtocolManager(mode, blocks, generator, newtx) pm, db, err := newTestProtocolManager(mode, blocks, generator, newtx)
if err != nil { if err != nil {
t.Fatalf("Failed to create protocol manager: %v", err) t.Fatalf("Failed to create protocol manager: %v", err)
} }
return pm return pm, db
} }
// testTxPool is a fake, helper transaction pool for testing purposes // testTxPool is a fake, helper transaction pool for testing purposes

View file

@ -41,7 +41,7 @@ func TestStatusMsgErrors62(t *testing.T) { testStatusMsgErrors(t, 62) }
func TestStatusMsgErrors63(t *testing.T) { testStatusMsgErrors(t, 63) } func TestStatusMsgErrors63(t *testing.T) { testStatusMsgErrors(t, 63) }
func testStatusMsgErrors(t *testing.T, protocol int) { func testStatusMsgErrors(t *testing.T, protocol int) {
pm := newTestProtocolManagerMust(t, downloader.FullSync, 0, nil, nil) pm, _ := newTestProtocolManagerMust(t, downloader.FullSync, 0, nil, nil)
var ( var (
genesis = pm.blockchain.Genesis() genesis = pm.blockchain.Genesis()
head = pm.blockchain.CurrentHeader() head = pm.blockchain.CurrentHeader()
@ -98,7 +98,7 @@ func TestRecvTransactions63(t *testing.T) { testRecvTransactions(t, 63) }
func testRecvTransactions(t *testing.T, protocol int) { func testRecvTransactions(t *testing.T, protocol int) {
txAdded := make(chan []*types.Transaction) txAdded := make(chan []*types.Transaction)
pm := newTestProtocolManagerMust(t, downloader.FullSync, 0, nil, txAdded) pm, _ := newTestProtocolManagerMust(t, downloader.FullSync, 0, nil, txAdded)
pm.acceptTxs = 1 // mark synced to accept transactions pm.acceptTxs = 1 // mark synced to accept transactions
p, _ := newTestPeer("peer", protocol, pm, true) p, _ := newTestPeer("peer", protocol, pm, true)
defer pm.Stop() defer pm.Stop()
@ -125,7 +125,7 @@ func TestSendTransactions62(t *testing.T) { testSendTransactions(t, 62) }
func TestSendTransactions63(t *testing.T) { testSendTransactions(t, 63) } func TestSendTransactions63(t *testing.T) { testSendTransactions(t, 63) }
func testSendTransactions(t *testing.T, protocol int) { func testSendTransactions(t *testing.T, protocol int) {
pm := newTestProtocolManagerMust(t, downloader.FullSync, 0, nil, nil) pm, _ := newTestProtocolManagerMust(t, downloader.FullSync, 0, nil, nil)
defer pm.Stop() defer pm.Stop()
// Fill the pool with big transactions. // Fill the pool with big transactions.

View file

@ -30,12 +30,12 @@ import (
// imported into the blockchain. // imported into the blockchain.
func TestFastSyncDisabling(t *testing.T) { func TestFastSyncDisabling(t *testing.T) {
// Create a pristine protocol manager, check that fast sync is left enabled // Create a pristine protocol manager, check that fast sync is left enabled
pmEmpty := newTestProtocolManagerMust(t, downloader.FastSync, 0, nil, nil) pmEmpty, _ := newTestProtocolManagerMust(t, downloader.FastSync, 0, nil, nil)
if atomic.LoadUint32(&pmEmpty.fastSync) == 0 { if atomic.LoadUint32(&pmEmpty.fastSync) == 0 {
t.Fatalf("fast sync disabled on pristine blockchain") t.Fatalf("fast sync disabled on pristine blockchain")
} }
// Create a full protocol manager, check that fast sync gets disabled // Create a full protocol manager, check that fast sync gets disabled
pmFull := newTestProtocolManagerMust(t, downloader.FastSync, 1024, nil, nil) pmFull, _ := newTestProtocolManagerMust(t, downloader.FastSync, 1024, nil, nil)
if atomic.LoadUint32(&pmFull.fastSync) == 1 { if atomic.LoadUint32(&pmFull.fastSync) == 1 {
t.Fatalf("fast sync not disabled on non-empty blockchain") t.Fatalf("fast sync not disabled on non-empty blockchain")
} }

View file

@ -146,7 +146,7 @@ func newTestProtocolManager(lightSync bool, blocks int, generator func(int, *cor
if lightSync { if lightSync {
chain, _ = light.NewLightChain(odr, gspec.Config, engine) chain, _ = light.NewLightChain(odr, gspec.Config, engine)
} else { } else {
blockchain, _ := core.NewBlockChain(db, gspec.Config, engine, vm.Config{}) blockchain, _ := core.NewBlockChain(db, nil, gspec.Config, engine, vm.Config{})
gchain, _ := core.GenerateChain(gspec.Config, genesis, ethash.NewFaker(), db, blocks, generator) gchain, _ := core.GenerateChain(gspec.Config, genesis, ethash.NewFaker(), db, blocks, generator)
if _, err := blockchain.InsertChain(gchain); err != nil { if _, err := blockchain.InsertChain(gchain); err != nil {
panic(err) panic(err)

View file

@ -22,8 +22,8 @@ import (
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/trie"
) )
// NodeSet stores a set of trie nodes. It implements trie.Database and can also // NodeSet stores a set of trie nodes. It implements trie.Database and can also
@ -99,7 +99,7 @@ func (db *NodeSet) NodeList() NodeList {
} }
// Store writes the contents of the set to the given database // Store writes the contents of the set to the given database
func (db *NodeSet) Store(target trie.DatabaseWriter) { func (db *NodeSet) Store(target ethdb.Putter) {
db.lock.RLock() db.lock.RLock()
defer db.lock.RUnlock() defer db.lock.RUnlock()
@ -108,11 +108,11 @@ func (db *NodeSet) Store(target trie.DatabaseWriter) {
} }
} }
// NodeList stores an ordered list of trie nodes. It implements trie.DatabaseWriter. // NodeList stores an ordered list of trie nodes. It implements ethdb.Putter.
type NodeList []rlp.RawValue type NodeList []rlp.RawValue
// Store writes the contents of the list to the given database // Store writes the contents of the list to the given database
func (n NodeList) Store(db trie.DatabaseWriter) { func (n NodeList) Store(db ethdb.Putter) {
for _, node := range n { for _, node := range n {
db.Put(crypto.Keccak256(node), node) db.Put(crypto.Keccak256(node), node)
} }

View file

@ -239,7 +239,7 @@ func testChainOdr(t *testing.T, protocol int, fn odrTestFn) {
) )
gspec.MustCommit(ldb) gspec.MustCommit(ldb)
// Assemble the test environment // Assemble the test environment
blockchain, _ := core.NewBlockChain(sdb, params.TestChainConfig, ethash.NewFullFaker(), vm.Config{}) blockchain, _ := core.NewBlockChain(sdb, nil, params.TestChainConfig, ethash.NewFullFaker(), vm.Config{})
gchain, _ := core.GenerateChain(params.TestChainConfig, genesis, ethash.NewFaker(), sdb, 4, testChainGen) gchain, _ := core.GenerateChain(params.TestChainConfig, genesis, ethash.NewFaker(), sdb, 4, testChainGen)
if _, err := blockchain.InsertChain(gchain); err != nil { if _, err := blockchain.InsertChain(gchain); err != nil {
t.Fatal(err) t.Fatal(err)

View file

@ -40,7 +40,7 @@ func TestNodeIterator(t *testing.T) {
genesis = gspec.MustCommit(fulldb) genesis = gspec.MustCommit(fulldb)
) )
gspec.MustCommit(lightdb) gspec.MustCommit(lightdb)
blockchain, _ := core.NewBlockChain(fulldb, params.TestChainConfig, ethash.NewFullFaker(), vm.Config{}) blockchain, _ := core.NewBlockChain(fulldb, nil, params.TestChainConfig, ethash.NewFullFaker(), vm.Config{})
gchain, _ := core.GenerateChain(params.TestChainConfig, genesis, ethash.NewFaker(), fulldb, 4, testChainGen) gchain, _ := core.GenerateChain(params.TestChainConfig, genesis, ethash.NewFaker(), fulldb, 4, testChainGen)
if _, err := blockchain.InsertChain(gchain); err != nil { if _, err := blockchain.InsertChain(gchain); err != nil {
panic(err) panic(err)

View file

@ -88,7 +88,7 @@ func TestTxPool(t *testing.T) {
) )
gspec.MustCommit(ldb) gspec.MustCommit(ldb)
// Assemble the test environment // Assemble the test environment
blockchain, _ := core.NewBlockChain(sdb, params.TestChainConfig, ethash.NewFullFaker(), vm.Config{}) blockchain, _ := core.NewBlockChain(sdb, nil, params.TestChainConfig, ethash.NewFullFaker(), vm.Config{})
gchain, _ := core.GenerateChain(params.TestChainConfig, genesis, ethash.NewFaker(), sdb, poolTestBlocks, txPoolTestChainGen) gchain, _ := core.GenerateChain(params.TestChainConfig, genesis, ethash.NewFaker(), sdb, poolTestBlocks, txPoolTestChainGen)
if _, err := blockchain.InsertChain(gchain); err != nil { if _, err := blockchain.InsertChain(gchain); err != nil {
panic(err) panic(err)

View file

@ -110,7 +110,7 @@ func (t *BlockTest) Run() error {
return fmt.Errorf("genesis block state root does not match test: computed=%x, test=%x", gblock.Root().Bytes()[:6], t.json.Genesis.StateRoot[:6]) return fmt.Errorf("genesis block state root does not match test: computed=%x, test=%x", gblock.Root().Bytes()[:6], t.json.Genesis.StateRoot[:6])
} }
chain, err := core.NewBlockChain(db, config, ethash.NewShared(), vm.Config{}) chain, err := core.NewBlockChain(db, nil, config, ethash.NewShared(), vm.Config{})
if err != nil { if err != nil {
return err return err
} }

View file

@ -126,7 +126,7 @@ func (t *StateTest) Run(subtest StateSubtest, vmconfig vm.Config) (*state.StateD
if !ok { if !ok {
return nil, UnsupportedForkError{subtest.Fork} return nil, UnsupportedForkError{subtest.Fork}
} }
block, _, _ := t.genesis(config).ToBlock() block := t.genesis(config).ToBlock(nil)
db, _ := ethdb.NewMemDatabase() db, _ := ethdb.NewMemDatabase()
statedb := MakePreState(db, t.json.Pre) statedb := MakePreState(db, t.json.Pre)

View file

@ -21,6 +21,7 @@ import (
"time" "time"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
) )
@ -39,18 +40,11 @@ type DatabaseReader interface {
Has(key []byte) (bool, error) Has(key []byte) (bool, error)
} }
// DatabaseWriter wraps the Put method of a backing store for the trie.
type DatabaseWriter interface {
// Put stores the mapping key->value in the database. Implementations must not
// hold onto the value as the trie will reuse the slice across calls to Put.
Put(key, value []byte) error
}
// Database is an intermediate write layer between the trie data structures and // Database is an intermediate write layer between the trie data structures and
// the disk database. The aim is to accumulate trie writes in-memory and only // the disk database. The aim is to accumulate trie writes in-memory and only
// periodically flush a couple tries to disk, garbage collecting the remainder. // periodically flush a couple tries to disk, garbage collecting the remainder.
type Database struct { type Database struct {
diskdb DatabaseReader // Persistent storage for matured trie nodes diskdb ethdb.Database // Persistent storage for matured trie nodes
nodes map[common.Hash][]byte // Cached data blocks of the trie nodes nodes map[common.Hash][]byte // Cached data blocks of the trie nodes
parents map[common.Hash]int // Number of live nodes referencing a given one parents map[common.Hash]int // Number of live nodes referencing a given one
@ -59,6 +53,9 @@ type Database struct {
preimages map[common.Hash][]byte // Preimages of nodes from the secure trie preimages map[common.Hash][]byte // Preimages of nodes from the secure trie
seckeybuf [secureKeyLength]byte // Ephemeral buffer for calculating preimage keys seckeybuf [secureKeyLength]byte // Ephemeral buffer for calculating preimage keys
nodeArena map[common.Hash][]byte // Trie nodes actively being comitted (avoids locking during writes)
preimageArena map[common.Hash][]byte // Trie node preimages actively being comitted (avoids locking during writes)
gctime time.Duration // Time spent on garbage collection since last commit gctime time.Duration // Time spent on garbage collection since last commit
gcnodes uint64 // Nodes garbage collected since last commit gcnodes uint64 // Nodes garbage collected since last commit
gcsize common.StorageSize // Data storage garbage collected since last commit gcsize common.StorageSize // Data storage garbage collected since last commit
@ -69,7 +66,7 @@ type Database 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. // its written out to disk or garbage collected.
func NewDatabase(diskdb DatabaseReader) *Database { func NewDatabase(diskdb ethdb.Database) *Database {
db := &Database{ db := &Database{
diskdb: diskdb, diskdb: diskdb,
nodes: make(map[common.Hash][]byte), nodes: make(map[common.Hash][]byte),
@ -124,6 +121,9 @@ func (db *Database) Node(hash common.Hash) ([]byte, error) {
// Retrieve the node from cache if available // Retrieve the node from cache if available
db.lock.RLock() db.lock.RLock()
blob := db.nodes[hash] blob := db.nodes[hash]
if blob == nil && db.nodeArena != nil {
blob = db.nodeArena[hash]
}
db.lock.RUnlock() db.lock.RUnlock()
if blob != nil { if blob != nil {
@ -139,6 +139,9 @@ func (db *Database) preimage(hash common.Hash) ([]byte, error) {
// Retrieve the node from cache if available // Retrieve the node from cache if available
db.lock.RLock() db.lock.RLock()
preimage := db.preimages[hash] preimage := db.preimages[hash]
if preimage == nil && db.preimageArena != nil {
preimage = db.preimageArena[hash]
}
db.lock.RUnlock() db.lock.RUnlock()
if preimage != nil { if preimage != nil {
@ -243,47 +246,75 @@ func (db *Database) dereference(child common.Hash, parent common.Hash) {
// to disk, forcefully tearing down all references in both directions. // to disk, forcefully tearing down all references in both directions.
// //
// As a side effect, all pre-images accumulated up to this point are also written. // As a side effect, all pre-images accumulated up to this point are also written.
func (db *Database) Commit(node common.Hash, writer DatabaseWriter) error { func (db *Database) Commit(node common.Hash) error {
// Create a database batch to flush persistent data out. It is important that
// outside code doesn't see an inconsistent state (referenced data removed from
// memory cache during commit but not yet in persistent storage). This could be
// achieved by locking the entire database during commit, but a slow writer then
// would starve all readers. Instead, all data-to-be-committed is moved into a
// staging area, where it's temporarilly still accessible during write.
db.lock.Lock() db.lock.Lock()
defer db.lock.Unlock()
// Write out all the accumulated trie node preimages db.nodeArena = make(map[common.Hash][]byte)
db.preimageArena = make(map[common.Hash][]byte)
start := time.Now()
batch := db.diskdb.NewBatch()
// Move all of the accumulated preimages into a write batch and preimage staging area
for hash, preimage := range db.preimages { for hash, preimage := range db.preimages {
if err := writer.Put(db.secureKey(hash[:]), preimage); err != nil { db.preimageArena[hash] = preimage
log.Error("Failed to commit preimage from mempool", "err", err) if err := batch.Put(db.secureKey(hash[:]), preimage); err != nil {
log.Error("Failed to commit preimage from trie database", "err", err)
db.lock.Unlock()
return err return err
} }
db.size -= common.StorageSize(common.HashLength + len(preimage)) db.size -= common.StorageSize(common.HashLength + len(preimage))
} }
db.preimages = make(map[common.Hash][]byte) db.preimages = make(map[common.Hash][]byte)
// Write out the trie itself and dereference any flushed content // Move the trie itself into the batch and staging area and dereference any reachable content
nodes, storage, start := len(db.nodes), db.size, time.Now() nodes, storage := len(db.nodes), db.size
if err := db.commit(node, writer); err != nil { if err := db.commit(node, batch); err != nil {
log.Error("Failed to commit trie from mempool", "err", err) log.Error("Failed to commit trie from trie database", "err", err)
db.lock.Unlock()
return err return err
} }
log.Debug("Persistend trie from memory database", "nodes", nodes-len(db.nodes), "size", storage-db.size, "time", time.Since(start), // Write batch and staging area ready, unlock for readers during persistence
db.lock.Unlock()
if err := batch.Write(); err != nil {
log.Error("Failed to write trie to disk", "err", err)
return err
}
// Write successful, clear out the staging area and return
db.lock.Lock()
defer db.lock.Unlock()
db.nodeArena, db.preimageArena = nil, nil
log.Debug("Persisted trie from memory database", "nodes", nodes-len(db.nodes), "size", storage-db.size, "time", time.Since(start),
"gcnodes", db.gcnodes, "gcsize", db.gcsize, "gctime", db.gctime, "livenodes", len(db.nodes), "livesize", db.size) "gcnodes", db.gcnodes, "gcsize", db.gcsize, "gctime", db.gctime, "livenodes", len(db.nodes), "livesize", db.size)
// Reset the garbage collection statistics // Reset the garbage collection statistics
db.gcnodes, db.gcsize, db.gctime = 0, 0, 0 db.gcnodes, db.gcsize, db.gctime = 0, 0, 0
return nil return nil
} }
// commit is the private locked version of Commit. // commit is the private locked version of Commit.
func (db *Database) commit(node common.Hash, writer DatabaseWriter) error { func (db *Database) commit(node common.Hash, batch ethdb.Batch) error {
// If the node does not exist, it's a previously comitted node. // If the node does not exist, it's a previously comitted node.
blob, ok := db.nodes[node] blob, ok := db.nodes[node]
if !ok { if !ok {
return nil return nil
} }
for child := range db.children[node] { for child := range db.children[node] {
if err := db.commit(child, writer); err != nil { if err := db.commit(child, batch); err != nil {
return err return err
} }
} }
if err := writer.Put(node[:], blob); err != nil { db.nodeArena[node] = blob
if err := batch.Put(node[:], blob); err != nil {
return err return err
} }
delete(db.nodes, node) delete(db.nodes, node)

View file

@ -296,7 +296,7 @@ func testIteratorContinueAfterError(t *testing.T, memonly bool) {
} }
tr.Commit(nil) tr.Commit(nil)
if !memonly { if !memonly {
triedb.Commit(tr.Hash(), diskdb) triedb.Commit(tr.Hash())
} }
wantNodeCount := checkIteratorNoDups(t, tr.NodeIterator(nil), nil) wantNodeCount := checkIteratorNoDups(t, tr.NodeIterator(nil), nil)
@ -382,7 +382,7 @@ func testIteratorContinueAfterSeekError(t *testing.T, memonly bool) {
} }
root, _ := ctr.Commit(nil) root, _ := ctr.Commit(nil)
if !memonly { if !memonly {
triedb.Commit(root, diskdb) triedb.Commit(root)
} }
barNodeHash := common.HexToHash("05041990364eb72fcb1127652ce40d8bab765f2bfe53225b1170d276cc101c2e") barNodeHash := common.HexToHash("05041990364eb72fcb1127652ce40d8bab765f2bfe53225b1170d276cc101c2e")
var barNodeBlob []byte var barNodeBlob []byte

View file

@ -22,6 +22,7 @@ import (
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rlp"
) )
@ -33,7 +34,7 @@ import (
// If the trie does not contain a value for key, the returned proof contains all // If the trie does not contain a value for key, the returned proof contains all
// nodes of the longest existing prefix of the key (at least the root node), ending // nodes of the longest existing prefix of the key (at least the root node), ending
// with the node that proves the absence of the key. // with the node that proves the absence of the key.
func (t *Trie) Prove(key []byte, fromLevel uint, proofDb DatabaseWriter) error { func (t *Trie) Prove(key []byte, fromLevel uint, proofDb ethdb.Putter) error {
// Collect all nodes on the path to key. // Collect all nodes on the path to key.
key = keybytesToHex(key) key = keybytesToHex(key)
nodes := []node{} nodes := []node{}

View file

@ -21,6 +21,7 @@ import (
"fmt" "fmt"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/ethdb"
"gopkg.in/karalabe/cookiejar.v2/collections/prque" "gopkg.in/karalabe/cookiejar.v2/collections/prque"
) )
@ -212,7 +213,7 @@ func (s *TrieSync) Process(results []SyncResult) (bool, int, error) {
// Commit flushes the data stored in the internal membatch out to persistent // Commit flushes the data stored in the internal membatch out to persistent
// storage, returning th enumber of items written and any occurred error. // storage, returning th enumber of items written and any occurred error.
func (s *TrieSync) Commit(dbw DatabaseWriter) (int, error) { func (s *TrieSync) Commit(dbw ethdb.Putter) (int, error) {
// Dump the membatch into a database dbw // Dump the membatch into a database dbw
for i, key := range s.membatch.order { for i, key := range s.membatch.order {
if err := dbw.Put(key[:], s.membatch.batch[key]); err != nil { if err := dbw.Put(key[:], s.membatch.batch[key]); err != nil {

View file

@ -90,7 +90,7 @@ func testMissingNode(t *testing.T, memonly bool) {
updateString(trie, "123456", "asdfasdfasdfasdfasdfasdfasdfasdf") updateString(trie, "123456", "asdfasdfasdfasdfasdfasdfasdfasdf")
root, _ := trie.Commit(nil) root, _ := trie.Commit(nil)
if !memonly { if !memonly {
triedb.Commit(root, diskdb) triedb.Commit(root)
} }
trie, _ = New(root, triedb) trie, _ = New(root, triedb)
@ -319,13 +319,13 @@ func TestLargeValue(t *testing.T) {
} }
type countingDB struct { type countingDB struct {
DatabaseReader ethdb.Database
gets map[string]int gets map[string]int
} }
func (db *countingDB) Get(key []byte) ([]byte, error) { func (db *countingDB) Get(key []byte) ([]byte, error) {
db.gets[string(key)]++ db.gets[string(key)]++
return db.DatabaseReader.Get(key) return db.Database.Get(key)
} }
// TestCacheUnload checks that decoded nodes are unloaded after a // TestCacheUnload checks that decoded nodes are unloaded after a
@ -339,12 +339,12 @@ func TestCacheUnload(t *testing.T) {
updateString(trie, key2, "this is the branch of key2.") updateString(trie, key2, "this is the branch of key2.")
root, _ := trie.Commit(nil) root, _ := trie.Commit(nil)
trie.db.Commit(root, trie.db.diskdb.(DatabaseWriter)) trie.db.Commit(root)
// Commit the trie repeatedly and access key1. // Commit the trie repeatedly and access key1.
// The branch containing it is loaded from DB exactly two times: // The branch containing it is loaded from DB exactly two times:
// in the 0th and 6th iteration. // in the 0th and 6th iteration.
db := &countingDB{DatabaseReader: trie.db.diskdb, gets: make(map[string]int)} db := &countingDB{Database: trie.db.diskdb, gets: make(map[string]int)}
trie, _ = New(root, NewDatabase(db)) trie, _ = New(root, NewDatabase(db))
trie.SetCacheLimit(5) trie.SetCacheLimit(5)
for i := 0; i < 12; i++ { for i := 0; i < 12; i++ {