mirror of
https://github.com/status-im/nimbus-eth2.git
synced 2025-02-05 03:03:26 +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()
|
let found = queryRes.expectDb()
|
||||||
if not found: panic()
|
if not found: panic()
|
||||||
|
|
||||||
proc init*(T: type FinalizedBlocks, db: SqStoreRef, name: string): KvResult[T] =
|
proc init*(T: type FinalizedBlocks, db: SqStoreRef, name: string,
|
||||||
? db.exec("""
|
readOnly = false): KvResult[T] =
|
||||||
CREATE TABLE IF NOT EXISTS """ & name & """(
|
if not readOnly:
|
||||||
id INTEGER PRIMARY KEY,
|
? db.exec("""
|
||||||
value BLOB NOT NULL
|
CREATE TABLE IF NOT EXISTS """ & name & """(
|
||||||
);
|
id INTEGER PRIMARY KEY,
|
||||||
""")
|
value BLOB NOT NULL
|
||||||
|
);
|
||||||
|
""")
|
||||||
|
|
||||||
let
|
let
|
||||||
insertStmt = db.prepareStmt(
|
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
|
# https://github.com/ethereum/consensus-specs/blob/v1.1.9/specs/altair/beacon-chain.md#get_flag_index_deltas
|
||||||
func get_unslashed_participating_increment*(
|
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
|
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
|
# 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
|
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
|
# 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(
|
validator[].get_slashing_penalty(
|
||||||
adjusted_total_slashing_balance, total_balance).int64
|
adjusted_total_slashing_balance, total_balance).int64
|
||||||
|
|
||||||
func getFinalizedCheckpoint(state: ForkyBeaconState,
|
proc collectEpochRewardsAndPenalties*(
|
||||||
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*(
|
|
||||||
rewardsAndPenalties: var seq[RewardsAndPenalties],
|
rewardsAndPenalties: var seq[RewardsAndPenalties],
|
||||||
state: phase0.BeaconState, cache: var StateCache, cfg: RuntimeConfig,
|
state: var phase0.BeaconState, cache: var StateCache, cfg: RuntimeConfig,
|
||||||
flags: UpdateFlags) =
|
flags: UpdateFlags) =
|
||||||
if get_current_epoch(state) == GENESIS_EPOCH:
|
if get_current_epoch(state) == GENESIS_EPOCH:
|
||||||
return
|
return
|
||||||
@ -227,9 +155,10 @@ func collectEpochRewardsAndPenalties*(
|
|||||||
doAssert info.validators.len == state.validators.len
|
doAssert info.validators.len == state.validators.len
|
||||||
rewardsAndPenalties.setLen(state.validators.len)
|
rewardsAndPenalties.setLen(state.validators.len)
|
||||||
|
|
||||||
|
process_justification_and_finalization(state, info.balances, flags)
|
||||||
|
|
||||||
let
|
let
|
||||||
finalized_checkpoint = state.getFinalizedCheckpoint(info.balances)
|
finality_delay = get_finality_delay(state)
|
||||||
finality_delay = getFinalityDelay(state, finalized_checkpoint)
|
|
||||||
total_balance = info.balances.current_epoch
|
total_balance = info.balances.current_epoch
|
||||||
total_balance_sqrt = integer_squareroot(total_balance)
|
total_balance_sqrt = integer_squareroot(total_balance)
|
||||||
|
|
||||||
@ -278,9 +207,9 @@ func collectEpochRewardsAndPenalties*(
|
|||||||
|
|
||||||
rewardsAndPenalties.collectSlashings(state, info.balances.current_epoch)
|
rewardsAndPenalties.collectSlashings(state, info.balances.current_epoch)
|
||||||
|
|
||||||
func collectEpochRewardsAndPenalties*(
|
proc collectEpochRewardsAndPenalties*(
|
||||||
rewardsAndPenalties: var seq[RewardsAndPenalties],
|
rewardsAndPenalties: var seq[RewardsAndPenalties],
|
||||||
state: altair.BeaconState | bellatrix.BeaconState,
|
state: var (altair.BeaconState | bellatrix.BeaconState),
|
||||||
cache: var StateCache, cfg: RuntimeConfig, flags: UpdateFlags) =
|
cache: var StateCache, cfg: RuntimeConfig, flags: UpdateFlags) =
|
||||||
if get_current_epoch(state) == GENESIS_EPOCH:
|
if get_current_epoch(state) == GENESIS_EPOCH:
|
||||||
return
|
return
|
||||||
@ -290,12 +219,14 @@ func collectEpochRewardsAndPenalties*(
|
|||||||
doAssert info.validators.len == state.validators.len
|
doAssert info.validators.len == state.validators.len
|
||||||
rewardsAndPenalties.setLen(state.validators.len)
|
rewardsAndPenalties.setLen(state.validators.len)
|
||||||
|
|
||||||
|
process_justification_and_finalization(state, info.balances, flags)
|
||||||
|
process_inactivity_updates(cfg, state, info)
|
||||||
|
|
||||||
let
|
let
|
||||||
total_active_balance = info.balances.current_epoch
|
total_active_balance = info.balances.current_epoch
|
||||||
base_reward_per_increment = get_base_reward_per_increment(
|
base_reward_per_increment = get_base_reward_per_increment(
|
||||||
total_active_balance)
|
total_active_balance)
|
||||||
finalized_checkpoint = state.getFinalizedCheckpoint(info.balances)
|
finality_delay = get_finality_delay(state)
|
||||||
finality_delay = getFinalityDelay(state, finalized_checkpoint)
|
|
||||||
|
|
||||||
for flag_index in 0 ..< PARTICIPATION_FLAG_WEIGHTS.len:
|
for flag_index in 0 ..< PARTICIPATION_FLAG_WEIGHTS.len:
|
||||||
for validator_index, delta in get_flag_index_deltas(
|
for validator_index, delta in get_flag_index_deltas(
|
||||||
|
@ -757,10 +757,10 @@ proc printComponents(info: RewardsAndPenalties) =
|
|||||||
|
|
||||||
proc checkBalance(validatorIndex: int64,
|
proc checkBalance(validatorIndex: int64,
|
||||||
validator: RewardStatus | ParticipationInfo,
|
validator: RewardStatus | ParticipationInfo,
|
||||||
currentEpochBalance, previousEpochBalance: Gwei,
|
currentEpochBalance, previousEpochBalance: int64,
|
||||||
validatorInfo: RewardsAndPenalties) =
|
validatorInfo: RewardsAndPenalties) =
|
||||||
let delta = validatorInfo.calculateDelta
|
let delta = validatorInfo.calculateDelta
|
||||||
if currentEpochBalance.int64 == previousEpochBalance.int64 + delta:
|
if currentEpochBalance == previousEpochBalance + delta:
|
||||||
return
|
return
|
||||||
echo "Validator: ", validatorIndex
|
echo "Validator: ", validatorIndex
|
||||||
echo "Is eligible: ", is_eligible_validator(validator)
|
echo "Is eligible: ", is_eligible_validator(validator)
|
||||||
@ -901,8 +901,8 @@ proc cmdValidatorDb(conf: DbConf, cfg: RuntimeConfig) =
|
|||||||
for index, validator in info.validators.pairs:
|
for index, validator in info.validators.pairs:
|
||||||
template rp: untyped = rewardsAndPenalties[index]
|
template rp: untyped = rewardsAndPenalties[index]
|
||||||
|
|
||||||
checkBalance(index, validator, state.data.balances[index],
|
checkBalance(index, validator, state.data.balances[index].int64,
|
||||||
previousEpochBalances[index], rp)
|
previousEpochBalances[index].int64, rp)
|
||||||
|
|
||||||
when infoFork == EpochInfoFork.Phase0:
|
when infoFork == EpochInfoFork.Phase0:
|
||||||
rp.inclusion_delay = block:
|
rp.inclusion_delay = block:
|
||||||
@ -939,8 +939,9 @@ proc cmdValidatorDb(conf: DbConf, cfg: RuntimeConfig) =
|
|||||||
|
|
||||||
if nextSlot.isEpoch:
|
if nextSlot.isEpoch:
|
||||||
withState(tmpState[].data):
|
withState(tmpState[].data):
|
||||||
|
var stateData = newClone(state.data)
|
||||||
rewardsAndPenalties.collectEpochRewardsAndPenalties(
|
rewardsAndPenalties.collectEpochRewardsAndPenalties(
|
||||||
state.data, cache, cfg, flags)
|
stateData[], cache, cfg, flags)
|
||||||
|
|
||||||
let res = process_slots(cfg, tmpState[].data, nextSlot, cache, forkedInfo, flags)
|
let res = process_slots(cfg, tmpState[].data, nextSlot, cache, forkedInfo, flags)
|
||||||
doAssert res.isOk, "Slot processing can't fail with correct inputs"
|
doAssert res.isOk, "Slot processing can't fail with correct inputs"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user