Hyperledger fabric Enable COUCHDB for the state database

Source: Internet
Author: User
Tags sprintf ticket couchdb hyperledger fabric value store

Hyperledger Fabric enables COUCHDB as a state database

I. Overview

    1. Data Request Flow

The super ledger uses an endorsement/consensus model, where simulation execution and block validation are performed separately in the nodes of different roles. Simulation execution is concurrent, which can improve scalability and throughput:

    • Endorsement node: Analog execution chain code
    • Peer node: Validate transactions and submit

2. Super Ledger storage elements

The super ledger contains the following elements:

    • Ledger number: Quickly query which ledger exists
    • Ledger data: Actual chunk data storage
    • Chunk index: Quickly query chunks/trades
    • State data: The latest World State data
    • Historical data: History of tracking Keys

Each peer node maintains four db, respectively:

    • Ledger index Library (idstore): Storage Chainid
    • State database (STATEDB): Storing world States
    • History Database (HISTORYDB): Store the version change of key
    • Block Index Library (Blockindex): Store block index

3. State database

The state database optional types include LEVELDB and couchdb. LEVELDB is the default key/value state database embedded in the peer process, and COUCHDB is an optional external state database. As with the LEVELDB key/value store, COUCHDB can store any binary data modeled as Chaincode (the COUCHDB attachment function is used internally for non-JSON binary data). However, when Chaincode values (for example, assets) are modeled as JSON data, stored as JSON documents, COUCHDB supports rich querying of chaincode data.

Both LEVELDB and COUCHDB support core chaincode operations, such as getting and setting a key (asset) and querying based on the key. A key can be queried by a range, and a composite key can be modeled to support an equivalent query against multiple parameters. For example, the asset ID can be used to query all assets owned by an entity as a key combination of the owner. These key-based queries can be used for read-only queries against the ledger, and for transactions that update the general ledger.

If you model the asset as JSON and use COUCHDB, you can use the Couchdb JSON query language in Chaincode to perform complex rich queries on the Chaincode data values, which are useful for understanding the contents of the ledger. For these types of queries, transactional protocol responses are typically useful to client applications, but are typically not submitted as transactions to the sort service. In fact, there is no guarantee that the result set will be stable between chaincode execution and rich query commit time, so it is not appropriate to use the results of rich queries to perform the final transaction update operation, unless you can guarantee the stability of the result set between the Chaincode execution time and the commit time. Or you can deal with potential changes in subsequent transactions. For example, if you perform a rich query on all of Alice's assets and transfer it to Bob, a new asset may be assigned to Alice by another transaction, which is another transaction between the Chaincode execution time and the commit time, which may miss this "virtual value" during this process.

COUCHDB is run as a standalone database process with peer, so there are additional considerations for setup, management, and operation. We can consider starting with the default embedded Leveldb, which can be transferred to COUCHDB if additional complex rich queries are required. Modeling Chaincode Asset data as JSON is a good practice so that we can execute the complex rich queries we need in the future.

Two. Enable Couchdb

This article uses the relevant components and resources in the Fabric-samples in the Hyperledger Fabric1.2, in the test environment (Fabric-samples/chaincode-docker-devmode) Launching the COUCHDB service via Docker

1. Configuring COUCHDB Boot Information

Reference: 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

Modified: Add and modify Fabric-samples/chaincode-docker-devmode/docker-compose-simple.yaml end

  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. Configuring COUCHDB Connection Information

Reference 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

Modified: Fabric-samples/chaincode-docker-devmode/docker-compose-simple.yaml in the peer module

Before modification

  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

After modification

  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

Note The format of the JSON file and the consistency of the configuration information, such as the couchdb name, etc.

3. Start the test environment

# docker-compose  -f docker-compose-simple.yaml  up -d# docker container ls

Three. Writing a chain code

1. Code structure

Code Package: TestDB

Code files

    • DOMAIN.GO//Data structure code
    • MAIN.GO//Business test code

2. Data structure

package maintype BillStruct struct {    ObjectType   string `json:"DocType"`      //对象类型定义    BillInfoID   string `json:"BillInfoID"`   //票据ID    BillInfoAmt  string `json:"BillInfoAmt"`  //票据金额    BillInfoType string `json:"BillInfoType"` //票据类型    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. Test code

Please read the comment information carefully and do not do a code splitting description here

Package Mainimport ("Github.com/hyperledger/fabric/core/chaincode/shim" "FMT" "Github.com/hyperledger/fabric/pro Tos/peer "" Encoding/json "" bytes ")//define structural body couchdbchaincode as shim. Chaincodestubinterface implements the Class object type Couchdbchaincode struct {}//overrides shim. The Init method of the Chaincodestubinterface interface is the func (T *couchdbchaincode) init (stub shim. Chaincodestubinterface) Peer. Response {return shim. Success (nil)}//rewrite shim. The Invoke method of the Chaincodestubinterface interface is func (T *couchdbchaincode) invoke (stub shim. Chaincodestubinterface) Peer. Response {//Get user intent with parameter fun, args: = Stub. Getfunctionandparameters ()//based on user intent to determine what implementation function to use if fun = "Billinit" {return billinit} else if fun = = "Querybills" {return querybills (stub, args)} else if fun = = "Querywaitbills" {return Querywaitbills ( Stub, args)}//If the user's intent does not match, make the error prompt return shim. Error ("Illegal operation, invalid function name specified")}//billinit function: Initialize ticket data func billinit (stub shim. Chaincodestubinterface) Peer. Response {/* Defines the first ticket: Bearer name: AAA bearer Id:aiD to endorser name: No to endorser ID: none */billA: = billstruct{ObjectType: "Billobj", Billinfoid: "POC001", Billinfoamt: "£", Billinfotype: "111", Billissedata: "20180501", Billduedate: "20  180508 ", Holdracct:" AAA ", Holdrcmid:" AID ", Waitendroseacct:", Waitendorsecmid: "",}//through JSON. The Marshal method serializes the ticket to Billabyte, _: = json. Marshal (BillA)//through stubs. The Putstate method stores the serialized byte array as err: = Stub. Putstate (billa.billinfoid, billabyte) if err! = Nil {return shim. Error ("Initialize first ticket failed:" + err.) Error ())} BILLB: = billstruct{ObjectType: "Billobj", Billinfoid: "POC002", Billinfo        AMT: "$", 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 ("Initialize second ticket failed:" + err.) Error ())} BILLC: = billstruct{ObjectType: "Billobj", Billinfoid: "POC003", Billinfo        AMT: "$", 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 ("Initialize third ticket failed:" + err.) Error ())} BILLD: = billstruct{ObjectType: "Billobj", Billinfoid: "POC004", Billinfo        AMT: "$", 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 ("Initialize fourth ticket failed:" + err.) Error ())} return shim. Success ([]byte ("All tickets initialized successfully"))}//querybills function: Bulk Query the list of bearer votes for the specified user func querybills (stub shim. Chaincodestubinterface, args []string) peer. Response {//Determines if there are parameters passed in if Len (args)! = 1 {return shim. Error ("must specify the bearer's ID number")}//Use the first parameter as User ID Holdrcmid: = args[0]/* To stitch the COUCHDB query string into a JSON string in the following format: {"s Elector ": {" DocType ":" Billobj "," Holdrcmid ":"%s "}} */queryString: = FMT. Sprintf ("{\" selector\ ": {\" doctype\ ": \" billobj\ ", \" holdrcmid\ ": \"%s\ "}}", Holdrcmid)// Data query operation with custom getbillbyquerystring function result, err: = getbillbyquerystring (stub, queryString) if err! = Nil {R Eturn Shim. Error ("Errors occurred" when checking the bearer's list of tickets in bulk according to the bearer's document number + Err.) Error ())} return shim. Success (Result)}//querywaitbills function: Bulk Query the list of pending notes for the specified user func QuerywaitbilLS (stub shim. Chaincodestubinterface, args []string) peer. Response {if Len (args)! = 1 {return shim. Error ("must specify the ID number of the person to be endorsed")} Waitendorsecmid: = Args[0] queryString: = FMT. Sprintf ("{\" selector\ ": {\" doctype\ ": \" billobj\ ", \" waitendorsecmid\ ": \"%s\ "}}", Waitendorsecmid) result, err: = Getbillbyquerystring (stub, queryString) if err! = Nil {return shim. Error ("Errors occurred" in bulk querying the list of notes to be endorsed according to the ID number of the person to be endorsed + ERR. Error ())} return shim. Success (Result)}//Custom function: getbillbyquerystring: Queries data based on the specified query string (COUCHDB query statement) Func getbillbyquerystring (stub shim. Chaincodestubinterface, queryString string) ([]byte, error) {//through stub. The Getqueryresult method gets the iterator iterator iterator, err: = Stub. Getqueryresult (queryString) if err! = Nil {return nil, err}//delay close iterator iterator defer iterator. Close ()//define byte buffer variable var buffer bytes. Buffer//define delimiter var issplit bool//iterate over iterators for iterator. Hasnext () {///through the next () method of the iterator gets the key and value of the next object (*queryresult. KV) Result, err: = iterator. Next () if err! = Nil {return nil, err} if issplit {buffer.        WriteString (";") }//define format//Key:result.key result. Value buffer. WriteString ("key:") buffer. WriteString (result. Key) buffer. WriteString (", Value:") buffer. WriteString (String (Result). Value)////Get to the first value, set Issplit to True to split with the second value Issplit = true}//returns the byte type of the buffer object, return buffer. Bytes (), Nil}func Main () {//Start the chain code couchdbchaincode ERR: = shim. Start (New (Couchdbchaincode))//If there is an error, prompt for an error message if err! = Nil {fmt. Errorf (Err. Error ())}}

Four. Install the chain code

1. Upload the link code

Upload chain code package TestDB to: Fabric-samples/chaincode

# ls /home/bruce/hyfa/fabric-samples/chaincode/testdb/ domain.go  main.go

2. Compile the chain code

# cd  /home/bruce/hyfa/fabric-samples/chaincode/testdb/# go build # ls domain.go  main.go  testdb

3. Start the chain code

Enter the Chaincode container for operation

# docker container exec -it chaincode bash #进入chaincode容器进行操作# cd testdb/# CORE_PEER_ADDRESS=peer:7052 CORE_CHAINCODE_ID_NAME=testCouchDB:1.0  ./testdb2018-08-05 10:33:37.063 UTC [shim] SetupChaincodeLogging -> INFO 001 Chaincode log level not provided; defaulting to: INFO2018-08-05 10:33:37.063 UTC [shim] SetupChaincodeLogging -> INFO 002 Chaincode (build level: ) starting up ...

4. Installation and instantiation of the chain code

Entering the CLI container for operation

# 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"]}‘

Five. Test chain code

1. Initializing notes

# peer chaincode  invoke  -n testCouchDB -C myc -c ‘{"Args":["billInit"]}‘

2. Query the notes held by the specified user

# peer chaincode  query  -n testCouchDB -C myc -c ‘{"Args":["queryBills","AID"]}‘

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"}

The query results can see the delimiter we define;

3. Query the specified user to endorse the ticket

# peer chaincode  query  -n testCouchDB -C myc -c ‘{"Args":["queryWaitBills","BID"]}‘

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"}

Also about LEVELDB,COUCHDB or MongoDB, in the future with the Hyperledger fabric version of the changes to take a different database type, we will wait and see, now the only thing to do is to use the existing resources under the Hyperledger Fabric creates the greatest business value for business scenarios.

Hyperledger fabric Enable COUCHDB for the state database

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.