Marcin Pawlowski 525d081a3c
Keep the deployed rule as the default; make the proposal an explicit arm
I had planned to flip uncle_window_anchor's default to "parent" so new configs
would measure the proposed design by default. Tried it, and it breaks a
documented guarantee: key() appends the anchor only when it is not "uncle", so
with "parent" as the default an --old run's key is no longer byte-identical to
the pre-redesign key and --old stops bit-reproducing historical runs (sec 9).
o.key() == o._base_key() fails outright.

Reverted, and on reflection the default was wrong for a second reason anyway.
The report's job is to describe the protocol as deployed and to RECOMMEND
changes; the default should therefore be the deployed rule, with the proposal
as an explicit arm. That is exactly the convention fixed_point already follows
(default exact f = the analysis convention, explicit True = spec-faithful).
Both reasons are recorded on the field.

To make the distinction visible rather than implicit, the spec-as-is studies
now pin uncle_window_anchor: uncle explicitly -- spec_point.py, spec_jitter.py
and the three spec-point-*.yaml configs answer "what does the DEPLOYED chain
do", so they must not drift onto a proposal if a default ever moves.

Adds uncle_window_anchor as a sweep axis (SweepConfig field plus _SWEEP_AXES),
and configs/absorption-window-anchor.yaml: the sec 3.4 absorption sweep re-run
under both anchors at three delays. That study is the one that can move a
recommendation -- the W >= 7/f floor was measured against the uncle gap, and the
parent gap runs about one block-interval longer, so the floor should sit higher
and the margin behind W = 10/f shrink.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-07 17:53:47 +02:00

49 lines
3.6 KiB
YAML

# The diagnostic: does per-recipient delay variance reproduce the standalone result?
#
# The one experiment here that could invalidate the REPORT rather than the spec section it is
# checking. The hypothesis for the discrepancy is modelling, not measurement: the
# standalone simulation drew an INDEPENDENT propagation delay per (block, recipient), while this
# simulator's blend topology runs a cascade of relays and then floods network-wide from the LAST
# relay, so nodes receive a block at nearly the same time and their views stay synchronised.
# Independent per-recipient draws maximise view divergence, which is exactly what manufactures the
# depth->=2 forks the first-fork rule cannot recover.
#
# jitter_mean adds per-(block, node) arrival noise on top of the cascade, so sweeping it
# interpolates between the two models: 0 is the report's cascade, large values approach the
# standalone's independent-delay regime. The observable that decides it is deep_orphan_share --
# the fraction of in-window orphans sitting deeper than the first block of their fork, which is
# precisely the structural quantity behind the spec section's deep-fork claim.
#
# This is also the report's open item 15 (correlated/heterogeneous latency untested).
#
# EXACT ORACLE REQUIRED: the windowed fork choice and the arrival prune are only bit-exact at
# jitter_mean == 0 (a jittered arrival can cross the deterministic horizon), so both are off. That
# forces the full (N x n_blocks) matrix and makes each run ~17 s at k = 2160.
#
# Run TWICE — default (countable) and with --old. `--old` is NOT a candidate design: under the
# current spec a block carrying a deep-fork reference is REJECTED, so it is an unreachable upper
# bound on what any counting rule could recover, and the countable-vs-ceiling gap is the
# first-fork cost. Latency is in SLOTS (1 slot = 1 s).
n_nodes: [1000] # network size
stake_dist: [pareto] # heavy-tailed (realistic) stake distribution
topology: [blend] # Blend mixnet — the deployment transport
degree: [6] # peering degree of the d-regular graph
link_latency_mean: [0.5] # natural geographic transport (sub-slot)
link_latency_dist: [geo] # real-world geographic band mixture
blend_hops: [3] # the spec's Blend cascade length
blend_delay_max: [4.0] # the spec's operating point (rho ~ 0.27)
max_uncles: [0, 1, 2, 4] # 0 = negative control; 4 = the spec's MAX_UNCLES
uncle_strategy: [oldest] # spec Uncle Selection
uncle_window_anchor: [uncle] # SPEC AS DEPLOYED, not the sec 6.12 proposal
init_dest: [common] # per-node initial D_est from agreement
replicates: 12 # exact-oracle runs are ~17 s each; 12 x 40 cells
base: # per-run settings shared by every cell
k: 2160 # true security parameter
epochs: 20 # equilibrium within ~2 epochs; burn 50%
f: 0.03333333333333333 # slot activation coefficient (1/30)
genesis_d_factor: 0.5 # start near true stake (cheap epoch 0)
early_stop: true
windowed_fork_choice: false # exact oracle: required once jitter > 0
prune_arrival: false # ...and the prune needs the same horizon
jitter_mean: 0.0 # OVERRIDDEN per run by scripts/spec_jitter.py