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 mainimport ("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)// 反序列化userif err := json.Unmarshal(userBytes, user); err != nil {return shim.Error(fmt.Sprintf("unmarshal user error: %s", err))}user.Assets = append(user.Assets, assetId)// 序列化useruserBytes, 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)// 反序列化userif err := json.Unmarshal(originOwnerBytes, originOwner); err != nil {return shim.Error(fmt.Sprintf("unmarshal user error: %s", err))}aidexist := falsefor _, aid := range originOwner.Assets {if aid == assetID {aidexist = truebreak}}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 = assetIdsoriginOwnerBytes, 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))}// 当前拥有者插入资产idcurrentOwner := new(User)// 反序列化userif 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": // 不添加任何附件keydefault: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":[""]}'

转载于:https://blog.51cto.com/9291927/2322310

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

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

    HyperLeger Fabric开发(七)--HyperLeger Fabric链码开发 一.链码开发模式 1.链码开发模式简介 Fabric的链码开发调试比较繁琐.在不使用链码开发模式的情况下,链 ...

  2. HyperLeger Fabric学习(一)

    HyperLeger Fabric学习(一) 一.HyperLeger简介 1.HyperLeger简介 2.Hyperledger社区组织结构 3.Hyperledger顶级项目 二.Hyperle ...

  3. hyperledger fabric 实战开发——水产品溯源交易平台(二)

    文章目录 前言 一.技术学习 1.Hyperledger fabric 1.1 流程 1.2 配置 1.3 范例解析并自写 1.3 算法实现 二.Web编写 前言 hyperledger fabric ...

  4. hyperledger fabric 实战开发——水产品溯源交易平台(一)

    文章目录 前言 环境准备 水产品溯源交易平台设计 实现步骤 1. 模板获取 2. 模板修改 虚拟机优化(根据个人喜好选择) 前言 在万字解析--区块链hyperledger fabric2.2部署实战 ...

  5. 链码实战(二)——资产交易平台

    链码实战(二)--资产交易平台 本节代码都是按照教学课程敲出来的,参考教学课程在文末!强烈安利 一.需求分析 资产:某个人拥有的某个可被转让的东西 房产.车辆 1.平台功能 用户开户&销户 资 ...

  6. 开发属于自己的数字资产交易平台必须要了解的几点

    在做几乎任何事情之前,准备和规划都是很重要的,开发平台重要是的Web开发阶段 - 无论是交易平台还是SaaS平台还是业务网站都很复杂.让我们来看看数字资产交换中必须具备的功能,以满足客户和所有者的需求 ...

  7. 5G 时代的 Android App 开发入门与项目实战

    随着移动互联网的持续发展,Android系统从智能手机逐步拓展到平板电脑.智能电视.车载大屏.智能家居.智能手表等诸多设备,Android开发依然是前景可期的IT岗位. 当然,整个社会正在迈向5G时代 ...

  8. 利用Hyperledger Fabric开发你的第一个区块链应用

    利用Hyperledger Fabric开发你的第一个区块链应用 本文示例源于fabric-samples中的fabcar https://github.com/hyperledger/fabric- ...

  9. 利用Hyperledger Fabric开发第一个区块链应用

    利用Hyperledger Fabric开发第一个区块链应用 Fabric入门 Fabric 我们通过一个简单的示例程序来了解Fabric应用是如何运行的.在这个例子中使用的应用程序和智能合约(链码) ...

最新文章

  1. 程序员因重复记录日志撑爆ELK被辞退!
  2. tf.keras.losses.categorical_hinge 分类铰链 损失函数 示例
  3. 确定多重选择列表控件 (List Control) 中的选定内容
  4. 虚拟机——虚拟机的初步认识
  5. php正则大小写字母,php 常见email,url,英文大小写,字母数字组合等正则表达式详解...
  6. 盘点神奇却少为人知的IntelliJ IDEA快捷键
  7. 【渝粤教育】电大中专会计电算化作业 题库
  8. python game_Python游戏
  9. 小干货:Linux 系统的备份恢复
  10. 如何在 macOS 中将用户帐户拆分为两个单独的帐户?
  11. 12条人生规则《12 Rules for Life: An Antidote to Chaos》
  12. 第4章 最基础的分类算法-k近邻算法 kNN 学习笔记 中
  13. 如何做一个基于微信外卖点餐小程序系统毕业设计毕设作品
  14. Activity启动流程(二)system_server进程处理启动Activity请求
  15. 基于python的手机销售系统
  16. 97年大学计算机考试是 级,1997年4月等级考试一级笔试试卷、答案
  17. 授课型英硕申请Ph.D (带奖)历程
  18. 谷歌地图离线发布系列之偏移处理(三)纠偏算法
  19. 如何笔记本盖上连接显示器不熄屏?
  20. 异步爬虫(高效爬虫)

热门文章

  1. 硬件工程师需要知道的DFM可制造性设计
  2. VayoPro-DFM Expert|区域分析设置方法
  3. 游戏测试员—游戏即人生
  4. 交通事故等级为同责应该怎么理赔
  5. 计算机端口的安全知识大全,整的明明白白!
  6. 【软件工程】需求引导的方法
  7. php软文范例,怎样撰写软文
  8. VR技术、特点、应用领域简介
  9. ubuntu强制退出当前命令
  10. C++基础 输入输出流及常用头文件