SCRIPT Writer
ESCRIPT: a human readable language for programming Bitcoin scripts
SCRIPT Writer defines a human friendly Bitcoin SCRIPT language and provides a set of functions to translate programs written in this extended language (called ESCRIPT, which is an acronym for Extended SCRIPT) to Bitcoin SCRIPT bytecode. See below for the syntax description, or visit https://vm100.cs.stir.ac.uk/~rkl/docu.php.
The extended language is a superset of the bytecode SCRIPT language. This means that it is possible to define a script partially using extended syntax constructions, and partially in direct streams of bytecode. Bytecodes are passed through the parser as-is.
The language can be tried out at https://vm100.cs.stir.ac.uk/~rkl/home.html (at the bottom of the page, beneath "Serialization of a script:").
Installation
Dependency: Haskell's Stack (https://docs.haskellstack.org/en/stable/install_and_upgrade/)
In the root directory of this repository run:
# On some machines it is required to first manually install happy:
stack install happy
# Install Script Writer and all of its dependencies (other than Happy)
stack install
The executable (SCRIPTWriter-exe) can be executed in any directory of this repository through Stack as follows: stack exec SCRIPTWriter-exe -- arguments
Call SCRIPTWriter-exe, with the custom script in stdin.
For example, if file scriptA contains a script written in the human friendly language, run the following in Bash: stack exec SCRIPTWriter-exe -- < scriptA
Some example scripts can be found in folder scripts/
The custom syntax
The supported syntax is described below.
Instructions on how to interpret the description:
- The "*" symbol specifies a repeated parsing of 0 or more times
- The "+" symbol specifies a repeated parsing of 1 or more times
- The "|" specifies an or (either parses following the left hand
side or the right hand side)
- The ".." specifies a range of allowed characters.
Any amount of whitespace is allowed between each instruction and between
the PUSH keyword and the subsequent bytestring. Parsing starts by applying the
Start rule. Anything after "#" on a line is treated as a comment (similar to how comments work in Bash).\
Start := (Whitespace* Instruction | Whitespace* Byte)* Whitespace*
Instruction := Push | Mnemonic
Push := "PUSH" Whitespace* Bytestring | "PUSH" Whitespace* Integer
Integer := "i" Num+ | "i-" Num+
Num := "0".."9"
Bytestring := Byte+
Byte := Hexadecimal Hexadecimal
Hexadecimal := "0".."9" | "a".."f" | "A".."F"
Whitespace := " " | "\t" | "\n" | "\r"
Mnemonic := "OP_0" | "OP_FALSE" | "OP_PUSHDATA1" | "OP_PUSHDATA2"
| "OP_PUSHDATA4" | "OP_1NEGATE" | "OP_RESERVED" | "OP_1"
| "OP_2" | "OP_3" | "OP_4" | "OP_5"
| "OP_6" | "OP_7" | "OP_8" | "OP_9"
| "OP_10" | "OP_11" | "OP_12" | "OP_13"
| "OP_14" | "OP_15" | "OP_16" | "OP_NOP"
| "OP_VER" | "OP_IF" | "OP_NOTIF" | "OP_VERIF"
| "OP_VERNOTIF" | "OP_ELSE" | "OP_ENDIF" | "OP_VERIFY"
| "OP_RETURN" | "OP_TOALTSTACK" | "OP_FROMALTSTACK" | "OP_2DROP"
| "OP_2DUP" | "OP_3DUP" | "OP_2OVER" | "OP_2ROT"
| "OP_2SWAP" | "OP_IFDUP" | "OP_DEPTH" | "OP_DROP"
| "OP_DUP" | "OP_NIP" | "OP_OVER" | "OP_PICK"
| "OP_ROLL" | "OP_ROT" | "OP_SWAP" | "OP_TUCK"
| "OP_CAT" | "OP_SUBSTR" | "OP_LEFT" | "OP_RIGHT"
| "OP_SIZE" | "OP_INVERT" | "OP_AND" | "OP_OR"
| "OP_XOR" | "OP_EQUAL" | "OP_EQUALVERIFY" | "OP_RESERVED1"
| "OP_RESERVED2" | "OP_1ADD" | "OP_1SUB" | "OP_2MUL"
| "OP_2DIV" | "OP_NEGATE" | "OP_ABS" | "OP_NOT"
| "OP_0NOTEQUAL" | "OP_ADD" | "OP_SUB" | "OP_MUL"
| "OP_DIV" | "OP_MOD" | "OP_LSHIFT" | "OP_RSHIFT"
| "OP_BOOLAND" | "OP_BOOLOR" | "OP_NUMEQUAL" | "OP_NUMEQUALVERIFY"
| "OP_NUMNOTEQUAL" | "OP_LESSTHAN" | "OP_GREATERTHAN" | "OP_LESSTHANOREQUAL"
| "OP_GREATERTHANOREQUAL" | "OP_MIN" | "OP_MAX" | "OP_WITHIN"
| "OP_RIPEMD160" | "OP_SHA1" | "OP_SHA256" | "OP_HASH160"
| "OP_HASH256" | "OP_CODESEPARATOR" | "OP_CHECKSIG" | "OP_CHECKSIGVERIFY"
| "OP_CHECKMULTISIG" | "OP_CHECKMULTISIGVERIFY" | "OP_NOP1" | "OP_CHECKLOCKTIMEVERIFY"
| "OP_CHECKSEQUENCEVERIFY" | "OP_NOP4" | "OP_NOP5" | "OP_NOP6"
| "OP_NOP7" | "OP_NOP8" | "OP_NOP9" | "OP_NOP10"