HyperLedger Fabric官方文档
中文网址:https://hyperledger-fabric.readthedocs.io/zh_CN/latest
英文网址:https://hyperledger-fabric.readthedocs.io/en/latest
一般情况英文网址的内容更全面,版本也比中文新。
前言
本教程介绍了 Fabric 应用程序如何与已部署的区块链网络进行交互。本教程使用使用 Fabric Gateway 客户端 API 构建的示例程序来调用智能合约,该智能合约使用智能合约 API 查询和更新账本 – 在将智能合约部署到通道中详细描述。
asset-transfer-basic
该示例演示了如何创建、更新和查询资产。它涉及以下2个组件:
- 示例应用程序。调用区块链网络,调用智能合约中实现的交易。该应用程序位于
asset-transfer-basic/application-gateway-typescript
目录中。 - 智能合约。实现与账本交互的交易,该智能合约位于
asset-transfer-basic/chaincode-(typerscript,go,java)
目录中。
本示例使用TypeScript智能合约,主要有以下两部分内容。
搭建区块链网络。 我们的应用程序需要一个区块链网络与之交互,因此我们将启动一个基础网络并为我们的应用程序部署一个智能合约。
运行示例应用程序与智能合约进行交互。我们的应用程序将使用assetTransfer 智能合约在账本上创建、查询和更新资产。我们将逐步介绍应用程序的代码及其调用的交易,包括创建一些初始资产、查询资产、查询一系列资产、创建新资产以及将资产转移给新所有者。
示例内容
设置区块链网络
启动区块链网络
cd fabric-samples/test-network./network.sh down./network.sh up createChannel -c mychannel -ca
此命令将部署具有2个Peer、1个排序服务和3个证书颁发机构(Orderer、Org1、Org2)的 Fabric 测试网络。我们没有使用 cryptogen 工具,而是使用证书颁发机构启动测试网络,因此使用了-ca标志。此外,当证书颁发机构启动时,org admin 用户注册会自我启动(bootstrapped)。
部署智能合约
示例使用typerscrip
./network.sh deployCC -ccn basic -ccp ../asset-transfer-basic/chaincode-typescript/ -ccl typescript
想使用go就把最后的typerscript
改成go
,使用java就改成java
准备示例应用程序
打开一个新终端到application-gateway-typescript
目录。
cd asset-transfer-basic/application-gateway-typescript
安装依赖项构建应用程序
npm install
运行示例应用程序
运行:
npm start
1.建立到网关的gRPC连接
gRPC
客户端应用程序与 Fabric Gateway 服务建立gRPC连接,该服务将用于与区块链网络进行交易。为此,它只需要 Fabric Gateway 的端口地址,如果配置为使用 TLS,则需要适当的 TLS 证书。在本示例中,网关端口地址是提供 Fabric Gateway 服务的Peer的地址。
TypeScript 应用程序使用签名证书颁发机构的 TLS 证书创建 gRPC 连接,以便可以验证网关的 TLS 证书的真实性。
要成功建立 TLS 连接,客户端使用的端口地址必须与网关的 TLS 证书中的地址匹配。由于客户端通过一个地址访问网关的 Docker 容器,localhost因此指定了一个 gRPC 选项以强制将此端口地址解释为网关的配置主机名。
const peerEndpoint = 'localhost:7051';async function newGrpcConnection(): Promise<grpc.Client> {const tlsRootCert = await fs.readFile(tlsCertPath);const tlsCredentials = grpc.credentials.createSsl(tlsRootCert);return new grpc.Client(peerEndpoint, tlsCredentials, {'grpc.ssl_target_name_override': 'peer0.org1.example.com',});}
2.创建网关连接
然后,应用程序创建一个Gateway连接,用于访问NetworksFabric 网关可访问的任何(类似于通道),并随后智能Contracts部署到这些网络。连接 Gateway具有三个要求:
- gRPC 连接到 Fabric 网关。
- 用于与网络进行交易的客户端身份。
- 用于为客户端身份生成数字签名的签名实现。
示例应用程序使用 Org1 用户的 X.509 证书作为客户端身份,并使用基于该用户私钥的签名实现。
const client = await newGrpcConnection();const gateway = connect({client,identity: await newIdentity(),signer: await newSigner(),});async function newIdentity(): Promise<Identity> {const credentials = await fs.readFile(certPath);return { mspId: 'Org1MSP', credentials };}async function newSigner(): Promise<Signer> {const privateKeyPem = await fs.readFile(keyPath);const privateKey = crypto.createPrivateKey(privateKeyPem);return signers.newPrivateKeySigner(privateKey);}
3.访问要调用的智能合约
示例应用程序使用Gateway连接来获取对 的引用Network,然后是 Contract部署在该网络上的链代码中的默认值。
const network = gateway.getNetwork(channelName);const contract = network.getContract(chaincodeName);
当链码包包含多个智能合约时,您可以将链码的名称和特定智能合约的名称作为参数提供给getContract() 调用。例如:
const contract = network.getContract(chaincodeName, smartContractName);
4.用sample asset更新账本
链码包初始部署后,账本立即为空。该应用程序用于 submitTransaction()调用InitLedger事务函数,该函数使用一些示例资产填充分类帐。submitTransaction()将使用 Fabric Gateway 来:
- 批准交易提案。
- 将背书的交易提交给排序服务。
- 等待事务提交,更新账本状态。
示例应用程序InitLedger调用:
await contract.submitTransaction('InitLedger');
5.调用交易函数
现在,应用程序已准备好执行业务逻辑,通过调用智能合约上的交易功能来查询、创建附加资产和修改账本上的资产。
查询所有资产
应用程序用于evaluateTransaction()通过执行只读事务调用来查询分类帐。 evaluateTransaction()将使用 Fabric Gateway 调用事务函数并返回其结果。交易不会发送到排序服务,也不会发生账本更新。
下面,示例应用程序只是获取在我们填充分类帐时在上一步中创建的所有资产。
示例应用程序GetAllAssets调用:
const resultBytes = await contract.evaluateTransaction('GetAllAssets');const resultJson = utf8Decoder.decode(resultBytes);const result = JSON.parse(resultJson);console.log('*** Result:', result);
创建新资产
示例应用程序提交交易以创建新资产。
示例应用程序CreateAsset调用:
const assetId = `asset${Date.now()}`;await contract.submitTransaction('CreateAsset',assetId,'yellow','5','Tom','1300',);
更新资产
示例应用程序提交交易以转移新创建资产的所有权。这次使用 调用交易submitAsync(),它在成功将背书交易提交到排序服务后返回,而不是等到交易提交到分类帐。这允许应用程序在等待提交时使用事务结果执行工作。
示例应用程序TransferAsset调用:
const commit = await contract.submitAsync('TransferAsset', {arguments: [assetId, 'Saptha'],});const oldOwner = utf8Decoder.decode(commit.getResult());console.log(`*** Successfully submitted transaction to transfer ownership from ${oldOwner} to Saptha`);console.log('*** Waiting for transaction commit');const status = await commit.getStatus();if (!status.successful) {throw new Error(`Transaction ${status.transactionId} failed to commit with status code ${status.code}`);}console.log('*** Transaction committed successfully');
查询更新的资产
然后,示例应用程序评估转移资产的查询,显示它是使用描述的属性创建的,然后转移给新所有者。
示例应用程序ReadAsset调用:
const resultBytes = await contract.evaluateTransaction('ReadAsset', assetId);const resultJson = utf8Decoder.decode(resultBytes);const result = JSON.parse(resultJson);console.log('*** Result:', result);
处理交易错误
该序列的最后一部分演示了提交交易时的错误。在此示例中,应用程序尝试提交UpdateAsset交易,但指定了不存在的资产 ID。事务函数返回错误响应,submitTransaction()调用失败。
失败可能会产生几种不同类型的submitTransaction()错误,指示提交流程中发生错误的点,并包含使应用程序能够适当响应的附加信息。 有关可能生成的不同错误类型的详细信息,请参阅API 文档。
示例应用程序调用失败UpdateAsset:
try {await contract.submitTransaction('UpdateAsset','asset70','blue','5','Tomoko','300',);console.log('******** FAILED to return an error');} catch (error) {console.log('*** Successfully caught the error: \n', error);}
EndorseErrortype表示背书失败, gRPC状态码ABORTED表示应用调用Fabric Gateway成功,但背书过程失败。的 gRPC 状态代码UNAVAILABLEorDEADLINE_EXCEEDED将表明 Fabric Gateway 不可访问或未收到及时响应,因此重试操作可能是合适的。