1.fabric链码版本区别
Fabric链码分了两个大版本,1.x和2.x版本,两者主要区别为:
1、导入包的不同
1.x导入的包为:
"[github.com/hyperledger/fabric/core/chaincode/shim](http://github.com/hyperledger/fabric/core/chaincode/shim)"
pb "[github.com/hyperledger/fabric/protos/peer](http://github.com/hyperledger/fabric/protos/peer)"
2.0导入的包为:
"[github.com/hyperledger/fabric-contract-api-go/contractapi](http://github.com/hyperledger/fabric-contract-api-go/contractapi)"
2、方法结构不同
Fabric 2.0链码不需要 Invoke 和Init 方法。
3、方法中调用形式参数类型、返回值不同
1.x方法为:
createCar1(stub shim.ChaincodeStubInterface, args []string) pb.Response { }
2.0方法为:
Create(ctx contractapi.TransactionContextInterface,key string,value string)error { }
freerent链码还没开始编写,该篇文章主要对链码编写的主要思路和部分API进行进行梳理。
2. contractapi包简单分析
从官方的fabirc-samples提供各种简单的Go链码可以看到,一般我们链码方法都是自定义的一个SmartContract
struct,里面包含了contractapi包中的Contract
结构体。Contract
struct实现了ContractInterface
接口,自定义的结构体通过水平组合Contract
可以快速帮我自定义结构体实现ContractInterface
接口。
type SmartContract struct {
contractapi.Contract
}
type Contract struct {
Name string
Info metadata.InfoMetadata
UnknownTransaction interface{}
BeforeTransaction interface{}
AfterTransaction interface{}
TransactionContextHandler SettableTransactionContextInterface
}
ContractInterface
定义了有效合约应具有的功能。链码中使用的合约必须实现此接口。因此应用链码会直接将contractapi.Contract
嵌套在链码结构体中,以实现ContractInterface
接口。
type ContractInterface interface {
// 取得当前描述智能合约的元数据
GetInfo() metadata.InfoMetadata
//在合约向链码转化的时候,获取合约中的未知的交易集合
GetUnknownTransaction() interface{}
//获取 before transaction 集合
GetBeforeTransaction() interface{}
//获取 after transaction 集合
GetAfterTransaction() interface{}
GetName() string
//GetTransactionContextHandler返回约定函数使用的SettableTransactionContextInterface。
// 合约向链码转化时,将调用此函数并存储返回的事务上下文。
// 当通过Init/Invoke调用链代码时,如果函数需要其参数列表中的上下文,
// 则会创建存储类型的事务上下文,并将其作为参数发送给命名合约函数(以及之前/之后和未知函数)。
// 如果采用事务上下文的函数采用接口作为上下文,则此函数返回的事务上下文必须满足该接口
GetTransactionContextHandler() SettableTransactionContextInterface
}
除正常链码之外,contractapi包也提供了很多正常链码的扩展功能:
IgnoreContractInterface
扩展了ContractInterface,并提供了其他功能,可用于标记哪些函数不应通过调用/查询链码来访问;EvaluationContractInterface
扩展了ContractInterface ,标示这些function应该被query(查询)而不是被invoke(调用)
从上面图可以看出来我们定义的应用结构体,通过入参NewChaincode(contracts ...ContractInterface) (*ContractChaincode, error)
函数,可以转换为ContractChaincode
结构体,NewChaincode
基本逻辑就是该函数解析每个传递的函数,并存储链码要使用的组成细节,合约的公共函数存储在链代码中,并且可以调用。同时系统合约被添加到链码中,它提供了获取链码元数据的功能。生成的元数据是一个JSON格式的MetadataContractChaincode,包含每个合约的名称以及它们接受/返回的公共函数和类型的详细信息。它还概述了合同和链码的版本详细信息。
同时ContractChaincode
实现了 Chaincode
接口,该接口是fabric每一个链码都必须要实现的接口,里面提供了 Invoke 和Init 方法,这就是fabric1.X链码的交互的接口,fabric2.X链码实现就是对fabric1.X进行封装,便于开发人员的链码编写。
2. shim包简单分析
2.1 shim包结构内容
在contractapi包中发现,contractapi包仅仅为了是为了方便交互对shim进行一个封装,调用接口还是需要获取shim包中的ChaincodeStub
//ctx contractapi.TransactionContextInterface
ctx.GetStub().PutState(id, assetJSON)
type ChaincodeStub struct {
TxID string
ChannelID string
chaincodeEvent *pb.ChaincodeEvent
args [][]byte
handler *Handler
signedProposal *pb.SignedProposal
proposal *pb.Proposal
validationParameterMetakey string
// Additional fields extracted from the signedProposal
creator []byte
transient map[string][]byte
binding []byte
decorations map[string][]byte
}
shim里面的handler.go是链码服务和Peer服务通信的功能代码,我们主要关注提供了哪些与账本交互的API接口,方便编写链码。从interfaces.go看出,链码的与账本交互API主要有ChaincodeStubInterface
来提供,剩下的三个Iterator
都是账本富查询的迭代器接口。
ChaincodeStubInterface
由ChaincodeStub
来实现,了解ChaincodeStub
结构体的方法,我们基本上就掌握了fabric链码编写的方法了,其实直接看官方API文档也一样可以学习到。
2.2 API功能分类
链码最最最主要功能就是对账本进行操作,加上一些msp身份认证一些附加功能,目的也是更安全的对账本进行操作。
- 辅助功能:例如参数获取、获取交易、网络信息等。
- 参数获取:这类方法在链码2.X中已经不需要了,contractapi包中的NewChaincode函数已经帮我们把参数进行了填充,让我们可以和编写其他方法一样去编写链码方法。
- 获取信息类型的函数:
GetTxID()
、GetChannelID()
、GetCreator()
、GetSignedProposal()
、GetTxTimestamp()
- 状态操作:对账本的k-v进行操作,
- 读写:
PutState
、DelState
、GetStateByRange
、GetStateByRangeWithPagination、GetHistoryForKey
- 复合键:
SplitCompositeKey
、CreateCompositeKey
、GetStateByPartialCompositeKey
、GetStateByPartialCompositeKeyWithPagination
- 对此Key的背书策略设置:
SetStateValidationParameter
、GetStateValidationParameter
- 读写:
- 私有数据库操作:
- 读写:
GetPrivateData
、GetPrivateDataHash
(方便非私有成员对交易进行验证,仅可读取数据的Hash)、PutPrivateData
、DelPrivateData
、PurgePrivateData
、GetPrivateDataByRange
- key级别的背书策略设置:
SetPrivateDataValidationParameter
、GetPrivateDataValidationParameter
- 复合键:
GetPrivateDataByPartialCompositeKey
- 读写:
- 富查询:
GetQueryResult
、GetPrivateDataQueryResult
- 暂态数据:
GetTransient
(暂态数据主要为了保护数据,字面理解通过暂态数据传递进来的数据不会永久存储,会有专门的数据对他进行暂存,目前只是用来传递结构体数据。 ) - 事件设置:
SetEvent
(主要绑定在专门的函数执行完后触发所设定的事件**,一个方法中最多只能设置一个Event**,不然第一个会被第二个给覆盖。) GetBinding
(没用过):返回事务绑定,该绑定用于强制应用程序数据(如上面瞬态字段中存储的数据)与提案本身之间的链接。这有助于避免可能的重放攻击。GetDecorations
(没用过):返回关于源自对等方的提案的附加数据(如果适用)。这些数据是由对等端的装饰器设置的,这些装饰器附加或变异传递给链代码的链代码输入。