Skip to content

Add quick slots (EIP-8198) - #5592

Open
barnabemonnot wants to merge 34 commits into
ethereum:masterfrom
barnabemonnot:eip8198-quick-slots
Open

Add quick slots (EIP-8198)#5592
barnabemonnot wants to merge 34 commits into
ethereum:masterfrom
barnabemonnot:eip8198-quick-slots

Conversation

@barnabemonnot

@barnabemonnot barnabemonnot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

EIP-8198 (quick slots) proposes shortening the slot duration. This PR adds the consensus-layer feature specification under specs/_features/eip8198, built on Heze, so the mechanism can be reviewed and prototyped.

Updates and design decisions

  • The slot duration becomes governed by a SLOT_DURATION_SCHEDULE configuration that changes it at a scheduled epoch (the intended mainnet entry is 10 seconds, epoch TBD), making the mapping between wall-clock time and slot number piecewise across the schedule's eras. The schedule does not intend to support BPO-like slot duration changes in-between upgrades: We make the strong assumption here that slot time reductions will always happen during non-BPO-like network upgrades. The schedule is simply a way to loop over historical slot durations in functions that require accumulating this data, such as computing the current slot number given a timestamp.
  • Issuance, penalties, and churn limits rescale with the active duration.
  • Intra-slot deadlines do not rescale with the active duration, as these may benefit from individual optimisation accounting for fixed overheads in certain slot moments. Choosing the deadlines is deferred until further testing.
  • The fork-choice store clock gains millisecond precision
  • Gossip slot gates and sidecar retention windows preserve their wall-clock semantics across a duration change. In particular, blobs are kept for the same wall-clock duration as they were before the transition to shorter slots.
  • To keep throughput unchanged at the transition, we choose the approach of programmatically reducing the gas limit per block hours before the fork epoch, arriving at the gas limit per block that maintains throughput once the transition is performed.
  • Beacon-chain, fork, fork-choice, p2p, validator, and optimistic-sync documents are included, along with unit and fork-choice tests.

Execution layer considerations

We do not require tight coupling with the execution layer. In particular, the execution layer is not expected to consume the slot time from the consensus layer, keeping the separation of concerns. That said, two items require updating:

  • The base fee update rule needs a one-time constant update, to maintain the same rate of change per unit of time.
  • The blob schedules must be re-parameterised, to maintain constant blob target.

@github-actions github-actions Bot added the testing CI, actions, tests, testing infra label Sep 2, 2026
@jihoonsong

Copy link
Copy Markdown
Member

I would appreciate it if the PR authors would review and simplify AI-generated comments.

@jtraglia jtraglia changed the title Add EIP-8198 quick slots feature specification Add quick slots (EIP-8198) Sep 2, 2026
@jtraglia jtraglia added the eip8198 Quick Slots label Sep 2, 2026
Comment thread configs/mainnet.yaml Outdated
Comment thread configs/mainnet.yaml Outdated
Comment thread configs/mainnet.yaml Outdated
Comment thread configs/minimal.yaml Outdated
Comment thread configs/minimal.yaml Outdated
Comment thread specs/_features/eip8198/fork-choice.md Outdated
Comment on lines +129 to +139
###### Modified `execution_payload_bid`

The gas-limit _[IGNORE]_ condition is replaced with the following, where
`parent_execution_payload_slot` is the slot of the beacon block associated with
the known execution payload identified by `bid.parent_block_hash`:

- _[IGNORE]_ `bid.parent_block_hash` is the block hash of a known execution
payload in fork choice and
`is_gas_limit_transition_compatible(parent_gas_limit, bid.gas_limit, proposer_preferences.target_gas_limit, parent_execution_payload_slot, bid.slot)`
is `True` where `parent_gas_limit` is the `gas_limit` of that execution
payload.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This change needs to exist inside an executable function. See:

def validate_execution_payload_bid_gossip(
seen: Seen,
store: Store,
signed_execution_payload_bid: SignedExecutionPayloadBid,
current_time_ms: Uint64,
) -> None:
"""
Validate a SignedExecutionPayloadBid for gossip propagation.
Raises GossipIgnore or GossipReject on validation failure.
"""
bid = signed_execution_payload_bid.message
# [IGNORE] This is the first bid for this slot, parent, and builder
bid_key = (bid.slot, bid.parent_block_hash, bid.parent_block_root, bid.builder_index)
if bid_key in seen.execution_payload_bids:
raise GossipIgnore("already seen valid bid for this slot, parent, and builder")
# [IGNORE] This is the highest value bid seen for the slot and parent
best_bid_key = (bid.slot, bid.parent_block_hash, bid.parent_block_root)
if best_bid_key in seen.best_execution_payload_bid:
if bid.value <= seen.best_execution_payload_bid[best_bid_key]:
raise GossipIgnore("bid is not the highest value bid seen for this slot and parent")
# [IGNORE] The bid's slot is the current slot or the next slot
if not is_current_or_next_slot(store, bid.slot, current_time_ms):
raise GossipIgnore("bid's slot is not the current or next slot")
# [REJECT] The bid's execution payment is zero
if bid.execution_payment != 0:
raise GossipReject("bid's execution payment must be zero")
# [REJECT] The bid's blob KZG commitment count is within the per-epoch limit
proposal_epoch = compute_epoch_at_slot(bid.slot)
max_blobs = get_blob_parameters(proposal_epoch).max_blobs_per_block
if len(bid.blob_kzg_commitments) > max_blobs:
raise GossipReject("too many blob kzg commitments")
# [IGNORE] The bid's parent block root is a known beacon block
# (MAY be queued until parent is retrieved)
if bid.parent_block_root not in store.blocks:
raise GossipIgnore("bid's parent block root is not a known beacon block")
# [REJECT] The bid is for a higher slot than its parent block
if bid.slot <= store.blocks[bid.parent_block_root].slot:
raise GossipReject("bid's slot is not higher than its parent's slot")
# [IGNORE] The bid's parent block has been imported
# (MAY be queued until parent is imported)
if bid.parent_block_root not in store.block_states:
raise GossipIgnore("bid's parent block post-state is unavailable")
state = store.block_states[bid.parent_block_root]
# [IGNORE] The bid's slot is within the parent's proposer lookahead
if proposal_epoch > get_current_epoch(state) + MIN_SEED_LOOKAHEAD:
raise GossipIgnore("bid's slot is past the parent's proposer lookahead")
# [IGNORE] The matching proposer preferences have been seen
dependent_root = get_shuffling_dependent_root(store, bid.parent_block_root, proposal_epoch)
prefs_key = (bid.slot, dependent_root)
if prefs_key not in seen.proposer_preferences:
raise GossipIgnore("matching proposer preferences have not been seen")
proposer_preferences = seen.proposer_preferences[prefs_key]
# [IGNORE] The bid's fee recipient matches the proposer's preference
if bid.fee_recipient != proposer_preferences.fee_recipient:
raise GossipIgnore("bid's fee recipient does not match the proposer's preference")
# [IGNORE] The bid's parent block hash is the hash of a known execution payload
if bid.parent_block_hash not in seen.execution_payloads:
raise GossipIgnore("bid's parent block hash is not a known execution payload")
# [IGNORE] The bid's gas limit is compatible with the proposer's target gas limit
parent_gas_limit = seen.execution_payloads[bid.parent_block_hash].gas_limit
if not is_gas_limit_target_compatible(
parent_gas_limit, bid.gas_limit, proposer_preferences.target_gas_limit
):
raise GossipIgnore("bid's gas limit is not compatible with the proposer's target")
# [IGNORE] The bid is compatible with the current head branch
if not is_bid_compatible_with_head(store, bid):
raise GossipIgnore("bid is not compatible with the current head branch")
# [REJECT] The bid's previous randao is correct
if bid.prev_randao != get_randao_mix(state, get_current_epoch(state)):
raise GossipReject("bid's previous randao is incorrect")
state = state.copy()
process_slots(state, bid.slot)
# [REJECT] The builder index is valid
if bid.builder_index >= len(state.builders):
raise GossipReject("builder index out of range")
builder = state.builders[bid.builder_index]
# [REJECT] The builder is a payload builder
if builder.version != PAYLOAD_BUILDER_VERSION:
raise GossipReject("builder is not a payload builder")
# [REJECT] The builder is active
if not is_active_builder(state, bid.builder_index):
raise GossipReject("builder is not active")
# [IGNORE] The builder can cover the bid
if not can_builder_cover_bid(state, bid.builder_index, bid.value):
raise GossipIgnore("builder cannot cover bid value")
# [IGNORE] The parent's payload does not try to exit the builder
if bid.parent_block_hash == state.latest_execution_payload_bid.block_hash:
envelope = store.payloads[bid.parent_block_root]
for request in envelope.execution_requests.builder_exits:
if request.pubkey == builder.pubkey:
if request.source_address == builder.execution_address:
raise GossipIgnore("builder may exit")
# [REJECT] The bid signature is valid
if not verify_execution_payload_bid_signature(state, signed_execution_payload_bid):
raise GossipReject("invalid bid signature")
# Mark this bid as seen and update the highest-value bid for this slot/parent
seen.execution_payload_bids.add(bid_key)
seen.best_execution_payload_bid[best_bid_key] = bid.value

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

But again, not sure this is a good idea. If this depends on an update in the execution layer (does it?), this EIP becomes way more complicated.

Comment on lines +146 to +161
def is_gas_limit_transition_compatible(
parent_gas_limit: Uint64,
gas_limit: Uint64,
target_gas_limit: Uint64,
parent_execution_payload_slot: Slot,
bid_slot: Slot,
) -> bool:
"""
Check the bid gas limit, including the one-time scaling at a slot
duration change.
"""
parent_duration_ms = get_slot_duration_ms(compute_epoch_at_slot(parent_execution_payload_slot))
bid_duration_ms = get_slot_duration_ms(compute_epoch_at_slot(bid_slot))
if parent_duration_ms != bid_duration_ms:
return gas_limit == parent_gas_limit * bid_duration_ms // parent_duration_ms
return is_gas_limit_target_compatible(parent_gas_limit, gas_limit, target_gas_limit)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I think this could exist as a separate check from is_gas_limit_target_compatible. Eg:

  • IGNORE if is_gas_limit_transition_compatible() is False.
  • IGNORE if is_gas_limit_target_compatible() is False.

Also, it appears that this forces bids at the fork to use a gas limit based on parent_gas_limit which might not be what they want to do.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Could make it a bound, within ± 1024 of that value. However, I am also not sure about the necessity for this. We will most likely do more scaling with Hegota, we could just adjust the target gas limit based on the slot time. E.g. if the plan would be to scale from 200M to 300M but slot time reduction implies a 25% reduction in per slot throughput, we could just target 225M instead.

Another reason to take this approach is that simply scaling by (new slot time / old slot time) isn't really accurate in general

Comment thread specs/_features/eip8198/optimistic.md Outdated
Comment thread specs/_features/eip8198/beacon-chain.md Outdated
Comment thread specs/_features/eip8198/p2p-interface.md Outdated
Comment thread specs/_features/eip8198/validator.md Outdated
@jtraglia

jtraglia commented Sep 3, 2026

Copy link
Copy Markdown
Member

Nice updates @barnabemonnot! I'll re-review this again early next week.

@barnabemonnot

Copy link
Copy Markdown
Contributor Author

Nice updates @barnabemonnot! I'll re-review this again early next week.

Thank you @jtraglia . For reference here is a quick digest of open items I'll try and address by next week:

  • Deprecate SLOT_DURATION_MS?
  • Prevent leaks with EL => Decide how to handle base fee update rule re-parameterisation
  • Handle block gas limit at the fork to maintain constant throughput: either reduce gas progressively before the fork itself, or as @fradamt mentioned, assume that gas will be increased post-fork given scaling gains, and adjust gas per block before the block.
  • Should fork digest depend on the slot duration? Only helpful if slot time decreases are done out-of-forks.
  • Resolve blob schedule question: keep throughput constant, or keep blob targets/limits integers?

barnabemonnot and others added 21 commits September 5, 2026 00:35
Introduces specs/_features/eip8198 as an experimental fork off Heze:
- fork.md: 0xe8198000 fork version + Heze-shaped state upgrade
- beacon-chain.md: rescaled issuance (BASE_REWARD_FACTOR 42), inactivity
  penalty quotient (x9/4), and churn limits (quotients x3/2, Gwei limits x2/3)
  overriding Gloas's activation/exit/consolidation churn functions
- fork-choice.md: override get_slot_component_duration_ms so all Heze intra-slot
  deadlines rescale to the 8s slot, plus 12->8 boundary slot-counting
- configs + build wiring (constants, md_doc_paths, spec_builders, Makefile, tests)

Blob x2/3 reduction and sidecar-window x3/2 documented as notes (not injected
into the live blob schedule). Builds cleanly via make _pyspec.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
get_inactivity_penalty_quotient now returns INACTIVITY_PENALTY_QUOTIENT_EIP8198
for post-EIP8198 specs, matching the rescaled inactivity penalty. Fixes the
phase0 rewards tests when run on the eip8198 fork.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- SLOT_DURATION_MS_EIP8198 12000->10000 (minimal 6000->5000)
- Replace pre-rounded *_EIP8198 constants with exact deferred-division ratio
  applied in-formula: base reward x r, inactivity penalty x r**2, churn x r
  where r = SLOT_DURATION_MS_EIP8198 / SLOT_DURATION_MS (= 5/6). This preserves
  the fractional base reward factor (effective 53.333, not rounded 53) and makes
  SLOT_DURATION_MS_EIP8198 the single source of truth.
- Remove the CHURN_*_EIP8198 config entries (now derived).
- Revert the inactivity-penalty test oracle (no EIP8198 quotient constant); the
  phase0 rewards inactivity test joins the churn tests as documented
  known-failures (oracle mismatch, not a spec bug).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Post-fork slot boundaries are no longer genesis-aligned modulo
SLOT_DURATION_MS, so the inherited `ms_since_genesis % SLOT_DURATION_MS`
pattern breaks every intra-slot timeliness measurement after the fork.

Add a piecewise get_time_into_slot_ms helper (mirroring
get_slot_from_time) and rebase the three inherited users on it:
record_block_timeliness (attestation/PTC timeliness, proposer boost),
is_proposing_on_time (proposer reorg cutoff), and on_inclusion_list
(inclusion list deadline).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ReTeMvAQpkkTH4k5wsqihM
compute_time_at_slot inherited genesis_time + slot * SLOT_DURATION_MS,
so post-fork EL payload timestamps (asserted in
process_execution_payload) would drift ahead of wall-clock time by 2s
per slot without bound. Override it with the piecewise mapping, and
rebase get_forkchoice_store's initial store time on it so a post-fork
anchor state (checkpoint sync) starts at the correct wall-clock slot.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ReTeMvAQpkkTH4k5wsqihM
Restore the intent of the reverted oracle fix, but mirror the spec's
in-formula slot-ratio scaling instead of a quotient constant: a shared
get_expected_inactivity_penalty helper reproduces the two interleaved
multiply-then-divide steps, and the consolidation churn expectation is
rescaled and re-rounded. Fixes the two tests failing on --fork eip8198
(phase0 test_full_random_without_leak_0, gloas
test_get_consolidation_churn_limit_independent).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ReTeMvAQpkkTH4k5wsqihM
Move EIP8198_FORK_EPOCH into the config files so it compiles as a
config variable rather than a constant: the state-transition and
fork-choice time functions branch on it, and with a compiled
FAR_FUTURE_EPOCH the fork-boundary branches were unreachable in tests.

Add eip8198 tests: a fork transition suite modeled on Heze's (upgrade
covers every BeaconState field, only `fork` changes), and unittests
that override EIP8198_FORK_EPOCH to exercise the piecewise time math —
slot/time round trips, time-into-slot rebasing, compute_time_at_slot
consistency with the fork choice, post-fork anchor store init, and
on_tick catch-up across the boundary. Pass on both presets.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ReTeMvAQpkkTH4k5wsqihM
- Fix the base-reward note, which claimed the effective factor was
  exactly the rounded integer ratio -- the opposite of what the
  deferred division achieves.
- Reword the data-availability notes to prospective: the blob schedule
  entry and rescaled retention windows are scheduled together with the
  fork epoch, not yet present in the configs.
- Note that validator and p2p documents are still missing and inherit
  genesis-anchored slot math needing the same piecewise remap.
- mdformat reflow of the eip8198 docs and ruff cleanups in the new
  unittests.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ReTeMvAQpkkTH4k5wsqihM
New p2p-interface.md overriding compute_time_at_slot_ms, the millisecond
counterpart of compute_time_at_slot. The gossip validation gates
is_not_from_future_slot and is_within_slot_range are defined in terms of
it and inherit the corrected timeline; without the override, honest
post-fork messages would be rejected as from-the-future by a margin
growing 2s per slot. Boundary unittests cover the piecewise mapping and
both gates on both presets.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ReTeMvAQpkkTH4k5wsqihM
Add compute_fork_version with the EIP-8198 branch, the gossip topic
carry-over note (no new message types; topics re-key under the new fork
digest), and interpretation notes for time-sensitive constants:
ATTESTATION_PROPAGATION_SLOT_RANGE stays slot-denominated (a deliberate
wall-clock shrink), absolute allowances (clock disparity, req/resp
timeouts) must not be rescaled, and seen_ttl follows the new slot
duration through its defining formula. Cross-reference the DA retention
rescale from the Req/Resp side.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ReTeMvAQpkkTH4k5wsqihM
Prose-only: no new duty, deadline, or constant. Defines the two timing
rules — slot starts follow the remapped compute_time_at_slot timeline
(genesis-anchored derivation is incorrect post-fork), and intra-slot
deadlines rescale automatically through get_slot_component_duration_ms —
and records the behaviors inherited correctly (payload timestamp, eth1
voting period, epoch-denominated durations).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ReTeMvAQpkkTH4k5wsqihM
Replace the missing-documents note with an overview of the complete
document set and the shared piecewise time-remapping rule.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ReTeMvAQpkkTH4k5wsqihM
Address review feedback on the quick-slots specification. Drop stale
Gloas/Heze annotations from re-declared items, move the modified
compute_time_at_slot_ms back to the networking document where its base
lives, and restore the base documents' section grouping and ordering.

Remove content that does not belong: the behavior-neutral
validate_bls_to_execution_change_gossip override, the dead
whole-second helpers, the duplicated Data availability section, the
compute_seen_ttl helper (seen_ttl is a prose parameter in phase0), and
most restating notes. Replace Store.time with time_ms as the single
store clock and deprecate on_tick_per_slot.

Simplify the inactivity penalty to a single scaled-denominator
division (error is at most one Gwei per validator-epoch in realistic
states). Replace the pre-fork sidecar-retention ramp and its backfill
requirement with a lazy clamp that grows the window one epoch per
epoch from the fork, matching the Deneb retention idiom; a node
retaining the inherited window at the fork needs no pre-fork action.
Rename the fork-suffixed gas-limit helper to
is_gas_limit_transition_compatible and document that block-request
retention deliberately keeps its epoch count.
Replace the single SLOT_DURATION_MS_EIP8198 config value with a
SLOT_DURATION_SCHEDULE list-of-records config, following the blob
schedule mechanism: each entry activates a new slot duration at an
epoch, the timeline helpers accumulate the schedule's eras, and the
economics rescale by the ratio of the duration in effect to
SLOT_DURATION_MS. The schedule is empty until the fork is scheduled;
the intended first mainnet entry is 10000 ms at the fork epoch.

Deadlines become era-dependent, so get_slot_component_duration_ms and
the deadline helpers gain a slot parameter, mirroring how Gloas
redefined the same family. Drop the modified get_blob_parameters: a
duration change must instead be accompanied by a manual BLOB_SCHEDULE
entry, matching how blob throughput changes are scheduled today.
Replace the epoch-count retention selectors with wall-clock
window-start helpers that preserve the retention windows' wall-clock
length across any number of duration changes. Key the one-time gas
limit scaling to the durations in effect at the parent payload and the
bid, and note that it mirrors the execution-layer rule. Add tests for
two-era schedules, alternate durations (including an increase), and
integer-second exactness of slot boundaries.
Drop the blob-sidecar retention-start helper: the blob sidecar
Req/Resp messages are already deprecated as of
FULU_FORK_EPOCH + MIN_EPOCHS_FOR_BLOB_SIDECARS_REQUESTS, so only the
data-column window needs schedule-aware treatment. Note explicitly
that the inherited bls_to_execution_change epoch gate keeps its
pre-schedule slot computation and is provably unaffected. Make the
modified compute_time_at_slot self-contained within the beacon chain
document, fix the misleading remark about per-entry basis-point
values, and define parent_gas_limit in the builder's step 8.
Ship the schedule with a single placeholder entry: a 10000 ms slot
duration at an epoch that is TBD and intended to be the fork epoch
(5000 ms in the minimal config). An entry whose epoch is
FAR_FUTURE_EPOCH is defined as not scheduled and has no effect, and
the timeline helpers skip such entries, so the placeholder documents
the target without activating anything.
Make the unscheduled-entry rule exact: get_slot_duration_ms now also
skips FAR_FUTURE_EPOCH entries, so the placeholder has no effect for
any input, and the inertness test covers that input. State in the
introduction that epoch- and slot-denominated quantities keep their
counts, so their wall-clock spans scale with the slot duration, and
note that exit and consolidation epochs assigned before a duration
change keep their assigned epochs and consumed quota. Clarify that the
retention-start helper replaces only the rolling window term and the
inherited FULU_FORK_EPOCH floor is unchanged. Extend the config
invariants to require a same-epoch BLOB_SCHEDULE entry for every
scheduled duration change.
The probe times fall strictly between the new and the old deadlines of
the first new-duration slot, so judging block timeliness or the
proposer reorg cutoff with the wrong slot's duration flips the
assertions. Also state in the validator document that the deadline
helpers take the duty's slot, whose era determines the duration.
barnabemonnot and others added 13 commits September 5, 2026 00:35
Rebasing onto master brought several upstream changes the EIP-8198
branch predates: the mypy-to-ty switch, `Store`-typed gossip time
helpers, `Epoch`/`Slot`-typed config constants, the pydantic-based
SSZ backend, and the extra validation in Heze's `on_inclusion_list`.
Align the EIP-8198 documents and tests with these conventions: use
plain `bool` in the `Store` timeliness fields, seed the anchor PTC
votes in `get_forkchoice_store`, key `compute_time_at_slot_ms` on
the store, and port the current `on_inclusion_list` body.
Trim docstrings that restate their function's signature, shorten notes
to the one subtle point they exist to make, and condense multi-line test
comments, per review feedback on the verbosity of generated comments.
Each `SLOT_DURATION_SCHEDULE` entry now carries explicit millisecond
deadlines for its era instead of deriving them from the inherited basis
points, so the intra-slot layout can be adjusted independently whenever
the slot duration changes again. A new `get_slot_timing_parameters`
helper returns the entry in effect at an epoch, with pre-schedule
epochs falling back to the basis-point values, and the deadline helpers
read from it. The default entries place every deadline at its inherited
fraction of the slot duration, so behavior is unchanged.
Apply the review suggestions that need no design discussion: move
`SLOT_DURATION_SCHEDULE` under the existing scheduling section and drop
the extra config comments, remove the no-op `FAR_FUTURE_EPOCH` entry
and the guard that existed only to serve it, annotate the new `slot`
parameter inside the deadline helper signatures, drop the
`field(default_factory=...)` defaults from `Store`, break the
inactivity penalty denominator into named variables, delete the note
about the unchanged blob sidecar retention window, and qualify "era"
as "slot duration era".
Per review, the specs do not mention items that are unchanged.
The review asked for the whole section to go, not just its final
paragraph: the specs do not describe what an upgrade leaves unchanged.
The data-column retention window helper remains defined in the helpers
section.
Rewards and penalties granted during epoch processing pay for
participation in the previous epoch, but were priced at the current
epoch's slot duration, so the first epoch after a slot duration change
underpaid attestation rewards by the duration ratio and undersized
inactivity penalties by its square. Introduce epoch-priced variants of
the base reward helpers, point `get_flag_index_deltas` and
`get_inactivity_penalty_deltas` at the previous epoch's duration, and
keep the unmodified-signature wrappers pricing at the current epoch for
in-block rewards.
The proposer reward for a newly included attestation was priced at the
inclusion epoch, so an old-era attestation included after a slot
duration change gave the proposer a smaller cut than the attesters'
rewards for the same work. Override `process_attestation` to price the
proposer's share at the attestation's target epoch.
Couple timing changes to network upgrades and retain normal gas-limit
adjustment instead of forcing a transition jump. Use genesis timing
for normalization, preserve wall-clock sidecar retention, and cover
reward and deadline boundaries. Carry forward upstream gossip and
inclusion-list validation changes.
Exclude deprecated config fields when generating fork specifications,
while retaining the scalar in earlier forks. Derive shared test clocks
from slot-start helpers and verify that EIP-8198 exports only the timing
schedule, so legacy scalar dependencies cannot remain hidden.
@terencechain

Copy link
Copy Markdown
Contributor
  1. On retention in p2p-interface.md:

this only rescales the data column window. MIN_EPOCHS_FOR_BLOCK_REQUESTS stays an epoch count, so the block serve window shrinks by r in wall clock, while compute_weak_subjectivity_period is driven by per-epoch churn that this PR scales by r, so the WS period in epochs grows by 1/r. Those move in opposite directions and can cross.

  1. On gas limit:

Shorter slots at the same per-block gas limit raise throughput by 1/r at the fork, and the usual adjustment rule can only move 1/1024 per block, having proposers to coordinate hours ahead may be prune to bugs. Suggest the schedule entries at or after the duration change are scaled to gas_limit * new_ms // old_ms, so GAS_LIMIT_SCHEDULE stay consistent

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

eip8198 Quick Slots phase0 testing CI, actions, tests, testing infra

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants