2020-05-22 17:04:52 +00:00
|
|
|
# beacon_chain
|
2021-03-23 06:57:10 +00:00
|
|
|
# Copyright (c) 2018-2021 Status Research & Development GmbH
|
2020-05-22 17:04:52 +00:00
|
|
|
# Licensed and distributed under either of
|
|
|
|
# * MIT license (license terms in the root directory or at https://opensource.org/licenses/MIT).
|
|
|
|
# * Apache v2 license (license terms in the root directory or at https://www.apache.org/licenses/LICENSE-2.0).
|
|
|
|
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
|
|
|
|
2021-03-26 06:52:01 +00:00
|
|
|
{.push raises: [Defect].}
|
|
|
|
|
2020-05-27 17:06:28 +00:00
|
|
|
# Common routines for a BeaconNode and a ValidatorClient
|
2020-05-22 17:04:52 +00:00
|
|
|
|
|
|
|
import
|
|
|
|
# Standard library
|
2021-03-23 06:57:10 +00:00
|
|
|
std/[os, tables, strutils, typetraits],
|
2020-05-22 17:04:52 +00:00
|
|
|
|
|
|
|
# Nimble packages
|
2020-07-15 13:15:55 +00:00
|
|
|
chronos, confutils/defs,
|
2020-10-27 09:00:57 +00:00
|
|
|
chronicles, chronicles/helpers as chroniclesHelpers, chronicles/topics_registry,
|
2020-08-27 18:23:41 +00:00
|
|
|
stew/io2,
|
2020-05-22 17:04:52 +00:00
|
|
|
|
|
|
|
# Local modules
|
2021-06-21 08:35:24 +00:00
|
|
|
./spec/[crypto, helpers], ./spec/datatypes/base, beacon_clock, filepath,
|
2021-03-05 13:12:00 +00:00
|
|
|
./networking/eth2_network
|
2020-05-22 17:04:52 +00:00
|
|
|
|
2021-03-16 08:06:45 +00:00
|
|
|
when defined(posix):
|
|
|
|
import termios
|
|
|
|
|
2020-08-27 13:24:30 +00:00
|
|
|
proc setupStdoutLogging*(logLevel: string) =
|
2020-05-22 17:04:52 +00:00
|
|
|
when compiles(defaultChroniclesStream.output.writer):
|
2020-07-15 13:15:55 +00:00
|
|
|
defaultChroniclesStream.outputs[0].writer =
|
2020-05-22 17:04:52 +00:00
|
|
|
proc (logLevel: LogLevel, msg: LogOutputStr) {.gcsafe, raises: [Defect].} =
|
|
|
|
try:
|
|
|
|
stdout.write(msg)
|
2021-06-29 13:53:36 +00:00
|
|
|
stdout.flushFile()
|
2020-05-22 17:04:52 +00:00
|
|
|
except IOError as err:
|
|
|
|
logLoggingFailure(cstring(msg), err)
|
|
|
|
|
2021-03-26 06:52:01 +00:00
|
|
|
proc updateLogLevel*(logLevel: string) {.raises: [Defect, ValueError].} =
|
2020-11-12 10:46:02 +00:00
|
|
|
# Updates log levels (without clearing old ones)
|
|
|
|
let directives = logLevel.split(";")
|
|
|
|
try:
|
|
|
|
setLogLevel(parseEnum[LogLevel](directives[0]))
|
|
|
|
except ValueError:
|
|
|
|
raise (ref ValueError)(msg: "Please specify one of TRACE, DEBUG, INFO, NOTICE, WARN, ERROR or FATAL")
|
|
|
|
|
|
|
|
if directives.len > 1:
|
|
|
|
for topicName, settings in parseTopicDirectives(directives[1..^1]):
|
|
|
|
if not setTopicState(topicName, settings.state, settings.logLevel):
|
|
|
|
warn "Unrecognized logging topic", topic = topicName
|
|
|
|
|
2020-08-27 13:24:30 +00:00
|
|
|
proc setupLogging*(logLevel: string, logFile: Option[OutFile]) =
|
2020-07-15 13:15:55 +00:00
|
|
|
if logFile.isSome:
|
|
|
|
when defaultChroniclesStream.outputs.type.arity > 1:
|
|
|
|
block openLogFile:
|
|
|
|
let
|
|
|
|
logFile = logFile.get.string
|
|
|
|
logFileDir = splitFile(logFile).dir
|
2020-10-27 11:04:17 +00:00
|
|
|
let lres = secureCreatePath(logFileDir)
|
2020-08-27 18:23:41 +00:00
|
|
|
if lres.isErr():
|
|
|
|
error "Failed to create directory for log file",
|
|
|
|
path = logFileDir, err = ioErrorMsg(lres.error)
|
2020-07-15 13:15:55 +00:00
|
|
|
break openLogFile
|
|
|
|
|
2021-03-26 06:52:01 +00:00
|
|
|
try:
|
|
|
|
if not defaultChroniclesStream.outputs[1].open(logFile):
|
|
|
|
error "Failed to create log file", logFile
|
|
|
|
except CatchableError as exc:
|
|
|
|
# TODO why is there both exception and bool?
|
|
|
|
error "Failed to create log file", logFile, msg = exc.msg
|
2020-07-15 13:15:55 +00:00
|
|
|
else:
|
|
|
|
warn "The --log-file option is not active in the current build"
|
|
|
|
|
2020-05-22 17:04:52 +00:00
|
|
|
try:
|
2020-11-12 10:46:02 +00:00
|
|
|
updateLogLevel(logLevel)
|
2020-05-22 17:04:52 +00:00
|
|
|
except ValueError as err:
|
2021-03-26 06:52:01 +00:00
|
|
|
try:
|
|
|
|
stderr.write "Invalid value for --log-level. " & err.msg
|
2021-05-28 12:51:15 +00:00
|
|
|
except IOError:
|
2021-03-26 06:52:01 +00:00
|
|
|
echo "Invalid value for --log-level. " & err.msg
|
2020-05-22 17:04:52 +00:00
|
|
|
quit 1
|
|
|
|
|
2020-05-27 17:06:28 +00:00
|
|
|
template makeBannerAndConfig*(clientId: string, ConfType: type): untyped =
|
2020-06-30 12:23:52 +00:00
|
|
|
let
|
|
|
|
version = clientId & "\p" & copyrights & "\p\p" &
|
|
|
|
"eth2 specification v" & SPEC_VERSION & "\p\p" &
|
|
|
|
nimBanner
|
|
|
|
# TODO for some reason, copyrights are printed when doing `--help`
|
2020-08-04 13:00:55 +00:00
|
|
|
{.push warning[ProveInit]: off.}
|
|
|
|
let config = ConfType.load(
|
2020-06-30 12:23:52 +00:00
|
|
|
version = version,
|
|
|
|
copyrightBanner = clientId) # but a short version string makes more sense...
|
2020-08-04 13:00:55 +00:00
|
|
|
{.pop.}
|
|
|
|
config
|
2020-06-05 09:57:40 +00:00
|
|
|
|
2021-03-05 13:12:00 +00:00
|
|
|
# TODO not sure if this belongs here but it doesn't belong in `beacon_clock.nim` either
|
2020-06-05 09:57:40 +00:00
|
|
|
proc sleepToSlotOffset*(clock: BeaconClock, extra: chronos.Duration,
|
|
|
|
slot: Slot, msg: static string): Future[bool] {.async.} =
|
|
|
|
let
|
|
|
|
fromNow = clock.fromNow(slot.toBeaconTime(extra))
|
|
|
|
|
|
|
|
if fromNow.inFuture:
|
|
|
|
trace msg,
|
|
|
|
slot = shortLog(slot),
|
2020-07-16 13:16:51 +00:00
|
|
|
fromNow = shortLog(fromNow.offset)
|
2020-06-05 09:57:40 +00:00
|
|
|
|
|
|
|
await sleepAsync(fromNow.offset)
|
|
|
|
return true
|
|
|
|
return false
|
2020-09-01 13:38:34 +00:00
|
|
|
|
|
|
|
proc checkIfShouldStopAtEpoch*(scheduledSlot: Slot, stopAtEpoch: uint64) =
|
|
|
|
# Offset backwards slightly to allow this epoch's finalization check to occur
|
|
|
|
if scheduledSlot > 3 and stopAtEpoch > 0'u64 and
|
|
|
|
(scheduledSlot - 3).compute_epoch_at_slot() >= stopAtEpoch:
|
|
|
|
info "Stopping at pre-chosen epoch",
|
|
|
|
chosenEpoch = stopAtEpoch,
|
|
|
|
epoch = scheduledSlot.compute_epoch_at_slot(),
|
|
|
|
slot = scheduledSlot
|
|
|
|
|
|
|
|
# Brute-force, but ensure it's reliable enough to run in CI.
|
|
|
|
quit(0)
|
2021-03-16 08:06:45 +00:00
|
|
|
|
|
|
|
proc resetStdin*() =
|
|
|
|
when defined(posix):
|
|
|
|
# restore echoing, in case it was disabled by a password prompt
|
|
|
|
let fd = stdin.getFileHandle()
|
|
|
|
var attrs: Termios
|
|
|
|
discard fd.tcGetAttr(attrs.addr)
|
|
|
|
attrs.c_lflag = attrs.c_lflag or Cflag(ECHO)
|
|
|
|
discard fd.tcSetAttr(TCSANOW, attrs.addr)
|
|
|
|
|