All plugins in the libraries/plugins
directory are iterated over by CMakeLists.txt
. The manifest directory iterates through all plugins, adding them to the steem_plugins
build target. Any other build target wanting to access all plugins
available at build time should link to this target.
There is a plugin in example_plugins
called example_api_plugin
which is a working example of adding a custom API call.
- Plugins are enabled with the
plugins
config file option. - By default, steemd runs the
chain
,p2p
, andwebserver
plugins. - Some plugins may keep records in the database (such as
account_history
). If you change whether such a plugin is disabled/enabled, you should also replay the chain. Detecting this situation and automatically replaying when needed will be implemented in a future release. - To make an API visible, include the associated plugin in the
plugins
config file option. Only APIs explicitly made available through the config will be registered.
A skeleton structure containing much of the boilerplate of creating a new plugin with an API can be autogenerated by running programs/util/newplugin.py
.
- Register signal handlers and custom evaluators in
plugin_initialize()
(if needed) - Register APIs in
plugin_initialize()
- API plugins should be separate from the stateful plugin they grant access to. This allows for the API to be disabled while keeping track of state for the plugin.
- Plugins can specify dependencies with the
APPBASE_PLUGIN_REQUIRES
macro, which takes a bubble list of dependencies. - There is undefined behavior if there are circular dependencies. Be careful how you design the data flow between plugins to eliminate circular dependencies.
plugin_initialze
andplugin_startup
both guarantee that any dependencies are already initialized or started before the dependent plugin is called.plugin_shutdown
is called in the reverse order from startup. Any dependencies will still be running when shutdown is called.- You can get a reference to a plugin with
appbase::app().get_plugin< PLUIGN >()
or a pointer withappbase::app().find_plugin< PLUGIN >()
. get_plugin will fail and find_plugin will return a nullptr if the plugin is not registered or has not been initialized. These methods guarantee you are accessing initialized date. - Because of the inialization order, it is safe to call get/find_plugin on dependencies in initialization, startup, and shutdown.
- It is only safe to call get/find_plugin on an optional dependency in startup. An optional dependency is not explicitly specified. It is simply some optional behavior if a plugin is enabled. See
condenser_api
for an example of optional dependencies.
-The json rpc plugin dispatches API requests asynchronously. This means your API call can be preempted or delayed and must be designed with parallelism in mind.
- To help, chainbase provides a simple read/write locking mechanism:
with_read_lock
andwith_write_lock
. - This is a global lock over the entire database.
- The methods take a lambda that will be protected with the lock. For most API calls,
with_read_lock
will be sufficient. - To ensure consistent block times, the read lock automatically expires after 1 second. Design your API call with this in mind. If the API call takes longer than 1 second, your results are undefined. It is best to create limitations on the API call to ensure it does not take longer than 1 second to execute (shorter than 250ms is preferable).