simpa: adversarial mining scenarios driven by DAGKnight counters (#1112) - #1124
Kali123411 wants to merge 1 commit into
Conversation
…panet#1112) Honest Simpa runs with a uniform broadcast delay produce almost no DAG conflicts, so DAGKnight's UMC cascade never runs and the DK counters stay ~0. This adds deterministic adversarial mining scenarios that manufacture the conflict shapes the convergence proof reasons about, and asserts via Consensus::dagknight_counters() that each scenario exercised the intended consensus flow. New simpa/src/simulator/adversary.rs defines the Scenario enum, the seed-derived AdversaryParams/AdversaryPlan, and per-miner AdversaryRuntime. MinerOptions gains an optional adversary plan; Miner::mine() branches on it: - withheld-side-dag: mine privately (insert-local, no broadcast), accumulate a hidden side-DAG, release it in one staggered burst at a scheduled time. - latency-burst: hold blocks mined during a window and release them together once it closes, forming a transient parallel subgroup. - equal-rank-ties: multiple adversaries build symmetric private forks. - weak/strong-shortcut: one-shot vs sustained conflict zone (checkpoint reuse). - gray-context-change: run under baseline-debugging to cross-check the paper baseline against the cascade (no score-disagreement panic == agreement). - variable-difficulty: modulate the adversary mining rate for high-rank bursts. network.rs gains init_with_adversary() (a thin wrapper over the shared init_inner alongside init_with_lane_producer, whose public signature is unchanged). main.rs adds --scenario/--seed and params, and after the run snapshots the DK counters, asserts the expected counters moved, and logs SCENARIO <name>: PASS/FAIL (exiting non-zero on failure). All schedules derive from --seed. Adversary miners mine empty blocks (the DAG shape, not tx load, drives the cascade). Every scenario passes end-to-end on the dagknight branch, e.g. withheld-side-dag yields total_calls>0, total_cascade_flips>0, total_voting_blocks>0. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EQKGio1HUH4jCvSJwVgFeT
63cdc3a to
948bf38
Compare
|
Pushed The re-run is sitting in |
|
Outsider review of the scenario assertions (not driving the adversary logic).
Suggested one-line tighten in Scenario::WithheldSideDag => (
s.total_calls > 0 && s.total_cascade_flips > 0 && s.total_voting_blocks > 0,
Follow-ups, not this PR:
Patch: https://github.com/STP-KAS/dagknight-test-grok/blob/main/tests/pr1124_withheld_cascade_flips.patch |
|
Exact suggested change, since the previous comment mangled fences: // simpa/src/main.rs fn scenario_assertion
Scenario::WithheldSideDag => (
s.total_calls > 0 && s.total_cascade_flips > 0 && s.total_voting_blocks > 0,
format!(
"total_calls={}, total_cascade_flips={}, total_voting_blocks={}",
s.total_calls, s.total_cascade_flips, s.total_voting_blocks
),
),Today it is Patch: https://github.com/STP-KAS/dagknight-test-grok/blob/main/tests/pr1124_withheld_cascade_flips.patch |
Closes #1112.
Honest Simpa runs with a uniform broadcast delay produce almost no DAG conflicts, so DAGKnight's UMC cascade never runs and the DK counters stay ~0. This PR adds deterministic adversarial mining scenarios that manufacture the conflict shapes the convergence proof reasons about, and asserts — via
Consensus::dagknight_counters()— that each scenario exercised the intended consensus flow.What's added
simpa/src/simulator/adversary.rs(new): theScenarioenum, seed-derivedAdversaryParams/AdversaryPlan, and per-minerAdversaryRuntime.miner.rs:MinerOptionsgains an optional adversary plan;Miner::mine()branches into private-fork / latency-burst handlers. Withhold-family adversaries ignore incoming honest blocks (keeping a pure private fork), insert locally, and release via staggered targetedenv.send(a monotonic sequence guarantees parents arrive before children). Adversary blocks are mined empty — the DAG shape, not tx load, drives the cascade.network.rs:init_with_adversary()over a sharedinit_inner; the publicinit_with_lane_producersignature is unchanged.main.rs:--scenario/--seed+ params; after the run it snapshots the DK counters, asserts the expected counters moved, logsSCENARIO <name>: PASS/FAIL, and exits non-zero on failure.All schedules derive from
--seed, so a(scenario, seed)pair is fully reproducible.Scenarios and results
Run on the
dagknightbranch (seed 1, 2 miners unless noted,--sim-time 400,--tpb 32). Each scenario's "done-when" is the counter(s) it must move:withheld-side-daglatency-burstequal-rank-ties(3 miners)strong-shortcutweak-shortcutvariable-difficultygray-context-change(--features baseline-debugging)Determinism: the same seed reproduces
total_callsexactly; a different seed differs clearly.total_calls(DAG-shape driven) is fully deterministic; minor variance in flip/voting sub-counts comes from the consensus's internal parallel processors, not the adversary schedule.Notes for reviewers
-t/--tpbis transactions-per-block, not sim-time (-s/--sim-time). Scenario runs should pass a modest--tpb: the stockOnetimeTxSelectorbypasses mass-aware selection, so a large tpb makes the honest miner's own blocks exceed the compute-mass limit and panic — a pre-existing simpa property, unrelated to the adversary logic.weak-shortcutandstrong-shortcutboth move their required counter (from_scratch>0/from_checkpoint>0), but both runs are checkpoint-dominant in practice — a sustained conflict zone reuses checkpoints either way. Cleanly forcingfrom_scratchdominance would need re-rooting the fork at a fresh recent ancestor, which the current pure-private-fork mechanism doesn't do; noting it as a possible follow-up.🤖 Generated with Claude Code