Skip to content

Commit ee79c10

Browse files
authored
update validator key cache on startup (#2760)
* update validator key cache on startup Versions prior to 1.1.0 do not write a validator key cache at all. Versions from 1.4.0 and upwards require an immutable validator key cache to verify blocks - normally, block verification fills the cache but that assumes that at least one block was verified by a version that has the key cache. Taken together, this breaks direct upgrades from anything <1.1.0 to 1.4.0. The fix is simply to refresh fill the cache from an existing state on startup. * also log serious block validation failures at info level
1 parent 07a1c57 commit ee79c10

File tree

4 files changed

+67
-6
lines changed

4 files changed

+67
-6
lines changed

beacon_chain/beacon_chain_db.nim

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -799,3 +799,15 @@ iterator getAncestorSummaries*(db: BeaconChainDB, root: Eth2Digest):
799799
newSummaries.add(res)
800800

801801
res.root = res.summary.parent_root
802+
803+
# Test operations used to create broken and/or legacy database
804+
805+
proc putStateV0*(db: BeaconChainDB, key: Eth2Digest, value: BeaconState) =
806+
# Writes to KVStore, as done in 1.0.12 and earlier
807+
db.v0.backend.putSnappySSZ(subkey(type value, key), value)
808+
809+
proc putBlockV0*(db: BeaconChainDB, value: TrustedSignedBeaconBlock) =
810+
# Write to KVStore, as done in 1.0.12 and earlier
811+
# In particular, no summary is written here - it should be recreated
812+
# automatically
813+
db.v0.backend.putSnappySSZ(subkey(SignedBeaconBlock, value.root), value)

beacon_chain/consensus_object_pools/block_clearance.nim

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -205,11 +205,15 @@ proc addRawBlockKnownParent(
205205
onBlockAdded: OnBlockAdded
206206
): Result[BlockRef, (ValidationResult, BlockError)] =
207207
## Add a block whose parent is known, after performing validity checks
208+
logScope:
209+
blck = shortLog(signedBlock.message)
210+
blockRoot = shortLog(signedBlock.root)
211+
signature = shortLog(signedBlock.signature)
208212

209213
if parent.slot >= signedBlock.message.slot:
210214
# A block whose parent is newer than the block itself is clearly invalid -
211215
# discard it immediately
212-
debug "Invalid block slot",
216+
info "Block with invalid parent, dropping",
213217
parentBlock = shortLog(parent)
214218

215219
return err((ValidationResult.Reject, Invalid))
@@ -225,7 +229,7 @@ proc addRawBlockKnownParent(
225229
# correct - from their point of view, the head block they have is the
226230
# latest thing that happened on the chain and they're performing their
227231
# duty correctly.
228-
debug "Unviable block, dropping",
232+
info "Unviable block, dropping",
229233
finalizedHead = shortLog(dag.finalizedHead),
230234
tail = shortLog(dag.tail)
231235

@@ -249,11 +253,15 @@ proc addRawBlockKnownParent(
249253
# TODO: remove skipBLSValidation
250254

251255
var sigs: seq[SignatureSet]
252-
if sigs.collectSignatureSets(
253-
signedBlock, dag.db.immutableValidators, dag.clearanceState, cache).isErr():
256+
if (let e = sigs.collectSignatureSets(
257+
signedBlock, dag.db.immutableValidators, dag.clearanceState, cache); e.isErr()):
258+
info "Unable to load signature sets",
259+
err = e.error()
260+
254261
# A PublicKey or Signature isn't on the BLS12-381 curve
255262
return err((ValidationResult.Reject, Invalid))
256263
if not quarantine.batchVerify(sigs):
264+
info "Block signature verification failed"
257265
return err((ValidationResult.Reject, Invalid))
258266

259267
let sigVerifyTick = Moment.now()

beacon_chain/consensus_object_pools/blockchain_dag.nim

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -440,6 +440,10 @@ proc init*(T: type ChainDAGRef,
440440
# Pruning metadata
441441
dag.lastPrunePoint = dag.finalizedHead
442442

443+
# Fill validator key cache in case we're loading an old database that doesn't
444+
# have a cache
445+
dag.updateValidatorKeys(getStateField(dag.headState, validators).asSeq())
446+
443447
info "Block dag initialized",
444448
head = shortLog(headRef),
445449
finalizedHead = shortLog(dag.finalizedHead),

tests/test_block_pool.nim

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,10 @@ import
1212
unittest2,
1313
stew/assign2,
1414
eth/keys,
15-
../beacon_chain/spec/[datatypes, digest, helpers, state_transition, presets],
15+
../beacon_chain/spec/[
16+
beaconstate, datatypes, digest, helpers, state_transition, presets],
1617
../beacon_chain/beacon_node_types,
17-
../beacon_chain/[beacon_chain_db, ssz],
18+
../beacon_chain/[beacon_chain_db, ssz, extras],
1819
../beacon_chain/consensus_object_pools/[
1920
blockchain_dag, block_quarantine, block_clearance, statedata_helpers],
2021
./testutil, ./testdbutil, ./testblockutil
@@ -523,3 +524,39 @@ suite "chain DAG finalization tests" & preset():
523524
dag2.finalizedHead.blck.root == dag.finalizedHead.blck.root
524525
dag2.finalizedHead.slot == dag.finalizedHead.slot
525526
hash_tree_root(dag2.headState) == hash_tree_root(dag.headState)
527+
528+
suite "Old database versions" & preset():
529+
setup:
530+
let
531+
genState = initialize_beacon_state_from_eth1(
532+
defaultRuntimePreset,
533+
Eth2Digest(),
534+
0,
535+
makeInitialDeposits(SLOTS_PER_EPOCH.uint64, flags = {skipBlsValidation}),
536+
{skipBlsValidation})
537+
genBlock = get_initial_beacon_block(genState[])
538+
quarantine = QuarantineRef.init(keys.newRng())
539+
540+
test "pre-1.1.0":
541+
# only kvstore, no immutable validator keys
542+
543+
let db = BeaconChainDB.new(defaultRuntimePreset, "", inMemory = true)
544+
545+
# preInit a database to a v1.0.12 state
546+
db.putStateV0(genBlock.message.state_root, genState[])
547+
db.putBlockV0(genBlock)
548+
db.putTailBlock(genBlock.root)
549+
db.putHeadBlock(genBlock.root)
550+
db.putStateRoot(genBlock.root, genState.slot, genBlock.message.state_root)
551+
db.putGenesisBlockRoot(genBlock.root)
552+
553+
var
554+
dag = init(ChainDAGRef, defaultRuntimePreset, db)
555+
state = newClone(dag.headState.data)
556+
cache = StateCache()
557+
att0 = makeFullAttestations(state[], dag.tail.root, 0.Slot, cache)
558+
b1 = addTestBlock(state[], dag.tail.root, cache, attestations = att0)
559+
b1Add = dag.addRawBlock(quarantine, b1, nil)
560+
561+
check:
562+
b1Add.isOk()

0 commit comments

Comments
 (0)