区块链技术开发教程

区块链技术指南,区块链技术的工作原理讲解,区块链技术入门教程

以太坊源码分析——交易池初始化

2018-12-13

本章节主要介绍交易池的初始化函数。

点击区块链技术培训课程获取更多区块链技术学习资料。


一、TxPool概述

1.1 前言

本章节主要介绍交易池的初始化函数。


1.2 NewTxPool

NewTxpool主要是初始化Txpool类,启动监听事件的go程。这个函数在以太坊启动的过程中被调用。

func NewTxPool(config TxPoolConfig, chainconfig *params.ChainConfig, chain blockChain) *TxPool {
// Sanitize the input to ensure no vulnerable gas prices are set
config = (&config).sanitize()
 
//1. 初始化TxPool类
// Create the transaction pool with its initial settings
pool := &TxPool{
config:      config,
chainconfig: chainconfig,
chain:       chain,
signer:      types.NewEIP155Signer(chainconfig.ChainID),
pending:     make(map[common.Address]*txList),
queue:       make(map[common.Address]*txList),
beats:       make(map[common.Address]time.Time),
all:         newTxLookup(),
chainHeadCh: make(chan ChainHeadEvent, chainHeadChanSize),
gasPrice:    new(big.Int).SetUint64(config.PriceLimit),
}
pool.locals = newAccountSet(pool.signer)
pool.priced = newTxPricedList(pool.all)
pool.reset(nil, chain.CurrentBlock().Header())
//2. 将持久化的本地交易重新读取放入交易池
// If local transactions and journaling is enabled, load from disk
if !config.NoLocals && config.Journal != "" {
pool.journal = newTxJournal(config.Journal)
 
if err := pool.journal.load(pool.AddLocals); err != nil {
log.Warn("Failed to load transaction journal", "err", err)
}
if err := pool.journal.rotate(pool.local()); err != nil {
log.Warn("Failed to rotate transaction journal", "err", err)
}
}
 //3. 订阅BlockChain更新事件
// Subscribe events from blockchain
pool.chainHeadSub = pool.chain.SubscribeChainHeadEvent(pool.chainHeadCh)
// Start the event loop and return
pool.wg.Add(1)
 //4. 启动事件监听Goroutine
go pool.loop()
 
return pool
}


上面代码的第2步的作用是把上一次保存到文件的本地交易重新放入交易池,只要是经过的本地RPC接收到的交易都被认为是本地交易,比如通过web3.js连接到本节点发送的交易和本节点交互式命令行发送的交易。如果是从p2p网路上收到的交易则被认为是远程交易,以太坊中本地交易具有比较高的优先级,如果交易池满了,以太坊也不会轻易删除这些交易。


第3步是注册BlockChain的规范链头更新事件,因为规范链的头更新意味着世界状态的改变,交易池需要根据最新的世界状态来调整pending和queue中的交易。


第4步是启动一个goroutine来监听一些事件,当然也包括第3步注册的规范链头更新事件。


func (pool *TxPool) loop() {
defer pool.wg.Done()
 
// Start the stats reporting and transaction eviction tickers
var prevPending, prevQueued, prevStales int
 
 //报告打印时钟,8秒
report := time.NewTicker(statsReportInterval)
defer report.Stop()
 
 //处理超时交易时钟,1分钟
evict := time.NewTicker(evictionInterval)
defer evict.Stop()
 
 //定时写日志时钟,1小时
journal := time.NewTicker(pool.config.Rejournal)
defer journal.Stop()
 
// Track the previous head headers for transaction reorgs
head := pool.chain.CurrentBlock()
 
// Keep waiting for and reacting to the various events
for {
select {
// Handle ChainHeadEvent
//1. 如果有本地规范链有更新
case ev := <-pool.chainHeadCh:
if ev.Block != nil {
pool.mu.Lock()
if pool.chainconfig.IsHomestead(ev.Block.Number()) {
pool.homestead = true
}
//使用最新区块头重置交易池
pool.reset(head.Header(), ev.Block.Header())
head = ev.Block
 
pool.mu.Unlock()
}
// Be unsubscribed due to system stopped
case <-pool.chainHeadSub.Err():
return
 
// Handle stats reporting ticks
 //2. 如果交易池状态有变更,打印最新状态(pending长度,queue长度,stales大小)
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
 
 //3. 删除queue中的超时交易,3小时没有处理的交易
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
//4. 定时写本地交易日志
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()
}
}
}
}


上面代码的第1步是监听到规范链头区块更新事件之后的处理,会直接调用reset函数对交易池进行重新整理,包括降级和升级。

第2步是一个定时器事件,每隔8秒钟会打印一次交易池的状态,比如pending和queue的总交易数。

第3步是queue列表交易超时处理的函数,每隔1分钟检查一次,如果queue列表中的某些账户3个小时都没有交易进入pending列表,则将这个账户在queue中的所有交易全部删除。

第4步是定时将将交易池的中的本地交易持久化到文件中。


1.3 总结

本章节主要介绍了交易池的初始化流程,下一章节我们介绍交易池重置函数。


-END-


001周末班+0628二维码.jpg

区块链应用案例手箭头.png

点击查看更多区块链应用成功案例 ,区块链技术开发教程 。

003学习路径+公众号.jpg

7*24客服电话

150-1118-1611

扫码添加助教微信

二维码
菜单 在线咨询 回到顶部
关闭