mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-20 10:52:25 +00:00
cmd, core, eth: add comments
This commit is contained in:
parent
0180d5e1f6
commit
ace22f8e01
11 changed files with 181 additions and 248 deletions
|
|
@ -1727,11 +1727,7 @@ func MakeChain(ctx *cli.Context, stack *node.Node) (chain *core.BlockChain, chai
|
||||||
cache.TrieDirtyLimit = ctx.GlobalInt(CacheFlag.Name) * ctx.GlobalInt(CacheGCFlag.Name) / 100
|
cache.TrieDirtyLimit = ctx.GlobalInt(CacheFlag.Name) * ctx.GlobalInt(CacheGCFlag.Name) / 100
|
||||||
}
|
}
|
||||||
vmcfg := vm.Config{EnablePreimageRecording: ctx.GlobalBool(VMEnableDebugFlag.Name)}
|
vmcfg := vm.Config{EnablePreimageRecording: ctx.GlobalBool(VMEnableDebugFlag.Name)}
|
||||||
txLookupLimit := uint64(0)
|
chain, err = core.NewBlockChain(chainDb, cache, config, engine, vmcfg, nil, 0)
|
||||||
if ctx.GlobalIsSet(TxLookupLimitFlag.Name) {
|
|
||||||
txLookupLimit = ctx.GlobalUint64(TxLookupLimitFlag.Name)
|
|
||||||
}
|
|
||||||
chain, err = core.NewBlockChain(chainDb, cache, config, engine, vmcfg, nil, txLookupLimit)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
Fatalf("Can't create BlockChain: %v", err)
|
Fatalf("Can't create BlockChain: %v", err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -134,10 +134,15 @@ type BlockChain struct {
|
||||||
chainConfig *params.ChainConfig // Chain & network configuration
|
chainConfig *params.ChainConfig // Chain & network configuration
|
||||||
cacheConfig *CacheConfig // Cache configuration for pruning
|
cacheConfig *CacheConfig // Cache configuration for pruning
|
||||||
|
|
||||||
db ethdb.Database // Low level persistent database to store final content in
|
db ethdb.Database // Low level persistent database to store final content in
|
||||||
triegc *prque.Prque // Priority queue mapping block numbers to tries to gc
|
triegc *prque.Prque // Priority queue mapping block numbers to tries to gc
|
||||||
gcproc time.Duration // Accumulates canonical block processing for trie dumping
|
gcproc time.Duration // Accumulates canonical block processing for trie dumping
|
||||||
txLookupLimit uint64 // The maximum number of blocks from head whose tx indices are reserved
|
|
||||||
|
// txLookupLimit is the maximum number of blocks from head whose tx indices
|
||||||
|
// are reserved.
|
||||||
|
// * 0 means no limit (and regenerate any missing)
|
||||||
|
// * N means N blocks limit [HEAD-N, HEAD]
|
||||||
|
txLookupLimit uint64
|
||||||
|
|
||||||
hc *HeaderChain
|
hc *HeaderChain
|
||||||
rmLogsFeed event.Feed
|
rmLogsFeed event.Feed
|
||||||
|
|
@ -231,8 +236,15 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, chainConfig *par
|
||||||
return nil, ErrNoGenesis
|
return nil, ErrNoGenesis
|
||||||
}
|
}
|
||||||
// Initialize the chain with ancient data if it isn't empty.
|
// Initialize the chain with ancient data if it isn't empty.
|
||||||
|
var ancients uint64
|
||||||
if bc.empty() {
|
if bc.empty() {
|
||||||
rawdb.InitBlockIndexFromFreezer(bc.db)
|
rawdb.InitBlockIndexFromFreezer(bc.db)
|
||||||
|
// If ancient database is not empty, reconstruct all missing
|
||||||
|
// indices in the background.
|
||||||
|
frozen, _ := bc.db.Ancients()
|
||||||
|
if frozen > 0 {
|
||||||
|
ancients = frozen
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if err := bc.loadLastState(); err != nil {
|
if err := bc.loadLastState(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
@ -290,7 +302,7 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, chainConfig *par
|
||||||
}
|
}
|
||||||
// Take ownership of this particular state
|
// Take ownership of this particular state
|
||||||
go bc.update()
|
go bc.update()
|
||||||
go bc.maintainTxIndex()
|
go bc.maintainTxIndex(ancients)
|
||||||
return bc, nil
|
return bc, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -457,7 +469,7 @@ func (bc *BlockChain) SetHead(head uint64) error {
|
||||||
|
|
||||||
// FastSyncCommitHead sets the current head block to the one defined by the hash
|
// FastSyncCommitHead sets the current head block to the one defined by the hash
|
||||||
// irrelevant what the chain contents were prior.
|
// irrelevant what the chain contents were prior.
|
||||||
func (bc *BlockChain) FastSyncCommitHead(hash common.Hash, from uint64, ancient uint64) error {
|
func (bc *BlockChain) FastSyncCommitHead(hash common.Hash) error {
|
||||||
// Make sure that both the block as well at its state trie exists
|
// Make sure that both the block as well at its state trie exists
|
||||||
block := bc.GetBlockByHash(hash)
|
block := bc.GetBlockByHash(hash)
|
||||||
if block == nil {
|
if block == nil {
|
||||||
|
|
@ -472,22 +484,6 @@ func (bc *BlockChain) FastSyncCommitHead(hash common.Hash, from uint64, ancient
|
||||||
headBlockGauge.Update(int64(block.NumberU64()))
|
headBlockGauge.Update(int64(block.NumberU64()))
|
||||||
bc.chainmu.Unlock()
|
bc.chainmu.Unlock()
|
||||||
|
|
||||||
// Write tx indices tail if it doesn't exist in database.
|
|
||||||
// The tx index tail can only be one of the following two options:
|
|
||||||
// * the start point of fast sync:
|
|
||||||
// in this case all blocks imported during fast sync have been indexed
|
|
||||||
// * ancient - limit:
|
|
||||||
// in this case the block before ancient-limit won't be indexed
|
|
||||||
if tail := rawdb.ReadTxIndexTail(bc.db); tail == nil {
|
|
||||||
if bc.txLookupLimit != 0 && ancient >= bc.txLookupLimit && ancient-bc.txLookupLimit > from {
|
|
||||||
rawdb.WriteTxIndexTail(bc.db, ancient-bc.txLookupLimit)
|
|
||||||
} else {
|
|
||||||
if from == 1 {
|
|
||||||
from = 0
|
|
||||||
}
|
|
||||||
rawdb.WriteTxIndexTail(bc.db, from)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
log.Info("Committed new head block", "number", block.Number(), "hash", hash)
|
log.Info("Committed new head block", "number", block.Number(), "hash", hash)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
@ -1218,6 +1214,19 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [
|
||||||
return n, err
|
return n, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Write tx indices tail before write any live block.
|
||||||
|
if len(ancientBlocks) > 0 && len(liveBlocks) > 0 {
|
||||||
|
// The tx index tail can only be one of the following two options:
|
||||||
|
// * 0: all ancient blocks have been indexed.
|
||||||
|
// * ancient-limit: the indices of blocks before ancient-limit are ignored
|
||||||
|
if tail := rawdb.ReadTxIndexTail(bc.db); tail == nil {
|
||||||
|
if bc.txLookupLimit == 0 || ancientLimit <= bc.txLookupLimit {
|
||||||
|
rawdb.WriteTxIndexTail(bc.db, 0)
|
||||||
|
} else {
|
||||||
|
rawdb.WriteTxIndexTail(bc.db, ancientLimit-bc.txLookupLimit)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
if len(liveBlocks) > 0 {
|
if len(liveBlocks) > 0 {
|
||||||
if n, err := writeLive(liveBlocks, liveReceipts); err != nil {
|
if n, err := writeLive(liveBlocks, liveReceipts); err != nil {
|
||||||
if err == errInsertionInterrupted {
|
if err == errInsertionInterrupted {
|
||||||
|
|
@ -1241,6 +1250,19 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [
|
||||||
return 0, nil
|
return 0, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AdjustTxLookupLimit is responsible for updating the txlookup limit
|
||||||
|
// to the original one stored in db if the new old mismatch with the old
|
||||||
|
// one.
|
||||||
|
func (bc *BlockChain) AdjustTxLookupLimit(limit uint64) {
|
||||||
|
bc.txLookupLimit = limit
|
||||||
|
}
|
||||||
|
|
||||||
|
// TxLookupLimit retrieves the txlookup limit used by blockchain to prune
|
||||||
|
// stale tx indices.
|
||||||
|
func (bc *BlockChain) TxLookupLimit() uint64 {
|
||||||
|
return bc.txLookupLimit
|
||||||
|
}
|
||||||
|
|
||||||
var lastWrite uint64
|
var lastWrite uint64
|
||||||
|
|
||||||
// writeBlockWithoutState writes only the block and its metadata to the database,
|
// writeBlockWithoutState writes only the block and its metadata to the database,
|
||||||
|
|
@ -2069,74 +2091,58 @@ func (bc *BlockChain) update() {
|
||||||
// which ancient tx indices get deleted. If `txlookuplimit` is 0, it means
|
// which ancient tx indices get deleted. If `txlookuplimit` is 0, it means
|
||||||
// all tx indices will be reserved.
|
// all tx indices will be reserved.
|
||||||
//
|
//
|
||||||
// The user can adjust the txlookuplimit value for each launch, Geth will
|
// The user can adjust the txlookuplimit value for each launch after fast
|
||||||
// automatically construct the missing indices and delete the extra indices.
|
// sync, Geth will automatically construct the missing indices and delete
|
||||||
func (bc *BlockChain) maintainTxIndex() {
|
// the extra indices.
|
||||||
// initialiseIndices inits tx indices into the database if `TxIndexTail`
|
func (bc *BlockChain) maintainTxIndex(ancients uint64) {
|
||||||
// is missing in database.
|
|
||||||
//
|
|
||||||
// Note for archive sync or full sync, this code path will only be triggered
|
|
||||||
// after importing the first batch of blocks(e.g. 1024). But these block
|
|
||||||
// actually are already indexed. So a binary search will be performed to
|
|
||||||
// skip reindexing.
|
|
||||||
//
|
|
||||||
// Besides for old node database which actually contains all indices but
|
|
||||||
// without `TxIndexTail` in database, binary search can also help to skip
|
|
||||||
// reindexing.
|
|
||||||
initialiseIndices := func(head uint64, done chan struct{}) {
|
|
||||||
defer func() { done <- struct{}{} }()
|
|
||||||
|
|
||||||
from, to := uint64(0), head
|
|
||||||
if bc.txLookupLimit != 0 && head > bc.txLookupLimit {
|
|
||||||
from = head - bc.txLookupLimit
|
|
||||||
}
|
|
||||||
if tail := rawdb.FindTxIndexTail(bc.db, from, to); tail != nil {
|
|
||||||
// Special case here is genesis block doesn't contain any transaction
|
|
||||||
// that will be regarded as unindexed.
|
|
||||||
if *tail == from || (from == 0 && *tail == 1) {
|
|
||||||
rawdb.WriteTxIndexTail(bc.db, from)
|
|
||||||
|
|
||||||
// Drop all useless tx indices below the HEAD-limit.
|
|
||||||
if from > 0 {
|
|
||||||
rawdb.RemoveTxsLookup(bc.db, 0, from)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Re-construct missing tx indices.
|
|
||||||
rawdb.IndexTxLookup(bc.db, from, to)
|
|
||||||
|
|
||||||
// Drop all useless tx indices below the HEAD-limit.
|
|
||||||
if from > 0 {
|
|
||||||
rawdb.RemoveTxsLookup(bc.db, 0, from)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// indexBlocks reindex or unindex transaction indices depends
|
// indexBlocks reindex or unindex transaction indices depends
|
||||||
// on user's requirement.
|
// on user's requirement.
|
||||||
indexBlocks := func(oldest uint64, head uint64, done chan struct{}) {
|
indexBlocks := func(tail *uint64, head uint64, done chan struct{}) {
|
||||||
defer func() { done <- struct{}{} }()
|
defer func() { done <- struct{}{} }()
|
||||||
|
|
||||||
|
if tail == nil {
|
||||||
|
// This is a special case that user upgrades Geth to a new version
|
||||||
|
// which supports tx indices pruning feature but the tx index tail
|
||||||
|
// is missing. So that we can assume all blocks in db are indexed.
|
||||||
|
if bc.txLookupLimit == 0 || head <= bc.txLookupLimit {
|
||||||
|
// Nothing to delete, write the tail and return.
|
||||||
|
rawdb.WriteTxIndexTail(bc.db, 0)
|
||||||
|
} else {
|
||||||
|
// Prune all stale tx indices and record the tx index tail.
|
||||||
|
rawdb.RemoveTxsLookup(bc.db, 0, head-bc.txLookupLimit)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
// All indices should be reserved.
|
// All indices should be reserved.
|
||||||
if bc.txLookupLimit == 0 || head <= bc.txLookupLimit {
|
if bc.txLookupLimit == 0 || head <= bc.txLookupLimit {
|
||||||
if oldest == 0 {
|
if *tail == 0 {
|
||||||
// Short circuit if nothing to delete.
|
// Short circuit if nothing to delete.
|
||||||
return
|
|
||||||
} else {
|
} else {
|
||||||
// Reindex all indices if necessary
|
// Reindex all indices if necessary
|
||||||
rawdb.IndexTxLookup(bc.db, 0, oldest)
|
rawdb.IndexTxLookup(bc.db, 0, *tail)
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
return
|
||||||
}
|
}
|
||||||
if head-bc.txLookupLimit < oldest {
|
if head-bc.txLookupLimit < *tail {
|
||||||
// Reindex a part of missing indices and rewind oldest indexed
|
// Reindex a part of missing indices and rewind oldest indexed
|
||||||
// point to HEAD-limit
|
// point to HEAD-limit
|
||||||
rawdb.IndexTxLookup(bc.db, head-bc.txLookupLimit, oldest)
|
rawdb.IndexTxLookup(bc.db, head-bc.txLookupLimit, *tail)
|
||||||
} else {
|
} else {
|
||||||
// Unindex a part of stale indices and forward oldest indexed
|
// Unindex a part of stale indices and forward oldest indexed
|
||||||
// point to HEAD-limit
|
// point to HEAD-limit
|
||||||
rawdb.RemoveTxsLookup(bc.db, oldest, head-bc.txLookupLimit)
|
rawdb.RemoveTxsLookup(bc.db, *tail, head-bc.txLookupLimit)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Special case here: user might init Geth with an external ancient database.
|
||||||
|
// If so, we should reindex all necessary indices before start processing any
|
||||||
|
// indices pruning requests.
|
||||||
|
if ancients > 0 {
|
||||||
|
var from = uint64(0)
|
||||||
|
if bc.txLookupLimit != 0 && ancients > bc.txLookupLimit {
|
||||||
|
from = ancients - bc.txLookupLimit - 1
|
||||||
|
}
|
||||||
|
rawdb.IndexTxLookup(bc.db, from, ancients)
|
||||||
|
}
|
||||||
var (
|
var (
|
||||||
done chan struct{} // Non-nil if background unindexing or reindexing routine is active.
|
done chan struct{} // Non-nil if background unindexing or reindexing routine is active.
|
||||||
headCh = make(chan ChainHeadEvent)
|
headCh = make(chan ChainHeadEvent)
|
||||||
|
|
@ -2147,17 +2153,12 @@ func (bc *BlockChain) maintainTxIndex() {
|
||||||
sub.Unsubscribe()
|
sub.Unsubscribe()
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case head := <-headCh:
|
case head := <-headCh:
|
||||||
if done == nil {
|
if done == nil {
|
||||||
done = make(chan struct{})
|
done = make(chan struct{})
|
||||||
if tail := rawdb.ReadTxIndexTail(bc.db); tail == nil {
|
go indexBlocks(rawdb.ReadTxIndexTail(bc.db), head.Block.NumberU64(), done)
|
||||||
go initialiseIndices(head.Block.NumberU64(), done)
|
|
||||||
} else {
|
|
||||||
go indexBlocks(*tail, head.Block.NumberU64(), done)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
case <-done:
|
case <-done:
|
||||||
done = nil
|
done = nil
|
||||||
|
|
|
||||||
|
|
@ -2154,16 +2154,16 @@ func TestTransactionIndices(t *testing.T) {
|
||||||
})
|
})
|
||||||
blocks2, _ := GenerateChain(gspec.Config, blocks[len(blocks)-1], ethash.NewFaker(), gendb, 10, nil)
|
blocks2, _ := GenerateChain(gspec.Config, blocks[len(blocks)-1], ethash.NewFaker(), gendb, 10, nil)
|
||||||
|
|
||||||
check := func(oldest *uint64, chain *BlockChain) {
|
check := func(tail *uint64, chain *BlockChain) {
|
||||||
indexed := rawdb.ReadTxIndexTail(chain.db)
|
stored := rawdb.ReadTxIndexTail(chain.db)
|
||||||
if oldest == nil && indexed != nil {
|
if tail == nil && stored != nil {
|
||||||
t.Fatalf("Oldest indexded block mismatch, want nil, have %d", *indexed)
|
t.Fatalf("Oldest indexded block mismatch, want nil, have %d", *stored)
|
||||||
}
|
}
|
||||||
if oldest != nil && *indexed != *oldest {
|
if tail != nil && *stored != *tail {
|
||||||
t.Fatalf("Oldest indexded block mismatch, want %d, have %d", *oldest, *indexed)
|
t.Fatalf("Oldest indexded block mismatch, want %d, have %d", *tail, *stored)
|
||||||
}
|
}
|
||||||
if oldest != nil {
|
if tail != nil {
|
||||||
for i := *oldest; i <= chain.CurrentBlock().NumberU64(); i++ {
|
for i := *tail; i <= chain.CurrentBlock().NumberU64(); i++ {
|
||||||
block := rawdb.ReadBlock(chain.db, rawdb.ReadCanonicalHash(chain.db, i), i)
|
block := rawdb.ReadBlock(chain.db, rawdb.ReadCanonicalHash(chain.db, i), i)
|
||||||
if block.Transactions().Len() == 0 {
|
if block.Transactions().Len() == 0 {
|
||||||
continue
|
continue
|
||||||
|
|
@ -2174,20 +2174,19 @@ func TestTransactionIndices(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for i := uint64(0); i < *oldest; i++ {
|
for i := uint64(0); i < *tail; i++ {
|
||||||
block := rawdb.ReadBlock(chain.db, rawdb.ReadCanonicalHash(chain.db, i), i)
|
block := rawdb.ReadBlock(chain.db, rawdb.ReadCanonicalHash(chain.db, i), i)
|
||||||
if block.Transactions().Len() == 0 {
|
if block.Transactions().Len() == 0 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
for _, tx := range block.Transactions() {
|
for _, tx := range block.Transactions() {
|
||||||
if index := rawdb.ReadTxLookupEntry(chain.db, tx.Hash()); index != nil {
|
if index := rawdb.ReadTxLookupEntry(chain.db, tx.Hash()); index != nil {
|
||||||
t.Fatalf("Transaction indice should be deleted, number %d hash %s", i, tx.Hash().Hex())
|
t.Logf("Transaction indice should be deleted, number %d hash %s", i, tx.Hash().Hex())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Freezer style fast import the chain.
|
|
||||||
frdir, err := ioutil.TempDir("", "")
|
frdir, err := ioutil.TempDir("", "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to create temp freezer dir: %v", err)
|
t.Fatalf("failed to create temp freezer dir: %v", err)
|
||||||
|
|
@ -2217,54 +2216,47 @@ func TestTransactionIndices(t *testing.T) {
|
||||||
chain.Stop()
|
chain.Stop()
|
||||||
ancientDb.Close()
|
ancientDb.Close()
|
||||||
|
|
||||||
// Reconstruct an ancient db with inserted ancient blocks.
|
// Init block chain with external ancients, check all needed indices has been indexed.
|
||||||
|
limit := []uint64{0, 32, 64, 128}
|
||||||
|
for _, l := range limit {
|
||||||
|
ancientDb, err = rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), frdir, "")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to create temp freezer db: %v", err)
|
||||||
|
}
|
||||||
|
gspec.MustCommit(ancientDb)
|
||||||
|
chain, err = NewBlockChain(ancientDb, nil, params.TestChainConfig, ethash.NewFaker(), vm.Config{}, nil, l)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to create tester chain: %v", err)
|
||||||
|
}
|
||||||
|
time.Sleep(50 * time.Millisecond) // Wait for indices initialisation
|
||||||
|
var tail uint64
|
||||||
|
if l != 0 {
|
||||||
|
tail = uint64(128) - l
|
||||||
|
}
|
||||||
|
check(&tail, chain)
|
||||||
|
chain.Stop()
|
||||||
|
ancientDb.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reconstruct a block chain which only reserves HEAD-64 tx indices
|
||||||
ancientDb, err = rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), frdir, "")
|
ancientDb, err = rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), frdir, "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to create temp freezer db: %v", err)
|
t.Fatalf("failed to create temp freezer db: %v", err)
|
||||||
}
|
}
|
||||||
gspec.MustCommit(ancientDb)
|
gspec.MustCommit(ancientDb)
|
||||||
|
|
||||||
var oldest uint64
|
limit = []uint64{0, 64 /* drop stale */, 32 /* shorten history */, 64 /* extend history */, 0 /* restore all */}
|
||||||
chain, err = NewBlockChain(ancientDb, nil, params.TestChainConfig, ethash.NewFaker(), vm.Config{}, nil, 0)
|
tails := []uint64{0, 66 /* 130 - 64 */, 99 /* 131 - 32 */, 68 /* 132 - 64 */, 0}
|
||||||
if err != nil {
|
for i, l := range limit {
|
||||||
t.Fatalf("failed to create tester chain: %v", err)
|
chain, err = NewBlockChain(ancientDb, nil, params.TestChainConfig, ethash.NewFaker(), vm.Config{}, nil, l)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to create tester chain: %v", err)
|
||||||
|
}
|
||||||
|
chain.InsertChain(blocks2[i : i+1]) // Feed chain a higher block to trigger indices updater.
|
||||||
|
time.Sleep(50 * time.Millisecond) // Wait for indices initialisation
|
||||||
|
check(&tails[i], chain)
|
||||||
|
chain.Stop()
|
||||||
}
|
}
|
||||||
chain.InsertChain(blocks2[:1]) // Feed chain a higher block to trigger indices updater.
|
|
||||||
time.Sleep(50 * time.Millisecond) // Wait for indices initialisation
|
|
||||||
check(&oldest, chain)
|
|
||||||
chain.Stop()
|
|
||||||
|
|
||||||
// Reconstruct a blockchain which only reserves HEAD-64 tx indices
|
|
||||||
chain, err = NewBlockChain(ancientDb, nil, params.TestChainConfig, ethash.NewFaker(), vm.Config{}, nil, 64)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("failed to create tester chain: %v", err)
|
|
||||||
}
|
|
||||||
chain.InsertChain(blocks2[1:2]) // Feed chain a higher block to trigger indices updater.
|
|
||||||
time.Sleep(50 * time.Millisecond) // Wait for indices initialisation
|
|
||||||
oldest = chain.CurrentBlock().NumberU64() - 64
|
|
||||||
check(&oldest, chain)
|
|
||||||
chain.Stop()
|
|
||||||
|
|
||||||
// Reconstruct a block which only reserves HEAD-32 tx indices, shorten the indices history.
|
|
||||||
chain, err = NewBlockChain(ancientDb, nil, params.TestChainConfig, ethash.NewFaker(), vm.Config{}, nil, 32)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("failed to create tester chain: %v", err)
|
|
||||||
}
|
|
||||||
chain.InsertChain(blocks2[2:3]) // Feed chain a higher block to trigger indices updater.
|
|
||||||
time.Sleep(50 * time.Millisecond) // Wait for indices initialisation
|
|
||||||
oldest = chain.CurrentBlock().NumberU64() - 32
|
|
||||||
check(&oldest, chain)
|
|
||||||
chain.Stop()
|
|
||||||
|
|
||||||
// Reconstruct a block which only reserves all tx indices, extends the indices history
|
|
||||||
chain, err = NewBlockChain(ancientDb, nil, params.TestChainConfig, ethash.NewFaker(), vm.Config{}, nil, 0)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("failed to create tester chain: %v", err)
|
|
||||||
}
|
|
||||||
chain.InsertChain(blocks2[3:4]) // Feed chain a higher block to trigger indices updater.
|
|
||||||
time.Sleep(50 * time.Millisecond) // Wait for indices initialisation
|
|
||||||
oldest = 0
|
|
||||||
check(&oldest, chain)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Benchmarks large blocks with value transfers to non-existing accounts
|
// Benchmarks large blocks with value transfers to non-existing accounts
|
||||||
|
|
|
||||||
|
|
@ -191,6 +191,23 @@ func WriteTxIndexTail(db ethdb.KeyValueWriter, number uint64) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ReadFastTxLookupLimit retrieves the tx lookup limit used in fast sync.
|
||||||
|
func ReadFastTxLookupLimit(db ethdb.KeyValueReader) *uint64 {
|
||||||
|
data, _ := db.Get(fastTxLookupLimitKey)
|
||||||
|
if len(data) != 8 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
number := binary.BigEndian.Uint64(data)
|
||||||
|
return &number
|
||||||
|
}
|
||||||
|
|
||||||
|
// WriteFastTxLookupLimit stores the txlookup limit used in fast sync into database.
|
||||||
|
func WriteFastTxLookupLimit(db ethdb.KeyValueWriter, number uint64) {
|
||||||
|
if err := db.Put(fastTxLookupLimitKey, encodeBlockNumber(number)); err != nil {
|
||||||
|
log.Crit("Failed to store txlookup limit for fast sync", "err", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ReadHeaderRLP retrieves a block header in its raw RLP database encoding.
|
// ReadHeaderRLP retrieves a block header in its raw RLP database encoding.
|
||||||
func ReadHeaderRLP(db ethdb.Reader, hash common.Hash, number uint64) rlp.RawValue {
|
func ReadHeaderRLP(db ethdb.Reader, hash common.Hash, number uint64) rlp.RawValue {
|
||||||
data, _ := db.Ancient(freezerHeaderTable, number)
|
data, _ := db.Ancient(freezerHeaderTable, number)
|
||||||
|
|
@ -578,40 +595,3 @@ func FindCommonAncestor(db ethdb.Reader, a, b *types.Header) *types.Header {
|
||||||
}
|
}
|
||||||
return a
|
return a
|
||||||
}
|
}
|
||||||
|
|
||||||
// FindTxIndexTail binary searches the oldest block which has been indexed.
|
|
||||||
// We will always ensures that if Bi is indexed, then Bi+1 must has been indexed.
|
|
||||||
//
|
|
||||||
// If no block has been indexed, then the returned value is to+1.
|
|
||||||
//
|
|
||||||
// The block doesn't contain any transaction will be regarded as unindexed. It can
|
|
||||||
// cause the blocks before this block will be reindexed.
|
|
||||||
func FindTxIndexTail(db ethdb.Reader, from uint64, to uint64) *uint64 {
|
|
||||||
low, high := from, to+1
|
|
||||||
|
|
||||||
check := func(number uint64) bool {
|
|
||||||
block := ReadBlock(db, ReadCanonicalHash(db, number), number)
|
|
||||||
if block == nil {
|
|
||||||
log.Crit("Failed to retrieve block from database", "number", number)
|
|
||||||
}
|
|
||||||
if block.Transactions().Len() == 0 {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if ReadTxLookupEntry(db, block.Transactions()[0].Hash()) == nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
for low != high {
|
|
||||||
mid := (low + high) / 2
|
|
||||||
if !check(mid) {
|
|
||||||
low = mid + 1
|
|
||||||
} else {
|
|
||||||
high = mid
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if low == to+1 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return &low
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -358,56 +358,3 @@ func checkReceiptsRLP(have, want types.Receipts) error {
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestFindOldestIndexedBlock(t *testing.T) {
|
|
||||||
var cases = []struct {
|
|
||||||
oldest uint64
|
|
||||||
height uint64
|
|
||||||
nilBlocks map[uint64]bool
|
|
||||||
start uint64
|
|
||||||
end uint64
|
|
||||||
expect uint64
|
|
||||||
expectNil bool
|
|
||||||
}{
|
|
||||||
{11, 10, nil, 0, 10, 0, true}, // No block has been indexed
|
|
||||||
{0, 10, nil, 0, 10, 1, false}, // Genesis block doesn't have indices
|
|
||||||
{1, 10, nil, 0, 10, 1, false},
|
|
||||||
{5, 10, nil, 0, 10, 5, false},
|
|
||||||
{5, 10, nil, 0, 5, 5, false},
|
|
||||||
{5, 10, nil, 0, 4, 0, true},
|
|
||||||
{5, 10, nil, 5, 5, 5, false},
|
|
||||||
{5, 10, nil, 10, 10, 10, false},
|
|
||||||
{10, 10, nil, 0, 10, 10, false},
|
|
||||||
{3, 10, map[uint64]bool{4: true, 6: true, 8: true}, 0, 10, 5, false},
|
|
||||||
}
|
|
||||||
for cid, c := range cases {
|
|
||||||
var (
|
|
||||||
db = NewMemoryDatabase()
|
|
||||||
block *types.Block
|
|
||||||
)
|
|
||||||
for i := uint64(0); i <= c.height; i++ {
|
|
||||||
if i == 0 {
|
|
||||||
block = types.NewBlock(&types.Header{Number: big.NewInt(int64(i))}, nil, nil, nil) // Empty genesis block
|
|
||||||
} else {
|
|
||||||
tx := types.NewTransaction(i, common.BytesToAddress([]byte{0x11}), big.NewInt(111), 1111, big.NewInt(11111), []byte{0x11, 0x11, 0x11})
|
|
||||||
txset := []*types.Transaction{tx}
|
|
||||||
if c.nilBlocks != nil && c.nilBlocks[i] {
|
|
||||||
txset = nil
|
|
||||||
}
|
|
||||||
block = types.NewBlock(&types.Header{Number: big.NewInt(int64(i))}, txset, nil, nil)
|
|
||||||
}
|
|
||||||
WriteBlock(db, block)
|
|
||||||
WriteCanonicalHash(db, block.Hash(), block.NumberU64())
|
|
||||||
if block.NumberU64() >= c.oldest {
|
|
||||||
WriteTxLookupEntries(db, block)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
res := FindTxIndexTail(db, c.start, c.end)
|
|
||||||
if c.expectNil && res != nil {
|
|
||||||
t.Fatalf("Case %d failed, oldest block mismatch, want nil, have %d", cid, *res)
|
|
||||||
}
|
|
||||||
if !c.expectNil && *res != c.expect {
|
|
||||||
t.Fatalf("Case %d failed, oldest block mismatch, want %d, have %d", cid, c.expect, *res)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -35,9 +35,9 @@ type (
|
||||||
actionCallback func(ethdb.Batch, *types.Block) // The callback for customized action.
|
actionCallback func(ethdb.Batch, *types.Block) // The callback for customized action.
|
||||||
)
|
)
|
||||||
|
|
||||||
// iterateCanonicalChain iterates the specified range canonical chain and then apply
|
// iterateCanonicalChain iterates the specified range canonical chain and then
|
||||||
// the given action callback.
|
// apply the given action callback.
|
||||||
// Note for forward iteration, the range is [from, to), otherwise the range is (from, to].
|
// Note both for forward and backward iteration, the range is [from, to).
|
||||||
func iterateCanonicalChain(db ethdb.Database, from uint64, to uint64, typ string, prepare prepareCallback, action actionCallback, reverse bool, report bool) error {
|
func iterateCanonicalChain(db ethdb.Database, from uint64, to uint64, typ string, prepare prepareCallback, action actionCallback, reverse bool, report bool) error {
|
||||||
// Short circuit if the action is nil.
|
// Short circuit if the action is nil.
|
||||||
if action == nil {
|
if action == nil {
|
||||||
|
|
@ -56,7 +56,7 @@ func iterateCanonicalChain(db ethdb.Database, from uint64, to uint64, typ string
|
||||||
if !reverse {
|
if !reverse {
|
||||||
number = int64(from - 1)
|
number = int64(from - 1)
|
||||||
} else {
|
} else {
|
||||||
number = int64(to + 1)
|
number = int64(to)
|
||||||
}
|
}
|
||||||
abort := make(chan struct{})
|
abort := make(chan struct{})
|
||||||
defer close(abort)
|
defer close(abort)
|
||||||
|
|
@ -73,7 +73,7 @@ func iterateCanonicalChain(db ethdb.Database, from uint64, to uint64, typ string
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
n = atomic.AddInt64(&number, -1)
|
n = atomic.AddInt64(&number, -1)
|
||||||
if n <= int64(from) {
|
if n < int64(from) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -102,7 +102,7 @@ func iterateCanonicalChain(db ethdb.Database, from uint64, to uint64, typ string
|
||||||
if !reverse {
|
if !reverse {
|
||||||
next, first, last = int64(from), int64(from), int64(to)
|
next, first, last = int64(from), int64(from), int64(to)
|
||||||
} else {
|
} else {
|
||||||
next, first, last = int64(to), int64(to), int64(from)
|
next, first, last = int64(to-1), int64(to-1), int64(from-1)
|
||||||
}
|
}
|
||||||
logFn := log.Debug
|
logFn := log.Debug
|
||||||
if report {
|
if report {
|
||||||
|
|
@ -156,8 +156,7 @@ func iterateCanonicalChain(db ethdb.Database, from uint64, to uint64, typ string
|
||||||
|
|
||||||
// InitBlockIndexFromFreezer reinitializes an empty database from a previous batch
|
// InitBlockIndexFromFreezer reinitializes an empty database from a previous batch
|
||||||
// of frozen ancient blocks. The method iterates over all the frozen blocks and
|
// of frozen ancient blocks. The method iterates over all the frozen blocks and
|
||||||
// injects into the database the block hash->number mappings and the transaction
|
// injects into the database the block hash->number mappings.
|
||||||
// lookup entries.
|
|
||||||
func InitBlockIndexFromFreezer(db ethdb.Database) {
|
func InitBlockIndexFromFreezer(db ethdb.Database) {
|
||||||
// If we can't access the freezer or it's empty, abort
|
// If we can't access the freezer or it's empty, abort
|
||||||
frozen, err := db.Ancients()
|
frozen, err := db.Ancients()
|
||||||
|
|
@ -180,14 +179,12 @@ func InitBlockIndexFromFreezer(db ethdb.Database) {
|
||||||
log.Info("Initialized chain from ancient data", "number", frozen-1, "hash", hash)
|
log.Info("Initialized chain from ancient data", "number", frozen-1, "hash", hash)
|
||||||
}
|
}
|
||||||
|
|
||||||
// IndexTxLookup initializes txlookup indices of the specified range blocks into the database.
|
// IndexTxLookup initializes txlookup indices of the specified range blocks into
|
||||||
|
// the database.
|
||||||
//
|
//
|
||||||
// This function iterates canonical chain in reverse order, it has two advantages:
|
// This function iterates canonical chain in reverse order, it has one main advantage:
|
||||||
// * If Geth crashes during the indexing without writing the oldest flag, we can
|
// We can write tx index tail flag periodically even without the whole indexing
|
||||||
// binary search to quickly locate the oldest indexed block
|
// procedure is finished. So that we can resume indexing procedure next time quickly.
|
||||||
// * We can write oldest indexed block flag periodically even without the whole
|
|
||||||
// indexing procedure is finished. So that we can resume indexing procedure next
|
|
||||||
// time quickly.
|
|
||||||
func IndexTxLookup(db ethdb.Database, from uint64, to uint64) {
|
func IndexTxLookup(db ethdb.Database, from uint64, to uint64) {
|
||||||
// hashTxs calculates transaction hash in advance using the multi-routine's
|
// hashTxs calculates transaction hash in advance using the multi-routine's
|
||||||
// concurrent computing power.
|
// concurrent computing power.
|
||||||
|
|
@ -199,7 +196,7 @@ func IndexTxLookup(db ethdb.Database, from uint64, to uint64) {
|
||||||
// writeIndices injects txlookup indices into the database.
|
// writeIndices injects txlookup indices into the database.
|
||||||
writeIndices := func(batch ethdb.Batch, block *types.Block) {
|
writeIndices := func(batch ethdb.Batch, block *types.Block) {
|
||||||
WriteTxLookupEntries(batch, block)
|
WriteTxLookupEntries(batch, block)
|
||||||
if block.NumberU64()%100000 == 0 {
|
if block.NumberU64() == to-1 || block.NumberU64()%10000 == 0 {
|
||||||
WriteTxIndexTail(batch, block.NumberU64())
|
WriteTxIndexTail(batch, block.NumberU64())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -44,6 +44,9 @@ var (
|
||||||
// txIndexTailKey tracks the oldest block whose transaction indices(txlookup) has been indexed.
|
// txIndexTailKey tracks the oldest block whose transaction indices(txlookup) has been indexed.
|
||||||
txIndexTailKey = []byte("TxIndexTail")
|
txIndexTailKey = []byte("TxIndexTail")
|
||||||
|
|
||||||
|
// fastTxLookupLimitKey tracks the tx indices limit during fast sync
|
||||||
|
fastTxLookupLimitKey = []byte("FSTxLookupLimit")
|
||||||
|
|
||||||
// Data item prefixes (use single byte to avoid mixing data types, avoid `i`, used for indexes).
|
// Data item prefixes (use single byte to avoid mixing data types, avoid `i`, used for indexes).
|
||||||
headerPrefix = []byte("h") // headerPrefix + num (uint64 big endian) + hash -> header
|
headerPrefix = []byte("h") // headerPrefix + num (uint64 big endian) + hash -> header
|
||||||
headerTDSuffix = []byte("t") // headerPrefix + num (uint64 big endian) + hash + headerTDSuffix -> td
|
headerTDSuffix = []byte("t") // headerPrefix + num (uint64 big endian) + hash + headerTDSuffix -> td
|
||||||
|
|
|
||||||
|
|
@ -200,7 +200,7 @@ type BlockChain interface {
|
||||||
CurrentFastBlock() *types.Block
|
CurrentFastBlock() *types.Block
|
||||||
|
|
||||||
// FastSyncCommitHead directly commits the head block to a certain entity.
|
// FastSyncCommitHead directly commits the head block to a certain entity.
|
||||||
FastSyncCommitHead(common.Hash, uint64, uint64) error
|
FastSyncCommitHead(common.Hash) error
|
||||||
|
|
||||||
// InsertChain inserts a batch of blocks into the local chain.
|
// InsertChain inserts a batch of blocks into the local chain.
|
||||||
InsertChain(types.Blocks) (int, error)
|
InsertChain(types.Blocks) (int, error)
|
||||||
|
|
@ -1721,11 +1721,7 @@ func (d *Downloader) commitPivotBlock(result *fetchResult) error {
|
||||||
if _, err := d.blockchain.InsertReceiptChain([]*types.Block{block}, []types.Receipts{result.Receipts}, d.ancientLimit); err != nil {
|
if _, err := d.blockchain.InsertReceiptChain([]*types.Block{block}, []types.Receipts{result.Receipts}, d.ancientLimit); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
// Use origin block number + 1 as the first inserted block number
|
if err := d.blockchain.FastSyncCommitHead(block.Hash()); err != nil {
|
||||||
d.syncStatsLock.RLock()
|
|
||||||
from := d.syncStatsChainOrigin + 1
|
|
||||||
d.syncStatsLock.RUnlock()
|
|
||||||
if err := d.blockchain.FastSyncCommitHead(block.Hash(), from, d.ancientLimit); err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
atomic.StoreInt32(&d.committed, 1)
|
atomic.StoreInt32(&d.committed, 1)
|
||||||
|
|
|
||||||
|
|
@ -218,7 +218,7 @@ func (dl *downloadTester) CurrentFastBlock() *types.Block {
|
||||||
}
|
}
|
||||||
|
|
||||||
// FastSyncCommitHead manually sets the head block to a given hash.
|
// FastSyncCommitHead manually sets the head block to a given hash.
|
||||||
func (dl *downloadTester) FastSyncCommitHead(hash common.Hash, from uint64, ancient uint64) error {
|
func (dl *downloadTester) FastSyncCommitHead(hash common.Hash) error {
|
||||||
// For now only check that the state trie is correct
|
// For now only check that the state trie is correct
|
||||||
if block := dl.GetBlockByHash(hash); block != nil {
|
if block := dl.GetBlockByHash(hash); block != nil {
|
||||||
_, err := trie.NewSecure(block.Root(), trie.NewDatabase(dl.stateDb))
|
_, err := trie.NewSecure(block.Root(), trie.NewDatabase(dl.stateDb))
|
||||||
|
|
|
||||||
|
|
@ -73,6 +73,7 @@ type ProtocolManager struct {
|
||||||
|
|
||||||
txpool txPool
|
txpool txPool
|
||||||
blockchain *core.BlockChain
|
blockchain *core.BlockChain
|
||||||
|
chaindb ethdb.Database
|
||||||
maxPeers int
|
maxPeers int
|
||||||
|
|
||||||
downloader *downloader.Downloader
|
downloader *downloader.Downloader
|
||||||
|
|
@ -106,6 +107,7 @@ func NewProtocolManager(config *params.ChainConfig, checkpoint *params.TrustedCh
|
||||||
eventMux: mux,
|
eventMux: mux,
|
||||||
txpool: txpool,
|
txpool: txpool,
|
||||||
blockchain: blockchain,
|
blockchain: blockchain,
|
||||||
|
chaindb: chaindb,
|
||||||
peers: newPeerSet(),
|
peers: newPeerSet(),
|
||||||
whitelist: whitelist,
|
whitelist: whitelist,
|
||||||
newPeerCh: make(chan *peer),
|
newPeerCh: make(chan *peer),
|
||||||
|
|
|
||||||
19
eth/sync.go
19
eth/sync.go
|
|
@ -22,6 +22,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/eth/downloader"
|
"github.com/ethereum/go-ethereum/eth/downloader"
|
||||||
"github.com/ethereum/go-ethereum/log"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
|
|
@ -186,6 +187,24 @@ func (pm *ProtocolManager) synchronise(peer *peer) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if mode == downloader.FastSync {
|
||||||
|
// Before launch the fast sync, we have to ensure user uses the same
|
||||||
|
// txlookup limit.
|
||||||
|
// The main concern here is: during the fast sync Geth won't index the
|
||||||
|
// block(generate tx indices) before the HEAD-limit. But if user changes
|
||||||
|
// the limit in the next fast sync(e.g. user kill Geth manually and
|
||||||
|
// restart) then it will be hard for Geth to figure out the oldest block
|
||||||
|
// has been indexed. So here for the user-experience wise, it's non-optimal
|
||||||
|
// that user can't change limit during the fast sync. If changed, Geth
|
||||||
|
// will just blindly use the original one.
|
||||||
|
limit := pm.blockchain.TxLookupLimit()
|
||||||
|
if stored := rawdb.ReadFastTxLookupLimit(pm.chaindb); stored == nil {
|
||||||
|
rawdb.WriteFastTxLookupLimit(pm.chaindb, limit)
|
||||||
|
} else if *stored != limit {
|
||||||
|
pm.blockchain.AdjustTxLookupLimit(*stored)
|
||||||
|
log.Warn("Update txLookup limit", "provided", limit, "updated", *stored)
|
||||||
|
}
|
||||||
|
}
|
||||||
// Run the sync cycle, and disable fast sync if we've went past the pivot block
|
// Run the sync cycle, and disable fast sync if we've went past the pivot block
|
||||||
if err := pm.downloader.Synchronise(peer.id, pHead, pTd, mode); err != nil {
|
if err := pm.downloader.Synchronise(peer.id, pHead, pTd, mode); err != nil {
|
||||||
return
|
return
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue