mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-18 18:02:24 +00:00
cmd, core, eth: add support for Reth style ExEx plugins
This commit is contained in:
parent
18a591811f
commit
004c37032a
15 changed files with 543 additions and 2 deletions
|
|
@ -102,7 +102,7 @@ if one is set. Otherwise it prints the genesis from the datadir.`,
|
||||||
utils.VMTraceJsonConfigFlag,
|
utils.VMTraceJsonConfigFlag,
|
||||||
utils.TransactionHistoryFlag,
|
utils.TransactionHistoryFlag,
|
||||||
utils.StateHistoryFlag,
|
utils.StateHistoryFlag,
|
||||||
}, utils.DatabaseFlags),
|
}, utils.DatabaseFlags, utils.ExExPluginFlags()),
|
||||||
Description: `
|
Description: `
|
||||||
The import command imports blocks from an RLP-encoded form. The form can be one file
|
The import command imports blocks from an RLP-encoded form. The form can be one file
|
||||||
with several RLP-encoded blocks, or several files can be used.
|
with several RLP-encoded blocks, or several files can be used.
|
||||||
|
|
|
||||||
|
|
@ -156,7 +156,7 @@ var (
|
||||||
utils.BeaconGenesisRootFlag,
|
utils.BeaconGenesisRootFlag,
|
||||||
utils.BeaconGenesisTimeFlag,
|
utils.BeaconGenesisTimeFlag,
|
||||||
utils.BeaconCheckpointFlag,
|
utils.BeaconCheckpointFlag,
|
||||||
}, utils.NetworkFlags, utils.DatabaseFlags)
|
}, utils.NetworkFlags, utils.DatabaseFlags, utils.ExExPluginFlags())
|
||||||
|
|
||||||
rpcFlags = []cli.Flag{
|
rpcFlags = []cli.Flag{
|
||||||
utils.HTTPEnabledFlag,
|
utils.HTTPEnabledFlag,
|
||||||
|
|
|
||||||
|
|
@ -41,6 +41,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/common/fdlimit"
|
"github.com/ethereum/go-ethereum/common/fdlimit"
|
||||||
"github.com/ethereum/go-ethereum/core"
|
"github.com/ethereum/go-ethereum/core"
|
||||||
|
"github.com/ethereum/go-ethereum/core/exex/exex"
|
||||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||||
"github.com/ethereum/go-ethereum/core/txpool/blobpool"
|
"github.com/ethereum/go-ethereum/core/txpool/blobpool"
|
||||||
"github.com/ethereum/go-ethereum/core/txpool/legacypool"
|
"github.com/ethereum/go-ethereum/core/txpool/legacypool"
|
||||||
|
|
@ -71,6 +72,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/p2p/nat"
|
"github.com/ethereum/go-ethereum/p2p/nat"
|
||||||
"github.com/ethereum/go-ethereum/p2p/netutil"
|
"github.com/ethereum/go-ethereum/p2p/netutil"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
|
_ "github.com/ethereum/go-ethereum/plugins" // Load all ExEx plugins
|
||||||
"github.com/ethereum/go-ethereum/rpc"
|
"github.com/ethereum/go-ethereum/rpc"
|
||||||
"github.com/ethereum/go-ethereum/triedb"
|
"github.com/ethereum/go-ethereum/triedb"
|
||||||
"github.com/ethereum/go-ethereum/triedb/hashdb"
|
"github.com/ethereum/go-ethereum/triedb/hashdb"
|
||||||
|
|
@ -973,6 +975,19 @@ var (
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// ExExPluginFlags constructs a live set of CLI flags from the registered plugins.
|
||||||
|
func ExExPluginFlags() []cli.Flag {
|
||||||
|
var flagset []cli.Flag
|
||||||
|
for _, name := range exex.Plugins() {
|
||||||
|
flagset = append(flagset, &cli.BoolFlag{
|
||||||
|
Name: fmt.Sprintf("exex.%s", name),
|
||||||
|
Usage: fmt.Sprintf("Enables the %s execution extension plugin", name),
|
||||||
|
Category: flags.ExExCategory,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return flagset
|
||||||
|
}
|
||||||
|
|
||||||
// MakeDataDir retrieves the currently requested data directory, terminating
|
// MakeDataDir retrieves the currently requested data directory, terminating
|
||||||
// if none (or the empty string) is specified. If the node is starting a testnet,
|
// if none (or the empty string) is specified. If the node is starting a testnet,
|
||||||
// then a subdirectory of the specified datadir will be used.
|
// then a subdirectory of the specified datadir will be used.
|
||||||
|
|
@ -1908,6 +1923,16 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) {
|
||||||
cfg.VMTraceJsonConfig = config
|
cfg.VMTraceJsonConfig = config
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Execution extension plugins
|
||||||
|
for _, flag := range ExExPluginFlags() {
|
||||||
|
if ctx.IsSet(flag.(*cli.BoolFlag).Name) {
|
||||||
|
plugin := strings.TrimLeft(flag.(*cli.BoolFlag).Name, "exex.") // TODO(karalabe): Custom flag
|
||||||
|
if err := exex.Instantiate(plugin); err != nil {
|
||||||
|
Fatalf("Failed to instantiate ExEx plugin %s: %v", plugin, err)
|
||||||
|
}
|
||||||
|
log.Info("Instantiated ExEx plugin", "name", plugin)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetDNSDiscoveryDefaults configures DNS discovery with the given URL if
|
// SetDNSDiscoveryDefaults configures DNS discovery with the given URL if
|
||||||
|
|
@ -2197,6 +2222,15 @@ func MakeChain(ctx *cli.Context, stack *node.Node, readonly bool) (*core.BlockCh
|
||||||
vmcfg.Tracer = t
|
vmcfg.Tracer = t
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
for _, flag := range ExExPluginFlags() {
|
||||||
|
if ctx.IsSet(flag.(*cli.BoolFlag).Name) {
|
||||||
|
plugin := strings.TrimLeft(flag.(*cli.BoolFlag).Name, "exex.") // TODO(karalabe): Custom flag
|
||||||
|
if err := exex.Instantiate(plugin); err != nil {
|
||||||
|
Fatalf("Failed to instantiate ExEx plugin %s: %v", plugin, err)
|
||||||
|
}
|
||||||
|
log.Info("Instantiated ExEx plugin", "name", plugin)
|
||||||
|
}
|
||||||
|
}
|
||||||
// Disable transaction indexing/unindexing by default.
|
// Disable transaction indexing/unindexing by default.
|
||||||
chain, err := core.NewBlockChain(chainDb, cache, gspec, nil, engine, vmcfg, nil)
|
chain, err := core.NewBlockChain(chainDb, cache, gspec, nil, engine, vmcfg, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
||||||
|
|
@ -35,6 +35,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/common/prque"
|
"github.com/ethereum/go-ethereum/common/prque"
|
||||||
"github.com/ethereum/go-ethereum/consensus"
|
"github.com/ethereum/go-ethereum/consensus"
|
||||||
"github.com/ethereum/go-ethereum/consensus/misc/eip4844"
|
"github.com/ethereum/go-ethereum/consensus/misc/eip4844"
|
||||||
|
"github.com/ethereum/go-ethereum/core/exex/exex"
|
||||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||||
"github.com/ethereum/go-ethereum/core/state"
|
"github.com/ethereum/go-ethereum/core/state"
|
||||||
"github.com/ethereum/go-ethereum/core/state/snapshot"
|
"github.com/ethereum/go-ethereum/core/state/snapshot"
|
||||||
|
|
@ -467,6 +468,7 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, genesis *Genesis
|
||||||
if txLookupLimit != nil {
|
if txLookupLimit != nil {
|
||||||
bc.txIndexer = newTxIndexer(*txLookupLimit, bc)
|
bc.txIndexer = newTxIndexer(*txLookupLimit, bc)
|
||||||
}
|
}
|
||||||
|
exex.TriggerInitHook(bc)
|
||||||
return bc, nil
|
return bc, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -578,6 +580,7 @@ func (bc *BlockChain) SetHead(head uint64) error {
|
||||||
log.Error("Current block not found in database", "block", header.Number, "hash", header.Hash())
|
log.Error("Current block not found in database", "block", header.Number, "hash", header.Hash())
|
||||||
return fmt.Errorf("current block missing: #%d [%x..]", header.Number, header.Hash().Bytes()[:4])
|
return fmt.Errorf("current block missing: #%d [%x..]", header.Number, header.Hash().Bytes()[:4])
|
||||||
}
|
}
|
||||||
|
exex.TriggerHeadHook(header)
|
||||||
bc.chainHeadFeed.Send(ChainHeadEvent{Header: header})
|
bc.chainHeadFeed.Send(ChainHeadEvent{Header: header})
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
@ -599,6 +602,7 @@ func (bc *BlockChain) SetHeadWithTimestamp(timestamp uint64) error {
|
||||||
log.Error("Current block not found in database", "block", header.Number, "hash", header.Hash())
|
log.Error("Current block not found in database", "block", header.Number, "hash", header.Hash())
|
||||||
return fmt.Errorf("current block missing: #%d [%x..]", header.Number, header.Hash().Bytes()[:4])
|
return fmt.Errorf("current block missing: #%d [%x..]", header.Number, header.Hash().Bytes()[:4])
|
||||||
}
|
}
|
||||||
|
exex.TriggerHeadHook(header)
|
||||||
bc.chainHeadFeed.Send(ChainHeadEvent{Header: header})
|
bc.chainHeadFeed.Send(ChainHeadEvent{Header: header})
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
@ -1157,6 +1161,8 @@ func (bc *BlockChain) Stop() {
|
||||||
if bc.logger != nil && bc.logger.OnClose != nil {
|
if bc.logger != nil && bc.logger.OnClose != nil {
|
||||||
bc.logger.OnClose()
|
bc.logger.OnClose()
|
||||||
}
|
}
|
||||||
|
exex.TriggerCloseHook()
|
||||||
|
|
||||||
// Close the trie database, release all the held resources as the last step.
|
// Close the trie database, release all the held resources as the last step.
|
||||||
if err := bc.triedb.Close(); err != nil {
|
if err := bc.triedb.Close(); err != nil {
|
||||||
log.Error("Failed to close trie database", "err", err)
|
log.Error("Failed to close trie database", "err", err)
|
||||||
|
|
@ -1560,6 +1566,7 @@ func (bc *BlockChain) writeBlockAndSetHead(block *types.Block, receipts []*types
|
||||||
// we will fire an accumulated ChainHeadEvent and disable fire
|
// we will fire an accumulated ChainHeadEvent and disable fire
|
||||||
// event here.
|
// event here.
|
||||||
if emitHeadEvent {
|
if emitHeadEvent {
|
||||||
|
exex.TriggerHeadHook(block.Header())
|
||||||
bc.chainHeadFeed.Send(ChainHeadEvent{Header: block.Header()})
|
bc.chainHeadFeed.Send(ChainHeadEvent{Header: block.Header()})
|
||||||
}
|
}
|
||||||
return CanonStatTy, nil
|
return CanonStatTy, nil
|
||||||
|
|
@ -1625,6 +1632,7 @@ func (bc *BlockChain) insertChain(chain types.Blocks, setHead bool, makeWitness
|
||||||
// Fire a single chain head event if we've progressed the chain
|
// Fire a single chain head event if we've progressed the chain
|
||||||
defer func() {
|
defer func() {
|
||||||
if lastCanon != nil && bc.CurrentBlock().Hash() == lastCanon.Hash() {
|
if lastCanon != nil && bc.CurrentBlock().Hash() == lastCanon.Hash() {
|
||||||
|
exex.TriggerHeadHook(lastCanon.Header())
|
||||||
bc.chainHeadFeed.Send(ChainHeadEvent{Header: lastCanon.Header()})
|
bc.chainHeadFeed.Send(ChainHeadEvent{Header: lastCanon.Header()})
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
@ -2410,6 +2418,7 @@ func (bc *BlockChain) SetCanonical(head *types.Block) (common.Hash, error) {
|
||||||
if len(logs) > 0 {
|
if len(logs) > 0 {
|
||||||
bc.logsFeed.Send(logs)
|
bc.logsFeed.Send(logs)
|
||||||
}
|
}
|
||||||
|
exex.TriggerHeadHook(head.Header())
|
||||||
bc.chainHeadFeed.Send(ChainHeadEvent{Header: head.Header()})
|
bc.chainHeadFeed.Send(ChainHeadEvent{Header: head.Header()})
|
||||||
|
|
||||||
context := []interface{}{
|
context := []interface{}{
|
||||||
|
|
|
||||||
42
core/exex/exex.go
Normal file
42
core/exex/exex.go
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
// Copyright 2024 The go-ethereum Authors
|
||||||
|
// This file is part of the go-ethereum library.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public License
|
||||||
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
// Package exex contains the stable API of the Geth Execution Extensions.
|
||||||
|
package exex
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/ethereum/go-ethereum/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
// RegisterV1 registers an execution extension plugin with a unique name.
|
||||||
|
func RegisterV1(name string, constructor NewPluginV1) {
|
||||||
|
globalRegistry.RegisterV1(name, constructor)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewPluginV1 is the constructor signature for making a new plugin.
|
||||||
|
type NewPluginV1 func(logger log.Logger) (*PluginV1, error)
|
||||||
|
|
||||||
|
// PluginV1 is an Execution Extension module that can be injected into Geth's
|
||||||
|
// processing pipeline to subscribe to different node, chain and EVM lifecycle
|
||||||
|
// events.
|
||||||
|
//
|
||||||
|
// Note, V1 of the Execution Extension plugin module has not yet been stabilized.
|
||||||
|
// There might be breaking changes until it is tagged as released!
|
||||||
|
type PluginV1 struct {
|
||||||
|
OnInit InitHook // Called when the chain gets initialized within Geth
|
||||||
|
OnClose CloseHook // Called when the chain gets torn down within Geth
|
||||||
|
OnHead HeadHook // Called when the chain head block is updated in Geth
|
||||||
|
}
|
||||||
78
core/exex/exex/adapter_chain.go
Normal file
78
core/exex/exex/adapter_chain.go
Normal file
|
|
@ -0,0 +1,78 @@
|
||||||
|
// Copyright 2024 The go-ethereum Authors
|
||||||
|
// This file is part of the go-ethereum library.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public License
|
||||||
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package exex
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/core/exex"
|
||||||
|
"github.com/ethereum/go-ethereum/core/state"
|
||||||
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
// gethChain provides dep-free read access to Geth's internal chain object.
|
||||||
|
//
|
||||||
|
// The methods here are not documented as this interface is not a public thing,
|
||||||
|
// rather it's a dynamic cast to break cross-package dependencies.
|
||||||
|
type gethChain interface {
|
||||||
|
CurrentBlock() *types.Header
|
||||||
|
GetHeaderByNumber(number uint64) *types.Header
|
||||||
|
GetBlockByNumber(number uint64) *types.Block
|
||||||
|
StateAt(root common.Hash) (*state.StateDB, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// chainAdapter is an adapter to convert Geth's internal blockchain (unstable
|
||||||
|
// and legacy API) into the exex chain interface (stable API).
|
||||||
|
type chainAdapter struct {
|
||||||
|
chain gethChain
|
||||||
|
}
|
||||||
|
|
||||||
|
// wrapChain wraps a Geth internal chain object into an exex stable API.
|
||||||
|
func wrapChain(chain gethChain) exex.Chain {
|
||||||
|
return &chainAdapter{chain: chain}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Head retrieves the current head block's header from the canonical chain.
|
||||||
|
func (a *chainAdapter) Head() *types.Header {
|
||||||
|
// Headers have public fields, copy to prevent modification
|
||||||
|
return types.CopyHeader(a.chain.CurrentBlock())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Header retrieves a block header with the given number from the canonical
|
||||||
|
// chain. Headers on side-chains are not exposed by the Chain interface.
|
||||||
|
func (a *chainAdapter) Header(number uint64) *types.Header {
|
||||||
|
// Headers have public fields, copy to prevent modification
|
||||||
|
if header := a.chain.GetHeaderByNumber(number); header != nil {
|
||||||
|
return types.CopyHeader(header)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Block retrieves a block header with the given number from the canonical
|
||||||
|
// chain. Blocks on side-chains are not exposed by the Chain interface.
|
||||||
|
func (a *chainAdapter) Block(number uint64) *types.Block {
|
||||||
|
// Blocks don't have public fields, return live objects directly
|
||||||
|
return a.chain.GetBlockByNumber(number)
|
||||||
|
}
|
||||||
|
|
||||||
|
// State retrieves a state accessor at a given root hash.
|
||||||
|
func (a *chainAdapter) State(root common.Hash) exex.State {
|
||||||
|
state, err := a.chain.StateAt(root)
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return wrapState(state)
|
||||||
|
}
|
||||||
43
core/exex/exex/adapter_state.go
Normal file
43
core/exex/exex/adapter_state.go
Normal file
|
|
@ -0,0 +1,43 @@
|
||||||
|
package exex
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/core/exex"
|
||||||
|
"github.com/ethereum/go-ethereum/core/state"
|
||||||
|
"github.com/holiman/uint256"
|
||||||
|
)
|
||||||
|
|
||||||
|
// stateAdapter is an adapter to convert Geth's internal state db (unstable
|
||||||
|
// and legacy API) into the exex state interface (stable API).
|
||||||
|
type stateAdapter struct {
|
||||||
|
state *state.StateDB
|
||||||
|
}
|
||||||
|
|
||||||
|
// wrapState wraps a Geth internal state object into an exex stable API.
|
||||||
|
func wrapState(state *state.StateDB) exex.State {
|
||||||
|
return &stateAdapter{state: state}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Balance retrieves the balance of the given account, or 0 if the account is
|
||||||
|
// not found in the state.
|
||||||
|
func (a *stateAdapter) Balance(addr common.Address) *uint256.Int {
|
||||||
|
return a.state.GetBalance(addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Nonce retrieves the nonce of the given account, or 0 if the account is not
|
||||||
|
// found in the state.
|
||||||
|
func (a *stateAdapter) Nonce(addr common.Address) uint64 {
|
||||||
|
return a.state.GetNonce(addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Code retrieves the bytecode associated with the given account, or a nil slice
|
||||||
|
// if the account is not found.
|
||||||
|
func (a *stateAdapter) Code(addr common.Address) []byte {
|
||||||
|
return common.CopyBytes(a.state.GetCode(addr))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Storage retrieves the value associated with a specific storage slot key within
|
||||||
|
// a specific account.
|
||||||
|
func (a *stateAdapter) Storage(addr common.Address, slot common.Hash) common.Hash {
|
||||||
|
return a.state.GetState(addr, slot)
|
||||||
|
}
|
||||||
66
core/exex/exex/registry.go
Normal file
66
core/exex/exex/registry.go
Normal file
|
|
@ -0,0 +1,66 @@
|
||||||
|
// Copyright 2024 The go-ethereum Authors
|
||||||
|
// This file is part of the go-ethereum library.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public License
|
||||||
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package exex
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/ethereum/go-ethereum/core/exex"
|
||||||
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
// globalRegistry is the Geth internal version of the exex registry with the
|
||||||
|
// trigger methods exposed to be callable from within Geth.
|
||||||
|
var globalRegistry registry
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
globalRegistry = exex.Registry().(registry)
|
||||||
|
}
|
||||||
|
|
||||||
|
// registry exposes all the hidden methods on the plugin registry to allow event
|
||||||
|
// triggers to be invoked.
|
||||||
|
type registry interface {
|
||||||
|
Plugins() []string
|
||||||
|
Instantiate(name string) error
|
||||||
|
|
||||||
|
TriggerInitHook(chain exex.Chain)
|
||||||
|
TriggerCloseHook()
|
||||||
|
TriggerHeadHook(head *types.Header)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Plugins returns a list of all registered plugins to generate CLI flags.
|
||||||
|
func Plugins() []string {
|
||||||
|
return globalRegistry.Plugins()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Instantiate constructs an execution extension plugin from a unique name.
|
||||||
|
func Instantiate(name string) error {
|
||||||
|
return globalRegistry.Instantiate(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TriggerInitHook triggers the OnInit hook in exex plugins.
|
||||||
|
func TriggerInitHook(chain gethChain) {
|
||||||
|
globalRegistry.TriggerInitHook(wrapChain(chain))
|
||||||
|
}
|
||||||
|
|
||||||
|
// TriggerCloseHook triggers the OnClose hook in exex plugins.
|
||||||
|
func TriggerCloseHook() {
|
||||||
|
globalRegistry.TriggerCloseHook()
|
||||||
|
}
|
||||||
|
|
||||||
|
// TriggerHeadHook triggers the OnHead hook in exex plugins.
|
||||||
|
func TriggerHeadHook(head *types.Header) {
|
||||||
|
globalRegistry.TriggerHeadHook(head)
|
||||||
|
}
|
||||||
38
core/exex/hooks.go
Normal file
38
core/exex/hooks.go
Normal file
|
|
@ -0,0 +1,38 @@
|
||||||
|
// Copyright 2024 The go-ethereum Authors
|
||||||
|
// This file is part of the go-ethereum library.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public License
|
||||||
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package exex
|
||||||
|
|
||||||
|
import "github.com/ethereum/go-ethereum/core/types"
|
||||||
|
|
||||||
|
// InitHook is called when the chain gets initialized within Geth.
|
||||||
|
type InitHook = func(chain Chain)
|
||||||
|
|
||||||
|
// CloseHook is called when the chain gets torn down within Geth.
|
||||||
|
type CloseHook = func()
|
||||||
|
|
||||||
|
// HeadHook is called when the chain head block is updated.
|
||||||
|
//
|
||||||
|
// - During full sync, this will be called for each block
|
||||||
|
// - During snap sync, this will be called from pivot onwards
|
||||||
|
// - In sync, this will be called on fork-choice updates
|
||||||
|
type HeadHook = func(head *types.Header)
|
||||||
|
|
||||||
|
// TODO(karalabe): This is interesting. We need to keep events in sync with the
|
||||||
|
// user's chain access capabilities. We either need to provide side-chain access,
|
||||||
|
// which gets nasty fast; or we need to reorg in lockstep; or we need two events
|
||||||
|
// one to start a reorg (going back) and one having finished (going forward).
|
||||||
|
// type ReorgHook = func(old, new *types.Header) error
|
||||||
42
core/exex/interface_chain.go
Normal file
42
core/exex/interface_chain.go
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
// Copyright 2024 The go-ethereum Authors
|
||||||
|
// This file is part of the go-ethereum library.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public License
|
||||||
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package exex
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Chain provides read access to Geth's internal chain object.
|
||||||
|
type Chain interface {
|
||||||
|
// TODO(karalabe): Wrap chain config into an exex interface
|
||||||
|
// Config() *params.ChainConfig
|
||||||
|
|
||||||
|
// Head retrieves the current head block's header from the canonical chain.
|
||||||
|
Head() *types.Header
|
||||||
|
|
||||||
|
// Header retrieves a block header with the given number from the canonical
|
||||||
|
// chain. Headers on side-chains are not exposed by the Chain interface.
|
||||||
|
Header(number uint64) *types.Header
|
||||||
|
|
||||||
|
// Block retrieves an entire block with the given number from the canonical
|
||||||
|
// chain. Blocks on side-chains are not exposed by the Chain interface.
|
||||||
|
Block(number uint64) *types.Block
|
||||||
|
|
||||||
|
// State retrieves a state accessor at a given root hash.
|
||||||
|
State(root common.Hash) State
|
||||||
|
}
|
||||||
25
core/exex/interface_state.go
Normal file
25
core/exex/interface_state.go
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
package exex
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/holiman/uint256"
|
||||||
|
)
|
||||||
|
|
||||||
|
// State provides read access to Geth's internal state object.
|
||||||
|
type State interface {
|
||||||
|
// Balance retrieves the balance of the given account, or 0 if the account
|
||||||
|
// is not found in the state.
|
||||||
|
Balance(addr common.Address) *uint256.Int
|
||||||
|
|
||||||
|
// Nonce retrieves the nonce of the given account, or 0 if the account is
|
||||||
|
// not found in the state.
|
||||||
|
Nonce(addr common.Address) uint64
|
||||||
|
|
||||||
|
// Code retrieves the bytecode associated with the given account, or a nil
|
||||||
|
// slice if the account is not found.
|
||||||
|
Code(addr common.Address) []byte
|
||||||
|
|
||||||
|
// Storage retrieves the value associated with a specific storage slot key
|
||||||
|
// within a specific account.
|
||||||
|
Storage(addr common.Address, slot common.Hash) common.Hash
|
||||||
|
}
|
||||||
48
core/exex/registry.go
Normal file
48
core/exex/registry.go
Normal file
|
|
@ -0,0 +1,48 @@
|
||||||
|
// Copyright 2024 The go-ethereum Authors
|
||||||
|
// This file is part of the go-ethereum library.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public License
|
||||||
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package exex
|
||||||
|
|
||||||
|
// globalRegistry is the public plugin registry to inject execution extensions into.
|
||||||
|
var globalRegistry = newRegistry()
|
||||||
|
|
||||||
|
// Registry retrieves the global plugin registry.
|
||||||
|
//
|
||||||
|
// Note, the downcast to interface{} is deliberate to hide all the methods on the
|
||||||
|
// registry and avoid plugins from accidentally poking at unintended internals.
|
||||||
|
func Registry() interface{} {
|
||||||
|
return globalRegistry
|
||||||
|
}
|
||||||
|
|
||||||
|
// registry is the collection of Execution Extension plugins which can be used
|
||||||
|
// to extend Geth's functionality with external code.
|
||||||
|
type registry struct {
|
||||||
|
pluginsMakersV1 map[string]NewPluginV1
|
||||||
|
pluginsV1 map[string]*PluginV1
|
||||||
|
}
|
||||||
|
|
||||||
|
// newRegistry creates a new exex plugin registry.
|
||||||
|
func newRegistry() *registry {
|
||||||
|
return ®istry{
|
||||||
|
pluginsMakersV1: make(map[string]NewPluginV1),
|
||||||
|
pluginsV1: make(map[string]*PluginV1),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// RegisterV1 registers an execution extension plugin with a unique name.
|
||||||
|
func (reg *registry) RegisterV1(name string, constructor NewPluginV1) {
|
||||||
|
reg.pluginsMakersV1[name] = constructor
|
||||||
|
}
|
||||||
77
core/exex/registry_internal.go
Normal file
77
core/exex/registry_internal.go
Normal file
|
|
@ -0,0 +1,77 @@
|
||||||
|
// Copyright 2024 The go-ethereum Authors
|
||||||
|
// This file is part of the go-ethereum library.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public License
|
||||||
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package exex
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"sort"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
|
"github.com/ethereum/go-ethereum/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Plugins returns a list of all registered plugins to generate CLI flags.
|
||||||
|
func (reg *registry) Plugins() []string {
|
||||||
|
plugins := make([]string, 0, len(reg.pluginsMakersV1))
|
||||||
|
for name := range reg.pluginsMakersV1 {
|
||||||
|
plugins = append(plugins, name)
|
||||||
|
}
|
||||||
|
sort.Strings(plugins)
|
||||||
|
return plugins
|
||||||
|
}
|
||||||
|
|
||||||
|
// Instantiate constructs an execution extension plugin from a unique name.
|
||||||
|
func (reg *registry) Instantiate(name string) error {
|
||||||
|
// Try instantiating a V1 plugin
|
||||||
|
if constructor, ok := globalRegistry.pluginsMakersV1[name]; ok {
|
||||||
|
plugin, err := constructor(log.New("exex", name))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
globalRegistry.pluginsV1[name] = plugin
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
// No plugins matched across any versions, return a failure
|
||||||
|
return errors.New("not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
// TriggerInitHook triggers the OnInit hook in exex plugins.
|
||||||
|
func (reg *registry) TriggerInitHook(chain Chain) {
|
||||||
|
for _, plugin := range globalRegistry.pluginsV1 {
|
||||||
|
if plugin.OnInit != nil {
|
||||||
|
plugin.OnInit(chain)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TriggerCloseHook triggers the OnClose hook in exex plugins.
|
||||||
|
func (reg *registry) TriggerCloseHook() {
|
||||||
|
for _, plugin := range globalRegistry.pluginsV1 {
|
||||||
|
if plugin.OnClose != nil {
|
||||||
|
plugin.OnClose()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TriggerHeadHook triggers the OnHead hook in exex plugins.
|
||||||
|
func (reg *registry) TriggerHeadHook(head *types.Header) {
|
||||||
|
for _, plugin := range globalRegistry.pluginsV1 {
|
||||||
|
if plugin.OnHead != nil {
|
||||||
|
plugin.OnHead(head)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -32,6 +32,7 @@ const (
|
||||||
MinerCategory = "MINER"
|
MinerCategory = "MINER"
|
||||||
GasPriceCategory = "GAS PRICE ORACLE"
|
GasPriceCategory = "GAS PRICE ORACLE"
|
||||||
VMCategory = "VIRTUAL MACHINE"
|
VMCategory = "VIRTUAL MACHINE"
|
||||||
|
ExExCategory = "EXECUTION EXTENSIONS"
|
||||||
LoggingCategory = "LOGGING AND DEBUGGING"
|
LoggingCategory = "LOGGING AND DEBUGGING"
|
||||||
MetricsCategory = "METRICS AND STATS"
|
MetricsCategory = "METRICS AND STATS"
|
||||||
MiscCategory = "MISC"
|
MiscCategory = "MISC"
|
||||||
|
|
|
||||||
38
plugins/minimal.go
Normal file
38
plugins/minimal.go
Normal file
|
|
@ -0,0 +1,38 @@
|
||||||
|
// Copyright 2024 The go-ethereum Authors
|
||||||
|
// This file is part of the go-ethereum library.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public License
|
||||||
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package plugins
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/ethereum/go-ethereum/core/exex"
|
||||||
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
|
"github.com/ethereum/go-ethereum/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Register the minimal ExEx plugin into Geth.
|
||||||
|
func init() {
|
||||||
|
exex.RegisterV1("minimal", newMinimalPlugin)
|
||||||
|
}
|
||||||
|
|
||||||
|
// newMinimalPlugin creates a minimal Execution Extension plugin to react to some
|
||||||
|
// chain events.
|
||||||
|
func newMinimalPlugin(logger log.Logger) (*exex.PluginV1, error) {
|
||||||
|
return &exex.PluginV1{
|
||||||
|
OnHead: func(head *types.Header) {
|
||||||
|
logger.Info("Chain head updated", "number", head.Number, "hash", head.Hash())
|
||||||
|
},
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue