From 3ae5518a9ba3f80438874e8acdc5af3f06e9420b Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Wed, 20 Sep 2023 16:04:25 +0800 Subject: [PATCH] Use PR#3433 style parameters to replace `INTERVALS_PER_SLOT` time setting --- presets/mainnet/altair.yaml | 8 ++++++++ presets/mainnet/phase0.yaml | 16 +++++++++++++++- presets/minimal/altair.yaml | 8 ++++++++ presets/minimal/phase0.yaml | 16 +++++++++++++++- specs/altair/light-client/p2p-interface.md | 6 +++--- specs/altair/validator.md | 12 ++++++++++-- specs/bellatrix/fork-choice.md | 4 ++-- specs/capella/fork-choice.md | 4 ++-- specs/deneb/fork-choice.md | 4 ++-- specs/phase0/fork-choice.md | 14 +++++++------- specs/phase0/validator.md | 12 ++++++++++-- .../altair/unittests/test_config_invariants.py | 7 +++++++ .../test/phase0/fork_choice/test_on_block.py | 6 +++--- .../phase0/unittests/test_config_invariants.py | 3 ++- 14 files changed, 94 insertions(+), 26 deletions(-) diff --git a/presets/mainnet/altair.yaml b/presets/mainnet/altair.yaml index 813ef72122..c278d3807d 100644 --- a/presets/mainnet/altair.yaml +++ b/presets/mainnet/altair.yaml @@ -24,3 +24,11 @@ EPOCHS_PER_SYNC_COMMITTEE_PERIOD: 256 MIN_SYNC_COMMITTEE_PARTICIPANTS: 1 # SLOTS_PER_EPOCH * EPOCHS_PER_SYNC_COMMITTEE_PERIOD (= 32 * 256) UPDATE_TIMEOUT: 8192 + + +# Validator duties +# --------------------------------------------------------------- +# 4000ms +SYNC_MESSAGE_DUE_MS: 4000 +# 8000ms +CONTRIBUTION_DUE_MS: 8000 diff --git a/presets/mainnet/phase0.yaml b/presets/mainnet/phase0.yaml index 00133ba369..8f9de991c2 100644 --- a/presets/mainnet/phase0.yaml +++ b/presets/mainnet/phase0.yaml @@ -85,4 +85,18 @@ MAX_ATTESTATIONS: 128 # 2**4 (= 16) MAX_DEPOSITS: 16 # 2**4 (= 16) -MAX_VOLUNTARY_EXITS: 16 \ No newline at end of file +MAX_VOLUNTARY_EXITS: 16 + + +# Fork choice +# --------------------------------------------------------------- +# 4000ms +LATE_BLOCK_CUTOFF_MS: 4000 + + +# Validator duties +# --------------------------------------------------------------- +# 4000ms +ATTESTATION_DUE_MS: 4000 +# 8000ms +AGGREGATE_DUE_MS: 8000 diff --git a/presets/minimal/altair.yaml b/presets/minimal/altair.yaml index 5e472c49cf..3b0b759968 100644 --- a/presets/minimal/altair.yaml +++ b/presets/minimal/altair.yaml @@ -24,3 +24,11 @@ EPOCHS_PER_SYNC_COMMITTEE_PERIOD: 8 MIN_SYNC_COMMITTEE_PARTICIPANTS: 1 # SLOTS_PER_EPOCH * EPOCHS_PER_SYNC_COMMITTEE_PERIOD (= 8 * 8) UPDATE_TIMEOUT: 64 + + +# Validator duties +# --------------------------------------------------------------- +# [customized] 2000ms +SYNC_MESSAGE_DUE_MS: 2000 +# [customized] 4000ms +CONTRIBUTION_DUE_MS: 4000 diff --git a/presets/minimal/phase0.yaml b/presets/minimal/phase0.yaml index d9a6a2b6c0..6e3bc90a46 100644 --- a/presets/minimal/phase0.yaml +++ b/presets/minimal/phase0.yaml @@ -85,4 +85,18 @@ MAX_ATTESTATIONS: 128 # 2**4 (= 16) MAX_DEPOSITS: 16 # 2**4 (= 16) -MAX_VOLUNTARY_EXITS: 16 \ No newline at end of file +MAX_VOLUNTARY_EXITS: 16 + + +# Fork choice +# --------------------------------------------------------------- +# [customized] 2000ms +LATE_BLOCK_CUTOFF_MS: 2000 + + +# Validator duties +# --------------------------------------------------------------- +# [customized] 2000ms +ATTESTATION_DUE_MS: 2000 +# [customized] 4000ms +AGGREGATE_DUE_MS: 4000 diff --git a/specs/altair/light-client/p2p-interface.md b/specs/altair/light-client/p2p-interface.md index 63e1ac4370..09a86ab2dc 100644 --- a/specs/altair/light-client/p2p-interface.md +++ b/specs/altair/light-client/p2p-interface.md @@ -60,7 +60,7 @@ This topic is used to propagate the latest `LightClientFinalityUpdate` to light The following validations MUST pass before forwarding the `finality_update` on the network. - _[IGNORE]_ The `finalized_header.beacon.slot` is greater than that of all previously forwarded `finality_update`s -- _[IGNORE]_ The `finality_update` is received after the block at `signature_slot` was given enough time to propagate through the network -- i.e. validate that one-third of `finality_update.signature_slot` has transpired (`SECONDS_PER_SLOT / INTERVALS_PER_SLOT` seconds after the start of the slot, with a `MAXIMUM_GOSSIP_CLOCK_DISPARITY` allowance) +- _[IGNORE]_ The `finality_update` is received after the block at `signature_slot` was given enough time to propagate through the network -- i.e. validate that `SYNC_MESSAGE_DUE_MS` milliseconds after the start of the `finality_update.signature_slot` slot has transpired, with a `MAXIMUM_GOSSIP_CLOCK_DISPARITY` allowance. For full nodes, the following validations MUST additionally pass before forwarding the `finality_update` on the network. - _[IGNORE]_ The received `finality_update` matches the locally computed one exactly (as defined in [`create_light_client_finality_update`](./full-node.md#create_light_client_finality_update)) @@ -88,7 +88,7 @@ This topic is used to propagate the latest `LightClientOptimisticUpdate` to ligh The following validations MUST pass before forwarding the `optimistic_update` on the network. - _[IGNORE]_ The `attested_header.beacon.slot` is greater than that of all previously forwarded `optimistic_update`s -- _[IGNORE]_ The `optimistic_update` is received after the block at `signature_slot` was given enough time to propagate through the network -- i.e. validate that one-third of `optimistic_update.signature_slot` has transpired (`SECONDS_PER_SLOT / INTERVALS_PER_SLOT` seconds after the start of the slot, with a `MAXIMUM_GOSSIP_CLOCK_DISPARITY` allowance) +- _[IGNORE]_ The `optimistic_update` is received after the block at `signature_slot` was given enough time to propagate through the network -- i.e. validate that `SYNC_MESSAGE_DUE_MS` milliseconds after the start of the `optimistic_update.signature_slot` slot has transpired, with a `MAXIMUM_GOSSIP_CLOCK_DISPARITY` allowance. For full nodes, the following validations MUST additionally pass before forwarding the `optimistic_update` on the network. - _[IGNORE]_ The received `optimistic_update` matches the locally computed one exactly (as defined in [`create_light_client_optimistic_update`](./full-node.md#create_light_client_optimistic_update)) @@ -276,4 +276,4 @@ Whenever fork choice selects a new head block with a sync aggregate participatio - If `finalized_header.beacon.slot` increased, a `LightClientFinalityUpdate` SHOULD be broadcasted to the pubsub topic `light_client_finality_update` if no matching message has not yet been forwarded as part of gossip validation. - If `attested_header.beacon.slot` increased, a `LightClientOptimisticUpdate` SHOULD be broadcasted to the pubsub topic `light_client_optimistic_update` if no matching message has not yet been forwarded as part of gossip validation. -These messages SHOULD be broadcasted after one-third of `slot` has transpired (`SECONDS_PER_SLOT / INTERVALS_PER_SLOT` seconds after the start of the slot). To ensure that the corresponding block was given enough time to propagate through the network, they SHOULD NOT be sent earlier. Note that this is different from how other messages are handled, e.g., attestations, which may be sent early. +These messages SHOULD be broadcasted after `SYNC_MESSAGE_DUE_MS` milliseconds after the start of the slot. To ensure that the corresponding block was given enough time to propagate through the network, they SHOULD NOT be sent earlier. Note that this is different from how other messages are handled, e.g., attestations, which may be sent early. diff --git a/specs/altair/validator.md b/specs/altair/validator.md index 3602377acd..6f7442865e 100644 --- a/specs/altair/validator.md +++ b/specs/altair/validator.md @@ -12,6 +12,7 @@ This is an accompanying document to [Altair -- The Beacon Chain](./beacon-chain. - [Prerequisites](#prerequisites) - [Constants](#constants) - [Misc](#misc) +- [Presets](#presets) - [Containers](#containers) - [`SyncCommitteeMessage`](#synccommitteemessage) - [`SyncCommitteeContribution`](#synccommitteecontribution) @@ -71,6 +72,13 @@ Please see this document before continuing and use as a reference throughout. | `TARGET_AGGREGATORS_PER_SYNC_SUBCOMMITTEE` | `2**4` (= 16) | validators | | `SYNC_COMMITTEE_SUBNET_COUNT` | `4` | The number of sync committee subnets used in the gossipsub aggregation protocol. | +## Presets + +| Name | Value | +| --------------------- | ----------- | +| `SYNC_MESSAGE_DUE_MS` | `4000` | +| `CONTRIBUTION_DUE_MS` | `8000` | + ## Containers ### `SyncCommitteeMessage` @@ -263,7 +271,7 @@ This process occurs each slot. If a validator is in the current sync committee (i.e. `is_assigned_to_sync_committee()` above returns `True`), then for every `slot` in the current sync committee period, the validator should prepare a `SyncCommitteeMessage` for the previous slot (`slot - 1`) according to the logic in `get_sync_committee_message` as soon as they have determined the head block of `slot - 1`. This means that when assigned to `slot` a `SyncCommitteeMessage` is prepared and broadcast in `slot-1 ` instead of `slot`. This logic is triggered upon the same conditions as when producing an attestation. -Meaning, a sync committee member should produce and broadcast a `SyncCommitteeMessage` either when (a) the validator has received a valid block from the expected block proposer for the current `slot` or (b) one-third of the slot has transpired (`SECONDS_PER_SLOT / INTERVALS_PER_SLOT` seconds after the start of the slot) -- whichever comes first. +Meaning, a sync committee member should produce and broadcast a `SyncCommitteeMessage` either when (a) the validator has received a valid block from the expected block proposer for the current `slot` or (b) `SYNC_MESSAGE_DUE_MS` milliseconds after the start of the slot has transpired -- whichever comes first. `get_sync_committee_message(state, block_root, validator_index, privkey)` assumes the parameter `state` is the head state corresponding to processing the block up to the current slot as determined by the fork choice (including any empty slots up to the current slot processed with `process_slots` on top of the latest block), `block_root` is the root of the head block, `validator_index` is the index of the validator in the registry `state.validators` controlled by `privkey`, and `privkey` is the BLS private key for the validator. @@ -380,7 +388,7 @@ The collection of input signatures should include one signature per validator wh ##### Broadcast sync committee contribution -If the validator is selected to aggregate (`is_sync_committee_aggregator()`), then they broadcast their best aggregate as a `SignedContributionAndProof` to the global aggregate channel (`sync_committee_contribution_and_proof` topic) two-thirds of the way through the `slot`-that is, `SECONDS_PER_SLOT * 2 / INTERVALS_PER_SLOT` seconds after the start of `slot`. +If the validator is selected to aggregate (`is_sync_committee_aggregator()`), then they broadcast their best aggregate as a `SignedContributionAndProof` to the global aggregate channel (`sync_committee_contribution_and_proof` topic) `CONTRIBUTION_DUE_MS` milliseconds after the start of `slot` has transpired. Selection proofs are provided in `ContributionAndProof` to prove to the gossip channel that the validator has been selected as an aggregator. diff --git a/specs/bellatrix/fork-choice.md b/specs/bellatrix/fork-choice.md index c8475195fc..a0a3ad29f2 100644 --- a/specs/bellatrix/fork-choice.md +++ b/specs/bellatrix/fork-choice.md @@ -193,9 +193,9 @@ def on_block(store: Store, signed_block: SignedBeaconBlock) -> None: # Add proposer score boost if the block is timely time_into_slot = (store.time - store.genesis_time) % SECONDS_PER_SLOT - is_before_attesting_interval = time_into_slot < SECONDS_PER_SLOT // INTERVALS_PER_SLOT + is_before_late_block_cutoff = time_into_slot * 1000 < LATE_BLOCK_CUTOFF_MS is_first_block = store.proposer_boost_root == Root() - if get_current_slot(store) == block.slot and is_before_attesting_interval and is_first_block: + if get_current_slot(store) == block.slot and is_before_late_block_cutoff and is_first_block: store.proposer_boost_root = hash_tree_root(block) # Update checkpoints in store if necessary diff --git a/specs/capella/fork-choice.md b/specs/capella/fork-choice.md index a830080c11..21e14935fd 100644 --- a/specs/capella/fork-choice.md +++ b/specs/capella/fork-choice.md @@ -105,9 +105,9 @@ def on_block(store: Store, signed_block: SignedBeaconBlock) -> None: # Add proposer score boost if the block is timely time_into_slot = (store.time - store.genesis_time) % SECONDS_PER_SLOT - is_before_attesting_interval = time_into_slot < SECONDS_PER_SLOT // INTERVALS_PER_SLOT + is_before_late_block_cutoff = time_into_slot * 1000 < LATE_BLOCK_CUTOFF_MS is_first_block = store.proposer_boost_root == Root() - if get_current_slot(store) == block.slot and is_before_attesting_interval and is_first_block: + if get_current_slot(store) == block.slot and is_before_late_block_cutoff and is_first_block: store.proposer_boost_root = hash_tree_root(block) # Update checkpoints in store if necessary diff --git a/specs/deneb/fork-choice.md b/specs/deneb/fork-choice.md index 5a700cc7a6..96df315f9e 100644 --- a/specs/deneb/fork-choice.md +++ b/specs/deneb/fork-choice.md @@ -105,9 +105,9 @@ def on_block(store: Store, signed_block: SignedBeaconBlock) -> None: # Add proposer score boost if the block is timely time_into_slot = (store.time - store.genesis_time) % SECONDS_PER_SLOT - is_before_attesting_interval = time_into_slot < SECONDS_PER_SLOT // INTERVALS_PER_SLOT + is_before_late_block_cutoff = time_into_slot * 1000 < LATE_BLOCK_CUTOFF_MS is_first_block = store.proposer_boost_root == Root() - if get_current_slot(store) == block.slot and is_before_attesting_interval and is_first_block: + if get_current_slot(store) == block.slot and is_before_late_block_cutoff and is_first_block: store.proposer_boost_root = hash_tree_root(block) # Update checkpoints in store if necessary diff --git a/specs/phase0/fork-choice.md b/specs/phase0/fork-choice.md index 8b52186dda..6c06c53b85 100644 --- a/specs/phase0/fork-choice.md +++ b/specs/phase0/fork-choice.md @@ -7,7 +7,7 @@ - [Introduction](#introduction) - [Fork choice](#fork-choice) - - [Constant](#constant) + - [Presets](#presets) - [Configuration](#configuration) - [Helpers](#helpers) - [`LatestMessage`](#latestmessage) @@ -68,11 +68,11 @@ Any of the above handlers that trigger an unhandled exception (e.g. a failed ass 5) **Implementation**: The implementation found in this specification is constructed for ease of understanding rather than for optimization in computation, space, or any other resource. A number of optimized alternatives can be found [here](https://github.com/protolambda/lmd-ghost). -### Constant +### Presets -| Name | Value | -| -------------------- | ----------- | -| `INTERVALS_PER_SLOT` | `uint64(3)` | +| Name | Value | +| ---------------------- | ----------- | +| `LATE_BLOCK_CUTOFF_MS` | `4000` | ### Configuration @@ -538,9 +538,9 @@ def on_block(store: Store, signed_block: SignedBeaconBlock) -> None: # Add proposer score boost if the block is timely time_into_slot = (store.time - store.genesis_time) % SECONDS_PER_SLOT - is_before_attesting_interval = time_into_slot < SECONDS_PER_SLOT // INTERVALS_PER_SLOT + is_before_late_block_cutoff = time_into_slot * 1000 < LATE_BLOCK_CUTOFF_MS is_first_block = store.proposer_boost_root == Root() - if get_current_slot(store) == block.slot and is_before_attesting_interval and is_first_block: + if get_current_slot(store) == block.slot and is_before_late_block_cutoff and is_first_block: store.proposer_boost_root = hash_tree_root(block) # Update checkpoints in store if necessary diff --git a/specs/phase0/validator.md b/specs/phase0/validator.md index 86b230654c..51ded5189a 100644 --- a/specs/phase0/validator.md +++ b/specs/phase0/validator.md @@ -12,6 +12,7 @@ This is an accompanying document to [Phase 0 -- The Beacon Chain](./beacon-chain - [Prerequisites](#prerequisites) - [Constants](#constants) - [Misc](#misc) +- [Presets](#presets) - [Containers](#containers) - [`Eth1Block`](#eth1block) - [`AggregateAndProof`](#aggregateandproof) @@ -89,6 +90,13 @@ All terminology, constants, functions, and protocol mechanics defined in the [Ph | - | - | :-: | :-: | | `TARGET_AGGREGATORS_PER_COMMITTEE` | `2**4` (= 16) | validators | +## Presets + +| Name | Value | +| --------------------- | ----------- | +| `ATTESTATION_DUE_MS` | `4000` | +| `AGGREGATE_DUE_MS` | `8000` | + ## Containers ### `Eth1Block` @@ -445,7 +453,7 @@ def get_block_signature(state: BeaconState, block: BeaconBlock, privkey: int) -> A validator is expected to create, sign, and broadcast an attestation during each epoch. The `committee`, assigned `index`, and assigned `slot` for which the validator performs this role during an epoch are defined by `get_committee_assignment(state, epoch, validator_index)`. -A validator should create and broadcast the `attestation` to the associated attestation subnet when either (a) the validator has received a valid block from the expected block proposer for the assigned `slot` or (b) `1 / INTERVALS_PER_SLOT` of the `slot` has transpired (`SECONDS_PER_SLOT / INTERVALS_PER_SLOT` seconds after the start of `slot`) -- whichever comes _first_. +A validator should create and broadcast the `attestation` to the associated attestation subnet when either (a) the validator has received a valid block from the expected block proposer for the assigned `slot` or (b) `ATTESTATION_DUE_MS` milliseconds after the start of `slot` has transpired -- whichever comes _first_. *Note*: Although attestations during `GENESIS_EPOCH` do not count toward FFG finality, these initial attestations do give weight to the fork choice, are rewarded, and should be made. @@ -570,7 +578,7 @@ def get_aggregate_signature(attestations: Sequence[Attestation]) -> BLSSignature #### Broadcast aggregate -If the validator is selected to aggregate (`is_aggregator`), then they broadcast their best aggregate as a `SignedAggregateAndProof` to the global aggregate channel (`beacon_aggregate_and_proof`) `2 / INTERVALS_PER_SLOT` of the way through the `slot`-that is, `SECONDS_PER_SLOT * 2 / INTERVALS_PER_SLOT` seconds after the start of `slot`. +If the validator is selected to aggregate (`is_aggregator`), then they broadcast their best aggregate as a `SignedAggregateAndProof` to the global aggregate channel (`beacon_aggregate_and_proof`) `AGGREGATE_DUE_MS` milliseconds after the start of `slot` has transpired. Selection proofs are provided in `AggregateAndProof` to prove to the gossip channel that the validator has been selected as an aggregator. diff --git a/tests/core/pyspec/eth2spec/test/altair/unittests/test_config_invariants.py b/tests/core/pyspec/eth2spec/test/altair/unittests/test_config_invariants.py index 91be0a0a61..f4398ca02b 100644 --- a/tests/core/pyspec/eth2spec/test/altair/unittests/test_config_invariants.py +++ b/tests/core/pyspec/eth2spec/test/altair/unittests/test_config_invariants.py @@ -20,3 +20,10 @@ def test_weight_denominator(spec, state): @spec_state_test def test_inactivity_score(spec, state): assert spec.config.INACTIVITY_SCORE_BIAS <= spec.config.INACTIVITY_SCORE_RECOVERY_RATE + + +@with_altair_and_later +@spec_state_test +def test_fork_choice(spec, state): + assert spec.SYNC_MESSAGE_DUE_MS < spec.config.SECONDS_PER_SLOT * 1000 + assert spec.CONTRIBUTION_DUE_MS < spec.config.SECONDS_PER_SLOT * 1000 diff --git a/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_on_block.py b/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_on_block.py index cd41350496..b3df8e6a9b 100644 --- a/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_on_block.py +++ b/tests/core/pyspec/eth2spec/test/phase0/fork_choice/test_on_block.py @@ -465,7 +465,7 @@ def test_proposer_boost(spec, state): # Process block on timely arrival just before end of boost interval time = (store.genesis_time + block.slot * spec.config.SECONDS_PER_SLOT + - spec.config.SECONDS_PER_SLOT // spec.INTERVALS_PER_SLOT - 1) + (spec.LATE_BLOCK_CUTOFF_MS - 1) // 1000) on_tick_and_append_step(spec, store, time, test_steps) yield from add_block(spec, store, signed_block, test_steps) assert store.proposer_boost_root == spec.hash_tree_root(block) @@ -524,7 +524,7 @@ def test_proposer_boost_root_same_slot_untimely_block(spec, state): # Process block on untimely arrival in the same slot time = (store.genesis_time + block.slot * spec.config.SECONDS_PER_SLOT + - spec.config.SECONDS_PER_SLOT // spec.INTERVALS_PER_SLOT) + spec.LATE_BLOCK_CUTOFF_MS // 1000) on_tick_and_append_step(spec, store, time, test_steps) yield from add_block(spec, store, signed_block, test_steps) @@ -559,7 +559,7 @@ def test_proposer_boost_is_first_block(spec, state): # Process block on timely arrival just before end of boost interval time = (store.genesis_time + block_a.slot * spec.config.SECONDS_PER_SLOT + - spec.config.SECONDS_PER_SLOT // spec.INTERVALS_PER_SLOT - 1) + (spec.LATE_BLOCK_CUTOFF_MS - 1) // 1000) on_tick_and_append_step(spec, store, time, test_steps) yield from add_block(spec, store, signed_block_a, test_steps) # `proposer_boost_root` is now `block_a` diff --git a/tests/core/pyspec/eth2spec/test/phase0/unittests/test_config_invariants.py b/tests/core/pyspec/eth2spec/test/phase0/unittests/test_config_invariants.py index 6216ea2f00..f35012a498 100644 --- a/tests/core/pyspec/eth2spec/test/phase0/unittests/test_config_invariants.py +++ b/tests/core/pyspec/eth2spec/test/phase0/unittests/test_config_invariants.py @@ -89,5 +89,6 @@ def test_networking(spec, state): @with_all_phases @spec_state_test def test_fork_choice(spec, state): - assert spec.INTERVALS_PER_SLOT < spec.config.SECONDS_PER_SLOT + assert spec.ATTESTATION_DUE_MS < spec.config.SECONDS_PER_SLOT * 1000 + assert spec.AGGREGATE_DUE_MS < spec.config.SECONDS_PER_SLOT * 1000 assert spec.config.PROPOSER_SCORE_BOOST <= 100