文章目录
快速编写和运行一个Chaincode
创建和安装链码
系统管理接口
GetFunctionAndParameters
该方法负责接收调用者传过来的参数。
//方法定义
GetFunctionAndParameters() (string, []string)
//调用
peer chaincode instantiate -o orderer.simple-network.com:7050 -C testchannel -n r_test_cc6 -v 1.0 -c '{"Args":["init", "a", "100", "b", "200"]}' -P "OR ('Org1MSP.member', 'Org2MSP.member')"
//chaincode
func (t *SimpleChaincode) Init(stub shim.ChaincodeStubInterface) pb.Response {
//fn是方法名 function name,传入invoke或者init
fn, args := stub.GetFunctionAndParameters()
fmt.Println(fn, args)
return shim.Success(nil)
}
//输出 fn:init ,args是数组,arg[0]=a
init [a 100 b 200]
其他可以参考API。
存储管理接口
PutState
该方法负责把数据保存到fabric账本中。
//方法定义
PutState(key string, value []byte) error
//chaincode代码示例
type Student struct {
Name string
Age uint8
Score uint8
}
func (t *SimpleChaincode) Invoke(stub shim.ChaincodeStubInterface) pb.Response {
stu := Student{
"zhangsan",
25,
99,
}
bytes, err := json.Marshal(stu)
if err != nil {
return shim.Error(err.Error())
}
stub.PutState("zhangsan", bytes)
return shim.Success(nil)
}
GetState
该方法负责从fabric账本中读取数据。
//方法定义
GetState(key string) ([]byte, error)
//chaincode代码示例
type Student struct {
Name string
Age uint8
Score uint8
}
func (t *SimpleChaincode) Invoke(stub shim.ChaincodeStubInterface) pb.Response {
bytes, err := stub.GetState("zhangsan")
if err != nil{
return shim.Error(err.Error())
}
stu := new(Student)
err = json.Unmarshal(bytes, stu)
if err != nil{
return shim.Error(err.Error())
}
fmt.Println(stu)
return shim.Success(nil)
}
DelState
DelState用来删除账本中的key,如果对一个不存在的的key-value进行删除不会返回错误。
//方法定义
DelState(key string) error
//chaincode代码示例
func (t *SimpleChaincode) Invoke(stub shim.ChaincodeStubInterface) pb.Response {
err := stub.DelState("zhangsan")
if err != nil{
fmt.Println(err)
}
return shim.Success(nil)
}
GetStateByRange
根据key的范围来查询数据。
//方法定义
//包含startKey,不包含endKey
//如果startKey和endKey为空,则无限范围查询
GetStateByRange(startKey, endKey string) (StateQueryIteratorInterface, error)
//chaincode代码示例
func (t *SimpleChaincode) set(stub shim.ChaincodeStubInterface, args []string)pb.Response {
stub.PutState("1", []byte("zhangsan"))
stub.PutState("2", []byte("lisi"))
stub.PutState("3", []byte("wangwu"))
stub.PutState("4", []byte("zhaoliu"))
stub.PutState("5", []byte("sunqi"))
return shim.Success(nil)
}
func (t *SimpleChaincode) get(stub shim.ChaincodeStubInterface, args []string) pb.Response {
keysIter, err := stub.GetStateByRange("2", "5")
if err != nil{
return shim.Error(err.Error())
}
rsp := make(map[string]string)
for keysIter.HasNext(){
response, interErr := keysIter.Next()
if interErr != nil{
return shim.Error(interErr.Error())
}
rsp[response.Key] = string(response.Value)
fmt.Println(response.Key, string(response.Value))
}
//将结果以json字符串返回
jsonRsp, err := json.Marshal(rsp)
if err != nil{
return shim.Error(err.Error())
}
return shim.Success(jsonRsp)
}
func (t *SimpleChaincode) Invoke(stub shim.ChaincodeStubInterface) pb.Response {
function, args := stub.GetFunctionAndParameters()
if function == "set" {
return t.set(stub, args)
} else if function == "get" {
return t.get(stub, args)
}
return shim.Success(nil)
}
GetHistoryForKey
查询某个键的历史修改记录。返回的记录包括交易的编号、修改的值、当前key的有没有被删除,交易发生的时间戳,GetHistoryForKey需要peer的配置参数core.ledger.history.enableHistoryDatabase为true。
查询交易所在区块中其他更新操作,本次查询无法查询到。
//方法定义
GetHistoryForKey(key string) (HistoryQueryIteratorInterface, error)
//chaincode代码示例
func (t *SimpleChaincode) set(stub shim.ChaincodeStubInterface, args []string)pb.Response {
name := args[0]
err := stub.PutState("userName", []byte(name))
if err != nil{
return shim.Error(err.Error())
}
return shim.Success(nil)
}
func (t *SimpleChaincode) history(stub shim.ChaincodeStubInterface, args []string)pb.Response {
keyInter, err := stub.GetHistoryForKey("name")
if err != nil{
return shim.Error(err.Error())
}
for keyInter.HasNext(){
response, interErr := keyInter.Next()
if interErr != nil{
return shim.Error(interErr.Error())
}
txid := response.TxId
txvalue := response.Value
txstatus := response.IsDelete
txtimestamp := response.Timestamp
tm := time.Unix(txtimestamp.Seconds, 0)
timeString := tm.Format("2006-01-02 03:04:05 PM")
fmt.Println(txid, string(txvalue), txstatus, timeString)
}
return shim.Success(nil)
}
链码日志输出:
先调用链码的set方法修改三次,然后在调用history方法获取记录
peer chaincode invoke -o orderer.simple-network.com:7050 -C testchannel -n r_test_cc6 -v 1.0 -c '{"Args":["set", "zhangsan"]}'
peer chaincode invoke -o orderer.simple-network.com:7050 -C testchannel -n r_test_cc6 -v 1.0 -c '{"Args":["set", "lisi"]}'
peer chaincode invoke -o orderer.simple-network.com:7050 -C testchannel -n r_test_cc6 -v 1.0 -c '{"Args":["set", "wangwu"]}'
输出:
bdfd3d29622d41350493d5421d6759c56a38273033a31e3d70e091856e62dc69 zhangsan false 2019-03-14 03:28:02 PM
692dba2bf1c7c24ede17248310df58726a974210afe2097b3ff10ce8a3381ab5 lisi false 2019-03-14 03:28:02 PM
5864c768fad78049969891a315838ed1336fa86ed138f2ca87aee4e09900b4bc wangwu false 2019-03-14 03:28:02 PM
CreateCompositeKey & GetStatePyPartialCompositeKey
创建组合键。
//方法定义
CreateCompositeKey(objectType string, attributes []string) (string, error)
//方法定义
GetStateByPartialCompositeKey(objectType string, keys []string) (StateQueryIteratorInterface, error)
//chaincode代码示例
func (t *SimpleChaincode) set(stub shim.ChaincodeStubInterface, args []string)pb.Response {
stus := []Student{
{"lisi", 20, 99},
{"lisi",20,98},
{"lisi",21,100},
}
for i, _ := range(stus) {
stu := stus[i]
key, err := stub.CreateCompositeKey(stu.Name, []string{strconv.Itoa(stu.Age), strconv.Itoa(stu.Score)})
if err != nil {
fmt.Println(err)
return shim.Error(err.Error())
}
bytes, err := json.Marshal(stu)
if err != nil {
fmt.Println(err)
return shim.Error(err.Error())
}
stub.PutState(key, bytes)
}
return shim.Success(nil)
}
func (t *SimpleChaincode) get(stub shim.ChaincodeStubInterface, args []string) pb.Response {
rs, err := stub.GetStateByPartialCompositeKey("lisi", []string{})
if err != nil{
fmt.Println(err)
return shim.Error(err.Error())
}
defer rs.Close()
for rs.HasNext(){
responseRange, err := rs.Next()
if err != nil{
fmt.Println(err)
}
stu := new(Student)
err = json.Unmarshal(responseRange.Value, stu)
if err != nil{
fmt.Println(err)
}
fmt.Println(responseRange.Key, stu)
}
return shim.Success(nil)
}
链码日志输出:
lisi2098 &{lisi 20 98}
lisi2099 &{lisi 20 99}
lisi21100 &{lisi 21 100}
由于stub.GetStateByPartialCompositeKey(“lisi”, []string{}),只指定了一个主键,所以把叫lisi的学生都查询出来了。
如果按修改代码如下:
rs, err := stub.GetStateByPartialCompositeKey("lisi", []string{"20"})
链码日志输出如下:
lisi2098 &{lisi 20 98}
lisi2099 &{lisi 20 99}
由于21岁的lisi不满足组合键要求,所以没有查询出来
SplitCompositeKey
分割组合键, 将创建的组合键分割开来
//方法定义
SplitCompositeKey(compositeKey string) (string, []string, error)
//chaincode代码示例
func (t *SimpleChaincode) Invoke(stub shim.ChaincodeStubInterface) pb.Response {
key, _ := stub.CreateCompositeKey("sili", []string{"20", "100"})
key1, keyPart, _:= stub.SplitCompositeKey(key)
fmt.Println(key1, keyPart)
return shim.Success(nil)
}
链码日志输出:
sili20100
sili [20 100]
交易管理接口
GetTxID
获取当前调用交易的ID号
//方法定义
GetTxID() string
//chaincode代码示例
func (t *SimpleChaincode) get(stub shim.ChaincodeStubInterface, args []string) pb.Response {
id := stub.GetTxID()
fmt.Println(id)
return shim.Success(nil)
}
链码日志输出:
66a925816afb2c2067f014a29ad8609ae40a405d5997f79c4c930e4d79c2eb5f
GetCreator
获得提案的签名者证书
//方法定义
GetTxID() string
//chaincode代码示例
func (t *SimpleChaincode) get(stub shim.ChaincodeStubInterface, args []string) pb.Response {
id := stub.GetTxID()
fmt.Println(id)
return shim.Success(nil)
}
链码日志输出:
Org1MSP�-----BEGIN -----
MIICLjCCAdWgAwIBAgIQJcxoHNiOAWmIXH7VZ/6fDDAKBggqhkjOPQQDAjCBgTEL
MAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExFjAUBgNVBAcTDVNhbiBG
cmFuY2lzY28xIDAeBgNVBAoTF29yZzEuc2ltcGxlLW5ldHdvcmsuY29tMSMwIQYD
VQQDExpjYS5vcmcxLnNpbXBsZS1uZXR3b3JrLmNvbTAeFw0xOTAzMTAxNTA5MzBa
Fw0yOTAzMDcxNTA5MzBaMGIxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9y
bmlhMRYwFAYDVQQHEw1TYW4gRnJhbmNpc2NvMSYwJAYDVQQDDB1BZG1pbkBvcmcx
LnNpbXBsZS1uZXR3b3JrLmNvbTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABJWM
1LNOMnDGnbpe0XEx7HNFyBZgOZk5E+PDtHer/ZbbFvCIvoFoafzw7qIwlOxXTj99
bOG3kHQX2OPp/+7/nEajTTBLMA4GA1UdDwEB/wQEAwIHgDAMBgNVHRMBAf8EAjAA
MCsGA1UdIwQkMCKAIPXBTNkkfW7yIcRyZvip+yXmvzwPBujCNja1BMv3f+BmMAoG
CCqGSM49BAMCA0cAMEQCIG1Q+qvhCSv/GZtINP51WZLUpUDtABiY8RsmetPqK1Rh
AiBsYoFWSQeVRspiTuEz1dhW+ke2iq05P3k+gFzN1yiUUw==
-----END -----
GetTxTimestamp
//方法定义
GetTxTimestamp() (*timestamp.Timestamp, error)
//chaincode代码示例
func (t *SimpleChaincode) get(stub shim.ChaincodeStubInterface, args []string) pb.Response {
timestamp, _ := stub.GetTxTimestamp()
tm:= time.Unix(timestamp.Seconds, 0)
ts := tm.Format("2006-01-02 03:04:05 PM")
fmt.Println(ts)
return shim.Success(nil)
}
链码日志输出:
2019-03-14 06:12:49 PM
InvokeChaincode
调用外部chaincode
//方法定义
InvokeChaincode(chaincodeName string, args [][]byte, channel string) pb.Response
//chaincode代码示例
func (t *SimpleChaincode) set(stub shim.ChaincodeStubInterface, args []string)pb.Response {
parms := []string{"get"}
querArgs := make([][]byte, len(parms))
for i, arg := range parms{
querArgs[i] = []byte(arg)
}
response := stub.InvokeChaincode("r_test_cc8", querArgs, "testchannel")
if response.Status != shim.OK{
err := fmt.Sprintln("fail to query chaincode, Got error :%s", response.Payload)
shim.Error(err)
}
return shim.Success(nil)
}
//r_test_cc8链码中的get函数实现
func (t *SimpleChaincode) get(stub shim.ChaincodeStubInterface, args []string) pb.Response {
fmt.Println("Thank you for calling me")
return shim.Success(nil)
}