Hyperledger Fabric 应用实战(9)-- 智能合约编写

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包简单分析

在这里插入图片描述

扫描二维码关注公众号,回复: 14650542 查看本文章

从官方的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都是账本富查询的迭代器接口。

ChaincodeStubInterfaceChaincodeStub来实现,了解ChaincodeStub结构体的方法,我们基本上就掌握了fabric链码编写的方法了,其实直接看官方API文档也一样可以学习到。

2.2 API功能分类

链码最最最主要功能就是对账本进行操作,加上一些msp身份认证一些附加功能,目的也是更安全的对账本进行操作。

  • 辅助功能:例如参数获取、获取交易、网络信息等。
    • 参数获取:这类方法在链码2.X中已经不需要了,contractapi包中的NewChaincode函数已经帮我们把参数进行了填充,让我们可以和编写其他方法一样去编写链码方法。
    • 获取信息类型的函数:GetTxID()GetChannelID()GetCreator()GetSignedProposal()GetTxTimestamp()
  • 状态操作:对账本的k-v进行操作,
    • 读写:PutStateDelStateGetStateByRangeGetStateByRangeWithPagination、GetHistoryForKey
    • 复合键:SplitCompositeKeyCreateCompositeKeyGetStateByPartialCompositeKeyGetStateByPartialCompositeKeyWithPagination
    • 对此Key的背书策略设置:SetStateValidationParameterGetStateValidationParameter
  • 私有数据库操作:
    • 读写:GetPrivateDataGetPrivateDataHash(方便非私有成员对交易进行验证,仅可读取数据的Hash)、PutPrivateDataDelPrivateDataPurgePrivateDataGetPrivateDataByRange
    • key级别的背书策略设置:SetPrivateDataValidationParameterGetPrivateDataValidationParameter
    • 复合键:GetPrivateDataByPartialCompositeKey
  • 富查询:GetQueryResultGetPrivateDataQueryResult
  • 暂态数据:GetTransient(暂态数据主要为了保护数据,字面理解通过暂态数据传递进来的数据不会永久存储,会有专门的数据对他进行暂存,目前只是用来传递结构体数据。 )
  • 事件设置:SetEvent(主要绑定在专门的函数执行完后触发所设定的事件**,一个方法中最多只能设置一个Event**,不然第一个会被第二个给覆盖。)
  • GetBinding(没用过):返回事务绑定,该绑定用于强制应用程序数据(如上面瞬态字段中存储的数据)与提案本身之间的链接。这有助于避免可能的重放攻击。
  • GetDecorations(没用过):返回关于源自对等方的提案的附加数据(如果适用)。这些数据是由对等端的装饰器设置的,这些装饰器附加或变异传递给链代码的链代码输入。

猜你喜欢

转载自blog.csdn.net/weixin_43274469/article/details/129643800