From 11260c2c546e02ee394fa9eee5946bd7f2fbadd1 Mon Sep 17 00:00:00 2001 From: Haneen Khalifa Date: Wed, 26 Jun 2024 13:38:28 +0300 Subject: [PATCH 01/31] feat: add method GetRelaysURI and adjust mevboostrelaylist package --- .../MEVBoostRelayAllowedList.abi | 0 .../MEVBoostRelayAllowedList.bin | 0 .../MEVBoostRelayAllowedList.go | 0 .../get_relays.go | 28 +++++++++++++++++-- .../get_relays_test.go | 0 .../relays.yaml | 0 6 files changed, 26 insertions(+), 2 deletions(-) rename internal/lido/contracts/{mevboost_relay_list => mevboostrelaylist}/MEVBoostRelayAllowedList.abi (100%) rename internal/lido/contracts/{mevboost_relay_list => mevboostrelaylist}/MEVBoostRelayAllowedList.bin (100%) rename internal/lido/contracts/{mevboost_relay_list => mevboostrelaylist}/MEVBoostRelayAllowedList.go (100%) rename internal/lido/contracts/{mevboost_relay_list => mevboostrelaylist}/get_relays.go (81%) rename internal/lido/contracts/{mevboost_relay_list => mevboostrelaylist}/get_relays_test.go (100%) rename internal/lido/contracts/{mevboost_relay_list => mevboostrelaylist}/relays.yaml (100%) diff --git a/internal/lido/contracts/mevboost_relay_list/MEVBoostRelayAllowedList.abi b/internal/lido/contracts/mevboostrelaylist/MEVBoostRelayAllowedList.abi similarity index 100% rename from internal/lido/contracts/mevboost_relay_list/MEVBoostRelayAllowedList.abi rename to internal/lido/contracts/mevboostrelaylist/MEVBoostRelayAllowedList.abi diff --git a/internal/lido/contracts/mevboost_relay_list/MEVBoostRelayAllowedList.bin b/internal/lido/contracts/mevboostrelaylist/MEVBoostRelayAllowedList.bin similarity index 100% rename from internal/lido/contracts/mevboost_relay_list/MEVBoostRelayAllowedList.bin rename to internal/lido/contracts/mevboostrelaylist/MEVBoostRelayAllowedList.bin diff --git a/internal/lido/contracts/mevboost_relay_list/MEVBoostRelayAllowedList.go b/internal/lido/contracts/mevboostrelaylist/MEVBoostRelayAllowedList.go similarity index 100% rename from internal/lido/contracts/mevboost_relay_list/MEVBoostRelayAllowedList.go rename to internal/lido/contracts/mevboostrelaylist/MEVBoostRelayAllowedList.go diff --git a/internal/lido/contracts/mevboost_relay_list/get_relays.go b/internal/lido/contracts/mevboostrelaylist/get_relays.go similarity index 81% rename from internal/lido/contracts/mevboost_relay_list/get_relays.go rename to internal/lido/contracts/mevboostrelaylist/get_relays.go index 396e486a..8fa8596a 100644 --- a/internal/lido/contracts/mevboost_relay_list/get_relays.go +++ b/internal/lido/contracts/mevboostrelaylist/get_relays.go @@ -19,7 +19,7 @@ type Relay struct { } // Define deployed contract addresses for Mainnet and Holesky -var deployedContractAddresses = map[string]string{ +var DeployedContractAddresses = map[string]string{ configs.NetworkMainnet: "0xF95f069F9AD107938F6ba802a3da87892298610E", configs.NetworkHolesky: "0x2d86C5855581194a386941806E38cA119E50aEA3", } @@ -94,7 +94,7 @@ func GetRelays(network string) ([]Relay, error) { Data string `json:"data"` } args := CallArgs{ - To: deployedContractAddresses[network], + To: DeployedContractAddresses[network], Data: "0x" + hex.EncodeToString(data), } @@ -118,3 +118,27 @@ func GetRelays(network string) ([]Relay, error) { return relays, nil } + +/* +GetRelaysURI : +This function is responsible for :- +retrieving a list of relays URI from the MEV-Boost Allowed List contract for a given network. +params :- +network (string): The name of the network (e.g., "mainnet", "holesky"). +returns :- +a. []string +List of relays URI +b. error +Error if any +*/ +func GetRelaysURI(network string) ([]string, error) { + relays, err := GetRelays(network) + if err != nil{ + return nil, err + } + relayURIs := []string{} + for _, relay := range relays { + relayURIs = append(relayURIs, relay.Uri) + } + return relayURIs, err +} diff --git a/internal/lido/contracts/mevboost_relay_list/get_relays_test.go b/internal/lido/contracts/mevboostrelaylist/get_relays_test.go similarity index 100% rename from internal/lido/contracts/mevboost_relay_list/get_relays_test.go rename to internal/lido/contracts/mevboostrelaylist/get_relays_test.go diff --git a/internal/lido/contracts/mevboost_relay_list/relays.yaml b/internal/lido/contracts/mevboostrelaylist/relays.yaml similarity index 100% rename from internal/lido/contracts/mevboost_relay_list/relays.yaml rename to internal/lido/contracts/mevboostrelaylist/relays.yaml From 68bb591f7b738668334a8e72ac25c4e251c404a7 Mon Sep 17 00:00:00 2001 From: Haneen Khalifa Date: Wed, 26 Jun 2024 13:45:38 +0300 Subject: [PATCH 02/31] feat: add lido flow for cli.go --- cli/cli.go | 68 +++++++++ cli/cli_test.go | 135 ++++++++++++++++++ go.mod | 3 +- go.sum | 8 +- .../contracts/mevboostrelaylist/get_relays.go | 8 +- 5 files changed, 213 insertions(+), 9 deletions(-) diff --git a/cli/cli.go b/cli/cli.go index 42a20101..2c557854 100644 --- a/cli/cli.go +++ b/cli/cli.go @@ -25,6 +25,8 @@ import ( eth2 "github.com/protolambda/zrnt/eth2/configs" + "github.com/NethermindEth/sedge/internal/lido/contracts" + "github.com/NethermindEth/sedge/internal/lido/contracts/mevboostrelaylist" "github.com/NethermindEth/sedge/internal/pkg/clients" "github.com/NethermindEth/sedge/internal/pkg/dependencies" "github.com/NethermindEth/sedge/internal/pkg/generate" @@ -67,6 +69,7 @@ type CliCmdOptions struct { nodeType string withValidator bool withMevBoost bool + withLido bool importSlashingProtection bool slashingProtectionFrom string jwtSourceType string @@ -96,12 +99,16 @@ func CliCmd(p ui.Prompter, actions actions.SedgeActions, depsMgr dependencies.De - Execution Node - Consensus Node - Validator Node +- Lido CSM Node Follow the prompts to select the options you want for your node. At the end of the process, you will be asked to run the generated setup or not. If you chose to run the setup, it will be executed for you using docker compose command behind the scenes. `, RunE: func(cmd *cobra.Command, args []string) error { + if err := confirmWithLido(p, o); err != nil { + return err + } if err := runPromptActions(p, o, selectNetwork, selectNodeType, @@ -793,6 +800,25 @@ func confirmInstallDependencies(p ui.Prompter, o *CliCmdOptions) (err error) { func confirmEnableMEVBoost(p ui.Prompter, o *CliCmdOptions) (err error) { o.withMevBoost, err = p.Confirm("Enable MEV Boost?", true) + if o.withLido && o.withMevBoost { + _, ok := mevboostrelaylist.DeployedContractAddresses[o.genData.Network] + if !ok { + options := []string{} + for option := range mevboostrelaylist.DeployedContractAddresses { + options = append(options, option) + } + index, err := p.Select("Invalid network: Choose valid network for Lido with MEV-Boost", "", options) + if err != nil { + return err + } + o.genData.Network = options[index] + } + } + return +} + +func confirmWithLido(p ui.Prompter, o *CliCmdOptions) (err error) { + o.withLido, err = p.Confirm("Do you want to set up a Lido CSM node?", false) return } @@ -847,6 +873,14 @@ func inputMevBoostEndpoint(p ui.Prompter, o *CliCmdOptions) (err error) { } func inputRelayURL(p ui.Prompter, o *CliCmdOptions) (err error) { + if o.withLido { + relayURLs, err := mevboostrelaylist.GetRelaysURI(o.genData.Network) + if err != nil { + return err + } + o.genData.RelayURLs = relayURLs + return nil + } var defaultValue []string = configs.NetworksConfigs()[o.genData.Network].RelayURLs relayURLs, err := p.InputList("Insert relay URLs if you don't want to use the default values listed below", defaultValue, func(list []string) error { badUri, ok := utils.UriValidator(list) @@ -870,11 +904,45 @@ func inputCheckpointSyncURL(p ui.Prompter, o *CliCmdOptions) (err error) { } func inputFeeRecipient(p ui.Prompter, o *CliCmdOptions) (err error) { + if o.withLido { + _, ok := contracts.FeeRecipient[o.genData.Network] + if !ok { + options := []string{} + for option := range contracts.FeeRecipient { + options = append(options, option) + } + index, err := p.Select("Invalid network: Choose valid network for Lido", "", options) + if err != nil { + return err + } + o.genData.Network = options[index] + } + feeRecipient := contracts.FeeRecipient[o.genData.Network] + o.genData.FeeRecipient = feeRecipient.FeeRecipientAddress + return + } o.genData.FeeRecipient, err = p.EthAddress("Please enter the Fee Recipient address", "", true) return } func inputFeeRecipientNoValidator(p ui.Prompter, o *CliCmdOptions) (err error) { + if o.withLido { + _, ok := contracts.FeeRecipient[o.genData.Network] + if !ok { + options := []string{} + for option := range contracts.FeeRecipient { + options = append(options, option) + } + index, err := p.Select("Choose valid network for Lido with MEV-Boost", "", options) + if err != nil { + return err + } + o.genData.Network = options[index] + } + feeRecipient, ok := contracts.FeeRecipient[o.genData.Network] + o.genData.FeeRecipient = feeRecipient.FeeRecipientAddress + return + } o.genData.FeeRecipient, err = p.EthAddress("Please enter the Fee Recipient address (press enter to skip it)", "", false) return } diff --git a/cli/cli_test.go b/cli/cli_test.go index a65f1168..8de439b2 100644 --- a/cli/cli_test.go +++ b/cli/cli_test.go @@ -22,6 +22,7 @@ import ( "github.com/NethermindEth/sedge/cli/actions" "github.com/NethermindEth/sedge/configs" + "github.com/NethermindEth/sedge/internal/lido/contracts/mevboostrelaylist" "github.com/NethermindEth/sedge/internal/pkg/clients" "github.com/NethermindEth/sedge/internal/pkg/dependencies" "github.com/NethermindEth/sedge/internal/pkg/generate" @@ -37,6 +38,8 @@ func TestCli(t *testing.T) { // Silence logger log.SetOutput(io.Discard) + mevboostRelayListUris, _ := mevboostrelaylist.GetRelaysURI("mainnet") + ETHClients := map[string][]string{ "execution": clients.AllClients["execution"], "consensus": clients.AllClients["consensus"], @@ -94,6 +97,7 @@ func TestCli(t *testing.T) { } sedgeActions.EXPECT().GetCommandRunner().Return(&test.SimpleCMDRunner{}) gomock.InOrder( + prompter.EXPECT().Confirm("Do you want to set up a Lido CSM node?", false).Return(false, nil), prompter.EXPECT().Select("Select network", "", []string{NetworkMainnet, NetworkSepolia, NetworkGnosis, NetworkChiado, NetworkHolesky}).Return(0, nil), prompter.EXPECT().Select("Select node type", "", []string{NodeTypeFullNode, NodeTypeExecution, NodeTypeConsensus, NodeTypeValidator}).Return(0, nil), prompter.EXPECT().Input("Generation path", configs.DefaultAbsSedgeDataPath, false, nil).Return(generationPath, nil), @@ -164,6 +168,7 @@ func TestCli(t *testing.T) { JWTSecretPath: filepath.Join(generationPath, "jwtsecret"), } gomock.InOrder( + prompter.EXPECT().Confirm("Do you want to set up a Lido CSM node?", false).Return(false, nil), prompter.EXPECT().Select("Select network", "", []string{NetworkMainnet, NetworkSepolia, NetworkGnosis, NetworkChiado, NetworkHolesky}).Return(0, nil), prompter.EXPECT().Select("Select node type", "", []string{NodeTypeFullNode, NodeTypeExecution, NodeTypeConsensus, NodeTypeValidator}).Return(0, nil), prompter.EXPECT().Input("Generation path", configs.DefaultAbsSedgeDataPath, false, nil).Return(generationPath, nil), @@ -207,6 +212,7 @@ func TestCli(t *testing.T) { JWTSecretPath: filepath.Join(generationPath, "jwtsecret"), } gomock.InOrder( + prompter.EXPECT().Confirm("Do you want to set up a Lido CSM node?", false).Return(false, nil), prompter.EXPECT().Select("Select network", "", []string{NetworkMainnet, NetworkSepolia, NetworkGnosis, NetworkChiado, NetworkHolesky}).Return(4, nil), prompter.EXPECT().Select("Select node type", "", []string{NodeTypeFullNode, NodeTypeExecution, NodeTypeConsensus, NodeTypeValidator}).Return(0, nil), prompter.EXPECT().Input("Generation path", configs.DefaultAbsSedgeDataPath, false, nil).Return(generationPath, nil), @@ -243,6 +249,7 @@ func TestCli(t *testing.T) { } gomock.InOrder( + prompter.EXPECT().Confirm("Do you want to set up a Lido CSM node?", false).Return(false, nil), prompter.EXPECT().Select("Select network", "", []string{NetworkMainnet, NetworkSepolia, NetworkGnosis, NetworkChiado, NetworkHolesky}).Return(0, nil), prompter.EXPECT().Select("Select node type", "", []string{NodeTypeFullNode, NodeTypeExecution, NodeTypeConsensus, NodeTypeValidator}).Return(1, nil), prompter.EXPECT().Input("Generation path", configs.DefaultAbsSedgeDataPath, false, nil).Return(generationPath, nil), @@ -286,6 +293,7 @@ func TestCli(t *testing.T) { } gomock.InOrder( + prompter.EXPECT().Confirm("Do you want to set up a Lido CSM node?", false).Return(false, nil), prompter.EXPECT().Select("Select network", "", []string{NetworkMainnet, NetworkSepolia, NetworkGnosis, NetworkChiado, NetworkHolesky}).Return(4, nil), prompter.EXPECT().Select("Select node type", "", []string{NodeTypeFullNode, NodeTypeExecution, NodeTypeConsensus, NodeTypeValidator}).Return(1, nil), prompter.EXPECT().Input("Generation path", configs.DefaultAbsSedgeDataPath, false, nil).Return(generationPath, nil), @@ -335,6 +343,7 @@ func TestCli(t *testing.T) { } gomock.InOrder( + prompter.EXPECT().Confirm("Do you want to set up a Lido CSM node?", false).Return(false, nil), prompter.EXPECT().Select("Select network", "", []string{NetworkMainnet, NetworkSepolia, NetworkGnosis, NetworkChiado, NetworkHolesky}).Return(0, nil), prompter.EXPECT().Select("Select node type", "", []string{NodeTypeFullNode, NodeTypeExecution, NodeTypeConsensus, NodeTypeValidator}).Return(2, nil), prompter.EXPECT().Input("Generation path", configs.DefaultAbsSedgeDataPath, false, nil).Return(generationPath, nil), @@ -378,6 +387,7 @@ func TestCli(t *testing.T) { } gomock.InOrder( + prompter.EXPECT().Confirm("Do you want to set up a Lido CSM node?", false).Return(false, nil), prompter.EXPECT().Select("Select network", "", []string{NetworkMainnet, NetworkSepolia, NetworkGnosis, NetworkChiado, NetworkHolesky}).Return(4, nil), prompter.EXPECT().Select("Select node type", "", []string{NodeTypeFullNode, NodeTypeExecution, NodeTypeConsensus, NodeTypeValidator}).Return(2, nil), prompter.EXPECT().Input("Generation path", configs.DefaultAbsSedgeDataPath, false, nil).Return(generationPath, nil), @@ -421,6 +431,7 @@ func TestCli(t *testing.T) { } gomock.InOrder( + prompter.EXPECT().Confirm("Do you want to set up a Lido CSM node?", false).Return(false, nil), prompter.EXPECT().Select("Select network", "", []string{NetworkMainnet, NetworkSepolia, NetworkGnosis, NetworkChiado, NetworkHolesky}).Return(0, nil), prompter.EXPECT().Select("Select node type", "", []string{NodeTypeFullNode, NodeTypeExecution, NodeTypeConsensus, NodeTypeValidator}).Return(3, nil), prompter.EXPECT().Input("Generation path", configs.DefaultAbsSedgeDataPath, false, nil).Return(generationPath, nil), @@ -460,6 +471,130 @@ func TestCli(t *testing.T) { ) }, }, + { + name: "consensus node", + setup: func(t *testing.T, sedgeActions *sedge_mocks.MockSedgeActions, prompter *sedge_mocks.MockPrompter, depsMgr *sedge_mocks.MockDependenciesManager) { + generationPath := t.TempDir() + genData := generate.GenData{ + Services: []string{"consensus"}, + ConsensusClient: &clients.Client{ + Name: "lodestar", + Type: "consensus", + Image: configs.ClientImages.Consensus.Lodestar.String(), + }, + Network: NetworkMainnet, + CheckpointSyncUrl: "http://checkpoint.sync", + FeeRecipient: "0x2d07a21ebadde0c13e8b91022a7e5732eb6bf5d5", + MapAllPorts: true, + ExecutionApiUrl: "http://execution:5051", + ExecutionAuthUrl: "http://execution:5051", + MevBoostEndpoint: "http://mev-boost:3030", + ContainerTag: "tag", + JWTSecretPath: filepath.Join(generationPath, "jwtsecret"), + } + + gomock.InOrder( + prompter.EXPECT().Confirm("Do you want to set up a Lido CSM node?", false).Return(false, nil), + prompter.EXPECT().Select("Select network", "", []string{NetworkMainnet, NetworkSepolia, NetworkGnosis, NetworkChiado, NetworkHolesky}).Return(0, nil), + prompter.EXPECT().Select("Select node type", "", []string{NodeTypeFullNode, NodeTypeExecution, NodeTypeConsensus, NodeTypeValidator}).Return(2, nil), + prompter.EXPECT().Input("Generation path", configs.DefaultAbsSedgeDataPath, false, nil).Return(generationPath, nil), + prompter.EXPECT().Input("Container tag, sedge will add to each container and the network, a suffix with the tag", "", false, nil).Return("tag", nil), + prompter.EXPECT().Select("Select consensus client", "", ETHClients["consensus"]).Return(3, nil), + prompter.EXPECT().InputURL("Checkpoint sync URL", configs.NetworksConfigs()[genData.Network].CheckpointSyncURL, false).Return("http://checkpoint.sync", nil), + prompter.EXPECT().InputURL("Mev-Boost endpoint", "", false).Return("http://mev-boost:3030", nil), + prompter.EXPECT().InputURL("Execution API URL", "", true).Return("http://execution:5051", nil), + prompter.EXPECT().InputURL("Execution Auth API URL", "", true).Return("http://execution:5051", nil), + prompter.EXPECT().EthAddress("Please enter the Fee Recipient address (press enter to skip it)", "", false).Return("0x2d07a21ebadde0c13e8b91022a7e5732eb6bf5d5", nil), + prompter.EXPECT().Confirm("Do you want to expose all ports?", false).Return(true, nil), + prompter.EXPECT().Select("Select JWT source", "", []string{SourceTypeCreate, SourceTypeExisting, SourceTypeSkip}).Return(0, nil), + sedgeActions.EXPECT().Generate(gomock.Eq(actions.GenerateOptions{ + GenerationPath: generationPath, + GenerationData: genData, + })).Return(genData, nil), + prompter.EXPECT().Confirm("Run services now?", false).Return(false, nil), + ) + }, + }, + { + name: "full node with Lido", + setup: func(t *testing.T, sedgeActions *sedge_mocks.MockSedgeActions, prompter *sedge_mocks.MockPrompter, depsMgr *sedge_mocks.MockDependenciesManager) { + generationPath := t.TempDir() + genData := generate.GenData{ + Services: []string{"execution", "consensus", "validator", "mev-boost"}, + ExecutionClient: &clients.Client{ + Name: "nethermind", + Type: "execution", + Image: configs.ClientImages.Execution.Nethermind.String(), + }, + ConsensusClient: &clients.Client{ + Name: "prysm", + Type: "consensus", + Image: configs.ClientImages.Consensus.Prysm.String(), + }, + ValidatorClient: &clients.Client{ + Name: "prysm", + Type: "validator", + Image: configs.ClientImages.Validator.Prysm.String(), + }, + Network: "mainnet", + CheckpointSyncUrl: "http://checkpoint.sync", + FeeRecipient: "0x388C818CA8B9251b393131C08a736A67ccB19297", + MapAllPorts: true, + Graffiti: "test graffiti", + VLStartGracePeriod: 840, + Mev: true, + MevImage: "flashbots/mev-boost:latest", + RelayURLs: mevboostRelayListUris, + ContainerTag: "tag", + JWTSecretPath: filepath.Join(generationPath, "jwtsecret"), + } + sedgeActions.EXPECT().GetCommandRunner().Return(&test.SimpleCMDRunner{}) + gomock.InOrder( + prompter.EXPECT().Confirm("Do you want to set up a Lido CSM node?", false).Return(true, nil), + prompter.EXPECT().Select("Select network", "", []string{NetworkMainnet, NetworkSepolia, NetworkGnosis, NetworkChiado, NetworkHolesky}).Return(0, nil), + prompter.EXPECT().Select("Select node type", "", []string{NodeTypeFullNode, NodeTypeExecution, NodeTypeConsensus, NodeTypeValidator}).Return(0, nil), + prompter.EXPECT().Input("Generation path", configs.DefaultAbsSedgeDataPath, false, nil).Return(generationPath, nil), + prompter.EXPECT().Input("Container tag, sedge will add to each container and the network, a suffix with the tag", "", false, nil).Return("tag", nil), + prompter.EXPECT().Confirm("Do you want to set up a validator?", true).Return(true, nil), + prompter.EXPECT().Confirm("Enable MEV Boost?", true).Return(true, nil), + prompter.EXPECT().Input("Mev-Boost image", "flashbots/mev-boost:latest", false, nil).Return("flashbots/mev-boost:latest", nil), + prompter.EXPECT().Select("Select execution client", "", ETHClients["execution"]).Return(0, nil), + prompter.EXPECT().Select("Select consensus client", "", ETHClients["consensus"]).Return(1, nil), + prompter.EXPECT().Select("Select validator client", "", ETHClients["validator"]).Return(1, nil), + 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), + prompter.EXPECT().Input("Graffiti to be used by the validator (press enter to skip it)", "", false, gomock.AssignableToTypeOf(ui.GraffitiValidator)).Return("test graffiti", nil), + prompter.EXPECT().InputURL("Checkpoint sync URL", configs.NetworksConfigs()[genData.Network].CheckpointSyncURL, false).Return("http://checkpoint.sync", nil), + prompter.EXPECT().Confirm("Do you want to expose all ports?", false).Return(true, nil), + prompter.EXPECT().Select("Select JWT source", "", []string{SourceTypeCreate, SourceTypeExisting}).Return(0, nil), + sedgeActions.EXPECT().Generate(gomock.Eq(actions.GenerateOptions{ + GenerationPath: generationPath, + GenerationData: genData, + })).Return(genData, nil), + prompter.EXPECT().Select("Select keystore source", "", []string{SourceTypeCreate, SourceTypeExisting, SourceTypeSkip}).Return(0, nil), + prompter.EXPECT().Select("Select mnemonic source", "", []string{SourceTypeCreate, SourceTypeExisting}).Return(0, nil), + prompter.EXPECT().Select("Select passphrase source", "", []string{SourceTypeRandom, SourceTypeExisting, SourceTypeCreate}).Return(0, nil), + prompter.EXPECT().Input("Withdrawal address", "", false, gomock.AssignableToTypeOf(func(s string) error { return ui.EthAddressValidator(s, true) })).Return("0x00000007abca72jmd83jd8u3jd9kdn32j38abc", nil), + prompter.EXPECT().InputInt64("Number of validators", int64(1)).Return(int64(1), nil), + prompter.EXPECT().InputInt64("Existing validators. This number will be used as the initial index for the generated keystores.", int64(0)).Return(int64(0), nil), + depsMgr.EXPECT().Check([]string{dependencies.Docker}).Return([]string{dependencies.Docker}, nil), + depsMgr.EXPECT().DockerEngineIsOn().Return(nil), + depsMgr.EXPECT().DockerComposeIsInstalled().Return(nil), + sedgeActions.EXPECT().SetupContainers(actions.SetupContainersOptions{ + GenerationPath: generationPath, + Services: []string{"validator"}, + }), + sedgeActions.EXPECT().ImportValidatorKeys(actions.ImportValidatorKeysOptions{ + ValidatorClient: "prysm", + Network: NetworkMainnet, + GenerationPath: generationPath, + From: filepath.Join(generationPath, "keystore"), + ContainerTag: "tag", + }).Return(nil), + prompter.EXPECT().Confirm("Do you want to import slashing protection data?", false).Return(false, nil), + prompter.EXPECT().Confirm("Run services now?", false).Return(false, nil), + ) + }, + }, } for _, tt := range tests { diff --git a/go.mod b/go.mod index 7a345ca1..fa8020c7 100644 --- a/go.mod +++ b/go.mod @@ -55,7 +55,7 @@ require ( github.com/fsnotify/fsnotify v1.6.0 // indirect github.com/go-ole/go-ole v1.3.0 // indirect github.com/gogo/protobuf v1.3.2 // indirect - github.com/google/go-cmp v0.5.9 // indirect + github.com/google/go-cmp v0.6.0 // indirect github.com/google/go-querystring v1.1.0 // indirect github.com/gorilla/websocket v1.4.2 // indirect github.com/holiman/uint256 v1.2.4 // indirect @@ -79,6 +79,7 @@ require ( github.com/pmezard/go-difflib v1.0.0 // indirect github.com/protolambda/bls12-381-util v0.1.0 // indirect github.com/rivo/uniseg v0.4.4 // indirect + github.com/rogpeppe/go-internal v1.12.0 // indirect github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible // indirect github.com/spf13/pflag v1.0.5 // indirect github.com/supranational/blst v0.3.11 // indirect diff --git a/go.sum b/go.sum index 07facd2e..7dc33232 100644 --- a/go.sum +++ b/go.sum @@ -109,8 +109,8 @@ github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb h1:PBC98N2aIaM3XXi github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= -github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-github/v47 v47.1.0 h1:Cacm/WxQBOa9lF0FT0EMjZ2BWMetQ1TQfyurn4yF1z8= github.com/google/go-github/v47 v47.1.0/go.mod h1:VPZBXNbFSJGjyjFRUKo9vZGawTajnWzC/YjGw/oFKi0= github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= @@ -234,8 +234,8 @@ github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJ github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis= github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= -github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= -github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= +github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= +github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= github.com/rs/cors v1.7.0 h1:+88SsELBHx5r+hZ8TCkggzSstaWNbDvThkVK8H6f9ik= github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= diff --git a/internal/lido/contracts/mevboostrelaylist/get_relays.go b/internal/lido/contracts/mevboostrelaylist/get_relays.go index 8fa8596a..1754dd9c 100644 --- a/internal/lido/contracts/mevboostrelaylist/get_relays.go +++ b/internal/lido/contracts/mevboostrelaylist/get_relays.go @@ -133,12 +133,12 @@ Error if any */ func GetRelaysURI(network string) ([]string, error) { relays, err := GetRelays(network) - if err != nil{ + if err != nil { return nil, err } relayURIs := []string{} - for _, relay := range relays { - relayURIs = append(relayURIs, relay.Uri) - } + for _, relay := range relays { + relayURIs = append(relayURIs, relay.Uri) + } return relayURIs, err } From be9b864f74c713468dc5cbf142f90e40962ab5e4 Mon Sep 17 00:00:00 2001 From: Haneen Khalifa Date: Fri, 28 Jun 2024 16:26:06 +0300 Subject: [PATCH 03/31] refactor: adjust prompts for lido flow --- cli/cli.go | 78 +++++++------------ cli/cli_test.go | 22 +++--- internal/lido/contracts/feeRecipient.go | 8 ++ .../contracts/mevboostrelaylist/get_relays.go | 8 ++ 4 files changed, 56 insertions(+), 60 deletions(-) diff --git a/cli/cli.go b/cli/cli.go index 2c557854..86a475d3 100644 --- a/cli/cli.go +++ b/cli/cli.go @@ -53,6 +53,9 @@ const ( NodeTypeConsensus = "consensus" NodeTypeValidator = "validator" + EthereumNode = "ethereum-node" + LidoNode = "lido-node" + Randomize = "randomize" SourceTypeExisting = "existing" @@ -69,7 +72,6 @@ type CliCmdOptions struct { nodeType string withValidator bool withMevBoost bool - withLido bool importSlashingProtection bool slashingProtectionFrom string jwtSourceType string @@ -82,6 +84,7 @@ type CliCmdOptions struct { keystorePassphrasePath string keystorePassphrase string withdrawalAddress string + nodeSetup string numberOfValidators int64 existingValidators int64 installDependencies bool @@ -106,10 +109,8 @@ be asked to run the generated setup or not. If you chose to run the setup, it wi using docker compose command behind the scenes. `, RunE: func(cmd *cobra.Command, args []string) error { - if err := confirmWithLido(p, o); err != nil { - return err - } if err := runPromptActions(p, o, + selectNodeSetup, selectNetwork, selectNodeType, inputGenerationPath, @@ -621,8 +622,24 @@ func runPromptActions(p ui.Prompter, o *CliCmdOptions, actions ...promptAction) return nil } + +func selectNodeSetup(p ui.Prompter, o *CliCmdOptions) (err error) { + options := []string{EthereumNode, LidoNode} + index, err := p.Select("Select node setup", "", options) + if err != nil { + return err + } + o.nodeSetup = options[index] + return +} + func selectNetwork(p ui.Prompter, o *CliCmdOptions) error { - options := []string{NetworkMainnet, NetworkSepolia, NetworkGnosis, NetworkChiado, NetworkHolesky} + var options []string + if o.nodeSetup == LidoNode{ + options = contracts.GetLidoSupportedNetworks() + }else{ + options = []string{NetworkMainnet, NetworkSepolia, NetworkGnosis, NetworkChiado, NetworkHolesky} + } index, err := p.Select("Select network", "", options) if err != nil { return err @@ -799,26 +816,13 @@ func confirmInstallDependencies(p ui.Prompter, o *CliCmdOptions) (err error) { } func confirmEnableMEVBoost(p ui.Prompter, o *CliCmdOptions) (err error) { - o.withMevBoost, err = p.Confirm("Enable MEV Boost?", true) - if o.withLido && o.withMevBoost { + if o.nodeSetup == LidoNode { _, ok := mevboostrelaylist.DeployedContractAddresses[o.genData.Network] if !ok { - options := []string{} - for option := range mevboostrelaylist.DeployedContractAddresses { - options = append(options, option) - } - index, err := p.Select("Invalid network: Choose valid network for Lido with MEV-Boost", "", options) - if err != nil { - return err - } - o.genData.Network = options[index] + return } } - return -} - -func confirmWithLido(p ui.Prompter, o *CliCmdOptions) (err error) { - o.withLido, err = p.Confirm("Do you want to set up a Lido CSM node?", false) + o.withMevBoost, err = p.Confirm("Enable MEV Boost?", true) return } @@ -873,7 +877,7 @@ func inputMevBoostEndpoint(p ui.Prompter, o *CliCmdOptions) (err error) { } func inputRelayURL(p ui.Prompter, o *CliCmdOptions) (err error) { - if o.withLido { + if o.nodeSetup == LidoNode { relayURLs, err := mevboostrelaylist.GetRelaysURI(o.genData.Network) if err != nil { return err @@ -904,19 +908,7 @@ func inputCheckpointSyncURL(p ui.Prompter, o *CliCmdOptions) (err error) { } func inputFeeRecipient(p ui.Prompter, o *CliCmdOptions) (err error) { - if o.withLido { - _, ok := contracts.FeeRecipient[o.genData.Network] - if !ok { - options := []string{} - for option := range contracts.FeeRecipient { - options = append(options, option) - } - index, err := p.Select("Invalid network: Choose valid network for Lido", "", options) - if err != nil { - return err - } - o.genData.Network = options[index] - } + if o.nodeSetup == LidoNode { feeRecipient := contracts.FeeRecipient[o.genData.Network] o.genData.FeeRecipient = feeRecipient.FeeRecipientAddress return @@ -926,20 +918,8 @@ func inputFeeRecipient(p ui.Prompter, o *CliCmdOptions) (err error) { } func inputFeeRecipientNoValidator(p ui.Prompter, o *CliCmdOptions) (err error) { - if o.withLido { - _, ok := contracts.FeeRecipient[o.genData.Network] - if !ok { - options := []string{} - for option := range contracts.FeeRecipient { - options = append(options, option) - } - index, err := p.Select("Choose valid network for Lido with MEV-Boost", "", options) - if err != nil { - return err - } - o.genData.Network = options[index] - } - feeRecipient, ok := contracts.FeeRecipient[o.genData.Network] + if o.nodeSetup == LidoNode { + feeRecipient:= contracts.FeeRecipient[o.genData.Network] o.genData.FeeRecipient = feeRecipient.FeeRecipientAddress return } diff --git a/cli/cli_test.go b/cli/cli_test.go index 8de439b2..22c61207 100644 --- a/cli/cli_test.go +++ b/cli/cli_test.go @@ -97,7 +97,7 @@ func TestCli(t *testing.T) { } sedgeActions.EXPECT().GetCommandRunner().Return(&test.SimpleCMDRunner{}) gomock.InOrder( - prompter.EXPECT().Confirm("Do you want to set up a Lido CSM node?", false).Return(false, nil), + prompter.EXPECT().Select("Select node setup", "", []string{EthereumNode, LidoNode}).Return(0, nil), prompter.EXPECT().Select("Select network", "", []string{NetworkMainnet, NetworkSepolia, NetworkGnosis, NetworkChiado, NetworkHolesky}).Return(0, nil), prompter.EXPECT().Select("Select node type", "", []string{NodeTypeFullNode, NodeTypeExecution, NodeTypeConsensus, NodeTypeValidator}).Return(0, nil), prompter.EXPECT().Input("Generation path", configs.DefaultAbsSedgeDataPath, false, nil).Return(generationPath, nil), @@ -168,7 +168,7 @@ func TestCli(t *testing.T) { JWTSecretPath: filepath.Join(generationPath, "jwtsecret"), } gomock.InOrder( - prompter.EXPECT().Confirm("Do you want to set up a Lido CSM node?", false).Return(false, nil), + prompter.EXPECT().Select("Select node setup", "", []string{EthereumNode, LidoNode}).Return(0, nil), prompter.EXPECT().Select("Select network", "", []string{NetworkMainnet, NetworkSepolia, NetworkGnosis, NetworkChiado, NetworkHolesky}).Return(0, nil), prompter.EXPECT().Select("Select node type", "", []string{NodeTypeFullNode, NodeTypeExecution, NodeTypeConsensus, NodeTypeValidator}).Return(0, nil), prompter.EXPECT().Input("Generation path", configs.DefaultAbsSedgeDataPath, false, nil).Return(generationPath, nil), @@ -212,7 +212,7 @@ func TestCli(t *testing.T) { JWTSecretPath: filepath.Join(generationPath, "jwtsecret"), } gomock.InOrder( - prompter.EXPECT().Confirm("Do you want to set up a Lido CSM node?", false).Return(false, nil), + prompter.EXPECT().Select("Select node setup", "", []string{EthereumNode, LidoNode}).Return(0, nil), prompter.EXPECT().Select("Select network", "", []string{NetworkMainnet, NetworkSepolia, NetworkGnosis, NetworkChiado, NetworkHolesky}).Return(4, nil), prompter.EXPECT().Select("Select node type", "", []string{NodeTypeFullNode, NodeTypeExecution, NodeTypeConsensus, NodeTypeValidator}).Return(0, nil), prompter.EXPECT().Input("Generation path", configs.DefaultAbsSedgeDataPath, false, nil).Return(generationPath, nil), @@ -249,7 +249,7 @@ func TestCli(t *testing.T) { } gomock.InOrder( - prompter.EXPECT().Confirm("Do you want to set up a Lido CSM node?", false).Return(false, nil), + prompter.EXPECT().Select("Select node setup", "", []string{EthereumNode, LidoNode}).Return(0, nil), prompter.EXPECT().Select("Select network", "", []string{NetworkMainnet, NetworkSepolia, NetworkGnosis, NetworkChiado, NetworkHolesky}).Return(0, nil), prompter.EXPECT().Select("Select node type", "", []string{NodeTypeFullNode, NodeTypeExecution, NodeTypeConsensus, NodeTypeValidator}).Return(1, nil), prompter.EXPECT().Input("Generation path", configs.DefaultAbsSedgeDataPath, false, nil).Return(generationPath, nil), @@ -293,7 +293,7 @@ func TestCli(t *testing.T) { } gomock.InOrder( - prompter.EXPECT().Confirm("Do you want to set up a Lido CSM node?", false).Return(false, nil), + prompter.EXPECT().Select("Select node setup", "", []string{EthereumNode, LidoNode}).Return(0, nil), prompter.EXPECT().Select("Select network", "", []string{NetworkMainnet, NetworkSepolia, NetworkGnosis, NetworkChiado, NetworkHolesky}).Return(4, nil), prompter.EXPECT().Select("Select node type", "", []string{NodeTypeFullNode, NodeTypeExecution, NodeTypeConsensus, NodeTypeValidator}).Return(1, nil), prompter.EXPECT().Input("Generation path", configs.DefaultAbsSedgeDataPath, false, nil).Return(generationPath, nil), @@ -343,7 +343,7 @@ func TestCli(t *testing.T) { } gomock.InOrder( - prompter.EXPECT().Confirm("Do you want to set up a Lido CSM node?", false).Return(false, nil), + prompter.EXPECT().Select("Select node setup", "", []string{EthereumNode, LidoNode}).Return(0, nil), prompter.EXPECT().Select("Select network", "", []string{NetworkMainnet, NetworkSepolia, NetworkGnosis, NetworkChiado, NetworkHolesky}).Return(0, nil), prompter.EXPECT().Select("Select node type", "", []string{NodeTypeFullNode, NodeTypeExecution, NodeTypeConsensus, NodeTypeValidator}).Return(2, nil), prompter.EXPECT().Input("Generation path", configs.DefaultAbsSedgeDataPath, false, nil).Return(generationPath, nil), @@ -387,7 +387,7 @@ func TestCli(t *testing.T) { } gomock.InOrder( - prompter.EXPECT().Confirm("Do you want to set up a Lido CSM node?", false).Return(false, nil), + prompter.EXPECT().Select("Select node setup", "", []string{EthereumNode, LidoNode}).Return(0, nil), prompter.EXPECT().Select("Select network", "", []string{NetworkMainnet, NetworkSepolia, NetworkGnosis, NetworkChiado, NetworkHolesky}).Return(4, nil), prompter.EXPECT().Select("Select node type", "", []string{NodeTypeFullNode, NodeTypeExecution, NodeTypeConsensus, NodeTypeValidator}).Return(2, nil), prompter.EXPECT().Input("Generation path", configs.DefaultAbsSedgeDataPath, false, nil).Return(generationPath, nil), @@ -431,7 +431,7 @@ func TestCli(t *testing.T) { } gomock.InOrder( - prompter.EXPECT().Confirm("Do you want to set up a Lido CSM node?", false).Return(false, nil), + prompter.EXPECT().Select("Select node setup", "", []string{EthereumNode, LidoNode}).Return(0, nil), prompter.EXPECT().Select("Select network", "", []string{NetworkMainnet, NetworkSepolia, NetworkGnosis, NetworkChiado, NetworkHolesky}).Return(0, nil), prompter.EXPECT().Select("Select node type", "", []string{NodeTypeFullNode, NodeTypeExecution, NodeTypeConsensus, NodeTypeValidator}).Return(3, nil), prompter.EXPECT().Input("Generation path", configs.DefaultAbsSedgeDataPath, false, nil).Return(generationPath, nil), @@ -494,7 +494,7 @@ func TestCli(t *testing.T) { } gomock.InOrder( - prompter.EXPECT().Confirm("Do you want to set up a Lido CSM node?", false).Return(false, nil), + prompter.EXPECT().Select("Select node setup", "", []string{EthereumNode, LidoNode}).Return(0, nil), prompter.EXPECT().Select("Select network", "", []string{NetworkMainnet, NetworkSepolia, NetworkGnosis, NetworkChiado, NetworkHolesky}).Return(0, nil), prompter.EXPECT().Select("Select node type", "", []string{NodeTypeFullNode, NodeTypeExecution, NodeTypeConsensus, NodeTypeValidator}).Return(2, nil), prompter.EXPECT().Input("Generation path", configs.DefaultAbsSedgeDataPath, false, nil).Return(generationPath, nil), @@ -550,8 +550,8 @@ func TestCli(t *testing.T) { } sedgeActions.EXPECT().GetCommandRunner().Return(&test.SimpleCMDRunner{}) gomock.InOrder( - prompter.EXPECT().Confirm("Do you want to set up a Lido CSM node?", false).Return(true, nil), - prompter.EXPECT().Select("Select network", "", []string{NetworkMainnet, NetworkSepolia, NetworkGnosis, NetworkChiado, NetworkHolesky}).Return(0, nil), + prompter.EXPECT().Select("Select node setup", "", []string{EthereumNode, LidoNode}).Return(1, nil), + prompter.EXPECT().Select("Select network", "", []string{NetworkMainnet, NetworkHolesky, NetworkSepolia}).Return(0, nil), prompter.EXPECT().Select("Select node type", "", []string{NodeTypeFullNode, NodeTypeExecution, NodeTypeConsensus, NodeTypeValidator}).Return(0, nil), prompter.EXPECT().Input("Generation path", configs.DefaultAbsSedgeDataPath, false, nil).Return(generationPath, nil), prompter.EXPECT().Input("Container tag, sedge will add to each container and the network, a suffix with the tag", "", false, nil).Return("tag", nil), diff --git a/internal/lido/contracts/feeRecipient.go b/internal/lido/contracts/feeRecipient.go index 080c774f..0ca9dc47 100644 --- a/internal/lido/contracts/feeRecipient.go +++ b/internal/lido/contracts/feeRecipient.go @@ -23,3 +23,11 @@ var FeeRecipient = map[string]FeeRecipientConfig{ FeeRecipientAddress: "0x94B1B8e2680882f8652882e7F196169dE3d9a3B2", }, } + +func GetLidoSupportedNetworks()[]string{ + networks := []string{} + for network := range FeeRecipient { + networks = append(networks, network) + } + return networks +} \ No newline at end of file diff --git a/internal/lido/contracts/mevboostrelaylist/get_relays.go b/internal/lido/contracts/mevboostrelaylist/get_relays.go index 1754dd9c..18084727 100644 --- a/internal/lido/contracts/mevboostrelaylist/get_relays.go +++ b/internal/lido/contracts/mevboostrelaylist/get_relays.go @@ -142,3 +142,11 @@ func GetRelaysURI(network string) ([]string, error) { } return relayURIs, err } + +func GetLidoSupportedNetworksMevBoost()[]string{ + networks := []string{} + for network := range DeployedContractAddresses { + networks= append(networks, network) + } + return networks +} From d4a35f0250237aa75492dd58c9501a046b9184d8 Mon Sep 17 00:00:00 2001 From: Haneen Khalifa Date: Fri, 28 Jun 2024 17:01:11 +0300 Subject: [PATCH 04/31] feat: add non-interactive lido flow --- cli/generate.go | 33 ++++++++++++++++++++++++++++++++- cli/sub_gen.go | 27 ++++++++++++++++++++++----- 2 files changed, 54 insertions(+), 6 deletions(-) diff --git a/cli/generate.go b/cli/generate.go index b8596628..1f316cf5 100644 --- a/cli/generate.go +++ b/cli/generate.go @@ -25,6 +25,9 @@ import ( "time" "github.com/NethermindEth/sedge/internal/crypto" + "github.com/NethermindEth/sedge/internal/lido/contracts" + "github.com/NethermindEth/sedge/internal/lido/contracts/mevboostrelaylist" + "github.com/NethermindEth/sedge/cli/actions" "github.com/NethermindEth/sedge/configs" @@ -42,6 +45,7 @@ var ( network string logging string containerTag string + lidoNode bool ) const ( @@ -100,6 +104,7 @@ You can generate: - Consensus Node - Validator Node - Mev-Boost Instance +- Lido CSM node `, Args: cobra.NoArgs, } @@ -109,7 +114,8 @@ You can generate: cmd.AddCommand(ConsensusSubCmd(sedgeAction)) cmd.AddCommand(ValidatorSubCmd(sedgeAction)) cmd.AddCommand(MevBoostSubCmd(sedgeAction)) - + + cmd.PersistentFlags().BoolVar(&lidoNode, "lido", false, "generate Lido CSM node") cmd.PersistentFlags().StringVarP(&generationPath, "path", "p", configs.DefaultAbsSedgeDataPath, "generation path for sedge data. Default is sedge-data") cmd.PersistentFlags().StringVarP(&network, "network", "n", "mainnet", "Target network. e.g. mainnet,sepolia, holesky, gnosis, chiado, etc.") cmd.PersistentFlags().StringVar(&logging, "logging", "json", fmt.Sprintf("Docker logging driver used by all the services. Set 'none' to use the default docker logging driver. Possible values: %v", configs.ValidLoggingFlags())) @@ -257,6 +263,14 @@ func runGenCmd(out io.Writer, flags *GenCmdFlags, sedgeAction actions.SedgeActio } } + //Overwrite feeRecipient and relayURLs for Lido Node + if lidoNode{ + feeRecipient := contracts.FeeRecipient[network] + flags.feeRecipient = feeRecipient.FeeRecipientAddress + + flags.relayURLs, _ = mevboostrelaylist.GetRelaysURI(network) + } + // Warning if no fee recipient is set if flags.feeRecipient == "" { log.Warn(configs.EmptyFeeRecipientError) @@ -477,3 +491,20 @@ func loadJWTSecret(from string) (absFrom string, err error) { } return } + +func validateLido(network string, flags *GenCmdFlags) error { + if !flags.noMev{ + _, ok := mevboostrelaylist.DeployedContractAddresses[network] + if !ok { + options := mevboostrelaylist.GetLidoSupportedNetworksMevBoost() + return fmt.Errorf("invalid network: Choose valid network for Lido with MEV-Boost: %v", options) + } + } + _, ok := contracts.FeeRecipient[network] + if !ok { + options := contracts.GetLidoSupportedNetworks() + return fmt.Errorf("invalid network: Choose valid network for Lido: %v", options) + } + + return nil +} \ No newline at end of file diff --git a/cli/sub_gen.go b/cli/sub_gen.go index 5f58aaaa..f1c438d3 100644 --- a/cli/sub_gen.go +++ b/cli/sub_gen.go @@ -55,6 +55,9 @@ Additionally, you can use this syntax ':' to override the if err := validateCustomNetwork(&flags.CustomFlags, network); err != nil { return err } + if err := validateLido(network, &flags); err != nil && lidoNode { + return err + } return preValidationGenerateCmd(network, logging, &flags) }, RunE: func(cmd *cobra.Command, args []string) error { @@ -79,10 +82,12 @@ Additionally, you can use this syntax ':' to override the cmd.Flags().StringVarP(&flags.validatorName, "validator", "v", "", "Validator engine client, e.g. teku, lodestar, prysm, lighthouse, Nimbus. Additionally, you can use this syntax ':' to override the docker image used for the client. If you want to use the default docker image, just use the client name") cmd.Flags().BoolVar(&flags.latestVersion, "latest", false, "Use the latest version of clients. This sets the \"latest\" tag on the client's docker images. Latest version might not work.") cmd.Flags().StringVar(&flags.checkpointSyncUrl, "checkpoint-sync-url", "", "Initial state endpoint (trusted synced consensus endpoint) for the consensus client to sync from a finalized checkpoint. Provide faster sync process for the consensus client and protect it from long-range attacks affored by Weak Subjetivity. Each network has a default checkpoint sync url.") - cmd.Flags().StringVar(&flags.feeRecipient, "fee-recipient", "", "Suggested fee recipient. Is a 20-byte Ethereum address which the execution layer might choose to set as the coinbase and the recipient of other fees or rewards. There is no guarantee that an execution node will use the suggested fee recipient to collect fees, it may use any address it chooses. It is assumed that an honest execution node will use the suggested fee recipient, but users should note this trust assumption") + cmd.Flags().StringVar(&flags.feeRecipient, "fee-recipient", "", "Suggested fee recipient. Is a 20-byte Ethereum address which the execution layer might choose to set as the coinbase and the recipient of other fees or rewards. There is no guarantee that an execution node will use the suggested fee recipient to collect fees, it may use any address it chooses. It is assumed that an honest execution node will use the suggested fee recipient, but users should note this trust assumption.\n"+ + "Note: When setting up a Lido node, fee recipient address will be automatically set by the system.") cmd.Flags().BoolVar(&flags.noMev, "no-mev-boost", false, "Not use mev-boost if supported") cmd.Flags().StringVarP(&flags.mevImage, "mev-boost-image", "m", "", "Custom docker image to use for Mev Boost. Example: 'sedge generate full-node --mev-boost-image flashbots/mev-boost:latest-portable'") - cmd.Flags().StringSliceVar(&flags.relayURLs, "relay-urls", []string{}, "List of comma separated relay URLs used to connect to mev relay. Example: 'sedge generate full-node --relay-urls=https://0xac6e77dfe25ecd6110b8e780608cce0dab71fdd5ebea22a16c0205200f2f8e2e3ad3b71d3499c54ad14d6c21b41a37ae@boost-relay.flashbots.net,https://0xa1559ace749633b997cb3fdacffb890aeebdb0f5a3b6aaa7eeeaf1a38af0a8fe88b9e4b1f61f236d2e64d95733327a62@relay.ultrasound.money'") + cmd.Flags().StringSliceVar(&flags.relayURLs, "relay-urls", []string{}, "List of comma separated relay URLs used to connect to mev relay. Example: 'sedge generate full-node --relay-urls=https://0xac6e77dfe25ecd6110b8e780608cce0dab71fdd5ebea22a16c0205200f2f8e2e3ad3b71d3499c54ad14d6c21b41a37ae@boost-relay.flashbots.net,https://0xa1559ace749633b997cb3fdacffb890aeebdb0f5a3b6aaa7eeeaf1a38af0a8fe88b9e4b1f61f236d2e64d95733327a62@relay.ultrasound.money'\n"+ + "Note: When setting up a Lido node, the provided relay URLs will be automatically set by the system.") cmd.Flags().BoolVar(&flags.noValidator, "no-validator", false, "Exclude the validator from the full node setup. Designed for execution and consensus nodes setup without a validator node. Exclude also the validator from other flags. If set, mev-boost will not be used.") cmd.Flags().StringVar(&flags.jwtPath, "jwt-secret-path", "", "Path to the JWT secret file") cmd.Flags().StringVar(&flags.graffiti, "graffiti", "", "Graffiti to be used by the validator") @@ -125,6 +130,9 @@ func ExecutionSubCmd(sedgeAction actions.SedgeActions) *cobra.Command { if err := validateCustomNetwork(&flags.CustomFlags, network); err != nil { return err } + if err := validateLido(network, &flags); err != nil && lidoNode { + return err + } return preValidationGenerateCmd(network, logging, &flags) }, RunE: func(cmd *cobra.Command, args []string) error { @@ -170,6 +178,9 @@ func ConsensusSubCmd(sedgeAction actions.SedgeActions) *cobra.Command { if err := validateCustomNetwork(&flags.CustomFlags, network); err != nil { return err } + if err := validateLido(network, &flags); err != nil && lidoNode { + return err + } return preValidationGenerateCmd(network, logging, &flags) }, RunE: func(cmd *cobra.Command, args []string) error { @@ -181,7 +192,8 @@ func ConsensusSubCmd(sedgeAction actions.SedgeActions) *cobra.Command { cmd.Flags().StringVar(&flags.executionApiUrl, "execution-api-url", "", "Execution API endpoint for the consensus client. Example: 'sedge generate consensus -r --execution-api-url=https://api.url.endpoint'") cmd.Flags().StringVar(&flags.executionAuthUrl, "execution-auth-url", "", "Execution AUTH endpoint for the consensus client. Example: 'sedge generate consensus -r --execution-auth-url=https://auth.url.endpoint'") cmd.Flags().StringVar(&flags.checkpointSyncUrl, "checkpoint-sync-url", "", "Initial state endpoint (trusted synced consensus endpoint) for the consensus client to sync from a finalized checkpoint. Provide faster sync process for the consensus client and protect it from long-range attacks affored by Weak Subjetivity. Each network has a default checkpoint sync url.") - cmd.Flags().StringVar(&flags.feeRecipient, "fee-recipient", "", "Suggested fee recipient. Is a 20-byte Ethereum address which the execution layer might choose to set as the coinbase and the recipient of other fees or rewards. There is no guarantee that an execution node will use the suggested fee recipient to collect fees, it may use any address it chooses. It is assumed that an honest execution node will use the suggested fee recipient, but users should note this trust assumption") + cmd.Flags().StringVar(&flags.feeRecipient, "fee-recipient", "", "Suggested fee recipient. Is a 20-byte Ethereum address which the execution layer might choose to set as the coinbase and the recipient of other fees or rewards. There is no guarantee that an execution node will use the suggested fee recipient to collect fees, it may use any address it chooses. It is assumed that an honest execution node will use the suggested fee recipient, but users should note this trust assumption.\n"+ + "Note: When setting up a Lido node, fee recipient address will be automatically set by the system.") cmd.Flags().StringVar(&flags.jwtPath, "jwt-secret-path", "", "Path to the JWT secret file") cmd.Flags().StringVar(&flags.mevBoostUrl, "mev-boost-url", "", "If you are running a mev boost node, and you want to connect to it, you need to set mev-boost-url, if not set, node will not load any mev boost related config.") cmd.Flags().BoolVar(&flags.mapAllPorts, "map-all", false, "Map all clients ports to host. Use with care. Useful to allow remote access to the clients") @@ -229,6 +241,9 @@ func ValidatorSubCmd(sedgeAction actions.SedgeActions) *cobra.Command { if err := validateCustomNetwork(&flags.CustomFlags, network); err != nil { return err } + if err := validateLido(network, &flags); err != nil && lidoNode { + return err + } return preValidationGenerateCmd(network, logging, &flags) }, RunE: func(cmd *cobra.Command, args []string) error { @@ -239,7 +254,8 @@ func ValidatorSubCmd(sedgeAction actions.SedgeActions) *cobra.Command { // Bind flags cmd.Flags().BoolVar(&flags.latestVersion, "latest", false, "Use the latest version of clients. This sets the \"latest\" tag on the client's docker images. Latest version might not work.") cmd.Flags().StringVar(&flags.consensusApiUrl, "consensus-url", "", "Consensus endpoint for the validator client to connect to. Example: 'sedge generate validator --consensus-url http://localhost:4000'") - cmd.Flags().StringVar(&flags.feeRecipient, "fee-recipient", "", "Suggested fee recipient. Is a 20-byte Ethereum address which the execution layer might choose to set as the coinbase and the recipient of other fees or rewards. There is no guarantee that an execution node will use the suggested fee recipient to collect fees, it may use any address it chooses. It is assumed that an honest execution node will use the suggested fee recipient, but users should note this trust assumption") + cmd.Flags().StringVar(&flags.feeRecipient, "fee-recipient", "", "Suggested fee recipient. Is a 20-byte Ethereum address which the execution layer might choose to set as the coinbase and the recipient of other fees or rewards. There is no guarantee that an execution node will use the suggested fee recipient to collect fees, it may use any address it chooses. It is assumed that an honest execution node will use the suggested fee recipient, but users should note this trust assumption.\n"+ + "Note: When setting up a Lido node, fee recipient address will be automatically set by the system.") cmd.Flags().StringVar(&flags.graffiti, "graffiti", "", "Graffiti to be used by the validator") cmd.Flags().BoolVar(&flags.mevBoostOnVal, "mev-boost", false, "Use mev-boost while turning on validator node") cmd.Flags().StringVar(&flags.customNetworkConfig, "custom-config", "", "File path or url to use as custom network config file for consensus client.") @@ -271,7 +287,8 @@ func MevBoostSubCmd(sedgeAction actions.SedgeActions) *cobra.Command { }, } // Bind flags - cmd.Flags().StringSliceVar(&flags.relayURLs, "relay-urls", []string{}, "List of comma separated relay URLs used to connect to mev relay. Example: 'sedge generate mev-boost --relay-urls=https://0xac6e77dfe25ecd6110b8e780608cce0dab71fdd5ebea22a16c0205200f2f8e2e3ad3b71d3499c54ad14d6c21b41a37ae@boost-relay.flashbots.net,https://0xa1559ace749633b997cb3fdacffb890aeebdb0f5a3b6aaa7eeeaf1a38af0a8fe88b9e4b1f61f236d2e64d95733327a62@relay.ultrasound.money'") + cmd.Flags().StringSliceVar(&flags.relayURLs, "relay-urls", []string{}, "List of comma separated relay URLs used to connect to mev relay. Example: 'sedge generate full-node --relay-urls=https://0xac6e77dfe25ecd6110b8e780608cce0dab71fdd5ebea22a16c0205200f2f8e2e3ad3b71d3499c54ad14d6c21b41a37ae@boost-relay.flashbots.net,https://0xa1559ace749633b997cb3fdacffb890aeebdb0f5a3b6aaa7eeeaf1a38af0a8fe88b9e4b1f61f236d2e64d95733327a62@relay.ultrasound.money'\n"+ + "Note: When setting up a Lido node, the provided relay URLs will be automatically set by the system.") cmd.Flags().StringVarP(&flags.mevImage, "mev-boost-image", "m", "", "Custom docker image to use for Mev Boost. Example: 'sedge generate mev-boost --mev-boost-image flashbots/mev-boost:latest-portable'") cmd.Flags().StringVarP(&network, "network", "n", "mainnet", "Target network. e.g. mainnet, sepolia etc.") cmd.Flags().SortFlags = false From c7859e4ea54a745193ed39f765994bc20179d26d Mon Sep 17 00:00:00 2001 From: Haneen Khalifa Date: Fri, 28 Jun 2024 17:31:17 +0300 Subject: [PATCH 05/31] style: adjust formatting --- cli/cli.go | 11 +++++------ cli/generate.go | 17 ++++++++--------- internal/lido/contracts/feeRecipient.go | 4 ++-- .../contracts/mevboostrelaylist/get_relays.go | 4 ++-- 4 files changed, 17 insertions(+), 19 deletions(-) diff --git a/cli/cli.go b/cli/cli.go index 86a475d3..2eee3f51 100644 --- a/cli/cli.go +++ b/cli/cli.go @@ -54,8 +54,8 @@ const ( NodeTypeValidator = "validator" EthereumNode = "ethereum-node" - LidoNode = "lido-node" - + LidoNode = "lido-node" + Randomize = "randomize" SourceTypeExisting = "existing" @@ -622,7 +622,6 @@ func runPromptActions(p ui.Prompter, o *CliCmdOptions, actions ...promptAction) return nil } - func selectNodeSetup(p ui.Prompter, o *CliCmdOptions) (err error) { options := []string{EthereumNode, LidoNode} index, err := p.Select("Select node setup", "", options) @@ -635,9 +634,9 @@ func selectNodeSetup(p ui.Prompter, o *CliCmdOptions) (err error) { func selectNetwork(p ui.Prompter, o *CliCmdOptions) error { var options []string - if o.nodeSetup == LidoNode{ + if o.nodeSetup == LidoNode { options = contracts.GetLidoSupportedNetworks() - }else{ + } else { options = []string{NetworkMainnet, NetworkSepolia, NetworkGnosis, NetworkChiado, NetworkHolesky} } index, err := p.Select("Select network", "", options) @@ -919,7 +918,7 @@ func inputFeeRecipient(p ui.Prompter, o *CliCmdOptions) (err error) { func inputFeeRecipientNoValidator(p ui.Prompter, o *CliCmdOptions) (err error) { if o.nodeSetup == LidoNode { - feeRecipient:= contracts.FeeRecipient[o.genData.Network] + feeRecipient := contracts.FeeRecipient[o.genData.Network] o.genData.FeeRecipient = feeRecipient.FeeRecipientAddress return } diff --git a/cli/generate.go b/cli/generate.go index 1f316cf5..39a34358 100644 --- a/cli/generate.go +++ b/cli/generate.go @@ -28,7 +28,6 @@ import ( "github.com/NethermindEth/sedge/internal/lido/contracts" "github.com/NethermindEth/sedge/internal/lido/contracts/mevboostrelaylist" - "github.com/NethermindEth/sedge/cli/actions" "github.com/NethermindEth/sedge/configs" "github.com/NethermindEth/sedge/internal/pkg/clients" @@ -45,7 +44,7 @@ var ( network string logging string containerTag string - lidoNode bool + lidoNode bool ) const ( @@ -114,7 +113,7 @@ You can generate: cmd.AddCommand(ConsensusSubCmd(sedgeAction)) cmd.AddCommand(ValidatorSubCmd(sedgeAction)) cmd.AddCommand(MevBoostSubCmd(sedgeAction)) - + cmd.PersistentFlags().BoolVar(&lidoNode, "lido", false, "generate Lido CSM node") cmd.PersistentFlags().StringVarP(&generationPath, "path", "p", configs.DefaultAbsSedgeDataPath, "generation path for sedge data. Default is sedge-data") cmd.PersistentFlags().StringVarP(&network, "network", "n", "mainnet", "Target network. e.g. mainnet,sepolia, holesky, gnosis, chiado, etc.") @@ -263,11 +262,11 @@ func runGenCmd(out io.Writer, flags *GenCmdFlags, sedgeAction actions.SedgeActio } } - //Overwrite feeRecipient and relayURLs for Lido Node - if lidoNode{ + // Overwrite feeRecipient and relayURLs for Lido Node + if lidoNode { feeRecipient := contracts.FeeRecipient[network] flags.feeRecipient = feeRecipient.FeeRecipientAddress - + flags.relayURLs, _ = mevboostrelaylist.GetRelaysURI(network) } @@ -493,7 +492,7 @@ func loadJWTSecret(from string) (absFrom string, err error) { } func validateLido(network string, flags *GenCmdFlags) error { - if !flags.noMev{ + if !flags.noMev { _, ok := mevboostrelaylist.DeployedContractAddresses[network] if !ok { options := mevboostrelaylist.GetLidoSupportedNetworksMevBoost() @@ -505,6 +504,6 @@ func validateLido(network string, flags *GenCmdFlags) error { options := contracts.GetLidoSupportedNetworks() return fmt.Errorf("invalid network: Choose valid network for Lido: %v", options) } - + return nil -} \ No newline at end of file +} diff --git a/internal/lido/contracts/feeRecipient.go b/internal/lido/contracts/feeRecipient.go index 0ca9dc47..f5dd7361 100644 --- a/internal/lido/contracts/feeRecipient.go +++ b/internal/lido/contracts/feeRecipient.go @@ -24,10 +24,10 @@ var FeeRecipient = map[string]FeeRecipientConfig{ }, } -func GetLidoSupportedNetworks()[]string{ +func GetLidoSupportedNetworks() []string { networks := []string{} for network := range FeeRecipient { networks = append(networks, network) } return networks -} \ No newline at end of file +} diff --git a/internal/lido/contracts/mevboostrelaylist/get_relays.go b/internal/lido/contracts/mevboostrelaylist/get_relays.go index 18084727..9790b326 100644 --- a/internal/lido/contracts/mevboostrelaylist/get_relays.go +++ b/internal/lido/contracts/mevboostrelaylist/get_relays.go @@ -143,10 +143,10 @@ func GetRelaysURI(network string) ([]string, error) { return relayURIs, err } -func GetLidoSupportedNetworksMevBoost()[]string{ +func GetLidoSupportedNetworksMevBoost() []string { networks := []string{} for network := range DeployedContractAddresses { - networks= append(networks, network) + networks = append(networks, network) } return networks } From b9cd4f801683b412efa244cc8fff7c5a354f7cbc Mon Sep 17 00:00:00 2001 From: Haneen Khalifa Date: Fri, 28 Jun 2024 17:40:09 +0300 Subject: [PATCH 06/31] fix: adjust formatting --- cli/sub_gen.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/cli/sub_gen.go b/cli/sub_gen.go index f1c438d3..eb97cead 100644 --- a/cli/sub_gen.go +++ b/cli/sub_gen.go @@ -55,7 +55,7 @@ Additionally, you can use this syntax ':' to override the if err := validateCustomNetwork(&flags.CustomFlags, network); err != nil { return err } - if err := validateLido(network, &flags); err != nil && lidoNode { + if err := validateLido(network, &flags); err != nil && lidoNode { return err } return preValidationGenerateCmd(network, logging, &flags) @@ -130,7 +130,7 @@ func ExecutionSubCmd(sedgeAction actions.SedgeActions) *cobra.Command { if err := validateCustomNetwork(&flags.CustomFlags, network); err != nil { return err } - if err := validateLido(network, &flags); err != nil && lidoNode { + if err := validateLido(network, &flags); err != nil && lidoNode { return err } return preValidationGenerateCmd(network, logging, &flags) @@ -178,7 +178,7 @@ func ConsensusSubCmd(sedgeAction actions.SedgeActions) *cobra.Command { if err := validateCustomNetwork(&flags.CustomFlags, network); err != nil { return err } - if err := validateLido(network, &flags); err != nil && lidoNode { + if err := validateLido(network, &flags); err != nil && lidoNode { return err } return preValidationGenerateCmd(network, logging, &flags) @@ -241,7 +241,7 @@ func ValidatorSubCmd(sedgeAction actions.SedgeActions) *cobra.Command { if err := validateCustomNetwork(&flags.CustomFlags, network); err != nil { return err } - if err := validateLido(network, &flags); err != nil && lidoNode { + if err := validateLido(network, &flags); err != nil && lidoNode { return err } return preValidationGenerateCmd(network, logging, &flags) @@ -288,7 +288,7 @@ func MevBoostSubCmd(sedgeAction actions.SedgeActions) *cobra.Command { } // Bind flags cmd.Flags().StringSliceVar(&flags.relayURLs, "relay-urls", []string{}, "List of comma separated relay URLs used to connect to mev relay. Example: 'sedge generate full-node --relay-urls=https://0xac6e77dfe25ecd6110b8e780608cce0dab71fdd5ebea22a16c0205200f2f8e2e3ad3b71d3499c54ad14d6c21b41a37ae@boost-relay.flashbots.net,https://0xa1559ace749633b997cb3fdacffb890aeebdb0f5a3b6aaa7eeeaf1a38af0a8fe88b9e4b1f61f236d2e64d95733327a62@relay.ultrasound.money'\n"+ - "Note: When setting up a Lido node, the provided relay URLs will be automatically set by the system.") + "Note: When setting up a Lido node, the provided relay URLs will be automatically set by the system.") cmd.Flags().StringVarP(&flags.mevImage, "mev-boost-image", "m", "", "Custom docker image to use for Mev Boost. Example: 'sedge generate mev-boost --mev-boost-image flashbots/mev-boost:latest-portable'") cmd.Flags().StringVarP(&network, "network", "n", "mainnet", "Target network. e.g. mainnet, sepolia etc.") cmd.Flags().SortFlags = false From 5c83be234a9b4fad813b6bf053592fc0f1c72bb2 Mon Sep 17 00:00:00 2001 From: Haneen Khalifa Date: Mon, 1 Jul 2024 13:59:08 +0300 Subject: [PATCH 07/31] test: add e2e test for non-interactive setup --- cli/cli.go | 2 +- e2e/generate_test.go | 46 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+), 1 deletion(-) diff --git a/cli/cli.go b/cli/cli.go index 2eee3f51..78b749bb 100644 --- a/cli/cli.go +++ b/cli/cli.go @@ -635,7 +635,7 @@ func selectNodeSetup(p ui.Prompter, o *CliCmdOptions) (err error) { func selectNetwork(p ui.Prompter, o *CliCmdOptions) error { var options []string if o.nodeSetup == LidoNode { - options = contracts.GetLidoSupportedNetworks() + options = []string{NetworkMainnet, NetworkHolesky, NetworkSepolia} } else { options = []string{NetworkMainnet, NetworkSepolia, NetworkGnosis, NetworkChiado, NetworkHolesky} } diff --git a/e2e/generate_test.go b/e2e/generate_test.go index 08c28970..4b91f6aa 100644 --- a/e2e/generate_test.go +++ b/e2e/generate_test.go @@ -28,3 +28,49 @@ func TestGenerate_FullNode_GoerliNotSupported(t *testing.T) { // Run test case e2eTest.run() } + +func TestGenerate_FullNode_Lido_Sepolia_NoMEV(t *testing.T) { + // Test context + var ( + runErr error + ) + // Build test case + e2eTest := newE2ETestCase( + t, + // Arrange + nil, + // Act + func(t *testing.T, binaryPath string) { + runErr = runSedge(t, binaryPath, "generate", "full-node", "--lido", "--network", "sepolia", "--no-mev-boost") + }, + // Assert + func(t *testing.T) { + assert.NoError(t, runErr, "generate command should succeed with the given arguments") + }, + ) + // Run test case + e2eTest.run() +} + +func TestGenerate_FullNode_Lido_Sepolia(t *testing.T) { + // Test context + var ( + runErr error + ) + // Build test case + e2eTest := newE2ETestCase( + t, + // Arrange + nil, + // Act + func(t *testing.T, binaryPath string) { + runErr = runSedge(t, binaryPath, "generate", "full-node", "--lido", "--network", "sepolia") + }, + // Assert + func(t *testing.T) { + assert.NoError(t, runErr, "generate command should fail") + }, + ) + // Run test case + e2eTest.run() +} \ No newline at end of file From b4d431a31a410c12f47e3a917ef46c8b52494640 Mon Sep 17 00:00:00 2001 From: Haneen Khalifa Date: Mon, 1 Jul 2024 14:01:14 +0300 Subject: [PATCH 08/31] fix: adjust e2e test --- e2e/generate_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/e2e/generate_test.go b/e2e/generate_test.go index 4b91f6aa..6abbf9a8 100644 --- a/e2e/generate_test.go +++ b/e2e/generate_test.go @@ -68,7 +68,7 @@ func TestGenerate_FullNode_Lido_Sepolia(t *testing.T) { }, // Assert func(t *testing.T) { - assert.NoError(t, runErr, "generate command should fail") + assert.Error(t, runErr, "generate command should fail") }, ) // Run test case From 9d8f23d89979e7dc7b4c77a35d2850090982622a Mon Sep 17 00:00:00 2001 From: Haneen Khalifa Date: Mon, 1 Jul 2024 14:53:23 +0300 Subject: [PATCH 09/31] style: adjust formatting --- e2e/generate_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/e2e/generate_test.go b/e2e/generate_test.go index 6abbf9a8..37cd70d9 100644 --- a/e2e/generate_test.go +++ b/e2e/generate_test.go @@ -73,4 +73,4 @@ func TestGenerate_FullNode_Lido_Sepolia(t *testing.T) { ) // Run test case e2eTest.run() -} \ No newline at end of file +} From 8ef0e7da7ea3409414ddae1ccd1b1d11c6138105 Mon Sep 17 00:00:00 2001 From: Haneen Khalifa Date: Mon, 1 Jul 2024 15:13:58 +0300 Subject: [PATCH 10/31] test: Add mocks test for non-interactive setup in cli/generate_test.go --- cli/generate_test.go | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/cli/generate_test.go b/cli/generate_test.go index dcfabe7f..c84aa38e 100644 --- a/cli/generate_test.go +++ b/cli/generate_test.go @@ -26,6 +26,7 @@ import ( "github.com/NethermindEth/sedge/cli/actions" "github.com/NethermindEth/sedge/configs" + "github.com/NethermindEth/sedge/internal/lido/contracts/mevboostrelaylist" "github.com/NethermindEth/sedge/test" log "github.com/sirupsen/logrus" "github.com/stretchr/testify/assert" @@ -48,6 +49,7 @@ type globalFlags struct { generationPath string network string logging string + lidoNode bool } type generateCmdTestCase struct { @@ -72,6 +74,9 @@ func (flags *globalFlags) argsList() []string { if flags.logging != "" { s = append(s, "--logging", flags.logging) } + if flags.lidoNode { + s = append(s, "--lido") + } return s } @@ -1310,6 +1315,34 @@ func TestGenerateCmd(t *testing.T) { globalFlags{}, nil, }, + { + "Lido Full-node - Sepolia without MEV", + subCmd{ + name: "full-node", + }, + GenCmdFlags{ + noMev: true, + }, + globalFlags{ + network: NetworkSepolia, + lidoNode: true, + }, + nil, + }, + { + "Lido Full-node - unsupported Sepolia with MEV", + subCmd{ + name: "full-node", + }, + GenCmdFlags{ + noMev: false, + }, + globalFlags{ + network: NetworkSepolia, + lidoNode: true, + }, + fmt.Errorf("invalid network: Choose valid network for Lido with MEV-Boost: %v", mevboostrelaylist.GetLidoSupportedNetworksMevBoost()), + }, } // TODO: Add test cases for Execution fallback urls From 1025efadd5a9510641bb5542445e388a9b695965 Mon Sep 17 00:00:00 2001 From: Marcos Maceo Date: Thu, 4 Jul 2024 17:04:56 +0400 Subject: [PATCH 11/31] doc: documentation for lido csm on sedge --- docs/docs/commands/generate.mdx | 10 +++++ docs/docs/quickstart/lido.mdx | 75 +++++++++++++++++++++++++++++++++ 2 files changed, 85 insertions(+) create mode 100644 docs/docs/quickstart/lido.mdx diff --git a/docs/docs/commands/generate.mdx b/docs/docs/commands/generate.mdx index 4bd2cb7e..21ae03a4 100644 --- a/docs/docs/commands/generate.mdx +++ b/docs/docs/commands/generate.mdx @@ -36,6 +36,7 @@ Available Commands: Flags: --container-tag string Container tag to use. If defined, sedge will add to each container and the network, a suffix with the tag. e.g. sedge-validator-client -> sedge-validator-client-. -h, --help help for generate + --lido generate Lido CSM node --logging string Docker logging driver used by all the services. Set 'none' to use the default docker logging driver. Possible values: [none json] (default "json") -n, --network string Target network. e.g. mainnet, sepolia, holesky, gnosis, chiado, etc. (default "mainnet") -p, --path string generation path for sedge data. Default is sedge-data (default "/path/to/sedge-data") @@ -46,6 +47,15 @@ Global Flags: Use "sedge generate [command] --help" for more information about a command. ``` +### Lido Flow + +With the `--lido` flag, you can generate a Lido CSM node. This will generate a full node, a execution, a consensus, a validator o a mev-boost node with the Lido CSM configuration. + +It will include in the configuration of the selected node: +- Lido Withdrawal Credentials +- Lido Fee Recipient Address +- Designated MEV-Boost Relay URL + ### Sub Commands #### Full-Node diff --git a/docs/docs/quickstart/lido.mdx b/docs/docs/quickstart/lido.mdx new file mode 100644 index 00000000..8745fe08 --- /dev/null +++ b/docs/docs/quickstart/lido.mdx @@ -0,0 +1,75 @@ +--- +sidebar_position: 9 +id: staking-with-lido +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +# Staking with Lido using Sedge + +## What is the Lido Community Staking Module (CSM)? + +The Lido Community Staking Module (CSM) is a protocol that allows anyone, especially community stakers and solo stakers, +to become a Lido Node Operator by providing an ETH-based bond as security collateral. CSM offers various benefits, +such as smoothed rewards, lower bond requirements, and a user-friendly experience. + +Sedge supports the Lido CSM, allowing users to generate validator keys and set up their full nodes with ease. This guide +will walk you through the process of staking with Lido using Sedge. + +## Generating Validator Keys with Sedge + +To get started with CSM using Sedge, you first need to generate your validator keys. Sedge simplifies this process +with the `sedge keys` command, to generate keys compatible with Lido CSM, use the `--lido` flag: + +```bash +sedge keys --lido +``` + +Once generated the keys, you can setup your full node: + +```bash +sedge generate full-node --lido -n holesky + +:::note + +This command will generate a setup with the Lido Community Staking Module (CSM) enabled for the Holesky testnet. +You can set other networks by changing the `-n` flag, if supported. + +::: + +``` +This command will generate a new set of validator keys with the following parameters: + +- `withdrawal_address` set to the Lido Withdrawal Vault address +- `deposit_amount` set to 32 ETH +- `chain` set to the appropriate network (e.g., `holesky`, `mainnet`) +- `fee_recipient` set to the Lido Execution Layer Rewards Vault address +- `mev_relay` set to the designated MEV relays for Lido CSM on the appropriate network + +## Configuring Your Node Operator + +After generating your validator keys and setting up your node, you need to configure yourself as a node operator to +work with Lido CSM: + +1. Upload the newly generated deposit data file pertaining to your CSM keystores onto the Lido CSM Widget +and provide the required bond amount in ETH, stETH, or wstETH. Before uploading, ensure that your nodes are synced, +running, and ready for validator activation. + +2. Wait for your CSM validator keys to be deposited through the protocol and ensure your node remains online in the +meantime. + +## Managing Your Node Operator + +Once your validator keys are deposited and your node is running, you can manage your Node Operator using the Lido +CSM Widget. This interface allows you to: + +- Claim bond and rewards +- Add extra bond amount +- Propose new Rewards and Manager addresses +- Add or remove validator keys + +Remember to keep your node running smoothly and follow the Lido protocol rules to avoid any penalties or bond confiscation. + +For more detailed information on managing your Node Operator, refer to the +[Lido CSM documentation](https://operatorportal.lido.fi/modules/community-staking-module). \ No newline at end of file From c113b6109bc7bbc597f0cb4bafa25c96d85e73dd Mon Sep 17 00:00:00 2001 From: Haneen Khalifa Date: Wed, 3 Jul 2024 21:48:24 +0300 Subject: [PATCH 12/31] test: adjust e2e tests for lido flow --- cli/generate_test.go | 27 ++++++++++++ e2e/generate_test.go | 102 +++++++++++++++++++++++++++++++++++++++---- go.mod | 4 ++ go.sum | 8 ++++ 4 files changed, 133 insertions(+), 8 deletions(-) diff --git a/cli/generate_test.go b/cli/generate_test.go index c84aa38e..5ed7e4af 100644 --- a/cli/generate_test.go +++ b/cli/generate_test.go @@ -26,6 +26,7 @@ import ( "github.com/NethermindEth/sedge/cli/actions" "github.com/NethermindEth/sedge/configs" + "github.com/NethermindEth/sedge/internal/lido/contracts" "github.com/NethermindEth/sedge/internal/lido/contracts/mevboostrelaylist" "github.com/NethermindEth/sedge/test" log "github.com/sirupsen/logrus" @@ -1343,6 +1344,32 @@ func TestGenerateCmd(t *testing.T) { }, fmt.Errorf("invalid network: Choose valid network for Lido with MEV-Boost: %v", mevboostrelaylist.GetLidoSupportedNetworksMevBoost()), }, + { + "Lido Full-node - Holesky no validator", + subCmd{ + name: "full-node", + }, + GenCmdFlags{ + noValidator: true, + }, + globalFlags{ + network: NetworkHolesky, + lidoNode: true, + }, + nil, + }, + { + "Lido Full-node - unsupported Gnosis", + subCmd{ + name: "full-node", + }, + GenCmdFlags{}, + globalFlags{ + network: NetworkGnosis, + lidoNode: true, + }, + fmt.Errorf("invalid network: Choose valid network for Lido: %v", contracts.GetLidoSupportedNetworks()), + }, } // TODO: Add test cases for Execution fallback urls diff --git a/e2e/generate_test.go b/e2e/generate_test.go index 37cd70d9..5197ceff 100644 --- a/e2e/generate_test.go +++ b/e2e/generate_test.go @@ -1,8 +1,13 @@ package e2e import ( + "path/filepath" + "strings" "testing" + "github.com/NethermindEth/sedge/internal/lido/contracts" + "github.com/NethermindEth/sedge/internal/lido/contracts/mevboostrelaylist" + "github.com/joho/godotenv" "github.com/stretchr/testify/assert" ) @@ -29,6 +34,52 @@ func TestGenerate_FullNode_GoerliNotSupported(t *testing.T) { e2eTest.run() } +func TestGenerate_FullNode_Lido_GnosisNotSupported(t *testing.T) { + // Test context + var ( + runErr error + ) + // Build test case + e2eTest := newE2ETestCase( + t, + // Arrange + nil, + // Act + func(t *testing.T, binaryPath string, dataDirPath string) { + runErr = runSedge(t, binaryPath, "generate", "--lido", "full-node", "--network", "gnosis") + }, + // Assert + func(t *testing.T, dataDirPath string) { + assert.Error(t, runErr, "generate command should fail") + }, + ) + // Run test case + e2eTest.run() +} + +func TestGenerate_FullNode_Lido_SepoliaNotSupported(t *testing.T) { + // Test context + var ( + runErr error + ) + // Build test case + e2eTest := newE2ETestCase( + t, + // Arrange + nil, + // Act + func(t *testing.T, binaryPath string, dataDirPath string) { + runErr = runSedge(t, binaryPath, "generate", "--lido", "full-node", "--network", "sepolia") + }, + // Assert + func(t *testing.T, dataDirPath string) { + assert.Error(t, runErr, "generate command should fail") + }, + ) + // Run test case + e2eTest.run() +} + func TestGenerate_FullNode_Lido_Sepolia_NoMEV(t *testing.T) { // Test context var ( @@ -40,19 +91,35 @@ func TestGenerate_FullNode_Lido_Sepolia_NoMEV(t *testing.T) { // Arrange nil, // Act - func(t *testing.T, binaryPath string) { - runErr = runSedge(t, binaryPath, "generate", "full-node", "--lido", "--network", "sepolia", "--no-mev-boost") + func(t *testing.T, binaryPath string, dataDirPath string) { + runErr = runSedge(t, binaryPath, "generate", "--lido", "full-node", "--network", "sepolia", "--no-mev-boost") }, // Assert - func(t *testing.T) { + func(t *testing.T, dataDirPath string) { assert.NoError(t, runErr, "generate command should succeed with the given arguments") + generateDataFilePath := filepath.Join(dataDirPath, ".env") + assert.FileExists(t, generateDataFilePath, ".env file should be created") + + // Read .env file + envMap, err := godotenv.Read(generateDataFilePath) + assert.NoError(t, err, "should be able to read .env file") + + // Read FEE_RECIPIENT value + feeRecipient, exists := envMap["FEE_RECIPIENT"] + assert.True(t, exists, "FEE_RECIPIENT should exist in .env file") + expectedFeeRecipient := contracts.FeeRecipient["sepolia"].FeeRecipientAddress + assert.Equal(t, expectedFeeRecipient, feeRecipient, "FEE_RECIPIENT value should match expected value") + + // Read RELAY_URLS value + _, exists = envMap["RELAY_URLS"] + assert.False(t, exists, "RELAY_URLS shouldn't exist in .env file") }, ) // Run test case e2eTest.run() } -func TestGenerate_FullNode_Lido_Sepolia(t *testing.T) { +func TestGenerate_FullNode_Lido_Mainnet(t *testing.T) { // Test context var ( runErr error @@ -63,12 +130,31 @@ func TestGenerate_FullNode_Lido_Sepolia(t *testing.T) { // Arrange nil, // Act - func(t *testing.T, binaryPath string) { - runErr = runSedge(t, binaryPath, "generate", "full-node", "--lido", "--network", "sepolia") + func(t *testing.T, binaryPath string, dataDirPath string) { + runErr = runSedge(t, binaryPath, "generate", "--lido", "full-node", "--network", "mainnet") }, // Assert - func(t *testing.T) { - assert.Error(t, runErr, "generate command should fail") + func(t *testing.T, dataDirPath string) { + assert.NoError(t, runErr, "generate command should succeed") + generateDataFilePath := filepath.Join(dataDirPath, ".env") + assert.FileExists(t, generateDataFilePath, ".env file should be created") + + // Read .env file + envMap, err := godotenv.Read(generateDataFilePath) + assert.NoError(t, err, "should be able to read .env file") + + // Read FEE_RECIPIENT value + feeRecipient, exists := envMap["FEE_RECIPIENT"] + assert.True(t, exists, "FEE_RECIPIENT should exist in .env file") + expectedFeeRecipient := contracts.FeeRecipient["mainnet"].FeeRecipientAddress + assert.Equal(t, expectedFeeRecipient, feeRecipient, "FEE_RECIPIENT value should match expected value") + + // Read RELAY_URLS value + relayURLs, exists := envMap["RELAY_URLS"] + assert.True(t, exists, "RELAY_URLS should exist in .env file") + relayURLsList := strings.Split(relayURLs, ",") + expectedRelayURLs, _ := mevboostrelaylist.GetRelaysURI("mainnet") + assert.Equal(t, expectedRelayURLs, relayURLsList, "RELAY_URLS value should match expected value") }, ) // Run test case diff --git a/go.mod b/go.mod index fa8020c7..250f125a 100644 --- a/go.mod +++ b/go.mod @@ -16,6 +16,7 @@ require ( github.com/google/uuid v1.4.0 github.com/herumi/bls-eth-go-binary v1.29.1 github.com/jarcoal/httpmock v1.2.0 + github.com/joho/godotenv v1.5.1 github.com/opencontainers/image-spec v1.0.2 github.com/otiai10/copy v1.9.0 github.com/protolambda/go-keystorev4 v0.0.0-20211007151826-f20444f6d564 @@ -91,9 +92,12 @@ require ( github.com/xeipuuv/gojsonschema v1.2.0 // indirect golang.org/x/crypto v0.22.0 // indirect golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa // indirect + golang.org/x/mod v0.17.0 // indirect golang.org/x/net v0.24.0 // indirect golang.org/x/sys v0.20.0 // indirect golang.org/x/term v0.19.0 // indirect golang.org/x/text v0.14.0 // indirect + golang.org/x/tools v0.20.0 // indirect + mvdan.cc/gofumpt v0.6.0 // indirect rsc.io/tmplfunc v0.0.3 // indirect ) diff --git a/go.sum b/go.sum index 7dc33232..0762f748 100644 --- a/go.sum +++ b/go.sum @@ -147,6 +147,8 @@ github.com/jackpal/go-nat-pmp v1.0.2 h1:KzKSgb7qkJvOUTqYl9/Hg/me3pWgBmERKrTGD7Bd github.com/jackpal/go-nat-pmp v1.0.2/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc= github.com/jarcoal/httpmock v1.2.0 h1:gSvTxxFR/MEMfsGrvRbdfpRUMBStovlSRLw0Ep1bwwc= github.com/jarcoal/httpmock v1.2.0/go.mod h1:oCoTsnAz4+UoOUIf5lJOWV2QQIW5UoeUI6aM2YnWAZk= +github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= +github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs= github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= github.com/kilic/bls12-381 v0.1.0 h1:encrdjqKMEvabVQ7qYOKu1OvhqpK4s47wDYtNiPtlp4= @@ -301,6 +303,8 @@ golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa/go.mod h1:zk2irFbV9DP96SEBUU golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= +golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -353,6 +357,8 @@ golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roY golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.20.0 h1:hz/CVckiOxybQvFw6h7b/q80NTr9IUQb4s1IIzW7KNY= +golang.org/x/tools v0.20.0/go.mod h1:WvitBU7JJf6A4jOdg4S1tviW9bhUxkgeCui/0JHctQg= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -372,5 +378,7 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gotest.tools/v3 v3.4.0 h1:ZazjZUfuVeZGLAmlKKuyv3IKP5orXcwtOwDQH6YVr6o= gotest.tools/v3 v3.4.0/go.mod h1:CtbdzLSsqVhDgMtKsx03ird5YTGB3ar27v0u/yKBW5g= +mvdan.cc/gofumpt v0.6.0 h1:G3QvahNDmpD+Aek/bNOLrFR2XC6ZAdo62dZu65gmwGo= +mvdan.cc/gofumpt v0.6.0/go.mod h1:4L0wf+kgIPZtcCWXynNS2e6bhmj73umwnuXSZarixzA= rsc.io/tmplfunc v0.0.3 h1:53XFQh69AfOa8Tw0Jm7t+GV7KZhOi6jzsCzTtKbMvzU= rsc.io/tmplfunc v0.0.3/go.mod h1:AG3sTPzElb1Io3Yg4voV9AGZJuleGAwaVRxL9M49PhA= From 1c147a1550104679e2b02ec48bd2dadd61589810 Mon Sep 17 00:00:00 2001 From: Haneen Khalifa Date: Wed, 3 Jul 2024 21:53:23 +0300 Subject: [PATCH 13/31] fix: update go.mod --- go.mod | 3 --- go.sum | 6 ------ 2 files changed, 9 deletions(-) diff --git a/go.mod b/go.mod index 250f125a..44e9acb5 100644 --- a/go.mod +++ b/go.mod @@ -92,12 +92,9 @@ require ( github.com/xeipuuv/gojsonschema v1.2.0 // indirect golang.org/x/crypto v0.22.0 // indirect golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa // indirect - golang.org/x/mod v0.17.0 // indirect golang.org/x/net v0.24.0 // indirect golang.org/x/sys v0.20.0 // indirect golang.org/x/term v0.19.0 // indirect golang.org/x/text v0.14.0 // indirect - golang.org/x/tools v0.20.0 // indirect - mvdan.cc/gofumpt v0.6.0 // indirect rsc.io/tmplfunc v0.0.3 // indirect ) diff --git a/go.sum b/go.sum index 0762f748..7ca5a72b 100644 --- a/go.sum +++ b/go.sum @@ -303,8 +303,6 @@ golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa/go.mod h1:zk2irFbV9DP96SEBUU golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= -golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -357,8 +355,6 @@ golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roY golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.20.0 h1:hz/CVckiOxybQvFw6h7b/q80NTr9IUQb4s1IIzW7KNY= -golang.org/x/tools v0.20.0/go.mod h1:WvitBU7JJf6A4jOdg4S1tviW9bhUxkgeCui/0JHctQg= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -378,7 +374,5 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gotest.tools/v3 v3.4.0 h1:ZazjZUfuVeZGLAmlKKuyv3IKP5orXcwtOwDQH6YVr6o= gotest.tools/v3 v3.4.0/go.mod h1:CtbdzLSsqVhDgMtKsx03ird5YTGB3ar27v0u/yKBW5g= -mvdan.cc/gofumpt v0.6.0 h1:G3QvahNDmpD+Aek/bNOLrFR2XC6ZAdo62dZu65gmwGo= -mvdan.cc/gofumpt v0.6.0/go.mod h1:4L0wf+kgIPZtcCWXynNS2e6bhmj73umwnuXSZarixzA= rsc.io/tmplfunc v0.0.3 h1:53XFQh69AfOa8Tw0Jm7t+GV7KZhOi6jzsCzTtKbMvzU= rsc.io/tmplfunc v0.0.3/go.mod h1:AG3sTPzElb1Io3Yg4voV9AGZJuleGAwaVRxL9M49PhA= From cd62b93c3eb988c47f0b59a00df1113633014288 Mon Sep 17 00:00:00 2001 From: Haneen Khalifa Date: Wed, 3 Jul 2024 22:05:17 +0300 Subject: [PATCH 14/31] fix: Adjust generate_test.go --- cli/generate_test.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/cli/generate_test.go b/cli/generate_test.go index 5ed7e4af..999bebf6 100644 --- a/cli/generate_test.go +++ b/cli/generate_test.go @@ -1363,7 +1363,9 @@ func TestGenerateCmd(t *testing.T) { subCmd{ name: "full-node", }, - GenCmdFlags{}, + GenCmdFlags{ + noMev: true, + }, globalFlags{ network: NetworkGnosis, lidoNode: true, From 36d69998a8a5c7f12c1a91539b17c65ea5106526 Mon Sep 17 00:00:00 2001 From: Haneen Khalifa Date: Thu, 4 Jul 2024 18:02:13 +0300 Subject: [PATCH 15/31] feat: Add --lido to sedge keys --- cli/keys.go | 19 ++- configs/messages.go | 1 + configs/public_rpcs.go | 1 - e2e/keys_test.go | 145 +++++++++++++++++++ internal/lido/contracts/withdrawalAddress.go | 29 ++++ 5 files changed, 193 insertions(+), 2 deletions(-) create mode 100644 internal/lido/contracts/withdrawalAddress.go diff --git a/cli/keys.go b/cli/keys.go index 3b9d4ea9..f6550ff7 100644 --- a/cli/keys.go +++ b/cli/keys.go @@ -21,6 +21,7 @@ import ( "path/filepath" "github.com/NethermindEth/sedge/configs" + "github.com/NethermindEth/sedge/internal/lido/contracts" "github.com/NethermindEth/sedge/internal/pkg/commands" "github.com/NethermindEth/sedge/internal/pkg/keystores" "github.com/NethermindEth/sedge/internal/ui" @@ -40,6 +41,7 @@ type KeysCmdFlags struct { numberVal int64 randomPassphrase bool install bool + lidoNode bool } func KeysCmd(cmdRunner commands.CommandRunner, p ui.Prompter) *cobra.Command { @@ -98,6 +100,18 @@ func KeysCmd(cmdRunner commands.CommandRunner, p ui.Prompter) *cobra.Command { return nil }, Run: func(cmd *cobra.Command, args []string) { + // Incompatible --lido and --eth1-withdrawal-address together + if flags.lidoNode && flags.eth1WithdrawalAddress != ""{ + log.Fatalf(configs.IncompatibleLidoAndEth1Withdrawal) + } + // validate network for Lido + if flags.lidoNode{ + _, ok := contracts.FeeRecipient[network] + if !ok { + options := contracts.GetLidoKeysSupportedNetworks() + log.Fatalf("invalid network: Choose valid network for Lido: %v", options) + } + } // Warn about withdrawal address if flags.eth1WithdrawalAddress != "" { log.Warn(configs.WithdrawalAddressDefinedWarning) @@ -164,7 +178,9 @@ func KeysCmd(cmdRunner commands.CommandRunner, p ui.Prompter) *cobra.Command { keystorePath := filepath.Join(flags.path, "keystore") var withdrawalAddress string - if flags.eth1WithdrawalAddress != "" { + if flags.lidoNode{ + withdrawalAddress = contracts.WithdrawalAddress[flags.network].WithdrawalAddress[2:] + }else if flags.eth1WithdrawalAddress != "" { withdrawalAddress = flags.eth1WithdrawalAddress[2:] } @@ -197,6 +213,7 @@ func KeysCmd(cmdRunner commands.CommandRunner, p ui.Prompter) *cobra.Command { }, } // Flag binds + cmd.PersistentFlags().BoolVar(&flags.lidoNode, "lido", false, "Lido CSM node") cmd.Flags().StringVarP(&flags.network, "network", "n", "mainnet", "Target network. e.g. mainnet,sepolia, holesky, gnosis, chiado etc.") cmd.Flags().StringVarP(&flags.path, "path", "p", configs.DefaultAbsSedgeDataPath, "Absolute path to keystore folder. e.g. /home/user/keystore") cmd.Flags().StringVar(&flags.eth1WithdrawalAddress, "eth1-withdrawal-address", "", "If this field is set and valid, the given Eth1 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.") diff --git a/configs/messages.go b/configs/messages.go index f57f2177..c0ab2891 100644 --- a/configs/messages.go +++ b/configs/messages.go @@ -114,6 +114,7 @@ Happy Staking! GettingCustomGenesis = "Getting custom genesis..." GettingCustomNetworkConfig = "Getting custom network config..." WritingCustomDeployBlock = "Writing custom deploy block..." + IncompatibleLidoAndEth1Withdrawal = "Incompatible flags --lido, and --eth1-withdrawal-address can't be used together" ) var DefaultAbsSedgeDataPath string diff --git a/configs/public_rpcs.go b/configs/public_rpcs.go index f014eb92..e40ece86 100644 --- a/configs/public_rpcs.go +++ b/configs/public_rpcs.go @@ -19,7 +19,6 @@ var networkRPCs = map[string]RPC{ NetworkHolesky: { NetworkName: NetworkHolesky, PublicRPCs: []string{ - "https://1rpc.io/holesky", "https://holesky.drpc.org", "https://ethereum-holesky-rpc.publicnode.com", "https://endpoints.omniatech.io/v1/eth/holesky/public", diff --git a/e2e/keys_test.go b/e2e/keys_test.go index 619891ed..baed486b 100644 --- a/e2e/keys_test.go +++ b/e2e/keys_test.go @@ -7,6 +7,7 @@ import ( "regexp" "testing" + "github.com/NethermindEth/sedge/internal/lido/contracts" "github.com/NethermindEth/sedge/internal/pkg/keystores" "github.com/stretchr/testify/assert" ) @@ -82,3 +83,147 @@ func TestKeys_Eth1_Withdrawal_Keys_Mainnet(t *testing.T) { // Run test case e2eTest.run() } + +func TestKeys_Lido_Mainnet(t *testing.T) { + // Test context + var ( + runErr error + ) + // Build test case + e2eTest := newE2ETestCase( + t, + // Arrange + func(t *testing.T, binaryPath string) error { + mnemonicPathFile := filepath.Join(filepath.Dir(binaryPath), "mnemonic.txt") + file, err := os.Create(mnemonicPathFile) + if err != nil { + return err + } + defer file.Close() + mnemonicText := "science ill robust clump oxygen intact barely horror athlete eyebrow cave target hero input entry citizen wink affair entire alert sick flight gossip refuse" + _, err = file.WriteString(mnemonicText) + return err + }, + // Act + func(t *testing.T, binaryPath, dataDirPath string) { + mnemonicPathFile := filepath.Join(filepath.Dir(binaryPath), "mnemonic.txt") + runErr = runSedge(t, binaryPath, "keys", + "--lido", + "--network", "mainnet", + "--num-validators", "10", + "--mnemonic-path", mnemonicPathFile, + "--existing", "0", + "--random-passphrase", + "--path", dataDirPath) + }, + // Assert + func(t *testing.T, dataDirPath string) { + assert.NoError(t, runErr, "keys command should not fail") + // Check if the deposit_data.json was created + depositDataFilePath := filepath.Join(dataDirPath, "keystore", keystores.DepositDataFileName) + assert.FileExists(t, depositDataFilePath, "deposit_data.json should be created") + + // Check if the deposit_data.json is valid + var keys []depositDataKey + jsonData, err := os.ReadFile(depositDataFilePath) + assert.NoError(t, err, "error reading deposit_data.json") + err = json.Unmarshal([]byte(jsonData), &keys) + assert.NoError(t, err, "error unmarshalling json") + + pattern := `^010000000000000000000000[a-fA-F0-9]{40}$` + regex := regexp.MustCompile(pattern) + for _, key := range keys { + assert.Regexp(t, regex, key.WithdrawalCredentials, "withdrawal_credentials should match the pattern") + assert.Equal(t, key.NetworkName, "mainnet", "network_name should be mainnet") + expectedWithdrawalAddress := "010000000000000000000000" + (contracts.WithdrawalAddress["mainnet"].WithdrawalAddress[2:]) + assert.Equal(t, expectedWithdrawalAddress, key.WithdrawalCredentials, "WithdrawalAddress value should match expected value") + } + }, + ) + // Run test case + e2eTest.run() +} + +func TestKeys_Lido_Eth1Withdrawal_HoleskyInvalid(t *testing.T) { + // Test context + var ( + runErr error + ) + // Build test case + e2eTest := newE2ETestCase( + t, + // Arrange + func(t *testing.T, binaryPath string) error { + mnemonicPathFile := filepath.Join(filepath.Dir(binaryPath), "mnemonic.txt") + file, err := os.Create(mnemonicPathFile) + if err != nil { + return err + } + defer file.Close() + mnemonicText := "science ill robust clump oxygen intact barely horror athlete eyebrow cave target hero input entry citizen wink affair entire alert sick flight gossip refuse" + _, err = file.WriteString(mnemonicText) + return err + }, + // Act + func(t *testing.T, binaryPath, dataDirPath string) { + mnemonicPathFile := filepath.Join(filepath.Dir(binaryPath), "mnemonic.txt") + runErr = runSedge(t, binaryPath, "keys", + "--lido", + "--eth1-withdrawal-address", "0xb794f5ea0ba39494ce839613fffba74279579268", + "--network", "holesky", + "--num-validators", "10", + "--mnemonic-path", mnemonicPathFile, + "--existing", "0", + "--random-passphrase", + "--path", dataDirPath) + }, + // Assert + func(t *testing.T, dataDirPath string) { + assert.Error(t, runErr, "keys command should fail") + }, + ) + // Run test case + e2eTest.run() +} + +func TestKeys_Lido_GnosisUnsupported(t *testing.T) { + // Test context + var ( + runErr error + ) + // Build test case + e2eTest := newE2ETestCase( + t, + // Arrange + func(t *testing.T, binaryPath string) error { + mnemonicPathFile := filepath.Join(filepath.Dir(binaryPath), "mnemonic.txt") + file, err := os.Create(mnemonicPathFile) + if err != nil { + return err + } + defer file.Close() + mnemonicText := "science ill robust clump oxygen intact barely horror athlete eyebrow cave target hero input entry citizen wink affair entire alert sick flight gossip refuse" + _, err = file.WriteString(mnemonicText) + return err + }, + // Act + func(t *testing.T, binaryPath, dataDirPath string) { + mnemonicPathFile := filepath.Join(filepath.Dir(binaryPath), "mnemonic.txt") + runErr = runSedge(t, binaryPath, "keys", + "--lido", + "--eth1-withdrawal-address", "0xb794f5ea0ba39494ce839613fffba74279579268", + "--network", "gnosis", + "--num-validators", "10", + "--mnemonic-path", mnemonicPathFile, + "--existing", "0", + "--random-passphrase", + "--path", dataDirPath) + }, + // Assert + func(t *testing.T, dataDirPath string) { + assert.Error(t, runErr, "keys command should fail") + }, + ) + // Run test case + e2eTest.run() +} diff --git a/internal/lido/contracts/withdrawalAddress.go b/internal/lido/contracts/withdrawalAddress.go new file mode 100644 index 00000000..3109b867 --- /dev/null +++ b/internal/lido/contracts/withdrawalAddress.go @@ -0,0 +1,29 @@ +package contracts + +import ( + "github.com/NethermindEth/sedge/configs" +) + +type WithdrawalAddressConfig struct { + Network string + WithdrawalAddress string +} + +var WithdrawalAddress = map[string]WithdrawalAddressConfig{ + configs.NetworkMainnet: { + Network: configs.NetworkMainnet, + WithdrawalAddress: "0xb9d7934878b5fb9610b3fe8a5e441e8fad7e293f", + }, + configs.NetworkHolesky: { + Network: configs.NetworkHolesky, + WithdrawalAddress: "0xF0179dEC45a37423EAD4FaD5fCb136197872EAd9", + }, +} + +func GetLidoKeysSupportedNetworks() []string { + networks := []string{} + for network := range WithdrawalAddress { + networks = append(networks, network) + } + return networks +} From c967343bc4b5820eb548c4eb5e7e63cb618f0a2f Mon Sep 17 00:00:00 2001 From: Haneen Khalifa Date: Thu, 4 Jul 2024 18:36:39 +0300 Subject: [PATCH 16/31] feat: Overwrite WithdrawalAddress in prompts setup --- cli/cli.go | 7 +++++++ cli/cli_test.go | 1 - cli/keys.go | 2 +- internal/lido/contracts/feeRecipient.go | 3 +++ internal/lido/contracts/mevboostrelaylist/get_relays.go | 2 ++ internal/lido/contracts/withdrawalAddress.go | 3 +++ 6 files changed, 16 insertions(+), 2 deletions(-) diff --git a/cli/cli.go b/cli/cli.go index 78b749bb..d0f3269f 100644 --- a/cli/cli.go +++ b/cli/cli.go @@ -976,6 +976,13 @@ func inputKeystorePassphrase(p ui.Prompter, o *CliCmdOptions) (err error) { } func inputWithdrawalAddress(p ui.Prompter, o *CliCmdOptions) (err error) { + if o.nodeSetup == LidoNode { + _, ok := contracts.WithdrawalAddress[network] + if ok{ + o.withdrawalAddress = contracts.WithdrawalAddress[network].WithdrawalAddress + } + return + } o.withdrawalAddress, err = p.Input("Withdrawal address", "", false, func(s string) error { return ui.EthAddressValidator(s, true) }) return } diff --git a/cli/cli_test.go b/cli/cli_test.go index 22c61207..ece55cbb 100644 --- a/cli/cli_test.go +++ b/cli/cli_test.go @@ -573,7 +573,6 @@ func TestCli(t *testing.T) { prompter.EXPECT().Select("Select keystore source", "", []string{SourceTypeCreate, SourceTypeExisting, SourceTypeSkip}).Return(0, nil), prompter.EXPECT().Select("Select mnemonic source", "", []string{SourceTypeCreate, SourceTypeExisting}).Return(0, nil), prompter.EXPECT().Select("Select passphrase source", "", []string{SourceTypeRandom, SourceTypeExisting, SourceTypeCreate}).Return(0, nil), - prompter.EXPECT().Input("Withdrawal address", "", false, gomock.AssignableToTypeOf(func(s string) error { return ui.EthAddressValidator(s, true) })).Return("0x00000007abca72jmd83jd8u3jd9kdn32j38abc", nil), prompter.EXPECT().InputInt64("Number of validators", int64(1)).Return(int64(1), nil), prompter.EXPECT().InputInt64("Existing validators. This number will be used as the initial index for the generated keystores.", int64(0)).Return(int64(0), nil), depsMgr.EXPECT().Check([]string{dependencies.Docker}).Return([]string{dependencies.Docker}, nil), diff --git a/cli/keys.go b/cli/keys.go index f6550ff7..e1ea0fc1 100644 --- a/cli/keys.go +++ b/cli/keys.go @@ -106,7 +106,7 @@ func KeysCmd(cmdRunner commands.CommandRunner, p ui.Prompter) *cobra.Command { } // validate network for Lido if flags.lidoNode{ - _, ok := contracts.FeeRecipient[network] + _, ok := contracts.WithdrawalAddress[network] if !ok { options := contracts.GetLidoKeysSupportedNetworks() log.Fatalf("invalid network: Choose valid network for Lido: %v", options) diff --git a/internal/lido/contracts/feeRecipient.go b/internal/lido/contracts/feeRecipient.go index f5dd7361..f1eeb614 100644 --- a/internal/lido/contracts/feeRecipient.go +++ b/internal/lido/contracts/feeRecipient.go @@ -1,6 +1,8 @@ package contracts import ( + "sort" + "github.com/NethermindEth/sedge/configs" ) @@ -29,5 +31,6 @@ func GetLidoSupportedNetworks() []string { for network := range FeeRecipient { networks = append(networks, network) } + sort.Strings(networks) return networks } diff --git a/internal/lido/contracts/mevboostrelaylist/get_relays.go b/internal/lido/contracts/mevboostrelaylist/get_relays.go index 9790b326..247fac11 100644 --- a/internal/lido/contracts/mevboostrelaylist/get_relays.go +++ b/internal/lido/contracts/mevboostrelaylist/get_relays.go @@ -3,6 +3,7 @@ package mevboostrelaylist import ( "encoding/hex" "fmt" + "sort" "strings" "github.com/NethermindEth/sedge/configs" @@ -148,5 +149,6 @@ func GetLidoSupportedNetworksMevBoost() []string { for network := range DeployedContractAddresses { networks = append(networks, network) } + sort.Strings(networks) return networks } diff --git a/internal/lido/contracts/withdrawalAddress.go b/internal/lido/contracts/withdrawalAddress.go index 3109b867..d0bcf19e 100644 --- a/internal/lido/contracts/withdrawalAddress.go +++ b/internal/lido/contracts/withdrawalAddress.go @@ -1,6 +1,8 @@ package contracts import ( + "sort" + "github.com/NethermindEth/sedge/configs" ) @@ -25,5 +27,6 @@ func GetLidoKeysSupportedNetworks() []string { for network := range WithdrawalAddress { networks = append(networks, network) } + sort.Strings(networks) return networks } From 7ea3c4011543ec2d3b3f04c6b3e7bcda0c51a3bd Mon Sep 17 00:00:00 2001 From: Haneen Khalifa Date: Thu, 4 Jul 2024 18:52:13 +0300 Subject: [PATCH 17/31] refactor: error messages --- cli/cli.go | 2 +- cli/generate.go | 4 ++-- cli/generate_test.go | 4 ++-- cli/keys.go | 12 ++++++------ configs/errors.go | 3 +++ configs/messages.go | 1 - internal/lido/contracts/withdrawalAddress.go | 6 +++--- 7 files changed, 17 insertions(+), 15 deletions(-) diff --git a/cli/cli.go b/cli/cli.go index d0f3269f..574813e3 100644 --- a/cli/cli.go +++ b/cli/cli.go @@ -978,7 +978,7 @@ func inputKeystorePassphrase(p ui.Prompter, o *CliCmdOptions) (err error) { func inputWithdrawalAddress(p ui.Prompter, o *CliCmdOptions) (err error) { if o.nodeSetup == LidoNode { _, ok := contracts.WithdrawalAddress[network] - if ok{ + if ok { o.withdrawalAddress = contracts.WithdrawalAddress[network].WithdrawalAddress } return diff --git a/cli/generate.go b/cli/generate.go index 39a34358..a28e195a 100644 --- a/cli/generate.go +++ b/cli/generate.go @@ -496,13 +496,13 @@ func validateLido(network string, flags *GenCmdFlags) error { _, ok := mevboostrelaylist.DeployedContractAddresses[network] if !ok { options := mevboostrelaylist.GetLidoSupportedNetworksMevBoost() - return fmt.Errorf("invalid network: Choose valid network for Lido with MEV-Boost: %v", options) + return fmt.Errorf(configs.InvalidNetworkForLidoMevBoost, options) } } _, ok := contracts.FeeRecipient[network] if !ok { options := contracts.GetLidoSupportedNetworks() - return fmt.Errorf("invalid network: Choose valid network for Lido: %v", options) + return fmt.Errorf(configs.InvalidNetworkForLido, options) } return nil diff --git a/cli/generate_test.go b/cli/generate_test.go index 999bebf6..d3eddfe1 100644 --- a/cli/generate_test.go +++ b/cli/generate_test.go @@ -1342,7 +1342,7 @@ func TestGenerateCmd(t *testing.T) { network: NetworkSepolia, lidoNode: true, }, - fmt.Errorf("invalid network: Choose valid network for Lido with MEV-Boost: %v", mevboostrelaylist.GetLidoSupportedNetworksMevBoost()), + fmt.Errorf(configs.InvalidNetworkForLidoMevBoost, mevboostrelaylist.GetLidoSupportedNetworksMevBoost()), }, { "Lido Full-node - Holesky no validator", @@ -1370,7 +1370,7 @@ func TestGenerateCmd(t *testing.T) { network: NetworkGnosis, lidoNode: true, }, - fmt.Errorf("invalid network: Choose valid network for Lido: %v", contracts.GetLidoSupportedNetworks()), + fmt.Errorf(configs.InvalidNetworkForLido, contracts.GetLidoSupportedNetworks()), }, } diff --git a/cli/keys.go b/cli/keys.go index e1ea0fc1..d8da2079 100644 --- a/cli/keys.go +++ b/cli/keys.go @@ -101,15 +101,15 @@ func KeysCmd(cmdRunner commands.CommandRunner, p ui.Prompter) *cobra.Command { }, Run: func(cmd *cobra.Command, args []string) { // Incompatible --lido and --eth1-withdrawal-address together - if flags.lidoNode && flags.eth1WithdrawalAddress != ""{ + if flags.lidoNode && flags.eth1WithdrawalAddress != "" { log.Fatalf(configs.IncompatibleLidoAndEth1Withdrawal) } // validate network for Lido - if flags.lidoNode{ + if flags.lidoNode { _, ok := contracts.WithdrawalAddress[network] if !ok { - options := contracts.GetLidoKeysSupportedNetworks() - log.Fatalf("invalid network: Choose valid network for Lido: %v", options) + options := contracts.GetLidoKeysSupportedNetworks() + log.Fatalf("invalid network: Choose valid network for Lido: %v", options) } } // Warn about withdrawal address @@ -178,9 +178,9 @@ func KeysCmd(cmdRunner commands.CommandRunner, p ui.Prompter) *cobra.Command { keystorePath := filepath.Join(flags.path, "keystore") var withdrawalAddress string - if flags.lidoNode{ + if flags.lidoNode { withdrawalAddress = contracts.WithdrawalAddress[flags.network].WithdrawalAddress[2:] - }else if flags.eth1WithdrawalAddress != "" { + } else if flags.eth1WithdrawalAddress != "" { withdrawalAddress = flags.eth1WithdrawalAddress[2:] } diff --git a/configs/errors.go b/configs/errors.go index ca1de8c1..55704afa 100644 --- a/configs/errors.go +++ b/configs/errors.go @@ -87,4 +87,7 @@ const ( ErrWithdrawalEth1SecretKeyCreation = "failed to create withdrawal private key for address 0x%s: %v" ErrWithdrawalBLSSecretKeyCreation = "failed to create withdrawal private key for path %q: %v" ErrInvalidWithdrawalAddr = "provided withdrawal address is not a valid Ethereum address" + IncompatibleLidoAndEth1Withdrawal = "incompatible flags --lido, and --eth1-withdrawal-address can't be used together" + InvalidNetworkForLido = "invalid network: Choose valid network for Lido: %v" + InvalidNetworkForLidoMevBoost = "invalid network: Choose valid network for Lido with MEV-Boost: %v" ) diff --git a/configs/messages.go b/configs/messages.go index c0ab2891..f57f2177 100644 --- a/configs/messages.go +++ b/configs/messages.go @@ -114,7 +114,6 @@ Happy Staking! GettingCustomGenesis = "Getting custom genesis..." GettingCustomNetworkConfig = "Getting custom network config..." WritingCustomDeployBlock = "Writing custom deploy block..." - IncompatibleLidoAndEth1Withdrawal = "Incompatible flags --lido, and --eth1-withdrawal-address can't be used together" ) var DefaultAbsSedgeDataPath string diff --git a/internal/lido/contracts/withdrawalAddress.go b/internal/lido/contracts/withdrawalAddress.go index d0bcf19e..d394311a 100644 --- a/internal/lido/contracts/withdrawalAddress.go +++ b/internal/lido/contracts/withdrawalAddress.go @@ -7,17 +7,17 @@ import ( ) type WithdrawalAddressConfig struct { - Network string + Network string WithdrawalAddress string } var WithdrawalAddress = map[string]WithdrawalAddressConfig{ configs.NetworkMainnet: { - Network: configs.NetworkMainnet, + Network: configs.NetworkMainnet, WithdrawalAddress: "0xb9d7934878b5fb9610b3fe8a5e441e8fad7e293f", }, configs.NetworkHolesky: { - Network: configs.NetworkHolesky, + Network: configs.NetworkHolesky, WithdrawalAddress: "0xF0179dEC45a37423EAD4FaD5fCb136197872EAd9", }, } From 144eef188e45d8d73be77b29f7ffeeeeaf4c813e Mon Sep 17 00:00:00 2001 From: Haneen Khalifa Date: Thu, 4 Jul 2024 20:05:25 +0300 Subject: [PATCH 18/31] refactor: adjust tests and naming --- cli/keys.go | 34 ++++++++++----------- configs/errors.go | 2 +- e2e/generate_test.go | 1 + e2e/keys_test.go | 73 +++++++++++++++++++++++++++++++++++++++++--- 4 files changed, 87 insertions(+), 23 deletions(-) diff --git a/cli/keys.go b/cli/keys.go index d8da2079..78cd438f 100644 --- a/cli/keys.go +++ b/cli/keys.go @@ -32,16 +32,16 @@ import ( ) type KeysCmdFlags struct { - network string - path string - eth1WithdrawalAddress string - mnemonicPath string - passphrasePath string - existingVal int64 - numberVal int64 - randomPassphrase bool - install bool - lidoNode bool + network string + path string + ethWithdrawalAddress string + mnemonicPath string + passphrasePath string + existingVal int64 + numberVal int64 + randomPassphrase bool + install bool + lidoNode bool } func KeysCmd(cmdRunner commands.CommandRunner, p ui.Prompter) *cobra.Command { @@ -87,7 +87,7 @@ func KeysCmd(cmdRunner commands.CommandRunner, p ui.Prompter) *cobra.Command { log.Fatal(err.Error()) } // Validate fee recipient - if flags.eth1WithdrawalAddress != "" && !utils.IsAddress(flags.eth1WithdrawalAddress) { + if flags.ethWithdrawalAddress != "" && !utils.IsAddress(flags.ethWithdrawalAddress) { log.Fatal(configs.ErrInvalidWithdrawalAddr) } // Ensure that path is absolute @@ -101,7 +101,7 @@ func KeysCmd(cmdRunner commands.CommandRunner, p ui.Prompter) *cobra.Command { }, Run: func(cmd *cobra.Command, args []string) { // Incompatible --lido and --eth1-withdrawal-address together - if flags.lidoNode && flags.eth1WithdrawalAddress != "" { + if flags.lidoNode && flags.ethWithdrawalAddress != "" { log.Fatalf(configs.IncompatibleLidoAndEth1Withdrawal) } // validate network for Lido @@ -113,7 +113,7 @@ func KeysCmd(cmdRunner commands.CommandRunner, p ui.Prompter) *cobra.Command { } } // Warn about withdrawal address - if flags.eth1WithdrawalAddress != "" { + if flags.ethWithdrawalAddress != "" { log.Warn(configs.WithdrawalAddressDefinedWarning) } // Get keystore passphrase @@ -180,8 +180,8 @@ func KeysCmd(cmdRunner commands.CommandRunner, p ui.Prompter) *cobra.Command { var withdrawalAddress string if flags.lidoNode { withdrawalAddress = contracts.WithdrawalAddress[flags.network].WithdrawalAddress[2:] - } else if flags.eth1WithdrawalAddress != "" { - withdrawalAddress = flags.eth1WithdrawalAddress[2:] + } else if flags.ethWithdrawalAddress != "" { + withdrawalAddress = flags.ethWithdrawalAddress[2:] } data := keystores.ValidatorKeysGenData{ @@ -213,10 +213,10 @@ func KeysCmd(cmdRunner commands.CommandRunner, p ui.Prompter) *cobra.Command { }, } // Flag binds - cmd.PersistentFlags().BoolVar(&flags.lidoNode, "lido", false, "Lido CSM node") + cmd.PersistentFlags().BoolVar(&flags.lidoNode, "lido", false, "Enable Lido CSM compatible keys. Similar to using --eth-withdrawal-address with the Lido Withdrawal Vault address.") cmd.Flags().StringVarP(&flags.network, "network", "n", "mainnet", "Target network. e.g. mainnet,sepolia, holesky, gnosis, chiado etc.") cmd.Flags().StringVarP(&flags.path, "path", "p", configs.DefaultAbsSedgeDataPath, "Absolute path to keystore folder. e.g. /home/user/keystore") - cmd.Flags().StringVar(&flags.eth1WithdrawalAddress, "eth1-withdrawal-address", "", "If this field is set and valid, the given Eth1 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.") + 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.") cmd.Flags().StringVar(&flags.mnemonicPath, "mnemonic-path", "", "Path to file with a existing mnemonic to use.") cmd.Flags().StringVar(&flags.passphrasePath, "passphrase-path", "", "Path to file with a keystores passphrase to use.") cmd.Flags().Int64Var(&flags.existingVal, "existing", -1, `Number of validators generated with the provided mnemonic. Will be ignored if "--mnemonic-path" its not set. This number will be used as the initial index for the generated keystores.`) diff --git a/configs/errors.go b/configs/errors.go index 55704afa..f729c2e9 100644 --- a/configs/errors.go +++ b/configs/errors.go @@ -87,7 +87,7 @@ const ( ErrWithdrawalEth1SecretKeyCreation = "failed to create withdrawal private key for address 0x%s: %v" ErrWithdrawalBLSSecretKeyCreation = "failed to create withdrawal private key for path %q: %v" ErrInvalidWithdrawalAddr = "provided withdrawal address is not a valid Ethereum address" - IncompatibleLidoAndEth1Withdrawal = "incompatible flags --lido, and --eth1-withdrawal-address can't be used together" + IncompatibleLidoAndEth1Withdrawal = "incompatible flags --lido, and --eth-withdrawal-address can't be used together" InvalidNetworkForLido = "invalid network: Choose valid network for Lido: %v" InvalidNetworkForLidoMevBoost = "invalid network: Choose valid network for Lido with MEV-Boost: %v" ) diff --git a/e2e/generate_test.go b/e2e/generate_test.go index 5197ceff..2c71000d 100644 --- a/e2e/generate_test.go +++ b/e2e/generate_test.go @@ -73,6 +73,7 @@ func TestGenerate_FullNode_Lido_SepoliaNotSupported(t *testing.T) { }, // Assert func(t *testing.T, dataDirPath string) { + // Sepolia not supported Lido with MEV-Boost assert.Error(t, runErr, "generate command should fail") }, ) diff --git a/e2e/keys_test.go b/e2e/keys_test.go index baed486b..86995f23 100644 --- a/e2e/keys_test.go +++ b/e2e/keys_test.go @@ -5,6 +5,7 @@ import ( "os" "path/filepath" "regexp" + "strings" "testing" "github.com/NethermindEth/sedge/internal/lido/contracts" @@ -26,7 +27,7 @@ type depositDataKey struct { WithdrawalCredentials string `json:"withdrawal_credentials"` } -func TestKeys_Eth1_Withdrawal_Keys_Mainnet(t *testing.T) { +func TestKeys_Eth_Withdrawal_Keys_Mainnet(t *testing.T) { // Test context var ( runErr error @@ -50,7 +51,7 @@ func TestKeys_Eth1_Withdrawal_Keys_Mainnet(t *testing.T) { func(t *testing.T, binaryPath, dataDirPath string) { mnemonicPathFile := filepath.Join(filepath.Dir(binaryPath), "mnemonic.txt") runErr = runSedge(t, binaryPath, "keys", - "--eth1-withdrawal-address", "0xb794f5ea0ba39494ce839613fffba74279579268", + "--eth-withdrawal-address", "0xb794f5ea0ba39494ce839613fffba74279579268", "--network", "mainnet", "--num-validators", "10", "--mnemonic-path", mnemonicPathFile, @@ -144,7 +145,7 @@ func TestKeys_Lido_Mainnet(t *testing.T) { e2eTest.run() } -func TestKeys_Lido_Eth1Withdrawal_HoleskyInvalid(t *testing.T) { +func TestKeys_Lido_Holesky(t *testing.T) { // Test context var ( runErr error @@ -169,7 +170,6 @@ func TestKeys_Lido_Eth1Withdrawal_HoleskyInvalid(t *testing.T) { mnemonicPathFile := filepath.Join(filepath.Dir(binaryPath), "mnemonic.txt") runErr = runSedge(t, binaryPath, "keys", "--lido", - "--eth1-withdrawal-address", "0xb794f5ea0ba39494ce839613fffba74279579268", "--network", "holesky", "--num-validators", "10", "--mnemonic-path", mnemonicPathFile, @@ -179,6 +179,69 @@ func TestKeys_Lido_Eth1Withdrawal_HoleskyInvalid(t *testing.T) { }, // Assert func(t *testing.T, dataDirPath string) { + assert.NoError(t, runErr, "keys command should not fail") + // Check if the deposit_data.json was created + depositDataFilePath := filepath.Join(dataDirPath, "keystore", keystores.DepositDataFileName) + assert.FileExists(t, depositDataFilePath, "deposit_data.json should be created") + + // Check if the deposit_data.json is valid + var keys []depositDataKey + jsonData, err := os.ReadFile(depositDataFilePath) + assert.NoError(t, err, "error reading deposit_data.json") + err = json.Unmarshal([]byte(jsonData), &keys) + assert.NoError(t, err, "error unmarshalling json") + + pattern := `^010000000000000000000000[a-fA-F0-9]{40}$` + regex := regexp.MustCompile(pattern) + for _, key := range keys { + assert.Regexp(t, regex, key.WithdrawalCredentials, "withdrawal_credentials should match the pattern") + assert.Equal(t, key.NetworkName, "holesky", "network_name should be holesky") + expectedWithdrawalCredentials := "010000000000000000000000" + (contracts.WithdrawalAddress["holesky"].WithdrawalAddress[2:]) + strings.ToLower(expectedWithdrawalCredentials) + assert.Equal(t, expectedWithdrawalCredentials, key.WithdrawalCredentials, "WithdrawalAddress value should match expected value") + } + }, + ) + // Run test case + e2eTest.run() +} + +func TestKeys_Lido_EthWithdrawal_HoleskyInvalid(t *testing.T) { + // Test context + var ( + runErr error + ) + // Build test case + e2eTest := newE2ETestCase( + t, + // Arrange + func(t *testing.T, binaryPath string) error { + mnemonicPathFile := filepath.Join(filepath.Dir(binaryPath), "mnemonic.txt") + file, err := os.Create(mnemonicPathFile) + if err != nil { + return err + } + defer file.Close() + mnemonicText := "science ill robust clump oxygen intact barely horror athlete eyebrow cave target hero input entry citizen wink affair entire alert sick flight gossip refuse" + _, err = file.WriteString(mnemonicText) + return err + }, + // Act + func(t *testing.T, binaryPath, dataDirPath string) { + mnemonicPathFile := filepath.Join(filepath.Dir(binaryPath), "mnemonic.txt") + runErr = runSedge(t, binaryPath, "keys", + "--lido", + "--eth-withdrawal-address", "0xb794f5ea0ba39494ce839613fffba74279579268", + "--network", "holesky", + "--num-validators", "10", + "--mnemonic-path", mnemonicPathFile, + "--existing", "0", + "--random-passphrase", + "--path", dataDirPath) + }, + // Assert + func(t *testing.T, dataDirPath string) { + // incompatible flags --lido, and --eth-withdrawal-address can't be used together assert.Error(t, runErr, "keys command should fail") }, ) @@ -211,7 +274,7 @@ func TestKeys_Lido_GnosisUnsupported(t *testing.T) { mnemonicPathFile := filepath.Join(filepath.Dir(binaryPath), "mnemonic.txt") runErr = runSedge(t, binaryPath, "keys", "--lido", - "--eth1-withdrawal-address", "0xb794f5ea0ba39494ce839613fffba74279579268", + "--eth-withdrawal-address", "0xb794f5ea0ba39494ce839613fffba74279579268", "--network", "gnosis", "--num-validators", "10", "--mnemonic-path", mnemonicPathFile, From 020ce1a76f9e31872694a32e00e5d59b0fce3e70 Mon Sep 17 00:00:00 2001 From: Haneen Khalifa Date: Thu, 4 Jul 2024 20:10:59 +0300 Subject: [PATCH 19/31] test: adjust keys for holesky --- e2e/keys_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/e2e/keys_test.go b/e2e/keys_test.go index 86995f23..e04e5ce8 100644 --- a/e2e/keys_test.go +++ b/e2e/keys_test.go @@ -197,7 +197,7 @@ func TestKeys_Lido_Holesky(t *testing.T) { assert.Regexp(t, regex, key.WithdrawalCredentials, "withdrawal_credentials should match the pattern") assert.Equal(t, key.NetworkName, "holesky", "network_name should be holesky") expectedWithdrawalCredentials := "010000000000000000000000" + (contracts.WithdrawalAddress["holesky"].WithdrawalAddress[2:]) - strings.ToLower(expectedWithdrawalCredentials) + expectedWithdrawalCredentials = strings.ToLower(expectedWithdrawalCredentials) assert.Equal(t, expectedWithdrawalCredentials, key.WithdrawalCredentials, "WithdrawalAddress value should match expected value") } }, From e1f760a4f393586fcc2657a557a5bccba592615d Mon Sep 17 00:00:00 2001 From: Miguel Tenorio <46824157+AntiD2ta@users.noreply.github.com> Date: Fri, 5 Jul 2024 11:02:49 +0200 Subject: [PATCH 20/31] doc: Update Sedge with Lido guide (#390) --- docs/docs/quickstart/lido.mdx | 76 +++++++++++++++++++++++++++-------- 1 file changed, 59 insertions(+), 17 deletions(-) diff --git a/docs/docs/quickstart/lido.mdx b/docs/docs/quickstart/lido.mdx index 8745fe08..5afb340d 100644 --- a/docs/docs/quickstart/lido.mdx +++ b/docs/docs/quickstart/lido.mdx @@ -10,51 +10,93 @@ import TabItem from '@theme/TabItem'; ## What is the Lido Community Staking Module (CSM)? -The Lido Community Staking Module (CSM) is a protocol that allows anyone, especially community stakers and solo stakers, -to become a Lido Node Operator by providing an ETH-based bond as security collateral. CSM offers various benefits, -such as smoothed rewards, lower bond requirements, and a user-friendly experience. +The [Lido Community Staking Module](https://operatorportal.lido.fi/modules/community-staking-module) (CSM) is a protocol that allows anyone, especially community stakers and solo stakers, +to become a Lido Node Operator in a permissionless manner by providing an ETH-based bond as security collateral. CSM offers various benefits, +such as [smoothed rewards](https://operatorportal.lido.fi/modules/community-staking-module#block-b449869616354425a1c616f292cd43db) with other Lido modules received via stETH, reasonable low bond requirements (accepted in ETH, stETH, wstETH), and a user-friendly experience. Sedge supports the Lido CSM, allowing users to generate validator keys and set up their full nodes with ease. This guide -will walk you through the process of staking with Lido using Sedge. +will walk you through the process of staking with Lido CSM using Sedge. -## Generating Validator Keys with Sedge +If you are interested in becoming a Lido node operator, make sure to read the [Node Operator Expectations](https://operatorportal.lido.fi/modules/community-staking-module#block-c58d307283e942ecab5eeb96f9a89235) -To get started with CSM using Sedge, you first need to generate your validator keys. Sedge simplifies this process -with the `sedge keys` command, to generate keys compatible with Lido CSM, use the `--lido` flag: +:::info + +Lido CSM is **currently available on the Holesky testnet for [early adopters](https://operatorportal.lido.fi/modules/community-staking-module#block-ef60a1fa96ae4c7995dd7794de2a3e22)**. Check your eligibility on the Early Adoption program [here](https://lidofinance.github.io/csm-ea-checker/). The Mainnet launch is expected soon. + +::: + +## Workflow breakdown + +The Lido CSM staking process involves several steps: + +1. **Generate Validator Keys and Deposit Data**: Generate validator keys compatible with Lido CSM with withdrawal credentials set to Lido Withdrawal Vault. +2. **Set Up Your Full Node**: Set up your full node with Lido CSM settings enabled (Fee recipient pointing to Lido Execution Layer Rewards Vault, and using designated MEV relays for Lido CSM) and the designed validator keys. +3. **Upload Deposit Data**: Upload the deposit data to the Lido CSM Widget and provide the required bond amount in ETH, stETH, or wstETH. +4. **Wait for Deposit Confirmation**: Wait for your CSM validator keys to be deposited through the protocol and ensure your node remains online in the meantime. + +Sedge simplifies the process of staking with Lido CSM by providing a seamless workflow for: + +- Generating validator keys with the respective deposit data (Step 1) +- Setting up your full node with Lido CSM settings enabled (Step 2) +- Importing existing Lido CSM validator keys to your Sedge node (In case you already have validator keys generated and running but want to migrate your clients or to Sedge) + +This means you only have to [upload the deposit data to Lido CSM and purchase the bond manually](#uploading-deposit-data-and-registering-the-node-operator). Sedge will take care of the rest. + +Let's dive into each step in detail. + +## Using Sedge for Lido CSM + +You can run `sedge cli` to kickstart Sedge's [one-click interactive setup](./complete-guide#21-one-click-interactive-setup). In this step, you will be guided through generating validator keys and setting up your full node with Lido CSM settings enabled. + +Alternatively, for a more controlled setup, you can use the following commands to generate validator keys and set up your full node with Lido CSM settings enabled. + +### Generating Validator Keys with the respective deposit data + +To get started with CSM using Sedge, you first need to generate your validator keys and deposit data. Sedge simplifies this process +with the `sedge keys` [command](../commands/keys.mdx). Use the `--lido` flag to generate keys compatible with Lido CSM: ```bash sedge keys --lido ``` -Once generated the keys, you can setup your full node: +### Setting up your full node with Lido CSM settings enabled + +Once the keys are generated, you can set up your full node: ```bash -sedge generate full-node --lido -n holesky +sedge generate full-node --lido --network holesky +``` :::note -This command will generate a setup with the Lido Community Staking Module (CSM) enabled for the Holesky testnet. -You can set other networks by changing the `-n` flag, if supported. +This command will generate a setup with the Lido CSM enabled for the Holesky testnet. +If supported, you can set other networks by changing the `--network` flag. ::: -``` This command will generate a new set of validator keys with the following parameters: - `withdrawal_address` set to the Lido Withdrawal Vault address -- `deposit_amount` set to 32 ETH - `chain` set to the appropriate network (e.g., `holesky`, `mainnet`) - `fee_recipient` set to the Lido Execution Layer Rewards Vault address - `mev_relay` set to the designated MEV relays for Lido CSM on the appropriate network -## Configuring Your Node Operator +You can now proceed to run the generated setup for your full node with the Lido CSM settings enabled and the validator keys you want to run with [`sedge run`](../commands/run.mdx) (although not filled and registered yet). -After generating your validator keys and setting up your node, you need to configure yourself as a node operator to +## Uploading deposit data and registering the Node Operator + +After generating your validator keys and setting up your node, you need to register your wallet as a Node Operator to work with Lido CSM: 1. Upload the newly generated deposit data file pertaining to your CSM keystores onto the Lido CSM Widget and provide the required bond amount in ETH, stETH, or wstETH. Before uploading, ensure that your nodes are synced, -running, and ready for validator activation. +running and ready for validator activation. + +:::info + +The official [instructions guide] (https://dvt-homestaker.stakesaurus.com/bonded-validators-setup/lido-csm/upload-remove-view-validator-keys#upload-keys) provides detailed instructions on how to upload the deposit data and register your Node Operatort. + +::: 2. Wait for your CSM validator keys to be deposited through the protocol and ensure your node remains online in the meantime. @@ -72,4 +114,4 @@ CSM Widget. This interface allows you to: Remember to keep your node running smoothly and follow the Lido protocol rules to avoid any penalties or bond confiscation. For more detailed information on managing your Node Operator, refer to the -[Lido CSM documentation](https://operatorportal.lido.fi/modules/community-staking-module). \ No newline at end of file +[Lido CSM documentation](https://operatorportal.lido.fi/modules/community-staking-module#block-d3ad2b2bd3994a06b19dccc0794ac8d6). \ No newline at end of file From 17c99e74b42ea5697f3745c9a4b223aee3d4d87c Mon Sep 17 00:00:00 2001 From: Haneen Khalifa Date: Fri, 5 Jul 2024 13:33:36 +0300 Subject: [PATCH 21/31] feat: MEV prompt with lido --- cli/cli.go | 5 +++-- cli/cli_test.go | 1 - 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cli/cli.go b/cli/cli.go index 574813e3..68fb463d 100644 --- a/cli/cli.go +++ b/cli/cli.go @@ -817,9 +817,10 @@ func confirmInstallDependencies(p ui.Prompter, o *CliCmdOptions) (err error) { func confirmEnableMEVBoost(p ui.Prompter, o *CliCmdOptions) (err error) { if o.nodeSetup == LidoNode { _, ok := mevboostrelaylist.DeployedContractAddresses[o.genData.Network] - if !ok { - return + if ok { + o.withMevBoost = true } + return } o.withMevBoost, err = p.Confirm("Enable MEV Boost?", true) return diff --git a/cli/cli_test.go b/cli/cli_test.go index ece55cbb..f4567cd7 100644 --- a/cli/cli_test.go +++ b/cli/cli_test.go @@ -556,7 +556,6 @@ func TestCli(t *testing.T) { prompter.EXPECT().Input("Generation path", configs.DefaultAbsSedgeDataPath, false, nil).Return(generationPath, nil), prompter.EXPECT().Input("Container tag, sedge will add to each container and the network, a suffix with the tag", "", false, nil).Return("tag", nil), prompter.EXPECT().Confirm("Do you want to set up a validator?", true).Return(true, nil), - prompter.EXPECT().Confirm("Enable MEV Boost?", true).Return(true, nil), prompter.EXPECT().Input("Mev-Boost image", "flashbots/mev-boost:latest", false, nil).Return("flashbots/mev-boost:latest", nil), prompter.EXPECT().Select("Select execution client", "", ETHClients["execution"]).Return(0, nil), prompter.EXPECT().Select("Select consensus client", "", ETHClients["consensus"]).Return(1, nil), From c5b7dee67f46465962903f6182b44255f75ebef8 Mon Sep 17 00:00:00 2001 From: Haneen Khalifa Date: Fri, 5 Jul 2024 15:13:47 +0300 Subject: [PATCH 22/31] refactor: Validate lido supported networks --- cli/cli.go | 10 +++++----- cli/generate.go | 10 ++++------ cli/keys.go | 7 +++---- configs/errors.go | 1 + internal/lido/contracts/feeRecipient.go | 14 ++++++++++++++ .../lido/contracts/mevboostrelaylist/get_relays.go | 14 ++++++++++++++ internal/lido/contracts/withdrawalAddress.go | 14 ++++++++++++++ 7 files changed, 55 insertions(+), 15 deletions(-) diff --git a/cli/cli.go b/cli/cli.go index 68fb463d..36c0e7b4 100644 --- a/cli/cli.go +++ b/cli/cli.go @@ -816,8 +816,8 @@ func confirmInstallDependencies(p ui.Prompter, o *CliCmdOptions) (err error) { func confirmEnableMEVBoost(p ui.Prompter, o *CliCmdOptions) (err error) { if o.nodeSetup == LidoNode { - _, ok := mevboostrelaylist.DeployedContractAddresses[o.genData.Network] - if ok { + _, supported := mevboostrelaylist.NetworkSupportedByLidoMevBoost(o.genData.Network) + if supported { o.withMevBoost = true } return @@ -978,9 +978,9 @@ func inputKeystorePassphrase(p ui.Prompter, o *CliCmdOptions) (err error) { func inputWithdrawalAddress(p ui.Prompter, o *CliCmdOptions) (err error) { if o.nodeSetup == LidoNode { - _, ok := contracts.WithdrawalAddress[network] - if ok { - o.withdrawalAddress = contracts.WithdrawalAddress[network].WithdrawalAddress + _, supported := contracts.NetworkSupportedByLidoKeys(o.genData.Network) + if supported { + o.withdrawalAddress = contracts.WithdrawalAddress[o.genData.Network].WithdrawalAddress } return } diff --git a/cli/generate.go b/cli/generate.go index a28e195a..34fc635b 100644 --- a/cli/generate.go +++ b/cli/generate.go @@ -493,15 +493,13 @@ func loadJWTSecret(from string) (absFrom string, err error) { func validateLido(network string, flags *GenCmdFlags) error { if !flags.noMev { - _, ok := mevboostrelaylist.DeployedContractAddresses[network] - if !ok { - options := mevboostrelaylist.GetLidoSupportedNetworksMevBoost() + options, supported := mevboostrelaylist.NetworkSupportedByLidoMevBoost(network) + if !supported { return fmt.Errorf(configs.InvalidNetworkForLidoMevBoost, options) } } - _, ok := contracts.FeeRecipient[network] - if !ok { - options := contracts.GetLidoSupportedNetworks() + options, supported := contracts.NetworkSupportedByLido(network) + if !supported { return fmt.Errorf(configs.InvalidNetworkForLido, options) } diff --git a/cli/keys.go b/cli/keys.go index 78cd438f..a5f5f931 100644 --- a/cli/keys.go +++ b/cli/keys.go @@ -106,10 +106,9 @@ func KeysCmd(cmdRunner commands.CommandRunner, p ui.Prompter) *cobra.Command { } // validate network for Lido if flags.lidoNode { - _, ok := contracts.WithdrawalAddress[network] - if !ok { - options := contracts.GetLidoKeysSupportedNetworks() - log.Fatalf("invalid network: Choose valid network for Lido: %v", options) + options, supported := contracts.NetworkSupportedByLidoKeys(flags.network) + if !supported { + log.Fatalf(configs.InvalidNetworkForLidoKeys, options) } } // Warn about withdrawal address diff --git a/configs/errors.go b/configs/errors.go index f729c2e9..8b82002f 100644 --- a/configs/errors.go +++ b/configs/errors.go @@ -90,4 +90,5 @@ const ( IncompatibleLidoAndEth1Withdrawal = "incompatible flags --lido, and --eth-withdrawal-address can't be used together" InvalidNetworkForLido = "invalid network: Choose valid network for Lido: %v" InvalidNetworkForLidoMevBoost = "invalid network: Choose valid network for Lido with MEV-Boost: %v" + InvalidNetworkForLidoKeys = "invalid network: Choose valid network for Lido Withdrawal Address: %v" ) diff --git a/internal/lido/contracts/feeRecipient.go b/internal/lido/contracts/feeRecipient.go index f1eeb614..e73e4b63 100644 --- a/internal/lido/contracts/feeRecipient.go +++ b/internal/lido/contracts/feeRecipient.go @@ -34,3 +34,17 @@ func GetLidoSupportedNetworks() []string { sort.Strings(networks) return networks } + +func NetworkSupportedByLido(network string) ([]string, bool) { + supportedNetworks := GetLidoSupportedNetworks() + var supported bool + for _, supportedNetwork := range supportedNetworks { + if network == supportedNetwork { + supported = true + } + } + if !supported { + return supportedNetworks, supported + } + return nil, supported +} diff --git a/internal/lido/contracts/mevboostrelaylist/get_relays.go b/internal/lido/contracts/mevboostrelaylist/get_relays.go index 247fac11..a9854203 100644 --- a/internal/lido/contracts/mevboostrelaylist/get_relays.go +++ b/internal/lido/contracts/mevboostrelaylist/get_relays.go @@ -152,3 +152,17 @@ func GetLidoSupportedNetworksMevBoost() []string { sort.Strings(networks) return networks } + +func NetworkSupportedByLidoMevBoost(network string) ([]string, bool) { + supportedNetworks := GetLidoSupportedNetworksMevBoost() + var supported bool + for _, supportedNetwork := range supportedNetworks { + if network == supportedNetwork { + supported = true + } + } + if !supported { + return supportedNetworks, supported + } + return nil, supported +} diff --git a/internal/lido/contracts/withdrawalAddress.go b/internal/lido/contracts/withdrawalAddress.go index d394311a..087410ed 100644 --- a/internal/lido/contracts/withdrawalAddress.go +++ b/internal/lido/contracts/withdrawalAddress.go @@ -30,3 +30,17 @@ func GetLidoKeysSupportedNetworks() []string { sort.Strings(networks) return networks } + +func NetworkSupportedByLidoKeys(network string) ([]string, bool) { + supportedNetworks := GetLidoKeysSupportedNetworks() + var supported bool + for _, supportedNetwork := range supportedNetworks { + if network == supportedNetwork { + supported = true + } + } + if !supported { + return supportedNetworks, supported + } + return nil, supported +} From 381a99f8d4edc125d9a267655d844c558d02a21d Mon Sep 17 00:00:00 2001 From: Haneen Khalifa Date: Fri, 5 Jul 2024 18:25:55 +0300 Subject: [PATCH 23/31] refactor: NodeOptionsFactory interface for cli --- cli/cli.go | 49 ++++++++++++++--------------- cli/nodeSetup.go | 81 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 105 insertions(+), 25 deletions(-) create mode 100644 cli/nodeSetup.go diff --git a/cli/cli.go b/cli/cli.go index 36c0e7b4..6ab251f1 100644 --- a/cli/cli.go +++ b/cli/cli.go @@ -25,8 +25,6 @@ import ( eth2 "github.com/protolambda/zrnt/eth2/configs" - "github.com/NethermindEth/sedge/internal/lido/contracts" - "github.com/NethermindEth/sedge/internal/lido/contracts/mevboostrelaylist" "github.com/NethermindEth/sedge/internal/pkg/clients" "github.com/NethermindEth/sedge/internal/pkg/dependencies" "github.com/NethermindEth/sedge/internal/pkg/generate" @@ -815,11 +813,10 @@ func confirmInstallDependencies(p ui.Prompter, o *CliCmdOptions) (err error) { } func confirmEnableMEVBoost(p ui.Prompter, o *CliCmdOptions) (err error) { - if o.nodeSetup == LidoNode { - _, supported := mevboostrelaylist.NetworkSupportedByLidoMevBoost(o.genData.Network) - if supported { - o.withMevBoost = true - } + factory := GetNodeOptions(o.nodeSetup) + enableMev, overwrite := factory.EnableMEVBoost(o.genData.Network) + if overwrite{ + o.withMevBoost = enableMev return } o.withMevBoost, err = p.Confirm("Enable MEV Boost?", true) @@ -877,16 +874,17 @@ func inputMevBoostEndpoint(p ui.Prompter, o *CliCmdOptions) (err error) { } func inputRelayURL(p ui.Prompter, o *CliCmdOptions) (err error) { - if o.nodeSetup == LidoNode { - relayURLs, err := mevboostrelaylist.GetRelaysURI(o.genData.Network) - if err != nil { - return err - } + factory := GetNodeOptions(o.nodeSetup) + relayURLs, err := factory.GetRelayURLs(o.genData.Network) + if err != nil { + return err + } + if relayURLs != nil{ o.genData.RelayURLs = relayURLs - return nil + return } var defaultValue []string = configs.NetworksConfigs()[o.genData.Network].RelayURLs - relayURLs, err := p.InputList("Insert relay URLs if you don't want to use the default values listed below", defaultValue, func(list []string) error { + relayURLs, err = p.InputList("Insert relay URLs if you don't want to use the default values listed below", defaultValue, func(list []string) error { badUri, ok := utils.UriValidator(list) if !ok { return fmt.Errorf(configs.InvalidUrlFlagError, "relay", badUri) @@ -908,9 +906,10 @@ func inputCheckpointSyncURL(p ui.Prompter, o *CliCmdOptions) (err error) { } func inputFeeRecipient(p ui.Prompter, o *CliCmdOptions) (err error) { - if o.nodeSetup == LidoNode { - feeRecipient := contracts.FeeRecipient[o.genData.Network] - o.genData.FeeRecipient = feeRecipient.FeeRecipientAddress + factory := GetNodeOptions(o.nodeSetup) + feeRecipient, overwrite := factory.GetFeeRecipient(o.genData.Network) + if overwrite { + o.genData.FeeRecipient = feeRecipient return } o.genData.FeeRecipient, err = p.EthAddress("Please enter the Fee Recipient address", "", true) @@ -918,9 +917,10 @@ func inputFeeRecipient(p ui.Prompter, o *CliCmdOptions) (err error) { } func inputFeeRecipientNoValidator(p ui.Prompter, o *CliCmdOptions) (err error) { - if o.nodeSetup == LidoNode { - feeRecipient := contracts.FeeRecipient[o.genData.Network] - o.genData.FeeRecipient = feeRecipient.FeeRecipientAddress + factory := GetNodeOptions(o.nodeSetup) + feeRecipient, overwrite := factory.GetFeeRecipient(o.genData.Network) + if overwrite { + o.genData.FeeRecipient = feeRecipient return } o.genData.FeeRecipient, err = p.EthAddress("Please enter the Fee Recipient address (press enter to skip it)", "", false) @@ -977,11 +977,10 @@ func inputKeystorePassphrase(p ui.Prompter, o *CliCmdOptions) (err error) { } func inputWithdrawalAddress(p ui.Prompter, o *CliCmdOptions) (err error) { - if o.nodeSetup == LidoNode { - _, supported := contracts.NetworkSupportedByLidoKeys(o.genData.Network) - if supported { - o.withdrawalAddress = contracts.WithdrawalAddress[o.genData.Network].WithdrawalAddress - } + factory := GetNodeOptions(o.nodeSetup) + withdrawalAddress, overwrite := factory.GetWithdrawalAddress(o.genData.Network) + if overwrite { + o.withdrawalAddress = withdrawalAddress return } o.withdrawalAddress, err = p.Input("Withdrawal address", "", false, func(s string) error { return ui.EthAddressValidator(s, true) }) diff --git a/cli/nodeSetup.go b/cli/nodeSetup.go new file mode 100644 index 00000000..2e562f9f --- /dev/null +++ b/cli/nodeSetup.go @@ -0,0 +1,81 @@ +package cli + + +import ( + + "github.com/NethermindEth/sedge/internal/lido/contracts" + "github.com/NethermindEth/sedge/internal/lido/contracts/mevboostrelaylist" +) + +// NodeOptionsFactory is the interface for creating options +type NodeOptionsFactory interface { + CreateNetworkOptions() []string + GetWithdrawalAddress(network string) (string, bool) + GetFeeRecipient(network string) (string, bool) + GetRelayURLs(network string) ([]string, error) + EnableMEVBoost(network string) (bool, bool) +} + +// GetNodeOptions returns the appropriate NodeOptionsFactory based on node setup +func GetNodeOptions(nodeSetup string) NodeOptionsFactory { + switch nodeSetup { + case LidoNode: + return &LidoNodeOptions{} + default: + return &EthereumNodeOptions{} + } +} + +// LidoNodeOptions for Lido Node +type LidoNodeOptions struct{} +func (l *LidoNodeOptions) CreateNetworkOptions() []string { + options := contracts.GetLidoSupportedNetworks() + return options +} +func (l *LidoNodeOptions) GetWithdrawalAddress(network string) (string, bool) { + _, supported := contracts.NetworkSupportedByLidoKeys(network) + if supported { + return contracts.WithdrawalAddress[network].WithdrawalAddress, true + } + return "", true +} +func (l *LidoNodeOptions) GetFeeRecipient(network string) (string, bool) { + _, supported := contracts.NetworkSupportedByLido(network) + if supported { + return contracts.FeeRecipient[network].FeeRecipientAddress, true + } + return "", true +} +func (l *LidoNodeOptions) GetRelayURLs(network string) ([]string, error) { + relayURLs, err := mevboostrelaylist.GetRelaysURI(network) + if err != nil { + return nil, err + } + return relayURLs, nil +} +func (l *LidoNodeOptions) EnableMEVBoost(network string) (bool, bool) { + _, supported := mevboostrelaylist.NetworkSupportedByLidoMevBoost(network) + if supported { + return true, true + } + return false, true +} + +// EthereumNodeOptions for Ethereum Node +type EthereumNodeOptions struct{} +func (e *EthereumNodeOptions) CreateNetworkOptions() []string { + return []string{NetworkMainnet, NetworkSepolia, NetworkGnosis, NetworkChiado, NetworkHolesky} +} + +func (e *EthereumNodeOptions) GetWithdrawalAddress(network string) (string, bool) { + return "", false +} +func (e *EthereumNodeOptions) GetFeeRecipient(network string) (string, bool) { + return "", false // Default implementation returns false for EthereumNode +} +func (e *EthereumNodeOptions) GetRelayURLs(network string) ([]string, error) { + return nil, nil +} +func (e *EthereumNodeOptions) EnableMEVBoost(network string) (bool, bool) { + return false, false +} From ca7ad5e978436664ee6f1f626f4b03febff5a263 Mon Sep 17 00:00:00 2001 From: Haneen Khalifa Date: Fri, 5 Jul 2024 19:11:27 +0300 Subject: [PATCH 24/31] refactor: NodeOptionsFactory interface for sedge generate --- cli/cli.go | 4 ++-- cli/generate.go | 19 +++++++------------ cli/nodeSetup.go | 40 +++++++++++++++++++++++++++++++++++----- cli/sub_gen.go | 12 ++++++++---- 4 files changed, 52 insertions(+), 23 deletions(-) diff --git a/cli/cli.go b/cli/cli.go index 6ab251f1..284914a0 100644 --- a/cli/cli.go +++ b/cli/cli.go @@ -815,7 +815,7 @@ func confirmInstallDependencies(p ui.Prompter, o *CliCmdOptions) (err error) { func confirmEnableMEVBoost(p ui.Prompter, o *CliCmdOptions) (err error) { factory := GetNodeOptions(o.nodeSetup) enableMev, overwrite := factory.EnableMEVBoost(o.genData.Network) - if overwrite{ + if overwrite { o.withMevBoost = enableMev return } @@ -879,7 +879,7 @@ func inputRelayURL(p ui.Prompter, o *CliCmdOptions) (err error) { if err != nil { return err } - if relayURLs != nil{ + if relayURLs != nil { o.genData.RelayURLs = relayURLs return } diff --git a/cli/generate.go b/cli/generate.go index 34fc635b..4fe421ed 100644 --- a/cli/generate.go +++ b/cli/generate.go @@ -491,17 +491,12 @@ func loadJWTSecret(from string) (absFrom string, err error) { return } -func validateLido(network string, flags *GenCmdFlags) error { - if !flags.noMev { - options, supported := mevboostrelaylist.NetworkSupportedByLidoMevBoost(network) - if !supported { - return fmt.Errorf(configs.InvalidNetworkForLidoMevBoost, options) - } - } - options, supported := contracts.NetworkSupportedByLido(network) - if !supported { - return fmt.Errorf(configs.InvalidNetworkForLido, options) +func nodeType() string { + var nodeType string + if lidoNode { + nodeType = LidoNode + } else { + nodeType = EthereumNode } - - return nil + return nodeType } diff --git a/cli/nodeSetup.go b/cli/nodeSetup.go index 2e562f9f..e1d2140d 100644 --- a/cli/nodeSetup.go +++ b/cli/nodeSetup.go @@ -1,8 +1,9 @@ package cli - import ( + "fmt" + "github.com/NethermindEth/sedge/configs" "github.com/NethermindEth/sedge/internal/lido/contracts" "github.com/NethermindEth/sedge/internal/lido/contracts/mevboostrelaylist" ) @@ -14,6 +15,7 @@ type NodeOptionsFactory interface { GetFeeRecipient(network string) (string, bool) GetRelayURLs(network string) ([]string, error) EnableMEVBoost(network string) (bool, bool) + ValidateNode(network string, flags *GenCmdFlags) error } // GetNodeOptions returns the appropriate NodeOptionsFactory based on node setup @@ -28,10 +30,12 @@ func GetNodeOptions(nodeSetup string) NodeOptionsFactory { // LidoNodeOptions for Lido Node type LidoNodeOptions struct{} + func (l *LidoNodeOptions) CreateNetworkOptions() []string { options := contracts.GetLidoSupportedNetworks() return options } + func (l *LidoNodeOptions) GetWithdrawalAddress(network string) (string, bool) { _, supported := contracts.NetworkSupportedByLidoKeys(network) if supported { @@ -39,6 +43,7 @@ func (l *LidoNodeOptions) GetWithdrawalAddress(network string) (string, bool) { } return "", true } + func (l *LidoNodeOptions) GetFeeRecipient(network string) (string, bool) { _, supported := contracts.NetworkSupportedByLido(network) if supported { @@ -46,13 +51,15 @@ func (l *LidoNodeOptions) GetFeeRecipient(network string) (string, bool) { } return "", true } + func (l *LidoNodeOptions) GetRelayURLs(network string) ([]string, error) { relayURLs, err := mevboostrelaylist.GetRelaysURI(network) - if err != nil { - return nil, err - } + if err != nil { + return nil, err + } return relayURLs, nil } + func (l *LidoNodeOptions) EnableMEVBoost(network string) (bool, bool) { _, supported := mevboostrelaylist.NetworkSupportedByLidoMevBoost(network) if supported { @@ -61,21 +68,44 @@ func (l *LidoNodeOptions) EnableMEVBoost(network string) (bool, bool) { return false, true } +func (l *LidoNodeOptions) ValidateNode(network string, flags *GenCmdFlags) error { + if !flags.noMev { + options, supported := mevboostrelaylist.NetworkSupportedByLidoMevBoost(network) + if !supported { + return fmt.Errorf(configs.InvalidNetworkForLidoMevBoost, options) + } + } + options, supported := contracts.NetworkSupportedByLido(network) + if !supported { + return fmt.Errorf(configs.InvalidNetworkForLido, options) + } + + return nil +} + // EthereumNodeOptions for Ethereum Node type EthereumNodeOptions struct{} + func (e *EthereumNodeOptions) CreateNetworkOptions() []string { return []string{NetworkMainnet, NetworkSepolia, NetworkGnosis, NetworkChiado, NetworkHolesky} } func (e *EthereumNodeOptions) GetWithdrawalAddress(network string) (string, bool) { - return "", false + return "", false } + func (e *EthereumNodeOptions) GetFeeRecipient(network string) (string, bool) { return "", false // Default implementation returns false for EthereumNode } + func (e *EthereumNodeOptions) GetRelayURLs(network string) ([]string, error) { return nil, nil } + func (e *EthereumNodeOptions) EnableMEVBoost(network string) (bool, bool) { return false, false } + +func (e *EthereumNodeOptions) ValidateNode(network string, flags *GenCmdFlags) error { + return nil +} diff --git a/cli/sub_gen.go b/cli/sub_gen.go index eb97cead..bc84a7c7 100644 --- a/cli/sub_gen.go +++ b/cli/sub_gen.go @@ -55,7 +55,8 @@ Additionally, you can use this syntax ':' to override the if err := validateCustomNetwork(&flags.CustomFlags, network); err != nil { return err } - if err := validateLido(network, &flags); err != nil && lidoNode { + factory := GetNodeOptions(nodeType()) + if err := factory.ValidateNode(network, &flags); err != nil { return err } return preValidationGenerateCmd(network, logging, &flags) @@ -130,7 +131,8 @@ func ExecutionSubCmd(sedgeAction actions.SedgeActions) *cobra.Command { if err := validateCustomNetwork(&flags.CustomFlags, network); err != nil { return err } - if err := validateLido(network, &flags); err != nil && lidoNode { + factory := GetNodeOptions(nodeType()) + if err := factory.ValidateNode(network, &flags); err != nil { return err } return preValidationGenerateCmd(network, logging, &flags) @@ -178,7 +180,8 @@ func ConsensusSubCmd(sedgeAction actions.SedgeActions) *cobra.Command { if err := validateCustomNetwork(&flags.CustomFlags, network); err != nil { return err } - if err := validateLido(network, &flags); err != nil && lidoNode { + factory := GetNodeOptions(nodeType()) + if err := factory.ValidateNode(network, &flags); err != nil { return err } return preValidationGenerateCmd(network, logging, &flags) @@ -241,7 +244,8 @@ func ValidatorSubCmd(sedgeAction actions.SedgeActions) *cobra.Command { if err := validateCustomNetwork(&flags.CustomFlags, network); err != nil { return err } - if err := validateLido(network, &flags); err != nil && lidoNode { + factory := GetNodeOptions(nodeType()) + if err := factory.ValidateNode(network, &flags); err != nil { return err } return preValidationGenerateCmd(network, logging, &flags) From dd49a00d253a62f01d552faf6f835ec157f0887f Mon Sep 17 00:00:00 2001 From: AntiD2ta Date: Mon, 8 Jul 2024 13:01:23 +0000 Subject: [PATCH 25/31] fix: Holesky fork version --- configs/init.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configs/init.go b/configs/init.go index db5dd533..e1006089 100644 --- a/configs/init.go +++ b/configs/init.go @@ -58,7 +58,7 @@ var networksConfigs map[string]NetworkConfig = map[string]NetworkConfig{ NetworkHolesky: { Name: NetworkHolesky, NetworkService: "merge", - GenesisForkVersion: "0x00017000", + GenesisForkVersion: "0x01017000", SupportsMEVBoost: true, CheckpointSyncURL: "https://checkpoint-sync.holesky.ethpandaops.io/", RelayURLs: []string{ From c8777431cd3e3e51ba26f345cb8fd4e0c27332f9 Mon Sep 17 00:00:00 2001 From: AntiD2ta Date: Tue, 9 Jul 2024 00:47:52 +0200 Subject: [PATCH 26/31] refac: New pkg options for Sedge setup type logic --- cli/cli.go | 48 ++-- cli/cli_test.go | 41 +-- cli/generate.go | 14 +- cli/generate_test.go | 14 +- cli/keys.go | 7 +- cli/nodeSetup.go | 111 --------- cli/sub_gen.go | 41 ++- configs/init.go | 6 + configs/types.go | 1 + e2e/generate_test.go | 8 +- e2e/keys_test.go | 8 +- internal/lido/contracts/feeRecipient.go | 70 +++--- .../contracts/mevboostrelaylist/get_relays.go | 35 ++- .../mevboostrelaylist/get_relays_test.go | 19 +- internal/lido/contracts/withdrawalAddress.go | 55 ++-- internal/pkg/options/consts.go | 21 ++ internal/pkg/options/errors.go | 28 +++ internal/pkg/options/ethereum.go | 92 +++++++ internal/pkg/options/ethereum_test.go | 202 +++++++++++++++ internal/pkg/options/interface.go | 43 ++++ internal/pkg/options/lido.go | 92 +++++++ internal/pkg/options/lido_test.go | 234 ++++++++++++++++++ internal/pkg/options/types.go | 30 +++ 23 files changed, 968 insertions(+), 252 deletions(-) delete mode 100644 cli/nodeSetup.go create mode 100644 internal/pkg/options/consts.go create mode 100644 internal/pkg/options/errors.go create mode 100644 internal/pkg/options/ethereum.go create mode 100644 internal/pkg/options/ethereum_test.go create mode 100644 internal/pkg/options/interface.go create mode 100644 internal/pkg/options/lido.go create mode 100644 internal/pkg/options/lido_test.go create mode 100644 internal/pkg/options/types.go diff --git a/cli/cli.go b/cli/cli.go index 284914a0..e7dd21d1 100644 --- a/cli/cli.go +++ b/cli/cli.go @@ -28,6 +28,7 @@ import ( "github.com/NethermindEth/sedge/internal/pkg/clients" "github.com/NethermindEth/sedge/internal/pkg/dependencies" "github.com/NethermindEth/sedge/internal/pkg/generate" + sedgeOpts "github.com/NethermindEth/sedge/internal/pkg/options" "github.com/NethermindEth/sedge/internal/ui" "github.com/NethermindEth/sedge/cli/actions" @@ -51,9 +52,6 @@ const ( NodeTypeConsensus = "consensus" NodeTypeValidator = "validator" - EthereumNode = "ethereum-node" - LidoNode = "lido-node" - Randomize = "randomize" SourceTypeExisting = "existing" @@ -621,7 +619,7 @@ func runPromptActions(p ui.Prompter, o *CliCmdOptions, actions ...promptAction) } func selectNodeSetup(p ui.Prompter, o *CliCmdOptions) (err error) { - options := []string{EthereumNode, LidoNode} + options := []string{sedgeOpts.EthereumNode, sedgeOpts.LidoNode} index, err := p.Select("Select node setup", "", options) if err != nil { return err @@ -631,12 +629,9 @@ func selectNodeSetup(p ui.Prompter, o *CliCmdOptions) (err error) { } func selectNetwork(p ui.Prompter, o *CliCmdOptions) error { - var options []string - if o.nodeSetup == LidoNode { - options = []string{NetworkMainnet, NetworkHolesky, NetworkSepolia} - } else { - options = []string{NetworkMainnet, NetworkSepolia, NetworkGnosis, NetworkChiado, NetworkHolesky} - } + opts := sedgeOpts.CreateSedgeOptions(o.nodeSetup) + options := opts.SupportedNetworks() + index, err := p.Select("Select network", "", options) if err != nil { return err @@ -813,9 +808,9 @@ func confirmInstallDependencies(p ui.Prompter, o *CliCmdOptions) (err error) { } func confirmEnableMEVBoost(p ui.Prompter, o *CliCmdOptions) (err error) { - factory := GetNodeOptions(o.nodeSetup) - enableMev, overwrite := factory.EnableMEVBoost(o.genData.Network) - if overwrite { + opts := sedgeOpts.CreateSedgeOptions(o.nodeSetup) + enableMev := opts.MEVBoostEnabled(o.genData.Network) + if opts.OverwriteSettings().MevBoost { o.withMevBoost = enableMev return } @@ -874,17 +869,16 @@ func inputMevBoostEndpoint(p ui.Prompter, o *CliCmdOptions) (err error) { } func inputRelayURL(p ui.Prompter, o *CliCmdOptions) (err error) { - factory := GetNodeOptions(o.nodeSetup) - relayURLs, err := factory.GetRelayURLs(o.genData.Network) + opts := sedgeOpts.CreateSedgeOptions(o.nodeSetup) + relayURLs, err := opts.RelayURLs(o.genData.Network) if err != nil { return err } - if relayURLs != nil { + if relayURLs != nil && opts.OverwriteSettings().RelayURLs { o.genData.RelayURLs = relayURLs return } - var defaultValue []string = configs.NetworksConfigs()[o.genData.Network].RelayURLs - relayURLs, err = p.InputList("Insert relay URLs if you don't want to use the default values listed below", defaultValue, func(list []string) error { + relayURLs, err = p.InputList("Insert relay URLs if you don't want to use the default values listed below", relayURLs, func(list []string) error { badUri, ok := utils.UriValidator(list) if !ok { return fmt.Errorf(configs.InvalidUrlFlagError, "relay", badUri) @@ -906,9 +900,9 @@ func inputCheckpointSyncURL(p ui.Prompter, o *CliCmdOptions) (err error) { } func inputFeeRecipient(p ui.Prompter, o *CliCmdOptions) (err error) { - factory := GetNodeOptions(o.nodeSetup) - feeRecipient, overwrite := factory.GetFeeRecipient(o.genData.Network) - if overwrite { + opts := sedgeOpts.CreateSedgeOptions(o.nodeSetup) + feeRecipient := opts.FeeRecipient(o.genData.Network) + if opts.OverwriteSettings().FeeRecipient { o.genData.FeeRecipient = feeRecipient return } @@ -917,9 +911,9 @@ func inputFeeRecipient(p ui.Prompter, o *CliCmdOptions) (err error) { } func inputFeeRecipientNoValidator(p ui.Prompter, o *CliCmdOptions) (err error) { - factory := GetNodeOptions(o.nodeSetup) - feeRecipient, overwrite := factory.GetFeeRecipient(o.genData.Network) - if overwrite { + opts := sedgeOpts.CreateSedgeOptions(o.nodeSetup) + feeRecipient := opts.FeeRecipient(o.genData.Network) + if opts.OverwriteSettings().FeeRecipient { o.genData.FeeRecipient = feeRecipient return } @@ -977,9 +971,9 @@ func inputKeystorePassphrase(p ui.Prompter, o *CliCmdOptions) (err error) { } func inputWithdrawalAddress(p ui.Prompter, o *CliCmdOptions) (err error) { - factory := GetNodeOptions(o.nodeSetup) - withdrawalAddress, overwrite := factory.GetWithdrawalAddress(o.genData.Network) - if overwrite { + opts := sedgeOpts.CreateSedgeOptions(o.nodeSetup) + withdrawalAddress := opts.WithdrawalAddress(o.genData.Network) + if opts.OverwriteSettings().WithdrawalAddress { o.withdrawalAddress = withdrawalAddress return } diff --git a/cli/cli_test.go b/cli/cli_test.go index f4567cd7..033503fc 100644 --- a/cli/cli_test.go +++ b/cli/cli_test.go @@ -26,6 +26,7 @@ import ( "github.com/NethermindEth/sedge/internal/pkg/clients" "github.com/NethermindEth/sedge/internal/pkg/dependencies" "github.com/NethermindEth/sedge/internal/pkg/generate" + sedgeOpts "github.com/NethermindEth/sedge/internal/pkg/options" "github.com/NethermindEth/sedge/internal/ui" "github.com/NethermindEth/sedge/internal/utils" sedge_mocks "github.com/NethermindEth/sedge/mocks" @@ -38,7 +39,7 @@ func TestCli(t *testing.T) { // Silence logger log.SetOutput(io.Discard) - mevboostRelayListUris, _ := mevboostrelaylist.GetRelaysURI("mainnet") + mevboostRelayListUris, _ := mevboostrelaylist.RelaysURI("mainnet") ETHClients := map[string][]string{ "execution": clients.AllClients["execution"], @@ -97,8 +98,8 @@ func TestCli(t *testing.T) { } sedgeActions.EXPECT().GetCommandRunner().Return(&test.SimpleCMDRunner{}) gomock.InOrder( - prompter.EXPECT().Select("Select node setup", "", []string{EthereumNode, LidoNode}).Return(0, nil), - prompter.EXPECT().Select("Select network", "", []string{NetworkMainnet, NetworkSepolia, NetworkGnosis, NetworkChiado, NetworkHolesky}).Return(0, nil), + prompter.EXPECT().Select("Select node setup", "", []string{sedgeOpts.EthereumNode, sedgeOpts.LidoNode}).Return(0, nil), + prompter.EXPECT().Select("Select network", "", []string{NetworkMainnet, NetworkHolesky, NetworkSepolia, NetworkGnosis, NetworkChiado}).Return(0, nil), prompter.EXPECT().Select("Select node type", "", []string{NodeTypeFullNode, NodeTypeExecution, NodeTypeConsensus, NodeTypeValidator}).Return(0, nil), prompter.EXPECT().Input("Generation path", configs.DefaultAbsSedgeDataPath, false, nil).Return(generationPath, nil), prompter.EXPECT().Input("Container tag, sedge will add to each container and the network, a suffix with the tag", "", false, nil).Return("tag", nil), @@ -168,8 +169,8 @@ func TestCli(t *testing.T) { JWTSecretPath: filepath.Join(generationPath, "jwtsecret"), } gomock.InOrder( - prompter.EXPECT().Select("Select node setup", "", []string{EthereumNode, LidoNode}).Return(0, nil), - prompter.EXPECT().Select("Select network", "", []string{NetworkMainnet, NetworkSepolia, NetworkGnosis, NetworkChiado, NetworkHolesky}).Return(0, nil), + prompter.EXPECT().Select("Select node setup", "", []string{sedgeOpts.EthereumNode, sedgeOpts.LidoNode}).Return(0, nil), + prompter.EXPECT().Select("Select network", "", []string{NetworkMainnet, NetworkHolesky, NetworkSepolia, NetworkGnosis, NetworkChiado}).Return(0, nil), prompter.EXPECT().Select("Select node type", "", []string{NodeTypeFullNode, NodeTypeExecution, NodeTypeConsensus, NodeTypeValidator}).Return(0, nil), prompter.EXPECT().Input("Generation path", configs.DefaultAbsSedgeDataPath, false, nil).Return(generationPath, nil), prompter.EXPECT().Input("Container tag, sedge will add to each container and the network, a suffix with the tag", "", false, nil).Return("tag", nil), @@ -212,8 +213,8 @@ func TestCli(t *testing.T) { JWTSecretPath: filepath.Join(generationPath, "jwtsecret"), } gomock.InOrder( - prompter.EXPECT().Select("Select node setup", "", []string{EthereumNode, LidoNode}).Return(0, nil), - prompter.EXPECT().Select("Select network", "", []string{NetworkMainnet, NetworkSepolia, NetworkGnosis, NetworkChiado, NetworkHolesky}).Return(4, nil), + prompter.EXPECT().Select("Select node setup", "", []string{sedgeOpts.EthereumNode, sedgeOpts.LidoNode}).Return(0, nil), + prompter.EXPECT().Select("Select network", "", []string{NetworkMainnet, NetworkHolesky, NetworkSepolia, NetworkGnosis, NetworkChiado}).Return(1, nil), prompter.EXPECT().Select("Select node type", "", []string{NodeTypeFullNode, NodeTypeExecution, NodeTypeConsensus, NodeTypeValidator}).Return(0, nil), prompter.EXPECT().Input("Generation path", configs.DefaultAbsSedgeDataPath, false, nil).Return(generationPath, nil), prompter.EXPECT().Input("Container tag, sedge will add to each container and the network, a suffix with the tag", "", false, nil).Return("tag", nil), @@ -249,8 +250,8 @@ func TestCli(t *testing.T) { } gomock.InOrder( - prompter.EXPECT().Select("Select node setup", "", []string{EthereumNode, LidoNode}).Return(0, nil), - prompter.EXPECT().Select("Select network", "", []string{NetworkMainnet, NetworkSepolia, NetworkGnosis, NetworkChiado, NetworkHolesky}).Return(0, nil), + prompter.EXPECT().Select("Select node setup", "", []string{sedgeOpts.EthereumNode, sedgeOpts.LidoNode}).Return(0, nil), + prompter.EXPECT().Select("Select network", "", []string{NetworkMainnet, NetworkHolesky, NetworkSepolia, NetworkGnosis, NetworkChiado}).Return(0, nil), prompter.EXPECT().Select("Select node type", "", []string{NodeTypeFullNode, NodeTypeExecution, NodeTypeConsensus, NodeTypeValidator}).Return(1, nil), prompter.EXPECT().Input("Generation path", configs.DefaultAbsSedgeDataPath, false, nil).Return(generationPath, nil), prompter.EXPECT().Input("Container tag, sedge will add to each container and the network, a suffix with the tag", "", false, nil).Return("tag", nil), @@ -293,8 +294,8 @@ func TestCli(t *testing.T) { } gomock.InOrder( - prompter.EXPECT().Select("Select node setup", "", []string{EthereumNode, LidoNode}).Return(0, nil), - prompter.EXPECT().Select("Select network", "", []string{NetworkMainnet, NetworkSepolia, NetworkGnosis, NetworkChiado, NetworkHolesky}).Return(4, nil), + prompter.EXPECT().Select("Select node setup", "", []string{sedgeOpts.EthereumNode, sedgeOpts.LidoNode}).Return(0, nil), + prompter.EXPECT().Select("Select network", "", []string{NetworkMainnet, NetworkHolesky, NetworkSepolia, NetworkGnosis, NetworkChiado}).Return(1, nil), prompter.EXPECT().Select("Select node type", "", []string{NodeTypeFullNode, NodeTypeExecution, NodeTypeConsensus, NodeTypeValidator}).Return(1, nil), prompter.EXPECT().Input("Generation path", configs.DefaultAbsSedgeDataPath, false, nil).Return(generationPath, nil), prompter.EXPECT().Input("Container tag, sedge will add to each container and the network, a suffix with the tag", "", false, nil).Return("tag", nil), @@ -343,8 +344,8 @@ func TestCli(t *testing.T) { } gomock.InOrder( - prompter.EXPECT().Select("Select node setup", "", []string{EthereumNode, LidoNode}).Return(0, nil), - prompter.EXPECT().Select("Select network", "", []string{NetworkMainnet, NetworkSepolia, NetworkGnosis, NetworkChiado, NetworkHolesky}).Return(0, nil), + prompter.EXPECT().Select("Select node setup", "", []string{sedgeOpts.EthereumNode, sedgeOpts.LidoNode}).Return(0, nil), + prompter.EXPECT().Select("Select network", "", []string{NetworkMainnet, NetworkHolesky, NetworkSepolia, NetworkGnosis, NetworkChiado}).Return(0, nil), prompter.EXPECT().Select("Select node type", "", []string{NodeTypeFullNode, NodeTypeExecution, NodeTypeConsensus, NodeTypeValidator}).Return(2, nil), prompter.EXPECT().Input("Generation path", configs.DefaultAbsSedgeDataPath, false, nil).Return(generationPath, nil), prompter.EXPECT().Input("Container tag, sedge will add to each container and the network, a suffix with the tag", "", false, nil).Return("tag", nil), @@ -387,8 +388,8 @@ func TestCli(t *testing.T) { } gomock.InOrder( - prompter.EXPECT().Select("Select node setup", "", []string{EthereumNode, LidoNode}).Return(0, nil), - prompter.EXPECT().Select("Select network", "", []string{NetworkMainnet, NetworkSepolia, NetworkGnosis, NetworkChiado, NetworkHolesky}).Return(4, nil), + prompter.EXPECT().Select("Select node setup", "", []string{sedgeOpts.EthereumNode, sedgeOpts.LidoNode}).Return(0, nil), + prompter.EXPECT().Select("Select network", "", []string{NetworkMainnet, NetworkHolesky, NetworkSepolia, NetworkGnosis, NetworkChiado}).Return(1, nil), prompter.EXPECT().Select("Select node type", "", []string{NodeTypeFullNode, NodeTypeExecution, NodeTypeConsensus, NodeTypeValidator}).Return(2, nil), prompter.EXPECT().Input("Generation path", configs.DefaultAbsSedgeDataPath, false, nil).Return(generationPath, nil), prompter.EXPECT().Input("Container tag, sedge will add to each container and the network, a suffix with the tag", "", false, nil).Return("tag", nil), @@ -431,8 +432,8 @@ func TestCli(t *testing.T) { } gomock.InOrder( - prompter.EXPECT().Select("Select node setup", "", []string{EthereumNode, LidoNode}).Return(0, nil), - prompter.EXPECT().Select("Select network", "", []string{NetworkMainnet, NetworkSepolia, NetworkGnosis, NetworkChiado, NetworkHolesky}).Return(0, nil), + prompter.EXPECT().Select("Select node setup", "", []string{sedgeOpts.EthereumNode, sedgeOpts.LidoNode}).Return(0, nil), + prompter.EXPECT().Select("Select network", "", []string{NetworkMainnet, NetworkHolesky, NetworkSepolia, NetworkGnosis, NetworkChiado}).Return(0, nil), prompter.EXPECT().Select("Select node type", "", []string{NodeTypeFullNode, NodeTypeExecution, NodeTypeConsensus, NodeTypeValidator}).Return(3, nil), prompter.EXPECT().Input("Generation path", configs.DefaultAbsSedgeDataPath, false, nil).Return(generationPath, nil), prompter.EXPECT().Input("Container tag, sedge will add to each container and the network, a suffix with the tag", "", false, nil).Return("tag", nil), @@ -494,8 +495,8 @@ func TestCli(t *testing.T) { } gomock.InOrder( - prompter.EXPECT().Select("Select node setup", "", []string{EthereumNode, LidoNode}).Return(0, nil), - prompter.EXPECT().Select("Select network", "", []string{NetworkMainnet, NetworkSepolia, NetworkGnosis, NetworkChiado, NetworkHolesky}).Return(0, nil), + prompter.EXPECT().Select("Select node setup", "", []string{sedgeOpts.EthereumNode, sedgeOpts.LidoNode}).Return(0, nil), + prompter.EXPECT().Select("Select network", "", []string{NetworkMainnet, NetworkHolesky, NetworkSepolia, NetworkGnosis, NetworkChiado}).Return(0, nil), prompter.EXPECT().Select("Select node type", "", []string{NodeTypeFullNode, NodeTypeExecution, NodeTypeConsensus, NodeTypeValidator}).Return(2, nil), prompter.EXPECT().Input("Generation path", configs.DefaultAbsSedgeDataPath, false, nil).Return(generationPath, nil), prompter.EXPECT().Input("Container tag, sedge will add to each container and the network, a suffix with the tag", "", false, nil).Return("tag", nil), @@ -550,7 +551,7 @@ func TestCli(t *testing.T) { } sedgeActions.EXPECT().GetCommandRunner().Return(&test.SimpleCMDRunner{}) gomock.InOrder( - prompter.EXPECT().Select("Select node setup", "", []string{EthereumNode, LidoNode}).Return(1, nil), + prompter.EXPECT().Select("Select node setup", "", []string{sedgeOpts.EthereumNode, sedgeOpts.LidoNode}).Return(1, nil), prompter.EXPECT().Select("Select network", "", []string{NetworkMainnet, NetworkHolesky, NetworkSepolia}).Return(0, nil), prompter.EXPECT().Select("Select node type", "", []string{NodeTypeFullNode, NodeTypeExecution, NodeTypeConsensus, NodeTypeValidator}).Return(0, nil), prompter.EXPECT().Input("Generation path", configs.DefaultAbsSedgeDataPath, false, nil).Return(generationPath, nil), diff --git a/cli/generate.go b/cli/generate.go index 4fe421ed..4cc90f82 100644 --- a/cli/generate.go +++ b/cli/generate.go @@ -25,13 +25,12 @@ import ( "time" "github.com/NethermindEth/sedge/internal/crypto" - "github.com/NethermindEth/sedge/internal/lido/contracts" - "github.com/NethermindEth/sedge/internal/lido/contracts/mevboostrelaylist" "github.com/NethermindEth/sedge/cli/actions" "github.com/NethermindEth/sedge/configs" "github.com/NethermindEth/sedge/internal/pkg/clients" "github.com/NethermindEth/sedge/internal/pkg/generate" + sedgeOpts "github.com/NethermindEth/sedge/internal/pkg/options" "github.com/NethermindEth/sedge/internal/ui" "github.com/NethermindEth/sedge/internal/utils" log "github.com/sirupsen/logrus" @@ -264,10 +263,9 @@ func runGenCmd(out io.Writer, flags *GenCmdFlags, sedgeAction actions.SedgeActio // Overwrite feeRecipient and relayURLs for Lido Node if lidoNode { - feeRecipient := contracts.FeeRecipient[network] - flags.feeRecipient = feeRecipient.FeeRecipientAddress - - flags.relayURLs, _ = mevboostrelaylist.GetRelaysURI(network) + opts := sedgeOpts.CreateSedgeOptions(sedgeOpts.LidoNode) + flags.feeRecipient = opts.FeeRecipient(network) + flags.relayURLs, _ = opts.RelayURLs(network) } // Warning if no fee recipient is set @@ -494,9 +492,9 @@ func loadJWTSecret(from string) (absFrom string, err error) { func nodeType() string { var nodeType string if lidoNode { - nodeType = LidoNode + nodeType = sedgeOpts.LidoNode } else { - nodeType = EthereumNode + nodeType = sedgeOpts.EthereumNode } return nodeType } diff --git a/cli/generate_test.go b/cli/generate_test.go index d3eddfe1..00178796 100644 --- a/cli/generate_test.go +++ b/cli/generate_test.go @@ -1317,7 +1317,7 @@ func TestGenerateCmd(t *testing.T) { nil, }, { - "Lido Full-node - Sepolia without MEV", + "Lido Full-node - Holesky without MEV", subCmd{ name: "full-node", }, @@ -1325,24 +1325,22 @@ func TestGenerateCmd(t *testing.T) { noMev: true, }, globalFlags{ - network: NetworkSepolia, + network: NetworkHolesky, lidoNode: true, }, nil, }, { - "Lido Full-node - unsupported Sepolia with MEV", + "Lido Full-node - Sepolia", subCmd{ name: "full-node", }, - GenCmdFlags{ - noMev: false, - }, + GenCmdFlags{}, globalFlags{ network: NetworkSepolia, lidoNode: true, }, - fmt.Errorf(configs.InvalidNetworkForLidoMevBoost, mevboostrelaylist.GetLidoSupportedNetworksMevBoost()), + fmt.Errorf(configs.InvalidNetworkForLidoMevBoost, mevboostrelaylist.LidoSupportedNetworksMevBoost()), }, { "Lido Full-node - Holesky no validator", @@ -1370,7 +1368,7 @@ func TestGenerateCmd(t *testing.T) { network: NetworkGnosis, lidoNode: true, }, - fmt.Errorf(configs.InvalidNetworkForLido, contracts.GetLidoSupportedNetworks()), + fmt.Errorf(configs.InvalidNetworkForLido, contracts.LidoSupportedNetworks()), }, } diff --git a/cli/keys.go b/cli/keys.go index a5f5f931..cd11717c 100644 --- a/cli/keys.go +++ b/cli/keys.go @@ -106,9 +106,9 @@ func KeysCmd(cmdRunner commands.CommandRunner, p ui.Prompter) *cobra.Command { } // validate network for Lido if flags.lidoNode { - options, supported := contracts.NetworkSupportedByLidoKeys(flags.network) + supported := contracts.NetworkSupportedByLidoWithdrawal(flags.network) if !supported { - log.Fatalf(configs.InvalidNetworkForLidoKeys, options) + log.Fatalf(configs.InvalidNetworkForLidoKeys, contracts.LidoWithdrawalSupportedNetworks()) } } // Warn about withdrawal address @@ -178,7 +178,8 @@ func KeysCmd(cmdRunner commands.CommandRunner, p ui.Prompter) *cobra.Command { var withdrawalAddress string if flags.lidoNode { - withdrawalAddress = contracts.WithdrawalAddress[flags.network].WithdrawalAddress[2:] + withdrawalAddress, _ = contracts.WithdrawalAddress(flags.network) + withdrawalAddress = withdrawalAddress[2:] } else if flags.ethWithdrawalAddress != "" { withdrawalAddress = flags.ethWithdrawalAddress[2:] } diff --git a/cli/nodeSetup.go b/cli/nodeSetup.go deleted file mode 100644 index e1d2140d..00000000 --- a/cli/nodeSetup.go +++ /dev/null @@ -1,111 +0,0 @@ -package cli - -import ( - "fmt" - - "github.com/NethermindEth/sedge/configs" - "github.com/NethermindEth/sedge/internal/lido/contracts" - "github.com/NethermindEth/sedge/internal/lido/contracts/mevboostrelaylist" -) - -// NodeOptionsFactory is the interface for creating options -type NodeOptionsFactory interface { - CreateNetworkOptions() []string - GetWithdrawalAddress(network string) (string, bool) - GetFeeRecipient(network string) (string, bool) - GetRelayURLs(network string) ([]string, error) - EnableMEVBoost(network string) (bool, bool) - ValidateNode(network string, flags *GenCmdFlags) error -} - -// GetNodeOptions returns the appropriate NodeOptionsFactory based on node setup -func GetNodeOptions(nodeSetup string) NodeOptionsFactory { - switch nodeSetup { - case LidoNode: - return &LidoNodeOptions{} - default: - return &EthereumNodeOptions{} - } -} - -// LidoNodeOptions for Lido Node -type LidoNodeOptions struct{} - -func (l *LidoNodeOptions) CreateNetworkOptions() []string { - options := contracts.GetLidoSupportedNetworks() - return options -} - -func (l *LidoNodeOptions) GetWithdrawalAddress(network string) (string, bool) { - _, supported := contracts.NetworkSupportedByLidoKeys(network) - if supported { - return contracts.WithdrawalAddress[network].WithdrawalAddress, true - } - return "", true -} - -func (l *LidoNodeOptions) GetFeeRecipient(network string) (string, bool) { - _, supported := contracts.NetworkSupportedByLido(network) - if supported { - return contracts.FeeRecipient[network].FeeRecipientAddress, true - } - return "", true -} - -func (l *LidoNodeOptions) GetRelayURLs(network string) ([]string, error) { - relayURLs, err := mevboostrelaylist.GetRelaysURI(network) - if err != nil { - return nil, err - } - return relayURLs, nil -} - -func (l *LidoNodeOptions) EnableMEVBoost(network string) (bool, bool) { - _, supported := mevboostrelaylist.NetworkSupportedByLidoMevBoost(network) - if supported { - return true, true - } - return false, true -} - -func (l *LidoNodeOptions) ValidateNode(network string, flags *GenCmdFlags) error { - if !flags.noMev { - options, supported := mevboostrelaylist.NetworkSupportedByLidoMevBoost(network) - if !supported { - return fmt.Errorf(configs.InvalidNetworkForLidoMevBoost, options) - } - } - options, supported := contracts.NetworkSupportedByLido(network) - if !supported { - return fmt.Errorf(configs.InvalidNetworkForLido, options) - } - - return nil -} - -// EthereumNodeOptions for Ethereum Node -type EthereumNodeOptions struct{} - -func (e *EthereumNodeOptions) CreateNetworkOptions() []string { - return []string{NetworkMainnet, NetworkSepolia, NetworkGnosis, NetworkChiado, NetworkHolesky} -} - -func (e *EthereumNodeOptions) GetWithdrawalAddress(network string) (string, bool) { - return "", false -} - -func (e *EthereumNodeOptions) GetFeeRecipient(network string) (string, bool) { - return "", false // Default implementation returns false for EthereumNode -} - -func (e *EthereumNodeOptions) GetRelayURLs(network string) ([]string, error) { - return nil, nil -} - -func (e *EthereumNodeOptions) EnableMEVBoost(network string) (bool, bool) { - return false, false -} - -func (e *EthereumNodeOptions) ValidateNode(network string, flags *GenCmdFlags) error { - return nil -} diff --git a/cli/sub_gen.go b/cli/sub_gen.go index bc84a7c7..f9aadd68 100644 --- a/cli/sub_gen.go +++ b/cli/sub_gen.go @@ -19,6 +19,7 @@ import ( "errors" "github.com/NethermindEth/sedge/cli/actions" + sedgeOpts "github.com/NethermindEth/sedge/internal/pkg/options" "github.com/spf13/cobra" ) @@ -55,11 +56,15 @@ Additionally, you can use this syntax ':' to override the if err := validateCustomNetwork(&flags.CustomFlags, network); err != nil { return err } - factory := GetNodeOptions(nodeType()) - if err := factory.ValidateNode(network, &flags); err != nil { + if err := preValidationGenerateCmd(network, logging, &flags); err != nil { return err } - return preValidationGenerateCmd(network, logging, &flags) + opts := sedgeOpts.CreateSedgeOptions(nodeType()) + settings := sedgeOpts.OptionSettings{ + Network: network, + MEVBoostEnabled: !flags.noMev && !flags.noValidator, + } + return opts.ValidateSettings(settings) }, RunE: func(cmd *cobra.Command, args []string) error { services := []string{execution, consensus} @@ -131,11 +136,15 @@ func ExecutionSubCmd(sedgeAction actions.SedgeActions) *cobra.Command { if err := validateCustomNetwork(&flags.CustomFlags, network); err != nil { return err } - factory := GetNodeOptions(nodeType()) - if err := factory.ValidateNode(network, &flags); err != nil { + if err := preValidationGenerateCmd(network, logging, &flags); err != nil { return err } - return preValidationGenerateCmd(network, logging, &flags) + opts := sedgeOpts.CreateSedgeOptions(nodeType()) + settings := sedgeOpts.OptionSettings{ + Network: network, + MEVBoostEnabled: false, // MEV Boost is not supported for execution nodes only + } + return opts.ValidateSettings(settings) }, RunE: func(cmd *cobra.Command, args []string) error { return runGenCmd(cmd.OutOrStdout(), &flags, sedgeAction, []string{execution}) @@ -180,11 +189,15 @@ func ConsensusSubCmd(sedgeAction actions.SedgeActions) *cobra.Command { if err := validateCustomNetwork(&flags.CustomFlags, network); err != nil { return err } - factory := GetNodeOptions(nodeType()) - if err := factory.ValidateNode(network, &flags); err != nil { + if err := preValidationGenerateCmd(network, logging, &flags); err != nil { return err } - return preValidationGenerateCmd(network, logging, &flags) + opts := sedgeOpts.CreateSedgeOptions(nodeType()) + settings := sedgeOpts.OptionSettings{ + Network: network, + MEVBoostEnabled: false, // MEV Boost is not supported for consensus nodes only + } + return opts.ValidateSettings(settings) }, RunE: func(cmd *cobra.Command, args []string) error { return runGenCmd(cmd.OutOrStdout(), &flags, sedgeAction, []string{consensus}) @@ -244,11 +257,15 @@ func ValidatorSubCmd(sedgeAction actions.SedgeActions) *cobra.Command { if err := validateCustomNetwork(&flags.CustomFlags, network); err != nil { return err } - factory := GetNodeOptions(nodeType()) - if err := factory.ValidateNode(network, &flags); err != nil { + if err := preValidationGenerateCmd(network, logging, &flags); err != nil { return err } - return preValidationGenerateCmd(network, logging, &flags) + opts := sedgeOpts.CreateSedgeOptions(nodeType()) + settings := sedgeOpts.OptionSettings{ + Network: network, + MEVBoostEnabled: !flags.noMev && !flags.noValidator, + } + return opts.ValidateSettings(settings) }, RunE: func(cmd *cobra.Command, args []string) error { flags.noMev = true diff --git a/configs/init.go b/configs/init.go index e1006089..77e953d1 100644 --- a/configs/init.go +++ b/configs/init.go @@ -32,6 +32,7 @@ var networksConfigs map[string]NetworkConfig = map[string]NetworkConfig{ "https://0xac6e77dfe25ecd6110b8e780608cce0dab71fdd5ebea22a16c0205200f2f8e2e3ad3b71d3499c54ad14d6c21b41a37ae@boost-relay.flashbots.net", "https://0xa1559ace749633b997cb3fdacffb890aeebdb0f5a3b6aaa7eeeaf1a38af0a8fe88b9e4b1f61f236d2e64d95733327a62@relay.ultrasound.money", }, + Weight: 1, }, NetworkSepolia: { Name: NetworkSepolia, @@ -42,18 +43,21 @@ var networksConfigs map[string]NetworkConfig = map[string]NetworkConfig{ RelayURLs: []string{ "https://0x845bd072b7cd566f02faeb0a4033ce9399e42839ced64e8b2adcfc859ed1e8e1a5a293336a49feac6d9a5edb779be53a@builder-relay-sepolia.flashbots.net", }, + Weight: 3, }, NetworkChiado: { Name: NetworkChiado, NetworkService: "merge", GenesisForkVersion: "0x0000006f", CheckpointSyncURL: "https://checkpoint.chiadochain.net", + Weight: 5, }, NetworkGnosis: { Name: NetworkGnosis, NetworkService: "merge", GenesisForkVersion: "0x00000064", CheckpointSyncURL: "https://checkpoint.gnosischain.com", + Weight: 4, }, NetworkHolesky: { Name: NetworkHolesky, @@ -67,11 +71,13 @@ var networksConfigs map[string]NetworkConfig = map[string]NetworkConfig{ "https://0xab78bf8c781c58078c3beb5710c57940874dd96aef2835e7742c866b4c7c0406754376c2c8285a36c630346aa5c5f833@holesky.aestus.live", "https://0xaa58208899c6105603b74396734a6263cc7d947f444f396a90f7b7d3e65d102aec7e5e5291b27e08d02c50a050825c2f@holesky.titanrelay.xyz", }, + Weight: 2, }, NetworkCustom: { Name: NetworkCustom, NetworkService: "merge", GenesisForkVersion: "0x00000000", // TODO: only affects keystores generation, ensure the deposit method does not conflict over this. + Weight: 6, }, } diff --git a/configs/types.go b/configs/types.go index cb3af28b..599b3288 100644 --- a/configs/types.go +++ b/configs/types.go @@ -34,4 +34,5 @@ type NetworkConfig struct { SupportsMEVBoost bool CheckpointSyncURL string RelayURLs []string + Weight int // Weight of the network for sorting purposes } diff --git a/e2e/generate_test.go b/e2e/generate_test.go index 2c71000d..9fefd490 100644 --- a/e2e/generate_test.go +++ b/e2e/generate_test.go @@ -108,7 +108,8 @@ func TestGenerate_FullNode_Lido_Sepolia_NoMEV(t *testing.T) { // Read FEE_RECIPIENT value feeRecipient, exists := envMap["FEE_RECIPIENT"] assert.True(t, exists, "FEE_RECIPIENT should exist in .env file") - expectedFeeRecipient := contracts.FeeRecipient["sepolia"].FeeRecipientAddress + expectedFeeRecipient, ok := contracts.FeeRecipient("sepolia") + assert.True(t, ok, "FeeRecipient should be found") assert.Equal(t, expectedFeeRecipient, feeRecipient, "FEE_RECIPIENT value should match expected value") // Read RELAY_URLS value @@ -147,14 +148,15 @@ func TestGenerate_FullNode_Lido_Mainnet(t *testing.T) { // Read FEE_RECIPIENT value feeRecipient, exists := envMap["FEE_RECIPIENT"] assert.True(t, exists, "FEE_RECIPIENT should exist in .env file") - expectedFeeRecipient := contracts.FeeRecipient["mainnet"].FeeRecipientAddress + expectedFeeRecipient, ok := contracts.FeeRecipient("mainnet") + assert.True(t, ok, "FeeRecipient should be found") assert.Equal(t, expectedFeeRecipient, feeRecipient, "FEE_RECIPIENT value should match expected value") // Read RELAY_URLS value relayURLs, exists := envMap["RELAY_URLS"] assert.True(t, exists, "RELAY_URLS should exist in .env file") relayURLsList := strings.Split(relayURLs, ",") - expectedRelayURLs, _ := mevboostrelaylist.GetRelaysURI("mainnet") + expectedRelayURLs, _ := mevboostrelaylist.RelaysURI("mainnet") assert.Equal(t, expectedRelayURLs, relayURLsList, "RELAY_URLS value should match expected value") }, ) diff --git a/e2e/keys_test.go b/e2e/keys_test.go index e04e5ce8..88c45740 100644 --- a/e2e/keys_test.go +++ b/e2e/keys_test.go @@ -131,12 +131,14 @@ func TestKeys_Lido_Mainnet(t *testing.T) { err = json.Unmarshal([]byte(jsonData), &keys) assert.NoError(t, err, "error unmarshalling json") + wa, ok := contracts.WithdrawalAddress("mainnet") + assert.True(t, ok, "WithdrawalAddress should be found") pattern := `^010000000000000000000000[a-fA-F0-9]{40}$` regex := regexp.MustCompile(pattern) for _, key := range keys { assert.Regexp(t, regex, key.WithdrawalCredentials, "withdrawal_credentials should match the pattern") assert.Equal(t, key.NetworkName, "mainnet", "network_name should be mainnet") - expectedWithdrawalAddress := "010000000000000000000000" + (contracts.WithdrawalAddress["mainnet"].WithdrawalAddress[2:]) + expectedWithdrawalAddress := "010000000000000000000000" + (wa[2:]) assert.Equal(t, expectedWithdrawalAddress, key.WithdrawalCredentials, "WithdrawalAddress value should match expected value") } }, @@ -191,12 +193,14 @@ func TestKeys_Lido_Holesky(t *testing.T) { err = json.Unmarshal([]byte(jsonData), &keys) assert.NoError(t, err, "error unmarshalling json") + wa, ok := contracts.WithdrawalAddress("holesky") + assert.True(t, ok, "WithdrawalAddress should be found") pattern := `^010000000000000000000000[a-fA-F0-9]{40}$` regex := regexp.MustCompile(pattern) for _, key := range keys { assert.Regexp(t, regex, key.WithdrawalCredentials, "withdrawal_credentials should match the pattern") assert.Equal(t, key.NetworkName, "holesky", "network_name should be holesky") - expectedWithdrawalCredentials := "010000000000000000000000" + (contracts.WithdrawalAddress["holesky"].WithdrawalAddress[2:]) + expectedWithdrawalCredentials := "010000000000000000000000" + (wa[2:]) expectedWithdrawalCredentials = strings.ToLower(expectedWithdrawalCredentials) assert.Equal(t, expectedWithdrawalCredentials, key.WithdrawalCredentials, "WithdrawalAddress value should match expected value") } diff --git a/internal/lido/contracts/feeRecipient.go b/internal/lido/contracts/feeRecipient.go index e73e4b63..6daa33d9 100644 --- a/internal/lido/contracts/feeRecipient.go +++ b/internal/lido/contracts/feeRecipient.go @@ -1,50 +1,64 @@ +/* +Copyright 2022 Nethermind + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ package contracts import ( - "sort" + "slices" "github.com/NethermindEth/sedge/configs" ) -type FeeRecipientConfig struct { - Network string - FeeRecipientAddress string +type feeRecipientConfig struct { + network string + feeRecipientAddress string + weight int // Weight of the network for sorting purposes } -var FeeRecipient = map[string]FeeRecipientConfig{ +var feeRecipient = map[string]feeRecipientConfig{ configs.NetworkMainnet: { - Network: configs.NetworkMainnet, - FeeRecipientAddress: "0x388C818CA8B9251b393131C08a736A67ccB19297", + network: configs.NetworkMainnet, + feeRecipientAddress: "0x388C818CA8B9251b393131C08a736A67ccB19297", + weight: 1, }, configs.NetworkHolesky: { - Network: configs.NetworkHolesky, - FeeRecipientAddress: "0xE73a3602b99f1f913e72F8bdcBC235e206794Ac8", + network: configs.NetworkHolesky, + feeRecipientAddress: "0xE73a3602b99f1f913e72F8bdcBC235e206794Ac8", + weight: 2, }, configs.NetworkSepolia: { - Network: configs.NetworkSepolia, - FeeRecipientAddress: "0x94B1B8e2680882f8652882e7F196169dE3d9a3B2", + network: configs.NetworkSepolia, + feeRecipientAddress: "0x94B1B8e2680882f8652882e7F196169dE3d9a3B2", + weight: 3, }, } -func GetLidoSupportedNetworks() []string { +// FeeRecipient returns the fee recipient address for the given network +func FeeRecipient(network string) (string, bool) { + config, ok := feeRecipient[network] + return config.feeRecipientAddress, ok +} + +// LidoSupportedNetworks returns the supported networks for Lido +func LidoSupportedNetworks() []string { networks := []string{} - for network := range FeeRecipient { + for network := range feeRecipient { networks = append(networks, network) } - sort.Strings(networks) + slices.SortFunc(networks, func(a, b string) int { + return feeRecipient[a].weight - feeRecipient[b].weight + }) return networks } - -func NetworkSupportedByLido(network string) ([]string, bool) { - supportedNetworks := GetLidoSupportedNetworks() - var supported bool - for _, supportedNetwork := range supportedNetworks { - if network == supportedNetwork { - supported = true - } - } - if !supported { - return supportedNetworks, supported - } - return nil, supported -} diff --git a/internal/lido/contracts/mevboostrelaylist/get_relays.go b/internal/lido/contracts/mevboostrelaylist/get_relays.go index a9854203..7a3c8d1e 100644 --- a/internal/lido/contracts/mevboostrelaylist/get_relays.go +++ b/internal/lido/contracts/mevboostrelaylist/get_relays.go @@ -1,3 +1,18 @@ +/* +Copyright 2022 Nethermind + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ package mevboostrelaylist import ( @@ -20,7 +35,7 @@ type Relay struct { } // Define deployed contract addresses for Mainnet and Holesky -var DeployedContractAddresses = map[string]string{ +var deployedContractAddresses = map[string]string{ configs.NetworkMainnet: "0xF95f069F9AD107938F6ba802a3da87892298610E", configs.NetworkHolesky: "0x2d86C5855581194a386941806E38cA119E50aEA3", } @@ -52,7 +67,7 @@ func connectToRPC(RPCs []string) (*rpc.Client, error) { } /* -GetRelays : +Relays : This function is responsible for :- retrieving a list of relays from the MEV-Boost Allowed List contract for a given network. params :- @@ -63,7 +78,7 @@ List of relays b. error Error if any */ -func GetRelays(network string) ([]Relay, error) { +func Relays(network string) ([]Relay, error) { var relays []Relay rpcs, err := configs.GetPublicRPCs(network) if err != nil { @@ -95,7 +110,7 @@ func GetRelays(network string) ([]Relay, error) { Data string `json:"data"` } args := CallArgs{ - To: DeployedContractAddresses[network], + To: deployedContractAddresses[network], Data: "0x" + hex.EncodeToString(data), } @@ -121,7 +136,7 @@ func GetRelays(network string) ([]Relay, error) { } /* -GetRelaysURI : +RelaysURI : This function is responsible for :- retrieving a list of relays URI from the MEV-Boost Allowed List contract for a given network. params :- @@ -132,8 +147,8 @@ List of relays URI b. error Error if any */ -func GetRelaysURI(network string) ([]string, error) { - relays, err := GetRelays(network) +func RelaysURI(network string) ([]string, error) { + relays, err := Relays(network) if err != nil { return nil, err } @@ -144,9 +159,9 @@ func GetRelaysURI(network string) ([]string, error) { return relayURIs, err } -func GetLidoSupportedNetworksMevBoost() []string { +func LidoSupportedNetworksMevBoost() []string { networks := []string{} - for network := range DeployedContractAddresses { + for network := range deployedContractAddresses { networks = append(networks, network) } sort.Strings(networks) @@ -154,7 +169,7 @@ func GetLidoSupportedNetworksMevBoost() []string { } func NetworkSupportedByLidoMevBoost(network string) ([]string, bool) { - supportedNetworks := GetLidoSupportedNetworksMevBoost() + supportedNetworks := LidoSupportedNetworksMevBoost() var supported bool for _, supportedNetwork := range supportedNetworks { if network == supportedNetwork { diff --git a/internal/lido/contracts/mevboostrelaylist/get_relays_test.go b/internal/lido/contracts/mevboostrelaylist/get_relays_test.go index 4f8f975f..fd596b3a 100644 --- a/internal/lido/contracts/mevboostrelaylist/get_relays_test.go +++ b/internal/lido/contracts/mevboostrelaylist/get_relays_test.go @@ -1,3 +1,18 @@ +/* +Copyright 2022 Nethermind + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ package mevboostrelaylist import ( @@ -45,7 +60,7 @@ func loadRelays(filename string) (map[string][]Relay, error) { }, nil } -func TestGetRelays(t *testing.T) { +func TestRelays(t *testing.T) { // Silence logger log.SetOutput(io.Discard) @@ -70,7 +85,7 @@ func TestGetRelays(t *testing.T) { for _, tc := range tcs { t.Run(tc.name, func(t *testing.T) { - relays, err := GetRelays(tc.network) + relays, err := Relays(tc.network) if err != nil { t.Fatalf("Failed to call GetRelays: %v", err) } diff --git a/internal/lido/contracts/withdrawalAddress.go b/internal/lido/contracts/withdrawalAddress.go index 087410ed..eb1acb7c 100644 --- a/internal/lido/contracts/withdrawalAddress.go +++ b/internal/lido/contracts/withdrawalAddress.go @@ -1,3 +1,18 @@ +/* +Copyright 2022 Nethermind + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ package contracts import ( @@ -6,41 +21,45 @@ import ( "github.com/NethermindEth/sedge/configs" ) -type WithdrawalAddressConfig struct { - Network string - WithdrawalAddress string +type withdrawalAddressConfig struct { + network string + withdrawalAddress string } -var WithdrawalAddress = map[string]WithdrawalAddressConfig{ +var withdrawalAddress = map[string]withdrawalAddressConfig{ configs.NetworkMainnet: { - Network: configs.NetworkMainnet, - WithdrawalAddress: "0xb9d7934878b5fb9610b3fe8a5e441e8fad7e293f", + network: configs.NetworkMainnet, + withdrawalAddress: "0xb9d7934878b5fb9610b3fe8a5e441e8fad7e293f", }, configs.NetworkHolesky: { - Network: configs.NetworkHolesky, - WithdrawalAddress: "0xF0179dEC45a37423EAD4FaD5fCb136197872EAd9", + network: configs.NetworkHolesky, + withdrawalAddress: "0xF0179dEC45a37423EAD4FaD5fCb136197872EAd9", }, } -func GetLidoKeysSupportedNetworks() []string { +// WithdrawalAddress returns the withdrawal address for the given network +func WithdrawalAddress(network string) (string, bool) { + config, ok := withdrawalAddress[network] + return config.withdrawalAddress, ok +} + +// LidoWithdrawalSupportedNetworks returns the supported networks for Lido withdrawal +func LidoWithdrawalSupportedNetworks() []string { networks := []string{} - for network := range WithdrawalAddress { + for network := range withdrawalAddress { networks = append(networks, network) } sort.Strings(networks) return networks } -func NetworkSupportedByLidoKeys(network string) ([]string, bool) { - supportedNetworks := GetLidoKeysSupportedNetworks() - var supported bool +// NetworkSupportedByLidoWithdrawal checks if the given network is supported by Lido +func NetworkSupportedByLidoWithdrawal(network string) bool { + supportedNetworks := LidoWithdrawalSupportedNetworks() for _, supportedNetwork := range supportedNetworks { if network == supportedNetwork { - supported = true + return true } } - if !supported { - return supportedNetworks, supported - } - return nil, supported + return false } diff --git a/internal/pkg/options/consts.go b/internal/pkg/options/consts.go new file mode 100644 index 00000000..71cc78f7 --- /dev/null +++ b/internal/pkg/options/consts.go @@ -0,0 +1,21 @@ +/* +Copyright 2022 Nethermind + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +package options + +const ( + EthereumNode = "ethereum-node" + LidoNode = "lido-node" +) diff --git a/internal/pkg/options/errors.go b/internal/pkg/options/errors.go new file mode 100644 index 00000000..fe427553 --- /dev/null +++ b/internal/pkg/options/errors.go @@ -0,0 +1,28 @@ +/* +Copyright 2022 Nethermind + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +package options + +import ( + "fmt" +) + +var ErrInvalidNetworkMevBoost = func(network []string, option string) error { + return fmt.Errorf("invalid network: Choose valid network for %s with MEV-Boost: %v", option, network) +} + +var ErrInvalidNetwork = func(network []string, option string) error { + return fmt.Errorf("invalid network: Choose valid network for %s: %v", option, network) +} diff --git a/internal/pkg/options/ethereum.go b/internal/pkg/options/ethereum.go new file mode 100644 index 00000000..9b91bc83 --- /dev/null +++ b/internal/pkg/options/ethereum.go @@ -0,0 +1,92 @@ +/* +Copyright 2022 Nethermind + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +package options + +import ( + "golang.org/x/exp/slices" + + "github.com/NethermindEth/sedge/configs" +) + +// ethereumOptions for Ethereum Node +type ethereumOptions struct{} + +var _ SedgeOptions = (*ethereumOptions)(nil) + +func (e *ethereumOptions) SupportedNetworks() []string { + networks := []string{} + for _, network := range configs.NetworksConfigs() { + if network.Name == configs.NetworkCustom { + continue + } + networks = append(networks, network.Name) + } + slices.SortFunc(networks, func(a, b string) int { + return configs.NetworksConfigs()[a].Weight - configs.NetworksConfigs()[b].Weight + }) + return networks +} + +func (e *ethereumOptions) OverwriteSettings() OverwriteSettings { + return OverwriteSettings{ + FeeRecipient: false, + RelayURLs: false, + MevBoost: false, + WithdrawalAddress: false, + } +} + +func (e *ethereumOptions) WithdrawalAddress(network string) string { + return "" +} + +func (e *ethereumOptions) FeeRecipient(network string) string { + return "" +} + +func (e *ethereumOptions) RelayURLs(network string) ([]string, error) { + networkConfig, ok := configs.NetworksConfigs()[network] + if !ok { + return nil, ErrInvalidNetwork(e.SupportedNetworks(), "Ethereum") + } + return networkConfig.RelayURLs, nil +} + +func (e *ethereumOptions) MEVBoostEnabled(network string) bool { + return configs.NetworksConfigs()[network].SupportsMEVBoost +} + +func (e *ethereumOptions) ValidateSettings(settings OptionSettings) error { + // HACK: Ethereum manages MEV Boost at a template level, thus we don't need to check if MEV Boost is enabled and supported by the network + // It might be a good idea to remove this logic from the template and manage MEV Boost at the network level + // Check if MEVBoost is enabled and supported by the network + // if settings.MEVBoostEnabled && !e.MEVBoostEnabled(settings.Network) { + // mevNetworks := make([]string, 0) + // for _, network := range e.SupportedNetworks() { + // if e.MEVBoostEnabled(network) { + // mevNetworks = append(mevNetworks, network) + // } + // } + // return ErrInvalidNetworkMevBoost(mevNetworks, "Ethereum") + // } + + // Check if the network is supported + supportedNetworks := e.SupportedNetworks() + if !slices.Contains(supportedNetworks, settings.Network) { + return ErrInvalidNetwork(supportedNetworks, "Ethereum") + } + return nil +} diff --git a/internal/pkg/options/ethereum_test.go b/internal/pkg/options/ethereum_test.go new file mode 100644 index 00000000..2f85a305 --- /dev/null +++ b/internal/pkg/options/ethereum_test.go @@ -0,0 +1,202 @@ +/* +Copyright 2022 Nethermind + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +package options + +import ( + "errors" + "testing" + + "github.com/NethermindEth/sedge/configs" + "github.com/stretchr/testify/assert" +) + +func TestEthereumOptions_SupportedNetworks(t *testing.T) { + options := CreateSedgeOptions(EthereumNode) + got := options.SupportedNetworks() + want := []string{"mainnet", "holesky", "sepolia", "gnosis", "chiado"} + assert.Equal(t, want, got) +} + +func TestEthereumOptions_OverwriteSettings(t *testing.T) { + options := CreateSedgeOptions(EthereumNode) + got := options.OverwriteSettings() + want := OverwriteSettings{} + assert.Equal(t, want, got) +} + +func TestEthereumOptions_WithdrawalAddress(t *testing.T) { + options := CreateSedgeOptions(EthereumNode) + + tests := []struct { + network string + expected string + }{ + {"mainnet", ""}, + {"unsupported", ""}, + } + + for _, tt := range tests { + t.Run(tt.network, func(t *testing.T) { + address := options.WithdrawalAddress(tt.network) + assert.Equal(t, tt.expected, address) + }) + } +} + +func TestEthereumOptions_FeeRecipient(t *testing.T) { + options := CreateSedgeOptions(EthereumNode) + + tests := []struct { + network string + expected string + }{ + {"mainnet", ""}, + {"unsupported", ""}, + } + + for _, tt := range tests { + t.Run(tt.network, func(t *testing.T) { + recipient := options.FeeRecipient(tt.network) + assert.Equal(t, tt.expected, recipient) + }) + } +} + +func TestEthereumOptions_RelayURLs(t *testing.T) { + options := CreateSedgeOptions(EthereumNode) + expected := func(network string) []string { + return configs.NetworksConfigs()[network].RelayURLs + } + empty := func(network string) []string { + return nil + } + + tests := []struct { + network string + expected func(string) []string + err error + }{ + {"mainnet", expected, nil}, + {"holesky", expected, nil}, + {"sepolia", expected, nil}, + {"gnosis", expected, nil}, + {"chiado", expected, nil}, + {"unsupported", empty, ErrInvalidNetwork(options.SupportedNetworks(), "Ethereum")}, + } + + for _, tt := range tests { + t.Run(tt.network, func(t *testing.T) { + urls, err := options.RelayURLs(tt.network) + assert.Equal(t, tt.expected(tt.network), urls) + if tt.err != nil { + assert.ErrorContains(t, err, "invalid network: Choose valid network for Ethereum") + } else { + assert.NoError(t, err) + } + }) + } +} + +func TestEthereumOptions_MEVBoostEnabled(t *testing.T) { + options := CreateSedgeOptions(EthereumNode) + enabled := func(network string) bool { + return configs.NetworksConfigs()[network].SupportsMEVBoost + } + empty := func(network string) bool { + return false + } + + tests := []struct { + network string + expected func(string) bool + }{ + {"mainnet", enabled}, + {"holesky", enabled}, + {"sepolia", enabled}, + {"gnosis", enabled}, + {"chiado", enabled}, + {"unsupported", empty}, + } + + for _, tt := range tests { + t.Run(tt.network, func(t *testing.T) { + enabled := options.MEVBoostEnabled(tt.network) + assert.Equal(t, tt.expected(tt.network), enabled) + }) + } +} + +func TestEthereumOptions_ValidateSettings(t *testing.T) { + options := CreateSedgeOptions(EthereumNode) + + tests := []struct { + name string + settings OptionSettings + expected error + }{ + { + name: "valid settings, mainnet", + settings: OptionSettings{ + Network: "mainnet", + MEVBoostEnabled: true, + }, + expected: nil, + }, + { + name: "valid settings, holesky", + settings: OptionSettings{ + Network: "holesky", + MEVBoostEnabled: true, + }, + expected: nil, + }, + { + name: "valid settings, sepolia", + settings: OptionSettings{ + Network: "sepolia", + MEVBoostEnabled: true, + }, + expected: nil, + }, + { + name: "valid settings, gnosis", + settings: OptionSettings{ + Network: "gnosis", + MEVBoostEnabled: false, + }, + expected: nil, + }, + { + name: "invalid settings, unsupported network", + settings: OptionSettings{ + Network: "unsupported", + MEVBoostEnabled: false, + }, + expected: errors.New("invalid network: Choose valid network for Ethereum"), + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := options.ValidateSettings(tt.settings) + if tt.expected != nil { + assert.ErrorContains(t, err, tt.expected.Error()) + } else { + assert.NoError(t, err) + } + }) + } +} diff --git a/internal/pkg/options/interface.go b/internal/pkg/options/interface.go new file mode 100644 index 00000000..dce64c04 --- /dev/null +++ b/internal/pkg/options/interface.go @@ -0,0 +1,43 @@ +/* +Copyright 2022 Nethermind + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +package options + +// SedgeOptions is an interface for creating Sedge setup options +type SedgeOptions interface { + Layer1Options + SupportedNetworks() []string + OverwriteSettings() OverwriteSettings + // ValidateSettings can be used to validate the settings for the Sedge Option + ValidateSettings(settings OptionSettings) error +} + +// Layer1Options is an interface for creating Layer 1 setup options +type Layer1Options interface { + WithdrawalAddress(network string) string + FeeRecipient(network string) string + RelayURLs(network string) ([]string, error) + MEVBoostEnabled(network string) bool +} + +// CreateSedgeOptions returns the appropriate SedgeOptions based on the Sedge Node Setup kind. +func CreateSedgeOptions(sedgeSetup string) SedgeOptions { + switch sedgeSetup { + case LidoNode: + return &lidoOptions{} + default: + return ðereumOptions{} + } +} diff --git a/internal/pkg/options/lido.go b/internal/pkg/options/lido.go new file mode 100644 index 00000000..1780d13b --- /dev/null +++ b/internal/pkg/options/lido.go @@ -0,0 +1,92 @@ +/* +Copyright 2022 Nethermind + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +package options + +import ( + lido "github.com/NethermindEth/sedge/internal/lido/contracts" + "github.com/NethermindEth/sedge/internal/lido/contracts/mevboostrelaylist" + "golang.org/x/exp/slices" +) + +// lidoOptions for Lido Node +type lidoOptions struct{} + +var _ SedgeOptions = (*lidoOptions)(nil) + +func (l *lidoOptions) SupportedNetworks() []string { + options := lido.LidoSupportedNetworks() + return options +} + +func (l *lidoOptions) OverwriteSettings() OverwriteSettings { + return OverwriteSettings{ + FeeRecipient: true, + RelayURLs: true, + MevBoost: true, + WithdrawalAddress: true, + } +} + +func (l *lidoOptions) WithdrawalAddress(network string) string { + supported := lido.NetworkSupportedByLidoWithdrawal(network) + if supported { + wa, _ := lido.WithdrawalAddress(network) + return wa + } + return "" +} + +func (l *lidoOptions) FeeRecipient(network string) string { + supported := lido.LidoSupportedNetworks() + if slices.Contains(supported, network) { + fr, _ := lido.FeeRecipient(network) + return fr + } + return "" +} + +func (l *lidoOptions) RelayURLs(network string) ([]string, error) { + relayURLs, err := mevboostrelaylist.RelaysURI(network) + if err != nil { + return nil, err + } + return relayURLs, nil +} + +func (l *lidoOptions) MEVBoostEnabled(network string) bool { + _, supported := mevboostrelaylist.NetworkSupportedByLidoMevBoost(network) + if supported { + return true + } + return false +} + +func (l *lidoOptions) ValidateSettings(settings OptionSettings) error { + // Check if MEVBoost is enabled and supported by the network + if settings.MEVBoostEnabled { + options, supported := mevboostrelaylist.NetworkSupportedByLidoMevBoost(settings.Network) + if !supported { + return ErrInvalidNetworkMevBoost(options, "Lido") + } + } + + // Check if the network is supported by Lido + supportedNetworks := lido.LidoSupportedNetworks() + if !slices.Contains(supportedNetworks, settings.Network) { + return ErrInvalidNetwork(supportedNetworks, "Lido") + } + return nil +} diff --git a/internal/pkg/options/lido_test.go b/internal/pkg/options/lido_test.go new file mode 100644 index 00000000..f9e1e752 --- /dev/null +++ b/internal/pkg/options/lido_test.go @@ -0,0 +1,234 @@ +/* +Copyright 2022 Nethermind + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +package options + +import ( + "testing" + + lido "github.com/NethermindEth/sedge/internal/lido/contracts" + "github.com/NethermindEth/sedge/internal/lido/contracts/mevboostrelaylist" + "github.com/stretchr/testify/assert" +) + +func TestLidoOptions_SupportedNetworks(t *testing.T) { + options := CreateSedgeOptions(LidoNode) + expected := []string{"mainnet", "holesky", "sepolia"} + assert.Equal(t, expected, options.SupportedNetworks()) +} + +func TestLidoOptions_OverwriteSettings(t *testing.T) { + options := CreateSedgeOptions(LidoNode) + got := options.OverwriteSettings() + want := OverwriteSettings{ + FeeRecipient: true, + RelayURLs: true, + MevBoost: true, + WithdrawalAddress: true, + } + assert.Equal(t, want, got) +} + +func TestLidoOptions_WithdrawalAddress(t *testing.T) { + options := CreateSedgeOptions(LidoNode) + expected := func(network string) string { + address, _ := lido.WithdrawalAddress(network) + return address + } + + tests := []struct { + network string + expected func(network string) string + }{ + { + "mainnet", + expected, + }, + { + "sepolia", + expected, + }, + { + "holesky", + expected, + }, + { + "unsupported", + func(network string) string { + return "" + }, + }, + } + + for _, tt := range tests { + t.Run(tt.network, func(t *testing.T) { + address := options.WithdrawalAddress(tt.network) + assert.Equal(t, tt.expected(tt.network), address) + }) + } +} + +func TestLidoOptions_FeeRecipient(t *testing.T) { + options := CreateSedgeOptions(LidoNode) + expected := func(network string) string { + address, _ := lido.FeeRecipient(network) + return address + } + + tests := []struct { + network string + expected func(network string) string + }{ + { + "mainnet", + expected, + }, + { + "sepolia", + expected, + }, + { + "holesky", + expected, + }, + { + "unsupported", + func(network string) string { + return "" + }, + }, + } + + for _, tt := range tests { + t.Run(tt.network, func(t *testing.T) { + recipient := options.FeeRecipient(tt.network) + assert.Equal(t, tt.expected(tt.network), recipient) + }) + } +} + +func TestLidoOptions_RelayURLs(t *testing.T) { + options := CreateSedgeOptions(LidoNode) + expected := func(network string) []string { + got, _ := mevboostrelaylist.RelaysURI(network) + return got + } + + tests := []struct { + network string + expected func(network string) []string + }{ + {"mainnet", expected}, + {"sepolia", expected}, + {"holesky", expected}, + {"unsupported", func(network string) []string { + return nil + }}, + } + + for _, tt := range tests { + t.Run(tt.network, func(t *testing.T) { + urls, err := options.RelayURLs(tt.network) + assert.Equal(t, tt.expected(tt.network), urls) + if tt.expected(tt.network) != nil { + assert.NoError(t, err) + } else { + assert.Error(t, err) + } + }) + } +} + +func TestLidoOptions_MEVBoostEnabled(t *testing.T) { + options := CreateSedgeOptions(LidoNode) + + tests := []struct { + network string + enabled bool + }{ + {"mainnet", true}, + {"sepolia", false}, + {"holesky", true}, + {"unsupported", false}, + } + + for _, tt := range tests { + t.Run(tt.network, func(t *testing.T) { + enabled := options.MEVBoostEnabled(tt.network) + assert.Equal(t, tt.enabled, enabled) + }) + } +} + +func TestLidoOptionsValidateSettings(t *testing.T) { + options := CreateSedgeOptions(LidoNode) + + tests := []struct { + name string + settings OptionSettings + expected error + }{ + { + name: "valid settings, mainnet", + settings: OptionSettings{ + Network: "mainnet", + MEVBoostEnabled: true, + }, + expected: nil, + }, + { + name: "valid settings, sepolia, no mevboost", + settings: OptionSettings{ + Network: "sepolia", + MEVBoostEnabled: false, + }, + expected: nil, + }, + { + name: "invalid settings, sepolia, mevboost", + settings: OptionSettings{ + Network: "sepolia", + MEVBoostEnabled: true, + }, + expected: ErrInvalidNetworkMevBoost(mevboostrelaylist.LidoSupportedNetworksMevBoost(), "Lido"), + }, + { + name: "invalid settings, unsupported network", + settings: OptionSettings{ + Network: "unsupported", + MEVBoostEnabled: false, + }, + expected: ErrInvalidNetwork(lido.LidoSupportedNetworks(), "Lido"), + }, + { + name: "valid settings, holesky", + settings: OptionSettings{ + Network: "holesky", + MEVBoostEnabled: true, + }, + expected: nil, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := options.ValidateSettings(tt.settings) + if tt.expected != nil { + assert.EqualError(t, err, tt.expected.Error()) + } else { + assert.NoError(t, err) + } + }) + } +} diff --git a/internal/pkg/options/types.go b/internal/pkg/options/types.go new file mode 100644 index 00000000..d884a4a0 --- /dev/null +++ b/internal/pkg/options/types.go @@ -0,0 +1,30 @@ +/* +Copyright 2022 Nethermind + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +package options + +// OptionSettings is a struct for setting up options for Sedge Node +type OptionSettings struct { + Network string + MEVBoostEnabled bool +} + +// OverwriteSettings is a struct for defining what settings should be overwritten +type OverwriteSettings struct { + FeeRecipient bool + RelayURLs bool + MevBoost bool + WithdrawalAddress bool +} From aa1af714cd6bfa946ae9e1e0dd208fa6ee705fa1 Mon Sep 17 00:00:00 2001 From: AntiD2ta Date: Tue, 9 Jul 2024 00:50:35 +0200 Subject: [PATCH 27/31] feat(keys): Log keystore path after generation --- cli/keys.go | 1 + configs/messages.go | 1 + 2 files changed, 2 insertions(+) diff --git a/cli/keys.go b/cli/keys.go index cd11717c..10dd40fb 100644 --- a/cli/keys.go +++ b/cli/keys.go @@ -210,6 +210,7 @@ func KeysCmd(cmdRunner commands.CommandRunner, p ui.Prompter) *cobra.Command { log.Fatal(err) } log.Info(configs.DepositDataGenerated) + log.Infof(configs.KeystorePath, keystorePath) }, } // Flag binds diff --git a/configs/messages.go b/configs/messages.go index f57f2177..d4b9be4f 100644 --- a/configs/messages.go +++ b/configs/messages.go @@ -57,6 +57,7 @@ const ( KeystoresGenerated = "Keystores generated successfully" GeneratingDepositData = "Generating deposit data..." DepositDataGenerated = "Deposit data generated successfully" + KeystorePath = "Keystore path: %s" KeysFoundAt = "If everything went well, your keys can be found at: %s" ImageNotFoundBuilding = "Image %s not found, building it" ImageNotFoundPulling = "Image %s not found, pulling it" From bb06092162af3117458bbd5866bdca11e384d86f Mon Sep 17 00:00:00 2001 From: AntiD2ta Date: Tue, 9 Jul 2024 00:51:57 +0200 Subject: [PATCH 28/31] doc: Add --network holesky to sedge keys --lido example --- docs/docs/quickstart/lido.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/docs/quickstart/lido.mdx b/docs/docs/quickstart/lido.mdx index 5afb340d..d36dcdb0 100644 --- a/docs/docs/quickstart/lido.mdx +++ b/docs/docs/quickstart/lido.mdx @@ -56,7 +56,7 @@ To get started with CSM using Sedge, you first need to generate your validator k with the `sedge keys` [command](../commands/keys.mdx). Use the `--lido` flag to generate keys compatible with Lido CSM: ```bash -sedge keys --lido +sedge keys --lido --network holesky ``` ### Setting up your full node with Lido CSM settings enabled From 89a6ca70fb1c67880bf22dc42db418057cf04783 Mon Sep 17 00:00:00 2001 From: AntiD2ta Date: Tue, 9 Jul 2024 00:55:07 +0200 Subject: [PATCH 29/31] chore: Update go.mod --- go.mod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go.mod b/go.mod index 44e9acb5..9144927d 100644 --- a/go.mod +++ b/go.mod @@ -29,6 +29,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/exp v0.0.0-20231110203233-9a3e6036ecaa golang.org/x/sync v0.7.0 gopkg.in/yaml.v2 v2.4.0 gopkg.in/yaml.v3 v3.0.1 @@ -91,7 +92,6 @@ require ( github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect github.com/xeipuuv/gojsonschema v1.2.0 // indirect golang.org/x/crypto v0.22.0 // indirect - golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa // indirect golang.org/x/net v0.24.0 // indirect golang.org/x/sys v0.20.0 // indirect golang.org/x/term v0.19.0 // indirect From 9749a996fe641f07c81ac106cd853484c39c61ef Mon Sep 17 00:00:00 2001 From: Marcos Maceo Date: Tue, 9 Jul 2024 12:29:12 +0400 Subject: [PATCH 30/31] fix documentation for update on commands --- cli/keys.go | 2 +- docs/docs/commands/cli.mdx | 1 + docs/docs/commands/generate.mdx | 36 ++++++++++++++++++--------- docs/docs/commands/importKey.mdx | 7 +++--- docs/docs/commands/keys.mdx | 24 +++++++++--------- docs/docs/commands/logs.mdx | 5 ++-- docs/docs/commands/networks.mdx | 17 +++++++------ docs/docs/commands/run.mdx | 3 ++- docs/docs/commands/show.mdx | 2 +- docs/docs/commands/slashingExport.mdx | 17 ++++++------- docs/docs/commands/slashingImport.mdx | 18 ++++++-------- docs/docs/commands/version.mdx | 2 +- 12 files changed, 73 insertions(+), 61 deletions(-) diff --git a/cli/keys.go b/cli/keys.go index 10dd40fb..f7bef209 100644 --- a/cli/keys.go +++ b/cli/keys.go @@ -100,7 +100,7 @@ func KeysCmd(cmdRunner commands.CommandRunner, p ui.Prompter) *cobra.Command { return nil }, Run: func(cmd *cobra.Command, args []string) { - // Incompatible --lido and --eth1-withdrawal-address together + // Incompatible --lido and --eth-withdrawal-address together if flags.lidoNode && flags.ethWithdrawalAddress != "" { log.Fatalf(configs.IncompatibleLidoAndEth1Withdrawal) } diff --git a/docs/docs/commands/cli.mdx b/docs/docs/commands/cli.mdx index ed73be9f..e947b3b7 100644 --- a/docs/docs/commands/cli.mdx +++ b/docs/docs/commands/cli.mdx @@ -18,6 +18,7 @@ This command will guide you through the process of setting up one of these node - Execution Node - Consensus Node - Validator Node +- Lido CSM Node Follow the prompts to select the options you want for your node. At the end of the process, you will be asked to run the generated setup or not. If you chose to run the setup, it will be executed for you diff --git a/docs/docs/commands/generate.mdx b/docs/docs/commands/generate.mdx index 21ae03a4..7f07ec99 100644 --- a/docs/docs/commands/generate.mdx +++ b/docs/docs/commands/generate.mdx @@ -22,6 +22,7 @@ You can generate: - Consensus Node - Validator Node - Mev-Boost Instance +- Lido CSM node Usage: sedge generate [command] @@ -87,10 +88,12 @@ Flags: -v, --validator string Validator engine client, e.g. teku, lodestar, prysm, lighthouse, Nimbus. Additionally, you can use this syntax ':' to override the docker image used for the client. If you want to use the default docker image, just use the client name --latest Use the latest version of clients. This sets the "latest" tag on the client's docker images. Latest version might not work. --checkpoint-sync-url string Initial state endpoint (trusted synced consensus endpoint) for the consensus client to sync from a finalized checkpoint. Provide faster sync process for the consensus client and protect it from long-range attacks affored by Weak Subjetivity. Each network has a default checkpoint sync url. - --fee-recipient string Suggested fee recipient. Is a 20-byte Ethereum address which the execution layer might choose to set as the coinbase and the recipient of other fees or rewards. There is no guarantee that an execution node will use the suggested fee recipient to collect fees, it may use any address it chooses. It is assumed that an honest execution node will use the suggested fee recipient, but users should note this trust assumption + --fee-recipient string Suggested fee recipient. Is a 20-byte Ethereum address which the execution layer might choose to set as the coinbase and the recipient of other fees or rewards. There is no guarantee that an execution node will use the suggested fee recipient to collect fees, it may use any address it chooses. It is assumed that an honest execution node will use the suggested fee recipient, but users should note this trust assumption. + Note: When setting up a Lido node, fee recipient address will be automatically set by the system. --no-mev-boost Not use mev-boost if supported -m, --mev-boost-image string Custom docker image to use for Mev Boost. Example: 'sedge generate full-node --mev-boost-image flashbots/mev-boost:latest-portable' - --relay-urls strings List of comma separated relay URLs used to connect to mev relay. Example: 'sedge generate full-node --relay-url=https://0xac6e77dfe25ecd6110b8e780608cce0dab71fdd5ebea22a16c0205200f2f8e2e3ad3b71d3499c54ad14d6c21b41a37ae@boost-relay.flashbots.net,https://0xa1559ace749633b997cb3fdacffb890aeebdb0f5a3b6aaa7eeeaf1a38af0a8fe88b9e4b1f61f236d2e64d95733327a62@relay.ultrasound.money + --relay-urls strings List of comma separated relay URLs used to connect to mev relay. Example: 'sedge generate full-node --relay-urls=https://0xac6e77dfe25ecd6110b8e780608cce0dab71fdd5ebea22a16c0205200f2f8e2e3ad3b71d3499c54ad14d6c21b41a37ae@boost-relay.flashbots.net,https://0xa1559ace749633b997cb3fdacffb890aeebdb0f5a3b6aaa7eeeaf1a38af0a8fe88b9e4b1f61f236d2e64d95733327a62@relay.ultrasound.money' + Note: When setting up a Lido node, the provided relay URLs will be automatically set by the system. --no-validator Exclude the validator from the full node setup. Designed for execution and consensus nodes setup without a validator node. Exclude also the validator from other flags. If set, mev-boost will not be used. --jwt-secret-path string Path to the JWT secret file --graffiti string Graffiti to be used by the validator @@ -110,9 +113,10 @@ Flags: Global Flags: --container-tag string Container tag to use. If defined, sedge will add to each container and the network, a suffix with the tag. e.g. sedge-validator-client -> sedge-validator-client-. + --lido generate Lido CSM node --log-level string Set Log Level, e.g panic, fatal, error, warn, warning, info, debug, trace (default "info") - --logging string Docker logging driver used by all the services. Set 'none' to use the default docker logging driver. Possible values: [none json] (default "json") - -n, --network string Target network. e.g. mainnet, sepolia, holesky, gnosis, chiado, etc. (default "mainnet") + --logging string Docker logging driver used by all the services. Set 'none' to use the default docker logging driver. Possible values: [json none] (default "json") + -n, --network string Target network. e.g. mainnet,sepolia, holesky, gnosis, chiado, etc. (default "mainnet") -p, --path string generation path for sedge data. Default is sedge-data (default "/path/to/sedge/sedge-data") ``` @@ -138,7 +142,8 @@ Flags: --execution-api-url string Execution API endpoint for the consensus client. Example: 'sedge generate consensus -r --execution-api-url=https://api.url.endpoint' --execution-auth-url string Execution AUTH endpoint for the consensus client. Example: 'sedge generate consensus -r --execution-auth-url=https://auth.url.endpoint' --checkpoint-sync-url string Initial state endpoint (trusted synced consensus endpoint) for the consensus client to sync from a finalized checkpoint. Provide faster sync process for the consensus client and protect it from long-range attacks affored by Weak Subjetivity. Each network has a default checkpoint sync url. - --fee-recipient string Suggested fee recipient. Is a 20-byte Ethereum address which the execution layer might choose to set as the coinbase and the recipient of other fees or rewards. There is no guarantee that an execution node will use the suggested fee recipient to collect fees, it may use any address it chooses. It is assumed that an honest execution node will use the suggested fee recipient, but users should note this trust assumption + --fee-recipient string Suggested fee recipient. Is a 20-byte Ethereum address which the execution layer might choose to set as the coinbase and the recipient of other fees or rewards. There is no guarantee that an execution node will use the suggested fee recipient to collect fees, it may use any address it chooses. It is assumed that an honest execution node will use the suggested fee recipient, but users should note this trust assumption. + Note: When setting up a Lido node, fee recipient address will be automatically set by the system. --jwt-secret-path string Path to the JWT secret file --mev-boost-url string If you are running a mev boost node, and you want to connect to it, you need to set mev-boost-url, if not set, node will not load any mev boost related config. --map-all Map all clients ports to host. Use with care. Useful to allow remote access to the clients @@ -152,9 +157,10 @@ Flags: Global Flags: --container-tag string Container tag to use. If defined, sedge will add to each container and the network, a suffix with the tag. e.g. sedge-validator-client -> sedge-validator-client-. + --lido generate Lido CSM node --log-level string Set Log Level, e.g panic, fatal, error, warn, warning, info, debug, trace (default "info") --logging string Docker logging driver used by all the services. Set 'none' to use the default docker logging driver. Possible values: [none json] (default "json") - -n, --network string Target network. e.g. mainnet, sepolia, etc. (default "mainnet") + -n, --network string Target network. e.g. mainnet,sepolia, holesky, gnosis, chiado, etc. (default "mainnet") -p, --path string generation path for sedge data. Default is sedge-data (default "/path/to/sedge/sedge-data") ``` @@ -172,7 +178,7 @@ Usage: sedge generate execution [flags] [args] Flags: - --latest Use the latest version of clients. This sets the "latest" tag on the client's docker images. Latest version might not work. + --latest Use the latest version of clients. This sets the "latest" tag on the client's docker images. Latest version might not work. --jwt-secret-path string Path to the JWT secret file --map-all Map all clients ports to host. Use with care. Useful to allow remote access to the clients --custom-chainSpec string File path or url to use as custom network chainSpec for execution client. @@ -182,9 +188,10 @@ Flags: Global Flags: --container-tag string Container tag to use. If defined, sedge will add to each container and the network, a suffix with the tag. e.g. sedge-validator-client -> sedge-validator-client-. + --lido generate Lido CSM node --log-level string Set Log Level, e.g panic, fatal, error, warn, warning, info, debug, trace (default "info") --logging string Docker logging driver used by all the services. Set 'none' to use the default docker logging driver. Possible values: [none json] (default "json") - -n, --network string Target network. e.g. mainnet, sepolia, holesky, gnosis, chiado, etc. (default "mainnet") + -n, --network string Target network. e.g. mainnet,sepolia, holesky, gnosis, chiado, etc. (default "mainnet") -p, --path string generation path for sedge data. Default is sedge-data (default "/path/to/sedge/sedge-data") ``` @@ -197,6 +204,7 @@ You should use `--fee-recipient` flag to set the fee recipient address. If you d ::: ``` +$ sedge generate validator -h Generate a docker-compose and an environment file with a validator node configuration Valid args: name of execution clients according to network @@ -212,7 +220,8 @@ Usage: Flags: --latest Use the latest version of clients. This sets the "latest" tag on the client's docker images. Latest version might not work. --consensus-url string Consensus endpoint for the validator client to connect to. Example: 'sedge generate validator --consensus-url http://localhost:4000' - --fee-recipient string Suggested fee recipient. Is a 20-byte Ethereum address which the execution layer might choose to set as the coinbase and the recipient of other fees or rewards. There is no guarantee that an execution node will use the suggested fee recipient to collect fees, it may use any address it chooses. It is assumed that an honest execution node will use the suggested fee recipient, but users should note this trust assumption + --fee-recipient string Suggested fee recipient. Is a 20-byte Ethereum address which the execution layer might choose to set as the coinbase and the recipient of other fees or rewards. There is no guarantee that an execution node will use the suggested fee recipient to collect fees, it may use any address it chooses. It is assumed that an honest execution node will use the suggested fee recipient, but users should note this trust assumption. + Note: When setting up a Lido node, fee recipient address will be automatically set by the system. --graffiti string Graffiti to be used by the validator --mev-boost Use mev-boost while turning on validator node --custom-config string File path or url to use as custom network config file for consensus client. @@ -224,9 +233,10 @@ Flags: Global Flags: --container-tag string Container tag to use. If defined, sedge will add to each container and the network, a suffix with the tag. e.g. sedge-validator-client -> sedge-validator-client-. + --lido generate Lido CSM node --log-level string Set Log Level, e.g panic, fatal, error, warn, warning, info, debug, trace (default "info") --logging string Docker logging driver used by all the services. Set 'none' to use the default docker logging driver. Possible values: [none json] (default "json") - -n, --network string Target network. e.g. mainnet, sepolia, holesky, gnosis, chiado, etc. (default "mainnet") + -n, --network string Target network. e.g. mainnet,sepolia, holesky, gnosis, chiado, etc. (default "mainnet") -p, --path string generation path for sedge data. Default is sedge-data (default "/path/to/sedge/sedge-data") ``` @@ -240,14 +250,16 @@ Usage: sedge generate mev-boost [flags] Flags: - --relay-urls strings List of comma separated relay URLs used to connect to mev relay. Example: 'sedge generate mev-boost --relay-url=https://0xac6e77dfe25ecd6110b8e780608cce0dab71fdd5ebea22a16c0205200f2f8e2e3ad3b71d3499c54ad14d6c21b41a37ae@boost-relay.flashbots.net,https://0xa1559ace749633b997cb3fdacffb890aeebdb0f5a3b6aaa7eeeaf1a38af0a8fe88b9e4b1f61f236d2e64d95733327a62@relay.ultrasound.money + --relay-urls strings List of comma separated relay URLs used to connect to mev relay. Example: 'sedge generate full-node --relay-urls=https://0xac6e77dfe25ecd6110b8e780608cce0dab71fdd5ebea22a16c0205200f2f8e2e3ad3b71d3499c54ad14d6c21b41a37ae@boost-relay.flashbots.net,https://0xa1559ace749633b997cb3fdacffb890aeebdb0f5a3b6aaa7eeeaf1a38af0a8fe88b9e4b1f61f236d2e64d95733327a62@relay.ultrasound.money' + Note: When setting up a Lido node, the provided relay URLs will be automatically set by the system. -m, --mev-boost-image string Custom docker image to use for Mev Boost. Example: 'sedge generate mev-boost --mev-boost-image flashbots/mev-boost:latest-portable' + -n, --network string Target network. e.g. mainnet, sepolia etc. (default "mainnet") -h, --help help for mev-boost Global Flags: --container-tag string Container tag to use. If defined, sedge will add to each container and the network, a suffix with the tag. e.g. sedge-validator-client -> sedge-validator-client-. + --lido generate Lido CSM node --log-level string Set Log Level, e.g panic, fatal, error, warn, warning, info, debug, trace (default "info") --logging string Docker logging driver used by all the services. Set 'none' to use the default docker logging driver. Possible values: [none json] (default "json") - -n, --network string Target network. e.g. mainnet, sepolia, etc. (default "mainnet") -p, --path string generation path for sedge data. Default is sedge-data (default "/path/to/sedge/sedge-data") ``` \ No newline at end of file diff --git a/docs/docs/commands/importKey.mdx b/docs/docs/commands/importKey.mdx index 87dc3671..c6fb4adf 100644 --- a/docs/docs/commands/importKey.mdx +++ b/docs/docs/commands/importKey.mdx @@ -14,7 +14,7 @@ initialized sedge setup containing a validator client. To know more about the command options, run `sedge import-key --help`: ``` -$ sedge import-key --help +$ sedge import-key --help Import validator client keys, use the 'from' flag to specify the keys location, and make sure that follows the EIP-2335: BLS12-381 Keystore standard. This command @@ -36,18 +36,19 @@ Usage: sedge import-key [flags] [validator] Flags: + --container-tag string Container tag to use. If defined, sedge will add to each container and the network, a suffix with the tag. e.g. sedge-validator-client -> sedge-validator-client-. --custom-config string file path or url to use as custom network config. --custom-deploy-block string custom network deploy block. --custom-genesis string file path or url to use as custom network genesis. --from string path to the validator keys, must follow the EIP-2335: BLS12-381 Keystore standard (default "[WORK_DIR]/sedge-data/keystore") -h, --help help for import-key - -n, --network string network + -n, --network string network (default "mainnet") -p, --path string path to the generation directory (default "[WORK_DIR]/sedge-data") --start-validator starts the validator client after import, regardless of the state the validator was in before --stop-validator stops the validator client after import, regardless of the state the validator was in before Global Flags: - --logLevel string Set Log Level, e.g panic, fatal, error, warn, warning, info, debug, trace (default "info") + --log-level string Set Log Level, e.g panic, fatal, error, warn, warning, info, debug, trace (default "info") ``` ## Execution examples diff --git a/docs/docs/commands/keys.mdx b/docs/docs/commands/keys.mdx index 82913221..848b1386 100644 --- a/docs/docs/commands/keys.mdx +++ b/docs/docs/commands/keys.mdx @@ -17,26 +17,26 @@ Users acknowledge that generating the keystore for any network is an unaudited f ``` $ sedge keys -h -Initializing configuration Generate keystore folder using the eth2.0-deposit-cli tool Usage: sedge keys [flags] Flags: - --eth1-withdrawal-address string If this field is set and valid, the given Eth1 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. - --existing int Number of validators generated with the provided mnemonic. Will be ignored if "--mnemonic-path" its not set. This number will be used as the initial index for the generated keystores. (default -1) - -h, --help help for keys - -i, --install Install dependencies if not installed without asking - --mnemonic-path string Path to file with a existing mnemonic to use. - -n, --network string Target network. e.g. mainnet, sepolia, holesky, gnosis, chiado etc. (default "mainnet") - --num-validators int Number of validators to generate. This number will be used in addition to the existing flag as the end index for the generated keystores. (default -1) - --passphrase-path string Path to file with a keystores passphrase to use. - -p, --path string Absolute path to keystore folder. e.g. /home/user/keystore (default "./docker-compose-scripts") - --random-passphrase Usa a randomly generated passphrase to encrypt keystores. + --eth-withdrawal-address string 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. + --existing int Number of validators generated with the provided mnemonic. Will be ignored if "--mnemonic-path" its not set. This number will be used as the initial index for the generated keystores. (default -1) + -h, --help help for keys + -i, --install Install dependencies if not installed without asking + --lido Enable Lido CSM compatible keys. Similar to using --eth-withdrawal-address with the Lido Withdrawal Vault address. + --mnemonic-path string Path to file with a existing mnemonic to use. + -n, --network string Target network. e.g. mainnet,sepolia, holesky, gnosis, chiado etc. (default "mainnet") + --num-validators int Number of validators to generate. This number will be used in addition to the existing flag as the end index for the generated keystores. (default -1) + --passphrase-path string Path to file with a keystores passphrase to use. + -p, --path string Absolute path to keystore folder. e.g. /home/user/keystore (default "/path/to/sedge-data") + --random-passphrase Usa a randomly generated passphrase to encrypt keystores. Global Flags: - --logLevel string Set Log Level (default "info") + --log-level string Set Log Level, e.g panic, fatal, error, warn, warning, info, debug, trace (default "info") ``` ## Execution Example diff --git a/docs/docs/commands/logs.mdx b/docs/docs/commands/logs.mdx index 967668d3..66cb2536 100644 --- a/docs/docs/commands/logs.mdx +++ b/docs/docs/commands/logs.mdx @@ -11,7 +11,6 @@ Running `sedge logs` will get running container logs using docker compose CLI. ``` $ sedge logs -h -Initializing configuration Get running container logs using docker-compose CLI. If no services are provided, the logs of all running services will be displayed. By default will run 'docker compose -f