前言
在上個(gè)實(shí)驗(yàn)中,我們已經(jīng)實(shí)現(xiàn)了簡單智能合約實(shí)現(xiàn)及客戶端開發(fā),但該實(shí)驗(yàn)中智能合約只有基礎(chǔ)的增刪改查功能,且其中的數(shù)據(jù)管理功能與傳統(tǒng) MySQL 比相差甚遠(yuǎn)。本文將在前面實(shí)驗(yàn)的基礎(chǔ)上,將 Hyperledger Fabric 的默認(rèn)數(shù)據(jù)庫支持 LevelDB 改為 CouchDB 模式,以實(shí)現(xiàn)更復(fù)雜的數(shù)據(jù)檢索功能。此外,對上個(gè)實(shí)驗(yàn)的簡單智能合約進(jìn)一步進(jìn)行功能上和設(shè)計(jì)上的擴(kuò)展,最終實(shí)現(xiàn)了智能合約的分包、分頁查詢、多字段富查詢、查詢交易歷史記錄等功能。
網(wǎng)絡(luò)架構(gòu)
本文網(wǎng)絡(luò)結(jié)構(gòu)直接將 Hyperledger Fabric無排序組織以Raft協(xié)議啟動多個(gè)Orderer服務(wù)、TLS組織運(yùn)行維護(hù)Orderer服務(wù) 中創(chuàng)建的 4-2_RunOrdererByCouncil 復(fù)制為 7_CouchDBAndComplexContract 并修改(建議直接將本案例倉庫 FabricLearn 下的 7_CouchDBAndComplexContract 目錄拷貝到本地運(yùn)行),文中大部分命令在 Hyperledger Fabric定制聯(lián)盟鏈網(wǎng)絡(luò)工程實(shí)踐 中已有介紹因此不會詳細(xì)說明,默認(rèn)情況下,所有操作皆在 7_CouchDBAndComplexContract 根目錄下執(zhí)行。修改成功后網(wǎng)絡(luò)共包含四個(gè)組織—— council 、 soft 、 web 、 hard , 其中 council 組織為網(wǎng)絡(luò)提供 TLS-CA 服務(wù),并且運(yùn)行維護(hù)著三個(gè) orderer 服務(wù);其余每個(gè)組織都運(yùn)行維護(hù)著一個(gè) peer 節(jié)點(diǎn)、一個(gè) couchDB 服務(wù)、一個(gè) admin 用戶和一個(gè) user 用戶,實(shí)驗(yàn)最終網(wǎng)絡(luò)結(jié)構(gòu)如下:
項(xiàng) | 運(yùn)行端口 | 說明 |
---|---|---|
council.ifantasy.net |
7050 | council 組織的 CA 服務(wù), 為聯(lián)盟鏈網(wǎng)絡(luò)提供 TLS-CA 服務(wù) |
orderer1.council.ifantasy.net |
7051 | council 組織的 orderer1 服務(wù) |
orderer1.council.ifantasy.net |
7052 | council 組織的 orderer1 服務(wù)的 admin 服務(wù) |
orderer2.council.ifantasy.net |
7054 | council 組織的 orderer2 服務(wù) |
orderer2.council.ifantasy.net |
7055 | council 組織的 orderer2 服務(wù)的 admin 服務(wù) |
orderer3.council.ifantasy.net |
7057 | council 組織的 orderer3 服務(wù) |
orderer3.council.ifantasy.net |
7058 | council 組織的 orderer3 服務(wù)的 admin 服務(wù) |
soft.ifantasy.net |
7250 | soft 組織的 CA 服務(wù), 包含成員: peer1 、 admin1 、user1 |
peer1.soft.ifantasy.net |
7251 | soft 組織的 peer1 成員節(jié)點(diǎn) |
couchdb.soft.ifantasy.net |
7255 | soft 組織的 couchdb 成員節(jié)點(diǎn) |
web.ifantasy.net |
7350 | web 組織的 CA 服務(wù), 包含成員: peer1 、 admin1 、user1 |
peer1.web.ifantasy.net |
7351 | web 組織的 peer1 成員節(jié)點(diǎn) |
couchdb.web.ifantasy.net |
7355 | web 組織的 couchdb 成員節(jié)點(diǎn) |
hard.ifantasy.net |
7450 | hard 組織的 CA 服務(wù), 包含成員: peer1 、 admin1 、user1 |
peer1.hard.ifantasy.net |
7451 | hard 組織的 peer1 成員節(jié)點(diǎn) |
couchdb.hard.ifantasy.net |
7455 | hard 組織的 couchdb 成員節(jié)點(diǎn) |
添加CouchDB支持并啟動網(wǎng)絡(luò)
添加CouchDB支持
首先,在 envpeer1soft
、 envpeer1soft
、 envpeer1soft
中添加 CouchDB 版本變量:
export COUCHDB_VERSION=3.2
然后,向 compose/docker-base.yaml
文件添加基礎(chǔ) CouchDB 鏡像:
couchdb-base:
image: couchdb:${COUCHDB_VERSION}
environment:
- COUCHDB_USER=admin
- COUCHDB_PASSWORD=adminpw
networks:
- ${DOCKER_NETWORKS}
之后,向 compose/docker-compose.yaml
中的每個(gè)組織添加 CouchDB 容器:
couchdb.soft.ifantasy.net:
container_name: couchdb.soft.ifantasy.net
extends:
file: docker-base.yaml
service: couchdb-base
ports:
- 7255:5984
couchdb.web.ifantasy.net:
container_name: couchdb.web.ifantasy.net
extends:
file: docker-base.yaml
service: couchdb-base
ports:
- 7355:5984
couchdb.hard.ifantasy.net:
container_name: couchdb.hard.ifantasy.net
extends:
file: docker-base.yaml
service: couchdb-base
ports:
- 7455:5984
最后,修改 compose/docker-compose.yaml
中每個(gè) peer 容器的儲存方式(以 peer1.soft.ifantasy.net 為例):
peer1.soft.ifantasy.net:
container_name: peer1.soft.ifantasy.net
extends:
file: docker-base.yaml
service: peer-base
environment:
- CORE_PEER_ID=peer1.soft.ifantasy.net
- CORE_PEER_LISTENADDRESS=0.0.0.0:7251
- CORE_PEER_ADDRESS=peer1.soft.ifantasy.net:7251
- CORE_PEER_LOCALMSPID=softMSP
- CORE_PEER_GOSSIP_EXTERNALENDPOINT=peer1.soft.ifantasy.net:7251
- CORE_LEDGER_STATE_STATEDATABASE=CouchDB
- CORE_LEDGER_STATE_COUCHDBCONFIG_COUCHDBADDRESS=couchdb.soft.ifantasy.net:5984 # 必須為容器內(nèi)端口
- CORE_LEDGER_STATE_COUCHDBCONFIG_USERNAME=admin
- CORE_LEDGER_STATE_COUCHDBCONFIG_PASSWORD=adminpw
volumes:
- ${LOCAL_CA_PATH}/soft.ifantasy.net/registers/peer1:${DOCKER_CA_PATH}/peer
ports:
- 7251:7251
depends_on:
- couchdb.soft.ifantasy.net
注意,參數(shù) CORE_LEDGER_STATE_COUCHDBCONFIG_COUCHDBADDRESS
后的服務(wù)端口必須為 couchdb 容器的內(nèi)部端口,原因不得而知, 完整代碼見 FabricLearn 下的 7_CouchDBAndComplexContract/compose 目錄。
啟動實(shí)驗(yàn)網(wǎng)絡(luò)
在上述修改完成后,在 7_CouchDBAndComplexContract 目錄下按順序執(zhí)行以下命令啟動基礎(chǔ)實(shí)驗(yàn)網(wǎng)絡(luò):
- 設(shè)置DNS(如果未設(shè)置):
./setDNS.sh
- 設(shè)置環(huán)境變量:
source envpeer1soft
- 啟動CA網(wǎng)絡(luò):
./0_Restart.sh
- 注冊用戶:
./1_RegisterUser.sh
- 獲取用戶證書:
./2_EnrollUser.sh
- 配置通道:
./3_Configtxgen.sh
網(wǎng)絡(luò)啟動成功后可見包含 couchdb 容器:
合約開發(fā)
本節(jié)所用智能合約由前一篇文章 Hyperledger Fabric 智能合約開發(fā)及 fabric-sdk-go/fabric-gateway 使用示例 改進(jìn)(拆分)而來,在上篇文章的基礎(chǔ)上對合約進(jìn)行分包分文件處理,使項(xiàng)目具有更好的目錄結(jié)構(gòu)。在實(shí)驗(yàn)根目錄 7_CouchDBAndComplexContract 下創(chuàng)建目錄 project_contract 作為智能合約根目錄,在 project_contract 下執(zhí)行以下命令初始化 GO 模塊:
go mod init github.com/wefantasy/FabricLearn/7_CouchDBAndComplexContract/project_contract
tools 層
tools 層主要用于編寫智能合約通用工具,創(chuàng)建 tools/contract.go 工具類,主要包含以下函數(shù):
-
ConstructResultByIterator
: 根據(jù) fabric 查詢結(jié)果shim.StateQueryIteratorInterface
生成對應(yīng)切片。// 根據(jù)查詢結(jié)果生成切片 func ConstructResultByIterator[T interface{}](resultsIterator shim.StateQueryIteratorInterface) ([]*T, error) { var txs []*T for resultsIterator.HasNext() { queryResult, err := resultsIterator.Next() if err != nil { return nil, err } var tx T err = json.Unmarshal(queryResult.Value, &tx) if err != nil { return nil, err } txs = append(txs, &tx) } fmt.Println("select result length: ", len(txs)) return txs, nil }
-
SelectByQueryString
: 根據(jù) couchdb 查詢字符串完成查詢操作,并返回對應(yīng)切片。// 根據(jù)查詢字符串查詢 func SelectByQueryString[T interface{}](ctx contractapi.TransactionContextInterface, queryString string) ([]*T, error) { resultsIterator, err := ctx.GetStub().GetQueryResult(queryString) if err != nil { return nil, err } defer resultsIterator.Close() return ConstructResultByIterator[T](resultsIterator) }
-
SelectByQueryStringWithPagination
: 根據(jù) couchdb 查詢字符串分頁查詢,并返回對應(yīng)切片。// 根據(jù)擦查詢字符串分頁查詢 func SelectByQueryStringWithPagination[T interface{}](ctx contractapi.TransactionContextInterface, queryString string, pageSize int32, bookmark string) (*model.PaginatedQueryResult[T], error) { resultsIterator, responseMetadata, err := ctx.GetStub().GetQueryResultWithPagination(queryString, pageSize, bookmark) if err != nil { return nil, err } defer resultsIterator.Close() var txs []T for resultsIterator.HasNext() { queryResult, err := resultsIterator.Next() if err != nil { return nil, err } var tx T err = json.Unmarshal(queryResult.Value, &tx) if err != nil { return nil, err } txs = append(txs, tx) } return &model.PaginatedQueryResult[T]{ Records: txs, FetchedRecordsCount: responseMetadata.FetchedRecordsCount, Bookmark: responseMetadata.Bookmark, }, nil }
-
SelectHistoryByIndex
: 獲得交易創(chuàng)建之后的所有變化(區(qū)塊鏈賬本)。// 獲得交易創(chuàng)建之后的所有變化. func SelectHistoryByIndex[T interface{}](ctx contractapi.TransactionContextInterface, index string) ([]model.HistoryQueryResult[T], error) { resultsIterator, err := ctx.GetStub().GetHistoryForKey(index) if err != nil { return nil, err } defer resultsIterator.Close() var records []model.HistoryQueryResult[T] for resultsIterator.HasNext() { response, err := resultsIterator.Next() if err != nil { return nil, err } var tx T if len(response.Value) > 0 { err = json.Unmarshal(response.Value, &tx) if err != nil { return nil, err } } record := model.HistoryQueryResult[T]{ TxId: response.TxId, Record: tx, IsDelete: response.IsDelete, } records = append(records, record) } return records, nil }
model 層
model層主要用于申明合約所用數(shù)據(jù)結(jié)構(gòu),其中 model/project.go
內(nèi)容如下:
package model
type Project struct {
Table string `json:"table" form:"table"` // 數(shù)據(jù)庫標(biāo)記
ID string `json:"ID"` // 項(xiàng)目唯一ID
Name string `json:"Name"` // 項(xiàng)目名稱
Username string `json:"username"` // 項(xiàng)目主要負(fù)責(zé)人
Organization string `json:"Organization"` // 項(xiàng)目所屬組織
Category string `json:"Category"` // 項(xiàng)目所屬類別
Url string `json:"Url"` // 項(xiàng)目介紹地址
Describes string `json:"Describes"` // 項(xiàng)目描述
}
func (o *Project) Index() string {
o.Table = "project"
return o.ID
}
func (o *Project) IndexKey() string {
return "table~ID~name"
}
func (o *Project) IndexAttr() []string {
return []string{o.Table, o.ID, o.Name}
}
其中 Index
函數(shù)用于標(biāo)識模型的唯一主鍵; IndexKey
函數(shù)用于標(biāo)識自建索引的字段,其中命名方式必須與字段申明的結(jié)構(gòu)體標(biāo)記 json 一致(大小寫);IndexAttr
用于構(gòu)造具體的索引。model/user.go
申明了用戶的字段信息:
package model
// User 用戶表
type User struct {
Table string `json:"table" form:"table"` // 數(shù)據(jù)庫標(biāo)記
Username string `json:"username" form:"username"` //用戶賬戶
Name string `json:"name" form:"name"` //真實(shí)姓名
Email string `json:"email" form:"email"` // 郵箱
Phone string `json:"phone" form:"phone"` // 手機(jī)
}
func (o *User) Index() string {
o.Table = "user"
return o.Username
}
func (o *User) IndexKey() string {
return "table~username~name"
}
func (o *User) IndexAttr() []string {
return []string{o.Table, o.Username, o.Name}
}
model/base.go
申明了基于 CouchDB 的富查詢結(jié)果模型:
package model
import "time"
// 歷史查詢結(jié)果
type HistoryQueryResult[T interface{}] struct {
Record T `json:"record"`
TxId string `json:"txId"`
Timestamp time.Time `json:"timestamp"`
IsDelete bool `json:"isDelete"`
}
// 分頁查詢結(jié)果
type PaginatedQueryResult[T interface{}] struct {
Records []T `json:"records"`
FetchedRecordsCount int32 `json:"fetchedRecordsCount"`
Bookmark string `json:"bookmark"`
}
contract 層
contract 層用于實(shí)現(xiàn)智能合約的核心邏輯(本示例為 model 的增刪改查),由于結(jié)合了 CouchDB ,所以相比上個(gè)實(shí)驗(yàn)需要更復(fù)雜的實(shí)現(xiàn)。以 contract/project.go
為例進(jìn)行介紹,由于代碼太長在此就不再粘貼(完整代碼參考 project.go),其中主要功能及實(shí)現(xiàn)方式如下:
- 插入數(shù)據(jù)( Insert ):先使用
ctx.GetStub().PutState(tx.Index(), txb)
方法插入數(shù)據(jù),然后調(diào)用ctx.GetStub().CreateCompositeKey(tx.IndexKey(), tx.IndexAttr())
方法為該數(shù)據(jù)創(chuàng)建 CouchDB 索引,最后調(diào)用ctx.GetStub().PutState(indexKey, value)
將索引存入鏈上。 - 更新數(shù)據(jù)( Update ):先使用
indexKey, err := ctx.GetStub().CreateCompositeKey(otx.IndexKey(), otx.IndexAttr())
得到舊數(shù)據(jù)的索引,再調(diào)用ctx.GetStub().DelState(indexKey)
刪除舊數(shù)據(jù)的索引,然后調(diào)用ctx.GetStub().PutState(tx.Index(), txb)
更新數(shù)據(jù),最后分別調(diào)用ctx.GetStub().CreateCompositeKey(tx.IndexKey(), tx.IndexAttr())
和ctx.GetStub().PutState(indexKey, value)
創(chuàng)建新數(shù)據(jù)索引并存入鏈上。 - 刪除數(shù)據(jù)( Delete ):先使用
ctx.GetStub().DelState(anstx.Index())
刪除舊數(shù)據(jù),再調(diào)用indexKey, err := ctx.GetStub().CreateCompositeKey(tx.IndexKey(), tx.IndexAttr())
得到舊數(shù)據(jù)索引,最后通過ctx.GetStub().DelState(indexKey)
刪除舊數(shù)據(jù)索引。 - 讀取指定index的記錄( SelectByIndex ):使用形如
{"selector":{"ID":"%s", "table":"project"}}
的 CouchDB 查詢語法根據(jù)索引查詢數(shù)據(jù)。 - 讀取所有數(shù)據(jù)( SelectAll ):使用形如
{"selector":{"table":"project"}}
的 CouchDB 查詢語法查詢所有相關(guān)數(shù)據(jù)。 - 按某索引查詢所有數(shù)據(jù)( SelectBySome ):使用形如
{"selector":{"%s":"%s", "table":"project"}}
的 CouchDB 查詢語法根據(jù)索引查詢數(shù)據(jù)。 - 富分頁查詢所有數(shù)據(jù)( SelectAllWithPagination ):使用形如
{"selector":{"table":"project"}}
的 CouchDB 查詢語法調(diào)用上述分頁查詢數(shù)據(jù)工具 tools.SelectByQueryStringWithPagination 來查詢數(shù)據(jù)。 - 按關(guān)鍵字富分頁查詢所有數(shù)據(jù) SelectBySomeWithPagination ):使用形如
{"selector":{"%s":"%s","table":"project"}}
的 CouchDB 查詢語法調(diào)用上述分頁查詢數(shù)據(jù)工具tools.SelectByQueryStringWithPagination
來查詢數(shù)據(jù)。 - 按某索引查詢數(shù)據(jù)歷史( SelectHistoryByIndex ):調(diào)用上述歷史數(shù)據(jù)查詢工具
tools.SelectHistoryByIndex
來查詢數(shù)據(jù)。
contract/user.go
為 model/user.go
的核心操作邏輯,此示例只包含簡單的功能,完整源碼參考 user.go。
main 主函數(shù)
主函數(shù)完整代碼如下所示:
package main
import (
"github.com/hyperledger/fabric-contract-api-go/contractapi"
"github.com/wefantasy/FabricLearn/7_CouchDBAndComplexContract/project_contract/contract"
)
func main() {
chaincode, err := contractapi.NewChaincode(&contract.UserContract{}, &contract.ProjectContract{})
if err != nil {
panic(err)
}
if err := chaincode.Start(); err != nil {
panic(err)
}
}
多智能合約只需在 main 的 contractapi.NewChaincode
函數(shù)中按順序申明即可。在智能合約編寫完畢后使用 go mod vendor
來打包依賴,上述工作完成后 project_contract 目錄結(jié)構(gòu)及解釋如下所示:
project_contract
├── contract // 智能合約核心邏輯
│ ├── project.go
│ └── user.go
├── go.mod
├── go.sum
├── main.go // 智能合約入口函數(shù)
├── model // 申明數(shù)據(jù)模型
│ ├── base.go // 申明分頁等數(shù)據(jù)結(jié)構(gòu)
│ ├── project.go
│ └── user.go
├── tools // 工具目錄
│ └── contract.go // 智能合約通用工具,查詢歷史/分頁查詢等
└── vendor // 依賴目錄
合約部署和測試
如無特殊說明,以下命令默認(rèn)運(yùn)行于實(shí)驗(yàn)根目錄 7_CouchDBAndComplexContract 下:
- 合約打包
source envpeer1soft peer lifecycle chaincode package basic.tar.gz --path project_contract --label basic_1
- 三組織安裝
source envpeer1soft peer lifecycle chaincode install basic.tar.gz peer lifecycle chaincode queryinstalled source envpeer1web peer lifecycle chaincode install basic.tar.gz peer lifecycle chaincode queryinstalled source envpeer1hard peer lifecycle chaincode install basic.tar.gz peer lifecycle chaincode queryinstalled
- 三組織批準(zhǔn)
注意:由于我們有兩個(gè)智能合約,且每個(gè)智能合約都包含export CHAINCODE_ID=basic_1:22e38a78d2ddfe9c3cbeff91140ee209c901adcc24cd2b11f863a53abcdc825a source envpeer1soft peer lifecycle chaincode approveformyorg -o orderer1.council.ifantasy.net:7051 --tls --cafile $ORDERER_CA --channelID testchannel --name basic --version 1.0 --sequence 1 --waitForEvent --package-id $CHAINCODE_ID peer lifecycle chaincode queryapproved -C testchannel -n basic --sequence 1 source envpeer1web peer lifecycle chaincode approveformyorg -o orderer3.council.ifantasy.net:7057 --tls --cafile $ORDERER_CA --channelID testchannel --name basic --version 1.0 --sequence 1 --waitForEvent --package-id $CHAINCODE_ID peer lifecycle chaincode queryapproved -C testchannel -n basic --sequence 1 source envpeer1hard peer lifecycle chaincode approveformyorg -o orderer2.council.ifantasy.net:7054 --tls --cafile $ORDERER_CA --channelID testchannel --name basic --version 1.0 --sequence 1 --waitForEvent --package-id $CHAINCODE_ID peer lifecycle chaincode queryapproved -C testchannel -n basic --sequence 1
InitLedger
函數(shù)來初始化數(shù)據(jù),所以在這里以及后續(xù)鏈碼操作中需要?jiǎng)h除--init-required
參數(shù)(因?yàn)楹霞s不需要初始化)。 - 提交鏈碼
source envpeer1soft peer lifecycle chaincode commit -o orderer2.council.ifantasy.net:7054 --tls --cafile $ORDERER_CA --channelID testchannel --name basic --version 1.0 --sequence 1 --peerAddresses peer1.soft.ifantasy.net:7251 --tlsRootCertFiles $CORE_PEER_TLS_ROOTCERT_FILE --peerAddresses peer1.web.ifantasy.net:7351 --tlsRootCertFiles $CORE_PEER_TLS_ROOTCERT_FILE
- 初始化鏈碼數(shù)據(jù)并測試
source envpeer1soft peer chaincode invoke -o orderer1.council.ifantasy.net:7051 --tls --cafile $ORDERER_CA --channelID testchannel --name basic --peerAddresses peer1.soft.ifantasy.net:7251 --tlsRootCertFiles $CORE_PEER_TLS_ROOTCERT_FILE --peerAddresses peer1.web.ifantasy.net:7351 --tlsRootCertFiles $CORE_PEER_TLS_ROOTCERT_FILE -c '{"Args":["UserContract:InitLedger"]}' peer chaincode invoke -o orderer1.council.ifantasy.net:7051 --tls --cafile $ORDERER_CA --channelID testchannel --name basic --peerAddresses peer1.soft.ifantasy.net:7251 --tlsRootCertFiles $CORE_PEER_TLS_ROOTCERT_FILE --peerAddresses peer1.web.ifantasy.net:7351 --tlsRootCertFiles $CORE_PEER_TLS_ROOTCERT_FILE -c '{"Args":["ProjectContract:InitLedger"]}' peer chaincode invoke -o orderer1.council.ifantasy.net:7051 --tls --cafile $ORDERER_CA --channelID testchannel --name basic --peerAddresses peer1.soft.ifantasy.net:7251 --tlsRootCertFiles $CORE_PEER_TLS_ROOTCERT_FILE --peerAddresses peer1.web.ifantasy.net:7351 --tlsRootCertFiles $CORE_PEER_TLS_ROOTCERT_FILE -c '{"Args":["UserContract:GetAllUsers"]}' peer chaincode invoke -o orderer1.council.ifantasy.net:7051 --tls --cafile $ORDERER_CA --channelID testchannel --name basic --peerAddresses peer1.soft.ifantasy.net:7251 --tlsRootCertFiles $CORE_PEER_TLS_ROOTCERT_FILE --peerAddresses peer1.web.ifantasy.net:7351 --tlsRootCertFiles $CORE_PEER_TLS_ROOTCERT_FILE -c '{"Args":["ProjectContract:SelectAll"]}' peer chaincode invoke -o orderer1.council.ifantasy.net:7051 --tls --cafile $ORDERER_CA --channelID testchannel --name basic --peerAddresses peer1.soft.ifantasy.net:7251 --tlsRootCertFiles $CORE_PEER_TLS_ROOTCERT_FILE --peerAddresses peer1.web.ifantasy.net:7351 --tlsRootCertFiles $CORE_PEER_TLS_ROOTCERT_FILE -c '{"Args":["ProjectContract:SelectBySome", "name", "工作室聯(lián)盟鏈管理系統(tǒng)"]}'
注意,在多合約的情況下調(diào)用鏈碼,需要在所調(diào)用的合約函數(shù)前指定所屬合約,如 ProjectContract:SelectBySome
,其它合約示例調(diào)用方式大致一樣,在此不再贅述。此外由于 CouchDB 自帶了數(shù)據(jù)庫管理界面,則可以通過本例中任意一個(gè) CouchDB 的服務(wù)地址來訪問鏈上數(shù)據(jù),如 http://192.168.27.72:7355/_utils/#login
(虛擬機(jī)IP為 192.168.27.72
,soft 組織的 CouchDB 端口 7355
),輸入docker中配置的賬戶admin
密碼adminpw
即可進(jìn)入系統(tǒng):
至此,本實(shí)驗(yàn)基本完成。
可能存在的問題
-
peer lifecycle chaincode install
時(shí)遇到錯(cuò)誤:
Error creating tx-manage chaincode: Error compiling schema for DataContract [SelectBySomeWithPagination]. Return schema invalid. Object has no key 'PaginatedQueryResult[github.com'
panic: Error creating tx-manage chaincode: Error compiling schema for DataContract [SelectBySomeWithPagination]. Return schema invalid. Object has no key 'PaginatedQueryResult[github.com'
goroutine 1 [running]:
log.Panicf({0xa24b02?, 0x1?}, {0xc00014ff50?, 0x407679?, 0x404c71?})
/usr/local/go/src/log/log.go:392 +0x67
main.main()
/chaincode/input/src/main.go:201 +0x8e
原因及解決方法: 所用 docker fabric 2.4 鏡像的 Golang 版本太低不支持泛型,需要?jiǎng)h除并重新安裝 docker fabric 2.4 (盡管 tag 一樣,但鏡像內(nèi)容會更新)。
- 智能合約調(diào)用時(shí)遇到錯(cuò)誤:
[notice] 2022-11-13T12:13:49.502557Z nonode@nohost <0.286.0> -------- rexi_server : started servers
[notice] 2022-11-13T12:13:49.504490Z nonode@nohost <0.290.0> -------- rexi_buffer : started servers
[warning] 2022-11-13T12:13:49.530610Z nonode@nohost <0.298.0> -------- creating missing database: _nodes
[info] 2022-11-13T12:13:49.530670Z nonode@nohost <0.299.0> -------- open_result error {not_found,no_db_file} for _nodes
[error] 2022-11-13T12:13:49.537681Z nonode@nohost <0.304.0> -------- CRASH REPORT Process (<0.304.0>) with 2 neighbors crashed with reason: no match of right hand value {error,enospc} at couch_bt_engine:init/2(line:154) <=
……
原因及解決方法: 可能是 docker volume 把硬盤占滿了,使用 docker volume rm $(docker volume ls -qf dangling=true)
清除所有再重試
使用
- 遇到錯(cuò)誤:
# github.com/hyperledger/fabric-sdk-go/internal/github.com/hyperledger/fabric/discovery/client
/root/go/pkg/mod/github.com/hyperledger/fabric-sdk-go@v1.0.0/internal/github.com/hyperledger/fabric/discovery/client/api.go:47:38: undefined: discovery.ChaincodeCall
/root/go/pkg/mod/github.com/hyperledger/fabric-sdk-go@v1.0.0/internal/github.com/hyperledger/fabric/discovery/client/client.go:83:63: undefined: discovery.ChaincodeInterest
/root/go/pkg/mod/github.com/hyperledger/fabric-sdk-go@v1.0.0/internal/github.com/hyperledger/fabric/discovery/client/client.go:120:65: undefined: discovery.ChaincodeCall
/root/go/pkg/mod/github.com/hyperledger/fabric-sdk-go@v1.0.0/internal/github.com/hyperledger/fabric/discovery/client/client.go:124:23: undefined: discovery.ChaincodeInterest
/root/go/pkg/mod/github.com/hyperledger/fabric-sdk-go@v1.0.0/internal/github.com/hyperledger/fabric/discovery/client/client.go:229:105: undefined: discovery.ChaincodeCall
/root/go/pkg/mod/github.com/hyperledger/fabric-sdk-go@v1.0.0/internal/github.com/hyperledger/fabric/discovery/client/client.go:247:64: undefined: discovery.ChaincodeCall
/root/go/pkg/mod/github.com/hyperledger/fabric-sdk-go@v1.0.0/internal/github.com/hyperledger/fabric/discovery/client/client.go:604:48: undefined: discovery.ChaincodeInterest
/root/go/pkg/mod/github.com/hyperledger/fabric-sdk-go@v1.0.0/internal/github.com/hyperledger/fabric/discovery/client/client.go:620:35: undefined: discovery.ChaincodeCall
原因及解決方法: github.com/hyperledger/fabric-sdk-go
需要指定 20220117 版本,將 go.mod
文件對應(yīng)依賴替換如下:
github.com/hyperledger/fabric-sdk-go v1.0.1-0.20220117114400-c848d119936b。
- 遇到錯(cuò)誤:
Error compiling schema for ****[**]. Return schema invalid. Object has no key 'Wrapper[[]<part of module name>'
原因及解決方法:智能合約返回值不支持泛型,將智能合約返回值換成 interface{} 即可。
- 查詢歷史記錄出現(xiàn)遇到錯(cuò)誤:
Error: could not assemble transaction: ProposalResponsePayloads do not match (base64):
原因及解決方法:鏈碼輸出(返回)數(shù)據(jù)中不要使用地址傳遞(推薦值傳遞),因?yàn)榈刂范际莿討B(tài)分配,每次取到的值都不一樣,造成共識失敗。文章來源:http://www.zghlxwxcb.cn/news/detail-414435.html
- 遇到錯(cuò)誤:
Failed to evaluate: Multiple errors occurred: - Transaction processing for endorser [localhost:7451]: Chaincode status Code: (500) UNKNOWN. Description: Error handling success response. Value did not match schema:\n1. return: Invalid type. Expected: array, given: string - Transaction processing for endorser [localhost:7251]: Chaincode status Code: (500) UNKNOWN. Description: Error handling success response. Value did not match schema:\n1. return: Invalid type. Expected: array, given: string
原因及解決方法:鏈碼返回值不能為 []byte ,這是一個(gè) fabric 的 bug,對于復(fù)雜返回類型建議直接返回字符串 string文章來源地址http://www.zghlxwxcb.cn/news/detail-414435.html
到了這里,關(guān)于Hyperledger Fabric 使用 CouchDB 和復(fù)雜智能合約開發(fā)的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!