EcoStrategy sim suite: extend coverage for I-01-eco audit gaps (cooldown, sybil, flag mechanics, UTC rollover) #167
Labels
No labels
agent:fix_bugfix
agent:fix_conflicts
agent:fix_security
agent:gap_analysis
agent:implement
agent:open_issues
agents
agent:security_audit
agent:verify
area
timecurve
arena
arena-v2
audit
block:feature_review
block:security
bug
contracts
design
devops
docs
documentation
enhancement
epic
feature
frontend
indexer
low priority
megaeth
operations
ops
performance
QA
qa checklist
referrals
removal
security
smart-contracts
tech-debt
testing
time-arena
timecurve
visual-qa
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
code/yieldomega#167
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
follow-up to #161 after the D + E ship — the A–E scope as-shipped is fine (verified separately on `
390f9be`), but pulling four audit-coverage gaps out into their own ticket since closing them needs real implementation, not just polish.every numeric constant the sim mirrors matches the contract. the gaps below are about which audit-finding scenarios got modeled vs tallied vs skipped — not correctness errors in what's already there.
Gap 1 — buy cooldown not enforced in `_simulate_sale` (M-03-eco baseline)
`scenarios.py:131-202` arrival loop draws `idx = rng.randrange(population)` per Poisson tick and lets the same wallet buy on consecutive ticks. no per-wallet `nextBuyAllowedAt` array, no gate.
onchain (`TimeCurve.sol:115`, `:128`, `:463`, `:598`) the `buyCooldownSec` gate is per-wallet and is the central mechanism the audit's M-03-eco section asks the sim to reason about. without modeling it, sybil-vs-whale comparisons can't separate "many wallets" from "one whale" — they're behaviorally identical in this sim — and any predator-pace experiment in C/D understates what the contract actually allows.
verified: `grep -n "cooldown|next_allowed|nextBuyAllowed" simulations/ecostrategy/scenarios.py` returns nothing.
fix shape: per-wallet `next_allowed_at: list[float]` next to budgets, gate arrivals on `now >= next_allowed_at[idx]`, set `next_allowed_at[idx] = now + p.buy_cooldown_sec` on a successful buy. pull `buy_cooldown_sec` from canonical params.
Gap 2 — M-03-eco sybil scenario structurally absent
audit M-03-eco asks for "scenarios with a single capital pool split across 1, 5, 25, and 100 wallets, measuring spend share, CHARM share, podium share, BP share, and value returned to early non-sybil buyers."
none of A–E expose a `wallets_per_operator` knob. scenario A's believer/shark split is a cohort partition, not a sybil cluster. scenario B's `cluster_frac` aggregates charm weight onto one index to demonstrate referral stacking, not buy-cadence parallelism.
verified: `grep -rn "wallets_per_operator|cluster_size|operator_capital|sybil" simulations/ecostrategy/` returns nothing.
fix shape: add a scenario F (or extend B) parameterized by `(operator_capital, wallets_per_operator)`. metric set: spend share, CHARM weight share, podium-slot count, BP share for the operator vs solo wallets at equal aggregate spend. depends on Gap 1 — without cooldown the sybil run is a no-op.
Gap 3 — L-02-eco flag claim/penalty mechanics are tally-only
scenario D advertises "optional WarBow flag plant tally" but only counts `flags_planted += 1`. it doesn't track `warbowPendingFlagOwner`, the 300-second silence window, the +1000 claim award, or the −2000 penalty when an interrupting buy lands after silence.
audit L-02-eco asks for "same-block flag claim vs interrupt-buy scenarios in simulations and manual QA." current code can't distinguish a successful claim from a penalty event.
contract refs: plant slot at `TimeCurve.sol:570-572`, penalty branch at `:511-518` with `pen = WARBOW_FLAG_CLAIM_BP * 2`, claim award at `:613` via `_addBattlePoints(msg.sender, WARBOW_FLAG_CLAIM_BP)`, silence at `WARBOW_FLAG_SILENCE_SEC = 300` (`:66`).
verified: `grep -n "WARBOW_FLAG|FLAG_CLAIM|FLAG_SILENCE|FLAG_PENALTY"` across `constants.py`, `warbow_pvp.py`, `scenarios.py` returns nothing — none of these constants are defined in sim.
fix shape: add `WARBOW_FLAG_CLAIM_BP = 1000`, `WARBOW_FLAG_SILENCE_SEC = 300` to constants. add `pending_flag_owner: int | None`, `pending_flag_plant_at: float` state to `WarBowWorld`. implement `try_plant_flag`, `try_claim_flag`, and an interrupter hook on each buy that applies the 2× penalty if silence elapsed.
Gap 4 — L-01-eco UTC rollover burst not exercised under default horizons
`utc_day = int(now_sec // 86400)` is correct (`warbow_pvp.py:58-60`), matches `block.timestamp / SECONDS_PER_DAY` at `TimeCurve.sol:628`. but every scenario starts at `now=0.0` (`scenarios.py:120`) with default `horizon_sec=28_800` (8h) — `now` never crosses a multiple of 86400 inside a single run.
the audit's rollover-burst attack pattern (3 steals before midnight + 3 after) is structurally unreachable on the default harness. `test_daily_limit_blocks_without_bypass` exercises caps within one UTC day but doesn't test the rollover transition.
fix shape: add an optional `start_unix_offset_sec` to `_simulate_sale` (or a dedicated rollover scenario) that begins `now` shortly before a UTC boundary. add a unit test asserting a 4th steal succeeds at `now = SECONDS_PER_DAY` without bypass burn.
Audit-coverage table at `
390f9be`Priority order
Gap 1 (cooldown) is the unblocker — Gap 2 needs it. Gap 3 is independently scoped. Gap 4 is a small param + one unit test. cooldown + sybil together close the audit's biggest open question; flag mechanics + rollover close the L-tier gaps.
cc @PlasticDigits — file separately or roll into one MR, your call. happy to take cooldown + rollover myself if you'd rather keep flag mechanics + sybil scenario in your queue.
mentioned in commit
544e1dca1bShipped: EcoStrategy sim coverage (GitLab #167)
Merged to main (includes merge with latest origin/main so #169 scenario D isolated `after_tick` stays intact).
What changed
Verification checklist
Please verify when you have a cycle. @brouie — requesting your sign-off on whether this closes the I-01-eco gap set from #167 or if you want follow-up (e.g. auto `try_claim_flag` ticks in scenarios).
Issue left open per maintainer request.
@PlasticDigits — verification PASS on
544e1dc(merge14e0375).unittest discover — 42/42 OK in 0.502s. includes new #167 cases (
test_utc_rollover_fourth_steal_without_bypass, scenario F operator metrics, flag mechanics interrupt/penalty/claim) and the #169 regressions (test_scenario_d_warbow_guards_parity_seed_42,test_scenario_d_ordering_branches).python3 -m ecostrategy --scenario all --seed 42— runs A through F, JSON payload includesgitlab_issue_coverage: https://gitlab.com/PlasticDigits/yieldomega/-/issues/167. scenario F metrics at seed 42 / pop 1000 default:(0 podium slots at this scale is expected — operator pool is 5 wallets out of 1000 sybiling against ~$500k of $40M+ aggregate spend; the BP share advantage shows up but not enough to crack top-3 at default seed. confirms the metric machinery works without implying that 5/1000 sybils dominate, which would itself be a finding.)
MIRRORED_FROM_CONTRACTatsimulations/ecostrategy/constants.py:34-56lists bothWARBOW_FLAG_CLAIM_BPandWARBOW_FLAG_SILENCE_SEC. cross-checked vs contract — both matchTimeCurve.sol:64(1000 BP) and:66(300s). penalty math matches contract:511-518(2 × WARBOW_FLAG_CLAIM_BP).INV-SIM-167atdocs/testing/invariants-and-business-logic.md:1123reads cleanly — all 4 audit-gap anchors present (cooldown M-03-eco baseline, scenario F sybil M-03-eco slice, flag interrupt/claim L-02-eco, UTC offset L-01-eco). cross-links fromINV-SIM-161at:1117andINV-SIM-169-Dat the same anchor.on your auto
try_claim_flagquestion — recommendation: yes-later, not now. the mechanism is implemented and unit-tested in isolation (test_warbow_flag_*cases attest_ecostrategy.py:99-114). adding it to scenario after_tick flow is mechanically trivial (the helper has access toworld,now,rngalready), but it would shift the seed-42 metrics on D —_scenario_c_style_after_tickrecomputes the leader each tick, and flag claims add BP that could flip leader tracking. since seed-42 just got pinned by #169 as a regression test, churning those expectations right after lands feels premature. the audit narrative #167 cites (interrupt-buy penalty, claim BP award) is already covered as unit tests; the downstream "operator who plants AND claims" or "leader who plants AND defends-via-silence" narratives are adjacent to L-02-eco but not in the audit scope.better fit: spawn a follow-up ticket like "scenario G: planters who actually claim" or extend D with a
claim_flag_probarg + paired-run parity test, when an audit thread asks for it explicitly. happy to file when you decide.one nit on test pinning that would be useful follow-up:
> 0.0floor (test_scenario_f_operator_metrics); a regression range like0.01 ≤ operator_spend_share ≤ 0.05for fixed seed would catch numeric drift betterwarbow_pvp.py:147(buyer == owner: return 0) covered by branch but no isolated test pins itready for close on your side.
mentioned in commit
a6634e6328