Board logo

标题: 对使用 Go 为 Hyperledger Fabric v0.6 编写的区块链链代码进行单元测试(7) [打印本页]

作者: look_w    时间: 2018-6-18 13:48     标题: 对使用 Go 为 Hyperledger Fabric v0.6 编写的区块链链代码进行单元测试(7)

测试非确定性函数本教程系列的  已详细介绍,链代码必须是确定性的。下面将通过一个示例进行演示。以一个基于                4 对等节点 Hyperledger Fabric 的区块链网络为例,其中所有 4                个对等节点都是验证对等节点。这意味着只要有一个交易需要写入区块链中,所有 4 个对等节点都将在其本地账本副本上独立执行交易。简言之,4                个对等节点中的每个节点都将使用相同的输入独立执行同一个链代码函数,以便更新它们的本地账本状态。通过这种方式,所有 4                个对等节点最终将具有相同的账本状态。
因此,对等节点对链代码的所有 4 次执行都必须获得相同的结果,从而使它们最终获得相同的账本状态。这被称为确定性链代码。
清单 23 演示了 CreateLoanApplication                函数的一个非确定性版本。这意味着,如果使用相同输入多次执行此函数,将会得到不同的结果。
清单 23. CreateLoanApplication                函数的一个非确定性版本
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
func NonDeterministicFunction(stub shim.ChaincodeStubInterface, args []string) ([]byte, error) {
    fmt.Println("Entering NonDeterministicFunction")
    //Use random number generator to generate the ID
    var random = rand.New(rand.NewSource(time.Now().UnixNano()))
    var loanApplicationID = "la1" + strconv.Itoa(random.Intn(1000))
    var loanApplication = args[0]
    var la LoanApplication
    err := json.Unmarshal([]byte(loanApplication), &la)
    if err != nil {
        fmt.Println("Could not unmarshal loan application", err)
        return nil, err
    }
    la.ID = loanApplicationID
    laBytes, err := json.Marshal(&la)
    if err != nil {
        fmt.Println("Could not marshal loan application", err)
        return nil, err
    }
    err = stub.PutState(loanApplicationID, laBytes)
    if err != nil {
        fmt.Println("Could not save loan application to ledger", err)
        return nil, err
    }

    fmt.Println("Successfully saved loan application")
    return []byte(loanApplicationID), nil
}




不同于传入贷款申请 ID 作为输入的原始 CreateLoanApplication                方法,上面的方法使用一个随机数生成器生成该 ID,并将它附加到传入的贷款申请内容中。第 4 和第 5 行演示了如何生成贷款申请 ID。第 19                行将更新后的贷款申请内容存储到账本上。
清单 24 展示了如何测试某个方法是否是非确定性的。
清单 24. 测试某个方法是否是非确定性的
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
func TestNonDeterministicFunction(t *testing.T) {
    fmt.Println("Entering TestNonDeterministicFunction")
    attributes := make(map[string][]byte)
    const peerSize = 4
    var stubs [peerSize]*shim.CustomMockStub
    var responses [peerSize][]byte
    var loanApplicationCustom = `{"propertyId":"prop1","landId":"land1","permitId":"permit1","buyerId":"vojha24","personalInfo":{"firstname":"Varun","lastname":"Ojha","dob":"dob","email":"varun@gmail.com","mobile":"99999999"},"financialInfo":{"monthlySalary":16000,"otherExpenditure":0,"monthlyRent":4150,"monthlyLoanPayment":4000},"status":"Submitted","requestedAmount":40000,"fairMarketValue":58000,"approvedAmount":40000,"reviewedBy":"bond","lastModifiedDate":"21/09/2016 2:30pm"}`
    //Simulate execution of the chaincode function by multiple peers on their local ledgers
    for j := 0; j < peerSize; j++ {
        stub := shim.NewCustomMockStub("mockStub", new(SampleChaincode), attributes)
        if stub == nil {
            t.Fatalf("MockStub creation failed")
        }
        stub.MockTransactionStart("tx" + string(j))
        resp, err := NonDeterministicFunction(stub, []string{loanApplicationCustom})
        if err != nil {
            t.Fatalf("Could not execute NonDeterministicFunction ")
        }
        stub.MockTransactionEnd("tx" + string(j))
        stubs[j] = stub
        responses[j] = resp
    }

    for i := 0; i < peerSize; i++ {
        if i < (peerSize - 1) {
            la1Bytes, _ := stubs.GetState(string(responses))
            la2Bytes, _ := stubs[i+1].GetState(string(responses[i+1]))
            la1 := string(la1Bytes)
            la2 := string(la2Bytes)
            if la1 != la2 {
                //TODO: Compare individual values to find mismatch
                t.Fatalf("Expected all loan applications to be identical. Non Deterministic chaincode error")
            }
        }
        //All loan applications retrieved from each of the peer's ledger's match. Function is deterministic

    }

}




第 4 行定义了我们想模拟的验证对等节点数量。
第 6 行创建了与验证对等节点大小匹配的桩代码。每个桩代码都将用于执行链代码函数,并更新其账本状态。
第 9 到第 22 行使用了之前创建的桩代码,使用相同的输入参数来执行该链代码函数,以便模拟验证对等节点在实际场景中将如何执行链代码函数。
第 21 行存储对链代码函数的每次执行的响应。在本例中,调用的函数名为                NonDeterministicFunction,它将返回存储在账本上的贷款申请 ID。
第 25 到第 38 行使用之前创建的桩代码和链代码函数的单独执行所返回的贷款申请 ID,以便从各个账本检索贷款申请并比较它们是否相同。
对于确定性函数,这些贷款申请应该是相同的。
现在使用 go test                运行测试。跟预期一样,TestNonDeterministicFunction 测试将会失败。
因为 NonDeterministicFunction 使用随机数生成器来生成贷款申请                ID,所以对此函数的多次调用将获得不同的                ID。因此,在将贷款申请最终保存到各个对等账本时,贷款申请内容将会有所不同,并导致各个验证对等节点的账本状态不一致。




欢迎光临 电子技术论坛_中国专业的电子工程师学习交流社区-中电网技术论坛 (http://bbs.eccn.com/) Powered by Discuz! 7.0.0