Skip to content

Check --no-chronicle results in both eras; write ASM only when it reads back exactly - #3

Merged
codenlighten merged 2 commits into
mainfrom
fix/era-checks-and-asm-tail
Sep 17, 2026
Merged

codenlighten merged 2 commits into
mainfrom
fix/era-checks-and-asm-tail

Conversation

@codenlighten

Copy link
Copy Markdown
Owner

Interpreter tests in both eras

differential() ran under the current (Chronicle) consensus flags only, including for scripts optimized with --no-chronicle, which are meant to hold under the earlier rules as well. The OP_VERIF dead-block bug fixed in 0.1.1 changed behaviour only before Chronicle, so those tests could not catch it.

  • differential(a, b, { flags }) now also takes a list of flag sets. It stops at the first era that disagrees and returns that flag set with the counterexample.
  • eraFlags({ chronicle }) in src/verify.js: [current], or [current, current without SCRIPT_UTXO_AFTER_CHRONICLE and SCRIPT_ENABLE_CHRONICLE]. Both bits have to be cleared: either one enables Chronicle in the interpreter.
  • optimize(…, { chronicle: false }) runs its interpreter tests in both eras. report.verification.differential.eras is 2.

Exact ASM output

--asm -o wrote <tail N bytes> for the data after a top-level OP_RETURN (noted in #1) and OP_PUSHDATA1:<hex> for pushes of 76 bytes or more. Neither reads back, so the written file no longer described the optimized script.

  • Output ASM now uses the form @smartledger/bsv reads: data as bare hex, and the OP_RETURN data as ASM.
  • exactAsm(script) (exported) returns ASM only if it reads back as the same bytes. Otherwise it throws and names the reason:
    • a non-minimal push (optimized code is always minimal, but OP_0 OP_IF blocks and OP_RETURN data are kept verbatim);
    • a truncated push;
    • an unnamed opcode (0xba–0xfc, see below);
    • data pushes alone, which scriptmin reads back as hex.
  • The CLI (optimize and compile) exits 2 with that message instead of writing a different script. Hex and --binary output are unchanged.
  • toAsm for display (--explain, --json rewrites) is unchanged.

Tests

  • New unit test:
    • round-trip through exactAsm for P2PKH with OP_RETURN data, a 300-byte envelope, and OP_RETURN OP_INVALIDOPCODE 0;
    • each refusal reason;
    • CLI --asm -o writes the OP_RETURN data as ASM and exits 2 for a verbatim OP_PUSHDATA1;
    • eraFlags, and a hand-made wrong optimization that passes under Chronicle flags but is caught in both-era mode.
  • New fuzz test: 400 scripts with OP_0 OP_IF blocks holding OP_VERIF/OP_VERNOTIF inside other conditionals, optimized with chronicle: false and checked in both eras. With the 0.1.1 OP_VERIF fix reverted, it fails.
  • npm test: 28/28, 34 s locally.

Found along the way: @smartledger/bsv 9.11.0

Script#toASM prints an unnamed opcode (0xba–0xfc, 67 of them) as bare hex, e.g. 51ba → OP_1 ba, and fromASM reads that back as a one-byte data push (5101ba). toString prints 0xba, which fromString rejects. Not changed here; exactAsm refuses such scripts.

codenlighten added 2 commits September 17, 2026 10:58
…ds back exactly

Interpreter tests ran under the current (Chronicle) flags only, even for a
script optimized with --no-chronicle, whose point is to hold under the earlier
rules too. The OP_VERIF dead-block bug fixed in 0.1.1 changed behaviour only
before Chronicle, so these tests could not see it. differential() now accepts a
list of flag sets, and optimize() passes both eras when chronicle is false.
A new fuzz test covers OP_0 OP_IF blocks holding OP_VERIF/OP_VERNOTIF inside
other conditionals, and fails if that fix is reverted.

--asm -o wrote <tail N bytes> for the data after a top-level OP_RETURN and
OP_PUSHDATA1:<hex> for longer pushes, neither of which reads back. Output ASM
now uses the form @smartledger/bsv reads, prints the OP_RETURN data as ASM, and
must read back as the same bytes. When ASM cannot hold the script (a
non-minimal or truncated push kept verbatim, an unnamed opcode, or data pushes
alone, which read back as hex) the CLI refuses and says why. exactAsm() is
exported for library use.
@codenlighten

Copy link
Copy Markdown
Owner Author

Reviewed. Both changes checked, and the tests were break-tested:

  • Eras: the claim that clearing either Chronicle bit alone leaves Chronicle on matches Interpreter#isAfterChronicle in @smartledger/bsv 9.11.0. With eraFlags reduced to the current era only, a test fails.
  • Exact ASM: with the round-trip check in exactAsm removed, a test fails.

Added d033c19, two small fixes:

  • dropped the parse import the CLI no longer uses (the only new lint finding in this PR)
  • moved differential()'s comment back above it, noting that flags may be a list

The unnamed-opcode ASM round trip reported here is confirmed in @smartledger/bsv and is being fixed there.

@codenlighten
codenlighten merged commit 9e0fcd0 into main Sep 17, 2026
5 checks passed
@codenlighten
codenlighten deleted the fix/era-checks-and-asm-tail branch September 17, 2026 15:12
codenlighten added a commit to codenlighten/smartledger-bsv that referenced this pull request Sep 17, 2026
… script (#175)

toASM wrote an opcode with no name (0xba-0xfc) as bare hex, `OP_1 ba`, which is
also how ASM writes a one-byte data push, so fromASM read 51ba back as 5101ba.
toString wrote `OP_1 0xba`, which fromString rejected. Both now write the raw
byte as 0xba, as bitcoind's script test format does, and both read it back. The
opcodes stay unnamed: Opcode.map invents none above OP_NOP10, and they still
execute as SCRIPT_ERR_BAD_OPCODE.

fromASM('') now returns an empty script instead of OP_0, so an empty script
written with toASM reads back as itself.

Every other script writes the same text as before, and every text read before
is read the same way, checked by writing 4,000 generated scripts and reading
8,000 texts with both versions. The conformance fixture recorded the old ASM
for one script holding a raw 0xda; only that value is regenerated.

Reported in codenlighten/scriptmin#3.
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