HyperLedger Fabric官方文档
中文网址:https://hyperledger-fabric.readthedocs.io/zh_CN/latest
英文网址:https://hyperledger-fabric.readthedocs.io/en/latest
一般情况英文网址的内容更全面,版本也比中文新。
前言
终端用户通过调用智能合约与区块链账本进行交互。在 Hyperledger Fabric 中,智能合约部署在称为链码的包中。想要验证交易或查询账本的组织需要在其Peer节点上安装链码。在加入通道的节点上安装链码后,通道成员可以将链码部署到通道并使用链码中的智能合约在通道账本上创建或更新资产。
链码部署到通道使用称为 Fabric 链码生命周期的过程。Fabric 链码生命周期允许多个组织在链码用于创建交易之前就如何操作链码达成一致。例如,虽然背书策略指定了哪些组织需要执行链码来验证交易,但通道成员需要使用 Fabric 链码生命周期来就链码背书策略达成一致。有关如何在通道上部署和管理链代码的更深入概述,请参阅Fabric 链代码生命周期。
使用Peer CLI部署asset-transfer-basic
链码的四个步骤:
- 第一步:打包智能合约
- 第二步:安装链码包
- 第三步:批准链码定义
- 第四步:将链码定义提交到通道
设置Logspout(可选)
此步骤不是必需的,但对于排查链代码非常有用。要监控智能合约的日志,管理员可以使用该logspout
工具查看一组 Docker 容器的聚合输出。该工具将来自不同 Docker 容器的输出流收集到一个位置,从而可以轻松地从单个窗口查看正在发生的事情。这可以帮助管理员在安装智能合约或开发人员调用智能合约时调试问题。由于某些容器纯粹是为了启动智能合约而创建的,并且只存在很短的时间,因此从您的网络收集所有日志会很有帮助。
Fabric 示例的目录中monitordocker.sh
已经包含一个用于安装和配置 Logspout 的脚本,位于test-network
目录中。Logspout 工具会不断地将日志流式传输到您的终端,因此您需要使用新的终端窗口。打开一个新终端并导航到该test-network
目录。
cd fabric-samples/test-network
然后,您可以通过运行以下命令来启动 Logspout:
./monitordocker.sh fabric_test
打包智能合约
这里只介绍GO语言
在打包链码之前,我们需要安装链码依赖项。导航到包含资产转移(基本)链码的 Go 版本的文件夹。
cd fabric-samples/asset-transfer-basic/chaincode-go
该示例使用 Go 模块来安装链码依赖项。依赖项列在asset-transfer-basic/chaincode-go
目录中的go.mod
文件。
该go.mod
文件将 Fabric 合约 API 导入到智能合约包中。您可以打开asset-transfer-basic/chaincode-go/chaincode/smartcontract.go
以查看如何使用合约 API 来定义SmartContract
智能合约开头的类型:
// SmartContract provides functions for managing an Asset
type SmartContract struct {
contractapi.Contract
}
然后,使用Smartcontract类型为在智能合约中定义的功能创建事务上下文,这些功能将数据读写到区块链账本。
// CreateAsset issues a new asset to the world state with given details.
func (s *SmartContract) CreateAsset(ctx contractapi.TransactionContextInterface, id string, color string, size int, owner string, appraisedValue int) error {
exists, err := s.AssetExists(ctx, id)
if err != nil {
return err
}
if exists {
return fmt.Errorf("the asset %s already exists", id)
}
asset := Asset{
ID: id,
Color: color,
Size: size,
Owner: owner,
AppraisedValue: appraisedValue,
}
assetJSON, err := json.Marshal(asset)
if err != nil {
return err
}
return ctx.GetStub().PutState(id, assetJSON)
}
安装智能合约依赖,在asset-transfer-basic/chaincode-go
目录下运行:
GO111MODULE=on go mod vendor
然后可以回到test-network
目录
使用Peer CLI创建链码包。二进制文件位于fabric-samples/bin
。添加二进制文件到当前路径:
export PATH=${PWD}/../bin:$PATH
同时要设置环境变量FABRIC_CFG_PATH
指向core.yaml
:
export FABRIC_CFG_PATH=$PWD/../config/
创建链码包:
peer lifecycle chaincode package basic.tar.gz --path ../asset-transfer-basic/chaincode-go/ --lang golang --label basic_1.0
安装链码包
在我们打包完asset-transfer-basic
智能合约后,我们可以在我们的Peer节点上安装链码。链码需要安装在每个将支持交易的对等方上。因为我们要将背书策略设置为需要来自 Org1 和 Org2 的背书,所以我们需要在两个组织运营的对等节点上安装链码:
- peer0.org1.example.com
- peer0.org2.example.com
让我们先在 Org1 peer 上安装链码。设置以下环境变量,以peer Org1 管理员用户身份操作 CLI。将CORE_PEER_ADDRESS
指向 Org1 Peer,peer0.org1.example.com
:
export CORE_PEER_TLS_ENABLED=true
export CORE_PEER_LOCALMSPID="Org1MSP"
export CORE_PEER_TLS_ROOTCERT_FILE=${PWD}/organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt
export CORE_PEER_MSPCONFIGPATH=${PWD}/organizations/peerOrganizations/org1.example.com/users/[email protected]/msp
export CORE_PEER_ADDRESS=localhost:7051
安装链码:
peer lifecycle chaincode install basic.tar.gz
使用Peer Org2进行同样的操作:
export CORE_PEER_LOCALMSPID="Org2MSP"
export CORE_PEER_TLS_ROOTCERT_FILE=${PWD}/organizations/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt
export CORE_PEER_MSPCONFIGPATH=${PWD}/organizations/peerOrganizations/org2.example.com/users/[email protected]/msp
export CORE_PEER_ADDRESS=localhost:9051
peer lifecycle chaincode install basic.tar.gz
批准链码定义
安装链码包后,您需要为您的组织批准链码定义。该定义包括链码治理的重要参数,例如名称、版本和链码背书策略。
在部署链码之前需要批准链码的通道成员集由该/Channel/Application/LifecycleEndorsement
策略管理。默认情况下,此策略要求大多数通道成员需要批准链码才能在通道上使用。因为我们在通道上只有两个组织,并且2的大多数是 2,所以我们需要作为 Org1 和 Org2批准asset-transfer-basic
的链码定义。
如果组织已在其对等节点上安装了链码,则他们需要在其组织批准的链码定义中包含 packageID。packageID 用于将安装在Peer节点上的链码与批准的链码定义相关联,并允许组织使用链码来背书交易。您可以使用peer lifecycle chaincode queryinstalled
命令查询您的对等点来查找链代码的包 ID。
peer lifecycle chaincode queryinstalled
命令行会显示已经安装的链码packageID。
当我们批准链码时,我们将使用packageID,将其保存为环境变量。将返回的包 ID 粘贴到下面的命令的“=”后面。
注意:并非所有用户的 packageID 都相同,因此您需要使用上一步命令窗口返回的包 ID 完成此步骤。
export CC_PACKAGE_ID=
+复制的packageID
当前是以Peer Org2 管理员身份运行 CLI,因此我们可以作为Org2批准asset-transfer-basic
的链代码定义。链码在组织级别获得批准,因此该命令只需要针对一个节点。使用Gossip将批准消息分发给组织内的其他Peer。使用命令 peer lifecycle chaincode approveformyorg
批准链代码定义:
peer lifecycle chaincode approveformyorg -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com --channelID mychannel --name basic --version 1.0 --package-id $CC_PACKAGE_ID --sequence 1 --tls --cafile "${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem"
上面的命令使用--package-id
标志在l链码定义中包含pakageID。--sequence
参数是一个整数,用于跟踪链码被定义或更新的次数。由于链码是第一次部署到通道中,因此序列号为1。当asset-transfer-basic
链码升级时,序列号将增加到2。如果你正在使用Fabric Chaincode Shim API提供的低级别APIl,你可以将--Init-required
标志传递给上面的命令,以请求执行Init函数来初始化Chaincode。链代码的第一次调用需要以Init函数为目标,并包含--isInit
标志,然后才能使用链代码中的其他函数与账本交互。
我们可以为approveformyor
g命令提供--signature-policy
或--channel-config-policy
参数来指定链码背书策略。背书策略指定了针对给定的链码交易需要多少属于不同通道成员的Peer来验证。因为我们没有设置策略,所以asset-transfer-basic
的定义将使用默认的背书策略,该策略要求在提交交易时,交易必须得到大多数在场的通道成员的背书。这意味着,如果在通道中添加或删除新的组织,背书政策将自动更新,以要求更多或更少的背书。在本教程中,默认策略将需要2 / 2的大多数,事务将需要由来自Org1和Org2的Peer批准。如果需要指定自定义的背书策略,可以通过背书策略操作指南了解该策略的语法。
您需要批准具有管理员角色的标识的链码定义。因此,CORE_PEER_MSPCONFIGPATH
变量需要指向包含管理标识的MSP文件夹。您不能通过客户端用户来批准链码定义。批准消息需要提交给排序服务,它将验证管理员签名,然后将批准分发给您的对等点。
作为Org1批准链码定义也是一样:
export CORE_PEER_LOCALMSPID="Org1MSP"
export CORE_PEER_MSPCONFIGPATH=${PWD}/organizations/peerOrganizations/org1.example.com/users/[email protected]/msp
export CORE_PEER_TLS_ROOTCERT_FILE=${PWD}/organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt
export CORE_PEER_ADDRESS=localhost:7051
peer lifecycle chaincode approveformyorg -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com --channelID mychannel --name basic --version 1.0 --package-id $CC_PACKAGE_ID --sequence 1 --tls --cafile "${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem"
提交链码定义到通道
在足够数量的组织批准链码定义后,一个组织可以将链码定义提交给通道。如果大多数通道成员都批准了定义,则提交事务将成功,链码定义中约定的参数将在通道上实现。
您可以使用 peer lifecycle chaincode checkcommitreadiness
命令检查通道成员是否批准了相同的链码定义。用于该checkcommitreadiness
命令的标志与用于批准您的组织的链代码的标志相同。但是,您不需要包含--package-id
标志。
peer lifecycle chaincode checkcommitreadiness --channelID mychannel --name basic --version 1.0 --sequence 1 --tls --cafile "${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem" --output json
该命令将生成一个JSON映射,显示通道成员是否批准。
由于作为通道成员的两个组织都批准了相同的参数,因此链码定义可以提交给通道。可以使用peer lifecycle chaincode commit
命令将链码定义提交给该通道。提交命令也需要由组织管理员提交。
peer lifecycle chaincode commit -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com --channelID mychannel --name basic --version 1.0 --sequence 1 --tls --cafile "${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem" --peerAddresses localhost:7051 --tlsRootCertFiles "${PWD}/organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt" --peerAddresses localhost:9051 --tlsRootCertFiles "${PWD}/organizations/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt"
使用--peerAddresses
标志来定位peer0.org1.example.comOrg1
和peer0.org2.example.com
。commit
交易被提交给加入通道的对等点,以查询操作Peer的组织批准的链码定义。该命令需要定位来自足够数量的组织的Peer,以满足部署链代码的策略。由于批准分布在每个组织内,因此您可以针对属于通道成员的任何Peer。
通道成员对链码定义的背书提交给排序服务,以添加到块中并分发到通道。然后通道上的Peer验证是否有足够数量的组织批准了链码定义。命令peer lifecycle chaincode commit
将在返回响应之前等待Peer的验证。
使用peer lifecycle chaincode querycommitted
命令来确认链码定义是否被提交到通道。
peer lifecycle chaincode querycommitted --channelID mychannel --name basic --cafile "${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem"
调用链码
将链码定义提交到通道后,链码将在加入安装链码的通道的Peer点上启动。asset-transfer-basic
链码现在已准备好由客户端应用程序调用。使用以下命令在分类帐上创建一组初始资产。请注意,invoke 命令需要针对足够数量的Peer以满足链码背书策略。(注意 CLI 不访问 Fabric Gateway 对等体,因此必须指定每个背书对等体。)
peer chaincode invoke -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com --tls --cafile "${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem" -C mychannel -n basic --peerAddresses localhost:7051 --tlsRootCertFiles "${PWD}/organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt" --peerAddresses localhost:9051 --tlsRootCertFiles "${PWD}/organizations/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt" -c '{"function":"InitLedger","Args":[]}'
查询链码创建的car集合
peer chaincode query -C mychannel -n basic -c '{"Args":["GetAllAssets"]}'
更新智能合约
您可以使用相同的 Fabric 链码生命周期流程来升级已部署到通道的链码。通道成员可以通过安装新的链码包,然后批准具有新包 ID、新链码版本和序列号加一的链码定义来升级链码。在将链码定义提交到通道后,可以使用新的链码。此过程允许通道成员在升级链代码时进行协调,并确保有足够数量的通道成员准备好使用新的链代码,然后再将其部署到通道。
通道成员还可以使用升级过程来更改链码背书策略。通过使用新的背书策略批准链码定义并将链码定义提交给通道,通道成员可以更改管理链码的背书策略,而无需安装新的链码包。
为了提供升级我们刚刚部署的asset-transfer-basic
链码的场景,我们假设 Org1 和 Org2 想要安装用另一种语言编写的链代码版本。他们将使用 Fabric 链码生命周期来更新链码版本,并确保两个组织在新链码在通道上激活之前都已安装它。
我们将假设 Org1 和 Org2 最初安装了asset-transfer-basic
链码的 GO 版本,但使用 JavaScript 编写的链码会更舒服。第一步是打包资产转移(基本)链码的 JavaScript 版本。如果您在学习本教程时使用 JavaScript 说明打包链代码,则可以按照打包用Go或TypeScript编写的链代码的步骤安装新的链代码二进制文件。
安装依赖:
cd ../asset-transfer-basic/chaincode-javascript
npm install
cd ../../test-network
打包JavaScript链码:
export PATH=${PWD}/../bin:$PATH
export FABRIC_CFG_PATH=$PWD/../config/
export CORE_PEER_MSPCONFIGPATH=${PWD}/organizations/peerOrganizations/org1.example.com/users/[email protected]/msp
peer lifecycle chaincode package basic_2.tar.gz --path ../asset-transfer-basic/chaincode-javascript/ --lang node --label basic_2.0
作为Org安装链码:
export CORE_PEER_TLS_ENABLED=true
export CORE_PEER_LOCALMSPID="Org1MSP"
export CORE_PEER_TLS_ROOTCERT_FILE=${PWD}/organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt
export CORE_PEER_MSPCONFIGPATH=${PWD}/organizations/peerOrganizations/org1.example.com/users/[email protected]/msp
export CORE_PEER_ADDRESS=localhost:7051
peer lifecycle chaincode install basic_2.tar.gz
查询packageID:
peer lifecycle chaincode queryinstalled
将新版本的ID复制到环境变量:
export NEW_CC_PACKAGE_ID=
+新packageID
同意链码定义:
peer lifecycle chaincode approveformyorg -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com --channelID mychannel --name basic --version 2.0 --package-id $NEW_CC_PACKAGE_ID --sequence 2 --tls --cafile "${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem"
Org2一样:
export CORE_PEER_LOCALMSPID="Org2MSP"
export CORE_PEER_TLS_ROOTCERT_FILE=${PWD}/organizations/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt
export CORE_PEER_MSPCONFIGPATH=${PWD}/organizations/peerOrganizations/org2.example.com/users/[email protected]/msp
export CORE_PEER_ADDRESS=localhost:9051
peer lifecycle chaincode install basic_2.tar.gz
peer lifecycle chaincode approveformyorg -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com --channelID mychannel --name basic --version 2.0 --package-id $NEW_CC_PACKAGE_ID --sequence 2 --tls --cafile "${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem"
检查sequence2是否可以提交到通道:
peer lifecycle chaincode checkcommitreadiness --channelID mychannel --name basic --version 2.0 --sequence 2 --tls --cafile "${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem" --output json
升级:
peer lifecycle chaincode commit -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com --channelID mychannel --name basic --version 2.0 --sequence 2 --tls --cafile "${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem" --peerAddresses localhost:7051 --tlsRootCertFiles "${PWD}/organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt" --peerAddresses localhost:9051 --tlsRootCertFiles "${PWD}/organizations/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt"
可以使用docker ps
查看是否更新
在更新前使用--init-required
需要调用初始化函数初始化账本:
peer chaincode invoke -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com --tls --cafile "${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem" -C mychannel -n basic --peerAddresses localhost:7051 --tlsRootCertFiles "${PWD}/organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt" --peerAddresses localhost:9051 --tlsRootCertFiles "${PWD}/organizations/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt" -c '{"function":"CreateAsset","Args":["asset8","blue","16","Kelley","750"]}'