Skip to content

Commit

Permalink
Kiln Support (#46)
Browse files Browse the repository at this point in the history
* fix: Skip tests that only work on linux envs

* feat: Add CheckErr utility function for tests

* chore(gitignore): Ignore .vscode folder

* doc(makefile): Comment codecov-test rule

* feat(cli): Integrate posmoni monitoring tool to track sync progress of nodes

Also start validator service after execution and consensus clients are sync

* fix(cli): Incomplete validation check for --run-clients flag

Added ContainsOnly utility function for string slices

* refac: Move cobra commands files to new cli package

Leave main.go in cmd/

* feat(cli): Make 1click completely 1click

Run validator node after execution and consensus client are synced

* feat(makefile): Add compile-linux rule for linux build

* ci: Update binary

* chore: Update go.mod and go.sum

* style: Add a newline at the end of every env template file

* feat: Implement logic for fallback EL nodes and checkpoint sync urls

* feat: Add fallback EL and checkpoint sync url to consensus templates

* feat(cli): Update client selection flow

Changes:
- Randomized flag removed
- Randomized clients are default values, overriden by user selection via flags
- Select same pair of consensus and validator clients if one of them is missing from user selection

* ci: Update binary

* feat(templates): Change structure to allow templates by network

* feat: Add function to get supported networks

* feat: Update client's RandomChoice to select only supported clients

* feat: Add network flag to cli cmd and update codebase given the new templates structure

* feat: Create kiln folder in envs and services alongside mainnet

* feat: Update clients command

Changes:
- clients cmd now list supported clients for every network
- remove 'Get' prefix from GetSupportedNetworks
- refac clients::clients.go

* fix: Add network to GenerationData, thus correct templates for network can be loaded

* style: Remove 'Get' prefix from GetConfigClients

* feat(cli): Validate network input

* feat: Add 'networks' command to display supported networks

* feat: Update envs and services templates

Updates:
- Use custom network '1click'
- Expose api port for internal containers and not to host network
- Add metrics configuration
- Add condition to depends_on field of validator services
- Add snap sync config to execution clients

* feat(cli): Allow running none docker-compose services

* feat: Add execution clients templates for kiln

* feat(templates): Use TTD in services only if the TTD variable is set in env

* feat: Use filepath.Join for path delimiter in GenerateConfig function

* feat: Add support for suggested fee recipient

* refac(templates): Use service ports as strings

* feat: Add consensus clients templates for kiln

* feat: Add validator clients templates for kiln

* feat: Upgrade version of base compose script to 3.9

* feat: Update local execution and consensus default endpoints

* refac: Move check TTD in env logic to new env pkg

* feat(templates): Add support for config and genesis file for prysm

* style(templates): Update With statements to not left black spaces when they are empty

* fix(templates): Add FeeRecipient to ConsensusEnv data object

* ci: Update binary

* feat: Handle JWT secret for client authentication

* feat(cli): Add prompt for feeRecipient and validate feeRecipient

* fix: Use y flag instead of run for fee recipient prompt

* feat: Change some select for confirm prompts

* feat: Upgrade 'keys' command workflow

Changes:
- Add prompt for keystore password. The password is given to the deposit-cli tool and is used to create the keystore_password.txt file
- Check for keystore generation completion. If it failed then don't create keystore_password.txt and other logs
- Pull and use nethermindeth/staking-deposit-cli image instead of building it

* feat: Update services templates structure to reuse testnet's compose services

* doc: Comment utils::IsAddress function

* doc: Update README

* ci: Update binary

* test: Unit test utils::IsAddress

* ci: Update go version of workflows

* fix: TTD value substitution at Teku consensus service template

* feat: Add install dependencies support for Ubuntu 22.04

* test: Unit test ui::WriteListNetworksTable

* test: Add more test cases for cli command

* chore: Remove pflag pkg from go.mod tidy

* feat: Remove linux binary from project

* feat: Extend docker-compose ps and docker inspect commands

* feat: Prepare datadir for teku before running it

* fix: Change 'mainner_pruned' config on nethermind mainnet to 'mainnet'

* feat: Get containers's internal IP address for Posmoni

* chore(templates): Update some .env variables prefixes

* style: Lower case client's names in flags description

* fix(cli): Posmoni requests timeout to consensus node

Solved by waiting one minute to start track sync after nodes launch. Also raised request timeout time to 30 seconds instead of 1 second

* style: Upper case 'version' command description

* feat: Update .env generation

Added NETWORK and TTD as global settings. .env generation is now unified

* test: Update cli unit tests

Adapted to new sleep after running scripts, and to preprocessing before tracking sync

* feat: Unify execution client env variables and remove ethstats from Nethermind

* feat: Update scripts generation to use global TTD

* Allow custom docker images for clients (#47)

* feat: allow to pass custom docker images for clients

* fix: additional white spaces in templates

* feat(keys): Double check keystore password

* feat(keys): Support eth1 withdrawal address

* chore(makefile): Update run-cli rule

* fix: GenerateValidatorKey unit test. Bad arguments

* feat!: Use docker compose instead of docker-compose

* feat: Add ability to inject additional flags to clients command. ExtraFlag feature

* fix: wrong test case for handle dependencies

* feat: Add metrics to mainnet validator services templates

* feat: map all clients ports to host (#48)

* feat: map all clients ports to host

* fix: rename flag to map all

* fix: missing property

* doc(cli): Fix typo in comment

Co-authored-by: Miguel Tenorio <[email protected]>

* feat: Check for port occupation before generating ports for services

* feat(templates): Add placeholders for ports in services

* chore: Update go.sum

* refac(templates): Move API and Auth ports responsability to .env variables

* fix(templates): Update merge::lighthouse template with new ports change

Co-authored-by: Carlos Bermudez Porto <[email protected]>
Co-authored-by: Carlos Bermudez <[email protected]>
  • Loading branch information
3 people authored Jun 30, 2022
1 parent 7b1f396 commit e9755cb
Show file tree
Hide file tree
Showing 166 changed files with 3,395 additions and 812 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ jobs:
- name: Setup Go
uses: actions/setup-go@v2
with:
go-version: '1.18.0'
go-version: '1.18.2'

- name: Check Go fmt
run: make gofmt
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/unit_tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
go: ["1.18"]
go: ["1.18.2"]

steps:
- name: Checkout
Expand Down
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,5 @@ coverage/*
.vscode/

.DS_Store

build/1click
4 changes: 2 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ compile-linux: ## compile:
run: ## run
@./build/1click

run-cli: compile ## run randomized cli
@./build/1click cli -r --config ./config.yaml
run-cli: compile ## run cli
@./build/1click cli --config ./config.yaml

test: ## run tests
@mkdir -p coverage
Expand Down
41 changes: 21 additions & 20 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ We don't want to stop at Ethereum. We also want to help stakers of other PoS net

#### Using Go

If you have at least `go1.18.0` installed then this command will install the `1click` executable along with the library and its dependencies:
If you have at least `go1.18.2` installed then this command will install the `1click` executable along with the library and its dependencies:

```
go install github.com/NethermindEth/1click/cmd/1click@latest
Expand Down Expand Up @@ -64,33 +64,23 @@ sudo $GOPATH/bin/1click /usr/local/bin/
sudo cp 1click/build/1click /usr/local/bin/
```

#### Download the binary (only for linux)

> This is temporary until the first release
Download directly the binary and put it in `/usr/local/bin`:

```
sudo curl -LJ -o /usr/local/bin/1click https://github.com/NethermindEth/1click/raw/main/build/1click
sudo chmod +x /usr/local/bin/1click
```

### Dependencies
`1click` dependencies are `docker` and `docker-compose`, but if you don't have those installed, `1click` will show instructions to install them, or install them for you.
`1click` dependencies are `docker` with `docker compose` plugin, but if you don't have those installed, `1click` will show instructions to install them, or install them for you.

### Quick run
With `1click cli` you can go through the entire workflow setup:
1. Check dependencies
2. Generate a `docker-compose` script with randomized clients selection and `.env`
3. Execute the `docker-compose` script (only execution and consensus nodes will be executed by default)
2. Generate jwtsecret (not for mainnet and prater)
3. Generate a `docker-compose` script with randomized clients selection and `.env`
4. Execute the `docker-compose` script (only execution and consensus nodes will be executed by default)

## 🔥 What can you do right now with this tool?

- Select an execution, consensus and validator node (manually or automatically) and generate a `docker-compose` script with production tested configurations to run the setup as you want.
- Generate the keystore folder using the [staking-deposit-cli](https://github.com/ethereum/staking-deposit-cli) tool with `1click keys`
- Don't remember `docker-compose` commands or flags? No problem, you can check the logs of the running services of the generated `docker-compose` script with `1click logs` and shutdown the services with `1click down`

> The setup is currently designed to start all the three nodes required to start a validator (execution, consensus and validator node). This will change soon and `1click` will let you connect to a public or remote node, or to automatically start the validator node when the execution and consensus nodes in the setup are synced. Although you can do all of this after generating the docker-compose script 😉
> The setup is currently designed to start all the three nodes required to start a validator (execution, consensus and validator node). This will change soon and `1click` will let you connect to a public or remote node. The execution and consensus nodes will be executed first, and the validator node will be executed automatically after those nodes are sync, giving you time to prepare the keystore and make the deposit for your staked ETH.
## Supported networks and clients

Expand All @@ -103,20 +93,31 @@ With `1click cli` you can go through the entire workflow setup:
| | Prysm | Prysm |
| | Teku | Teku |

### Kiln

| Execution | Consensus | Validator |
| ---------- | ---------- | ---------- |
| Geth | Lighthouse | Lighthouse |
| Nethermind | Lodestar | Lodestar |
| | Prysm | Prysm |
| | Teku | Teku |

## ✅ Roadmap
The following roadmap covers the main features and ideas we want to implement but doesn't cover everything we are planning for this tool. Stay touched if you are interested, a lot of improvements are to come in the next two months.

### Version 0.1 (coming soon in May-June 2022)
- [x] Generate `docker-compose` scripts and `.env` files for selected clients with a cli tool
- [x] Generate keystore folder with the cli
- [ ] Test coverage (unit and integration tests)
- [ ] Monitoring tool for alerting, tracking validator balance, and tracking sync progress and status of nodes
- [x] Test coverage (unit tests)
- [x] Integrate Kiln network
- [ ] Integrate MEV-Boost as recommended setting
- [ ] Use public execution and consensus nodes

### Version 0.X
- [ ] Use public execution and consensus nodes
- [ ] Monitoring tool for alerting, tracking validator balance, and tracking sync progress and status of nodes
- [ ] TUI for guided and more interactive setup (better UX)
- [ ] Integrate Kiln network
- [ ] Integrate Ropsten network
- [ ] Integrate Sepolia network
- [ ] Integrate Prater network
- [ ] Off-premise setup support
- [ ] Improve documentation
Expand Down
Binary file modified build/1click
Binary file not shown.
147 changes: 129 additions & 18 deletions cli/cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ limitations under the License.
package cli

import (
"errors"
"fmt"
"os"
"strings"
Expand All @@ -31,20 +32,30 @@ import (
posmonidb "github.com/NethermindEth/posmoni/pkg/eth2/db"
posmoninet "github.com/NethermindEth/posmoni/pkg/eth2/networking"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
)

var (
executionName string
executionImage string
consensusName string
consensusImage string
validatorName string
validatorImage string
generationPath string
checkpointSyncUrl string
network string
feeRecipient string
jwtPath string
install bool
run bool
y bool
services *[]string
fallbackEL *[]string
elExtraFlags *[]string
clExtraFlags *[]string
vlExtraFlags *[]string
waitingTime time.Duration
mapAllPorts bool
)

const (
Expand All @@ -58,19 +69,21 @@ var cliCmd = &cobra.Command{
Long: `Run the setup tool on-premise in a quick way. Provide only the command line
options and the tool will do all the work.
First it will check if dependencies like docker and docker-compose are installed on your machine
First it will check if dependencies such as docker are installed on your machine
and provide instructions for installing them if they are not installed.
Second, it will generate docker-compose scripts to run the full setup according to your selection.
Finally, it will run the generated docker-compose script. Only execution and consensus clients will be executed by default.`,
Args: cobra.NoArgs,
PreRun: func(cmd *cobra.Command, args []string) {
// notest
if err := preRunCliCmd(cmd, args); err != nil {
log.Fatal(err)
}
},
Run: func(cmd *cobra.Command, args []string) {
// notest
if errs := runCliCmd(cmd, args); len(errs) > 0 {
for _, err := range errs {
log.Error(err)
Expand All @@ -81,15 +94,6 @@ Finally, it will run the generated docker-compose script. Only execution and con
}

func preRunCliCmd(cmd *cobra.Command, args []string) error {
// Count flags being set
count := 0
// HACKME: LocalFlags() doesn't work, so we count manually and check for parent flag config
cmd.Flags().Visit(func(f *pflag.Flag) {
if f.Name != "config" {
count++
}
})

// Quick run
if y {
install, run = true, true
Expand All @@ -104,15 +108,66 @@ func preRunCliCmd(cmd *cobra.Command, args []string) error {
// Ambiguous value
return fmt.Errorf(configs.RunClientsFlagAmbiguousError, *services)
}
} else if utils.Contains(*services, "none") {
if len(*services) == 1 {
// all used correctly
services = &[]string{}
} else {
// Ambiguous value
return fmt.Errorf(configs.RunClientsFlagAmbiguousError, *services)
}
} else if !utils.ContainsOnly(*services, []string{execution, consensus, validator}) {
return fmt.Errorf(configs.RunClientsError, strings.Join(*services, ","), strings.Join([]string{execution, consensus, validator}, ","))
}

// Validate network
networks, err := utils.SupportedNetworks()
if err != nil {
return fmt.Errorf(configs.NetworkValidationFailedError, err)
}
if !utils.Contains(networks, network) {
return fmt.Errorf(configs.UnknownNetworkError, network)
}

// Validate fee recipient
if feeRecipient != "" && !utils.IsAddress(feeRecipient) {
return errors.New(configs.InvalidFeeRecipientError)
}

// Prepare custom images
if executionName != "" {
executionParts := strings.Split(executionName, ":")
executionName = executionParts[0]
executionImage = strings.Join(executionParts[1:], ":")
}
if consensusName != "" {
consensusParts := strings.Split(consensusName, ":")
consensusName = consensusParts[0]
consensusImage = strings.Join(consensusParts[1:], ":")
}
if validatorName != "" {
validatorParts := strings.Split(validatorName, ":")
validatorName = validatorParts[0]
validatorImage = strings.Join(validatorParts[1:], ":")
}

return nil
}

func runCliCmd(cmd *cobra.Command, args []string) []error {
// Warnings
// Warn if custom images are used
if executionImage != "" || consensusImage != "" || validatorImage != "" {
log.Warn(configs.CustomImagesWarning)
}
// Warn if exposed ports are used
if mapAllPorts {
log.Warn(configs.MapAllPortsWarning)
}

// Get all clients: supported + configured
clientsMap, errors := clients.GetClients([]string{execution, consensus, validator})
c := clients.ClientInfo{Network: network}
clientsMap, errors := c.Clients([]string{execution, consensus, validator})
if len(errors) > 0 {
return errors
}
Expand Down Expand Up @@ -143,19 +198,54 @@ func runCliCmd(cmd *cobra.Command, args []string) []error {
}
log.Info(configs.DependenciesOK)

// Generate JWT secret if necessary
if jwtPath == "" && configs.JWTNetworks[network] {
if err = handleJWTSecret(); err != nil {
return []error{err}
}
}

// Get fee recipient
if !y && feeRecipient == "" {
if err = feeRecipientPrompt(); err != nil {
return []error{err}
}
}

// Generate docker-compose scripts
gd := generate.GenerationData{
ExecutionClient: combinedClients.Execution.Name,
ExecutionImage: executionImage,
ConsensusClient: combinedClients.Consensus.Name,
ConsensusImage: consensusImage,
ValidatorClient: combinedClients.Validator.Name,
ValidatorImage: validatorImage,
GenerationPath: generationPath,
Network: network,
CheckpointSyncUrl: checkpointSyncUrl,
FeeRecipient: feeRecipient,
JWTSecretPath: jwtPath,
FallbackELUrls: *fallbackEL,
ElExtraFlags: *elExtraFlags,
ClExtraFlags: *clExtraFlags,
VlExtraFlags: *vlExtraFlags,
MapAllPorts: mapAllPorts,
}
if err = generate.GenerateScripts(gd); err != nil {
return []error{err}
}

// If --run-clients=none was set then exit and don't run anything
if len(*services) == 0 {
log.Info(configs.HappyStaking2)
return nil
}

// If teku is chosen, then prepare datadir with 777 permissions
if combinedClients.Consensus.Name == "teku" {
preRunTeku()
}

if run {
if err = runAndShowContainers(*services); err != nil {
return []error{err}
Expand All @@ -171,6 +261,9 @@ func runCliCmd(cmd *cobra.Command, args []string) []error {

// Run validator after execution and consensus clients are synced, unless the user intencionally wants to run the validator service in the previous step
if !utils.Contains(*services, validator) {
// Wait for clients to start
log.Info(configs.WaitingForNodesToStart)
time.Sleep(waitingTime)
// Track sync of execution and consensus clients
// TODO: Parameterize wait arg of trackSync
if err = trackSync(monitor, time.Minute); err != nil {
Expand All @@ -197,27 +290,43 @@ func runCliCmd(cmd *cobra.Command, args []string) []error {
func init() {
rootCmd.AddCommand(cliCmd)

cliCmd.Flags().SortFlags = false

// Local flags
cliCmd.Flags().StringVarP(&executionName, "execution", "e", "", "Execution engine client, e.g. Geth, Nethermind, Besu, Erigon")
cliCmd.Flags().StringVarP(&executionName, "execution", "e", "", "Execution engine client, e.g. geth, nethermind, besu, erigon. Additionally, you can use this syntax '<CLIENT>:<DOCKER_IMAGE>' to override the docker image used for the client. If you want to use the default docker image, just use the client name.")

cliCmd.Flags().StringVarP(&consensusName, "consensus", "c", "", "Consensus engine client, e.g. Teku, Lodestar, Prysm, Lighthouse, Nimbus")
cliCmd.Flags().StringVarP(&consensusName, "consensus", "c", "", "Consensus engine client, e.g. teku, lodestar, prysm, lighthouse, Nimbus. Additionally, you can use this syntax '<CLIENT>:<DOCKER_IMAGE>' to override the docker image used for the client. If you want to use the default docker image, just use the client name.")

cliCmd.Flags().StringVarP(&validatorName, "validator", "v", "", "Validator engine client, e.g. Teku, Lodestar, Prysm, Lighthouse, Nimbus")
cliCmd.Flags().StringVarP(&validatorName, "validator", "v", "", "Validator engine client, e.g. teku, lodestar, prysm, lighthouse, Nimbus. Additionally, you can use this syntax '<CLIENT>:<DOCKER_IMAGE>' to override the docker image used for the client. If you want to use the default docker image, just use the client name.")

cliCmd.Flags().StringVarP(&generationPath, "path", "p", configs.DefaultDockerComposeScriptsPath, "docker-compose scripts generation path")

cliCmd.Flags().StringVar(&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")

cliCmd.Flags().StringVarP(&network, "network", "n", "mainnet", "Target network. e.g. mainnet, prater, kiln, etc.")

cliCmd.Flags().StringVar(&feeRecipient, "fee-recipient", "", "Suggested fee recipient. Is 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.")

cliCmd.Flags().StringVar(&jwtPath, "jwt-secret-path", "", "Path to the JWT secret file")

cliCmd.Flags().BoolVarP(&install, "install", "i", false, "Install dependencies if not installed without asking")

cliCmd.Flags().BoolVarP(&run, "run", "r", false, "Run the generated docker-compose scripts without asking")

cliCmd.Flags().BoolVarP(&y, "yes", "y", false, "Shortcut for '1click cli -r -i --run'. Run without prompts")

services = cliCmd.Flags().StringSlice("run-clients", []string{execution, consensus}, "Run only the specified clients. Possible values: execution, consensus, validator, all. The 'all' option must be used alone. Example: '1click cli -r --run-clients=consensus,validator'")
cliCmd.Flags().BoolVar(&mapAllPorts, "map-all", false, "Map all clients ports to host. Use with care. Useful to allow remote access to the clients.")

services = cliCmd.Flags().StringSlice("run-clients", []string{execution, consensus}, "Run only the specified clients. Possible values: execution, consensus, validator, all, none. The 'all' and 'none' option must be used alone. Example: '1click cli -r --run-clients=consensus,validator'")

fallbackEL = cliCmd.Flags().StringSlice("fallback-execution-urls", []string{}, "Fallback/backup execution endpoints for the consensus client. Not supported by Teku. Example: '1click cli -r --fallback-execution=https://mainnet.infura.io/v3/YOUR-PROJECT-ID,https://eth-mainnet.alchemyapi.io/v2/YOUR-PROJECT-ID'")

elExtraFlags = cliCmd.Flags().StringArray("el-extra-flag", []string{}, "Additional flag to configure the execution client service in the generated docker-compose script. Example: '1click cli --el-extra-flag \"<flag1>=value1\" --el-extra-flag \"<flag2>=\\\"value2\\\"\"'")

clExtraFlags = cliCmd.Flags().StringArray("cl-extra-flag", []string{}, "Additional flag to configure the consensus client service in the generated docker-compose script. Example: '1click cli --cl-extra-flag \"<flag1>=value1\" --cl-extra-flag \"<flag2>=\\\"value2\\\"\"'")

vlExtraFlags = cliCmd.Flags().StringArray("vl-extra-flag", []string{}, "Additional flag to configure the validator client service in the generated docker-compose script. Example: '1click cli --vl-extra-flag \"<flag1>=value1\" --vl-extra-flag \"<flag2>=\\\"value2\\\"\"'")

// Initialize monitoring tool
initMonitor(func() MonitoringTool {
// Initialize Eth2 Monitoring tool
Expand All @@ -229,8 +338,8 @@ func init() {
}
m, err := posmoni.NewEth2Monitor(
posmonidb.EmptyRepository{},
&posmoninet.BeaconClient{RetryDuration: time.Second},
&posmoninet.ExecutionClient{RetryDuration: time.Second},
&posmoninet.BeaconClient{RetryDuration: time.Second * 30},
&posmoninet.ExecutionClient{RetryDuration: time.Second * 30},
posmoninet.SubscribeOpts{},
moniCfg,
)
Expand All @@ -240,4 +349,6 @@ func init() {

return m
})

waitingTime = time.Minute
}
Loading

0 comments on commit e9755cb

Please sign in to comment.