Plugin Interface Refactor
This makes two main changes to the plugin system: * Instead of assuming that each plugin will have exactly one type, inspect each plugin to see which interfaces it provides, and register it as a provider of each provided interface. This can allow a single .so file to provide multiple interfaces, which will likely be necessary for aggregating certain types of info. * Rather than using dependency injection and having to propagate the plugin system all throughout Geth, have a default plugin loader so we need only import the module and make calls to it. If the plan were to integrate this into mainline Geth, I would say we use dependency injection and take the time to pass the plugin loader throughout the codebase, but as I expect this to be a fork that has to pull upstream changes, this approach should make merge conflicts much less common.
This commit is contained in:
@@ -24,7 +24,7 @@ import (
|
||||
type Backend interface {
|
||||
// General Ethereum API
|
||||
Downloader() *downloader.Downloader
|
||||
SuggestPrice(ctx context.Context) (*big.Int, error)
|
||||
SuggestGasTipCap(ctx context.Context) (*big.Int, error)
|
||||
ChainDb() ethdb.Database
|
||||
AccountManager() *accounts.Manager
|
||||
ExtRPCEnabled() bool
|
||||
@@ -46,7 +46,7 @@ type Backend interface {
|
||||
StateAndHeaderByNumberOrHash(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) (*state.StateDB, *types.Header, error)
|
||||
GetReceipts(ctx context.Context, hash common.Hash) (types.Receipts, error)
|
||||
GetTd(ctx context.Context, hash common.Hash) *big.Int
|
||||
GetEVM(ctx context.Context, msg core.Message, state *state.StateDB, header *types.Header) (*vm.EVM, func() error, error)
|
||||
GetEVM(ctx context.Context, msg core.Message, state *state.StateDB, header *types.Header, cfg *vm.Config) (*vm.EVM, func() error, error)
|
||||
SubscribeChainEvent(ch chan<- core.ChainEvent) event.Subscription
|
||||
SubscribeChainHeadEvent(ch chan<- core.ChainHeadEvent) event.Subscription
|
||||
SubscribeChainSideEvent(ch chan<- core.ChainSideEvent) event.Subscription
|
||||
|
||||
+46
-48
@@ -17,16 +17,6 @@ import (
|
||||
type APILoader func(*node.Node, Backend) []rpc.API
|
||||
type Subcommand func(*cli.Context, []string) error
|
||||
|
||||
type PluginType int
|
||||
|
||||
const (
|
||||
TracerPluginType PluginType = iota
|
||||
StateHookType
|
||||
ChainEventHookType
|
||||
RPCPluginType
|
||||
SubcommandType
|
||||
)
|
||||
|
||||
|
||||
type PluginLoader struct{
|
||||
TracerPlugins map[string]interface{} // TODO: Set interface
|
||||
@@ -37,6 +27,8 @@ type PluginLoader struct{
|
||||
Flags []*flag.FlagSet
|
||||
}
|
||||
|
||||
var defaultPluginLoader *PluginLoader
|
||||
|
||||
|
||||
func NewPluginLoader(target string) (*PluginLoader, error) {
|
||||
pl := &PluginLoader{
|
||||
@@ -52,7 +44,7 @@ func NewPluginLoader(target string) (*PluginLoader, error) {
|
||||
for _, file := range files {
|
||||
fpath := path.Join(target, file.Name())
|
||||
if !strings.HasSuffix(file.Name(), ".so") {
|
||||
log.Warn("File inplugin directory is not '.so' file. Skipping.", "file", fpath)
|
||||
log.Debug("File inplugin directory is not '.so' file. Skipping.", "file", fpath)
|
||||
continue
|
||||
}
|
||||
plug, err := plugin.Open(fpath)
|
||||
@@ -70,54 +62,39 @@ func NewPluginLoader(target string) (*PluginLoader, error) {
|
||||
pl.Flags = append(pl.Flags, flagset)
|
||||
}
|
||||
}
|
||||
t, err := plug.Lookup("Type")
|
||||
if err != nil {
|
||||
log.Warn("Could not load plugin Type", "file", fpath, "error", err.Error())
|
||||
continue
|
||||
}
|
||||
switch pt := t.(*PluginType); *pt {
|
||||
case RPCPluginType:
|
||||
fn, err := plug.Lookup("GetAPIs")
|
||||
if err != nil {
|
||||
log.Warn("Could not load GetAPIs from plugin", "file", fpath, "error", err.Error())
|
||||
continue
|
||||
}
|
||||
fn, err := plug.Lookup("GetAPIs")
|
||||
if err == nil {
|
||||
apiLoader, ok := fn.(func(*node.Node, Backend) []rpc.API)
|
||||
if !ok {
|
||||
log.Warn("Could not cast plugin.GetAPIs to APILoader", "file", fpath)
|
||||
continue
|
||||
} else {
|
||||
pl.RPCPlugins = append(pl.RPCPlugins, APILoader(apiLoader))
|
||||
}
|
||||
pl.RPCPlugins = append(pl.RPCPlugins, APILoader(apiLoader))
|
||||
case SubcommandType:
|
||||
n, err := plug.Lookup("Name")
|
||||
if err != nil {
|
||||
log.Warn("Could not load Name from subcommand plugin", "file", fpath, "error", err.Error())
|
||||
continue
|
||||
}
|
||||
name, ok := n.(*string)
|
||||
} else { log.Debug("Error retrieving GetAPIs for plugin", "file", fpath, "error", err.Error()) }
|
||||
|
||||
sb, err := plug.Lookup("Subcommands")
|
||||
if err == nil {
|
||||
subcommands, ok := sb.(map[string]func(*cli.Context, []string) error)
|
||||
if !ok {
|
||||
log.Warn("Could not cast plugin.Name to string", "file", fpath)
|
||||
continue
|
||||
log.Warn("Could not cast plugin.Subocmmands to `map[string]func(*cli.Context, []string) error`", "file", fpath)
|
||||
} else {
|
||||
for k, v := range subcommands {
|
||||
if _, ok := pl.Subcommands[k]; ok {
|
||||
log.Warn("Subcommand redeclared", "file", fpath, "subcommand", k)
|
||||
}
|
||||
pl.Subcommands[k] = v
|
||||
}
|
||||
}
|
||||
fn, err := plug.Lookup("Main")
|
||||
if err != nil {
|
||||
log.Warn("Could not load Main from plugin", "file", fpath, "error", err.Error())
|
||||
continue
|
||||
}
|
||||
subcommand, ok := fn.(func(*cli.Context, []string) error)
|
||||
if !ok {
|
||||
log.Warn("Could not cast plugin.Main to Subcommand", "file", fpath)
|
||||
continue
|
||||
}
|
||||
if _, ok := pl.Subcommands[*name]; ok {
|
||||
return pl, fmt.Errorf("Multiple subcommand plugins with the same name: %v", *name)
|
||||
}
|
||||
pl.Subcommands[*name] = subcommand
|
||||
}
|
||||
}
|
||||
return pl, nil
|
||||
}
|
||||
|
||||
func Initialize(target string) (err error) {
|
||||
defaultPluginLoader, err = NewPluginLoader(target)
|
||||
return err
|
||||
}
|
||||
|
||||
func (pl *PluginLoader) RunSubcommand(ctx *cli.Context) (bool, error) {
|
||||
args := ctx.Args()
|
||||
if len(args) == 0 { return false, fmt.Errorf("No subcommand arguments")}
|
||||
@@ -126,6 +103,11 @@ func (pl *PluginLoader) RunSubcommand(ctx *cli.Context) (bool, error) {
|
||||
return true, subcommand(ctx, args[1:])
|
||||
}
|
||||
|
||||
func RunSubcommand(ctx *cli.Context) (bool, error) {
|
||||
if defaultPluginLoader == nil { return false, fmt.Errorf("Plugin loader not initialized") }
|
||||
return defaultPluginLoader.RunSubcommand(ctx)
|
||||
}
|
||||
|
||||
func (pl *PluginLoader) ParseFlags(args []string) bool {
|
||||
for _, flagset := range pl.Flags {
|
||||
flagset.Parse(args)
|
||||
@@ -133,6 +115,14 @@ func (pl *PluginLoader) ParseFlags(args []string) bool {
|
||||
return len(pl.Flags) > 0
|
||||
}
|
||||
|
||||
func ParseFlags(args []string) bool {
|
||||
if defaultPluginLoader == nil {
|
||||
log.Warn("Attempting to parse flags, but default PluginLoader has not been initialized")
|
||||
return false
|
||||
}
|
||||
return defaultPluginLoader.ParseFlags(args)
|
||||
}
|
||||
|
||||
func (pl *PluginLoader) GetAPIs(stack *node.Node, backend Backend) []rpc.API {
|
||||
apis := []rpc.API{}
|
||||
for _, apiLoader := range pl.RPCPlugins {
|
||||
@@ -140,3 +130,11 @@ func (pl *PluginLoader) GetAPIs(stack *node.Node, backend Backend) []rpc.API {
|
||||
}
|
||||
return apis
|
||||
}
|
||||
|
||||
func GetAPIs(stack *node.Node, backend Backend) []rpc.API {
|
||||
if defaultPluginLoader == nil {
|
||||
log.Warn("Attempting GetAPIs ,but default PluginLoader has not been initialized")
|
||||
return []rpc.API{}
|
||||
}
|
||||
return defaultPluginLoader.GetAPIs(stack, backend)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user