本文是在阅读《区块链开发实战-Hyperledger Fabric关键技术与案例分析》一书的同时,在实践中记录的一些实践步骤与经验分享。
Hyperledger Fabric开发实战-05证书服务器
在上一节Hyperledger Fabric开发实战-03智能合约中,演示了一个简单的Chaincode示例,可以看到,Chaincode中主要是Init和Invoke方法的实现。两个方法的原型如下:
func (t *mychaincode) Init(stub shim.ChaincodeStubInterface) pb.Response{
}
func (t *mychaincode) Invoke(stub shim.ChaincodeStubInterface) pb.Response{
}
可以看到,主要是对shim.ChaincodeStubInterface的使用。
shim接口
shim.ChaincodeStubInterface
// 获取传入的参数,args是一个数组
function,args = stub.GetFunctionAndParameters()
// 将数据保存到fabric中
PutState(key string, value []byte) error
// 将数据保存到fabric中
GetState(key string) ([]byte,error)
// 获取区间内的key
GetStateByRange(startKey,endKey string) (StateQueryInterface,error)
// 获取key的历史
GetHistoryForKey(key string) (HistoryQueryIteratorInterface,error)
// 删除key
DelState(key string) error
// 创建一个组合对象,类似hash对象
CreateCompositeKey(objectType string,attributes [] string) (string,error)
// 获取hash对象的属性内容
GetStateByPartialCompositeKey(objectType string,keys []string) (StateQueryInterface,error)
// 拆分复合对象的key
SplitCompositeKey(compositeKey string) (string,[]string,error)
pb.Response
shim提供了一组方法,用来包装返回的信息
返回成功
// 原型
func Success(payload []byte) pb.Response
// 使用
shim.Success([]byte("success"))
返回失败
// 原型
func Error(msg string) pb.Response
// 使用
shim.Error("success"))
Chaincode存取数据
通过上面的shim接口,我们可以看到如何将数据存放到账本中,如何从账本取出,下面编写一个例子来演示,对上一节的代码进行改动。
Init方法中,接收两个名字和金额,如["init","Alice","100","Bob","200"]
func (t *mychaincode) Init(stub shim.ChaincodeStubInterface) pb.Response{
var args []string
fmt.Println(" << ====[Init] success init it is view in docker ======")
_,args = stub.GetFunctionAndParameters()
var a ,b string
var aAmt,bAmt int
var err error
a = args[0]
aAmt,err = strconv.Atoi(args[1])
b = args[2]
bAmt,err = strconv.Atoi(args[3])
fmt.Println("aAmt is ",aAmt)
fmt.Println("bAmt is ",bAmt)
stub.PutState(a,[]byte(args[1]))
stub.PutState(b,[]byte(args[3]))
if err != nil{
fmt.Println("error")
}
return shim.Success([]byte("success init"))
}
Invoke方法中,接受A到B的转账,如["invoke","Alice","Bob","10"]
func (t *mychaincode) Invoke(stub shim.ChaincodeStubInterface) pb.Response{
var args []string
var aAmtBytes,bAmtBytes []byte
fmt.Println(" << ====[Invoke] success init it is view in docker ======")
_,args = stub.GetFunctionAndParameters()
var a ,b string
var aAmt,bAmt int
var transAmt int
//var err error
a = args[0]
b = args[1]
transAmt,_ = strconv.Atoi(args[2])
aAmtBytes,_ = stub.GetState(a)
bAmtBytes,_ = stub.GetState(b)
aAmt, _ = strconv.Atoi(string(aAmtBytes))
bAmt, _ = strconv.Atoi(string(bAmtBytes))
fmt.Println("aAmt form fabric is ",aAmt)
fmt.Println("bAmt form fabric is ",bAmt)
aAmt = aAmt - transAmt
bAmt = bAmt + transAmt
stub.PutState(a, []byte(strconv.Itoa(aAmt)))
stub.PutState(b, []byte(strconv.Itoa(bAmt)))
return shim.Success([]byte("success init"))
}
编写完成后,还需要编写main方法
func main(){
err := shim.Start(new(mychaincode))
if err != nil{
fmt.Println("Error starting Simple chaincode : %s",err)
}
}
之后,使用go build
构建,再按照上一节的方法,安装,实例化,调用测试,使用docker logs查询日志
<< ====[Init] success init it is view in docker ======
aAmt is 100
bAmt is 200
<< ====[Invoke] success init it is view in docker ======
aAmt form fabric is 100
bAmt form fabric is 200
<< ====[Invoke] success init it is view in docker ======
aAmt form fabric is 90
bAmt form fabric is 210
<< ====[Invoke] success init it is view in docker ======
aAmt form fabric is 80
bAmt form fabric is 220
编写完成Chaincode后,可以执行
go build
查看是否编译成功,如果安装和实例化之后更改了Chaincode程序,需要重新build,然后使用peer chaincode upgrade命令,设置最新的版本号,更新chaincode