Problem
simf/lib/ is the product; everything else is scaffolding. Today that scaffolding uses one shape for every feature — a per-feature program with witness dispatch plus per-feature Rust plumbing — but the wishlist features differ in what context they need to run:
| Feature |
Needs |
Category |
and/or/not, EC point_to_gej, decimals, identities, deep_eq, is_some/is_none |
known input → known output |
Vector |
| checked/safe arithmetic |
randomized inputs + failure injection |
Property |
u256 store/load, Merkle tools |
program storage state |
Stateful |
asset/amount utils, script-hash utils, OP_RETURN, covenants, relative timelocks |
a crafted transaction |
Scenario |
The bulk of the wishlist is Scenario, which has no shared support today.
Proposal
Standardize the universal piece (spend plumbing), then pick the lightest category per feature. Build order:
- Shared helpers — extract the duplicated fund / spend / broadcast / assert block. Everything below builds on it.
- Vector tests (default) — self-asserting
main(), no witness; Rust side is one call to the helpers. A generic runner enumerates a registry of should-succeed programs. Prove out by converting logical and ct/point.
- Scenario tests (main future investment) — reusable transaction-scenario builder (inputs/outputs, assets, sequence/version,
OP_RETURN, storage) plus a thin contract entrypoint per function. Unblocks covenants, assets, script hashes, timelocks.
Property tests already exist and stay as-is.
Layout
simf/
lib/
logical.simf ct_point.simf uint.simf ... # test entrypoints, mirroring lib/
tests/
common/ # shared helpers + scenario builder
logical.rs ct_point.rs uint.rs ...
Rename *_mock.simf → *_test.simf; these are entrypoints, not mocks.
Entrypoints can't live in simf/tests/. A contract resolves modules (crate::lib::...) relative to its main file's directory, so a main under simf/tests/ can't see simf/lib/. Decoupling source root from entrypoint location needs separate Simplex/SimplicityHL work — out of scope here.
Future: enum dispatch
Witness-driven contracts currently select a function via numeric witness::FUNCTION_INDEX with one match if_test_this_function(N, ...) block per case. Once PR #336 lands, indices become named variants and dispatch collapses to a single match with one arm per case. Readability only.
Problem
simf/lib/is the product; everything else is scaffolding. Today that scaffolding uses one shape for every feature — a per-feature program with witness dispatch plus per-feature Rust plumbing — but the wishlist features differ in what context they need to run:and/or/not, ECpoint_to_gej, decimals, identities,deep_eq,is_some/is_noneu256store/load, Merkle toolsOP_RETURN, covenants, relative timelocksThe bulk of the wishlist is Scenario, which has no shared support today.
Proposal
Standardize the universal piece (spend plumbing), then pick the lightest category per feature. Build order:
main(), no witness; Rust side is one call to the helpers. A generic runner enumerates a registry of should-succeed programs. Prove out by convertinglogicalandct/point.OP_RETURN, storage) plus a thin contract entrypoint per function. Unblocks covenants, assets, script hashes, timelocks.Property tests already exist and stay as-is.
Layout
Rename
*_mock.simf→*_test.simf; these are entrypoints, not mocks.Entrypoints can't live in
simf/tests/. A contract resolves modules (crate::lib::...) relative to itsmainfile's directory, so a main undersimf/tests/can't seesimf/lib/. Decoupling source root from entrypoint location needs separate Simplex/SimplicityHL work — out of scope here.Future: enum dispatch
Witness-driven contracts currently select a function via numeric
witness::FUNCTION_INDEXwith onematch if_test_this_function(N, ...)block per case. Once PR #336 lands, indices become named variants and dispatch collapses to a singlematchwith one arm per case. Readability only.