Skip to content

Remove covenant struct desugaring and unify on lowered ABI contract#54

Merged
someone235 merged 19 commits intokaspanet:masterfrom
someone235:no-desugar
Mar 10, 2026
Merged

Remove covenant struct desugaring and unify on lowered ABI contract#54
someone235 merged 19 commits intokaspanet:masterfrom
someone235:no-desugar

Conversation

@someone235
Copy link
Contributor

No description provided.

someone235 and others added 15 commits March 10, 2026 14:28
Inline-call debug recording could fail with `CyclicIdentifier("<param>")`
when a function took a struct-array parameter such as `S[]` or `State[]`
and the caller passed the same identifier through.

The regression came from `prepare_inline_call_bindings()`: for array-like
params it could insert a self-alias binding of the form

    x -> Identifier("x")

into the inline-call env. Runtime compilation tolerated this because
`resolve_expr_for_runtime()` preserves array identifiers, but debug-info
recording uses `resolve_expr_for_debug()` / `resolve_expr()`, which
recursively resolves env bindings and therefore detected the self-cycle.

Fix this by never inserting self-alias bindings for inline-call params.
This keeps the env semantically clean and fixes debug-info compilation
without changing runtime behavior.

Add compiler coverage showing:
- plain `int[]` inline calls with debug info still compile
- struct-array `S[]` inline calls with debug info now compile
Add focused runtime regressions around the selector-dispatched
`validateOutputState` path.

The new tests cover three cases:
- validating a `State` sourced from `State[]` indexing
- validating a `State` sourced from an inline-returned `State[]`
- validating a `State` under selector dispatch

Together these isolate that the remaining failure is selector-specific:
non-selector `validateOutputState` cases succeed, while the
selector-dispatched path still rejects what should be valid reflected
outputs.

Also update the many-to-many covenant security coverage to reflect the
same underlying issue in generated covenant wrappers.
Fix `validateOutputState` for selector-dispatched contracts by making
state reflection operate on a single shared contract-state segment and
correcting the prefix substring extraction.

The previous selector layout encoded contract state separately inside
each dispatch branch. `validateOutputState` reconstructs the expected
redeem script by replacing one contiguous state segment, so selector
contracts could not be reflected correctly.

This change:
- preserves the selector on the alt stack
- emits the contract field prolog once before selector dispatch
- restores the selector before dispatch execution
- fixes the prefix substring in `validateOutputState` to slice
  `[redeem_start, redeem_start + state_start_offset)`

With this, selector-dispatched `validateOutputState` works correctly,
including the many-to-many covenant leader path that depends on it.
Add runtime security coverage for N:M covenant transition wrappers that
currently accept caller-supplied `prev_states` on the leader path.

The new test first proves the honest transition succeeds when the leader
passes the real previous states, then attempts to spoof different
`prev_states` through the leader sigscript and expects rejection.

Today the spoofed case is accepted, so this test captures the regression
at the runtime level instead of only through ABI shape assertions.
Fix covenant transition lowering so N:M leader entrypoints do not accept
`prev_states` through the ABI.

For `binding = cov, mode = transition`, the previous states are committed
by the covenant input set and must be sourced with `readInputState(...)`
from `OpCovInputIdx(...)`. Exposing them as leader entrypoint arguments
allowed callers to spoof the transition inputs through the sigscript.

This change:
- removes the first policy param (`prev_states`) from cov-transition
  leader entrypoint params
- rebuilds `prev_states` inside the generated leader wrapper from the
  covenant inputs before calling the policy
- updates the expected lowered AST accordingly
- updates covenant sigscript coverage to reflect the new leader ABI
- adds security coverage that rejects spoofed `prev_states` through the
  leader ABI

This keeps the transition policy shape intact while enforcing that
previous states come from the covenant context rather than caller input.
Add a focused runtime regression showing that `readInputState` still
fails under selector dispatch.

The new test uses the minimal form:
- two entrypoints (so selector dispatch is enabled)
- `State s = readInputState(this.activeInputIndex);`
- `require(s.x == 5);`

The same pattern works without selector dispatch, so this isolates the
remaining bug to selector-mode `readInputState` handling rather than
covenant lowering, struct arrays, or `validateOutputState`.

Keep the single-field cov-transition AST example for review of the
generated wrapper shape.
Fix `readInputState` when lowering `State` values under selector dispatch.

The remaining selector regression was not in covenant lowering itself, but
in the symbolic `State` lowering path used for expressions like:

    State s = readInputState(...)

The runtime `readInputState` helper already used the correct state start
offset, but the symbolic lowering path still assumed contract state
started at offset 0. That worked without selector dispatch and failed
once selector prefixes were present.

This change:
- makes symbolic `readInputState` use the same selector-aware state
offset model as the runtime helper
- narrows the fix to actual `readInputState` lowering instead of pushing
selector offsets through all struct lowering paths
- keeps generic struct lowering unchanged, avoiding unrelated regressions
- updates covenant AST fixtures to match the current generated wrapper
shape

This fixes the selector-mode `readInputState` regression and restores
the honest many-to-many transition happy path.
Add compiler regressions for selector-dispatched `readInputState` and
`validateOutputState` using a three-field state with mixed widths:

- `int`
- `byte[2]`
- `byte[32]`

These tests extend the selector state-offset coverage beyond the
single-field and two-field cases and help guard against future prefix /
field-offset regressions in mixed-width state layouts.
@michaelsutton michaelsutton mentioned this pull request Mar 10, 2026
michaelsutton and others added 4 commits March 10, 2026 17:04
Require `out_count == new_states.length` in generated verification
wrappers for both auth and cov bindings.

This removes the previous behavior where under-provided `new_states`
failed only indirectly and surplus entries were silently ignored. Also
update the AST fixtures, security tests, and DECL spec to match the new
wrapper invariant.
@someone235 someone235 merged commit d3a46ea into kaspanet:master Mar 10, 2026
4 checks passed
Manyfestation added a commit to Manyfestation/silverscript that referenced this pull request Mar 10, 2026
@someone235 someone235 deleted the no-desugar branch March 12, 2026 12:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants