Check --no-chronicle results in both eras; write ASM only when it reads back exactly - #3
Merged
Merged
Conversation
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.
… comment above it
Owner
Author
|
Reviewed. Both changes checked, and the tests were break-tested:
Added
The unnamed-opcode ASM round trip reported here is confirmed in @smartledger/bsv and is being fixed there. |
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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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. TheOP_VERIFdead-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 })insrc/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.erasis 2.Exact ASM output
--asm -owrote<tail N bytes>for the data after a top-levelOP_RETURN(noted in #1) andOP_PUSHDATA1:<hex>for pushes of 76 bytes or more. Neither reads back, so the written file no longer described the optimized script.@smartledger/bsvreads: data as bare hex, and theOP_RETURNdata as ASM.exactAsm(script)(exported) returns ASM only if it reads back as the same bytes. Otherwise it throws and names the reason:OP_0 OP_IFblocks andOP_RETURNdata are kept verbatim);0xba–0xfc, see below);optimizeandcompile) exits 2 with that message instead of writing a different script. Hex and--binaryoutput are unchanged.toAsmfor display (--explain,--jsonrewrites) is unchanged.Tests
exactAsmfor P2PKH withOP_RETURNdata, a 300-byte envelope, andOP_RETURN OP_INVALIDOPCODE 0;--asm -owrites theOP_RETURNdata as ASM and exits 2 for a verbatimOP_PUSHDATA1;eraFlags, and a hand-made wrong optimization that passes under Chronicle flags but is caught in both-era mode.OP_0 OP_IFblocks holdingOP_VERIF/OP_VERNOTIFinside other conditionals, optimized withchronicle: falseand checked in both eras. With the 0.1.1OP_VERIFfix reverted, it fails.npm test: 28/28, 34 s locally.Found along the way: @smartledger/bsv 9.11.0
Script#toASMprints an unnamed opcode (0xba–0xfc, 67 of them) as bare hex, e.g.51ba→OP_1 ba, andfromASMreads that back as a one-byte data push (5101ba).toStringprints0xba, whichfromStringrejects. Not changed here;exactAsmrefuses such scripts.