欢迎访问宙启技术站
智能推送

HyperLeger Fabric开发(十)——资产交易平台实战

发布时间:2023-05-13 21:37:10

本文主要介绍如何使用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('