Update today, add hook writing guide

This commit is contained in:
Austin Roberts 2021-10-15 15:27:20 -05:00
parent 322eb88b8c
commit 9c6b92c284
2 changed files with 112 additions and 9 deletions

View File

@ -4,4 +4,102 @@
Hook Writing Guide
==================
.. todo:: Austin: guide to writing plugin hooks
If you're trying to interact with Geth in a way not already supported by
PluGeth, we're happy to accept pull requests adding new hooks so long as they
comply with certain standards. We strongly encourage you to :ref:`contact us <contact>`
first. We may have suggestions on how to do what you're trying to do without
adding new hooks, or easier ways to implement hooks to get the information you
need.
.. warning::
Plugin hooks *must not* require plugins to import any packages from ``github.com/ethereum/go-ethereum``.
Doing so means that plugins must be recompiled for each version of Geth.
Many types have been re-implemented in ``github.com/openrelayxyz/plugeth-utils``.
If you need a type for your hook not already provided by plugeth-utils, you
may make a pull request to that project as well.
When extending the plugin API, a primary concern is leaving a minimal footprint
in the core Geth codebase to avoid future merge conflicts. To achieve this,
when we want to add a hook within some existing Geth code, we create a
plugin_hooks.go in the same package. For example, in the core/rawdb package we
have:
.. code-block:: Go
// This file is part of the package we are adding hooks to
package rawdb
// Import whatever is necessary
import (
"github.com/ethereum/go-ethereum/plugins"
"github.com/ethereum/go-ethereum/log"
)
// PluginAppendAncient is the public plugin hook function, available for testing
func PluginAppendAncient(pl *plugins.PluginLoader, number uint64, hash, header, body, receipts, td []byte) {
fnList := pl.Lookup("AppendAncient", func(item interface{}) bool {
_, ok := item.(func(number uint64, hash, header, body, receipts, td []byte))
return ok
})
for _, fni := range fnList {
if fn, ok := fni.(func(number uint64, hash, header, body, receipts, td []byte)); ok {
fn(number, hash, header, body, receipts, td)
}
}
}
// pluginAppendAncient is the private plugin hook function
func pluginAppendAncient(number uint64, hash, header, body, receipts, td []byte) {
if plugins.DefaultPluginLoader == nil {
log.Warn("Attempting AppendAncient, but default PluginLoader has not been initialized")
return
}
PluginAppendAncient(plugins.DefaultPluginLoader, number, hash, header, body, receipts, td)
}
The Public Plugin Hook Function
*******************************
The public plugin hook function should follow the naming convention
Plugin$HookName. The first argument should be a ``*plugins.PluginLoader``, followed
by any arguments required by the functions to be provided by nay plugins
implementing this hook.
The plugin hook function should use ``PluginLoader.Lookup("$HookName", func(item interface{}) bool``
to get a list of the plugin-provided functions to be invoked. The provided
function should verify that the provided function implements the expected
interface. After the first time a given hook is looked up through the plugin
loader, the PluginLoader will cache references to those hooks.
Given the function list provided by the plugin loader, the public plugin hook
function should iterate over the list, cast the elements to the appropriate
type, and call the function with the provided arguments.
Unless there is a clear justification to the contrary, the function should be
called in the current goroutine. Plugins may choose to spawn off a separate
goroutine as appropriate, but for the sake of thread safety we should generally
not assume that plugins will be implemented in a threadsafe manner. If a plugin
degrades the performance of Geth significantly, that will generally be obvious,
and plugin authors can take appropriate measures to improve performance. If a
plugin introduces thread safety issues, those can go unnoticed during testing.
The Private Plugin Hook Function
********************************
The private plugin hook function should bear the same name as the public plugin
hook function, but with a lower case first letter. The signature should match
the public plugin hook function, except that the first argument referencing the
PluginLoader should be removed. It should invoke the public plugin hook
function on ``plugins.DefaultPluginLoader``. It should always verify that the
DefaultPluginLoader is non-nil, log warning and return if the
DefaultPluginLoader has not been initialized.
In-Line Invocation
******************
Within the Geth codebase, the private plugin hook function should be invoked
with the appropriate arguments in a single line, to minimize unexpected
conflicts merging the upstream geth codebase into plugeth.

View File

@ -4,7 +4,7 @@
Basic Types of Plugins
======================
While PluGeth has been designed to be versatile and customizable, when learning the project it can be helpful to think of plugins as being of four different archetypes.
While PluGeth has been designed to be versatile and customizable, when learning the project it can be helpful to think of plugins as being of four different archetypes.
.. contents:: :local:
@ -17,12 +17,12 @@ These plugins provide new json rpc methods to access several objects containing
Subcommand
------------
A subcommand redifines the total behavior of Geth and could stand on its own. In contrast with the other plugin types which, in general, are meant to capture and manipulate information, a subcommand is meant to change to overall behavior of Geth. It may do this in order to capture information but the primary fuctionality is a modulation of geth behaviour.
A subcommand redifines the total behavior of Geth and could stand on its own. In contrast with the other plugin types which, in general, are meant to capture and manipulate information, a subcommand is meant to change to overall behavior of Geth. It may do this in order to capture information but the primary fuctionality is a modulation of geth behaviour.
Tracers
-------
Tracers rely on historic data recompiled after execution to give insight into a transaction.
Tracers rely on historic data recompiled after execution to give insight into a transaction.
**placeholder for eventual discusion of LiveTracers**
@ -30,13 +30,18 @@ Tracers rely on historic data recompiled after execution to give insight into a
Subscriptions
-------------
Subscriptions provide real time notification of data from the EVM as it processes transactions.
Subscriptions provide real time notification of data from the EVM as it processes transactions.
.. NOTE:: Plugins are not limited to a singular functionality and can be customized to operate as hybrids of the above. See `blockupdates`_ as an example.
.. NOTE:: Plugins are not limited to a singular functionality and can be customized to operate as hybrids of the above. See `blockupdates`_ as an example.
.. todo:: Austin: I don't love this page. The informations is too
shallow.
.. todo:: Austin: I don't love this page. The informations is too
shallow.
Reply: I'd be inclined to add links out to the tutorial page for each
of the types. I think it's useful to have brief descriptions of each
type in one place, but putting each of the tutorials all on one page
is a bit much. It might also be a good idea to link to real plugins
that exemplify each type, though we won't have one of each type yet.
.. _blockupdates: https://github.com/openrelayxyz/plugeth-plugins/tree/master/packages/blockupdates