mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-18 01:43:47 +00:00
core, trie: thread-safe triedb commit, configurable caches
This commit is contained in:
parent
34d766a243
commit
80eb5be015
33 changed files with 295 additions and 197 deletions
|
|
@ -69,7 +69,7 @@ func NewSimulatedBackend(alloc core.GenesisAlloc) *SimulatedBackend {
|
|||
database, _ := ethdb.NewMemDatabase()
|
||||
genesis := core.Genesis{Config: params.AllEthashProtocolChanges, Alloc: alloc}
|
||||
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{
|
||||
database: database,
|
||||
|
|
|
|||
|
|
@ -97,7 +97,9 @@ func runCmd(ctx *cli.Context) error {
|
|||
}
|
||||
if 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
|
||||
} else {
|
||||
db, _ := ethdb.NewMemDatabase()
|
||||
|
|
|
|||
|
|
@ -89,6 +89,9 @@ var (
|
|||
utils.LightPeersFlag,
|
||||
utils.LightKDFFlag,
|
||||
utils.CacheFlag,
|
||||
utils.CacheDatabaseFlag,
|
||||
utils.CacheGCFlag,
|
||||
utils.CacheGCTimeoutFlag,
|
||||
utils.TrieCacheGenFlag,
|
||||
utils.ListenPortFlag,
|
||||
utils.MaxPeersFlag,
|
||||
|
|
|
|||
|
|
@ -22,10 +22,11 @@ import (
|
|||
"io"
|
||||
"sort"
|
||||
|
||||
"strings"
|
||||
|
||||
"github.com/ethereum/go-ethereum/cmd/utils"
|
||||
"github.com/ethereum/go-ethereum/internal/debug"
|
||||
"gopkg.in/urfave/cli.v1"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// AppHelpTemplate is the test template for the default, global app help topic.
|
||||
|
|
@ -127,6 +128,9 @@ var AppHelpFlagGroups = []flagGroup{
|
|||
Name: "PERFORMANCE TUNING",
|
||||
Flags: []cli.Flag{
|
||||
utils.CacheFlag,
|
||||
utils.CacheDatabaseFlag,
|
||||
utils.CacheGCFlag,
|
||||
utils.CacheGCTimeoutFlag,
|
||||
utils.TrieCacheGenFlag,
|
||||
},
|
||||
},
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ import (
|
|||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/accounts"
|
||||
"github.com/ethereum/go-ethereum/accounts/keystore"
|
||||
|
|
@ -293,8 +294,23 @@ var (
|
|||
// Performance tuning settings
|
||||
CacheFlag = cli.IntFlag{
|
||||
Name: "cache",
|
||||
Usage: "Megabytes of memory allocated to internal caching (min 16MB / database forced)",
|
||||
Value: 128,
|
||||
Usage: "Megabytes of memory allocated to internal caching",
|
||||
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{
|
||||
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)
|
||||
}
|
||||
|
||||
if ctx.GlobalIsSet(CacheFlag.Name) {
|
||||
cfg.DatabaseCache = ctx.GlobalInt(CacheFlag.Name)
|
||||
if ctx.GlobalIsSet(CacheFlag.Name) || ctx.GlobalIsSet(CacheDatabaseFlag.Name) {
|
||||
cfg.DatabaseCache = ctx.GlobalInt(CacheFlag.Name) * ctx.GlobalInt(CacheDatabaseFlag.Name) / 100
|
||||
}
|
||||
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) {
|
||||
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.
|
||||
func MakeChainDatabase(ctx *cli.Context, stack *node.Node) ethdb.Database {
|
||||
var (
|
||||
cache = ctx.GlobalInt(CacheFlag.Name)
|
||||
cache = ctx.GlobalInt(CacheFlag.Name) * ctx.GlobalInt(CacheDatabaseFlag.Name) / 100
|
||||
handles = makeDatabaseHandles()
|
||||
)
|
||||
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)}
|
||||
chain, err = core.NewBlockChain(chainDb, config, engine, vmcfg)
|
||||
chain, err = core.NewBlockChain(chainDb, cache, config, engine, vmcfg)
|
||||
if err != nil {
|
||||
Fatalf("Can't create BlockChain: %v", err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -173,7 +173,7 @@ func benchInsertChain(b *testing.B, disk bool, gen func(int, *BlockGen)) {
|
|||
|
||||
// Time the insertion of the new chain.
|
||||
// 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()
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
|
|
@ -283,7 +283,7 @@ func benchReadChain(b *testing.B, full bool, count uint64) {
|
|||
if err != nil {
|
||||
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 {
|
||||
b.Fatalf("error creating chain: %v", err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ func TestHeaderVerification(t *testing.T) {
|
|||
headers[i] = block.Header()
|
||||
}
|
||||
// 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()
|
||||
|
||||
for i := 0; i < len(blocks); i++ {
|
||||
|
|
@ -106,11 +106,11 @@ func testHeaderConcurrentVerification(t *testing.T, threads int) {
|
|||
var results <-chan error
|
||||
|
||||
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)
|
||||
chain.Stop()
|
||||
} 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)
|
||||
chain.Stop()
|
||||
}
|
||||
|
|
@ -173,7 +173,7 @@ func testHeaderConcurrentAbortion(t *testing.T, threads int) {
|
|||
defer runtime.GOMAXPROCS(old)
|
||||
|
||||
// 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()
|
||||
|
||||
abort, results := chain.engine.VerifyHeaders(chain, headers, seals)
|
||||
|
|
|
|||
|
|
@ -56,11 +56,19 @@ const (
|
|||
maxFutureBlocks = 256
|
||||
maxTimeFutureBlocks = 30
|
||||
badBlockLimit = 10
|
||||
triesInMemory = 128
|
||||
|
||||
// BlockChainVersion ensures that an incompatible database forces a resync from scratch.
|
||||
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
|
||||
// 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
|
||||
// canonical chain.
|
||||
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
|
||||
chainDb ethdb.Database
|
||||
rmLogsFeed event.Feed
|
||||
chainFeed event.Feed
|
||||
chainSideFeed event.Feed
|
||||
|
|
@ -121,17 +132,27 @@ type BlockChain struct {
|
|||
// NewBlockChain returns a fully initialised block chain using information
|
||||
// available in the database. It initialises the default Ethereum Validator and
|
||||
// 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)
|
||||
bodyRLPCache, _ := lru.New(bodyCacheLimit)
|
||||
blockCache, _ := lru.New(blockCacheLimit)
|
||||
futureBlocks, _ := lru.New(maxFutureBlocks)
|
||||
badBlocks, _ := lru.New(badBlockLimit)
|
||||
|
||||
triedb := trie.NewDatabase(diskdb)
|
||||
|
||||
bc := &BlockChain{
|
||||
config: config,
|
||||
chainDb: chainDb,
|
||||
stateCache: state.NewDatabase(trie.NewDatabase(chainDb)),
|
||||
chainConfig: chainConfig,
|
||||
cacheConfig: cacheConfig,
|
||||
diskdb: diskdb,
|
||||
triedb: triedb,
|
||||
stateCache: state.NewDatabase(triedb),
|
||||
quit: make(chan struct{}),
|
||||
bodyCache: bodyCache,
|
||||
bodyRLPCache: bodyRLPCache,
|
||||
|
|
@ -141,11 +162,11 @@ func NewBlockChain(chainDb ethdb.Database, config *params.ChainConfig, engine co
|
|||
vmConfig: vmConfig,
|
||||
badBlocks: badBlocks,
|
||||
}
|
||||
bc.SetValidator(NewBlockValidator(config, bc, engine))
|
||||
bc.SetProcessor(NewStateProcessor(config, bc, engine))
|
||||
bc.SetValidator(NewBlockValidator(chainConfig, bc, engine))
|
||||
bc.SetProcessor(NewStateProcessor(chainConfig, bc, engine))
|
||||
|
||||
var err error
|
||||
bc.hc, err = NewHeaderChain(chainDb, config, engine, bc.getProcInterrupt)
|
||||
bc.hc, err = NewHeaderChain(diskdb, chainConfig, engine, bc.getProcInterrupt)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -182,7 +203,7 @@ func (bc *BlockChain) getProcInterrupt() bool {
|
|||
// assumes that the chain manager mutex is held.
|
||||
func (bc *BlockChain) loadLastState() error {
|
||||
// Restore the last known head block
|
||||
head := GetHeadBlockHash(bc.chainDb)
|
||||
head := GetHeadBlockHash(bc.diskdb)
|
||||
if head == (common.Hash{}) {
|
||||
// Corrupt or empty database, init from scratch
|
||||
log.Warn("Empty database, resetting chain")
|
||||
|
|
@ -206,7 +227,7 @@ func (bc *BlockChain) loadLastState() error {
|
|||
|
||||
// Restore the last known head 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 {
|
||||
currentHeader = header
|
||||
}
|
||||
|
|
@ -215,7 +236,7 @@ func (bc *BlockChain) loadLastState() error {
|
|||
|
||||
// Restore the last known head fast block
|
||||
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 {
|
||||
bc.currentFastBlock = block
|
||||
}
|
||||
|
|
@ -245,7 +266,7 @@ func (bc *BlockChain) SetHead(head uint64) error {
|
|||
|
||||
// Rewind the header chain, deleting all block bodies until then
|
||||
delFn := func(hash common.Hash, num uint64) {
|
||||
DeleteBody(bc.chainDb, hash, num)
|
||||
DeleteBody(bc.diskdb, hash, num)
|
||||
}
|
||||
bc.hc.SetHead(head, delFn)
|
||||
currentHeader := bc.hc.CurrentHeader()
|
||||
|
|
@ -277,10 +298,10 @@ func (bc *BlockChain) SetHead(head uint64) error {
|
|||
if bc.currentFastBlock == nil {
|
||||
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)
|
||||
}
|
||||
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)
|
||||
}
|
||||
return bc.loadLastState()
|
||||
|
|
@ -294,7 +315,7 @@ func (bc *BlockChain) FastSyncCommitHead(hash common.Hash) error {
|
|||
if block == nil {
|
||||
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
|
||||
}
|
||||
// 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 {
|
||||
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)
|
||||
}
|
||||
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!
|
||||
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
|
||||
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
|
||||
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)
|
||||
}
|
||||
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)
|
||||
}
|
||||
bc.currentBlock = block
|
||||
|
|
@ -454,7 +475,7 @@ func (bc *BlockChain) insert(block *types.Block) {
|
|||
if updateHeads {
|
||||
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)
|
||||
}
|
||||
bc.currentFastBlock = block
|
||||
|
|
@ -474,7 +495,7 @@ func (bc *BlockChain) GetBody(hash common.Hash) *types.Body {
|
|||
body := cached.(*types.Body)
|
||||
return body
|
||||
}
|
||||
body := GetBody(bc.chainDb, hash, bc.hc.GetBlockNumber(hash))
|
||||
body := GetBody(bc.diskdb, hash, bc.hc.GetBlockNumber(hash))
|
||||
if body == nil {
|
||||
return nil
|
||||
}
|
||||
|
|
@ -490,7 +511,7 @@ func (bc *BlockChain) GetBodyRLP(hash common.Hash) rlp.RawValue {
|
|||
if cached, ok := bc.bodyRLPCache.Get(hash); ok {
|
||||
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 {
|
||||
return nil
|
||||
}
|
||||
|
|
@ -504,7 +525,7 @@ func (bc *BlockChain) HasBlock(hash common.Hash, number uint64) bool {
|
|||
if bc.blockCache.Contains(hash) {
|
||||
return true
|
||||
}
|
||||
ok, _ := bc.chainDb.Has(blockBodyKey(hash, number))
|
||||
ok, _ := bc.diskdb.Has(blockBodyKey(hash, number))
|
||||
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 {
|
||||
return block.(*types.Block)
|
||||
}
|
||||
block := GetBlock(bc.chainDb, hash, number)
|
||||
block := GetBlock(bc.diskdb, hash, number)
|
||||
if block == 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
|
||||
// (associated with its hash) if found.
|
||||
func (bc *BlockChain) GetBlockByNumber(number uint64) *types.Block {
|
||||
hash := GetCanonicalHash(bc.chainDb, number)
|
||||
hash := GetCanonicalHash(bc.diskdb, number)
|
||||
if hash == (common.Hash{}) {
|
||||
return nil
|
||||
}
|
||||
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.
|
||||
// [deprecated by eth/62]
|
||||
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
|
||||
}
|
||||
|
||||
// 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
|
||||
// it will abort them using the procInterrupt.
|
||||
func (bc *BlockChain) Stop() {
|
||||
|
|
@ -595,13 +627,9 @@ func (bc *BlockChain) Stop() {
|
|||
// Ensure the state of the latest block is also stored to disk
|
||||
root := bc.CurrentHeader().Root
|
||||
|
||||
batch := bc.chainDb.NewBatch()
|
||||
if err := bc.stateCache.TrieDB().Commit(root, batch); err != nil {
|
||||
if err := bc.triedb.Commit(root); err != nil {
|
||||
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")
|
||||
}
|
||||
|
||||
|
|
@ -646,11 +674,11 @@ func (bc *BlockChain) Rollback(chain []common.Hash) {
|
|||
}
|
||||
if bc.currentFastBlock.Hash() == hash {
|
||||
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 {
|
||||
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 }{}
|
||||
start = time.Now()
|
||||
bytes = 0
|
||||
batch = bc.chainDb.NewBatch()
|
||||
batch = bc.diskdb.NewBatch()
|
||||
)
|
||||
for i, block := range blockChain {
|
||||
receipts := receiptChain[i]
|
||||
|
|
@ -727,7 +755,7 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [
|
|||
continue
|
||||
}
|
||||
// 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
|
||||
if err := WriteBody(batch, block.Hash(), block.NumberU64(), block.Body()); err != nil {
|
||||
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]
|
||||
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 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)
|
||||
}
|
||||
bc.currentFastBlock = head
|
||||
|
|
@ -802,46 +830,38 @@ func (bc *BlockChain) WriteBlockAndState(block *types.Block, receipts []*types.R
|
|||
return NonStatTy, err
|
||||
}
|
||||
// Write other block data using a batch.
|
||||
batch := bc.chainDb.NewBatch()
|
||||
batch := bc.diskdb.NewBatch()
|
||||
if err := WriteBlock(batch, block); err != nil {
|
||||
return NonStatTy, err
|
||||
}
|
||||
root, err := state.Commit(bc.config.IsEIP158(block.Number()))
|
||||
root, err := state.Commit(bc.chainConfig.IsEIP158(block.Number()))
|
||||
if err != nil {
|
||||
return NonStatTy, err
|
||||
}
|
||||
db := bc.stateCache.TrieDB()
|
||||
|
||||
var (
|
||||
writeRetention = uint64(128) // Number of trie nodes we need to retain in memory
|
||||
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 {
|
||||
bc.triedb.Reference(root, common.Hash{}) // metadata reference to keep trie alive
|
||||
if current := block.NumberU64(); current > triesInMemory {
|
||||
// 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()
|
||||
|
||||
// Only write to disk if we exceeded our memory allowance *and* also have at
|
||||
// least a given number of tries gapped.
|
||||
if (db.Size() > memoryAllowance || bc.procTime > timeAllowance) && chosen >= lastWrite+writeRetention {
|
||||
db.Commit(header.Root, batch)
|
||||
if (bc.triedb.Size()/(1024*1024) > common.StorageSize(bc.cacheConfig.TrieNodeLimit) || bc.procTime > bc.cacheConfig.TrieTimeLimit) && chosen >= lastWrite+triesInMemory {
|
||||
bc.triedb.Commit(header.Root)
|
||||
lastWrite = chosen
|
||||
bc.procTime = 0
|
||||
}
|
||||
// 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 {
|
||||
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 {
|
||||
return NonStatTy, err
|
||||
}
|
||||
|
||||
// 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.
|
||||
// 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
|
||||
}
|
||||
// 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
|
||||
}
|
||||
status = CanonStatTy
|
||||
|
|
@ -1106,7 +1126,7 @@ func (bc *BlockChain) reorg(oldBlock, newBlock *types.Block) error {
|
|||
// These logs are later announced as deleted.
|
||||
collectLogs = func(h common.Hash) {
|
||||
// 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 _, log := range receipt.Logs {
|
||||
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
|
||||
bc.insert(newChain[i])
|
||||
// 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
|
||||
}
|
||||
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
|
||||
// receipts that were created in the fork must also be deleted
|
||||
for _, tx := range diff {
|
||||
DeleteTxLookupEntry(bc.chainDb, tx.Hash())
|
||||
DeleteTxLookupEntry(bc.diskdb, tx.Hash())
|
||||
}
|
||||
if len(deletedLogs) > 0 {
|
||||
go bc.rmLogsFeed.Send(RemovedLogsEvent{deletedLogs})
|
||||
|
|
@ -1277,7 +1297,7 @@ Hash: 0x%x
|
|||
|
||||
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
|
||||
|
|
@ -1384,7 +1404,7 @@ func (bc *BlockChain) GetHeaderByNumber(number uint64) *types.Header {
|
|||
}
|
||||
|
||||
// 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.
|
||||
func (bc *BlockChain) Engine() consensus.Engine { return bc.engine }
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@ func newTestBlockChain(fake bool) *BlockChain {
|
|||
if !fake {
|
||||
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 {
|
||||
panic(err)
|
||||
}
|
||||
|
|
@ -148,8 +148,8 @@ func testBlockChainImport(chain types.Blocks, blockchain *BlockChain) error {
|
|||
return err
|
||||
}
|
||||
blockchain.mu.Lock()
|
||||
WriteTd(blockchain.chainDb, block.Hash(), block.NumberU64(), new(big.Int).Add(block.Difficulty(), blockchain.GetTdByHash(block.ParentHash())))
|
||||
WriteBlock(blockchain.chainDb, block)
|
||||
WriteTd(blockchain.diskdb, block.Hash(), block.NumberU64(), new(big.Int).Add(block.Difficulty(), blockchain.GetTdByHash(block.ParentHash())))
|
||||
WriteBlock(blockchain.diskdb, block)
|
||||
statedb.Commit(false)
|
||||
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)
|
||||
blockchain.mu.Lock()
|
||||
WriteTd(blockchain.chainDb, header.Hash(), header.Number.Uint64(), new(big.Int).Add(header.Difficulty, blockchain.GetTdByHash(header.ParentHash)))
|
||||
WriteHeader(blockchain.chainDb, header)
|
||||
WriteTd(blockchain.diskdb, header.Hash(), header.Number.Uint64(), new(big.Int).Add(header.Difficulty, blockchain.GetTdByHash(header.ParentHash)))
|
||||
WriteHeader(blockchain.diskdb, header)
|
||||
blockchain.mu.Unlock()
|
||||
}
|
||||
return nil
|
||||
|
|
@ -186,9 +186,9 @@ func TestLastBlock(t *testing.T) {
|
|||
bchain := newTestBlockChain(false)
|
||||
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)
|
||||
if block.Hash() != GetHeadBlockHash(bchain.chainDb) {
|
||||
if block.Hash() != GetHeadBlockHash(bchain.diskdb) {
|
||||
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.
|
||||
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 {
|
||||
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
|
||||
archiveDb, _ := ethdb.NewMemDatabase()
|
||||
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()
|
||||
|
||||
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
|
||||
fastDb, _ := ethdb.NewMemDatabase()
|
||||
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()
|
||||
|
||||
headers := make([]*types.Header, len(blocks))
|
||||
|
|
@ -696,7 +696,7 @@ func TestLightVsFastVsFullChainHeads(t *testing.T) {
|
|||
archiveDb, _ := ethdb.NewMemDatabase()
|
||||
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 {
|
||||
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
|
||||
fastDb, _ := ethdb.NewMemDatabase()
|
||||
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()
|
||||
|
||||
headers := make([]*types.Header, len(blocks))
|
||||
|
|
@ -730,7 +730,7 @@ func TestLightVsFastVsFullChainHeads(t *testing.T) {
|
|||
lightDb, _ := ethdb.NewMemDatabase()
|
||||
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 {
|
||||
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.
|
||||
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 {
|
||||
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)
|
||||
)
|
||||
|
||||
blockchain, _ := NewBlockChain(db, gspec.Config, ethash.NewFaker(), vm.Config{})
|
||||
blockchain, _ := NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{})
|
||||
defer blockchain.Stop()
|
||||
|
||||
rmLogsCh := make(chan RemovedLogsEvent)
|
||||
|
|
@ -917,7 +917,7 @@ func TestReorgSideEvent(t *testing.T) {
|
|||
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()
|
||||
|
||||
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)
|
||||
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
|
||||
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.
|
||||
for {
|
||||
ch := GetCanonicalHash(bc.chainDb, block.NumberU64())
|
||||
ch := GetCanonicalHash(bc.diskdb, block.NumberU64())
|
||||
if ch == (common.Hash{}) {
|
||||
continue // busy wait for canonical hash to be written
|
||||
}
|
||||
if ch != block.Hash() {
|
||||
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 {
|
||||
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)
|
||||
)
|
||||
|
||||
blockchain, _ := NewBlockChain(db, gspec.Config, ethash.NewFaker(), vm.Config{})
|
||||
blockchain, _ := NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{})
|
||||
defer blockchain.Stop()
|
||||
|
||||
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)
|
||||
)
|
||||
blockchain, _ := NewBlockChain(db, gspec.Config, ethash.NewFaker(), vm.Config{})
|
||||
blockchain, _ := NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{})
|
||||
defer blockchain.Stop()
|
||||
|
||||
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()
|
||||
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 {
|
||||
t.Fatalf("failed to create tester chain: %v", err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
// 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.
|
||||
blockchain, _ := NewBlockChain(db, config, engine, vm.Config{})
|
||||
blockchain, _ := NewBlockChain(db, nil, config, engine, vm.Config{})
|
||||
defer blockchain.Stop()
|
||||
|
||||
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 {
|
||||
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))
|
||||
}
|
||||
return block, b.receipts
|
||||
|
|
@ -252,7 +252,7 @@ func newCanonical(engine consensus.Engine, n int, full bool) (ethdb.Database, *B
|
|||
db, _ := ethdb.NewMemDatabase()
|
||||
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
|
||||
if n == 0 {
|
||||
return db, blockchain, nil
|
||||
|
|
|
|||
|
|
@ -79,7 +79,7 @@ func ExampleGenerateChain() {
|
|||
})
|
||||
|
||||
// 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()
|
||||
|
||||
if i, err := blockchain.InsertChain(chain); err != nil {
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ func TestDAOForkRangeExtradata(t *testing.T) {
|
|||
proConf.DAOForkBlock = forkBlock
|
||||
proConf.DAOForkSupport = true
|
||||
|
||||
proBc, _ := NewBlockChain(proDb, &proConf, ethash.NewFaker(), vm.Config{})
|
||||
proBc, _ := NewBlockChain(proDb, nil, &proConf, ethash.NewFaker(), vm.Config{})
|
||||
defer proBc.Stop()
|
||||
|
||||
conDb, _ := ethdb.NewMemDatabase()
|
||||
|
|
@ -55,7 +55,7 @@ func TestDAOForkRangeExtradata(t *testing.T) {
|
|||
conConf.DAOForkBlock = forkBlock
|
||||
conConf.DAOForkSupport = false
|
||||
|
||||
conBc, _ := NewBlockChain(conDb, &conConf, ethash.NewFaker(), vm.Config{})
|
||||
conBc, _ := NewBlockChain(conDb, nil, &conConf, ethash.NewFaker(), vm.Config{})
|
||||
defer conBc.Stop()
|
||||
|
||||
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
|
||||
db, _ = ethdb.NewMemDatabase()
|
||||
gspec.MustCommit(db)
|
||||
bc, _ := NewBlockChain(db, &conConf, ethash.NewFaker(), vm.Config{})
|
||||
bc, _ := NewBlockChain(db, nil, &conConf, ethash.NewFaker(), vm.Config{})
|
||||
defer bc.Stop()
|
||||
|
||||
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 {
|
||||
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)
|
||||
}
|
||||
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
|
||||
db, _ = ethdb.NewMemDatabase()
|
||||
gspec.MustCommit(db)
|
||||
bc, _ = NewBlockChain(db, &proConf, ethash.NewFaker(), vm.Config{})
|
||||
bc, _ = NewBlockChain(db, nil, &proConf, ethash.NewFaker(), vm.Config{})
|
||||
defer bc.Stop()
|
||||
|
||||
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 {
|
||||
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)
|
||||
}
|
||||
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
|
||||
db, _ = ethdb.NewMemDatabase()
|
||||
gspec.MustCommit(db)
|
||||
bc, _ := NewBlockChain(db, &conConf, ethash.NewFaker(), vm.Config{})
|
||||
bc, _ := NewBlockChain(db, nil, &conConf, ethash.NewFaker(), vm.Config{})
|
||||
defer bc.Stop()
|
||||
|
||||
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 {
|
||||
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)
|
||||
}
|
||||
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
|
||||
db, _ = ethdb.NewMemDatabase()
|
||||
gspec.MustCommit(db)
|
||||
bc, _ = NewBlockChain(db, &proConf, ethash.NewFaker(), vm.Config{})
|
||||
bc, _ = NewBlockChain(db, nil, &proConf, ethash.NewFaker(), vm.Config{})
|
||||
defer bc.Stop()
|
||||
|
||||
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 {
|
||||
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)
|
||||
}
|
||||
blocks, _ = GenerateChain(&conConf, proBc.CurrentBlock(), ethash.NewFaker(), db, 1, func(i int, gen *BlockGen) {})
|
||||
|
|
|
|||
|
|
@ -170,10 +170,9 @@ func SetupGenesisBlock(db ethdb.Database, genesis *Genesis) (*params.ChainConfig
|
|||
|
||||
// Check whether the genesis block is already written.
|
||||
if genesis != nil {
|
||||
block, _, _ := genesis.ToBlock()
|
||||
hash := block.Hash()
|
||||
hash := genesis.ToBlock(nil).Hash()
|
||||
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.
|
||||
func (g *Genesis) ToBlock() (*types.Block, *state.StateDB, *trie.Database) {
|
||||
diskdb, _ := ethdb.NewMemDatabase()
|
||||
triedb := trie.NewDatabase(diskdb)
|
||||
// ToBlock creates the genesis block and writes state of a genesis specification
|
||||
// to the given database (or discards it if nil).
|
||||
func (g *Genesis) ToBlock(db ethdb.Database) *types.Block {
|
||||
if db == nil {
|
||||
db, _ = ethdb.NewMemDatabase()
|
||||
}
|
||||
triedb := trie.NewDatabase(db)
|
||||
statedb, _ := state.New(common.Hash{}, state.NewDatabase(triedb))
|
||||
for addr, account := range g.Alloc {
|
||||
statedb.AddBalance(addr, account.Balance)
|
||||
|
|
@ -254,22 +256,19 @@ func (g *Genesis) ToBlock() (*types.Block, *state.StateDB, *trie.Database) {
|
|||
if g.Difficulty == nil {
|
||||
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.
|
||||
// The block is committed as the canonical head block.
|
||||
func (g *Genesis) Commit(db ethdb.Database) (*types.Block, error) {
|
||||
block, statedb, triedb := g.ToBlock()
|
||||
block := g.ToBlock(db)
|
||||
if block.Number().Sign() != 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 {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,11 +30,11 @@ import (
|
|||
)
|
||||
|
||||
func TestDefaultGenesisBlock(t *testing.T) {
|
||||
block, _, _ := DefaultGenesisBlock().ToBlock()
|
||||
block := DefaultGenesisBlock().ToBlock(nil)
|
||||
if 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 {
|
||||
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.
|
||||
// Advance to block #4, past the homestead transition block of customg.
|
||||
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()
|
||||
bc.SetValidator(bproc{})
|
||||
bc.InsertChain(makeBlockChainWithDiff(genesis, []int{2, 3, 4, 5}, 0))
|
||||
|
|
|
|||
|
|
@ -144,9 +144,11 @@ func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) {
|
|||
}
|
||||
core.WriteBlockChainVersion(chainDb, core.BlockChainVersion)
|
||||
}
|
||||
|
||||
vmConfig := vm.Config{EnablePreimageRecording: config.EnablePreimageRecording}
|
||||
eth.blockchain, err = core.NewBlockChain(chainDb, eth.chainConfig, eth.engine, vmConfig)
|
||||
var (
|
||||
vmConfig = vm.Config{EnablePreimageRecording: config.EnablePreimageRecording}
|
||||
cacheConfig = &core.CacheConfig{TrieNodeLimit: config.TrieCache, TrieTimeLimit: config.TrieTimeout}
|
||||
)
|
||||
eth.blockchain, err = core.NewBlockChain(chainDb, cacheConfig, eth.chainConfig, eth.engine, vmConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ import (
|
|||
"os/user"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
|
|
@ -44,7 +45,9 @@ var DefaultConfig = Config{
|
|||
},
|
||||
NetworkId: 1,
|
||||
LightPeers: 100,
|
||||
DatabaseCache: 128,
|
||||
DatabaseCache: 768,
|
||||
TrieCache: 256,
|
||||
TrieTimeout: 5 * time.Minute,
|
||||
GasPrice: big.NewInt(18 * params.Shannon),
|
||||
|
||||
TxPool: core.DefaultTxPoolConfig,
|
||||
|
|
@ -87,6 +90,8 @@ type Config struct {
|
|||
SkipBcVersionCheck bool `toml:"-"`
|
||||
DatabaseHandles int `toml:"-"`
|
||||
DatabaseCache int
|
||||
TrieCache int
|
||||
TrieTimeout time.Duration
|
||||
|
||||
// Mining-related options
|
||||
Etherbase common.Address `toml:",omitempty"`
|
||||
|
|
|
|||
|
|
@ -71,7 +71,6 @@ type ProtocolManager struct {
|
|||
|
||||
txpool txPool
|
||||
blockchain *core.BlockChain
|
||||
chaindb ethdb.Database
|
||||
chainconfig *params.ChainConfig
|
||||
maxPeers int
|
||||
|
||||
|
|
@ -106,7 +105,6 @@ func NewProtocolManager(config *params.ChainConfig, mode downloader.SyncMode, ne
|
|||
eventMux: mux,
|
||||
txpool: txpool,
|
||||
blockchain: blockchain,
|
||||
chaindb: chaindb,
|
||||
chainconfig: config,
|
||||
peers: newPeerSet(),
|
||||
newPeerCh: make(chan *peer),
|
||||
|
|
@ -538,7 +536,7 @@ func (pm *ProtocolManager) handleMsg(p *peer) error {
|
|||
return errResp(ErrDecode, "msg %v: %v", msg, err)
|
||||
}
|
||||
// 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)
|
||||
bytes += len(entry)
|
||||
}
|
||||
|
|
@ -576,7 +574,7 @@ func (pm *ProtocolManager) handleMsg(p *peer) error {
|
|||
return errResp(ErrDecode, "msg %v: %v", msg, err)
|
||||
}
|
||||
// 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 header := pm.blockchain.GetHeaderByHash(hash); header == nil || header.ReceiptHash != types.EmptyRootHash {
|
||||
continue
|
||||
|
|
|
|||
|
|
@ -57,7 +57,7 @@ func TestProtocolCompatibility(t *testing.T) {
|
|||
for i, tt := range tests {
|
||||
ProtocolVersions = []uint{tt.version}
|
||||
|
||||
pm, err := newTestProtocolManager(tt.mode, 0, nil, nil)
|
||||
pm, _, err := newTestProtocolManager(tt.mode, 0, nil, nil)
|
||||
if pm != nil {
|
||||
defer pm.Stop()
|
||||
}
|
||||
|
|
@ -72,7 +72,7 @@ func TestGetBlockHeaders62(t *testing.T) { testGetBlockHeaders(t, 62) }
|
|||
func TestGetBlockHeaders63(t *testing.T) { testGetBlockHeaders(t, 63) }
|
||||
|
||||
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)
|
||||
defer peer.close()
|
||||
|
||||
|
|
@ -231,7 +231,7 @@ func TestGetBlockBodies62(t *testing.T) { testGetBlockBodies(t, 62) }
|
|||
func TestGetBlockBodies63(t *testing.T) { testGetBlockBodies(t, 63) }
|
||||
|
||||
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)
|
||||
defer peer.close()
|
||||
|
||||
|
|
@ -338,13 +338,13 @@ func testGetNodeData(t *testing.T, protocol int) {
|
|||
}
|
||||
}
|
||||
// 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)
|
||||
defer peer.close()
|
||||
|
||||
// Fetch for now the entire chain db
|
||||
hashes := []common.Hash{}
|
||||
for _, key := range pm.chaindb.(*ethdb.MemDatabase).Keys() {
|
||||
for _, key := range db.Keys() {
|
||||
if len(key) == len(common.Hash{}) {
|
||||
hashes = append(hashes, common.BytesToHash(key))
|
||||
}
|
||||
|
|
@ -430,7 +430,7 @@ func testGetReceipt(t *testing.T, protocol int) {
|
|||
}
|
||||
}
|
||||
// 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)
|
||||
defer peer.close()
|
||||
|
||||
|
|
@ -440,7 +440,7 @@ func testGetReceipt(t *testing.T, protocol int) {
|
|||
block := pm.blockchain.GetBlockByNumber(i)
|
||||
|
||||
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
|
||||
p2p.Send(peer.app, 0x0f, hashes)
|
||||
|
|
@ -473,7 +473,7 @@ func testDAOChallenge(t *testing.T, localForked, remoteForked bool, timeout bool
|
|||
config = ¶ms.ChainConfig{DAOForkBlock: big.NewInt(1), DAOForkSupport: localForked}
|
||||
gspec = &core.Genesis{Config: config}
|
||||
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)
|
||||
if err != nil {
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ var (
|
|||
// newTestProtocolManager creates a new protocol manager for testing purposes,
|
||||
// with the given number of blocks already known, and potential notification
|
||||
// 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 (
|
||||
evmux = new(event.TypeMux)
|
||||
engine = ethash.NewFaker()
|
||||
|
|
@ -59,7 +59,7 @@ func newTestProtocolManager(mode downloader.SyncMode, blocks int, generator func
|
|||
Alloc: core.GenesisAlloc{testBank: {Balance: big.NewInt(1000000)}},
|
||||
}
|
||||
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)
|
||||
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)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, nil, err
|
||||
}
|
||||
pm.Start(1000)
|
||||
return pm, nil
|
||||
return pm, db, nil
|
||||
}
|
||||
|
||||
// newTestProtocolManagerMust creates a new protocol manager for testing purposes,
|
||||
// with the given number of blocks already known, and potential notification
|
||||
// channels for different events. In case of an error, the constructor force-
|
||||
// fails the test.
|
||||
func newTestProtocolManagerMust(t *testing.T, mode downloader.SyncMode, blocks int, generator func(int, *core.BlockGen), newtx chan<- []*types.Transaction) *ProtocolManager {
|
||||
pm, err := newTestProtocolManager(mode, blocks, generator, newtx)
|
||||
func newTestProtocolManagerMust(t *testing.T, mode downloader.SyncMode, blocks int, generator func(int, *core.BlockGen), newtx chan<- []*types.Transaction) (*ProtocolManager, *ethdb.MemDatabase) {
|
||||
pm, db, err := newTestProtocolManager(mode, blocks, generator, newtx)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create protocol manager: %v", err)
|
||||
}
|
||||
return pm
|
||||
return pm, db
|
||||
}
|
||||
|
||||
// testTxPool is a fake, helper transaction pool for testing purposes
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ func TestStatusMsgErrors62(t *testing.T) { testStatusMsgErrors(t, 62) }
|
|||
func TestStatusMsgErrors63(t *testing.T) { testStatusMsgErrors(t, 63) }
|
||||
|
||||
func testStatusMsgErrors(t *testing.T, protocol int) {
|
||||
pm := newTestProtocolManagerMust(t, downloader.FullSync, 0, nil, nil)
|
||||
pm, _ := newTestProtocolManagerMust(t, downloader.FullSync, 0, nil, nil)
|
||||
var (
|
||||
genesis = pm.blockchain.Genesis()
|
||||
head = pm.blockchain.CurrentHeader()
|
||||
|
|
@ -98,7 +98,7 @@ func TestRecvTransactions63(t *testing.T) { testRecvTransactions(t, 63) }
|
|||
|
||||
func testRecvTransactions(t *testing.T, protocol int) {
|
||||
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
|
||||
p, _ := newTestPeer("peer", protocol, pm, true)
|
||||
defer pm.Stop()
|
||||
|
|
@ -125,7 +125,7 @@ func TestSendTransactions62(t *testing.T) { testSendTransactions(t, 62) }
|
|||
func TestSendTransactions63(t *testing.T) { testSendTransactions(t, 63) }
|
||||
|
||||
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()
|
||||
|
||||
// Fill the pool with big transactions.
|
||||
|
|
|
|||
|
|
@ -30,12 +30,12 @@ import (
|
|||
// imported into the blockchain.
|
||||
func TestFastSyncDisabling(t *testing.T) {
|
||||
// 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 {
|
||||
t.Fatalf("fast sync disabled on pristine blockchain")
|
||||
}
|
||||
// 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 {
|
||||
t.Fatalf("fast sync not disabled on non-empty blockchain")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -146,7 +146,7 @@ func newTestProtocolManager(lightSync bool, blocks int, generator func(int, *cor
|
|||
if lightSync {
|
||||
chain, _ = light.NewLightChain(odr, gspec.Config, engine)
|
||||
} 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)
|
||||
if _, err := blockchain.InsertChain(gchain); err != nil {
|
||||
panic(err)
|
||||
|
|
|
|||
|
|
@ -22,8 +22,8 @@ import (
|
|||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
"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
|
||||
|
|
@ -99,7 +99,7 @@ func (db *NodeSet) NodeList() NodeList {
|
|||
}
|
||||
|
||||
// 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()
|
||||
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
|
||||
|
||||
// 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 {
|
||||
db.Put(crypto.Keccak256(node), node)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -239,7 +239,7 @@ func testChainOdr(t *testing.T, protocol int, fn odrTestFn) {
|
|||
)
|
||||
gspec.MustCommit(ldb)
|
||||
// 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)
|
||||
if _, err := blockchain.InsertChain(gchain); err != nil {
|
||||
t.Fatal(err)
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ func TestNodeIterator(t *testing.T) {
|
|||
genesis = gspec.MustCommit(fulldb)
|
||||
)
|
||||
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)
|
||||
if _, err := blockchain.InsertChain(gchain); err != nil {
|
||||
panic(err)
|
||||
|
|
|
|||
|
|
@ -88,7 +88,7 @@ func TestTxPool(t *testing.T) {
|
|||
)
|
||||
gspec.MustCommit(ldb)
|
||||
// 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)
|
||||
if _, err := blockchain.InsertChain(gchain); err != nil {
|
||||
panic(err)
|
||||
|
|
|
|||
|
|
@ -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])
|
||||
}
|
||||
|
||||
chain, err := core.NewBlockChain(db, config, ethash.NewShared(), vm.Config{})
|
||||
chain, err := core.NewBlockChain(db, nil, config, ethash.NewShared(), vm.Config{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -126,7 +126,7 @@ func (t *StateTest) Run(subtest StateSubtest, vmconfig vm.Config) (*state.StateD
|
|||
if !ok {
|
||||
return nil, UnsupportedForkError{subtest.Fork}
|
||||
}
|
||||
block, _, _ := t.genesis(config).ToBlock()
|
||||
block := t.genesis(config).ToBlock(nil)
|
||||
db, _ := ethdb.NewMemDatabase()
|
||||
statedb := MakePreState(db, t.json.Pre)
|
||||
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
)
|
||||
|
||||
|
|
@ -39,18 +40,11 @@ type DatabaseReader interface {
|
|||
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
|
||||
// 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.
|
||||
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
|
||||
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
|
||||
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
|
||||
gcnodes uint64 // Nodes 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
|
||||
// its written out to disk or garbage collected.
|
||||
func NewDatabase(diskdb DatabaseReader) *Database {
|
||||
func NewDatabase(diskdb ethdb.Database) *Database {
|
||||
db := &Database{
|
||||
diskdb: diskdb,
|
||||
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
|
||||
db.lock.RLock()
|
||||
blob := db.nodes[hash]
|
||||
if blob == nil && db.nodeArena != nil {
|
||||
blob = db.nodeArena[hash]
|
||||
}
|
||||
db.lock.RUnlock()
|
||||
|
||||
if blob != nil {
|
||||
|
|
@ -139,6 +139,9 @@ func (db *Database) preimage(hash common.Hash) ([]byte, error) {
|
|||
// Retrieve the node from cache if available
|
||||
db.lock.RLock()
|
||||
preimage := db.preimages[hash]
|
||||
if preimage == nil && db.preimageArena != nil {
|
||||
preimage = db.preimageArena[hash]
|
||||
}
|
||||
db.lock.RUnlock()
|
||||
|
||||
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.
|
||||
//
|
||||
// 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()
|
||||
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 {
|
||||
if err := writer.Put(db.secureKey(hash[:]), preimage); err != nil {
|
||||
log.Error("Failed to commit preimage from mempool", "err", err)
|
||||
db.preimageArena[hash] = preimage
|
||||
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
|
||||
}
|
||||
db.size -= common.StorageSize(common.HashLength + len(preimage))
|
||||
}
|
||||
db.preimages = make(map[common.Hash][]byte)
|
||||
|
||||
// Write out the trie itself and dereference any flushed content
|
||||
nodes, storage, start := len(db.nodes), db.size, time.Now()
|
||||
if err := db.commit(node, writer); err != nil {
|
||||
log.Error("Failed to commit trie from mempool", "err", err)
|
||||
// Move the trie itself into the batch and staging area and dereference any reachable content
|
||||
nodes, storage := len(db.nodes), db.size
|
||||
if err := db.commit(node, batch); err != nil {
|
||||
log.Error("Failed to commit trie from trie database", "err", err)
|
||||
db.lock.Unlock()
|
||||
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)
|
||||
|
||||
// Reset the garbage collection statistics
|
||||
db.gcnodes, db.gcsize, db.gctime = 0, 0, 0
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// 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.
|
||||
blob, ok := db.nodes[node]
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
for child := range db.children[node] {
|
||||
if err := db.commit(child, writer); err != nil {
|
||||
if err := db.commit(child, batch); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if err := writer.Put(node[:], blob); err != nil {
|
||||
db.nodeArena[node] = blob
|
||||
if err := batch.Put(node[:], blob); err != nil {
|
||||
return err
|
||||
}
|
||||
delete(db.nodes, node)
|
||||
|
|
|
|||
|
|
@ -296,7 +296,7 @@ func testIteratorContinueAfterError(t *testing.T, memonly bool) {
|
|||
}
|
||||
tr.Commit(nil)
|
||||
if !memonly {
|
||||
triedb.Commit(tr.Hash(), diskdb)
|
||||
triedb.Commit(tr.Hash())
|
||||
}
|
||||
wantNodeCount := checkIteratorNoDups(t, tr.NodeIterator(nil), nil)
|
||||
|
||||
|
|
@ -382,7 +382,7 @@ func testIteratorContinueAfterSeekError(t *testing.T, memonly bool) {
|
|||
}
|
||||
root, _ := ctr.Commit(nil)
|
||||
if !memonly {
|
||||
triedb.Commit(root, diskdb)
|
||||
triedb.Commit(root)
|
||||
}
|
||||
barNodeHash := common.HexToHash("05041990364eb72fcb1127652ce40d8bab765f2bfe53225b1170d276cc101c2e")
|
||||
var barNodeBlob []byte
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ import (
|
|||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"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
|
||||
// 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.
|
||||
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.
|
||||
key = keybytesToHex(key)
|
||||
nodes := []node{}
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ import (
|
|||
"fmt"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
"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
|
||||
// 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
|
||||
for i, key := range s.membatch.order {
|
||||
if err := dbw.Put(key[:], s.membatch.batch[key]); err != nil {
|
||||
|
|
|
|||
|
|
@ -90,7 +90,7 @@ func testMissingNode(t *testing.T, memonly bool) {
|
|||
updateString(trie, "123456", "asdfasdfasdfasdfasdfasdfasdfasdf")
|
||||
root, _ := trie.Commit(nil)
|
||||
if !memonly {
|
||||
triedb.Commit(root, diskdb)
|
||||
triedb.Commit(root)
|
||||
}
|
||||
|
||||
trie, _ = New(root, triedb)
|
||||
|
|
@ -319,13 +319,13 @@ func TestLargeValue(t *testing.T) {
|
|||
}
|
||||
|
||||
type countingDB struct {
|
||||
DatabaseReader
|
||||
ethdb.Database
|
||||
gets map[string]int
|
||||
}
|
||||
|
||||
func (db *countingDB) Get(key []byte) ([]byte, error) {
|
||||
db.gets[string(key)]++
|
||||
return db.DatabaseReader.Get(key)
|
||||
return db.Database.Get(key)
|
||||
}
|
||||
|
||||
// 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.")
|
||||
|
||||
root, _ := trie.Commit(nil)
|
||||
trie.db.Commit(root, trie.db.diskdb.(DatabaseWriter))
|
||||
trie.db.Commit(root)
|
||||
|
||||
// Commit the trie repeatedly and access key1.
|
||||
// The branch containing it is loaded from DB exactly two times:
|
||||
// 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.SetCacheLimit(5)
|
||||
for i := 0; i < 12; i++ {
|
||||
|
|
|
|||
Loading…
Reference in a new issue