Share and get +16 +16

This is part 2 of our Best Bitcoin Script Guide. It is highly recommended that you read part 1 before you proceed with this.

The Best Bitcoin Script Guide Part 2

In part 1 we covered the following:

Introduction to bitcoin Script.

How do transactions in Bitcoin work?

How Scripts work?

The game of locking and unlocking in scripts.

ECDSA cryptography in bitcoin script.

NOTE: From here on out, we won’t be using the “OP_” command as frequently because it should be understood that “OP_” will always be prefixed to each opcode. Please keep this in mind. We didn’t use “OP_” to enhance readability, when you are executing the script please remember to use “OP_”.

Multisignature Transactions

The transactions that we have seen up till now are all very simple (one-to-one in its nature). However, transactions can become much more complicated and layered than that.

First up, we will be checking out multi-signature transactions. In a multi-signature transaction, the only way that the bitcoin outputs can be unlocked is if multiple people verify the transaction.

So, where is this useful?

Imagine there is a huge multi-national company. Obviously, they won’t let one single person control all of its funds, right? They will have a board of people who will be in charge of the funds.

Now, how will that work in the context of bitcoin script transactions?

Multi-signature scripts set a condition where N public keys are recorded in the script and at least M of those must provide signatures to unlock the funds. This is also known as an M-of-N scheme, where N is the total number of keys and M is the least number of signatures required for validation. The transaction is also called M-of-N multisig.

The script format of a multisig output looks like this:

M <Public Key 1> <Public Key 2> … <Public Key N> N CHECKMULTISIG

Let’s see how this works in an example.

Suppose we are sending money to a company headed by 3 people (Alice, Bob, and Charlie) and two out of those three people need to verify the transaction for it to go through. This transaction is also called a 2-of-3 multisig.

How will the output look like?

2 <Public Key Alice> <Public Key Bob> <Public Key Charlie> 3 CHECKMULTISIG

Alright, so this is the output. How will the company unlock the output and gain access to the funds? Remember that this is a 2-of-3 multisig meaning, 2 out of the 3 people involved must present their signatures.

So, the signature combination used can be any of the following:

<Signature Alice> <Signature Bob>

<Signature Bob> <Signature Charlie>

<Signature Charlie> <Signature Alice>

Suppose Bob and Charlie are the ones verifying the transaction. The complete validation script will then read like this:

<Signature Bob> <Signature Charlie> 2 <Public Key Alice> <Public Key Bob> <Public Key Charlie> 3 CHECKMULTISIG

IF the conditions above hold true and the signatures are correct, then the transaction will be TRUE and it will go through. However, if the signatures are incorrect, then the transaction will fail.

However, if you check any examples of a multi-sig transaction you will notice that the format that we have used:

<Signature Bob> <Signature Charlie> 2 <Public Key Alice> <Public Key Bob> <Public Key Charlie> 3 CHECKMULTISIG

…is incorrect.

The reason why that is so is that there is a bug in the CHECKMULTISIG opcode.

The Bug in CHECKMULTISIG opcode

Ideally, a CHECKMULTISIG opcode should pop out M+N+2 elements out from the stack. Let’s take the example that we have been working with so far:

<Signature Bob> <Signature Charlie> 2 <Public Key Alice> <Public Key Bob> <Public Key Charlie> 3 CHECKMULTISIG

Before CHECKMULTISIG gets executed, the stack will look like this:

CHECKMULTISIG, in essence, pops out all elements in the stack.

In this example M = 3 and N = 2.

The Items that CHECKMULTISIG pops out are: The 3 (M) public keys, 2 (N) signatures, and two constants which basically means that the total amount of items that should ideally be popped out are (M+N+2=) 3+2+2 = 7 items.

However, there is a glitch in the CHECKMULTISIG opcode which makes it pop one more item than are available in the stack. This obviously ends up causing a stack error.

In order to avoid this error, a workaround is used to counter it. Whenever we have a combined validation script we always begin with an “0”. This means that the script will now look like:

0 <Signature Bob> <Signature Charlie> 2 <Public Key Alice> <Public Key Bob> <Public Key Charlie> 3 CHECKMULTISIG

Subsequently, our stack now looks like this:

By adding that one extra element “0” in the beginning, we make sure that we don’t face any errors.

This also means that instead of using this unlocking script:

<Signature Bob> <Signature Charlie>

We end up using this:

0 <Signature Bob> <Signature Charlie>

What is Pay-to-Script-Hash or P2SH?

While Multisignature transactions give your transactions a lot of flexibility, they can get a tad complicated. Imagine a company which has 5 partners involved in the operation. If Alice were to send some bitcoins to this company, her output script will look like this (assuming 2 out of the 5 people involved need to verify the transaction):

2 <Public Key 1> <Public Key 2> <Public Key 3> <Public Key 4> <Public Key 5> 5 CHECKMULTISIG

As you can see, this becomes very cumbersome and has a whole of of disadvantages:

Firstly, the company will actually have to relay this information and script to their customers.

The customers then will need to use a special bitcoin wallet software with the ability to create custom transaction script.

The resulting transaction would be fives times larger than a normal transaction and would need more fees.

A solution was needed to make this bitcoin script a little less complicated. It was decided that the complex multisig script would be hashed and people will include the hash instead of the script in the locking script.

In order to unlock and redeem the transaction, the receiver will need to present the original script along with the signatures. Since the sender is sending money to a hash instead of a public address, this type of transaction is called Pay to Public Script Hash or P2SH.

So, how did P2SH change the transactions (for the example given above)?

Before P2SH

Locking Script: 2 <Public Key 1> <Public Key 2> <Public Key 3> <Public Key 4> <Public Key 5> 5 CHECKMULTISIG

Unlocking Script: <Sig 1> <Sig 2>

After P2SH

Redeem Script: 2 <Public Key 1> <Public Key 2> <Public Key 3> <Public Key 4> <Public Key 5> 5 CHECKMULTISIG

Locking Script: HASH160 <Hash of Redeem Script> EQUAL

Unlocking Script: <Sig 1> <Sig 2> <Redeem Script>

As it can be seen, the responsibility for supplying the conditions to redeem a transaction transfers from the sender to the redeemer who presents the redeeming script.

So, what would our company’s script look like if P2SH was not used? Sans P2SH the bitcoin script they would have been using would be this:

2 <Public Key 1> <Public Key 2> <Public Key 3> <Public Key 4> <Public Key 5> 5 CHECKMULTISIG

Which would translate into something like this:

2 04C16B8698A9ABF84250A7C3EA7EEDEF9897D1C8C6ADF47F06CF73370D74DCCA01CDCA79DCC5C395D7EEC6984D83F1F50C900A24DD47F569FD4193AF5DE762C58704A2192968D8655D6A935BEAF2CA23E3FB87A3495E7AF308EDF08DAC3C1FCBFC2C75B4B0F4D0B1B70CD2423657738C0C2B1D5CE65C97D78D0E34224858008E8B49047E63248B75DB7379BE9CDA8CE5751D16485F431E46117B9D0C1837C9D5737812F393DA7D4420D7E1A9162F0279CFC10F1E8E8F3020DECDBC3C0DD389D99779650421D65CBD7149B255382ED7F78E946580657EE6FDA162A187543A9D85BAAA93A4AB3A8F044DADA618D087227440645ABE8A35DA8C5B73997AD343BE5C2AFD94A5043752580AFA1ECED3C68D446BCAB69AC0BA7DF50D56231BE0AABF1FDEEC78A6A45E394BA29A1EDF518C022DD618DA774D207D137AAB59E0B000EB7ED238F4D800 5 CHECKMULTISIG

No, someone didn’t puke out those numbers and letters, that’s how the 5 public keys combined would look like!

Imagine customers sending that bitcoin script to send their bitcoin UTXOs.

Now, if that same hexadecimal block was parsed through SHA 256 followed by RIPEMD160, it would look something like this:

54c557e07dde5bb6cb791c7a540e0a4796f5e97e

So much neater and manageable, right?

The P2SH version of the locking script would now look like this:

HASH160 54c557e07dde5bb6cb791c7a540e0a4796f5e97e EQUAL

In order to unlock, the unlocking script would now look like:

: <Sig 1> <Sig 2> <2 Public Key 1 Public Key 2 Public Key 3 Public Key 4 Public Key 5 5 MULTISIG>

The combining of these two bitcoin scripts takes place in two stages.

Firstly, the redeem script is matched up against the hash to see if it matches up:

<2 Public Key 1 Public Key 2 Public Key 3 Public Key 4 Public Key 5 5 MULTISIG> HASH160 <HASH OF REDEEM SCRIPT> EQUAL.

If this bitcoin script returns TRUE then the second part of the unlocking takes place via this piece of code which takes place automatically. This one is our traditional multisig unlock:

<Sig 1> <Sig 2> 2 Public Key 1 Public Key 2 Public Key 3 Public Key 4 Public Key 5 5 CHECKMULTISIG

P2SH addresses always start with a 3 instead of 1.

3KP3okFKoGs53JqgyGB27pqaum8tCz2BvW <- A P2sh address.

One of the most interesting aspects of programming is Flow Control. By using certain conditions one can detect which commands get executed and when. Anyone who has some programming base is familiar with the concept of IF-ELSE programming.

This is the simplest and most basic form of flow control.

In bitcoin script the following are used for state control:

IF

ELSEIF

ENDIF

NOTIF

In a normal program, the IF-ELSE condition looks like this:

If(condition)

{

STATEMENT A

}

else

{

STATEMENT B

}

STATEMENT C

So, what is happening here?

If the condition is True, then execute STATEMENT A.

Otherwise, execute STATEMENT B.

Afterward, STATEMENT C gets executed regardless of which condition one uses.

However, in bitcoin script, it has a different look. Remember that the main feature of bitcoin script is that it is a stack-based language. That’s why in this case, the condition comes BEFORE the IF statement.

So, it looks something like this:

CONDITION

IF

STATEMENT A

ELSE

STATEMENT B

END IF

STATEMENT C

What will the execution of this block of script look like?

Step 1: Condition gets popped on the stack:

Step 2: The IF opcode pops out of the Condition and checks if it is TRUE or not.

Step 3: IF true, STATEMENT A gets executed and the bitcoin script skips the ELSE statement to jump to ENDIF and push STATEMENT C onto the stack.

Step 4: If the IF opcode returns FALSE then the ELSE block gets executed and STATEMENT B gets pushed onto the stack.

Step 5: After STATEMENT B gets pushed onto the stack, the ENDIF condition gets activated and STATEMENT C proceeds to get pushed on the stack.

Sidenote:

As we have seen before, flow control could be established via appending the VERIFY statement to certain conditions.

2 4 EQUALVERIFY 3

In this case, since 2 is not equal to 4, the script would stop executing then and there without even going to 3.

The Use of Flow Control in Bitcoin Script

One of the most common applications of Flow Control is to create redeem scripts which have multiple paths of execution.

We have already seen examples of multi-level executions with Multisignatures. Now let’s how the same multisig transactions can be written via Flow Control statements.

Suppose we are going to execute a 1-of-2 Multisig. The 2 people involved in the multisig are Alice and Bob and only of these two need to verify the transaction for it to go through.

So, for this multisig, how will the locking script be designed using flow controls?

Locking Bitcoin Script

IF

<Alice’s Public Key> CHECKSIG

ELSE

<Bob’s Public Key> CHECKSIG

ENDIF

As you can see, it is a simple straightforward if-else loop. However, there is something wrong here.

What do you think is missing?

That’s correct….the condition!

The Condition itself is missing… so is that an error? Really think about this right now… why didn’t we put the condition in the Locking Script?

Having a condition will unlock the IF-ELSE script and let you access the statements, WITHOUT providing a verification. Remember, the IF-ELSE script can only be unlocked if certain conditions are met.

This is why, in the locking script we provide the IF-ELSE code without the condition.

Unlocking Bitcoin Script

In the unlocking script, Bob or Alice provide their signature and the conditions required to unlock the script.

According to the script, Alice’s code gets unlocked if the condition is TRUE and Bob’s code gets unlocked if the condition is FALSE. We use “1” to signify TRUE and “0” to signify FALSE.

So, keeping all this info in mind, Alice’s unlocking script would be:

<Alice’s Signature> 1

Bob’s unlocking script is:

<Bob’s Signature> 0

Script Execution

Suppose Bob wants to unlock the UTXO, this is how the combined validation script would look like (we are going to use <locking script> in this example instead of the whole IF-ELSE code to enhance understanding):

<Bob’s Signature> 0 <Locking script>

Step 1: Bob’s Signature will get pushed on the stack.

Step 3: Now the fun part begins.

It is time for the <Locking Script> to get executed which happens to look like this:

IF

<Alice’s Public Key> CHECKSIG

ELSE

<Bob’s Public Key> CHECKSIG

ENDIF

First, the IF condition pops “0” out of the stack.

Step 5: CHECKSIG gets executed which pop’s both the Signature and Public Key from the stack and runs the CHECKSIG operation on them.

Alright, so this was a simple 1-of-2 multisig.

However, let’s up the ante a bit more and see what happens when we increase the Flow Control in a script.

IF STATEMENT A ELSE IF STATEMENT B ELSE STATEMENT C ENDIF ENDIF

Alright, so we have three statements that we want to execute. In order to execute a particular statement, we will need to input certain conditions to reach them.

To execute STATEMENT B, what conditions need to be input? The conditions will be: 1, 0 OR {TRUE, FALSE}.

But wait, Statement B, comes after one ELSE and one IF right? So shouldn’t the condition sequence be 0,1 OR {FALSE, TRUE}?

Well… remember that Script is a Stack-based programming language. So, an input of {TRUE, FALSE} would appear like this on the stack:

So, what’s the first thing that will get popped out?

FALSE right?

This FALSE condition will make the script skip IF and jump right to ELSE.

That’s why whenever we need to put in the conditions, remember the way a stack works.

What is Timelock?

A timelock is a primitive smart contract that levies time-based restrictions on Bitcoin spending. The three timelocks used in bitcoin:

Transaction Locktime (nLocktime).

CHECKLOCKTIMEVERIFY or CLTV.

CHECKSEQUENCEVERIFY or CSV

nLocktime

“nLocktime” is basically the parameter which defines the time before which the transaction couldn’t be accepted into the block. Every transaction set includes the nLocktime. There are three possible routes that nLocktime can take:

If nLocktime is set to 0 then the transaction is propagated immediately.

If 0 <nLocktime< 500 million, then it is thought of as a block height and the transaction isn’t included in the blockchain until the specified block height.

If nLocktime> 500 million, then it is interpreted as a Unix Epoch timestamp and the transaction isn’t valid until the specified time has passed.

While nLocktime looks good on paper, there is a very glaring weakness.

Suppose Alice wants to send Bob some BTC with a locktime of 1 week. There is a possibility that Alice can use the same UTXOs to create another transaction before the 1 week has elapsed with 0 locktime.

So, the receiver is completely dependent on the ethics of the sender.

CLTV

The problem with the nLocktime was that the time locking was applied to the transaction which made it vulnerable to malicious users. So, CLTV or CHECKLOCKTIMEVERIFY made the locking applicable on individual outputs. So, to put it in simple terms, CLTV made the individual outputs of a transaction accountable to time locking.

CLTV has been implemented in a primitive off-chain payment channel which gives resistance against possible malleability. CLTV-Style Payment channels were implemented post BIP 65. Let’s see how it works:

Alice (The Merchant) gives her public key to Bob (the customer).

Bob uses his public key and Alice’s to make a P2SH address using the following conditions:Condition 1: Both Alice and Bob sign on any transaction that happens through this address.Condition 2: Only Bob can sign of any transaction on his own but that transaction must have a locktime greater than the refund deposit.

Bob immediately creates a deposit transaction and broadcasts it on the blockchain. Because of condition 2 above, he is assured of the fact that he can pretty much generate a refund on demand.

Now, remember, the first condition states that the Alice and Bob both need to sign on any transaction that happens in the P2SH address. So, Bob (the customer) can sign his part of the transaction and Alice can sign her part without revealing her signature details to Bob. By doing this Alice can broadcast the final payment to the blockchain before the refund can get broadcasted.

CSV

Before we understand how CHECKSEQUENCEVERIFY or CSV works, you need to know the difference between absolute timelock and relative timelock.

While nLocktime and CLTV are absolute timelocks, meaning they specifically mention an absolute point in time, relative timelocks specify an elapsed time from the confirmation of the output in the blockchain.

CSV or CHECKSEQUENCEVERIFY is a relative timelock. The CSV opcode specifies an “nSequence” variable.

The CSV opcode, when called, will stop the script from executing unless the nSequence indicates that an equal or greater amount of relative locktime has passed than the one mentioned in the CSV opcode.

Alright, so let’s have some fun now. Let’s utilize a script using flow control and CSV!

Imagine we have a company owned by 3 people: B, C, and D. The company runs on a 2-of-3 Multisig meaning, the funds of the company can only be used if at least 2 out of the 3 people present their keys.

However, in case of the people end up losing their keys, they need to make a fail-safe to make sure that the funds can still be activated by 2-of-3 Multisig. In this case, they make a backup key and give to their lawyer A.

So, how will that look in the script?

IF IF 2 ELSE < 30 days > CHECKSEQUENCEVERIFY DROP < A 's Pubkey> CHECKSIGVERIFY 1 ENDIF < B 's Pubkey> <C' s Pubkey > < D 's Pubkey> 3 CHECKMULTISIG ELSE < 90 days > CHECKSEQUENCEVERIFY DROP < A 's Pubkey> CHECKSIG ENDIF

As you can see, there are three ways that the flow can go:

If everything is well and good, then it acts like a simple 2-of-3 multisig.

If one of the owners loses their key and can’t produce it in 30 days, then the conditions required to get access to the funds are the lawyer’s key AND a 1-of-3 multisig with the 3 owners.

If all 3 owners lose their keys, then the lawyer gets to access the funds after 90 days.

Condition #1: Everything is Good

If everything is good and nobody has lost any keys, then 2 of the 3 owners will verify the transactions with their signatures like in any normal multisig.

Unlocking Script: 0 <B’s signature> <C’s signature> <TRUE> <TRUE>

Note: Don’t forget that all multisig transactions begin with a “0” to workaround the CHECKMULTISIG bug.

Complete Validation script: 0 <B’s signature> <C’s signature> <TRUE> <TRUE> <Locking Script>

So, how does the execution looks like?

Step #1: The Unlocking script gets pushed on to the stack step-by-step

Does that look familiar?

Yup…that’s the 2-of-3 multisig and the bitcoin script gets executed normally.

Condition #2: One person loses their keys

In this case, the lawyer A comes into play.

Unlocking Script: 0 <B’s signature> <A’s signature><FALSE> <TRUE>

Complete Validation script: 0 <B’s signature> <A’s signature> <FALSE> <TRUE> <Locking Script>

So, how does the execution looks like?

Step #1: The unlocking script gets pushed on to the stack.

Step #3: The upcoming sequences are:

<30 days> CHECKSEQUENCEVERIFY DROP <A’s Pubkey> CHECKSIGVERIFY

The “<30 days> CHECKSEQUENCEVERIFY DROP” basically activates a timelock of 30 days. If within the timelock the owner who lost their key can’t retrieve it then the script moves on to the next element in the stack.

However, if they can retrieve the key and show proof, the script immediate stops executing (since the VERIFY code is appended).

Note: So what is DROP? Suppose the parameter needed to satisfy CHECKSEQUENCEVERIFY is submitted, the time parameter that preceded it i.e. “<30 days>” is still on the stack. The DROP function pops the last item on the stack which happens to be, in this case, <30 days>.

Now, this becomes a 1-of-3 multisig. If B’s signature checks out then the script executes properly.

Condition #3: All three owners misplace their keys

In this case, the Script won’t even execute the first IF, it will jump straight to the ELSE statement.

Unlocking Script: <A’s Signature><FALSE>

Complete Validation script: <A’s Signature><FALSE> <Locking Script>

So, how does the execution looks like?

Step #1: The unlocking script gets pushed onto the stack.

Conclusion

The aim of this guide was to make you understand the logic behind various bitcoin Script transaction scenarios. It is intriguing to see the mechanism behind these transactions, to say the least. If you haven’t read part 1 of the Bitcoin Script Guide be sure to check it out!

