前言

在上个试验中,咱们曾经实现了简略智能合约实现及客户端开发,但该试验中智能合约只有根底的增删改查性能,且其中的数据管理性能与传统 MySQL 比相差甚远。本文将在后面试验的根底上,将 Hyperledger Fabric 的默认数据库反对 LevelDB 改为 CouchDB 模式,以实现更简单的数据检索性能。此外,对上个试验的简略智能合约进一步进行性能上和设计上的扩大,最终实现了智能合约的分包、分页查问、多字段富查问、查问交易历史记录等性能。

网络架构

本文网络结构间接将 Hyperledger Fabric无排序组织以Raft协定启动多个Orderer服务、TLS组织运行保护Orderer服务 中创立的 4-2_RunOrdererByCouncil 复制为 7_CouchDBAndComplexContract 并批改(倡议间接将本案例仓库 FabricLearn 下的 7_CouchDBAndComplexContract 目录拷贝到本地运行),文中大部分命令在 Hyperledger Fabric定制联盟链网络工程实际 中已有介绍因而不会具体阐明,默认状况下,所有操作皆在 7_CouchDBAndComplexContract 根目录下执行。批改胜利后网络共蕴含四个组织—— council 、 soft 、 web 、 hard , 其中 council 组织为网络提供 TLS-CA 服务,并且运行保护着三个 orderer 服务;其余每个组织都运行保护着一个 peer 节点、一个 couchDB 服务、一个 admin 用户和一个 user 用户,试验最终网络结构如下:

运行端口阐明
council.ifantasy.net7050council 组织的 CA 服务, 为联盟链网络提供 TLS-CA 服务
orderer1.council.ifantasy.net7051council 组织的 orderer1 服务
orderer1.council.ifantasy.net7052council 组织的 orderer1 服务的 admin 服务
orderer2.council.ifantasy.net7054council 组织的 orderer2 服务
orderer2.council.ifantasy.net7055council 组织的 orderer2 服务的 admin 服务
orderer3.council.ifantasy.net7057council 组织的 orderer3 服务
orderer3.council.ifantasy.net7058council 组织的 orderer3 服务的 admin 服务
soft.ifantasy.net7250soft 组织的 CA 服务, 蕴含成员: peer1 、 admin1 、user1
peer1.soft.ifantasy.net7251soft 组织的 peer1 成员节点
couchdb.soft.ifantasy.net7255soft 组织的 couchdb 成员节点
web.ifantasy.net7350web 组织的 CA 服务, 蕴含成员: peer1 、 admin1 、user1
peer1.web.ifantasy.net7351web 组织的 peer1 成员节点
couchdb.web.ifantasy.net7355web 组织的 couchdb 成员节点
hard.ifantasy.net7450hard 组织的 CA 服务, 蕴含成员: peer1 、 admin1 、user1
peer1.hard.ifantasy.net7451hard 组织的 peer1 成员节点
couchdb.hard.ifantasy.net7455hard 组织的 couchdb 成员节点

增加CouchDB反对并启动网络

增加CouchDB反对

首先,在 envpeer1softenvpeer1softenvpeer1soft 中增加 CouchDB 版本变量:

export COUCHDB_VERSION=3.2

而后,向 compose/docker-base.yaml 文件增加根底 CouchDB 镜像:

couchdb-base:    image: couchdb:${COUCHDB_VERSION}    environment:      - COUCHDB_USER=admin      - COUCHDB_PASSWORD=adminpw    networks:      - ${DOCKER_NETWORKS}

之后,向 compose/docker-compose.yaml 中的每个组织增加 CouchDB 容器:

couchdb.soft.ifantasy.net:    container_name: couchdb.soft.ifantasy.net    extends:      file: docker-base.yaml      service: couchdb-base    ports:      - 7255:5984couchdb.web.ifantasy.net:    container_name: couchdb.web.ifantasy.net    extends:      file: docker-base.yaml      service: couchdb-base    ports:      - 7355:5984couchdb.hard.ifantasy.net:    container_name: couchdb.hard.ifantasy.net    extends:      file: docker-base.yaml      service: couchdb-base    ports:      - 7455:5984

最初,批改 compose/docker-compose.yaml 中每个 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   # 必须为容器内端口      - 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

留神,参数 CORE_LEDGER_STATE_COUCHDBCONFIG_COUCHDBADDRESS 后的服务端口必须为 couchdb 容器的外部端口,起因不得而知, 残缺代码见 FabricLearn 下的 7_CouchDBAndComplexContract/compose 目录。

启动试验网络

在上述批改实现后,在 7_CouchDBAndComplexContract 目录下按程序执行以下命令启动根底试验网络:

  1. 设置DNS(如果未设置): ./setDNS.sh
  2. 设置环境变量: source envpeer1soft
  3. 启动CA网络: ./0_Restart.sh
  4. 注册用户: ./1_RegisterUser.sh
  5. 获取用户证书: ./2_EnrollUser.sh
  6. 配置通道: ./3_Configtxgen.sh

网络启动胜利后可见蕴含 couchdb 容器:

合约开发

本节所用智能合约由前一篇文章 Hyperledger Fabric 智能合约开发及 fabric-sdk-go/fabric-gateway 应用示例 改良(拆分)而来,在上篇文章的根底上对合约进行分包分文件解决,使我的项目具备更好的目录构造。在试验根目录 7_CouchDBAndComplexContract 下创立目录 project_contract 作为智能合约根目录,在 project_contract 下执行以下命令初始化 GO 模块:

go mod init github.com/wefantasy/FabricLearn/7_CouchDBAndComplexContract/project_contract

tools 层

tools 层次要用于编写智能合约通用工具,创立 tools/contract.go 工具类,次要蕴含以下函数:

  • ConstructResultByIterator : 依据 fabric 查问后果 shim.StateQueryIteratorInterface 生成对应切片。

    // 依据查问后果生成切片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 : 依据 couchdb 查问字符串实现查问操作,并返回对应切片。

    // 依据查问字符串查问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 : 依据 couchdb 查问字符串分页查问,并返回对应切片。

    // 依据擦查问字符串分页查问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 : 取得交易创立之后的所有变动(区块链账本)。

    // 取得交易创立之后的所有变动.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层次要用于申明合约所用数据结构,其中 model/project.go 内容如下:

package modeltype Project struct {Table        string `json:"table" form:"table"` //  数据库标记ID           string `json:"ID"`                 // 我的项目惟一IDName         string `json:"Name"`               // 项目名称Username     string `json:"username"`           // 我的项目次要负责人Organization string `json:"Organization"`       // 我的项目所属组织Category     string `json:"Category"`           // 我的项目所属类别Url          string `json:"Url"`                // 我的项目介绍地址Describes    string `json:"Describes"`          // 我的项目形容}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 函数用于标识模型的惟一主键; IndexKey 函数用于标识自建索引的字段,其中命名形式必须与字段申明的构造体标记 json 统一(大小写);IndexAttr 用于结构具体的索引。model/user.go 申明了用户的字段信息:

package model// User  用户表type User struct {Table    string `json:"table" form:"table"`       //  数据库标记Username string `json:"username" form:"username"` //用户账户Name     string `json:"name" form:"name"`         //实在姓名Email    string `json:"email" form:"email"`       //  邮箱Phone    string `json:"phone" form:"phone"`       //  手机}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 的富查问后果模型:

package modelimport "time"// 历史查问后果type HistoryQueryResult[T interface{}] struct {Record    T         `json:"record"`TxId      string    `json:"txId"`Timestamp time.Time `json:"timestamp"`IsDelete  bool      `json:"isDelete"`}// 分页查问后果type PaginatedQueryResult[T interface{}] struct {Records             []T    `json:"records"`FetchedRecordsCount int32  `json:"fetchedRecordsCount"`Bookmark            string `json:"bookmark"`}

contract 层

contract 层用于实现智能合约的外围逻辑(本示例为 model 的增删改查),因为联合了 CouchDB ,所以相比上个试验须要更简单的实现。以 contract/project.go 为例进行介绍,因为代码太长在此就不再粘贴(残缺代码参考 project.go),其中次要性能及实现形式如下:

  • 插入数据( Insert ):先应用 ctx.GetStub().PutState(tx.Index(), txb) 办法插入数据,而后调用ctx.GetStub().CreateCompositeKey(tx.IndexKey(), tx.IndexAttr()) 办法为该数据创立 CouchDB 索引,最初调用 ctx.GetStub().PutState(indexKey, value) 将索引存入链上。
  • 更新数据( Update ):先应用 indexKey, err := ctx.GetStub().CreateCompositeKey(otx.IndexKey(), otx.IndexAttr()) 失去旧数据的索引,再调用 ctx.GetStub().DelState(indexKey) 删除旧数据的索引,而后调用 ctx.GetStub().PutState(tx.Index(), txb) 更新数据,最初别离调用 ctx.GetStub().CreateCompositeKey(tx.IndexKey(), tx.IndexAttr())ctx.GetStub().PutState(indexKey, value) 创立新数据索引并存入链上。
  • 删除数据( Delete ):先应用 ctx.GetStub().DelState(anstx.Index()) 删除旧数据,再调用 indexKey, err := ctx.GetStub().CreateCompositeKey(tx.IndexKey(), tx.IndexAttr()) 失去旧数据索引,最初通过 ctx.GetStub().DelState(indexKey) 删除旧数据索引。
  • 读取指定index的记录( SelectByIndex ):应用形如 {"selector":{"ID":"%s", "table":"project"}} 的 CouchDB 查问语法依据索引查问数据。
  • 读取所有数据( SelectAll ):应用形如 {"selector":{"table":"project"}} 的 CouchDB 查问语法查问所有相干数据。
  • 按某索引查问所有数据( SelectBySome ):应用形如 {"selector":{"%s":"%s", "table":"project"}} 的 CouchDB 查问语法依据索引查问数据。
  • 富分页查问所有数据( SelectAllWithPagination ):应用形如 {"selector":{"table":"project"}} 的 CouchDB 查问语法调用上述分页查问数据工具 tools.SelectByQueryStringWithPagination 来查问数据。
  • 按关键字富分页查问所有数据 SelectBySomeWithPagination ):应用形如 {"selector":{"%s":"%s","table":"project"}} 的 CouchDB 查问语法调用上述分页查问数据工具 tools.SelectByQueryStringWithPagination 来查问数据。
  • 按某索引查问数据历史( SelectHistoryByIndex ):调用上述历史数据查问工具 tools.SelectHistoryByIndex 来查问数据。

contract/user.gomodel/user.go 的外围操作逻辑,此示例只蕴含简略的性能,残缺源码参考 user.go。

main 主函数

主函数残缺代码如下所示:

package mainimport (        "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 函数中按程序申明即可。在智能合约编写结束后应用 go mod vendor 来打包依赖,上述工作实现后 project_contract 目录构造及解释如下所示:

project_contract├── contract            // 智能合约外围逻辑│   ├── project.go│   └── user.go├── go.mod├── go.sum├── main.go             // 智能合约入口函数├── model               // 申明数据模型│   ├── base.go         // 申明分页等数据结构│   ├── project.go│   └── user.go├── tools               // 工具目录│   └── contract.go     // 智能合约通用工具,查问历史/分页查问等└── vendor              // 依赖目录

合约部署和测试

如无非凡阐明,以下命令默认运行于试验根目录 7_CouchDBAndComplexContract 下:

  1. 合约打包

    source envpeer1softpeer lifecycle chaincode package basic.tar.gz --path project_contract --label basic_1
  2. 三组织装置

     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
  3. 三组织批准

    export CHAINCODE_ID=basic_1:22e38a78d2ddfe9c3cbeff91140ee209c901adcc24cd2b11f863a53abcdc825asource envpeer1softpeer 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_IDpeer lifecycle chaincode queryapproved -C testchannel -n basic --sequence 1source envpeer1webpeer 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_IDpeer lifecycle chaincode queryapproved -C testchannel -n basic --sequence 1source envpeer1hardpeer 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_IDpeer lifecycle chaincode queryapproved -C testchannel -n basic --sequence 1

    留神:因为咱们有两个智能合约,且每个智能合约都蕴含 InitLedger 函数来初始化数据,所以在这里以及后续链码操作中须要删除 --init-required 参数(因为合约不须要初始化)。

  4. 提交链码

    source envpeer1softpeer 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
  5. 初始化链码数据并测试

    source envpeer1softpeer 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", "工作室联盟链管理系统"]}'

留神,在多合约的状况下调用链码,须要在所调用的合约函数前指定所属合约,如 ProjectContract:SelectBySome ,其它合约示例调用形式大抵一样,在此不再赘述。此外因为 CouchDB 自带了数据库治理界面,则能够通过本例中任意一个 CouchDB 的服务地址来拜访链上数据,如 http://192.168.27.72:7355/_utils/#login (虚拟机IP为 192.168.27.72 ,soft 组织的 CouchDB 端口 7355),输出docker中配置的账户admin明码adminpw即可进入零碎:


至此,本试验根本实现。

可能存在的问题

  1. peer lifecycle chaincode install 时遇到谬误:

    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 +0x67main.main()     /chaincode/input/src/main.go:201 +0x8e

    起因及解决办法: 所用 docker fabric 2.4 镜像的 Golang 版本太低不反对泛型,须要删除并重新安装 docker fabric 2.4 (只管 tag 一样,但镜像内容会更新)。

  2. 智能合约调用时遇到谬误:

    [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) 革除所有再重试
    应用

  3. 遇到谬误:

    # 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 文件对应依赖替换如下:

    github.com/hyperledger/fabric-sdk-go v1.0.1-0.20220117114400-c848d119936b。
  4. 遇到谬误:

    Error compiling schema for ****[**]. Return schema invalid. Object has no key 'Wrapper[[]<part of module name>'

    起因及解决办法:智能合约返回值不反对泛型,将智能合约返回值换成 interface{} 即可。

  5. 查问历史记录呈现遇到谬误:

    Error: could not assemble transaction: ProposalResponsePayloads do not match (base64):

    起因及解决办法:链码输入(返回)数据中不要应用地址传递(推荐值传递),因为地址都是动态分配,每次取到的值都不一样,造成共识失败。

  6. 遇到谬误:

    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 ,这是一个 fabric 的 bug,对于简单返回类型倡议间接返回字符串 string

<!-- ## 参考 -->
<!-- 1: 作者. 文章题目. 发表地. [发表或更新日期] -->


  1. 1 ↩