github.com/kaituanwang/hyperledger@v2.0.1+incompatible/docs/source/pluggable_endorsement_and_validation.rst (about)

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