mirror of
https://github.com/status-im/nimbus-eth2.git
synced 2025-01-27 06:47:13 +00:00
Fix a reward calculation bug affecting Prater epoch 64781 (#3428)
To calculate the deltas correctly, the `process_inactivity_updates` function must be called before the rewards and penalties processing code in order to update the `inactivity_scores` field in the state. This would have required duplicating more logic from the spec in the ncli modules, so I've decided to pay the price of introducing a run-time copy of the state at each epoch which eliminates the need to duplicate logic (both for this fix and the previous one). Other changes: * Fixes for the read-only mode of the `BeaconChainDb` * Fix an uint64 underflow in the debug output procedure for printing balance deltas * Allow Bellatrix states in the reward computation helpers
This commit is contained in:
parent
7de3f00f35
commit
9c1ff78f84
@ -275,13 +275,15 @@ proc get*[T](s: DbSeq[T], idx: int64): T =
|
||||
let found = queryRes.expectDb()
|
||||
if not found: panic()
|
||||
|
||||
proc init*(T: type FinalizedBlocks, db: SqStoreRef, name: string): KvResult[T] =
|
||||
? db.exec("""
|
||||
CREATE TABLE IF NOT EXISTS """ & name & """(
|
||||
id INTEGER PRIMARY KEY,
|
||||
value BLOB NOT NULL
|
||||
);
|
||||
""")
|
||||
proc init*(T: type FinalizedBlocks, db: SqStoreRef, name: string,
|
||||
readOnly = false): KvResult[T] =
|
||||
if not readOnly:
|
||||
? db.exec("""
|
||||
CREATE TABLE IF NOT EXISTS """ & name & """(
|
||||
id INTEGER PRIMARY KEY,
|
||||
value BLOB NOT NULL
|
||||
);
|
||||
""")
|
||||
|
||||
let
|
||||
insertStmt = db.prepareStmt(
|
||||
|
@ -641,11 +641,11 @@ func get_flag_index_reward*(state: altair.BeaconState | bellatrix.BeaconState,
|
||||
|
||||
# https://github.com/ethereum/consensus-specs/blob/v1.1.9/specs/altair/beacon-chain.md#get_flag_index_deltas
|
||||
func get_unslashed_participating_increment*(
|
||||
info: altair.EpochInfo, flag_index: int): Gwei =
|
||||
info: altair.EpochInfo | bellatrix.BeaconState, flag_index: int): Gwei =
|
||||
info.balances.previous_epoch[flag_index] div EFFECTIVE_BALANCE_INCREMENT
|
||||
|
||||
# https://github.com/ethereum/consensus-specs/blob/v1.1.9/specs/altair/beacon-chain.md#get_flag_index_deltas
|
||||
func get_active_increments*(info: altair.EpochInfo): Gwei =
|
||||
func get_active_increments*(info: altair.EpochInfo | bellatrix.BeaconState): Gwei =
|
||||
info.balances.current_epoch div EFFECTIVE_BALANCE_INCREMENT
|
||||
|
||||
# https://github.com/ethereum/consensus-specs/blob/v1.1.9/specs/altair/beacon-chain.md#get_flag_index_deltas
|
||||
|
@ -141,81 +141,9 @@ func collectSlashings(
|
||||
validator[].get_slashing_penalty(
|
||||
adjusted_total_slashing_balance, total_balance).int64
|
||||
|
||||
func getFinalizedCheckpoint(state: ForkyBeaconState,
|
||||
total_active_balance,
|
||||
previous_epoch_target_balance,
|
||||
current_epoch_target_balance: Gwei):
|
||||
Checkpoint =
|
||||
if get_current_epoch(state) <= GENESIS_EPOCH + 1:
|
||||
return state.finalized_checkpoint
|
||||
|
||||
let
|
||||
current_epoch = get_current_epoch(state)
|
||||
old_previous_justified_checkpoint = state.previous_justified_checkpoint
|
||||
old_current_justified_checkpoint = state.current_justified_checkpoint
|
||||
|
||||
# https://github.com/ethereum/consensus-specs/blob/v1.1.9/specs/phase0/beacon-chain.md#misc
|
||||
const JUSTIFICATION_BITS_LENGTH = 4
|
||||
|
||||
var justification_bits = JustificationBits(
|
||||
(uint8(state.justification_bits) shl 1) and
|
||||
uint8((2^JUSTIFICATION_BITS_LENGTH) - 1))
|
||||
|
||||
if previous_epoch_target_balance * 3 >= total_active_balance * 2:
|
||||
uint8(justification_bits).setBit 1
|
||||
|
||||
if current_epoch_target_balance * 3 >= total_active_balance * 2:
|
||||
uint8(justification_bits).setBit 0
|
||||
|
||||
# Process finalizations
|
||||
let bitfield = uint8(justification_bits)
|
||||
|
||||
## The 2nd/3rd/4th most recent epochs are justified, the 2nd using the 4th
|
||||
## as source
|
||||
if (bitfield and 0b1110) == 0b1110 and
|
||||
old_previous_justified_checkpoint.epoch + 3 == current_epoch:
|
||||
return old_previous_justified_checkpoint
|
||||
|
||||
## The 2nd/3rd most recent epochs are justified, the 2nd using the 3rd as
|
||||
## source
|
||||
if (bitfield and 0b110) == 0b110 and
|
||||
old_previous_justified_checkpoint.epoch + 2 == current_epoch:
|
||||
return old_previous_justified_checkpoint
|
||||
|
||||
## The 1st/2nd/3rd most recent epochs are justified, the 1st using the 3rd as
|
||||
## source
|
||||
if (bitfield and 0b111) == 0b111 and
|
||||
old_current_justified_checkpoint.epoch + 2 == current_epoch:
|
||||
return old_current_justified_checkpoint
|
||||
|
||||
## The 1st/2nd most recent epochs are justified, the 1st using the 2nd as
|
||||
## source
|
||||
if (bitfield and 0b11) == 0b11 and
|
||||
old_current_justified_checkpoint.epoch + 1 == current_epoch:
|
||||
return old_current_justified_checkpoint
|
||||
|
||||
state.finalized_checkpoint
|
||||
|
||||
func getFinalizedCheckpoint(state: phase0.BeaconState, balances: TotalBalances):
|
||||
Checkpoint =
|
||||
getFinalizedCheckpoint(state, balances.current_epoch,
|
||||
balances.previous_epoch_target_attesters,
|
||||
balances.current_epoch_target_attesters)
|
||||
|
||||
func getFinalizedCheckpoint(
|
||||
state: altair.BeaconState | bellatrix.BeaconState,
|
||||
balances: UnslashedParticipatingBalances): Checkpoint =
|
||||
getFinalizedCheckpoint(state, balances.current_epoch,
|
||||
balances.previous_epoch[TIMELY_TARGET_FLAG_INDEX],
|
||||
balances.current_epoch_TIMELY_TARGET)
|
||||
|
||||
func getFinalityDelay*(state: ForkyBeaconState,
|
||||
finalizedCheckpoint: Checkpoint): uint64 =
|
||||
state.get_previous_epoch - finalizedCheckpoint.epoch
|
||||
|
||||
func collectEpochRewardsAndPenalties*(
|
||||
proc collectEpochRewardsAndPenalties*(
|
||||
rewardsAndPenalties: var seq[RewardsAndPenalties],
|
||||
state: phase0.BeaconState, cache: var StateCache, cfg: RuntimeConfig,
|
||||
state: var phase0.BeaconState, cache: var StateCache, cfg: RuntimeConfig,
|
||||
flags: UpdateFlags) =
|
||||
if get_current_epoch(state) == GENESIS_EPOCH:
|
||||
return
|
||||
@ -227,9 +155,10 @@ func collectEpochRewardsAndPenalties*(
|
||||
doAssert info.validators.len == state.validators.len
|
||||
rewardsAndPenalties.setLen(state.validators.len)
|
||||
|
||||
process_justification_and_finalization(state, info.balances, flags)
|
||||
|
||||
let
|
||||
finalized_checkpoint = state.getFinalizedCheckpoint(info.balances)
|
||||
finality_delay = getFinalityDelay(state, finalized_checkpoint)
|
||||
finality_delay = get_finality_delay(state)
|
||||
total_balance = info.balances.current_epoch
|
||||
total_balance_sqrt = integer_squareroot(total_balance)
|
||||
|
||||
@ -278,9 +207,9 @@ func collectEpochRewardsAndPenalties*(
|
||||
|
||||
rewardsAndPenalties.collectSlashings(state, info.balances.current_epoch)
|
||||
|
||||
func collectEpochRewardsAndPenalties*(
|
||||
proc collectEpochRewardsAndPenalties*(
|
||||
rewardsAndPenalties: var seq[RewardsAndPenalties],
|
||||
state: altair.BeaconState | bellatrix.BeaconState,
|
||||
state: var (altair.BeaconState | bellatrix.BeaconState),
|
||||
cache: var StateCache, cfg: RuntimeConfig, flags: UpdateFlags) =
|
||||
if get_current_epoch(state) == GENESIS_EPOCH:
|
||||
return
|
||||
@ -290,12 +219,14 @@ func collectEpochRewardsAndPenalties*(
|
||||
doAssert info.validators.len == state.validators.len
|
||||
rewardsAndPenalties.setLen(state.validators.len)
|
||||
|
||||
process_justification_and_finalization(state, info.balances, flags)
|
||||
process_inactivity_updates(cfg, state, info)
|
||||
|
||||
let
|
||||
total_active_balance = info.balances.current_epoch
|
||||
base_reward_per_increment = get_base_reward_per_increment(
|
||||
total_active_balance)
|
||||
finalized_checkpoint = state.getFinalizedCheckpoint(info.balances)
|
||||
finality_delay = getFinalityDelay(state, finalized_checkpoint)
|
||||
finality_delay = get_finality_delay(state)
|
||||
|
||||
for flag_index in 0 ..< PARTICIPATION_FLAG_WEIGHTS.len:
|
||||
for validator_index, delta in get_flag_index_deltas(
|
||||
|
@ -757,10 +757,10 @@ proc printComponents(info: RewardsAndPenalties) =
|
||||
|
||||
proc checkBalance(validatorIndex: int64,
|
||||
validator: RewardStatus | ParticipationInfo,
|
||||
currentEpochBalance, previousEpochBalance: Gwei,
|
||||
currentEpochBalance, previousEpochBalance: int64,
|
||||
validatorInfo: RewardsAndPenalties) =
|
||||
let delta = validatorInfo.calculateDelta
|
||||
if currentEpochBalance.int64 == previousEpochBalance.int64 + delta:
|
||||
if currentEpochBalance == previousEpochBalance + delta:
|
||||
return
|
||||
echo "Validator: ", validatorIndex
|
||||
echo "Is eligible: ", is_eligible_validator(validator)
|
||||
@ -901,8 +901,8 @@ proc cmdValidatorDb(conf: DbConf, cfg: RuntimeConfig) =
|
||||
for index, validator in info.validators.pairs:
|
||||
template rp: untyped = rewardsAndPenalties[index]
|
||||
|
||||
checkBalance(index, validator, state.data.balances[index],
|
||||
previousEpochBalances[index], rp)
|
||||
checkBalance(index, validator, state.data.balances[index].int64,
|
||||
previousEpochBalances[index].int64, rp)
|
||||
|
||||
when infoFork == EpochInfoFork.Phase0:
|
||||
rp.inclusion_delay = block:
|
||||
@ -939,8 +939,9 @@ proc cmdValidatorDb(conf: DbConf, cfg: RuntimeConfig) =
|
||||
|
||||
if nextSlot.isEpoch:
|
||||
withState(tmpState[].data):
|
||||
var stateData = newClone(state.data)
|
||||
rewardsAndPenalties.collectEpochRewardsAndPenalties(
|
||||
state.data, cache, cfg, flags)
|
||||
stateData[], cache, cfg, flags)
|
||||
|
||||
let res = process_slots(cfg, tmpState[].data, nextSlot, cache, forkedInfo, flags)
|
||||
doAssert res.isOk, "Slot processing can't fail with correct inputs"
|
||||
|
Loading…
x
Reference in New Issue
Block a user