mirror of
https://github.com/status-im/nimbus-eth2.git
synced 2025-02-02 17:53:52 +00:00
Add support for starting from weak subjectivity checkpoints
Also removes the `genesis.ssz` file stored in the data folder. The `medalla-fast-sync` target has been adapted to use the new features.
This commit is contained in:
parent
dc428e00db
commit
aed291128a
7
Makefile
7
Makefile
@ -200,12 +200,14 @@ define CONNECT_TO_NETWORK
|
|||||||
--base-metrics-port $$(($(BASE_METRICS_PORT) + $(NODE_ID))) \
|
--base-metrics-port $$(($(BASE_METRICS_PORT) + $(NODE_ID))) \
|
||||||
--config-file "build/data/shared_$(1)_$(NODE_ID)/prometheus.yml"
|
--config-file "build/data/shared_$(1)_$(NODE_ID)/prometheus.yml"
|
||||||
|
|
||||||
|
[ "$(2)" == "FastSync" ] && { export CHECKPOINT_PARAMS="--finalized-checkpoint-state=vendor/eth2-testnets/shared/$(1)/recent-finalized-state.ssz \
|
||||||
|
--finalized-checkpoint-block=vendor/eth2-testnets/shared/$(1)/recent-finalized-block.ssz" ; }; \
|
||||||
$(CPU_LIMIT_CMD) build/beacon_node \
|
$(CPU_LIMIT_CMD) build/beacon_node \
|
||||||
--network=$(1) \
|
--network=$(1) \
|
||||||
--log-level="$(LOG_LEVEL)" \
|
--log-level="$(LOG_LEVEL)" \
|
||||||
--log-file=build/data/shared_$(1)_$(NODE_ID)/nbc_bn_$$(date +"%Y%m%d%H%M%S").log \
|
--log-file=build/data/shared_$(1)_$(NODE_ID)/nbc_bn_$$(date +"%Y%m%d%H%M%S").log \
|
||||||
--data-dir=build/data/shared_$(1)_$(NODE_ID) \
|
--data-dir=build/data/shared_$(1)_$(NODE_ID) \
|
||||||
$(GOERLI_TESTNETS_PARAMS) $(NODE_PARAMS)
|
$$CHECKPOINT_PARAMS $(GOERLI_TESTNETS_PARAMS) $(NODE_PARAMS)
|
||||||
endef
|
endef
|
||||||
|
|
||||||
define CONNECT_TO_NETWORK_IN_DEV_MODE
|
define CONNECT_TO_NETWORK_IN_DEV_MODE
|
||||||
@ -293,6 +295,9 @@ medalla: | beacon_node signing_process
|
|||||||
medalla-vc: | beacon_node signing_process validator_client
|
medalla-vc: | beacon_node signing_process validator_client
|
||||||
$(call CONNECT_TO_NETWORK_WITH_VALIDATOR_CLIENT,medalla)
|
$(call CONNECT_TO_NETWORK_WITH_VALIDATOR_CLIENT,medalla)
|
||||||
|
|
||||||
|
medalla-fast-sync: | beacon_node
|
||||||
|
$(call connect_to_network,medalla,FastSync)
|
||||||
|
|
||||||
ifneq ($(LOG_LEVEL), TRACE)
|
ifneq ($(LOG_LEVEL), TRACE)
|
||||||
medalla-dev:
|
medalla-dev:
|
||||||
+ "$(MAKE)" LOG_LEVEL=TRACE $@
|
+ "$(MAKE)" LOG_LEVEL=TRACE $@
|
||||||
|
@ -26,14 +26,19 @@ type
|
|||||||
DbKeyKind = enum
|
DbKeyKind = enum
|
||||||
kHashToState
|
kHashToState
|
||||||
kHashToBlock
|
kHashToBlock
|
||||||
kHeadBlock # Pointer to the most recent block selected by the fork choice
|
kHeadBlock
|
||||||
kTailBlock ##\
|
## Pointer to the most recent block selected by the fork choice
|
||||||
|
kTailBlock
|
||||||
## Pointer to the earliest finalized block - this is the genesis block when
|
## Pointer to the earliest finalized block - this is the genesis block when
|
||||||
## the chain starts, but might advance as the database gets pruned
|
## the chain starts, but might advance as the database gets pruned
|
||||||
## TODO: determine how aggressively the database should be pruned. For a
|
## TODO: determine how aggressively the database should be pruned. For a
|
||||||
## healthy network sync, we probably need to store blocks at least
|
## healthy network sync, we probably need to store blocks at least
|
||||||
## past the weak subjectivity period.
|
## past the weak subjectivity period.
|
||||||
kBlockSlotStateRoot ## BlockSlot -> state_root mapping
|
kBlockSlotStateRoot
|
||||||
|
## BlockSlot -> state_root mapping
|
||||||
|
kGenesisBlockRoot
|
||||||
|
## Immutable reference to the network genesis state
|
||||||
|
## (needed for satisfying requests to the beacon node API).
|
||||||
|
|
||||||
const
|
const
|
||||||
maxDecompressedDbRecordSize = 16*1024*1024
|
maxDecompressedDbRecordSize = 16*1024*1024
|
||||||
@ -165,6 +170,9 @@ proc putHeadBlock*(db: BeaconChainDB, key: Eth2Digest) =
|
|||||||
proc putTailBlock*(db: BeaconChainDB, key: Eth2Digest) =
|
proc putTailBlock*(db: BeaconChainDB, key: Eth2Digest) =
|
||||||
db.put(subkey(kTailBlock), key)
|
db.put(subkey(kTailBlock), key)
|
||||||
|
|
||||||
|
proc putGenesisBlockRoot*(db: BeaconChainDB, key: Eth2Digest) =
|
||||||
|
db.put(subkey(kGenesisBlockRoot), key)
|
||||||
|
|
||||||
proc getBlock*(db: BeaconChainDB, key: Eth2Digest): Opt[TrustedSignedBeaconBlock] =
|
proc getBlock*(db: BeaconChainDB, key: Eth2Digest): Opt[TrustedSignedBeaconBlock] =
|
||||||
# We only store blocks that we trust in the database
|
# We only store blocks that we trust in the database
|
||||||
result.ok(TrustedSignedBeaconBlock())
|
result.ok(TrustedSignedBeaconBlock())
|
||||||
@ -207,6 +215,9 @@ proc getHeadBlock*(db: BeaconChainDB): Opt[Eth2Digest] =
|
|||||||
proc getTailBlock*(db: BeaconChainDB): Opt[Eth2Digest] =
|
proc getTailBlock*(db: BeaconChainDB): Opt[Eth2Digest] =
|
||||||
db.get(subkey(kTailBlock), Eth2Digest)
|
db.get(subkey(kTailBlock), Eth2Digest)
|
||||||
|
|
||||||
|
proc getGenesisBlockRoot*(db: BeaconChainDB): Eth2Digest =
|
||||||
|
db.get(subkey(kGenesisBlockRoot), Eth2Digest).expect("The database must be seeded with the genesis state")
|
||||||
|
|
||||||
proc containsBlock*(db: BeaconChainDB, key: Eth2Digest): bool =
|
proc containsBlock*(db: BeaconChainDB, key: Eth2Digest): bool =
|
||||||
db.backend.contains(subkey(SignedBeaconBlock, key)).expect("working database")
|
db.backend.contains(subkey(SignedBeaconBlock, key)).expect("working database")
|
||||||
|
|
||||||
|
@ -23,7 +23,7 @@ import
|
|||||||
|
|
||||||
# Local modules
|
# Local modules
|
||||||
spec/[datatypes, digest, crypto, beaconstate, helpers, network, presets],
|
spec/[datatypes, digest, crypto, beaconstate, helpers, network, presets],
|
||||||
spec/state_transition,
|
spec/[state_transition, weak_subjectivity],
|
||||||
conf, time, beacon_chain_db, validator_pool, extras,
|
conf, time, beacon_chain_db, validator_pool, extras,
|
||||||
attestation_pool, exit_pool, eth2_network, eth2_discovery,
|
attestation_pool, exit_pool, eth2_network, eth2_discovery,
|
||||||
beacon_node_common, beacon_node_types, beacon_node_status,
|
beacon_node_common, beacon_node_types, beacon_node_status,
|
||||||
@ -36,7 +36,6 @@ import
|
|||||||
./eth2_processor
|
./eth2_processor
|
||||||
|
|
||||||
const
|
const
|
||||||
genesisFile* = "genesis.ssz"
|
|
||||||
hasPrompt = not defined(withoutPrompt)
|
hasPrompt = not defined(withoutPrompt)
|
||||||
|
|
||||||
type
|
type
|
||||||
@ -60,62 +59,6 @@ declareGauge ticks_delay,
|
|||||||
|
|
||||||
logScope: topics = "beacnde"
|
logScope: topics = "beacnde"
|
||||||
|
|
||||||
proc getStateFromSnapshot(conf: BeaconNodeConf, stateSnapshotContents: ref string): NilableBeaconStateRef =
|
|
||||||
var
|
|
||||||
genesisPath = conf.dataDir/genesisFile
|
|
||||||
snapshotContents: TaintedString
|
|
||||||
writeGenesisFile = false
|
|
||||||
|
|
||||||
if conf.stateSnapshot.isSome:
|
|
||||||
let
|
|
||||||
snapshotPath = conf.stateSnapshot.get.string
|
|
||||||
snapshotExt = splitFile(snapshotPath).ext
|
|
||||||
|
|
||||||
if cmpIgnoreCase(snapshotExt, ".ssz") != 0:
|
|
||||||
error "The supplied state snapshot must be a SSZ file",
|
|
||||||
suppliedPath = snapshotPath
|
|
||||||
quit 1
|
|
||||||
|
|
||||||
snapshotContents = readFile(snapshotPath)
|
|
||||||
if fileExists(genesisPath):
|
|
||||||
let genesisContents = readFile(genesisPath)
|
|
||||||
if snapshotContents != genesisContents:
|
|
||||||
error "Data directory not empty. Existing genesis state differs from supplied snapshot",
|
|
||||||
dataDir = conf.dataDir.string, snapshot = snapshotPath
|
|
||||||
quit 1
|
|
||||||
else:
|
|
||||||
debug "No previous genesis state. Importing snapshot",
|
|
||||||
genesisPath, dataDir = conf.dataDir.string
|
|
||||||
writeGenesisFile = true
|
|
||||||
genesisPath = snapshotPath
|
|
||||||
elif fileExists(genesisPath):
|
|
||||||
try: snapshotContents = readFile(genesisPath)
|
|
||||||
except CatchableError as err:
|
|
||||||
error "Failed to read genesis file", err = err.msg
|
|
||||||
quit 1
|
|
||||||
elif stateSnapshotContents != nil:
|
|
||||||
swap(snapshotContents, TaintedString stateSnapshotContents[])
|
|
||||||
else:
|
|
||||||
# No snapshot was provided. We should wait for genesis.
|
|
||||||
return nil
|
|
||||||
|
|
||||||
result = try:
|
|
||||||
newClone(SSZ.decode(snapshotContents, BeaconState))
|
|
||||||
except SerializationError:
|
|
||||||
error "Failed to import genesis file", path = genesisPath
|
|
||||||
quit 1
|
|
||||||
|
|
||||||
info "Loaded genesis state", path = genesisPath
|
|
||||||
|
|
||||||
if writeGenesisFile:
|
|
||||||
try:
|
|
||||||
notice "Writing genesis to data directory", path = conf.dataDir/genesisFile
|
|
||||||
writeFile(conf.dataDir/genesisFile, snapshotContents.string)
|
|
||||||
except CatchableError as err:
|
|
||||||
error "Failed to persist genesis file to data dir",
|
|
||||||
err = err.msg, genesisFile = conf.dataDir/genesisFile
|
|
||||||
quit 1
|
|
||||||
|
|
||||||
func enrForkIdFromState(state: BeaconState): ENRForkID =
|
func enrForkIdFromState(state: BeaconState): ENRForkID =
|
||||||
let
|
let
|
||||||
forkVer = state.fork.current_version
|
forkVer = state.fork.current_version
|
||||||
@ -129,21 +72,59 @@ func enrForkIdFromState(state: BeaconState): ENRForkID =
|
|||||||
proc init*(T: type BeaconNode,
|
proc init*(T: type BeaconNode,
|
||||||
rng: ref BrHmacDrbgContext,
|
rng: ref BrHmacDrbgContext,
|
||||||
conf: BeaconNodeConf,
|
conf: BeaconNodeConf,
|
||||||
stateSnapshotContents: ref string): Future[BeaconNode] {.async.} =
|
genesisStateContents: ref string): Future[BeaconNode] {.async.} =
|
||||||
let
|
let
|
||||||
netKeys = getPersistentNetKeys(rng[], conf)
|
netKeys = getPersistentNetKeys(rng[], conf)
|
||||||
nickname = if conf.nodeName == "auto": shortForm(netKeys)
|
nickname = if conf.nodeName == "auto": shortForm(netKeys)
|
||||||
else: conf.nodeName
|
else: conf.nodeName
|
||||||
db = BeaconChainDB.init(kvStore SqStoreRef.init(conf.databaseDir, "nbc").tryGet())
|
db = BeaconChainDB.init(kvStore SqStoreRef.init(conf.databaseDir, "nbc").tryGet())
|
||||||
|
|
||||||
var mainchainMonitor: MainchainMonitor
|
var
|
||||||
|
mainchainMonitor: MainchainMonitor
|
||||||
|
genesisState, checkpointState: ref BeaconState
|
||||||
|
checkpointBlock: SignedBeaconBlock
|
||||||
|
|
||||||
|
if conf.finalizedCheckpointState.isSome:
|
||||||
|
let checkpointStatePath = conf.finalizedCheckpointState.get.string
|
||||||
|
checkpointState = try:
|
||||||
|
newClone(SSZ.loadFile(checkpointStatePath, BeaconState))
|
||||||
|
except SerializationError as err:
|
||||||
|
fatal "Checkpoint state deserialization failed",
|
||||||
|
err = formatMsg(err, checkpointStatePath)
|
||||||
|
quit 1
|
||||||
|
except CatchableError as err:
|
||||||
|
fatal "Failed to read checkpoint state file", err = err.msg
|
||||||
|
quit 1
|
||||||
|
|
||||||
|
if conf.finalizedCheckpointBlock.isNone:
|
||||||
|
if checkpointState.slot > 0:
|
||||||
|
fatal "Specifying a non-genesis --finalized-checkpoint-state requires specifying --finalized-checkpoint-block as well"
|
||||||
|
quit 1
|
||||||
|
else:
|
||||||
|
let checkpointBlockPath = conf.finalizedCheckpointBlock.get.string
|
||||||
|
try:
|
||||||
|
checkpointBlock = SSZ.loadFile(checkpointBlockPath, SignedBeaconBlock)
|
||||||
|
except SerializationError as err:
|
||||||
|
fatal "Invalid checkpoint block", err = err.formatMsg(checkpointBlockPath)
|
||||||
|
quit 1
|
||||||
|
except IOError as err:
|
||||||
|
fatal "Failed to load the checkpoint block", err = err.msg
|
||||||
|
quit 1
|
||||||
|
elif conf.finalizedCheckpointBlock.isSome:
|
||||||
|
# TODO We can download the state from somewhere in the future relying
|
||||||
|
# on the trusted `state_root` appearing in the checkpoint block.
|
||||||
|
fatal "--finalized-checkpoint-block cannot be specified without --finalized-checkpoint-state"
|
||||||
|
quit 1
|
||||||
|
|
||||||
if not ChainDAGRef.isInitialized(db):
|
if not ChainDAGRef.isInitialized(db):
|
||||||
# Fresh start - need to load a genesis state from somewhere
|
var
|
||||||
var genesisState = conf.getStateFromSnapshot(stateSnapshotContents)
|
tailState: ref BeaconState
|
||||||
|
tailBlock: SignedBeaconBlock
|
||||||
|
|
||||||
# Try file from command line first
|
if genesisStateContents == nil and checkpointState == nil:
|
||||||
if genesisState.isNil:
|
# This is a fresh start without a known genesis state
|
||||||
|
# (most likely, it hasn't arrived yet). We'll try to
|
||||||
|
# obtain a genesis through the Eth1 deposits monitor:
|
||||||
if conf.web3Url.len == 0:
|
if conf.web3Url.len == 0:
|
||||||
fatal "Web3 URL not specified"
|
fatal "Web3 URL not specified"
|
||||||
quit 1
|
quit 1
|
||||||
@ -186,42 +167,68 @@ proc init*(T: type BeaconNode,
|
|||||||
if bnStatus == BeaconNodeStatus.Stopping:
|
if bnStatus == BeaconNodeStatus.Stopping:
|
||||||
return nil
|
return nil
|
||||||
|
|
||||||
|
tailState = genesisState
|
||||||
|
tailBlock = get_initial_beacon_block(genesisState[])
|
||||||
|
|
||||||
notice "Eth2 genesis state detected",
|
notice "Eth2 genesis state detected",
|
||||||
genesisTime = genesisState.genesisTime,
|
genesisTime = genesisState.genesisTime,
|
||||||
eth1Block = genesisState.eth1_data.block_hash,
|
eth1Block = genesisState.eth1_data.block_hash,
|
||||||
totalDeposits = genesisState.eth1_data.deposit_count
|
totalDeposits = genesisState.eth1_data.deposit_count
|
||||||
|
|
||||||
# This is needed to prove the not nil property from here on
|
elif genesisStateContents == nil:
|
||||||
if genesisState == nil:
|
if checkpointState.slot == GENESIS_SLOT:
|
||||||
doAssert false
|
genesisState = checkpointState
|
||||||
|
tailState = checkpointState
|
||||||
|
tailBlock = get_initial_beacon_block(genesisState[])
|
||||||
else:
|
else:
|
||||||
if genesisState.slot != GENESIS_SLOT:
|
fatal "State checkpoints cannot be provided for a network without a known genesis state"
|
||||||
# TODO how to get a block from a non-genesis state?
|
|
||||||
error "Starting from non-genesis state not supported",
|
|
||||||
stateSlot = genesisState.slot,
|
|
||||||
stateRoot = hash_tree_root(genesisState[])
|
|
||||||
quit 1
|
quit 1
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
genesisState = newClone(SSZ.decode(genesisStateContents[], BeaconState))
|
||||||
|
except CatchableError as err:
|
||||||
|
raiseAssert "The baked-in state must be valid"
|
||||||
|
|
||||||
let tailBlock = get_initial_beacon_block(genesisState[])
|
if checkpointState != nil:
|
||||||
|
tailState = checkpointState
|
||||||
|
tailBlock = checkpointBlock
|
||||||
|
else:
|
||||||
|
tailState = genesisState
|
||||||
|
tailBlock = get_initial_beacon_block(genesisState[])
|
||||||
|
|
||||||
try:
|
try:
|
||||||
ChainDAGRef.preInit(db, genesisState[], tailBlock)
|
ChainDAGRef.preInit(db, genesisState[], tailState[], tailBlock)
|
||||||
doAssert ChainDAGRef.isInitialized(db), "preInit should have initialized db"
|
doAssert ChainDAGRef.isInitialized(db), "preInit should have initialized db"
|
||||||
except CatchableError as e:
|
except CatchableError as e:
|
||||||
error "Failed to initialize database", err = e.msg
|
error "Failed to initialize database", err = e.msg
|
||||||
quit 1
|
quit 1
|
||||||
|
|
||||||
if stateSnapshotContents != nil:
|
|
||||||
# The memory for the initial snapshot won't be needed anymore
|
|
||||||
stateSnapshotContents[] = ""
|
|
||||||
|
|
||||||
# TODO check that genesis given on command line (if any) matches database
|
# TODO check that genesis given on command line (if any) matches database
|
||||||
let
|
let
|
||||||
chainDagFlags = if conf.verifyFinalization: {verifyFinalization}
|
chainDagFlags = if conf.verifyFinalization: {verifyFinalization}
|
||||||
else: {}
|
else: {}
|
||||||
chainDag = init(ChainDAGRef, conf.runtimePreset, db, chainDagFlags)
|
chainDag = init(ChainDAGRef, conf.runtimePreset, db, chainDagFlags)
|
||||||
|
beaconClock = BeaconClock.init(chainDag.headState.data.data)
|
||||||
quarantine = QuarantineRef()
|
quarantine = QuarantineRef()
|
||||||
|
|
||||||
|
if conf.weakSubjectivityCheckpoint.isSome:
|
||||||
|
let
|
||||||
|
currentSlot = beaconClock.now.slotOrZero
|
||||||
|
isCheckpointStale = not is_within_weak_subjectivity_period(
|
||||||
|
currentSlot,
|
||||||
|
chainDag.headState.data.data,
|
||||||
|
conf.weakSubjectivityCheckpoint.get)
|
||||||
|
|
||||||
|
if isCheckpointStale:
|
||||||
|
error "Weak subjectivity checkpoint is stale",
|
||||||
|
currentSlot,
|
||||||
|
checkpoint = conf.weakSubjectivityCheckpoint.get,
|
||||||
|
headStateSlot = chainDag.headState.data.data.slot
|
||||||
|
quit 1
|
||||||
|
|
||||||
|
if checkpointState != nil:
|
||||||
|
chainDag.setTailState(checkpointState[], checkpointBlock)
|
||||||
|
|
||||||
if mainchainMonitor.isNil and
|
if mainchainMonitor.isNil and
|
||||||
conf.web3Url.len > 0 and
|
conf.web3Url.len > 0 and
|
||||||
conf.depositContractAddress.isSome:
|
conf.depositContractAddress.isSome:
|
||||||
@ -259,7 +266,7 @@ proc init*(T: type BeaconNode,
|
|||||||
attestationPool: attestationPool,
|
attestationPool: attestationPool,
|
||||||
exitPool: exitPool,
|
exitPool: exitPool,
|
||||||
mainchainMonitor: mainchainMonitor,
|
mainchainMonitor: mainchainMonitor,
|
||||||
beaconClock: BeaconClock.init(chainDag.headState.data.data),
|
beaconClock: beaconClock,
|
||||||
rpcServer: rpcServer,
|
rpcServer: rpcServer,
|
||||||
forkDigest: enrForkId.forkDigest,
|
forkDigest: enrForkId.forkDigest,
|
||||||
topicBeaconBlocks: topicBeaconBlocks,
|
topicBeaconBlocks: topicBeaconBlocks,
|
||||||
@ -649,8 +656,7 @@ proc startSyncManager(node: BeaconNode) =
|
|||||||
epoch.compute_start_slot_at_epoch()
|
epoch.compute_start_slot_at_epoch()
|
||||||
|
|
||||||
func getFirstSlotAtFinalizedEpoch(): Slot =
|
func getFirstSlotAtFinalizedEpoch(): Slot =
|
||||||
let fepoch = node.chainDag.headState.data.data.finalized_checkpoint.epoch
|
node.chainDag.finalizedHead.slot
|
||||||
compute_start_slot_at_epoch(fepoch)
|
|
||||||
|
|
||||||
proc scoreCheck(peer: Peer): bool =
|
proc scoreCheck(peer: Peer): bool =
|
||||||
if peer.score < PeerScoreLowLimit:
|
if peer.score < PeerScoreLowLimit:
|
||||||
@ -1091,7 +1097,7 @@ programMain:
|
|||||||
var
|
var
|
||||||
config = makeBannerAndConfig(clientId, BeaconNodeConf)
|
config = makeBannerAndConfig(clientId, BeaconNodeConf)
|
||||||
# This is ref so we can mutate it (to erase it) after the initial loading.
|
# This is ref so we can mutate it (to erase it) after the initial loading.
|
||||||
stateSnapshotContents: ref string
|
genesisStateContents: ref string
|
||||||
|
|
||||||
setupStdoutLogging(config.logLevel)
|
setupStdoutLogging(config.logLevel)
|
||||||
|
|
||||||
@ -1110,8 +1116,8 @@ programMain:
|
|||||||
for node in metadata.bootstrapNodes:
|
for node in metadata.bootstrapNodes:
|
||||||
config.bootstrapNodes.add node
|
config.bootstrapNodes.add node
|
||||||
|
|
||||||
if config.stateSnapshot.isNone and metadata.genesisData.len > 0:
|
if metadata.genesisData.len > 0:
|
||||||
stateSnapshotContents = newClone metadata.genesisData
|
genesisStateContents = newClone metadata.genesisData
|
||||||
|
|
||||||
template checkForIncompatibleOption(flagName, fieldName) =
|
template checkForIncompatibleOption(flagName, fieldName) =
|
||||||
# TODO: This will have to be reworked slightly when we introduce config files.
|
# TODO: This will have to be reworked slightly when we introduce config files.
|
||||||
@ -1224,9 +1230,11 @@ programMain:
|
|||||||
address = metricsAddress, port = config.metricsPort
|
address = metricsAddress, port = config.metricsPort
|
||||||
metrics.startHttpServer($metricsAddress, config.metricsPort)
|
metrics.startHttpServer($metricsAddress, config.metricsPort)
|
||||||
|
|
||||||
var node = waitFor BeaconNode.init(rng, config, stateSnapshotContents)
|
var node = waitFor BeaconNode.init(rng, config, genesisStateContents)
|
||||||
if bnStatus == BeaconNodeStatus.Stopping:
|
if bnStatus == BeaconNodeStatus.Stopping:
|
||||||
return
|
return
|
||||||
|
# The memory for the initial snapshot won't be needed anymore
|
||||||
|
if genesisStateContents != nil: genesisStateContents[] = ""
|
||||||
|
|
||||||
when hasPrompt:
|
when hasPrompt:
|
||||||
initPrompt(node)
|
initPrompt(node)
|
||||||
|
@ -207,6 +207,12 @@ func shortLog*(v: BlockRef): string =
|
|||||||
else:
|
else:
|
||||||
&"{v.root.data.toOpenArray(0, 3).toHex()}:{v.slot}"
|
&"{v.root.data.toOpenArray(0, 3).toHex()}:{v.slot}"
|
||||||
|
|
||||||
|
func shortLog*(v: EpochRef): string =
|
||||||
|
if v == nil:
|
||||||
|
"EpochRef(nil)"
|
||||||
|
else:
|
||||||
|
&"(epoch ref: {v.epoch})"
|
||||||
|
|
||||||
chronicles.formatIt BlockSlot: shortLog(it)
|
chronicles.formatIt BlockSlot: shortLog(it)
|
||||||
chronicles.formatIt BlockRef: shortLog(it)
|
chronicles.formatIt BlockRef: shortLog(it)
|
||||||
|
|
||||||
|
@ -383,8 +383,12 @@ proc init*(T: type ChainDAGRef,
|
|||||||
# state we loaded might be older than head block - nonetheless, it will be
|
# state we loaded might be older than head block - nonetheless, it will be
|
||||||
# from the same epoch as the head, thus the finalized and justified slots are
|
# from the same epoch as the head, thus the finalized and justified slots are
|
||||||
# the same - these only change on epoch boundaries.
|
# the same - these only change on epoch boundaries.
|
||||||
res.finalizedHead = headRef.atEpochStart(
|
# When we start from a snapshot state, the `finalized_checkpoint` in the
|
||||||
res.headState.data.data.finalized_checkpoint.epoch)
|
# snapshot will point to an even older state, but we trust the tail state
|
||||||
|
# (the snapshot) to be finalized, hence the `max` expression below.
|
||||||
|
let finalizedEpoch = max(res.headState.data.data.finalized_checkpoint.epoch,
|
||||||
|
tailRef.slot.epoch)
|
||||||
|
res.finalizedHead = headRef.atEpochStart(finalizedEpoch)
|
||||||
|
|
||||||
res.clearanceState = res.headState
|
res.clearanceState = res.headState
|
||||||
|
|
||||||
@ -398,6 +402,7 @@ proc init*(T: type ChainDAGRef,
|
|||||||
|
|
||||||
proc findEpochRef*(blck: BlockRef, epoch: Epoch): EpochRef = # may return nil!
|
proc findEpochRef*(blck: BlockRef, epoch: Epoch): EpochRef = # may return nil!
|
||||||
let ancestor = blck.epochAncestor(epoch)
|
let ancestor = blck.epochAncestor(epoch)
|
||||||
|
doAssert ancestor.blck != nil
|
||||||
for epochRef in ancestor.blck.epochRefs:
|
for epochRef in ancestor.blck.epochRefs:
|
||||||
if epochRef.epoch == epoch:
|
if epochRef.epoch == epoch:
|
||||||
return epochRef
|
return epochRef
|
||||||
@ -415,7 +420,8 @@ proc getEpochRef*(dag: ChainDAGRef, blck: BlockRef, epoch: Epoch): EpochRef =
|
|||||||
|
|
||||||
dag.withState(dag.tmpState, ancestor):
|
dag.withState(dag.tmpState, ancestor):
|
||||||
let
|
let
|
||||||
prevEpochRef = blck.findEpochRef(epoch - 1)
|
prevEpochRef = if dag.tail.slot.epoch >= epoch: nil
|
||||||
|
else: blck.findEpochRef(epoch - 1)
|
||||||
newEpochRef = EpochRef.init(state, cache, prevEpochRef)
|
newEpochRef = EpochRef.init(state, cache, prevEpochRef)
|
||||||
|
|
||||||
# TODO consider constraining the number of epochrefs per state
|
# TODO consider constraining the number of epochrefs per state
|
||||||
@ -512,14 +518,18 @@ func getBlockRange*(
|
|||||||
## at this index.
|
## at this index.
|
||||||
##
|
##
|
||||||
## If there were no blocks in the range, `output.len` will be returned.
|
## If there were no blocks in the range, `output.len` will be returned.
|
||||||
let requestedCount = output.lenu64
|
let
|
||||||
|
requestedCount = output.lenu64
|
||||||
|
headSlot = dag.head.slot
|
||||||
|
|
||||||
trace "getBlockRange entered",
|
trace "getBlockRange entered",
|
||||||
head = shortLog(dag.head.root), requestedCount, startSlot, skipStep
|
head = shortLog(dag.head.root), requestedCount, startSlot, skipStep, headSlot
|
||||||
|
|
||||||
|
if startSlot < dag.tail.slot or headSlot <= startSlot:
|
||||||
|
return output.len # Identical to returning an empty set of block as indicated above
|
||||||
|
|
||||||
let
|
let
|
||||||
headSlot = dag.head.slot
|
runway = uint64(headSlot - startSlot)
|
||||||
runway = if headSlot > startSlot: uint64(headSlot - startSlot)
|
|
||||||
else: return output.len # Identical to returning an empty set of block as indicated above
|
|
||||||
skipStep = max(skipStep, 1) # Treat 0 step as 1
|
skipStep = max(skipStep, 1) # Treat 0 step as 1
|
||||||
count = min(1'u64 + (runway div skipStep), requestedCount)
|
count = min(1'u64 + (runway div skipStep), requestedCount)
|
||||||
endSlot = startSlot + count * skipStep
|
endSlot = startSlot + count * skipStep
|
||||||
@ -702,7 +712,7 @@ proc updateHead*(
|
|||||||
## blocks that were once considered potential candidates for a tree will
|
## blocks that were once considered potential candidates for a tree will
|
||||||
## now fall from grace, or no longer be considered resolved.
|
## now fall from grace, or no longer be considered resolved.
|
||||||
doAssert not newHead.isNil()
|
doAssert not newHead.isNil()
|
||||||
doAssert not newHead.parent.isNil() or newHead.slot == 0
|
doAssert not newHead.parent.isNil() or newHead.slot <= dag.tail.slot
|
||||||
logScope:
|
logScope:
|
||||||
newHead = shortLog(newHead)
|
newHead = shortLog(newHead)
|
||||||
|
|
||||||
@ -843,25 +853,56 @@ proc isInitialized*(T: type ChainDAGRef, db: BeaconChainDB): bool =
|
|||||||
true
|
true
|
||||||
|
|
||||||
proc preInit*(
|
proc preInit*(
|
||||||
T: type ChainDAGRef, db: BeaconChainDB, state: BeaconState,
|
T: type ChainDAGRef, db: BeaconChainDB,
|
||||||
signedBlock: SignedBeaconBlock) =
|
genesisState, tailState: BeaconState, tailBlock: SignedBeaconBlock) =
|
||||||
# write a genesis state, the way the ChainDAGRef expects it to be stored in
|
# write a genesis state, the way the ChainDAGRef expects it to be stored in
|
||||||
# database
|
# database
|
||||||
# TODO probably should just init a block pool with the freshly written
|
# TODO probably should just init a block pool with the freshly written
|
||||||
# state - but there's more refactoring needed to make it nice - doing
|
# state - but there's more refactoring needed to make it nice - doing
|
||||||
# a minimal patch for now..
|
# a minimal patch for now..
|
||||||
doAssert signedBlock.message.state_root == hash_tree_root(state)
|
doAssert tailBlock.message.state_root == hash_tree_root(tailState)
|
||||||
notice "New database from snapshot",
|
notice "New database from snapshot",
|
||||||
blockRoot = shortLog(signedBlock.root),
|
blockRoot = shortLog(tailBlock.root),
|
||||||
stateRoot = shortLog(signedBlock.message.state_root),
|
stateRoot = shortLog(tailBlock.message.state_root),
|
||||||
fork = state.fork,
|
fork = tailState.fork,
|
||||||
validators = state.validators.len()
|
validators = tailState.validators.len()
|
||||||
|
|
||||||
db.putState(state)
|
db.putState(tailState)
|
||||||
db.putBlock(signedBlock)
|
db.putBlock(tailBlock)
|
||||||
db.putTailBlock(signedBlock.root)
|
db.putTailBlock(tailBlock.root)
|
||||||
db.putHeadBlock(signedBlock.root)
|
db.putHeadBlock(tailBlock.root)
|
||||||
db.putStateRoot(signedBlock.root, state.slot, signedBlock.message.state_root)
|
db.putStateRoot(tailBlock.root, tailState.slot, tailBlock.message.state_root)
|
||||||
|
|
||||||
|
if tailState.slot == GENESIS_SLOT:
|
||||||
|
db.putGenesisBlockRoot(tailBlock.root)
|
||||||
|
else:
|
||||||
|
doAssert genesisState.slot == GENESIS_SLOT
|
||||||
|
db.putState(genesisState)
|
||||||
|
let genesisBlock = get_initial_beacon_block(genesisState)
|
||||||
|
db.putBlock(genesisBlock)
|
||||||
|
db.putStateRoot(genesisBlock.root, GENESIS_SLOT, genesisBlock.message.state_root)
|
||||||
|
db.putGenesisBlockRoot(genesisBlock.root)
|
||||||
|
|
||||||
|
proc setTailState*(dag: ChainDAGRef,
|
||||||
|
checkpointState: BeaconState,
|
||||||
|
checkpointBlock: SignedBeaconBlock) =
|
||||||
|
# TODO
|
||||||
|
# Delete all records up to the tail node. If the tail node is not
|
||||||
|
# in the database, init the dabase in a way similar to `preInit`.
|
||||||
|
discard
|
||||||
|
|
||||||
|
proc getGenesisBlockData*(dag: ChainDAGRef): BlockData =
|
||||||
|
let
|
||||||
|
genesisBlockRoot = dag.db.getGenesisBlockRoot()
|
||||||
|
# The database should be seeded with the genesis block root by `preInit`:
|
||||||
|
genesisBlock = dag.db.getBlock(genesisBlockRoot).get()
|
||||||
|
|
||||||
|
BlockData(data: genesisBlock,
|
||||||
|
refs: BlockRef(root: genesisBlockRoot, slot: GENESIS_SLOT))
|
||||||
|
|
||||||
|
proc getGenesisBlockSlot*(dag: ChainDAGRef): BlockSlot =
|
||||||
|
let blockData = dag.getGenesisBlockData()
|
||||||
|
BlockSlot(blck: blockData.refs, slot: GENESIS_SLOT)
|
||||||
|
|
||||||
proc getProposer*(
|
proc getProposer*(
|
||||||
dag: ChainDAGRef, head: BlockRef, slot: Slot):
|
dag: ChainDAGRef, head: BlockRef, slot: Slot):
|
||||||
|
@ -4,10 +4,10 @@ import
|
|||||||
os, options, unicode,
|
os, options, unicode,
|
||||||
chronicles, chronicles/options as chroniclesOptions,
|
chronicles, chronicles/options as chroniclesOptions,
|
||||||
confutils, confutils/defs, confutils/std/net, stew/shims/net as stewNet,
|
confutils, confutils/defs, confutils/std/net, stew/shims/net as stewNet,
|
||||||
unicodedb/properties, normalize,
|
stew/io2, unicodedb/properties, normalize,
|
||||||
json_serialization, web3/[ethtypes, confutils_defs],
|
json_serialization, web3/[ethtypes, confutils_defs],
|
||||||
spec/[crypto, keystore, digest, datatypes, network], network_metadata,
|
spec/[crypto, keystore, digest, datatypes, network, weak_subjectivity],
|
||||||
stew/io2
|
network_metadata
|
||||||
|
|
||||||
export
|
export
|
||||||
defaultEth2TcpPort, enabledLogLevel, ValidIpAddress,
|
defaultEth2TcpPort, enabledLogLevel, ValidIpAddress,
|
||||||
@ -142,13 +142,17 @@ type
|
|||||||
abbr: "v"
|
abbr: "v"
|
||||||
name: "validator" }: seq[ValidatorKeyPath]
|
name: "validator" }: seq[ValidatorKeyPath]
|
||||||
|
|
||||||
stateSnapshot* {.
|
weakSubjectivityCheckpoint* {.
|
||||||
desc: "SSZ file specifying a recent state snapshot"
|
desc: "Weak subjectivity checkpoint in the format block_root:epoch_number"
|
||||||
abbr: "s"
|
name: "weak-subjectivity-checkpoint" }: Option[WeakSubjectivityCheckpoint]
|
||||||
name: "state-snapshot" }: Option[InputFile]
|
|
||||||
|
|
||||||
stateSnapshotContents* {.hidden.}: ref string
|
finalizedCheckpointState* {.
|
||||||
# This is ref so we can mutate it (to erase it) after the initial loading.
|
desc: "SSZ file specifying a recent finalized state"
|
||||||
|
name: "finalized-checkpoint-state" }: Option[InputFile]
|
||||||
|
|
||||||
|
finalizedCheckpointBlock* {.
|
||||||
|
desc: "SSZ file specifying a recent finalized block"
|
||||||
|
name: "finalized-checkpoint-block" }: Option[InputFile]
|
||||||
|
|
||||||
runtimePreset* {.hidden.}: RuntimePreset
|
runtimePreset* {.hidden.}: RuntimePreset
|
||||||
|
|
||||||
@ -459,6 +463,13 @@ func parseCmdArg*(T: type GraffitiBytes, input: TaintedString): T
|
|||||||
func completeCmdArg*(T: type GraffitiBytes, input: TaintedString): seq[string] =
|
func completeCmdArg*(T: type GraffitiBytes, input: TaintedString): seq[string] =
|
||||||
return @[]
|
return @[]
|
||||||
|
|
||||||
|
func parseCmdArg*(T: type WeakSubjectivityCheckpoint, input: TaintedString): T
|
||||||
|
{.raises: [ValueError, Defect].} =
|
||||||
|
init(T, input)
|
||||||
|
|
||||||
|
func completeCmdArg*(T: type WeakSubjectivityCheckpoint, input: TaintedString): seq[string] =
|
||||||
|
return @[]
|
||||||
|
|
||||||
proc isPrintable(rune: Rune): bool =
|
proc isPrintable(rune: Rune): bool =
|
||||||
# This can be eventually replaced by the `unicodeplus` package, but a single
|
# This can be eventually replaced by the `unicodeplus` package, but a single
|
||||||
# proc does not justify the extra dependencies at the moment:
|
# proc does not justify the extra dependencies at the moment:
|
||||||
|
@ -304,15 +304,17 @@ func is_valid_genesis_state*(preset: RuntimePreset,
|
|||||||
return false
|
return false
|
||||||
true
|
true
|
||||||
|
|
||||||
|
func emptyBeaconBlockBody*(): BeaconBlockBody =
|
||||||
|
# TODO: This shouldn't be necessary if OpaqueBlob is the default
|
||||||
|
BeaconBlockBody(randao_reveal: ValidatorSig(kind: OpaqueBlob))
|
||||||
|
|
||||||
# TODO this is now a non-spec helper function, and it's not really accurate
|
# TODO this is now a non-spec helper function, and it's not really accurate
|
||||||
# so only usable/used in research/ and tests/
|
# so only usable/used in research/ and tests/
|
||||||
func get_initial_beacon_block*(state: BeaconState): SignedBeaconBlock =
|
func get_initial_beacon_block*(state: BeaconState): SignedBeaconBlock =
|
||||||
let message = BeaconBlock(
|
let message = BeaconBlock(
|
||||||
slot: GENESIS_SLOT,
|
slot: state.slot,
|
||||||
state_root: hash_tree_root(state),
|
state_root: hash_tree_root(state),
|
||||||
body: BeaconBlockBody(
|
body: emptyBeaconBlockBody())
|
||||||
# TODO: This shouldn't be necessary if OpaqueBlob is the default
|
|
||||||
randao_reveal: ValidatorSig(kind: OpaqueBlob)))
|
|
||||||
# parent_root, randao_reveal, eth1_data, signature, and body automatically
|
# parent_root, randao_reveal, eth1_data, signature, and body automatically
|
||||||
# initialized to default values.
|
# initialized to default values.
|
||||||
SignedBeaconBlock(message: message, root: hash_tree_root(message))
|
SignedBeaconBlock(message: message, root: hash_tree_root(message))
|
||||||
|
@ -64,6 +64,11 @@ func get_active_validator_indices*(state: BeaconState, epoch: Epoch):
|
|||||||
if is_active_validator(val, epoch):
|
if is_active_validator(val, epoch):
|
||||||
result.add idx.ValidatorIndex
|
result.add idx.ValidatorIndex
|
||||||
|
|
||||||
|
func get_active_validator_indices_len*(state: BeaconState, epoch: Epoch): uint64 =
|
||||||
|
for idx, val in state.validators:
|
||||||
|
if is_active_validator(val, epoch):
|
||||||
|
inc result
|
||||||
|
|
||||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.12.3/specs/phase0/beacon-chain.md#get_current_epoch
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.12.3/specs/phase0/beacon-chain.md#get_current_epoch
|
||||||
func get_current_epoch*(state: BeaconState): Epoch =
|
func get_current_epoch*(state: BeaconState): Epoch =
|
||||||
## Return the current epoch.
|
## Return the current epoch.
|
||||||
|
48
beacon_chain/spec/weak_subjectivity.nim
Normal file
48
beacon_chain/spec/weak_subjectivity.nim
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
import
|
||||||
|
strutils,
|
||||||
|
datatypes, digest, helpers
|
||||||
|
|
||||||
|
{.push raises: [Defect].}
|
||||||
|
|
||||||
|
const
|
||||||
|
SAFETY_DECAY* = 10'u64
|
||||||
|
|
||||||
|
type
|
||||||
|
WeakSubjectivityCheckpoint* = object
|
||||||
|
root*: Eth2Digest
|
||||||
|
epoch*: Epoch
|
||||||
|
|
||||||
|
func init*(T: type WeakSubjectivityCheckpoint, str: TaintedString): T
|
||||||
|
{.raises: [ValueError, Defect].} =
|
||||||
|
let sepIdx = find(str.string, ':')
|
||||||
|
if sepIdx == -1:
|
||||||
|
raise newException(ValueError,
|
||||||
|
"The weak subjectivity checkpoint must be provided in the `block_root:epoch_number` format")
|
||||||
|
T(root: Eth2Digest.fromHex(str[0 ..< sepIdx]),
|
||||||
|
epoch: parseBiggestUInt(str[sepIdx .. ^1]).Epoch)
|
||||||
|
|
||||||
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.12.3/specs/phase0/weak-subjectivity.md#calculating-the-weak-subjectivity-period
|
||||||
|
func compute_weak_subjectivity_period*(state: BeaconState): uint64 =
|
||||||
|
var weak_subjectivity_period = MIN_VALIDATOR_WITHDRAWABILITY_DELAY
|
||||||
|
let validator_count = get_active_validator_indices_len(state, get_current_epoch(state))
|
||||||
|
if validator_count >= MIN_PER_EPOCH_CHURN_LIMIT * CHURN_LIMIT_QUOTIENT:
|
||||||
|
weak_subjectivity_period += SAFETY_DECAY * CHURN_LIMIT_QUOTIENT div (2 * 100)
|
||||||
|
else:
|
||||||
|
weak_subjectivity_period += SAFETY_DECAY * validator_count div (2 * 100 * MIN_PER_EPOCH_CHURN_LIMIT)
|
||||||
|
return weak_subjectivity_period
|
||||||
|
|
||||||
|
# https://github.com/ethereum/eth2.0-specs/blob/v0.12.3/specs/phase0/weak-subjectivity.md#checking-for-stale-weak-subjectivity-checkpoint
|
||||||
|
func is_within_weak_subjectivity_period*(current_slot: Slot,
|
||||||
|
ws_state: BeaconState,
|
||||||
|
ws_checkpoint: WeakSubjectivityCheckpoint): bool =
|
||||||
|
# Clients may choose to validate the input state against the input Weak Subjectivity Checkpoint
|
||||||
|
doAssert ws_state.latest_block_header.state_root == ws_checkpoint.root
|
||||||
|
doAssert compute_epoch_at_slot(ws_state.slot) == ws_checkpoint.epoch
|
||||||
|
|
||||||
|
let
|
||||||
|
ws_period = compute_weak_subjectivity_period(ws_state)
|
||||||
|
ws_state_epoch = compute_epoch_at_slot(ws_state.slot)
|
||||||
|
current_epoch = compute_epoch_at_slot(current_slot)
|
||||||
|
|
||||||
|
current_epoch <= ws_state_epoch + ws_period
|
||||||
|
|
@ -142,7 +142,7 @@ proc getBlockDataFromBlockId(node: BeaconNode, blockId: string): BlockData =
|
|||||||
of "head":
|
of "head":
|
||||||
node.chainDag.get(node.chainDag.head)
|
node.chainDag.get(node.chainDag.head)
|
||||||
of "genesis":
|
of "genesis":
|
||||||
node.chainDag.get(node.chainDag.tail)
|
node.chainDag.getGenesisBlockData()
|
||||||
of "finalized":
|
of "finalized":
|
||||||
node.chainDag.get(node.chainDag.finalizedHead.blck)
|
node.chainDag.get(node.chainDag.finalizedHead.blck)
|
||||||
else:
|
else:
|
||||||
@ -163,7 +163,7 @@ proc stateIdToBlockSlot(node: BeaconNode, stateId: string): BlockSlot =
|
|||||||
of "head":
|
of "head":
|
||||||
node.chainDag.head.toBlockSlot()
|
node.chainDag.head.toBlockSlot()
|
||||||
of "genesis":
|
of "genesis":
|
||||||
node.chainDag.tail.toBlockSlot()
|
node.chainDag.getGenesisBlockSlot()
|
||||||
of "finalized":
|
of "finalized":
|
||||||
node.chainDag.finalizedHead
|
node.chainDag.finalizedHead
|
||||||
of "justified":
|
of "justified":
|
||||||
|
@ -54,7 +54,7 @@ cli do(slots = SLOTS_PER_EPOCH * 6,
|
|||||||
db = BeaconChainDB.init(kvStore SqStoreRef.init(".", "block_sim").tryGet())
|
db = BeaconChainDB.init(kvStore SqStoreRef.init(".", "block_sim").tryGet())
|
||||||
defer: db.close()
|
defer: db.close()
|
||||||
|
|
||||||
ChainDAGRef.preInit(db, state[].data, genesisBlock)
|
ChainDAGRef.preInit(db, state[].data, state[].data, genesisBlock)
|
||||||
|
|
||||||
var
|
var
|
||||||
chainDag = init(ChainDAGRef, defaultRuntimePreset, db)
|
chainDag = init(ChainDAGRef, defaultRuntimePreset, db)
|
||||||
|
@ -225,7 +225,7 @@ if [[ $USE_GANACHE == "0" ]]; then
|
|||||||
--insecure-netkey-password=true \
|
--insecure-netkey-password=true \
|
||||||
--genesis-offset=${GENESIS_OFFSET} # Delay in seconds
|
--genesis-offset=${GENESIS_OFFSET} # Delay in seconds
|
||||||
|
|
||||||
STATE_SNAPSHOT_ARG="--state-snapshot=${NETWORK_DIR}/genesis.ssz"
|
STATE_SNAPSHOT_ARG="--finalized-checkpoint-state=${NETWORK_DIR}/genesis.ssz"
|
||||||
else
|
else
|
||||||
echo "Launching ganache"
|
echo "Launching ganache"
|
||||||
ganache-cli --blockTime 17 --gasLimit 100000000 -e 100000 --verbose > "${DATA_DIR}/log_ganache.txt" 2>&1 &
|
ganache-cli --blockTime 17 --gasLimit 100000000 -e 100000 --verbose > "${DATA_DIR}/log_ganache.txt" 2>&1 &
|
||||||
|
@ -78,7 +78,7 @@ mkdir -p "$NODE_DATA_DIR/dump"
|
|||||||
|
|
||||||
SNAPSHOT_ARG=""
|
SNAPSHOT_ARG=""
|
||||||
if [ -f "${SNAPSHOT_FILE}" ]; then
|
if [ -f "${SNAPSHOT_FILE}" ]; then
|
||||||
SNAPSHOT_ARG="--state-snapshot=${SNAPSHOT_FILE}"
|
SNAPSHOT_ARG="--finalized-checkpoint-state=${SNAPSHOT_FILE}"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
cd "$NODE_DATA_DIR"
|
cd "$NODE_DATA_DIR"
|
||||||
|
@ -99,7 +99,7 @@ template timedTest*(name, body) =
|
|||||||
|
|
||||||
proc makeTestDB*(tailState: BeaconState, tailBlock: SignedBeaconBlock): BeaconChainDB =
|
proc makeTestDB*(tailState: BeaconState, tailBlock: SignedBeaconBlock): BeaconChainDB =
|
||||||
result = init(BeaconChainDB, kvStore MemStoreRef.init())
|
result = init(BeaconChainDB, kvStore MemStoreRef.init())
|
||||||
ChainDAGRef.preInit(result, tailState, tailBlock)
|
ChainDAGRef.preInit(result, tailState, tailState, tailBlock)
|
||||||
|
|
||||||
proc makeTestDB*(validators: Natural): BeaconChainDB =
|
proc makeTestDB*(validators: Natural): BeaconChainDB =
|
||||||
let
|
let
|
||||||
|
2
vendor/eth2-testnets
vendored
2
vendor/eth2-testnets
vendored
@ -1 +1 @@
|
|||||||
Subproject commit f04512f15adccfc01b68f15c6216d681d08a1f83
|
Subproject commit d4e434148b851ef5baecbd289ac9df6c4fb62d47
|
2
vendor/nim-eth
vendored
2
vendor/nim-eth
vendored
@ -1 +1 @@
|
|||||||
Subproject commit 3ddb498f2a41e1e470d780757faeedc0b8cb3a21
|
Subproject commit de2d43a7e7afb7b094ca251bdbbd58fbf47df031
|
Loading…
x
Reference in New Issue
Block a user