2021-06-28 17:11:38 +00:00
|
|
|
# PluGeth
|
|
|
|
|
|
|
|
PluGeth is a fork of the [Go Ethereum Client](https://github.com/ethereum/go-ethereum)
|
|
|
|
(Geth) that implements a plugin architecture, allowing developers to extend
|
|
|
|
Geth's capabilities in a number of different ways using plugins, rather than
|
|
|
|
having to create additional, new forks of Geth.
|
|
|
|
|
2021-06-28 18:02:05 +00:00
|
|
|
## WARNING: UNSTABLE API
|
|
|
|
|
|
|
|
Right now PluGeth is in early development. We are still settling on some of the
|
|
|
|
plugin APIs, and are not yet making official releases. From an operational
|
2021-06-30 13:49:23 +00:00
|
|
|
perspective, PluGeth should be as stable as upstream Geth less whatever
|
2021-06-28 18:02:05 +00:00
|
|
|
instability is added by plugins you might run. But if you plan to run PluGeth
|
|
|
|
today, be aware that future updates will likely break your plugins.
|
|
|
|
|
2021-06-28 17:53:50 +00:00
|
|
|
## System Requirements
|
|
|
|
|
|
|
|
System requirements will vary depending on which network you are connecting to.
|
|
|
|
On the Ethereum mainnet, you should have at least 8 GB RAM, 2 CPUs, and 350 GB
|
|
|
|
of SSD disks.
|
|
|
|
|
|
|
|
PluGeth relies on Golang's Plugin implementation, which is only supported on
|
|
|
|
Linux, FreeBSD, and macOS. Windows support is unlikely to be added in the
|
|
|
|
foreseeable future.
|
|
|
|
|
2021-06-28 17:11:38 +00:00
|
|
|
## Design Goals
|
|
|
|
|
|
|
|
The upstream Geth client exists primarily to serve as a client for the Ethereum
|
|
|
|
mainnet, though it also supports a number of popular testnets. Supporting the
|
|
|
|
Ethereum mainnet is a big enough challenge in its own right that the Geth team
|
|
|
|
generally avoids changes to support other networks, or to provide features only
|
|
|
|
a small handful of users would be interested in.
|
|
|
|
|
|
|
|
The result is that many projects have forked Geth. Some implement their own
|
|
|
|
consensus protocols or alter the behavior of the EVM to support other networks.
|
|
|
|
Others are designed to extract information from the Ethereum mainnet in ways
|
|
|
|
the standard Geth client does not support.
|
|
|
|
|
|
|
|
Creating numerous different forks to fill a variety of different needs comes
|
|
|
|
with a number of drawbacks. Forks tend to drift apart from each other. Many
|
|
|
|
networks that forked from Geth long ago have stopped merging updates from Geth;
|
|
|
|
this makes some sense, given that those networks have moved in different
|
|
|
|
directions than Geth and merging upstream changes while properly maintaining
|
|
|
|
consensus rules of an existing network could prove quite challenging. But not
|
|
|
|
merging changes from upstream can mean that security updates are easily missed,
|
|
|
|
especially when the upstream team [obscures security updates as optimizations](https://blog.openrelay.xyz/vulnerability-lifecycle-framework-geth/)
|
|
|
|
as a matter of process.
|
|
|
|
|
|
|
|
PluGeth aims to provide a single Geth fork that developers can choose to extend
|
|
|
|
rather than forking the Geth project. Out of the box, PluGeth behaves exactly
|
|
|
|
like upstream Geth, but by installing plugins written in Golang, developers can
|
|
|
|
extend its functionality in a wide variety of way.
|
|
|
|
|
|
|
|
## Anatomy of a Plugin
|
|
|
|
|
|
|
|
Plugins for Plugeth use Golang's [Native Plugin System](https://golang.org/pkg/plugin/).
|
|
|
|
Plugin modules must export variables using specific names and types. These will
|
|
|
|
be processed by the plugin loader, and invoked at certain points during Geth's
|
|
|
|
operations.
|
|
|
|
|
|
|
|
### API
|
|
|
|
|
|
|
|
#### Flags
|
|
|
|
|
|
|
|
* **Name**: Flags
|
|
|
|
* **Type**: [flag.FlagSet](https://golang.org/pkg/flag/#FlagSet)
|
|
|
|
* **Behavior**: This FlagSet will be parsed and your plugin will be able to
|
|
|
|
access the resulting flags. Note that if any flags are provided, certain
|
|
|
|
checks are disabled within Geth to avoid failing due to unexpected flags.
|
|
|
|
|
|
|
|
#### Subcommands
|
|
|
|
|
|
|
|
* **Name**: Subcommands
|
|
|
|
* **Type**: map[string]func(ctx [*cli.Context](https://pkg.go.dev/github.com/urfave/cli#Context), args []string) error
|
|
|
|
* **Behavior**: If Geth is invoked with `geth YOUR_COMMAND`, the plugin loader
|
|
|
|
will look for `YOUR_COMMAND` within this map, and invoke the corresponding
|
|
|
|
function. This can be useful for certain behaviors like manipulating Geth's
|
|
|
|
database without having to build a separate binary.
|
|
|
|
|
|
|
|
#### Tracers
|
|
|
|
|
|
|
|
* **Name**: Tracer
|
|
|
|
* **Type**: map[string]TracerResult
|
|
|
|
* **Behavior**: When calling debug.traceX functions (such as debug_traceCall
|
|
|
|
and debug_traceTransaction) the tracer can be specified as a key to this map
|
|
|
|
and the tracer used will be the TracerResult specified here. TracerResult
|
|
|
|
objects must match the interface:
|
2015-02-05 20:34:47 +00:00
|
|
|
|
2019-06-13 13:23:22 +00:00
|
|
|
```
|
2021-06-28 17:11:38 +00:00
|
|
|
// CaptureStart is called at the start of each transaction
|
2021-07-14 18:50:17 +00:00
|
|
|
CaptureStart(env *vm.EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) {}
|
2021-06-28 17:11:38 +00:00
|
|
|
// CaptureState is called for each opcode
|
2021-07-14 18:50:17 +00:00
|
|
|
CaptureState(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, rData []byte, depth int, err error) {}
|
2021-06-28 17:11:38 +00:00
|
|
|
// CaptureFault is called when an error occurs in the EVM
|
2021-07-14 18:50:17 +00:00
|
|
|
CaptureFault(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, depth int, err error) {}
|
2021-06-28 17:11:38 +00:00
|
|
|
// CaptureEnd is called at the end of each transaction
|
2021-07-14 18:50:17 +00:00
|
|
|
CaptureEnd(output []byte, gasUsed uint64, t time.Duration, err error) {}
|
2021-06-28 17:11:38 +00:00
|
|
|
// GetResult should return a JSON serializable result object to respond to the trace call
|
2021-07-14 18:50:17 +00:00
|
|
|
GetResult() (interface{}, error) {}
|
2015-04-28 10:13:42 +00:00
|
|
|
|
2019-06-13 13:23:22 +00:00
|
|
|
```
|
2016-05-23 13:19:17 +00:00
|
|
|
|
2021-06-28 17:11:38 +00:00
|
|
|
* **Caution**: Modifying of the values passed into tracer functions can alter
|
|
|
|
the results of the EVM execution in unpredictable ways. Additionally, some
|
|
|
|
objects may be reused across calls, so data you wish to capture should be
|
|
|
|
copied rather than retained by reference.
|
2016-04-29 14:40:19 +00:00
|
|
|
|
2021-06-28 17:11:38 +00:00
|
|
|
#### LiveTracer
|
2014-01-11 14:27:08 +00:00
|
|
|
|
2021-06-28 17:11:38 +00:00
|
|
|
* **Name**: LiveTracer
|
|
|
|
* **Type**: vm.Tracer
|
|
|
|
* **Behavior**: This tracer is used for tracing transactions as they are
|
|
|
|
processed within blocks. Note that if a block does not validate, some
|
|
|
|
transactions may be processed that don't end up in blocks, so be sure to
|
|
|
|
check transactions against finalized blocks.
|
2014-02-15 10:49:29 +00:00
|
|
|
|
2021-06-28 17:53:50 +00:00
|
|
|
The interface for a vm.Tracer is similar to a TracerResult (above), but does
|
2021-06-28 17:11:38 +00:00
|
|
|
not require a `GetResult()` function.
|
2021-06-28 17:53:50 +00:00
|
|
|
|
2021-07-14 18:50:17 +00:00
|
|
|
#### GetAPIs
|
|
|
|
|
|
|
|
* **Name**: GetAPIs
|
|
|
|
* **Type**: func(*node.Node, interfaces.Backend) []rpc.API
|
|
|
|
* **Behavior**: This allows you to register new RPC methods to run within Geth.
|
|
|
|
* **Example**:
|
|
|
|
|
|
|
|
The GetAPIs function itself will generally be fairly brief, and will looks
|
|
|
|
something like this:
|
|
|
|
|
|
|
|
```
|
|
|
|
func GetAPIs(stack *node.Node, backend plugins.Backend) []rpc.API {
|
|
|
|
return []rpc.API{
|
|
|
|
{
|
|
|
|
Namespace: "mynamespace",
|
|
|
|
Version: "1.0",
|
|
|
|
Service: &MyService{backend},
|
|
|
|
Public: true,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
|
|
|
The bulk of the implementation will be in the `MyService` struct. MyService
|
|
|
|
should be a struct with public functions. These functions can have two
|
|
|
|
different types of signatures:
|
|
|
|
|
|
|
|
* RPC Calls: For straight RPC calls, a function should have a `context.Context`
|
|
|
|
object as the first argument, followed by an arbitrary number of JSON
|
|
|
|
marshallable arguments, and return either a single JSON marshal object, or a
|
|
|
|
JSON marshallable object and an error. The RPC framework will take care of
|
|
|
|
decoding inputs to this function and encoding outputs, and if the error is
|
|
|
|
non-nil it will serve an error response.
|
|
|
|
* Subscriptions: For subscriptions (supported on IPC and websockets), a
|
|
|
|
function should have a `context.Context` object as the first argument
|
|
|
|
followed by an arbitrary number of JSON marshallable arguments, and should
|
|
|
|
return an `*rpc.Subscription` object. The subscription object can be created
|
|
|
|
with `rpcSub := notifier.CreateSubscription()`, and JSON marshallable data
|
|
|
|
can be sent to the subscriber with `notifier.Notify(rpcSub.ID, b)`.
|
|
|
|
|
|
|
|
A very simple MyService might look like:
|
|
|
|
|
|
|
|
```
|
|
|
|
type MyService struct{}
|
|
|
|
|
|
|
|
func (h *MyService) HelloWorld(ctx context.Context) string {
|
|
|
|
return "Hello World"
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
|
|
|
And the user could then access this with
|
2021-06-28 17:53:50 +00:00
|
|
|
|
|
|
|
## Extending The Plugin API
|
|
|
|
|
2021-07-14 18:50:17 +00:00
|
|
|
.Lookup("GetAPIs", func(item interface{}) bool {
|
|
|
|
.Lookup("InitializeNode", func(item interface{}) bool {
|
|
|
|
.Lookup("PreProcessBlock", func(item interface{}) bool {
|
|
|
|
.Lookup("PreProcessTransaction", func(item interface{}) bool {
|
|
|
|
.Lookup("BlockProcessingError", func(item interface{}) bool {
|
|
|
|
.Lookup("PostProcessTransaction", func(item interface{}) bool {
|
|
|
|
.Lookup("PostProcessBlock", func(item interface{}) bool {
|
|
|
|
.Lookup("NewHead", func(item interface{}) bool {
|
|
|
|
.Lookup("NewSideBlock", func(item interface{}) bool {
|
|
|
|
.Lookup("Reorg", func(item interface{}) bool {
|
|
|
|
.Lookup("AppendAncient", func(item interface{}) bool {
|
|
|
|
.Lookup("StateUpdate", func(item interface{}) bool {
|
|
|
|
.Lookup("CreateConsensusEngine", func(item interface{}) bool {
|
|
|
|
.Lookup("UpdateBlockchainVMConfig", func(item interface{}) bool {
|
|
|
|
.Lookup("BlockUpdates", func(item interface{}) bool {
|
|
|
|
.Lookup("Subcommands")
|
|
|
|
.Lookup("Initialize", func(i interface{}) bool {
|
|
|
|
|
2021-06-28 17:53:50 +00:00
|
|
|
While we can imagine lots of ways plugins might like to extract or change
|
|
|
|
information in Geth, we're trying not to go too crazy with the plugin API based
|
|
|
|
purely on hypotheticals. The Plugin API in its current form reflects the needs
|
|
|
|
of projects currently building on PluGeth, and we're happy to extend it for
|
|
|
|
people who are building something. If you're trying to do something that isn't
|
|
|
|
supported by the current plugin system, we're happy to help. Reach out to us on
|
2021-06-28 17:55:48 +00:00
|
|
|
[Discord](https://discord.gg/Epf7b7Gr) and we'll help you figure out how to
|
|
|
|
make it work.
|