Skip to content

Script Templates

Allows 3 agent (covenant, owner, and spender) trustless interaction

Introduction

A script template is a generalization of the P2SH (pay-to-script-hash) format, that allows for the implementation of covenants. It is perhaps conceptually difficult to understand at first, but is both easy to implement over the existing Bitcoin scripting system and is powerful.

A script template factors all data in a traditional script out into arguments that are furnished by the owner and spender. For example, a traditional pay-to-pubkey-hash script looks like this:

DUP HASH160 PUSH pubkeyhash EQUALVERIFY CHECKSIG

Since the pubkeyhash changes for each address, a slightly different script is created for each address. A script template factors this data out of the script, expecting it as arguments (i.e. on the stack) before the script is executed. This means that every pay-to-pubkey-hash script is the exact same bytes.

This confers several advantages: * easy script identification * parameter extraction is unnecessary * The hash of the script template does not change when parameters change * 3 participant interaction

Note that the arguments may themselves be scripts, executed via the EXEC opcode.

3 Participant Interaction

A smart contract or covenant system contains 3 agents (an "agent" is defined by lack of trust) whereas traditional bitcoin contains two. The 3 agents are: * The contract/covenant author (often the group creator) * The current holder * The spender

The contributions of these three agents are captured in scripts that are wrapped into variable sized data spaces that (for legacy reasons) use the same format as executable scripts and are often referred to as scripts. The following definitions may provide clarity:

Outputs/ Creating UTXOs

The template script is defined by the contract/covenant author. It defines the overall structure in which the other scripts execute. It may be conserved from input to output so implements covenants. It implements any constraints (or spending permissions) defined by the Group, and decides when to delegate constraints/permissions to constraint scriptlets. The template script is not actually placed into an output, but a commitment to it is.

The constraint args script refers to "constraining" arguments to the template script. It is provided by current holder ("owner") of a UTXO to constrain future spending in a manner enforced by both the template AND by the current owner. It typically consists of a commitment to data and/or scriptlets (small script fragments/functions), and "raw" or "visible" data and scriptlets. This data and scriptlets are used/executed at various times as controlled by the template (so the contract/covenant author may give the holder the ability to add restrictions on spending). A constraint script is optional (its possible that the template script allows no additional constraints).

All of the above is combined with token information and optional arbitrary data to form the "script" that appears in a transaction output. This is called the locking script for legacy reasons, even though it is not a script -- it is actually set of variable length fields.

Inputs / Spending UTXOs

The satisfier args script is analogous to the traditional "scriptSig". It is provided by the spender and provides the arguments that the template script (and constraint scriptlets) that are needed to "satisfy" the constraints enforced by the other scripts. The satisfier script may only push data items onto the stack, so is not truly a script.

The unlocking script is the complete disclosure required to execute and verify spendability. It contains the actual template script, the actual constraint script, and the satisfier script.

Relationship to P2SH

Note that if the holder's modifications are not factored out of a script, the system reduces to P2SH with 2 parties. But the underlying implementation of script templates is much cleaner since P2SH was deployed as a soft fork. This meant that it had to be partly compatible with, but also patch the standard script execution model.

In contrast, script templates specifies a separate execution model (described below).

Script Template Structure Within a Transaction

Locking

To indicate a template, a transaction output's type is set to TEMPLATE(1). A template's "scriptPubKey" field contains the "locking script". The locking script contains the following, serialized as a "push-only" script, but never executed.

GroupId or empty stack item, Group amount if GroupId, H(script template) or well-known script template, H(constraint script) or empty stack item, any additional visible constraint args (as multiple pushes)

Unlocking Script (spending)

This template is spent with a "push-only" script located in the transaction "vin" inputs. It consists of:

Script template (as a single data "push"), if the template is not well-known. If the template is well-known do not push anything. Constraint script (as a single data "push"). If there is no constraint script (if H(constraint script) in the locking script is empty), do not push anything. satisfier script data pushes.

Note that the script template and args script are not actually pushed onto the script machine stack so can exceed script stack limits.

Hash Functions

H() is either the Hash256 or Hash160 "standard" hash functions (creator's choice). The choice is clear based on the size of the pushed item. Hash160 is recommended unless the creation of the preimage is multi-party and potentially susceptible to Wagner's Birthday Attack.

Well-known script templates

Certain well-known script templates are specified with hard-coded numbers. From time to time new well-known script templates may be added via a hard fork.

Additionally, software may choose to compress commonly used script templates by using >2 byte (and <9 byte) numbers. In such a case, the software MUST provide an interface to query the mapping from number to script template (which is beyond the scope of this document). The difference between a "well-known" and "commonly used" script template is that the "commonly used" number must be replaced with the actual hash, and its script with the actual script, when the transaction is included in the blockchain or hashed.

Constraint Script Visibility

To minimize space in the UTXO, and manage data visibility, a creator can choose to add a commitment to a push-only script of arguments, and directly push additional (visible) arguments.

Example

The equivalent of "pay-to-pubkeyhash" (p2pkh) is encoded as follows (italics denote data):

Template = FROMALTSTACK # We will discuss this later CHECKSIGVERIFY # Could be CHECKSIG

Constraint argument script = PUSH pubkey

Satisfier = PUSH signature

The locking output is: FALSE # No group PUSH Hash160(Template) PUSH Hash160(Constraint argument script)

The unlocking input is: PUSH Template PUSH Constraint argument script PUSH signature # that is, the satisfier script is appended.

Note that since the hash of the constraint argument script is pushed as the locking output the pubkey is hidden until spend. So there is no need to explicitly code this like in the standard P2PKH script.

Well-known Example

The script described above is actually "well-known" script number 1, so it could be rewritten as:

The locking output is:
FALSE # No group
OP_1
PUSH Hash160(Constraint argument script)

The unlocking input is: PUSH AsData(PUSH pubkey) PUSH signature

On detail to note is that the constraint arguments are pushed as a script even if it is just one argument. This is emphasized above with the "AsData(...script...)" notation.

Script Template Execution Model

The unlocking input and locking output data are analyzed and the satisfier, constraint (potentially in 2 parts, the hashed and visible), and template scripts are extracted as described above.

The satisfier and constraint scripts are executed in separate "script virtual machine" environments with "push-only" restrictions. When executing the constraint script, The hashed portion is executed first, and then the visible portion is executed.

The main stacks from these two executions are saved.

A third "template" script virtual machine is created. The "main" stack from the satisfier execution is copied to the "main" template stack. The "main" stack from the constraint execution is copied to the "alt" template stack. The template script is then executed.

In other words, the template script can access the result of the satisfier script on its main stack, and the result of the constraint script on its alt stack.

Execution is successful if the main stack is empty (and no "VERIFY" operations failed during execution).

Address Formats

Well known templates are identified by address prefix bytes, similar to Bitcoin/Bitcoin Cash, resulting in an address similar to what we use today.

A complete address is possible, specifying group, template hash, and constraint args is possible in around 72 raw bytes (or 40 bytes without the group). This turns into a long ascii-encoded address, but not an unusable one.

Advantages

  • Allows interaction between 3 agents (who do not trust eachother).

  • Combined with OP_EXEC, this system allows 2 agents (the covenant author, and the output "owner") to specify constraints/permissions via scripts and data, rather than data only.

  • All scripts are provided to the blockchain when they are executed. This minimizes data storage in the UTXO database which is an important feature to support nontrivial scripts.

  • This also means that transaction size more accurately reflects execution time.

  • While trivial scripts (< 20 bytes) would be more efficiently encoded as raw bytes, it is expected that any such popular scripts will be assigned well-known numbers

  • The most common (pay someone) format is encoded slightly more efficiently than p2pkh.

  • Addresses remain reasonable for arbitrary scripts & templates, although this is larger than the addresses we currently have. In the worst case, an address must contain 2 SHA160 or SHA256 numbers (the template and the constraint scripts) and a Group. However, if the template is a group covenant, its value is enforced so does not need to be specified in the address. If no constraint script is used it does not need to be specified.

  • Allows P2SH and OP_GROUP to be removed from old-style scripts (templates should be used).