diff --git a/CHANGELOG.md b/CHANGELOG.md index 215e3b1a6..fa5bd3fa1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - New command `lido-status` to display data of Lido Node Operator. - Monitoring stack setup with Grafana, Prometheus, and Node Exporter. - Security policy. +- Support for Nimbus as Consensus and Validator client. ### Changed - Update Go version from 1.21 to 1.22. diff --git a/README.md b/README.md index 432323812..d6a2569f0 100644 --- a/README.md +++ b/README.md @@ -141,11 +141,12 @@ read more about it in [our documentation](https://docs.sedge.nethermind.io/docs/ ### Mainnet | Execution | Consensus | Validator | -| ---------- | ---------- | ---------- | +| ---------- |------------|------------| | Geth | Lighthouse | Lighthouse | | Nethermind | Lodestar | Lodestar | | Erigon | Prysm | Prysm | | Besu | Teku | Teku | +| | Nimbus | Nimbus | ### Sepolia @@ -155,6 +156,7 @@ read more about it in [our documentation](https://docs.sedge.nethermind.io/docs/ | Nethermind | Lodestar | Lodestar | | Erigon | Prysm | Prysm | | Besu | Teku | Teku | +| | Nimbus | Nimbus | ### Holesky @@ -164,6 +166,7 @@ read more about it in [our documentation](https://docs.sedge.nethermind.io/docs/ | Nethermind | Lodestar | Lodestar | | Erigon | Teku | Teku | | Besu | Prysm | Prysm | +| | Nimbus | Nimbus | ### Gnosis @@ -172,23 +175,26 @@ read more about it in [our documentation](https://docs.sedge.nethermind.io/docs/ | Nethermind | Lighthouse | Lighthouse | | Erigon | Lodestar | Lodestar | | | Teku | Teku | +| | Nimbus | Nimbus | ### Chiado (Gnosis testnet) | Execution | Consensus | Validator | -| ------------- | ---------- | ---------- | +|---------------| ---------- | ---------- | | Nethermind | Lighthouse | Lighthouse | | Erigon (soon) | Lodestar | Lodestar | | | Teku | Teku | +| | Nimbus | Nimbus | ### CL clients with Mev-Boost -| Client | Mev-Boost | Networks | -| ---------- | --------- |---------------------------| -| Lighthouse | yes | Mainnet, Sepolia, Holesky | -| Lodestar | yes | Mainnet, Sepolia, Holesky | -| Prysm | yes | Mainnet, Sepolia, Holesky | -| Teku | yes | Mainnet, Sepolia, Holesky | +| Client | Mev-Boost | Networks | +|------------|------------|---------------------------| +| Lighthouse | yes | Mainnet, Sepolia, Holesky | +| Lodestar | yes | Mainnet, Sepolia, Holesky | +| Prysm | yes | Mainnet, Sepolia, Holesky | +| Teku | yes | Mainnet, Sepolia, Holesky | +| Nimbus | yes | Mainnet, Sepolia, Holesky | ## Supported Linux flavours for dependency installation @@ -256,9 +262,9 @@ The following roadmap covers the main features and ideas we want to implement bu - [x] Support Erigon on Gnosis - [x] Support for Lido CSM +- [x] Support for Nimbus client as Consensus and Validator - [ ] Include monitoring tool for alerting, tracking validator balance, and tracking sync progress and status of nodes - [ ] More tests!!! -- [ ] Support for Nimbus client ## 💪 Want to contribute? diff --git a/cli/actions/generation_test.go b/cli/actions/generation_test.go index c91cacfdb..895c3a9ab 100644 --- a/cli/actions/generation_test.go +++ b/cli/actions/generation_test.go @@ -346,7 +346,11 @@ func TestGenerateDockerCompose(t *testing.T) { } // Check that Checkpoint Sync URL is set if tc.genData.CheckpointSyncUrl != "" { - assert.True(t, contains(t, cmpData.Services.Consensus.Command, tc.genData.CheckpointSyncUrl), "Checkpoint Sync URL not found in consensus service command: %s", cmpData.Services.Consensus.Command) + if tc.genData.ConsensusClient != nil && tc.genData.ConsensusClient.Name == "nimbus" { + assert.True(t, contains(t, cmpData.Services.ConsensusSync.Command, tc.genData.CheckpointSyncUrl), "Checkpoint Sync URL not found in consensus service command: %s", cmpData.Services.ConsensusSync.Command) + } else { + assert.True(t, contains(t, cmpData.Services.Consensus.Command, tc.genData.CheckpointSyncUrl), "Checkpoint Sync URL not found in consensus service command: %s", cmpData.Services.Consensus.Command) + } } // Check ccImage has the right format diff --git a/cli/actions/importKeys.go b/cli/actions/importKeys.go index a68fefa0e..b6895b51f 100644 --- a/cli/actions/importKeys.go +++ b/cli/actions/importKeys.go @@ -19,9 +19,11 @@ import ( "context" "errors" "fmt" + "io" "os" "os/signal" "path/filepath" + "syscall" "github.com/NethermindEth/sedge/configs" "github.com/NethermindEth/sedge/internal/images/validator-import/lighthouse" @@ -35,6 +37,7 @@ import ( v1 "github.com/opencontainers/image-spec/specs-go/v1" "github.com/otiai10/copy" log "github.com/sirupsen/logrus" + "golang.org/x/crypto/ssh/terminal" ) var ErrInterrupted = errors.New("interrupt") @@ -110,6 +113,12 @@ func (s *sedgeActions) ImportValidatorKeys(options ImportValidatorKeysOptions) e return err } ctID = prysmCtID + case "nimbus": + nimbusCtID, err := setupNimbusValidatorImport(s.dockerClient, s.dockerServiceManager, options) + if err != nil { + return err + } + ctID = nimbusCtID case "lodestar": lodestarCtID, err := setupLodestarValidatorImport(s.dockerClient, s.dockerServiceManager, options) if err != nil { @@ -132,7 +141,12 @@ func (s *sedgeActions) ImportValidatorKeys(options ImportValidatorKeysOptions) e return fmt.Errorf("%w: %s", ErrUnsupportedValidatorClient, options.ValidatorClient) } log.Info("Importing validator keys") - runErr := runAndWaitImportKeys(s.dockerClient, s.dockerServiceManager, ctID) + var runErr error + if options.ValidatorClient == "nimbus" { + runErr = runAndWaitImportKeysNimbus(s.dockerClient, s.dockerServiceManager, ctID) + } else { + runErr = runAndWaitImportKeys(s.dockerClient, s.dockerServiceManager, ctID) + } // Run validator again if (previouslyRunning && !options.StopValidator) || options.StartValidator { log.Info("The validator container is being restarted") @@ -208,6 +222,69 @@ func setupPrysmValidatorImportContainer(dockerClient client.APIClient, dockerSer return ct.ID, nil } +func setupNimbusValidatorImport(dockerClient client.APIClient, dockerServiceManager DockerServiceManager, options ImportValidatorKeysOptions) (string, error) { + var ( + // In the case of Nimbus, it's the consensus client the one that import the keys. + consensusCtName = services.ContainerNameWithTag(services.DefaultSedgeConsensusClient, options.ContainerTag) + validatorCtName = services.ContainerNameWithTag(services.DefaultSedgeValidatorClient, options.ContainerTag) + validatorImportCtName = services.ContainerNameWithTag(services.ServiceCtValidatorImport, options.ContainerTag) + ) + validatorImage, err := dockerServiceManager.Image(consensusCtName) + if err != nil { + return "", err + } + // Mounts + mounts := []mount.Mount{ + { + Type: mount.TypeBind, + Source: options.From, + Target: "/keystore", + }, + } + // CMD + cmd := []string{ + "deposits", + "import", + "--data-dir=/data", + "--method=single-salt", + "/keystore", + } + // Custom options + if options.CustomConfig.NetworkConfigPath != "" { + mounts = append(mounts, mount.Mount{ + Type: mount.TypeBind, + Source: options.CustomConfig.NetworkConfigPath, + Target: "/network_config/config.yml", + }) + cmd = append(cmd, "--config-file=/network_config/config.yml") + } else { + cmd = append(cmd, "--network="+options.Network) + } + log.Debugf("Creating %s container", validatorImportCtName) + ct, err := dockerClient.ContainerCreate(context.Background(), + &container.Config{ + Image: validatorImage, + Cmd: cmd, + AttachStdin: true, + AttachStderr: true, + AttachStdout: true, + OpenStdin: true, + Tty: true, + }, + &container.HostConfig{ + Mounts: mounts, + VolumesFrom: []string{consensusCtName, validatorCtName}, + }, + &network.NetworkingConfig{}, + &v1.Platform{}, + validatorImportCtName, + ) + if err != nil { + return "", err + } + return ct.ID, nil +} + func setupLodestarValidatorImport(dockerClient client.APIClient, dockerServiceManager DockerServiceManager, options ImportValidatorKeysOptions) (string, error) { var ( validatorCtName = services.ContainerNameWithTag(services.DefaultSedgeValidatorClient, options.ContainerTag) @@ -437,3 +514,89 @@ func runAndWaitImportKeys(dockerClient client.APIClient, dockerServiceManager Do } } } + +// runAndWaitImportKeysNimbus starts the container in interactive mode and waits for it to finish. +func runAndWaitImportKeysNimbus(dockerClient client.APIClient, dockerServiceManager DockerServiceManager, ctID string) error { + log.Debugf("Starting interactive container with id: %s", ctID) + + // Attach to the container's input/output for direct interaction + resp, err := dockerClient.ContainerAttach(context.Background(), ctID, container.AttachOptions{ + Stream: true, + Stdin: true, + Stdout: true, + Stderr: true, + Logs: false, // Don't attach previous logs + }) + if err != nil { + return err + } + defer resp.Close() + + // Put the terminal in raw mode for proper TTY handling + oldState, err := terminal.MakeRaw(int(syscall.Stdin)) + if err != nil { + return err + } + defer func() { + // Restore the terminal state immediately when the container finishes + terminal.Restore(int(syscall.Stdin), oldState) + // Clear the line again before printing the final success logs + fmt.Print("\033[2K\r") // Clear the current line in the terminal + }() + + // Start the container + if err := dockerClient.ContainerStart(context.Background(), ctID, container.StartOptions{}); err != nil { + return err + } + + // Use goroutines to pipe stdin, stdout, and stderr directly to the user's terminal + go func() { + // Pipe container stdout and stderr to the terminal + _, err := io.Copy(os.Stdout, resp.Reader) + if err != nil { + log.Errorf("Error piping container output: %v", err) + } + }() + go func() { + // Pipe terminal input to the container stdin + _, err := io.Copy(resp.Conn, os.Stdin) + if err != nil { + log.Errorf("Error piping user input: %v", err) + } + }() + + // Wait for the container to finish execution + ctExit, errChan := dockerServiceManager.Wait(ctID, container.WaitConditionNextExit) + + // Handle OS interrupts (e.g., Ctrl+C) to gracefully stop the container + osSignals := make(chan os.Signal, 1) + signal.Notify(osSignals, os.Interrupt, syscall.SIGTERM) + + select { + case exitResult := <-ctExit: + if err = deleteContainer(dockerClient, ctID); err != nil { + return err + } + // Wait for the container to exit normally + if exitResult.StatusCode != 0 { + log.Errorf("Container exited with non-zero status: %d", exitResult.StatusCode) + return newValidatorImportCtBadExitCodeError(ctID, exitResult.StatusCode, "Container logs...") + } + + case <-osSignals: + // If the user interrupts (e.g., Ctrl+C), stop the container + log.Infof("Received interrupt signal, stopping container %s", ctID) + if err := stopContainer(dockerClient, ctID); err != nil { + log.Errorf("Error stopping container: %v", err) + } + if err = deleteContainer(dockerClient, ctID); err != nil { + return err + } + return ErrInterrupted + + case err := <-errChan: + return err + } + + return nil +} diff --git a/cli/actions/slashing.go b/cli/actions/slashing.go index d642ece83..8e60c5273 100644 --- a/cli/actions/slashing.go +++ b/cli/actions/slashing.go @@ -47,6 +47,7 @@ type SlashingImportOptions struct { func (s *sedgeActions) ImportSlashingInterchangeData(options SlashingImportOptions) error { validatorContainerName := services.ContainerNameWithTag(services.DefaultSedgeValidatorClient, options.ContainerTag) + consensusContainerName := services.ContainerNameWithTag(services.DefaultSedgeConsensusClient, options.ContainerTag) slashingContainerName := services.ContainerNameWithTag(services.ServiceCtSlashingData, options.ContainerTag) // Check validator container exists _, err := s.dockerServiceManager.ContainerID(validatorContainerName) @@ -105,11 +106,18 @@ func (s *sedgeActions) ImportSlashingInterchangeData(options SlashingImportOptio "--data-path=/data", "--from=/data/slashing_protection.json", } + case "nimbus": + cmd = []string{ + "slashingdb", + "import", + "/data/slashing_protection.json", + "--validators-dir=/data", + } default: return fmt.Errorf("%w: %s", ErrUnsupportedValidatorClient, options.ValidatorClient) } log.Infof("Importing slashing data to client %s from %s", options.ValidatorClient, options.From) - if err := runSlashingContainer(s.dockerClient, s.dockerServiceManager, cmd, validatorContainerName, slashingContainerName); err != nil { + if err := runSlashingContainer(s.dockerClient, s.dockerServiceManager, cmd, validatorContainerName, consensusContainerName, slashingContainerName, options.ValidatorClient == "nimbus"); err != nil { return err } @@ -136,6 +144,7 @@ type SlashingExportOptions struct { func (s *sedgeActions) ExportSlashingInterchangeData(options SlashingExportOptions) error { validatorContainerName := services.ContainerNameWithTag(services.DefaultSedgeValidatorClient, options.ContainerTag) + consensusContainerName := services.ContainerNameWithTag(services.DefaultSedgeConsensusClient, options.ContainerTag) slashingContainerName := services.ContainerNameWithTag(services.ServiceCtSlashingData, options.ContainerTag) // Check validator container exists _, err := s.dockerServiceManager.ContainerID(validatorContainerName) @@ -187,11 +196,18 @@ func (s *sedgeActions) ExportSlashingInterchangeData(options SlashingExportOptio "--data-path=/data", "--to=/data/slashing_protection.json", } + case "nimbus": + cmd = []string{ + "slashingdb", + "export", + "/data/slashing_protection.json", + "--validators-dir=/data", + } default: return fmt.Errorf("%w: %s", ErrUnsupportedValidatorClient, options.ValidatorClient) } log.Infof("Exporting slashing data from client %s", options.ValidatorClient) - if err := runSlashingContainer(s.dockerClient, s.dockerServiceManager, cmd, validatorContainerName, slashingContainerName); err != nil { + if err := runSlashingContainer(s.dockerClient, s.dockerServiceManager, cmd, validatorContainerName, consensusContainerName, slashingContainerName, options.ValidatorClient == "nimbus"); err != nil { return err } copyFrom := filepath.Join(options.GenerationPath, configs.ValidatorDir, "slashing_protection.json") @@ -212,16 +228,25 @@ func (s *sedgeActions) ExportSlashingInterchangeData(options SlashingExportOptio } func runSlashingContainer(dockerClient client.APIClient, dockerServiceManager DockerServiceManager, cmd []string, - validatorContainerName string, slashingContainerName string, + validatorContainerName string, consensusContainerName string, slashingContainerName string, isNimbus bool, ) error { - validatorImage, err := dockerServiceManager.Image(validatorContainerName) - if err != nil { - return err + slashingImage := "" + var err error + if isNimbus { + slashingImage, err = dockerServiceManager.Image(consensusContainerName) + if err != nil { + return err + } + } else { + slashingImage, err = dockerServiceManager.Image(validatorContainerName) + if err != nil { + return err + } } log.Debugf("Creating %s container", services.ServiceCtSlashingData) ct, err := dockerClient.ContainerCreate(context.Background(), &container.Config{ - Image: validatorImage, + Image: slashingImage, Cmd: cmd, }, &container.HostConfig{ diff --git a/cli/importKeys.go b/cli/importKeys.go index 0d9c293ba..44102a303 100644 --- a/cli/importKeys.go +++ b/cli/importKeys.go @@ -87,10 +87,18 @@ the importation.`, DeployBlockPath: customDeployBlock, }, } - err := sedgeActions.SetupContainers(actions.SetupContainersOptions{ - GenerationPath: generationPath, - Services: []string{validator}, - }) + var err error + if validatorClient == "nimbus" { + err = sedgeActions.SetupContainers(actions.SetupContainersOptions{ + GenerationPath: generationPath, + Services: []string{validator, consensus}, + }) + } else { + err = sedgeActions.SetupContainers(actions.SetupContainersOptions{ + GenerationPath: generationPath, + Services: []string{validator}, + }) + } if err != nil { return err } diff --git a/cli/slashingExport_test.go b/cli/slashingExport_test.go index 3e2f40d45..5925018ad 100644 --- a/cli/slashingExport_test.go +++ b/cli/slashingExport_test.go @@ -195,6 +195,18 @@ func TestSlashingExport_Params(t *testing.T) { Out: filepath.Join(outDir, "file.json"), }, }, + { + name: "nimbus flag", + args: []string{"nimbus"}, + actionOptions: actions.SlashingExportOptions{ + ValidatorClient: "nimbus", + Network: "mainnet", + StopValidator: false, + StartValidator: false, + GenerationPath: configs.DefaultAbsSedgeDataPath, + Out: filepath.Join(configs.DefaultAbsSedgeDataPath, "slashing_protection.json"), + }, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { diff --git a/cli/slashingImport.go b/cli/slashingImport.go index 770ea53e4..cc52b206c 100644 --- a/cli/slashingImport.go +++ b/cli/slashingImport.go @@ -92,9 +92,13 @@ sedge slashing-import --from slashing-data.json --start-validator lighthouse`, return sedgeActions.ValidateDockerComposeFile(filepath.Join(generationPath, configs.DefaultDockerComposeScriptName)) }, RunE: func(cmd *cobra.Command, args []string) error { + services := []string{validator} + if validatorClient == "nimbus" { + services = append(services, consensus) + } err := sedgeActions.SetupContainers(actions.SetupContainersOptions{ GenerationPath: generationPath, - Services: []string{validator}, + Services: services, }) if err != nil { return err diff --git a/cli/slashingImport_test.go b/cli/slashingImport_test.go index ca028448a..0fc48e43f 100644 --- a/cli/slashingImport_test.go +++ b/cli/slashingImport_test.go @@ -183,6 +183,18 @@ func TestSlashingImport_Params(t *testing.T) { }, from: true, }, + { + name: "nimbus", + args: []string{"nimbus"}, + actionOptions: actions.SlashingImportOptions{ + ValidatorClient: "nimbus", + Network: "mainnet", + StopValidator: false, + StartValidator: false, + GenerationPath: configs.DefaultAbsSedgeDataPath, + }, + from: true, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { @@ -209,6 +221,12 @@ func TestSlashingImport_Params(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() + services := []string{"validator"} + + if tt.actionOptions.ValidatorClient == "nimbus" { + services = append(services, "consensus") + } + mockActions := sedge_mocks.NewMockSedgeActions(ctrl) depsMgr := sedge_mocks.NewMockDependenciesManager(ctrl) gomock.InOrder( @@ -218,7 +236,7 @@ func TestSlashingImport_Params(t *testing.T) { mockActions.EXPECT().ValidateDockerComposeFile(filepath.Join(tt.actionOptions.GenerationPath, "docker-compose.yml")).Return(nil).Times(1), mockActions.EXPECT().SetupContainers(actions.SetupContainersOptions{ GenerationPath: tt.actionOptions.GenerationPath, - Services: []string{"validator"}, + Services: services, }).Return(nil).Times(1), mockActions.EXPECT().ImportSlashingInterchangeData(tt.actionOptions).Times(1), ) @@ -254,6 +272,11 @@ func TestSlashingImport_Errors(t *testing.T) { run: true, err: errors.New("action error"), }, + { + name: "nimbus invalid network", + args: []string{"nimbus", "--network", "invalid_network"}, + err: errors.New("invalid network: invalid_network"), + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { diff --git a/configs/client_images.yaml b/configs/client_images.yaml index 02de5d200..bc651fb16 100644 --- a/configs/client_images.yaml +++ b/configs/client_images.yaml @@ -24,6 +24,9 @@ consensus: prysm: name: gcr.io/prysmaticlabs/prysm/beacon-chain version: v5.1.0 + nimbus: + name: statusim/nimbus-eth2 + version: multiarch-v24.8.0 validator: lighthouse: name: sigp/lighthouse @@ -37,6 +40,9 @@ validator: prysm: name: gcr.io/prysmaticlabs/prysm/validator version: v5.1.0 + nimbus: + name: statusim/nimbus-validator-client + version: multiarch-v24.8.0 optimism: optimism: name: us-docker.pkg.dev/oplabs-tools-artifacts/images/op-node diff --git a/configs/images.go b/configs/images.go index dcb2ac93e..621339569 100644 --- a/configs/images.go +++ b/configs/images.go @@ -29,12 +29,14 @@ var ClientImages struct { Lodestar Image `yaml:"lodestar"` Teku Image `yaml:"teku"` Prysm Image `yaml:"prysm"` + Nimbus Image `yaml:"nimbus"` } Validator struct { Lighthouse Image `yaml:"lighthouse"` Lodestar Image `yaml:"lodestar"` Teku Image `yaml:"teku"` Prysm Image `yaml:"prysm"` + Nimbus Image `yaml:"nimbus"` } Optimism struct { OpNode Image `yaml:"optimism"` diff --git a/docs/docs/commands/clients.mdx b/docs/docs/commands/clients.mdx index 4fa85fc04..773e08691 100644 --- a/docs/docs/commands/clients.mdx +++ b/docs/docs/commands/clients.mdx @@ -41,6 +41,7 @@ $ sedge clients 1 │nethermind │lighthouse │lighthouse 2 │- │teku │teku 3 │- │lodestar │lodestar + 4 │- │nimbus │nimbus 2024-08-21 12:17:08 -- [INFO] Listing supported clients for network custom @@ -64,6 +65,7 @@ $ sedge clients 1 │nethermind │lighthouse │lighthouse 2 │erigon │teku │teku 3 │- │lodestar │lodestar + 4 │- │nimbus │nimbus 2024-08-21 12:17:08 -- [INFO] Listing supported clients for network holesky @@ -76,6 +78,7 @@ $ sedge clients 2 │geth │prysm │prysm 3 │erigon │teku │teku 4 │besu │lodestar │lodestar + 5 │- │nimbus │nimbus 2024-08-21 12:17:08 -- [INFO] Listing supported clients for network mainnet @@ -88,6 +91,7 @@ $ sedge clients 2 │geth │prysm │prysm 3 │erigon │teku │teku 4 │besu │lodestar │lodestar + 5 │- │nimbus │nimbus 2024-08-21 12:17:08 -- [INFO] Listing supported clients for network sepolia @@ -100,5 +104,6 @@ $ sedge clients 2 │geth │prysm │prysm 3 │erigon │teku │teku 4 │besu │lodestar │lodestar + 5 │- │nimbus │nimbus ``` \ No newline at end of file diff --git a/docs/docs/commands/importKey.mdx b/docs/docs/commands/importKey.mdx index 44e6afb74..5e503a029 100644 --- a/docs/docs/commands/importKey.mdx +++ b/docs/docs/commands/importKey.mdx @@ -114,6 +114,12 @@ The resulted folder structure is: └── validator-data/ ``` +:::note + +If you have to import your keys to nimbus, you will need to manually type the password when loading the keys, since the nimbus client does not support non interactive password loading. + +::: + Notice the new folder `keystore` inside the `sedge-data` folder, this is where the validator keys are copied to. Also notice the new folder `validator-data`, this is where the validator client stores its data. diff --git a/docs/docs/networks/chiado.mdx b/docs/docs/networks/chiado.mdx index a2040c1e4..b0e724c8c 100644 --- a/docs/docs/networks/chiado.mdx +++ b/docs/docs/networks/chiado.mdx @@ -15,11 +15,13 @@ decentralized prediction markets. - [Lighthouse](https://lighthouse-book.sigmaprime.io/) - [Lodestar](https://chainsafe.github.io/lodestar/) - [Teku](https://docs.teku.consensys.net/en/latest/) +- [Nimbus](https://nimbus.guide/) ## Supported Validator Clients - [Lighthouse](https://lighthouse-book.sigmaprime.io/) - [Lodestar](https://chainsafe.github.io/lodestar/) - [Teku](https://docs.teku.consensys.net/en/latest/) +- [Nimbus](https://nimbus.guide/) ## Run a Validator or a Full Node diff --git a/docs/docs/networks/gnosis.mdx b/docs/docs/networks/gnosis.mdx index 5b7606b2c..b90485329 100644 --- a/docs/docs/networks/gnosis.mdx +++ b/docs/docs/networks/gnosis.mdx @@ -15,11 +15,13 @@ It is a permissionless network governed by the Gnosis community. - [Lighthouse](https://lighthouse-book.sigmaprime.io/) - [Lodestar](https://chainsafe.github.io/lodestar/) - [Teku](https://docs.teku.consensys.net/en/latest/) +- [Nimbus](https://nimbus.guide/) ## Supported Validator Clients - [Lighthouse](https://lighthouse-book.sigmaprime.io/) - [Lodestar](https://chainsafe.github.io/lodestar/) - [Teku](https://docs.teku.consensys.net/en/latest/) +- [Nimbus](https://nimbus.guide/) ## Run a Validator or Full Node diff --git a/docs/docs/networks/holesky.mdx b/docs/docs/networks/holesky.mdx index 51e732dc4..66166b5e2 100644 --- a/docs/docs/networks/holesky.mdx +++ b/docs/docs/networks/holesky.mdx @@ -18,12 +18,14 @@ Holesky is Ethereum's public testnet that serves as a technical experimentation - [Lodestar](https://chainsafe.github.io/lodestar/) - [Prysm](https://docs.prylabs.network/docs/getting-started/) - [Teku](https://docs.teku.consensys.net/en/latest/) +- [Nimbus](https://nimbus.guide/) ## Supported Validator Clients - [Lighthouse](https://lighthouse-book.sigmaprime.io/) - [Lodestar](https://chainsafe.github.io/lodestar/) - [Prysm](https://docs.prylabs.network/docs/getting-started/) - [Teku](https://docs.teku.consensys.net/en/latest/) +- [Nimbus](https://nimbus.guide/) ## MEV-Boost diff --git a/docs/docs/networks/mainnet.mdx b/docs/docs/networks/mainnet.mdx index e12e0e19c..a8b87529b 100644 --- a/docs/docs/networks/mainnet.mdx +++ b/docs/docs/networks/mainnet.mdx @@ -20,12 +20,14 @@ secured by the Proof-of-Stake (PoS) algorithm. - [Lodestar](https://chainsafe.github.io/lodestar/) - [Prysm](https://docs.prylabs.network/docs/getting-started/) - [Teku](https://docs.teku.consensys.net/en/latest/) +- [Nimbus](https://nimbus.guide/) ## Supported Validator Clients - [Lighthouse](https://lighthouse-book.sigmaprime.io/) - [Lodestar](https://chainsafe.github.io/lodestar/) - [Prysm](https://docs.prylabs.network/docs/getting-started/) - [Teku](https://docs.teku.consensys.net/en/latest/) +- [Nimbus](https://nimbus.guide/) ## MEV-Boost diff --git a/docs/docs/networks/sepolia.mdx b/docs/docs/networks/sepolia.mdx index c60fbb91b..6996e1ce0 100644 --- a/docs/docs/networks/sepolia.mdx +++ b/docs/docs/networks/sepolia.mdx @@ -18,12 +18,14 @@ Sepolia is a permissioned Ethereum test network. It is one of the testnets that - [Lodestar](https://chainsafe.github.io/lodestar/) - [Prysm](https://docs.prylabs.network/docs/getting-started/) - [Teku](https://docs.teku.consensys.net/en/latest/) +- [Nimbus](https://nimbus.guide/) ## Supported Validator Clients - [Lighthouse](https://lighthouse-book.sigmaprime.io/) - [Lodestar](https://chainsafe.github.io/lodestar/) - [Prysm](https://docs.prylabs.network/docs/getting-started/) - [Teku](https://docs.teku.consensys.net/en/latest/) +- [Nimbus](https://nimbus.guide/) ## MEV-Boost diff --git a/go.mod b/go.mod index 3ce089b9f..ff4854551 100644 --- a/go.mod +++ b/go.mod @@ -41,6 +41,7 @@ require ( github.com/tyler-smith/go-bip39 v1.1.0 github.com/wealdtech/go-eth2-types/v2 v2.8.0 github.com/wealdtech/go-eth2-util v1.8.0 + golang.org/x/crypto v0.25.0 golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa golang.org/x/sync v0.7.0 golang.org/x/sys v0.22.0 diff --git a/internal/pkg/clients/clients_test.go b/internal/pkg/clients/clients_test.go index ddb40428b..f7879091f 100644 --- a/internal/pkg/clients/clients_test.go +++ b/internal/pkg/clients/clients_test.go @@ -91,8 +91,8 @@ func TestClients(t *testing.T) { inputs := [...]clientsTestCase{ { map[string][]string{ - "consensus": {"lighthouse", "prysm", "teku", "lodestar"}, - "validator": {"lighthouse", "prysm", "teku", "lodestar"}, + "consensus": {"lighthouse", "prysm", "teku", "lodestar", "nimbus"}, + "validator": {"lighthouse", "prysm", "teku", "lodestar", "nimbus"}, "execution": {"nethermind", "geth", "besu", "erigon"}, }, []string{"consensus"}, @@ -111,7 +111,7 @@ func TestClients(t *testing.T) { }, { map[string][]string{ - "validator": {"lighthouse", "prysm", "teku", "lodestar"}, + "validator": {"lighthouse", "prysm", "teku", "lodestar", "nimbus"}, "execution": {"nethermind", "geth", "besu", "erigon"}, }, []string{"execution", "validator"}, @@ -120,8 +120,8 @@ func TestClients(t *testing.T) { }, { map[string][]string{ - "validator": {"lighthouse", "prysm", "teku", "lodestar"}, - "consensus": {"lighthouse", "prysm", "teku", "lodestar"}, + "validator": {"lighthouse", "prysm", "teku", "lodestar", "nimbus"}, + "consensus": {"lighthouse", "prysm", "teku", "lodestar", "nimbus"}, "execution": {"nethermind", "geth", "besu", "erigon"}, }, []string{"consensus", "other"}, @@ -130,8 +130,8 @@ func TestClients(t *testing.T) { }, { map[string][]string{ - "validator": {"lighthouse", "teku", "lodestar"}, - "consensus": {"lighthouse", "teku", "lodestar"}, + "validator": {"lighthouse", "teku", "lodestar", "nimbus"}, + "consensus": {"lighthouse", "teku", "lodestar", "nimbus"}, "execution": {"nethermind", "erigon"}, }, []string{"consensus", "execution", "validator"}, diff --git a/internal/pkg/clients/init.go b/internal/pkg/clients/init.go index 2eaf1cc4d..7c43e4d97 100644 --- a/internal/pkg/clients/init.go +++ b/internal/pkg/clients/init.go @@ -27,12 +27,14 @@ var AllClients map[string][]string = map[string][]string{ "prysm", "teku", "lodestar", + "nimbus", }, "validator": { "lighthouse", "prysm", "teku", "lodestar", + "nimbus", }, "optimism": { "optimism", diff --git a/internal/pkg/clients/types.go b/internal/pkg/clients/types.go index 19f5116ca..be7e9a921 100644 --- a/internal/pkg/clients/types.go +++ b/internal/pkg/clients/types.go @@ -62,6 +62,8 @@ func (c *Client) setConsensusImage(image string) { c.Image = valueOrDefault(image, configs.ClientImages.Consensus.Teku.String()) case "lodestar": c.Image = valueOrDefault(image, configs.ClientImages.Consensus.Lodestar.String()) + case "nimbus": + c.Image = valueOrDefault(image, configs.ClientImages.Consensus.Nimbus.String()) } } @@ -75,6 +77,8 @@ func (c *Client) setValidatorImage(image string) { c.Image = valueOrDefault(image, configs.ClientImages.Validator.Teku.String()) case "lodestar": c.Image = valueOrDefault(image, configs.ClientImages.Validator.Lodestar.String()) + case "nimbus": + c.Image = valueOrDefault(image, configs.ClientImages.Validator.Nimbus.String()) } } diff --git a/internal/pkg/generate/types.go b/internal/pkg/generate/types.go index 02063695a..72d5ddf8e 100644 --- a/internal/pkg/generate/types.go +++ b/internal/pkg/generate/types.go @@ -219,6 +219,18 @@ type Mevboost struct { Restart string `yaml:"restart"` Entrypoint []string `yaml:"entrypoint"` } +type ConsensusSync struct { + StopGracePeriod string `yaml:"stop_grace_period"` + ContainerName string `yaml:"container_name"` + Restart string `yaml:"restart"` + Image string `yaml:"image"` + Networks []string `yaml:"networks"` + Volumes []string `yaml:"volumes"` + Ports []string `yaml:"ports"` + Expose []int `yaml:"expose"` + Command []string `yaml:"command"` + Logging *Logging `yaml:"logging,omitempty"` +} type Consensus struct { StopGracePeriod string `yaml:"stop_grace_period"` ContainerName string `yaml:"container_name"` @@ -263,6 +275,7 @@ type Services struct { Execution *Execution `yaml:"execution,omitempty"` Mevboost *Mevboost `yaml:"mev-boost,omitempty"` Consensus *Consensus `yaml:"consensus,omitempty"` + ConsensusSync *ConsensusSync `yaml:"consensus-sync,omitempty"` ValidatorBlocker *ValidatorBlocker `yaml:"validator-blocker,omitempty"` Validator *Validator `yaml:"validator,omitempty"` ConfigConsensus *ConfigConsensus `yaml:"config_consensus,omitempty"` diff --git a/templates/envs/chiado/consensus/nimbus.tmpl b/templates/envs/chiado/consensus/nimbus.tmpl new file mode 100644 index 000000000..74890c2ea --- /dev/null +++ b/templates/envs/chiado/consensus/nimbus.tmpl @@ -0,0 +1,14 @@ +{{/* nimbus.tmpl */}} +{{ define "consensus" }} +# --- Consensus Layer - Beacon Node - configuration --- +CC_PEER_COUNT=50 +CC_LOG_LEVEL=info +EC_API_URL={{.ExecutionApiURL}} +EC_AUTH_URL={{.ExecutionAuthURL}} +CC_INSTANCE_NAME=Nimbus +CC_IMAGE_VERSION={{.CcImage}} +CC_DATA_DIR={{.CcDataDir}} +CC_JWT_SECRET_PATH={{.JWTSecretPath}} +CC_FEE_RECIPIENT={{.FeeRecipient}} +{{if .CheckpointSyncUrl}}CHECKPOINT_SYNC_URL={{.CheckpointSyncUrl}}{{end}} +{{ end }} \ No newline at end of file diff --git a/templates/envs/chiado/validator/nimbus.tmpl b/templates/envs/chiado/validator/nimbus.tmpl new file mode 100644 index 000000000..a960061ec --- /dev/null +++ b/templates/envs/chiado/validator/nimbus.tmpl @@ -0,0 +1,12 @@ +{{/* nimbus.tmpl */}} +{{ define "validator" }} +# --- Consensus Layer - Validator Node - configuration --- +CC_API_URL={{.ConsensusApiURL}} +CC_ADD_API_URL={{.ConsensusAdditionalApiURL}} +GRAFFITI={{.Graffiti}} +VL_LOG_LEVEL=info +VL_IMAGE_VERSION={{.VlImage}} +KEYSTORE_DIR={{.KeystoreDir}} +WALLET_DIR=./wallet +VL_DATA_DIR={{.VlDataDir}} +{{ end }} diff --git a/templates/envs/gnosis/consensus/nimbus.tmpl b/templates/envs/gnosis/consensus/nimbus.tmpl new file mode 100644 index 000000000..74890c2ea --- /dev/null +++ b/templates/envs/gnosis/consensus/nimbus.tmpl @@ -0,0 +1,14 @@ +{{/* nimbus.tmpl */}} +{{ define "consensus" }} +# --- Consensus Layer - Beacon Node - configuration --- +CC_PEER_COUNT=50 +CC_LOG_LEVEL=info +EC_API_URL={{.ExecutionApiURL}} +EC_AUTH_URL={{.ExecutionAuthURL}} +CC_INSTANCE_NAME=Nimbus +CC_IMAGE_VERSION={{.CcImage}} +CC_DATA_DIR={{.CcDataDir}} +CC_JWT_SECRET_PATH={{.JWTSecretPath}} +CC_FEE_RECIPIENT={{.FeeRecipient}} +{{if .CheckpointSyncUrl}}CHECKPOINT_SYNC_URL={{.CheckpointSyncUrl}}{{end}} +{{ end }} \ No newline at end of file diff --git a/templates/envs/gnosis/validator/nimbus.tmpl b/templates/envs/gnosis/validator/nimbus.tmpl new file mode 100644 index 000000000..a960061ec --- /dev/null +++ b/templates/envs/gnosis/validator/nimbus.tmpl @@ -0,0 +1,12 @@ +{{/* nimbus.tmpl */}} +{{ define "validator" }} +# --- Consensus Layer - Validator Node - configuration --- +CC_API_URL={{.ConsensusApiURL}} +CC_ADD_API_URL={{.ConsensusAdditionalApiURL}} +GRAFFITI={{.Graffiti}} +VL_LOG_LEVEL=info +VL_IMAGE_VERSION={{.VlImage}} +KEYSTORE_DIR={{.KeystoreDir}} +WALLET_DIR=./wallet +VL_DATA_DIR={{.VlDataDir}} +{{ end }} diff --git a/templates/envs/holesky/consensus/nimbus.tmpl b/templates/envs/holesky/consensus/nimbus.tmpl new file mode 100644 index 000000000..74890c2ea --- /dev/null +++ b/templates/envs/holesky/consensus/nimbus.tmpl @@ -0,0 +1,14 @@ +{{/* nimbus.tmpl */}} +{{ define "consensus" }} +# --- Consensus Layer - Beacon Node - configuration --- +CC_PEER_COUNT=50 +CC_LOG_LEVEL=info +EC_API_URL={{.ExecutionApiURL}} +EC_AUTH_URL={{.ExecutionAuthURL}} +CC_INSTANCE_NAME=Nimbus +CC_IMAGE_VERSION={{.CcImage}} +CC_DATA_DIR={{.CcDataDir}} +CC_JWT_SECRET_PATH={{.JWTSecretPath}} +CC_FEE_RECIPIENT={{.FeeRecipient}} +{{if .CheckpointSyncUrl}}CHECKPOINT_SYNC_URL={{.CheckpointSyncUrl}}{{end}} +{{ end }} \ No newline at end of file diff --git a/templates/envs/holesky/validator/nimbus.tmpl b/templates/envs/holesky/validator/nimbus.tmpl new file mode 100644 index 000000000..12289e386 --- /dev/null +++ b/templates/envs/holesky/validator/nimbus.tmpl @@ -0,0 +1,13 @@ +{{/* nimbus.tmpl */}} +{{ define "validator" }} +# --- Consensus Layer - Validator Node - configuration --- +CC_API_URL={{.ConsensusApiURL}} +CC_ADD_API_URL={{.ConsensusAdditionalApiURL}} +GRAFFITI={{.Graffiti}} +VL_LOG_LEVEL=info +VL_IMAGE_VERSION={{.VlImage}} +KEYSTORE_DIR={{.KeystoreDir}} +WALLET_DIR=./wallet +VL_DATA_DIR={{.VlDataDir}} +MEV=true +{{ end }} diff --git a/templates/envs/mainnet/consensus/nimbus.tmpl b/templates/envs/mainnet/consensus/nimbus.tmpl new file mode 100644 index 000000000..74890c2ea --- /dev/null +++ b/templates/envs/mainnet/consensus/nimbus.tmpl @@ -0,0 +1,14 @@ +{{/* nimbus.tmpl */}} +{{ define "consensus" }} +# --- Consensus Layer - Beacon Node - configuration --- +CC_PEER_COUNT=50 +CC_LOG_LEVEL=info +EC_API_URL={{.ExecutionApiURL}} +EC_AUTH_URL={{.ExecutionAuthURL}} +CC_INSTANCE_NAME=Nimbus +CC_IMAGE_VERSION={{.CcImage}} +CC_DATA_DIR={{.CcDataDir}} +CC_JWT_SECRET_PATH={{.JWTSecretPath}} +CC_FEE_RECIPIENT={{.FeeRecipient}} +{{if .CheckpointSyncUrl}}CHECKPOINT_SYNC_URL={{.CheckpointSyncUrl}}{{end}} +{{ end }} \ No newline at end of file diff --git a/templates/envs/mainnet/validator/nimbus.tmpl b/templates/envs/mainnet/validator/nimbus.tmpl new file mode 100644 index 000000000..12289e386 --- /dev/null +++ b/templates/envs/mainnet/validator/nimbus.tmpl @@ -0,0 +1,13 @@ +{{/* nimbus.tmpl */}} +{{ define "validator" }} +# --- Consensus Layer - Validator Node - configuration --- +CC_API_URL={{.ConsensusApiURL}} +CC_ADD_API_URL={{.ConsensusAdditionalApiURL}} +GRAFFITI={{.Graffiti}} +VL_LOG_LEVEL=info +VL_IMAGE_VERSION={{.VlImage}} +KEYSTORE_DIR={{.KeystoreDir}} +WALLET_DIR=./wallet +VL_DATA_DIR={{.VlDataDir}} +MEV=true +{{ end }} diff --git a/templates/envs/sepolia/consensus/nimbus.tmpl b/templates/envs/sepolia/consensus/nimbus.tmpl new file mode 100644 index 000000000..74890c2ea --- /dev/null +++ b/templates/envs/sepolia/consensus/nimbus.tmpl @@ -0,0 +1,14 @@ +{{/* nimbus.tmpl */}} +{{ define "consensus" }} +# --- Consensus Layer - Beacon Node - configuration --- +CC_PEER_COUNT=50 +CC_LOG_LEVEL=info +EC_API_URL={{.ExecutionApiURL}} +EC_AUTH_URL={{.ExecutionAuthURL}} +CC_INSTANCE_NAME=Nimbus +CC_IMAGE_VERSION={{.CcImage}} +CC_DATA_DIR={{.CcDataDir}} +CC_JWT_SECRET_PATH={{.JWTSecretPath}} +CC_FEE_RECIPIENT={{.FeeRecipient}} +{{if .CheckpointSyncUrl}}CHECKPOINT_SYNC_URL={{.CheckpointSyncUrl}}{{end}} +{{ end }} \ No newline at end of file diff --git a/templates/envs/sepolia/validator/nimbus.tmpl b/templates/envs/sepolia/validator/nimbus.tmpl new file mode 100644 index 000000000..12289e386 --- /dev/null +++ b/templates/envs/sepolia/validator/nimbus.tmpl @@ -0,0 +1,13 @@ +{{/* nimbus.tmpl */}} +{{ define "validator" }} +# --- Consensus Layer - Validator Node - configuration --- +CC_API_URL={{.ConsensusApiURL}} +CC_ADD_API_URL={{.ConsensusAdditionalApiURL}} +GRAFFITI={{.Graffiti}} +VL_LOG_LEVEL=info +VL_IMAGE_VERSION={{.VlImage}} +KEYSTORE_DIR={{.KeystoreDir}} +WALLET_DIR=./wallet +VL_DATA_DIR={{.VlDataDir}} +MEV=true +{{ end }} diff --git a/templates/services/merge/consensus/nimbus.tmpl b/templates/services/merge/consensus/nimbus.tmpl new file mode 100644 index 000000000..9dd058a59 --- /dev/null +++ b/templates/services/merge/consensus/nimbus.tmpl @@ -0,0 +1,84 @@ +{{/* nimbus.tmpl */}} +{{ define "consensus" }}{{if or .ClCheckpointSyncUrl .CheckpointSyncUrl}} + consensus-sync: + user: "root:root" + stop_grace_period: 30s + container_name: sedge-consensus-trusted-sync{{if .ContainerTag}}-{{.ContainerTag}}{{end}} + image: ${CC_IMAGE_VERSION} + networks: + - sedge + volumes: + - ${CC_DATA_DIR}:/var/lib/nimbus + - ${CC_JWT_SECRET_PATH}:/tmp/jwt/jwtsecret{{if .CustomConsensusConfigs}}{{if .CustomNetworkConfigPath}} + - {{.CustomNetworkConfigPath}}:/network_config/config.yaml{{end}}{{if .CustomGenesisPath}} + - {{.CustomGenesisPath}}:/network_config/genesis.ssz{{end}}{{if .CustomDeployBlockPath}} + - {{.CustomDeployBlockPath}}:/network_config/deploy_block.txt{{end}}{{end}} + command: + - trustedNodeSync + - --network:{{if .SplittedNetwork}}${CL_NETWORK}{{else}}${NETWORK}{{end}} + - --data-dir=/var/lib/nimbus + - --trusted-node-url={{if .CheckpointSyncUrl}}{{ .CheckpointSyncUrl }}{{else}}${CHECKPOINT_SYNC_URL}{{end}}{{if .LoggingDriver}} + logging: + driver: "{{.LoggingDriver}}"{{if eq .LoggingDriver "json-file"}} + options: + max-size: "10m" + max-file: "10"{{end}}{{end}}{{end}} + + consensus: + user: "root:root" + stop_grace_period: 30s + container_name: sedge-consensus-client{{if .ContainerTag}}-{{.ContainerTag}}{{end}} + restart: unless-stopped + image: ${CC_IMAGE_VERSION}{{if or .ClCheckpointSyncUrl .CheckpointSyncUrl .Mev}} + depends_on:{{if .Mev}} + mev-boost: + condition: service_started{{end}}{{if or .ClCheckpointSyncUrl .CheckpointSyncUrl}} + consensus-sync: + condition: service_completed_successfully{{end}}{{end}} + networks: + - sedge + volumes: + - ${CC_DATA_DIR}:/var/lib/nimbus + - ${CC_JWT_SECRET_PATH}:/tmp/jwt/jwtsecret{{if .CustomConsensusConfigs}}{{if .CustomNetworkConfigPath}} + - {{.CustomNetworkConfigPath}}:/network_config/config.yaml{{end}}{{if .CustomGenesisPath}} + - {{.CustomGenesisPath}}:/network_config/genesis.ssz{{end}}{{if .CustomDeployBlockPath}} + - {{.CustomDeployBlockPath}}:/network_config/deploy_block.txt{{end}}{{end}} + ports: + - "{{.ClDiscoveryPort}}:{{.ClDiscoveryPort}}/tcp" + - "{{.ClDiscoveryPort}}:{{.ClDiscoveryPort}}/udp" + - "{{.ClMetricsPort}}:{{.ClMetricsPort}}/tcp"{{if .MapAllPorts}} + - "{{.ClApiPort}}:{{.ClApiPort}}"{{end}} + expose: + - {{.ClApiPort}} + command: + - --non-interactive=true + - --network={{if .SplittedNetwork}}${CL_NETWORK}{{else}}${NETWORK}{{end}} + - --max-peers=${CC_PEER_COUNT} + - --data-dir=/var/lib/nimbus + - --web3-url=${EC_AUTH_URL}{{range $url := .FallbackELUrls}} --web3-url={{$url}}{{end}} + - --bootstrap-node={{.CCBootnodes}} + - --udp-port={{.ClDiscoveryPort}} + - --tcp-port={{.ClDiscoveryPort}} + - --listen-address=0.0.0.0 + - --enr-auto-update=false + - --subscribe-all-subnets + - --rest + - --rest-port={{.ClApiPort}} + - --rest-address=0.0.0.0{{if .FeeRecipient}} + - --suggested-fee-recipient=${CC_FEE_RECIPIENT}{{end}} + - --metrics + - --metrics-port={{.ClMetricsPort}} + - --metrics-address=0.0.0.0 + - --doppelganger-detection=off + - --jwt-secret=/tmp/jwt/jwtsecret + - --log-level=${CC_LOG_LEVEL} + - --dump:on{{range $flag := .ClExtraFlags}} + - --{{$flag}}{{end}}{{if .MevBoostEndpoint}} + - --payload-builder + - --payload-builder-url={{.MevBoostEndpoint}}{{end}}{{if .LoggingDriver}} + logging: + driver: "{{.LoggingDriver}}"{{if eq .LoggingDriver "json-file"}} + options: + max-size: "10m" + max-file: "10"{{end}}{{end}} +{{ end }} \ No newline at end of file diff --git a/templates/services/merge/validator/nimbus.tmpl b/templates/services/merge/validator/nimbus.tmpl new file mode 100644 index 000000000..aacd40b9a --- /dev/null +++ b/templates/services/merge/validator/nimbus.tmpl @@ -0,0 +1,32 @@ +{{/* nimbus.tmpl */}} +{{ define "validator" }} + validator: + container_name: sedge-validator-client{{if .ContainerTag}}-{{.ContainerTag}}{{end}} + image: ${VL_IMAGE_VERSION} + depends_on: + validator-blocker: + condition: service_completed_successfully + networks: + - sedge + ports: + - "{{.VlMetricsPort}}:{{.VlMetricsPort}}" + volumes: + - ${VL_DATA_DIR}:/data{{if .CustomConsensusConfigs}}{{if .CustomNetworkConfigPath}} + - {{.CustomNetworkConfigPath}}:/network_config/config.yml{{end}}{{end}} + command: + - --data-dir=/data + - --log-file=/data/logs/validator.log + - --log-level=${VL_LOG_LEVEL} + - --beacon-node=${CC_API_URL} + - --metrics=true + - --metrics-address=0.0.0.0 + - --metrics-port={{.VlMetricsPort}}{{with .FeeRecipient}} + - --suggested-fee-recipient=${FEE_RECIPIENT}{{end}}{{range $flag := .VlExtraFlags}} + - --{{$flag}}{{end}} + - --graffiti=${GRAFFITI}{{if .LoggingDriver}} + logging: + driver: "{{.LoggingDriver}}"{{if eq .LoggingDriver "json-file"}} + options: + max-size: "10m" + max-file: "10"{{end}}{{end}} +{{ end }}