eth-tx_pool

Ethereum core.tx-pool

1: Introduce tx-pool,

源码位置 core/tx_pool.go
eth的交易池主要存放Local、Remote提交且等待被写入区块的交易,提交的大概分为以下几种
1:交易基本Check失败被Discard的
2:检验通过,但是暂时还无法执行的,在queue中等待执行,比如nonce太高 (nonce定序)
3:校验通过,等待执行(pending)

2:交易基本校验校验流程

  • 计算交易的RLP 编码的size,如果超过128KB,则校验失败。预防Dos攻击

  • 校验交易的gaslimit,是否满足当前tx_pool的配置

  • 校验交易的签名, 是否正确

  • 校验交易的gasPrice是否符合矿池的gasPrice要求

  • 校验缓存的from用户的Nonce大于交易的Nonce

  • 校验from账户的资产是否充足(tx.value + GasPrice * GasLimit)

  • ps: 交易池的信息会随着区块头信息的更新而改变,更新了区块头,有一些交易会被作废,pending和queue里的交易也会发生变化,TxPool.reset方法确保更新了header之后,交易池的相关信息都会被更新成对当前header有效

3: tx-pool结构分析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
type TxPool struct {
config TxPoolConfig
chainconfig *params.ChainConfig
chain blockChain // 当前blockchain信息
gasPrice *big.Int // 最低能接受的gasprice
txFeed event.Feed //通过txFeed订阅TxPool消息
scope event.SubscriptionScope
signer types.Signer // 事务签名处理
mu sync.RWMutex

istanbul bool // Fork indicator whether we are in the istanbul stage.

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, 当前pending的交易
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, 所有的交易map
priced *txPricedList // All transactions sorted by price, 根据price排序的交易列表

chainHeadCh chan ChainHeadEvent // 区块头channel,更新当前TxPool的依据来源
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
}

4:tx_pool主loop

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
func (pool *TxPool) loop() {
defer pool.wg.Done()

var (
prevPending, prevQueued, prevStales int
// Start the stats reporting and transaction eviction tickers
// TxPool相关定时器
report = time.NewTicker(statsReportInterval)
evict = time.NewTicker(evictionInterval) /
journal = time.NewTicker(pool.config.Rejournal)
// Track the previous head headers for transaction reorgs
head = pool.chain.CurrentBlock()
)
defer report.Stop()
defer evict.Stop()
defer journal.Stop()

for {
select {
// Handle ChainHeadEvent
case ev := <-pool.chainHeadCh:
// 更新交易池的chainHead信息
if ev.Block != nil {
pool.requestReset(head.Header(), ev.Block.Header())
head = ev.Block
}

// System shutdown.
case <-pool.chainHeadSub.Err():
// 关闭
close(pool.reorgShutdownCh)
return

// Handle stats reporting ticks
case <-report.C:
pool.mu.RLock()
pending, queued := pool.stats()
stales := pool.priced.stales
pool.mu.RUnlock()

if pending != prevPending || queued != prevQueued || stales != prevStales {
log.Debug("Transaction pool status report", "executable", pending, "queued", queued, "stales", stales)
prevPending, prevQueued, prevStales = pending, queued, stales
}

// Handle inactive account transaction eviction
// 驱逐超时的heartbeat交易(忽略本地交易)
case <-evict.C:
pool.mu.Lock()
for addr := range pool.queue {
// Skip local transactions from the eviction mechanism
if pool.locals.contains(addr) {
continue
}
// Any non-locals old enough should be removed
if time.Since(pool.beats[addr]) > pool.config.Lifetime {
for _, tx := range pool.queue[addr].Flatten() {
pool.removeTx(tx.Hash(), true)
}
}
}
pool.mu.Unlock()

// Handle local transaction journal rotation
// 定时落地本地交易,服务重启会自动加载本地交易
case <-journal.C:
if pool.journal != nil {
pool.mu.Lock()
if err := pool.journal.rotate(pool.local()); err != nil {
log.Warn("Failed to rotate local tx journal", "err", err)
}
pool.mu.Unlock()
}
}
}
}