1、fabric gossip
Fabric通过组件化来分离各个实体,如节点和orderers,orderer提供了ordering服务,节点维持了账本和世界状态(world state),同时链码的执行是独立于ordering服务,这种设计的主要目的是为了显著提高可扩展性。但是这种设计就需要一种通信方式来保证各个节点间的消息传播是可信,可扩展的并且是支持拜赞庭容错。用任何中心化通信方式都不能解决可信分布式问题,所以在区块链中使用基于点对点的数据传播基础架构,这种架构更适合于区块链的动态化和分布式特性。
数据传播基础架构约定了在区块链网络中数据只能在它相关的管道里进行传输,不在这个管道的成员节点接收不到这个管道的数据,这种约定或是限制让Fabric可以支持多管道的传输方式,同时能保证整个系统即使在少量节点的情况下也可以运行良好。但是ordering服务和节点分离机制同样也带来了在拜赞庭环境下更为复杂的数据传播和验证问题。所以引入了Gossip,在Fabric中gossip的主要目的是:
- 使得在一个管道内的所有节点都有相同的账本,同时避免所有的节点都连接到ordering服务上,缓解ordering服务的压力
- 当有新的节点加入,同步账本的操作可以独立于ordering服务
在Fabric里,所有经过共识之后的有序交易序列需要通知给所有的节点,让这些节点更新节点状态和账本信息。这种情况下,就要去ordering服务和节点之间有连接,并且可以传播已排序的交易信息。当然不是所有的节点都会连接到ordering服务上,连接到ordering服务的节点是选举出来的,被选举出来的节点通过标准的Deliver协议和ordering服务连接。这些节点负责分发接收到的交易数据到其他各个节点上。每个联盟或是组织都会选择一个Leader节点,由Leader节点连接ordering服务,并传播接收到的交易信息。
上述的数据传播方案要在状态转换,同步机制上起到关键作用。首先,对于由于某些情况下没有收到完整的交易的节点,基于gossip的状态同步机制要能保证这些节点在条件允许的情况下可以同步节点缺失的交易数据。其次,为了支持当系统运行一段时间后有新的节点加入情况,系统要提供一个基于反熵的状态同步,通过它可以在节点之间传输大数据量。
多管道的支持
管道的创建是用来定义信息分享的范围,并与账本相关连。每个交易都关联到某个管道,这个管道明确的定义了哪些节点是可以接收同步这个交易的。当管道被创建后,客户端SDK可以指示属于组织内的哪些节点加入到新创建的管道内。这些刚加入的节点通过gossip在全组织内广播。
为了使得gossip在多管道内可以正常工作,需要管道内的所有节点维护这个管道内的成员关系,通俗的说,就是管道内的每个节点都需要知道管道内的其他所有节点。对于新加入的节点,ordering服务需要确保该节点确实属于这个组织。作为Leader节点,则有义务和责任把从ordering接受来的消息分发给其他节点。Gossip组件是在管道中的成员关系创建好后才工作,这样保证了通过gossip网络传播的每一笔交易都在特定的成员关系群中传播,而不会传播到其他管道去。为了实现上述功能,当一个节点加入到一个管道后,客户端SDK会为这个节点提供该管道的最新配置来识别有哪些节点也加入了这个管道。如果是一个新的管道,配置信息为创世纪块。
当一个组织从一个管道上移除,客户端sdk会发送配置交易的背书申请,当背书签名后,交易将发送给ordering服务。每个gossip组件都会把消息发给那些没有被移除的节点。一开始,节点自己是不知道是否被移除的,在一段时间内没有接收到从其他节点发来的alive消息,才会知道原来自己已经被移除了这个组织。
2、leader区块同步
一个节点被选中为leader节点后,会通过 StartDeliverForChannel 方法启动deliver,就可以向同一个组织里的其他节点gossip消息了,具体代码在 gossip_service.go中。
1.首先重新生成一个新的deliveryclient, 通过这个deliveryclient生成一个blockProvider (deliveryclient.go)
- 调用 blocksprovider.go中 DeliverBlocks 方法,会先验证一次区块,验证的内容主要包括:
- 区块序列号
- channelId是否一致
- Metadata 能否反序列化
- 块数据的hash值是否和header中存的hash值一致
通过channel policy来验证签名是否有效 (mcs.go)
- 通过gossip把块发出去
2.1 Leader节点的Gossip流程
Leader的gossip主流程在gossip_impl.go中实现。
- 通过 batcher.go的Add方法把数据放在 batchingEmitterImpl的buffer中,然后调用 Emit方法
- 通过源码可以看到, Emit方法最后调用的 **p.cb(msgs2beEmitted) **进行操作,那cb又是什么东东呢? cb其实是一个 emitBatchCallback ,是在 gossip_impl.go中NewGossipService就初始化好的
1 g.emitter = newBatchingEmitter(conf.PropagateIterations,
2 conf.MaxPropagationBurstSize,
3 conf.MaxPropagationBurstLatency,
4 g.sendGossipBatch)
看到这里,就应该清楚,cb最终的实现是在 g.sendGossipBatch 方法
3. 在sendGossioBatch里调用的GossipBatch方法,在这个方法里,首先会把消息进行分区,划分成多个区块(这里的区块不是用于提交的,里面的数据可能包含多个不同channel下的数据。这里的gossip数据包括三种:实际的区块,leasdership 消息和 状态信息),因为gossip服务是针对整个fabric系统的,所以在一段时间内,会有多条不同的数据同时存在。
4. 从这一批区块消息里获取到这些区块所在的channel,对每一个channel,从整个区块中抽取出这个channel下的所有消息,再次分区成多个区块(这里的区块是用于提交的,数据只包含了一个channel下的所有数据)
5. 获取这个channel下的所有可见成员peer,如果本节点是leader节点,就选取所有可见节点进行gossip发送消息。如果不是leader节点,就根据core.yaml里配置的 propagatePeerNum 个数来选择,默认是3个。
2.2非Leader节点的Gossip流程
同一个组织内的非leader节点,是接收leader节点发过来的gossip消息。本身不进行deliver消息
1 在NewGossipService 一开始就通过起一个goroutine,接收gossip过来的消息,在Start方法中。
2 收到gossip消息后,如果是leadership的消息,只需要验证消息有效性就可以,否则就通过gossipChannel 的HandleMessage 进行消息处理
3 首先会验证消息的有效性,然后判断发送该消息的节点所在组织是否在这个channel中
4. 判断消息类型
- 如果是stateInfoPullRequest 类型,就直接创建一个状态的快照信息返回;
- 如果是状态快照信息,就针对状态快照信息进行处理;
- 如果是区块信息,则先验证区块链有效性,验证的内容主要包括:区块序列号,channelId是否一致,Metadata 能否反序列化,块数据的hash值是否和header中存的hash值一致,通过channel policy来验证签名是否有效 (mcs.go)
5. 通过 DeMultiplex ,使得数据放入到 gossipChain中
6. 在 state.go中,NewGossipStateProvider 中就开始起了一个goroutine来监听gossipChain的数据变化,当有数据变化时候,就把消息 通过 queueNewMessage 方法把数据加入到payloads 中。
7. state.go 中的 deliverPayloads 通过获取 payload的ready状态,通过CommitBlock 方法把数据块提交到账本
这样一个块就完成了从leader接收后到同一个组织的其他节点的同步过程。
3、MSP
在区块链网络中用于颁发和验证证书和身份的一组加密机制和协议。是一个可插拔的接口。
理论上讲,可以通过以下组件来定义一个MSP:
- 身份格式,或是说证书,有时还带有一个产生身份的算法
- 一种签名算法,利用与身份相关的秘钥和消息,生成一组byte数组(实际上就是签名),这组byte数组也和该身份唯一绑定。
- 一种签名验证算法,算法的输入为身份,消息和签名(bytes数组),如果签名数组对于输入消息是一个有效的签名,那么输出是“接受”,否则就是“拒绝”
- 一组规则。对于MSP来说,满足这个规则的身份必须是一个有效的身份
- 一组admin身份集合,用于修改MSP指定的一些配置
一个区块链网络中可以管理一个或是多个MSP。对于很多MSP来说,前面三点是比较通用规则,4和5对于不同的MSP会有所变化。从这个角度看,Fabric-CA也算是一个MSP。而官网提供的first-network,没有用到Farbic-CA, 用的是localMSP。
3.1 使用MSP
这里列举一个节点侧的典型MSP场景。注意这里的节点在网络中不涉及到身份发布流程,只限制于客户端的背书请求,客户端身份验证和客户端身份签名验证三个过程。
在这个场景中,身份(id) 具有X.509证书格式,由一个明确的根CA颁发。根据证书秘钥,签名和签名验证算法都是基于 ECDSA或是RSA(取决于生成证书时候的设置的算法),身份的验证主要包含有:
- 通过可信的根CA,验证证书链是否正确
- 确保身份没有被吊销,这个可以通过在msp启动阶段配置吊销列表或是MSP身份白名单实现
3.2 节点签名能力
Orderer节点,peer节点都需要有签名能力,为了实现这个,节点的管理员要在节点设置的时候指定MSP的配置。由于此处包含的MSP实例仅用于实例化节点的签名标识,因此我们通过SignerMSP引用此MSP。签名者MSP是可以被管理员手动更改,所以可以在各个节点之间变化。如果需要设置签名者MSP,需要将下面四组文件复制到节点文件系统中的专用位置,四组文件为:
- Cacert – PEM文件,MSP的根证书
- Admincerts – PEM文件,MSP的管理员证书
- Keystore- 节点的签名私钥
- Signcerts – PEM文件,节点身份的编码证书,fabric有四种身份:user,peer, orderer,client
如下为peer相关证书文件对应的目录
|——org1.example.com 组织名称
|
|——-ca 组织的根证书
|
|——-msp 组织的msp
|
|——-peers 节点相关证书
|
|——-tlsca 组织内部的tlsca证书
|
|——-users 组织所属用户
3.3 链的参与者
链的创世块必现包含有链的参与者的MSP说明。如果一个MSP涵盖了多条链,那么需要维护每条链上的MSP状态。这是为了避免重新配置不一致攻击,所谓的重新配置不一致攻击是指通过每个组织的MSP的重新配置交易信息到达每个链的顺序不一致导致,这样就会出现在一定时间内,同一个管道内的节点MSP信息不一致情况。
我们一般定义一个MSP都是基于链或管道的上下文。这是为了orderer节点和peer节点可以对这条链上或是这条管道内的所有交易签名者,背书者,管道的广播和链或是管道创建,终止等请求进行认证。
特别是在orderer的系统管道中指定的MSP允许策略规范去管理以下请求:管道读请求(认证,验证管道的delivery请求),写请求(认证和验证管道广播请求),链的创建者(鉴定链的创建请求)和管理员(认证和验证管道配置请求)。可以看出,链中的MSP具有验证者身份,和本地提供的MSP提供的签名者形成对比。更具体的说,peer和orderer在系统管道和链的上下文设置MSP是为了认证交易和配置相关的请求,这里的MSP不提供生成签名的能力。
Peer和orderer节点可以验证由多个MSP颁发的身份签名。为了方便实现这点,fabric引入了MSP管理者概念。Fabric组件中的MSPManager接口可以在链设置阶段或是orderer管道启动时候被创建一个或是多个实例,使用这些MSP管理者来验证交易签名。MSPManager接口的引入给fabric带来了几个重要优势:
- 可以插拔的MSP
- 同时支持多个MSP提供者
-
隐藏了单一MSP和MSP架构内部复杂性
MSPManager 使用链的配置块信息来实例化MSP,如下图所示
msp
网友评论