2019-10-28 23:07:31 +00:00
import
2019-11-12 14:57:33 +00:00
confutils, strutils, strformat, os
2019-10-28 23:07:31 +00:00
const
rootDir = thisDir() / ".."
2020-01-13 00:03:52 +00:00
bootstrapTxtFileName = "bootstrap_nodes.txt"
bootstrapYamlFileName = "boot_enr.yaml"
depositContractFileName = "deposit_contract.txt"
2020-06-19 17:42:28 +00:00
depositContractBlockFileName = "deposit_contract_block.txt"
2019-10-28 23:07:31 +00:00
genesisFile = "genesis.ssz"
2020-04-27 19:03:03 +00:00
configFile = "config.yaml"
2019-10-28 23:07:31 +00:00
testnetsRepo = "eth2-testnets"
2020-06-29 09:43:01 +00:00
web3Url = "wss://goerli.infura.io/ws/v3/6224f3c792cc443fafb64e70a98f871e"
2019-11-05 13:00:11 +00:00
let
2019-11-09 10:46:34 +00:00
testnetsOrg = getEnv("ETH2_TESTNETS_ORG", "eth2-clients")
2019-11-05 13:00:11 +00:00
testnetsGitUrl = getEnv("ETH2_TESTNETS_GIT_URL", "https://github.com/" & testnetsOrg & "/" & testnetsRepo)
2019-10-28 23:07:31 +00:00
2019-11-09 10:46:34 +00:00
mode = Verbose
2019-10-28 23:07:31 +00:00
proc validateTestnetName(parts: openarray[string]): auto =
if parts.len != 2:
echo "The testnet name should have the format `client/network-name`"
quit 1
(parts[0], parts[1])
2020-06-23 22:41:38 +00:00
# reduces the error output when interrupting an external command with Ctrl+C
proc execIgnoringExitCode(s: string) =
try:
exec s
except OsError:
discard
proc updateTestnetsRepo(allTestnetsDir, buildDir: string) =
rmDir(allTestnetsDir)
let cwd = system.getCurrentDir()
cd buildDir
exec &"git clone --quiet --depth=1 {testnetsGitUrl}"
cd cwd
proc makePrometheusConfig(nodeID, baseMetricsPort: int, dataDir: string) =
# macOS may not have gnu-getopts installed and in the PATH
execIgnoringExitCode &"""./scripts/make_prometheus_config.sh --nodes """ & $(1 + nodeID) & &""" --base-metrics-port {baseMetricsPort} --config-file "{dataDir}/prometheus.yml""""
proc buildNode(nimFlags, preset, beaconNodeBinary: string) =
exec &"""nim c {nimFlags} -d:"const_preset={preset}" -o:"{beaconNodeBinary}" beacon_chain/beacon_node.nim"""
2020-06-28 01:51:00 +00:00
proc becomeValidator(validatorsDir, beaconNodeBinary, secretsDir, depositContractOpt, privateGoerliKey: string,
becomeValidatorOnly: bool) =
2020-06-23 22:41:38 +00:00
mode = Silent
2020-06-28 01:51:00 +00:00
var privKey = privateGoerliKey
if privKey.len == 0:
echo "\nPlease enter your Goerli Eth1 private key in hex form (e.g. 0x1a2...f3c) in order to become a validator (you'll need access to 32 GoETH)."
echo "Hit Enter to skip this."
# is there no other way to print without a trailing newline?
exec "printf '> '"
privKey = readLineFromStdin()
2020-06-23 22:41:38 +00:00
if privKey.len > 0:
mkDir validatorsDir
mode = Verbose
exec replace(&"""{beaconNodeBinary} deposits create
--count=1
--out-deposits-dir="{validatorsDir}"
--out-secrets-dir="{secretsDir}"
--deposit-private-key={privKey}
--web3-url={web3Url}
{depositContractOpt}
""", "\n", " ")
mode = Silent
2020-06-28 01:51:00 +00:00
if becomeValidatorOnly:
echo "\nDeposit sent."
else:
echo "\nDeposit sent, wait for confirmation then press enter to continue"
discard readLineFromStdin()
2020-06-23 22:41:38 +00:00
2020-06-29 10:05:00 +00:00
proc runNode(dataDir, beaconNodeBinary, bootstrapFileOpt, depositContractOpt,
2020-06-30 17:16:09 +00:00
genesisFileOpt, extraBeaconNodeOptions: string,
2020-06-29 10:05:00 +00:00
basePort, nodeID, baseMetricsPort, baseRpcPort: int,
printCmdOnly: bool) =
2020-06-23 22:41:38 +00:00
let logLevel = getEnv("LOG_LEVEL")
var logLevelOpt = ""
if logLevel.len > 0:
logLevelOpt = &"""--log-level="{logLevel}" """
mode = Verbose
2020-06-28 01:51:00 +00:00
var cmd: string
2020-06-26 02:44:24 +00:00
if printCmdOnly:
2020-06-28 01:51:00 +00:00
# When you reinvent getopt() and you forget to support repeating the same
# option to overwrite the old value...
cmd = replace(&"""{beaconNodeBinary}
--data-dir="{dataDir}"
--web3-url={web3Url}
{bootstrapFileOpt}
{depositContractOpt}
{genesisFileOpt} """, "\n", " ")
2020-06-26 02:44:24 +00:00
echo &"cd {dataDir}; exec {cmd}"
else:
cd dataDir
2020-06-28 01:51:00 +00:00
cmd = replace(&"""{beaconNodeBinary}
--data-dir="{dataDir}"
--dump
--web3-url={web3Url}
--tcp-port=""" & $(basePort + nodeID) & &"""
--udp-port=""" & $(basePort + nodeID) & &"""
--metrics
--metrics-port=""" & $(baseMetricsPort + nodeID) & &"""
--rpc
--rpc-port=""" & $(baseRpcPort + nodeID) & &"""
{bootstrapFileOpt}
{logLevelOpt}
{depositContractOpt}
2020-06-30 17:16:09 +00:00
{genesisFileOpt}
{extraBeaconNodeOptions}""", "\n", " ")
2020-06-26 02:44:24 +00:00
execIgnoringExitCode cmd
2020-02-25 01:49:47 +00:00
cli do (skipGoerliKey {.
desc: "Don't prompt for an Eth1 Goerli key to become a validator" .}: bool,
2020-04-27 19:03:03 +00:00
2020-06-28 01:51:00 +00:00
privateGoerliKey {.
desc: "Use this private Eth1 Goerli key to become a validator (careful with this option, the private key will end up in your shell's command history)" .} = "",
2020-06-19 17:42:28 +00:00
specVersion {.
desc: "Spec version"
2020-06-28 01:51:00 +00:00
name: "spec" .} = "v0.11.3",
2020-06-19 17:42:28 +00:00
2020-04-27 19:03:03 +00:00
constPreset {.
desc: "The Ethereum 2.0 const preset of the network (optional)"
2020-04-28 10:43:40 +00:00
name: "const-preset" .} = "",
2020-06-11 16:42:58 +00:00
nodeID {.
desc: "Node ID" .} = 0.int,
basePort {.
desc: "Base TCP/UDP port (nodeID will be added to it)" .} = 9000.int,
baseMetricsPort {.
desc: "Base metrics port (nodeID will be added to it)" .} = 8008.int,
2020-06-17 11:04:24 +00:00
baseRpcPort {.
2020-06-17 14:03:34 +00:00
desc: "Base rpc port (nodeID will be added to it)" .} = 9190.int,
2020-06-17 11:04:24 +00:00
2020-06-30 17:16:09 +00:00
extraBeaconNodeOptions {.
desc: "Extra options to pass to our beacon_node instance" .} = "",
2020-06-29 10:05:00 +00:00
2020-06-23 22:41:38 +00:00
writeLogFile {.
desc: "Write a log file in dataDir" .} = true,
buildOnly {.
desc: "Just the build, please." .} = false,
2020-06-28 01:51:00 +00:00
becomeValidatorOnly {.
desc: "Just become a validator." .} = false,
2020-06-23 22:41:38 +00:00
runOnly {.
desc: "Just run it." .} = false,
2020-06-26 02:44:24 +00:00
printCmdOnly {.
desc: "Just print the commands (suitable for passing to 'eval'; might replace current shell)." .} = false,
2020-04-27 19:03:03 +00:00
testnetName {.argument .}: string):
2019-10-28 23:07:31 +00:00
let
nameParts = testnetName.split "/"
(team, testnet) = if nameParts.len > 1: validateTestnetName nameParts
else: ("nimbus", testnetName)
let
buildDir = rootDir / "build"
allTestnetsDir = buildDir / testnetsRepo
2020-06-28 01:51:00 +00:00
if not (runOnly or becomeValidatorOnly):
2020-06-23 22:41:38 +00:00
updateTestnetsRepo(allTestnetsDir, buildDir)
2020-01-13 00:03:52 +00:00
var
depositContractOpt = ""
bootstrapFileOpt = ""
2020-06-19 17:42:28 +00:00
genesisFileOpt = ""
2020-06-28 01:51:00 +00:00
doBuild, doBecomeValidator, doRun = true
# step-skipping logic
if skipGoerliKey:
doBecomeValidator = false
if buildOnly:
doBecomeValidator = false
doRun = false
if becomeValidatorOnly:
doBuild = false
doRun = false
if runOnly:
doBuild = false
doBecomeValidator = false
2020-06-19 17:42:28 +00:00
let
testnetDir = allTestnetsDir / team / testnet
genesisFilePath = testnetDir / genesisFile
2019-10-28 23:07:31 +00:00
2019-11-12 14:57:33 +00:00
if not system.dirExists(testnetDir):
2019-10-28 23:07:31 +00:00
echo &"No metadata files exists for the '{testnetName}' testnet"
quit 1
2020-06-19 17:42:28 +00:00
if system.fileExists(genesisFilePath):
genesisFileOpt = &"--state-snapshot=\"{genesisFilePath}\""
2019-10-28 23:07:31 +00:00
2020-01-13 00:03:52 +00:00
let bootstrapTxtFile = testnetDir / bootstrapTxtFileName
if system.fileExists(bootstrapTxtFile):
bootstrapFileOpt = &"--bootstrap-file=\"{bootstrapTxtFile}\""
else:
let bootstrapYamlFile = testnetDir / bootstrapYamlFileName
if system.fileExists(bootstrapYamlFile):
2020-06-23 19:40:44 +00:00
bootstrapFileOpt = &"--bootstrap-file=\"{bootstrapYamlFile}\""
2020-01-13 00:03:52 +00:00
else:
echo "Warning: the network metadata doesn't include a bootstrap file"
2020-04-27 19:03:03 +00:00
var preset = testnetDir / configFile
if not system.fileExists(preset):
preset = constPreset
if preset.len == 0: preset = "minimal"
2020-06-23 19:40:44 +00:00
doAssert specVersion in ["v0.11.3", "v0.12.1"]
2019-10-28 23:07:31 +00:00
let
dataDirName = testnetName.replace("/", "_")
2020-01-27 18:53:12 +00:00
.replace("(", "_")
2020-06-11 16:42:58 +00:00
.replace(")", "_") & "_" & $nodeID
2019-10-28 23:07:31 +00:00
dataDir = buildDir / "data" / dataDirName
2019-12-02 23:27:59 +00:00
validatorsDir = dataDir / "validators"
2020-06-01 19:48:20 +00:00
secretsDir = dataDir / "secrets"
2019-10-28 23:07:31 +00:00
beaconNodeBinary = buildDir / "beacon_node_" & dataDirName
2020-02-06 11:44:11 +00:00
var
2020-06-29 18:08:58 +00:00
nimFlags = &"-d:chronicles_log_level=TRACE " & getEnv("NIM_PARAMS")
2019-10-28 23:07:31 +00:00
2020-06-23 22:41:38 +00:00
if writeLogFile:
# write the logs to a file
nimFlags.add """ -d:"chronicles_sinks=textlines,json[file(nbc""" & staticExec("date +\"%Y%m%d%H%M%S\"") & """.log)]" """
2020-04-28 10:43:40 +00:00
2020-01-13 00:03:52 +00:00
let depositContractFile = testnetDir / depositContractFileName
2019-11-12 14:57:33 +00:00
if system.fileExists(depositContractFile):
2019-10-29 00:27:13 +00:00
depositContractOpt = "--deposit-contract=" & readFile(depositContractFile).strip
2020-06-19 17:42:28 +00:00
let depositContractBlockFile = testnetDir / depositContractBlockFileName
if system.fileExists(depositContractBlockFile):
depositContractOpt.add " --deposit-contract-block=" & readFile(depositContractBlockFile).strip
2020-06-11 02:11:30 +00:00
cd rootDir
2020-06-11 15:37:27 +00:00
mkDir dataDir
2020-06-11 02:11:30 +00:00
2020-06-28 01:51:00 +00:00
if doBuild:
2020-06-23 22:41:38 +00:00
makePrometheusConfig(nodeID, baseMetricsPort, dataDir)
buildNode(nimFlags, preset, beaconNodeBinary)
2020-06-11 02:11:30 +00:00
2020-06-28 01:51:00 +00:00
if doBecomeValidator and depositContractOpt.len > 0 and not system.dirExists(validatorsDir):
becomeValidator(validatorsDir, beaconNodeBinary, secretsDir, depositContractOpt, privateGoerliKey, becomeValidatorOnly)
2020-02-07 23:57:48 +00:00
2020-06-30 17:16:09 +00:00
echo &"extraBeaconNodeOptions = '{extraBeaconNodeOptions}'"
2020-06-28 01:51:00 +00:00
if doRun:
2020-06-29 10:05:00 +00:00
runNode(dataDir, beaconNodeBinary, bootstrapFileOpt, depositContractOpt,
2020-06-30 17:16:09 +00:00
genesisFileOpt, extraBeaconNodeOptions,
basePort, nodeID, baseMetricsPort, baseRpcPort,
printCmdOnly)