github.com/ethereumproject/go-ethereum@v5.5.2+incompatible/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 "log" 22 "math/big" 23 "sync" 24 "sync/atomic" 25 "time" 26 27 "github.com/ethereumproject/go-ethereum/accounts" 28 "github.com/ethereumproject/go-ethereum/common" 29 "github.com/ethereumproject/go-ethereum/core" 30 "github.com/ethereumproject/go-ethereum/core/state" 31 "github.com/ethereumproject/go-ethereum/core/types" 32 "github.com/ethereumproject/go-ethereum/core/vm" 33 "github.com/ethereumproject/go-ethereum/ethdb" 34 "github.com/ethereumproject/go-ethereum/event" 35 "github.com/ethereumproject/go-ethereum/logger" 36 "github.com/ethereumproject/go-ethereum/logger/glog" 37 "gopkg.in/fatih/set.v0" 38 ) 39 40 const ( 41 resultQueueSize = 10 42 miningLogAtDepth = 5 43 ) 44 45 // Agent can register itself with the worker 46 type Agent interface { 47 Work() chan<- *Work 48 SetReturnCh(chan<- *Result) 49 Stop() 50 Start() 51 GetHashRate() int64 52 } 53 54 type uint64RingBuffer struct { 55 ints []uint64 //array of all integers in buffer 56 next int //where is the next insertion? assert 0 <= next < len(ints) 57 } 58 59 // environment is the workers current environment and holds 60 // all of the current state information 61 type Work struct { 62 config *core.ChainConfig 63 signer types.Signer 64 state *state.StateDB // apply state changes here 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 config *core.ChainConfig 93 94 mu sync.Mutex 95 96 // update loop 97 mux *event.TypeMux 98 events event.Subscription 99 wg sync.WaitGroup 100 101 agents map[Agent]struct{} 102 recv chan *Result 103 104 eth core.Backend 105 chain *core.BlockChain 106 proc core.Validator 107 chainDb ethdb.Database 108 109 coinbase common.Address 110 gasPrice *big.Int 111 112 currentMu sync.Mutex 113 current *Work 114 115 uncleMu sync.Mutex 116 possibleUncles map[common.Hash]*types.Block 117 118 txQueue map[common.Hash]*types.Transaction 119 120 // atomic status counters 121 mining int32 122 atWork int32 123 124 fullValidation bool 125 } 126 127 func newWorker(config *core.ChainConfig, coinbase common.Address, eth core.Backend) *worker { 128 worker := &worker{ 129 config: config, 130 eth: eth, 131 mux: eth.EventMux(), 132 chainDb: eth.ChainDb(), 133 recv: make(chan *Result, resultQueueSize), 134 gasPrice: new(big.Int), 135 chain: eth.BlockChain(), 136 proc: eth.BlockChain().Validator(), 137 possibleUncles: make(map[common.Hash]*types.Block), 138 coinbase: coinbase, 139 txQueue: make(map[common.Hash]*types.Transaction), 140 agents: make(map[Agent]struct{}), 141 fullValidation: false, 142 } 143 worker.events = worker.mux.Subscribe(core.ChainHeadEvent{}, core.ChainSideEvent{}, core.TxPreEvent{}) 144 go worker.update() 145 146 go worker.wait() 147 worker.commitNewWork() 148 149 return worker 150 } 151 152 func (self *worker) setEtherbase(addr common.Address) { 153 self.mu.Lock() 154 defer self.mu.Unlock() 155 self.coinbase = addr 156 } 157 158 func (self *worker) pending() (*types.Block, *state.StateDB) { 159 self.currentMu.Lock() 160 defer self.currentMu.Unlock() 161 162 if atomic.LoadInt32(&self.mining) == 0 { 163 return types.NewBlock( 164 self.current.header, 165 self.current.txs, 166 nil, 167 self.current.receipts, 168 ), self.current.state 169 } 170 return self.current.Block, self.current.state.Copy() 171 } 172 173 func (self *worker) start() { 174 self.mu.Lock() 175 defer self.mu.Unlock() 176 177 atomic.StoreInt32(&self.mining, 1) 178 179 // spin up agents 180 for agent := range self.agents { 181 agent.Start() 182 } 183 } 184 185 func (self *worker) stop() { 186 self.wg.Wait() 187 188 self.mu.Lock() 189 defer self.mu.Unlock() 190 if atomic.LoadInt32(&self.mining) == 1 { 191 // Stop all agents. 192 for agent := range self.agents { 193 agent.Stop() 194 // Remove CPU agents. 195 if _, ok := agent.(*CpuAgent); ok { 196 delete(self.agents, agent) 197 } 198 } 199 } 200 201 atomic.StoreInt32(&self.mining, 0) 202 atomic.StoreInt32(&self.atWork, 0) 203 } 204 205 func (self *worker) register(agent Agent) { 206 self.mu.Lock() 207 defer self.mu.Unlock() 208 self.agents[agent] = struct{}{} 209 agent.SetReturnCh(self.recv) 210 } 211 212 func (self *worker) unregister(agent Agent) { 213 self.mu.Lock() 214 defer self.mu.Unlock() 215 delete(self.agents, agent) 216 agent.Stop() 217 } 218 219 func (self *worker) update() { 220 for event := range self.events.Chan() { 221 // A real event arrived, process interesting content 222 switch ev := event.Data.(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(self.mux, types.Transactions{ev.Tx}, self.gasPrice, self.chain) 234 self.currentMu.Unlock() 235 } 236 } 237 } 238 } 239 240 func newLocalMinedBlock(blockNumber uint64, prevMinedBlocks *uint64RingBuffer) (minedBlocks *uint64RingBuffer) { 241 if prevMinedBlocks == nil { 242 minedBlocks = &uint64RingBuffer{next: 0, ints: make([]uint64, miningLogAtDepth+1)} 243 } else { 244 minedBlocks = prevMinedBlocks 245 } 246 247 minedBlocks.ints[minedBlocks.next] = blockNumber 248 minedBlocks.next = (minedBlocks.next + 1) % len(minedBlocks.ints) 249 return minedBlocks 250 } 251 252 func (self *worker) wait() { 253 for { 254 for result := range self.recv { 255 atomic.AddInt32(&self.atWork, -1) 256 257 if result == nil { 258 continue 259 } 260 block := result.Block 261 work := result.Work 262 263 if self.fullValidation { 264 if res := self.chain.InsertChain(types.Blocks{block}); res.Error != nil { 265 log.Printf("mine: ignoring invalid block #%d (%x) received: %v\n", block.Number(), block.Hash(), res.Error) 266 continue 267 } 268 go self.mux.Post(core.NewMinedBlockEvent{Block: block}) 269 } else { 270 work.state.CommitTo(self.chainDb, false) 271 parent := self.chain.GetBlock(block.ParentHash()) 272 if parent == nil { 273 glog.V(logger.Error).Infoln("Invalid block found during mining") 274 continue 275 } 276 277 auxValidator := self.eth.BlockChain().AuxValidator() 278 if err := core.ValidateHeader(self.config, auxValidator, block.Header(), parent.Header(), true, false); err != nil && err != core.BlockFutureErr { 279 glog.V(logger.Error).Infoln("Invalid header on mined block:", err) 280 continue 281 } 282 283 stat, err := self.chain.WriteBlock(block) 284 if err != nil { 285 glog.V(logger.Error).Infoln("error writing block to chain", err) 286 continue 287 } 288 289 // update block hash since it is now available and not when the receipt/log of individual transactions were created 290 for _, r := range work.receipts { 291 for _, l := range r.Logs { 292 l.BlockHash = block.Hash() 293 } 294 } 295 for _, log := range work.state.Logs() { 296 log.BlockHash = block.Hash() 297 } 298 299 // check if canon block and write transactions 300 if stat == core.CanonStatTy { 301 // This puts transactions in a extra db for rpc 302 core.WriteTransactions(self.chainDb, block) 303 // store the receipts 304 core.WriteReceipts(self.chainDb, work.receipts) 305 // Write map map bloom filters 306 core.WriteMipmapBloom(self.chainDb, block.NumberU64(), work.receipts) 307 } 308 309 // broadcast before waiting for validation 310 go func(block *types.Block, logs vm.Logs, receipts []*types.Receipt) { 311 self.mux.Post(core.NewMinedBlockEvent{Block: block}) 312 self.mux.Post(core.ChainEvent{Block: block, Hash: block.Hash(), Logs: logs}) 313 314 if stat == core.CanonStatTy { 315 self.mux.Post(core.ChainHeadEvent{Block: block}) 316 self.mux.Post(logs) 317 } 318 if err := core.WriteBlockReceipts(self.chainDb, block.Hash(), receipts); err != nil { 319 glog.V(logger.Warn).Infoln("error writing block receipts:", err) 320 } 321 }(block, work.state.Logs(), work.receipts) 322 } 323 324 // check staleness and display confirmation 325 var stale, confirm, staleOrConfirmMsg string 326 canonBlock := self.chain.GetBlockByNumber(block.NumberU64()) 327 if canonBlock != nil && canonBlock.Hash() != block.Hash() { 328 stale = "stale " 329 staleOrConfirmMsg = "stale" 330 } else { 331 confirm = "Wait 5 blocks for confirmation" 332 staleOrConfirmMsg = "wait_confirm" 333 work.localMinedBlocks = newLocalMinedBlock(block.Number().Uint64(), work.localMinedBlocks) 334 } 335 if logger.MlogEnabled() { 336 mlogMinerMineBlock.AssignDetails( 337 block.Number(), 338 block.Hash().Hex(), 339 staleOrConfirmMsg, 340 miningLogAtDepth, 341 ).Send(mlogMiner) 342 } 343 glog.V(logger.Info).Infof("🔨 Mined %sblock (#%v / %x). %s", stale, block.Number(), block.Hash().Bytes()[:4], confirm) 344 345 self.commitNewWork() 346 } 347 } 348 } 349 350 // push sends a new work task to currently live miner agents. 351 func (self *worker) push(work *Work) { 352 if atomic.LoadInt32(&self.mining) != 1 { 353 return 354 } 355 for agent := range self.agents { 356 atomic.AddInt32(&self.atWork, 1) 357 if ch := agent.Work(); ch != nil { 358 ch <- work 359 } 360 } 361 } 362 363 // makeCurrent creates a new environment for the current cycle. 364 func (self *worker) makeCurrent(parent *types.Block, header *types.Header) error { 365 state, err := self.chain.StateAt(parent.Root()) 366 if err != nil { 367 return err 368 } 369 work := &Work{ 370 config: self.config, 371 signer: types.NewChainIdSigner(self.config.GetChainID()), 372 state: state, 373 ancestors: set.New(), 374 family: set.New(), 375 uncles: set.New(), 376 header: header, 377 createdAt: time.Now(), 378 } 379 380 // when 08 is processed ancestors contain 07 (quick block) 381 for _, ancestor := range self.chain.GetBlocksFromHash(parent.Hash(), 7) { 382 for _, uncle := range ancestor.Uncles() { 383 work.family.Add(uncle.Hash()) 384 } 385 work.family.Add(ancestor.Hash()) 386 work.ancestors.Add(ancestor.Hash()) 387 } 388 accounts := self.eth.AccountManager().Accounts() 389 390 // Keep track of transactions which return errors so they can be removed 391 work.remove = set.New() 392 work.tcount = 0 393 work.ignoredTransactors = set.New() 394 work.lowGasTransactors = set.New() 395 work.ownedAccounts = accountAddressesSet(accounts) 396 if self.current != nil { 397 work.localMinedBlocks = self.current.localMinedBlocks 398 } 399 self.current = work 400 return nil 401 } 402 403 func (w *worker) setGasPrice(p *big.Int) { 404 w.mu.Lock() 405 defer w.mu.Unlock() 406 407 // calculate the minimal gas price the miner accepts when sorting out transactions. 408 const pct = int64(90) 409 w.gasPrice = gasprice(p, pct) 410 411 w.mux.Post(core.GasPriceChanged{Price: w.gasPrice}) 412 } 413 414 func (self *worker) isBlockLocallyMined(current *Work, deepBlockNum uint64) bool { 415 //Did this instance mine a block at {deepBlockNum} ? 416 var isLocal = false 417 for idx, blockNum := range current.localMinedBlocks.ints { 418 if deepBlockNum == blockNum { 419 isLocal = true 420 current.localMinedBlocks.ints[idx] = 0 //prevent showing duplicate logs 421 break 422 } 423 } 424 //Short-circuit on false, because the previous and following tests must both be true 425 if !isLocal { 426 return false 427 } 428 429 //Does the block at {deepBlockNum} send earnings to my coinbase? 430 var block = self.chain.GetBlockByNumber(deepBlockNum) 431 return block != nil && block.Coinbase() == self.coinbase 432 } 433 434 func (self *worker) logLocalMinedBlocks(current, previous *Work) { 435 if previous != nil && current.localMinedBlocks != nil { 436 nextBlockNum := current.Block.NumberU64() 437 for checkBlockNum := previous.Block.NumberU64(); checkBlockNum < nextBlockNum; checkBlockNum++ { 438 inspectBlockNum := checkBlockNum - miningLogAtDepth 439 if self.isBlockLocallyMined(current, inspectBlockNum) { 440 if logger.MlogEnabled() { 441 mlogMinerConfirmMinedBlock.AssignDetails( 442 inspectBlockNum, 443 ).Send(mlogMiner) 444 } 445 glog.V(logger.Info).Infof("🔨 🔗 Mined %d blocks back: block #%v", miningLogAtDepth, inspectBlockNum) 446 } 447 } 448 } 449 } 450 451 func (self *worker) commitNewWork() { 452 self.mu.Lock() 453 defer self.mu.Unlock() 454 self.uncleMu.Lock() 455 defer self.uncleMu.Unlock() 456 self.currentMu.Lock() 457 defer self.currentMu.Unlock() 458 459 tstart := time.Now() 460 parent := self.chain.CurrentBlock() 461 tstamp := tstart.Unix() 462 if parent.Time().Cmp(new(big.Int).SetInt64(tstamp)) >= 0 { 463 tstamp = parent.Time().Int64() + 1 464 } 465 // this will ensure we're not going off too far in the future 466 if now := time.Now().Unix(); tstamp > now+4 { 467 wait := time.Duration(tstamp-now) * time.Second 468 glog.V(logger.Info).Infoln("We are too far in the future. Waiting for", wait) 469 time.Sleep(wait) 470 } 471 472 num := parent.Number() 473 header := &types.Header{ 474 ParentHash: parent.Hash(), 475 Number: num.Add(num, common.Big1), 476 Difficulty: core.CalcDifficulty(self.config, uint64(tstamp), parent.Time().Uint64(), parent.Number(), parent.Difficulty()), 477 GasLimit: core.CalcGasLimit(parent), 478 GasUsed: new(big.Int), 479 Coinbase: self.coinbase, 480 Extra: HeaderExtra, 481 Time: big.NewInt(tstamp), 482 } 483 previous := self.current 484 // Could potentially happen if starting to mine in an odd state. 485 err := self.makeCurrent(parent, header) 486 if err != nil { 487 glog.V(logger.Info).Infoln("Could not create new env for mining, retrying on next block.") 488 return 489 } 490 // Create the current work task and check any fork transitions needed 491 work := self.current 492 493 /* //approach 1 494 transactions := self.eth.TxPool().GetTransactions() 495 sort.Sort(types.TxByNonce(transactions)) 496 */ 497 498 //approach 2 499 transactions := self.eth.TxPool().GetTransactions() 500 types.SortByPriceAndNonce(transactions) 501 502 /* // approach 3 503 // commit transactions for this run. 504 txPerOwner := make(map[common.Address]types.Transactions) 505 // Sort transactions by owner 506 for _, tx := range self.eth.TxPool().GetTransactions() { 507 from, _ := tx.From() // we can ignore the sender error 508 txPerOwner[from] = append(txPerOwner[from], tx) 509 } 510 var ( 511 singleTxOwner types.Transactions 512 multiTxOwner types.Transactions 513 ) 514 // Categorise transactions by 515 // 1. 1 owner tx per block 516 // 2. multi txs owner per block 517 for _, txs := range txPerOwner { 518 if len(txs) == 1 { 519 singleTxOwner = append(singleTxOwner, txs[0]) 520 } else { 521 multiTxOwner = append(multiTxOwner, txs...) 522 } 523 } 524 sort.Sort(types.TxByPrice(singleTxOwner)) 525 sort.Sort(types.TxByNonce(multiTxOwner)) 526 transactions := append(singleTxOwner, multiTxOwner...) 527 */ 528 529 work.commitTransactions(self.mux, transactions, self.gasPrice, self.chain) 530 self.eth.TxPool().RemoveTransactions(work.lowGasTxs) 531 532 // compute uncles for the new block. 533 var ( 534 uncles []*types.Header 535 badUncles []common.Hash 536 ) 537 for hash, uncle := range self.possibleUncles { 538 if len(uncles) == 2 { 539 break 540 } 541 if err := self.commitUncle(work, uncle.Header()); err != nil { 542 if glog.V(logger.Ridiculousness) { 543 glog.V(logger.Detail).Infof("Bad uncle found and will be removed (%x)\n", hash[:4]) 544 glog.V(logger.Detail).Infoln(uncle) 545 } 546 badUncles = append(badUncles, hash) 547 } else { 548 glog.V(logger.Debug).Infof("commiting %x as uncle\n", hash[:4]) 549 uncles = append(uncles, uncle.Header()) 550 } 551 } 552 for _, hash := range badUncles { 553 delete(self.possibleUncles, hash) 554 } 555 556 if atomic.LoadInt32(&self.mining) == 1 { 557 // commit state root after all state transitions. 558 core.AccumulateRewards(work.config, work.state, header, uncles) 559 header.Root = work.state.IntermediateRoot(false) 560 } 561 562 // create the new block whose nonce will be mined. 563 work.Block = types.NewBlock(header, work.txs, uncles, work.receipts) 564 565 // We only care about logging if we're actually mining. 566 if atomic.LoadInt32(&self.mining) == 1 { 567 elapsed := time.Since(tstart) 568 if logger.MlogEnabled() { 569 mlogMinerCommitWorkBlock.AssignDetails( 570 work.Block.Number(), 571 work.tcount, 572 len(uncles), 573 elapsed, 574 ).Send(mlogMiner) 575 } 576 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), elapsed) 577 self.logLocalMinedBlocks(work, previous) 578 } 579 self.push(work) 580 } 581 582 func (self *worker) commitUncle(work *Work, uncle *types.Header) error { 583 hash := uncle.Hash() 584 var e error 585 if logger.MlogEnabled() { 586 defer func() { 587 mlogMinerCommitUncle.AssignDetails( 588 uncle.Number, 589 hash.Hex(), 590 e, 591 ).Send(mlogMiner) 592 }() 593 } 594 if work.uncles.Has(hash) { 595 e = core.UncleError("Uncle not unique") 596 return e 597 } 598 if !work.ancestors.Has(uncle.ParentHash) { 599 e = core.UncleError(fmt.Sprintf("Uncle's parent unknown (%x)", uncle.ParentHash[0:4])) 600 return e 601 } 602 if work.family.Has(hash) { 603 e = core.UncleError(fmt.Sprintf("Uncle already in family (%x)", hash)) 604 return e 605 } 606 work.uncles.Add(uncle.Hash()) 607 return nil 608 } 609 610 func (env *Work) commitTransactions(mux *event.TypeMux, transactions types.Transactions, gasPrice *big.Int, bc *core.BlockChain) { 611 gp := new(core.GasPool).AddGas(env.header.GasLimit) 612 613 var coalescedLogs vm.Logs 614 for _, tx := range transactions { 615 // Error may be ignored here. The error has already been checked 616 // during transaction acceptance is the transaction pool. 617 // We use the eip155 signer regardless of the current hf. 618 tx.SetSigner(env.signer) 619 from, _ := types.Sender(env.signer, tx) 620 // Check whether the tx is replay protected. If we're not in the EIP155 hf 621 // phase, start ignoring the sender until we do. 622 if tx.Protected() && !env.config.IsDiehard(env.header.Number) { 623 glog.V(logger.Detail).Infof("Transaction (%x) is replay protected, but we haven't yet hardforked. Transaction will be ignored until we hardfork.\n", tx.Hash()) 624 continue 625 } 626 627 // Check if it falls within margin. Txs from owned accounts are always processed. 628 if tx.GasPrice().Cmp(gasPrice) < 0 && !env.ownedAccounts.Has(from) { 629 // ignore the transaction and transactor. We ignore the transactor 630 // because nonce will fail after ignoring this transaction so there's 631 // no point 632 env.lowGasTransactors.Add(from) 633 634 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]) 635 } 636 637 // Continue with the next transaction if the transaction sender is included in 638 // the low gas tx set. This will also remove the tx and all sequential transaction 639 // from this transactor 640 if env.lowGasTransactors.Has(from) { 641 // add tx to the low gas set. This will be removed at the end of the run 642 // owned accounts are ignored 643 if !env.ownedAccounts.Has(from) { 644 env.lowGasTxs = append(env.lowGasTxs, tx) 645 } 646 continue 647 } 648 649 // Move on to the next transaction when the transactor is in ignored transactions set 650 // This may occur when a transaction hits the gas limit. When a gas limit is hit and 651 // the transaction is processed (that could potentially be included in the block) it 652 // will throw a nonce error because the previous transaction hasn't been processed. 653 // Therefor we need to ignore any transaction after the ignored one. 654 if env.ignoredTransactors.Has(from) { 655 continue 656 } 657 658 env.state.StartRecord(tx.Hash(), common.Hash{}, 0) 659 660 err, logs := env.commitTransaction(tx, bc, gp) 661 switch { 662 case core.IsGasLimitErr(err): 663 // ignore the transactor so no nonce errors will be thrown for this account 664 // next time the worker is run, they'll be picked up again. 665 env.ignoredTransactors.Add(from) 666 667 glog.V(logger.Detail).Infof("Gas limit reached for (%x) in this block. Continue to try smaller txs\n", from[:4]) 668 case err != nil: 669 env.remove.Add(tx.Hash()) 670 671 if glog.V(logger.Detail) { 672 glog.Infof("TX (%x) failed, will be removed: %v\n", tx.Hash().Bytes()[:4], err) 673 } 674 default: 675 env.tcount++ 676 coalescedLogs = append(coalescedLogs, logs...) 677 } 678 679 } 680 if len(coalescedLogs) > 0 || env.tcount > 0 { 681 go func(logs vm.Logs, tcount int) { 682 if len(logs) > 0 { 683 mux.Post(core.PendingLogsEvent{Logs: logs}) 684 } 685 if tcount > 0 { 686 mux.Post(core.PendingStateEvent{}) 687 } 688 }(coalescedLogs, env.tcount) 689 } 690 } 691 692 func (env *Work) commitTransaction(tx *types.Transaction, bc *core.BlockChain, gp *core.GasPool) (error, vm.Logs) { 693 snap := env.state.Snapshot() 694 695 receipt, logs, _, err := core.ApplyTransaction(env.config, bc, gp, env.state, env.header, tx, env.header.GasUsed) 696 697 if logger.MlogEnabled() { 698 defer func() { 699 mlogMinerCommitTx.AssignDetails( 700 env.header.Number, 701 tx.Hash().Hex(), 702 err, 703 ).Send(mlogMiner) 704 }() 705 } 706 707 if err != nil { 708 env.state.RevertToSnapshot(snap) 709 return err, nil 710 } 711 env.txs = append(env.txs, tx) 712 env.receipts = append(env.receipts, receipt) 713 714 return nil, logs 715 } 716 717 // TODO: remove or use 718 func (self *worker) HashRate() int64 { 719 return 0 720 } 721 722 // gasprice calculates a reduced gas price based on the pct 723 // XXX Use big.Rat? 724 func gasprice(price *big.Int, pct int64) *big.Int { 725 p := new(big.Int).Set(price) 726 p.Div(p, big.NewInt(100)) 727 p.Mul(p, big.NewInt(pct)) 728 return p 729 } 730 731 func accountAddressesSet(accounts []accounts.Account) *set.Set { 732 accountSet := set.New() 733 for _, account := range accounts { 734 accountSet.Add(account.Address) 735 } 736 return accountSet 737 }