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 }