Bitcoin Scripting Applications: Decision Based Spending

With the creation of Bitcoin Cash the hard work begins to realize the promise of Bitcoin as described in the Bitcoin white paper, and by doing so drive use of Bitcoin Cash so that it becomes the pre-eminent crypto-currency competing successfully with Bitcoin Segwit and other coins such as Ethereum, or forces these coins to adopt similar innovations.

Right now the Bitcoin scripting language is only used to repeat the same canned phrases, like the common “pay to public key hash” script. In this series I will investigate common use cases, determine whether they are expressible in the Bitcoin scripting language and if they are not determine and propose the extensions are needed to support the use case.

This is important so that Bitcoin will fulfill its promise as a programmable digital currency.

Decision Based Spending (Simple Programmable Money)

The simplest form of programmable money is an if statement:

if (condition) then pay A else pay B

The first problem is determining the truth or falsehood of the condition — that is, importing external data into the blockchain. To do so, we must propose a trusted entity (called an “oracle”) that consumes real-world events and translates them into a data format usable by a bitcoin script. This oracle could deliberately mis-report real world events, but this known problem is beyond the scope of this document and is present in all cryptocurrencies.

One could imagine an oracle service that presents quantitative data such as stock prices. This would allow our simple if statement to be used to create binary options. Provided that the value of the options open are much less than the reputational damage to the oracle if it misreported a price, we would have a viable system.

The oracle could place its data into the blockchain via an OP_RETURN, but there is no way another script can access that data. Let us propose adding a new opcode, call it “data = OP_GET_DATA(address)” [note: I am using a more modern opcode syntax RET = OP_A(B,C), but you can read this as pop B, C from the stack, execute OP_A and push RET onto the stack]. This opcode looks in blockchain history for a transaction signed by address, and pushes its OP_RETURN data onto the stack. If no transaction is signed by address, the opcode fails — that is, the UTXO is currently unspendable. Although we still have the problem that the oracle could lie, at least the authenticity of the origin of the statement is verified because (presumably) only the oracle can spend money from address.

One drawback to this approach is that the oracle is generating a lot of blockchain transactions whose information may be unused. But the idea of this opcode — access to blockchain history — feels suitably general to be useful many other types of scripts.

A more viable option is that the oracle separately publishes signed statements that may then be used as part of the binary option spend script. But this means that the script must verify the authenticity of the origin of the statement. Bitcoin can easily verify signatures, but the CHECKSIG family of opcodes only verify the transaction data, not arbitrary data.

We must add a new opcode,

“data = OP_DATASIGVERIFY(signature, data, pubkeyhash)”.

This script basically does what OP_CHECKSIGVERIFY does, but it does it for data on the stack rather than for the transaction. Specifically, it pops the top 3 items off the stack, the pubkeyhash, the signature, and some data and verifies that the signature is valid for the provided data and that the signature’s public key hashes to pubkeyhash. If this is invalid the transaction validation fails. If it validates the data is pushed back onto the stack for use by the rest of the script.

Let’s assume that the data the oracle signs is the median price of the stock throughout the day. It would also be necessary to make this data unique by including the stock ticker and the current date. If the validation of this metadata is done on the blockchain, it would require the re-enabling of the string manipulation opcodes, so that the data could be chopped into parts (ticker name, date, price), and then independently tested.

To avoid the on-chain verification of this metadata, it would be possible for the oracle to create and pre-publish a unique pubkeyhash for every piece of data it will produce (in the next year, for example). But this solution seems awkward…

Let’s ignore the data uniquification detail and create a binary option script:

First, note that the spend script will set up the stack like this:

oracle signature
oracle data
winner's public key (as in normal p2pkh transactions)
winner's transaction signature (as in normal p2pkh transactions)

The output script would therefore look something like this.

# push the oracle's public key onto the stack
OP_PUSHDATA(oracle pubkeyhash)
# verify the data and signature that the spend script left on the stack
# with the pubkey we just pushed (our new opcode).
OP_DATASIGVERIFY
# if it verifies, the oracle's data is left on the stack
# so push our binary option strike price onto the stack to compare
OP_PUSHDATA(binary option strike price)
# compare the price we just pushed with the oracle’s data
OP_LESSTHAN
# depending on the result, we'll allow payment to one or the other
# of our participants
OP_IF
# the normal beginning of a P2PKH tx
OP_DUP # Duplicating the winner's public key
OP_HASH160 # turn it into a bitcoin address
OP_DATA(bitcoin address of participant A)
OP_ELSE
OP_DUP
OP_HASH160
OP_DATA(bitcoin address of participant B)
OP_ENDIF
# The normal end of a P2PKH tx
OP_EQUALVERIFY # compare the bitcoin address against the hashed pub key
OP_CHECKSIGVERIFY # validate the transaction matches the sig

Summary

To enable the simplest form of programmable money we must have additional opcodes that either access data from prior blockchain transactions, or verify data and signatures pushed onto the script’s stack. And from a practical perspective, we must re-enable bitcoin’s “string” processing instructions so that compound data can be parsed before comparison. This requires a hard fork to extend the bitcoin scripting language with a new opcode and to re-enable some previously disabled opcodes.