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

本节代码都是按照教学课程敲出来的,参考教学课程在文末!强烈安利

一、需求分析

资产:某个人拥有的某个可被转让的东西 房产、车辆

1、平台功能

  • 用户开户&销户
  • 资产登记 资产上链 or 用户绑定资产
  • 资产转让 资产所有权的变更
  • 查询功能 用户查询、资产查询、资产变更历史查询

2、业务实体

1)用户(User)

  • 名字
  • 标识(身份证…)
  • 资产列表

2)资产(Asset)

  • 名字
  • 标识
  • 特殊属性列表 (车辆:排量、品牌、座位数等)

3) 资产变更记录(AssetHistory)

  • 资产标识
  • 资产的原始拥有者 (登记==null)
  • 资产变更后的拥有者

3、交互方法(Invoke方法)

1)用户开户(userRegister)

参数

  • 名字
  • 标识

2)用户销户(userDestroy)

参数

  • 标识

3)资产登记(assetEnroll)

参数

  • 名字
  • 标识
  • 特殊属性列表
  • 拥有者

4)资产转让(assetExchange)

参数

  • 拥有者
  • 资产标识
  • 受让者

5)用户查询(queryUser)

参数

  • 标识

返回值

  • 用户实体

6)资产查询(queryAsset)

参数

  • 标识

返回值

  • 资产实体

7)资产的变更记录(queryAssetHistor)

参数

  • 资产的标识

  • 记录类型(登记/转让/全部)

返回值

  • 资产变更列表

二、链码开发

其实,开发链码不是想象中的那么难,只需要知道各程序的调用接口和返回值,再加上简单的go语言基础就能开发出一套完整的链码。 下面列出相关步骤:

1、包头导入

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

2、Invoke函数书写

根据调用时传入的参数选择相应的函数进行处理即可,具体的函分别实现:

func (c *AssertsExchangeCC) Invoke(stub shim.ChaincodeStubInterface) pb.Response {funcName, args := stub.GetFunctionAndParameters()switch funcName {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", funcName))}// stub.SetEvent("name", []byte("data"))
}

3、userRegister()函数实现

其实各个函数的实现都是有套路的,基本套路都是一样,下面以userRegister()为例子重点说一下,其他的都只贴相关的代码了。

  • 步骤一:检验参数个数是否合格
if len(args) != 2 {return shim.Error("not enough args")
}
  • 步骤二:验证参数形式是否正确
name := args[0]
id := args[1]
if name == "" || id == "" {return shim.Error("invalid args")
}
  • 步骤三:验证数据是否存在:应该存在存在 或者 不应该存在
//不应该存在的范例
userBytes, err != stub.GetState(constructUserKey(id))
if err == nil && len(userBytes) != 0 {return shim.Error(msg:"user already exist")
}
  • 步骤四:将数据写入Ledger,这一部分一般都是每部分差距最大的地方,需要序列化和反序列化进行相关处理
user := &User{Name:   name,Id:     id,Assets: make([]string, 0),
}// 序列化对象
userBytes, err := json.Marshal(user)
if err != nil {return shim.Error(fmt.Sprintf("marshal user error %s", err))
}if err := stub.PutState(constructUserKey(id), userBytes); err != nil {return shim.Error(fmt.Sprintf("put user error %s", err))
}
  • 返回值
return shim.Success(nil)

好了,一个完整的用户开户函数已经完成,后边的几个函数都是按照这个套路来的,只是有些小步骤有一定的区别,后边就写在代码中了。

4、userDestroy()函数实现

// 用户销户
func userDestroy(stub shim.ChaincodeStubInterface, args []string) pb.Response {// 套路1:检查参数的个数if len(args) != 1 {return shim.Error("not enough args")}// 套路2:验证参数的正确性id := args[0]if id == "" {return shim.Error("invalid args")}// 套路3:验证数据是否存在 应该存在 or 不应该存在userBytes, err := stub.GetState(constructUserKey(id))if err != nil || len(userBytes) == 0 {return shim.Error("user not found")}// 套路4:写入状态if err := stub.DelState(constructUserKey(id)); err != nil {return shim.Error(fmt.Sprintf("delete user error: %s", err))}// 删除用户名下的资产user := new(User)//反序列化if err := json.Unmarshal(userBytes, user); 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)
}

5、assetEnrol()函数实现

// 资产登记
func assetEnroll(stub shim.ChaincodeStubInterface, args []string) pb.Response {// 套路1:检查参数的个数if len(args) != 4 {return shim.Error("not enough args")}// 套路2:验证参数的正确性assetName := args[0]assetId := args[1]metadata := args[2]ownerId := args[3]if assetName == "" || assetId == "" || ownerId == "" {return shim.Error("invalid args")}// 套路3:验证数据是否存在 应该存在 or 不应该存在// ownerId是否存在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")}// 套路4:写入状态// 1. 写入资产对象 2. 更新用户对象 3. 写入资产变更记录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(nil)
}

6、assetExchange()函数实现

// 资产转让
func assetExchange(stub shim.ChaincodeStubInterface, args []string) pb.Response {// 套路1:检查参数的个数if len(args) != 3 {return shim.Error("not enough args")}// 套路2:验证参数的正确性ownerId := args[0]assetId := args[1]currentOwnerId := args[2]if ownerId == "" || assetId == "" || currentOwnerId == "" {return shim.Error("invalid args")}// 套路3:验证数据是否存在 应该存在 or 不应该存在// ownerId 是否存在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")}// 套路4:写入状态// 1. 原是拥有者删除资产id 2. 新拥有者加入资产id 3. 资产变更记录。不操作资产实体// 删除拥有者资产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))}// 当前拥有者插入资产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 assert 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 assert history error: %s", err))}return shim.Success(nil)
}

7、queryUser()函数实现

// 用户查询
func queryUser(stub shim.ChaincodeStubInterface, args []string) pb.Response {// 套路1:检查参数的个数if len(args) != 1 {return shim.Error("not enough args")}// 套路2:验证参数的正确性ownerId := args[0]if ownerId == "" {return shim.Error("invalid args")}// 套路3:验证数据是否存在 应该存在 or 不应该存在userBytes, err := stub.GetState(constructUserKey(ownerId))if err != nil || len(userBytes) == 0 {return shim.Error("user not found")}return shim.Success(userBytes)
}

8、queryAsset()函数实现

// 资产查询
func queryAsset(stub shim.ChaincodeStubInterface, args []string) pb.Response {// 套路1:检查参数的个数if len(args) != 1 {return shim.Error("not enough args")}// 套路2:验证参数的正确性assetId := args[0] //别整成1了就会造成越界。if assetId == "" {return shim.Error("invalid args")}// 套路3:验证数据是否存在 应该存在 or 不应该存在assetBytes, err := stub.GetState(constructAssetKey(assetId))if err != nil || len(assetBytes) == 0 {return shim.Error("asset not found")}return shim.Success(assetBytes)
}

9、queryAssetHistory()函数实现

// 资产变更历史查询
func queryAssetHistory(stub shim.ChaincodeStubInterface, args []string) pb.Response {// 套路1:检查参数的个数if len(args) != 2 && len(args) != 1 {return shim.Error("not enough args")}// 套路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))}// 套路3:验证数据是否存在 应该存在 or 不应该存在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)
}

10、Init()相关

别忘了Init函数的实现,如果不想有功能上的实现直接返回忘了创建成功即可

func (c *AssertsExchangeCC) Init(stub shim.ChaincodeStubInterface) pb.Response {return shim.Success(nil)
}

11、main()函数相关

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

三、总结

了解相关go相关语法、有基本的编程开发功底、熟悉相关API的调用(可以不熟悉,但是要会查找)最后最重要的就是有套路。这样就可以开发出一个链码了。 上边的代码功能错误已经没有了,剩下的只是逻辑错误了,需要我们将其安装到Fabric上进行相关的测试。

四、参考链接

学习Hyperledger Fabric实战联盟链

链码实战(二)——资产交易平台相关推荐

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

    HyperLeger Fabric开发(十)--资产交易平台实战 一.资产交易平台需求分析 1.资产交易平台的功能 A.用户开户.销户功能 B.资产登记,即资产上链,资产绑定到用户 C.资产转让,即资 ...

  2. Fabric链码实战(二)公民身份信息

    title: Fabric链码实战(二)公民身份信息 tags: Hyperledger, fabric ,区块链,chaincode 功能简述 使用链码可以添加和查询公民信息 功能实现 1.导入包 ...

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

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

  4. java毕业设计校园二手书交易平台源码+lw文档+mybatis+系统+mysql数据库+调试

    java毕业设计校园二手书交易平台源码+lw文档+mybatis+系统+mysql数据库+调试 java毕业设计校园二手书交易平台源码+lw文档+mybatis+系统+mysql数据库+调试 开发软件 ...

  5. VANSI致力成为全球优质数字资产交易平台的典范

    区块链是比特币的一个重要概念,其本质是一个去中心化的数据库.是在2009年由中本聪开创的,以区块链作为底层架构的数字货币带来的影响,迅速从广度和深度上扩展开来的. 区块链是分布式数据存储.点对点传输. ...

  6. 【Qt】数据库实战(二)

    00. 目录 文章目录 00. 目录 01. 概述 02. 开发环境 03. 连接到数据库 04. 操作结果集 06. 附录 01. 概述 SQL即结构化查询语言,是关系数据库的标准语言.前面已经在Q ...

  7. 浅析去中化区块链游戏资产交易平台

    资产本身就已经具备交易能力: 基于以太坊区块链去中心化的游戏厂商,在开发游戏角色.道具等资产时,所编写的智能合约本身就支持了角色.道具等资产转移功能,也就是在以太坊区块链上运行的智能合约游戏资产本身就 ...

  8. GridView实战二:使用ObjectDataSource数据源控件(自定义缓存机制实现Sort)

    参考资料:http://www.cnblogs.com/fsjohnhuang/archive/2011/12/17/2291200.html 因为使用ObjectDataSource自带的缓存机制无 ...

  9. CCNP精粹系列之十八--路由映射实战二,博主推荐文章

    路由映射实战二 本篇博文和上一篇是紧密结合的,只是在上个试验的基础上作了改动,达到其他的试验效果. 试验二: 在R1上增加一个网段,并发布路由.这里采用三种方法. 如下是第一种,是在试验一的基础上直接 ...

最新文章

  1. Transformer LambdaNetworks
  2. 【机器学习】分类算法-K-近邻算法
  3. 算法导论练习 10.4-5及12.1-3
  4. hibernate正向生成数据库表以及配置——Student.hbm.xml
  5. python django 是啥_python的django做什么的
  6. 妖帝q群机器人_有关酷Q 晨风机器人,契约 qqlite qqlight ,mypc等QQ机器人关停一事的一点想法...
  7. freemarker判断是否为空
  8. 数字图像处理-- 图像的统计方法
  9. D3 treecluster
  10. [Web Chart系列之五] 5. 实战draw2d之figure tooltip 实现
  11. mysql 格式化时间_每天一个常用MySQL函数-from_unixtime等
  12. 远程调试Hadoop
  13. Python压平嵌套列表的一种方法
  14. Rust : Tonic 基于Rust的gRPC实现
  15. 找出区间偶数c语言,c语言实践输出某个区间中不是3的倍数的偶数
  16. Excel与用友ERP-U8的数据集成方法(一)
  17. 做量化投研必须掌握的三大基本模型理论
  18. 如何规避rm-rf导致的服务器删除问题
  19. C语言实验——圆周率 sdut oj
  20. sqlserver数据库18456错误怎么解决?

热门文章

  1. 直播平台搭建,完整产业链和操作流程解析
  2. anki ios android 同步,让记忆更轻松的Anki,支持Windows,Linux,iOS,Android平台
  3. 高性能计算GPU解决方案系列教程三--高性能计算集群测试程序
  4. 测试员在面试中被问到 “你对加班的看法” 该如何回答?
  5. Windows PE
  6. 学美工设计需要会哪些软件?零基础怎么入门学UI设计?
  7. 对口高考计算机网络知识点,对口高考计算机网络概述复习
  8. jenkins自定义邮件发送Editable
  9. java代码批量下载_Java代码实战:线程池实现批量下载文件
  10. iOS 如何计算图片加载内存中所占的大小