It is a stack based language like FORTH. The parameters of the operators come before the operators themselves. Every operator pops the required parameters off the stack and pushes the answers into the stack.
In Bitcoin script 1+1 would look like
OP_1 OP_1 OP_ADD
P2PKH script:
When in Bitcoin you create a transaction that says Alice pays Bob for a cup of coffee, in the Bitcoin transaction what you get is a bunch of Alice's inputs with a bunch of signatures to an output which identifies Bob's address as the destination pay using a script address called P2PKH:
OP_DUP OP_HASH160 <BOB_PKH> OP_EQUALVERIFY CHECKSIG
Some of the first transactions are payed just using P2PK but now P2PKH is used to make it more secure by hashing the public key twice.
How do you redeem this script?
Let's say you are Bob, you wanna spend this. You put Bob_Sig and Bob_pub_key. They get executed in sequence before this is verified. So, for spending that transaction Bob, puts his signature and his public key in the scriptSig of the input.
BOB_PUBKEY
BOB_PUBKEY //OP_DUP
BOB_SIG
Execution pointer goes to OP_HASH160
It pops BOB_PUBKEY and does the HASH160 operation which is a SHA256 wrapped in a RIPEMD160
BOB_PUBKEYHASH //HASH160 performed
BOB_PUBKEY
BOB_SIG
Execution pointer goes to which is the number where the execution pointer goes and so it gets pushed in the stack and gets compared by OP_EQUAL and pushes the result back on.
How does CHECKSIG
know what is being signed?
CHECKSIG
takes more than these parameters. First of all Bob's signature consists of three parts, a R value, a S value and a SigHash type. The SigHash type tells CHECKSIG
what type of hash it is checking. For example if the SigHash type is SigHashAll, you create a hash with a very specific recipe by mixing together all of the inputs and all of the outputs. If it is SigHashSingle, you create a hash for this input and this output only. What's being signed is the hash and how you construct the hash depends upon whether it is SIgHashAll or some other SigHash.
Difference between OP_EQUAL
and OP_EQUALVERIFY
So there are certain OP codes which have the verify subscript like EQUAL and EQUALVERIFY
, CHECKSIG
and CHECKSIGVERIFY
, CHECKMULTISIG
and CHECKMULTISIGVERIFY
, anything that has verify will only continue the execution only if the output is true whereas without the verify subscript the execution continues with true or false as the parameter on the stack. TimeLock only has the verify version CHECKLOGTIMEVERIFY
and CHECKSEQUENCEVERIFY
Clauses that decide whether the following lines will be executed or not are called Guard Clauses. If you wrap the code around a certain Guard Clause, the code won't execute unless the clause is satisfied.
So, the VERIFY clause at the end of the clause is a form of a conditional and is also a type of flow control. It tells if true, continue and if False RETURN(which terminates the script execution).
Most important OPCODES are flow control statements(IF,ELSE,etc).
In Bitcoin script we have:
X //this is the condition, which is missing and added by the person spending the output
IF
A //this is the then part and not the condtition, it runs when we have true
ELSE
B //this is the part that is executed if the condition is false
ENDIF
The problem is that X isn't in the locking script. Then If X was an EQUAL operator, if the output would be true, it would execute A else it would execute B. This is very non-intuitive because X is missing.
The script basically says that this can be spent in two ways, if it is true it is spent with method A, and if false, script B. So the condition you are testing is user input.
Example:
IF
BOB_PUBKEY CHECKSIG
ELSE
ALICE_PUBKEY CHECKSIG
ENDIF
This script says that if we provide it with a condition that is true, it will check if it is BOB's public key or if the condition is false, it will try to verify with Alice's public key.
Bob passes the unlocking Script to spend:
BOB_SIG
TRUE
Or, Alice could spend by passing:
ALICE_SIG
FALSE
This is One of Two MULTISIG
script using an IF clause.
If we had to enhance it, we could do:
IF
BOB_PUBKEY CHECKSIGVERIFY
HASH160 <BOBHASH> EQUAL//Bob needs some secret hash to spend this
ELSE
ALICE_PUBKEY CHECKSIG
ENDIF
Another Example:
IF
IF
2//this is one form of M
ELSE
<+30DAYS> CHECKSEQUENCEVERIFY DROP
<A_PUBKEY> CHECKSIGVERIFY
1//this is the secod form of M
ENDIF
<B PUBKEY><C PUBKEY><D PUBKEY> 3 CHECKMULTISIG
//in the above line 3 is the N
ELSE
<+90DAYS> CHECKSEQUENCEVERIFY DROP
<A PUBKEY> CHECKSIG
ENDIF
This is a partnership company with 3 partners, B,C and D. They have a lawyer who is not part of the company A, who is responsible in case of a key loss. That lawyer has to step in and provide a backup key. 2 out of 3 partners can spend this anytime. The basic clause is 2 of 3 MULTISIG
unless the output has not been spent within 30 days of mined. It can also be redeemed by the signature of Lawyer and one of the partner. If 90 days are completed, the lawyer can spend it without any of the partners. 30 days later, 2 clauses are an option and 90 days later all 3 clauses can be performed.
BIP68 introduced CHECKSEQUENCEVERIFY
. It also came with BIP 113 and it measures the median time of last 11 Blocks so that no single miner can mess with it. 11 Blocks is just short of 2 hours and the mid point is just an hour ago.
To roll it back over they have to do 2 of 3 signatures within every 29 days.
Now let's get into the nuances.
CHECKMULTISIG
takes at least 3 parameters — M, which is the threshold of signatures required for validation, the actual public keys and N, number of total keys. In the above example we take CHECKMULTISIG
and spread it between 2 M conditions. It shows how flexible the CHECKMULTISIG
actually is.
The above can also be said as a type of 2 of 4 signature because there are 4 public key options. Actually it is not 2 of 4, it is 1 of 1 and 1 of 3. This stuff needs debugging. You have to carefully think about all of the scenarios.
What you need to put on the stack as the unlocking script to get a 2 of 3?
2 signatures and 2 trues
TRUE//we enter the first IF
TRUE//we enter the second(nested) IF which puts 2 on the stack
//everything from ELSE and ENDIF goes away and the CHECKSEQUENCE line gets evaluated
<PUBKEY_B>
<PUBKEY_C>
the DROP
is there to remove the leftover values from the stack. The leftover values from CHECKLOGTIMEVERIFY
and CHECKSEQUENCEVERIFY
is there to be able to perform more operations on top of that.