-
Notifications
You must be signed in to change notification settings - Fork 265
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Trusted node sync #3209
Merged
+542
−19
Merged
Trusted node sync #3209
Changes from 1 commit
Commits
Show all changes
3 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
fixes
* load genesis from network metadata * check checkpoint block root against state * fix invalid block root in rest json decoding * odds and ends
commit 63116d75147466026e20c72d9252395dc954647a
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,13 @@ | ||
# Copyright (c) 2018-2022 Status Research & Development GmbH | ||
# 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. | ||
|
||
{.push raises: [Defect].} | ||
|
||
import | ||
std/[os], | ||
|
||
stew/[assign2, base10], | ||
chronicles, chronos, | ||
./sync/sync_manager, | ||
|
@@ -9,8 +16,6 @@ import | |
./spec/[beaconstate, eth2_merkleization, forks, presets, state_transition], | ||
"."/[beacon_clock, beacon_chain_db] | ||
|
||
{.push raises: [Defect].} | ||
|
||
type | ||
DbCache = object | ||
summaries: Table[Eth2Digest, BeaconBlockSummary] | ||
|
@@ -22,7 +27,8 @@ const | |
proc updateSlots(cache: var DbCache, root: Eth2Digest, slot: Slot) = | ||
# The slots mapping stores one linear block history - we construct it by | ||
# starting from a given root/slot and walking the known parents as far back | ||
# as possible - this ensures that | ||
# as possible which ensures that all blocks belong to the same history | ||
|
||
if cache.slots.len() < slot.int + 1: | ||
cache.slots.setLen(slot.int + 1) | ||
|
||
|
@@ -57,14 +63,14 @@ proc update(cache: var DbCache, blck: ForkySignedBeaconBlock) = | |
cache.updateSlots(blck.root, blck.message.slot) | ||
|
||
proc isKnown(cache: DbCache, slot: Slot): bool = | ||
slot.int < cache.slots.len and cache.slots[slot.int].isSome() | ||
slot < cache.slots.lenu64 and cache.slots[slot.int].isSome() | ||
|
||
proc doTrustedNodeSync*( | ||
cfg: RuntimeConfig, databaseDir: string, restUrl: string, | ||
blockId: string, backfill: bool, | ||
genesisState: ref ForkedHashedBeaconState = nil) {.async.} = | ||
notice "Starting trusted node sync", | ||
databaseDir, restUrl, blockId | ||
databaseDir, restUrl, blockId, backfill | ||
|
||
let | ||
db = BeaconChainDB.new(databaseDir, inMemory = false) | ||
|
@@ -80,6 +86,7 @@ proc doTrustedNodeSync*( | |
# over in this case | ||
error "Database missing head block summary - database too old or corrupt" | ||
quit 1 | ||
|
||
let slot = dbCache.summaries[dbHead.get()].slot | ||
dbCache.updateSlots(dbHead.get(), slot) | ||
slot | ||
|
@@ -101,7 +108,7 @@ proc doTrustedNodeSync*( | |
warn "Retrying download of block", slot, err = exc.msg | ||
client = RestClientRef.new(restUrl).get() | ||
|
||
error "Unable to download block", | ||
error "Unable to download block - backfill incomplete, but will resume when you start the beacon node", | ||
slot, error = lastError.msg, url = client.address | ||
|
||
quit 1 | ||
|
@@ -131,7 +138,8 @@ proc doTrustedNodeSync*( | |
|
||
withState(genesisState[]): | ||
info "Writing genesis state", | ||
stateRoot = shortLog(state.root) | ||
stateRoot = shortLog(state.root), | ||
genesis_validators_root = shortLog(state.data.genesis_validators_root) | ||
|
||
db.putStateRoot(state.latest_block_root(), state.data.slot, state.root) | ||
db.putState(state.root, state.data) | ||
|
@@ -154,10 +162,11 @@ proc doTrustedNodeSync*( | |
error "Unable to download genesis header", | ||
error = exc.msg, restUrl | ||
quit 1 | ||
|
||
if genesisHeader.root != genesisRoot: | ||
error "Server genesis block root does not match local genesis", | ||
localGenesis = shortLog(genesisRoot), | ||
remoteGenesis = shortLog(genesisHeader.root) | ||
localGenesisRoot = shortLog(genesisRoot), | ||
remoteGenesisRoot = shortLog(genesisHeader.root) | ||
quit 1 | ||
|
||
notice "Downloading checkpoint block", restUrl, blockId | ||
|
@@ -191,7 +200,7 @@ proc doTrustedNodeSync*( | |
checkpointSlot, checkpointRoot = shortLog(checkpointBlock.root), headSlot | ||
quit 1 | ||
|
||
if checkpointSlot.uint64 mod SLOTS_PER_EPOCH != 0: | ||
if not checkpointSlot.is_epoch(): | ||
# Else the ChainDAG logic might get messed up - this constraint could | ||
# potentially be avoided with appropriate refactoring | ||
error "Checkpoint block must fall on an epoch boundary", | ||
|
@@ -205,7 +214,7 @@ proc doTrustedNodeSync*( | |
dbCache.updateSlots(blck.root, blck.message.slot) | ||
|
||
else: | ||
notice "Downloading checkpoint state", restUrl, blockId, checkpointSlot | ||
notice "Downloading checkpoint state", restUrl, checkpointSlot | ||
|
||
let | ||
state = try: | ||
|
@@ -220,6 +229,15 @@ proc doTrustedNodeSync*( | |
quit 1 | ||
|
||
withState(state[]): | ||
let latest_block_root = state.latest_block_root | ||
|
||
if latest_block_root != checkpointBlock.root: | ||
error "Checkpoint state does not match checkpoint block, server error?", | ||
blockRoot = shortLog(checkpointBlock.root), | ||
blck = shortLog(checkpointBlock), | ||
stateBlockRoot = shortLog(latest_block_root) | ||
quit 1 | ||
|
||
info "Writing checkpoint state", | ||
stateRoot = shortLog(state.root) | ||
db.putStateRoot(state.latest_block_root(), state.data.slot, state.root) | ||
|
@@ -253,7 +271,7 @@ proc doTrustedNodeSync*( | |
if missingSlots == 0: | ||
info "Database fully backfilled" | ||
elif backfill: | ||
notice "Backfilling historical blocks", | ||
notice "Downloading historical blocks - you can interrupt this process at any time and it automatically be completed when you start the beacon node", | ||
checkpointSlot, missingSlots | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Will this part be mutualized with deferred backfill in #3263 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The backfill of 3263 picks up wherever this back fill is interrupted |
||
|
||
var # Same averaging as SyncManager | ||
|
@@ -262,6 +280,7 @@ proc doTrustedNodeSync*( | |
avgSyncSpeed = 0.0 | ||
stamp = SyncMoment.now(0) | ||
|
||
# Download several blocks in parallel but process them serially | ||
var gets: array[8, Future[Option[ForkedSignedBeaconBlock]]] | ||
proc processBlock(fut: Future[Option[ForkedSignedBeaconBlock]], slot: Slot) {.async.} = | ||
processed += 1 | ||
|
@@ -278,7 +297,7 @@ proc doTrustedNodeSync*( | |
|
||
var childSlot = blck.message.slot + 1 | ||
while true: | ||
if childSlot.int >= dbCache.slots.len(): | ||
if childSlot >= dbCache.slots.lenu64(): | ||
error "Downloaded block does not match checkpoint history" | ||
quit 1 | ||
|
||
|
@@ -332,23 +351,24 @@ proc doTrustedNodeSync*( | |
# Download blocks backwards from the checkpoint slot, skipping the ones we | ||
# already have in the database. We'll do a few downloads in parallel which | ||
# risks having some redundant downloads going on, but speeds things up | ||
for i in 0..checkpointSlot.int64 + gets.len(): | ||
if not isNil(gets[i mod gets.len]): | ||
await processBlock(gets[i mod gets.len], Slot( | ||
gets.len().int64 + checkpointSlot.int64 - i)) | ||
gets[i mod gets.len] = nil | ||
|
||
if i < checkpointSlot.int64: | ||
let slot = Slot(checkpointSlot.int64 - i) | ||
for i in 0'u64..<(checkpointSlot.uint64 + gets.lenu64()): | ||
if not isNil(gets[int(i mod gets.lenu64)]): | ||
await processBlock( | ||
gets[int(i mod gets.lenu64)], | ||
checkpointSlot + gets.lenu64() - uint64(i)) | ||
gets[int(i mod gets.lenu64)] = nil | ||
|
||
if i < checkpointSlot: | ||
let slot = checkpointSlot - i | ||
if dbCache.isKnown(slot): | ||
continue | ||
|
||
gets[i mod gets.len] = downloadBlock(slot) | ||
gets[int(i mod gets.lenu64)] = downloadBlock(slot) | ||
else: | ||
notice "Database initialized, historical blocks will be backfilled when starting the node", | ||
missingSlots | ||
|
||
notice "Done, your beacon node is ready to serve you! Don't forget to check that you're on the canoncial chain by comparing the checkpoint root with other online sources. See https://nimbus.guide/trusted-node-sync.html for more infromation.", | ||
notice "Done, your beacon node is ready to serve you! Don't forget to check that you're on the canoncial chain by comparing the checkpoint root with other online sources. See https://nimbus.guide/trusted-node-sync.html for more information.", | ||
checkpointRoot = checkpointBlock.root | ||
|
||
when isMainModule: | ||
|
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lenu64
would allow this to be somewhat type-saferThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
...and crash on the next line where
setLen(slot.int + 1)
happens :/There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I don't see a great approach to this. Maybe better to at least be consistent within these two lines, leave it as is.