1. Overview of chain code structure
Here, the development of the chain code uses the Go language. For this reason, you need to learn the Go language briefly. This is a lightweight language. The interesting thing is that it has its own channels and can be concurrent, which is very suitable for large-scale distributed systems. Development.
To start the chain code, the Start function in the shim package must be called. The parameter of this function is a Chaincode interface type. There are two methods in the Chaincode interface type, Init and Invoke. These are two extremely important methods in chain code development:
- Init: Called when the chain code is instantiated or upgraded to complete data initialization;
- Invoke: Called when updating or querying the status of the ledger data in a proposal transaction.
In actual development, you need to define a structure and rewrite the Init and Invoke methods to complete related functions. Let's take a concrete look at the necessary structure of a chain code:
package main //所写的包的名称
import (
"fmt"
"github.com/hyperledger/fabric/core/chaincode/shim"
"github.com/hyperledger/fabric/protos/peer"
)//引入必要的包
type HelloChaincode struct{
}//定义一个结构体
func main() {
err := shim.Start(new(HelloChaincode))
if err != nil {
fmt.Printf("链码启动失败: %v", err)
}
}//主函数,调用shim.Start方发启动链码
func (t *HelloChaincode) Init(stub shim.ChaincodeStubInterface) peer.Response{
}
func (t *HelloChaincode) Invoke(stub shim.ChaincodeStubInterface) peer.Response{
fun, args := stub.GetFunctionAndParameters()
}
2. Familiar with chaincode related API
Mainly the API provided by the shim package, divided into 5 categories:
- Parameter analysis API: used to obtain parameters
- Ledger data status operation API: query and update account data, etc.
- Transaction information acquisition API: get submitted transaction information
- Event processing API: related to event processing
- API for private data manipulation: specifically for private data manipulation
The number of APIs is still relatively large, most of which are for obtaining relevant information. If you have time, you can take a closer look. The main ones used in the initial stage:
- GetFunctionAndParameters()(function string, params []string) returns the name of the called function and the list of parameters
- GetStringArgs()[]string returns the parameter list directly
- GetState(key string)([]byte,error) Query the data state according to the specified key value
- PutState(key string,value []byte)error According to the specified key, save the corresponding value to the ledger
3. Chaincode implements Hello World
3.1 Chaincode development
First write a hello world to practice hands.
- Go to the chaincode directory and create a folder named hello, then create and edit the chaincode file:
sudo mkdir hello && cd hello
sudo vim hello.go
- Import the chain code dependency package:
package main
import (
"fmt"
"github.com/hyperledger/fabric/core/chaincode/shim"
"github.com/hyperledger/fabric/protos/peer"
)
- Write the main function:
func main() {
err := shim.Start(new(HelloChaincode))
if err != nil {
fmt.Printf("链码启动失败: %v", err)
}
}
- Custom structure:
type HelloChaincode struct{
}
- Rewrite the Init method. Its function is to initialize the data state. A simple logical step is as follows:
- Obtain parameters and determine whether the parameters are legal;
- Call PutState function to write the state into the ledger;
- Whether there is a write error;
- Finally, call the Success function to return a success status.
func (t *HelloChaincode) Init(stub shim.ChaincodeStubInterface) peer.Response{
fmt.Println("开始实例化链码")
_, args := stub.GetFunctionAndParameters()
if len(args) != 2 {
return shim.Error("指定了错误的参数个数")
}
fmt.Println("保存数据")
err := stub.PutState(args[0],[]byte(args[1]))
if err !=nil{
return shim.Error("保存数据时发生错误")
}
fmt.Println("实例化成功")
return shim.Success(nil)
}
You can pay attention to the GetFunctionAndParameters() function, where the parameter form is: "Args":["init","Hello","Wzh"], the function gets the called function "init" and the passed parameter "Hello" ","Wzh", so written in the form of _,args, can also be replaced with GetStringArgs() function to directly get the following parameters.
- Rewrite the Invoke method, its simple logic is:
- Obtain parameters and determine whether the parameters are legal;
- Use the key value to get the status;
- Complete the function of the called function;
- Return data status or success status.
func (t *HelloChaincode) Invoke(stub shim.ChaincodeStubInterface) peer.Response{
fun, args := stub.GetFunctionAndParameters()
if fun == "query"{
return query(stub,args)
}
return shim.Error("非法操作,指定功能不能实现")
}
func query(stub shim.ChaincodeStubInterface,args []string) peer.Response{
if len(args) !=1{
return shim.Error("指定的参数错误,必须且只能指定相应的Key")
}
result,err :=stub.GetState(args[0])
if err !=nil {
return shim.Error("根据指定的" +args[0] +"查询数据时发生错误")
}
if result ==nil {
return shim.Error("根据指定的" +args[0] +"没有查询到相应的数据")
}
return shim.Success(result)
}
This piece of code is very simple and clear, so I won't repeat it.
3.2 Chaincode test
It's the link of chain code testing again. The specific process is in the previous log. Only the code is attached below:
cd chaincode-docker-devmode
sudo docker-compose -f docker-compose-simple.yaml up -d
Open the terminal 2 window:
sudo docker exec -it chaincode bash
cd hello
go build
CORE_PEER_ADDRESS=peer:7052 CORE_CHAINCODE_ID_NAME=hellocc:0 ./hello
Open the terminal 3 window:
sudo docker exec -it cli bash、
peer chaincode install -p chaincodedev/chaincode/hello -n hellocc -v 0
peer chaincode instantiate -n hellocc -v 0 -c '{"Args":["init","Hello","Wzh"]}' -C myc
peer chaincode query -n hellocc -c '{"Args":["query","Hello"]}' -C myc
The final query should be able to get Wzh (that is, the content of the Hello key assignment)