1.Chaincode接口必须被所有的链上代码实现,fabric运行交易通过调用这些指定的函数
// 在容器建立连接之后再部署交易期间调用Init函数,准许链上代码初始化内部数据
Init(stub ChaincodeStubInterface, function string, args []string) ([]byte, error)
// 每次调用交易都会调用Invoke接口. 链上代码可能会改变状态变量
Invoke(stub ChaincodeStubInterface, function string, args []string) ([]byte, error)
// 查询交易时调用Query接口. 链上代码仅仅读(而不是修改)它的状态变量并其返回结果
Query(stub ChaincodeStubInterface, function string, args []string) ([]byte, error)
}
2. ChaincodeStubInterface用来部署链上代码apps来进入和修改他们的账本
type ChaincodeStubInterface interface {
// Get the arguments to the stub call as a 2D byte array
// 获取stub调用的参数来作为一个2维字节数组
GetArgs() [][]byte
// Get the arguments to the stub call as a string array
// 获取stub调用的参数来作为一个字符数组
GetStringArgs() []string
// 获取交易的ID
GetTxID() string
// InvokeChaincode 本地调用指定的链上代码,`Invoke`使用相同的交易,也就说链上代码调用
// 链上代码不会创建一个新的交易消息
InvokeChaincode(chaincodeName string, args [][]byte) ([]byte, error)
// InvokeChaincode 本地调用指定的链上代码,`Query`使用相同的交易,也就说链上代码调用
// 链上代码不会创建一个新的交易消息
QueryChaincode(chaincodeName string, args [][]byte) ([]byte, error)
// GetState通过Key来返回数组的特定值
GetState(key string) ([]byte, error)
// PutState向账本中写入特定的键和值
PutState(key string, value []byte) error
// DelState从账本中移除指定的键和值
DelState(key string) error
// RangeQueryState函数可以通过chaincode调用来查询状态范围内的键。假设startKey和endKey
// 在词典中,将返回一个迭代器,它可以用来遍历startKey和endKey之间的所有键。
// 迭代器返回键的顺序是随机的。
RangeQueryState(startKey, endKey string) (StateRangeQueryIteratorInterface, error)
// CreateTable创建一张新表,给出表名和列定义
CreateTable(name string, columnDefinitions []*ColumnDefinition) error
// GetTable如果表存在,返回指定的一张表,如果表不存在ErrTableNotFound错误
GetTable(tableName string) (*Table, error)
// DeleteTable删除表和实体相关的所有行
DeleteTable(tableName string) error
// InsertRow 插入一个新行进入指定的表
// 返回 -
// 如果行成功插入返回true和no error
// 如果已经存在给定的行就返回false和no error
// 如果指定的表名不存在返回false和TableNotFoundError
// 如果出现一个没有预料到的错误条件返回false和error
InsertRow(tableName string, row Row) (bool, error)
// ReplaceRow 在指定的表中更新行.
// 返回 -
// 如果行成功更新就返回false和no error
// 如果给出的行不存在相应的键就返回false和no error
// 如果指定的表名不存在返回false和TableNotFoundError
// 如果出现一个没有预料到的错误条件返回false和error
ReplaceRow(tableName string, row Row) (bool, error)
// 通过键从指定的表中获取行
GetRow(tableName string, key []Column) (Row, error)
// 基于特定的key来返回多行。例如,给出表| A | B | C | D |,A,C和D是键,可以使用[A,C]调用GetRows来返回所有具有A,
// C和任何D值的行作为它们的键。 GetRows也可以用A调用,返回所有具有A和C和D作为其键值的行。
GetRows(tableName string, key []Column) (<-chan Row, error)
//DeleteRow从指定的表中通过key来删除特定的行
DeleteRow(tableName string, key []Column) error
// ReadCertAttribute用来从交易证书中读取指定的属性
// *attributeName* 是这个函数的入参.
// 例如:
// attrValue,error:=stub.ReadCertAttribute("position")
ReadCertAttribute(attributeName string) ([]byte, error)
// VerifyAttribute用于验证事务证书是否具有名称为* attribute Name *和value * attributeValue *的属性,
// attributeName和attributeValue是此函数接收的输入参数
// 例如:
// containsAttr, error := stub.VerifyAttribute("position", "Software Engineer")
VerifyAttribute(attributeName string, attributeValue []byte) (bool, error)
// VerifyAttributes与VerifyAttribute相同,但它检查属性列表及其相应的值,而不是单个属性/值对
// 例如:
// containsAttrs, error:= stub.VerifyAttributes(&attr.Attribute{"position", "Software Engineer"}, &attr.Attribute{"company", "ACompany"})
VerifyAttributes(attrs ...*attr.Attribute) (bool, error)
// VerifySignature核实交易的签名,如果正确返回true,否则返回false
VerifySignature(certificate, signature, message []byte) (bool, error)
// GetCallerCertificate 返回调用者证书
GetCallerCertificate() ([]byte, error)
// GetCallerMetadata 返回调用方元数据
GetCallerMetadata() ([]byte, error)
// GetBinding 返回交易捆绑
GetBinding() ([]byte, error)
// GetPayload 返回交易的payload, payload是一个定义在fabric/protos/chaincode.proto
// 中的ChaincodeSpecwhich
GetPayload() ([]byte, error)
// GetTxTimestamp返回交易创建的时间戳,这个时间戳是peer收到交易的当前时间。
// 请注意,此时间戳可能与其他对等端peer的时间不同
GetTxTimestamp() (*timestamp.Timestamp, error)
// SetEvent保存当交易组成一个块时要发送的事件
SetEvent(name string, payload []byte) error
}
3.StateRangeQueryIteratorInterface允许在一个链上代码在状态上迭代一定范围的键值
type StateRangeQueryIteratorInterface interface {
// HasNext如果查询迭代器范围内包含额外的键和值就返回true
HasNext() bool
// Next在迭代器范围内返回下一个键和值
Next() (string, []byte, error)
// Close 关闭范围查询迭代器,当从迭代器中读完被释放资源的时候被调用
Close() error
}
4.chaincode_example02解析
package main
import (
"errors"
"fmt"
"strconv"
"github.com/hyperledger/fabric/core/chaincode/shim"
)
// SimpleChaincode 样例链上代码实现
type SimpleChaincode struct {
}
func (t *SimpleChaincode) Init(stub shim.ChaincodeStubInterface, function string, args []string) ([]byte, error) {
var A, B string // 字符实体
var Aval, Bval int // 资产持股
var err error
if len(args) != 4 {
return nil, errors.New("Incorrect number of arguments. Expecting 4")
}
// 初始化链上代码
A = args[0]
Aval, err = strconv.Atoi(args[1])
if err != nil {
return nil, errors.New("Expecting integer value for asset holding")
}
B = args[2]
Bval, err = strconv.Atoi(args[3])
if err != nil {
return nil, errors.New("Expecting integer value for asset holding")
}
fmt.Printf("Aval = %d, Bval = %d\n", Aval, Bval)
// 写状态到账本
err = stub.PutState(A, []byte(strconv.Itoa(Aval)))
if err != nil {
return nil, err
}
err = stub.PutState(B, []byte(strconv.Itoa(Bval)))
if err != nil {
return nil, err
}
return nil, nil
}
// 支持从A到B支付X股
func (t *SimpleChaincode) Invoke(stub shim.ChaincodeStubInterface, function string, args []string) ([]byte, error) {
if function == "delete" {
// Deletes an entity from its state
// 从他的状态中删除entity
return t.delete(stub, args)
}
var A, B string // 字符实体
var Aval, Bval int // 持股资产
var X int // 交易值
var err error
if len(args) != 3 {
return nil, errors.New("Incorrect number of arguments. Expecting 3")
}
A = args[0]
B = args[1]
// 从账本中获取状态
// TODO: will be nice to have a GetAllState call to ledger
Avalbytes, err := stub.GetState(A)
if err != nil {
return nil, errors.New("Failed to get state")
}
if Avalbytes == nil {
return nil, errors.New("Entity not found")
}
Aval, _ = strconv.Atoi(string(Avalbytes))
Bvalbytes, err := stub.GetState(B)
if err != nil {
return nil, errors.New("Failed to get state")
}
if Bvalbytes == nil {
return nil, errors.New("Entity not found")
}
Bval, _ = strconv.Atoi(string(Bvalbytes))
// 执行execution
X, err = strconv.Atoi(args[2])
if err != nil {
return nil, errors.New("Invalid transaction amount, expecting a integer value")
}
Aval = Aval - X
Bval = Bval + X
fmt.Printf("Aval = %d, Bval = %d\n", Aval, Bval)
// 写状态到账本
err = stub.PutState(A, []byte(strconv.Itoa(Aval)))
if err != nil {
return nil, err
}
err = stub.PutState(B, []byte(strconv.Itoa(Bval)))
if err != nil {
return nil, err
}
return nil, nil
}
// 从账本中删除实体
func (t *SimpleChaincode) delete(stub shim.ChaincodeStubInterface, args []string) ([]byte, error) {
if len(args) != 1 {
return nil, errors.New("Incorrect number of arguments. Expecting 1")
}
A := args[0]
// 从账本的状态中删除密钥
err := stub.DelState(A)
if err != nil {
return nil, errors.New("Failed to delete state")
}
return nil, nil
}
// Query callback representing the query of a chaincode
// Query 查询链上代码
func (t *SimpleChaincode) Query(stub shim.ChaincodeStubInterface, function string, args []string) ([]byte, error) {
if function != "query" {
return nil, errors.New("Invalid query function name. Expecting \"query\"")
}
var A string // 字符实体
var err error
if len(args) != 1 {
return nil, errors.New("Incorrect number of arguments. Expecting name of the person to query")
}
A = args[0]
// 从账本中获取状态
Avalbytes, err := stub.GetState(A)
if err != nil {
jsonResp := "{\"Error\":\"Failed to get state for " + A + "\"}"
return nil, errors.New(jsonResp)
}
if Avalbytes == nil {
jsonResp := "{\"Error\":\"Nil amount for " + A + "\"}"
return nil, errors.New(jsonResp)
}
jsonResp := "{\"Name\":\"" + A + "\",\"Amount\":\"" + string(Avalbytes) + "\"}"
fmt.Printf("Query Response:%s\n", jsonResp)
return Avalbytes, nil
}
func main() {
// ChainCode 调用 err := shim.Start(new(SimpleChaincode))
// 接入到ChainCodeSupportServer
err := shim.Start(new(SimpleChaincode))
if err != nil {
fmt.Printf("Error starting Simple chaincode: %s", err)
}
}