HyperLeger Fabric开发(十)——资产交易平台实战
本文主要介绍如何使用HyperLedger Fabric搭建资产交易平台的实战操作。我们将会创建一个简单的资产交易平台,包括资产的创建和交易,同时提供防止双重支付的机制。
1. 安装HyperLedger Fabric
首先,我们需要安装并配置HyperLedger Fabric。安装步骤可以参考官方文档:https://hyperledger-fabric.readthedocs.io/en/latest/install.html
2. 创建区块链网络
使用Fabric提供的二进制文件和docker-compose.yaml文件,我们可以很容易地创建一个简单的Fabric网络。创建网络的详细步骤可以参考本系列的前几篇文章。
3. 创建智能合约
接下来,我们需要创建一个智能合约,用于资产的创建和交易。在这个实例中,我们将使用基于Go语言的Chaincode。Go语言Chaincode的详细说明可以参考官方文档:https://hyperledger-fabric.readthedocs.io/en/latest/chaincode4noahsark.html
我们的智能合约需要实现以下几个功能:
- 创建资产
- 查询资产列表
- 查询指定资产的所有权
- 转移资产所有权
以下是一个简单的示例Chaincode:
package main
import (
"encoding/json"
"fmt"
"strconv"
"github.com/hyperledger/fabric-contract-api-go/contractapi"
)
//Asset represents a single asset on the ledger
type Asset struct {
OwnerName string json:"ownerName"
AssetType string json:"assetType"
AssetValue int json:"assetValue"
}
//SimpleAssetExchange is a smart contract for managing assets
type SimpleAssetExchange struct {
contractapi.Contract
}
//InitLedger creates sample assets on the ledger
func (s *SimpleAssetExchange) InitLedger(ctx contractapi.TransactionContextInterface) error {
assets := []Asset{
{OwnerName: "Alice", AssetType: "House", AssetValue: 100000},
{OwnerName: "Bob", AssetType: "Car", AssetValue: 50000},
}
for i, asset := range assets {
assetJSON, err := json.Marshal(asset)
if err != nil {
return err
}
err = ctx.GetStub().PutState(fmt.Sprintf("ASSET%d", i), assetJSON)
if err != nil {
return err
}
}
return nil
}
//CreateAsset creates a new asset on the ledger
func (s *SimpleAssetExchange) CreateAsset(ctx contractapi.TransactionContextInterface, assetID string, ownerName string, assetType string, assetValue int) error {
asset := Asset{
OwnerName: ownerName,
AssetType: assetType,
AssetValue: assetValue,
}
assetJSON, err := json.Marshal(asset)
if err != nil {
return err
}
err = ctx.GetStub().PutState(assetID, assetJSON)
if err != nil {
return err
}
return nil
}
//QueryAllAssets returns all assets on the ledger
func (s *SimpleAssetExchange) QueryAllAssets(ctx contractapi.TransactionContextInterface) ([]Asset, error) {
startKey := "ASSET0"
endKey := "ASSET999"
resultsIterator, err := ctx.GetStub().GetStateByRange(startKey, endKey)
if err != nil {
return nil, err
}
defer resultsIterator.Close()
var assets []Asset
for resultsIterator.HasNext() {
queryResponse, err := resultsIterator.Next()
if err != nil {
return nil, err
}
var asset Asset
err = json.Unmarshal(queryResponse.Value, &asset)
if err != nil {
return nil, err
}
assets = append(assets, asset)
}
return assets, nil
}
//QueryAssetOwner returns the owner of a specified asset
func (s *SimpleAssetExchange) QueryAssetOwner(ctx contractapi.TransactionContextInterface, assetID string) (string, error) {
assetJSON, err := ctx.GetStub().GetState(assetID)
if err != nil {
return "", err
}
if assetJSON == nil {
return "", fmt.Errorf("Asset with ID %s does not exist", assetID)
}
var asset Asset
err = json.Unmarshal(assetJSON, &asset)
if err != nil {
return "", err
}
return asset.OwnerName, nil
}
//TransferAssetOwner transfers the ownership of a specified asset to a new owner
func (s *SimpleAssetExchange) TransferAssetOwner(ctx contractapi.TransactionContextInterface, assetID string, newOwnerName string) error {
assetJSON, err := ctx.GetStub().GetState(assetID)
if err != nil {
return err
}
if assetJSON == nil {
return fmt.Errorf("Asset with ID %s does not exist", assetID)
}
var asset Asset
err = json.Unmarshal(assetJSON, &asset)
if err != nil {
return err
}
asset.OwnerName = newOwnerName
assetJSON, err = json.Marshal(asset)
if err != nil {
return err
}
err = ctx.GetStub().PutState(assetID, assetJSON)
if err != nil {
return err
}
return nil
}
func main() {
chaincode, err := contractapi.NewChaincode(new(SimpleAssetExchange))
if err != nil {
fmt.Printf("Error starting SimpleAssetExchange Chaincode: %s", err.Error())
return
}
if err := chaincode.Start(); err != nil {
fmt.Printf("Error starting SimpleAssetExchange Chaincode: %s", err.Error())
}
}
在这个智能合约中,我们定义了一个Asset结构体,用于表示一个资产的所有者、类型和价值。我们提供了以下函数:
- InitLedger:初始化网络上的样例资产。在这里,我们创建了两个资产,并将它们放在了ASSET0和ASSET1的键值下。
- CreateAsset:创建新资产,将其存储到键值对里。
- QueryAllAssets:查询所有资产。
- QueryAssetOwner:查询特定资产的所有者。
- TransferAssetOwner:将一个资产的所有权转移给新的所有者,防止双重支付。
4. 安装并实例化智能合约
在上一步中,我们定义了智能合约,现在我们需要在Fabric网络上安装和实例化它。可以使用fabric peer cli的命令来实现这一步骤:
peer chaincode install -n exchange -v 1 -p github.com/simpleAssetExchange/go
peer chaincode instantiate -o orderer.example.com:7050 -C mychannel -n exchange -v 1 -c '{"Args":[]}'
在这个命令中,我们将合约安装到网络上,然后实例化它。当实例化时,我们需要指定一个配置文件,以定义合约的参数。在这里,我们使用“{}”符号定义一个空参数列表。
5. 创建客户端
现在我们已经有了一个智能合约和一个Fabric网络,下一步是创建一个客户端,可以与合约进行交互。在这个例子中,我们将创建一个基于Node.js和Fabric Node SDK的命令行客户端。
以下是一个简单的例子,它演示了如何与智能合约进行交互:
`javascript
const { Gateway, Wallets } = require('fabric-network');
const path = require('path');
const ccpPath = path.resolve(__dirname, '..', '..', 'network', 'connection.json');
async function main() {
try {
// Create a new file system based wallet for managing identities.
const walletPath = path.join(process.cwd(), 'wallet');
const wallet = await Wallets.newFileSystemWallet(walletPath);
// Check to see if we've already enrolled the user.
const identity = await wallet.get('user1');
if (!identity) {
console.log('An identity for the user "user1" does not exist in the wallet');
return;
}
// Create a new gateway for connecting to our peer node.
const gateway = new Gateway();
await gateway.connect(ccpPath, { wallet, identity: 'user1', discovery: { enabled: true, asLocalhost: true } });
// Get the network (channel) our contract is deployed to.
const network = await gateway.getNetwork('mychannel');
// Get the contract from the network.
const contract = network.getContract('
