github.com/avence12/go-ethereum@v1.5.10-0.20170320123548-1dfd65f6d047/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  	"bytes"
    21  	"fmt"
    22  	"math/big"
    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/core/vm"
    33  	"github.com/ethereum/go-ethereum/ethdb"
    34  	"github.com/ethereum/go-ethereum/event"
    35  	"github.com/ethereum/go-ethereum/log"
    36  	"github.com/ethereum/go-ethereum/params"
    37  	"github.com/ethereum/go-ethereum/pow"
    38  	"gopkg.in/fatih/set.v0"
    39  )
    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  // Work is the workers current environment and holds
    56  // all of the current state information
    57  type Work struct {
    58  	config *params.ChainConfig
    59  	signer types.Signer
    60  
    61  	state         *state.StateDB // apply state changes here
    62  	ancestors     *set.Set       // ancestor set (used for checking uncle parent validity)
    63  	family        *set.Set       // family set (used for checking uncle invalidity)
    64  	uncles        *set.Set       // uncle set
    65  	tcount        int            // tx count in cycle
    66  	ownedAccounts *set.Set
    67  	lowGasTxs     types.Transactions
    68  	failedTxs     types.Transactions
    69  
    70  	Block *types.Block // the new block
    71  
    72  	header   *types.Header
    73  	txs      []*types.Transaction
    74  	receipts []*types.Receipt
    75  
    76  	createdAt time.Time
    77  }
    78  
    79  type Result struct {
    80  	Work  *Work
    81  	Block *types.Block
    82  }
    83  
    84  // worker is the main object which takes care of applying messages to the new state
    85  type worker struct {
    86  	config *params.ChainConfig
    87  
    88  	mu sync.Mutex
    89  
    90  	// update loop
    91  	mux    *event.TypeMux
    92  	events *event.TypeMuxSubscription
    93  	wg     sync.WaitGroup
    94  
    95  	agents map[Agent]struct{}
    96  	recv   chan *Result
    97  	pow    pow.PoW
    98  
    99  	eth     Backend
   100  	chain   *core.BlockChain
   101  	proc    core.Validator
   102  	chainDb ethdb.Database
   103  
   104  	coinbase common.Address
   105  	gasPrice *big.Int
   106  	extra    []byte
   107  
   108  	currentMu sync.Mutex
   109  	current   *Work
   110  
   111  	uncleMu        sync.Mutex
   112  	possibleUncles map[common.Hash]*types.Block
   113  
   114  	txQueueMu sync.Mutex
   115  	txQueue   map[common.Hash]*types.Transaction
   116  
   117  	unconfirmed *unconfirmedBlocks // set of locally mined blocks pending canonicalness confirmations
   118  
   119  	// atomic status counters
   120  	mining int32
   121  	atWork int32
   122  
   123  	fullValidation bool
   124  }
   125  
   126  func newWorker(config *params.ChainConfig, coinbase common.Address, eth Backend, mux *event.TypeMux) *worker {
   127  	worker := &worker{
   128  		config:         config,
   129  		eth:            eth,
   130  		mux:            mux,
   131  		chainDb:        eth.ChainDb(),
   132  		recv:           make(chan *Result, resultQueueSize),
   133  		gasPrice:       new(big.Int),
   134  		chain:          eth.BlockChain(),
   135  		proc:           eth.BlockChain().Validator(),
   136  		possibleUncles: make(map[common.Hash]*types.Block),
   137  		coinbase:       coinbase,
   138  		txQueue:        make(map[common.Hash]*types.Transaction),
   139  		agents:         make(map[Agent]struct{}),
   140  		unconfirmed:    newUnconfirmedBlocks(eth.BlockChain(), 5),
   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) setExtra(extra []byte) {
   159  	self.mu.Lock()
   160  	defer self.mu.Unlock()
   161  	self.extra = extra
   162  }
   163  
   164  func (self *worker) pending() (*types.Block, *state.StateDB) {
   165  	self.currentMu.Lock()
   166  	defer self.currentMu.Unlock()
   167  
   168  	if atomic.LoadInt32(&self.mining) == 0 {
   169  		return types.NewBlock(
   170  			self.current.header,
   171  			self.current.txs,
   172  			nil,
   173  			self.current.receipts,
   174  		), self.current.state.Copy()
   175  	}
   176  	return self.current.Block, self.current.state.Copy()
   177  }
   178  
   179  func (self *worker) pendingBlock() *types.Block {
   180  	self.currentMu.Lock()
   181  	defer self.currentMu.Unlock()
   182  
   183  	if atomic.LoadInt32(&self.mining) == 0 {
   184  		return types.NewBlock(
   185  			self.current.header,
   186  			self.current.txs,
   187  			nil,
   188  			self.current.receipts,
   189  		)
   190  	}
   191  	return self.current.Block
   192  }
   193  
   194  func (self *worker) start() {
   195  	self.mu.Lock()
   196  	defer self.mu.Unlock()
   197  
   198  	atomic.StoreInt32(&self.mining, 1)
   199  
   200  	// spin up agents
   201  	for agent := range self.agents {
   202  		agent.Start()
   203  	}
   204  }
   205  
   206  func (self *worker) stop() {
   207  	self.wg.Wait()
   208  
   209  	self.mu.Lock()
   210  	defer self.mu.Unlock()
   211  	if atomic.LoadInt32(&self.mining) == 1 {
   212  		// Stop all agents.
   213  		for agent := range self.agents {
   214  			agent.Stop()
   215  			// Remove CPU agents.
   216  			if _, ok := agent.(*CpuAgent); ok {
   217  				delete(self.agents, agent)
   218  			}
   219  		}
   220  	}
   221  
   222  	atomic.StoreInt32(&self.mining, 0)
   223  	atomic.StoreInt32(&self.atWork, 0)
   224  }
   225  
   226  func (self *worker) register(agent Agent) {
   227  	self.mu.Lock()
   228  	defer self.mu.Unlock()
   229  	self.agents[agent] = struct{}{}
   230  	agent.SetReturnCh(self.recv)
   231  }
   232  
   233  func (self *worker) unregister(agent Agent) {
   234  	self.mu.Lock()
   235  	defer self.mu.Unlock()
   236  	delete(self.agents, agent)
   237  	agent.Stop()
   238  }
   239  
   240  func (self *worker) update() {
   241  	for event := range self.events.Chan() {
   242  		// A real event arrived, process interesting content
   243  		switch ev := event.Data.(type) {
   244  		case core.ChainHeadEvent:
   245  			self.commitNewWork()
   246  		case core.ChainSideEvent:
   247  			self.uncleMu.Lock()
   248  			self.possibleUncles[ev.Block.Hash()] = ev.Block
   249  			self.uncleMu.Unlock()
   250  		case core.TxPreEvent:
   251  			// Apply transaction to the pending state if we're not mining
   252  			if atomic.LoadInt32(&self.mining) == 0 {
   253  				self.currentMu.Lock()
   254  
   255  				acc, _ := types.Sender(self.current.signer, ev.Tx)
   256  				txs := map[common.Address]types.Transactions{acc: {ev.Tx}}
   257  				txset := types.NewTransactionsByPriceAndNonce(txs)
   258  
   259  				self.current.commitTransactions(self.mux, txset, self.gasPrice, self.chain)
   260  				self.currentMu.Unlock()
   261  			}
   262  		}
   263  	}
   264  }
   265  
   266  func (self *worker) wait() {
   267  	for {
   268  		mustCommitNewWork := true
   269  		for result := range self.recv {
   270  			atomic.AddInt32(&self.atWork, -1)
   271  
   272  			if result == nil {
   273  				continue
   274  			}
   275  			block := result.Block
   276  			work := result.Work
   277  
   278  			if self.fullValidation {
   279  				if _, err := self.chain.InsertChain(types.Blocks{block}); err != nil {
   280  					log.Error(fmt.Sprint("mining err", err))
   281  					continue
   282  				}
   283  				go self.mux.Post(core.NewMinedBlockEvent{Block: block})
   284  			} else {
   285  				work.state.Commit(self.config.IsEIP158(block.Number()))
   286  				parent := self.chain.GetBlock(block.ParentHash(), block.NumberU64()-1)
   287  				if parent == nil {
   288  					log.Error(fmt.Sprint("Invalid block found during mining"))
   289  					continue
   290  				}
   291  
   292  				auxValidator := self.eth.BlockChain().AuxValidator()
   293  				if err := core.ValidateHeader(self.config, auxValidator, block.Header(), parent.Header(), true, false); err != nil && err != core.BlockFutureErr {
   294  					log.Error(fmt.Sprint("Invalid header on mined block:", err))
   295  					continue
   296  				}
   297  
   298  				stat, err := self.chain.WriteBlock(block)
   299  				if err != nil {
   300  					log.Error(fmt.Sprint("error writing block to chain", err))
   301  					continue
   302  				}
   303  
   304  				// update block hash since it is now available and not when the receipt/log of individual transactions were created
   305  				for _, r := range work.receipts {
   306  					for _, l := range r.Logs {
   307  						l.BlockHash = block.Hash()
   308  					}
   309  				}
   310  				for _, log := range work.state.Logs() {
   311  					log.BlockHash = block.Hash()
   312  				}
   313  
   314  				// check if canon block and write transactions
   315  				if stat == core.CanonStatTy {
   316  					// This puts transactions in a extra db for rpc
   317  					core.WriteTransactions(self.chainDb, block)
   318  					// store the receipts
   319  					core.WriteReceipts(self.chainDb, work.receipts)
   320  					// Write map map bloom filters
   321  					core.WriteMipmapBloom(self.chainDb, block.NumberU64(), work.receipts)
   322  					// implicit by posting ChainHeadEvent
   323  					mustCommitNewWork = false
   324  				}
   325  
   326  				// broadcast before waiting for validation
   327  				go func(block *types.Block, logs []*types.Log, receipts []*types.Receipt) {
   328  					self.mux.Post(core.NewMinedBlockEvent{Block: block})
   329  					self.mux.Post(core.ChainEvent{Block: block, Hash: block.Hash(), Logs: logs})
   330  
   331  					if stat == core.CanonStatTy {
   332  						self.mux.Post(core.ChainHeadEvent{Block: block})
   333  						self.mux.Post(logs)
   334  					}
   335  					if err := core.WriteBlockReceipts(self.chainDb, block.Hash(), block.NumberU64(), receipts); err != nil {
   336  						log.Warn(fmt.Sprint("error writing block receipts:", err))
   337  					}
   338  				}(block, work.state.Logs(), work.receipts)
   339  			}
   340  			// Insert the block into the set of pending ones to wait for confirmations
   341  			self.unconfirmed.Insert(block.NumberU64(), block.Hash())
   342  
   343  			if mustCommitNewWork {
   344  				self.commitNewWork()
   345  			}
   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.NewEIP155Signer(self.config.ChainId),
   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  	wallets := self.eth.AccountManager().Wallets()
   389  	accounts := make([]accounts.Account, 0, len(wallets))
   390  	for _, wallet := range wallets {
   391  		accounts = append(accounts, wallet.Accounts()...)
   392  	}
   393  	// Keep track of transactions which return errors so they can be removed
   394  	work.tcount = 0
   395  	work.ownedAccounts = accountAddressesSet(accounts)
   396  	self.current = work
   397  	return nil
   398  }
   399  
   400  func (w *worker) setGasPrice(p *big.Int) {
   401  	w.mu.Lock()
   402  	defer w.mu.Unlock()
   403  
   404  	// calculate the minimal gas price the miner accepts when sorting out transactions.
   405  	const pct = int64(90)
   406  	w.gasPrice = gasprice(p, pct)
   407  
   408  	w.mux.Post(core.GasPriceChanged{Price: w.gasPrice})
   409  }
   410  
   411  func (self *worker) commitNewWork() {
   412  	self.mu.Lock()
   413  	defer self.mu.Unlock()
   414  	self.uncleMu.Lock()
   415  	defer self.uncleMu.Unlock()
   416  	self.currentMu.Lock()
   417  	defer self.currentMu.Unlock()
   418  
   419  	tstart := time.Now()
   420  	parent := self.chain.CurrentBlock()
   421  
   422  	tstamp := tstart.Unix()
   423  	if parent.Time().Cmp(new(big.Int).SetInt64(tstamp)) >= 0 {
   424  		tstamp = parent.Time().Int64() + 1
   425  	}
   426  	// this will ensure we're not going off too far in the future
   427  	if now := time.Now().Unix(); tstamp > now+4 {
   428  		wait := time.Duration(tstamp-now) * time.Second
   429  		log.Info(fmt.Sprint("We are too far in the future. Waiting for", wait))
   430  		time.Sleep(wait)
   431  	}
   432  
   433  	num := parent.Number()
   434  	header := &types.Header{
   435  		ParentHash: parent.Hash(),
   436  		Number:     num.Add(num, common.Big1),
   437  		Difficulty: core.CalcDifficulty(self.config, uint64(tstamp), parent.Time().Uint64(), parent.Number(), parent.Difficulty()),
   438  		GasLimit:   core.CalcGasLimit(parent),
   439  		GasUsed:    new(big.Int),
   440  		Coinbase:   self.coinbase,
   441  		Extra:      self.extra,
   442  		Time:       big.NewInt(tstamp),
   443  	}
   444  	// If we are care about TheDAO hard-fork check whether to override the extra-data or not
   445  	if daoBlock := self.config.DAOForkBlock; daoBlock != nil {
   446  		// Check whether the block is among the fork extra-override range
   447  		limit := new(big.Int).Add(daoBlock, params.DAOForkExtraRange)
   448  		if header.Number.Cmp(daoBlock) >= 0 && header.Number.Cmp(limit) < 0 {
   449  			// Depending whether we support or oppose the fork, override differently
   450  			if self.config.DAOForkSupport {
   451  				header.Extra = common.CopyBytes(params.DAOForkBlockExtra)
   452  			} else if bytes.Equal(header.Extra, params.DAOForkBlockExtra) {
   453  				header.Extra = []byte{} // If miner opposes, don't let it use the reserved extra-data
   454  			}
   455  		}
   456  	}
   457  	// Could potentially happen if starting to mine in an odd state.
   458  	err := self.makeCurrent(parent, header)
   459  	if err != nil {
   460  		log.Info(fmt.Sprint("Could not create new env for mining, retrying on next block."))
   461  		return
   462  	}
   463  	// Create the current work task and check any fork transitions needed
   464  	work := self.current
   465  	if self.config.DAOForkSupport && self.config.DAOForkBlock != nil && self.config.DAOForkBlock.Cmp(header.Number) == 0 {
   466  		core.ApplyDAOHardFork(work.state)
   467  	}
   468  
   469  	pending, err := self.eth.TxPool().Pending()
   470  	if err != nil {
   471  		log.Error(fmt.Sprintf("Could not fetch pending transactions: %v", err))
   472  		return
   473  	}
   474  
   475  	txs := types.NewTransactionsByPriceAndNonce(pending)
   476  	work.commitTransactions(self.mux, txs, self.gasPrice, self.chain)
   477  
   478  	self.eth.TxPool().RemoveBatch(work.lowGasTxs)
   479  	self.eth.TxPool().RemoveBatch(work.failedTxs)
   480  
   481  	// compute uncles for the new block.
   482  	var (
   483  		uncles    []*types.Header
   484  		badUncles []common.Hash
   485  	)
   486  	for hash, uncle := range self.possibleUncles {
   487  		if len(uncles) == 2 {
   488  			break
   489  		}
   490  		if err := self.commitUncle(work, uncle.Header()); err != nil {
   491  			log.Trace(fmt.Sprintf("Bad uncle found and will be removed (%x)\n", hash[:4]))
   492  			log.Trace(fmt.Sprint(uncle))
   493  
   494  			badUncles = append(badUncles, hash)
   495  		} else {
   496  			log.Debug(fmt.Sprintf("committing %x as uncle\n", hash[:4]))
   497  			uncles = append(uncles, uncle.Header())
   498  		}
   499  	}
   500  	for _, hash := range badUncles {
   501  		delete(self.possibleUncles, hash)
   502  	}
   503  
   504  	if atomic.LoadInt32(&self.mining) == 1 {
   505  		// commit state root after all state transitions.
   506  		core.AccumulateRewards(work.state, header, uncles)
   507  		header.Root = work.state.IntermediateRoot(self.config.IsEIP158(header.Number))
   508  	}
   509  
   510  	// create the new block whose nonce will be mined.
   511  	work.Block = types.NewBlock(header, work.txs, uncles, work.receipts)
   512  
   513  	// We only care about logging if we're actually mining.
   514  	if atomic.LoadInt32(&self.mining) == 1 {
   515  		log.Info(fmt.Sprintf("commit new work on block %v with %d txs & %d uncles. Took %v\n", work.Block.Number(), work.tcount, len(uncles), time.Since(tstart)))
   516  		self.unconfirmed.Shift(work.Block.NumberU64() - 1)
   517  	}
   518  	self.push(work)
   519  }
   520  
   521  func (self *worker) commitUncle(work *Work, uncle *types.Header) error {
   522  	hash := uncle.Hash()
   523  	if work.uncles.Has(hash) {
   524  		return core.UncleError("Uncle not unique")
   525  	}
   526  	if !work.ancestors.Has(uncle.ParentHash) {
   527  		return core.UncleError(fmt.Sprintf("Uncle's parent unknown (%x)", uncle.ParentHash[0:4]))
   528  	}
   529  	if work.family.Has(hash) {
   530  		return core.UncleError(fmt.Sprintf("Uncle already in family (%x)", hash))
   531  	}
   532  	work.uncles.Add(uncle.Hash())
   533  	return nil
   534  }
   535  
   536  func (env *Work) commitTransactions(mux *event.TypeMux, txs *types.TransactionsByPriceAndNonce, gasPrice *big.Int, bc *core.BlockChain) {
   537  	gp := new(core.GasPool).AddGas(env.header.GasLimit)
   538  
   539  	var coalescedLogs []*types.Log
   540  
   541  	for {
   542  		// Retrieve the next transaction and abort if all done
   543  		tx := txs.Peek()
   544  		if tx == nil {
   545  			break
   546  		}
   547  		// Error may be ignored here. The error has already been checked
   548  		// during transaction acceptance is the transaction pool.
   549  		//
   550  		// We use the eip155 signer regardless of the current hf.
   551  		from, _ := types.Sender(env.signer, tx)
   552  		// Check whether the tx is replay protected. If we're not in the EIP155 hf
   553  		// phase, start ignoring the sender until we do.
   554  		if tx.Protected() && !env.config.IsEIP155(env.header.Number) {
   555  			log.Trace(fmt.Sprintf("Transaction (%x) is replay protected, but we haven't yet hardforked. Transaction will be ignored until we hardfork.\n", tx.Hash()))
   556  
   557  			txs.Pop()
   558  			continue
   559  		}
   560  
   561  		// Ignore any transactions (and accounts subsequently) with low gas limits
   562  		if tx.GasPrice().Cmp(gasPrice) < 0 && !env.ownedAccounts.Has(from) {
   563  			// Pop the current low-priced transaction without shifting in the next from the account
   564  			log.Info(fmt.Sprintf("Transaction (%x) below gas price (tx=%dwei ask=%dwei). All sequential txs from this address(%x) will be ignored\n", tx.Hash().Bytes()[:4], tx.GasPrice(), gasPrice, from[:4]))
   565  
   566  			env.lowGasTxs = append(env.lowGasTxs, tx)
   567  			txs.Pop()
   568  
   569  			continue
   570  		}
   571  		// Start executing the transaction
   572  		env.state.StartRecord(tx.Hash(), common.Hash{}, env.tcount)
   573  
   574  		err, logs := env.commitTransaction(tx, bc, gp)
   575  		switch {
   576  		case core.IsGasLimitErr(err):
   577  			// Pop the current out-of-gas transaction without shifting in the next from the account
   578  			log.Trace(fmt.Sprintf("Gas limit reached for (%x) in this block. Continue to try smaller txs\n", from[:4]))
   579  			txs.Pop()
   580  
   581  		case err != nil:
   582  			// Pop the current failed transaction without shifting in the next from the account
   583  			log.Trace(fmt.Sprintf("Transaction (%x) failed, will be removed: %v\n", tx.Hash().Bytes()[:4], err))
   584  			env.failedTxs = append(env.failedTxs, tx)
   585  			txs.Pop()
   586  
   587  		default:
   588  			// Everything ok, collect the logs and shift in the next transaction from the same account
   589  			coalescedLogs = append(coalescedLogs, logs...)
   590  			env.tcount++
   591  			txs.Shift()
   592  		}
   593  	}
   594  
   595  	if len(coalescedLogs) > 0 || env.tcount > 0 {
   596  		// make a copy, the state caches the logs and these logs get "upgraded" from pending to mined
   597  		// logs by filling in the block hash when the block was mined by the local miner. This can
   598  		// cause a race condition if a log was "upgraded" before the PendingLogsEvent is processed.
   599  		cpy := make([]*types.Log, len(coalescedLogs))
   600  		for i, l := range coalescedLogs {
   601  			cpy[i] = new(types.Log)
   602  			*cpy[i] = *l
   603  		}
   604  		go func(logs []*types.Log, tcount int) {
   605  			if len(logs) > 0 {
   606  				mux.Post(core.PendingLogsEvent{Logs: logs})
   607  			}
   608  			if tcount > 0 {
   609  				mux.Post(core.PendingStateEvent{})
   610  			}
   611  		}(cpy, env.tcount)
   612  	}
   613  }
   614  
   615  func (env *Work) commitTransaction(tx *types.Transaction, bc *core.BlockChain, gp *core.GasPool) (error, []*types.Log) {
   616  	snap := env.state.Snapshot()
   617  
   618  	receipt, _, err := core.ApplyTransaction(env.config, bc, gp, env.state, env.header, tx, env.header.GasUsed, vm.Config{})
   619  	if err != nil {
   620  		env.state.RevertToSnapshot(snap)
   621  		return err, nil
   622  	}
   623  	env.txs = append(env.txs, tx)
   624  	env.receipts = append(env.receipts, receipt)
   625  
   626  	return nil, receipt.Logs
   627  }
   628  
   629  // TODO: remove or use
   630  func (self *worker) HashRate() int64 {
   631  	return 0
   632  }
   633  
   634  // gasprice calculates a reduced gas price based on the pct
   635  // XXX Use big.Rat?
   636  func gasprice(price *big.Int, pct int64) *big.Int {
   637  	p := new(big.Int).Set(price)
   638  	p.Div(p, big.NewInt(100))
   639  	p.Mul(p, big.NewInt(pct))
   640  	return p
   641  }
   642  
   643  func accountAddressesSet(accounts []accounts.Account) *set.Set {
   644  	accountSet := set.New()
   645  	for _, account := range accounts {
   646  		accountSet.Add(account.Address)
   647  	}
   648  	return accountSet
   649  }