go-ethereum发起与执行交易的流程

原文】在geth控制台使用如下命令来发起转账交易:

 
  1. personal.unlockAccount(eth.accounts[0])

  2. eth.sendTransaction({from:eth.accounts[0],to:eth.accounts[1],value:web3.toWei(200,"ether")})

将执行到txpool.go的validateTx()函数进行交易验证,验证项目包括交易大小、交易Gas、账户余额检查等。

 
  1. func (pool *TxPool) validateTx(tx *types.Transaction, local bool) error {

  2. // Heuristic limit, reject transactions over 32KB to prevent DOS attacks

  3. if tx.Size() > 320*1024 {//lzj change from 32 to 320

  4. return ErrOversizedData

  5. }

  6. // Transactions can't be negative. This may never happen using RLP decoded

  7. // transactions but may occur if you create a transaction using the RPC.

  8. if tx.Value().Sign() < 0 {

  9. return ErrNegativeValue

  10. }

  11. // Ensure the transaction doesn't exceed the current block limit gas.

  12. if pool.currentMaxGas < tx.Gas() {

  13. return ErrGasLimit

  14. }

  15. // Make sure the transaction is signed properly

  16. from, err := types.Sender(pool.signer, tx)

  17. if err != nil {

  18. return ErrInvalidSender

  19. }

  20. // Drop non-local transactions under our own minimal accepted gas price

  21. local = local || pool.locals.contains(from) // account may be local even if the transaction arrived from the network

  22. if !local && pool.gasPrice.Cmp(tx.GasPrice()) > 0 {

  23. return ErrUnderpriced

  24. }

  25. // Ensure the transaction adheres to nonce ordering

  26. if pool.currentState.GetNonce(from) > tx.Nonce() {

  27. return ErrNonceTooLow

  28. }

  29. // Transactor should have enough funds to cover the costs

  30. // cost == V + GP * GL

  31. if pool.currentState.GetBalance(from).Cmp(tx.Cost()) < 0 {

  32. return ErrInsufficientFunds

  33. }

  34. intrGas, err := IntrinsicGas(tx.Data(), tx.To() == nil, pool.homestead)

  35. if err != nil {

  36. return err

  37. }

  38.  
  39. if tx.Gas() < intrGas {

  40. return ErrIntrinsicGas

  41. }

  42. return nil

  43. }

然后在tx_pool.go中的promoteExecutables()函数中进行进一步检查:

 
  1. func (pool *TxPool) promoteExecutables(accounts []common.Address) {

  2. // Gather all the accounts potentially needing updates

  3. if accounts == nil {

  4. accounts = make([]common.Address, 0, len(pool.queue))

  5. for addr := range pool.queue {

  6. accounts = append(accounts, addr)

  7. }

  8. }

  9.  
  10. // Iterate over all accounts and promote any executable transactions

  11. for _, addr := range accounts {

  12. list := pool.queue[addr]

  13. if list == nil {

  14. continue // Just in case someone calls with a non existing account

  15. }

  16. // Drop all transactions that are deemed too old (low nonce)

  17. for _, tx := range list.Forward(pool.currentState.GetNonce(addr)) {

  18. hash := tx.Hash()

  19. log.Trace("Removed old queued transaction", "hash", hash)

  20. delete(pool.all, hash)

  21. pool.priced.Removed()

  22. }

  23. // Drop all transactions that are too costly (low balance or out of gas)

  24. drops, _ := list.Filter(pool.currentState.GetBalance(addr), pool.currentMaxGas)

  25. for _, tx := range drops {

  26. hash := tx.Hash()

  27. log.Trace("Removed unpayable queued transaction", "hash", hash)

  28. delete(pool.all, hash)

  29. pool.priced.Removed()

  30. queuedNofundsCounter.Inc(1)

  31. }

  32.  
  33. // Gather all executable transactions and promote them

  34. for _, tx := range list.Ready(pool.pendingState.GetNonce(addr)) {

  35. hash := tx.Hash()

  36. log.Trace("Promoting queued transaction", "hash", hash)

  37. pool.promoteTx(addr, hash, tx)

  38. }

  39. // Drop all transactions over the allowed limit

  40. if !pool.locals.contains(addr) {

  41. for _, tx := range list.Cap(int(pool.config.AccountQueue)) {

  42. hash := tx.Hash()

  43. delete(pool.all, hash)

  44. pool.priced.Removed()

  45. queuedRateLimitCounter.Inc(1)

  46. log.Trace("Removed cap-exceeding queued transaction", "hash", hash)

  47. }

  48. }.....

其中重要的是pool.promoteTx(addr,hash,tx),从这里进入promoteTx函数:

 
  1. func (pool *TxPool) promoteTx(addr common.Address, hash common.Hash, tx *types.Transaction) {

  2. // Try to insert the transaction into the pending queue

  3. if pool.pending[addr] == nil {

  4. pool.pending[addr] = newTxList(true)

  5. }

  6. list := pool.pending[addr]

  7.  
  8. inserted, old := list.Add(tx, pool.config.PriceBump)

  9. if !inserted {

  10. // An older transaction was better, discard this

  11. delete(pool.all, hash)

  12. pool.priced.Removed()

  13.  
  14. pendingDiscardCounter.Inc(1)

  15. return

  16. }

  17. // Otherwise discard any previous transaction and mark this

  18. if old != nil {

  19. delete(pool.all, old.Hash())

  20. pool.priced.Removed()

  21.  
  22. pendingReplaceCounter.Inc(1)

  23. }

  24. // Failsafe to work around direct pending inserts (tests)

  25. if pool.all[hash] == nil {

  26. pool.all[hash] = tx

  27. pool.priced.Put(tx)

  28. }

  29. // Set the potentially new pending nonce and notify any subsystems of the new tx

  30. pool.beats[addr] = time.Now()

  31. pool.pendingState.SetNonce(addr, tx.Nonce()+1)

  32.  
  33. go pool.txFeed.Send(TxPreEvent{tx})

  34. }

promoteTx函数最后用

go pool.txFeed.Send(TxPreEvent{tx})

将交易发布发布到TxPreEvent类型的监听通道上去。接受该交易的通道定义在miner/worker.go中:

 
  1. func newWorker(config *params.ChainConfig, engine consensus.Engine, coinbase common.Address, eth Backend, mux *event.TypeMux) *worker {

  2. worker := &worker{

  3. config: config,

  4. engine: engine,

  5. eth: eth,

  6. mux: mux,

  7. txCh: make(chan core.TxPreEvent, txChanSize),

  8. chainHeadCh: make(chan core.ChainHeadEvent, chainHeadChanSize),

  9. chainSideCh: make(chan core.ChainSideEvent, chainSideChanSize),

  10. chainDb: eth.ChainDb(),

  11. recv: make(chan *Result, resultQueueSize),

  12. chain: eth.BlockChain(),

  13. proc: eth.BlockChain().Validator(),

  14. possibleUncles: make(map[common.Hash]*types.Block),

  15. coinbase: coinbase,

  16. agents: make(map[Agent]struct{}),

  17. unconfirmed: newUnconfirmedBlocks(eth.BlockChain(), miningLogAtDepth),

  18. }

  19. // Subscribe TxPreEvent for tx pool

  20. worker.txSub = eth.TxPool().SubscribeTxPreEvent(worker.txCh)

  21. // Subscribe events for blockchain

  22. worker.chainHeadSub = eth.BlockChain().SubscribeChainHeadEvent(worker.chainHeadCh)

  23. worker.chainSideSub = eth.BlockChain().SubscribeChainSideEvent(worker.chainSideCh)

  24. go worker.update()

  25.  
  26. go worker.wait()

  27. worker.commitNewWork()

  28.  
  29. return worker

  30. }

接受交易的通道是worker.txCh,通过

 
  1. // Subscribe TxPreEvent for tx pool

  2. worker.txSub = eth.TxPool().SubscribeTxPreEvent(worker.txCh)

来注册对TxPreEvent事件的监听。go-ethereum中的事件监听机制见这篇文章。txCh通道的监听执行函数在worker.go中的update()函数:

 
  1. func (self *worker) update() {

  2. defer self.txSub.Unsubscribe()

  3. defer self.chainHeadSub.Unsubscribe()

  4. defer self.chainSideSub.Unsubscribe()

  5.  
  6. for {

  7. // A real event arrived, process interesting content

  8. select {

  9. // Handle ChainHeadEvent

  10. case <-self.chainHeadCh:

  11. self.commitNewWork()

  12.  
  13. // Handle ChainSideEvent

  14. case ev := <-self.chainSideCh:

  15. self.uncleMu.Lock()

  16. self.possibleUncles[ev.Block.Hash()] = ev.Block

  17. self.uncleMu.Unlock()

  18.  
  19. // Handle TxPreEvent

  20. case ev := <-self.txCh:

  21. log.Info("worker self.txch now")

  22. // Apply transaction to the pending state if we're not mining

  23. if atomic.LoadInt32(&self.mining) == 0 {

  24. self.currentMu.Lock()

  25. acc, _ := types.Sender(self.current.signer, ev.Tx)

  26. txs := map[common.Address]types.Transactions{acc: {ev.Tx}}

  27. txset := types.NewTransactionsByPriceAndNonce(self.current.signer, txs)

  28.  
  29. self.current.commitTransactions(self.mux, txset, self.chain, self.coinbase)

  30. self.updateSnapshot()

  31. self.currentMu.Unlock()

  32. } else {

  33. // If we're mining, but nothing is being processed, wake on new transactions

  34. if self.config.Clique != nil && self.config.Clique.Period == 0 {

  35. self.commitNewWork()

  36. }

  37. }

  38.  
  39. // System stopped

  40. case <-self.txSub.Err():

  41. return

  42. case <-self.chainHeadSub.Err():

  43. return

  44. case <-self.chainSideSub.Err():

  45. return

  46. }

  47. }

  48. }

在通过commitTransactions()函数将交易提交给区块链执行。commitTransactions()函数中遍历每一笔交易,然后通过

commitTransaction()函数提交并执行交易:

 
  1. func (env *Work) commitTransaction(tx *types.Transaction, bc *core.BlockChain, coinbase common.Address, gp *core.GasPool) (error, []*types.Log) {

  2. snap := env.state.Snapshot()

  3.  
  4. receipt, _, err := core.ApplyTransaction(env.config, bc, &coinbase, gp, env.state, env.header, tx, &env.header.GasUsed, vm.Config{})

  5. if err != nil {

  6. log.Info("worker commitTransaction err","err",err)

  7. env.state.RevertToSnapshot(snap)

  8. return err, nil

  9. }

  10. env.txs = append(env.txs, tx)

  11. env.receipts = append(env.receipts, receipt)

  12.  
  13. return nil, receipt.Logs

  14. }

commitTransaction()函数先创建区块链状态的快照,然后通过core.ApplyTransaction()函数来执行交易,如果执行出错,就又回退交易到快照状态。ApplyTransaction()的核心函数是ApplyMessage()。基本调用流程是:

state_processor.go的ApplyTransaction()-->state_transition.go中的ApplyMessage()-->TransitionDb()-->vm/evm.go中的Call()函数。

猜你喜欢

转载自blog.csdn.net/dwjpeng2/article/details/81713316