以太坊 交易模块Transaction
Transaction数据结构
Transaction位于 core/types/transaction.go 文件。
// Transaction is an Ethereum transaction. type Transaction struct { inner TxData // Consensus contents of a transaction time time.Time // Time first seen locally (spam avoidance) // caches hash atomic.Value size atomic.Value from atomic.Value }
其中TxData字段是接口,目前有三个实现DynamicFeeTx, LegacyTx and AccessListTx。分别位于 core/types/dynamic_fee_tx.go,core/types/legacy_tx.go 和 core/types/access_list_tx.go。
// Transaction types. const ( // iota:0,以下递增 LegacyTxType = iota AccessListTxType DynamicFeeTxType )
type TxData interface { // 交易类型id txType() byte // returns the type ID // 复制交易 copy() TxData // creates a deep copy and initializes all fields // 链id chainID() *big.Int // 交易可达列表 accessList() AccessList // 交易的input data数据 data() []byte // 最大gas gas() uint64 // gas 价格 gasPrice() *big.Int gasTipCap() *big.Int gasFeeCap() *big.Int // 交易的ether数量 value() *big.Int // 发送者的交易次数 nonce() uint64 // 交易to地址 to() *common.Address // 返回交易签名的v,r,s rawSignatureValues() (v, r, s *big.Int) //设置交易的v,r,s(单纯赋值) setSignatureValues(chainID, v, r, s *big.Int) }
创建交易
方法位于 internal/ethapi/api.go,该文件是 api 文件,常见的方法有:
PublicTransactionPoolAPI: SignTransaction(ctx context.Context, args TransactionArgs) PendingTransactions() ([]*RPCTransaction, error) PrivateAccountAPI: ListAccounts() NewAccount(password string) UnlockAccount(ctx context.Context, addr common.Address, password string, duration *uint64) LockAccount(addr common.Address) SendTransaction(ctx context.Context, args TransactionArgs, passwd string) )
// SendTransaction will create a transaction from the given arguments and // tries to sign it with the key associated with args.From. If the given // passwd isn't able to decrypt the key it fails. func (s *PrivateAccountAPI) SendTransaction(ctx context.Context, args TransactionArgs, passwd string) (common.Hash, error) { // 交易发送者nonce为空时,锁定交易发起者地址 // 后面也没有nonce赋值操作,这步为什么不直接返回? // 在交易签名方法中会判断nonce值,nonce, err := b.GetPoolNonce(ctx, args.from()) // nonce为空先锁定发送者地址,利用go的defer关键字,方法结束后解锁 if args.Nonce == nil { // 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()) } // 签名交易 signed, err := s.signTransaction(ctx, &args, passwd) if err != nil { log.Warn("Failed transaction send attempt", "from", args.from(), "to", args.To, "value", args.Value.ToInt(), "err", err) return common.Hash{}, err } // 提交交易 return SubmitTransaction(ctx, s.b, signed) }
进入签名交易方法:signTransaction。
// signTransaction sets defaults and signs the given transaction // NOTE: the caller needs to ensure that the nonceLock is held, if applicable, // and release it after the transaction has been submitted to the tx pool func (s *PrivateAccountAPI) signTransaction(ctx context.Context, args *TransactionArgs, passwd string) (*types.Transaction, error) { // Look up the wallet containing the requested signer // 拿到发送者的账号 account := accounts.Account{Address: args.from()} // 查询钱包 wallet, err := s.am.Find(account) if err != nil { return nil, err } // Set some sanity defaults and terminate on failure // 设置一下数据(nonce,value,gas,chainID等),判断交易是否满足条件 if err := args.setDefaults(ctx, s.b); err != nil { return nil, err } // Assemble the transaction and sign with the wallet // 组装交易对象 根据args对象属性生成相对应的交易类型(上面提出的三种交易类型) // args.MaxFeePerGas != nil ==> DynamicFeeTx // args.AccessList != nil ==> AccessListTx // default ==> LegacyTx tx := args.toTransaction() // 签名 return wallet.SignTxWithPassphrase(account, passwd, tx, s.b.ChainConfig().ChainID) }
进入签名方法:SignTxWithPassphrase。
接下来是提交交易:SubmitTransaction。
// SubmitTransaction is a helper function that submits tx to txPool and logs a message. func SubmitTransaction(ctx context.Context, b Backend, tx *types.Transaction) (common.Hash, error) { // If the transaction fee cap is already specified, ensure the // fee of the given transaction is _reasonable_. // 校验交易费是否超过最大值 // b.RPCTxFeeCap() ==> 交易费上限 if err := checkTxFee(tx.GasPrice(), tx.Gas(), b.RPCTxFeeCap()); err != nil { return common.Hash{}, err } if !b.UnprotectedAllowed() && !tx.Protected() { // Ensure only eip155 signed transactions are submitted if EIP155Required is set. return common.Hash{}, errors.New("only replay-protected (EIP-155) transactions allowed over RPC") } // 保存交易到交易池 if err := b.SendTx(ctx, tx); err != nil { return common.Hash{}, err } // Print a log with full tx details for manual investigations and interventions // 打印日志信息 signer := types.MakeSigner(b.ChainConfig(), b.CurrentBlock().Number()) from, err := types.Sender(signer, tx) if err != nil { return common.Hash{}, err } if tx.To() == nil { // 创建合约交易 addr := crypto.CreateAddress(from, tx.Nonce()) log.Info("Submitted contract creation", "hash", tx.Hash().Hex(), "from", from, "nonce", tx.Nonce(), "contract", addr.Hex(), "value", tx.Value()) } else { // 普通交易 log.Info("Submitted transaction", "hash", tx.Hash().Hex(), "from", from, "nonce", tx.Nonce(), "recipient", tx.To(), "value", tx.Value()) } return tx.Hash(), nil }
进入 SendTx 方法,位于 internal/ethapi/backend.go。
// Backend interface provides the common API services (that are provided by // both full and light clients) with access to necessary functions. // 为全节点和轻节点提供通用服务 type Backend interface { // General Ethereum API SyncProgress() ethereum.SyncProgress SuggestGasTipCap(ctx context.Context) (*big.Int, error) FeeHistory(ctx context.Context, blockCount int, lastBlock rpc.BlockNumber, rewardPercentiles []float64) (*big.Int, [][]*big.Int, []*big.Int, []float64, error) ChainDb() ethdb.Database AccountManager() *accounts.Manager ExtRPCEnabled() bool RPCGasCap() uint64 // global gas cap for eth_call over rpc: DoS protection RPCTxFeeCap() float64 // global tx fee cap for all transaction related APIs UnprotectedAllowed() bool // allows only for EIP155 transactions. // Blockchain API SetHead(number uint64) HeaderByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Header, error) HeaderByHash(ctx context.Context, hash common.Hash) (*types.Header, error) HeaderByNumberOrHash(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) (*types.Header, error) CurrentHeader() *types.Header CurrentBlock() *types.Block BlockByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Block, error) BlockByHash(ctx context.Context, hash common.Hash) (*types.Block, error) BlockByNumberOrHash(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) (*types.Block, error) StateAndHeaderByNumber(ctx context.Context, number rpc.BlockNumber) (*state.StateDB, *types.Header, error) StateAndHeaderByNumberOrHash(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) (*state.StateDB, *types.Header, error) GetReceipts(ctx context.Context, hash common.Hash) (types.Receipts, error) GetTd(ctx context.Context, hash common.Hash) *big.Int GetEVM(ctx context.Context, msg core.Message, state *state.StateDB, header *types.Header, vmConfig *vm.Config) (*vm.EVM, func() error, error) SubscribeChainEvent(ch chan<- core.ChainEvent) event.Subscription SubscribeChainHeadEvent(ch chan<- core.ChainHeadEvent) event.Subscription SubscribeChainSideEvent(ch chan<- core.ChainSideEvent) event.Subscription // Transaction pool API // 交易池服务 SendTx(ctx context.Context, signedTx *types.Transaction) error GetTransaction(ctx context.Context, txHash common.Hash) (*types.Transaction, common.Hash, uint64, uint64, error) GetPoolTransactions() (types.Transactions, error) GetPoolTransaction(txHash common.Hash) *types.Transaction GetPoolNonce(ctx context.Context, addr common.Address) (uint64, error) Stats() (pending int, queued int) TxPoolContent() (map[common.Address]types.Transactions, map[common.Address]types.Transactions) TxPoolContentFrom(addr common.Address) (types.Transactions, types.Transactions) SubscribeNewTxsEvent(chan<- core.NewTxsEvent) event.Subscription // Filter API BloomStatus() (uint64, uint64) GetLogs(ctx context.Context, blockHash common.Hash) ([][]*types.Log, error) ServiceFilter(ctx context.Context, session *bloombits.MatcherSession) SubscribeLogsEvent(ch chan<- []*types.Log) event.Subscription SubscribePendingLogsEvent(ch chan<- []*types.Log) event.Subscription SubscribeRemovedLogsEvent(ch chan<- core.RemovedLogsEvent) event.Subscription ChainConfig() *params.ChainConfig Engine() consensus.Engine }
backend 接口有两个实现:eth/api_backend.go EthAPIBackend(全节点实现),les/api_backend.go LesApiBackend(LES轻节点实现)。
先看全节点实现 EthAPIBackend:
// EthAPIBackend implements ethapi.Backend for full nodes type EthAPIBackend struct { extRPCEnabled bool allowUnprotectedTxs bool eth *Ethereum gpo *gasprice.Oracle }
func (b *EthAPIBackend) SendTx(ctx context.Context, signedTx *types.Transaction) error { return b.eth.txPool.AddLocal(signedTx) }
交易池数据结构:core/tx_pool.go。
// TxPool contains all currently known transactions. Transactions // enter the pool when they are received from the network or submitted // locally. They exit the pool when they are included in the blockchain. // // The pool separates processable transactions (which can be applied to the // current state) and future transactions. Transactions move between those // two states over time as they are received and processed. type TxPool struct { // 池配置信息 config TxPoolConfig // 链的配置 chainconfig *params.ChainConfig // 链状态 chain blockChain gasPrice *big.Int txFeed event.Feed scope event.SubscriptionScope signer types.Signer mu sync.RWMutex istanbul bool // Fork indicator whether we are in the istanbul stage. eip2718 bool // Fork indicator whether we are using EIP-2718 type transactions. eip1559 bool // Fork indicator whether we are using EIP-1559 type transactions. currentState *state.StateDB // Current state in the blockchain head pendingNonces *txNoncer // Pending state tracking virtual nonces currentMaxGas uint64 // Current gas limit for transaction caps locals *accountSet // Set of local transaction to exempt from eviction rules journal *txJournal // Journal of local transaction to back up to disk pending map[common.Address]*txList // All currently processable transactions queue map[common.Address]*txList // Queued but non-processable transactions beats map[common.Address]time.Time // Last heartbeat from each known account all *txLookup // All transactions to allow lookups priced *txPricedList // All transactions sorted by price chainHeadCh chan ChainHeadEvent chainHeadSub event.Subscription reqResetCh chan *txpoolResetRequest reqPromoteCh chan *accountSet queueTxEventCh chan *types.Transaction reorgDoneCh chan chan struct{} reorgShutdownCh chan struct{} // requests shutdown of scheduleReorgLoop wg sync.WaitGroup // tracks loop, scheduleReorgLoop initDoneCh chan struct{} // is closed once the pool is initialized (for tests) changesSinceReorg int // A counter for how many drops we've performed in-between reorg. }
添加交易到本地:
// AddLocal enqueues a single local transaction into the pool if it is valid. This is // a convenience wrapper aroundd AddLocals. func (pool *TxPool) AddLocal(tx *types.Transaction) error { errs := pool.AddLocals([]*types.Transaction{tx}) return errs[0] }
调用批量添加接口:
// AddLocals enqueues a batch of transactions into the pool if they are valid, marking the // senders as a local ones, ensuring they go around the local pricing constraints. // // This method is used to add transactions from the RPC API and performs synchronous pool // reorganization and event propagation. func (pool *TxPool) AddLocals(txs []*types.Transaction) []error { return pool.addTxs(txs, !pool.config.NoLocals, true) }
// addTxs attempts to queue a batch of transactions if they are valid. func (pool *TxPool) addTxs(txs []*types.Transaction, local, sync bool) []error { // Filter out known ones without obtaining the pool lock or recovering signatures var ( errs = make([]error, len(txs)) news = make([]*types.Transaction, 0, len(txs)) ) for i, tx := range txs { // If the transaction is known, pre-set the error slot // 交易池的all字段是txLookup类型 // txLookup 有两个map // type txLookup struct { // slots int // lock sync.RWMutex // locals map[common.Hash]*types.Transaction // remotes map[common.Hash]*types.Transaction //} // 本地交易和其他节点同步的交易集合 // Get方法会先查询本地是否有,没有再查remotes中 if pool.all.Get(tx.Hash()) != nil { errs[i] = ErrAlreadyKnown knownTxMeter.Mark(1) continue } // Exclude transactions with invalid signatures as soon as // possible and cache senders in transactions before // obtaining lock // 判断交易的缓存中的签名和交易池的是否相等,相等返回交易中的缓存数据,否则 // 返回交易的发送者地址,并缓存到交易中,见下图代码 // 这步暂时没看出有什么作用?是其他地方会用交易的缓存数据? _, err := types.Sender(pool.signer, tx) if err != nil { errs[i] = ErrInvalidSender invalidTxMeter.Mark(1) continue } // Accumulate all unknown transactions for deeper processing // 添加到news数组中 news = append(news, tx) } // for循环结束,判断是否有满足条件的交易 if len(news) == 0 { return errs } // Process all the new transaction and merge any errors into the original slice // 加读写锁 pool.mu.Lock() // 添加交易到池 newErrs, dirtyAddrs := pool.addTxsLocked(news, local) pool.mu.Unlock() var nilSlot = 0 for _, err := range newErrs { for errs[nilSlot] != nil { nilSlot++ } errs[nilSlot] = err nilSlot++ } // Reorg the pool internals if needed and return done := pool.requestPromoteExecutables(dirtyAddrs) if sync { <-done } return errs }
进入addTxsLocked,遍历交易数组。
// addTxsLocked attempts to queue a batch of transactions if they are valid. // The transaction pool lock must be held. func (pool *TxPool) addTxsLocked(txs []*types.Transaction, local bool) ([]error, *accountSet) { dirty := newAccountSet(pool.signer) errs := make([]error, len(txs)) for i, tx := range txs { replaced, err := pool.add(tx, local) errs[i] = err if err == nil && !replaced { dirty.addTx(tx) } } validTxMeter.Mark(int64(len(dirty.accounts))) return errs, dirty }
进入pool.add方法,处理单个交易数据。
// add validates a transaction and inserts it into the non-executable queue for later // pending promotion and execution. If the transaction is a replacement for an already // pending or queued one, it overwrites the previous transaction if its price is higher. // // If a newly added transaction is marked as local, its sending account will be // be added to the allowlist, preventing any associated transaction from being dropped // out of the pool due to pricing constraints. func (pool *TxPool) add(tx *types.Transaction, local bool) (replaced bool, err error) { // If the transaction is already known, discard it // 判断集合中是否已经存在该交易 // 前面方法已经判断过,又判断一遍? hash := tx.Hash() if pool.all.Get(hash) != nil { log.Trace("Discarding already known transaction", "hash", hash) knownTxMeter.Mark(1) return false, ErrAlreadyKnown } // Make the local flag. If it's from local source or it's from the network but // the sender is marked as local previously, treat it as the local transaction. isLocal := local || pool.locals.containsTx(tx) // If the transaction fails basic validation, discard it // 校验交易数据 if err := pool.validateTx(tx, isLocal); err != nil { log.Trace("Discarding invalid transaction", "hash", hash, "err", err) invalidTxMeter.Mark(1) return false, err } // If the transaction pool is full, discard underpriced transactions // pool.all.Slots()当前所有交易占用的槽 // numSlots(tx) 计算该交易需要的槽,交易的int((tx.Size() + txSlotSize - 1) /。txSlotSize)。txSlotSize = 32 * 1024(32KB) // pool.config.GlobalSlots 最大可执行交易槽 // pool.config.GlobalQueue 最大不可执行交易槽数 if uint64(pool.all.Slots()+numSlots(tx)) > pool.config.GlobalSlots+pool.config.GlobalQueue { // If the new transaction is underpriced, don't accept it // 抛弃低价交易 if !isLocal && pool.priced.Underpriced(tx) { log.Trace("Discarding underpriced transaction", "hash", hash, "gasTipCap", tx.GasTipCap(), "gasFeeCap", tx.GasFeeCap()) underpricedTxMeter.Mark(1) return false, ErrUnderpriced } // We're about to replace a transaction. The reorg does a more thorough // analysis of what to remove and how, but it runs async. We don't want to // do too many replacements between reorg-runs, so we cap the number of // replacements to 25% of the slots if pool.changesSinceReorg > int(pool.config.GlobalSlots/4) { throttleTxMeter.Mark(1) return false, ErrTxPoolOverflow } // New transaction is better than our worse ones, make room for it. // If it's a local transaction, forcibly discard all available transactions. // Otherwise if we can't make enough room for new one, abort the operation. // 删除低价交易(如果交易tx比较大,不超过32k,删除的交易可能会有多个remote的交易) // 将txPricedList中的urgent排序堆,删除若干个交易,使得槽可以放下当前交易 // 感觉会删除pending列表的交易,这样的话当前交易会进入pending列表? // 代码见下 drop, success := pool.priced.Discard(pool.all.Slots()-int(pool.config.GlobalSlots+pool.config.GlobalQueue)+numSlots(tx), isLocal) // Special case, we still can't make the room for the new remote one. if !isLocal && !success { log.Trace("Discarding overflown transaction", "hash", hash) overflowedTxMeter.Mark(1) return false, ErrTxPoolOverflow } // Bump the counter of rejections-since-reorg pool.changesSinceReorg += len(drop) // Kick out the underpriced remote transactions. // 遍历出堆的交易,删除 for _, tx := range drop { log.Trace("Discarding freshly underpriced transaction", "hash", tx.Hash(), "gasTipCap", tx.GasTipCap(), "gasFeeCap", tx.GasFeeCap()) underpricedTxMeter.Mark(1) // 删除交易 pool.removeTx(tx.Hash(), false) } } // Try to replace an existing transaction in the pending pool // 获取交易的发送者 from, _ := types.Sender(pool.signer, tx) // already validated // 获取pending列表中该账号所有的交易,这些交易中是否含有当前交易相同的nonce值 // //func (l *txList) Overlaps(tx *types.Transaction) bool { // return l.txs.Get(tx.Nonce()) != nil //} if list := pool.pending[from]; list != nil && list.Overlaps(tx) { // Nonce already pending, check if required price bump is met // 当前交易添加到列表,替换相同nonce值的交易 inserted, old := list.Add(tx, pool.config.PriceBump) if !inserted { pendingDiscardMeter.Mark(1) return false, ErrReplaceUnderpriced } // New transaction is better, replace old one if old != nil { pool.all.Remove(old.Hash()) pool.priced.Removed(1) pendingReplaceMeter.Mark(1) } pool.all.Add(tx, isLocal) pool.priced.Put(tx, isLocal) pool.journalTx(from, tx) pool.queueTxEvent(tx) log.Trace("Pooled new executable transaction", "hash", hash, "from", from, "to", tx.To()) // Successful promotion, bump the heartbeat pool.beats[from] = time.Now() return old != nil, nil } // New transaction isn't replacing a pending one, push into queue // 如果当前交易的nonce值和pending中该账号的交易没有相同的,当前交易添加到queue列表中 replaced, err = pool.enqueueTx(hash, tx, isLocal, true) if err != nil { return false, err } // Mark local addresses and journal local transactions if local && !pool.locals.contains(from) { log.Info("Setting new local account", "address", from) pool.locals.add(from) pool.priced.Removed(pool.all.RemoteToLocals(pool.locals)) // Migrate the remotes if it's marked as local first time. } if isLocal { localGauge.Inc(1) } pool.journalTx(from, tx) log.Trace("Pooled new future transaction", "hash", hash, "from", from, "to", tx.To()) return replaced, nil }
TxPool中字段 pending列表的数据结构: pending map[common.Address]*txList // All currently processable transactions
// txList is a "list" of transactions belonging to an account, sorted by account // nonce. The same type can be used both for storing contiguous transactions for // the executable/pending queue; and for storing gapped transactions for the non- // executable/future queue, with minor behavioral changes. type txList struct { // 交易的nonce值是否连续 strict bool // Whether nonces are strictly continuous or not // 已排序的交易Map txs *txSortedMap // Heap indexed sorted hash map of the transactions // 最高交易价格 costcap *big.Int // Price of the highest costing transaction (reset only if exceeds balance) // 最高花费的gas gascap uint64 // Gas limit of the highest spending transaction (reset only if exceeds block limit) }
丢弃低价交易,为新交易腾出空间Discard:
// Discard finds a number of most underpriced transactions, removes them from the // priced list and returns them for further removal from the entire pool. // // Note local transaction won't be considered for eviction. func (l *txPricedList) Discard(slots int, force bool) (types.Transactions, bool) { drop := make(types.Transactions, 0, slots) // Remote underpriced transactions to drop for slots > 0 { // 先将urgent堆中的交易,转移到floating堆中,再删除floating堆中交易 // Two heaps are used for sorting: the urgent heap (based on effective tip in the next // block) and the floating heap (based on gasFeeCap). Always the bigger heap is chosen for // eviction. Transactions evicted from the urgent heap are first demoted into the floating heap. // In some cases (during a congestion, when blocks are full) the urgent heap can provide // better candidates for inclusion while in other cases (at the top of the baseFee peak) // the floating heap is better. When baseFee is decreasing they behave similarly. if len(l.urgent.list)*floatingRatio > len(l.floating.list)*urgentRatio || floatingRatio == 0 { // Discard stale transactions if found during cleanup tx := heap.Pop(&l.urgent).(*types.Transaction) if l.all.GetRemote(tx.Hash()) == nil { // Removed or migrated atomic.AddInt64(&l.stales, -1) continue } // Non stale transaction found, move to floating heap heap.Push(&l.floating, tx) } else { if len(l.floating.list) == 0 { // Stop if both heaps are empty break } // Discard stale transactions if found during cleanup tx := heap.Pop(&l.floating).(*types.Transaction) if l.all.GetRemote(tx.Hash()) == nil { // Removed or migrated atomic.AddInt64(&l.stales, -1) continue } // Non stale transaction found, discard it drop = append(drop, tx) slots -= numSlots(tx) } } // If we still can't make enough room for the new transaction // 数据空间仍不足,交易重新保存到urgent堆中 if slots > 0 && !force { for _, tx := range drop { heap.Push(&l.urgent, tx) } return nil, false } return drop, true }
删除需要丢弃的低价交易 removeTx:
func (pool *TxPool) removeTx(hash common.Hash, outofbound bool) { // Fetch the transaction we wish to delete // 从local和remote集合中查询需要删除的交易 tx := pool.all.Get(hash) if tx == nil { return } // 获得交易的发送者地址 addr, _ := types.Sender(pool.signer, tx) // already validated during insertion // Remove it from the list of known transactions // 从local和remote集合中删除该交易 pool.all.Remove(hash) if outofbound { pool.priced.Removed(1) } if pool.locals.contains(addr) { localGauge.Dec(1) } // Remove the transaction from the pending lists and reset the account nonce // 判断该交易是否在pending列表,若在,删除 if pending := pool.pending[addr]; pending != nil { if removed, invalids := pending.Remove(tx); removed { // If no more pending transactions are left, remove the list if pending.Empty() { delete(pool.pending, addr) } // Postpone any invalidated transactions for _, tx := range invalids { // Internal shuffle shouldn't touch the lookup set. pool.enqueueTx(tx.Hash(), tx, false, false) } // Update the account nonce if needed // 更新账号的nonce值 pool.pendingNonces.setIfLower(addr, tx.Nonce()) // Reduce the pending counter pendingGauge.Dec(int64(1 + len(invalids))) return } } // Transaction is in the future queue // 判断该交易是否在queue列表中,若在,删除 // 为什么不更新账号的nonce值? if future := pool.queue[addr]; future != nil { if removed, _ := future.Remove(tx); removed { // Reduce the queued counter queuedGauge.Dec(1) } if future.Empty() { delete(pool.queue, addr) delete(pool.beats, addr) } } }
当pending列表中包含当前提交的交易的相同nonce值时:
// Add tries to insert a new transaction into the list, returning whether the // transaction was accepted, and if yes, any previous transaction it replaced. // // If the new transaction is accepted into the list, the lists' cost and gas // thresholds are also potentially updated. func (l *txList) Add(tx *types.Transaction, priceBump uint64) (bool, *types.Transaction) { // If there's an older better transaction, abort old := l.txs.Get(tx.Nonce()) if old != nil { if old.GasFeeCapCmp(tx) >= 0 || old.GasTipCapCmp(tx) >= 0 { return false, nil } // thresholdFeeCap = oldFC * (100 + priceBump) / 100 a := big.NewInt(100 + int64(priceBump)) aFeeCap := new(big.Int).Mul(a, old.GasFeeCap()) aTip := a.Mul(a, old.GasTipCap()) // thresholdTip = oldTip * (100 + priceBump) / 100 b := big.NewInt(100) thresholdFeeCap := aFeeCap.Div(aFeeCap, b) thresholdTip := aTip.Div(aTip, b) // Have to ensure that either the new fee cap or tip is higher than the // old ones as well as checking the percentage threshold to ensure that // this is accurate for low (Wei-level) gas price replacements if tx.GasFeeCapIntCmp(thresholdFeeCap) < 0 || tx.GasTipCapIntCmp(thresholdTip) < 0 { return false, nil } } // Otherwise overwrite the old transaction with the current one l.txs.Put(tx) if cost := tx.Cost(); l.costcap.Cmp(cost) < 0 { l.costcap = cost } if gas := tx.Gas(); l.gascap < gas { l.gascap = gas } return true, old }
以上已将交易添加到交易池中。
步骤大约:
1,SendTransaction接受交易参数
2,签名交易signTransaction
查找交易的账号,设置交易的一些默认值,并将交易参数,组装成交易对象tx,调用钱包进行签名
3,提交交易SubmitTransaction
校验交易的手续费,添加本地交易,判断local和remote是否已经包含该交易,包含则返回
校验交易数据,判断剩余的槽是否足够,不够的话调用Discard丢弃(通过两个堆进行查找要丢弃的交易)
再真正丢弃remote和local中的交易
并丢弃pending和queue中的交易
判断交易在pending中是否有相同的nonce值,判断是否需要替换
如果没有,添加到queue列表中(这个地方感觉有点问题,如果上面Discard丢弃的都是pending中的交易,添加到queue中会有问题)
接下来是交易重构。
下一章:以太坊区块和交易存储
1. 区块存储区块(Block)是以太坊的核心数据结构之一,Block包含Header和Body两部分。区块的存储是由leveldb完成的,leveldb的数据是以键值对存储的。// BlockChain 表示了一个规 ...