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

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

一、资产交易平台需求分析

1、资产交易平台的功能

A、用户开户、销户功能
B、资产登记,即资产上链,资产绑定到用户
C、资产转让,即资产所有权的变更
D、查询功能,包括用户查询、资产查询、资产历史变更查询

2、业务实体

A、用户,属性包括名字、标识、资产列表、
B、资产,属性包括名字、标识、特殊属性列表
C、资产变更记录,属性包括资产标识、资产源所有者、资产变更后所有者

3、交互方法

A、用户注册,参数包括用户名称、用户标识
B、销户,参数包括用户标识
C、资产登记,参数包括资产名称、资产标识、特殊属性列表、资产所有者
D、资产转让,参数包括资产所有者、资产标识、资产受让者
E、用户查询,参数包括用户标识,返回用户实体
F、资产查询,参数包括资产标识,返回资产实体
G、资产变更记录,参数包括资产标识,记录类型(登记/转让/全部),返回资产变更记录列表。

二、链码开发

1、链码编写

链码需要定义资产交易平台定义的所有实体和交互方法,然后在Invoke接口中调用相应的交互方法。
在Fabric工程目录AssetExchange/chaincode/assetexchange目录下进行链码开发,链码AssetExchangeChainCode.go实现如下:

package main

import (
   "fmt"
   "encoding/json"
   "github.com/hyperledger/fabric/core/chaincode/shim"
   "github.com/hyperledger/fabric/protos/peer"
)

// 用户
type User struct {
   Name string `json:"name"`
   ID string `json:"id"`
   Assets []string `json:"assets"`
}
// 资产
type Asset struct {
   Name string `json:"name"`
   ID string `json:"id"`
   Metadata string `json:"metadata"`
}
// 资产变更记录
type AssetHistory struct {
   AssetID string `json:"asset_id"`
   OriginOwnerID string `json:"origin_owner_id"`
   CurrentOwnerID string `json:"current_owner_id"`
}
// 原始用户占位符
const (
   originOwner = "originOwnerPlaceholder"
)

func constructUserKey(userId string)string{
   return fmt.Sprint("user_%s",userId)
}

func constructAssetKey(assetID string)string{
   return fmt.Sprint("asset_%s",assetID)
}
// 用户注册(开户)
func userRegister(stub shim.ChaincodeStubInterface, args []string) peer.Response{
   // step 1:检查参数个数
   if len(args) != 2{
      return shim.Error("Not enough args")
   }

   // step 2:验证参数正确性
   name := args[0]
   id := args[1]
   if name == "" || id == ""{
      return shim.Error("Invalid args")
   }
   // step 3:验证数据是否存在
   if userBytes, err := stub.GetState(constructUserKey(id));err != nil || len(userBytes) != 0{
      return shim.Error("User alreay exist")
   }
   // step 4: 写入状态
   user := User{
      Name:name,
      ID:id,
      Assets:make([]string,0),
   }
   // 序列化对象
   userBytes, err := json.Marshal(user)
   if err != nil{
      return shim.Error(fmt.Sprint("marshal user error %s",err))
   }
   err = stub.PutState(constructUserKey(id), userBytes)
   if err != nil {
      return shim.Error(fmt.Sprint("put user error %s", err))
   }
   return shim.Success(nil)
}

// 用户注销
func userDestroy(stub shim.ChaincodeStubInterface,args []string)peer.Response{
   // step 1:检查参数个数
   if len(args) != 1{
      return shim.Error("Not enough args")
   }

   // step 2:验证参数正确性
   id := args[0]
   if id == ""{
      return shim.Error("Invalid args")
   }
   // step 3:验证数据是否存在
   userBytes, err := stub.GetState(constructUserKey(id));
   if err != nil || len(userBytes) == 0{
      return shim.Error("User not found")
   }
   // step 4: 写入状态
   if err := stub.DelState(constructUserKey(id)); err != nil {
      return shim.Error(fmt.Sprintf("delete user error: %s", err))
   }
   // 删除用户名下的资产
   user := new(User)
   err = json.Unmarshal(userBytes,user)
   if err != nil{
      return shim.Error(fmt.Sprintf("unmarshal user error: %s", err))
   }
   for _,assetId := range user.Assets{
      if err := stub.DelState(constructAssetKey(assetId)); err != nil {
         return shim.Error(fmt.Sprintf("delete asset error: %s", err))
      }
   }

   return shim.Success(nil)
}

// 资产登记
func assetEnroll(stub shim.ChaincodeStubInterface,args []string)peer.Response{
   // step 1:检查参数个数
   if len(args) != 4 {
      return shim.Error("Not enough args")
   }

   // step 2:验证参数正确性
   assetName := args[0]
   assetId := args[1]
   metadata := args[2]
   ownerId := args[3]
   if assetName == "" || assetId == "" || ownerId == ""{
      return shim.Error("Invalid args")
   }
   // step 3:验证数据是否存在
   userBytes, err := stub.GetState(constructUserKey(ownerId))
   if err != nil || len(userBytes) == 0{
      return shim.Error("User not found")
   }
   if assetBytes, err := stub.GetState(constructAssetKey(assetId)); err == nil && len(assetBytes) != 0 {
      return shim.Error("Asset already exist")
   }
   // step 4: 写入状态
   asset := &Asset{
      Name:     assetName,
      ID:       assetId,
      Metadata: metadata,
   }
   assetBytes, err := json.Marshal(asset)
   if err != nil {
      return shim.Error(fmt.Sprintf("marshal asset error: %s", err))
   }
   if err := stub.PutState(constructAssetKey(assetId), assetBytes); err != nil {
      return shim.Error(fmt.Sprintf("save asset error: %s", err))
   }

   user := new(User)
   // 反序列化user
   if err := json.Unmarshal(userBytes, user); err != nil {
      return shim.Error(fmt.Sprintf("unmarshal user error: %s", err))
   }
   user.Assets = append(user.Assets, assetId)
   // 序列化user
   userBytes, err = json.Marshal(user)
   if err != nil {
      return shim.Error(fmt.Sprintf("marshal user error: %s", err))
   }
   if err := stub.PutState(constructUserKey(user.ID), userBytes); err != nil {
      return shim.Error(fmt.Sprintf("update user error: %s", err))
   }

   // 资产变更历史
   history := &AssetHistory{
      AssetID:        assetId,
      OriginOwnerID:  originOwner,
      CurrentOwnerID: ownerId,
   }
   historyBytes, err := json.Marshal(history)
   if err != nil {
      return shim.Error(fmt.Sprintf("marshal assert history error: %s", err))
   }

   historyKey, err := stub.CreateCompositeKey("history", []string{
      assetId,
      originOwner,
      ownerId,
   })
   if err != nil {
      return shim.Error(fmt.Sprintf("create key error: %s", err))
   }

   if err := stub.PutState(historyKey, historyBytes); err != nil {
      return shim.Error(fmt.Sprintf("save assert history error: %s", err))
   }

   return shim.Success(historyBytes)
}

// 资产转让
func assetExchange(stub shim.ChaincodeStubInterface,args []string)peer.Response{
   // step 1:检查参数个数
   if len(args) != 3 {
      return shim.Error("Not enough args")
   }

   // step 2:验证参数正确性
   ownerID := args[0]
   assetID := args[1]
   currentOwnerID := args[2]
   if ownerID == "" || assetID == "" || currentOwnerID == ""{
      return shim.Error("Invalid args")
   }
   // step 3:验证数据是否存在
   originOwnerBytes, err := stub.GetState(constructUserKey(ownerID))
   if err != nil || len(originOwnerBytes) == 0 {
      return shim.Error("user not found")
   }

   currentOwnerBytes, err := stub.GetState(constructUserKey(currentOwnerID))
   if err != nil || len(currentOwnerBytes) == 0 {
      return shim.Error("user not found")
   }

   assetBytes, err := stub.GetState(constructAssetKey(assetID))
   if err != nil || len(assetBytes) == 0 {
      return shim.Error("asset not found")
   }

   // 校验原始拥有者确实拥有当前变更的资产
   originOwner := new(User)
   // 反序列化user
   if err := json.Unmarshal(originOwnerBytes, originOwner); err != nil {
      return shim.Error(fmt.Sprintf("unmarshal user error: %s", err))
   }
   aidexist := false
   for _, aid := range originOwner.Assets {
      if aid == assetID {
         aidexist = true
         break
      }
   }
   if !aidexist {
      return shim.Error("asset owner not match")
   }
   // step 4: 写入状态
   assetIds := make([]string, 0)
   for _, aid := range originOwner.Assets {
      if aid == assetID {
         continue
      }

      assetIds = append(assetIds, aid)
   }
   originOwner.Assets = assetIds

   originOwnerBytes, err = json.Marshal(originOwner)
   if err != nil {
      return shim.Error(fmt.Sprintf("marshal user error: %s", err))
   }
   if err := stub.PutState(constructUserKey(ownerID), originOwnerBytes); err != nil {
      return shim.Error(fmt.Sprintf("update user error: %s", err))
   }

   // 当前拥有者插入资产id
   currentOwner := new(User)
   // 反序列化user
   if err := json.Unmarshal(currentOwnerBytes, currentOwner); err != nil {
      return shim.Error(fmt.Sprintf("unmarshal user error: %s", err))
   }
   currentOwner.Assets = append(currentOwner.Assets, assetID)

   currentOwnerBytes, err = json.Marshal(currentOwner)
   if err != nil {
      return shim.Error(fmt.Sprintf("marshal user error: %s", err))
   }
   if err := stub.PutState(constructUserKey(currentOwnerID), currentOwnerBytes); err != nil {
      return shim.Error(fmt.Sprintf("update user error: %s", err))
   }

   // 插入资产变更记录
   history := &AssetHistory{
      AssetID:        assetID,
      OriginOwnerID:  ownerID,
      CurrentOwnerID: currentOwnerID,
   }
   historyBytes, err := json.Marshal(history)
   if err != nil {
      return shim.Error(fmt.Sprintf("marshal asset history error: %s", err))
   }

   historyKey, err := stub.CreateCompositeKey("history", []string{
      assetID,
      ownerID,
      currentOwnerID,
   })
   if err != nil {
      return shim.Error(fmt.Sprintf("create key error: %s", err))
   }

   if err := stub.PutState(historyKey, historyBytes); err != nil {
      return shim.Error(fmt.Sprintf("save asset history error: %s", err))
   }

   return shim.Success(nil)
}

// 用户查询
func queryUser(stub shim.ChaincodeStubInterface,args []string)peer.Response{
   // step 1:检查参数个数
   if len(args) != 1 {
      return shim.Error("Not enough args")
   }

   // step 2:验证参数正确性
   userID := args[0]
   if userID == ""{
      return shim.Error("Invalid args")
   }
   // step 3:验证数据是否存在
   userBytes, err := stub.GetState(constructUserKey(userID))
   if err != nil || len(userBytes) == 0 {
      return shim.Error("user not found")
   }

   return shim.Success(userBytes)
}

// 资产查询
func queryAsset(stub shim.ChaincodeStubInterface,args []string)peer.Response{
   // step 1:检查参数个数
   if len(args) != 1 {
      return shim.Error("Not enough args")
   }

   // step 2:验证参数正确性
   assetID := args[0]
   if assetID == ""{
      return shim.Error("Invalid args")
   }
   // step 3:验证数据是否存在
   assetBytes, err := stub.GetState(constructAssetKey(assetID))
   if err != nil || len(assetBytes) == 0 {
      return shim.Error("asset not found")
   }

   return shim.Success(assetBytes)
}

// 资产交易记录查询
func queryAssetHistory(stub shim.ChaincodeStubInterface,args []string)peer.Response{
   // step 1:检查参数个数
   if len(args) != 2 && len(args) != 1 {
      return shim.Error("Not enough args")
   }

   // step 2:验证参数正确性
   assetID := args[0]
   if assetID == ""{
      return shim.Error("Invalid args")
   }
   queryType := "all"
   if len(args) == 2 {
      queryType = args[1]
   }

   if queryType != "all" && queryType != "enroll" && queryType != "exchange" {
      return shim.Error(fmt.Sprintf("queryType unknown %s", queryType))
   }
   // step 3:验证数据是否存在
   assetBytes, err := stub.GetState(constructAssetKey(assetID))
   if err != nil || len(assetBytes) == 0 {
      return shim.Error("asset not found")
   }

   // 查询相关数据
   keys := make([]string, 0)
   keys = append(keys, assetID)
   switch queryType {
   case "enroll":
      keys = append(keys, originOwner)
   case "exchange", "all": // 不添加任何附件key
   default:
      return shim.Error(fmt.Sprintf("unsupport queryType: %s", queryType))
   }
   result, err := stub.GetStateByPartialCompositeKey("history", keys)
   if err != nil {
      return shim.Error(fmt.Sprintf("query history error: %s", err))
   }
   defer result.Close()

   histories := make([]*AssetHistory, 0)
   for result.HasNext() {
      historyVal, err := result.Next()
      if err != nil {
         return shim.Error(fmt.Sprintf("query error: %s", err))
      }

      history := new(AssetHistory)
      if err := json.Unmarshal(historyVal.GetValue(), history); err != nil {
         return shim.Error(fmt.Sprintf("unmarshal error: %s", err))
      }

      // 过滤掉不是资产转让的记录
      if queryType == "exchange" && history.OriginOwnerID == originOwner {
         continue
      }

      histories = append(histories, history)
   }

   historiesBytes, err := json.Marshal(histories)
   if err != nil {
      return shim.Error(fmt.Sprintf("marshal error: %s", err))
   }

   return shim.Success(historiesBytes)
}

type AssetExchangeChainCode struct {

}

func (t *AssetExchangeChainCode) Init(stub shim.ChaincodeStubInterface) peer.Response{

   return shim.Success(nil)
}

func (t *AssetExchangeChainCode) Invoke(stub shim.ChaincodeStubInterface) peer.Response{
   functionName, args := stub.GetFunctionAndParameters()
   switch functionName {
   case "userRegister":
      return userRegister(stub,args)
   case "userDestroy":
      return userDestroy(stub,args)
   case "assetEnroll":
      return assetEnroll(stub,args)
   case "assetExchange":
      return assetExchange(stub,args)
   case "queryUser":
      return queryUser(stub,args)
   case "queryAsset":
      return queryAsset(stub,args)
   case "queryAssetHistory":
      return queryAssetHistory(stub,args)
   default:
      return shim.Error(fmt.Sprintf("unsupported function: %s", functionName))
   }
   return shim.Success(nil)
}

func main() {
   err := shim.Start(new(AssetExchangeChainCode))
   if err != nil {
      fmt.Printf("Error starting AssetExchange chaincode: %s", err)
   }
}

2、链码编译

在Fabric工程AssetExchange/chaincode/assetexchange目录下编译链码:
go build -o assetexchange
编译成功的关键在于确保Fabric依赖的第三方包能够正确被从网络上下载到本地。由于部分第三方依赖包可能从google.golang.org下载,因此需要确保网络可以正确下载。

三、链码部署

1、Fabric区块链网络部署

进入Fabric工程AssetExchange/deploy
docker-compose up
启动服务是否正确启动,确保orderer、peer、cli节点都正确启动。
docker ps
进入cli容器:
docker exec -it cli bash
进入/etc/hyperledger/channel-artifacts目录:
cd /etc/hyperledger/channel-artifacts
创建业务通道:
peer channel create -o orderer.example.com:7050 -c assetchannel -f assetchannel.tx
在当前目录下会生成assetchannel.block区块
加入通道
peer channel join -b assetchannel.block
设置主节点
peer channel update -o orderer.example.com:7050 -c assetchannel -f Org1MSPanchors.tx

2、链码安装

安装assetexchange链码:
peer chaincode install -n assetexchange -v 1.0.0 -l golang -p github.com/chaincode/assetexchange

3、链码实例化

实例化assetexchange链码:
peer chaincode instantiate -o orderer.example.com:7050 -C assetchannel -n assetexchange -l golang -v 1.0.0 -c ‘{"Args":["user1","0"]}‘

4、链码交互

用户注册:
peer chaincode invoke -C assetchannel -n assetexchange -c ‘{"Args":["userRegister", "user1", "user1"]}‘
资产登记:
peer chaincode invoke -C assetchannel -n assetexchange -c ‘{"Args":["assetEnroll", "asset1", "asset1", "metadata", "user1"]}‘
用户注册:
peer chaincode invoke -C assetchannel -n assetexchange -c ‘{"Args":["userRegister", "user2", "user2"]}‘
资产转让:
peer chaincode invoke -C assetchannel -n assetexchange -c ‘{"Args":["assetExchange", "user1", "asset1", "user2"]}‘
用户注销:
peer chaincode invoke -C assetchannel -n assetexchange -c ‘{"Args":["userDestroy", "user1"]}‘

5、链码查询

用户查询:
peer chaincode query -C assetchannel -n assetexchange -c ‘{"Args":["queryUser", "user1"]}‘
资产查询:
peer chaincode query -C assetchannel -n assetexchange -c ‘{"Args":["queryAsset", "asset1"]}‘
用户查询:
peer chaincode query -C assetchannel -n assetexchange -c ‘{"Args":["queryUser", "user2"]}‘
资产交易记录查询:
peer chaincode query -C assetchannel -n assetexchange -c ‘{"Args":["queryAssetHistory", "asset1"]}‘
peer chaincode query -C assetchannel -n assetexchange -c ‘{"Args":["queryAssetHistory", "asset1", "all"]}‘

6、链码升级

peer chaincode install -n assetexchange -v 1.0.1 -l golang -p github.com/chaincode/assetexchange
peer chaincode upgrade -C assetchannel -n assetexchange -v 1.0.1 -c ‘{"Args":[""]}‘

四、Fabric区块链外部服务

Fabric区块链网络的外部服务根据应用场景分为三种:
A、智能硬件,基于Socket提供外部服务,如光伏发电设备。
B、游戏、电商、社交,基于HTTP提供web服务。
C、企业内部,基于RPC(gRPC)提供外部服务。
Fabric区块链网络的外部服务使用Fabric SDK进行开发。

原文地址:http://blog.51cto.com/9291927/2322310

时间: 2024-10-31 02:23:23

HyperLeger Fabric开发(十)——资产交易平台实例的相关文章

HyperLeger Fabric开发(八)——HyperLeger Fabric链码开发测试

HyperLeger Fabric开发(八)--HyperLeger Fabric链码开发测试 一.链码实例 SACC项目链码实例如下: package main import ( "fmt" "github.com/hyperledger/fabric/core/chaincode/shim" "github.com/hyperledger/fabric/protos/peer" ) // SimpleAsset implements a si

HyperLeger Fabric开发(一)——HyperLeger简介

HyperLeger Fabric开发(一)--HyperLeger简介 一.HyperLeger简介 1.HyperLeger简介 Hyperledger(超级账本)是Linux基金会于2015年发起的推进区块链数字技术和交易验证的开源项目,目标是让成员共同合作,共建开放平台,满足来自多个不同行业各种用户案例,并简化业务流程.由于点对点网络的特性,分布式账本技术是完全共享.透明和去中心化的,故非常适合于在金融行业的应用,以及其它的例如制造.银行.保险.物联网等行业.通过创建分布式账本的公开标准

HyperLeger Fabric开发(六)——HyperLeger Fabric智能合约(链码)

HyperLeger Fabric开发(六)--HyperLeger Fabric智能合约(ChainCode) 一.链码(Chaincode)简介 1.链码简介 在Fabric中,智能合约也称为链码(chaincode),分为用户链码和系统链码.系统链码用来实现系统层面的功能,包括系统的配置,用户链码的部署.升级,用户交易的签名和验证策略等:用户链码用于实现用户的应用功能,开发者编写链码应用程序并将其部署到区块链网络上,终端用户通过与网络节点交互的客户端应用程序调用链码.链码被编译成一个独立的

HyperLeger Fabric开发(七)——HyperLeger Fabric链码开发

HyperLeger Fabric开发(七)--HyperLeger Fabric链码开发 一.链码开发模式 1.链码开发模式简介 Fabric的链码开发调试比较繁琐.在不使用链码开发模式的情况下,链码不能在本地测试,必须部署到docker,install和instantiate后,Peer节点会在新的容器中启动链码.但只能通过docker logs查看链码日志,通过打印日志的方式进行链码调试.如果对链码进行了修改,需要重新开始上述流程.为了简化Fabric链码开发的调试过程,Fabric引入了

HyperLeger Fabric开发(九)——HyperLeger Fabric部署实战(单机)

HyperLeger Fabric开发(九)--HyperLeger Fabric部署实战(单机) 系统环境:RHEL 7.3操作系统Fabric release 1.3 一.crypto-config.yaml文件 1.Orderer节点组织 (1)单节点 OrdererOrgs: - Name: Orderer Domain: example.com Specs: - Hostname: orderer (2)多节点 OrdererOrgs: ??- Name: Orderer ????Do

HyperLeger Fabric开发(二)——HyperLeger Fabric入门

HyperLeger Fabric开发(二)--HyperLeger Fabric入门 本文使用RHEL 7.3 workstation版本操作系统. 一.HyperLeger Fabric环境部署 1.Go语言开发环境部署 (1)Go语言环境安装Go语言环境安装包下载地址:https://golang.org/dl/https://golang.google.cn/dl/将下载的源码包解压至/usr/local目录sudo tar -C /usr/local -xzf go1.10.1.lin

HyperLeger Fabric开发(三)——HyperLeger Fabric架构

HyperLeger Fabric开发(三)--HyperLeger Fabric架构 一.HyperLeger Fabric架构简介 1.通道简介 商业应用的一个重要的需求是私密×××易,为此Fabric设计了通道(Channel)来提供成员之间的隐私保护.通道是部分网络成员之间拥有独立的通信渠道,在通道中发送的交易只有属于通道的成员才可见,因此通道可以看作是Fabric的网络中部分成员的私有通信子网.通道由排序服务管理.在创建通道的时候,需要定义通道的成员和组织.锚节点(anchor pee

HyperLeger Fabric开发(四)——HyperLeger Fabric共识机制

HyperLeger Fabric开发(四)--HyperLeger Fabric共识机制 一.Fabric共识过程 1.Fabric共识过程 Fabric区块链的网络节点本质上是互相复制的状态机,节点之间需要保持相同的账本状态.为了实现分布式节点的一致性,各个节点需要通过共识过程,对账本状态的变化达成一致性的认同.Fabric区块链的共识过程包括3个阶段:背书.排序和校验. 2.背书 在背书(endorsement)阶段中,背书节点对客户端发来的交易提案进行合法性校验,然后模拟执行链码得到交易

HyperLeger Fabric开发(五)——HyperLeger Fabric账本存储

HyperLeger Fabric开发(五)--HyperLeger Fabric账本存储 一.HyperLeger Fabric账本简介 Fabric里的数据以分布式账本的形式存储.账本由一系列有顺序和防篡改的记录组成,记录包含着数据的全部状态改变.账本中的数据项以键值对的形式存放,账本中所有的键值对构成了账本的状态,也称为世界状态(World State).?每个通道中有唯一的账本,由通道中所有成员共同维护着账本,每个记账节点上都保存了所属通道的账本的一个副本,因而是分布式账本.对账本的访问