作者:明神特烦恼
公众号:明神特烦恼
提案一般是共识流程中第一步,后面步骤为多阶段的投票,最终达到一致。这里分析提案将独立于共识,分析如何生成待共识的消息。提案的高度信息、提案人确认等由其他共识模块传入,这里不做分析。
带着问题读代码:
1)提案的触发点有哪些?
2)从交易池获取的交易集合,作为待提案的消息,还要经过哪些检查?
3)提案的最终数据结构是什么?
4)生成的提案消息的生命周期是如何管理的?
5)交易的读写集是如何生成的?
第一个问题:提案的触发点有哪些?
触发点入口比较收敛,在下面的函数中,有两个条件可以触发提案:
1)提案定时器proposeTimer
到达定时时间
2)交易池打包成批
- 生成批超时时间500ms
- 某些交易重新放入交易池
RetryAndRemoveTxs
case <-bp.proposeTimer.C: if !bp.isSelfProposer() { break } go bp.proposeBlock() case signal := <-bp.txPoolSignalC: if !bp.isSelfProposer() { break } if signal.SignalType != txpoolpb.SignalType_BLOCK_PROPOSE { break } go bp.proposeBlock() case <-bp.exitC: bp.proposeTimer.Stop() bp.log.Info("block proposer loop stoped") return }
提案定时器何时触发、何时停止?
触发:
- 上一个提案块结束后,如果自己为提案人,开启定时器
- 共识模块变更提案人,如果提案人为自己,开启定时器
停止: - 提案模块停止工作时,关闭定时器
- 共识模块变更提案人,如果提案人不是自己,停止定时器
第二个问题:从交易池获取的交易集合,作为待提案的消息,还要经过哪些检查?
检测函数:txDuplicateCheck
,主要逻辑为并发查询数据库是否有相同的Txid,如果有则剔除。
在长安链共识结束后,需要同步将成功共识的交易从交易池中剔除,以防止提案过程中打包重复交易。
根据在之前的章节分析,交易进入交易池前已经判断过交易的Txid是否存在,但在提案前又进行检测,为什么这里会检测是否有重复交易?
答:交易池会同步接收各节点广播的batch,节点会检测这些batch是否在已有区块中有重复的Txid,但为了提高性能并没有加锁检测,这里并不会保证过来的batch中没有冲突Txid。也就是节点的各个Batch会有重复的Txid。
此处留下思考点一: txDuplicateCheck
是会从数据库中比较Txid是否冲突,能否减少这部分读取数据库的操作?
第三个问题: 提案的最终数据结构是什么?
提案的最终信息为:
- Header 区块头信息,包括链ID、块高度、前块Hash、世界状态默克尔根等。
- Dag 交易关联拓扑图,后续专门会讲解
- Txs 交易信息集合
- AdditionalData 扩展数据,例如:投票信息等。
type Block struct { // header of the block Header *BlockHeader `protobuf:"bytes,1,opt,name=header,proto3" json:"header,omitempty"` // execution sequence of intra block transactions is generated by proposer Dag *DAG `protobuf:"bytes,2,opt,name=dag,proto3" json:"dag,omitempty"` Txs []*Transaction `protobuf:"bytes,3,rep,name=txs,proto3" json:"txs,omitempty"` // stores the voting information of the current block // not included in block hash value calculation AdditionalData *AdditionalData `protobuf:"bytes,4,opt,name=additional_data,json=additionalData,proto3" json:"additional_data,omitempty"` }
第四个问题:生成的提案消息的生命周期是如何管理的?
上面流程分析一个提案Block是如何产生的,那提案过程中的提案Block如何保存、提案成功落块后如何清理?
长安链保存提案消息的数据结构为,主要结构为lastProposedBlock
, 存储的数据结构注释写的很清晰map[height]map[hash]*blockProposal
,即每个块高度会有多个提案,每个提案以Hash作为索引进行检索。
PS: ledgerCache 可否不暴露给其他模块,对于其他系统组件,他们认为账本信息应该从账本模块获取,而不是另外一个缓存模块。
type ProposalCache struct { // block height -> block hash -> block with rw set // since one block height may have multiple block proposals lastProposedBlock map[int64]map[string]*blockProposal rwMu sync.RWMutex chainConf protocol.ChainConf ledgerCache protocol.LedgerCache }
下面我们来分析lastProposedBlock 是如何管理提案区块的。
1. SetProposedBlock
SetProposedBlock
负责记录提案块到缓存,录入时机为:
- 本节点生成提案块后,在函数
generateNewBlock
中 - 在共识期间验证区块有效性后,在函数
VerifyBlock
中
2. SetProposedAt
SetProposedAt
负责设置某提案块为正在执行共识的提案块(当前轮次round)。记录时机为:
- 本节点生成提案块后,在函数
generateNewBlock
中 - 本节点生成提案过程中,如果已经存在某提案块,则直接调用
SetProposedAt
修改状态,在函数proposing
中
3. ResetProposedAt
ResetProposedAt
负责清空当前正在执行的共识为本提案块的标记。记录时机为:
- 本节点接收到共识发送的提案状态变更信号,在函数
OnReceiveProposeStatusChange
中
4. ClearProposedBlockAt
ClearProposedBlockAt
负责清空某个height的所有提案块,操作时机为:
- 共识完成落块期间,在函数
AddBlock
中
5. ClearTheBlock
ClearTheBlock
负责清除某个提案块,操作时机为:
- 当本节点提案时,发现之前在该height上的提案块已无法使用(前块Hash不匹配),需要删除。在函数
proposing
中。
6. KeepProposedBlock
KeepProposedBlock
负责获取并清除某个高度下非指定hash的所有提案块,操作时机为:
- 在区块验证期间,确定使用某个提案块,则其他相同高度的提案块需要删除,其中包含的、未使用的交易需要回归交易池。
第五个问题:交易的读写集是如何生成的?
1. 读写集:
世界状态是一组key-vaule集合,为每个key-value 添加 version属性,来实现多版本并发控制(MVCC)。
假设已存在世界状态(K1,V1,1)、(K2,V2,1)、(K3,V3,1)、(K4,V4,1)、(K5,V5,1)
,现有2笔交易并发T1 -> Read(K1) Write(K1,V1`)
、T2 -> Read(K1) Write(K1,V1``)
。
T1:读取K1、Version为1;写入K1,Version为2。
T2:读取K1、Version为1;写入K1,Version为2。
当这两笔交易发送至验证节点,会被识别为读写集冲突,为了便于理解,这里举个例子。张三账户原有金额10,将T1、T2理解为给张三账户增加10,那么执行完成张三账户应有30。但由于读写集冲突,最终张三的账户只有20,这种情况是不被允许的。
2. fabric与长安链读写集区别
- fabric每笔交易先发送至节点背书,背书过程中获取读写集,也就是每笔交易并不知晓其他交易的存在,背书过程都是基于同一世界状态生成,在后续排序、验证时发现读写集冲突,将冲突交易设置为无效。
- 长安链在发送交易时只有交易内容,并没有构造读写集。在提案过程中的所有交易生成读写集,理论上长安链并不会存在读写集冲突的情况,因为所有交易顺次执行即使有依赖,也会依赖前一交易的新读写集。长安链中的读写集冲突只发生在程序内部,外部并不感知。长安链是为了提高读写集生成效率,并发生成读写集,即使出现冲突也是内部处理逻辑,不影响程序执行。。
3. 长安链读写集生成
官方参考文档:https://docs.chainmaker.org.cn/tech/%E5%B9%B6%E8%A1%8C%E8%B0%83%E5%BA%A6.html
在提案期间对提案的交易集合生成读写集,在函数Schedule
中。
bp.txScheduler.Schedule(block, validatedTxs, snapshot)
长安链除了构造读写集外,还构造DAG有向无环图,将交易的关联关系以数据结构形式记录,记录的目的是更快的验证区块有效性。
读写集生成流程:
- 设置并发线程,所有交易并发执行VM,生成读写集。
- 每笔交易生成读写集后,调用
ApplyTxSimContext
判断是否与已生成读写集的交易存在冲突。如果有重复,则重新执行VM生成新的读写集。 - 第二步 由于读写集冲突会重新执行VM生成读写集,假设一批交易10W笔交易,每笔都是冲突的读写集,那么流程执行时间过长,也会影响共识的流程。因此增加超时定时器,超过指定时间(10s)发生截取,未执行的交易则不会进入本次提案。未进入提案的交易会重新归还给交易池,逻辑在
generateNewBlock
中。
读写集的生成到此为止,看上去不需要生成DAG也可以发起提案流程,为什么需要DAG呢?
答:因为上述的交易执行流程是并发的,每个节点的交易执行顺序是随机的,也就是其他节点无法构造出相同的最终读写集状态,需要有辅助数据提供节点的执行顺序。也可以表示出哪些交易可以并发验证,提高区块验证速度。
4. 长安链由读写集生成DAG
1. 关联关系判定
当两笔交易顺序互换,将产生不同结果的都属于有关联的交易。
1) t1: Read(K1) t2:Write(K1, V1)
2) t1:Write(K1, V1) t2: Read(K1)
3) t1:Write(K1,V1) t2:Write(K1,V2)
生成DAG的第一步是找到上述三种关系,具体实现如下:
1)调用buildRWBitmaps
函数,将原有交易读写集使用bitmap表示。将原读写集中的每个key转化为一个int类型,通过[]*bitmap.Bitmap
数据结构标记每笔交易涉及到的key,该函数返回readBitmap
、writeBitmap
。
2)调用buildCumulativeBitmap
函数,对读集和写集分别做叠加(或操作),例如:第一笔交易读集的bitmap为 1 0 1 1 0
,其中每个bit位为1的位置表示一个读集的key。第二笔交易读集的bitmap为 0 1 0 1 0
,那么经过叠加后的第二笔交易的读集为 1 1 1 1 0
,后面依次对前面交易进行叠加,最终形成叠加后的读集与写集,cumulativeReadBitmap
、cumulativeWriteBitmap
。
3)通过第一步与第二步形成的结果,来判断哪些交易存在关联性。
3.1)第 readBitmap[i]
与cumulativeWriteBitmap[i-1]
中指向的key一致。
3.2)第 writeBitmap[i]
与cumulativeReadBitmap[i-1]
中指向的key一致。
3.3)第 writeBitmap[i]
与cumulativeWriteBitmap[i-1]
中指向的key一致。
这三个判定条件刚好符合上面提到的关联关系判定。
4)记录关联关系。经过第三步判断某个交易与前面叠加后的交易有关联,这里需要进一步展开确定到底是与哪笔交易有关联。以3.1情况举例,将 readBitmap[i]
与 writeBitmap[0 ~ (i-1)]
逐一比较记录关联关系。
2. DAG表现方式
type DAG struct { // sequence number of transaction topological sort //the sequence number of the transaction topological sort associated with the transaction Vertexes []*DAG_Neighbor `protobuf:"bytes,2,rep,name=vertexes,proto3" json:"vertexes,omitempty"` } type DAG_Neighbor struct { Neighbors []int32 `protobuf:"varint,1,rep,packed,name=neighbors,proto3" json:"neighbors,omitempty"` }
其中Vertexes[i]
表示第i笔交易,Neighbors
数组中每个值表示与之关联的交易编号。
3. DAG的使用
DAG的使用在验证区块模块中,本章节主要介绍提案流程,这部分会在介绍区块验证流程时详细阐述。
关注作者,共同学习区块链技术。
2024最新激活全家桶教程,稳定运行到2099年,请移步至置顶文章:https://sigusoft.com/99576.html
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。 文章由激活谷谷主-小谷整理,转载请注明出处:https://sigusoft.com/165364.html