@ -213,8 +213,9 @@ type worker struct {
running int32 // The indicator whether the consensus engine is running or not.
running int32 // The indicator whether the consensus engine is running or not.
// Test hooks
// Test hooks
newTaskHook func ( * task ) // Method to call upon receiving a new sealing task
newTaskHook func ( * task ) // Method to call upon receiving a new sealing task
fullTaskInterval func ( ) // Method to call before pushing the full sealing task
skipSealHook func ( * task ) bool // Method to decide whether skipping the sealing.
fullTaskHook func ( ) // Method to call before pushing the full sealing task
}
}
func newWorker ( config * params . ChainConfig , engine consensus . Engine , eth Backend , mux * event . TypeMux ) * worker {
func newWorker ( config * params . ChainConfig , engine consensus . Engine , eth Backend , mux * event . TypeMux ) * worker {
@ -329,8 +330,32 @@ func (w *worker) mainLoop() {
w . commitNewWork ( )
w . commitNewWork ( )
case ev := <- w . chainSideCh :
case ev := <- w . chainSideCh :
if _ , exist := w . possibleUncles [ ev . Block . Hash ( ) ] ; exist {
continue
}
// Add side block to possible uncle block set.
// Add side block to possible uncle block set.
w . possibleUncles [ ev . Block . Hash ( ) ] = ev . Block
w . possibleUncles [ ev . Block . Hash ( ) ] = ev . Block
// If our mining block contains less than 2 uncle blocks,
// add the new uncle block if valid and regenerate a mining block.
if w . isRunning ( ) && w . current != nil && w . current . uncles . Cardinality ( ) < 2 {
start := time . Now ( )
if err := w . commitUncle ( w . current , ev . Block . Header ( ) ) ; err == nil {
var uncles [ ] * types . Header
w . current . uncles . Each ( func ( item interface { } ) bool {
hash , ok := item . ( common . Hash )
if ! ok {
return false
}
uncle , exist := w . possibleUncles [ hash ]
if ! exist {
return false
}
uncles = append ( uncles , uncle . Header ( ) )
return true
} )
w . commit ( uncles , nil , true , start )
}
}
case ev := <- w . txsCh :
case ev := <- w . txsCh :
// Apply transactions to the pending state if we're not mining.
// Apply transactions to the pending state if we're not mining.
@ -378,6 +403,10 @@ func (w *worker) seal(t *task, stop <-chan struct{}) {
res * task
res * task
)
)
if w . skipSealHook != nil && w . skipSealHook ( t ) {
return
}
if t . block , err = w . engine . Seal ( w . chain , t . block , stop ) ; t . block != nil {
if t . block , err = w . engine . Seal ( w . chain , t . block , stop ) ; t . block != nil {
log . Info ( "Successfully sealed new block" , "number" , t . block . Number ( ) , "hash" , t . block . Hash ( ) ,
log . Info ( "Successfully sealed new block" , "number" , t . block . Number ( ) , "hash" , t . block . Hash ( ) ,
"elapsed" , common . PrettyDuration ( time . Since ( t . createdAt ) ) )
"elapsed" , common . PrettyDuration ( time . Since ( t . createdAt ) ) )
@ -637,30 +666,9 @@ func (w *worker) commitNewWork() {
delete ( w . possibleUncles , hash )
delete ( w . possibleUncles , hash )
}
}
var (
emptyBlock , fullBlock * types . Block
emptyState , fullState * state . StateDB
)
// Create an empty block based on temporary copied state for sealing in advance without waiting block
// Create an empty block based on temporary copied state for sealing in advance without waiting block
// execution finished.
// execution finished.
emptyState = env . state . Copy ( )
w . commit ( uncles , nil , false , tstart )
if emptyBlock , err = w . engine . Finalize ( w . chain , header , emptyState , nil , uncles , nil ) ; err != nil {
log . Error ( "Failed to finalize block for temporary sealing" , "err" , err )
} else {
// Push empty work in advance without applying pending transaction.
// The reason is transactions execution can cost a lot and sealer need to
// take advantage of this part time.
if w . isRunning ( ) {
select {
case w . taskCh <- & task { receipts : nil , state : emptyState , block : emptyBlock , createdAt : time . Now ( ) } :
log . Info ( "Commit new empty mining work" , "number" , emptyBlock . Number ( ) , "uncles" , len ( uncles ) )
case <- w . exitCh :
log . Info ( "Worker has exited" )
return
}
}
}
// Fill the block with all available pending transactions.
// Fill the block with all available pending transactions.
pending , err := w . eth . TxPool ( ) . Pending ( )
pending , err := w . eth . TxPool ( ) . Pending ( )
@ -676,31 +684,38 @@ func (w *worker) commitNewWork() {
txs := types . NewTransactionsByPriceAndNonce ( w . current . signer , pending )
txs := types . NewTransactionsByPriceAndNonce ( w . current . signer , pending )
env . commitTransactions ( w . mux , txs , w . chain , w . coinbase )
env . commitTransactions ( w . mux , txs , w . chain , w . coinbase )
// Create the full block to seal with the consensus engine
w . commit ( uncles , w . fullTaskHook , true , tstart )
fullState = env . state . Copy ( )
}
if fullBlock , err = w . engine . Finalize ( w . chain , header , fullState , env . txs , uncles , env . receipts ) ; err != nil {
log . Error ( "Failed to finalize block for sealing" , "err" , err )
// commit runs any post-transaction state modifications, assembles the final block
return
// and commits new work if consensus engine is running.
}
func ( w * worker ) commit ( uncles [ ] * types . Header , interval func ( ) , update bool , start time . Time ) error {
// Deep copy receipts here to avoid interaction between different tasks.
// Deep copy receipts here to avoid interaction between different tasks.
cpy := make ( [ ] * types . Receipt , len ( env . receipts ) )
receipts := make ( [ ] * types . Receipt , len ( w . current . receipts ) )
for i , l := range env . receipts {
for i , l := range w . current . receipts {
cpy [ i ] = new ( types . Receipt )
receipts [ i ] = new ( types . Receipt )
* cpy [ i ] = * l
* receipts [ i ] = * l
}
s := w . current . state . Copy ( )
block , err := w . engine . Finalize ( w . chain , w . current . header , s , w . current . txs , uncles , w . current . receipts )
if err != nil {
return err
}
}
// We only care about logging if we're actually mining.
if w . isRunning ( ) {
if w . isRunning ( ) {
if w . fullTaskInterval != nil {
if i nterval != nil {
w . fullTaskInterval ( )
i nterval( )
}
}
select {
select {
case w . taskCh <- & task { receipts : cpy , state : fullState , block : fullBlock , createdAt : time . Now ( ) } :
case w . taskCh <- & task { receipts : receipts , state : s , block : block , createdAt : time . Now ( ) } :
w . unconfirmed . Shift ( fullBlock . NumberU64 ( ) - 1 )
w . unconfirmed . Shift ( block . NumberU64 ( ) - 1 )
log . Info ( "Commit new full mining work" , "number" , fullBlock . Number ( ) , "txs" , env . tcount , "uncles" , len ( uncles ) , "elapsed" , common . PrettyDuration ( time . Since ( tstart ) ) )
log . Info ( "Commit new mining work" , "number" , block . Number ( ) , "txs" , w . current . tcount , "uncles" , len ( uncles ) ,
"elapsed" , common . PrettyDuration ( time . Since ( start ) ) )
case <- w . exitCh :
case <- w . exitCh :
log . Info ( "Worker has exited" )
log . Info ( "Worker has exited" )
}
}
}
}
w . updateSnapshot ( )
if update {
w . updateSnapshot ( )
}
return nil
}
}