Plugins allow for supporting new transactions without modifying/adding to the core parts of the system. Supporting any new transaction requires:
- Defining new transaction types
- Defining new message validation rules or updates to any message validation rules
- Logic to handle new transactions, validation, making changes to state, etc (for more detail look at the doc on request handling).
- Storage of transactions in existing ledger or introducing new ledgers
- Changes to existing state or introducing new states
- Defining new constants
- Defining any new config variables
- Each plugin is a separate python package that lives under the package defined in
PLUGIN_ROOT
inconfig.py
. It is currently set toplenum.server.plugin
- The list of enabled plugins is defined in
ENABLED_PLUGINS
inconfig.py
. - Each plugin has unique name, this is the name in the list
ENABLED_PLUGINS
and has to match the package name of the plugin. - The plugin package contains defines several items in the
__init__.py
file- It contains a ledger id for each new ledger that the plugin introduces, see
LEDGER_IDS
- For each new field that needs to be added in the
Request
, the plugin defines the name and its corresponding input validation function in a dict, seeCLIENT_REQUEST_FIELDS
- Each write or query transaction type is defined in
AcceptableWriteTypes
andAcceptableQueryTypes
respectively
- It contains a ledger id for each new ledger that the plugin introduces, see
- Each plugin package contains a
main.py
that contains a methodintegrate_plugin_in_node
to initialise and register any new ledgers, states, authenticators, request handlers, etc. - If the plugin requires a new authenticator, it should be defined in
client_authn.py
in the plugin package. - New storage layers should be defined in
storage.py
in the package - New transactions need to be in
transactions.py
in the plugin package. To avoid conflicts in transaction type values, each plugin defines a unique prefix to be added before the value of the transaction type, seePREFIX
. - Any new config variables that have to be introduced are introduced in
get_config
inconfig.py
in the plugin package.
When plenum initialises, initialises all enabled plugins; this is done through setup_plugins
method defined in plenum/__init__.py
Plenum defines 2 project level globals:
PLUGIN_LEDGER_IDS
which would be the set of ids of all new ledgers defined by any enabled plugin and PLUGIN_CLIENT_REQUEST_FIELDS
would be a dictionary of all new Request
fields
defined by any plugin and its corresponding validator.
setup_plugins
iterates over each of the enabled plugin packages and updates PLUGIN_LEDGER_IDS
and PLUGIN_CLIENT_REQUEST_FIELDS
.
It then reloads some modules which defines input validation objects and schemas, this is done since set of valid ledger ids of request fields might have changed.
The Demo plugin serves as an example of how a plugin should be written, though it does some extra setup actions that an actual plugin will not do.
Since the configuration has been already loaded completely and the input validation objects have been initialised, the changes made by this plugin need to be reflected in them.
Therefore after updating the loaded config in tconf
in plenum/test/plugin/demo_plugin/conftest.py
, in the do_plugin_initialisation_for_tests
, all such modules are reloaded and do_plugin_initialisation_for_tests
is called after the plugin is setup (setup_plugins
)
The demo plugin is located at plenum/test/plugin/demo_plugin
. The demo plugin models a very trivial auction functionality.
- The plugin defines new transaction in
plenum/test/plugin/demo_plugin/transactions.py
- The plugin defines constants denoting transaction types in
plenum/test/plugin/demo_plugin/constants.py
- The demo plugin defines a new ledger for which it declares the ledger id
AUCTION_LEDGER_ID
inplenum/test/plugin/demo_plugin/__init__.py
. Also the plugin defines a new field in request calledfix_length_dummy
and corresponding input validator for that. - New ledger, hash store and state are defined in
plenum/test/plugin/demo_plugin/storage.py
- A request handler for the plugin introduced transactions are defined,
AuctionReqHandler
inplenum/test/plugin/demo_plugin/auction_req_handler.py
. The request handler defines methods for static validation, dynamic validation and state changes. - New config entries for ledger name, state name, storage type are defined in
plenum/test/plugin/demo_plugin/config.py
- An authenticator
AuctionAuthNr
is defined inplenum/test/plugin/demo_plugin/client_authnr.py
, that specifies the query and write types. - The integration of this plugin happens in
integrate_plugin_in_node
inplenum/test/plugin/demo_plugin/main.py
which takes the node as an argument. In this integration method:- Update the node's config with the plugin's config
- Initialise the plugin's ledger, state and other storage components
- Register the storage components in the node.
- Initialise the authenticator and register it with the node
- Initialise and register the request handler with the node.
- Now the above method can be called on any initialised node. For the tests, the
TestNode
s are updated in the fixturedo_post_node_creation
inplenum/test/plugin/demo_plugin/conftest.py