1. 程式人生 > >超級賬本hyperledger fabric第十二集:合約相關鏈碼

超級賬本hyperledger fabric第十二集:合約相關鏈碼

  • 編寫contract下的程式碼,編寫好後,拖到對應linux目錄
package main

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

//Invoke
//釋出合約
//響應合約
//合約成交
//合約關閉
//合約交易查詢
//合約交易歷史資料查詢

//定義合約結構體
//欄位有的可能用不到
//用於釋出合約
type Bill struct {
	//合約id
	TaskId string `json:"task_id"`
	//使用者程式碼
	UserCode string `json:"user_code"`
	//合同程式碼
	ContractCode string `json:"contract_code"`
	//採購方使用者程式碼
	PurchaserUserCode string `json:"purchaser_user_code"`
	//投標開始時間
	BidingStartTime string `json:"biding_start_time"`
	//投標結束時間
	BidingEndTime string `json:"biding_end_time"`
	//關閉時間
	CloseTime string `json:"close_time"`
	//合同開始時間
	ContractStartTime string `json:"contract_start_time"`
	//合同結束時間
	ContractEndTime string `json:"contract_end_time"`
	//合同狀態
	//釋出合同:yes  關閉合同: close  已成交: deal
	ContractStatus string `json:"contract_status"`
}

//定義用於響應合約的結構體
type BidingBill struct {
	//賬單ID
	TaskId string `json:"task_id"`
	//使用者程式碼
	UserCode string `json:"user_code"`
	//合同程式碼
	ContractCode string `json:"contract_code"`
	//合同狀態
	//釋出合同:yes  關閉合同: close  已成交: deal
	ContractStatus string `json:"contract_status"`
}

//合約成交
type BillDeal struct {
	//id
	TaskId string `json:"task_id"`
	//使用者程式碼
	UserCode string `json:"user_code"`
	//合同程式碼
	ContractCode string `json:"contract_code"`
	//成交時間
	DealTime string `json:"deal_time"`
	//合同狀態
	//釋出合同:yes  關閉合同: close  已成交: deal
	ContractStatus string `json:"contract_status"`
}

//用於合約關閉
type BillClose struct {
	//id
	TaskId string `json:"task_id"`
	//使用者程式碼
	UserCode string `json:"user_code"`
	//合同程式碼
	ContractCode string `json:"contract_code"`
	//結束時間
	CloseTime string `json:"close_time"`
	//合同狀態
	//釋出合同:yes  關閉合同: close  已成交: deal
	ContractStatus string `json:"contract_status"`
}

//鏈碼的返回結構
type chaincodeRet struct {
	//1代表成功,0代表失敗
	Result int `json:"result"`
	//0代表沒有錯誤
	//1000代表引數錯誤
	//2000代表內容格式錯誤
	ErrorCode int `json:"error_code"`
	//錯誤資訊
	ErrorMsg string `json:"error_msg"`
}

//合約查詢
type QueryBill struct {
	TaskId       string `json:"task_id"`
	UserCode     string `json:"user_code"`
	ContractCode string `json:"contract_code"`
	//last:查詢是的最新的合約交易,whole:查詢的是全部的合約的交易資訊
	VersionType string `json:"version_type"`
}

//定義用於返回查詢結果的結構體
type queryRet struct {
	//1代表成功,0代表失敗
	Result int `json:"result"`
	//0代表沒有錯誤
	//1000代表引數錯誤
	//2000代表內容格式錯誤
	ErrorCode int `json:"error_code"`
	//錯誤資訊
	ErrorMsg string `json:"error_msg"`
	//返回合同列表
	DataList []Bill `json"data_list"`
}

//結構體
type BillChaincode struct {
}

//初始化
func (a *BillChaincode) Init(stub shim.ChaincodeStubInterface) pb.Response {
	return shim.Success(nil)
}

//鏈碼入口
func (a *BillChaincode) Invoke(stub shim.ChaincodeStubInterface) pb.Response {
	//接收方法名
	function, args := stub.GetFunctionAndParameters()
	//判斷
	if function == "link_contract_create" {
		//釋出合約
		return a.LinkContractCreate(stub, args)
	} else if function == "link_contract_biding" {
		//響應合約
		return a.LinkContractBiding(stub, args)
	} else if function == "link_contract_deal" {
		//合約成交
		return a.LinkContractDeal(stub, args)
	} else if function == "link_contract_close" {
		//合約關閉
		return a.LinkContractClose(stub, args)
	} else if function == "query" {
		//合約查詢
		return a.query(stub, args)
	} else {
		//處理錯誤
		res := getRetString(0, 1000, "無效的方法名")
		return shim.Error(res)
	}
	return shim.Success(nil)
}

//根據傳入的引數,處理異常
func getRetString(result int, code int, msg string) string {
	var r chaincodeRet
	//1代表成功,0代表失敗
	r.Result = result
	//1000代表引數錯誤
	r.ErrorCode = code
	r.ErrorMsg = msg
	//序列化
	b, err := json.Marshal(r)
	if err != nil {
		fmt.Println("序列化失敗")
		return ""
	}
	//返回字串
	return string(b[:])
}

//根據傳入的引數,處理異常
func getRetByte(result int, code int, msg string) []byte {
	var r chaincodeRet
	//1代表成功,0代表失敗
	r.Result = result
	//1000代表引數錯誤
	r.ErrorCode = code
	r.ErrorMsg = msg
	//序列化
	b, err := json.Marshal(r)
	if err != nil {
		fmt.Println("序列化失敗")
		return nil
	}
	return b
}

//釋出合約
func (a *BillChaincode) LinkContractCreate(stub shim.ChaincodeStubInterface, args []string) pb.Response {
	//判斷引數個數
	if len(args) != 1 {
		//處理錯誤
		res := getRetString(0, 1000, "引數必須是一個")
		return shim.Error(res)
	}

	//將輸入引數解析到結構體
	arg := []byte(args[0])
	bill := Bill{}
	err := json.Unmarshal(arg, &bill)
	if err != nil {
		//處理錯誤
		res := getRetString(0, 2000, "合約解析失敗")
		return shim.Error(res)
	}
	//如果合約程式碼為空則返回錯誤
	if bill.ContractCode == "" {
		//處理錯誤
		res := getRetString(0, 2000, "合約程式碼不能為空")
		return shim.Error(res)
	}
	//進行校驗,判斷世界狀態中合約是否已經存在
	_, existbl := a.getBill(stub, bill.ContractCode)
	//合約已經存在
	if existbl {
		//處理錯誤
		res := getRetString(0, 2000, "合約已經存在")
		return shim.Error(res)
	}

	//儲存合約
	_, bl := a.putBill(stub, bill)
	if !bl {
		//儲存失敗
		//處理錯誤
		res := getRetString(0, 2000, "合約儲存失敗")
		return shim.Error(res)
	}
	//打印合約資訊
	fmt.Println(bill)
	res := getRetByte(1, 0, "釋出合約成功")
	return shim.Success(res)
}

//根據合約號取出合約
func (a *BillChaincode) getBill(stub shim.ChaincodeStubInterface, bill_No string) (Bill, bool) {
	var bill Bill
	key := bill_No
	//獲取合約
	b, err := stub.GetState(key)
	if b == nil {
		return bill, false
	}
	err = json.Unmarshal(b, &bill)
	if err != nil {
		return bill, false
	}
	//返回
	return bill, true
}

//儲存合約
func (a *BillChaincode) putBill(stub shim.ChaincodeStubInterface, bill Bill) ([]byte, bool) {
	//處理json
	byte, err := json.Marshal(bill)
	if err != nil {
		return nil, false
	}
	//儲存
	err = stub.PutState(bill.ContractCode, byte)
	if err != nil {
		return nil, false
	}
	return byte, true
}

//響應合約
func (a *BillChaincode) LinkContractBiding(stub shim.ChaincodeStubInterface, args []string) pb.Response {
	//判斷引數個數
	if len(args) != 1 {
		res := getRetString(0, 1000, "引數必須是一個")
		return shim.Error(res)
	}

	//解析
	arg := []byte(args[0])
	biding_bill := &BidingBill{}
	err := json.Unmarshal(arg, biding_bill)
	if err != nil {
		res := getRetString(0, 2000, "json解析失敗")
		return shim.Error(res)
	}

	//判斷合約是否存在
	key_id := biding_bill.ContractCode
	bill, bl := a.getBill(stub, key_id)
	if !bl {
		//沒查到
		res := getRetString(0, 1000, "合同程式碼不存在")
		return shim.Error(res)
	}

	//判斷合約是否已經成交,若成交,不能再響應
	if bill.ContractStatus == "deal" {
		res := getRetString(0, 2000, "合約已經成交")
		return shim.Error(res)
	} else if bill.ContractStatus == "close" {
		res := getRetString(0, 2000, "合約已經關閉")
		return shim.Error(res)
	}
	fmt.Println(bill)

	//儲存
	_, bl = a.putBill(stub, bill)
	//
	bill, bl = a.getBill(stub, key_id)
	fmt.Println(bill)
	if !bl {
		res := getRetString(0, 2000, "合約儲存失敗")
		return shim.Error(res)
	}

	res := getRetByte(1, 0, "響應成功")
	return shim.Success(res)
}

//合約成交
func (a *BillChaincode) LinkContractDeal(stub shim.ChaincodeStubInterface, args []string) pb.Response {
	//判斷輸入引數個數
	if len(args) != 1 {
		res := getRetString(0, 1000, "引數必須是一個")
		return shim.Error(res)
	}

	//解析
	arg := []byte(args[0])
	//
	billdeal := &BillDeal{}
	err := json.Unmarshal(arg, billdeal)
	if err != nil {
		res := getRetString(0, 1000, "解析失敗")
		return shim.Error(res)
	}

	//判斷合約是否存在
	bill, existbl := a.getBill(stub, billdeal.ContractCode)
	if !existbl {
		res := getRetString(0, 1000, "合約不存在")
		return shim.Error(res)
	}

	//判斷合約是否已經關閉或成交
	if bill.ContractStatus == "deal" {
		res := getRetString(0, 2000, "合約已經成交")
		return shim.Error(res)
	} else if bill.ContractStatus == "close" {
		res := getRetString(0, 2000, "合約已經關閉")
		return shim.Error(res)
	}

	//修改合約狀態
	bill.ContractStatus = "deal"
	//儲存合約
	_, bl := a.putBill(stub, bill)
	if !bl {
		res := getRetString(0, 2000, "合約儲存失敗")
		return shim.Error(res)
	}
	fmt.Println(bill)

	res := getRetByte(1, 0, "合約成交成功")
	return shim.Success(res)
}

//合約關閉
func (a *BillChaincode) LinkContractClose(stub shim.ChaincodeStubInterface, args []string) pb.Response {
	//判斷輸入引數個數
	if len(args) != 1 {
		res := getRetString(0, 1000, "引數必須是一個")
		return shim.Error(res)
	}

	//解析
	arg := []byte(args[0])
	//
	billclose := &BillClose{}
	err := json.Unmarshal(arg, billclose)
	if err != nil {
		res := getRetString(0, 1000, "解析失敗")
		return shim.Error(res)
	}

	//合約關閉
	err = stub.PutState(billclose.ContractCode, arg)
	if err != nil {
		return shim.Error("合約關閉失敗")
	}

	//查詢合約是否存在
	bill, exitbl := a.getBill(stub, billclose.ContractCode)
	if !exitbl {
		res := getRetString(0, 1000, "合約關閉失敗,合約不存在")
		return shim.Error(res)
	}

	//更改合約狀態和時間
	bill.ContractStatus = billclose.ContractStatus
	bill.CloseTime = billclose.CloseTime
	_, bl := a.putBill(stub, bill)
	if !bl {
		res := getRetString(0, 1000, "合約關閉失敗")
		return shim.Error(res)
	}

	res := getRetByte(1, 0, "合約關閉成功")
	return shim.Success(res)
}

//查詢合約
//支援查詢最新的,和查詢所有的
func (a *BillChaincode) query(stub shim.ChaincodeStubInterface, args []string) pb.Response {
	//判斷輸入引數個數
	if len(args) != 1 {
		res := getRetString(0, 1000, "引數必須是一個")
		return shim.Error(res)
	}

	//解析
	query_bill := &QueryBill{}
	err := json.Unmarshal([]byte(args[0]), query_bill)
	if err != nil {
		res := getRetString(0, 1000, "解析失敗")
		return shim.Error(res)
	}

	fmt.Println(query_bill)

	if query_bill.VersionType == "last" {
		//查詢最新合約資訊
		return a.queryLastBill(stub, query_bill.UserCode, query_bill.ContractCode)
	} else {
		//查詢所有合約資訊
		return a.queryWholeBill(stub, query_bill.UserCode, query_bill.ContractCode)
	}
}

//查詢最新合約資訊
func (a *BillChaincode) queryLastBill(stub shim.ChaincodeStubInterface, userCode string, contractCode string) pb.Response {
	key_id := contractCode
	//查詢合約
	bill, bl := a.getBill(stub, key_id)
	if !bl {
		res := getRetString(0, 1000, "查詢失敗")
		return shim.Error(res)
	}

	var history []Bill
	var hist = bill
	history = append(history, hist)
	res := getQueryByte(1, 0, "", history)
	return shim.Success(res)
}

//查詢所有合約資訊
func (a *BillChaincode) queryWholeBill(stub shim.ChaincodeStubInterface, userCode string, contractCode string) pb.Response {
	key_id := contractCode
	//fabric的API查詢歷史
	resultsIterator, err := stub.GetHistoryForKey(key_id)
	if err != nil {
		res := getRetString(0, 1000, "查詢合約歷史失敗")
		return shim.Error(res)
	}
	defer resultsIterator.Close()

	var history []Bill
	//迴圈遍歷
	for resultsIterator.HasNext() {
		historyData, err := resultsIterator.Next()
		if err != nil {
			res := getRetString(0, 1000, "遍歷合約歷史失敗")
			return shim.Error(res)
		}
		//定義合約結構
		var hist Bill
		if historyData.Value == nil {
			var emptyBill Bill
			hist = emptyBill
		} else {
			json.Unmarshal(historyData.Value, &hist)
			fmt.Println(hist)
		}
		history = append(history, hist)
		fmt.Println(history)
	}

	res := getQueryByte(1, 0, "", history)
	return shim.Success(res)
}

//根據傳的狀態碼,返回查詢的位元組陣列
func getQueryByte(result int, code int, msg string, hist []Bill) []byte {
	var r queryRet
	//組裝資料
	r.Result = result
	r.ErrorCode = code
	r.ErrorMsg = msg
	r.DataList = hist
	b, err := json.Marshal(r)
	if err != nil {
		fmt.Println("序列化失敗")
		return nil
	}
	return b
}

func main() {
	if err := shim.Start(new(BillChaincode)); err != nil {
		fmt.Printf("啟動鏈碼失敗:%s", err)
	}
}
  • 安裝鏈碼

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

  • 例項化鏈碼

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

  • 釋出合約

peer chaincode invoke -C mychannel -n contract -c '{"Args":["link_contract_create","{\"task_id\":\"123456\",\"user_code\":\"user1\",\"contract_code\":\"con123456\",\"purchaser_user_code\":\"user1\",\"biding_start_time\":\"20180101\",\"biding_end_time\":\"20181201\",\"close_time\":\"\",\"contract_start_time\":\"20190101\",\"contract_end_time\":\"20200101\",\"contract_status\":\"yes\"}"]}'

  • 響應合約

peer chaincode invoke -C mychannel -n contract -c '{"Args":["link_contract_deal","{\"task_id\":\"123456\",\"user_code\":\"user2\",\"contract_code\":\"con123456\",\"contract_status\":\"\"}"]}'

  • 合約成交

peer chaincode invoke -C mychannel -n contract -c '{"Args":["link_contract_deal","{\"task_id\":\"123456\",\"user_code\":\"user1\",\"contract_code\":\"con123456\",\"deal_time\":\"20181201\",\"contract_status\":\"deal\"}"]}'

  • 合約關閉

peer chaincode invoke -C mychannel -n contract -c '{"Args":["link_contract_close","{\"task_id\":\"123456\",\"user_code\":\"user1\",\"contract_code\":\"con123456\",\"close_time\":\"20200101\",\"contract_status\":\"close\"}"]}'

  • 查詢合約的最新資料

peer chaincode invoke -C mychannel -n contract -c '{"Args":["query","{\"task_id\":\"123456\",\"user_code\":\"user1\",\"contract_code\":\"con123456\",\"version_type\":\"last\"}"]}'

  • 查詢合約所有資料

peer chaincode invoke -C mychannel -n contract -c '{"Args":["query","{\"task_id\":\"123456\",\"user_code\":\"user1\",\"contract_code\":\"con123456\",\"version_type\":\"whole\"}"]}'