ethereum-optimism / optimism

Optimism is Ethereum, scaled.
https://optimism.io
MIT License
5.53k stars 3.17k forks source link

op-node: event-system customizability and plugins (ideas) #11152

Open protolambda opened 1 month ago

protolambda commented 1 month ago

The op-node core consists of an event-broadcast system:

Through these events, the op-node sub-systems communicate tasks like block-processing, block-derivation from the DA, sequencing, etc.

The deriver/emitter is registered through event.System, which can execute these components either synchronously (current default), or in parallel (work in progress).

The components are "registered" with the system by name. This makes it possible to attribute metrics to each sub-system, and unregister as necessary.

Plugins

This may be the beginning of a plugin-system; performing and triggering additional tasks by monitoring and emitting events is trivial.

The question is just how we load additional event derivers/emitters.

One option may be with Go plugins, to allow runtime customization. A Go plugin can be a DLL type file, auto-discovered in a directory by the op-node, and declare some global function as access-point to register new event-derivers/emitters from. Go plugins could be developed outside of the monorepo, import the monorepo as library, and utilize all the event/plugin types as necessary.

The drawback of Go plugins are the challenges with distribution; plugins are not packaged up with docker-images, or automatically platform-compatible. Still, runtime customization does open up a lot of extensibility, while keeping the core stack unified in the main repository. This may outweigh the distribution challenges. Besides, if the op-node docker image has a standard mounting point for a plugin-directory, and if a plugin is distributed as a bundle with each platform target, then it's relatively simple to work with.

Based on demand we may start a design doc in the design-docs repository to further expand on the idea.

Event deriver/emitter composition

Not all plugins are additive however, some may want to replace certain subsets of functionality. Unregistering/registering complete sub-systems might be too much, if only small things have to change.

To do so, we may want to change the event-deriver interface.

From:

func (m *myDeriver) OnEvent(ev event.Event) bool {
   switch ev.(type) {
    case FooEvent: ...
    case BarEvent: ...
  }
}

To something more like:

func (m *myDeriver) Setup(sys event.System) bool {
  event.Register[FooEvent](sys, "foo-handling", func(ev FooEvent) {
    ...  // replaces existing "foo-handling"
  })
  event.Register[BarEvent](sys, "bar-handling-my-extension", func(ev BarEvent) {
    ... // net-new event handler, to extend regular "bar-handling"
  })
}

This would enable a finer level of detail, while also being more explicit about exactly which events a deriver needs to listen to. This way we can drop the "bool" return parameter, and possibly reduce event-processing overhead (no broadcast to derivers that just ignore the event).

Permissions / metadata

If we are going with plugins, we may want the plugin to pre-declare which event types it will process and emit. And then enforce the input/output limitations to the plugin.

This way we can tell if a plugin is just listening to forkchoice-updates, or actually emitting engine-instructions.

Different event types could be declared, and categorized by how they affect the op-node, such that we can have standard-compatible extensions, as well as consensus-breaking extensions (for custom OP-Stack chains), or e.g. plasma specific extensions. This is similar to how apps in app-stores request system permission, and the user can trust that the app does not break their expectations. Or in OP-Stack context, that the plugin is compatible with a certain blockspace charter.

ImTei commented 1 month ago

That’s a good idea. It seems like it will make a lot of things possible.

One thing that just came to mind is, adding additional features as plugins is great, but how about also making the components in the monorepo into plugins? such as op-batcher or op-conductor.

We’re currently facing complexity in configuration and infrastructure because we need to connect multiple components over the network. If we switch to event-based communication, wouldn’t it simplify the deployment and operation of applications that require many features like a sequencer?

roninjin10 commented 1 month ago

I definitely could see myself having a use case for this in the future!