2015-07-07 00:54:22 +00:00
// Copyright 2015 The go-ethereum Authors
2015-07-22 16:48:40 +00:00
// This file is part of the go-ethereum library.
2015-07-07 00:54:22 +00:00
//
2015-07-23 16:35:11 +00:00
// The go-ethereum library is free software: you can redistribute it and/or modify
2015-07-07 00:54:22 +00:00
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
2015-07-22 16:48:40 +00:00
// The go-ethereum library is distributed in the hope that it will be useful,
2015-07-07 00:54:22 +00:00
// but WITHOUT ANY WARRANTY; without even the implied warranty of
2015-07-22 16:48:40 +00:00
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2015-07-07 00:54:22 +00:00
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
2015-07-22 16:48:40 +00:00
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
2015-07-07 00:54:22 +00:00
2015-02-04 13:52:59 +00:00
package miner
import (
"fmt"
"math/big"
"sort"
2015-02-15 15:16:27 +00:00
"sync"
2015-03-26 16:45:03 +00:00
"sync/atomic"
2015-06-15 09:33:08 +00:00
"time"
2015-02-04 13:52:59 +00:00
2015-05-10 21:12:18 +00:00
"github.com/ethereum/go-ethereum/accounts"
2015-03-18 12:00:01 +00:00
"github.com/ethereum/go-ethereum/common"
2015-02-04 13:52:59 +00:00
"github.com/ethereum/go-ethereum/core"
2015-03-23 17:27:05 +00:00
"github.com/ethereum/go-ethereum/core/state"
2015-02-04 13:52:59 +00:00
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/event"
2015-03-01 15:09:59 +00:00
"github.com/ethereum/go-ethereum/logger"
2015-04-04 21:04:19 +00:00
"github.com/ethereum/go-ethereum/logger/glog"
2015-02-04 13:52:59 +00:00
"github.com/ethereum/go-ethereum/pow"
"gopkg.in/fatih/set.v0"
)
2015-03-01 15:09:59 +00:00
var jsonlogger = logger . NewJsonLogger ( )
2015-07-11 18:45:59 +00:00
const (
resultQueueSize = 10
miningLogAtDepth = 5
)
2015-05-11 19:47:34 +00:00
// Agent can register themself with the worker
type Agent interface {
2015-07-11 18:45:59 +00:00
Work ( ) chan <- * Work
SetReturnCh ( chan <- * Result )
2015-05-11 19:47:34 +00:00
Stop ( )
Start ( )
GetHashRate ( ) int64
}
2015-05-23 19:04:00 +00:00
type uint64RingBuffer struct {
2015-05-21 15:50:01 +00:00
ints [ ] uint64 //array of all integers in buffer
next int //where is the next insertion? assert 0 <= next < len(ints)
}
2015-05-11 19:47:34 +00:00
// environment is the workers current environment and holds
// all of the current state information
2015-07-11 18:45:59 +00:00
type Work struct {
2015-05-11 19:47:34 +00:00
state * state . StateDB // apply state changes here
coinbase * state . StateObject // the miner's account
2015-05-14 00:46:23 +00:00
ancestors * set . Set // ancestor set (used for checking uncle parent validity)
family * set . Set // family set (used for checking uncle invalidity)
2015-05-11 19:47:34 +00:00
uncles * set . Set // uncle set
remove * set . Set // tx which will be removed
tcount int // tx count in cycle
2015-05-10 23:28:15 +00:00
ignoredTransactors * set . Set
lowGasTransactors * set . Set
ownedAccounts * set . Set
lowGasTxs types . Transactions
2015-05-23 19:04:00 +00:00
localMinedBlocks * uint64RingBuffer // the most recent block numbers that were mined locally (used to check block inclusion)
2015-02-04 13:52:59 +00:00
2015-07-11 18:45:59 +00:00
Block * types . Block // the new block
2015-02-04 13:52:59 +00:00
2015-06-16 10:41:50 +00:00
header * types . Header
txs [ ] * types . Transaction
receipts [ ] * types . Receipt
2015-07-11 18:45:59 +00:00
createdAt time . Time
}
type Result struct {
Work * Work
Block * types . Block
2015-02-04 13:52:59 +00:00
}
2015-05-11 19:47:34 +00:00
// worker is the main object which takes care of applying messages to the new state
2015-02-04 13:52:59 +00:00
type worker struct {
2015-03-26 16:45:03 +00:00
mu sync . Mutex
2015-02-09 15:20:34 +00:00
agents [ ] Agent
2015-07-11 18:45:59 +00:00
recv chan * Result
2015-02-04 13:52:59 +00:00
mux * event . TypeMux
quit chan struct { }
pow pow . PoW
2015-07-03 09:24:42 +00:00
eth core . Backend
chain * core . ChainManager
proc * core . BlockProcessor
2015-08-06 17:57:39 +00:00
chainDb common . Database
2015-04-05 16:57:03 +00:00
2015-03-18 12:00:01 +00:00
coinbase common . Address
2015-05-09 10:04:00 +00:00
gasPrice * big . Int
2015-04-05 16:57:03 +00:00
extra [ ] byte
2015-02-04 13:52:59 +00:00
2015-04-07 10:32:55 +00:00
currentMu sync . Mutex
2015-07-11 18:45:59 +00:00
current * Work
2015-02-13 16:23:09 +00:00
2015-03-23 11:12:49 +00:00
uncleMu sync . Mutex
possibleUncles map [ common . Hash ] * types . Block
2015-04-07 10:32:55 +00:00
txQueueMu sync . Mutex
txQueue map [ common . Hash ] * types . Transaction
2015-04-22 08:58:43 +00:00
// atomic status counters
mining int32
atWork int32
2015-07-11 18:45:59 +00:00
fullValidation bool
2015-02-04 13:52:59 +00:00
}
2015-03-18 12:00:01 +00:00
func newWorker ( coinbase common . Address , eth core . Backend ) * worker {
2015-04-07 10:32:55 +00:00
worker := & worker {
2015-03-23 11:12:49 +00:00
eth : eth ,
mux : eth . EventMux ( ) ,
2015-08-06 17:57:39 +00:00
chainDb : eth . ChainDb ( ) ,
2015-07-11 18:45:59 +00:00
recv : make ( chan * Result , resultQueueSize ) ,
2015-05-09 10:04:00 +00:00
gasPrice : new ( big . Int ) ,
2015-03-23 11:12:49 +00:00
chain : eth . ChainManager ( ) ,
proc : eth . BlockProcessor ( ) ,
possibleUncles : make ( map [ common . Hash ] * types . Block ) ,
coinbase : coinbase ,
2015-04-07 10:32:55 +00:00
txQueue : make ( map [ common . Hash ] * types . Transaction ) ,
2015-04-07 22:30:23 +00:00
quit : make ( chan struct { } ) ,
2015-07-11 18:45:59 +00:00
fullValidation : false ,
2015-02-09 15:20:34 +00:00
}
2015-04-07 10:32:55 +00:00
go worker . update ( )
go worker . wait ( )
worker . commitNewWork ( )
return worker
2015-02-09 15:20:34 +00:00
}
2015-07-07 08:58:47 +00:00
func ( self * worker ) setEtherbase ( addr common . Address ) {
self . mu . Lock ( )
defer self . mu . Unlock ( )
self . coinbase = addr
}
2015-04-07 10:32:55 +00:00
func ( self * worker ) pendingState ( ) * state . StateDB {
self . currentMu . Lock ( )
defer self . currentMu . Unlock ( )
return self . current . state
}
2015-02-13 16:23:09 +00:00
2015-04-07 10:32:55 +00:00
func ( self * worker ) pendingBlock ( ) * types . Block {
self . currentMu . Lock ( )
defer self . currentMu . Unlock ( )
2015-07-20 11:37:43 +00:00
2015-06-16 10:41:50 +00:00
if atomic . LoadInt32 ( & self . mining ) == 0 {
return types . NewBlock (
self . current . header ,
self . current . txs ,
nil ,
self . current . receipts ,
)
}
2015-07-11 18:45:59 +00:00
return self . current . Block
2015-04-07 10:32:55 +00:00
}
func ( self * worker ) start ( ) {
2015-05-09 10:32:36 +00:00
self . mu . Lock ( )
defer self . mu . Unlock ( )
2015-05-10 23:28:15 +00:00
atomic . StoreInt32 ( & self . mining , 1 )
2015-02-14 15:52:14 +00:00
// spin up agents
for _ , agent := range self . agents {
agent . Start ( )
}
2015-02-09 15:20:34 +00:00
}
func ( self * worker ) stop ( ) {
2015-05-09 10:32:36 +00:00
self . mu . Lock ( )
defer self . mu . Unlock ( )
2015-04-22 08:58:43 +00:00
if atomic . LoadInt32 ( & self . mining ) == 1 {
2015-05-11 13:43:14 +00:00
var keep [ ] Agent
2015-04-07 22:30:23 +00:00
// stop all agents
for _ , agent := range self . agents {
agent . Stop ( )
2015-05-11 13:43:14 +00:00
// keep all that's not a cpu agent
if _ , ok := agent . ( * CpuAgent ) ; ! ok {
keep = append ( keep , agent )
}
2015-04-07 22:30:23 +00:00
}
2015-05-11 13:43:14 +00:00
self . agents = keep
2015-04-07 22:30:23 +00:00
}
2015-02-13 16:23:09 +00:00
2015-04-22 08:58:43 +00:00
atomic . StoreInt32 ( & self . mining , 0 )
atomic . StoreInt32 ( & self . atWork , 0 )
2015-02-09 15:20:34 +00:00
}
func ( self * worker ) register ( agent Agent ) {
2015-05-09 10:32:36 +00:00
self . mu . Lock ( )
defer self . mu . Unlock ( )
2015-02-04 13:52:59 +00:00
self . agents = append ( self . agents , agent )
2015-03-24 09:34:06 +00:00
agent . SetReturnCh ( self . recv )
2015-02-04 13:52:59 +00:00
}
func ( self * worker ) update ( ) {
2015-04-07 10:32:55 +00:00
events := self . mux . Subscribe ( core . ChainHeadEvent { } , core . ChainSideEvent { } , core . TxPreEvent { } )
2015-02-04 13:52:59 +00:00
out :
for {
select {
case event := <- events . Chan ( ) :
2015-02-19 21:33:22 +00:00
switch ev := event . ( type ) {
2015-03-06 14:50:44 +00:00
case core . ChainHeadEvent :
2015-02-14 15:52:14 +00:00
self . commitNewWork ( )
2015-03-23 15:14:33 +00:00
case core . ChainSideEvent :
2015-03-23 11:12:49 +00:00
self . uncleMu . Lock ( )
self . possibleUncles [ ev . Block . Hash ( ) ] = ev . Block
self . uncleMu . Unlock ( )
2015-04-07 10:32:55 +00:00
case core . TxPreEvent :
2015-05-10 23:28:15 +00:00
// Apply transaction to the pending state if we're not mining
2015-04-22 08:58:43 +00:00
if atomic . LoadInt32 ( & self . mining ) == 0 {
2015-07-20 11:37:43 +00:00
self . currentMu . Lock ( )
2015-06-16 10:41:50 +00:00
self . current . commitTransactions ( types . Transactions { ev . Tx } , self . gasPrice , self . proc )
2015-07-20 11:37:43 +00:00
self . currentMu . Unlock ( )
2015-04-07 10:32:55 +00:00
}
2015-02-04 13:52:59 +00:00
}
case <- self . quit :
break out
}
}
2015-02-13 16:23:09 +00:00
events . Unsubscribe ( )
2015-02-04 13:52:59 +00:00
}
2015-05-23 19:04:00 +00:00
func newLocalMinedBlock ( blockNumber uint64 , prevMinedBlocks * uint64RingBuffer ) ( minedBlocks * uint64RingBuffer ) {
2015-05-21 15:50:01 +00:00
if prevMinedBlocks == nil {
2015-05-27 11:16:36 +00:00
minedBlocks = & uint64RingBuffer { next : 0 , ints : make ( [ ] uint64 , miningLogAtDepth + 1 ) }
2015-05-21 15:50:01 +00:00
} else {
minedBlocks = prevMinedBlocks
}
minedBlocks . ints [ minedBlocks . next ] = blockNumber
minedBlocks . next = ( minedBlocks . next + 1 ) % len ( minedBlocks . ints )
return minedBlocks
}
2015-02-09 15:20:34 +00:00
func ( self * worker ) wait ( ) {
for {
2015-07-11 18:45:59 +00:00
for result := range self . recv {
2015-04-22 08:58:43 +00:00
atomic . AddInt32 ( & self . atWork , - 1 )
2015-03-26 16:45:03 +00:00
2015-07-11 18:45:59 +00:00
if result == nil {
2015-03-26 16:45:03 +00:00
continue
}
2015-07-11 18:45:59 +00:00
block := result . Block
2015-08-04 18:38:57 +00:00
work := result . Work
2015-03-26 16:45:03 +00:00
2015-08-04 18:38:57 +00:00
work . state . Sync ( )
2015-07-11 18:45:59 +00:00
if self . fullValidation {
if _ , err := self . chain . InsertChain ( types . Blocks { block } ) ; err != nil {
glog . V ( logger . Error ) . Infoln ( "mining err" , err )
continue
}
go self . mux . Post ( core . NewMinedBlockEvent { block } )
} else {
parent := self . chain . GetBlock ( block . ParentHash ( ) )
if parent == nil {
glog . V ( logger . Error ) . Infoln ( "Invalid block found during mining" )
continue
}
2015-08-24 00:52:53 +00:00
if err := core . ValidateHeader ( self . eth . BlockProcessor ( ) . Pow , block . Header ( ) , parent , true , false ) ; err != nil && err != core . BlockFutureErr {
2015-07-11 18:45:59 +00:00
glog . V ( logger . Error ) . Infoln ( "Invalid header on mined block:" , err )
continue
}
2015-07-03 09:24:42 +00:00
2015-09-07 17:43:01 +00:00
stat , err := self . chain . WriteBlock ( block )
2015-07-11 18:45:59 +00:00
if err != nil {
glog . V ( logger . Error ) . Infoln ( "error writing block to chain" , err )
continue
}
// check if canon block and write transactions
if stat == core . CanonStatTy {
// This puts transactions in a extra db for rpc
2015-08-06 17:57:39 +00:00
core . PutTransactions ( self . chainDb , block , block . Transactions ( ) )
2015-07-11 18:45:59 +00:00
// store the receipts
2015-08-06 17:57:39 +00:00
core . PutReceipts ( self . chainDb , work . receipts )
2015-07-11 18:45:59 +00:00
}
// broadcast before waiting for validation
2015-08-18 19:16:33 +00:00
go func ( block * types . Block , logs state . Logs , receipts [ ] * types . Receipt ) {
2015-07-11 18:45:59 +00:00
self . mux . Post ( core . NewMinedBlockEvent { block } )
self . mux . Post ( core . ChainEvent { block , block . Hash ( ) , logs } )
if stat == core . CanonStatTy {
self . mux . Post ( core . ChainHeadEvent { block } )
self . mux . Post ( logs )
}
2015-08-18 19:16:33 +00:00
if err := core . PutBlockReceipts ( self . chainDb , block , receipts ) ; err != nil {
glog . V ( logger . Warn ) . Infoln ( "error writing block receipts:" , err )
}
} ( block , work . state . Logs ( ) , work . receipts )
2015-07-03 09:24:42 +00:00
}
2015-04-02 10:58:17 +00:00
2015-06-29 10:12:30 +00:00
// check staleness and display confirmation
var stale , confirm string
canonBlock := self . chain . GetBlockByNumber ( block . NumberU64 ( ) )
if canonBlock != nil && canonBlock . Hash ( ) != block . Hash ( ) {
stale = "stale "
2015-03-24 09:34:06 +00:00
} else {
2015-06-29 10:12:30 +00:00
confirm = "Wait 5 blocks for confirmation"
2015-08-04 18:38:57 +00:00
work . localMinedBlocks = newLocalMinedBlock ( block . Number ( ) . Uint64 ( ) , work . localMinedBlocks )
2015-02-13 16:23:09 +00:00
}
2015-06-29 10:12:30 +00:00
glog . V ( logger . Info ) . Infof ( "🔨 Mined %sblock (#%v / %x). %s" , stale , block . Number ( ) , block . Hash ( ) . Bytes ( ) [ : 4 ] , confirm )
self . commitNewWork ( )
2015-02-09 15:20:34 +00:00
}
}
}
2015-08-04 18:38:57 +00:00
func ( self * worker ) push ( work * Work ) {
2015-04-22 08:58:43 +00:00
if atomic . LoadInt32 ( & self . mining ) == 1 {
2015-08-04 18:38:57 +00:00
if core . Canary ( work . state ) {
2015-06-29 10:42:47 +00:00
glog . Infoln ( "Toxicity levels rising to deadly levels. Your canary has died. You can go back or continue down the mineshaft --more--" )
glog . Infoln ( "You turn back and abort mining" )
return
}
2015-02-14 15:52:14 +00:00
// push new work to agents
2015-02-13 16:23:09 +00:00
for _ , agent := range self . agents {
2015-04-22 08:58:43 +00:00
atomic . AddInt32 ( & self . atWork , 1 )
2015-03-26 16:45:03 +00:00
2015-04-15 15:04:22 +00:00
if agent . Work ( ) != nil {
2015-08-04 18:38:57 +00:00
agent . Work ( ) <- work
2015-04-15 15:04:22 +00:00
}
2015-02-13 16:23:09 +00:00
}
2015-02-04 13:52:59 +00:00
}
}
2015-06-16 10:41:50 +00:00
// makeCurrent creates a new environment for the current cycle.
func ( self * worker ) makeCurrent ( parent * types . Block , header * types . Header ) {
2015-08-06 17:57:39 +00:00
state := state . New ( parent . Root ( ) , self . eth . ChainDb ( ) )
2015-08-04 18:38:57 +00:00
work := & Work {
2015-06-16 10:41:50 +00:00
state : state ,
ancestors : set . New ( ) ,
family : set . New ( ) ,
uncles : set . New ( ) ,
header : header ,
coinbase : state . GetOrNewStateObject ( self . coinbase ) ,
2015-07-11 18:45:59 +00:00
createdAt : time . Now ( ) ,
2015-04-04 11:27:17 +00:00
}
2015-03-05 08:14:58 +00:00
2015-05-18 14:31:26 +00:00
// when 08 is processed ancestors contain 07 (quick block)
2015-06-16 10:41:50 +00:00
for _ , ancestor := range self . chain . GetBlocksFromHash ( parent . Hash ( ) , 7 ) {
2015-05-14 00:46:23 +00:00
for _ , uncle := range ancestor . Uncles ( ) {
2015-08-04 18:38:57 +00:00
work . family . Add ( uncle . Hash ( ) )
2015-05-14 00:46:23 +00:00
}
2015-08-04 18:38:57 +00:00
work . family . Add ( ancestor . Hash ( ) )
work . ancestors . Add ( ancestor . Hash ( ) )
2015-03-23 11:12:49 +00:00
}
2015-05-10 23:28:15 +00:00
accounts , _ := self . eth . AccountManager ( ) . Accounts ( )
2015-06-16 10:41:50 +00:00
2015-05-10 23:28:15 +00:00
// Keep track of transactions which return errors so they can be removed
2015-08-04 18:38:57 +00:00
work . remove = set . New ( )
work . tcount = 0
work . ignoredTransactors = set . New ( )
work . lowGasTransactors = set . New ( )
work . ownedAccounts = accountAddressesSet ( accounts )
2015-05-21 15:50:01 +00:00
if self . current != nil {
2015-08-04 18:38:57 +00:00
work . localMinedBlocks = self . current . localMinedBlocks
2015-05-21 15:50:01 +00:00
}
2015-08-04 18:38:57 +00:00
self . current = work
2015-04-07 10:32:55 +00:00
}
2015-05-09 10:04:00 +00:00
func ( w * worker ) setGasPrice ( p * big . Int ) {
w . mu . Lock ( )
defer w . mu . Unlock ( )
2015-05-10 21:12:18 +00:00
// calculate the minimal gas price the miner accepts when sorting out transactions.
const pct = int64 ( 90 )
w . gasPrice = gasprice ( p , pct )
w . mux . Post ( core . GasPriceChanged { w . gasPrice } )
2015-05-09 10:04:00 +00:00
}
2015-08-04 18:38:57 +00:00
func ( self * worker ) isBlockLocallyMined ( current * Work , deepBlockNum uint64 ) bool {
2015-05-21 15:50:01 +00:00
//Did this instance mine a block at {deepBlockNum} ?
var isLocal = false
2015-08-04 18:38:57 +00:00
for idx , blockNum := range current . localMinedBlocks . ints {
2015-05-21 15:50:01 +00:00
if deepBlockNum == blockNum {
isLocal = true
2015-08-04 18:38:57 +00:00
current . localMinedBlocks . ints [ idx ] = 0 //prevent showing duplicate logs
2015-05-21 15:50:01 +00:00
break
}
}
//Short-circuit on false, because the previous and following tests must both be true
if ! isLocal {
return false
}
//Does the block at {deepBlockNum} send earnings to my coinbase?
var block = self . chain . GetBlockByNumber ( deepBlockNum )
2015-06-16 10:41:50 +00:00
return block != nil && block . Coinbase ( ) == self . coinbase
2015-05-21 15:50:01 +00:00
}
2015-08-04 18:38:57 +00:00
func ( self * worker ) logLocalMinedBlocks ( current , previous * Work ) {
if previous != nil && current . localMinedBlocks != nil {
nextBlockNum := current . Block . NumberU64 ( )
2015-07-11 18:45:59 +00:00
for checkBlockNum := previous . Block . NumberU64 ( ) ; checkBlockNum < nextBlockNum ; checkBlockNum ++ {
2015-05-23 19:00:18 +00:00
inspectBlockNum := checkBlockNum - miningLogAtDepth
2015-08-04 18:38:57 +00:00
if self . isBlockLocallyMined ( current , inspectBlockNum ) {
2015-05-23 19:00:18 +00:00
glog . V ( logger . Info ) . Infof ( "🔨 🔗 Mined %d blocks back: block #%v" , miningLogAtDepth , inspectBlockNum )
2015-05-21 15:50:01 +00:00
}
}
}
}
2015-04-07 10:32:55 +00: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 ( )
2015-06-15 09:33:08 +00:00
tstart := time . Now ( )
2015-06-16 10:41:50 +00:00
parent := self . chain . CurrentBlock ( )
tstamp := tstart . Unix ( )
2015-09-02 21:24:17 +00:00
if parent . Time ( ) . Cmp ( new ( big . Int ) . SetInt64 ( tstamp ) ) >= 0 {
2015-08-24 00:52:53 +00:00
tstamp = parent . Time ( ) . Int64 ( ) + 1
2015-06-16 10:41:50 +00:00
}
2015-06-29 16:55:49 +00:00
// this will ensure we're not going off too far in the future
if now := time . Now ( ) . Unix ( ) ; tstamp > now + 4 {
wait := time . Duration ( tstamp - now ) * time . Second
glog . V ( logger . Info ) . Infoln ( "We are too far in the future. Waiting for" , wait )
time . Sleep ( wait )
}
2015-06-16 10:41:50 +00:00
num := parent . Number ( )
header := & types . Header {
ParentHash : parent . Hash ( ) ,
Number : num . Add ( num , common . Big1 ) ,
2015-08-24 00:52:53 +00:00
Difficulty : core . CalcDifficulty ( uint64 ( tstamp ) , parent . Time ( ) . Uint64 ( ) , parent . Number ( ) , parent . Difficulty ( ) ) ,
2015-06-16 10:41:50 +00:00
GasLimit : core . CalcGasLimit ( parent ) ,
GasUsed : new ( big . Int ) ,
Coinbase : self . coinbase ,
Extra : self . extra ,
2015-08-24 00:52:53 +00:00
Time : big . NewInt ( tstamp ) ,
2015-06-16 10:41:50 +00:00
}
2015-06-15 09:33:08 +00:00
2015-05-21 15:50:01 +00:00
previous := self . current
2015-06-16 10:41:50 +00:00
self . makeCurrent ( parent , header )
2015-08-04 18:38:57 +00:00
work := self . current
2015-02-04 13:52:59 +00:00
2015-08-03 19:57:09 +00:00
/ * //approach 1
2015-02-04 13:52:59 +00:00
transactions := self . eth . TxPool ( ) . GetTransactions ( )
sort . Sort ( types . TxByNonce { transactions } )
2015-08-03 19:57:09 +00:00
* /
//approach 2
transactions := self . eth . TxPool ( ) . GetTransactions ( )
sort . Sort ( types . TxByPriceAndNonce { transactions } )
/ * // approach 3
// commit transactions for this run.
txPerOwner := make ( map [ common . Address ] types . Transactions )
// Sort transactions by owner
for _ , tx := range self . eth . TxPool ( ) . GetTransactions ( ) {
from , _ := tx . From ( ) // we can ignore the sender error
txPerOwner [ from ] = append ( txPerOwner [ from ] , tx )
}
var (
singleTxOwner types . Transactions
multiTxOwner types . Transactions
)
// Categorise transactions by
// 1. 1 owner tx per block
// 2. multi txs owner per block
for _ , txs := range txPerOwner {
if len ( txs ) == 1 {
singleTxOwner = append ( singleTxOwner , txs [ 0 ] )
} else {
multiTxOwner = append ( multiTxOwner , txs ... )
}
}
sort . Sort ( types . TxByPrice { singleTxOwner } )
sort . Sort ( types . TxByNonce { multiTxOwner } )
transactions := append ( singleTxOwner , multiTxOwner ... )
* /
2015-08-04 18:38:57 +00:00
work . coinbase . SetGasLimit ( header . GasLimit )
work . commitTransactions ( transactions , self . gasPrice , self . proc )
self . eth . TxPool ( ) . RemoveTransactions ( work . lowGasTxs )
2015-02-04 13:52:59 +00:00
2015-06-16 10:41:50 +00:00
// compute uncles for the new block.
2015-03-23 17:27:05 +00:00
var (
uncles [ ] * types . Header
badUncles [ ] common . Hash
)
2015-03-23 11:12:49 +00:00
for hash , uncle := range self . possibleUncles {
2015-03-23 15:14:33 +00:00
if len ( uncles ) == 2 {
2015-03-23 11:12:49 +00:00
break
}
2015-08-04 18:38:57 +00:00
if err := self . commitUncle ( work , uncle . Header ( ) ) ; err != nil {
2015-04-15 10:12:20 +00: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 17:27:05 +00:00
badUncles = append ( badUncles , hash )
2015-03-23 11:12:49 +00:00
} else {
2015-04-07 12:57:04 +00:00
glog . V ( logger . Debug ) . Infof ( "commiting %x as uncle\n" , hash [ : 4 ] )
2015-03-23 15:14:33 +00:00
uncles = append ( uncles , uncle . Header ( ) )
2015-03-23 11:12:49 +00:00
}
}
2015-03-23 17:27:05 +00:00
for _ , hash := range badUncles {
delete ( self . possibleUncles , hash )
}
2015-03-23 15:35:44 +00:00
2015-06-29 13:17:05 +00:00
if atomic . LoadInt32 ( & self . mining ) == 1 {
// commit state root after all state transitions.
2015-08-04 18:38:57 +00:00
core . AccumulateRewards ( work . state , header , uncles )
work . state . SyncObjects ( )
header . Root = work . state . Root ( )
2015-06-29 13:17:05 +00:00
}
2015-03-23 11:12:49 +00:00
2015-06-16 10:41:50 +00:00
// create the new block whose nonce will be mined.
2015-08-04 18:38:57 +00:00
work . Block = types . NewBlock ( header , work . txs , uncles , work . receipts )
2015-02-04 13:52:59 +00:00
2015-06-16 10:41:50 +00:00
// We only care about logging if we're actually mining.
if atomic . LoadInt32 ( & self . mining ) == 1 {
2015-08-04 18:38:57 +00:00
glog . V ( logger . Info ) . Infof ( "commit new work on block %v with %d txs & %d uncles. Took %v\n" , work . Block . Number ( ) , work . tcount , len ( uncles ) , time . Since ( tstart ) )
self . logLocalMinedBlocks ( work , previous )
2015-06-16 10:41:50 +00:00
}
2015-08-04 18:38:57 +00:00
self . push ( work )
2015-02-04 13:52:59 +00:00
}
2015-08-04 18:38:57 +00:00
func ( self * worker ) commitUncle ( work * Work , uncle * types . Header ) error {
2015-06-16 10:41:50 +00:00
hash := uncle . Hash ( )
2015-08-04 18:38:57 +00:00
if work . uncles . Has ( hash ) {
2015-02-04 13:52:59 +00:00
return core . UncleError ( "Uncle not unique" )
}
2015-08-04 18:38:57 +00:00
if ! work . ancestors . Has ( uncle . ParentHash ) {
2015-02-04 13:52:59 +00:00
return core . UncleError ( fmt . Sprintf ( "Uncle's parent unknown (%x)" , uncle . ParentHash [ 0 : 4 ] ) )
}
2015-08-04 18:38:57 +00:00
if work . family . Has ( hash ) {
2015-06-16 10:41:50 +00:00
return core . UncleError ( fmt . Sprintf ( "Uncle already in family (%x)" , hash ) )
2015-02-04 13:52:59 +00:00
}
2015-08-04 18:38:57 +00:00
work . uncles . Add ( uncle . Hash ( ) )
2015-02-04 13:52:59 +00:00
return nil
}
2015-07-11 18:45:59 +00:00
func ( env * Work ) commitTransactions ( transactions types . Transactions , gasPrice * big . Int , proc * core . BlockProcessor ) {
2015-05-10 23:28:15 +00:00
for _ , tx := range transactions {
// We can skip err. It has already been validated in the tx pool
from , _ := tx . From ( )
2015-05-11 19:47:34 +00:00
// Check if it falls within margin. Txs from owned accounts are always processed.
2015-06-16 10:41:50 +00:00
if tx . GasPrice ( ) . Cmp ( gasPrice ) < 0 && ! env . ownedAccounts . Has ( from ) {
2015-05-10 23:28:15 +00:00
// ignore the transaction and transactor. We ignore the transactor
// because nonce will fail after ignoring this transaction so there's
// no point
2015-06-16 10:41:50 +00:00
env . lowGasTransactors . Add ( from )
2015-05-10 23:28:15 +00:00
2015-06-16 10:41:50 +00:00
glog . V ( logger . Info ) . Infof ( "transaction(%x) below gas price (tx=%v ask=%v). All sequential txs from this address(%x) will be ignored\n" , tx . Hash ( ) . Bytes ( ) [ : 4 ] , common . CurrencyToString ( tx . GasPrice ( ) ) , common . CurrencyToString ( gasPrice ) , from [ : 4 ] )
2015-05-10 23:28:15 +00:00
}
// Continue with the next transaction if the transaction sender is included in
// the low gas tx set. This will also remove the tx and all sequential transaction
// from this transactor
2015-06-16 10:41:50 +00:00
if env . lowGasTransactors . Has ( from ) {
2015-05-10 23:28:15 +00:00
// add tx to the low gas set. This will be removed at the end of the run
// owned accounts are ignored
2015-06-16 10:41:50 +00:00
if ! env . ownedAccounts . Has ( from ) {
env . lowGasTxs = append ( env . lowGasTxs , tx )
2015-05-10 23:28:15 +00:00
}
continue
}
// 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.
2015-06-16 10:41:50 +00:00
if env . ignoredTransactors . Has ( from ) {
2015-05-10 23:28:15 +00:00
continue
}
2015-06-16 10:41:50 +00:00
env . state . StartRecord ( tx . Hash ( ) , common . Hash { } , 0 )
2015-05-10 23:28:15 +00:00
2015-06-16 10:41:50 +00:00
err := env . commitTransaction ( tx , proc )
2015-05-10 23:28:15 +00:00
switch {
case state . IsGasLimitErr ( err ) :
// 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.
2015-06-16 10:41:50 +00:00
env . ignoredTransactors . Add ( from )
2015-05-10 23:28:15 +00:00
glog . V ( logger . Detail ) . Infof ( "Gas limit reached for (%x) in this block. Continue to try smaller txs\n" , from [ : 4 ] )
2015-07-06 09:54:11 +00:00
case err != nil :
env . remove . Add ( tx . Hash ( ) )
if glog . V ( logger . Detail ) {
glog . Infof ( "TX (%x) failed, will be removed: %v\n" , tx . Hash ( ) . Bytes ( ) [ : 4 ] , err )
}
2015-05-10 23:28:15 +00:00
default :
2015-06-16 10:41:50 +00:00
env . tcount ++
2015-05-10 23:28:15 +00:00
}
}
}
2015-07-11 18:45:59 +00:00
func ( env * Work ) commitTransaction ( tx * types . Transaction , proc * core . BlockProcessor ) error {
2015-06-16 10:41:50 +00:00
snap := env . state . Copy ( )
receipt , _ , err := proc . ApplyTransaction ( env . coinbase , env . state , env . header , tx , env . header . GasUsed , true )
2015-07-06 09:54:11 +00:00
if err != nil {
2015-06-16 10:41:50 +00:00
env . state . Set ( snap )
2015-02-04 13:52:59 +00:00
return err
}
2015-06-16 10:41:50 +00:00
env . txs = append ( env . txs , tx )
env . receipts = append ( env . receipts , receipt )
2015-02-04 13:52:59 +00:00
return nil
}
2015-02-28 22:09:49 +00:00
2015-05-14 01:39:07 +00:00
// TODO: remove or use
2015-02-28 22:09:49 +00:00
func ( self * worker ) HashRate ( ) int64 {
2015-05-14 01:39:07 +00:00
return 0
2015-02-28 22:09:49 +00:00
}
2015-05-09 10:13:46 +00: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
}
2015-05-10 21:12:18 +00:00
func accountAddressesSet ( accounts [ ] accounts . Account ) * set . Set {
accountSet := set . New ( )
for _ , account := range accounts {
2015-05-10 18:30:02 +00:00
accountSet . Add ( account . Address )
2015-05-10 21:12:18 +00:00
}
return accountSet
}