Advanced Bitcoin Scripting
Bitcoin uses a scripting system for transactions. Forth-like, Script is simple, stack-based, and processed from left to right. The script inside transaction outputs is intentionally not Turing-complete and has no jump instructions to prevent the formation of loops, however with the use of an off-chain agent, turing-complete processes can be built using the ledger as a ticker tape to store computational results.
A transaction output script is a predicate formed by a list of instructions that describe how the next person wanting to transfer the tokens locked in the script must unlock them. The script for a typical P2PKH script to Bitcoin address D simply encumbers future spending of the bitcoins with the provision of two things:
- a public key that, when hashed, yields destination address D embedded in the script, and
- a signature to prove ownership of the private key corresponding to the public key just provided.
Scripting provides the flexibility to change the parameters of what's needed to spend transferred bitcoins. For example, the scripting system could be used to require two private keys, or a combination of several keys, or even no keys at all. The tokens are unlocked if the solution provided by the spending party leaves a non-zero value on the top of the stack when the script terminates.
De facto, Bitcoin script is defined by the code run by the nodes building the Block chain. Nodes collectively agree on the opcode set that is available for use, and how to process those opcodes. Throughout the history of Bitcoin there have been numerous changes to the way script is processed including the addition of new opcodes and disablement or outright removal of opcodes from the set.
The nodes checking Bitcoin script process transaction inputs in a script evaluation engine. The engine is comprised of three stacks which are:
- The main stack
- The alt stack
In addition, the system also uses a subscript management system to track the depth of nested If-Loops
The main and alt stacks hold byte vectors which can be used by Bitcoin opcodes to process script outcomes. When used as numbers, byte vectors are interpreted as little-endian variable-length integers with the most significant bit determining the sign of the integer. Thus 0x81 represents -1. 0x80 is another representation of zero (so called negative 0). Positive 0 is represented by a null-length vector. Byte vectors are interpreted as Booleans where False is represented by any representation of zero and True is represented by any representation of non-zero.
Before the Genesis upgrade, byte vectors on the stack are not allowed to be more than 520 bytes long however in the unbounded Bitcoin protocol while pushdata opcodes are limited to pushing 4.3GB onto the stack it is theoretically possible to concatenate multiple objects on the stack to form larger singular data items for processing.
Before Genesis, Opcodes which take integers and bools off the stack require that they be no more than 4 bytes long, but addition and subtraction can overflow and result in a 5 byte integer being put on the stack. After the Genesis upgrade in early 2020, miners are now free to mine transactions with data items of any size possible within protocol rules. These will be usable with mathematical functions within script. At this time, Miners will collectively agree on appropriate data limits rather than allowing a centralised committee to form a set of default constraints.
More on Bitcoin Script
- Opcodes used in Bitcoin Script
- Number Encoding in Bitcoin Script
- Scripts with Flow Control (Conditional Clauses)
- Data passing in inputs
- Complex Script Examples
This content is based on content sourced from https://en.bitcoin.it/wiki/Script under Creative Commons Attribution 3.0. Although it may have been extensively revised and updated we acknowledge the original authors.