A、用戶開戶、銷戶功能
B、資產(chǎn)登記,即資產(chǎn)上鏈,資產(chǎn)綁定到用戶
C、資產(chǎn)轉(zhuǎn)讓,即資產(chǎn)所有權(quán)的變更
D、查詢功能,包括用戶查詢、資產(chǎn)查詢、資產(chǎn)歷史變更查詢
創(chuàng)新互聯(lián)建站網(wǎng)站建設(shè)提供從項目策劃、軟件開發(fā),軟件安全維護(hù)、網(wǎng)站優(yōu)化(SEO)、網(wǎng)站分析、效果評估等整套的建站服務(wù),主營業(yè)務(wù)為網(wǎng)站設(shè)計制作、成都網(wǎng)站設(shè)計,成都app軟件開發(fā)公司以傳統(tǒng)方式定制建設(shè)網(wǎng)站,并提供域名空間備案等一條龍服務(wù),秉承以專業(yè)、用心的態(tài)度為用戶提供真誠的服務(wù)。創(chuàng)新互聯(lián)建站深信只要達(dá)到每一位用戶的要求,就會得到認(rèn)可,從而選擇與我們長期合作。這樣,我們也可以走得更遠(yuǎn)!
A、用戶,屬性包括名字、標(biāo)識、資產(chǎn)列表、
B、資產(chǎn),屬性包括名字、標(biāo)識、特殊屬性列表
C、資產(chǎn)變更記錄,屬性包括資產(chǎn)標(biāo)識、資產(chǎn)源所有者、資產(chǎn)變更后所有者
A、用戶注冊,參數(shù)包括用戶名稱、用戶標(biāo)識
B、銷戶,參數(shù)包括用戶標(biāo)識
C、資產(chǎn)登記,參數(shù)包括資產(chǎn)名稱、資產(chǎn)標(biāo)識、特殊屬性列表、資產(chǎn)所有者
D、資產(chǎn)轉(zhuǎn)讓,參數(shù)包括資產(chǎn)所有者、資產(chǎn)標(biāo)識、資產(chǎn)受讓者
E、用戶查詢,參數(shù)包括用戶標(biāo)識,返回用戶實體
F、資產(chǎn)查詢,參數(shù)包括資產(chǎn)標(biāo)識,返回資產(chǎn)實體
G、資產(chǎn)變更記錄,參數(shù)包括資產(chǎn)標(biāo)識,記錄類型(登記/轉(zhuǎn)讓/全部),返回資產(chǎn)變更記錄列表。
鏈碼需要定義資產(chǎn)交易平臺定義的所有實體和交互方法,然后在Invoke接口中調(diào)用相應(yīng)的交互方法。
在Fabric工程目錄AssetExchange/chaincode/assetexchange目錄下進(jìn)行鏈碼開發(fā),鏈碼AssetExchangeChainCode.go實現(xiàn)如下:
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"`
}
// 資產(chǎn)
type Asset struct {
Name string `json:"name"`
ID string `json:"id"`
Metadata string `json:"metadata"`
}
// 資產(chǎn)變更記錄
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:檢查參數(shù)個數(shù)
if len(args) != 2{
return shim.Error("Not enough args")
}
// step 2:驗證參數(shù)正確性
name := args[0]
id := args[1]
if name == "" || id == ""{
return shim.Error("Invalid args")
}
// step 3:驗證數(shù)據(jù)是否存在
if userBytes, err := stub.GetState(constructUserKey(id));err != nil || len(userBytes) != 0{
return shim.Error("User alreay exist")
}
// step 4: 寫入狀態(tài)
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:檢查參數(shù)個數(shù)
if len(args) != 1{
return shim.Error("Not enough args")
}
// step 2:驗證參數(shù)正確性
id := args[0]
if id == ""{
return shim.Error("Invalid args")
}
// step 3:驗證數(shù)據(jù)是否存在
userBytes, err := stub.GetState(constructUserKey(id));
if err != nil || len(userBytes) == 0{
return shim.Error("User not found")
}
// step 4: 寫入狀態(tài)
if err := stub.DelState(constructUserKey(id)); err != nil {
return shim.Error(fmt.Sprintf("delete user error: %s", err))
}
// 刪除用戶名下的資產(chǎn)
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)
}
// 資產(chǎn)登記
func assetEnroll(stub shim.ChaincodeStubInterface,args []string)peer.Response{
// step 1:檢查參數(shù)個數(shù)
if len(args) != 4 {
return shim.Error("Not enough args")
}
// step 2:驗證參數(shù)正確性
assetName := args[0]
assetId := args[1]
metadata := args[2]
ownerId := args[3]
if assetName == "" || assetId == "" || ownerId == ""{
return shim.Error("Invalid args")
}
// step 3:驗證數(shù)據(jù)是否存在
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: 寫入狀態(tài)
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))
}
// 資產(chǎn)變更歷史
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)
}
// 資產(chǎn)轉(zhuǎn)讓
func assetExchange(stub shim.ChaincodeStubInterface,args []string)peer.Response{
// step 1:檢查參數(shù)個數(shù)
if len(args) != 3 {
return shim.Error("Not enough args")
}
// step 2:驗證參數(shù)正確性
ownerID := args[0]
assetID := args[1]
currentOwnerID := args[2]
if ownerID == "" || assetID == "" || currentOwnerID == ""{
return shim.Error("Invalid args")
}
// step 3:驗證數(shù)據(jù)是否存在
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")
}
// 校驗原始擁有者確實擁有當(dāng)前變更的資產(chǎn)
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: 寫入狀態(tài)
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))
}
// 當(dāng)前擁有者插入資產(chǎn)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))
}
// 插入資產(chǎn)變更記錄
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:檢查參數(shù)個數(shù)
if len(args) != 1 {
return shim.Error("Not enough args")
}
// step 2:驗證參數(shù)正確性
userID := args[0]
if userID == ""{
return shim.Error("Invalid args")
}
// step 3:驗證數(shù)據(jù)是否存在
userBytes, err := stub.GetState(constructUserKey(userID))
if err != nil || len(userBytes) == 0 {
return shim.Error("user not found")
}
return shim.Success(userBytes)
}
// 資產(chǎn)查詢
func queryAsset(stub shim.ChaincodeStubInterface,args []string)peer.Response{
// step 1:檢查參數(shù)個數(shù)
if len(args) != 1 {
return shim.Error("Not enough args")
}
// step 2:驗證參數(shù)正確性
assetID := args[0]
if assetID == ""{
return shim.Error("Invalid args")
}
// step 3:驗證數(shù)據(jù)是否存在
assetBytes, err := stub.GetState(constructAssetKey(assetID))
if err != nil || len(assetBytes) == 0 {
return shim.Error("asset not found")
}
return shim.Success(assetBytes)
}
// 資產(chǎn)交易記錄查詢
func queryAssetHistory(stub shim.ChaincodeStubInterface,args []string)peer.Response{
// step 1:檢查參數(shù)個數(shù)
if len(args) != 2 && len(args) != 1 {
return shim.Error("Not enough args")
}
// step 2:驗證參數(shù)正確性
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:驗證數(shù)據(jù)是否存在
assetBytes, err := stub.GetState(constructAssetKey(assetID))
if err != nil || len(assetBytes) == 0 {
return shim.Error("asset not found")
}
// 查詢相關(guān)數(shù)據(jù)
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))
}
// 過濾掉不是資產(chǎn)轉(zhuǎn)讓的記錄
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)
}
}
在Fabric工程AssetExchange/chaincode/assetexchange目錄下編譯鏈碼:go build -o assetexchange
編譯成功的關(guān)鍵在于確保Fabric依賴的第三方包能夠正確被從網(wǎng)絡(luò)上下載到本地。由于部分第三方依賴包可能從google.golang.org下載,因此需要確保網(wǎng)絡(luò)可以正確下載。
進(jìn)入Fabric工程AssetExchange/deploydocker-compose up
啟動服務(wù)是否正確啟動,確保orderer、peer、cli節(jié)點(diǎn)都正確啟動。docker ps
進(jìn)入cli容器:docker exec -it cli bash
進(jìn)入/etc/hyperledger/channel-artifacts目錄:cd /etc/hyperledger/channel-artifacts
創(chuàng)建業(yè)務(wù)通道:peer channel create -o orderer.example.com:7050 -c assetchannel -f assetchannel.tx
在當(dāng)前目錄下會生成assetchannel.block區(qū)塊
加入通道peer channel join -b assetchannel.block
設(shè)置主節(jié)點(diǎn)peer channel update -o orderer.example.com:7050 -c assetchannel -f Org1MSPanchors.tx
安裝assetexchange鏈碼:peer chaincode install -n assetexchange -v 1.0.0 -l golang -p github.com/chaincode/assetexchange
實例化assetexchange鏈碼:peer chaincode instantiate -o orderer.example.com:7050 -C assetchannel -n assetexchange -l golang -v 1.0.0 -c '{"Args":["user1","0"]}'
用戶注冊:peer chaincode invoke -C assetchannel -n assetexchange -c '{"Args":["userRegister", "user1", "user1"]}'
資產(chǎn)登記: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"]}'
資產(chǎn)轉(zhuǎn)讓:peer chaincode invoke -C assetchannel -n assetexchange -c '{"Args":["assetExchange", "user1", "asset1", "user2"]}'
用戶注銷:peer chaincode invoke -C assetchannel -n assetexchange -c '{"Args":["userDestroy", "user1"]}'
用戶查詢:peer chaincode query -C assetchannel -n assetexchange -c '{"Args":["queryUser", "user1"]}'
資產(chǎn)查詢:peer chaincode query -C assetchannel -n assetexchange -c '{"Args":["queryAsset", "asset1"]}'
用戶查詢:peer chaincode query -C assetchannel -n assetexchange -c '{"Args":["queryUser", "user2"]}'
資產(chǎn)交易記錄查詢:peer chaincode query -C assetchannel -n assetexchange -c '{"Args":["queryAssetHistory", "asset1"]}'
peer chaincode query -C assetchannel -n assetexchange -c '{"Args":["queryAssetHistory", "asset1", "all"]}'
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":[""]}'