区块链技术开发教程

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

以太坊源码分析——交易池重置

2018-12-14

本章节介绍以太坊交易池的重置方法,重置方法作用是检查pending和queue中的交易是否满足条件。

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


一、TxPool概述


1.1 前言


本章节介绍以太坊交易池的重置方法,重置方法作用是检查pending和queue中的交易是否满足条件,如果pending中的交易不满足存在条件,则直接删除或放入queue中,如果queue中的某些交易不满足存在的条件则直接删除,如果满足进入pending的条件则将这些交易放入pending列表。另外如果区块链产生分叉,还会将原来的规范链上不存在于新规范链上的交易重新放入交易池。


1.2 reset函数


func (pool *TxPool) reset(oldHead, newHead *types.Header) {
// If we're reorging an old state, reinject all dropped transactions
var reinject types.Transactions
 
//1. 找到由于规范链更新而作废的交易
//新区块头的父区块不等于老区快
if oldHead != nil && oldHead.Hash() != newHead.ParentHash {
// If the reorg is too deep, avoid doing it (will happen during fast sync)
oldNum := oldHead.Number.Uint64()
newNum := newHead.Number.Uint64()
 
if depth := uint64(math.Abs(float64(oldNum) - float64(newNum))); depth > 64 {
//新的头区块号与旧的头区块号差值大于64
log.Debug("Skipping deep transaction reorg", "depth", depth)
} else {
// Reorg seems shallow enough to pull in all transactions into memory
var discarded, included types.Transactions
 
var (
rem = pool.chain.GetBlock(oldHead.Hash(), oldHead.Number.Uint64())
add = pool.chain.GetBlock(newHead.Hash(), newHead.Number.Uint64())
)
//如果旧链的头区块大于新链的头区块高度,旧链向后回退,同时收集所有旧链中被回退的交易
for rem.NumberU64() > add.NumberU64() {
discarded = append(discarded, rem.Transactions()...)
//如果旧区块的父区块为空直接退出
if rem = pool.chain.GetBlock(rem.ParentHash(), rem.NumberU64()-1); rem == nil {
log.Error("Unrooted old chain seen by tx pool", "block", oldHead.Number, "hash", oldHead.Hash())
return
}
}
//如果新链的头区块大于旧链的头区块高度,旧链向后回退,同时收集所有旧链中被回退的交易
for add.NumberU64() > rem.NumberU64() {
included = append(included, add.Transactions()...)
//如果旧区块的父区块为空直接退出
if add = pool.chain.GetBlock(add.ParentHash(), add.NumberU64()-1); add == nil {
log.Error("Unrooted new chain seen by tx pool", "block", newHead.Number, "hash", newHead.Hash())
return
}
}
//新旧链同时向后回退,并收集被回退的交易
for rem.Hash() != add.Hash() {
discarded = append(discarded, rem.Transactions()...)
if rem = pool.chain.GetBlock(rem.ParentHash(), rem.NumberU64()-1); rem == nil {
log.Error("Unrooted old chain seen by tx pool", "block", oldHead.Number, "hash", oldHead.Hash())
return
}
included = append(included, add.Transactions()...)
if add = pool.chain.GetBlock(add.ParentHash(), add.NumberU64()-1); add == nil {
log.Error("Unrooted new chain seen by tx pool", "block", newHead.Number, "hash", newHead.Hash())
return
}
}
//找discarded中那些不存在于included中的交易
reinject = types.TxDifference(discarded, included)
}
}
// Initialize the internal state to the current head
if newHead == nil {
newHead = pool.chain.CurrentBlock().Header() // Special case during testing
}
//2. 给交易池设置最新的世界状态
//获取新链头区块的状态
statedb, err := pool.chain.StateAt(newHead.Root)
if err != nil {
log.Error("Failed to reset txpool state", "err", err)
return
}
//3. 设置新的currentState、pendingState、currentMaxGas
pool.currentState = statedb
pool.pendingState = state.ManageState(statedb)
pool.currentMaxGas = newHead.GasLimit
 
//4. 把旧链回退回来的交易重新放入交易池
// Inject any transactions discarded due to reorgs
log.Debug("Reinjecting stale transactions", "count", len(reinject))
senderCacher.recover(pool.signer, reinject)
pool.addTxsLocked(reinject, false)
 
// validate the pool of pending transactions, this will remove
// any transactions that have been included in the block or
// have been invalidated because of another transaction (e.g.
// higher gas price)
//5. 因为规范链的更新,将pending中的部分交易移到queue里面
pool.demoteUnexecutables()
//6. 更新pendingState中账户的nonce值。
// Update all accounts to the latest known pending nonce
for addr, list := range pool.pending {
txs := list.Flatten() // Heavy but will be cached and is needed by the miner anyway
pool.pendingState.SetNonce(addr, txs[len(txs)-1].Nonce()+1)
}
// Check the queue and move transactions over to the pending if possible
// or remove those that have become invalid
//7. 因为规范链的更新,将queue里面的交易移入到pending里面
pool.promoteExecutables(nil)
}


上面的第1步,如果区块链产生分叉,先找出原来规范链上的和新规范链上的交易,找出的方式是两条链同时回退,直到找到共同祖先,当然如果两条链是一样高,则先回退高的那条,然后同时回退。回退的过程中将两条链上的交易保存下来,分别是discarded, included。然后找出discarded中不存在于included中的交易,然后将这些交易重新放入交易池。


第2步是给交易池设定最新的世界状态,pendingState中记录的pending中每个账户的pendingNonce值,pendingNonce是pending列表中账户的最后一个交易的nonce值加1的结果,后面queue升级的时候要保证queue中的交易的nonce值必须是从pendingNonce起始的。第3步先把pendingState设置成currentState,后续等pending列表发生降级后,再重新调整。


第4步是把第1步中找出的交易重新放入交易池。


第5步是降级操作,后续章节详细介绍。


第6步是再次调整pendingState,由于降级发生后,pending中账户的交易有变动,所以需要重新设置pendingState中的那些变动的账户的pendingNonce值。


第7步是升级操作,后续章节详细介绍。


1.3 总结


本章节主要介绍交易池的reset函数,这个函数是因为规范链的更新而被调用的,规范链的更新导致世界状态的变更,所以重新调整pending和queue列表。另外还将分叉后原来规范链上不存在于新规范链上的交易重新放入交易池。下一章节我们介绍交易池的添加函数。



-END-


001周末班+0628二维码.jpg

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

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

003学习路径+公众号.jpg

7*24客服电话

150-1118-1611

扫码添加助教微信

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