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

View File

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

View File

@ -126,18 +126,15 @@ iterator realizePendingCheckpoints*(
self.currentEpochTips.clear()
# 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 =
var total_balance: uint64
for balance in validatorBalances:
total_balance += balance
let committee_weight = total_balance div SLOTS_PER_EPOCH
func calculateProposerBoost(justifiedTotalActiveBalance: Gwei): Gwei =
let committee_weight = justifiedTotalActiveBalance div SLOTS_PER_EPOCH
(committee_weight * PROPOSER_SCORE_BOOST) div 100
func applyScoreChanges*(self: var ProtoArray,
deltas: var openArray[Delta],
currentEpoch: Epoch,
checkpoints: FinalityCheckpoints,
newBalances: openArray[Gwei],
justifiedTotalActiveBalance: Gwei,
proposerBoostRoot: Eth2Digest): FcResult[void] =
## Iterate backwards through the array, touching all nodes and their parents
## and potentially the best-child of each parent.
@ -169,7 +166,7 @@ func applyScoreChanges*(self: var ProtoArray,
self.nodes.buf[nodePhysicalIdx]
# 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`
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
if (not proposerBoostRoot.isZero) and proposerBoostRoot == node.bid.root:
proposerBoostScore = calculateProposerBoost(newBalances)
proposerBoostScore = calculateProposerBoost(justifiedTotalActiveBalance)
if nodeDelta >= 0 and
high(Delta) - nodeDelta < proposerBoostScore.int64:
return err ForkChoiceError(