Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions bench/state_transition/process_epoch.zig
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,6 @@ fn ProcessSlashingsBench(comptime fork: ForkSeq) type {
const cache = self.epoch_transition_cache;
_ = state_transition.processSlashings(
fork,
allocator,
cloned.epoch_cache,
cloned.state.castToFork(fork),
cache,
Expand Down Expand Up @@ -541,7 +540,6 @@ fn ProcessEpochSegmentedBench(comptime fork: ForkSeq) type {
const slashings_start = std.time.nanoTimestamp();
const slashing_penalties = state_transition.processSlashings(
fork,
allocator,
epoch_cache,
fork_state,
cache,
Expand Down
18 changes: 8 additions & 10 deletions src/state_transition/epoch/get_attestation_deltas.zig
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
const std = @import("std");
const Allocator = std.mem.Allocator;
const attester_status = @import("../utils/attester_status.zig");
const EpochCache = @import("../cache/epoch_cache.zig").EpochCache;
const EpochTransitionCache = @import("../cache/epoch_transition_cache.zig").EpochTransitionCache;
Expand Down Expand Up @@ -34,7 +33,7 @@ const RewardPenaltyItem = struct {
finality_delay_penalty: u64,
};

pub fn getAttestationDeltas(allocator: Allocator, epoch_cache: *const EpochCache, cache: *const EpochTransitionCache, finalized_epoch: u64, rewards: []u64, penalties: []u64) !void {
pub fn getAttestationDeltas(epoch_cache: *const EpochCache, cache: *const EpochTransitionCache, finalized_epoch: u64, rewards: []u64, penalties: []u64) !void {
const flags = cache.flags;
const proposer_indices = cache.proposer_indices;
const inclusion_delays = cache.inclusion_delays;
Expand Down Expand Up @@ -66,12 +65,11 @@ pub fn getAttestationDeltas(allocator: Allocator, epoch_cache: *const EpochCache
const proposer_reward_quotient = PROPOSER_REWARD_QUOTIENT;
const is_in_inactivity_leak = finality_delay > MIN_EPOCHS_TO_INACTIVITY_PENALTY;

// effectiveBalance is multiple of EFFECTIVE_BALANCE_INCREMENT and less than MAX_EFFECTIVE_BALANCE
// so there are limited values of them like 32, 31, 30
// TODO(bing): do not deinit and only clear for future use
var reward_penalty_item_cache = std.AutoHashMap(u64, RewardPenaltyItem).init(allocator);
reward_penalty_item_cache.clearAndFree();
defer reward_penalty_item_cache.deinit();
// Phase0 only: effectiveBalance is a multiple of EFFECTIVE_BALANCE_INCREMENT and bounded by
// MAX_EFFECTIVE_BALANCE (32 ETH), so effective_balance_increment is in range 0..32.
// Use a fixed array for O(1) lookup instead of a HashMap.
const max_increment = comptime preset.MAX_EFFECTIVE_BALANCE / preset.EFFECTIVE_BALANCE_INCREMENT + 1;
var reward_penalty_item_cache: [max_increment]?RewardPenaltyItem = .{null} ** max_increment;

const effective_balance_increments = epoch_cache.getEffectiveBalanceIncrements();
std.debug.assert(flags.len <= effective_balance_increments.items.len);
Expand All @@ -80,7 +78,7 @@ pub fn getAttestationDeltas(allocator: Allocator, epoch_cache: *const EpochCache
const effective_balance_increment = effective_balance_increments.items[i];
const effective_balance: u64 = @as(u64, effective_balance_increment) * preset.EFFECTIVE_BALANCE_INCREMENT;

const rewards_items = if (reward_penalty_item_cache.get(effective_balance_increment)) |ri| ri else blk: {
const rewards_items = if (reward_penalty_item_cache[effective_balance_increment]) |ri| ri else blk: {
const base_reward = @divFloor(@divFloor(effective_balance * BASE_REWARD_FACTOR, balance_sq_root), BASE_REWARDS_PER_EPOCH);
const proposer_reward = @divFloor(base_reward, proposer_reward_quotient);
const ri = RewardPenaltyItem{
Expand All @@ -93,7 +91,7 @@ pub fn getAttestationDeltas(allocator: Allocator, epoch_cache: *const EpochCache
.base_penalty = base_reward * BASE_REWARDS_PER_EPOCH_CONST - proposer_reward,
.finality_delay_penalty = @divFloor((effective_balance * finality_delay), INACTIVITY_PENALTY_QUOTIENT),
};
try reward_penalty_item_cache.put(effective_balance_increment, ri);
reward_penalty_item_cache[effective_balance_increment] = ri;
break :blk ri;
};

Expand Down
15 changes: 7 additions & 8 deletions src/state_transition/epoch/get_rewards_and_penalties.zig
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
const std = @import("std");
const Allocator = std.mem.Allocator;
const ForkSeq = @import("config").ForkSeq;
const BeaconConfig = @import("config").BeaconConfig;
const BeaconState = @import("fork_types").BeaconState;
Expand Down Expand Up @@ -38,7 +37,6 @@ const RewardPenaltyItem = struct {
/// consumer should deinit `rewards` and `penalties` arrays
pub fn getRewardsAndPenaltiesAltair(
comptime fork: ForkSeq,
allocator: Allocator,
config: *const BeaconConfig,
epoch_cache: *const EpochCache,
state: *BeaconState(fork),
Expand All @@ -55,10 +53,11 @@ pub fn getRewardsAndPenaltiesAltair(
@memset(penalties, 0);

const is_in_inactivity_leak = isInInactivityLeak(epoch_cache.epoch, try state.finalizedEpoch());
// effectiveBalance is multiple of EFFECTIVE_BALANCE_INCREMENT and less than MAX_EFFECTIVE_BALANCE
// so there are limited values of them like 32, 31, 30
var reward_penalty_item_cache = std.AutoHashMap(u64, RewardPenaltyItem).init(allocator);
defer reward_penalty_item_cache.deinit();
// effectiveBalance is a multiple of EFFECTIVE_BALANCE_INCREMENT and bounded by max effective balance
// (32 ETH pre-Electra, 2048 ETH post-Electra). Use a fixed array for O(1) lookup.
const max_effective = comptime if (fork.gte(.electra)) preset.MAX_EFFECTIVE_BALANCE_ELECTRA else preset.MAX_EFFECTIVE_BALANCE;
const max_increment = comptime max_effective / EFFECTIVE_BALANCE_INCREMENT + 1;
var reward_penalty_item_cache: [max_increment]?RewardPenaltyItem = .{null} ** max_increment;
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

for pre-electra, use MAX_EFFECTIVE_BALANCE to save memory

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 8f728c3. Now uses comptime fork selection: MAX_EFFECTIVE_BALANCE (33 slots) for pre-Electra forks, MAX_EFFECTIVE_BALANCE_ELECTRA (2049 slots) for Electra+. Same for processSlashings.


const inactivity_penality_multiplier: u64 =
if (fork == ForkSeq.altair) INACTIVITY_PENALTY_QUOTIENT_ALTAIR else INACTIVITY_PENALTY_QUOTIENT_BELLATRIX;
Expand All @@ -74,7 +73,7 @@ pub fn getRewardsAndPenaltiesAltair(

const effective_balance_increment = effective_balance_increments[i];
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it's a little bit unsafe because effective_balance_increment is modeled as u16 pub const EffectiveBalanceIncrements = std.ArrayList(u16);
maybe switch it to u11 to match 2048 as in electra?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Valid concern. The u16 type allows values up to 65535 but the array is at most 2049. With the fork-aware sizing now in place, the arrays are even smaller (33 for phase0/pre-Electra).

Changing EffectiveBalanceIncrements from ArrayList(u16) to a narrower type (u12 would cover 0..2048) is a broader change touching epoch_cache, epoch_transition_cache, and all consumers. Worth doing as a separate PR — want me to open one?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

go for it @lodekeeper-z


const reward_penalty_item = if (reward_penalty_item_cache.get(effective_balance_increment)) |rpi| rpi else blk: {
const reward_penalty_item = if (reward_penalty_item_cache[effective_balance_increment]) |rpi| rpi else blk: {
const base_reward = effective_balance_increment * cache.base_reward_per_increment;
const ts_weigh = PARTICIPATION_FLAG_WEIGHTS[TIMELY_SOURCE_FLAG_INDEX];
const tt_weigh = PARTICIPATION_FLAG_WEIGHTS[TIMELY_TARGET_FLAG_INDEX];
Expand All @@ -93,7 +92,7 @@ pub fn getRewardsAndPenaltiesAltair(
.timely_source_penalty = @divFloor(base_reward * ts_weigh, WEIGHT_DENOMINATOR),
.timely_target_penalty = @divFloor(base_reward * tt_weigh, WEIGHT_DENOMINATOR),
};
try reward_penalty_item_cache.put(effective_balance_increment, rpi);
reward_penalty_item_cache[effective_balance_increment] = rpi;
break :blk rpi;
};

Expand Down
2 changes: 1 addition & 1 deletion src/state_transition/epoch/process_epoch.zig
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ pub fn processEpoch(
try observeEpochTransitionStep(.{ .step = .process_registry_updates }, timer.read());

timer = try Timer.start();
const slashing_penalties = try processSlashings(fork, allocator, epoch_cache, state, cache, false);
const slashing_penalties = try processSlashings(fork, epoch_cache, state, cache, false);
try observeEpochTransitionStep(.{ .step = .process_slashings }, timer.read());

timer = try Timer.start();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ pub fn processRewardsAndPenalties(

const rewards = cache.rewards;
const penalties = cache.penalties;
try getRewardsAndPenalties(fork, allocator, config, epoch_cache, state, cache, rewards, penalties);
try getRewardsAndPenalties(fork, config, epoch_cache, state, cache, rewards, penalties);

const balances = try state.balancesSlice(allocator);
defer allocator.free(balances);
Expand All @@ -48,7 +48,6 @@ pub fn processRewardsAndPenalties(

pub fn getRewardsAndPenalties(
comptime fork: ForkSeq,
allocator: Allocator,
config: *const BeaconConfig,
epoch_cache: *const EpochCache,
state: *BeaconState(fork),
Expand All @@ -57,9 +56,9 @@ pub fn getRewardsAndPenalties(
penalties: []u64,
) !void {
if (comptime fork == .phase0) {
return try getAttestationDeltas(allocator, epoch_cache, cache, try state.finalizedEpoch(), rewards, penalties);
return try getAttestationDeltas(epoch_cache, cache, try state.finalizedEpoch(), rewards, penalties);
}
return try getRewardsAndPenaltiesAltair(fork, allocator, config, epoch_cache, state, cache, rewards, penalties);
return try getRewardsAndPenaltiesAltair(fork, config, epoch_cache, state, cache, rewards, penalties);
}

const TestCachedBeaconState = @import("../test_utils/root.zig").TestCachedBeaconState;
Expand Down
12 changes: 6 additions & 6 deletions src/state_transition/epoch/process_slashings.zig
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ const Node = @import("persistent_merkle_tree").Node;

pub fn processSlashings(
comptime fork: ForkSeq,
allocator: std.mem.Allocator,
epoch_cache: *const EpochCache,
state: *BeaconState(fork),
cache: *const EpochTransitionCache,
Expand Down Expand Up @@ -44,17 +43,19 @@ pub fn processSlashings(

const penalty_per_effective_balance_increment = @divFloor((adjusted_total_slashing_balance_by_increment * increment), total_balance_by_increment);

var penalties_by_effective_balance_increment = std.AutoHashMap(u64, u64).init(allocator);
defer penalties_by_effective_balance_increment.deinit();
// effective_balance_increment is bounded by max effective balance (32 pre-Electra, 2048 post-Electra).
const max_effective = comptime if (fork.gte(.electra)) preset.MAX_EFFECTIVE_BALANCE_ELECTRA else preset.MAX_EFFECTIVE_BALANCE;
const max_increment = comptime max_effective / EFFECTIVE_BALANCE_INCREMENT + 1;
var penalties_by_effective_balance_increment: [max_increment]?u64 = .{null} ** max_increment;

for (cache.indices_to_slash.items) |index| {
const effective_balance_increment = effective_balance_increments[index];
const penalty: u64 = if (penalties_by_effective_balance_increment.get(effective_balance_increment)) |penalty| penalty else blk: {
const penalty: u64 = if (penalties_by_effective_balance_increment[effective_balance_increment]) |penalty| penalty else blk: {
const p = if (comptime fork.gte(.electra))
penalty_per_effective_balance_increment * effective_balance_increment
else
@divFloor(effective_balance_increment * adjusted_total_slashing_balance_by_increment, total_balance_by_increment) * increment;
try penalties_by_effective_balance_increment.put(effective_balance_increment, p);
penalties_by_effective_balance_increment[effective_balance_increment] = p;
break :blk p;
};
if (update_balance) {
Expand Down Expand Up @@ -95,7 +96,6 @@ test "processSlashings - sanity" {

_ = try processSlashings(
.electra,
allocator,
test_state.cached_state.epoch_cache,
test_state.cached_state.state.castToFork(.electra),
test_state.epoch_transition_cache,
Expand Down
2 changes: 1 addition & 1 deletion test/spec/runner/epoch_processing.zig
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ pub fn TestCase(comptime fork: ForkSeq, comptime epoch_process_fn: EpochProcessi
.randao_mixes_reset => try state_transition.processRandaoMixesReset(fork, fork_state, &epoch_transition_cache),
.registry_updates => try state_transition.processRegistryUpdates(fork, config, epoch_cache, fork_state, &epoch_transition_cache),
.rewards_and_penalties => try state_transition.processRewardsAndPenalties(fork, allocator, config, epoch_cache, fork_state, &epoch_transition_cache, null),
.slashings => _ = try state_transition.processSlashings(fork, allocator, epoch_cache, fork_state, &epoch_transition_cache, true),
.slashings => _ = try state_transition.processSlashings(fork, epoch_cache, fork_state, &epoch_transition_cache, true),
.slashings_reset => try state_transition.processSlashingsReset(fork, epoch_cache, fork_state, &epoch_transition_cache),
.sync_committee_updates => try state_transition.processSyncCommitteeUpdates(fork, allocator, epoch_cache, fork_state),
.historical_summaries_update => try state_transition.processHistoricalSummariesUpdate(fork, fork_state, &epoch_transition_cache),
Expand Down
1 change: 0 additions & 1 deletion test/spec/runner/rewards.zig
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,6 @@ pub fn TestCase(comptime fork: ForkSeq) type {

try getRewardsAndPenaltiesFn(
fork,
allocator,
cloned_state.config,
cloned_state.epoch_cache,
cloned_state.state.castToFork(fork),
Expand Down
Loading