2015-02-04 07:52:59 -06:00
package miner
import (
"fmt"
"math/big"
"sort"
2015-02-15 09:16:27 -06:00
"sync"
2015-03-26 11:45:03 -05:00
"sync/atomic"
2015-02-04 07:52:59 -06:00
2015-03-18 07:00:01 -05:00
"github.com/ethereum/go-ethereum/common"
2015-02-04 07:52:59 -06:00
"github.com/ethereum/go-ethereum/core"
2015-03-23 12:27:05 -05:00
"github.com/ethereum/go-ethereum/core/state"
2015-02-04 07:52:59 -06:00
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/event"
2015-03-01 09:09:59 -06:00
"github.com/ethereum/go-ethereum/logger"
2015-04-04 16:04:19 -05:00
"github.com/ethereum/go-ethereum/logger/glog"
2015-02-04 07:52:59 -06:00
"github.com/ethereum/go-ethereum/pow"
"gopkg.in/fatih/set.v0"
)
2015-03-01 09:09:59 -06:00
var jsonlogger = logger . NewJsonLogger ( )
2015-02-04 07:52:59 -06:00
type environment struct {
totalUsedGas * big . Int
state * state . StateDB
coinbase * state . StateObject
block * types . Block
2015-03-23 06:12:49 -05:00
family * set . Set
2015-02-04 07:52:59 -06:00
uncles * set . Set
}
2015-02-17 05:24:51 -06:00
func env ( block * types . Block , eth core . Backend ) * environment {
2015-03-06 11:26:16 -06:00
state := state . New ( block . Root ( ) , eth . StateDb ( ) )
2015-02-04 07:52:59 -06:00
env := & environment {
totalUsedGas : new ( big . Int ) ,
state : state ,
block : block ,
2015-03-23 06:12:49 -05:00
family : set . New ( ) ,
2015-02-04 07:52:59 -06:00
uncles : set . New ( ) ,
coinbase : state . GetOrNewStateObject ( block . Coinbase ( ) ) ,
}
return env
}
2015-02-13 10:23:09 -06:00
type Work struct {
2015-02-28 13:58:37 -06:00
Number uint64
2015-03-03 14:04:31 -06:00
Nonce uint64
2015-02-28 13:58:37 -06:00
MixDigest [ ] byte
SeedHash [ ] byte
2015-02-13 10:23:09 -06:00
}
2015-02-04 17:05:47 -06:00
type Agent interface {
2015-02-09 09:20:34 -06:00
Work ( ) chan <- * types . Block
2015-03-24 04:34:06 -05:00
SetReturnCh ( chan <- * types . Block )
2015-02-09 09:20:34 -06:00
Stop ( )
2015-02-14 09:52:14 -06:00
Start ( )
2015-03-20 11:42:09 -05:00
GetHashRate ( ) int64
2015-02-04 17:05:47 -06:00
}
2015-02-04 07:52:59 -06:00
type worker struct {
2015-03-26 11:45:03 -05:00
mu sync . Mutex
2015-02-09 09:20:34 -06:00
agents [ ] Agent
2015-03-24 04:34:06 -05:00
recv chan * types . Block
2015-02-04 07:52:59 -06:00
mux * event . TypeMux
quit chan struct { }
pow pow . PoW
2015-04-05 11:57:03 -05:00
eth core . Backend
chain * core . ChainManager
proc * core . BlockProcessor
2015-03-18 07:00:01 -05:00
coinbase common . Address
2015-05-09 05:04:00 -05:00
gasPrice * big . Int
2015-04-05 11:57:03 -05:00
extra [ ] byte
2015-02-04 07:52:59 -06:00
2015-04-07 05:32:55 -05:00
currentMu sync . Mutex
current * environment
2015-02-13 10:23:09 -06:00
2015-03-23 06:12:49 -05:00
uncleMu sync . Mutex
possibleUncles map [ common . Hash ] * types . Block
2015-04-07 05:32:55 -05:00
txQueueMu sync . Mutex
txQueue map [ common . Hash ] * types . Transaction
2015-04-22 03:58:43 -05:00
// atomic status counters
mining int32
atWork int32
2015-02-04 07:52:59 -06:00
}
2015-03-18 07:00:01 -05:00
func newWorker ( coinbase common . Address , eth core . Backend ) * worker {
2015-04-07 05:32:55 -05:00
worker := & worker {
2015-03-23 06:12:49 -05:00
eth : eth ,
mux : eth . EventMux ( ) ,
2015-03-24 04:34:06 -05:00
recv : make ( chan * types . Block ) ,
2015-05-09 05:04:00 -05:00
gasPrice : new ( big . Int ) ,
2015-03-23 06:12:49 -05:00
chain : eth . ChainManager ( ) ,
proc : eth . BlockProcessor ( ) ,
possibleUncles : make ( map [ common . Hash ] * types . Block ) ,
coinbase : coinbase ,
2015-04-07 05:32:55 -05:00
txQueue : make ( map [ common . Hash ] * types . Transaction ) ,
2015-04-07 17:30:23 -05:00
quit : make ( chan struct { } ) ,
2015-02-09 09:20:34 -06:00
}
2015-04-07 05:32:55 -05:00
go worker . update ( )
go worker . wait ( )
worker . commitNewWork ( )
return worker
2015-02-09 09:20:34 -06:00
}
2015-04-07 05:32:55 -05:00
func ( self * worker ) pendingState ( ) * state . StateDB {
self . currentMu . Lock ( )
defer self . currentMu . Unlock ( )
return self . current . state
}
2015-02-13 10:23:09 -06:00
2015-04-07 05:32:55 -05:00
func ( self * worker ) pendingBlock ( ) * types . Block {
self . currentMu . Lock ( )
defer self . currentMu . Unlock ( )
2015-02-13 10:23:09 -06:00
2015-04-07 05:32:55 -05:00
return self . current . block
}
func ( self * worker ) start ( ) {
2015-05-09 05:32:36 -05:00
self . mu . Lock ( )
defer self . mu . Unlock ( )
2015-02-14 09:52:14 -06:00
// spin up agents
for _ , agent := range self . agents {
agent . Start ( )
}
2015-04-22 03:58:43 -05:00
atomic . StoreInt32 ( & self . mining , 1 )
2015-02-09 09:20:34 -06:00
}
func ( self * worker ) stop ( ) {
2015-05-09 05:32:36 -05:00
self . mu . Lock ( )
defer self . mu . Unlock ( )
2015-04-22 03:58:43 -05:00
if atomic . LoadInt32 ( & self . mining ) == 1 {
2015-04-07 17:30:23 -05:00
// stop all agents
for _ , agent := range self . agents {
agent . Stop ( )
}
}
2015-02-13 10:23:09 -06:00
2015-04-22 03:58:43 -05:00
atomic . StoreInt32 ( & self . mining , 0 )
atomic . StoreInt32 ( & self . atWork , 0 )
2015-02-09 09:20:34 -06:00
}
func ( self * worker ) register ( agent Agent ) {
2015-05-09 05:32:36 -05:00
self . mu . Lock ( )
defer self . mu . Unlock ( )
2015-02-04 07:52:59 -06:00
self . agents = append ( self . agents , agent )
2015-03-24 04:34:06 -05:00
agent . SetReturnCh ( self . recv )
2015-02-04 07:52:59 -06:00
}
func ( self * worker ) update ( ) {
2015-04-07 05:32:55 -05:00
events := self . mux . Subscribe ( core . ChainHeadEvent { } , core . ChainSideEvent { } , core . TxPreEvent { } )
2015-02-04 07:52:59 -06:00
out :
for {
select {
case event := <- events . Chan ( ) :
2015-02-19 15:33:22 -06:00
switch ev := event . ( type ) {
2015-03-06 08:50:44 -06:00
case core . ChainHeadEvent :
2015-02-14 09:52:14 -06:00
self . commitNewWork ( )
2015-03-23 10:14:33 -05:00
case core . ChainSideEvent :
2015-03-23 06:12:49 -05:00
self . uncleMu . Lock ( )
self . possibleUncles [ ev . Block . Hash ( ) ] = ev . Block
self . uncleMu . Unlock ( )
2015-04-07 05:32:55 -05:00
case core . TxPreEvent :
2015-04-22 03:58:43 -05:00
if atomic . LoadInt32 ( & self . mining ) == 0 {
2015-04-07 05:32:55 -05:00
self . commitNewWork ( )
}
2015-02-04 07:52:59 -06:00
}
case <- self . quit :
break out
}
}
2015-02-13 10:23:09 -06:00
events . Unsubscribe ( )
2015-02-04 07:52:59 -06:00
}
2015-02-09 09:20:34 -06:00
func ( self * worker ) wait ( ) {
for {
2015-03-24 04:34:06 -05:00
for block := range self . recv {
2015-04-22 03:58:43 -05:00
atomic . AddInt32 ( & self . atWork , - 1 )
2015-03-26 11:45:03 -05:00
if block == nil {
continue
}
2015-04-29 07:00:24 -05:00
if _ , err := self . chain . InsertChain ( types . Blocks { block } ) ; err == nil {
2015-03-24 04:34:06 -05:00
for _ , uncle := range block . Uncles ( ) {
delete ( self . possibleUncles , uncle . Hash ( ) )
2015-02-14 09:52:14 -06:00
}
2015-03-24 04:34:06 -05:00
self . mux . Post ( core . NewMinedBlockEvent { block } )
2015-03-25 07:51:12 -05:00
2015-04-17 08:00:37 -05:00
glog . V ( logger . Info ) . Infof ( "🔨 Mined block #%v" , block . Number ( ) )
2015-04-02 05:58:17 -05:00
2015-03-25 07:51:12 -05:00
jsonlogger . LogJson ( & logger . EthMinerNewBlock {
BlockHash : block . Hash ( ) . Hex ( ) ,
BlockNumber : block . Number ( ) ,
ChainHeadHash : block . ParentHeaderHash . Hex ( ) ,
BlockPrevHash : block . ParentHeaderHash . Hex ( ) ,
} )
2015-03-24 04:34:06 -05:00
} else {
self . commitNewWork ( )
2015-02-13 10:23:09 -06:00
}
2015-02-09 09:20:34 -06:00
}
}
}
func ( self * worker ) push ( ) {
2015-04-22 03:58:43 -05:00
if atomic . LoadInt32 ( & self . mining ) == 1 {
2015-02-14 09:52:14 -06:00
self . current . block . Header ( ) . GasUsed = self . current . totalUsedGas
2015-02-13 10:23:09 -06:00
self . current . block . SetRoot ( self . current . state . Root ( ) )
2015-02-04 07:52:59 -06:00
2015-02-14 09:52:14 -06:00
// push new work to agents
2015-02-13 10:23:09 -06:00
for _ , agent := range self . agents {
2015-04-22 03:58:43 -05:00
atomic . AddInt32 ( & self . atWork , 1 )
2015-03-26 11:45:03 -05:00
2015-04-15 10:04:22 -05:00
if agent . Work ( ) != nil {
agent . Work ( ) <- self . current . block . Copy ( )
} else {
common . Report ( fmt . Sprintf ( "%v %T\n" , agent , agent ) )
}
2015-02-13 10:23:09 -06:00
}
2015-02-04 07:52:59 -06:00
}
}
2015-04-07 05:32:55 -05:00
func ( self * worker ) makeCurrent ( ) {
2015-03-05 02:14:58 -06:00
block := self . chain . NewBlock ( self . coinbase )
2015-04-04 06:27:17 -05:00
if block . Time ( ) == self . chain . CurrentBlock ( ) . Time ( ) {
block . Header ( ) . Time ++
}
2015-04-05 11:57:03 -05:00
block . Header ( ) . Extra = self . extra
2015-03-05 02:14:58 -06:00
self . current = env ( block , self . eth )
2015-03-23 06:12:49 -05:00
for _ , ancestor := range self . chain . GetAncestors ( block , 7 ) {
self . current . family . Add ( ancestor . Hash ( ) )
}
2015-02-04 07:52:59 -06:00
parent := self . chain . GetBlock ( self . current . block . ParentHash ( ) )
2015-04-26 04:19:40 -05:00
self . current . coinbase . SetGasPool ( core . CalcGasLimit ( parent ) )
2015-04-07 05:32:55 -05:00
}
2015-05-09 05:04:00 -05:00
func ( w * worker ) setGasPrice ( p * big . Int ) {
w . mu . Lock ( )
defer w . mu . Unlock ( )
w . gasPrice = p
}
2015-04-07 05:32:55 -05:00
func ( self * worker ) commitNewWork ( ) {
self . mu . Lock ( )
defer self . mu . Unlock ( )
self . uncleMu . Lock ( )
defer self . uncleMu . Unlock ( )
self . currentMu . Lock ( )
defer self . currentMu . Unlock ( )
self . makeCurrent ( )
2015-02-04 07:52:59 -06:00
transactions := self . eth . TxPool ( ) . GetTransactions ( )
sort . Sort ( types . TxByNonce { transactions } )
// Keep track of transactions which return errors so they can be removed
2015-03-23 10:35:44 -05:00
var (
2015-04-21 04:26:04 -05:00
remove = set . New ( )
tcount = 0
ignoredTransactors = set . New ( )
2015-03-23 10:35:44 -05:00
)
2015-04-21 15:03:32 -05:00
2015-05-09 05:13:46 -05:00
const pct = int64 ( 90 )
// calculate the minimal gas price the miner accepts when sorting out transactions.
minprice := gasprice ( self . gasPrice , pct )
2015-04-13 15:58:53 -05:00
for _ , tx := range transactions {
2015-04-21 04:26:04 -05:00
// We can skip err. It has already been validated in the tx pool
from , _ := tx . From ( )
2015-05-09 05:04:00 -05:00
// check if it falls within margin
if tx . GasPrice ( ) . Cmp ( minprice ) < 0 {
// ignore the transaction and transactor. We ignore the transactor
// because nonce will fail after ignoring this transaction so there's
// no point
ignoredTransactors . Add ( from )
glog . V ( logger . Info ) . Infof ( "transaction(%x) below gas price (<%d%% ask price). All sequential txs from this address(%x) will fail\n" , tx . Hash ( ) . Bytes ( ) [ : 4 ] , pct , from [ : 4 ] )
continue
}
2015-04-21 04:26:04 -05:00
// Move on to the next transaction when the transactor is in ignored transactions set
// This may occur when a transaction hits the gas limit. When a gas limit is hit and
// the transaction is processed (that could potentially be included in the block) it
// will throw a nonce error because the previous transaction hasn't been processed.
// Therefor we need to ignore any transaction after the ignored one.
if ignoredTransactors . Has ( from ) {
continue
}
2015-04-08 10:14:58 -05:00
self . current . state . StartRecord ( tx . Hash ( ) , common . Hash { } , 0 )
2015-02-04 07:52:59 -06:00
err := self . commitTransaction ( tx )
switch {
2015-04-07 17:30:23 -05:00
case core . IsNonceErr ( err ) || core . IsInvalidTxErr ( err ) :
2015-02-15 09:16:27 -06:00
// Remove invalid transactions
2015-03-18 07:00:01 -05:00
from , _ := tx . From ( )
2015-04-21 04:26:04 -05:00
2015-03-18 07:00:01 -05:00
self . chain . TxState ( ) . RemoveNonce ( from , tx . Nonce ( ) )
2015-04-07 17:30:23 -05:00
remove . Add ( tx . Hash ( ) )
2015-04-04 16:04:19 -05:00
2015-04-13 15:58:53 -05:00
if glog . V ( logger . Detail ) {
2015-04-04 16:04:19 -05:00
glog . Infof ( "TX (%x) failed, will be removed: %v\n" , tx . Hash ( ) . Bytes ( ) [ : 4 ] , err )
}
2015-02-19 15:33:22 -06:00
case state . IsGasLimitErr ( err ) :
2015-04-21 04:26:04 -05:00
from , _ := tx . From ( )
// ignore the transactor so no nonce errors will be thrown for this account
// next time the worker is run, they'll be picked up again.
ignoredTransactors . Add ( from )
2015-04-23 03:53:48 -05:00
glog . V ( logger . Detail ) . Infof ( "Gas limit reached for (%x) in this block. Continue to try smaller txs\n" , from [ : 4 ] )
2015-03-23 10:35:44 -05:00
default :
tcount ++
2015-02-04 07:52:59 -06:00
}
}
2015-03-23 12:27:05 -05:00
var (
uncles [ ] * types . Header
badUncles [ ] common . Hash
)
2015-03-23 06:12:49 -05:00
for hash , uncle := range self . possibleUncles {
2015-03-23 10:14:33 -05:00
if len ( uncles ) == 2 {
2015-03-23 06:12:49 -05:00
break
}
if err := self . commitUncle ( uncle . Header ( ) ) ; err != nil {
2015-04-15 05:12:20 -05:00
if glog . V ( logger . Ridiculousness ) {
glog . V ( logger . Detail ) . Infof ( "Bad uncle found and will be removed (%x)\n" , hash [ : 4 ] )
glog . V ( logger . Detail ) . Infoln ( uncle )
}
2015-03-23 12:27:05 -05:00
badUncles = append ( badUncles , hash )
2015-03-23 06:12:49 -05:00
} else {
2015-04-07 07:57:04 -05:00
glog . V ( logger . Debug ) . Infof ( "commiting %x as uncle\n" , hash [ : 4 ] )
2015-03-23 10:14:33 -05:00
uncles = append ( uncles , uncle . Header ( ) )
2015-03-23 06:12:49 -05:00
}
}
2015-04-07 07:57:04 -05:00
// We only care about logging if we're actually mining
2015-04-22 03:58:43 -05:00
if atomic . LoadInt32 ( & self . mining ) == 1 {
2015-04-07 07:57:04 -05:00
glog . V ( logger . Info ) . Infof ( "commit new work on block %v with %d txs & %d uncles\n" , self . current . block . Number ( ) , tcount , len ( uncles ) )
}
2015-03-23 12:27:05 -05:00
for _ , hash := range badUncles {
delete ( self . possibleUncles , hash )
}
2015-03-23 10:35:44 -05:00
2015-03-23 10:14:33 -05:00
self . current . block . SetUncles ( uncles )
2015-03-23 06:12:49 -05:00
2015-04-01 14:18:41 -05:00
core . AccumulateRewards ( self . current . state , self . current . block )
2015-02-04 07:52:59 -06:00
2015-04-01 16:58:26 -05:00
self . current . state . Update ( )
2015-04-07 05:32:55 -05:00
2015-02-09 09:20:34 -06:00
self . push ( )
2015-02-04 07:52:59 -06:00
}
var (
inclusionReward = new ( big . Int ) . Div ( core . BlockReward , big . NewInt ( 32 ) )
_uncleReward = new ( big . Int ) . Mul ( core . BlockReward , big . NewInt ( 15 ) )
uncleReward = new ( big . Int ) . Div ( _uncleReward , big . NewInt ( 16 ) )
)
func ( self * worker ) commitUncle ( uncle * types . Header ) error {
2015-03-18 07:00:01 -05:00
if self . current . uncles . Has ( uncle . Hash ( ) ) {
2015-02-04 07:52:59 -06:00
// Error not unique
return core . UncleError ( "Uncle not unique" )
}
2015-03-18 07:00:01 -05:00
self . current . uncles . Add ( uncle . Hash ( ) )
2015-02-04 07:52:59 -06:00
2015-03-23 06:12:49 -05:00
if ! self . current . family . Has ( uncle . ParentHash ) {
2015-02-04 07:52:59 -06:00
return core . UncleError ( fmt . Sprintf ( "Uncle's parent unknown (%x)" , uncle . ParentHash [ 0 : 4 ] ) )
}
2015-03-23 06:12:49 -05:00
if self . current . family . Has ( uncle . Hash ( ) ) {
return core . UncleError ( fmt . Sprintf ( "Uncle already in family (%x)" , uncle . Hash ( ) ) )
2015-02-04 07:52:59 -06:00
}
return nil
}
func ( self * worker ) commitTransaction ( tx * types . Transaction ) error {
2015-03-11 10:32:37 -05:00
snap := self . current . state . Copy ( )
2015-02-15 09:16:27 -06:00
receipt , _ , err := self . proc . ApplyTransaction ( self . current . coinbase , self . current . state , self . current . block , tx , self . current . totalUsedGas , true )
2015-03-11 10:32:37 -05:00
if err != nil && ( core . IsNonceErr ( err ) || state . IsGasLimitErr ( err ) || core . IsInvalidTxErr ( err ) ) {
self . current . state . Set ( snap )
2015-02-04 07:52:59 -06:00
return err
}
self . current . block . AddTransaction ( tx )
self . current . block . AddReceipt ( receipt )
return nil
}
2015-02-28 16:09:49 -06:00
func ( self * worker ) HashRate ( ) int64 {
var tot int64
for _ , agent := range self . agents {
2015-03-20 11:42:09 -05:00
tot += agent . GetHashRate ( )
2015-02-28 16:09:49 -06:00
}
return tot
}
2015-05-09 05:13:46 -05:00
// gasprice calculates a reduced gas price based on the pct
// XXX Use big.Rat?
func gasprice ( price * big . Int , pct int64 ) * big . Int {
p := new ( big . Int ) . Set ( price )
p . Div ( p , big . NewInt ( 100 ) )
p . Mul ( p , big . NewInt ( pct ) )
return p
}