// 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
在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區的交易廣播至它所鏈接到的節點,並返回通知到的節點的數量。 而後被通知到的節點繼續通知到它添加的節點,繼而廣播至全網。
至此,發送交易就結束了。此時交易池中的交易等待挖礦打包處理