智能合约链码开发和部署
一、环境和语言
Go语言开发fabric链码,Golang自行安装,语法基础需要自行学习。
二、便签版合约实现
2.1、定义链码对象
golang实现的链码需要先定义合约对象,对象定位空即可
type SmartContract struct {
}
2.2、便签对象实现
便签版合约的实现关键在于定义一个便签在区块链上存储的数据格式,首先定义一个便签的结构体。
type Note struct {
ID int ‘json:“id”’
Name string 'json:"name"'
Status string 'json:"status"'
Sex string 'json:"sex"'
}
2.3、Init接口的实现
在本合约中,并不需要初始化部分便签数据,所以init接口直接返回成功。
func (s *SmartContract) Init(APIstub shim.ChaincodeStubInterface) sc.Response {
return shim.Success(nil)
}
2.4、invoke接口实现和便签的增删改查
1、fabric链码的核心接口是invoke,实现特定地用户业务逻辑。
func (s *SmartContract) Invoke(APIstub shim.ChaincodeStubInterface) sc.Response {
function, args := APIstub.GetFunctionAndParameters()
if function == "queryAll" {
return s.queryAll(APIstub)
} else if function == "insert" {
return s.insert(APIstub, args)
} else if function == "update" {
return s.update(APIstub, args)
}
return shim.Error("Invalid Smart Contract function name.")
}
2、新增便签
新增便签需要构建一个Note对象,对象中的sex、name、status由用户定义传入,本例中为示例,id暂时由时间戳生成,实现实现中可采用其他算法来保证id的唯一性。
fabric链上的数据必须是key-value格式,key是string类型,value是byte类型。在本合约中,使用id作为key,在调用该方法时,传入的4个参数,id,name,status,sex都是字符串类型。在构建Note对象时首先需将int类型的id转换为string类型,并将Note对象转换为byte格式。
func (s *SmartContract) insert(APIstub shim.ChaincodeStubInterface, args []string) sc.Response {
if len(args) != 4 {
return shim.Error("Incorrect number of arguments. Expecting 3")
}
noteId, transErr := strconv.Atoi(args[0])
if transErr != nil {
fmt.Printf("Error trans args to get note id: %s", transErr)
return shim.Error("Incorrect type of arguments. Id expecting int")
}
var note = Note {
Id: noteId,
Name: args[1],
Status: args[2],
Sex: args[3],
}
carAsBytes, _ := json.Marshal(note)
err := APIstub.PutState(args[0], carAsBytes)
if err != nil {
return shim.Error(err.Error())
}
return shim.Success(nil)
}
3、更新便签
先从Note id中找到note对象,若不存在报错,若对象存在则进行更新
func (s *SmartContract) update(APIstub shim.ChaincodeStubInterface, args []string) sc.Response {
if len(args) != 4 {
return shim.Error("Incorrect number of arguments. Expecting 4")
}
noteAsBytes, _ := APIstub.GetState(args[0])
if noteAsBytes == nil {
return shim.Error("Note not found.")
}
note := Note{}
json.Unmarshal(noteAsBytes, ¬e)
note.Name= args[1]
note.Status = args[2]
note.Sex = args[3]
noteAsBytes, _ = json.Marshal(note)
err := APIstub.PutState(args[0], noteAsBytes)
if err != nil {
return shim.Error(err.Error())
}
return shim.Success(nil)
}
4。获取所有标签
Fabric链码提供的接口中支持遍历功能,通过指定start和endkey,以字典排序查询到相应记录,如果start和endkey都设置为空字符串,查询的是所有记录。与新增便签一样,查询出来的note对象也是以byte数组方式存储的,需要进行解码。之后以Id为key,以note对象为value构建Map,并将Map编码为byte数组返回。
unc (s *SmartContract) queryAll(APIstub shim.ChaincodeStubInterface) sc.Response {
resultsIterator, err := APIstub.GetStateByRange("", "")
if err != nil {
return shim.Error(err.Error())
}
defer resultsIterator.Close()
// buffer is a JSON array containing QueryResults
notes := map[int]Note{}
for resultsIterator.HasNext() {
queryResponse, err := resultsIterator.Next()
if err != nil {
return shim.Error(err.Error())
}
var tmpNote Note
noteId, keyTransErr := strconv.Atoi(queryResponse.Key)
if keyTransErr != nil {
fmt.Printf("Error trans note id: %s", keyTransErr)
continue
}
if transErr := json.Unmarshal(queryResponse.Value, &tmpNote); err != nil {
fmt.Printf("Error trans note: %s", transErr)
notes[noteId] = Note{}
} else {
notes[noteId] = tmpNote
}
}
result, _ := json.Marshal(notes)
return shim.Success(result)
}
5、链码完整代码
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 SmartContract struct {
}
type Note struct {
ID int ‘json:“id”’
Name string 'json:"name"'
Status string 'json:"status"'
Sex string 'json:"sex"'
}
func (s *SmartContract) Init(APIstub shim.ChaincodeStubInterface) sc.Response {
return shim.Success(nil)
}
func (s *SmartContract) Invoke(APIstub shim.ChaincodeStubInterface) sc.Response {
function, args := APIstub.GetFunctionAndParameters()
if function == "queryAll" {
return s.queryAll(APIstub)
} else if function == "insert" {
return s.insert(APIstub, args)
} else if function == "update" {
return s.update(APIstub, args)
}
return shim.Error("Invalid Smart Contract function name.")
}
func (s *SmartContract) queryAll(APIstub shim.ChaincodeStubInterface) sc.Response {
resultsIterator, err := APIstub.GetStateByRange("", "")
if err != nil {
return shim.Error(err.Error())
}
defer resultsIterator.Close()
// buffer is a JSON array containing QueryResults
notes := map[int]Note{}
for resultsIterator.HasNext() {
queryResponse, err := resultsIterator.Next()
if err != nil {
return shim.Error(err.Error())
}
var tmpNote Note
noteId, keyTransErr := strconv.Atoi(queryResponse.Key)
if keyTransErr != nil {
fmt.Printf("Error trans note id: %s", keyTransErr)
continue
}
if transErr := json.Unmarshal(queryResponse.Value, &tmpNote); err != nil {
fmt.Printf("Error trans note: %s", transErr)
notes[noteId] = Note{}
} else {
notes[noteId] = tmpNote
}
}
result, _ := json.Marshal(notes)
return shim.Success(result)
}
func (s *SmartContract) insert(APIstub shim.ChaincodeStubInterface, args []string) sc.Response {
if len(args) != 4{
return shim.Error("Incorrect number of arguments. Expecting 4")
}
noteId, transErr := strconv.Atoi(args[0])
if transErr != nil {
fmt.Printf("Error trans args to get note id: %s", transErr)
return shim.Error("Incorrect type of arguments. Id expecting int")
}
var note = Note {
Id: noteId,
note.Name= args[1]
note.Status = args[2]
note.Sex = args[3]
}
carAsBytes, _ := json.Marshal(note)
err := APIstub.PutState(args[0], carAsBytes)
if err != nil {
return shim.Error(err.Error())
}
return shim.Success(nil)
}
func (s *SmartContract) update(APIstub shim.ChaincodeStubInterface, args []string) sc.Response {
if len(args) != 4 {
return shim.Error("Incorrect number of arguments. Expecting 4")
}
noteAsBytes, _ := APIstub.GetState(args[0])
if noteAsBytes == nil {
return shim.Error("Note not found.")
}
note := Note{}
json.Unmarshal(noteAsBytes, ¬e)
note.Name= args[1]
note.Status = args[2]
note.Sex = args[3]
noteAsBytes, _ = json.Marshal(note)
err := APIstub.PutState(args[0], noteAsBytes)
if err != nil {
return shim.Error(err.Error())
}
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(SmartContract))
if err != nil {
fmt.Printf("Error creating new Smart Contract: %s", err)
}
}