avoid repeated total active balance computation in fork choice (#5437)

We currently compute `justified_total_active_balance` inside
`calculateProposerBoost`, despite that sum already being known
in the `EpochRef` cache. Tracking `justified_total_active_balance`
whenever the justified checkpoint updates allows replacing the
repeated computation with a lookup, at minimal memory cost.
This commit is contained in:
Etan Kissling 2023-09-19 22:04:55 +02:00 committed by GitHub
parent ee75d45a8b
commit c1b43d166b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 12 additions and 10 deletions

View File

@ -73,6 +73,7 @@ proc init*(
version: version, version: version,
justified: BalanceCheckpoint( justified: BalanceCheckpoint(
checkpoint: checkpoint, checkpoint: checkpoint,
total_active_balance: epochRef.total_active_balance,
balances: epochRef.effective_balances), balances: epochRef.effective_balances),
finalized: checkpoint, finalized: checkpoint,
best_justified: checkpoint)) best_justified: checkpoint))
@ -98,6 +99,7 @@ proc update_justified(
store = self.justified.checkpoint, state = justified store = self.justified.checkpoint, state = justified
self.justified = BalanceCheckpoint( self.justified = BalanceCheckpoint(
checkpoint: Checkpoint(root: blck.root, epoch: epochRef.epoch), checkpoint: Checkpoint(root: blck.root, epoch: epochRef.epoch),
total_active_balance: epochRef.total_active_balance,
balances: epochRef.effective_balances) balances: epochRef.effective_balances)
proc update_justified( proc update_justified(
@ -319,10 +321,11 @@ proc process_block*(self: var ForkChoice,
ok() ok()
func find_head*( func find_head(
self: var ForkChoiceBackend, self: var ForkChoiceBackend,
current_epoch: Epoch, current_epoch: Epoch,
checkpoints: FinalityCheckpoints, checkpoints: FinalityCheckpoints,
justified_total_active_balance: Gwei,
justified_state_balances: seq[Gwei], justified_state_balances: seq[Gwei],
proposer_boost_root: Eth2Digest proposer_boost_root: Eth2Digest
): FcResult[Eth2Digest] = ): FcResult[Eth2Digest] =
@ -341,7 +344,7 @@ func find_head*(
# Apply score changes # Apply score changes
? self.proto_array.applyScoreChanges( ? self.proto_array.applyScoreChanges(
deltas, current_epoch, checkpoints, deltas, current_epoch, checkpoints,
justified_state_balances, proposer_boost_root) justified_total_active_balance, proposer_boost_root)
self.balances = justified_state_balances self.balances = justified_state_balances
@ -365,6 +368,7 @@ proc get_head*(self: var ForkChoice,
FinalityCheckpoints( FinalityCheckpoints(
justified: self.checkpoints.justified.checkpoint, justified: self.checkpoints.justified.checkpoint,
finalized: self.checkpoints.finalized), finalized: self.checkpoints.finalized),
self.checkpoints.justified.total_active_balance,
self.checkpoints.justified.balances, self.checkpoints.justified.balances,
self.checkpoints.proposer_boost_root) self.checkpoints.proposer_boost_root)

View File

@ -113,6 +113,7 @@ type
BalanceCheckpoint* = object BalanceCheckpoint* = object
checkpoint*: Checkpoint checkpoint*: Checkpoint
total_active_balance*: Gwei
balances*: seq[Gwei] balances*: seq[Gwei]
Checkpoints* = object Checkpoints* = object

View File

@ -126,18 +126,15 @@ iterator realizePendingCheckpoints*(
self.currentEpochTips.clear() self.currentEpochTips.clear()
# https://github.com/ethereum/consensus-specs/blob/v1.4.0-beta.1/specs/phase0/fork-choice.md#get_weight # https://github.com/ethereum/consensus-specs/blob/v1.4.0-beta.1/specs/phase0/fork-choice.md#get_weight
func calculateProposerBoost(validatorBalances: openArray[Gwei]): uint64 = func calculateProposerBoost(justifiedTotalActiveBalance: Gwei): Gwei =
var total_balance: uint64 let committee_weight = justifiedTotalActiveBalance div SLOTS_PER_EPOCH
for balance in validatorBalances:
total_balance += balance
let committee_weight = total_balance div SLOTS_PER_EPOCH
(committee_weight * PROPOSER_SCORE_BOOST) div 100 (committee_weight * PROPOSER_SCORE_BOOST) div 100
func applyScoreChanges*(self: var ProtoArray, func applyScoreChanges*(self: var ProtoArray,
deltas: var openArray[Delta], deltas: var openArray[Delta],
currentEpoch: Epoch, currentEpoch: Epoch,
checkpoints: FinalityCheckpoints, checkpoints: FinalityCheckpoints,
newBalances: openArray[Gwei], justifiedTotalActiveBalance: Gwei,
proposerBoostRoot: Eth2Digest): FcResult[void] = proposerBoostRoot: Eth2Digest): FcResult[void] =
## Iterate backwards through the array, touching all nodes and their parents ## Iterate backwards through the array, touching all nodes and their parents
## and potentially the best-child of each parent. ## and potentially the best-child of each parent.
@ -169,7 +166,7 @@ func applyScoreChanges*(self: var ProtoArray,
self.nodes.buf[nodePhysicalIdx] self.nodes.buf[nodePhysicalIdx]
# Default value, if not otherwise set in first node loop # Default value, if not otherwise set in first node loop
var proposerBoostScore: uint64 var proposerBoostScore: Gwei
# Iterate backwards through all the indices in `self.nodes` # Iterate backwards through all the indices in `self.nodes`
for nodePhysicalIdx in countdown(self.nodes.len - 1, 0): for nodePhysicalIdx in countdown(self.nodes.len - 1, 0):
@ -194,7 +191,7 @@ func applyScoreChanges*(self: var ProtoArray,
# #
# https://github.com/ethereum/consensus-specs/blob/v1.4.0-beta.1/specs/phase0/fork-choice.md#get_weight # https://github.com/ethereum/consensus-specs/blob/v1.4.0-beta.1/specs/phase0/fork-choice.md#get_weight
if (not proposerBoostRoot.isZero) and proposerBoostRoot == node.bid.root: if (not proposerBoostRoot.isZero) and proposerBoostRoot == node.bid.root:
proposerBoostScore = calculateProposerBoost(newBalances) proposerBoostScore = calculateProposerBoost(justifiedTotalActiveBalance)
if nodeDelta >= 0 and if nodeDelta >= 0 and
high(Delta) - nodeDelta < proposerBoostScore.int64: high(Delta) - nodeDelta < proposerBoostScore.int64:
return err ForkChoiceError( return err ForkChoiceError(