Accounts源碼分析與邏輯結構2

Accounts源碼分析與邏輯結構2

listAccounts源碼分析

位於internal/ethapi/api.go中的ListAccounts方法
// ListAccounts will return a list of addresses for accounts this node manages.
func (s *PrivateAccountAPI) ListAccounts() []common.Address {
   addresses := make([]common.Address, 0) // return [] instead of nil if empty
   for _, wallet := range s.am.Wallets() {
      for _, account := range wallet.Accounts() {
         addresses = append(addresses, account.Address)
      }
   }
   return addresses
}
複製代碼

經過accounts/account/下的Wallets錢包管理,遍歷錢包中存儲的帳戶,將addresses數組返回控制檯node

sendTransaction 源碼分析

在瞭解sendTransaction以前咱們先看一下Transaction 的數據模型

在core/types/tracsaction.go文件下的Transaction結構體git

type Transaction struct {
   data txdata
   // caches
   hash atomic.Value
   size atomic.Value
   from atomic.Value
}
複製代碼

hash:交易的hashgithub

size:交易數據的大小json

from:發起交易的地址api

data:交易數據數組

type txdata struct {
   AccountNonce uint64          `json:"nonce" gencodec:"required"`
   Price        *big.Int        `json:"gasPrice" gencodec:"required"`
   GasLimit     uint64          `json:"gas" gencodec:"required"`
   Recipient    *common.Address `json:"to" rlp:"nil"` // nil means contract creation
   Amount       *big.Int        `json:"value" gencodec:"required"`
   Payload      []byte          `json:"input" gencodec:"required"`

   // Signature values
   V *big.Int `json:"v" gencodec:"required"`
   R *big.Int `json:"r" gencodec:"required"`
   S *big.Int `json:"s" gencodec:"required"`

   // This is only used when marshaling to JSON.
   Hash *common.Hash `json:"hash" rlp:"-"`
}
複製代碼

AccountNonce:發起者發起的交易總數量數據結構

Price:這次交易的gas 價格併發

GasLimit:這次交易容許消耗的最大gas數app

Recipient:交易接收者的地址less

Amount:這次交易的以太幣數量

Payload:對應的虛擬機指令

V:簽名數據

R:簽名數據

S:簽名數據

SendTxArgs:交易的一些參數

type SendTxArgs struct {
   From     common.Address  `json:"from"`
   To       *common.Address `json:"to"`
   Gas      *hexutil.Uint64 `json:"gas"`
   GasPrice *hexutil.Big    `json:"gasPrice"`
   Value    *hexutil.Big    `json:"value"`
   Nonce    *hexutil.Uint64 `json:"nonce"`
   // We accept "data" and "input" for backwards-compatibility reasons. "input" is the
   // newer name and should be preferred by clients.
   Data  *hexutil.Bytes `json:"data"`
   Input *hexutil.Bytes `json:"input"`
}
複製代碼
瞭解了交易的數據模型以後,咱們再來具體執行的源碼:

sendTransaction通過RPC調用後,會調用internal/ethapi/api.go中的sendTransaction方法

// SendTransaction爲給定的參數建立一個事務,簽名並提交給事務池
func (s *PublicTransactionPoolAPI) SendTransaction(ctx context.Context, args SendTxArgs) (common.Hash, error) {
	//經過From地址構造一個account
   // Look up the wallet containing the requested signer
   account := accounts.Account{Address: args.From}
	//調用帳戶管理器得到該account的錢包,Find方法會從帳戶管理系統中對錢包進行遍歷,找到包含這個 //account的錢包
   wallet, err := s.b.AccountManager().Find(account)
   if err != nil {
      return common.Hash{}, err
   }

   if args.Nonce == nil {
       //保持地址的互斥鎖在簽名附近,以防止併發分配一樣的方法能夠屢次使用
       //對於每個帳戶,nonce會隨着轉帳的增長而增長,以防止雙花攻擊
      // Hold the addresse's mutex around signing to prevent concurrent assignment of
      // the same nonce to multiple accounts.
      s.nonceLock.LockAddr(args.From)
      defer s.nonceLock.UnlockAddr(args.From)
   }
	//設置交易參數的默認值
   // Set some sanity defaults and terminate on failure
   if err := args.setDefaults(ctx, s.b); err != nil {
      return common.Hash{}, err
   }
    //利用toTransaction方法建立一筆交易
   // Assemble the transaction and sign with the wallet
   tx := args.toTransaction()
	//此時咱們已經建立好了一筆交易,接着咱們獲取區塊鏈的配置信息,檢查是不是EIP155的配置,並獲取鏈ID。
   var chainID *big.Int
   if config := s.b.ChainConfig(); config.IsEIP155(s.b.CurrentBlock().Number()) {
      chainID = config.ChainID
   }
    //對交易進行簽名 參數是帳戶,交易信息 ,鏈ID
   signed, err := wallet.SignTx(account, tx, chainID)
   if err != nil {
      return common.Hash{}, err
   }
   return submitTransaction(ctx, s.b, signed)
}
複製代碼

Find方法會從帳戶管理系統中對錢包進行遍歷,找到包含這個account的錢包

func (am *Manager) Find(account Account) (Wallet, error) {
    //將當前的帳戶管理器進行讀鎖
   am.lock.RLock()
    //調用棧結束解鎖
   defer am.lock.RUnlock()
	//遍歷帳戶管理器的全部錢包
   for _, wallet := range am.wallets {
       //找到這個account對應的錢包
      if wallet.Contains(account) {
          //返回找到的錢包
         return wallet, nil
      }
   }
    //沒有找到返回對應錯誤
   return nil, ErrUnknownAccount
}
複製代碼

得到錢包之後對交易參數中的帳戶nonce進行上鎖,以防止雙花攻擊,而後調用setDefaults方法對交易參數設置一些默認參數值

func (args *SendTxArgs) setDefaults(ctx context.Context, b Backend) error {
    //參數中Gas是否爲空
   if args.Gas == nil {
       //爲空設置默認值
      args.Gas = new(hexutil.Uint64)
      *(*uint64)(args.Gas) = 90000
   }
    //參數中GasPrice是否爲空
   if args.GasPrice == nil {
       //獲取建議的市場價格
      price, err := b.SuggestPrice(ctx)
      if err != nil {
         return err
      }
       //設置價格
      args.GasPrice = (*hexutil.Big)(price)
   }
    //參數中Value是否爲空
   if args.Value == nil {
      args.Value = new(hexutil.Big)
   }
    //參數中Nonce是否爲空 
   if args.Nonce == nil {
       //經過帳戶獲取帳戶的nonce
      nonce, err := b.GetPoolNonce(ctx, args.From)
      if err != nil {
         return err
      }
       //設置nonce
      args.Nonce = (*hexutil.Uint64)(&nonce)
   }
   if args.Data != nil && args.Input != nil && !bytes.Equal(*args.Data, *args.Input) {
      return errors.New(`Both "data" and "input" are set and not equal. Please use "input" to pass transaction call data.`)
   }
    //若是參數中To爲空
   if args.To == nil {
      // Contract creation
      var input []byte
      if args.Data != nil {
         input = *args.Data
      } else if args.Input != nil {
         input = *args.Input
      }
      if len(input) == 0 {
         return errors.New(`contract creation without any data provided`)
      }
   }
   return nil
}
複製代碼

toTransaction方法使用SendTxArgs參數建立一筆交易,將新的交易信息返回

func (args *SendTxArgs) toTransaction() *types.Transaction {
    //一個字節數組
   var input []byte
    //若是參數Data不爲空
   if args.Data != nil {
       //字節數組的值=Data
      input = *args.Data
       //若是參數Input不爲空
   } else if args.Input != nil {
       //字節數組的值=Input
      input = *args.Input
   }
    //這裏會對傳入的交易信息的to參數進行判斷。若是沒有to值,那麼這是一筆合約轉帳;而若是有to值,那麼就 是發起的一筆轉帳。最終,代碼會調用NewTransaction建立一筆交易信息
   if args.To == nil {
      return types.NewContractCreation(uint64(*args.Nonce), (*big.Int)(args.Value), uint64(*args.Gas), (*big.Int)(args.GasPrice), input)
   }
   return types.NewTransaction(uint64(*args.Nonce), *args.To, (*big.Int)(args.Value), uint64(*args.Gas), (*big.Int)(args.GasPrice), input)
}
複製代碼

事實上NewContractCreation也是調用newTransaction,知識to參數爲nil

func NewContractCreation(nonce uint64, amount *big.Int, gasLimit uint64, gasPrice *big.Int, data []byte) *Transaction {
   return newTransaction(nonce, nil, amount, gasLimit, gasPrice, data)
}
複製代碼

經過newTransaction構建一個完整的Transaction交易數據結構,將數據機構返回

func newTransaction(nonce uint64, to *common.Address, amount *big.Int, gasLimit uint64, gasPrice *big.Int, data []byte) *Transaction {
   if len(data) > 0 {
      data = common.CopyBytes(data)
   }
   d := txdata{
      AccountNonce: nonce,
      Recipient:    to,
      Payload:      data,
      Amount:       new(big.Int),
      GasLimit:     gasLimit,
      Price:        new(big.Int),
      V:            new(big.Int),
      R:            new(big.Int),
      S:            new(big.Int),
   }
   if amount != nil {
      d.Amount.Set(amount)
   }
   if gasPrice != nil {
      d.Price.Set(gasPrice)
   }

   return &Transaction{data: d}
}
複製代碼

這裏就是填充了交易結構體中的一些參數,來建立一個交易。到這裏,一筆交易就已經建立成功了。

回到sendTransaction方法中,此時咱們已經建立好了一筆交易,接着咱們獲取區塊鏈的配置信息,檢查是不是EIP155的配置,並獲取鏈ID。

//獲取鏈的一些配置信息
type ChainConfig struct {
   ChainID *big.Int `json:"chainId"` // chainId identifies the current chain and is used for replay protection

   HomesteadBlock *big.Int `json:"homesteadBlock,omitempty"` // Homestead switch block (nil = no fork, 0 = already homestead)

   DAOForkBlock   *big.Int `json:"daoForkBlock,omitempty"`   // TheDAO hard-fork switch block (nil = no fork)
   DAOForkSupport bool     `json:"daoForkSupport,omitempty"` // Whether the nodes supports or opposes the DAO hard-fork

   // EIP150 implements the Gas price changes (https://github.com/ethereum/EIPs/issues/150)
   EIP150Block *big.Int    `json:"eip150Block,omitempty"` // EIP150 HF block (nil = no fork)
   EIP150Hash  common.Hash `json:"eip150Hash,omitempty"`  // EIP150 HF hash (needed for header only clients as only gas pricing changed)

   EIP155Block *big.Int `json:"eip155Block,omitempty"` // EIP155 HF block
   EIP158Block *big.Int `json:"eip158Block,omitempty"` // EIP158 HF block

   ByzantiumBlock      *big.Int `json:"byzantiumBlock,omitempty"`      // Byzantium switch block (nil = no fork, 0 = already on byzantium)
   ConstantinopleBlock *big.Int `json:"constantinopleBlock,omitempty"` // Constantinople switch block (nil = no fork, 0 = already activated)
   EWASMBlock          *big.Int `json:"ewasmBlock,omitempty"`          // EWASM switch block (nil = no fork, 0 = already activated)

   // Various consensus engines
   Ethash *EthashConfig `json:"ethash,omitempty"`
   Clique *CliqueConfig `json:"clique,omitempty"`
}
複製代碼

爲了保證交易的真實有效,咱們須要對交易進行簽名,調用SingTx方法對交易簽名

func (ks *KeyStore) SignTx(account *Account, tx *Transaction, chainID *BigInt) (*Transaction, error) {
   if chainID == nil { // Null passed from mobile app
      chainID = new(BigInt)
   }
   signed, err := ks.keystore.SignTx(account.account, tx.tx, chainID.bigint)
   if err != nil {
      return nil, err
   }
    //返回簽名後的交易信息
   return &Transaction{signed}, nil
}
複製代碼

回到sendTransaction方法中這個時候須要提交交易,調用submitTransaction方法會將交易發送給backend進行處理,返回通過簽名後的交易的hash值。這裏主要是SendTx方法對交易進行處理。

sendTx方法會將參數轉給txpool的Addlocal方法進行處理,而AddLocal方法會將該筆交易放入到交易池中進行等待。這裏咱們看將交易放入到交易池中的方法。

func submitTransaction(ctx context.Context, b Backend, tx *types.Transaction) (common.Hash, error) {
   if err := b.SendTx(ctx, tx); err != nil {
      return common.Hash{}, err
   }
   if tx.To() == nil {
      signer := types.MakeSigner(b.ChainConfig(), b.CurrentBlock().Number())
      from, err := types.Sender(signer, tx)
      if err != nil {
         return common.Hash{}, err
      }
      addr := crypto.CreateAddress(from, tx.Nonce())
      log.Info("Submitted contract creation", "fullhash", tx.Hash().Hex(), "contract", addr.Hex())
   } else {
      log.Info("Submitted transaction", "fullhash", tx.Hash().Hex(), "recipient", tx.To())
   }
   return tx.Hash(), nil
}
複製代碼
func (b *EthAPIBackend) SendTx(ctx context.Context, signedTx *types.Transaction) error {
   return b.eth.txPool.AddLocal(signedTx)
}
複製代碼
func (pool *TxPool) AddLocal(tx *types.Transaction) error {
   return pool.addTx(tx, !pool.config.NoLocals)
}
複製代碼
func (pool *TxPool) addTx(tx *types.Transaction, local bool) error {
   pool.mu.Lock()
   defer pool.mu.Unlock()

   // Try to inject the transaction and update any state
   replace, err := pool.add(tx, local)
   if err != nil {
      return err
   }
   // If we added a new transaction, run promotion checks and return
   if !replace {
      from, _ := types.Sender(pool.signer, tx) // already validated
      pool.promoteExecutables([]common.Address{from})
   }
   return nil
}
複製代碼

這裏一共有兩部操做,第一步操做是調用add方法將交易放入到交易池中,第二步是判斷replace參數。若是該筆交易合法而且交易原來不存在在交易池中,則執行promoteExecutables方法,將可處理的交易變爲待處理(pending)。

首先看第一步add方法。

func (pool *TxPool) add(tx *types.Transaction, local bool) (bool, error) {
   // If the transaction is already known, discard it
   hash := tx.Hash()
    //判斷這個交易hash有麼有在交易池中,若是交易池中有這筆交易則返回報錯
   if pool.all.Get(hash) != nil {
      log.Trace("Discarding already known transaction", "hash", hash)
      return false, fmt.Errorf("known transaction: %x", hash)
   }
    //調用validateTx判斷交易是否合法,若是不合法則返回報錯
   // If the transaction fails basic validation, discard it
   if err := pool.validateTx(tx, local); err != nil {
      log.Trace("Discarding invalid transaction", "hash", hash, "err", err)
      invalidTxCounter.Inc(1)
      return false, err
   }
    //判斷交易池是否超過容量
   // If the transaction pool is full, discard underpriced transactions
   if uint64(pool.all.Count()) >= pool.config.GlobalSlots+pool.config.GlobalQueue {
      // If the new transaction is underpriced, don't accept it
       //若是超過容量,而且該筆交易的費用低於當前交易池中列表的最小值,則拒絕這一筆交易
      if !local && pool.priced.Underpriced(tx, pool.locals) {
         log.Trace("Discarding underpriced transaction", "hash", hash, "price", tx.GasPrice())
         underpricedTxCounter.Inc(1)
         return false, ErrUnderpriced
      }
       //若是超過容量,而且該筆交易的費用比當前交易池中列表最小值高,那麼從交易池中移除交易費用最低的交易,爲當前這一筆交易留出空間。
      // New transaction is better than our worse ones, make room for it
      drop := pool.priced.Discard(pool.all.Count()-int(pool.config.GlobalSlots+pool.config.GlobalQueue-1), pool.locals)
      for _, tx := range drop {
         log.Trace("Discarding freshly underpriced transaction", "hash", tx.Hash(), "price", tx.GasPrice())
         underpricedTxCounter.Inc(1)
         pool.removeTx(tx.Hash(), false)
      }
   }
    //若是事務正在替換一個已經掛起的事務,請直接執行
   // If the transaction is replacing an already pending one, do directly
   from, _ := types.Sender(pool.signer, tx) // already validated
    //接着繼續調用Overlaps方法檢查該筆交易的Nonce值,確認該用戶下的交易是否存在該筆交易
   if list := pool.pending[from]; list != nil && list.Overlaps(tx) {
      // Nonce already pending, check if required price bump is met
      inserted, old := list.Add(tx, pool.config.PriceBump)
     // 若是已經存在這筆交易,則刪除以前的交易,並將該筆交易放入交易池中,而後返回。
      if !inserted {
         pendingDiscardCounter.Inc(1)
         return false, ErrReplaceUnderpriced
      }
      // New transaction is better, replace old one
      
      if old != nil {
         pool.all.Remove(old.Hash())
         pool.priced.Removed()
         pendingReplaceCounter.Inc(1)
      }
       //在池裏添加新的交易信息
      pool.all.Add(tx)
       //添加新交易的價格
      pool.priced.Put(tx)
       //將交易信息寫到日誌中,只有是在本地帳戶的狀況下
      pool.journalTx(from, tx)

      log.Trace("Pooled new executable transaction", "hash", hash, "from", from, "to", tx.To())

      // We've directly injected a replacement transaction, notify subsystems
      go pool.txFeed.Send(NewTxsEvent{types.Transactions{tx}})

      return old != nil, nil
   }
    //若是不存在,則調用enqueueTx將該筆交易放入交易池中。若是交易是本地發出的,則將發送者保存在交易池的local中
   // New transaction isn't replacing a pending one, push into queue
   replace, err := pool.enqueueTx(hash, tx)
   if err != nil {
      return false, err
   }
    //標記本地地址並記錄本地事務
   // Mark local addresses and journal local transactions
   if local {
      if !pool.locals.contains(from) {
         log.Info("Setting new local account", "address", from)
         pool.locals.add(from)
      }
   }
    //記錄本地帳戶的事務交易信息
   pool.journalTx(from, tx)

   log.Trace("Pooled new future transaction", "hash", hash, "from", from, "to", tx.To())
   return replace, nil
}
複製代碼

總結:add方法執行流程

1.判斷這個交易hash有麼有在交易池中,若是交易池中有這筆交易則返回報錯

2.調用validateTx判斷交易是否合法,若是不合法則返回報錯

3.判斷交易池是否超過容量

4.若是超過容量,而且該筆交易的費用低於當前交易池中列表的最小值,則拒絕這一筆交易

5.若是超過容量,而且該筆交易的費用比當前交易池中列表最小值高,那麼從交易池中移除交易費用最低的交易,爲當前這一筆交易留出空間。

6.接着繼續調用Overlaps方法檢查該筆交易的Nonce值,確認該用戶下的交易是否存在該筆交易

7.若是已經存在這筆交易,則刪除以前的交易,並將該筆交易放入交易池中,而後返回。

8.若是不存在,則調用enqueueTx將該筆交易放入交易池中。若是交易是本地發出的,則將發送者保存在交易池的local中

9.返回執行結果 true /false和錯誤信息

validateTx方法執行的邏輯

func (pool *TxPool) validateTx(tx *types.Transaction, local bool) error {
   // Heuristic limit, reject transactions over 32KB to prevent DOS attacks
   if tx.Size() > 32*1024 {
      return ErrOversizedData
   }
   // Transactions can't be negative. This may never happen using RLP decoded
   // transactions but may occur if you create a transaction using the RPC.
   if tx.Value().Sign() < 0 {
      return ErrNegativeValue
   }
   // Ensure the transaction doesn't exceed the current block limit gas.
   if pool.currentMaxGas < tx.Gas() {
      return ErrGasLimit
   }
   // Make sure the transaction is signed properly
   from, err := types.Sender(pool.signer, tx)
   if err != nil {
      return ErrInvalidSender
   }
   // Drop non-local transactions under our own minimal accepted gas price
   local = local || pool.locals.contains(from) // account may be local even if the transaction arrived from the network
   if !local && pool.gasPrice.Cmp(tx.GasPrice()) > 0 {
      return ErrUnderpriced
   }
   // Ensure the transaction adheres to nonce ordering
   if pool.currentState.GetNonce(from) > tx.Nonce() {
      return ErrNonceTooLow
   }
   // Transactor should have enough funds to cover the costs
   // cost == V + GP * GL
   if pool.currentState.GetBalance(from).Cmp(tx.Cost()) < 0 {
      return ErrInsufficientFunds
   }
   intrGas, err := IntrinsicGas(tx.Data(), tx.To() == nil, pool.homestead)
   if err != nil {
      return err
   }
   if tx.Gas() < intrGas {
      return ErrIntrinsicGas
   }
   return nil
}
複製代碼

validateTx會驗證一筆交易的如下幾個特性: ​ 1.首先驗證這筆交易的大小,若是大於32kb則拒絕這筆交易,這樣主要是爲了防止DDOS攻擊。 ​ 2.接着驗證轉帳金額。若是金額小於0則拒絕這筆交易。 ​ 3.這筆交易的gas不能超過交易池的gas上限。 ​ 4.驗證這筆交易的簽名是否合法。 ​ 5.若是這筆交易不是來自本地而且這筆交易的gas小於當前交易池中的gas,則拒絕這筆交易。 ​ 6.當前用戶的nonce若是大於這筆交易的nonce,則拒絕這筆交易。 ​ 7.當前用戶的餘額是否充足,若是不充足則拒絕該筆交易。 ​ 8.驗證這筆交易的固有花費,若是小於交易池的gas,則拒絕該筆交易。 以上就是在進行交易驗證時所需驗證的參數。這一系列的驗證操做結束後,回到addTx的第二步。 會判斷replace。若是replace是false,則會執行promoteExecutables方法。

func (pool *TxPool) promoteExecutables(accounts []common.Address) {
   // Track the promoted transactions to broadcast them at once
   var promoted []*types.Transaction

   // Gather all the accounts potentially needing updates
   if accounts == nil {
      accounts = make([]common.Address, 0, len(pool.queue))
      for addr := range pool.queue {
         accounts = append(accounts, addr)
      }
   }
   // Iterate over all accounts and promote any executable transactions
   for _, addr := range accounts {
      list := pool.queue[addr]
      if list == nil {
         continue // Just in case someone calls with a non existing account
      }
      // Drop all transactions that are deemed too old (low nonce)
      for _, tx := range list.Forward(pool.currentState.GetNonce(addr)) {
         hash := tx.Hash()
         log.Trace("Removed old queued transaction", "hash", hash)
         pool.all.Remove(hash)
         pool.priced.Removed()
      }
      // Drop all transactions that are too costly (low balance or out of gas)
      drops, _ := list.Filter(pool.currentState.GetBalance(addr), pool.currentMaxGas)
      for _, tx := range drops {
         hash := tx.Hash()
         log.Trace("Removed unpayable queued transaction", "hash", hash)
         pool.all.Remove(hash)
         pool.priced.Removed()
         queuedNofundsCounter.Inc(1)
      }
      // Gather all executable transactions and promote them
      for _, tx := range list.Ready(pool.pendingState.GetNonce(addr)) {
         hash := tx.Hash()
         if pool.promoteTx(addr, hash, tx) {
            log.Trace("Promoting queued transaction", "hash", hash)
            promoted = append(promoted, tx)
         }
      }
      // Drop all transactions over the allowed limit
      if !pool.locals.contains(addr) {
         for _, tx := range list.Cap(int(pool.config.AccountQueue)) {
            hash := tx.Hash()
            pool.all.Remove(hash)
            pool.priced.Removed()
            queuedRateLimitCounter.Inc(1)
            log.Trace("Removed cap-exceeding queued transaction", "hash", hash)
         }
      }
      // Delete the entire queue entry if it became empty.
      if list.Empty() {
         delete(pool.queue, addr)
      }
   }
   // Notify subsystem for new promoted transactions.
   if len(promoted) > 0 {
      go pool.txFeed.Send(NewTxsEvent{promoted})
   }
   // If the pending limit is overflown, start equalizing allowances
   pending := uint64(0)
   for _, list := range pool.pending {
      pending += uint64(list.Len())
   }
   if pending > pool.config.GlobalSlots {
      pendingBeforeCap := pending
      // Assemble a spam order to penalize large transactors first
      spammers := prque.New(nil)
      for addr, list := range pool.pending {
         // Only evict transactions from high rollers
         if !pool.locals.contains(addr) && uint64(list.Len()) > pool.config.AccountSlots {
            spammers.Push(addr, int64(list.Len()))
         }
      }
      // Gradually drop transactions from offenders
      offenders := []common.Address{}
      for pending > pool.config.GlobalSlots && !spammers.Empty() {
         // Retrieve the next offender if not local address
         offender, _ := spammers.Pop()
         offenders = append(offenders, offender.(common.Address))

         // Equalize balances until all the same or below threshold
         if len(offenders) > 1 {
            // Calculate the equalization threshold for all current offenders
            threshold := pool.pending[offender.(common.Address)].Len()

            // Iteratively reduce all offenders until below limit or threshold reached
            for pending > pool.config.GlobalSlots && pool.pending[offenders[len(offenders)-2]].Len() > threshold {
               for i := 0; i < len(offenders)-1; i++ {
                  list := pool.pending[offenders[i]]
                  for _, tx := range list.Cap(list.Len() - 1) {
                     // Drop the transaction from the global pools too
                     hash := tx.Hash()
                     pool.all.Remove(hash)
                     pool.priced.Removed()

                     // Update the account nonce to the dropped transaction
                     if nonce := tx.Nonce(); pool.pendingState.GetNonce(offenders[i]) > nonce {
                        pool.pendingState.SetNonce(offenders[i], nonce)
                     }
                     log.Trace("Removed fairness-exceeding pending transaction", "hash", hash)
                  }
                  pending--
               }
            }
         }
      }
      // If still above threshold, reduce to limit or min allowance
      if pending > pool.config.GlobalSlots && len(offenders) > 0 {
         for pending > pool.config.GlobalSlots && uint64(pool.pending[offenders[len(offenders)-1]].Len()) > pool.config.AccountSlots {
            for _, addr := range offenders {
               list := pool.pending[addr]
               for _, tx := range list.Cap(list.Len() - 1) {
                  // Drop the transaction from the global pools too
                  hash := tx.Hash()
                  pool.all.Remove(hash)
                  pool.priced.Removed()

                  // Update the account nonce to the dropped transaction
                  if nonce := tx.Nonce(); pool.pendingState.GetNonce(addr) > nonce {
                     pool.pendingState.SetNonce(addr, nonce)
                  }
                  log.Trace("Removed fairness-exceeding pending transaction", "hash", hash)
               }
               pending--
            }
         }
      }
      pendingRateLimitCounter.Inc(int64(pendingBeforeCap - pending))
   }
   // If we've queued more transactions than the hard limit, drop oldest ones
   queued := uint64(0)
   for _, list := range pool.queue {
      queued += uint64(list.Len())
   }
   if queued > pool.config.GlobalQueue {
      // Sort all accounts with queued transactions by heartbeat
      addresses := make(addressesByHeartbeat, 0, len(pool.queue))
      for addr := range pool.queue {
         if !pool.locals.contains(addr) { // don't drop locals
            addresses = append(addresses, addressByHeartbeat{addr, pool.beats[addr]})
         }
      }
      sort.Sort(addresses)

      // Drop transactions until the total is below the limit or only locals remain
      for drop := queued - pool.config.GlobalQueue; drop > 0 && len(addresses) > 0; {
         addr := addresses[len(addresses)-1]
         list := pool.queue[addr.address]

         addresses = addresses[:len(addresses)-1]

         // Drop all transactions if they are less than the overflow
         if size := uint64(list.Len()); size <= drop {
            for _, tx := range list.Flatten() {
               pool.removeTx(tx.Hash(), true)
            }
            drop -= size
            queuedRateLimitCounter.Inc(int64(size))
            continue
         }
         // Otherwise drop only last few transactions
         txs := list.Flatten()
         for i := len(txs) - 1; i >= 0 && drop > 0; i-- {
            pool.removeTx(txs[i].Hash(), true)
            drop--
            queuedRateLimitCounter.Inc(1)
         }
      }
   }
}
複製代碼

在promoteExecutable中有一個promoteTx方法,這個方法是將交易防區pending區方法中。在promoteTx方法中,最後一步執行的是一個Send方法。 這個Send方法會同步將pending區的交易廣播至它所鏈接到的節點,並返回通知到的節點的數量。 而後被通知到的節點繼續通知到它添加的節點,繼而廣播至全網。

至此,發送交易就結束了。此時交易池中的交易等待挖礦打包處理

相關文章
相關標籤/搜索