Zellic finding 3.15 (Low). EVMVersion::supportShieldedStorage() and hasTimestampMs() gate on *this >= mercury(), with no upper bound. A later EVM version (e.g. Osaka, which sits after Mercury in the version enum) therefore satisfies the gate and the compiler happily emits CLOAD/CSTORE/TIMESTAMPMS and accepts shielded types. But the Seismic runtime (seismic-revm) is Mercury-pinned: SeismicSpecId defines only MERCURY and the handler exact-matches it, so those opcodes do not exist on any other target. Shielded code compiled for Osaka would be undeployable / mis-executed.
Fix
Change both gates to exact-match *this == mercury(). Audited all 14 callers: the two definitions, EVMVersion.cpp hasOpcode(), the TypeChecker/AsmAnalysis gates, and the codegen solAssert guards all behave correctly under exact-match (no caller wanted "Mercury or later"). Also drops "or later" from the two rejection messages (10001 in TypeChecker, 10002 in ReferencesResolver).
Tests
- New
shielded_rejected_on_osaka.sol (=osaka): shielded declaration rejected on Osaka.
- Corrected 6 pre-Mercury version tests (
shielded_storage_*_evm_version, etc.): they asserted 10001 x N but the compiler emits a single fatal 10002 (ReferencesResolver preempts the TypeChecker path) — a pre-existing wrong expectation that went unnoticed because CI only runs the default Mercury matrix, where these =cancun/=paris tests are skipped. Now assert actual output.
- Tightened shielded/timestamp syntax + docExample pins from
>=mercury to =mercury (they assert successful compilation, now Mercury-only).
- 10001 is now unreachable (fatal 10002 preempts every shielded declaration); recorded in
error_codes.py old_source_only_ids.
- Full syntax suite green at Mercury; Osaka rejects, Mercury compiles.
Note: seismic >=mercury pins on semantic and yul tests could similarly be tightened to =mercury as a follow-up hygiene pass (not done here — verifying those at Osaka needs revme).
Zellic finding 3.15 (Low).
EVMVersion::supportShieldedStorage()andhasTimestampMs()gate on*this >= mercury(), with no upper bound. A later EVM version (e.g. Osaka, which sits after Mercury in the version enum) therefore satisfies the gate and the compiler happily emits CLOAD/CSTORE/TIMESTAMPMS and accepts shielded types. But the Seismic runtime (seismic-revm) is Mercury-pinned:SeismicSpecIddefines only MERCURY and the handler exact-matches it, so those opcodes do not exist on any other target. Shielded code compiled for Osaka would be undeployable / mis-executed.Fix
Change both gates to exact-match
*this == mercury(). Audited all 14 callers: the two definitions,EVMVersion.cpphasOpcode(), the TypeChecker/AsmAnalysis gates, and the codegensolAssertguards all behave correctly under exact-match (no caller wanted "Mercury or later"). Also drops "or later" from the two rejection messages (10001 in TypeChecker, 10002 in ReferencesResolver).Tests
shielded_rejected_on_osaka.sol(=osaka): shielded declaration rejected on Osaka.shielded_storage_*_evm_version, etc.): they asserted10001 x Nbut the compiler emits a single fatal10002(ReferencesResolver preempts the TypeChecker path) — a pre-existing wrong expectation that went unnoticed because CI only runs the default Mercury matrix, where these =cancun/=paris tests are skipped. Now assert actual output.>=mercuryto=mercury(they assert successful compilation, now Mercury-only).error_codes.pyold_source_only_ids.Note: seismic
>=mercurypins on semantic and yul tests could similarly be tightened to=mercuryas a follow-up hygiene pass (not done here — verifying those at Osaka needs revme).