This article is the eighth – this time dedicated to transactions and scripts – of a series of in-depth explanations on the more technical part of Bitcoin, accessible even to those who aren’t experts in coding.
The following continues a sort of guide designed to gradually enter what many call the “rabbit hole”.
As far as the bibliography is concerned, it is necessary to mention the book “Mastering Bitcoin” by Andreas M. Antonopoulos, from which the images have been taken.
Multisignature scripts (or more simply multisig) set a condition where N public keys are stored in the script and at least M of them must sign to unlock a payment. They are also known as the M-of-N scheme, where N is the total number of keys and M is the signature threshold required for validation. Currently, standard multisig scripts are limited to a maximum of 3 public keys, however this limit only applies to standard multisig scripts (known as “bare”), not to wrapped multisig scripts in a Pay-to-Script-Hash (PS2H), limited to 15 keys (ergo allow up to 15-of-15).
The general form of a block script that sets the conditions for M-of-N multisignature is:
M <Public Key 1> <Public Key 2> … <Public Key N> N CHECKMULTISIG
Where N is the total number of public keys listed and M is the threshold of signatures required to spend the output. A block script that sets the condition 2-of-3 has this form:
2 <Public Key A> <Public Key B> <Public Key C> 3 CHECKMULTISIG
And it can be solved with an unlocking script containing pairs of signatures and public keys: <Signature B> <Signature C> or a combination of two signatures from the private keys corresponding to the three public keys listed. The two scripts together would form the combination of the validation script:
<Signature B> <Signature C> 2 <Public Key A> <Public Key B> <Public Key C> 3 CHECKMULTISIG
Due to a bug (which has become part of the consensus rules, ergo replicated forever), a workaround is applied that modifies the script to circumvent the bug, (simply by adding a 0 at the beginning), ergo this is the correct form:
0 <Signature B> <Signature C> 2 <Public Key A> <Public Key B> <Public Key C> 3 CHECKMULTISIG
P2SH was introduced in 2012 as a powerful new type of transaction that greatly simplifies the use of complex transaction scripts.
Imagine using a multisig script for each of your customers (AR or account receivable in fiscal language), which blocks payments so as to require at least two signatures to unlock them (eg the owner and a lawyer, or a partner) so as to offer guarantees against attacks, theft, etc..
2 <Owner’s Public Key> <Partner1 Public Key> <Partner2 Public Key> <Partner3 Public Key> <Attorney Public Key> 5 CHECKMULTISIG
This will cause problems:
- the owner should communicate this script to all customers before they make the payment;
- each customer should use a “special” wallet capable of creating custom transaction scripts (and be able to do so);
- the resulting transaction (or rather the destination address) would probably be 5 times heavier than a simple payment transaction (this script contains very long public keys);
- the transaction costs, in terms of fees, would be borne by the customer;
- a heavy (long) transaction script would remain in the UTXO set in the RAM (though not necessarily) of each full node (until it’s spent).
P2SH was built to solve (part of) these problems by replacing complex scripts with a hash. When a transaction tries to spend UTXO, it must contain the script that matches the hash, in addition to the unlock script, ergo “pay for the script that matches this hash, a script that will be presented later, when this output is spent”. The block script is replaced with a hash presented to the system at the time of redemption instead of a redeem script.
|Redeem Script||2 PubKey1 PubKey2 PubKey3 PubKey4 PubKey5 5 CHECKMULTISIG|
|Locking Script||HASH160 <20-byte hash of redeem script> EQUAL|
|Unlocking Script||Sig1 Sig2 <redeem script>|
The complex script that defines the output spending conditions (redeem script) is not present in the block script: instead there is only a hash and the redeem script is presented later, as part of the unlock script when the output is spent (this modifies the burden of commissions etc).
Later on, BIP 13 defined the ability to encode the hash of the script as an address in Base58Check, using the prefix 5 which in Base58Check becomes 3, such as 39RF6JqABiHdYHkfChV6USGMe6Nsr66Gzw which allows receiving payments (and therefore the payer to construct them) like a normal bitcoin address. It is important to remember that the fee burden is moved from the payer to the receiver, who will bear it when they include the script to spend the output.
It is not possible to insert a P2SH into a P2SH redeem script, since the specification is not recursive, whereas it is technically possible to include RETURN in a redeem script (no rule precludes it) but the transaction is reported as invalid. Note that because the redeem script is not presented to the network until you attempt to spend a P2SH output, if you lock an output with the hash of an invalid transaction it will be processed regardless.
Data Recording Output (RETURN)
The blockchain can have other uses besides payments, and many have tried to implement the use of script language for applications such as notarisation, smart contract, etc.. The first attempts to use Bitcoin’s script language for these purposes involved creating transaction outputs to store data on the blockchain (proof-of-existence). Many developers consider this an abuse and discourage such use since it “inflates” the blockchain by charging the cost of data storage to those who run a full node. In addition, these transactions create UTXO that cannot be spent, using the destination bitcoin address as a 20-byte free field: the data entered does not match any private key and the resulting UTXO can never be spent, ergo will never be removed from the database of UTXO sets which is precisely destined to “inflate” the blockchain.
In version 0.9 of the Bitcoin Core client, a compromise has been reached by introducing the RETURN operator that allows adding 80 bytes of nonpayment data to an output. Unlike using “UTXO fakes”, RETURN creates an explicitly provably unspendable, which does not need to be saved in the UTXO set. RETURN outputs are saved on the blockchain (they consume space and increase the size of the blockchain) but are not saved in the UTXO set so they do not inflate the memory pool of the UTXO and do not charge the nodes with RAM costs. A RETURN script looks like this:
RETURN <data> where data space is limited to 80 bytes and often represents a hash: many applications add a prefix to identify the type of application: Proof-of-Existence uses the 8 byte prefix DOCPROOF, which is encoded in ASCII with the hexadecimal 44 4f 43 50 52 4f 4f 46. It should be reiterated that there is no unlock script to spend a RETURN output (and it is usually a zero bitcoin output). If RETURN is referred to a transaction input, the script validation engine stops the execution and flags (marks) the transaction as invalid.
A standard transaction that complies with IsStandard() can only have one RETURN output, but a single RETURN output can be associated in a transaction with another output.
Two new command-line options have been added in Bitcoin Core 0.10, datacarrier controls the forwarding and mining of RETURN transactions, setting by default 1 to allow them; datacarriersize accepts a numeric argument that specifies the maximum size in bytes of a RETURN script, 83 bytes by default, which allows a maximum of 80 bytes of RETURN data plus 1 byte of the RETURN opcode and 2 bytes of the PUSHDATA opcode.
Timelocks are restrictions on transactions (or outputs) to make them spendable only after a certain time. The timelock at transaction level has been present in Bitcoin since the beginning, implemented in the nLocktime field. In 2015 and 2016 two new timelock features were introduced at the UTXO level: CHECKLOCKTIMEVERIFY and CHECKSEQUENCEVERIFY. Timelocks are useful for post-dating transactions and blocking value at a certain future date: even more, they extend bitcoin scripts to the time dimension, opening the doors to complex multi-step smart contracts.
Transaction Locktime (nLocktime)
The Transaction Locktime is a transaction-level configuration (a field in the transaction data structure) that defines the first moment after which a transaction is valid and can be forwarded to the network or added to the blockchain. It is also known as nLocktime from the name of the variable used in the Bitcoin Core codebase. In most transactions it is configured (set) to 0, which indicates immediate propagation and execution: if it is not 0 and below 500,000,000, the value is interpreted as the height of the block, which means that the transaction is invalid and is not forwarded or included in the blockchain before a specific block. If the transaction is transmitted to the network before the specified nLocktime, it is rejected as invalid by the first node receiving it, and not forwarded to others.
The limit of nLocktime is that although it makes it possible to spend some outputs in the future, it does not make it impossible to spend them up to that moment: it remains possible to send a transaction 3 months in the future and double-spend the same UTXO by the sender without the receiver noticing anything before the time. The only guarantee is that the receiver cannot spend before the time.
Check Lock Time Verify (CLTV)
In December 2015 a new form of timelock was introduced thanks to a soft-fork, based on a specification in BIP-65, the new script operator CHECKLOCKTIMEVERIFY (CLTV) was added to the script language. CLTV is a pre-output timelock (instead of a per-transaction timelock): adding the CLTV opcode in the redeem script of an output is restricted to the output itself, so that it cannot be spent before the set time. CLTV does not replace nLocktime, but restricts UTXO specifications so that they cannot be spent until the value is equal to or greater than nLocktime. The CLTV opcode takes a parameter as input, expressed as a number in the same format as nLocktime (block height or Unix epoch time), and, as indicated by the suffix VERIFY, it is the type of opcode that blocks script execution if the result (outcome) is FALSE. To block an output, CLTV is inserted into the output redeem script in the transaction that creates that output. If normally (P2PKH) there is:
DUP HASH160 <Public Key Hash> EQUALVERIFY CHECKSIG to block the output for three months from now, the transaction should be a P2SH with a redeem script:
<now + 3 months> CHECKLOCKTIMEVERIFY DROP DUP HASH160 <Public Key Hash> EQUALVERIFY CHECKSIG where <now + 3 months> is the height of the block or the time value estimated at 3 months from the time the transaction was mined: current block height + 12,960 (blocks) or current Unix epoch time + 7,760,000 (seconds).
When attempting to spend this UTXO, a transaction will be built that refers to the UTXO as an input. The signature and public key in the unlocking script of that input are used and the nLocktime of the transaction is configured to be equal to or greater than the timelock configured in the CHECKLOCKTIMEVERIFY. The transaction is then “broadcast” to the bitcoin network. If the CHECKLOCKTIMEVERIFY parameter configured by the sender is less than or equal to the nLocktime of the expense transaction, the script execution continues ( acting as if a no operation or a NOP opcode were executed). Otherwise the script execution stops and the transaction is considered invalid. More specifically, CLTV blocks the execution, marking the transaction as invalid if (BIP-65):
- the stack is empty;
- the item on top is less than 0;
- the lock-time type (block height vs. timestamp) of the object at the top of the stack and the nLocktime field are not equal;
- the object at the top of the stack is greater than the nLocktime field of the transaction;
- the nSequence input field is 0xffffffff.
After execution, if CLTV is met, the time parameter remains at the top of the stack and may need to be “dropped”, with DROP, for proper execution of the following opcode scripts.
nLocktime and CLTV are absolute timelocks that specify an absolute point in time. There are also relative timelocks that specify as a condition of output spending the passage of a certain time from the confirmation of the output in the blockchain. They are useful because they allow a chain of two or more interdependent transactions to be kept off-chain, while imposing a time constraint on a transaction that depends on the time elapsed from the confirmation of a previous transaction.
In other words, the clock does not start until the UTXO is recorded on the blockchain. This feature is particularly used in bi-directional channel states and in Lightning Network. Like absolute timelocks, relative timelocks are implemented both at transaction level and at the script opcode level. The transaction level is implemented as a consensus rule on the value of nSequence, a transaction field that is configured in each transaction input. At the script level, the related timelocks are implemented with the opcode CHECKSEQUENCEVERIFY (CSV) (BIP-68 and BIP-112, activated in May 2016 with a soft-fork of the consensus rules).
Relative timelocks can be configured in each transaction input via the nSequence field, originally (but never implemented in this context) to allow transaction changes in the mempool: a transaction containing nSequence input of less than 232-1(0xFFFFFFFF) indicates a transaction not yet “finalised”. This transaction would be kept in the mempool until it was replaced by another “spending” transaction with the same input and a higher nSequence value. Once the transaction whose inputs had an nSequence value of 232-1(0xFFFFFFFF) would be received, it would be considered finalised and mined. The original sense has never been implemented and the value of nSequence is basically configured to (0xFFFFFFFF) in transactions that do not use timelock. For transactions with nLocktime or CLTV the value must be configured as less than 231 in order for the timelock to work.
Like CLTV and nLocktime there is a script opcode for relative timelocks that takes advantage of the value of nSequence in the script. The opcode is CHECKSEQUENCEVERIFY (CSV). CSV relative timelocks are particularly used when many transactions are created and signed, but not propagated, ergo kept “off-chain”. A “daughter” transaction cannot be used until the “parent” has been propagated, mined, and the time specified in the relative timelock “matured”. An application of this use case can be seen in the Lightning Network.
In Bitcoin there is a subtle but significant difference between wall time and consensus time because in a decentralised network everyone has their own time perspective: events are not simultaneous and latency must be considered from this same perspective. The synchronisation between everyone, which creates a common ledger, is achieved every 10 minutes on how the same ledger existed in the past. The timestamp in the header of the block is configured by the miners, but there is a certain degree of freedom allowed by the consensus rules to take into account the differences in clock accuracy between the decentralised nodes.
Unfortunately, this creates an incentive for miners to lie about time within a block to earn extra fees by including “timelocked” transactions that are not yet “mature”. BIP-113 defines a new measurement of consensus time called Median-Time-Past: it is calculated by taking the timestamp of the last 11 blocks and taking the average, which becomes the consensus time and is used for all timelock calculations. By taking a midpoint from approximately two hours back, the influence of a block’s timestamp is reduced. Incorporating 11 blocks, no miner can influence the timestamp to earn fees by entering transactions that are not yet mature.
Median-Time-Past changes the implementation of the time calculation for nLocktime, CLTV, nSequence and CSV (the consensus time is approximately one hour behind the clock). When creating transactions with timelock, this should be taken into account when estimating the desired value to be encoded in nLocktime, CLTV, nSequence and CSV.