Skip to content
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

Light forward sync mechanism #6515

Open
wants to merge 66 commits into
base: unstable
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
66 commits
Select commit Hold shift + click to select a range
4ed334d
Initial commit.
cheatfate Jul 16, 2024
51b95eb
Add hybrid syncing.
cheatfate Jul 16, 2024
55ec036
Compilation fixes.
cheatfate Jul 16, 2024
578be20
Cast custom event for our purposes.
cheatfate Jul 16, 2024
5054f60
Instantiate AsyncEventQueue properly.
cheatfate Jul 16, 2024
0ead347
Fix mistype.
cheatfate Jul 16, 2024
b7d15fe
Further research on optimistic updates.
cheatfate Jul 17, 2024
cd0abac
Fixing circular deps.
cheatfate Jul 22, 2024
943b443
Add backfilling.
cheatfate Jul 22, 2024
f6acc8f
Add block download feature.
cheatfate Jul 24, 2024
2b55aea
Add block store.
cheatfate Jul 24, 2024
ce8057d
Update backfill information before storing block.
cheatfate Jul 24, 2024
e604653
Use custom block verifier for backfilling sync.
cheatfate Jul 25, 2024
17e8208
Skip signature verification in backfilling.
cheatfate Jul 25, 2024
0f3d721
Add one more generic reload to storeBackfillBlock().
cheatfate Jul 25, 2024
b929a5a
Add block verification debugging statements.
cheatfate Jul 25, 2024
1781285
Add more debugging
cheatfate Jul 25, 2024
605dbf0
Do not use database for backfilling, part 1.
cheatfate Aug 5, 2024
a56e5bb
Fix for stash.
cheatfate Aug 5, 2024
69b722a
Stash fixes part 2.
cheatfate Aug 5, 2024
6414b83
Prepare for testing.
cheatfate Aug 5, 2024
afb7ba7
Fix assertion.
cheatfate Aug 5, 2024
0c45eb4
Fix post-restart syncing process.
cheatfate Aug 5, 2024
9149ac9
Update backfill loading log statement.
cheatfate Aug 6, 2024
958bf24
Add handling of Duplicates.
cheatfate Aug 6, 2024
2ad2c38
Fix store duration and block backfilled log statements.
cheatfate Aug 6, 2024
ca67672
Add proper syncing state log statement.
cheatfate Aug 6, 2024
5c3a961
Add snappy compression to beaconchain_file.
cheatfate Aug 7, 2024
946e8f2
Add blobs verification.
cheatfate Aug 7, 2024
43b24a4
Add `slot` number to file structure for easy navigation over stream o…
cheatfate Aug 7, 2024
3daf8f9
Change database filename.
cheatfate Aug 7, 2024
aeb0539
Fix structure size.
cheatfate Aug 7, 2024
865992e
Add more consistency properties.
cheatfate Aug 9, 2024
11f8efa
Fix checkRepair() issues.
cheatfate Aug 11, 2024
1260d54
Preparation to state rebuild process.
cheatfate Aug 14, 2024
a7d5066
Add plain & compressed size.
cheatfate Aug 14, 2024
76a5beb
Debugging snappy encode process.
cheatfate Aug 14, 2024
5627c80
Add one more debugging line.
cheatfate Aug 14, 2024
a4d68bb
Dump blocks.
cheatfate Aug 14, 2024
0364cd9
One more filedump.
cheatfate Aug 14, 2024
f141e58
Fix chunk corruption code.
cheatfate Aug 14, 2024
ce2973f
Fix detection issue.
cheatfate Aug 14, 2024
6eb7b4a
Some fixes in state rebuilding process.
cheatfate Aug 16, 2024
f30602e
Add more clearance steps.
cheatfate Aug 19, 2024
144067f
Move updateHead() back to block_processor.
cheatfate Aug 20, 2024
fee34fc
Fix compilation issues.
cheatfate Aug 20, 2024
3db4e8b
Make code more async friendly.
cheatfate Aug 21, 2024
c5dceca
Fix async issues.
cheatfate Aug 23, 2024
30386e9
Fix 8192 slots issue.
cheatfate Aug 26, 2024
8b8d15c
Fix Future double completion issue.
cheatfate Aug 28, 2024
34d7dca
Pass updateFlags to some of the core procedures.
cheatfate Aug 28, 2024
870b182
Fix tests.
cheatfate Aug 28, 2024
bc7d5d1
Improve initial sync handling mechanism.
cheatfate Aug 30, 2024
f98e566
Fix checkStateTransition() performance improvements.
cheatfate Aug 30, 2024
026c16e
Add some performance tuning and meters.
cheatfate Aug 30, 2024
b6d7fa7
Light client performance tuning.
cheatfate Sep 1, 2024
e270e15
Remove debugging statement.
cheatfate Sep 2, 2024
071d9e5
Use single file descriptor for blockchain file.
cheatfate Sep 4, 2024
ad88f10
Attempt to fix LC.
cheatfate Sep 5, 2024
a3f6b2a
Fix timeleft calculation when untrusted sync backfilling started righ…
cheatfate Sep 6, 2024
36d42ba
Workaround for `chronicles` + `results` `error` issue.
cheatfate Sep 9, 2024
92c59cc
Address review comments.
cheatfate Sep 9, 2024
159f1da
Address review comments part 2.
cheatfate Sep 10, 2024
99ad032
Address review comments part 1.
cheatfate Sep 19, 2024
30e69cd
Rebase and fix the issues.
cheatfate Sep 19, 2024
f3451ad
Address review comments part 3.
cheatfate Sep 19, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
941 changes: 941 additions & 0 deletions beacon_chain/beacon_chain_file.nim
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The general idea of downloading trust-minimized data into a separate location before processing it is great.

I wonder though, is there an overlap between this format and .era/.erb files?
And if no, is there an advantage of using a custom format over era?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ERA files is more about static storage of limited number of blocks + state, without any need to read it from the tail.
Current file and format is made with sense that we downloading files from the head to tail and applying from tail to head.

Large diffs are not rendered by default.

13 changes: 10 additions & 3 deletions beacon_chain/beacon_node.nim
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,12 @@ import
./el/el_manager,
./consensus_object_pools/[
blockchain_dag, blob_quarantine, block_quarantine, consensus_manager,
attestation_pool, sync_committee_msg_pool, validator_change_pool],
attestation_pool, sync_committee_msg_pool, validator_change_pool,
blockchain_list],
./spec/datatypes/[base, altair],
./spec/eth2_apis/dynamic_fee_recipients,
./sync/[sync_manager, request_manager],
./spec/signatures_batch,
./sync/[sync_manager, request_manager, sync_types],
./validators/[
action_tracker, message_router, validator_monitor, validator_pool,
keystore_management],
Expand All @@ -38,7 +40,7 @@ export
eth2_network, el_manager, request_manager, sync_manager,
eth2_processor, optimistic_processor, blockchain_dag, block_quarantine,
base, message_router, validator_monitor, validator_pool,
consensus_manager, dynamic_fee_recipients
consensus_manager, dynamic_fee_recipients, sync_types

type
EventBus* = object
Expand All @@ -57,6 +59,7 @@ type
RestVersioned[ForkedLightClientFinalityUpdate]]
optUpdateQueue*: AsyncEventQueue[
RestVersioned[ForkedLightClientOptimisticUpdate]]
optFinHeaderUpdateQueue*: AsyncEventQueue[ForkedLightClientHeader]

BeaconNode* = ref object
nickname*: string
Expand All @@ -71,6 +74,7 @@ type
.Raising([CancelledError])
lightClient*: LightClient
dag*: ChainDAGRef
list*: ChainListRef
quarantine*: ref Quarantine
blobQuarantine*: ref BlobQuarantine
attestationPool*: ref AttestationPool
Expand All @@ -87,8 +91,11 @@ type
requestManager*: RequestManager
syncManager*: SyncManager[Peer, PeerId]
backfiller*: SyncManager[Peer, PeerId]
untrustedManager*: SyncManager[Peer, PeerId]
syncOverseer*: SyncOverseerRef
genesisSnapshotContent*: string
processor*: ref Eth2Processor
batchVerifier*: ref BatchVerifier
blockProcessor*: ref BlockProcessor
consensusManager*: ref ConsensusManager
attachedValidatorBalanceTotal*: Gwei
Expand Down
15 changes: 12 additions & 3 deletions beacon_chain/beacon_node_light_client.nim
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,10 @@ proc initLightClient*(
getBeaconTime, optimisticHandler)

shouldInhibitSync = func(): bool =
if node.syncManager != nil:
not node.syncManager.inProgress # No LC sync needed if DAG is in sync
else:
if isNil(node.syncOverseer):
false
else:
not node.syncOverseer.syncInProgress # No LC sync needed if DAG is in sync
lightClient = createLightClient(
node.network, rng, config, cfg, forkDigests, getBeaconTime,
genesis_validators_root, LightClientFinalizationMode.Strict,
Expand Down Expand Up @@ -107,7 +107,16 @@ proc initLightClient*(
# The execution block hash is only available from Capella onward
info "Ignoring new LC optimistic header until Capella"

proc onFinalizedHeader(
lightClient: LightClient,
finalizedHeader: ForkedLightClientHeader) =
if not node.consensusManager[].shouldSyncOptimistically(node.currentSlot):
return

node.eventBus.optFinHeaderUpdateQueue.emit(finalizedHeader)

lightClient.onOptimisticHeader = onOptimisticHeader
lightClient.onFinalizedHeader = onFinalizedHeader
lightClient.trustedBlockRoot = config.trustedBlockRoot

elif config.trustedBlockRoot.isSome:
Expand Down
131 changes: 121 additions & 10 deletions beacon_chain/consensus_object_pools/block_clearance.nim
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,15 @@
{.push raises: [].}

import
std/sequtils,
chronicles,
results,
stew/assign2,
../spec/[
beaconstate, forks, signatures, signatures_batch,
state_transition, state_transition_epoch],
"."/[block_dag, blockchain_dag, blockchain_dag_light_client]

from ../spec/datatypes/capella import asSigVerified, asTrusted, shortLog
from ../spec/datatypes/deneb import asSigVerified, asTrusted, shortLog
"."/[block_pools_types, block_dag, blockchain_dag,
blockchain_dag_light_client]

export results, signatures_batch, block_dag, blockchain_dag

Expand Down Expand Up @@ -114,15 +113,18 @@ proc addResolvedHeadBlock(
blockRef

proc checkStateTransition(
dag: ChainDAGRef, signedBlock: ForkySigVerifiedSignedBeaconBlock,
cache: var StateCache): Result[void, VerifierError] =
dag: ChainDAGRef,
signedBlock: ForkySigVerifiedSignedBeaconBlock,
cache: var StateCache,
updateFlags: UpdateFlags,
): Result[void, VerifierError] =
## Ensure block can be applied on a state
func restore(v: var ForkedHashedBeaconState) =
assign(dag.clearanceState, dag.headState)

let res = state_transition_block(
dag.cfg, dag.clearanceState, signedBlock,
cache, dag.updateFlags, restore)
cache, updateFlags, restore)

if res.isErr():
info "Invalid block",
Expand Down Expand Up @@ -150,7 +152,8 @@ proc advanceClearanceState*(dag: ChainDAGRef) =
var
cache = StateCache()
info = ForkedEpochInfo()
dag.advanceSlots(dag.clearanceState, next, true, cache, info)
dag.advanceSlots(dag.clearanceState, next, true, cache, info,
dag.updateFlags)
debug "Prepared clearance state for next block",
next, updateStateDur = Moment.now() - startTick

Expand Down Expand Up @@ -267,7 +270,7 @@ proc addHeadBlockWithParent*(
# onto which we can apply the new block
let clearanceBlock = BlockSlotId.init(parent.bid, signedBlock.message.slot)
if not updateState(
dag, dag.clearanceState, clearanceBlock, true, cache):
dag, dag.clearanceState, clearanceBlock, true, cache, dag.updateFlags):
# We should never end up here - the parent must be a block no older than and
# rooted in the finalized checkpoint, hence we should always be able to
# load its corresponding state
Expand Down Expand Up @@ -297,7 +300,8 @@ proc addHeadBlockWithParent*(

let sigVerifyTick = Moment.now()

? checkStateTransition(dag, signedBlock.asSigVerified(), cache)
? checkStateTransition(dag, signedBlock.asSigVerified(), cache,
dag.updateFlags)

let stateVerifyTick = Moment.now()
# Careful, clearanceState.data has been updated but not blck - we need to
Expand Down Expand Up @@ -449,3 +453,110 @@ proc addBackfillBlock*(
putBlockDur = putBlockTick - sigVerifyTick

ok()

template BlockAdded(kind: static ConsensusFork): untyped =
when kind == ConsensusFork.Electra:
OnElectraBlockAdded
elif kind == ConsensusFork.Deneb:
OnDenebBlockAdded
elif kind == ConsensusFork.Capella:
OnCapellaBlockAdded
elif kind == ConsensusFork.Bellatrix:
OnBellatrixBlockAdded
elif kind == ConsensusFork.Altair:
OnAltairBlockAdded
elif kind == ConsensusFork.Phase0:
OnPhase0BlockAdded
else:
static: raiseAssert "Unreachable"

proc verifyBlockProposer*(
dag: ChainDAGRef,
verifier: var BatchVerifier,
blocks: openArray[ForkedSignedBeaconBlock]
): Result[void, string] =
var sigs: seq[SignatureSet]

? sigs.collectProposerSignatureSet(
blocks, dag.db.immutableValidators, dag.clearanceState)

if not verifier.batchVerify(sigs):
err("Block batch signature verification failed")
else:
ok()

proc addBackfillBlockData*(
dag: ChainDAGRef,
bdata: BlockData,
onStateUpdated: OnStateUpdated,
onBlockAdded: OnForkedBlockAdded
): Result[void, VerifierError] =
var cache = StateCache()

withBlck(bdata.blck):
let
parent = checkHeadBlock(dag, forkyBlck).valueOr:
if error == VerifierError.Duplicate:
return ok()
return err(error)
startTick = Moment.now()
parentBlock = dag.getForkedBlock(parent.bid.root).get()
trustedStateRoot =
withBlck(parentBlock):
forkyBlck.message.state_root
clearanceBlock = BlockSlotId.init(parent.bid, forkyBlck.message.slot)
updateFlags1 = dag.updateFlags + {skipLastStateRootCalculation}

if not updateState(dag, dag.clearanceState, clearanceBlock, true, cache,
updateFlags1):
error "Unable to load clearance state for parent block, " &
"database corrupt?", clearanceBlock = shortLog(clearanceBlock)
return err(VerifierError.MissingParent)

dag.clearanceState.setStateRoot(trustedStateRoot)

let proposerVerifyTick = Moment.now()

if not(isNil(onStateUpdated)):
? onStateUpdated(forkyBlck.message.slot)

let
stateDataTick = Moment.now()
updateFlags2 =
dag.updateFlags + {skipBlsValidation, skipStateRootValidation}

? checkStateTransition(dag, forkyBlck.asSigVerified(), cache, updateFlags2)

let stateVerifyTick = Moment.now()

if bdata.blob.isSome():
for blob in bdata.blob.get():
dag.db.putBlobSidecar(blob[])

type Trusted = typeof forkyBlck.asTrusted()

proc onBlockAddedHandler(
blckRef: BlockRef,
trustedBlock: Trusted,
epochRef: EpochRef,
unrealized: FinalityCheckpoints
) {.gcsafe, raises: [].} =
onBlockAdded(
blckRef,
ForkedTrustedSignedBeaconBlock.init(trustedBlock),
epochRef,
unrealized)

let blockHandler: BlockAdded(consensusFork) = onBlockAddedHandler

discard addResolvedHeadBlock(
cheatfate marked this conversation as resolved.
Show resolved Hide resolved
dag, dag.clearanceState,
forkyBlck.asTrusted(),
true,
parent, cache,
blockHandler,
proposerVerifyTick - startTick,
stateDataTick - proposerVerifyTick,
stateVerifyTick - stateDataTick)

ok()
13 changes: 12 additions & 1 deletion beacon_chain/consensus_object_pools/block_pools_types.nim
Original file line number Diff line number Diff line change
Expand Up @@ -285,7 +285,11 @@ type
# balances, as used in fork choice
effective_balances_bytes*: seq[byte]

OnBlockAdded[T: ForkyTrustedSignedBeaconBlock] = proc(
BlockData* = object
blck*: ForkedSignedBeaconBlock
blob*: Opt[BlobSidecars]

OnBlockAdded*[T: ForkyTrustedSignedBeaconBlock] = proc(
blckRef: BlockRef, blck: T, epochRef: EpochRef,
unrealized: FinalityCheckpoints) {.gcsafe, raises: [].}
OnPhase0BlockAdded* = OnBlockAdded[phase0.TrustedSignedBeaconBlock]
Expand All @@ -299,6 +303,13 @@ type
OnPhase0BlockAdded | OnAltairBlockAdded | OnBellatrixBlockAdded |
OnCapellaBlockAdded | OnDenebBlockAdded | OnElectraBlockAdded

OnForkedBlockAdded* = proc(
blckRef: BlockRef, blck: ForkedTrustedSignedBeaconBlock, epochRef: EpochRef,
unrealized: FinalityCheckpoints) {.gcsafe, raises: [].}

OnStateUpdated* = proc(
slot: Slot): Result[void, VerifierError] {.gcsafe, raises: [].}

HeadChangeInfoObject* = object
slot*: Slot
block_root* {.serializedFieldName: "block".}: Eth2Digest
Expand Down
Loading
Loading