From 3359d8a32d5e1dc7c115083f0743f7d39f127af3 Mon Sep 17 00:00:00 2001 From: Jacek Sieka Date: Thu, 11 Nov 2021 15:21:23 +0100 Subject: [PATCH 1/4] speed up epoch processing 6x+ This change above all helps contain long replay times on epoch change, reorg and deep history inspection via REST/RPC * most effective balances don't actually change due to MAX_EFFECTIVE_BALANCE * ditto for inactivity scores * avoid signature check for trusted sync aggregates pre: ``` ./ncli_db --db:mainnet_0/db bench --start-slot=-3200 All time are ms Average, StdDev, Min, Max, Samples, Test Validation is turned off meaning that no BLS operations are performed 3468.621, 0.000, 3468.621, 3468.621, 1, Initialize DB 0.357, 0.938, 0.171, 52.752, 3155, Load block from database 15691.471, 0.000, 15691.471, 15691.471, 1, Load state from database 6.100, 9.469, 0.033, 526.816, 3101, Advance slot, non-epoch 579.131, 9.523, 566.936, 610.328, 100, Advance slot, epoch 18.551, 16.317, 12.664, 136.668, 3155, Apply block, no slot processing 0.000, 0.000, 0.000, 0.000, 0, Database load 0.000, 0.000, 0.000, 0.000, 0, Database store ``` post: ``` Average, StdDev, Min, Max, Samples, Test Validation is turned off meaning that no BLS operations are performed 3488.541, 0.000, 3488.541, 3488.541, 1, Initialize DB 0.369, 1.123, 0.183, 63.208, 3155, Load block from database 13430.642, 0.000, 13430.642, 13430.642, 1, Load state from database 6.522, 1.721, 0.034, 36.708, 3101, Advance slot, non-epoch 89.074, 3.162, 83.573, 101.436, 100, Advance slot, epoch 18.325, 18.346, 13.005, 145.040, 3155, Apply block, no slot processing 0.000, 0.000, 0.000, 0.000, 0, Database load 0.000, 0.000, 0.000, 0.000, 0, Database store ``` --- beacon_chain/spec/datatypes/altair.nim | 10 ++++++-- beacon_chain/spec/datatypes/merge.nim | 4 +-- beacon_chain/spec/state_transition_block.nim | 27 ++++++++++---------- beacon_chain/spec/state_transition_epoch.nim | 13 +++++++--- 4 files changed, 33 insertions(+), 21 deletions(-) diff --git a/beacon_chain/spec/datatypes/altair.nim b/beacon_chain/spec/datatypes/altair.nim index ad5221484d..5f757c078d 100644 --- a/beacon_chain/spec/datatypes/altair.nim +++ b/beacon_chain/spec/datatypes/altair.nim @@ -83,6 +83,10 @@ type sync_committee_bits*: BitArray[SYNC_COMMITTEE_SIZE] sync_committee_signature*: ValidatorSig + TrustedSyncAggregate* = object + sync_committee_bits*: BitArray[SYNC_COMMITTEE_SIZE] + sync_committee_signature*: TrustedSig + # https://github.com/ethereum/consensus-specs/blob/v1.1.5/specs/altair/beacon-chain.md#synccommittee SyncCommittee* = object pubkeys*: HashArray[Limit SYNC_COMMITTEE_SIZE, ValidatorPubKey] @@ -369,7 +373,7 @@ type voluntary_exits*: List[TrustedSignedVoluntaryExit, Limit MAX_VOLUNTARY_EXITS] # [New in Altair] - sync_aggregate*: SyncAggregate + sync_aggregate*: SyncAggregate # TODO TrustedSyncAggregate after batching SyncnetBits* = BitArray[SYNC_COMMITTEE_SUBNET_COUNT] @@ -393,7 +397,7 @@ type voluntary_exits*: List[TrustedSignedVoluntaryExit, Limit MAX_VOLUNTARY_EXITS] # [New in Altair] - sync_aggregate*: SyncAggregate + sync_aggregate*: TrustedSyncAggregate # https://github.com/ethereum/consensus-specs/blob/v1.1.5/specs/phase0/beacon-chain.md#signedbeaconblock SignedBeaconBlock* = object @@ -431,6 +435,8 @@ type SomeBeaconBlock* = BeaconBlock | SigVerifiedBeaconBlock | TrustedBeaconBlock SomeBeaconBlockBody* = BeaconBlockBody | SigVerifiedBeaconBlockBody | TrustedBeaconBlockBody + SomeSyncAggregate* = SyncAggregate | TrustedSyncAggregate + SyncSubcommitteeIndex* = distinct uint8 IndexInSyncCommittee* = distinct uint16 diff --git a/beacon_chain/spec/datatypes/merge.nim b/beacon_chain/spec/datatypes/merge.nim index b422eb1848..e792d9e9ae 100644 --- a/beacon_chain/spec/datatypes/merge.nim +++ b/beacon_chain/spec/datatypes/merge.nim @@ -263,7 +263,7 @@ type attestations*: List[Attestation, Limit MAX_ATTESTATIONS] deposits*: List[Deposit, Limit MAX_DEPOSITS] voluntary_exits*: List[SignedVoluntaryExit, Limit MAX_VOLUNTARY_EXITS] - sync_aggregate*: SyncAggregate + sync_aggregate*: SyncAggregate # TODO TrustedSyncAggregate after batching # Execution execution_payload*: ExecutionPayload # [New in Merge] @@ -283,7 +283,7 @@ type attestations*: List[TrustedAttestation, Limit MAX_ATTESTATIONS] deposits*: List[Deposit, Limit MAX_DEPOSITS] voluntary_exits*: List[TrustedSignedVoluntaryExit, Limit MAX_VOLUNTARY_EXITS] - sync_aggregate*: SyncAggregate + sync_aggregate*: TrustedSyncAggregate # Execution execution_payload*: ExecutionPayload # [New in Merge] diff --git a/beacon_chain/spec/state_transition_block.nim b/beacon_chain/spec/state_transition_block.nim index f8c02f968b..e08e56d245 100644 --- a/beacon_chain/spec/state_transition_block.nim +++ b/beacon_chain/spec/state_transition_block.nim @@ -429,7 +429,7 @@ proc process_operations(cfg: RuntimeConfig, # https://github.com/ethereum/consensus-specs/blob/v1.1.0-alpha.6/specs/altair/beacon-chain.md#sync-committee-processing proc process_sync_aggregate*( state: var (altair.BeaconState | merge.BeaconState), - aggregate: SyncAggregate, total_active_balance: Gwei, cache: var StateCache): + aggregate: SomeSyncAggregate, total_active_balance: Gwei, cache: var StateCache): Result[void, cstring] {.nbench.} = # Verify sync committee aggregate signature signing over the previous slot # block root @@ -439,21 +439,22 @@ proc process_sync_aggregate*( domain = get_domain(state, DOMAIN_SYNC_COMMITTEE, compute_epoch_at_slot(previous_slot)) signing_root = compute_signing_root(get_block_root_at_slot(state, previous_slot), domain) - var participant_pubkeys: seq[ValidatorPubKey] - for i in 0 ..< committee_pubkeys.len: - if aggregate.sync_committee_bits[i]: - participant_pubkeys.add committee_pubkeys[i] + when not (aggregate.sync_committee_signature is TrustedSig): + var participant_pubkeys: seq[ValidatorPubKey] + for i in 0 ..< committee_pubkeys.len: + if aggregate.sync_committee_bits[i]: + participant_pubkeys.add committee_pubkeys[i] # p2p-interface message validators check for empty sync committees, so it # shouldn't run except as part of test suite. - if participant_pubkeys.len == 0 and - aggregate.sync_committee_signature != default(CookedSig).toValidatorSig(): - return err("process_sync_aggregate: empty sync aggregates need signature of point at infinity") - - # Empty participants allowed - if participant_pubkeys.len > 0 and not blsFastAggregateVerify( - participant_pubkeys, signing_root.data, aggregate.sync_committee_signature): - return err("process_sync_aggregate: invalid signature") + if participant_pubkeys.len == 0 and + aggregate.sync_committee_signature != default(CookedSig).toValidatorSig(): + return err("process_sync_aggregate: empty sync aggregates need signature of point at infinity") + + # Empty participants allowed + if participant_pubkeys.len > 0 and not blsFastAggregateVerify( + participant_pubkeys, signing_root.data, aggregate.sync_committee_signature): + return err("process_sync_aggregate: invalid signature") # Compute participant and proposer rewards let diff --git a/beacon_chain/spec/state_transition_epoch.nim b/beacon_chain/spec/state_transition_epoch.nim index 80ee29b816..789818a8af 100644 --- a/beacon_chain/spec/state_transition_epoch.nim +++ b/beacon_chain/spec/state_transition_epoch.nim @@ -870,10 +870,13 @@ func process_effective_balance_updates*(state: var ForkyBeaconState) {.nbench.} let effective_balance = state.validators.asSeq()[index].effective_balance if balance + DOWNWARD_THRESHOLD < effective_balance or effective_balance + UPWARD_THRESHOLD < balance: - state.validators[index].effective_balance = + let new_effective_balance = min( balance - balance mod EFFECTIVE_BALANCE_INCREMENT, MAX_EFFECTIVE_BALANCE) + # Protect against unnecessary cache invalidation + if new_effective_balance != effective_balance: + state.validators[index].effective_balance = new_effective_balance # https://github.com/ethereum/consensus-specs/blob/v1.1.5/specs/phase0/beacon-chain.md#slashings-balances-updates func process_slashings_reset*(state: var ForkyBeaconState) {.nbench.} = @@ -946,13 +949,13 @@ func process_inactivity_updates*( previous_epoch = get_previous_epoch(state) # get_eligible_validator_indices() not_in_inactivity_leak = not is_in_inactivity_leak(state) - state.inactivity_scores.clearCache() for index in 0'u64 ..< state.validators.lenu64: if not is_eligible_validator(info.validators[index]): continue # Increase the inactivity score of inactive validators - var inactivity_score = state.inactivity_scores.asSeq()[index] + let pre_inactivity_score = state.inactivity_scores.asSeq()[index] + var inactivity_score = pre_inactivity_score # TODO activeness already checked; remove redundant checks between # is_active_validator and is_unslashed_participating_index if is_unslashed_participating_index( @@ -964,7 +967,9 @@ func process_inactivity_updates*( # leak-free epoch if not_in_inactivity_leak: inactivity_score -= min(INACTIVITY_SCORE_RECOVERY_RATE.uint64, inactivity_score) - state.inactivity_scores.asSeq()[index] = inactivity_score + # Most inactivity scores remain at 0 - avoid invalidating cache + if pre_inactivity_score != inactivity_score: + state.inactivity_scores[index] = inactivity_score # https://github.com/ethereum/consensus-specs/blob/v1.1.5/specs/phase0/beacon-chain.md#epoch-processing proc process_epoch*( From 1e2568f036b67cfa95e383b6f28d518430b2d261 Mon Sep 17 00:00:00 2001 From: Jacek Sieka Date: Thu, 11 Nov 2021 15:56:32 +0100 Subject: [PATCH 2/4] Update beacon_chain/spec/state_transition_block.nim Co-authored-by: zah --- beacon_chain/spec/state_transition_block.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/beacon_chain/spec/state_transition_block.nim b/beacon_chain/spec/state_transition_block.nim index e08e56d245..b7fa3ca779 100644 --- a/beacon_chain/spec/state_transition_block.nim +++ b/beacon_chain/spec/state_transition_block.nim @@ -439,7 +439,7 @@ proc process_sync_aggregate*( domain = get_domain(state, DOMAIN_SYNC_COMMITTEE, compute_epoch_at_slot(previous_slot)) signing_root = compute_signing_root(get_block_root_at_slot(state, previous_slot), domain) - when not (aggregate.sync_committee_signature is TrustedSig): + when aggregate.sync_committee_signature isnot TrustedSig: var participant_pubkeys: seq[ValidatorPubKey] for i in 0 ..< committee_pubkeys.len: if aggregate.sync_committee_bits[i]: From 2ce185873ec2edff602229ebc2f3279979636cb7 Mon Sep 17 00:00:00 2001 From: Jacek Sieka Date: Thu, 11 Nov 2021 16:37:59 +0100 Subject: [PATCH 3/4] avoid copying validator data in accessor ``` 5291.227, 0.000, 5291.227, 5291.227, 1, Initialize DB 0.436, 0.928, 0.138, 51.438, 3155, Load block from database 11962.826, 0.000, 11962.826, 11962.826, 1, Load state from database 6.477, 1.675, 0.037, 34.174, 3101, Advance slot, non-epoch 76.633, 3.705, 71.106, 98.085, 100, Advance slot, epoch 18.301, 18.593, 13.208, 149.153, 3155, Apply block, no slot processing 0.000, 0.000, 0.000, 0.000, 0, Database load 0.000, 0.000, 0.000, 0.000, 0, Database store ``` --- beacon_chain/spec/datatypes/base.nim | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/beacon_chain/spec/datatypes/base.nim b/beacon_chain/spec/datatypes/base.nim index c09a687b09..88c56f7574 100644 --- a/beacon_chain/spec/datatypes/base.nim +++ b/beacon_chain/spec/datatypes/base.nim @@ -623,13 +623,13 @@ proc readValue*(reader: var JsonReader, value: var ForkDigest) static: doAssert high(int) >= high(int32) # `ValidatorIndex` seq handling. -func `[]`*[T](a: var seq[T], b: ValidatorIndex): var T = +template `[]`*[T](a: var seq[T], b: ValidatorIndex): var T = a[b.int] -func `[]=`*[T](a: var seq[T], b: ValidatorIndex, c: T) = +template `[]=`*[T](a: var seq[T], b: ValidatorIndex, c: T) = a[b.int] = c -func `[]`*[T](a: seq[T], b: ValidatorIndex): auto = +template `[]`*[T](a: seq[T], b: ValidatorIndex): auto = a[b.int] # `ValidatorIndex` Nim integration From 96b1500247c75d7ea0219072d1d6a36e53c7996c Mon Sep 17 00:00:00 2001 From: Jacek Sieka Date: Thu, 11 Nov 2021 16:52:25 +0100 Subject: [PATCH 4/4] work around compiler bug --- beacon_chain/spec/datatypes/base.nim | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/beacon_chain/spec/datatypes/base.nim b/beacon_chain/spec/datatypes/base.nim index 88c56f7574..c75e869e28 100644 --- a/beacon_chain/spec/datatypes/base.nim +++ b/beacon_chain/spec/datatypes/base.nim @@ -623,13 +623,10 @@ proc readValue*(reader: var JsonReader, value: var ForkDigest) static: doAssert high(int) >= high(int32) # `ValidatorIndex` seq handling. -template `[]`*[T](a: var seq[T], b: ValidatorIndex): var T = - a[b.int] - template `[]=`*[T](a: var seq[T], b: ValidatorIndex, c: T) = a[b.int] = c -template `[]`*[T](a: seq[T], b: ValidatorIndex): auto = +template `[]`*[T](a: seq[T], b: ValidatorIndex): auto = # Also var seq (!) a[b.int] # `ValidatorIndex` Nim integration