Skip to content

Releases: codenlighten/scriptmin

@smartledger/scriptmin 0.1.3

Choose a tag to compare

@codenlighten codenlighten released this 18 Sep 00:30

Data and standard templates are kept

A script can be shorter without being the same output. Earlier versions made that mistake on real mainnet scripts, so both protections below are on by default:

  • Data that nothing uses is kept byte for byte. A push no operation takes as an operand, and that is not left on the stack, is a data carrier: <document> OP_DROP <pubkey> OP_CHECKSIG (121 B → 35 B in 0.1.2), signed protocol fields dropped in pairs, a Boost puzzle's content hash. Removing them leaves the script doing the same thing, but the output no longer carries what it was made to carry. Where a region carries data, its one-byte fields and empty pushes are kept too. --drop-unused-data (keepData: false) allows the old behaviour.
  • Standard output templates come back unchanged: P2PKH, P2PK, P2SH, bare multisig and OP_RETURN data outputs. A bare multisig listing the same key twice could reuse it with OP_OVER and save 65 bytes, and stop being a bare multisig, which wallets, indexers and relay policy recognise by its exact bytes. --rewrite-templates (templates: false) optimizes them anyway.

Generated contract code is unaffected: across 72 mainnet script templates the savings are the same as with the protections off, and the Miller loop, the final exponentiation and 19 other module scripts come out byte for byte as before.

--effort high no longer wastes minutes

The superoptimizer's A* search costs more the deeper the stack a window starts from, while its wins do not: on real scripts every replacement it found started from a stack of at most 15 items, and the searches on stacks of 20 or more found nothing while taking most of the time. Those windows now go to the lookup table.

script bytes before after
field-300.hex 2,607 45 s 8.9 s
a 934 B mainnet contract 694 155 s 51 s

Output is byte for byte what it was.

Also

  • The reason ASM output is refused now names the real cause for data after a top-level OP_RETURN too.
  • New: docs/mainnet-survey.md and examples/mainnet-survey.js — what mainnet locking scripts are made of, and what this saves. Half of outputs are P2PKH holding under 3% of the bytes; most bytes are data; the saving is concentrated in contracts that check their own transaction (a fifth to a third of compiled contract bytes, under 1% of all locking-script bytes).

Install

npm install @smartledger/scriptmin @smartledger/bsv

@smartledger/scriptmin 0.1.2

Choose a tag to compare

@codenlighten codenlighten released this 17 Sep 16:31

Fixes

  • --no-chronicle results are tested under the rules before and after Chronicle. Interpreter tests used to run under current consensus flags only, so a change that broke a script only under the older rules could pass them. The OP_VERIF dead-block bug fixed in 0.1.1 was one. differential() accepts a list of flag sets, and eraFlags({ chronicle }) gives them. (#3)
  • ASM output reads back as the same script, or is refused. --asm -o wrote <tail N bytes> in place of the data after OP_RETURN, and OP_PUSHDATA1:<hex> for pushes of 76 bytes or more, and neither reads back. Output now uses the ASM that @smartledger/bsv reads, with the OP_RETURN data included, and is checked by reading it back. When ASM cannot hold the script exactly, the CLI exits with the reason and suggests hex. Those cases are a non-minimal or truncated push kept verbatim, or data pushes alone. exactAsm() is exported. (#3)
  • Unnamed opcodes (0xba–0xfc) are written in ASM as 0xba, which @smartledger/bsv 9.11.1 and later reads back. With an older version such scripts are refused. (#4)
  • Tested against @smartledger/bsv 9.11.2, which writes empty pushes in text form correctly. OP_RETURN data such as buildDataOut('') now round-trips through ASM. (#5)

Install

npm install @smartledger/scriptmin @smartledger/bsv

@smartledger/scriptmin 0.1.1

Choose a tag to compare

@codenlighten codenlighten released this 17 Sep 14:51

Fixes

  • Data envelopes are kept byte for byte. An OP_0 OP_IF … OP_ENDIF block never runs, so the optimizer could rewrite what is inside it and still pass its equivalence checks. That is where inscription envelopes (1Sat Ordinals, BSV-21 and similar) carry their data, and 0.1.0 could change it, for example turning a repeated field into OP_OVER. Such blocks are now kept verbatim, and code around them is still optimized. Blocks containing OP_VERIF/OP_VERNOTIF are left to ordinary branch handling, because where they end depends on whether Chronicle rules apply. (#1)
  • No quadratic slowdown on very deep stacks. A constant ROLL/PICK index in the tens of thousands made stack scheduling quadratic: a 6-op script took 75 s. The scheduler now abandons schedules far larger than the code they would replace, and the script takes 3.8 s. The Miller loop, final exponentiation and other real module scripts optimize to the same bytes as before. (#2)
  • Wider fuzz test: every modelled opcode, unusual push encodings, boundary numbers, and computed PICK/ROLL indices.

Install

npm install @smartledger/scriptmin @smartledger/bsv

@smartledger/scriptmin 0.1.0

Choose a tag to compare

@codenlighten codenlighten released this 17 Sep 12:37

A Bitcoin Script minimizer. It takes a working locking script, returns a smaller one that does exactly the same thing, and proves the two are equivalent.

What's in it

  • scriptmin <script>: minimizes a script and reports where the savings came from. It uses:
    • a liveness-driven stack scheduler, with an optional beam search over its choices;
    • an exhaustive table plus A* superoptimizer for short stack sequences;
    • peephole rules and constant folding.
  • scriptmin --profile: shows where a script's bytes go (stack moves, arithmetic, constants, hashing), how deep its PICKs and ROLLs reach, and its most expensive repeated patterns.
  • scriptmin compile: compiles a prime-field circuit (JSON) to Script with lazy modular reduction.
  • scriptmin relax: lifts existing field-arithmetic Script back to its circuit and re-decides where it reduces.

Correctness

  • Symbolic proof: every result is proven equivalent region by region. It fails on exactly the stacks the original fails on, leaves identical main and alt stacks, and evaluates the same set of checks.
  • Interpreter tests: results are also run against the original on @smartledger/bsv's interpreter.
  • relax: its results are checked on the interpreter against the original, on canonical inputs and out-of-range ones.

Measured on real code

On script-high-level-modules, a library of BSV Script modules up to a full BLS12-381 pairing:

  • Miller loop: the 333 KB loop that ran on mainnet drops 40%, to 199 KB, and every test case of the library still passes.
  • Mainnet redeployment: the library's two-transaction pairing chain was redeployed to mainnet with minimized stage scripts.

Details are in docs/real-world.md.

Install

npm install @smartledger/scriptmin @smartledger/bsv

Requires Node 20.19+.