2

I am trying to write a script that will allow the receiver of my coins to only spend them on two wallets I specify. After that check has been done I want to perform the usual verification that the current client does to verify a transaction. This is where I am at, but I am not sure if things are in the right order, if OP_EQUAL pushes to the stack, and if OP_BOOLOR reads from it.

scriptPubKey: OP_EQUAL Wallet1_Value [Wallet] 
              OP_EQUAL Wallet2_Value [Wallet] 
              OP_BOOLOR 
              OP_VERIFY OP_DUP OP_HASH160 [pubKeyHash] OP_EQUALVERIFY OP_CHECKSIG

scriptSig: [Wallet] [Wallet] [sig] [pubKey]
1

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.

  • So with this script, could the output be hijacked? Or is your security argument at the end more focused on privacy of addresses? – CoinEnablers Jan 14 '14 at 13:15
  • @CoinEnablers the security is the same as a regular transaction. No more, no less. You can't hijack it just like you can't hijack a regular transaction (since it's still using the regular OP_CHECKSIG part) but my point is there's no extra security either (even if it has the extra wallet check) because it's checking against a value that's public. – kaoD Jan 14 '14 at 14:17
  • 1
    @kaoD, that is exactly what I was looking for! Extending the exercise a little bit more, what would be a good solution to the security points you bring up? 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. Thanks again for the thorough explanation! – Lull3rSkat3r Jan 15 '14 at 2:36
  • @Lull3rSkat3r sure. I updated the answer with a new section! Let me know if you have any more questions :) By the way, I don't know what's WalletN_value but I just assume it's a bunch of binary data (any data will do, hope that's clear, I just kept using Wallet because it's what you called them but there's no way to access Bitcoin wallet from a script). – kaoD Jan 15 '14 at 3:20
  • Awesome! I definitely have a better understanding now. In regards to my thought process about the value of Wallet, I was assuming the client would have some interface for the value. The client would add it to scriptsig similar to how sig and pubKey are added in the vanilla client. – Lull3rSkat3r Jan 15 '14 at 4:41

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.