@ -5,6 +5,7 @@ import (
"fmt"
"io"
"math/big"
"runtime"
"sync"
"time"
@ -15,6 +16,7 @@ import (
"github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/logger/glog"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/pow"
"github.com/ethereum/go-ethereum/rlp"
)
@ -100,9 +102,11 @@ type ChainManager struct {
quit chan struct { }
wg sync . WaitGroup
pow pow . PoW
}
func NewChainManager ( blockDb , stateDb common . Database , mux * event . TypeMux ) * ChainManager {
func NewChainManager ( blockDb , stateDb common . Database , pow pow . PoW , mux * event . TypeMux ) * ChainManager {
bc := & ChainManager {
blockDb : blockDb ,
stateDb : stateDb ,
@ -110,6 +114,7 @@ func NewChainManager(blockDb, stateDb common.Database, mux *event.TypeMux) *Chai
eventMux : mux ,
quit : make ( chan struct { } ) ,
cache : NewBlockCache ( blockCacheLimit ) ,
pow : pow ,
}
bc . setLastState ( )
@ -529,10 +534,19 @@ func (self *ChainManager) InsertChain(chain types.Blocks) (int, error) {
stats struct { queued , processed , ignored int }
tstart = time . Now ( )
)
// check the nonce in parallel to the block processing
// this speeds catching up significantly
nonceErrCh := make ( chan error )
go func ( ) {
nonceErrCh <- verifyNonces ( self . pow , chain )
} ( )
for i , block := range chain {
if block == nil {
continue
}
// Setting block.Td regardless of error (known for example) prevents errors down the line
// in the protocol handler
block . Td = new ( big . Int ) . Set ( CalcTD ( block , self . GetBlock ( block . ParentHash ( ) ) ) )
@ -562,11 +576,7 @@ func (self *ChainManager) InsertChain(chain types.Blocks) (int, error) {
continue
}
h := block . Header ( )
glog . V ( logger . Error ) . Infof ( "INVALID block #%v (%x)\n" , h . Number , h . Hash ( ) . Bytes ( ) )
glog . V ( logger . Error ) . Infoln ( err )
glog . V ( logger . Debug ) . Infoln ( block )
blockErr ( block , err )
return i , err
}
@ -620,6 +630,13 @@ func (self *ChainManager) InsertChain(chain types.Blocks) (int, error) {
}
// check and wait for the nonce error channel and
// make sure no nonce error was thrown in the process
err := <- nonceErrCh
if err != nil {
return 0 , err
}
if ( stats . queued > 0 || stats . processed > 0 || stats . ignored > 0 ) && bool ( glog . V ( logger . Info ) ) {
tend := time . Since ( tstart )
start , end := chain [ 0 ] , chain [ len ( chain ) - 1 ]
@ -718,3 +735,63 @@ out:
}
}
}
func blockErr ( block * types . Block , err error ) {
h := block . Header ( )
glog . V ( logger . Error ) . Infof ( "INVALID block #%v (%x)\n" , h . Number , h . Hash ( ) . Bytes ( ) )
glog . V ( logger . Error ) . Infoln ( err )
glog . V ( logger . Debug ) . Infoln ( block )
}
// verifyNonces verifies nonces of the given blocks in parallel and returns
// an error if one of the blocks nonce verifications failed.
func verifyNonces ( pow pow . PoW , blocks [ ] * types . Block ) error {
// Spawn a few workers. They listen for blocks on the in channel
// and send results on done. The workers will exit in the
// background when in is closed.
var (
in = make ( chan * types . Block )
done = make ( chan error , runtime . GOMAXPROCS ( 0 ) )
)
defer close ( in )
for i := 0 ; i < cap ( done ) ; i ++ {
go verifyNonce ( pow , in , done )
}
// Feed blocks to the workers, aborting at the first invalid nonce.
var (
running , i int
block * types . Block
sendin = in
)
for i < len ( blocks ) || running > 0 {
if i == len ( blocks ) {
// Disable sending to in.
sendin = nil
} else {
block = blocks [ i ]
i ++
}
select {
case sendin <- block :
running ++
case err := <- done :
running --
if err != nil {
return err
}
}
}
return nil
}
// verifyNonce is a worker for the verifyNonces method. It will run until
// in is closed.
func verifyNonce ( pow pow . PoW , in <- chan * types . Block , done chan <- error ) {
for block := range in {
if ! pow . Verify ( block ) {
done <- ValidationError ( "Block's nonce is invalid (= %x)" , block . Nonce )
} else {
done <- nil
}
}
}