How to use OP_BOOLOR
and OP_EQUAL
?
You can see the actual implementation for script opcodes in Bitcoin's script.cpp.
OP_BOOLOR
does this:
[...]
CBigNum bn1 = CastToBigNum(stacktop(-2));
CBigNum bn2 = CastToBigNum(stacktop(-1));
CBigNum bn;
[...]
case OP_BOOLOR: bn = (bn1 != bnZero || bn2 != bnZero); break;
[...]
popstack(stack);
popstack(stack);
stack.push_back(bn.getvch());
[...]
So it's effectively reading from the stack, popping the values and pushing the result.
OP_EQUAL
is pretty much the same:
[...]
popstack(stack);
popstack(stack);
stack.push_back(fEqual ? vchTrue : vchFalse);
[...]
It's popping two values from the top and pushing the result of the comparison.
Is the script correct?
Let's run the scripts and see what happens! scriptSig
is executed first:
scriptSig: [Wallet] [Wallet] [sig] [pubKey]
This code is just pushing into the stack, so after the scriptSig
runs the stack looks like this:
[pubKey] <- TOP OF THE STACK
[sig]
[Wallet]
[Wallet]
Notice the elements are reversed. The first thing pushed goes to the bottom of the stack.
Now scriptPubKey
is run keeping the contents of the stack resulting from running scriptSig
. If we do that with your script...
scriptPubKey: OP_EQUAL Wallet1_Value [Wallet]
OP_EQUAL Wallet2_Value [Wallet]
OP_BOOLOR
OP_VERIFY OP_DUP OP_HASH160 [pubKeyHash] OP_EQUALVERIFY OP_CHECKSIG
...the first thing that gets executed is OP_EQUAL
, which compares the top two elements of the stack [pubKey] [sig]
which are obviously not equal so 0
(false) is pushed. I'll just stop here since it won't make sense at all to continue.
Building the correct script
I guessed what you actually meant to do, and this are my proposed scripts.
First, I guess the spending user supplies [sig]
, [pubKey]
and [Wallet]
where [Wallet]
is either Wallet1_Value
or Wallet2_Value
to be able to spend the funds:
scriptSig: [sig] [pubKey] [Wallet]
Final stack after running:
[Wallet] <- TOP OF THE STACK
[pubKey]
[sig]
That's scriptSig
, and it's what a user supplies when he wants to spend the funds. We're done! Let's build the next step, i.e. the scriptPubKey
that checks if the supplied values are fine.
The first thing you want to do is to check whether [Wallet]
is one of Wallet1_Value
or Wallet2_Value
. First let's duplicate [Wallet]
so we can check it twice. The first step in scriptPubKey
is OP_DUP
. Let's run both scriptSig
first and the the new scriptPubKey
:
scriptSig: [sig] [pubKey] [Wallet]
scriptPubKey: OP_DUP
Final stack:
[Wallet] <- TOP OF THE STACK
[Wallet]
[pubKey]
[sig]
Of course we want to check it against Wallet1_Value
first:
scriptSig: [sig] [pubKey] [Wallet]
scriptPubKey: OP_DUP [Wallet1_value]
Final stack:
Wallet1_Value <- TOP OF THE STACK
[Wallet]
[Wallet]
[pubKey]
[sig]
And now check the top two items for equality:
scriptSig: [sig] [pubKey] [Wallet]
scriptPubKey: OP_DUP [Wallet1_value] OP_EQUAL
Final stack:
(== Wallet1, either TRUE or FALSE) <- TOP OF THE STACK
[Wallet]
[pubKey]
[sig]
Now we want to check the duplicate [Wallet]
against Wallet2_value
but the previous result is at the top of the stack. We must swap it first using OP_SWAP
:
scriptSig: [sig] [pubKey] [Wallet]
scriptPubKey: OP_DUP [Wallet1_value] OP_EQUAL OP_SWAP
Final stack:
[Wallet] <- TOP OF THE STACK
(== Wallet1)
[pubKey]
[sig]
Again, we push the value we want to check, and OP_EQUAL
it:
scriptSig: [sig] [pubKey] [Wallet]
scriptPubKey: OP_DUP [Wallet1_value] OP_EQUAL OP_SWAP [Wallet2_value] OP_EQUAL
Final stack:
(== Wallet2) <- TOP OF THE STACK
(== Wallet1)
[pubKey]
[sig]
The script is pretty much done. Now just OP_BOOLOR OP_VERIFY
:
scriptSig: [sig] [pubKey] [Wallet]
scriptPubKey: OP_DUP [Wallet1_value] OP_EQUAL OP_SWAP [Wallet2_value] OP_EQUAL OP_BOOLOR OP_VERIFY
If OP_VERIFY
finds FALSE
at the top of the stack it will reject the transaction. If it finds TRUE
the script continues and the stack looks like this:
Final stack if correct [Wallet] was provided:
[pubKey]
[sig]
Which is what the stack looks like after a regular scriptSig
! So the rest of the scriptPubKey
is just a regular pay-to-pubkey-hash
script. The final scriptPubKey
is:
OP_DUP [Wallet1_value] OP_EQUAL OP_SWAP [Wallet2_value] OP_EQUAL OP_BOOLOR OP_VERIFY
OP_DUP OP_HASH160 [pubKeyHash] OP_EQUALVERIFY OP_CHECKSIG
Actual security
Now, regardless of the order, the script is not very secure. I get this might just be an exercise in understanding scripts or is not meant to be secure (it might be only a check to avoid spending from unwanted wallets) but I think it's important to show how this is actually not secure at all.
Whenever you publish a transaction using this scheme, the output scriptPubKey
is publicly available and looks like this:
OP_DUP [Wallet1_value] OP_EQUAL OP_SWAP [Wallet2_value] OP_EQUAL OP_BOOLOR OP_VERIFY
OP_DUP OP_HASH160 [pubKeyHash] OP_EQUALVERIFY OP_CHECKSIG
Both Wallet1_value
and Wallet2_value
are public for anyone to see in the blockchain! The security still depends ONLY on [sig] [pubKey]
since I know exactly what's required to spend the transaction, i.e. the script security is equivalent to a regular pay-to-pubkey-hash
transaction!
EDIT: A little bit safer (response to comment)
Could you require Wallet1_value and Wallet2_value be hashes instead? Then the scriptsig would have to supply a hash of one of the two wallets.
Yes. Actually it's very easy (changes shown in bold face):
OP_HASH256 OP_DUP [Wallet1_hash] OP_EQUAL OP_SWAP [Wallet2_hash] OP_EQUAL OP_BOOLOR OP_VERIFY OP_DUP OP_HASH160 [pubKeyHash] OP_EQUALVERIFY OP_CHECKSIG
As you can see the top of the stack is hashed (OP_HASH160
or OP_HASH256
, whichever you prefer) before duplication, so now you can compare the provided data to match with one of two hashes instead of values.
This is a bit better since the data is not there in plain sight. You would have to brute-force to find one of WalletN_hash
. The problem is as soon as you spend it, the WalletN_value
becomes public in the spending transaction, so WalletN_hash
can't be used in a transaction again! It would be one-use only. If you published several transactions with the same WalletN_hash
, you'd have to spend all outputs at the same time or else the unspent ones might be hijacked.
Bear in mind the scheme is still secure because there are signatures involved. If the scriptPubKey
lacked the standard signatue verification it would just be:
OP_HASH256 OP_DUP Wallet1_hash OP_EQUAL OP_SWAP Wallet2_hash OP_EQUAL OP_BOOLOR OP_VERIFY
Any miner could listen for this type of transactions and, as soon as anyone tried to spend one with scriptSig = [WalletN_value]
, the transaction could be modified (even the outputs!) instead of relaying it since the transaction is not signed at all, so the miner could redeem the outputs himself.