Skip to content

Add bootnode component for EL and CL nodes. #99

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 44 additions & 0 deletions internal/components.go
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,7 @@ func (o *OpGeth) Run(service *service, ctx *ExContext) {
"--gcmode archive "+
"--state.scheme hash "+
"--port "+`{{Port "rpc" 30303}} `+
"--bootnodes enode://"+defaultDiscoveryEnodeID+"@bootnode:30301 "+
nodeKeyFlag+
"--metrics "+
"--metrics.addr 0.0.0.0 "+
Expand Down Expand Up @@ -253,6 +254,7 @@ func (r *RethEL) Run(svc *service, ctx *ExContext) {
"--ipcpath", "{{.Dir}}/reth.ipc",
"--addr", "127.0.0.1",
"--port", `{{Port "rpc" 30303}}`,
"--bootnodes", "enode://"+defaultDiscoveryEnodeID+"@bootnode:30301",
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You cannot hardcode any service name (bootnode in this case).

// "--disable-discovery",
// http config
"--http",
Expand Down Expand Up @@ -318,6 +320,7 @@ func (l *LighthouseBeaconNode) Run(svc *service, ctx *ExContext) {
"--always-prepare-payload",
"--prepare-payload-lookahead", "8000",
"--suggested-fee-recipient", "0x690B9A9E9aa1C9dB991C7721a92d351Db4FaC990",
"--boot-nodes", "enode://"+defaultDiscoveryEnodeID+"@bootnode:30301",
).
WithReady(ReadyCheck{
QueryURL: "http://localhost:3500/eth/v1/node/syncing",
Expand Down Expand Up @@ -544,3 +547,44 @@ func (p *OpReth) Watchdog(out io.Writer, service *service, ctx context.Context)
rethURL := fmt.Sprintf("http://localhost:%d", service.MustGetPort("http").HostPort)
return watchChainHead(out, rethURL, 2*time.Second)
}

// Bootnode represents a P2P discovery bootnode service that helps other nodes discover each other
// in the network. It runs a Geth bootnode instance that other Ethereum clients can connect to
// for peer discovery.
type Bootnode struct {
// DiscoveryPort is the UDP port used for P2P discovery
DiscoveryPort uint64

// PrivateKey is the hex-encoded private key used by the bootnode for P2P communication.
// If not provided, a default key will be used.
PrivateKey string
}

// Watchdog implements the ServiceWatchdog interface to monitor the bootnode's health.
// It checks if the bootnode is listening for connections on its discovery port.
var _ ServiceWatchdog = &Bootnode{}

// Run configures and starts the bootnode service using the ethereum/client-go image.
// It sets up the necessary arguments for the bootnode to run, including the private key
// and discovery port.
func (b *Bootnode) Run(service *service, ctx *ExContext) {
// Use the default discovery key if not provided
privateKey := b.PrivateKey
if privateKey == "" {
privateKey = defaultDiscoveryPrivKey
}

service.
WithImage("ethereum/client-go").
WithTag("v1.13.0").
WithEntrypoint("bootnode").
WithArgs(
"--nodekeyhex", privateKey,
"--addr", fmt.Sprintf("0.0.0.0:%d", b.DiscoveryPort),
"--verbosity", "3",
)
}

func (b *Bootnode) Name() string {
return "bootnode"
}
48 changes: 38 additions & 10 deletions internal/recipe_l1.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ type L1Recipe struct {
// will run on the host machine. This is useful if you want to bind to the Reth database and you
// are running a host machine (i.e Mac) that is differerent from the docker one (Linux)
useNativeReth bool

// bootnodePrivKey is the private key used by the bootnode for P2P discovery
bootnodePrivKey string
}

func (l *L1Recipe) Name() string {
Expand All @@ -39,6 +42,7 @@ func (l *L1Recipe) Flags() *flag.FlagSet {
flags.BoolVar(&l.useRethForValidation, "use-reth-for-validation", false, "use reth for validation")
flags.Uint64Var(&l.secondaryELPort, "secondary-el", 0, "port to use for the secondary builder")
flags.BoolVar(&l.useNativeReth, "use-native-reth", false, "use the native reth binary")
flags.StringVar(&l.bootnodePrivKey, "bootnode-privkey", "", "private key for the bootnode (optional)")
return flags
}

Expand All @@ -49,18 +53,33 @@ func (l *L1Recipe) Artifacts() *ArtifactsBuilder {
return builder
}

func (m *Manifest) AddServiceWithDeps(name string, service Service, deps ...string) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Dependencies are mostly defined at the components level, not at the recipe.

m.AddService(name, service)
for _, dep := range deps {
m.MustGetService(name).DependsOnHealthy(dep)
}
}

func (l *L1Recipe) Apply(ctx *ExContext, artifacts *Artifacts) *Manifest {
svcManager := NewManifest(ctx, artifacts.Out)

svcManager.AddService("el", &RethEL{
// Register bootnode without dependency
bootnode := &Bootnode{
DiscoveryPort: 30301,
PrivateKey: l.bootnodePrivKey,
}
svcManager.AddService("bootnode", bootnode)

// Register 'el' service with dependency on bootnode
el := &RethEL{
UseRethForValidation: l.useRethForValidation,
UseNativeReth: l.useNativeReth,
})
}
svcManager.AddServiceWithDeps("el", el, "bootnode")

var elService string
if l.secondaryELPort != 0 {
// we are going to use the cl-proxy service to connect the beacon node to two builders
// one the 'el' builder and another one the remote one
// Use cl-proxy service to connect the beacon node to two builders
elService = "cl-proxy"
svcManager.AddService("cl-proxy", &ClProxy{
PrimaryBuilder: "el",
Expand All @@ -70,22 +89,31 @@ func (l *L1Recipe) Apply(ctx *ExContext, artifacts *Artifacts) *Manifest {
elService = "el"
}

svcManager.AddService("beacon", &LighthouseBeaconNode{
// Register beacon with dependency on bootnode
beacon := &LighthouseBeaconNode{
ExecutionNode: elService,
MevBoostNode: "mev-boost",
})
svcManager.AddService("validator", &LighthouseValidator{
}
svcManager.AddServiceWithDeps("beacon", beacon, "bootnode")

// Register validator with dependency on beacon
validator := &LighthouseValidator{
BeaconNode: "beacon",
})
}
svcManager.AddServiceWithDeps("validator", validator, "beacon")

mevBoostValidationServer := ""
if l.useRethForValidation {
mevBoostValidationServer = "el"
}
svcManager.AddService("mev-boost", &MevBoostRelay{

// Register mev-boost with dependency on beacon
mevBoost := &MevBoostRelay{
BeaconClient: "beacon",
ValidationServer: mevBoostValidationServer,
})
}
svcManager.AddServiceWithDeps("mev-boost", mevBoost, "beacon")

return svcManager
}

Expand Down