Fabric原先一直使用chaincode level的背书策略,即在合约实例化或者升级的时候指定背书策略。
现在新增一种针对key设置背书策略的方法,key level背书策略生效后会覆盖chaincode level的背书策略。
适用场景
对于合约中某些记录的变更,原有的背书策略不能完全满足其要求,需要特殊的背书策略进行约束。比如车辆交易中,一般车辆的交易只需要交易双方的背书即可,但是如果某辆汽车具有一定的历史意义,则可能还需要鉴定人员的背书【例子源自官网】。
技术实现
合约提供的接口针对key设置背书策略,key level背书策略生效后会覆盖chaincode level的背书策略。如果key level背书策略移除,则会重新使用chaincode level背书策略。
具体代码实现上,验证区块的时候使用如下结构体,对区块中交易的读写集进行背书策略检查:
// StateBasedValidator is used to validate a transaction that performs changes to
// KVS keys that use key-level endorsement policies. This interface is supposed to be called
// by any validator plugin (including the default validator plugin). The functions of this
// interface are to be called as follows:
// 1) the validator plugin calls PreValidate (even before determining whether the transaction is
// valid)
// 2) the validator plugin calls Validate before or after having determined the validity of the
// transaction based on other considerations
// 3) the validator plugin determines the overall validity of the transaction and then calls
// PostValidate
type StateBasedValidator interface {
// PreValidate sets the internal data structures of the validator needed before validation
// of transaction `txNum` in the specified block can proceed
PreValidate(txNum uint64, block *common.Block)
// Validate determines whether the transaction on the specified channel at the specified height
// is valid according to its chaincode-level endorsement policy and any key-level validation
// parametres
Validate(cc string, blockNum, txNum uint64, rwset, prp, ep []byte, endorsements []*peer.Endorsement) commonerrors.TxValidationError
// PostValidate sets the internal data structures of the validator needed after the validation
// code was determined for a transaction on the specified channel at the specified height
PostValidate(cc string, blockNum, txNum uint64, err error)
}
其中Validate函数使用checkSBAndCCEP函数检查key的背书策略,首先会获取key level的背书策略进行检查,如果没有设置则使用chaincode level的背书策略。
接口
合约可以使用如下函数对key设置背书策略:
SetStateValidationParameter(key string, ep []byte) error
GetStateValidationParameter(key string) ([]byte, error)
可以使用如下函数对私密数据设置背书策略:
SetPrivateDataValidationParameter(collection, key string, ep []byte) error
GetPrivateDataValidationParameter(collection, key string) ([]byte, error)
背书策略的配置可以使用如下接口:
type KeyEndorsementPolicy interface {
// Policy returns the endorsement policy as bytes
Policy() ([]byte, error)
// AddOrgs adds the specified orgs to the list of orgs that are required
// to endorse
AddOrgs(roleType RoleType, organizations ...string) error
// DelOrgs delete the specified channel orgs from the existing key-level endorsement
// policy for this KVS key. If any org is not present, an error will be returned.
DelOrgs(organizations ...string) error
// ListOrgs returns an array of channel orgs that are required to endorse changes
ListOrgs() ([]string)
}
实际操作
【此处使用fabric-sample中提供的interest_rate_swaps示例进行说明,代码稍加改动】
合约通过如下代码对audit_limit设置背书策略,只允许指定身份的peer对其进行背书。即如需再对audit_limit进行修改,必须由该peer进行背书;查询audit_limit的值,任何安装合约的peer都可以。
err := stub.PutState("audit_limit", args[2])
if err != nil {
return shim.Error(err.Error())
}
// 新建背书策略
auditorEP, err := statebased.NewStateEP(nil)
if err != nil {
return shim.Error(err.Error())
}
// 添加需要背书的组织成员,身份可以为RoleTypePeer或RoleTypeMember;
// 如果添加了多个组织成员,则该背书策略需要所有这些成员的背书,即是AND关系
err = auditorEP.AddOrgs(statebased.RoleTypePeer, string(args[1]))
if err != nil {
return shim.Error(err.Error())
}
// 生成背书策略
epBytes, err := auditorEP.Policy()
if err != nil {
return shim.Error(err.Error())
}
// 为指定key添加key-level背书策略
err = stub.SetStateValidationParameter("audit_limit", epBytes)
if err != nil {
return shim.Error(err.Error())
}
client端发送交易时,需要指定对应的peer进行背书,用--peerAddress参数指定,如下所示:
peer chaincode invoke -o irs-orderer:7050 -C irs --waitForEvent -n irscc --peerAddresses irs-rrprovider:7051 -c '{"Args":["setReferenceRate","myrr","300"]}'
如果需要多个peer进行背书,可以用--peerAddress参数指定多个peer,如下所示:
peer chaincode invoke -o irs-orderer:7050 -C irs --waitForEvent -n irscc --peerAddresses irs-partya:7051 --peerAddresses irs-partyb:7051 --peerAddresses irs-auditor:7051 -c '{"Args":["createSwap","myswap","{\"StartDate\":\"2018-09-27T15:04:05Z\",\"EndDate\":\"2018-09-30T15:04:05Z\",\"PaymentInterval\":395,\"PrincipalAmount\":10,\"FixedRate\":400,\"FloatingRate\":500,\"ReferenceRate\":\"myrr\"}", "partya", "partyb"]}'
如果指定的peer不满足key level背书策略时会出现ENDORSEMENT_POLICY_FAILURE错误,交易仍会被写入区块,但是交易无效。如下所示:
// 查看区块高度为8
root@177bc827f9f5:/opt/gopath/src/github.com/hyperledger/fabric/peer# peer channel getinfo -c irs
2019-06-04 03:24:17.241 UTC [channelCmd] InitCmdFactory -> INFO 001 Endorser and orderer connections initialized
Blockchain info: {"height":8,"currentBlockHash":"EcyCJc20MKN1oRposS/epixmIz9FNkVHPC5OlylLC5U=","previousBlockHash":"6+1+vatI334Y3KkaLSEMZBEXcNxEVtSJ5duj0VWtcfw="}
// 使用不正确的peer背书,交易状态为ENDORSEMENT_POLICY_FAILURE,显示invoke成功
root@177bc827f9f5:/opt/gopath/src/github.com/hyperledger/fabric/peer# peer chaincode invoke -o irs-orderer:7050 -C irs --waitForEvent -n irscc --peerAddresses irs-partya:7051 -c '{"Args":["setReferenceRate","myrr","200"]}'
2019-06-04 03:24:29.277 UTC [chaincodeCmd] ClientWait -> INFO 001 txid [1424925664ebad2d2ce39e31e02c83b74d98d080a01acdac07f35284360f73ea] committed with status (ENDORSEMENT_POLICY_FAILURE) at irs-partya:7051
2019-06-04 03:24:29.279 UTC [chaincodeCmd] chaincodeInvokeOrQuery -> INFO 002 Chaincode invoke successful. result: status:200
// 查询区块高度为9,说明交易写入区块
root@177bc827f9f5:/opt/gopath/src/github.com/hyperledger/fabric/peer# peer channel getinfo -c irs
2019-06-04 03:24:31.934 UTC [channelCmd] InitCmdFactory -> INFO 001 Endorser and orderer connections initialized
Blockchain info: {"height":9,"currentBlockHash":"+bzi7o1Gk6h1dkYVI690ZKlNzTsDI3k2WCT/DQFpt+4=","previousBlockHash":"EcyCJc20MKN1oRposS/epixmIz9FNkVHPC5OlylLC5U="}
// 查看key的值,并没有被更改
root@177bc827f9f5:/opt/gopath/src/github.com/hyperledger/fabric/peer# peer chaincode query -C irs -n irscc --peerAddresses irs-partya:7051 -c '{"Args":["getReferenceRate","myrr"]}'
300
第一次设置key level背书策略时,必须满足chaincode level的背书策略;以后对key level背书策略进行修改时,必须满足先前的key level背书策略。即key level背书策略生效后会覆盖chaincode level的背书策略。
将key level背书策略设置为nil时,chaincode level背书策略重新生效,代码如下:
err := stub.SetStateValidationParameter(key, nil)
if err != nil {
return shim.Error(err.Error())
}
Fabric 1.4的功能改进
私密数据的功能改进主要包括以下两点:
- 对账:新加入collection的节点可以获取该collection先前交易的私密数据。
- 客户端权限控制:增加对client身份的验证:增加memberOnlyRead参数,设置为true时可以限制只有属于collection的组织的client才可以读取数据。