Scripts with Flow Control (Conditional Clauses)
Conditional clauses are implemented in Bitcoin script using IF/NOTIF statements. There are several varieties of these clauses that can be used to build if statements in Bitcoin. Conditional clauses use the third stack in the Script validation engine to manage nested conditions making sure that OP_ELSE or OP_ENDIF statements which are nested inside OP_ELSE conditional outcomes do not terminate the parent loop.
OP_IF, OP_NOTIF
OP_IF will execute the set of subsequent opcodes up-to the following OP_ELSE or OP_ENDIF if the value on top of the stack is True, or NON-ZERO. Similarly, OP_NOTIF will execute the set of subsequent opcodes up-to the following OP_ELSE or OP_ENDIF if the value on top of the stack is FALSE, or ZERO. If an OP_ELSE is the subsequent conditional clause and the first conditional clause was successful, the script will jump forward to the OP_ENDIF at the conditional-stack height of the original clause. the script will jump to the subsequent opcode following the else on a failure of the conditional entry. The loop concludes after an OP_ENDIF is processed.
OP_VERIF, OP_VERNOTIF
OP_VERIF will execute the set of subsequent opcodes up-to the following OP_ELSE or OP_ENDIF if the value on top of the stack matches the protocol VERSION indicated in the transaction serialisation string. Some, or all network nodes may refuse to evaluate certain protocol versions meaning transactions using these parameters must be careful. Similarly, OP_VERNOTIF will execute the set of subsequent opcodes up-to the following OP_ELSE or OP_ENDIF if the value on top of the stack does not match the protocol VERSION indicated in the transaction serialisation string. Similarly to OP_IF/OP_NOTIF, ifan OP_ELSE is the subsequent conditional clause and the first conditional clause was successful, the script will jump forward to the OP_ENDIF at the conditional-stack height of the original clause. the script will jump to the subsequent opcode following the else on a failure of the conditional entry. The loop concludes after an OP_ENDIF is processed.
Example 1
The following example uses IF, ELSE and ENDIF statements to manage four possible ScriptSigs which can unlock this output.
ScriptSig1: <sig1> <pk1> 1 ScriptSig2: <sig2> <pk2> 2 ScriptSig3: <sig3> <pk3> 3 ScriptSig4: <sigRP> <pkRP>
The first three are PKH signatures with a numeric signal to show which conditional loop to enter for evaluation. Depending on which numeric signal is present, the script will enter the relevant height conditional clause. Each subsequent conditional IF statement is nested inside the else statement of the preceding level. If no numeric signal is present, the script goes into the third level else statement where the ScriptSig is tested against an R-Puzzle Hash and the script exits the loops.
ScriptPubKey: DUP 1 EQUAL IF
DROP DUP HASH160 <PKH1> EQUALVERIFY CHECKSIG
ELSE
DUP 2 EQUAL IF DROP DUP HASH160 <PKH2> EQUALVERIFY CHECKSIG ELSE DUP 3 EQUAL IF DROP DUP HASH160 <PKH3> EQUALVERIFY CHECKSIG ELSE OVER 3 SPLIT SWAP DROP 1 SPLIT SWAP SPLIT DROP HASH160 <RPH> EQUALVERIFY CHECKSIG ENDIF ENDIF
ENDIF
Example 2
In this example, three possible ScriptSigs are accomodated. The entry to the conditional clauses is based on the depth of the stack subsequent to the processing of the ScriptSig.
ScriptSig1: <SIG> <PK>
ScriptSig2: 1 <SIG1> <SIG3> <SIG4 <PK1> <PK2> <PK3> <PK4> <PK5>
ScriptSig3: <PASSWORD> <SIG6> <PK6>
Either of the first two ScriptSigs are evaluated in their own separate IF loops which exit using RETURN statements. If the ScriptSig has a stack depth that doesn't match either of the requirements, it passes through to a final evaluation.
DEPTH 2 EQUAL IF DUP HASH160 EQUALVERIFY CHECKSIG RETURN ENDIF DEPTH 9 EQUAL IF TOALTSTACK TOALTSTACK TOALTSTACK TOALTSTACK 3 SWAP DUP HASH160 <PKH1> EQUALVERIFY FROMALTSTACK DUP HASH160 <PKH2> EQUALVERIFY FROMALTSTACK DUP HASH160 <PKH3> EQUALVERIFY FROMALTSTACK DUP HASH160 <PKH4> EQUALVERIFY FROMALTSTACK DUP HASH160 <PKH5> EQUALVERIFY 5 CHECKMULTISIG RETURN ENDIF OVER 3 SPLIT NIP 1 SPLIT SWAP SPLIT DROP 10 SPLIT DROP 0 NUMEQUALVERIFY DUP HASH160 <PKH6> EQUALVERIFY SHA256 <PASSWORDHASH> EQUAL RETURN