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