diff --git a/bench/state_transition/process_epoch.zig b/bench/state_transition/process_epoch.zig index ee4168781..16d44b0b2 100644 --- a/bench/state_transition/process_epoch.zig +++ b/bench/state_transition/process_epoch.zig @@ -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, @@ -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, diff --git a/src/state_transition/epoch/get_attestation_deltas.zig b/src/state_transition/epoch/get_attestation_deltas.zig index ea6ca9290..2013ee520 100644 --- a/src/state_transition/epoch/get_attestation_deltas.zig +++ b/src/state_transition/epoch/get_attestation_deltas.zig @@ -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; @@ -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; @@ -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); @@ -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{ @@ -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; }; diff --git a/src/state_transition/epoch/get_rewards_and_penalties.zig b/src/state_transition/epoch/get_rewards_and_penalties.zig index 48969ed5b..2c459e6af 100644 --- a/src/state_transition/epoch/get_rewards_and_penalties.zig +++ b/src/state_transition/epoch/get_rewards_and_penalties.zig @@ -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; @@ -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), @@ -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; const inactivity_penality_multiplier: u64 = if (fork == ForkSeq.altair) INACTIVITY_PENALTY_QUOTIENT_ALTAIR else INACTIVITY_PENALTY_QUOTIENT_BELLATRIX; @@ -74,7 +73,7 @@ pub fn getRewardsAndPenaltiesAltair( const effective_balance_increment = effective_balance_increments[i]; - 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]; @@ -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; }; diff --git a/src/state_transition/epoch/process_epoch.zig b/src/state_transition/epoch/process_epoch.zig index 84647c7b7..1720aa88a 100644 --- a/src/state_transition/epoch/process_epoch.zig +++ b/src/state_transition/epoch/process_epoch.zig @@ -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(); diff --git a/src/state_transition/epoch/process_rewards_and_penalties.zig b/src/state_transition/epoch/process_rewards_and_penalties.zig index 6e73cdce2..cae0d76b5 100644 --- a/src/state_transition/epoch/process_rewards_and_penalties.zig +++ b/src/state_transition/epoch/process_rewards_and_penalties.zig @@ -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); @@ -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), @@ -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; diff --git a/src/state_transition/epoch/process_slashings.zig b/src/state_transition/epoch/process_slashings.zig index 29b07429f..9f4983c1b 100644 --- a/src/state_transition/epoch/process_slashings.zig +++ b/src/state_transition/epoch/process_slashings.zig @@ -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, @@ -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) { @@ -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, diff --git a/test/spec/runner/epoch_processing.zig b/test/spec/runner/epoch_processing.zig index e4bec7573..e09cc110f 100644 --- a/test/spec/runner/epoch_processing.zig +++ b/test/spec/runner/epoch_processing.zig @@ -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), diff --git a/test/spec/runner/rewards.zig b/test/spec/runner/rewards.zig index e37532337..2a0143009 100644 --- a/test/spec/runner/rewards.zig +++ b/test/spec/runner/rewards.zig @@ -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),