Skip to content

Commit d5e9f9b

Browse files
committed
[FAB-8534] Document pluggable endorsement n' validation
This change sets documents the design of the pluggable endorsement and validation, along with configuration guidelines, and even adds guidelines to developers which want to develop their own endorsement or validation plugins. Change-Id: I3ada64b202a5c9899770c7de308cfbdef0bf6759 Signed-off-by: yacovm <yacovm@il.ibm.com> Signed-off-by: joe-alewine <Joe.Alewine@ibm.com>
1 parent aed26a5 commit d5e9f9b

File tree

2 files changed

+304
-0
lines changed

2 files changed

+304
-0
lines changed

docs/source/ops_guide.rst

+1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ Operations Guides
99
msp
1010
configtx
1111
endorsement-policies
12+
pluggable_endorsement_and_validation
1213
error-handling
1314
logging-control
1415
enable_tls
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,303 @@
1+
Pluggable transaction endorsement and validation
2+
================================================
3+
4+
Motivation
5+
----------
6+
7+
When a transaction is validated at time of commit, the peer performs various
8+
checks before applying the state changes that come with the transaction itself:
9+
10+
- Validating the identities that signed the transaction.
11+
- Verifying the signatures of the endorsers on the transaction.
12+
- Ensuring the transaction satisfies the endorsement policies of the namespaces
13+
of the corresponding chaincodes.
14+
15+
There are use cases which demand custom transaction validation rules different
16+
from the default Fabric validation rules, such as:
17+
18+
- **State-based endorsement:** When the endorsement policy depends on the key,
19+
and not only on the namespace.
20+
- **UTXO (Unspent Transaction Output):** When the validation takes into account
21+
whether the transaction doesn't double spend its inputs.
22+
- **Anonymous transactions:** When the endorsement doesn't contain the identity
23+
of the peer, but a signature and a public key are shared that can't be linked
24+
to the peer's identity.
25+
26+
Pluggable endorsement and validation logic
27+
------------------------------------------
28+
29+
Fabric allows for the implementation and deployment of custom endorsement and
30+
validation logic into the peer to be associated with chaincode handling in a
31+
pluggable manner. This logic can be either compiled into the peer as built in
32+
selectable logic, or compiled and deployed alongside the peer as a
33+
`Golang plugin <https://golang.org/pkg/plugin/>`_.
34+
35+
Recall that every chaincode is associated with its own endorsement and validation
36+
logic at the time of chaincode instantiation. If the user doesn't select one, the
37+
default built-in logic is selected implicitly. A peer administrator may alter the
38+
endorsement/validation logic that is selected by extending the peer's local
39+
configuration with the customization of the endorsement/validation logic which is
40+
loaded and applied at peer startup.
41+
42+
Configuration
43+
-------------
44+
45+
Each peer has a local configuration (``core.yaml``) that declares a mapping
46+
between the endorsement/validation logic name and the implementation that is to
47+
be run.
48+
49+
The default logic are called ``ESCC`` (with the "E" standing for endorsement) and
50+
``VSCC`` (validation), and they can be found in the peer local configuration in
51+
the ``handlers`` section:
52+
53+
.. code-block:: YAML
54+
55+
handlers:
56+
endorsers:
57+
escc:
58+
name: DefaultEndorsement
59+
validators:
60+
vscc:
61+
name: DefaultValidation
62+
63+
When the endorsement or validation implementation is compiled into the peer, the
64+
``name`` property represents the initialization function that is to be run in order
65+
to obtain the factory that creates instances of the endorsement/validation logic.
66+
67+
The function is an instance method of the ``HandlerLibrary`` construct under
68+
``core/handlers/library/library.go`` and in order for custom endorsement or
69+
validation logic to be added, this construct needs to be extended with any
70+
additional methods.
71+
72+
Since this is cumbersome and poses a deployment challenge, one can also deploy
73+
custom endorsement and validation as a Golang plugin by adding another property
74+
under the ``name`` called ``library``.
75+
76+
For example, if we have custom endorsement and validation logic that represents
77+
state-based endorsement which is implemented as a plugin, we would have the following
78+
entries in the configuration in ``core.yaml``:
79+
80+
.. code-block:: YAML
81+
82+
handlers:
83+
endorsers:
84+
escc:
85+
name: DefaultEndorsement
86+
statebased:
87+
name: state_based
88+
library: /etc/hyperledger/fabric/plugins/state_based_endorsement.so
89+
validators:
90+
vscc:
91+
name: DefaultValidation
92+
statebased:
93+
name: state_based
94+
library: /etc/hyperledger/fabric/plugins/state_based_validation.so
95+
96+
And we'd have to place the ``.so`` plugin files in the peer's local file system.
97+
98+
.. note:: Hereafter, custom endorsement or validation logic implementation is
99+
going to be referred to as "plugins", even if they are compiled into
100+
the peer.
101+
102+
Endorsement plugin implementation
103+
---------------------------------
104+
105+
To implement an endorsement plugin, one must implement the ``Plugin`` interface
106+
found in ``core/handlers/endorsement/api/endorsement.go``:
107+
108+
.. code-block:: Go
109+
110+
// Plugin endorses a proposal response
111+
type Plugin interface {
112+
// Endorse signs the given payload(ProposalResponsePayload bytes), and optionally mutates it.
113+
// Returns:
114+
// The Endorsement: A signature over the payload, and an identity that is used to verify the signature
115+
// The payload that was given as input (could be modified within this function)
116+
// Or error on failure
117+
Endorse(payload []byte, sp *peer.SignedProposal) (*peer.Endorsement, []byte, error)
118+
119+
// Init injects dependencies into the instance of the Plugin
120+
Init(dependencies ...Dependency) error
121+
}
122+
123+
An endorsement plugin instance of a given plugin type (identified either by the
124+
method name as an instance method of the ``HandlerLibrary`` or by the plugin ``.so``
125+
file path) is created for each channel by having the peer invoke the ``New``
126+
method in the ``PluginFactory`` interface which is also expected to be implemented
127+
by the plugin developer:
128+
129+
.. code-block:: Go
130+
131+
// PluginFactory creates a new instance of a Plugin
132+
type PluginFactory interface {
133+
New() Plugin
134+
}
135+
136+
137+
The ``Init`` method is expected to receive as input all the dependencies declared
138+
under ``core/handlers/endorsement/api/``, identified as embedding the ``Dependency``
139+
interface.
140+
141+
After the creation of the ``Plugin`` instance, the ``Init`` method is invoked on
142+
it by the peer with the ``dependencies`` passed as parameters.
143+
144+
Currently Fabric comes with the following dependencies for endorsement plugins:
145+
146+
- ``SigningIdentityFetcher``: Returns an instance of ``SigningIdentity`` based
147+
on a given signed proposal:
148+
149+
.. code-block:: Go
150+
151+
// SigningIdentity signs messages and serializes its public identity to bytes
152+
type SigningIdentity interface {
153+
// Serialize returns a byte representation of this identity which is used to verify
154+
// messages signed by this SigningIdentity
155+
Serialize() ([]byte, error)
156+
157+
// Sign signs the given payload and returns a signature
158+
Sign([]byte) ([]byte, error)
159+
}
160+
161+
- ``StateFetcher``: Fetches a **State** object which interacts with the world
162+
state:
163+
164+
.. code-block:: Go
165+
166+
// State defines interaction with the world state
167+
type State interface {
168+
// GetPrivateDataMultipleKeys gets the values for the multiple private data items in a single call
169+
GetPrivateDataMultipleKeys(namespace, collection string, keys []string) ([][]byte, error)
170+
171+
// GetStateMultipleKeys gets the values for multiple keys in a single call
172+
GetStateMultipleKeys(namespace string, keys []string) ([][]byte, error)
173+
174+
// GetTransientByTXID gets the values private data associated with the given txID
175+
GetTransientByTXID(txID string) ([]*rwset.TxPvtReadWriteSet, error)
176+
177+
// Done releases resources occupied by the State
178+
Done()
179+
}
180+
181+
Validation plugin implementation
182+
--------------------------------
183+
184+
To implement a validation plugin, one must implement the ``Plugin`` interface
185+
found in ``core/handlers/validation/api/validation.go``:
186+
187+
.. code-block:: Go
188+
189+
// Plugin validates transactions
190+
type Plugin interface {
191+
// Validate returns nil if the action at the given position inside the transaction
192+
// at the given position in the given block is valid, or an error if not.
193+
Validate(block *common.Block, namespace string, txPosition int, actionPosition int, contextData ...ContextDatum) error
194+
195+
// Init injects dependencies into the instance of the Plugin
196+
Init(dependencies ...Dependency) error
197+
}
198+
199+
Each ``ContextDatum`` is additional runtime-derived metadata that is passed by
200+
the peer to the validation plugin. Currently, the only ``ContextDatum`` that is
201+
passed is one that represents the endorsement policy of the chaincode:
202+
203+
.. code-block:: Go
204+
205+
// SerializedPolicy defines a serialized policy
206+
type SerializedPolicy interface {
207+
validation.ContextDatum
208+
209+
// Bytes returns the bytes of the SerializedPolicy
210+
Bytes() []byte
211+
}
212+
213+
A validation plugin instance of a given plugin type (identified either by the
214+
method name as an instance method of the ``HandlerLibrary`` or by the plugin ``.so``
215+
file path) is created for each channel by having the peer invoke the ``New``
216+
method in the ``PluginFactory`` interface which is also expected to be implemented
217+
by the plugin developer:
218+
219+
.. code-block:: Go
220+
221+
// PluginFactory creates a new instance of a Plugin
222+
type PluginFactory interface {
223+
New() Plugin
224+
}
225+
226+
The ``Init`` method is expected to receive as input all the dependencies declared
227+
under ``core/handlers/validation/api/``, identified as embedding the ``Dependency``
228+
interface.
229+
230+
After the creation of the ``Plugin`` instance, the **Init** method is invoked on
231+
it by the peer with the dependencies passed as parameters.
232+
233+
Currently Fabric comes with the following dependencies for validation plugins:
234+
235+
- ``IdentityDeserializer``: Converts byte representation of identities into
236+
``Identity`` objects that can be used to verify signatures signed by them, be
237+
validated themselves against their corresponding MSP, and see whether they
238+
satisfy a given **MSP Principal**. The full specification can be found in
239+
``core/handlers/validation/api/identities/identities.go``.
240+
241+
- ``PolicyEvaluator``: Evaluates whether a given policy is satisfied:
242+
243+
.. code-block:: Go
244+
245+
// PolicyEvaluator evaluates policies
246+
type PolicyEvaluator interface {
247+
validation.Dependency
248+
249+
// Evaluate takes a set of SignedData and evaluates whether this set of signatures satisfies
250+
// the policy with the given bytes
251+
Evaluate(policyBytes []byte, signatureSet []*common.SignedData) error
252+
}
253+
254+
- ``StateFetcher``: Fetches a ``State`` object which interacts with the world state:
255+
256+
.. code-block:: Go
257+
258+
// State defines interaction with the world state
259+
type State interface {
260+
// GetStateMultipleKeys gets the values for multiple keys in a single call
261+
GetStateMultipleKeys(namespace string, keys []string) ([][]byte, error)
262+
263+
// GetStateRangeScanIterator returns an iterator that contains all the key-values between given key ranges.
264+
// startKey is included in the results and endKey is excluded. An empty startKey refers to the first available key
265+
// and an empty endKey refers to the last available key. For scanning all the keys, both the startKey and the endKey
266+
// can be supplied as empty strings. However, a full scan should be used judiciously for performance reasons.
267+
// The returned ResultsIterator contains results of type *KV which is defined in protos/ledger/queryresult.
268+
GetStateRangeScanIterator(namespace string, startKey string, endKey string) (ResultsIterator, error)
269+
270+
// Done releases resources occupied by the State
271+
Done()
272+
}
273+
274+
Important notes
275+
---------------
276+
277+
- **Validation plugin consistency across peers:** In future releases, the Fabric
278+
channel infrastructure would guarantee that the same validation logic is used
279+
for a given chaincode by all peers in the channel at any given blockchain
280+
height in order to eliminate the chance of mis-configuration which would might
281+
lead to state divergence among peers that accidentally run different
282+
implementations. However, for now it is the sole responsibility of the system
283+
operators and administrators to ensure this doesn't happen.
284+
285+
- **Validation plugin error handling:** Whenever a validation plugin can't
286+
determine whether a given transaction is valid or not, because of some transient
287+
execution problem like inability to access the database, it should return an
288+
error of type **ExecutionFailureError** that is defined in ``core/handlers/validation/api/validation.go``.
289+
Any other error that is returned, is treated as an endorsement policy error
290+
and marks the transaction as invalidated by the validation logic. However,
291+
if an ``ExecutionFailureError`` is returned, the chain processing halts instead
292+
of marking the transaction as invalid. This is to prevent state divergence
293+
between different peers.
294+
295+
- **Importing Fabric code into the plugin**: Importing code that belongs to Fabric
296+
other than protobufs as part of the plugin is highly discouraged, and can lead
297+
to issues when the Fabric code changes between releases, or can cause inoperability
298+
issues when running mixed peer versions. Ideally, the plugin code should only
299+
use the dependencies given to it, and should import the bare minimum other
300+
than protobufs.
301+
302+
.. Licensed under Creative Commons Attribution 4.0 International License
303+
https://creativecommons.org/licenses/by/4.0/

0 commit comments

Comments
 (0)