内容简介:Hyperledger Fabric 启用CouchDB作为状态数据库一.概述超级账本采用背书/共识模型,模拟执行和区块验证是在不同角色的节点中分开执行的。模拟执行是并发的,这样可以提高扩展性和吞吐量:
Hyperledger Fabric 启用CouchDB作为状态数据库
一.概述
- 数据请求流
超级账本采用背书/共识模型,模拟执行和区块验证是在不同角色的节点中分开执行的。模拟执行是并发的,这样可以提高扩展性和吞吐量:
- 背书节点:模拟执行链码
- 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为业务场景创造最大的业务价值。
以上所述就是小编给大家介绍的《Hyperledger Fabric启用CouchDB为状态数据库 荐》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- Firefox 已逐渐开始启用 TLS 1.3 支持(附带启用指南)
- 2.启用Admin
- 启用HTTPS过程实例记录
- express启用https小记
- 为你的网站启用 Https
- RabbitMQ 启用 HTTP 后台认证
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
PHP and MySQL Web Development
Luke Welling、Laura Thomson / Sams / July 25, 2007 / $49.99
Book Description PHP and MySQL Web Development teaches you to develop dynamic, secure, commerical Web sites. Using the same accessible, popular teaching style of the three previous editions, this b......一起来看看 《PHP and MySQL Web Development》 这本书的介绍吧!