Pluggable transaction endorsement and verification
Motivation
When a transaction is validated in the commit, the peer node performs various checks before the state of the transaction itself changes:
- Verifying the identity of a signed transaction
- Verifying the signature of the endorser in the transaction
- An endorsement strategy to ensure that transactions meet the corresponding chain code namespace
Some use cases require custom transaction validation rules that differ from the fabric validation rules, such as:
- state-based Endorsemet (state-based endorsement): When an endorsement strategy depends on the key, it does not depend on the namespace alone.
- UTXO (unspent Transaction output): When the validation is taken into account, regardless of whether the transaction does not enter a double flower.
- Anonymous transactions (anonymous transaction): When the endorsement does not contain the identity of the peer node, the signature and public key that cannot be linked to the peer node identity are shared.
Pluggable endorsement and verification logic
The fabric runtime implements and deploys customized endorsement and validation logic in the peer node, and is associated with the link-code processing in a pluggable manner. This logic can not only be compiled into the peer node, built into the optional logic, but also can be compiled and deployed as a Golang plug-in with the peer node.
Recall that each chain code is associated with its own endorsement and validation logic when the chain code is instantiated. If the user does not select one, the default built-in logic is implicitly selected. Peer node administrators change the endorsement/validation logic chosen by extending the local configuration of the peer node by loading and applying custom endorsement/validation logic when the peer node is started.
Configuration
Each peer node has a local configuration file (Core.yaml) that declares the mapping between the endorsement/validation logical name and the implementation to run.
The default endorsement logic is called ESCC, and the default validation logic is called VSCC, and their definitions can be found in the "Handlers" section of the local configuration file:
handlers: endorsers: escc: name: DefaultEndorsement validators: vscc: name: DefaultValidation
When an endorsement or validation implementation is compiled into a peer node, the "name" attribute identifies the initialization function to run in order to obtain a project to create an endorsement/validation logic instance.
This function is an instance method of the Handlerlibrary construct under the "Core/handlers/library/library.go" path, and in order to add custom endorsement/validation logic, you need to extend this construct with any additional methods.
Because this is complex and difficult to deploy, you can deploy a custom endorsement/validation module in the form of a Golang plug-in by adding another property under the name "library" attribute.
For example, if we implement a custom endorsement and validation logic module in the form of a plugin as a status-based endorsement, we can define it in the Core.yaml configuration file as follows:
handlers: endorsers: escc: name: DefaultEndorsement statebased: name: state_based library: /etc/hyperledger/fabric/plugins/state_based_endorsement.so validators: vscc: name: DefaultValidation statebased: name: state_based library: /etc/hyperledger/fabric/plugins/state_based_validation.so
We must place the. So plug-in file in the Peer node local file system.
Thereafter, the custom endorsement or validation logic implementations will be called "plug-ins", even if they are compiled into the peer node.
Endorsement Plugin Implementation
In order to implement the endorsement plugin, the corresponding plug-in interface must be implemented in the "Core/handlers/endorsement/api/endorsement.go" file:
// 背书插件提案回复type Plugin interface { //为给定的有效载荷(ProposalResponsePayload字节)背书签名,并且可以选择改变它 // 返回: // 背书:有效载荷上签名,以及用于验证签名的标识。 // 作为输入提供的有效载荷(可在此功能中修改) // 或者失败时出错 Endorse(payload []byte, sp *peer.SignedProposal) (*peer.Endorsement, []byte, error) // 初始化将依赖注入插件的实例 Init(dependencies ...Dependency) error}
This method is expected to be implemented by the plug-in developer by having the peer node invoke the new method in the PlugInFactory interface to create an endorsement plug-in instance of the given plug-in type (by means of the method name identified as an instance method of Handlerlibrary or. So plug-in file path) for each channel:
// PluginFactory 创建一个新的插件实例type PluginFactory interface { New() Plugin}
The initialization method is expected to be declared under the "core/handlers/endorsement/api/" path, identifying all the dependencies that are embedded in the dependency interface as input.
After the plug-in instance is created, the peer node invokes the initialization method (Init) as a pass-through parameter.
Currently, Fabric provides a dependency for an endorsement plugin:
- Signingidentityfetcher: Returns a signingidentity instance of a local domain-given signature proposal:
// SigningIdentity对消息进行签名并将其公共标识序列化为字节数据type SigningIdentity interface { // Serialize 返回此标识的字节表示形式,用于验证此SigningIdentity签名的消息 Serialize() ([]byte, error) // Sign 为给定有效载荷签名并返回签名 Sign([]byte) ([]byte, error)}
- Statefetcher: Gets the Status object (state) that interacts with the state database.
// State 定义与状态数据的交互方式type State interface { // GetPrivateDataMultipleKeys 在一次调用中获取多个私有数据项的值 GetPrivateDataMultipleKeys(namespace, collection string, keys []string) ([][]byte, error) // GetStateMultipleKeys 在一次调用中获取多个键的值 GetStateMultipleKeys(namespace string, keys []string) ([][]byte, error) // GetTransientByTXID 获取与给定txID关联的私有数据值 GetTransientByTXID(txID string) ([]*rwset.TxPvtReadWriteSet, error) // Done 释放状态数据占用的资源 Done() }
Verifying the implementation of plug-ins
In order to implement the validation plug-in, you must implement the plug-in interface under the path "Core/handlers/validation/api/validation.go":
// 验证交易插件type Plugin interface { // 如果在给定块中给定位置的交易内给定位置的动作是有效的Validate函数返回nil,否则返回一个error错误 Validate(block *common.Block, namespace string, txPosition int, actionPosition int, contextData ...ContextDatum) error // 初始化将依赖注入插件的实例 Init(dependencies ...Dependency) error}
Each contextdatum is an additional runtime-derived metadata that is passed by the peer node to the validation plug-in. Currently, the only contextdatum that is passed is the endorsement strategy that represents the chain code:
// SerializedPolicy 定义一个序列化策略type SerializedPolicy interface { validation.ContextDatum // Bytes 返回SerializedPolicy字节形式数据 Bytes() []byte }
This method is expected to be implemented by the plug-in developer by having the peer node invoke the new method in the PlugInFactory interface to create a validation plug-in instance of the given plug-in type (by method name identified as an instance method of Handlerlibrary or. So plug-in file path) for each channel:
// PluginFactory 创建一个新的插件实例type PluginFactory interface { New() Plugin}
The initialization method is expected to be declared under the "core/handlers/validation/api/" path, identifying all the dependencies that are embedded in the dependency interface as input.
After the plug-in instance is created, the peer node invokes the initialization method (Init) as a pass-through parameter.
Currently, Fabric provides a dependency for an endorsement plugin:
Identitydeserializer: Converts the identity of a byte data to an identity object that can be used to verify its signature, validates against its corresponding MSP, and checks whether they meet the given MSP Principal ( See MSP Service related source code). The complete specification is defined in "Core/handlers/validation/api/identities/identities.go".
Policyevaluator: Evaluate whether a given policy is met:
// PolicyEvaluator 评估策略type PolicyEvaluator interface { validation.Dependency // Evaluate 接收一组签名数据并评估这组签名是否满足给定的字节数据的策略 Evaluate(policyBytes []byte, signatureSet []*common.SignedData) error}
- Statefetcher: Gets the Status object (state) that interacts with the state database.
// State 定义与状态数据的交互方式type State interface { // GetStateMultipleKeys 在一次调用中获取多个键的值 GetStateMultipleKeys(namespace string, keys []string) ([][]byte, error) // GetStateRangeScanIterator 返回一个包含给定键范围的所有键值集合的迭代器。startKey被包含在结果中,并且排除了endKey。空的startKey引用第一个可用键,空的endKey引用最后一个可用键。为了扫描所有键,startKey和endKey都可以作为空字符串提供。但是,出于性能原因,应谨慎使用完整扫描。返回的ResultsIterator包含类型为*KV的结果,该结果定义在"protos/ledger/queryresult" GetStateRangeScanIterator(namespace string, startKey string, endKey string) (ResultsIterator, error) // Done 释放状态数据占用的资源 Done()}
Important points to note
- All nodes Verify the consistency of the plug-in: In a future release, the fabric channel infrastructure will ensure that at any given blockchain height, all peer nodes in the channel use the same validation logic for a given chain code. To eliminate the possibility of error configurations that could cause state differences due to the unexpected running of different implementations between peer nodes. However, the current system does not do the meta and the administrator has the responsibility to ensure that this does not happen.
- verifies plug-in error handling: When the validation plug-in cannot determine that a given transaction is validated because of some transient execution problems (for example, the database cannot be accessed), it should return to the "core/handlers/validation/api/ Validation.go "Executionfailureerror type of error defined in the. However, if a executionfailureerror error is returned, the chain is paused instead of marking the transaction as invalid. Just to prevent differences in state between different peer nodes.
- Import the fabric code into the plug-in: It is highly discouraged to import fabric code other than the protocol as part of the plug-in, which may cause problems when code changes occur between fabric distributions, or cause non-operability when running different versions of the peer node.