Skip to content

fix: substConstants serializer-level parity: body-verbatim, OOB no-op, first-wins, v3 size-gate (match sigma-state)#871

Open
mwaddip wants to merge 2 commits into
ergoplatform:developfrom
mwaddip:fix/subst-const-oob-noop
Open

fix: substConstants serializer-level parity: body-verbatim, OOB no-op, first-wins, v3 size-gate (match sigma-state)#871
mwaddip wants to merge 2 commits into
ergoplatform:developfrom
mwaddip:fix/subst-const-oob-noop

Conversation

@mwaddip
Copy link
Copy Markdown

@mwaddip mwaddip commented Jun 1, 2026

SubstConstants full-parsed the script bytes and errored when a position was >= the tree's constant count. sigma-state's ErgoTreeSerializer.substituteConstants is byte-surgery: it parses only the header + constants, keeps the body verbatim (never deserializing it), substitutes in-range positions and no-ops out-of-range ones. sigma-rust thus rejected scripts the JVM accepts — an OOB position or an unparseable body — a latent consensus fork (not seen on mainnet).

Adds ErgoTree::substitute_constants mirroring the Scala serializer; SubstConstants::eval calls it. Verified byte-exact vs JVM-blessed v5 vectors (5 OOB entries, incl. one with an unparseable body, return input unchanged); in-range covered by proptests. New substitute_constants_oob_is_noop test.

mwaddip and others added 2 commits June 1, 2026 16:37
`SubstConstants` eval full-parsed the script bytes (`ErgoTree::
sigma_parse_bytes`) and errored when a requested position was >= the
tree's constant count. sigma-state's `ErgoTreeSerializer.
substituteConstants` is byte-surgery: it parses only the header and
constants segment, keeps the tree body verbatim (never deserializing
it), substitutes constants at in-range positions, and silently no-ops
out-of-range ones. sigma-rust therefore rejected scripts the JVM
accepts -- an out-of-range position, or an unparseable tree body -- a
latent consensus fork (not seen on mainnet).

Add `ErgoTree::substitute_constants` mirroring the Scala serializer
(header + constants parsed, body bytes kept raw, OOB positions dropped,
first position per index wins) and call it from `SubstConstants::eval`.

Verified byte-exact against SANTA's JVM-blessed v5 vectors
(`substConstants_equivalence.json`): the five out-of-range entries
(incl. `[0,0,8,-45]`, whose body the full parser rejects with
InvalidTypeCode) return the input unchanged; in-range substitution
stays covered by the existing proptests. New
`substitute_constants_oob_is_noop` regression test.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
ErgoTree::substitute_constants re-emitted the tree-size slot whenever the template header has_size bit was set. sigma-state's ErgoTreeSerializer.substituteConstants re-emits it only when the evaluation's ErgoTree version is >= V3 (the V6 soft-fork isV3OrLaterErgoTreeVersion gate); for v<=2 the slot is dropped even though the bit stays set. Thread the evaluation's tree_version (ctx.tree_version()) into the fn and gate the size emit on it.

Completes the substConstants serializer-level parity (body-verbatim and first-wins were already handled here). The v<=2 hasSize path is not mainnet-reachable, so it is covered by a unit test (v2 drops / v3 keeps the slot) certified against the Scala source rather than a blessed vector.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@mwaddip mwaddip changed the title fix: substConstants no-ops out-of-range positions (sigma-state parity) fix: substConstants serializer-level parity: body-verbatim, OOB no-op, first-wins, v3 size-gate (match sigma-state) Jun 1, 2026
@mwaddip
Copy link
Copy Markdown
Author

mwaddip commented Jun 1, 2026

Addendum: scope grew beyond the original out-of-range no-op to the full serializer-level substConstants rewrite (title updated). All four behaviors match sigma-state's ErgoTreeSerializer.substituteConstants. The v3 size-gate isn't mainnet-reachable, so it's covered by a unit test rather than a blessed vector.

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.

1 participant