作者:明神特烦恼
公众号:明神特烦恼

提案一般是共识流程中第一步,后面步骤为多阶段的投票,最终达到一致。这里分析提案将独立于共识,分析如何生成待共识的消息。提案的高度信息、提案人确认等由其他共识模块传入,这里不做分析。

带着问题读代码:
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 blockHeader *BlockHeader `protobuf:"bytes,1,opt,name=header,proto3" json:"header,omitempty"`// execution sequence of intra block transactions is generated by proposerDag *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 calculationAdditionalData *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 proposalslastProposedBlock map[int64]map[string]*blockProposalrwMu              sync.RWMutexchainConf         protocol.ChainConfledgerCache       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,该函数返回readBitmapwriteBitmap
    2)调用buildCumulativeBitmap函数,对读集和写集分别做叠加(或操作),例如:第一笔交易读集的bitmap为 1 0 1 1 0,其中每个bit位为1的位置表示一个读集的key。第二笔交易读集的bitmap为 0 1 0 1 0,那么经过叠加后的第二笔交易的读集为 1 1 1 1 0,后面依次对前面交易进行叠加,最终形成叠加后的读集与写集,cumulativeReadBitmapcumulativeWriteBitmap
    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 transactionVertexes []*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的使用在验证区块模块中,本章节主要介绍提案流程,这部分会在介绍区块验证流程时详细阐述。
关注作者,共同学习区块链技术。

长安链源码学习--提案(Proposer)(五)相关推荐

  1. 长安链源码学习v2.2.1--ioc机制(十)

    前面共同学习长安链ioc如何使用,下面聊聊IOC的实现原理.本节主要分析两个方法,Register.Resolve. 1.func (c *Container) Register(constructo ...

  2. Apollo 5.5 源码学习笔记(五) | transform模块 | Apollo中的坐标系统详解

    本系列博客旨在记录自己在学习百度无人驾驶开源框架Apollo的心得和体会,欢迎大家阅读和点赞,并提出宝贵意见,大家相互学习,如需转载,请注明出处,谢谢! 文章目录 1.前言 2.车辆传感器布局 3.传 ...

  3. Swoole源码学习记录(五)——锁和信号(二)

    Swoole版本:1.7.4-stable Github地址: https://github.com/LinkedDestiny/swoole-src-analysis 二.Mutex互斥锁 接下来是 ...

  4. 【Qt】通过QtCreator源码学习Qt(五):QLoggingCategory管理、分类、过滤打印信息

    1.QLoggingCategory简介 在QtCreator源码中新学到一个类QLoggingCategory. QLoggingCategory可以控制打印输出类别和区域.方便在调试时,过滤掉不关 ...

  5. vue.js源码学习分享(五)

    //配置项var config = {/*** Option merge strategies (used in core/util/options)//选项合并策略*/optionMergeStra ...

  6. Vuex 4源码学习笔记 - 通过dispatch一步步来掌握Vuex整个数据流(五)

    在上一篇笔记中:Vuex 4源码学习笔记 - Store 构造函数都干了什么(四) 我们通过查看Store 构造函数的源代码可以看到主要做了三件事情: 初始化一些内部变量以外 执行installMod ...

  7. TLD(Tracking-Learning-Detection)学习与源码理解之(五)

    TLD(Tracking-Learning-Detection)学习与源码理解之(五)   zouxy09@qq.com http://blog.csdn.net/zouxy09 下面是自己在看论文和 ...

  8. Vuex源码学习(五)加工后的module

    没有看过moduleCollection那可不行!Vuex源码学习(四)module与moduleCollection 感谢提出代码块和截图建议的小伙伴 代码块和截图的区别: 代码块部分希望大家按照我 ...

  9. Opencascade源码学习之模型数据

    Opencascade源码学习之模型数据 1.模型数据 2.几何工具 1.插值和拟合 1.分析一组点 2.基本插值和近似 3.2D 插值 4.3D 插值 5.2D 拟合 6.3D 拟合 7.曲面拟合 ...

最新文章

  1. Twitter能为你做什么?
  2. XenApp/XenDesktop 7.11中对于视频、图片、文字的优化的说明
  3. 非标准配置linux,剖析非标准波特率的设置和使用于Linux操作系统中
  4. JFrame 居中显示
  5. Eclipse中,多参列表光标跳转至第2个参数快捷键,及其他常用快捷键
  6. 一些Java面试题深入分析
  7. 韩国防部长会见美驻韩大使 或谈韩日舰机矛盾
  8. JVM整体架构与调优参数说明
  9. python中int input_关于python:如何接受int和float类型的输入?
  10. debian的甘特图工具
  11. 搭建dubbo客户端
  12. matlab中solver函数_matlab solve函数的用法
  13. Python求1+2+…+n
  14. java 检测u盘_Java简单U盘检测程序
  15. webView系列(五)----历史记录
  16. iOS touchID 处理办法
  17. 持续更新|有关最强绘画AI--Midjourney资料收集大全
  18. Windows桌面虚拟小猫Candy - From Neko
  19. 手机OTG 我的世界_怎么用手机把小说下载到mp3
  20. 网页背景图片拉伸效果

热门文章

  1. Java语言的基本介绍
  2. gmsl摄像头Android平台调试思路
  3. 大学web基础期末大作业~仿品优购商城页面制作(HTML+CSS+JavaScript)
  4. 金蝶插件常用引用dl
  5. java补空格_JAVA STRING 对象自动补齐空格
  6. 【AI测试】人工智能测试整体介绍——第六部分
  7. JDK8 下载与安装教程,超简单版(Windows)
  8. 安装打印机共享器(服务器)并用个人电脑连接进行打印
  9. Linux实训项目二 用户和组的管理
  10. Linux下thread编程