Skip to content

Commit 391fdba

Browse files
khalifaa55AntiD2ta
andauthored
fix: Remove cli commands bugs (#511)
* fix: Keys command for hoodi * fix: Update validator-blocker script * test(config): improve NetworkCheck unit tests * fix: Update lido-status command * fix: withdrawal credentials on cli * fix: nimbus keys import containers * chore: Update changelog * fix: add consensus setup for nimbus keys * fix: Update nimbus cli test * fix: withdrawal address for eth nodes --------- Co-authored-by: AntiD2ta <mtpotro41@gmail.com>
1 parent 2e56a01 commit 391fdba

File tree

9 files changed

+135
-49
lines changed

9 files changed

+135
-49
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
## Fixed
11+
- Fix `keys` command for `hoodi` network.
12+
- Update `lido-status` command links.
13+
- Fix `withdrawal_credentials` for lido node.
14+
- Fix importing keys for `nimbus` client.
15+
1016
## [v1.9.0] - 2025-03-27
1117

1218
### Added

cli/cli.go

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -495,13 +495,14 @@ func generateKeystore(p ui.Prompter, o *CliCmdOptions, a actions.SedgeActions, d
495495
// TODO: Create an Action for keystore generation
496496
log.Info("Generating keystores...")
497497
data := keystores.ValidatorKeysGenData{
498-
Mnemonic: o.keystoreMnemonic,
499-
Passphrase: o.keystorePassphrase,
500-
OutputPath: o.keystorePath,
501-
MinIndex: uint64(o.existingValidators),
502-
MaxIndex: uint64(o.existingValidators) + uint64(o.numberOfValidators),
503-
NetworkName: o.genData.Network,
504-
ForkVersion: configs.NetworksConfigs()[o.genData.Network].GenesisForkVersion,
498+
Mnemonic: o.keystoreMnemonic,
499+
Passphrase: o.keystorePassphrase,
500+
OutputPath: o.keystorePath,
501+
MinIndex: uint64(o.existingValidators),
502+
MaxIndex: uint64(o.existingValidators) + uint64(o.numberOfValidators),
503+
NetworkName: o.genData.Network,
504+
ForkVersion: configs.NetworksConfigs()[o.genData.Network].GenesisForkVersion,
505+
WithdrawalAddress: o.withdrawalAddress[2:],
505506
// Constants
506507
UseUniquePassphrase: true,
507508
Insecure: false,
@@ -540,10 +541,17 @@ func generateKeystore(p ui.Prompter, o *CliCmdOptions, a actions.SedgeActions, d
540541
if err := checkCLIDependencies(p, o, a, depsMgr); err != nil {
541542
return err
542543
}
544+
545+
var services []string
546+
if o.genData.ValidatorClient.Name == "nimbus" {
547+
services = []string{validator, consensus}
548+
} else {
549+
services = []string{validator}
550+
}
543551
log.Info("Importing validator keys into the validator client...")
544552
err := a.SetupContainers(actions.SetupContainersOptions{
545553
GenerationPath: o.generationPath,
546-
Services: []string{validator},
554+
Services: services,
547555
})
548556
if err != nil {
549557
return err
@@ -1000,7 +1008,7 @@ func inputWithdrawalAddress(p ui.Prompter, o *CliCmdOptions) (err error) {
10001008
opts := sedgeOpts.CreateSedgeOptions(o.nodeSetup)
10011009
withdrawalAddress := opts.WithdrawalAddress(o.genData.Network)
10021010
if opts.OverwriteSettings().WithdrawalAddress {
1003-
o.withdrawalAddress = withdrawalAddress
1011+
o.withdrawalAddress = withdrawalAddress[2:]
10041012
return
10051013
}
10061014
o.withdrawalAddress, err = p.Input("Withdrawal address", "", false, func(s string) error { return ui.EthAddressValidator(s, true) })

cli/cli_test.go

Lines changed: 85 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ func TestCli(t *testing.T) {
126126
prompter.EXPECT().Select("Select keystore source", "", []string{SourceTypeCreate, SourceTypeExisting, SourceTypeSkip}).Return(0, nil),
127127
prompter.EXPECT().Select("Select mnemonic source", "", []string{SourceTypeCreate, SourceTypeExisting}).Return(0, nil),
128128
prompter.EXPECT().Select("Select passphrase source", "", []string{SourceTypeRandom, SourceTypeExisting, SourceTypeCreate}).Return(0, nil),
129-
prompter.EXPECT().Input("Withdrawal address", "", false, gomock.AssignableToTypeOf(func(s string) error { return ui.EthAddressValidator(s, true) })).Return("0x00000007abca72jmd83jd8u3jd9kdn32j38abc", nil),
129+
prompter.EXPECT().Input("Withdrawal address", "", false, gomock.AssignableToTypeOf(func(s string) error { return ui.EthAddressValidator(s, true) })).Return("0x2d07a21ebadde0c13e6b91022a7e5722eb6bf5d5", nil),
130130
prompter.EXPECT().InputInt64("Number of validators", int64(1)).Return(int64(1), nil),
131131
prompter.EXPECT().InputInt64("Existing validators. This number will be used as the initial index for the generated keystores.", int64(0)).Return(int64(0), nil),
132132
depsMgr.EXPECT().Check([]string{dependencies.Docker}).Return([]string{dependencies.Docker}, nil),
@@ -746,7 +746,7 @@ func TestCli(t *testing.T) {
746746
prompter.EXPECT().Select("Select keystore source", "", []string{SourceTypeCreate, SourceTypeExisting, SourceTypeSkip}).Return(0, nil),
747747
prompter.EXPECT().Select("Select mnemonic source", "", []string{SourceTypeCreate, SourceTypeExisting}).Return(0, nil),
748748
prompter.EXPECT().Select("Select passphrase source", "", []string{SourceTypeRandom, SourceTypeExisting, SourceTypeCreate}).Return(0, nil),
749-
prompter.EXPECT().Input("Withdrawal address", "", false, gomock.AssignableToTypeOf(func(s string) error { return ui.EthAddressValidator(s, true) })).Return("0x00000007abca72jmd83jd8u3jd9kdn32j38abc", nil),
749+
prompter.EXPECT().Input("Withdrawal address", "", false, gomock.AssignableToTypeOf(func(s string) error { return ui.EthAddressValidator(s, true) })).Return("0x2d07a21ebadde0c13e6b91022a7e5722eb6bf5d5", nil),
750750
prompter.EXPECT().InputInt64("Number of validators", int64(1)).Return(int64(1), nil),
751751
prompter.EXPECT().InputInt64("Existing validators. This number will be used as the initial index for the generated keystores.", int64(0)).Return(int64(0), nil),
752752
depsMgr.EXPECT().Check([]string{dependencies.Docker}).Return([]string{dependencies.Docker}, nil),
@@ -847,6 +847,89 @@ func TestCli(t *testing.T) {
847847
)
848848
},
849849
},
850+
{
851+
name: "full node with nimbus validator mainnet",
852+
setup: func(t *testing.T, sedgeActions *sedge_mocks.MockSedgeActions, prompter *sedge_mocks.MockPrompter, depsMgr *sedge_mocks.MockDependenciesManager) {
853+
generationPath := t.TempDir()
854+
genData := generate.GenData{
855+
Services: []string{"execution", "consensus", "validator", "mev-boost"},
856+
ExecutionClient: &clients.Client{
857+
Name: "nethermind",
858+
Type: "execution",
859+
Image: configs.ClientImages.Execution.Nethermind.String(),
860+
},
861+
ConsensusClient: &clients.Client{
862+
Name: "nimbus",
863+
Type: "consensus",
864+
Image: configs.ClientImages.Consensus.Nimbus.String(),
865+
},
866+
ValidatorClient: &clients.Client{
867+
Name: "nimbus",
868+
Type: "validator",
869+
Image: configs.ClientImages.Validator.Nimbus.String(),
870+
},
871+
Network: "mainnet",
872+
CheckpointSyncUrl: "http://checkpoint.sync",
873+
FeeRecipient: "0x2d07a21ebadde0c13e6b91022a7e5722eb6bf5d5",
874+
MapAllPorts: true,
875+
Graffiti: "test graffiti",
876+
VLStartGracePeriod: 840,
877+
Mev: true,
878+
MevImage: "flashbots/mev-boost:latest",
879+
RelayURLs: configs.NetworksConfigs()[NetworkMainnet].RelayURLs,
880+
ContainerTag: "tag",
881+
JWTSecretPath: filepath.Join(generationPath, "jwtsecret"),
882+
}
883+
sedgeActions.EXPECT().GetCommandRunner().Return(&test.SimpleCMDRunner{})
884+
gomock.InOrder(
885+
prompter.EXPECT().Select("Select node setup", "", []string{sedgeOpts.EthereumNode, sedgeOpts.LidoNode}).Return(0, nil),
886+
prompter.EXPECT().Select("Select network", "", []string{NetworkMainnet, NetworkHoodi, NetworkHolesky, NetworkSepolia, NetworkGnosis, NetworkChiado}).Return(0, nil),
887+
prompter.EXPECT().Select("Select node type", "", []string{NodeTypeFullNode, NodeTypeExecution, NodeTypeConsensus, NodeTypeValidator}).Return(0, nil),
888+
prompter.EXPECT().Input("Generation path", configs.DefaultAbsSedgeDataPath, false, nil).Return(generationPath, nil),
889+
prompter.EXPECT().Input("Container tag, sedge will add to each container and the network, a suffix with the tag", "", false, nil).Return("tag", nil),
890+
prompter.EXPECT().Confirm("Do you want to set up a validator?", true).Return(true, nil),
891+
prompter.EXPECT().Confirm("Enable MEV Boost?", true).Return(true, nil),
892+
prompter.EXPECT().Input("Mev-Boost image", "flashbots/mev-boost:latest", false, nil).Return("flashbots/mev-boost:latest", nil),
893+
prompter.EXPECT().InputList("Insert relay URLs if you don't want to use the default values listed below", configs.NetworksConfigs()[NetworkMainnet].RelayURLs, gomock.AssignableToTypeOf(func([]string) error { return nil })).Return(configs.NetworksConfigs()[NetworkMainnet].RelayURLs, nil),
894+
prompter.EXPECT().Select("Select execution client", "", ETHClients["execution"]).Return(0, nil),
895+
prompter.EXPECT().Select("Select consensus client", "", ETHClients["consensus"]).Return(4, nil),
896+
prompter.EXPECT().Select("Select validator client", "", ETHClients["validator"]).Return(4, nil),
897+
prompter.EXPECT().InputInt64("Validator grace period. This is the number of epochs the validator will wait for security reasons before starting", int64(1)).Return(int64(2), nil),
898+
prompter.EXPECT().Input("Graffiti to be used by the validator (press enter to skip it)", "", false, gomock.AssignableToTypeOf(ui.GraffitiValidator)).Return("test graffiti", nil),
899+
prompter.EXPECT().InputURL("Checkpoint sync URL", configs.NetworksConfigs()[genData.Network].CheckpointSyncURL, false).Return("http://checkpoint.sync", nil),
900+
prompter.EXPECT().EthAddress("Please enter the Fee Recipient address", "", true).Return("0x2d07a21ebadde0c13e6b91022a7e5722eb6bf5d5", nil),
901+
prompter.EXPECT().Confirm("Do you want to expose all ports?", false).Return(true, nil),
902+
prompter.EXPECT().Select("Select JWT source", "", []string{SourceTypeCreate, SourceTypeExisting}).Return(0, nil),
903+
prompter.EXPECT().Confirm("Do you want to enable the monitoring stack?", false).Return(false, nil),
904+
sedgeActions.EXPECT().Generate(gomock.Eq(actions.GenerateOptions{
905+
GenerationPath: generationPath,
906+
GenerationData: genData,
907+
})).Return(genData, nil),
908+
prompter.EXPECT().Select("Select keystore source", "", []string{SourceTypeCreate, SourceTypeExisting, SourceTypeSkip}).Return(0, nil),
909+
prompter.EXPECT().Select("Select mnemonic source", "", []string{SourceTypeCreate, SourceTypeExisting}).Return(0, nil),
910+
prompter.EXPECT().Select("Select passphrase source", "", []string{SourceTypeRandom, SourceTypeExisting, SourceTypeCreate}).Return(0, nil),
911+
prompter.EXPECT().Input("Withdrawal address", "", false, gomock.AssignableToTypeOf(func(s string) error { return ui.EthAddressValidator(s, true) })).Return("0x2d07a21ebadde0c13e6b91022a7e5722eb6bf5d5", nil),
912+
prompter.EXPECT().InputInt64("Number of validators", int64(1)).Return(int64(1), nil),
913+
prompter.EXPECT().InputInt64("Existing validators. This number will be used as the initial index for the generated keystores.", int64(0)).Return(int64(0), nil),
914+
depsMgr.EXPECT().Check([]string{dependencies.Docker}).Return([]string{dependencies.Docker}, nil),
915+
depsMgr.EXPECT().DockerEngineIsOn().Return(nil),
916+
depsMgr.EXPECT().DockerComposeIsInstalled().Return(nil),
917+
sedgeActions.EXPECT().SetupContainers(actions.SetupContainersOptions{
918+
GenerationPath: generationPath,
919+
Services: []string{"validator", "consensus"},
920+
}),
921+
sedgeActions.EXPECT().ImportValidatorKeys(actions.ImportValidatorKeysOptions{
922+
ValidatorClient: "nimbus",
923+
Network: NetworkMainnet,
924+
GenerationPath: generationPath,
925+
From: filepath.Join(generationPath, "keystore"),
926+
ContainerTag: "tag",
927+
}).Return(nil),
928+
prompter.EXPECT().Confirm("Do you want to import slashing protection data?", false).Return(false, nil),
929+
prompter.EXPECT().Confirm("Run services now?", false).Return(false, nil),
930+
)
931+
},
932+
},
850933
}
851934

852935
for _, tt := range tests {

cli/keys.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -215,7 +215,7 @@ func KeysCmd(cmdRunner commands.CommandRunner, p ui.Prompter) *cobra.Command {
215215
}
216216
// Flag binds
217217
cmd.PersistentFlags().BoolVar(&flags.lidoNode, "lido", false, "Enable Lido CSM compatible keys. Similar to using --eth-withdrawal-address with the Lido Withdrawal Vault address.")
218-
cmd.Flags().StringVarP(&flags.network, "network", "n", "mainnet", "Target network. e.g. mainnet,sepolia, holesky, gnosis, chiado etc.")
218+
cmd.Flags().StringVarP(&flags.network, "network", "n", "mainnet", "Target network. e.g. mainnet, hoodi, sepolia, holesky, gnosis, chiado etc.")
219219
cmd.Flags().StringVarP(&flags.path, "path", "p", configs.DefaultAbsSedgeDataPath, "Absolute path to keystore folder. e.g. /home/user/keystore")
220220
cmd.Flags().StringVar(&flags.ethWithdrawalAddress, "eth-withdrawal-address", "", "If this field is set and valid, the given Eth address will be used to create the withdrawal credentials. Otherwise, it will generate withdrawal credentials with the mnemonic-derived withdrawal public key in EIP-2334 format.")
221221
cmd.Flags().StringVar(&flags.mnemonicPath, "mnemonic-path", "", "Path to file with a existing mnemonic to use.")

cli/lidoStatus.go

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ func LidoStatusCmd() *cobra.Command {
6161
cmd := &cobra.Command{
6262
Use: "lido-status [flags] [args]",
6363
Short: "Display status and information of Lido Node Operator",
64-
Long: `This command retrieves and displays the status and detailed information of Lido Node Operators.
64+
Long: `This command retrieves and displays the status and detailed information of Lido Node Operators.
6565
6666
This information includes:
6767
- Node Operator ID.
@@ -95,7 +95,7 @@ Valid args: reward address of Node Operator (rewards recipient)`,
9595
return nil
9696
},
9797
}
98-
cmd.Flags().StringVarP(&networkName, "network", "n", "holesky", "Target network. e.g. holesky, mainnet etc.")
98+
cmd.Flags().StringVarP(&networkName, "network", "n", "holesky", "Target network. e.g. holesky, mainnet, hoodi etc.")
9999
cmd.Flags().BoolVar(&longDescriptions, "l", false, "Show detailed descriptions for each value")
100100
cmd.Flags().Int64VarP(&nodeIDInt, "nodeID", "i", -1, "Your Node Operator ID (optional)")
101101
cmd.Flags().SortFlags = false
@@ -213,44 +213,45 @@ func buildLidoData(node *lidoData) map[string]struct {
213213
} {
214214
var nodeOpDetailed, keysDetailed, queueDetailed, bondDetailed, rewardsDetailed string
215215
var currentBond, requiredBond, excessBond, missedBond, rewards decimal.Decimal
216-
rewardAddressLink := fmt.Sprintf(`https://etherscan.io/address/%s`, node.nodeInfo.RewardAddress)
216+
217217
var prefix string
218218
if networkName == "mainnet" {
219219
prefix = ""
220220
} else {
221221
prefix = networkName + "."
222222
}
223223
claimRewardsLink := fmt.Sprintf(`https://%setherscan.io/address/%s#writeProxyContract#F10`, prefix, contracts.DeployedAddresses(contracts.CSModule)[networkName])
224+
rewardAddressLink := fmt.Sprintf(`https://%setherscan.io/address/%s`, prefix, node.nodeInfo.RewardAddress)
224225

225226
detailedDescriptions := map[string]string{
226227
nodeOpInfo: `
227-
## Description
228+
## Description
228229
- Node Operator ID: Unique identifier for the node operator.
229230
- Reward Address: Address that is the ultimate recipient of the rewards
230231
- Manager Address: Address used to perform routine management operations regarding the CSM Node Operator.`,
231232

232233
keysInfo: `
233-
## Description
234+
## Description
234235
- Stuck Keys Count: Number of keys stuck in the system. A validator is considered to be "stuck" if it has not been exited timely following an exit signal from the protocol.
235236
- Refunded Keys Count: Number of keys that were refunded.
236237
- Exited Keys Count: Number of keys that have exited.
237238
- Deposited Keys Count: Number of keys currently deposited.
238239
- Depositable Keys Count: Number of keys eligible for deposits.`,
239240

240241
queueInfo: `
241-
## Description
242+
## Description
242243
- Keys in the deposit queue: Number of the depositable keys that are in the deposit queue.`,
243244

244245
bondInfo: `
245-
## Description
246+
## Description
246247
- Bond : a security collateral that Node Operators must submit before uploading validator keys into CSM. It covers possible losses caused by inappropriate actions on the Node Operator's side.
247248
- Current Bond: The current amount of bonded ETH.
248249
- Required Bond: The required amount of ETH to maintain.
249250
- Excess Bond: The amount of excess bond over the required amount.
250251
- Missed Bond: The amount of bond that is missing.`,
251252

252253
rewardsInfo: `
253-
## Description
254+
## Description
254255
- Non-claimed Rewards: The amount of rewards available for claiming.`,
255256
}
256257

configs/networks.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ var ErrInvalidNetwork = errors.New("invalid network")
3737

3838
func NetworkCheck(value string) error {
3939
switch value {
40-
case NetworkMainnet, NetworkSepolia, NetworkGnosis, NetworkChiado, NetworkHolesky, NetworkCustom:
40+
case NetworkMainnet, NetworkSepolia, NetworkGnosis, NetworkChiado, NetworkHolesky, NetworkHoodi, NetworkCustom:
4141
return nil
4242
default:
4343
return fmt.Errorf("%w: %s", ErrInvalidNetwork, value)

configs/networks_test.go

Lines changed: 13 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -8,46 +8,33 @@ func TestNetworkCheck(t *testing.T) {
88
network string
99
wantErr bool
1010
}{
11-
{
12-
name: "Valid network, mainnet",
13-
network: "mainnet",
14-
wantErr: false,
15-
},
1611
{
1712
name: "Invalid network, goerli",
1813
network: "goerli",
1914
wantErr: true,
2015
},
21-
{
22-
name: "Valid network, sepolia",
23-
network: "sepolia",
24-
wantErr: false,
25-
},
26-
{
27-
name: "Valid network, gnosis",
28-
network: "gnosis",
29-
wantErr: false,
30-
},
31-
{
32-
name: "Valid network, chiado",
33-
network: "chiado",
34-
wantErr: false,
35-
},
36-
{
37-
name: "Valid network, custom",
38-
network: "custom",
39-
wantErr: false,
40-
},
4116
{
4217
name: "Invalid network",
4318
network: "invalid",
4419
wantErr: true,
4520
},
4621
}
22+
23+
for _, network := range NetworkSupported() {
24+
tests = append(tests, struct {
25+
name string
26+
network string
27+
wantErr bool
28+
}{
29+
name: network,
30+
network: network,
31+
wantErr: false,
32+
})
33+
}
4734
for _, tt := range tests {
4835
t.Run(tt.name, func(t *testing.T) {
4936
if err := NetworkCheck(tt.network); (err != nil) != tt.wantErr {
50-
t.Errorf("NetworkCheck() error = %v, wantErr %v", err, tt.wantErr)
37+
t.Errorf("NetworkCheck(%s) error = %v, wantErr %v", tt.network, err, tt.wantErr)
5138
}
5239
})
5340
}

docs/src/components/SupportedNetworks/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,7 @@ export const SupportedNetworks = () => {
215215
{
216216
name: 'Testnets',
217217
networks: [
218+
{ name: 'Hoodi', link: '/docs/networks/hoodi' },
218219
{ name: 'Holesky', link: '/docs/networks/holesky' },
219220
{ name: 'Sepolia', link: '/docs/networks/sepolia' },
220221
{ name: 'Chiado', link: '/docs/networks/chiado' }

templates/services/validator-blocker.tmpl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@
1111
sleep {{ .VLStartGracePeriod }};
1212
echo 'Done';
1313
while true; do
14-
response=$$(wget -S ${CC_API_URL}/eth/v1/node/health -O /dev/null 2>&1 | grep -m 1 "HTTP/" | awk '{print $$2}')
15-
if [ $$response -eq 200 ]; then
14+
response=$(wget -S ${CC_API_URL}/eth/v1/node/health -O /dev/null 2>&1 | grep -m 1 'HTTP/' | awk '{print $2}')
15+
if [ \"$response\" = \"200\" ]; then
1616
echo 'Endpoint is up!'
1717
break
1818
else

0 commit comments

Comments
 (0)