crypto

HOW TO: Write Custom Bitcoin Scripts in Bitauth IDE

Bitcoin Script is the language used by Bitcoin to validate transactions. Bitcoin “addresses” are actually locking scripts defined in this language, and in order to spend funds, every transaction must have a matching unlocking script, which provides the signatures and other data required to “unlock” the funds.


Bitauth IDE is an integrated development environment for bitcoin authentication. This guide will explain features of the IDE and language.

cryptoThis guide is also built-in to the IDE itself, so if you’re on a desktop, you can also read it there.

Bitauth Templates:

When you work in Bitauth IDE, you’re working on a Bitauth Template. It’s a JSON file which fully describes the authentication scheme for a bitcoin wallet. Compatible wallet software can import your template and generate a fully-functional wallet, even for complex, multi-party schemes. The IDE lets you write, test, and export Bitauth templates.

Bitauth templates include two primary concepts:

  • Entities — the individuals and/or devices participating in the wallet.

Entities:

A Bitauth template defines a set of entities which will use the template. Each entity can be assigned one or more variables for which they are responsible. There are currently six variable types: Key, HDKey, WalletData, AddressData, CurrentBlockHeight, and CurrentBlockTime.

When a wallet is created, each entity shares the public elements of their variables. Values are validated to prevent man-in-the-middle attacks, and then wallet addresses are generated.

crypto
You can find the Entities for the current Bitauth Template in the menu to the left. Here you can see the settings and variables for “Signer 1”, one of two co-owners in this 2-of-2 wallet with a time-delayed fallback.

Scripts:

Bitauth templates define a set of scripts used by the entities. There are four types of scripts:

  • Locking Scripts — scripts from which wallet addresses are generated.

Bitauth Templating Language (BTL)

Bitauth template scripts are written in Bitauth Templating Language (BTL). The language is very low-level — any bitcoin virtual machine bytecode can be represented in BTL.

Opcodes:

Opcode identifiers in BTL are prefixed with OP_. During compilation, opcode identifiers are replaced with their bytecode equivalents. E.g OP_0 OP_1 OP_ADD will compile to the bytecode 005193 (hex-encoded).

All opcodes are also auto-completed within the IDE. To read a description of a given opcode, hover over it in the editor. You can also find resources describing bitcoin opcodes online.

crypto

The IDE provides auto-completion and inline documentation for each opcode. Type “OP” to scroll through and explore the list.

Literal Data Types:

BTL supports 3 literal data types:

  • Hex literals — hex-encoded data, prefixed with 0x, e.g. 0xc0de.
btc

Try selecting “Scratch Pad” from the welcome screen to experiment with literal types.

Push Statements:

Push statements are surrounded by < and >, and generate the opcode to push their compiled contents to the stack.

For example <"abc"> will generate the bytecode to push "abc" (616263) to the stack: 03616263 (disassembled: OP_PUSHBYTES_3 0x616263). Pushes are automatically minimized: e.g. <1> compiles to 51 (disassembled: OP_1), and <OP_0> (equivalent to <0x00>) compiles to 0100 (disassembled: OP_PUSHBYTES_1 0x00).

Any valid BTL can be contained in a push statement (including further push statements), so code like <<<<1>>>> is valid. (Result: 03020151)

cryptography

Hover over any segment of code in the editor to see its compiled bytecode form (which is being evaluated on the right). In this example, we push a push (2) of a push (3) of a push (4) of the number 1. The first push is optimized to “OP_1” (0x51), the second push prefixes the result with an “OP_PUSHBYTE_1” (0x01), and so on. You can see that the stack reflects the final push of “0x020151" (a valid Script Number equal to 5308674).

Including Variables & Scripts:

Every Script and variable has a unique ID within the template. Both can be included by referencing the unique ID. E.g. an AddressData variable with an ID of nonce can be pushed to the stack with <nonce>.

When referenced, variables and scripts are included directly as bytecode. This makes it possible to provide segments of bytecode in variables and to use isolated scripts as macros. E.g. <my_number> pad_value might push the variable my_number and then insert the pad_value script, which might be defined as <8> OP_NUM2BIN, padding my_number to 8 bytes.

bitcoin

The “pad_nonce” script being used as a macro. In the top window, the functionality of “Pad Nonce” is tested by “Test Padding”, and the test is passing. 

Variable Types:

Each variable has a type which specifies its role in a template.

There are currently six variable types:

  • AddressData– Address Data is the most low-level variable type. It must be collected and stored each time a script is generated (usually, a locking script). Address Data can include any type of data, and can be used in any way. For more persistent data, use WalletData.

public key

The interface for defining a variable. Here you can see the definition of delay_seconds in the built-in “2-of-2 with Business Continuity” template.

Variable Operations:

Some variable types provide operations which are accessed with a period (.), e.g. the public key of the owner Key can be pushed to the stack with <owner.public_key>.

Several operations are available to Key and HDKey variables:

  • public_key– include the public key.
public key

All scripts, variables, and operations can be auto-completed, and documentation is provided inline.

Signatures:

Signatures (signature and schnorr_signature) are generated by serializing elements of the signed transaction in a standard way, hashing the serialization, and signing the message hash.

There are six signing serialization algorithms:

  • all_outputs– the recommended (and most commonly used) signing serialization algorithm. This signs each element of the transaction using the private key, preventing an attacker from being able to reuse the signature on a modified transaction. (A.K.A. “SIGHASH_ALL”)

Most authentication schemes should use the all_outputs setting, e.g. <owner.signature.all_outputs>. This prevents an attacker from being able to reuse the signature on a different transaction (which the key holder did not intend to authorize).

For unique circumstances, the other algorithms can also be specified — you can find resources online which describe some of these scenarios and their security implications.

To display debugging information, Bitauth IDE transparently integrates scripts into a simple transaction and evaluates it in the bitcoin-ts virtual machine implementation.

Bitauth IDE

The schnorr_signature operation being used with the all_outputs signing serialization algorithm.

Data Signatures:

For data signatures (data_signature and schnorr_data_signature), the message to hash and sign is provided as a script, e.g. <owner.data_signature.message> will hash the compiled bytecode representation of the message isolated script, signing the hash using the owner Key.

Bitauth IDE

Data signatures cover other scripts, so it’s easy to create a script for a preimage and use the resulting bytecode in both pushes and signing operations.

Evaluations:

Evaluations are segments of code surrounded by $( and) which use the bitcoin virtual machine itself to assist in generating bytecode. The contents of an evaluation are compiled and evaluated, and the top element on the resulting stack is then inserted as bytecode. E.g. $(<1> <2> OP_ADD) "abc" produces the bytecode 03616263 (disassembled: OP_PUSHBYTES_3 0x616263).

This is surprisingly useful — often the procedure to create a desired bytecode sequence is similar to the procedure later used to validate it. For example, a P2SH locking script is generated using this BTL:

OP_HASH160 <$(<redeem_script> OP_HASH160)> OP_EQUAL

First, in the evaluation, the compiled bytecode of redeem_script is pushed to the stack and hashed. Then the final locking script can be generated by inserting the bytecode for OP_HASH160, followed by a push of the generated redeem script hash, followed by the bytecode for OP_EQUAL.

Evaluations also offer a way to make variables more powerful — here we add delay_seconds to the block time (at wallet creation) to arrive at the value used in this locking script’s OP_CHECKLOCKTIMEVERIFY operation.

Getting Started:

The easiest way to get started working with Bitauth IDE is to review the example templates. You’ll find examples of both common wallet types and of complex, multi-entity authentication schemes.

CBNN

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.