gitlab.com/aquachain/aquachain@v1.17.16-rc3.0.20221018032414-e3ddf1e1c055/opt/miner/worker.go (about)

     1  // Copyright 2018 The aquachain Authors
     2  // This file is part of the aquachain library.
     3  //
     4  // The aquachain 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 aquachain 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 aquachain library. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  package miner
    18  
    19  import (
    20  	"fmt"
    21  	"math/big"
    22  	"sync"
    23  	"sync/atomic"
    24  	"time"
    25  
    26  	set "github.com/deckarep/golang-set"
    27  	"gitlab.com/aquachain/aquachain/aqua/event"
    28  	"gitlab.com/aquachain/aquachain/aquadb"
    29  	"gitlab.com/aquachain/aquachain/common"
    30  	"gitlab.com/aquachain/aquachain/common/log"
    31  	"gitlab.com/aquachain/aquachain/consensus"
    32  	"gitlab.com/aquachain/aquachain/consensus/misc"
    33  	"gitlab.com/aquachain/aquachain/core"
    34  	"gitlab.com/aquachain/aquachain/core/state"
    35  	"gitlab.com/aquachain/aquachain/core/types"
    36  	"gitlab.com/aquachain/aquachain/core/vm"
    37  	"gitlab.com/aquachain/aquachain/params"
    38  )
    39  
    40  const (
    41  	resultQueueSize  = 10
    42  	miningLogAtDepth = 5
    43  
    44  	// txChanSize is the size of channel listening to TxPreEvent.
    45  	// The number is referenced from the size of tx pool.
    46  	txChanSize = 4096
    47  	// chainHeadChanSize is the size of channel listening to ChainHeadEvent.
    48  	chainHeadChanSize = 10
    49  	// chainSideChanSize is the size of channel listening to ChainSideEvent.
    50  	chainSideChanSize = 10
    51  )
    52  
    53  // Agent can register themself with the worker
    54  type Agent interface {
    55  	Work() chan<- *Work
    56  	SetReturnCh(chan<- *Result)
    57  	Stop()
    58  	Start()
    59  	GetHashRate() int64
    60  }
    61  
    62  // Work is the workers current environment and holds
    63  // all of the current state information
    64  type Work struct {
    65  	config *params.ChainConfig
    66  	signer types.Signer
    67  
    68  	state     *state.StateDB // apply state changes here
    69  	ancestors set.Set        // ancestor set (used for checking uncle parent validity)
    70  	family    set.Set        // family set (used for checking uncle invalidity)
    71  	uncles    set.Set        // uncle set
    72  	tcount    int            // tx count in cycle
    73  
    74  	Block *types.Block // the new block
    75  
    76  	header   *types.Header
    77  	txs      []*types.Transaction
    78  	receipts []*types.Receipt
    79  
    80  	createdAt time.Time
    81  }
    82  
    83  type Result struct {
    84  	Work  *Work
    85  	Block *types.Block
    86  }
    87  
    88  // worker is the main object which takes care of applying messages to the new state
    89  type worker struct {
    90  	config *params.ChainConfig
    91  	engine consensus.Engine
    92  
    93  	mu sync.Mutex
    94  
    95  	// update loop
    96  	mux          *event.TypeMux
    97  	txCh         chan core.TxPreEvent
    98  	txSub        event.Subscription
    99  	chainHeadCh  chan core.ChainHeadEvent
   100  	chainHeadSub event.Subscription
   101  	chainSideCh  chan core.ChainSideEvent
   102  	chainSideSub event.Subscription
   103  	wg           sync.WaitGroup
   104  
   105  	agents map[Agent]struct{}
   106  	recv   chan *Result
   107  
   108  	aqua    Backend
   109  	chain   *core.BlockChain
   110  	proc    core.Validator
   111  	chainDb aquadb.Database
   112  
   113  	coinbase common.Address
   114  	extra    []byte
   115  
   116  	currentMu sync.Mutex
   117  	current   *Work
   118  
   119  	uncleMu        sync.Mutex
   120  	possibleUncles map[common.Hash]*types.Block
   121  
   122  	unconfirmed *unconfirmedBlocks // set of locally mined blocks pending canonicalness confirmations
   123  
   124  	// atomic status counters
   125  	mining int32
   126  	atWork int32
   127  }
   128  
   129  func newWorker(config *params.ChainConfig, engine consensus.Engine, coinbase common.Address, aqua Backend, mux *event.TypeMux) *worker {
   130  	worker := &worker{
   131  		config:         config,
   132  		engine:         engine,
   133  		aqua:           aqua,
   134  		mux:            mux,
   135  		txCh:           make(chan core.TxPreEvent, txChanSize),
   136  		chainHeadCh:    make(chan core.ChainHeadEvent, chainHeadChanSize),
   137  		chainSideCh:    make(chan core.ChainSideEvent, chainSideChanSize),
   138  		chainDb:        aqua.ChainDb(),
   139  		recv:           make(chan *Result, resultQueueSize),
   140  		chain:          aqua.BlockChain(),
   141  		proc:           aqua.BlockChain().Validator(),
   142  		possibleUncles: make(map[common.Hash]*types.Block),
   143  		coinbase:       coinbase,
   144  		agents:         make(map[Agent]struct{}),
   145  		unconfirmed:    newUnconfirmedBlocks(aqua.BlockChain(), miningLogAtDepth),
   146  	}
   147  	// Subscribe TxPreEvent for tx pool
   148  	worker.txSub = aqua.TxPool().SubscribeTxPreEvent(worker.txCh)
   149  	// Subscribe events for blockchain
   150  	worker.chainHeadSub = aqua.BlockChain().SubscribeChainHeadEvent(worker.chainHeadCh)
   151  	worker.chainSideSub = aqua.BlockChain().SubscribeChainSideEvent(worker.chainSideCh)
   152  	go worker.update()
   153  
   154  	go worker.wait()
   155  	worker.commitNewWork()
   156  
   157  	return worker
   158  }
   159  
   160  func (w *worker) setAquabase(addr common.Address) {
   161  	w.mu.Lock()
   162  	defer w.mu.Unlock()
   163  	w.coinbase = addr
   164  }
   165  
   166  func (w *worker) setExtra(extra []byte) {
   167  	w.mu.Lock()
   168  	defer w.mu.Unlock()
   169  	w.extra = extra
   170  }
   171  
   172  func (w *worker) pending() (*types.Block, *state.StateDB) {
   173  	w.currentMu.Lock()
   174  	defer w.currentMu.Unlock()
   175  
   176  	if atomic.LoadInt32(&w.mining) == 0 {
   177  		return types.NewBlock(
   178  			w.current.header,
   179  			w.current.txs,
   180  			nil,
   181  			w.current.receipts,
   182  		), w.current.state.Copy()
   183  	}
   184  	return w.current.Block, w.current.state.Copy()
   185  }
   186  
   187  func (w *worker) pendingBlock() *types.Block {
   188  	w.currentMu.Lock()
   189  	defer w.currentMu.Unlock()
   190  
   191  	if atomic.LoadInt32(&w.mining) == 0 {
   192  		return types.NewBlock(
   193  			w.current.header,
   194  			w.current.txs,
   195  			nil,
   196  			w.current.receipts,
   197  		)
   198  	}
   199  	return w.current.Block
   200  }
   201  
   202  func (w *worker) start() {
   203  	w.mu.Lock()
   204  	defer w.mu.Unlock()
   205  
   206  	atomic.StoreInt32(&w.mining, 1)
   207  
   208  	// spin up agents
   209  	for agent := range w.agents {
   210  		agent.Start()
   211  	}
   212  }
   213  
   214  func (w *worker) stop() {
   215  	w.wg.Wait()
   216  
   217  	w.mu.Lock()
   218  	defer w.mu.Unlock()
   219  	if atomic.LoadInt32(&w.mining) == 1 {
   220  		for agent := range w.agents {
   221  			agent.Stop()
   222  		}
   223  	}
   224  	atomic.StoreInt32(&w.mining, 0)
   225  	atomic.StoreInt32(&w.atWork, 0)
   226  }
   227  
   228  func (w *worker) register(agent Agent) {
   229  	w.mu.Lock()
   230  	defer w.mu.Unlock()
   231  	w.agents[agent] = struct{}{}
   232  	agent.SetReturnCh(w.recv)
   233  }
   234  
   235  func (w *worker) unregister(agent Agent) {
   236  	w.mu.Lock()
   237  	defer w.mu.Unlock()
   238  	delete(w.agents, agent)
   239  	agent.Stop()
   240  }
   241  
   242  func (w *worker) update() {
   243  	defer w.txSub.Unsubscribe()
   244  	defer w.chainHeadSub.Unsubscribe()
   245  	defer w.chainSideSub.Unsubscribe()
   246  
   247  	for {
   248  		// A real event arrived, process interesting content
   249  		select {
   250  		// Handle ChainHeadEvent
   251  		case <-w.chainHeadCh:
   252  			w.commitNewWork()
   253  
   254  		// Handle ChainSideEvent
   255  		case ev := <-w.chainSideCh:
   256  			w.uncleMu.Lock()
   257  			w.possibleUncles[ev.Block.Hash()] = ev.Block
   258  			w.uncleMu.Unlock()
   259  
   260  		// Handle TxPreEvent
   261  		case ev := <-w.txCh:
   262  			// Apply transaction to the pending state if we're not mining
   263  			if atomic.LoadInt32(&w.mining) == 0 {
   264  				w.currentMu.Lock()
   265  				acc, _ := types.Sender(w.current.signer, ev.Tx)
   266  				txs := map[common.Address]types.Transactions{acc: {ev.Tx}}
   267  				txset := types.NewTransactionsByPriceAndNonce(w.current.signer, txs)
   268  
   269  				w.current.commitTransactions(w.mux, txset, w.chain, w.coinbase)
   270  				w.currentMu.Unlock()
   271  			}
   272  
   273  		// System stopped
   274  		case <-w.txSub.Err():
   275  			return
   276  		case <-w.chainHeadSub.Err():
   277  			return
   278  		case <-w.chainSideSub.Err():
   279  			return
   280  		}
   281  	}
   282  }
   283  
   284  func (w *worker) wait() {
   285  	for {
   286  		mustCommitNewWork := true
   287  		for result := range w.recv {
   288  			atomic.AddInt32(&w.atWork, -1)
   289  
   290  			if result == nil {
   291  				continue
   292  			}
   293  			block := result.Block
   294  			if block == nil {
   295  				log.Error("submitblock: Skipping nil block")
   296  				continue
   297  			}
   298  			work := result.Work
   299  			hash := block.Hash()
   300  			// work is nil when using submitblock rpc
   301  			if work == nil {
   302  				log.Trace("inserting block via submitblock RPC", block.Number())
   303  				_, err := w.chain.InsertChain(types.Blocks{block})
   304  				if err != nil {
   305  					log.Error("Failed writing block to chain", "err", err)
   306  				}
   307  				continue
   308  			}
   309  
   310  			// Update the block hash in all logs since it is now available and not when the
   311  			// receipt/log of individual transactions were created.
   312  			for _, r := range work.receipts {
   313  				for _, l := range r.Logs {
   314  					l.BlockHash = hash
   315  				}
   316  			}
   317  			for _, log := range work.state.Logs() {
   318  				log.BlockHash = hash
   319  			}
   320  			stat, err := w.chain.WriteBlockWithState(block, work.receipts, work.state)
   321  			if err != nil {
   322  				log.Error("Failed writing block to chain", "err", err)
   323  				continue
   324  			}
   325  
   326  			// check if canon block and write transactions
   327  			if stat == core.CanonStatTy {
   328  				// implicit by posting ChainHeadEvent
   329  				mustCommitNewWork = false
   330  			}
   331  			// Broadcast the block and announce chain insertion event
   332  			w.mux.Post(core.NewMinedBlockEvent{Block: block})
   333  			var (
   334  				events []interface{}
   335  				logs   = work.state.Logs()
   336  			)
   337  			events = append(events, core.ChainEvent{Block: block, Hash: hash, Logs: logs})
   338  			if stat == core.CanonStatTy {
   339  				events = append(events, core.ChainHeadEvent{Block: block})
   340  			}
   341  			w.chain.PostChainEvents(events, logs)
   342  
   343  			// Insert the block into the set of pending ones to wait for confirmations
   344  			w.unconfirmed.Insert(block.NumberU64(), hash)
   345  
   346  			if mustCommitNewWork {
   347  				w.commitNewWork()
   348  			}
   349  		}
   350  	}
   351  }
   352  
   353  // push sends a new work task to currently live miner agents.
   354  func (w *worker) push(work *Work) {
   355  	if atomic.LoadInt32(&w.mining) != 1 {
   356  		return
   357  	}
   358  	for agent := range w.agents {
   359  		atomic.AddInt32(&w.atWork, 1)
   360  		if ch := agent.Work(); ch != nil {
   361  			ch <- work
   362  		}
   363  	}
   364  }
   365  
   366  // makeCurrent creates a new environment for the current cycle.
   367  func (w *worker) makeCurrent(parent *types.Block, header *types.Header) error {
   368  	state, err := w.chain.StateAt(parent.Root())
   369  	if err != nil {
   370  		return err
   371  	}
   372  
   373  	work := &Work{
   374  		config:    w.config,
   375  		signer:    types.NewEIP155Signer(w.config.ChainId),
   376  		state:     state,
   377  		ancestors: set.NewSet(),
   378  		family:    set.NewSet(),
   379  		uncles:    set.NewSet(),
   380  		header:    header,
   381  		createdAt: time.Now(),
   382  	}
   383  
   384  	// when 08 is processed ancestors contain 07 (quick block)
   385  	for _, ancestor := range w.chain.GetBlocksFromHash(parent.Hash(), 7) {
   386  		for _, uncle := range ancestor.Uncles() {
   387  			work.family.Add(uncle.SetVersion(byte(w.config.GetBlockVersion(uncle.Number))))
   388  		}
   389  		work.family.Add(ancestor.Hash())
   390  		work.ancestors.Add(ancestor.Hash())
   391  	}
   392  
   393  	// Keep track of transactions which return errors so they can be removed
   394  	work.tcount = 0
   395  	w.current = work
   396  	return nil
   397  }
   398  
   399  func (w *worker) commitNewWork() {
   400  	w.mu.Lock()
   401  	defer w.mu.Unlock()
   402  	w.uncleMu.Lock()
   403  	defer w.uncleMu.Unlock()
   404  	w.currentMu.Lock()
   405  	defer w.currentMu.Unlock()
   406  
   407  	tstart := time.Now()
   408  	parent := w.chain.CurrentBlock()
   409  
   410  	tstamp := tstart.Unix()
   411  	if parent.Time().Cmp(new(big.Int).SetInt64(tstamp)) >= 0 {
   412  		tstamp = parent.Time().Int64() + 1
   413  	}
   414  	// this will ensure we're not going off too far in the future
   415  	if now := time.Now().Unix(); tstamp > now+1 {
   416  		wait := time.Duration(tstamp-now) * time.Second
   417  		log.Info("Mining too far in the future", "wait", common.PrettyDuration(wait))
   418  		time.Sleep(wait)
   419  	}
   420  
   421  	num := parent.Number()
   422  	numnew := num.Add(num, common.Big1)
   423  	header := &types.Header{
   424  		ParentHash: parent.Hash(),
   425  		Number:     numnew,
   426  		GasLimit:   core.CalcGasLimit(parent),
   427  		Extra:      w.extra,
   428  		Time:       big.NewInt(tstamp),
   429  		Version:    w.chain.Config().GetBlockVersion(numnew),
   430  	}
   431  	// Only set the coinbase if we are mining (avoid spurious block rewards)
   432  	if atomic.LoadInt32(&w.mining) == 1 {
   433  		header.Coinbase = w.coinbase
   434  	}
   435  	if err := w.engine.Prepare(w.chain, header); err != nil {
   436  		log.Error("Failed to prepare header for mining", "err", err)
   437  		return
   438  	}
   439  	// // If we are care about TheDAO hard-fork check whether to override the extra-data or not
   440  	// if daoBlock := w.config.DAOForkBlock; daoBlock != nil {
   441  	// 	// Check whether the block is among the fork extra-override range
   442  	// 	limit := new(big.Int).Add(daoBlock, params.DAOForkExtraRange)
   443  	// 	if header.Number.Cmp(daoBlock) >= 0 && header.Number.Cmp(limit) < 0 {
   444  	// 		// Depending whether we support or oppose the fork, override differently
   445  	// 		if w.config.DAOForkSupport {
   446  	// 			header.Extra = common.CopyBytes(params.DAOForkBlockExtra)
   447  	// 		} else if bytes.Equal(header.Extra, params.DAOForkBlockExtra) {
   448  	// 			header.Extra = []byte{} // If miner opposes, don't let it use the reserved extra-data
   449  	// 		}
   450  	// 	}
   451  	// }
   452  	// Could potentially happen if starting to mine in an odd state.
   453  	err := w.makeCurrent(parent, header)
   454  	if err != nil {
   455  		log.Error("Failed to create mining context", "err", err)
   456  		return
   457  	}
   458  	// Create the current work task and check any fork transitions needed
   459  	work := w.current
   460  
   461  	// Mutate the work state according to any hard-fork specs
   462  	if hf4 := work.config.GetHF(4); hf4 != nil && hf4.Cmp(work.header.Number) == 0 {
   463  		misc.ApplyHardFork4(work.state)
   464  	}
   465  	if hf5 := work.config.GetHF(5); hf5 != nil && hf5.Cmp(work.header.Number) == 0 {
   466  		misc.ApplyHardFork5(work.state)
   467  	}
   468  
   469  	// compute uncles for the new block.
   470  	var (
   471  		uncles    []*types.Header
   472  		badUncles []common.Hash
   473  	)
   474  
   475  	for hash, uncle := range w.possibleUncles {
   476  		if len(uncles) == 1 {
   477  			break
   478  		}
   479  		unclehead := uncle.Header()
   480  		unclehead.Version = w.chain.Config().GetBlockVersion(unclehead.Number)
   481  		if err := w.commitUncle(work, unclehead); err != nil {
   482  			log.Trace("Bad uncle found and will be removed", "hash", hash, "reason", err)
   483  			log.Trace(uncle.String())
   484  
   485  			badUncles = append(badUncles, hash)
   486  			continue
   487  		}
   488  		log.Debug("Committing new uncle to block", "hash", hash)
   489  		uncles = append(uncles, uncle.Header())
   490  
   491  	}
   492  	for _, hash := range badUncles {
   493  		delete(w.possibleUncles, hash)
   494  	}
   495  
   496  	// compute tx for new block
   497  	pending, err := w.aqua.TxPool().Pending()
   498  	if err != nil {
   499  		log.Error("Failed to fetch pending transactions", "err", err)
   500  		return
   501  	}
   502  	txs := types.NewTransactionsByPriceAndNonce(w.current.signer, pending)
   503  	work.commitTransactions(w.mux, txs, w.chain, w.coinbase)
   504  
   505  	// Create the new block to seal with the consensus engine
   506  	if work.Block, err = w.engine.Finalize(w.chain, header, work.state, work.txs, uncles, work.receipts); err != nil {
   507  		log.Error("Failed to finalize block for sealing", "err", err)
   508  		return
   509  	}
   510  	// We only care about logging if we're actually mining.
   511  	if atomic.LoadInt32(&w.mining) == 1 {
   512  		log.Info("Commit new mining work", "number", work.Block.Number(),
   513  			"txs", work.tcount, "uncles", len(uncles), "fees", header.GasUsed,
   514  			"bench", common.PrettyDuration(time.Since(tstart)),
   515  			"algo", header.Version, "diff", header.Difficulty)
   516  		w.unconfirmed.Shift(work.Block.NumberU64() - 1)
   517  	}
   518  	w.push(work)
   519  }
   520  
   521  func (w *worker) commitUncle(work *Work, uncle *types.Header) error {
   522  	hash := uncle.Hash()
   523  	if work.uncles.Cardinality() > 0 {
   524  		return fmt.Errorf("too many uncles")
   525  	}
   526  	if work.uncles.Contains(hash) {
   527  		return fmt.Errorf("uncle not unique")
   528  	}
   529  	if !work.ancestors.Contains(uncle.ParentHash) {
   530  		return fmt.Errorf("uncle's parent unknown (%x)", uncle.ParentHash[0:4])
   531  	}
   532  	if work.family.Contains(hash) {
   533  		return fmt.Errorf("uncle already in family (%x)", hash)
   534  	}
   535  	work.uncles.Add(uncle.Hash())
   536  	return nil
   537  }
   538  
   539  func (env *Work) commitTransactions(mux *event.TypeMux, txs *types.TransactionsByPriceAndNonce, bc *core.BlockChain, coinbase common.Address) {
   540  	gp := new(core.GasPool).AddGas(env.header.GasLimit)
   541  
   542  	var coalescedLogs []*types.Log
   543  
   544  	for {
   545  		// If we don't have enough gas for any further transactions then we're done
   546  		if gp.Gas() < params.TxGas {
   547  			log.Trace("Not enough gas for further transactions", "gp", gp)
   548  			break
   549  		}
   550  		// Retrieve the next transaction and abort if all done
   551  		tx := txs.Peek()
   552  		if tx == nil {
   553  			break
   554  		}
   555  		// Error may be ignored here. The error has already been checked
   556  		// during transaction acceptance is the transaction pool.
   557  		//
   558  		// We use the eip155 signer regardless of the current hf.
   559  		from, _ := types.Sender(env.signer, tx)
   560  		// Check whether the tx is replay protected. If we're not in the EIP155 hf
   561  		// phase, start ignoring the sender until we do.
   562  		if tx.Protected() && !env.config.IsEIP155(env.header.Number) {
   563  			log.Trace("Ignoring replay protected transaction", "hash", tx.Hash(), "eip155", env.config.EIP155Block)
   564  			txs.Pop()
   565  			continue
   566  		}
   567  		// Start executing the transaction
   568  		env.state.Prepare(tx.Hash(), common.Hash{}, env.tcount)
   569  
   570  		err, logs := env.commitTransaction(tx, bc, coinbase, gp)
   571  		switch err {
   572  		case core.ErrGasLimitReached:
   573  			// Pop the current out-of-gas transaction without shifting in the next from the account
   574  			log.Trace("Fuel limit exceeded for current block", "sender", from)
   575  			txs.Pop()
   576  
   577  		case core.ErrNonceTooLow:
   578  			// New head notification data race between the transaction pool and miner, shift
   579  			log.Trace("Skipping transaction with low nonce", "sender", from, "nonce", tx.Nonce())
   580  			txs.Shift()
   581  
   582  		case core.ErrNonceTooHigh:
   583  			// Reorg notification data race between the transaction pool and miner, skip account =
   584  			log.Trace("Skipping account, nonce too high", "sender", from, "nonce", tx.Nonce())
   585  			txs.Pop()
   586  
   587  		case nil:
   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  		default:
   594  			// Strange error, discard the transaction and get the next in line (note, the
   595  			// nonce-too-high clause will prevent us from executing in vain).
   596  			log.Debug("Transaction failed, account skipped", "hash", tx.Hash(), "err", err)
   597  			txs.Shift()
   598  		}
   599  	}
   600  
   601  	if len(coalescedLogs) > 0 || env.tcount > 0 {
   602  		// make a copy, the state caches the logs and these logs get "upgraded" from pending to mined
   603  		// logs by filling in the block hash when the block was mined by the local miner. This can
   604  		// cause a race condition if a log was "upgraded" before the PendingLogsEvent is processed.
   605  		cpy := make([]*types.Log, len(coalescedLogs))
   606  		for i, l := range coalescedLogs {
   607  			cpy[i] = new(types.Log)
   608  			*cpy[i] = *l
   609  		}
   610  		go func(logs []*types.Log, tcount int) {
   611  			if len(logs) > 0 {
   612  				mux.Post(core.PendingLogsEvent{Logs: logs})
   613  			}
   614  			if tcount > 0 {
   615  				mux.Post(core.PendingStateEvent{})
   616  			}
   617  		}(cpy, env.tcount)
   618  	}
   619  }
   620  
   621  func (env *Work) commitTransaction(tx *types.Transaction, bc *core.BlockChain, coinbase common.Address, gp *core.GasPool) (error, []*types.Log) {
   622  	snap := env.state.Snapshot()
   623  
   624  	receipt, _, err := core.ApplyTransaction(env.config, bc, &coinbase, gp, env.state, env.header, tx, &env.header.GasUsed, vm.Config{})
   625  	if err != nil {
   626  		env.state.RevertToSnapshot(snap)
   627  		return err, nil
   628  	}
   629  	env.txs = append(env.txs, tx)
   630  	env.receipts = append(env.receipts, receipt)
   631  
   632  	return nil, receipt.Logs
   633  }