github.com/jeffallen/go-ethereum@v1.1.4-0.20150910155051-571d3236c49c/miner/worker.go (about) 1 // Copyright 2015 The go-ethereum Authors 2 // This file is part of the go-ethereum library. 3 // 4 // The go-ethereum library is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Lesser General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // The go-ethereum library is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Lesser General Public License for more details. 13 // 14 // You should have received a copy of the GNU Lesser General Public License 15 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 16 17 package miner 18 19 import ( 20 "fmt" 21 "math/big" 22 "sort" 23 "sync" 24 "sync/atomic" 25 "time" 26 27 "github.com/ethereum/go-ethereum/accounts" 28 "github.com/ethereum/go-ethereum/common" 29 "github.com/ethereum/go-ethereum/core" 30 "github.com/ethereum/go-ethereum/core/state" 31 "github.com/ethereum/go-ethereum/core/types" 32 "github.com/ethereum/go-ethereum/event" 33 "github.com/ethereum/go-ethereum/logger" 34 "github.com/ethereum/go-ethereum/logger/glog" 35 "github.com/ethereum/go-ethereum/pow" 36 "gopkg.in/fatih/set.v0" 37 ) 38 39 var jsonlogger = logger.NewJsonLogger() 40 41 const ( 42 resultQueueSize = 10 43 miningLogAtDepth = 5 44 ) 45 46 // Agent can register themself with the worker 47 type Agent interface { 48 Work() chan<- *Work 49 SetReturnCh(chan<- *Result) 50 Stop() 51 Start() 52 GetHashRate() int64 53 } 54 55 type uint64RingBuffer struct { 56 ints []uint64 //array of all integers in buffer 57 next int //where is the next insertion? assert 0 <= next < len(ints) 58 } 59 60 // environment is the workers current environment and holds 61 // all of the current state information 62 type Work struct { 63 state *state.StateDB // apply state changes here 64 coinbase *state.StateObject // the miner's account 65 ancestors *set.Set // ancestor set (used for checking uncle parent validity) 66 family *set.Set // family set (used for checking uncle invalidity) 67 uncles *set.Set // uncle set 68 remove *set.Set // tx which will be removed 69 tcount int // tx count in cycle 70 ignoredTransactors *set.Set 71 lowGasTransactors *set.Set 72 ownedAccounts *set.Set 73 lowGasTxs types.Transactions 74 localMinedBlocks *uint64RingBuffer // the most recent block numbers that were mined locally (used to check block inclusion) 75 76 Block *types.Block // the new block 77 78 header *types.Header 79 txs []*types.Transaction 80 receipts []*types.Receipt 81 82 createdAt time.Time 83 } 84 85 type Result struct { 86 Work *Work 87 Block *types.Block 88 } 89 90 // worker is the main object which takes care of applying messages to the new state 91 type worker struct { 92 mu sync.Mutex 93 94 agents []Agent 95 recv chan *Result 96 mux *event.TypeMux 97 quit chan struct{} 98 pow pow.PoW 99 100 eth core.Backend 101 chain *core.ChainManager 102 proc *core.BlockProcessor 103 chainDb common.Database 104 105 coinbase common.Address 106 gasPrice *big.Int 107 extra []byte 108 109 currentMu sync.Mutex 110 current *Work 111 112 uncleMu sync.Mutex 113 possibleUncles map[common.Hash]*types.Block 114 115 txQueueMu sync.Mutex 116 txQueue map[common.Hash]*types.Transaction 117 118 // atomic status counters 119 mining int32 120 atWork int32 121 122 fullValidation bool 123 } 124 125 func newWorker(coinbase common.Address, eth core.Backend) *worker { 126 worker := &worker{ 127 eth: eth, 128 mux: eth.EventMux(), 129 chainDb: eth.ChainDb(), 130 recv: make(chan *Result, resultQueueSize), 131 gasPrice: new(big.Int), 132 chain: eth.ChainManager(), 133 proc: eth.BlockProcessor(), 134 possibleUncles: make(map[common.Hash]*types.Block), 135 coinbase: coinbase, 136 txQueue: make(map[common.Hash]*types.Transaction), 137 quit: make(chan struct{}), 138 fullValidation: false, 139 } 140 go worker.update() 141 go worker.wait() 142 143 worker.commitNewWork() 144 145 return worker 146 } 147 148 func (self *worker) setEtherbase(addr common.Address) { 149 self.mu.Lock() 150 defer self.mu.Unlock() 151 self.coinbase = addr 152 } 153 154 func (self *worker) pendingState() *state.StateDB { 155 self.currentMu.Lock() 156 defer self.currentMu.Unlock() 157 return self.current.state 158 } 159 160 func (self *worker) pendingBlock() *types.Block { 161 self.currentMu.Lock() 162 defer self.currentMu.Unlock() 163 164 if atomic.LoadInt32(&self.mining) == 0 { 165 return types.NewBlock( 166 self.current.header, 167 self.current.txs, 168 nil, 169 self.current.receipts, 170 ) 171 } 172 return self.current.Block 173 } 174 175 func (self *worker) start() { 176 self.mu.Lock() 177 defer self.mu.Unlock() 178 179 atomic.StoreInt32(&self.mining, 1) 180 181 // spin up agents 182 for _, agent := range self.agents { 183 agent.Start() 184 } 185 } 186 187 func (self *worker) stop() { 188 self.mu.Lock() 189 defer self.mu.Unlock() 190 191 if atomic.LoadInt32(&self.mining) == 1 { 192 var keep []Agent 193 // stop all agents 194 for _, agent := range self.agents { 195 agent.Stop() 196 // keep all that's not a cpu agent 197 if _, ok := agent.(*CpuAgent); !ok { 198 keep = append(keep, agent) 199 } 200 } 201 self.agents = keep 202 } 203 204 atomic.StoreInt32(&self.mining, 0) 205 atomic.StoreInt32(&self.atWork, 0) 206 } 207 208 func (self *worker) register(agent Agent) { 209 self.mu.Lock() 210 defer self.mu.Unlock() 211 self.agents = append(self.agents, agent) 212 agent.SetReturnCh(self.recv) 213 } 214 215 func (self *worker) update() { 216 events := self.mux.Subscribe(core.ChainHeadEvent{}, core.ChainSideEvent{}, core.TxPreEvent{}) 217 218 out: 219 for { 220 select { 221 case event := <-events.Chan(): 222 switch ev := event.(type) { 223 case core.ChainHeadEvent: 224 self.commitNewWork() 225 case core.ChainSideEvent: 226 self.uncleMu.Lock() 227 self.possibleUncles[ev.Block.Hash()] = ev.Block 228 self.uncleMu.Unlock() 229 case core.TxPreEvent: 230 // Apply transaction to the pending state if we're not mining 231 if atomic.LoadInt32(&self.mining) == 0 { 232 self.currentMu.Lock() 233 self.current.commitTransactions(types.Transactions{ev.Tx}, self.gasPrice, self.proc) 234 self.currentMu.Unlock() 235 } 236 } 237 case <-self.quit: 238 break out 239 } 240 } 241 242 events.Unsubscribe() 243 } 244 245 func newLocalMinedBlock(blockNumber uint64, prevMinedBlocks *uint64RingBuffer) (minedBlocks *uint64RingBuffer) { 246 if prevMinedBlocks == nil { 247 minedBlocks = &uint64RingBuffer{next: 0, ints: make([]uint64, miningLogAtDepth+1)} 248 } else { 249 minedBlocks = prevMinedBlocks 250 } 251 252 minedBlocks.ints[minedBlocks.next] = blockNumber 253 minedBlocks.next = (minedBlocks.next + 1) % len(minedBlocks.ints) 254 return minedBlocks 255 } 256 257 func (self *worker) wait() { 258 for { 259 for result := range self.recv { 260 atomic.AddInt32(&self.atWork, -1) 261 262 if result == nil { 263 continue 264 } 265 block := result.Block 266 work := result.Work 267 268 work.state.Sync() 269 if self.fullValidation { 270 if _, err := self.chain.InsertChain(types.Blocks{block}); err != nil { 271 glog.V(logger.Error).Infoln("mining err", err) 272 continue 273 } 274 go self.mux.Post(core.NewMinedBlockEvent{block}) 275 } else { 276 parent := self.chain.GetBlock(block.ParentHash()) 277 if parent == nil { 278 glog.V(logger.Error).Infoln("Invalid block found during mining") 279 continue 280 } 281 if err := core.ValidateHeader(self.eth.BlockProcessor().Pow, block.Header(), parent, true, false); err != nil && err != core.BlockFutureErr { 282 glog.V(logger.Error).Infoln("Invalid header on mined block:", err) 283 continue 284 } 285 286 stat, err := self.chain.WriteBlock(block, false) 287 if err != nil { 288 glog.V(logger.Error).Infoln("error writing block to chain", err) 289 continue 290 } 291 // check if canon block and write transactions 292 if stat == core.CanonStatTy { 293 // This puts transactions in a extra db for rpc 294 core.PutTransactions(self.chainDb, block, block.Transactions()) 295 // store the receipts 296 core.PutReceipts(self.chainDb, work.receipts) 297 } 298 299 // broadcast before waiting for validation 300 go func(block *types.Block, logs state.Logs, receipts []*types.Receipt) { 301 self.mux.Post(core.NewMinedBlockEvent{block}) 302 self.mux.Post(core.ChainEvent{block, block.Hash(), logs}) 303 if stat == core.CanonStatTy { 304 self.mux.Post(core.ChainHeadEvent{block}) 305 self.mux.Post(logs) 306 } 307 if err := core.PutBlockReceipts(self.chainDb, block, receipts); err != nil { 308 glog.V(logger.Warn).Infoln("error writing block receipts:", err) 309 } 310 }(block, work.state.Logs(), work.receipts) 311 } 312 313 // check staleness and display confirmation 314 var stale, confirm string 315 canonBlock := self.chain.GetBlockByNumber(block.NumberU64()) 316 if canonBlock != nil && canonBlock.Hash() != block.Hash() { 317 stale = "stale " 318 } else { 319 confirm = "Wait 5 blocks for confirmation" 320 work.localMinedBlocks = newLocalMinedBlock(block.Number().Uint64(), work.localMinedBlocks) 321 } 322 glog.V(logger.Info).Infof("🔨 Mined %sblock (#%v / %x). %s", stale, block.Number(), block.Hash().Bytes()[:4], confirm) 323 324 self.commitNewWork() 325 } 326 } 327 } 328 329 func (self *worker) push(work *Work) { 330 if atomic.LoadInt32(&self.mining) == 1 { 331 if core.Canary(work.state) { 332 glog.Infoln("Toxicity levels rising to deadly levels. Your canary has died. You can go back or continue down the mineshaft --more--") 333 glog.Infoln("You turn back and abort mining") 334 return 335 } 336 337 // push new work to agents 338 for _, agent := range self.agents { 339 atomic.AddInt32(&self.atWork, 1) 340 341 if agent.Work() != nil { 342 agent.Work() <- work 343 } 344 } 345 } 346 } 347 348 // makeCurrent creates a new environment for the current cycle. 349 func (self *worker) makeCurrent(parent *types.Block, header *types.Header) { 350 state := state.New(parent.Root(), self.eth.ChainDb()) 351 work := &Work{ 352 state: state, 353 ancestors: set.New(), 354 family: set.New(), 355 uncles: set.New(), 356 header: header, 357 coinbase: state.GetOrNewStateObject(self.coinbase), 358 createdAt: time.Now(), 359 } 360 361 // when 08 is processed ancestors contain 07 (quick block) 362 for _, ancestor := range self.chain.GetBlocksFromHash(parent.Hash(), 7) { 363 for _, uncle := range ancestor.Uncles() { 364 work.family.Add(uncle.Hash()) 365 } 366 work.family.Add(ancestor.Hash()) 367 work.ancestors.Add(ancestor.Hash()) 368 } 369 accounts, _ := self.eth.AccountManager().Accounts() 370 371 // Keep track of transactions which return errors so they can be removed 372 work.remove = set.New() 373 work.tcount = 0 374 work.ignoredTransactors = set.New() 375 work.lowGasTransactors = set.New() 376 work.ownedAccounts = accountAddressesSet(accounts) 377 if self.current != nil { 378 work.localMinedBlocks = self.current.localMinedBlocks 379 } 380 self.current = work 381 } 382 383 func (w *worker) setGasPrice(p *big.Int) { 384 w.mu.Lock() 385 defer w.mu.Unlock() 386 387 // calculate the minimal gas price the miner accepts when sorting out transactions. 388 const pct = int64(90) 389 w.gasPrice = gasprice(p, pct) 390 391 w.mux.Post(core.GasPriceChanged{w.gasPrice}) 392 } 393 394 func (self *worker) isBlockLocallyMined(current *Work, deepBlockNum uint64) bool { 395 //Did this instance mine a block at {deepBlockNum} ? 396 var isLocal = false 397 for idx, blockNum := range current.localMinedBlocks.ints { 398 if deepBlockNum == blockNum { 399 isLocal = true 400 current.localMinedBlocks.ints[idx] = 0 //prevent showing duplicate logs 401 break 402 } 403 } 404 //Short-circuit on false, because the previous and following tests must both be true 405 if !isLocal { 406 return false 407 } 408 409 //Does the block at {deepBlockNum} send earnings to my coinbase? 410 var block = self.chain.GetBlockByNumber(deepBlockNum) 411 return block != nil && block.Coinbase() == self.coinbase 412 } 413 414 func (self *worker) logLocalMinedBlocks(current, previous *Work) { 415 if previous != nil && current.localMinedBlocks != nil { 416 nextBlockNum := current.Block.NumberU64() 417 for checkBlockNum := previous.Block.NumberU64(); checkBlockNum < nextBlockNum; checkBlockNum++ { 418 inspectBlockNum := checkBlockNum - miningLogAtDepth 419 if self.isBlockLocallyMined(current, inspectBlockNum) { 420 glog.V(logger.Info).Infof("🔨 🔗 Mined %d blocks back: block #%v", miningLogAtDepth, inspectBlockNum) 421 } 422 } 423 } 424 } 425 426 func (self *worker) commitNewWork() { 427 self.mu.Lock() 428 defer self.mu.Unlock() 429 self.uncleMu.Lock() 430 defer self.uncleMu.Unlock() 431 self.currentMu.Lock() 432 defer self.currentMu.Unlock() 433 434 tstart := time.Now() 435 parent := self.chain.CurrentBlock() 436 tstamp := tstart.Unix() 437 if parent.Time().Cmp(new(big.Int).SetInt64(tstamp)) >= 0 { 438 tstamp = parent.Time().Int64() + 1 439 } 440 // this will ensure we're not going off too far in the future 441 if now := time.Now().Unix(); tstamp > now+4 { 442 wait := time.Duration(tstamp-now) * time.Second 443 glog.V(logger.Info).Infoln("We are too far in the future. Waiting for", wait) 444 time.Sleep(wait) 445 } 446 447 num := parent.Number() 448 header := &types.Header{ 449 ParentHash: parent.Hash(), 450 Number: num.Add(num, common.Big1), 451 Difficulty: core.CalcDifficulty(uint64(tstamp), parent.Time().Uint64(), parent.Number(), parent.Difficulty()), 452 GasLimit: core.CalcGasLimit(parent), 453 GasUsed: new(big.Int), 454 Coinbase: self.coinbase, 455 Extra: self.extra, 456 Time: big.NewInt(tstamp), 457 } 458 459 previous := self.current 460 self.makeCurrent(parent, header) 461 work := self.current 462 463 /* //approach 1 464 transactions := self.eth.TxPool().GetTransactions() 465 sort.Sort(types.TxByNonce{transactions}) 466 */ 467 468 //approach 2 469 transactions := self.eth.TxPool().GetTransactions() 470 sort.Sort(types.TxByPriceAndNonce{transactions}) 471 472 /* // approach 3 473 // commit transactions for this run. 474 txPerOwner := make(map[common.Address]types.Transactions) 475 // Sort transactions by owner 476 for _, tx := range self.eth.TxPool().GetTransactions() { 477 from, _ := tx.From() // we can ignore the sender error 478 txPerOwner[from] = append(txPerOwner[from], tx) 479 } 480 var ( 481 singleTxOwner types.Transactions 482 multiTxOwner types.Transactions 483 ) 484 // Categorise transactions by 485 // 1. 1 owner tx per block 486 // 2. multi txs owner per block 487 for _, txs := range txPerOwner { 488 if len(txs) == 1 { 489 singleTxOwner = append(singleTxOwner, txs[0]) 490 } else { 491 multiTxOwner = append(multiTxOwner, txs...) 492 } 493 } 494 sort.Sort(types.TxByPrice{singleTxOwner}) 495 sort.Sort(types.TxByNonce{multiTxOwner}) 496 transactions := append(singleTxOwner, multiTxOwner...) 497 */ 498 499 work.coinbase.SetGasLimit(header.GasLimit) 500 work.commitTransactions(transactions, self.gasPrice, self.proc) 501 self.eth.TxPool().RemoveTransactions(work.lowGasTxs) 502 503 // compute uncles for the new block. 504 var ( 505 uncles []*types.Header 506 badUncles []common.Hash 507 ) 508 for hash, uncle := range self.possibleUncles { 509 if len(uncles) == 2 { 510 break 511 } 512 if err := self.commitUncle(work, uncle.Header()); err != nil { 513 if glog.V(logger.Ridiculousness) { 514 glog.V(logger.Detail).Infof("Bad uncle found and will be removed (%x)\n", hash[:4]) 515 glog.V(logger.Detail).Infoln(uncle) 516 } 517 badUncles = append(badUncles, hash) 518 } else { 519 glog.V(logger.Debug).Infof("commiting %x as uncle\n", hash[:4]) 520 uncles = append(uncles, uncle.Header()) 521 } 522 } 523 for _, hash := range badUncles { 524 delete(self.possibleUncles, hash) 525 } 526 527 if atomic.LoadInt32(&self.mining) == 1 { 528 // commit state root after all state transitions. 529 core.AccumulateRewards(work.state, header, uncles) 530 work.state.SyncObjects() 531 header.Root = work.state.Root() 532 } 533 534 // create the new block whose nonce will be mined. 535 work.Block = types.NewBlock(header, work.txs, uncles, work.receipts) 536 work.Block.Td = new(big.Int).Set(core.CalcTD(work.Block, self.chain.GetBlock(work.Block.ParentHash()))) 537 538 // We only care about logging if we're actually mining. 539 if atomic.LoadInt32(&self.mining) == 1 { 540 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)) 541 self.logLocalMinedBlocks(work, previous) 542 } 543 544 self.push(work) 545 } 546 547 func (self *worker) commitUncle(work *Work, uncle *types.Header) error { 548 hash := uncle.Hash() 549 if work.uncles.Has(hash) { 550 return core.UncleError("Uncle not unique") 551 } 552 if !work.ancestors.Has(uncle.ParentHash) { 553 return core.UncleError(fmt.Sprintf("Uncle's parent unknown (%x)", uncle.ParentHash[0:4])) 554 } 555 if work.family.Has(hash) { 556 return core.UncleError(fmt.Sprintf("Uncle already in family (%x)", hash)) 557 } 558 work.uncles.Add(uncle.Hash()) 559 return nil 560 } 561 562 func (env *Work) commitTransactions(transactions types.Transactions, gasPrice *big.Int, proc *core.BlockProcessor) { 563 for _, tx := range transactions { 564 // We can skip err. It has already been validated in the tx pool 565 from, _ := tx.From() 566 567 // Check if it falls within margin. Txs from owned accounts are always processed. 568 if tx.GasPrice().Cmp(gasPrice) < 0 && !env.ownedAccounts.Has(from) { 569 // ignore the transaction and transactor. We ignore the transactor 570 // because nonce will fail after ignoring this transaction so there's 571 // no point 572 env.lowGasTransactors.Add(from) 573 574 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]) 575 } 576 577 // Continue with the next transaction if the transaction sender is included in 578 // the low gas tx set. This will also remove the tx and all sequential transaction 579 // from this transactor 580 if env.lowGasTransactors.Has(from) { 581 // add tx to the low gas set. This will be removed at the end of the run 582 // owned accounts are ignored 583 if !env.ownedAccounts.Has(from) { 584 env.lowGasTxs = append(env.lowGasTxs, tx) 585 } 586 continue 587 } 588 589 // Move on to the next transaction when the transactor is in ignored transactions set 590 // This may occur when a transaction hits the gas limit. When a gas limit is hit and 591 // the transaction is processed (that could potentially be included in the block) it 592 // will throw a nonce error because the previous transaction hasn't been processed. 593 // Therefor we need to ignore any transaction after the ignored one. 594 if env.ignoredTransactors.Has(from) { 595 continue 596 } 597 598 env.state.StartRecord(tx.Hash(), common.Hash{}, 0) 599 600 err := env.commitTransaction(tx, proc) 601 switch { 602 case state.IsGasLimitErr(err): 603 // ignore the transactor so no nonce errors will be thrown for this account 604 // next time the worker is run, they'll be picked up again. 605 env.ignoredTransactors.Add(from) 606 607 glog.V(logger.Detail).Infof("Gas limit reached for (%x) in this block. Continue to try smaller txs\n", from[:4]) 608 case err != nil: 609 env.remove.Add(tx.Hash()) 610 611 if glog.V(logger.Detail) { 612 glog.Infof("TX (%x) failed, will be removed: %v\n", tx.Hash().Bytes()[:4], err) 613 } 614 default: 615 env.tcount++ 616 } 617 } 618 } 619 620 func (env *Work) commitTransaction(tx *types.Transaction, proc *core.BlockProcessor) error { 621 snap := env.state.Copy() 622 receipt, _, err := proc.ApplyTransaction(env.coinbase, env.state, env.header, tx, env.header.GasUsed, true) 623 if err != nil { 624 env.state.Set(snap) 625 return err 626 } 627 env.txs = append(env.txs, tx) 628 env.receipts = append(env.receipts, receipt) 629 return nil 630 } 631 632 // TODO: remove or use 633 func (self *worker) HashRate() int64 { 634 return 0 635 } 636 637 // gasprice calculates a reduced gas price based on the pct 638 // XXX Use big.Rat? 639 func gasprice(price *big.Int, pct int64) *big.Int { 640 p := new(big.Int).Set(price) 641 p.Div(p, big.NewInt(100)) 642 p.Mul(p, big.NewInt(pct)) 643 return p 644 } 645 646 func accountAddressesSet(accounts []accounts.Account) *set.Set { 647 accountSet := set.New() 648 for _, account := range accounts { 649 accountSet.Add(account.Address) 650 } 651 return accountSet 652 }