Hyperledger Fabric资产案例-链码实例

案例分析

功能

  1. 开户和销户
  2. 资产登记,资产上链,与具体的用户绑定
  3. 资产转让,资产所有权变更
  4. 查询功能,用户查询,资产查询,资产变更的历史查询

业务实体

用户: 名字、身份证(标识)、资产列表
资产:名字、标识、特殊属性列表(车:排量、品牌、座位)

资产变更记录

  1. 资产标识
  2. 资产的原始拥有者
  3. 资产变更后的拥有者

交互方法

  1. 用户开户
    参数:名字、标识
  2. 用户销户
    参数:标识
  3. 资产登记
    参数:名字、标识、特殊属性列表、拥有者
  4. 资产转让
    参数:拥有者、资产标识、受让者
  5. 用户查询
    参数:用户标识;返回值:用户实体
  6. 资产查询
    参数:资产标识;返回值:资产实体
  7. 资产的变更历史查询
    参数:资产标识;返回值:资产的变更记录列表

案例测试

创建通道的创世交易(channel名字不能有大写)

configtxgen -profile TwoOrgsChannel -outputCreateChannelTx ./config/assetschannel.tx -channelID assetschannel

启动网络

docker-compose up -d

交互执行

docker exec -it cli bash

创建通道

peer channel create -o orderer.example.com:7050 -c assetschannel -f /etc/hyperledger/config/assetschannel.tx

加入通道(一个peer可以加入多个通道)

peer channel join -b assetschannel.block

安装链码

peer chaincode install -n assets -v 1.0.0 -l golang -p github.com/chaincode/assetsExchange

实例化链码

peer chaincode instantiate -o orderer.example.com:7050 -C assetschannel -n assets -v 1.0.0 -l golang -c'{"Args":["init"]}'

测试方法

用户开户

peer chaincode invoke -C assetschannel -n assets -c '{"Args":["userRegister","user1","user1"]}'

用户查询

peer chaincode query -C assetschannel -n assets -c '{"Args":["queryUser","user1"]}'

资产登记

peer chaincode invoke -C assetschannel -n assets -c '{"Args":["assetEnroll","asset1","asset1","metadate","user1"]}'

资产查询

peer chaincode query -C assetschannel -n assets -c '{"Args":["queryAsset","asset1"]}'

再查询用户,可以看到用户资产已经改变

peer chaincode query -C assetschannel -n assets -c '{"Args":["queryUser","user1"]}'

资产转让
新建用户user2

peer chaincode invoke -C assetschannel -n assets -c '{"Args":["userRegister","user2","user2"]}'

资产转让,user1的资产asset1转给user2

peer chaincode invoke -C assetschannel -n assets -c '{"Args":["assetExchange","user1","asset1","user2"]}'

查询资产变更历史

peer chaincode query -C assetschannel -n assets -c '{"Args":["queryAssetHistory","asset1"]}'

删除用户,用户的资产会被同时删除

peer chaincode invoke -C assetschannel -n assets -c '{"Args":["userDestroy","user2"]}'

案例代码

package main

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

//定义链码
type AssetsExchangeCC struct {
}

//资产默认的原始拥有者
const (
	originOwner = "originOwnerPlaceholder"
)

//资产
type Asset struct {
	Name     string `json:"name"`
	Id       string `json:"id"`
	Metadata string `json:"metadata"`
}

//用户
type User struct {
	Name   string   `json:"name"`
	Id     string   `json:"id"`
	Assets []string `json:"assets"`
}

//资产变更记录
type AssetHistory struct {
	//资产标识
	AssetId string `json:"asset_id"`
	//资产的原始拥有者
	OriginOwnerId string `json:"origin_owner_id"`
	//变更后的拥有者
	CurrentOwnerId string `json:"current_owner_id"`
}

//链码的初始化
func (c *AssetsExchangeCC) Init(stub shim.ChaincodeStubInterface) pb.Response {
	return shim.Success(nil)
}

//链码交互
func (c *AssetsExchangeCC) 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("不支持的方法:%s", funcName))
	}
}

//用户开户
func userRegister(stub shim.ChaincodeStubInterface, args []string) pb.Response {
	//判断个数必须为2个
	if len(args) != 2 {
		return shim.Error("参数个数错误")
	}
	//判断传过来的参数是a否为空
	name := args[0]
	id := args[1]
	if name == "" || id == "" {
		return shim.Error("无效的参数")
	}
	//判断用户是否存在,若存在,则报错
	if userBytes, err := stub.GetState(constructUserKey(id)); err == nil && len(userBytes) != 0 {
		return shim.Error("用户已存在")
	}

	//写入世界状态,传过来的是用户的名字和id,绑定User结构体 make([]string,0)
	user := &User{
		Name:   name,
		Id:     id,
		Assets: make([]string, 0),
	}
	//序列化
	userBytes, err := json.Marshal(user)
	if err != nil {
		return shim.Error(fmt.Sprintf("序列化与用户失败 %s", err))
	}

	//将对象状态写入数据库
	if err := stub.PutState(constructUserKey(id), userBytes); err != nil {
		return shim.Error(fmt.Sprintf("存入用户失败 %s", err))
	}
	//返回成功
	return shim.Success(nil)
}
func constructUserKey(userId string) string {
	return fmt.Sprintf("user_%s", userId)
}

//用户销户
func userDestroy(stub shim.ChaincodeStubInterface, args []string) pb.Response {
	//参数个数1个
	if len(args) != 1 {
		return shim.Error("参数个数不对")
	}
	//校验参数正确性
	id := args[0]
	if id == "" {
		return shim.Error("无效的参数")
	}
	//判断用户是否存在
	userBytes, err := stub.GetState(constructUserKey(id))
	if err != nil && len(userBytes) == 0 {
		return shim.Error("找不到用户")
	}

	//写入状态
	if err := stub.DelState(constructUserKey(id)); err != nil {
		return shim.Error(fmt.Sprintf("删除用户失败 %s", err))
	}

	//删除用户名下的资产
	user := new(User)
	if err := json.Unmarshal(userBytes, user); err != nil {
		return shim.Error(fmt.Sprintf("反序列化失败 %s", err))
	}
	for _, assetid := range user.Assets {
		if err := stub.DelState(constructAssetKey(assetid)); err != nil {
			return shim.Error(fmt.Sprintf("删除资产失败 %s", err))
		}
	}
	return shim.Success(nil)
}

//使用组合键来区分
//所有的资产,用asset开头
func constructAssetKey(assetId string) string {
	return fmt.Sprintf("asset_%s", assetId)
}

//资产登记
func assetEnroll(stub shim.ChaincodeStubInterface, args []string) pb.Response {
	if len(args) != 4 {
		return shim.Error("参数个数不对")
	}
	//验证正确性
	assetName := args[0]
	assetId := args[1]
	metadata := args[2]
	ownerId := args[3]
	//metadata可以为空
	if assetName == "" || assetId == "" || ownerId == "" {
		return shim.Error("无效的参数")
	}
	//验证拥有者是否存在,拥有者必须存在
	userBytes, err := stub.GetState(constructUserKey(ownerId))
	if err != nil || len(userBytes) == 0 {
		return shim.Error("找不到用户")
	}
	//验证资产是否存在,资产必须不存在
	if assetBytes, err := stub.GetState(constructAssetKey(assetId)); err == nil && len(assetBytes) != 0 {
		return shim.Error("资产已经存在")
	}

	//写入状态
	asset := &Asset{
		Name:     assetName,
		Id:       assetId,
		Metadata: metadata,
	}
	//序列化
	assetBytes, err := json.Marshal(asset)
	if err != nil {
		return shim.Error(fmt.Sprintf("序列化失败 %s", err))
	}
	//保存资产
	if err := stub.PutState(constructAssetKey(assetId), assetBytes); err != nil {
		return shim.Error(fmt.Sprintf("保存失败 %s", err))
	}

	//拥有者
	user := new(User)
	if err := json.Unmarshal(userBytes, user); err != nil {
		return shim.Error(fmt.Sprintf("反序列化失败 %s", err))
	}

	user.Assets = append(user.Assets, assetId)
	if userBytes, err = json.Marshal(user); err != nil {
		return shim.Error(fmt.Sprintf("序列化用户失败 %s", err))
	}
	//存储用户状态
	if err := stub.PutState(constructUserKey(user.Id), userBytes); err != nil {
		return shim.Error(fmt.Sprintf("保存用户失败 %s", err))
	}

	//资产历史变更
	history := &AssetHistory{
		AssetId:        assetId,
		OriginOwnerId:  originOwner,
		CurrentOwnerId: ownerId,
	}
	historyBytes, err := json.Marshal(history)
	if err != nil {
		return shim.Error(fmt.Sprintf("序列化失败 %s", err))
	}

	//使用fabric内置的组合键机制
	historyKey, err := stub.CreateCompositeKey("history", []string{
		assetId,
		originOwner,
		ownerId,
	})
	if err != nil {
		return shim.Error(fmt.Sprintf("创建key失败 %s", err))
	}

	//资产变更存储
	if err := stub.PutState(historyKey, historyBytes); err != nil {
		return shim.Error(fmt.Sprintf("保存变更历史失败 %s", err))
	}

	return shim.Success(nil)
}

//资产转让
func assetExchange(stub shim.ChaincodeStubInterface, args []string) pb.Response {
	//参数个数为3个
	if len(args) != 3 {
		return shim.Error("参数个数不对")
	}
	//参数校验
	ownerId := args[0]
	assetId := args[1]
	currentOwnerId := args[2]
	if ownerId == "" || assetId == "" || currentOwnerId == "" {
		return shim.Error("无效的参数")
	}
	//验证当前和受让后的用户是否存在
	originOwnerBytes, err := stub.GetState(constructUserKey(ownerId))
	if err != nil || len(originOwnerBytes) == 0 {
		return shim.Error("用户找不到")
	}
	currentOwnerBytes, err := stub.GetState(constructUserKey(currentOwnerId))
	if err != nil || len(currentOwnerBytes) == 0 {
		return shim.Error("用户找不到")
	}
	//验证资产存在
	assetBytes, err := stub.GetState(constructAssetKey(assetId))
	if err != nil || len(assetBytes) == 0 {
		return shim.Error("资产找不到")
	}

	//校验原始拥有者确实拥有当前变更的资产
	originOwner := new(User)
	if err := json.Unmarshal(originOwnerBytes, originOwner); err != nil {
		return shim.Error(fmt.Sprintf("反序列化失败 %s", err))
	}

	//定义标记,标识资产是否存在
	aidexist := false
	for _, aid := range originOwner.Assets {
		if aid == assetId {
			//若找到该资产,则变更状态,结束循环
			aidexist = true
			break
		}
	}

	if !aidexist {
		return shim.Error("资产所有者不匹配")
	}

	//写入状态
	//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("序列化失败 %s", err))
	}

	//存储原始拥有者
	if err := stub.PutState(constructUserKey(ownerId), originOwnerBytes); err != nil {
		return shim.Error(fmt.Sprintf("存储用户失败 %s", err))
	}

	//当前拥有者
	currentOwner := new(User)
	if err := json.Unmarshal(currentOwnerBytes, currentOwner); err != nil {
		return shim.Error(fmt.Sprintf("反序列化失败 %s", err))
	}

	//绑定资产
	currentOwner.Assets = append(currentOwner.Assets, assetId)
	currentOwnerBytes, err = json.Marshal(currentOwner)
	if err != nil {
		return shim.Error(fmt.Sprintf("序列化失败 %s", err))
	}
	//存储
	if err := stub.PutState(constructUserKey(currentOwnerId), currentOwnerBytes); err != nil {
		return shim.Error("保存用户失败")
	}

	//插入资产变更记录
	history := &AssetHistory{
		AssetId:        assetId,
		OriginOwnerId:  ownerId,
		CurrentOwnerId: currentOwnerId,
	}

	historyBytes, err := json.Marshal(history)
	if err != nil {
		return shim.Error(fmt.Sprintf("序列化失败 %s", err))
	}
	historyKey, err := stub.CreateCompositeKey("history", []string{
		assetId,
		ownerId,
		currentOwnerId,
	})
	if err != nil {
		return shim.Error(fmt.Sprintf("创建key失败 %s", err))
	}
	//存储历史变更记录
	if err := stub.PutState(historyKey, historyBytes); err != nil {
		return shim.Error(fmt.Sprintf("保存失败 %s", err))
	}
	return shim.Success(nil)
}

//用户查询
func queryUser(stub shim.ChaincodeStubInterface, args []string) pb.Response {
	//参数个数1个
	if len(args) != 1 {
		return shim.Error("参数个数不对")
	}

	//校验正确性
	ownerId := args[0]
	if ownerId == "" {
		return shim.Error("无效的参数")
	}

	userBytes, err := stub.GetState(constructUserKey(ownerId))
	if err != nil || len(userBytes) == 0 {
		return shim.Error("找不到用户")
	}
	return shim.Success(userBytes)
}

//资产查询
func queryAsset(stub shim.ChaincodeStubInterface, args []string) pb.Response {
	//参数个数1个
	if len(args) != 1 {
		return shim.Error("参数个数不对")
	}

	//校验正确性
	assetId := args[0]
	if assetId == "" {
		return shim.Error("无效的参数")
	}

	assetBytes, err := stub.GetState(constructAssetKey(assetId))
	if err != nil || len(assetBytes) == 0 {
		return shim.Error("找不到资产")
	}
	return shim.Success(assetBytes)
}

//资产历史变更查询
func queryAssetHistory(stub shim.ChaincodeStubInterface, args []string) pb.Response {
	//参数个数1个
	if len(args) != 1 && len(args) != 2 {
		return shim.Error("参数个数不对")
	}

	//校验参数的正确性
	assetId := args[0]
	if assetId == "" {
		return shim.Error("无效的参数")
	}

	//queryType:all
	//默认为all
	queryType := "all"
	if len(args) == 2 {
		//变为用户传的值
		queryType = args[1]
	}

	//参数校验
	if queryType != "all" && queryType != "exchange" && queryType != "enroll" {
		return shim.Error(fmt.Sprintf("未知的查询类型 %s", queryType))
	}

	//校验资产是否存在
	assetBytes, err := stub.GetState(constructAssetKey(assetId))
	if err != nil || len(assetBytes) == 0 {
		return shim.Error("资产找不到")
	}

	keys := make([]string, 0)
	keys = append(keys, assetId)
	switch queryType {
	case "enroll":
		//资产登记
		keys = append(keys, originOwner)
	case "exchange", "all":
	default:
		return shim.Error(fmt.Sprintf("不支持的类型 %s", queryType))
	}

	//组合键
	//得到迭代器
	result, err := stub.GetStateByPartialCompositeKey("history", keys)
	if err != nil {
		return shim.Error(fmt.Sprintf("查询历史错误 %s", err))
	}

	//关闭
	defer result.Close()

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

		history := new(AssetHistory)
		if err := json.Unmarshal(historyVal.GetValue(), history); err != nil {
			return shim.Error(fmt.Sprintf("反序列化失败 %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("序列化失败 %s", err))
	}
	return shim.Success(historiesBytes)
}

func main() {
	err := shim.Start(new(AssetsExchangeCC))
	if err != nil {
		fmt.Println("启动链码失败")
	}
}


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: 下面是一个简单的 Hyperledger Fabric 2.0 Go语言链码示例: ``` package main import ( "fmt" "github.com/hyperledger/fabric-chaincode-go/shim" pb "github.com/hyperledger/fabric-protos-go/peer" ) // SimpleChaincode example simple Chaincode implementation type SimpleChaincode struct { } func (t *SimpleChaincode) Init(stub shim.ChaincodeStubInterface) pb.Response { fmt.Println("ex02 Init") _, args := stub.GetFunctionAndParameters() var A, B string // Entities var Aval, Bval int // Asset holdings var err error if len(args) != 4 { return shim.Error("Incorrect number of arguments. Expecting 4") } // Initialize the chaincode A = args[0] Aval, err = strconv.Atoi(args[1]) if err != nil { return shim.Error("Expecting integer value for asset holding") } B = args[2] Bval, err = strconv.Atoi(args[3]) if err != nil { return shim.Error("Expecting integer value for asset holding") } fmt.Printf("Aval = %d, Bval = %d\n", Aval, Bval) // Write the state to the ledger err = stub.PutState(A, []byte(strconv.Itoa(Aval))) if err != nil { return shim.Error(err.Error()) } err = stub.PutState(B, []byte(strconv.Itoa(Bval))) if err != nil { return shim.Error(err.Error()) } return shim.Success(nil) } func (t *SimpleChaincode) Invoke(stub shim.ChaincodeStubInterface) pb.Response { fmt.Println("ex02 Invoke") function, args := stub.GetFunctionAndParameters() if function == "invoke" { // Make payment of X units from A to B return t.invoke(stub, args) } else if function == "delete" { // Deletes an entity from its state return t.delete(stub, args) } else if function == "query" { // the old "Query" is now implemtned in invoke return t.query(stub, args) } return shim.Error("Invalid invoke function name. Expecting \"invoke\" \"delete\" \"query\"") } // Transaction makes payment of X units from A to B func (t *SimpleChaincode) invoke(stub shim.ChaincodeStubInterface, args []string) pb.Response { var A, B string // Entities var Aval, Bval int // Asset holdings var X int // Transaction value var err error ### 回答2: Hyperledger Fabric 是一个开源的区块链平台,可以用于构建企业级的去中心化应用程序。而 Hyperledger Fabric 2.0 是其最新版本,引入了许多新特性和改进。 编写一个 Hyperledger Fabric 2.0 的 Go 语言链码需要按照一定的流程进行: 1. 准备开发环境:首先,需要安装 Go 语言的开发环境和 Hyperledger Fabric 的相关工具,如 Hyperledger Fabric SDK 和 Hyperledger Fabric CA。 2. 编写链码逻辑:使用 Go 语言编写链码的逻辑,链码是在 Hyperledger Fabric 上运行的智能合约。可以根据项目需求和业务逻辑定义相关的数据结构和函数。 3. 定义链码接口:需要定义链码接口,包括 Init 和 Invoke 两个核心函数。Init 函数用于链码的初始化操作,而 Invoke 函数用于链码的业务逻辑执行。 4. 部署链码:将编写好的链码部署到 Hyperledger Fabric 网络中。可以使用 Hyperledger Fabric SDK 提供的工具和 API 来进行链码的部署操作。 5. 测试链码:编写相应的测试用例,对链码逻辑进行测试。可以使用模拟的 Fabric 网络进行测试,或者与实际的 Fabric 网络交互进行测试。 6. 部署链码应用程序:将编写好的链码应用程序部署到 Hyperledger Fabric 网络上。可以使用 Hyperledger Fabric SDK 提供的工具和 API 来进行链码应用程序的部署操作。 Go 语言是一种高性能的编程语言,适合于开发区块链平台和链码。编写 Hyperledger Fabric 2.0 的 Go 语言链码需要熟悉 Go 语言的基本语法和特性,以及了解 Hyperledger Fabric 的相关知识。通过合理的设计和编码,可以实现各种复杂的业务逻辑和功能。 ### 回答3: 编写一个Hyperledger Fabric 2.0的Go语言链码可以分为以下几个步骤: 1. 准备开发环境:首先,需要在开发机器上安装Go语言和Hyperledger Fabric的相关依赖。可以通过配置Golang环境变量,并使用Golang包管理器安装Fabric的Go SDK。 2. 创建链码项目:使用Go语言的IDE或文本编辑器创建一个新的文件夹,作为链码项目的根目录。 3. 定义链码结构:创建一个新的Go文件,并定义链码结构。链码结构应该实现fabricChaincode接口,并实现Init和Invoke两个方法。 4. 实现Init方法:Init方法在链码实例化时调用,并进行初始化设置。可以在该方法中初始化链码的状态数据和其他必要的准备工作。 5. 实现Invoke方法:Invoke方法在链码接收到调用请求时被调用。在该方法中处理具体的业务逻辑,并根据请求中的操作类型执行相应的操作。 6. 将链码打包:使用Fabric提供的命令行工具将链码打包成压缩文件,以便于后续部署和安装。 7. 部署和安装链码:使用Fabric提供的链码生命周期管理工具,将链码部署到指定的Fabric网络中,并安装到指定的通道上。 8. 实例链码:在指定的通道上实例链码,使其可以被其他参与方调用。 9. 调用链码:使用Fabric提供的客户端SDK或命令行工具,向链码发送调用请求,验证链码的功能和逻辑是否正确。 10. 测试链码:编写一些测试用例,用于对链码的功能和性能进行验证。 以上是一个简要的步骤,编写Hyperledger Fabric 2.0的Go语言链码还需要进一步了解链码开发的相关知识和Fabric的API,以有效地实现业务逻辑,并与Fabric网络进行交互。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值