HyperledgerFabric啟用CouchDB為狀態(tài)數(shù)據(jù)庫

Hyperledger Fabric 啟用CouchDB作為狀態(tài)數(shù)據(jù)庫

創(chuàng)新互聯(lián)建站是一家專注于成都做網(wǎng)站、成都網(wǎng)站建設、成都外貿(mào)網(wǎng)站建設和多線BGP機房的網(wǎng)絡公司,有著豐富的建站經(jīng)驗和案例。

一.概述

1. 數(shù)據(jù)請求流

超級賬本采用背書/共識模型,模擬執(zhí)行和區(qū)塊驗證是在不同角色的節(jié)點中分開執(zhí)行的。模擬執(zhí)行是并發(fā)的,這樣可以提高擴展性和吞吐量:

  • 背書節(jié)點:模擬執(zhí)行鏈碼
  • Peer節(jié)點:驗證交易并提交

Hyperledger Fabric啟用CouchDB為狀態(tài)數(shù)據(jù)庫

2.超級賬本存儲元素

超級賬本包含以下元素:

  • 賬本編號:快速查詢存在哪些賬本
  • 賬本數(shù)據(jù): 實際的區(qū)塊數(shù)據(jù)存儲
  • 區(qū)塊索引: 快速查詢區(qū)塊/交易
  • 狀態(tài)數(shù)據(jù): 最新的世界狀態(tài)數(shù)據(jù)
  • 歷史數(shù)據(jù): 跟蹤鍵的歷史

每個Peer節(jié)點會維護四個DB,分別為:

  • 賬本索引庫(IdStore):存儲ChainID
  • 狀態(tài)數(shù)據(jù)庫(StateDB): 存儲world state
  • 歷史數(shù)據(jù)庫(HistoryDB): 存儲Key的版本變化
  • 區(qū)塊索引庫(BlockIndex):存儲Block索引

Hyperledger Fabric啟用CouchDB為狀態(tài)數(shù)據(jù)庫

3.狀態(tài)數(shù)據(jù)庫

狀態(tài)數(shù)據(jù)庫可選類型包括LevelDB和CouchDB。LevelDB是嵌入在peer進程中的默認鍵/值狀態(tài)數(shù)據(jù)庫,CouchDB是一個可選的外部狀態(tài)數(shù)據(jù)庫。與LevelDB鍵/值存儲一樣,CouchDB可以存儲任何以chaincode建模的二進制數(shù)據(jù)(CouchDB附件函數(shù)在內(nèi)部用于非json二進制數(shù)據(jù))。但是,當chaincode值(例如,資產(chǎn))被建模為JSON數(shù)據(jù)時,作為JSON文檔存儲,CouchDB支持對chaincode數(shù)據(jù)進行豐富的查詢。

LevelDB和CouchDB都支持核心chaincode操作,例如獲取和設置一個鍵(資產(chǎn)),并根據(jù)鍵進行查詢。鍵可以通過范圍查詢,可以對組合鍵進行建模,以支持針對多個參數(shù)的等價查詢。例如,作為所有者的組合鍵,資產(chǎn)id可以用于查詢某個實體擁有的所有資產(chǎn)。這些基于key的查詢可以用于針對賬本的只讀查詢,以及更新總賬的事務。

如果將資產(chǎn)建模為JSON并使用CouchDB,那么就可以使用chaincode中的CouchDB JSON查詢語言對chaincode數(shù)據(jù)值執(zhí)行復雜的富查詢,這些類型的查詢對于理解賬本上的內(nèi)容很有幫助。對于這些類型的查詢,事務協(xié)議響應通常對客戶端應用程序有用,但通常不會作為事務提交到排序服務。事實上,也無法保證結果集在chaincode執(zhí)行與富查詢提交時間之間的穩(wěn)定性,因此使用富查詢的結果去執(zhí)行最終的事務更新操作是不合適的,除非可以保證結果集在chaincode執(zhí)行時間與提交時間之間的穩(wěn)定性,或者可以處理在后續(xù)交易中的潛在變化。例如,如果對Alice所擁有的所有資產(chǎn)執(zhí)行一個富查詢并將其傳輸給Bob,那么一個新的資產(chǎn)可能會被另一個事務分配給Alice,這是在chaincode執(zhí)行時間和提交時間之間的另一個事務,可能此過程中會錯過這個“虛值”。

CouchDB作為一個獨立的數(shù)據(jù)庫進程與peer一起運行,因此在設置、管理和操作方面有額外的考慮。我們可以考慮從默認的嵌入式LevelDB開始,如果需要額外的復雜的富查詢,可以轉移到CouchDB。將chaincode資產(chǎn)數(shù)據(jù)建模為JSON是一種很好的做法,這樣我們就可以在將來執(zhí)行需要的復雜的富查詢。

二. 啟用CouchDB

本文均采用Hyperledger Fabric1.2中fabric-samples中相關組件與資源,在測試環(huán)境(fabric-samples/chaincode-docker-devmode)通過Docker啟動CouchDB服務

1.配置CouchDB啟動信息

參考:fabric-samples/first-network/docker-compose-couch.yaml

  couchdb0:
    container_name: couchdb0
    image: hyperledger/fabric-couchdb
    # Populate the COUCHDB_USER and COUCHDB_PASSWORD to set an admin user and password
    # for CouchDB.  This will prevent CouchDB from operating in an "Admin Party" mode.
    environment:
      - COUCHDB_USER=
      - COUCHDB_PASSWORD=
    # Comment/Uncomment the port mapping if you want to hide/expose the CouchDB service,
    # for example map it to utilize Fauxton User Interface in dev environments.
    ports:
      - "5984:5984"
    networks:
      - byfn

修改:fabric-samples/chaincode-docker-devmode/docker-compose-simple.yaml 末尾添加并修改

  couchdb:
    container_name: couchdb
    image: hyperledger/fabric-couchdb
    # Populate the COUCHDB_USER and COUCHDB_PASSWORD to set an admin user and password
    # for CouchDB.  This will prevent CouchDB from operating in an "Admin Party" mode.
    environment:
      - COUCHDB_USER=
      - COUCHDB_PASSWORD=
    # Comment/Uncomment the port mapping if you want to hide/expose the CouchDB service,
    # for example map it to utilize Fauxton User Interface in dev environments.
    ports:
      - "5984:5984"
2.配置CouchDB連接信息

參考fabric-samples/first-network/docker-compose-couch.yaml

  peer0.org1.example.com:
    environment:
      - CORE_LEDGER_STATE_STATEDATABASE=CouchDB
      - CORE_LEDGER_STATE_COUCHDBCONFIG_COUCHDBADDRESS=couchdb0:5984
      # The CORE_LEDGER_STATE_COUCHDBCONFIG_USERNAME and CORE_LEDGER_STATE_COUCHDBCONFIG_PASSWORD
      # provide the credentials for ledger to connect to CouchDB.  The username and password must
      # match the username and password set for the associated CouchDB.
      - CORE_LEDGER_STATE_COUCHDBCONFIG_USERNAME=
      - CORE_LEDGER_STATE_COUCHDBCONFIG_PASSWORD=
    depends_on:
      - couchdb0

修改:fabric-samples/chaincode-docker-devmode/docker-compose-simple.yaml 中peer模塊

修改前

  peer:
    container_name: peer
    image: hyperledger/fabric-peer
    environment:
      - CORE_PEER_ID=peer
      - CORE_PEER_ADDRESS=peer:7051
      - CORE_PEER_GOSSIP_EXTERNALENDPOINT=peer:7051
      - CORE_PEER_LOCALMSPID=DEFAULT
      - CORE_VM_ENDPOINT=unix:///host/var/run/docker.sock
      - CORE_LOGGING_LEVEL=DEBUG
      - CORE_PEER_MSPCONFIGPATH=/etc/hyperledger/msp
    volumes:
        - /var/run/:/host/var/run/
        - ./msp:/etc/hyperledger/msp
    working_dir: /opt/gopath/src/github.com/hyperledger/fabric/peer
    command: peer node start --peer-chaincodedev=true -o orderer:7050
    ports:
      - 7051:7051
      - 7053:7053
    depends_on:
      - orderer

修改后

  peer:
    container_name: peer
    image: hyperledger/fabric-peer
    environment:
      - CORE_PEER_ID=peer
      - CORE_PEER_ADDRESS=peer:7051
      - CORE_PEER_GOSSIP_EXTERNALENDPOINT=peer:7051
      - CORE_PEER_LOCALMSPID=DEFAULT
      - CORE_VM_ENDPOINT=unix:///host/var/run/docker.sock
      - CORE_LOGGING_LEVEL=DEBUG
      - CORE_PEER_MSPCONFIGPATH=/etc/hyperledger/msp
      - CORE_LEDGER_STATE_STATEDATABASE=CouchDB
      - CORE_LEDGER_STATE_COUCHDBCONFIG_COUCHDBADDRESS=couchdb:5984
      - CORE_LEDGER_STATE_COUCHDBCONFIG_USERNAME=
      - CORE_LEDGER_STATE_COUCHDBCONFIG_PASSWORD=

    volumes:
        - /var/run/:/host/var/run/
        - ./msp:/etc/hyperledger/msp
    working_dir: /opt/gopath/src/github.com/hyperledger/fabric/peer
    command: peer node start --peer-chaincodedev=true -o orderer:7050
    ports:
      - 7051:7051
      - 7053:7053
    depends_on:
      - orderer
      - couchdb

注意JSON文件的格式以及配置信息的一致性,如couchdb名稱等

3.啟動測試環(huán)境
# docker-compose  -f docker-compose-simple.yaml  up -d
# docker container ls

Hyperledger Fabric啟用CouchDB為狀態(tài)數(shù)據(jù)庫

三.編寫鏈碼

1.代碼結構

代碼包:testdb

代碼文件

  • domain.go //數(shù)據(jù)結構代碼
  • main.go //業(yè)務測試代碼
2.數(shù)據(jù)結構
package main

type BillStruct struct {
    ObjectType   string `json:"DocType"`      //對象類型定義
    BillInfoID   string `json:"BillInfoID"`   //票據(jù)ID
    BillInfoAmt  string `json:"BillInfoAmt"`  //票據(jù)金額
    BillInfoType string `json:"BillInfoType"` //票據(jù)類型
    BillIsseData string `json:"BillIsseData"` //出票日期
    BillDueDate  string `json:"BillDueDate"`  //到期日期

    HoldrAcct       string `json:"HoldrAcct"`       //持票人名稱
    HoldrCmID       string `json:"HoldrCmID"`       //持票人ID
    WaitEndroseAcct string `json:"WaitEndroseAcct"` //待背書人名稱
    WaitEndorseCmID string `json:"WaitEndorseCmID"` //待背書人ID
}
3.測試代碼

請仔細閱讀注釋信息,此處不做代碼分割描述

package main

import (
    "github.com/hyperledger/fabric/core/chaincode/shim"
    "fmt"
    "github.com/hyperledger/fabric/protos/peer"
    "encoding/json"
    "bytes"
)

//定義結構體CouchDBChaincode,作為shim.ChaincodeStubInterface實現(xiàn)類對象
type CouchDBChaincode struct {
}

//重寫shim.ChaincodeStubInterface接口的Init方法
func (t *CouchDBChaincode) Init(stub shim.ChaincodeStubInterface) peer.Response {
    return shim.Success(nil)
}

//重寫shim.ChaincodeStubInterface接口的Invoke方法
func (t *CouchDBChaincode) Invoke(stub shim.ChaincodeStubInterface) peer.Response {
    //獲取用戶意圖與參數(shù)
    fun, args := stub.GetFunctionAndParameters()
    //根據(jù)用戶意圖判斷使用何種實現(xiàn)函數(shù)
    if fun == "billInit" {
        return billInit(stub)
    } else if fun == "queryBills" {
        return queryBills(stub, args)
    } else if fun == "queryWaitBills" {
        return queryWaitBills(stub, args)
    }
    //如果用戶意圖不符合如上,進行錯誤提示
    return shim.Error("非法操作,指定的函數(shù)名無效")
}

//billInit函數(shù):初始化票據(jù)數(shù)據(jù)
func billInit(stub shim.ChaincodeStubInterface) peer.Response {

    /*
定義第一個票據(jù):
持票人名稱:AAA
持票人ID:AID
待背書人名稱:無
待背書人ID:無
     */

    billA := BillStruct{
        ObjectType:      "billObj",
        BillInfoID:      "POC001",
        BillInfoAmt:     "1000",
        BillInfoType:    "111",
        BillIsseData:    "20180501",
        BillDueDate:     "20180508",
        HoldrAcct:       "AAA",
        HoldrCmID:       "AID",
        WaitEndroseAcct: "",
        WaitEndorseCmID: "",
    }
    //通過json.Marshal方法對票據(jù)進行序列化操作
    billAByte, _ := json.Marshal(billA)
    //通過stub.PutState方法存儲序列化后的字節(jié)數(shù)組
    err := stub.PutState(billA.BillInfoID, billAByte)
    if err != nil {
        return shim.Error("初始化第一個票據(jù)失敗:" + err.Error())
    }

    billB := BillStruct{
        ObjectType:      "billObj",
        BillInfoID:      "POC002",
        BillInfoAmt:     "1000",
        BillInfoType:    "111",
        BillIsseData:    "20180501",
        BillDueDate:     "20180508",
        HoldrAcct:       "AAA",
        HoldrCmID:       "AID",
        WaitEndroseAcct: "BBB",
        WaitEndorseCmID: "BID",
    }
    billBByte, _ := json.Marshal(billB)
    err = stub.PutState(billB.BillInfoID, billBByte)
    if err != nil {
        return shim.Error("初始化第二個票據(jù)失敗:" + err.Error())
    }

    billC := BillStruct{
        ObjectType:      "billObj",
        BillInfoID:      "POC003",
        BillInfoAmt:     "1000",
        BillInfoType:    "111",
        BillIsseData:    "20180501",
        BillDueDate:     "20180508",
        HoldrAcct:       "BBB",
        HoldrCmID:       "BID",
        WaitEndroseAcct: "CCC",
        WaitEndorseCmID: "CID",
    }

    billCByte, _ := json.Marshal(billC)
    err = stub.PutState(billC.BillInfoID, billCByte)
    if err != nil {
        return shim.Error("初始化第三個票據(jù)失敗:" + err.Error())
    }

    billD := BillStruct{
        ObjectType:      "billObj",
        BillInfoID:      "POC004",
        BillInfoAmt:     "1000",
        BillInfoType:    "111",
        BillIsseData:    "20180501",
        BillDueDate:     "20180508",
        HoldrAcct:       "CCC",
        HoldrCmID:       "CID",
        WaitEndroseAcct: "BBB",
        WaitEndorseCmID: "BID",
    }

    billDByte, _ := json.Marshal(billD)
    err = stub.PutState(billD.BillInfoID, billDByte)
    if err != nil {
        return shim.Error("初始化第四個票據(jù)失敗:" + err.Error())
    }

    return shim.Success([]byte("所有票據(jù)初始化成功"))

}

//queryBills函數(shù):批量查詢指定用戶的持票列表
func queryBills(stub shim.ChaincodeStubInterface, args []string) peer.Response {
    //判斷是否有參數(shù)傳入
    if len(args) != 1 {
        return shim.Error("必須指定持票人的證件號碼")
    }
    //將第一個參數(shù)作為用戶ID
    holdrCmID := args[0]

    /*將CouchDB查詢字符串拼接成一個JSON串,格式如下:
        {
        "selector": {
            "docType": "billObj",
            "HoldrCmID": "%s"
        }
    }
    */
    queryString := fmt.Sprintf("{\"selector\":{\"DocType\":\"billObj\",\"HoldrCmID\":\"%s\"}}", holdrCmID)
    //通過自定義的getBillByQueryString函數(shù)進行數(shù)據(jù)查詢操作
    result, err := getBillByQueryString(stub, queryString)
    if err != nil {
        return shim.Error("根據(jù)持票人的證件號碼批量查詢持票人持有票據(jù)列表時發(fā)生錯誤" + err.Error())
    }
    return shim.Success(result)

}

//queryWaitBills函數(shù):批量查詢指定用戶的待背書票據(jù)列表
func queryWaitBills(stub shim.ChaincodeStubInterface, args []string) peer.Response {
    if len(args) != 1 {
        return shim.Error("必須指定待背書人的證件號碼")
    }
    waitEndorseCmID := args[0]

    queryString := fmt.Sprintf("{\"selector\":{\"docType\":\"billObj\",\"WaitEndorseCmID\":\"%s\"}}", waitEndorseCmID)
    result, err := getBillByQueryString(stub, queryString)

    if err != nil {
        return shim.Error("根據(jù)待背書人的證件號碼批量查詢待背書票據(jù)列表時發(fā)生錯誤" + err.Error())
    }
    return shim.Success(result)
}

//自定義函數(shù):getBillByQueryString:根據(jù)指定的查詢字符串(CouchDB查詢語句)查詢數(shù)據(jù)
func getBillByQueryString(stub shim.ChaincodeStubInterface, queryString string) ([]byte, error) {
    //通過stub.GetQueryResult方法獲取迭代器iterator
    iterator, err := stub.GetQueryResult(queryString)
    if err != nil {
        return nil, err
    }
    //延遲關閉迭代器iterator
    defer iterator.Close()
    //定義字節(jié)緩沖變量
    var buffer bytes.Buffer
    //定義分割符
    var isSplit bool
    //對迭代器進行遍歷操作
    for iterator.HasNext() {
        //通過迭代器的Next()方法獲取下一個對象的Key與Value值(*queryresult.KV)
        result, err := iterator.Next()
        if err != nil {
            return nil, err
        }

        if isSplit {
            buffer.WriteString(";")
        }
        //定義格式
        // key:result.key result.Value
        buffer.WriteString("key:")
        buffer.WriteString(result.Key)
        buffer.WriteString(",value:")
        buffer.WriteString(string(result.Value))
        //獲取到第一個值后,將isSplit設置為true,用于跟第二個值進行分割
        isSplit = true

    }
    //返回buffer對象的字節(jié)類型
    return buffer.Bytes(), nil
}

func main() {
    //啟動鏈碼CouchDBChaincode
    err := shim.Start(new(CouchDBChaincode))
    //如有報錯,提示報錯信息
    if err != nil {
        fmt.Errorf(err.Error())
    }

}

四.安裝鏈碼

1.上傳鏈碼

上傳鏈碼包testdb至:fabric-samples/chaincode中

# ls /home/bruce/hyfa/fabric-samples/chaincode/testdb/
 domain.go  main.go
2.編譯鏈碼
# cd  /home/bruce/hyfa/fabric-samples/chaincode/testdb/
# go build 
# ls 
domain.go  main.go  testdb
3.啟動鏈碼

進入chaincode容器進行操作

# docker container exec -it chaincode bash #進入chaincode容器進行操作
# cd testdb/
# CORE_PEER_ADDRESS=peer:7052 CORE_CHAINCODE_ID_NAME=testCouchDB:1.0  ./testdb

2018-08-05 10:33:37.063 UTC [shim] SetupChaincodeLogging -> INFO 001 Chaincode log level not provided; defaulting to: INFO
2018-08-05 10:33:37.063 UTC [shim] SetupChaincodeLogging -> INFO 002 Chaincode (build level: ) starting up ...
4.安裝與實例化鏈碼

進入cli容器進行操作

# docker container exec -it cli bash
# peer chaincode install -n testCouchDB -v 1.0 -p chaincodedev/chaincode/testdb
# peer chaincode instantiate -n testCouchDB -v 1.0 -C myc -c '{"Args":["init"]}'
如有更新請用如下命令進行操作
# peer chaincode install -n testCouchDB -v 1.1 -p chaincodedev/chaincode/testdb
# peer chaincode upgrade -n testCouchDB -v 1.1 -C myc -c '{"Args":["init"]}'

五.測試鏈碼

1.初始化票據(jù)
# peer chaincode  invoke  -n testCouchDB -C myc -c '{"Args":["billInit"]}'
2.查詢指定用戶所持票據(jù)
# peer chaincode  query  -n testCouchDB -C myc -c '{"Args":["queryBills","AID"]}'

Hyperledger Fabric啟用CouchDB為狀態(tài)數(shù)據(jù)庫

key: POC001, value: {
    "BillDueDate": "20180508",
    "BillInfoAmt": "1000",
    "BillInfoID": "POC001",
    "BillInfoType": "111",
    "BillIsseData": "20180501",
    "HoldrAcct": "AAA",
    "HoldrCmID": "AID",
    "WaitEndorseCmID": "",
    "WaitEndroseAcct": "",
    "docType": "billObj"
};
key: POC002, value: {
    "BillDueDate": "20180508",
    "BillInfoAmt": "1000",
    "BillInfoID": "POC002",
    "BillInfoType": "111",
    "BillIsseData": "20180501",
    "HoldrAcct": "AAA",
    "HoldrCmID": "AID",
    "WaitEndorseCmID": "BID",
    "WaitEndroseAcct": "BBB",
    "docType": "billObj"
}

查詢結果可以看到我們定義的分隔符;

3.查詢指定用戶待背書票據(jù)
# peer chaincode  query  -n testCouchDB -C myc -c '{"Args":["queryWaitBills","BID"]}'

Hyperledger Fabric啟用CouchDB為狀態(tài)數(shù)據(jù)庫

key: POC002, value: {
    "BillDueDate": "20180508",
    "BillInfoAmt": "1000",
    "BillInfoID": "POC002",
    "BillInfoType": "111",
    "BillIsseData": "20180501",
    "HoldrAcct": "AAA",
    "HoldrCmID": "AID",
    "WaitEndorseCmID": "BID",
    "WaitEndroseAcct": "BBB",
    "docType": "billObj"
};
key: POC004, value: {
    "BillDueDate": "20180508",
    "BillInfoAmt": "1000",
    "BillInfoID": "POC004",
    "BillInfoType": "111",
    "BillIsseData": "20180501",
    "HoldrAcct": "CCC",
    "HoldrCmID": "CID",
    "WaitEndorseCmID": "BID",
    "WaitEndroseAcct": "BBB",
    "docType": "billObj"
}

另外關于LevelDB,CouchDB還是MongoDB,今后可能隨著Hyperledger Fabric的版本變化而采取不同的數(shù)據(jù)庫類型,我們拭目以待,現(xiàn)在唯一能做的,就是在已有的資源下面用Hyperledger Fabric為業(yè)務場景創(chuàng)造最大的業(yè)務價值。

網(wǎng)頁名稱:HyperledgerFabric啟用CouchDB為狀態(tài)數(shù)據(jù)庫
分享地址:http://muchs.cn/article4/pdgsoe.html

成都網(wǎng)站建設公司_創(chuàng)新互聯(lián),為您提供網(wǎng)站設計移動網(wǎng)站建設、網(wǎng)站建設服務器托管、網(wǎng)站制作、電子商務

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉載內(nèi)容為主,如果涉及侵權請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉載,或轉載時需注明來源: 創(chuàng)新互聯(lián)

微信小程序開發(fā)