From 9af541200b8fcf92a92c05ed39253abdcb4f8c97 Mon Sep 17 00:00:00 2001 From: Wolmin <44748271+Wolmin@users.noreply.github.com> Date: Thu, 12 Dec 2024 09:36:06 +0100 Subject: [PATCH] feat: Bump erigon tag (#274) * Bump erigon tag * Correct erigon repository * Install erigon using new installation method * Replace missing x86_64 link for nimbus * Update README with erigon version --- README.md | 12 ++-- commands/install.go | 10 +++ common/semver.go | 64 +++++++++++++++++ common/shared.go | 2 +- dependencies/clients/erigon.go | 121 ++++++++++++++++++++++++++++++-- dependencies/clients/nimbus2.go | 6 +- dependencies/clients/shared.go | 37 ++++++---- 7 files changed, 227 insertions(+), 25 deletions(-) create mode 100644 common/semver.go diff --git a/README.md b/README.md index 3fe6c07..a9436a7 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ The LUKSO CLI is a command line tool to install, manage and set up validators of The LUKSO CLI is able to install multiple clients for running the node. -- Execution Clients: [Geth](https://geth.ethereum.org/), [Erigon](https://github.com/ledgerwatch/erigon), [Nethermind](https://github.com/NethermindEth/nethermind), [Besu](https://www.hyperledger.org/projects/besu) +- Execution Clients: [Geth](https://geth.ethereum.org/), [Erigon](https://github.com/erigontech/erigon), [Nethermind](https://github.com/NethermindEth/nethermind), [Besu](https://www.hyperledger.org/projects/besu) - Consensus Clients: [Prysm](https://github.com/prysmaticlabs/prysm), [Lighthouse](https://github.com/sigp/lighthouse), [Teku](https://github.com/Consensys/teku), [Nimbus (eth-2)](https://nimbus.guide/index.html) - Validator Staking Clients: [Prysm](https://docs.prylabs.network/docs/how-prysm-works/prysm-validator-client), [Lighthouse](https://github.com/sigp/lighthouse), [Teku](https://github.com/Consensys/teku) @@ -21,7 +21,7 @@ The LUKSO CLI is able to install multiple clients for running the node. | Client | Version | Release | | -------------- | -------- | --------------------------------------------------------------- | | Geth | v1.14.12 | https://github.com/ethereum/go-ethereum/releases/tag/v1.14.12 | -| Erigon | v2.60.4 | https://github.com/ledgerwatch/erigon/releases/tag/v2.60.4 | +| Erigon | v2.60.10 | https://github.com/erigontech/erigon/releases/tag/v2.60.10 | | Nethermind | v1.27.0 | https://github.com/NethermindEth/nethermind/releases/tag/1.27.0 | | Besu | v24.7.0 | https://github.com/hyperledger/besu/releases/tag/24.7.0 | | Prysm | v5.1.2 | https://github.com/prysmaticlabs/prysm/releases/tag/v5.1.2 | @@ -206,7 +206,7 @@ $ lukso install --geth-tag 1.14.7 --geth-commit-hash aa55f5ea | --geth-commit-hash value | A hash of commit that is bound to given release tag | "293a300d" | | --validator-tag value | Tag for validator binary | "v5.1.2" | | --prysm-tag value | Tag for Prysm | "v5.1.2" | -| --erigon-tag value | Tag for Erigon | "2.60.4" | +| --erigon-tag value | Tag for Erigon | "2.60.10" | | --lighthouse-tag value | Tag for Lighthouse | "v5.2.1" | | --teku-tag value | Tag for Teku | "24.6.1" | | --besu-tag value | Tag for Besu | "24.7.0" | @@ -379,8 +379,8 @@ Please note that this won't affect your node in any way. | --geth-[command] | The `command` will be passed to the Geth client. [See the client docs for details](https://geth.ethereum.org/docs/fundamentals/command-line-options) | | --prysm-[command] | The `command` will be passed to the Prysm client. [See the client docs for details](https://docs.prylabs.network/docs/prysm-usage/parameters) | | --lighhouse-[command] | The `command` will be passed to the Lighthouse client. [See the client docs for details](https://lighthouse-book.sigmaprime.io/advanced-datadir.html) | -| --erigon-[command] | The `command` will be passed to the Erigon client. [See the client docs for details](https://github.com/ledgerwatch/erigon) | -| --teku-[command] | The `command` will be passed to the Teku client. [See the client docs for details](https://github.com/ledgerwatch/erigon) | +| --erigon-[command] | The `command` will be passed to the Erigon client. [See the client docs for details](https://github.com/erigontech/erigon) | +| --teku-[command] | The `command` will be passed to the Teku client. [See the client docs for details](https://github.com/Consensys/teku) | | --besu-[command] | The `command` will be passed to the Besu client. [See the client docs for details](https://github.com/hyperledger/besu) | | --nethermind-[command] | The `command` will be passed to the Nethermind client. [See the client docs for details](https://github.com/NethermindEth/nethermind) | | --nimbus2-[command] | The `command` will be passed to the Nethermind client. [See the client docs for details](https://nimbus.guide/index.html) | @@ -456,7 +456,7 @@ $ lukso status peers Ensure that the appropriate API is enabled when starting the node, as not all clients enable peer querying by default. For specific information about each client's peers queries, visit their documentation: - [Geth Peer Interaction](https://geth.ethereum.org/docs/interacting-with-geth/rpc/ns-admin) -- [Erigon Peer Commands](https://github.com/ledgerwatch/erigon/blob/devel/cmd/rpcdaemon/README.md#rpc-implementation-status) +- [Erigon Peer Commands](https://github.com/erigontech/erigon/blob/devel/cmd/rpcdaemon/README.md#rpc-implementation-status) All supported consensus clients follow the [Beacon API](https://ethereum.github.io/beacon-APIs/#/Node/getPeers) standardization. diff --git a/commands/install.go b/commands/install.go index 0fd79fa..919def8 100644 --- a/commands/install.go +++ b/commands/install.go @@ -95,6 +95,16 @@ func InstallBinaries(ctx *cli.Context) (err error) { case "2": selectedExecution = clients.Erigon executionTag = ctx.String(flags.ErigonTagFlag) + // backwards compatibility - after erigon edited the repository it came with a few breaking changes + // New tag format (_X.Y.Z_ -> _vX.Y.Z_) in GH release was introduced in v2.60.9 + tagVer := common.StringToSemver(executionTag) + erigonVer := common.StringToSemver("2.60.8") + if !tagVer.IsNewerThan(erigonVer) { + log.Warn("⚠️ Erigon has introduced changes in making new release URLs.\n" + + "This means that LUKSO CLI from now on will support versions from v2.60.9\n" + + "If you wish to install older erigon versions (not recommended), please use <=0.22.0 version of LUKSO CLI.") + } + case "3": selectedExecution = clients.Nethermind executionTag = ctx.String(flags.NethermindTagFlag) diff --git a/common/semver.go b/common/semver.go new file mode 100644 index 0000000..80c117f --- /dev/null +++ b/common/semver.go @@ -0,0 +1,64 @@ +package common + +import ( + "regexp" + "strconv" + "strings" +) + +// https://regexr.com/89qep +var semverExpr = regexp.MustCompile(`^v?\d+(\.\d+){2}$`) + +type Semver struct { + major int + minor int + patch int +} + +func StringToSemver(str string) (semver Semver) { + semver = Semver{ + major: 0, + minor: 0, + patch: 0, + } + + matches := semverExpr.FindAllString(str, -1) + if len(matches) != 1 { + return + } + + // already validated by the regex, conversions and slice accesses won't error/panic + v := strings.Split(strings.Trim(matches[0], "v"), ".") + major, _ := strconv.Atoi(v[0]) + minor, _ := strconv.Atoi(v[1]) + patch, _ := strconv.Atoi(v[2]) + + semver = Semver{ + major: major, + minor: minor, + patch: patch, + } + + return +} + +// IsNewerThan checks whether v1 is a newer version than v2. +// The check is strict (equal values return false) +func (v Semver) IsNewerThan(v2 Semver) (isNewer bool) { + switch { + case v.major > v2.major: + return true + case v.major < v2.major: + return false + case v.minor > v2.minor: + return true + case v.minor < v2.minor: + return false + case v.patch > v2.patch: + return true + case v.patch < v2.patch: + return false + } + + return false +} diff --git a/common/shared.go b/common/shared.go index 821668d..549ec0c 100644 --- a/common/shared.go +++ b/common/shared.go @@ -5,7 +5,7 @@ const ( SlotsPerEpoch = 32 GethTag = "1.14.12" - ErigonTag = "2.60.4" + ErigonTag = "v2.60.10" NethermindTag = "1.27.0" PrysmTag = "v5.1.2" LighthouseTag = "v5.2.1" diff --git a/dependencies/clients/erigon.go b/dependencies/clients/erigon.go index dcb2e1c..dfc8d72 100644 --- a/dependencies/clients/erigon.go +++ b/dependencies/clients/erigon.go @@ -2,13 +2,25 @@ package clients import ( "fmt" + "io/fs" + "os" + "os/exec" + "path/filepath" "strings" + "time" + log "github.com/sirupsen/logrus" "github.com/urfave/cli/v2" + "github.com/lukso-network/tools-lukso-cli/common" "github.com/lukso-network/tools-lukso-cli/common/errors" "github.com/lukso-network/tools-lukso-cli/common/utils" "github.com/lukso-network/tools-lukso-cli/flags" + "github.com/lukso-network/tools-lukso-cli/pid" +) + +const ( + erigonFolder = common.ClientDepsFolder + "/erigon" ) type ErigonClient struct { @@ -20,7 +32,7 @@ func NewErigonClient() *ErigonClient { &clientBinary{ name: erigonDependencyName, commandName: "erigon", - baseUrl: "https://github.com/ledgerwatch/erigon/releases/download/v|TAG|/erigon_|TAG|_|OS|_|ARCH|.tar.gz", + baseUrl: "https://github.com/erigontech/erigon/releases/download/|TAG|/erigon_|TAG|_|OS|_|ARCH|.tar.gz", githubLocation: erigonGithubLocation, }, } @@ -30,6 +42,99 @@ var Erigon = NewErigonClient() var _ ClientBinaryDependency = &ErigonClient{} +func (e *ErigonClient) Start(ctx *cli.Context, arguments []string) (err error) { + if e.IsRunning() { + log.Infof("🔄️ %s is already running - stopping first...", e.Name()) + + err = e.Stop() + if err != nil { + return + } + + log.Infof("🛑 Stopped %s", e.Name()) + } + + err = initClient(ctx, e) + if err != nil && err != errors.ErrAlreadyRunning { // if it is already running it will be caught during start + log.Errorf("❌ There was an error while initalizing %s. Error: %v", e.Name(), err) + + return err + } + + command := exec.Command(fmt.Sprintf("./%s/erigon", e.FilePath()), arguments...) + + var ( + logFile *os.File + fullPath string + ) + + logFolder := ctx.String(flags.LogFolderFlag) + if logFolder == "" { + return utils.Exit(fmt.Sprintf("%v- %s", errors.ErrFlagMissing, flags.LogFolderFlag), 1) + } + + fullPath, err = utils.PrepareTimestampedFile(logFolder, e.CommandName()) + if err != nil { + return + } + + err = os.WriteFile(fullPath, []byte{}, 0o750) + if err != nil { + return + } + + logFile, err = os.OpenFile(fullPath, os.O_RDWR, 0o750) + if err != nil { + return + } + + command.Stdout = logFile + command.Stderr = logFile + + log.Infof("🔄 Starting %s", e.Name()) + err = command.Start() + if err != nil { + return + } + + pidLocation := fmt.Sprintf("%s/%s.pid", pid.FileDir, e.CommandName()) + err = pid.Create(pidLocation, command.Process.Pid) + + time.Sleep(1 * time.Second) + + log.Infof("✅ %s started!", e.Name()) + + return +} + +func (e *ErigonClient) Install(url string, isUpdate bool) (err error) { + if utils.FileExists(e.FilePath()) && !isUpdate { + message := fmt.Sprintf("You already have the %s client installed, do you want to override your installation? [Y/n]: ", e.Name()) + input := utils.RegisterInputWithMessage(message) + if !strings.EqualFold(input, "y") && input != "" { + log.Info("⏭️ Skipping installation...") + + return nil + } + } + + err = installAndExtractFromURL(url, e.name, common.ClientDepsFolder, tarFormat, isUpdate) + if err != nil { + return + } + + permFunc := func(path string, d fs.DirEntry, err error) error { + return os.Chmod(path, fs.ModePerm) + } + + err = filepath.WalkDir(e.FilePath(), permFunc) + if err != nil { + return + } + + return +} + func (e *ErigonClient) PrepareStartFlags(ctx *cli.Context) (startFlags []string, err error) { if !utils.FlagFileExists(ctx, flags.ErigonConfigFileFlag) { err = errors.ErrFlagMissing @@ -43,13 +148,17 @@ func (e *ErigonClient) PrepareStartFlags(ctx *cli.Context) (startFlags []string, return } +func (e *ErigonClient) FilePath() string { + return erigonFolder +} + func (e *ErigonClient) Peers(ctx *cli.Context) (outbound, inbound int, err error) { return defaultExecutionPeers(ctx, 8545) } func (e *ErigonClient) Version() (version string) { cmdVer := execVersionCmd( - e.CommandName(), + fmt.Sprintf("./%s/%s", e.FilePath(), e.CommandName()), ) if cmdVer == VersionNotAvailable { @@ -58,6 +167,10 @@ func (e *ErigonClient) Version() (version string) { // Erigon version output to parse: - // erigon version 2.60.4 - return fmt.Sprintf("v%s", strings.Split(cmdVer, " ")[2]) + // erigon version 2.60.10-3afee08b + + // -> ...|2.60.10-3afee08b + s := strings.Split(cmdVer, " ")[2] + // -> 2.60.10|... + return fmt.Sprintf("v%s", strings.Split(s, "-")[0]) } diff --git a/dependencies/clients/nimbus2.go b/dependencies/clients/nimbus2.go index b877b4b..6df152d 100644 --- a/dependencies/clients/nimbus2.go +++ b/dependencies/clients/nimbus2.go @@ -66,9 +66,13 @@ func (n *Nimbus2Client) ParseUrl(tag, commitHash string) (url string) { urlSystem = "Linux" } - if arch == "arm" || arch == "aarch64" { + if (arch == "arm" || arch == "aarch64") && system.Os == system.Ubuntu { arch = "arm64v8" } + if arch == "x86_64" { + // x86_64 not present as a released tar - default to amd64 instead + arch = "amd64" + } url = n.baseUrl url = strings.ReplaceAll(url, "|TAG|", tag) diff --git a/dependencies/clients/shared.go b/dependencies/clients/shared.go index 76cd812..45ac3fc 100644 --- a/dependencies/clients/shared.go +++ b/dependencies/clients/shared.go @@ -46,7 +46,7 @@ const ( gethGithubLocation = "ethereum/go-ethereum" prysmaticLabsGithubLocation = "prysmaticlabs/prysm" lighthouseGithubLocation = "sigp/lighthouse" - erigonGithubLocation = "ledgerwatch/erigon" + erigonGithubLocation = "erigontech/erigon" tekuGithubLocation = "Consensys/teku" nethermindGithubLocation = "NethermindEth/nethermind" besuGithubLocation = "hyperledger/besu" @@ -308,7 +308,7 @@ func (client *clientBinary) Install(url string, isUpdate bool) (err error) { case gethDependencyName: targetHeader = "/" + client.CommandName() case erigonDependencyName: - targetHeader = "erigon" + targetHeader = "/erigon" case lighthouseDependencyName: targetHeader = "lighthouse" } @@ -425,15 +425,22 @@ Which initial LYX supply do you choose? } } - var dataDir string + var ( + dataDir string + cmdPath string + ) + switch client.Name() { case gethDependencyName: dataDir = fmt.Sprintf("--datadir=%s", ctx.String(flags.GethDatadirFlag)) + cmdPath = client.CommandName() + case erigonDependencyName: dataDir = fmt.Sprintf("--datadir=%s", ctx.String(flags.ErigonDatadirFlag)) + cmdPath = fmt.Sprintf("./%s/%s", client.FilePath(), client.CommandName()) } - command := exec.Command(client.CommandName(), "init", dataDir, ctx.String(flags.GenesisJsonFlag)) + command := exec.Command(cmdPath, "init", dataDir, ctx.String(flags.GenesisJsonFlag)) command.Stdout = os.Stdout command.Stderr = os.Stderr @@ -696,27 +703,31 @@ func untarDir(dst string, t *tar.Reader) error { return err } - path := filepath.Join(dst, header.Name) + var ( + path string + headerName = header.Name + ) // for the sake of compatibility with updated versions remove the tag from the tarred file - teku/teku-xx.x.x => teku/teku, same with jdk switch { case strings.Contains(header.Name, "teku-"): - newHeader := replaceRootFolderName(header.Name, "teku") - path = filepath.Join(dst, newHeader) + headerName = replaceRootFolderName(header.Name, "teku") case strings.Contains(header.Name, "jdk-"): - newHeader := replaceRootFolderName(header.Name, "jdk") - path = filepath.Join(dst, newHeader) + headerName = replaceRootFolderName(header.Name, "jdk") case strings.Contains(header.Name, "besu-"): - newHeader := replaceRootFolderName(header.Name, "besu") - path = filepath.Join(dst, newHeader) + headerName = replaceRootFolderName(header.Name, "besu") case strings.Contains(header.Name, "nimbus-eth2"): - newHeader := replaceRootFolderName(header.Name, "nimbus2") - path = filepath.Join(dst, newHeader) + headerName = replaceRootFolderName(header.Name, "nimbus2") + + case strings.Contains(header.Name, "erigon"): + headerName = replaceRootFolderName(header.Name, "erigon") } + path = filepath.Join(dst, headerName) + info := header.FileInfo() if info.IsDir() { err = os.MkdirAll(path, info.Mode())