Fabric中PBFT源码解读 (1)

1. 写在前面

笔者一直以来想学习一下PBFT的完整工程实践。但在网上搜了很多源码和博客,都没能找到一份比较独立且完整的PBFT实现。无奈之下,只能从Hyperledger Fabric的代码中学习其PBFT共识部分的代码。
因此,接下来的系列博客中,笔者将介绍在Fabric中是如何实现PBFT共识算法的。

为方便读者查阅,本系列博客的链接整理如下:

1.1 源码版本及获取

Fabric0.6版本中采用了PBFT,因此在从github中下载了源代码后,需要首先切换到v0.6.0-preview分支。PBFT共识相关的代码主要包含在fabric/consensusfabric/consensus/pbft路径下。前者包含了各种共识算法都需要的一些工具类,后者重点实现了PBFT共识。

1.2 对TestNetwork函数的测试

查看fabric/consensus/pbft路径下的代码,可以看到有很多测试文件。每个测试文件中又包含了若干个测试函数。这里我们选取了pbft-core_test.go文件中的TestNetwork测试函数。通过解读该函数,基本上可以了解PBFT代码的运行流程。

为降低流程分析的复杂度,首先将pbft-core_test.go中L258行的validator值改为4。如下所示:

// [pbft-core_test.go]TestNetwork
func TestNetwork(t *testing.T) {
	validatorCount := 4
	...
}

2. 构建测试网络

以下从TestNetwork函数开始源码的解读。TestNetwork函数的主体代码如下所示:

// [pbft-core_test.go]TestNetwork
func TestNetwork(t *testing.T) {
    validatorCount := 4                             // L258
    net := makePBFTNetwork(validatorCount, nil)       // L259
    ...
}

TestNetwork函数首先调用makePBFTNetwork函数生成了一个包含4个节点的PBFT网络,后者的代码主体如下。

// [pbft-core_test.go]TestNetwork -> [pbft-core_mock_test.go]makePBFTNetwork
func makePBFTNetwork(N int, config *viper.Viper) *pbftNetwork { // L152
    ...
    endpointFunc := func(id uint64, net *testnet) endpoint { // L159
        tep := makeTestEndpoint(id, net)
        pe := &pbftEndpoint{
			testEndpoint: tep,
			manager:      events.NewManagerImpl(),			// L162
		}
        ...
        pe.pbft = newPbftCore(id, config, pe.sc, events.NewTimerFactoryImpl(pe.manager))                     // L170
        ...
    }
    
    pn := &pbftNetwork{testnet: makeTestnet(N, endpointFunc)}   // L179
}

makePBFTNetwork函数L159行首先定义了一个用于生成endpoint的函数,其中每个endpoint代表了一个节点。endpoint其实是一个接口,为进行测试,源码中实现了一个testEndpoint类。L160行利用makeTestEndpoint函数生成了testEndpoint类的实例。
为实现pbft的测试,还在pbft-core_mock_test.go文件的L29行定义了一个辅助类pbftEndpoint。该辅助类将一个节点的多个功能部分进行了组合。
节点的核心功能由pbftCore类实现,因此在L170行调用newPbftCore函数生成了pbftCore类的实例。
需要注意的是,L519行只是定义了生成endpoint的函数,该函数在L179行传入到makeTestnet函数中进行了调用。makeTestnet函数生成了代表当前测试网络的testnet类的一个实例。该实例包含了若干个endpoint,定义了一个msgs变量和一个filterFn函数。testnet的具体定义如下所示。

// [pbft-core_mock_test.go] TestNetwork -> [pbft-core_mock_test.go]makePBFTNetwork -> testnet
type testnet struct {
    debug     bool
	N         int
	closed    chan struct{}
	endpoints []endpoint
	msgs      chan taggedMsg
	filterFn  func(int, int, []byte) []byte
}

3. 生成测试数据

回到TestNetwork函数,在生成测试网络后,该函数进一步生成了测试数据,并将其传入到节点0(即Primary节点)中。TestNetwork函数的相关代码如下所示:

// [pbft-core_test.go]TestNetwork
func TestNetwork(t *testing.T) {
    ...
    reqBatch := createPbftReqBatch(1, uint64(generateBroadcaster(validatorCount)))          // L261
    net.pbftEndpoints[0].manager.Queue() <- reqBatch  // L262
    ...
}

TestNetwork函数的L261行调用createPbftReqBatch函数生成类为RequestBatch的测试数据。该测试数据在L262行被发送到第0个节点中。由前面的介绍可知,net的第0个pbftEndpoint即代表第0个节点。
pbftEndpoint类的定义如下。pbftEndpoint类中定义了一个Manager类别的变量,Manager实际上是一个接口。由makePBFTNetwork函数的L162行可知,该变量实际上实例化了一个managerImpl类。后者是Manager接口的实现类。

// [pbft-core_test.go]TestNetwork -> [pbft-core_mock_test.go]pbftEndpoint
type pbftEndpoint struct {                      // L29
	*testEndpoint
	pbft    *pbftCore
	sc      *simpleConsumer
	manager events.Manager
}

managerImpl类中定义了类别为chan Eventevents变量,并定义Queue函数返回该变量,相关代码如下所示:

// [pbft-core_test.go]TestNetwork -> [pbft-core_mock_test.go]pbftEndpoint -> [events.go]managerImpl
type managerImpl struct {                   // L80
	threaded  
	receiver Receiver
	events   chan Event
}

func (em *managerImpl) Queue() chan<- Event {   // L105
	return em.events
}
### 使用Hyperledger Fabric搭建带有PBFT共识机制的私有区块链教程 #### 一、环境准备 为了设置一个基于PBFT (Practical Byzantine Fault Tolerance) 共识算法Hyperledger Fabric网络,首先需要安装并配置好必要的开发工具和依赖项。这包括但不限于Docker、Docker Compose以及Go语言环境等基础组件。 对于Hyperledger Fabric本身而言,则需下载对应版本的二进制文件与镜像资源[^3]。 ```bash export VERSION=2.4.8 curl -sSL https://bit.ly/2ysbOFE | bash -s $VERSION docker pull hyperledger/fabric-peer:$VERSION docker pull hyperledger/fabric-orderer:$VERSION ... ``` #### 二、创建通道及加入节点 定义网络拓扑结构之后,在启动任何服务之前应该先建立联盟链中的各个组织之间的通信渠道——即“通道”。通过`configtxgen`命令行工具来生成创世区块,并让不同成员机构下的peer节点参与到特定频道之中去。 值得注意的是,默认情况下Ordering Service采用Kafka作为消息队列实现最终一致性;然而要启用PBFT的话则必须调整此部分配置以适应拜占庭容错需求。 #### 三、修改配置文件支持PBFT 编辑`orderer.yaml`文件内的Consensus Type字段为etcdraft(尽管官方文档中并没有直接提到pbft选项),因为当前Fabric社区推荐使用Raft替代原有的Solo/Kafka模式来进行强一致性的排序服务部署。不过在某些特殊场景下也可以尝试自定义插件方式集成第三方提供的PBFT解决方案。 另外还需注意调整其他参数比如BatchTimeout/BatchSize等影响性能表现的关键属性值。 #### 四、编写智能合约逻辑 利用Chaincode SDK或者CLI接口完成业务功能编码工作,将其打包成tarball格式上传至指定路径供后续调用执行操作之用。这里建议开发者们遵循最佳实践指南确保代码质量可靠稳定。 ```go // Example.go package main import ( "fmt" "github.com/hyperledger/fabric-chaincode-go/shim" ) type SimpleChaincode struct{} func (t *SimpleChaincode) Init(stub shim.ChaincodeStubInterface) peer.Response { fmt.Println("Init ChainCode") return shim.Success(nil) } func (t *SimpleChaincode) Invoke(stub shim.ChaincodeStubInterface) peer.Response { function, args := stub.GetFunctionAndParameters() if function == "invoke" { // Perform the invoke operation } else if function == "query" { // Perform query operations here. } return shim.Error("Invalid invoke function name.") } ```
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值