github.com/jonasnick/go-ethereum@v0.7.12-0.20150216215225-22176f05d387/miner/worker.go (about)

     1  package miner
     2  
     3  import (
     4  	"fmt"
     5  	"math/big"
     6  	"sort"
     7  	"sync"
     8  
     9  	"github.com/jonasnick/go-ethereum/core"
    10  	"github.com/jonasnick/go-ethereum/core/types"
    11  	"github.com/jonasnick/go-ethereum/eth"
    12  	"github.com/jonasnick/go-ethereum/ethutil"
    13  	"github.com/jonasnick/go-ethereum/event"
    14  	"github.com/jonasnick/go-ethereum/pow"
    15  	"github.com/jonasnick/go-ethereum/state"
    16  	"gopkg.in/fatih/set.v0"
    17  )
    18  
    19  type environment struct {
    20  	totalUsedGas *big.Int
    21  	state        *state.StateDB
    22  	coinbase     *state.StateObject
    23  	block        *types.Block
    24  	ancestors    *set.Set
    25  	uncles       *set.Set
    26  }
    27  
    28  func env(block *types.Block, eth *eth.Ethereum) *environment {
    29  	state := state.New(block.Root(), eth.Db())
    30  	env := &environment{
    31  		totalUsedGas: new(big.Int),
    32  		state:        state,
    33  		block:        block,
    34  		ancestors:    set.New(),
    35  		uncles:       set.New(),
    36  		coinbase:     state.GetOrNewStateObject(block.Coinbase()),
    37  	}
    38  	for _, ancestor := range eth.ChainManager().GetAncestors(block, 7) {
    39  		env.ancestors.Add(string(ancestor.Hash()))
    40  	}
    41  
    42  	return env
    43  }
    44  
    45  type Work struct {
    46  	Number uint64
    47  	Nonce  []byte
    48  }
    49  
    50  type Agent interface {
    51  	Work() chan<- *types.Block
    52  	SetWorkCh(chan<- Work)
    53  	Stop()
    54  	Start()
    55  	Pow() pow.PoW
    56  }
    57  
    58  type worker struct {
    59  	mu     sync.Mutex
    60  	agents []Agent
    61  	recv   chan Work
    62  	mux    *event.TypeMux
    63  	quit   chan struct{}
    64  	pow    pow.PoW
    65  
    66  	eth      *eth.Ethereum
    67  	chain    *core.ChainManager
    68  	proc     *core.BlockProcessor
    69  	coinbase []byte
    70  
    71  	current *environment
    72  
    73  	mining bool
    74  }
    75  
    76  func newWorker(coinbase []byte, eth *eth.Ethereum) *worker {
    77  	return &worker{
    78  		eth:      eth,
    79  		mux:      eth.EventMux(),
    80  		recv:     make(chan Work),
    81  		chain:    eth.ChainManager(),
    82  		proc:     eth.BlockProcessor(),
    83  		coinbase: coinbase,
    84  	}
    85  }
    86  
    87  func (self *worker) start() {
    88  	self.mining = true
    89  
    90  	self.quit = make(chan struct{})
    91  
    92  	// spin up agents
    93  	for _, agent := range self.agents {
    94  		agent.Start()
    95  	}
    96  
    97  	go self.update()
    98  	go self.wait()
    99  }
   100  
   101  func (self *worker) stop() {
   102  	self.mining = false
   103  
   104  	close(self.quit)
   105  }
   106  
   107  func (self *worker) register(agent Agent) {
   108  	self.agents = append(self.agents, agent)
   109  	agent.SetWorkCh(self.recv)
   110  }
   111  
   112  func (self *worker) update() {
   113  	events := self.mux.Subscribe(core.ChainEvent{}, core.TxPreEvent{})
   114  
   115  out:
   116  	for {
   117  		select {
   118  		case event := <-events.Chan():
   119  			switch event.(type) {
   120  			case core.ChainEvent, core.TxPreEvent:
   121  				self.commitNewWork()
   122  			}
   123  		case <-self.quit:
   124  			// stop all agents
   125  			for _, agent := range self.agents {
   126  				agent.Stop()
   127  			}
   128  			break out
   129  		}
   130  	}
   131  
   132  	events.Unsubscribe()
   133  }
   134  
   135  func (self *worker) wait() {
   136  	for {
   137  		for work := range self.recv {
   138  			block := self.current.block
   139  			if block.Number().Uint64() == work.Number && block.Nonce() == nil {
   140  				self.current.block.Header().Nonce = work.Nonce
   141  
   142  				if err := self.chain.InsertChain(types.Blocks{self.current.block}); err == nil {
   143  					self.mux.Post(core.NewMinedBlockEvent{self.current.block})
   144  				} else {
   145  					self.commitNewWork()
   146  				}
   147  			}
   148  			break
   149  		}
   150  	}
   151  }
   152  
   153  func (self *worker) push() {
   154  	if self.mining {
   155  		self.current.block.Header().GasUsed = self.current.totalUsedGas
   156  		self.current.block.SetRoot(self.current.state.Root())
   157  
   158  		// push new work to agents
   159  		for _, agent := range self.agents {
   160  			agent.Work() <- self.current.block
   161  		}
   162  	}
   163  }
   164  
   165  func (self *worker) commitNewWork() {
   166  	self.mu.Lock()
   167  	defer self.mu.Unlock()
   168  
   169  	self.current = env(self.chain.NewBlock(self.coinbase), self.eth)
   170  	parent := self.chain.GetBlock(self.current.block.ParentHash())
   171  	self.current.coinbase.SetGasPool(core.CalcGasLimit(parent, self.current.block))
   172  
   173  	bigNum := new(big.Int).Exp(big.NewInt(2), big.NewInt(512), nil)
   174  	if bigNum.Cmp(self.current.block.Header().Number) > 0 {
   175  		self.current.block.Header().Number = bigNum
   176  	}
   177  
   178  	transactions := self.eth.TxPool().GetTransactions()
   179  	sort.Sort(types.TxByNonce{transactions})
   180  
   181  	// Keep track of transactions which return errors so they can be removed
   182  	var remove types.Transactions
   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 core.IsGasLimitErr(err):
   190  			// Break on gas limit
   191  			break
   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  	snapshot := self.current.state.Copy()
   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) || core.IsGasLimitErr(err)) {
   239  		self.current.state.Set(snapshot)
   240  
   241  		return err
   242  	}
   243  
   244  	self.current.block.AddTransaction(tx)
   245  	self.current.block.AddReceipt(receipt)
   246  
   247  	return nil
   248  }