Skip to content

Add BRC-Z: Bitcoin Script to and from Bitcoin BASIC — a compiler and a decompiler - #228

Open
sun-dive wants to merge 1 commit into
bsv-blockchain:masterfrom
sun-dive:add-bitcoin-basic
Open

Add BRC-Z: Bitcoin Script to and from Bitcoin BASIC — a compiler and a decompiler#228
sun-dive wants to merge 1 commit into
bsv-blockchain:masterfrom
sun-dive:add-bitcoin-basic

Conversation

@sun-dive

Copy link
Copy Markdown
Contributor

This pull request:

Proposes a new standard by creating a new markdown file in the appropriate directory and requests discussion and assignment of a BRC number

Summary

Bitcoin Script is a stack machine with no names, no types and no structure a reader can hold. It is also, almost exactly, the machine underneath every infix expression evaluator written for an 8-bit microcomputer: v = v + f/m - v*d becomes the same sequence of stack operations in either.

This proposes Bitcoin BASIC — a BASIC dialect in one-to-one correspondence with the opcode set — and both translations between it and Script: a compiler, and a decompiler that renders a deployed locking script back into readable form.

The decompiler is the half without precedent. A compiler reports what an author meant; a decompiler reports what the bytes say, including bytes the reader did not write, fetched from the chain, belonging to somebody else.

That matters because a covenant cannot be amended. A permanent constant sits somewhere inside a few thousand opcodes, one edit moves every offset and depth behind it, and what gets verified in practice is a mental model of the script rather than the script — so errors surface after publication and funding, when nothing can be changed.

Relationship to BRC-15 and BRC-106

This sits above the existing representations rather than competing with them, and the document says so explicitly.

BRC-15 and BRC-106 operate at one token per opcode: lossless, exact opcode sequence, and no structure recovered. OP_ADD states that an addition occurs; it does not state that a line reads v = v + f/m - v*d, nor which bytes of a script are its mutable state.

Bitcoin BASIC operates on sequences of opcodes and recovers structure — expressions, conditional blocks, named state fields, loop bodies where the loop survives in the script. It is therefore lossy in spelling and exact in behaviour. The layers compose: Bitcoin BASIC to Script, and Script to ASM under BRC-15 or BRC-106.

Normative content, briefly

  • 35 words mapped to their opcodes, with hex, so a listing produced by one tool is parsed by another. ⚠ = and SAMEBYTES MUST stay distinct: OP_NUMEQUAL compares numbers and OP_EQUAL compares byte strings, and 0, -0 and a padded zero are one number and three different byte strings.
  • Branch arms MUST be checked for equal stack shape, and emission refused otherwise — the defect class a compiler exists to remove.
  • FOR MUST be unrolled with static bounds and a bounded expansion; Script has no backward jump for a loop to compile to.
  • DIM widths are the widths carried, bounded 1–75, above which OP_PUSHDATA1 adds a length byte and shifts every offset after the field.
  • A construct with no opcode MUST be refused with a reason, never approximated. Exponentiation is the worked case: ^ folds at compile time and fails if either side is unknown until the script runs.
  • A decompiler MUST read scripts it did not produce, modify nothing, and report loss of stack tracking rather than guessing.
  • Decompiled programs MUST recompile to semantically identical scripts, and the measurement SHOULD include how many recompile byte-identically.

Examples and evidence

Four examples are given in both languages — an expression and a balanced conditional with their emitted opcodes in hex, noughts and crosses indexing by modular arithmetic in the absence of arrays, and Rule 110 compiled from one source with the loop in the script and with the loop in the chain, arriving at the same state.

On Turing completeness the document is deliberately careful. A single script is total. But a script iterates perfectly well — Rule 110 advances thirty-one cells per generation, unrolled, inside one script. What it cannot do is run an unknown number of times, and that is the only thing the chain is needed for: the chain does not supply the looping, it supplies the not knowing when to stop. The qualification is stated as the tape rather than the loop, since the deployed instance carries 31 cells.

Implementation

To use it: https://grafverse.com/basic.html — compiles, decompiles, and reads a script off the chain, with no installation and no build step.

To verify it, or to have it outlast this document: the archive is published on chain, so the reference implementation is a payload rather than a link.

payload   8e0a79e00ef3c38cf3c4fa33c7d8032cc5def3853c0e798730c48217f00a369a
archive   f308f18b952360b46d207beb8539bb202d704ab80e23c39425c76f024d908175   200,280 B · 15 files

The archive was recovered from that transaction, unpacked, and its three test suites run — 42/42, 18/18 and 10/10 — so verification is reproducible from the txid alone. The compiler and decompiler import only an opcode-number table and a type that erases at runtime: an implementation in another language needs a list of opcode numbers and nothing more.

Filed as BRC-Z for number assignment.

Bitcoin Script is a stack machine with no names and no structure a reader can
hold. It is also the machine underneath every infix expression evaluator written
for an 8-bit micro: v = v + f/m - v*d becomes the same sequence of stack
operations in either. This proposes a BASIC dialect in one-to-one correspondence
with the opcode set, and both translations between them.

The decompiler is the half without precedent. A compiler reports what an author
meant; a decompiler reports what the bytes say, including bytes the reader did
not write. That matters because a covenant cannot be amended: a permanent
constant sits inside a few thousand opcodes, one edit moves every offset behind
it, and what gets verified is a mental model rather than the script — so errors
surface after publication and funding, when nothing can change.

Normative: 35 words mapped to their opcodes, with = and SAMEBYTES kept distinct
because OP_NUMEQUAL and OP_EQUAL disagree about a padded zero; branch arms
checked for equal stack shape and emission refused otherwise; FOR unrolled with
static bounds; DIM widths bounded 1..75, above which OP_PUSHDATA1 shifts every
offset after the field; a construct with no opcode refused with a reason rather
than approximated; and a decompiler that reads scripts it did not produce,
modifies nothing, and reports loss of stack tracking rather than guessing.

It sits above BRC-15 and BRC-106 rather than competing with them. Those operate
at one token per opcode and recover no structure; this operates on sequences and
recovers expressions, blocks and named state, so it is lossy in spelling and
exact in behaviour. The layers compose.

Four examples are given in both languages, including the same Rule 110 source
compiled with the loop in the script and with the loop in the chain, arriving at
the same state. On Turing completeness the document states the distinction
carefully: a single script is total, a script iterates perfectly well, and what
it cannot do is run an unknown number of times. The chain does not supply the
looping — it supplies the not knowing when to stop. A timestamp server has no
unbounded time to offer a computation that will never finish.

The implementation is linked for use and published on chain for permanence; the
archive recovered from that transaction was unpacked and its tests run.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
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