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 }