Fabric 2.0之后对链码做了较大改进,在接下来的几篇博客中加以说明。本节主要讲解Fabric 2.x链码的基本使用。基本链码如下所示:
package main
import (
"errors"
"fmt"
"github.com/hyperledger/fabric-contract-api-go/contractapi"
)
type SimpleContract struct {
contractapi.Contract
}
//上链
func (sc *SimpleContract)Create(ctx contractapi.TransactionContextInterface,key string,value string)error {
existing, err := ctx.GetStub().GetState(key)
if err!=nil {
return errors.New("查询失败!")
}
if existing !=nil{
return fmt.Errorf("添加数据错误!%s已经存在。",key)
}
err = ctx.GetStub().PutState(key, []byte(value))
if err!=nil {
return errors.New("添加数据失败!")
}
return nil
}
//更新
func (sc *SimpleContract)Update(ctx contractapi.TransactionContextInterface,key string,value string)error {
bytes, err := ctx.GetStub().GetState(key)
if err!=nil {
return errors.New("查询失败!")
}
if bytes==nil {
return fmt.Errorf("没有查询到%s对应的数据",key)
}
err = ctx.GetStub().PutState(key, []byte(value))
if err!=nil {
return errors.New("更新失败:"+err.Error())
}
return nil
}
//查询
func (sc *SimpleContract)Read(ctx contractapi.TransactionContextInterface,key string)(string,error) {
bytes, err := ctx.GetStub().GetState(key)
if err!=nil {
return "",errors.New("查询失败!")
}
if bytes==nil {
return "", fmt.Errorf("数据不存在,读到的%s对应的数据为空!",key)
}
return string(bytes),nil
}
func main() {
contract := new(SimpleContract)
cc, err := contractapi.NewChaincode(contract)
if err!=nil {
panic("创建智能合约失败:"+err.Error())
}
if err:=cc.Start();err!=nil {
panic("启动智能合约失败:"+err.Error())
}
}
注:Fabric 2.0的链码与1.x相比,主要区别为:
1、导入包的不同
1.x导入的包为:
"github.com/hyperledger/fabric/core/chaincode/shim"
pb "github.com/hyperledger/fabric/protos/peer"2.0导入的包为:
"github.com/hyperledger/fabric-contract-api-go/contractapi"
2、方法结构不同
Fabric 2.0链码没有 Invoke(stub shim.ChaincodeStubInterface) pb.Response{ }方法。
3、方法中调用形式参数类型、返回值不同
1.x方法为:
createCar1(APIstub shim.ChaincodeStubInterface, args []string) pb.Response { }
2.0方法为:
Create(ctx contractapi.TransactionContextInterface,key string,value string)error { }
附录:
1.x对应的链码
package mian
/* Imports
* 4 utility libraries for formatting, handling bytes, reading and writing JSON, and string manipulation
* 2 specific Hyperledger Fabric specific libraries for Smart Contracts
*/
import (
"encoding/json"
"fmt"
"strconv"
"github.com/hyperledger/fabric/core/chaincode/shim"
sc "github.com/hyperledger/fabric/protos/peer"
)
// Define the Smart Contract structure
type SmartContract1 struct {
}
// Define the Car1 structure, with 4 properties. Structure tags are used by encoding/json library
type Car1 struct {
Make string `json:"make"`
Model string `json:"model"`
Colour string `json:"colour"`
Owner string `json:"owner"`
}
/*
* The Init method is called when the Smart Contract "fabCar1" is instantiated by the blockchain network
* Best practice is to have any Ledger initialization in separate function -- see initLedger()
*/
func (s *SmartContract1) Init(APIstub shim.ChaincodeStubInterface) sc.Response {
return shim.Success(nil)
}
/*
* The Invoke method is called as a result of an application request to run the Smart Contract "fabCar1"
* The calling application program has also specified the particular smart contract function to be called, with arguments
*/
func (s *SmartContract1) Invoke(APIstub shim.ChaincodeStubInterface) sc.Response {
// Retrieve the requested Smart Contract function and arguments
function, args := APIstub.GetFunctionAndParameters()
// Route to the appropriate handler function to interact with the ledger appropriately
if function == "queryCar1" {
return s.queryCar1(APIstub, args)
} else if function == "initLedger" {
return s.initLedger(APIstub)
} else if function == "createCar1" {
return s.createCar1(APIstub, args)
}
return shim.Error("Invalid Smart Contract function name.")
}
func (s *SmartContract1) queryCar1(APIstub shim.ChaincodeStubInterface, args []string) sc.Response {
if len(args) != 1 {
return shim.Error("Incorrect number of arguments. Expecting 1")
}
Car1AsBytes, _ := APIstub.GetState(args[0])
return shim.Success(Car1AsBytes)
}
func (s *SmartContract1) initLedger(APIstub shim.ChaincodeStubInterface) sc.Response {
Car1s := []Car1{
Car1{Make: "Toyota", Model: "Prius", Colour: "blue", Owner: "Tomoko"},
Car1{Make: "Ford", Model: "Mustang", Colour: "red", Owner: "Brad"},
Car1{Make: "Hyundai", Model: "Tucson", Colour: "green", Owner: "Jin Soo"},
Car1{Make: "Volkswagen", Model: "Passat", Colour: "yellow", Owner: "Max"},
Car1{Make: "Tesla", Model: "S", Colour: "black", Owner: "Adriana"},
Car1{Make: "Peugeot", Model: "205", Colour: "purple", Owner: "Michel"},
Car1{Make: "Chery", Model: "S22L", Colour: "white", Owner: "Aarav"},
Car1{Make: "Fiat", Model: "Punto", Colour: "violet", Owner: "Pari"},
Car1{Make: "Tata", Model: "Nano", Colour: "indigo", Owner: "Valeria"},
Car1{Make: "Holden", Model: "Barina", Colour: "brown", Owner: "Shotaro"},
}
i := 0
for i < len(Car1s) {
fmt.Println("i is ", i)
Car1AsBytes, _ := json.Marshal(Car1s[i])
APIstub.PutState("Car1"+strconv.Itoa(i), Car1AsBytes)
fmt.Println("Added", Car1s[i])
i = i + 1
}
return shim.Success(nil)
}
func (s *SmartContract1) createCar1(APIstub shim.ChaincodeStubInterface, args []string) sc.Response {
if len(args) != 5 {
return shim.Error("Incorrect number of arguments. Expecting 5")
}
var Car1 = Car1{Make: args[1], Model: args[2], Colour: args[3], Owner: args[4]}
Car1AsBytes, _ := json.Marshal(Car1)
APIstub.PutState(args[0], Car1AsBytes)
return shim.Success(nil)
}
// The main function is only relevant in unit test mode. Only included here for completeness.
func main() {
// Create a new Smart Contract
err := shim.Start(new(SmartContract1))
if err != nil {
fmt.Printf("Error creating new Smart Contract: %s", err)
}
}