From 70ef2128350d3cab562f0c5b32effa071d052039 Mon Sep 17 00:00:00 2001 From: Shannon Appelcline Date: Thu, 1 Jun 2017 12:21:59 -0700 Subject: [PATCH] Update 10_1_Using_Script_Conditionals.md --- 10_1_Using_Script_Conditionals.md | 33 +++++++++++++++++++++++++++---- 1 file changed, 29 insertions(+), 4 deletions(-) diff --git a/10_1_Using_Script_Conditionals.md b/10_1_Using_Script_Conditionals.md index 136f8c1..7edbd45 100644 --- a/10_1_Using_Script_Conditionals.md +++ b/10_1_Using_Script_Conditionals.md @@ -50,7 +50,7 @@ ENDIF OP_EQUALVERIFY OP_CHECKSIG ``` -So, if the `OP_DUP` is successful, then we get to do the first block, else the second. But that doesn't make any sense! Why wouldn't the `OP_DUP` succeed. +So, you might think, if the `OP_DUP` is successful, then we get to do the first block, else the second. But that doesn't make any sense! Why wouldn't the `OP_DUP` succeed?! And, indeed, it doesn't make any sense, because we accidentally read the statement using the wrong notation. The correct reading of this is: ``` @@ -71,7 +71,7 @@ ENDIF OP_EQUALVERIFY OP_CHECKSIG ``` -The `True` or `False` statement is placed on the stack _prior_ to running the `IF`, then the correct block is run base on that result. +The `True` or `False` statement is placed on the stack _prior_ to running the `IF`, then the correct block is run based on that result. This is intended as a poor man's 1-of-2 multisignature. The owner of `` would put ` True` in his locking script, while the owner of `` would put ` False` in her locking script. That trailing `True` or `False` tells the script which hash to check against, then the `OP_EQUALVERIFY` and the `OP_CHECKSIG` at the end do the real work. @@ -111,9 +111,11 @@ Stack: [ ] Then we run the first few, obvious commands, `OP_DUP` and `OP_HASH160` and push another constant: ``` Script: OP_HASH160 OP_EQUAL IF OP_CHECKSIG ELSE OP_DUP OP_HASH160 OP_EQUALVERIFY OP_CHECKSIG ENDIF +Running: OP_DUP Stack: [ ] Script: OP_EQUAL IF OP_CHECKSIG ELSE OP_DUP OP_HASH160 OP_EQUALVERIFY OP_CHECKSIG ENDIF +Running: OP_HASH160 Stack: [ ] Script: OP_EQUAL IF OP_CHECKSIG ELSE OP_DUP OP_HASH160 OP_EQUALVERIFY OP_CHECKSIG ENDIF @@ -122,16 +124,19 @@ Stack: [ ] Next we run the `OP_EQUAL`, which is what's going to feed the `IF`: ``` Script: IF OP_CHECKSIG ELSE OP_DUP OP_HASH160 OP_EQUALVERIFY OP_CHECKSIG ENDIF +Running: OP_EQUAL Stack: [ True ] ``` Now the `IF` runs, and since there's a `True`, it only runs the first block, eliminating all the rest: ``` Script: OP_CHECKSIG +Running: True IF Stack: [ ] ``` And the `OP_CHECKSIG` will end up `True` as well: ``` Script: +Running: OP_CHECKSIG Stack: [ True ] ``` #### Run the False Branch @@ -149,9 +154,11 @@ Stack: [ ] Then we run the first few, obvious commands, `OP_DUP` and `OP_HASH160` and push another constant: ``` Script: OP_HASH160 OP_EQUAL IF OP_CHECKSIG ELSE OP_DUP OP_HASH160 OP_EQUALVERIFY OP_CHECKSIG ENDIF +Running: OP_DUP Stack: [ ] Script: OP_EQUAL IF OP_CHECKSIG ELSE OP_DUP OP_HASH160 OP_EQUALVERIFY OP_CHECKSIG ENDIF +Running: OP_HASH160 Stack: [ ] Script: OP_EQUAL IF OP_CHECKSIG ELSE OP_DUP OP_HASH160 OP_EQUALVERIFY OP_CHECKSIG ENDIF @@ -160,30 +167,48 @@ Stack: [ ] Next we run the `OP_EQUAL`, which is what's going to feed the `IF`: ``` Script: IF OP_CHECKSIG ELSE OP_DUP OP_HASH160 OP_EQUALVERIFY OP_CHECKSIG ENDIF +Running: OP_EQUAL Stack: [ False ] ``` Whoop! The result was `False` because `` does not equal ``. Now when the `IF` runs, it collapses down to just the `ELSE` statement: ``` Script: OP_DUP OP_HASH160 OP_EQUALVERIFY OP_CHECKSIG +Running: False IF Stack: [ ] ``` Afterward, we go through the whole rigamarole again, starting with another `OP_DUP`, but eventually testing against the other `pubKeyHash`: ``` Script: OP_HASH160 OP_EQUALVERIFY OP_CHECKSIG +Running: OP_DUP Stack: [ ] Script: OP_EQUALVERIFY OP_CHECKSIG +Running: OP_HASH160 Stack: [ ] Script: OP_EQUALVERIFY OP_CHECKSIG Stack: [ ] -Script: OP_EQUALVERIFY OP_CHECKSIG +Script:OP_CHECKSIG +Running: OP_EQUALVERIFY Stack: [ ] Script: +Running: OP_CHECKSIG Stack: [ True ] ``` -This probably isn't nearly as efficient as a true Bitcoin multisig, but it's a good example of how results pushed onto the stack by previous tests can be used to feed future conditions. In this case, it's the failure of the first signature which tells the conditional that maybe it should go check the second. +This probably isn't nearly as efficient as a true Bitcoin multisig, but it's a good example of how results pushed onto the stack by previous tests can be used to feed future conditionals. In this case, it's the failure of the first signature which tells the conditional that maybe it should go check the second one. ## Understand Other Conditionals + +There are a few other conditionals of note. The big one is `OP_NOTIF` (0x64), which executes if the top item is `False`. An `ELSE` can be placed with it, which as usually is executed if the main block is not executed. You still end with `OP_ENDIF`. + +There's also an `OP_IFDUP` (0x73), which duplicates the top stack item only if it's not 0. + +These options are used much less than the main IF/ELSE/ENDIF construction. + +## Summary: Using Script Conditionals + +Conditionals in Bitcoin Script allow you to halt the Script (using `OP_VERIFY`) or to choose different branches of execution (using `OP_IF`). However, reading `OP_IF` can be a bit tricky. Remember that it's item pushed onto the stack _before_ the `OP_IF` is run that controls its execution; that item will typically be part of the unlocking script (or else a direct result of items in the unlocking script). + +_What is the power of conditionals?_ Script Conditionals are the final major building block in Bitcoin Script. They're what are required to turn simple, static Bitcoin Scripts into complex, dynamic Bitcoin Scripts that can evaluate differently based on different times, different circumstances, or differe user inputs. In other words, they're the final basis of smart contracts.