Use explicit slot component timing configs (#15999)

* Use new timing configs (due BPS)

* Bastin's feedback
This commit is contained in:
terence
2025-11-13 16:55:32 -05:00
committed by GitHub
parent 7ba60d93f2
commit f77b78943a
35 changed files with 402 additions and 100 deletions

View File

@@ -4,7 +4,7 @@ import "github.com/OffchainLabs/prysm/v7/consensus-types/primitives"
const BasisPoints = primitives.BP(10000)
// SlotBP returns the basis points for a given slot.
// SlotBP returns the duration of a slot expressed in milliseconds, represented as basis points of a slot.
func SlotBP() primitives.BP {
return primitives.BP(12000)
return primitives.BP(BeaconConfig().SlotDurationMillis())
}

View File

@@ -66,6 +66,7 @@ type BeaconChainConfig struct {
GenesisDelay uint64 `yaml:"GENESIS_DELAY" spec:"true"` // GenesisDelay is the minimum number of seconds to delay starting the Ethereum Beacon Chain genesis. Must be at least 1 second.
MinAttestationInclusionDelay primitives.Slot `yaml:"MIN_ATTESTATION_INCLUSION_DELAY" spec:"true"` // MinAttestationInclusionDelay defines how many slots validator has to wait to include attestation for beacon block.
SecondsPerSlot uint64 `yaml:"SECONDS_PER_SLOT" spec:"true"` // SecondsPerSlot is how many seconds are in a single slot.
SlotDurationMilliseconds uint64 `yaml:"SLOT_DURATION_MS" spec:"true"` // SlotDurationMilliseconds is the slot time expressed in milliseconds.
SlotsPerEpoch primitives.Slot `yaml:"SLOTS_PER_EPOCH" spec:"true"` // SlotsPerEpoch is the number of slots in an epoch.
SqrRootSlotsPerEpoch primitives.Slot // SqrRootSlotsPerEpoch is a hard coded value where we take the square root of `SlotsPerEpoch` and round down.
MinSeedLookahead primitives.Epoch `yaml:"MIN_SEED_LOOKAHEAD" spec:"true"` // MinSeedLookahead is the duration of randao look ahead seed.
@@ -84,6 +85,11 @@ type BeaconChainConfig struct {
ReorgParentWeightThreshold uint64 `yaml:"REORG_PARENT_WEIGHT_THRESHOLD" spec:"true"` // ReorgParentWeightThreshold defines a value that is a % of the committee weight to consider a parent block strong and subject its child to being orphaned.
ReorgMaxEpochsSinceFinalization primitives.Epoch `yaml:"REORG_MAX_EPOCHS_SINCE_FINALIZATION" spec:"true"` // This defines a limit to consider safe to orphan a block if the network is finalizing
IntervalsPerSlot uint64 `yaml:"INTERVALS_PER_SLOT"` // IntervalsPerSlot defines the number of fork choice intervals in a slot defined in the fork choice spec.
ProposerReorgCutoffBPS primitives.BP `yaml:"PROPOSER_REORG_CUTOFF_BPS" spec:"true"` // ProposerReorgCutoffBPS defines the proposer reorg deadline in basis points of the slot.
AttestationDueBPS primitives.BP `yaml:"ATTESTATION_DUE_BPS" spec:"true"` // AttestationDueBPS defines the attestation due time in basis points of the slot.
AggregrateDueBPS primitives.BP `yaml:"AGGREGRATE_DUE_BPS" spec:"true"` // AggregrateDueBPS defines the aggregate due time in basis points of the slot.
SyncMessageDueBPS primitives.BP `yaml:"SYNC_MESSAGE_DUE_BPS" spec:"true"` // SyncMessageDueBPS defines the sync message due time in basis points of the slot.
ContributionDueBPS primitives.BP `yaml:"CONTRIBUTION_DUE_BPS" spec:"true"` // ContributionDueBPS defines the contribution due time in basis points of the slot.
// Ethereum PoW parameters.
DepositChainID uint64 `yaml:"DEPOSIT_CHAIN_ID" spec:"true"` // DepositChainID of the eth1 network. This used for replay protection.
@@ -221,7 +227,6 @@ type BeaconChainConfig struct {
// Light client
MinSyncCommitteeParticipants uint64 `yaml:"MIN_SYNC_COMMITTEE_PARTICIPANTS" spec:"true"` // MinSyncCommitteeParticipants defines the minimum amount of sync committee participants for which the light client acknowledges the signature.
MaxRequestLightClientUpdates uint64 `yaml:"MAX_REQUEST_LIGHT_CLIENT_UPDATES" spec:"true"` // MaxRequestLightClientUpdates defines the maximum amount of light client updates that can be requested in a single request.
SyncMessageDueBPS uint64 `yaml:"SYNC_MESSAGE_DUE_BPS" spec:"true"` // SyncMessageDueBPS defines the due time for a sync message.
// Bellatrix
TerminalBlockHash common.Hash `yaml:"TERMINAL_BLOCK_HASH" spec:"true"` // TerminalBlockHash of beacon chain.
@@ -741,10 +746,29 @@ func SlotsForEpochs(count primitives.Epoch, b *BeaconChainConfig) primitives.Slo
// SlotsDuration returns the time duration of the given number of slots.
func SlotsDuration(count primitives.Slot, b *BeaconChainConfig) time.Duration {
return time.Duration(count) * SecondsPerSlot(b)
return time.Duration(count) * b.SlotDuration()
}
// SecondsPerSlot returns the time duration of a single slot.
func SecondsPerSlot(b *BeaconChainConfig) time.Duration {
return time.Duration(b.SecondsPerSlot) * time.Second
return b.SlotDuration()
}
// SlotDuration returns the configured slot duration as a time.Duration.
func (b *BeaconChainConfig) SlotDuration() time.Duration {
return time.Duration(b.SlotDurationMillis()) * time.Millisecond
}
// SlotDurationMillis returns the configured slot duration in milliseconds.
func (b *BeaconChainConfig) SlotDurationMillis() uint64 {
if b.SlotDurationMilliseconds > 0 {
return b.SlotDurationMilliseconds
}
return b.SecondsPerSlot * 1000
}
// SlotComponentDuration returns the duration representing the given portion (in basis points) of a slot.
func (b *BeaconChainConfig) SlotComponentDuration(bp primitives.BP) time.Duration {
ms := uint64(bp) * b.SlotDurationMillis() / uint64(BasisPoints)
return time.Duration(ms) * time.Millisecond
}

View File

@@ -6,6 +6,7 @@ import (
"math"
"sync"
"testing"
"time"
"github.com/OffchainLabs/prysm/v7/config/params"
"github.com/OffchainLabs/prysm/v7/consensus-types/primitives"
@@ -202,6 +203,127 @@ func fillGVR(value byte) [32]byte {
return gvr
}
func TestBeaconChainConfigSlotDuration(t *testing.T) {
t.Parallel()
tests := []struct {
name string
cfg params.BeaconChainConfig
want time.Duration
}{
{
name: "explicit duration",
cfg: params.BeaconChainConfig{SlotDurationMilliseconds: 12_000},
want: 12 * time.Second,
},
{
name: "fallback to seconds per slot",
cfg: params.BeaconChainConfig{SecondsPerSlot: 8},
want: 8 * time.Second,
},
{
name: "milliseconds override seconds per slot",
cfg: params.BeaconChainConfig{
SlotDurationMilliseconds: 7_000,
SecondsPerSlot: 4,
},
want: 7 * time.Second,
},
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
require.Equal(t, tt.want, tt.cfg.SlotDuration())
})
}
}
func TestBeaconChainConfigSlotDurationMillis(t *testing.T) {
t.Parallel()
tests := []struct {
name string
cfg params.BeaconChainConfig
want uint64
}{
{
name: "uses slot duration milliseconds when set",
cfg: params.BeaconChainConfig{SlotDurationMilliseconds: 4_800},
want: 4_800,
},
{
name: "derives from seconds per slot when unset",
cfg: params.BeaconChainConfig{SecondsPerSlot: 6},
want: 6_000,
},
{
name: "returns zero when no duration configured",
cfg: params.BeaconChainConfig{},
want: 0,
},
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
require.Equal(t, tt.want, tt.cfg.SlotDurationMillis())
})
}
}
func TestBeaconChainConfigSlotComponentDuration(t *testing.T) {
t.Parallel()
tests := []struct {
name string
cfg params.BeaconChainConfig
bp primitives.BP
want time.Duration
}{
{
name: "zero basis points produces zero duration",
cfg: params.BeaconChainConfig{SlotDurationMilliseconds: 12_000},
bp: 0,
want: 0,
},
{
name: "full slot basis points matches slot duration",
cfg: params.BeaconChainConfig{SlotDurationMilliseconds: 12_000},
bp: params.BasisPoints,
want: 12 * time.Second,
},
{
name: "quarter slot with explicit milliseconds",
cfg: params.BeaconChainConfig{SlotDurationMilliseconds: 12_000},
bp: params.BasisPoints / 4,
want: 3 * time.Second,
},
{
name: "fractional slot rounds down",
cfg: params.BeaconChainConfig{SlotDurationMilliseconds: 1_001},
bp: params.BasisPoints / 3,
want: 333 * time.Millisecond,
},
{
name: "uses seconds per slot fallback",
cfg: params.BeaconChainConfig{SecondsPerSlot: 9},
bp: params.BasisPoints / 2,
want: 4500 * time.Millisecond,
},
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
require.Equal(t, tt.want, tt.cfg.SlotComponentDuration(tt.bp))
})
}
}
func TestEntryWithForkDigest(t *testing.T) {
var zero [32]byte
one := fillGVR(byte(1))

View File

@@ -102,6 +102,7 @@ func compareConfigs(t *testing.T, expected, actual *BeaconChainConfig) {
require.DeepEqual(t, expected.GenesisDelay, actual.GenesisDelay)
require.DeepEqual(t, expected.MinAttestationInclusionDelay, actual.MinAttestationInclusionDelay)
require.DeepEqual(t, expected.SecondsPerSlot, actual.SecondsPerSlot)
require.DeepEqual(t, expected.SlotDurationMilliseconds, actual.SlotDurationMilliseconds)
require.DeepEqual(t, expected.SlotsPerEpoch, actual.SlotsPerEpoch)
require.DeepEqual(t, expected.SqrRootSlotsPerEpoch, actual.SqrRootSlotsPerEpoch)
require.DeepEqual(t, expected.MinSeedLookahead, actual.MinSeedLookahead)

View File

@@ -187,6 +187,7 @@ func ConfigToYaml(cfg *BeaconChainConfig) []byte {
fmt.Sprintf("GENESIS_FORK_VERSION: %#x", cfg.GenesisForkVersion),
fmt.Sprintf("CHURN_LIMIT_QUOTIENT: %d", cfg.ChurnLimitQuotient),
fmt.Sprintf("SECONDS_PER_SLOT: %d", cfg.SecondsPerSlot),
fmt.Sprintf("SLOT_DURATION_MS: %d", cfg.SlotDurationMilliseconds),
fmt.Sprintf("SLOTS_PER_EPOCH: %d", cfg.SlotsPerEpoch),
fmt.Sprintf("SECONDS_PER_ETH1_BLOCK: %d", cfg.SecondsPerETH1Block),
fmt.Sprintf("ETH1_FOLLOW_DISTANCE: %d", cfg.Eth1FollowDistance),
@@ -241,6 +242,11 @@ func ConfigToYaml(cfg *BeaconChainConfig) []byte {
fmt.Sprintf("MIN_EPOCHS_FOR_BLOCK_REQUESTS: %d", int(cfg.MinEpochsForBlockRequests)),
fmt.Sprintf("MIN_PER_EPOCH_CHURN_LIMIT_ELECTRA: %d", cfg.MinPerEpochChurnLimitElectra),
fmt.Sprintf("MAX_BLOBS_PER_BLOCK: %d", cfg.DeprecatedMaxBlobsPerBlock),
fmt.Sprintf("PROPOSER_REORG_CUTOFF_BPS: %d", cfg.ProposerReorgCutoffBPS),
fmt.Sprintf("ATTESTATION_DUE_BPS: %d", cfg.AttestationDueBPS),
fmt.Sprintf("AGGREGRATE_DUE_BPS: %d", cfg.AggregrateDueBPS),
fmt.Sprintf("SYNC_MESSAGE_DUE_BPS: %d", cfg.SyncMessageDueBPS),
fmt.Sprintf("CONTRIBUTION_DUE_BPS: %d", cfg.ContributionDueBPS),
}
if len(cfg.BlobSchedule) > 0 {

View File

@@ -27,11 +27,9 @@ var placeholderFields = []string{
"AGGREGATE_DUE_BPS",
"AGGREGATE_DUE_BPS_GLOAS",
"ATTESTATION_DEADLINE",
"ATTESTATION_DUE_BPS",
"ATTESTATION_DUE_BPS_GLOAS",
"BLOB_SIDECAR_SUBNET_COUNT_FULU",
"CELLS_PER_EXT_BLOB",
"CONTRIBUTION_DUE_BPS",
"CONTRIBUTION_DUE_BPS_GLOAS",
"EIP6110_FORK_EPOCH",
"EIP6110_FORK_VERSION",
@@ -60,10 +58,7 @@ var placeholderFields = []string{
"PAYLOAD_ATTESTATION_DUE_BPS",
"PROPOSER_INCLUSION_LIST_CUTOFF",
"PROPOSER_INCLUSION_LIST_CUTOFF_BPS",
"PROPOSER_REORG_CUTOFF_BPS",
"PROPOSER_SCORE_BOOST_EIP7732",
"PROPOSER_SELECTION_GAP",
"SLOT_DURATION_MS",
"SYNC_MESSAGE_DUE_BPS_GLOAS",
"TARGET_NUMBER_OF_PEERS",
"UPDATE_TIMEOUT",
@@ -101,6 +96,10 @@ func assertEqualConfigs(t *testing.T, name string, fields []string, expected, ac
assert.Equal(t, expected.HysteresisQuotient, actual.HysteresisQuotient, "%s: HysteresisQuotient", name)
assert.Equal(t, expected.HysteresisDownwardMultiplier, actual.HysteresisDownwardMultiplier, "%s: HysteresisDownwardMultiplier", name)
assert.Equal(t, expected.HysteresisUpwardMultiplier, actual.HysteresisUpwardMultiplier, "%s: HysteresisUpwardMultiplier", name)
assert.Equal(t, expected.AttestationDueBPS, actual.AttestationDueBPS, "%s: AttestationDueBPS", name)
assert.Equal(t, expected.AggregrateDueBPS, actual.AggregrateDueBPS, "%s: AggregrateDueBPS", name)
assert.Equal(t, expected.ContributionDueBPS, actual.ContributionDueBPS, "%s: ContributionDueBPS", name)
assert.Equal(t, expected.ProposerReorgCutoffBPS, actual.ProposerReorgCutoffBPS, "%s: ProposerReorgCutoffBPS", name)
assert.Equal(t, expected.SyncMessageDueBPS, actual.SyncMessageDueBPS, "%s: SyncMessageDueBPS", name)
// Validator params.
@@ -129,6 +128,7 @@ func assertEqualConfigs(t *testing.T, name string, fields []string, expected, ac
// Time parameters.
assert.Equal(t, expected.GenesisDelay, actual.GenesisDelay, "%s: GenesisDelay", name)
assert.Equal(t, expected.SecondsPerSlot, actual.SecondsPerSlot, "%s: SecondsPerSlot", name)
assert.Equal(t, expected.SlotDurationMilliseconds, actual.SlotDurationMilliseconds, "%s: SlotDurationMilliseconds", name)
assert.Equal(t, expected.MinAttestationInclusionDelay, actual.MinAttestationInclusionDelay, "%s: MinAttestationInclusionDelay", name)
assert.Equal(t, expected.SlotsPerEpoch, actual.SlotsPerEpoch, "%s: SlotsPerEpoch", name)
assert.Equal(t, expected.MinSeedLookahead, actual.MinSeedLookahead, "%s: MinSeedLookahead", name)

View File

@@ -5,6 +5,7 @@ import (
"time"
fieldparams "github.com/OffchainLabs/prysm/v7/config/fieldparams"
"github.com/OffchainLabs/prysm/v7/consensus-types/primitives"
"github.com/OffchainLabs/prysm/v7/encoding/bytesutil"
)
@@ -98,6 +99,7 @@ var mainnetBeaconConfig = &BeaconChainConfig{
// Time parameter constants.
MinAttestationInclusionDelay: 1,
SecondsPerSlot: 12,
SlotDurationMilliseconds: 12000,
SlotsPerEpoch: 32,
SqrRootSlotsPerEpoch: 5,
MinSeedLookahead: 1,
@@ -116,6 +118,13 @@ var mainnetBeaconConfig = &BeaconChainConfig{
ReorgMaxEpochsSinceFinalization: 2,
IntervalsPerSlot: 3,
// Time-based protocol parameters.
ProposerReorgCutoffBPS: primitives.BP(1667),
AttestationDueBPS: primitives.BP(3333),
AggregrateDueBPS: primitives.BP(6667),
SyncMessageDueBPS: primitives.BP(3333),
ContributionDueBPS: primitives.BP(6667),
// Ethereum PoW parameters.
DepositChainID: 1, // Chain ID of eth1 mainnet.
DepositNetworkID: 1, // Network ID of eth1 mainnet.
@@ -257,7 +266,6 @@ var mainnetBeaconConfig = &BeaconChainConfig{
// Light client
MinSyncCommitteeParticipants: 1,
MaxRequestLightClientUpdates: 128,
SyncMessageDueBPS: 3333,
// Bellatrix
TerminalBlockHashActivationEpoch: 18446744073709551615,

View File

@@ -34,6 +34,7 @@ func MinimalSpecConfig() *BeaconChainConfig {
// Time parameters
minimalConfig.SecondsPerSlot = 6
minimalConfig.SlotDurationMilliseconds = 6000
minimalConfig.MinAttestationInclusionDelay = 1
minimalConfig.SlotsPerEpoch = 8
minimalConfig.SqrRootSlotsPerEpoch = 2

View File

@@ -56,6 +56,8 @@ FULU_FORK_EPOCH: 18446744073709551615
# ---------------------------------------------------------------
# [customized] Faster for testing purposes
SECONDS_PER_SLOT: 10 # Override for e2e tests
# 10000 milliseconds, 10 seconds
SLOT_DURATION_MS: 10000
# 14 (estimate from Eth1 mainnet)
SECONDS_PER_ETH1_BLOCK: 2 # Override for e2e tests
# [customized] faster time for withdrawals
@@ -133,4 +135,4 @@ BLOB_SCHEDULE:
MAX_BLOBS_PER_BLOCK: 6
# Electra
- EPOCH: 14
MAX_BLOBS_PER_BLOCK: 9
MAX_BLOBS_PER_BLOCK: 9

View File

@@ -58,6 +58,7 @@ func compareConfigs(t *testing.T, expected, actual *params.BeaconChainConfig) {
require.DeepEqual(t, expected.GenesisDelay, actual.GenesisDelay)
require.DeepEqual(t, expected.MinAttestationInclusionDelay, actual.MinAttestationInclusionDelay)
require.DeepEqual(t, expected.SecondsPerSlot, actual.SecondsPerSlot)
require.DeepEqual(t, expected.SlotDurationMilliseconds, actual.SlotDurationMilliseconds)
require.DeepEqual(t, expected.SlotsPerEpoch, actual.SlotsPerEpoch)
require.DeepEqual(t, expected.SqrRootSlotsPerEpoch, actual.SqrRootSlotsPerEpoch)
require.DeepEqual(t, expected.MinSeedLookahead, actual.MinSeedLookahead)

View File

@@ -27,6 +27,7 @@ func E2ETestConfig() *BeaconChainConfig {
// Time parameters.
e2eConfig.SecondsPerSlot = 10
e2eConfig.SlotDurationMilliseconds = 10000
e2eConfig.SlotsPerEpoch = 6
e2eConfig.SqrRootSlotsPerEpoch = 2
e2eConfig.SecondsPerETH1Block = 2
@@ -81,6 +82,7 @@ func E2EMainnetTestConfig() *BeaconChainConfig {
// Time parameters.
e2eConfig.SecondsPerSlot = 6
e2eConfig.SlotDurationMilliseconds = 6000
e2eConfig.SqrRootSlotsPerEpoch = 5
e2eConfig.SecondsPerETH1Block = 2
e2eConfig.ShardCommitteePeriod = 4