Hyperledger Fabric启用CouchDB为状况数据库51CTO博客 - 凯发娱乐

Hyperledger Fabric启用CouchDB为状况数据库51CTO博客

2019年03月06日10时40分36秒 | 作者: 恨山 | 标签: 数据,数据库,状况 | 浏览: 1147

Hyperledger Fabric 启用CouchDB作为状况数据库

一.概述 1. 数据恳求流

超级账本选用背书/一致模型,模仿履行和区块验证是在不同人物的节点中分隔履行的。模仿履行是并发的,这样能够进步扩展性和吞吐量:

  • 背书节点:模仿履行链码
  • Peer节点:验证买卖并提交

2.超级账本存储元素

超级账本包含以下元素:

  • 账本编号:快速查询存在哪些账本
  • 账本数据: 实践的区块数据存储
  • 区块索引: 快速查询区块/买卖
  • 状况数据: 最新的国际状况数据
  • 前史数据: 盯梢键的前史

每个Peer节点会保护四个DB,分别为:

  • 账本索引库(IdStore):存储ChainID
  • 状况数据库(StateDB): 存储world state
  • 前史数据库(HistoryDB): 存储Key的版别改变
  • 区块索引库(BlockIndex):存储Block索引

3.状况数据库

状况数据库可选类型包含LevelDB和CouchDB。LevelDB是嵌入在peer进程中的默许键/值状况数据库,CouchDB是一个可选的外部状况数据库。与LevelDB键/值存储相同,CouchDB能够存储任何故chaincode建模的二进制数据(CouchDB附件函数在内部用于非json二进制数据)。可是,当chaincode值(例如,财物)被建模为JSON数据时,作为JSON文档存储,CouchDB支撑对chaincode数据进行丰厚的查询。

LevelDB和CouchDB都支撑中心chaincode操作,例如获取和设置一个键(财物),并依据键进行查询。键能够经过规模查询,能够对组合键进行建模,以支撑针对多个参数的等价查询。例如,作为一切者的组合键,财物id能够用于查询某个实体具有的一切财物。这些依据key的查询能够用于针对账本的只读查询,以及更新总账的事务。

假如将财物建模为JSON并运用CouchDB,那么就能够运用chaincode中的CouchDB JSON查询语言对chaincode数据值履行杂乱的富查询,这些类型的查询关于了解账本上的内容很有协助。关于这些类型的查询,事务协议呼应一般对客户端应用程序有用,但一般不会作为事务提交到排序效劳。事实上,也无法确保成果集在chaincode履行与富查询提交时刻之间的稳定性,因而运用富查询的成果去履行终究的事务更新操作是不合适的,除非能够确保成果集在chaincode履行时刻与提交时刻之间的稳定性,或许能够处理在后续买卖中的潜在改变。例如,假如对Alice所具有的一切财物履行一个富查询并将其传输给Bob,那么一个新的财物或许会被另一个事务分配给Alice,这是在chaincode履行时刻和提交时刻之间的另一个事务,或许此进程中会错失这个“虚值”。

CouchDB作为一个独立的数据库进程与peer一同运转,因而在设置、办理和操作方面有额定的考虑。咱们能够考虑从默许的嵌入式LevelDB开端,假如需求额定的杂乱的富查询,能够转移到CouchDB。将chaincode财物数据建模为JSON是一种很好的做法,这样咱们就能够在将来履行需求的杂乱的富查询。

二. 启用CouchDB

本文均选用Hyperledger Fabric1.2中fabric-samples中相关组件与资源,在测验环境(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.发动测验环境
# docker-compose  -f docker-compose-simple.yaml  up -d
# docker container ls

三.编写链码 1.代码结构

代码包:testdb

代码文件

  • domain.go //数据结构代码
  • main.go //事务测验代码
2.数据结构
package main

type 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.测验代码

请仔细阅读注释信息,此处不做代码切割描绘

package main

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

//界说结构体CouchDBChaincode,作为shim.ChaincodeStubInterface完成类目标
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 {
  //获取用户目的与参数
  fun, args := stub.GetFunctionAndParameters()
  //依据用户目的判别运用何种完成函数
  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("非法操作,指定的函数名无效")
}

//billInit函数:初始化收据数据
func billInit(stub shim.ChaincodeStubInterface) peer.Response {

  /*
界说第一个收据:
持票人称号: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办法对收据进行序列化操作
  billAByte, _ := json.Marshal(billA)
  //经过stub.PutState办法存储序列化后的字节数组
  err := stub.PutState(billA.BillInfoID, billAByte)
  if err != nil {
  return shim.Error("初始化第一个收据失利:" + 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("初始化第二个收据失利:" + 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("初始化第三个收据失利:" + 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("初始化第四个收据失利:" + err.Error())
  }

  return shim.Success([]byte("一切收据初始化成功"))

}

//queryBills函数:批量查询指定用户的持票列表
func queryBills(stub shim.ChaincodeStubInterface, args []string) peer.Response {
  //判别是否有参数传入
  if len(args) != 1 {
  return shim.Error("有必要指定持票人的证件号码")
  }
  //将第一个参数作为用户ID
  holdrCmID := args[0]

  /*将CouchDB查询字符串拼接成一个JSON串,格局如下:
  {
  "selector": {
  "docType": "billObj",
  "HoldrCmID": "%s"
  }
  }
  */
  queryString := fmt.Sprintf("{\"selector\":{\"DocType\":\"billObj\",\"HoldrCmID\":\"%s\"}}", holdrCmID)
  //经过自界说的getBillByQueryString函数进行数据查询操作
  result, err := getBillByQueryString(stub, queryString)
  if err != nil {
  return shim.Error("依据持票人的证件号码批量查询持票人持有收据列表时发作过错" + err.Error())
  }
  return shim.Success(result)

}

//queryWaitBills函数:批量查询指定用户的待背书收据列表
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("依据待背书人的证件号码批量查询待背书收据列表时发作过错" + err.Error())
  }
  return shim.Success(result)
}

//自界说函数:getBillByQueryString:依据指定的查询字符串(CouchDB查询句子)查询数据
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()
  //界说字节缓冲变量
  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目标的字节类型
  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.初始化收据
# peer chaincode  invoke  -n testCouchDB -C myc -c {"Args":["billInit"]}
2.查询指定用户所持收据
# 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"
}

查询成果能够看到咱们界说的分隔符;

3.查询指定用户待背书收据
# 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"
}

别的关于LevelDB,CouchDB仍是MongoDB,往后或许跟着Hyperledger Fabric的版别改变而采纳不同的数据库类型,咱们拭目而待,现在仅有能做的,就是在已有的资源下面用Hyperledger Fabric为事务场景发明最大的事务价值。

版权声明
本文来源于网络,版权归原作者所有,其内容与观点不代表凯发娱乐立场。转载文章仅为传播更有价值的信息,如采编人员采编有误或者版权原因,请与我们联系,我们核实后立即修改或删除。

猜您喜欢的文章