github.com/karalabe/go-ethereum@v0.8.5/miner/worker.go (about)

     1  package miner
     2  
     3  import (
     4  	"fmt"
     5  	"math/big"
     6  	"sort"
     7  	"sync"
     8  
     9  	"github.com/ethereum/go-ethereum/core"
    10  	"github.com/ethereum/go-ethereum/core/types"
    11  	"github.com/ethereum/go-ethereum/ethutil"
    12  	"github.com/ethereum/go-ethereum/event"
    13  	"github.com/ethereum/go-ethereum/pow"
    14  	"github.com/ethereum/go-ethereum/state"
    15  	"gopkg.in/fatih/set.v0"
    16  )
    17  
    18  type environment struct {
    19  	totalUsedGas *big.Int
    20  	state        *state.StateDB
    21  	coinbase     *state.StateObject
    22  	block        *types.Block
    23  	ancestors    *set.Set
    24  	uncles       *set.Set
    25  }
    26  
    27  func env(block *types.Block, eth core.Backend) *environment {
    28  	state := state.New(block.Root(), eth.Db())
    29  	env := &environment{
    30  		totalUsedGas: new(big.Int),
    31  		state:        state,
    32  		block:        block,
    33  		ancestors:    set.New(),
    34  		uncles:       set.New(),
    35  		coinbase:     state.GetOrNewStateObject(block.Coinbase()),
    36  	}
    37  	for _, ancestor := range eth.ChainManager().GetAncestors(block, 7) {
    38  		env.ancestors.Add(string(ancestor.Hash()))
    39  	}
    40  
    41  	return env
    42  }
    43  
    44  type Work struct {
    45  	Number uint64
    46  	Nonce  []byte
    47  }
    48  
    49  type Agent interface {
    50  	Work() chan<- *types.Block
    51  	SetWorkCh(chan<- Work)
    52  	Stop()
    53  	Start()
    54  	Pow() pow.PoW
    55  }
    56  
    57  type worker struct {
    58  	mu     sync.Mutex
    59  	agents []Agent
    60  	recv   chan Work
    61  	mux    *event.TypeMux
    62  	quit   chan struct{}
    63  	pow    pow.PoW
    64  
    65  	eth      core.Backend
    66  	chain    *core.ChainManager
    67  	proc     *core.BlockProcessor
    68  	coinbase []byte
    69  
    70  	current *environment
    71  
    72  	mining bool
    73  }
    74  
    75  func newWorker(coinbase []byte, eth core.Backend) *worker {
    76  	return &worker{
    77  		eth:      eth,
    78  		mux:      eth.EventMux(),
    79  		recv:     make(chan Work),
    80  		chain:    eth.ChainManager(),
    81  		proc:     eth.BlockProcessor(),
    82  		coinbase: coinbase,
    83  	}
    84  }
    85  
    86  func (self *worker) start() {
    87  	self.mining = true
    88  
    89  	self.quit = make(chan struct{})
    90  
    91  	// spin up agents
    92  	for _, agent := range self.agents {
    93  		agent.Start()
    94  	}
    95  
    96  	go self.update()
    97  	go self.wait()
    98  }
    99  
   100  func (self *worker) stop() {
   101  	self.mining = false
   102  
   103  	close(self.quit)
   104  }
   105  
   106  func (self *worker) register(agent Agent) {
   107  	self.agents = append(self.agents, agent)
   108  	agent.SetWorkCh(self.recv)
   109  }
   110  
   111  func (self *worker) update() {
   112  	events := self.mux.Subscribe(core.ChainEvent{}, core.NewMinedBlockEvent{})
   113  
   114  out:
   115  	for {
   116  		select {
   117  		case event := <-events.Chan():
   118  			switch ev := event.(type) {
   119  			case core.ChainEvent:
   120  				if self.current.block != ev.Block {
   121  					self.commitNewWork()
   122  				}
   123  			case core.NewMinedBlockEvent:
   124  				self.commitNewWork()
   125  			}
   126  		case <-self.quit:
   127  			// stop all agents
   128  			for _, agent := range self.agents {
   129  				agent.Stop()
   130  			}
   131  			break out
   132  		}
   133  	}
   134  
   135  	events.Unsubscribe()
   136  }
   137  
   138  func (self *worker) wait() {
   139  	for {
   140  		for work := range self.recv {
   141  			block := self.current.block
   142  			if block.Number().Uint64() == work.Number && block.Nonce() == nil {
   143  				self.current.block.Header().Nonce = work.Nonce
   144  
   145  				if err := self.chain.InsertChain(types.Blocks{self.current.block}); err == nil {
   146  					self.mux.Post(core.NewMinedBlockEvent{self.current.block})
   147  				} else {
   148  					self.commitNewWork()
   149  				}
   150  			}
   151  			break
   152  		}
   153  	}
   154  }
   155  
   156  func (self *worker) push() {
   157  	if self.mining {
   158  		self.current.block.Header().GasUsed = self.current.totalUsedGas
   159  		self.current.block.SetRoot(self.current.state.Root())
   160  
   161  		// push new work to agents
   162  		for _, agent := range self.agents {
   163  			agent.Work() <- self.current.block
   164  		}
   165  	}
   166  }
   167  
   168  func (self *worker) commitNewWork() {
   169  	self.mu.Lock()
   170  	defer self.mu.Unlock()
   171  
   172  	self.current = env(self.chain.NewBlock(self.coinbase), self.eth)
   173  	parent := self.chain.GetBlock(self.current.block.ParentHash())
   174  	self.current.coinbase.SetGasPool(core.CalcGasLimit(parent, self.current.block))
   175  
   176  	transactions := self.eth.TxPool().GetTransactions()
   177  	sort.Sort(types.TxByNonce{transactions})
   178  
   179  	minerlogger.Infof("committing new work with %d txs\n", len(transactions))
   180  	// Keep track of transactions which return errors so they can be removed
   181  	var remove types.Transactions
   182  gasLimit:
   183  	for _, tx := range transactions {
   184  		err := self.commitTransaction(tx)
   185  		switch {
   186  		case core.IsNonceErr(err):
   187  			// Remove invalid transactions
   188  			remove = append(remove, tx)
   189  		case state.IsGasLimitErr(err):
   190  			// Break on gas limit
   191  			break gasLimit
   192  		}
   193  
   194  		if err != nil {
   195  			minerlogger.Infoln(err)
   196  		}
   197  	}
   198  	self.eth.TxPool().RemoveSet(remove)
   199  
   200  	self.current.coinbase.AddAmount(core.BlockReward)
   201  
   202  	self.current.state.Update(ethutil.Big0)
   203  	self.push()
   204  }
   205  
   206  var (
   207  	inclusionReward = new(big.Int).Div(core.BlockReward, big.NewInt(32))
   208  	_uncleReward    = new(big.Int).Mul(core.BlockReward, big.NewInt(15))
   209  	uncleReward     = new(big.Int).Div(_uncleReward, big.NewInt(16))
   210  )
   211  
   212  func (self *worker) commitUncle(uncle *types.Header) error {
   213  	if self.current.uncles.Has(string(uncle.Hash())) {
   214  		// Error not unique
   215  		return core.UncleError("Uncle not unique")
   216  	}
   217  	self.current.uncles.Add(string(uncle.Hash()))
   218  
   219  	if !self.current.ancestors.Has(string(uncle.ParentHash)) {
   220  		return core.UncleError(fmt.Sprintf("Uncle's parent unknown (%x)", uncle.ParentHash[0:4]))
   221  	}
   222  
   223  	if !self.pow.Verify(types.NewBlockWithHeader(uncle)) {
   224  		return core.ValidationError("Uncle's nonce is invalid (= %v)", ethutil.Bytes2Hex(uncle.Nonce))
   225  	}
   226  
   227  	uncleAccount := self.current.state.GetAccount(uncle.Coinbase)
   228  	uncleAccount.AddAmount(uncleReward)
   229  
   230  	self.current.coinbase.AddBalance(uncleReward)
   231  
   232  	return nil
   233  }
   234  
   235  func (self *worker) commitTransaction(tx *types.Transaction) error {
   236  	//fmt.Printf("proc %x %v\n", tx.Hash()[:3], tx.Nonce())
   237  	receipt, _, err := self.proc.ApplyTransaction(self.current.coinbase, self.current.state, self.current.block, tx, self.current.totalUsedGas, true)
   238  	if err != nil && (core.IsNonceErr(err) || state.IsGasLimitErr(err)) {
   239  		return err
   240  	}
   241  
   242  	self.current.block.AddTransaction(tx)
   243  	self.current.block.AddReceipt(receipt)
   244  
   245  	return nil
   246  }