@ -23,7 +23,6 @@ import (
"io"
"math/big"
mrand "math/rand"
"runtime"
"sync"
"sync/atomic"
"time"
@ -515,10 +514,13 @@ func (bc *BlockChain) GetBodyRLP(hash common.Hash) rlp.RawValue {
return body
}
// HasBlock checks if a block is fully present in the database or not, caching
// it if present.
func ( bc * BlockChain ) HasBlock ( hash common . Hash ) bool {
return bc . GetBlockByHash ( hash ) != nil
// HasBlock checks if a block is fully present in the database or not.
func ( bc * BlockChain ) HasBlock ( hash common . Hash , number uint64 ) bool {
if bc . blockCache . Contains ( hash ) {
return true
}
ok , _ := bc . chainDb . Has ( blockBodyKey ( hash , number ) )
return ok
}
// HasBlockAndState checks if a block and associated state trie is fully present
@ -693,108 +695,73 @@ func SetReceiptsData(config *params.ChainConfig, block *types.Block, receipts ty
// InsertReceiptChain attempts to complete an already existing header chain with
// transaction and receipt data.
// XXX should this be moved to the test?
func ( bc * BlockChain ) InsertReceiptChain ( blockChain types . Blocks , receiptChain [ ] types . Receipts ) ( int , error ) {
bc . wg . Add ( 1 )
defer bc . wg . Done ( )
// Do a sanity check that the provided chain is actually ordered and linked
for i := 1 ; i < len ( blockChain ) ; i ++ {
if blockChain [ i ] . NumberU64 ( ) != blockChain [ i - 1 ] . NumberU64 ( ) + 1 || blockChain [ i ] . ParentHash ( ) != blockChain [ i - 1 ] . Hash ( ) {
// Chain broke ancestry, log a messge (programming error) and skip insertion
log . Error ( "Non contiguous receipt insert" , "number" , blockChain [ i ] . Number ( ) , "hash" , blockChain [ i ] . Hash ( ) , "parent" , blockChain [ i ] . ParentHash ( ) ,
"prevnumber" , blockChain [ i - 1 ] . Number ( ) , "prevhash" , blockChain [ i - 1 ] . Hash ( ) )
return 0 , fmt . Errorf ( "non contiguous insert: item %d is #%d [%x…], item %d is #%d [%x…] (parent [%x…])" , i - 1 , blockChain [ i - 1 ] . NumberU64 ( ) ,
blockChain [ i - 1 ] . Hash ( ) . Bytes ( ) [ : 4 ] , i , blockChain [ i ] . NumberU64 ( ) , blockChain [ i ] . Hash ( ) . Bytes ( ) [ : 4 ] , blockChain [ i ] . ParentHash ( ) . Bytes ( ) [ : 4 ] )
}
}
// Pre-checks passed, start the block body and receipt imports
bc . wg . Add ( 1 )
defer bc . wg . Done ( )
// Collect some import statistics to report on
stats := struct { processed , ignored int32 } { }
start := time . Now ( )
// Create the block importing task queue and worker functions
tasks := make ( chan int , len ( blockChain ) )
for i := 0 ; i < len ( blockChain ) && i < len ( receiptChain ) ; i ++ {
tasks <- i
}
close ( tasks )
errs , failed := make ( [ ] error , len ( tasks ) ) , int32 ( 0 )
process := func ( worker int ) {
for index := range tasks {
block , receipts := blockChain [ index ] , receiptChain [ index ]
var (
stats = struct { processed , ignored int32 } { }
start = time . Now ( )
bytes = 0
batch = bc . chainDb . NewBatch ( )
)
for i , block := range blockChain {
receipts := receiptChain [ i ]
// Short circuit insertion if shutting down or processing failed
if atomic . LoadInt32 ( & bc . procInterrupt ) == 1 {
return 0 , nil
}
// Short circuit if the owner header is unknown
if ! bc . HasHeader ( block . Hash ( ) , block . NumberU64 ( ) ) {
return i , fmt . Errorf ( "containing header #%d [%x…] unknown" , block . Number ( ) , block . Hash ( ) . Bytes ( ) [ : 4 ] )
}
// Skip if the entire data is already known
if bc . HasBlock ( block . Hash ( ) , block . NumberU64 ( ) ) {
stats . ignored ++
continue
}
// Compute all the non-consensus fields of the receipts
SetReceiptsData ( bc . config , 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 )
}
if err := WriteBlockReceipts ( batch , block . Hash ( ) , block . NumberU64 ( ) , receipts ) ; err != nil {
return i , fmt . Errorf ( "failed to write block receipts: %v" , err )
}
if err := WriteTxLookupEntries ( batch , block ) ; err != nil {
return i , fmt . Errorf ( "failed to write lookup metadata: %v" , err )
}
stats . processed ++
// Short circuit insertion if shutting down or processing failed
if atomic . LoadInt32 ( & bc . procInterrupt ) == 1 {
return
}
if atomic . LoadInt32 ( & failed ) > 0 {
return
}
// Short circuit if the owner header is unknown
if ! bc . HasHeader ( block . Hash ( ) ) {
errs [ index ] = fmt . Errorf ( "containing header #%d [%x…] unknown" , block . Number ( ) , block . Hash ( ) . Bytes ( ) [ : 4 ] )
atomic . AddInt32 ( & failed , 1 )
return
}
// Skip if the entire data is already known
if bc . HasBlock ( block . Hash ( ) ) {
atomic . AddInt32 ( & stats . ignored , 1 )
continue
}
// Compute all the non-consensus fields of the receipts
SetReceiptsData ( bc . config , block , receipts )
// Write all the data out into the database
if err := WriteBody ( bc . chainDb , block . Hash ( ) , block . NumberU64 ( ) , block . Body ( ) ) ; err != nil {
errs [ index ] = fmt . Errorf ( "failed to write block body: %v" , err )
atomic . AddInt32 ( & failed , 1 )
log . Crit ( "Failed to write block body" , "err" , err )
return
if batch . ValueSize ( ) >= ethdb . IdealBatchSize {
if err := batch . Write ( ) ; err != nil {
return 0 , err
}
if err := WriteBlockReceipts ( bc . chainDb , block . Hash ( ) , block . NumberU64 ( ) , receipts ) ; err != nil {
errs [ index ] = fmt . Errorf ( "failed to write block receipts: %v" , err )
atomic . AddInt32 ( & failed , 1 )
log . Crit ( "Failed to write block receipts" , "err" , err )
return
}
if err := WriteTxLookupEntries ( bc . chainDb , block ) ; err != nil {
errs [ index ] = fmt . Errorf ( "failed to write lookup metadata: %v" , err )
atomic . AddInt32 ( & failed , 1 )
log . Crit ( "Failed to write lookup metadata" , "err" , err )
return
}
atomic . AddInt32 ( & stats . processed , 1 )
bytes += batch . ValueSize ( )
batch = bc . chainDb . NewBatch ( )
}
}
// Start as many worker threads as goroutines allowed
pending := new ( sync . WaitGroup )
for i := 0 ; i < runtime . GOMAXPROCS ( 0 ) ; i ++ {
pending . Add ( 1 )
go func ( id int ) {
defer pending . Done ( )
process ( id )
} ( i )
}
pending . Wait ( )
// If anything failed, report
if failed > 0 {
for i , err := range errs {
if err != nil {
return i , err
}
if batch . ValueSize ( ) > 0 {
bytes += batch . ValueSize ( )
if err := batch . Write ( ) ; err != nil {
return 0 , err
}
}
if atomic . LoadInt32 ( & bc . procInterrupt ) == 1 {
log . Debug ( "Premature abort during receipts processing" )
return 0 , nil
}
// Update the head fast sync block if better
bc . mu . Lock ( )
head := blockChain [ len ( errs ) - 1 ]
head := blockChain [ len ( blockChain ) - 1 ]
if td := bc . GetTd ( head . Hash ( ) , head . NumberU64 ( ) ) ; td != nil { // Rewind may have occurred, skip in that case
if bc . GetTd ( bc . currentFastBlock . Hash ( ) , bc . currentFastBlock . NumberU64 ( ) ) . Cmp ( td ) < 0 {
if err := WriteHeadFastBlockHash ( bc . chainDb , head . Hash ( ) ) ; err != nil {
@ -805,16 +772,18 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [
}
bc . mu . Unlock ( )
// Report some public statistics so the user has a clue what's going on
last := blockChain [ len ( blockChain ) - 1 ]
log . Info ( "Imported new block receipts" , "count" , stats . processed , "elapsed" , common . PrettyDuration ( time . Since ( start ) ) ,
"number" , last . Number ( ) , "hash" , last . Hash ( ) , "ignored" , stats . ignored )
log . Info ( "Imported new block receipts" ,
"count" , stats . processed ,
"elapsed" , common . PrettyDuration ( time . Since ( start ) ) ,
"bytes" , bytes ,
"number" , head . Number ( ) ,
"hash" , head . Hash ( ) ,
"ignored" , stats . ignored )
return 0 , nil
}
// WriteBlock writes the block to the chain.
func ( bc * BlockChain ) WriteBlock ( block * types . Block ) ( status WriteStatus , err error ) {
func ( bc * BlockChain ) WriteBlockAndState ( block * types . Block , receipts [ ] * types . Receipt , state * state . StateDB ) ( status WriteStatus , err error ) {
bc . wg . Add ( 1 )
defer bc . wg . Done ( )
@ -827,7 +796,7 @@ func (bc *BlockChain) WriteBlock(block *types.Block) (status WriteStatus, err er
bc . mu . Lock ( )
defer bc . mu . Unlock ( )
if bc . HasBlock ( block . Hash ( ) ) {
if bc . HasBlock ( block . Hash ( ) , block . NumberU64 ( ) ) {
log . Trace ( "Block existed" , "hash" , block . Hash ( ) )
return
}
@ -837,10 +806,18 @@ func (bc *BlockChain) WriteBlock(block *types.Block) (status WriteStatus, err er
// Irrelevant of the canonical status, write the block itself to the database
if err := bc . hc . WriteTd ( block . Hash ( ) , block . NumberU64 ( ) , externTd ) ; err != nil {
log . Crit ( "Failed to write block total difficulty" , "err" , err )
return NonStatTy , err
}
if err := WriteBlock ( bc . chainDb , block ) ; err != nil {
log . Crit ( "Failed to write block contents" , "err" , err )
// Write other block data using a batch.
batch := bc . chainDb . NewBatch ( )
if err := WriteBlock ( batch , block ) ; err != nil {
return NonStatTy , err
}
if _ , err := state . CommitTo ( batch , bc . config . IsEIP158 ( block . Number ( ) ) ) ; err != nil {
return NonStatTy , err
}
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
@ -853,15 +830,28 @@ func (bc *BlockChain) WriteBlock(block *types.Block) (status WriteStatus, err er
return NonStatTy , err
}
}
bc . insert ( block ) // Insert the block as the new head of the chain
// Write the positional metadata for transaction and receipt lookups
if err := WriteTxLookupEntries ( batch , block ) ; err != nil {
return NonStatTy , err
}
// Write hash preimages
if err := WritePreimages ( bc . chainDb , block . NumberU64 ( ) , state . Preimages ( ) ) ; err != nil {
return NonStatTy , err
}
status = CanonStatTy
} else {
status = SideStatTy
}
if err := batch . Write ( ) ; err != nil {
return NonStatTy , err
}
// Set new head.
if status == CanonStatTy {
bc . insert ( block )
}
bc . futureBlocks . Remove ( block . Hash ( ) )
return
return status , nil
}
// InsertChain will attempt to insert the given chain in to the canonical chain or, otherwise, create a fork. If an error is returned
@ -975,29 +965,18 @@ func (bc *BlockChain) InsertChain(chain types.Blocks) (int, error) {
bc . reportBlock ( block , receipts , err )
return i , err
}
// Write state changes to database
if _ , err = state . CommitTo ( bc . chainDb , bc . config . IsEIP158 ( block . Number ( ) ) ) ; err != nil {
return i , err
}
// coalesce logs for later processing
coalescedLogs = append ( coalescedLogs , logs ... )
if err = WriteBlockReceipts ( bc . chainDb , block . Hash ( ) , block . NumberU64 ( ) , receipts ) ; err != nil {
return i , err
}
// write the block to the chain and get the status
status , err := bc . WriteBlock ( block )
// Write the block to the chain and get the status.
status , err := bc . WriteBlockAndState ( block , receipts , state )
if err != nil {
return i , err
}
switch status {
case CanonStatTy :
log . Debug ( "Inserted new block" , "number" , block . Number ( ) , "hash" , block . Hash ( ) , "uncles" , len ( block . Uncles ( ) ) ,
"txs" , len ( block . Transactions ( ) ) , "gas" , block . GasUsed ( ) , "elapsed" , common . PrettyDuration ( time . Since ( bstart ) ) )
// coalesce logs for later processing
coalescedLogs = append ( coalescedLogs , logs ... )
blockInsertTimer . UpdateSince ( bstart )
events = append ( events , ChainEvent { block , block . Hash ( ) , logs } )
// We need some control over the mining operation. Acquiring locks and waiting
@ -1006,15 +985,6 @@ func (bc *BlockChain) InsertChain(chain types.Blocks) (int, error) {
if bc . LastBlockHash ( ) == block . Hash ( ) {
events = append ( events , ChainHeadEvent { block } )
}
// Write the positional metadata for transaction and receipt lookups
if err := WriteTxLookupEntries ( bc . chainDb , block ) ; err != nil {
return i , err
}
// Write hash preimages
if err := WritePreimages ( bc . chainDb , block . NumberU64 ( ) , state . Preimages ( ) ) ; err != nil {
return i , err
}
case SideStatTy :
log . Debug ( "Inserted forked block" , "number" , block . Number ( ) , "hash" , block . Hash ( ) , "diff" , block . Difficulty ( ) , "elapsed" ,
common . PrettyDuration ( time . Since ( bstart ) ) , "txs" , len ( block . Transactions ( ) ) , "gas" , block . GasUsed ( ) , "uncles" , len ( block . Uncles ( ) ) )
@ -1357,8 +1327,8 @@ func (bc *BlockChain) GetHeaderByHash(hash common.Hash) *types.Header {
// HasHeader checks if a block header is present in the database or not, caching
// it if present.
func ( bc * BlockChain ) HasHeader ( hash common . Hash ) bool {
return bc . hc . HasHeader ( hash )
func ( bc * BlockChain ) HasHeader ( hash common . Hash , number uint64 ) bool {
return bc . hc . HasHeader ( hash , number )
}
// GetBlockHashesFromHash retrieves a number of block hashes starting at a given