超級賬本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\"}"]}'