From b505f86cc466a4a229bec8df6c3a7b2cec908c97 Mon Sep 17 00:00:00 2001 From: Simon Smilga Date: Tue, 8 Apr 2025 01:42:31 +0600 Subject: [PATCH 1/5] Add bootnode components --- internal/components.go | 33 ++++++++++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/internal/components.go b/internal/components.go index 470e69d..11063b0 100644 --- a/internal/components.go +++ b/internal/components.go @@ -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 "+ @@ -253,7 +254,7 @@ func (r *RethEL) Run(svc *service, ctx *ExContext) { "--ipcpath", "{{.Dir}}/reth.ipc", "--addr", "127.0.0.1", "--port", `{{Port "rpc" 30303}}`, - // "--disable-discovery", + "--bootnodes", "enode://"+defaultDiscoveryEnodeID+"@bootnode:30301", // http config "--http", "--http.addr", "0.0.0.0", @@ -318,6 +319,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", @@ -544,3 +546,32 @@ 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) } + +type Bootnode struct { + // Discovery port for P2P communication + DiscoveryPort uint64 + // Private key for the bootnode (optional) + PrivateKey string +} + +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" +} From 1ca515d8889f54f1c07a4c9c3703d86a02586b76 Mon Sep 17 00:00:00 2001 From: Simon Smilga Date: Tue, 8 Apr 2025 01:46:41 +0600 Subject: [PATCH 2/5] remove comment diff --- internal/components.go | 1 + 1 file changed, 1 insertion(+) diff --git a/internal/components.go b/internal/components.go index 11063b0..af98b15 100644 --- a/internal/components.go +++ b/internal/components.go @@ -255,6 +255,7 @@ func (r *RethEL) Run(svc *service, ctx *ExContext) { "--addr", "127.0.0.1", "--port", `{{Port "rpc" 30303}}`, "--bootnodes", "enode://"+defaultDiscoveryEnodeID+"@bootnode:30301", + // "--disable-discovery", // http config "--http", "--http.addr", "0.0.0.0", From 59327e7b61b15e5ce604b2888ab9a157c5364333 Mon Sep 17 00:00:00 2001 From: Simon Smilga Date: Tue, 8 Apr 2025 01:47:55 +0600 Subject: [PATCH 3/5] Update recipe_l1.go --- internal/recipe_l1.go | 36 ++++++++++++++++++++++++++++-------- 1 file changed, 28 insertions(+), 8 deletions(-) diff --git a/internal/recipe_l1.go b/internal/recipe_l1.go index ee267af..1802dbb 100644 --- a/internal/recipe_l1.go +++ b/internal/recipe_l1.go @@ -52,10 +52,18 @@ func (l *L1Recipe) Artifacts() *ArtifactsBuilder { func (l *L1Recipe) Apply(ctx *ExContext, artifacts *Artifacts) *Manifest { svcManager := NewManifest(ctx, artifacts.Out) - svcManager.AddService("el", &RethEL{ + // Add bootnode service first + bootnode := &Bootnode{ + DiscoveryPort: 30301, + } + svcManager.AddService("bootnode", bootnode) + + el := &RethEL{ UseRethForValidation: l.useRethForValidation, UseNativeReth: l.useNativeReth, - }) + } + svcManager.AddService("el", el) + svcManager.MustGetService("el").DependsOnHealthy("bootnode") var elService string if l.secondaryELPort != 0 { @@ -70,22 +78,34 @@ func (l *L1Recipe) Apply(ctx *ExContext, artifacts *Artifacts) *Manifest { elService = "el" } - svcManager.AddService("beacon", &LighthouseBeaconNode{ + // Add beacon node with dependency on bootnode + beacon := &LighthouseBeaconNode{ ExecutionNode: elService, MevBoostNode: "mev-boost", - }) - svcManager.AddService("validator", &LighthouseValidator{ + } + svcManager.AddService("beacon", beacon) + svcManager.MustGetService("beacon").DependsOnHealthy("bootnode") + + // Add validator with dependency on beacon node + validator := &LighthouseValidator{ BeaconNode: "beacon", - }) + } + svcManager.AddService("validator", validator) + svcManager.MustGetService("validator").DependsOnHealthy("beacon") mevBoostValidationServer := "" if l.useRethForValidation { mevBoostValidationServer = "el" } - svcManager.AddService("mev-boost", &MevBoostRelay{ + + // Add mev-boost with dependency on beacon node + mevBoost := &MevBoostRelay{ BeaconClient: "beacon", ValidationServer: mevBoostValidationServer, - }) + } + svcManager.AddService("mev-boost", mevBoost) + svcManager.MustGetService("mev-boost").DependsOnHealthy("beacon") + return svcManager } From 0ee97fac75b17ebf471aeb3fbf694a69f039ee0b Mon Sep 17 00:00:00 2001 From: Simon Smilga Date: Thu, 10 Apr 2025 23:19:45 +0600 Subject: [PATCH 4/5] optimise bootnote changes --- internal/components.go | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/internal/components.go b/internal/components.go index af98b15..75fdeaf 100644 --- a/internal/components.go +++ b/internal/components.go @@ -548,13 +548,25 @@ func (p *OpReth) Watchdog(out io.Writer, service *service, ctx context.Context) 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 { - // Discovery port for P2P communication + // DiscoveryPort is the UDP port used for P2P discovery DiscoveryPort uint64 - // Private key for the bootnode (optional) + + // 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 From c91c6c07490c87c4e23b4764cbc125d3b2540f99 Mon Sep 17 00:00:00 2001 From: Simon Smilga Date: Thu, 10 Apr 2025 23:21:13 +0600 Subject: [PATCH 5/5] optimise bootnode changes --- internal/recipe_l1.go | 38 +++++++++++++++++++++++--------------- 1 file changed, 23 insertions(+), 15 deletions(-) diff --git a/internal/recipe_l1.go b/internal/recipe_l1.go index 1802dbb..3ffa04e 100644 --- a/internal/recipe_l1.go +++ b/internal/recipe_l1.go @@ -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 { @@ -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 } @@ -49,26 +53,33 @@ func (l *L1Recipe) Artifacts() *ArtifactsBuilder { return builder } +func (m *Manifest) AddServiceWithDeps(name string, service Service, deps ...string) { + 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) - // Add bootnode service first + // 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.AddService("el", el) - svcManager.MustGetService("el").DependsOnHealthy("bootnode") + 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", @@ -78,33 +89,30 @@ func (l *L1Recipe) Apply(ctx *ExContext, artifacts *Artifacts) *Manifest { elService = "el" } - // Add beacon node with dependency on bootnode + // Register beacon with dependency on bootnode beacon := &LighthouseBeaconNode{ ExecutionNode: elService, MevBoostNode: "mev-boost", } - svcManager.AddService("beacon", beacon) - svcManager.MustGetService("beacon").DependsOnHealthy("bootnode") + svcManager.AddServiceWithDeps("beacon", beacon, "bootnode") - // Add validator with dependency on beacon node + // Register validator with dependency on beacon validator := &LighthouseValidator{ BeaconNode: "beacon", } - svcManager.AddService("validator", validator) - svcManager.MustGetService("validator").DependsOnHealthy("beacon") + svcManager.AddServiceWithDeps("validator", validator, "beacon") mevBoostValidationServer := "" if l.useRethForValidation { mevBoostValidationServer = "el" } - - // Add mev-boost with dependency on beacon node + + // Register mev-boost with dependency on beacon mevBoost := &MevBoostRelay{ BeaconClient: "beacon", ValidationServer: mevBoostValidationServer, } - svcManager.AddService("mev-boost", mevBoost) - svcManager.MustGetService("mev-boost").DependsOnHealthy("beacon") + svcManager.AddServiceWithDeps("mev-boost", mevBoost, "beacon") return svcManager }