0%

Fabric中PBFT源码解读(1)

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

I. 写在前面

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

A. 源码版本及获取

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

B. 对TestNetwork函数的测试

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

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

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

II. 构建测试网络

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

1
2
3
4
5
6
// [pbft-core_test.go]TestNetwork
func TestNetwork(t *testing.T) {
validatorCount := 4 // L258
net := makePBFTNetwork(validatorCount, nil) // L259
...
}
TestNetwork函数首先调用makePBFTNetwork函数生成了一个包含4个节点的PBFT网络,后者的代码主体如下。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// [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的具体定义如下所示。
1
2
3
4
5
6
7
8
9
// [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
}

III. 生成测试数据

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

1
2
3
4
5
6
7
// [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接口的实现类。
1
2
3
4
5
6
7
// [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函数返回该变量,相关代码如下所示:
1
2
3
4
5
6
7
8
9
10
// [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
}