Taking OP_CHECKDATASIG out for a test drive
On November 15th, the Bitcoin Cash network compatible with ABC and BU will do a network wide upgrade. One of the new features is a new opcode (operations code), OP_CHECKDATASIG, also known as DSV or CDS.
The new opcode verifies a message, returns true or false if the message is signed by the pubkey stated. This enables us to write something commonly referred to as an “oracle”. An oracle is a third party service that can be used as an authority for facts, statistics and data.
In practice, this means that the user spending an output will need to first get the oracle message and signature from the internet and put it in the ScriptSig.
The on-chain bet example
Let's imagine Bitcoin.com has an oracle that every day publishes the BCH price from markets.bitcoin.com. The data message is formatted as <4 byte price> and <4 bytes timestamp>, concatenated into a little endian byte array. This 8 byte message is then signed by the oracle's private key. The oracle publishes the price every even hour with the unix timestamp rounded for predictability.
Bob and Alice makes a wager of the price of BCH on November 1st at Midnight UTC 2018. Bob speculates the BCH price will be 500.00 USD or more and Alice speculates that the BCH price will be 499.99 USD or lower. They know that the Bitcoin.com oracle will publish the price every hour, so they can know for certain that the oracle will produce a message that states the BCH price on the exact time of 1541030400 (November 1st at Midnight UTC).
Bob and Alice agrees to use the following script to make this wager happen. They know the oracle’s public key, so they construct a P2SH redeem script that first splits the message from the oracle using OP_SPLIT, then verifies the timestamp using OP_CHECKLOCKTIMEVERIFY. If everything checks out, depending on the price value published by the oracle on the exact time of 1541030400 is 50000 or higher, it can be spent by Bob and not Alice. This allows both Alice and Bob to sign the redeem script before hand, there is no risk of double spent since only one of them can be valid and that depends on the outcome of the oracle’s message.
The script looks like this: ScriptSig: [<Bob_signature> | <Alice_signature>] [<Bob_pubkey> | <Alice_pubkey>] <oracle_signature> <oracle_message> Script: OP_DUP OP_TOALTSTACK # Duplicate the message, # put it on the alt stack <oracle_pub> OP_CHECKDATASIGVERIFY # Check message is signed # with oracle pubkey # If not signed by oracle, # invalidate the tx OP_FROMALTSTACK # Get the message from # alt stack 4 OP_SPLIT # Split the message 4 bytes # to the right 1541030400 OP_DUP # Duplicate the timestamp OP_TOALTSTACK # Put the duplicated # timestamp to alt stack OP_EQUAL OP_VERIFY # Compare that the # timestamp is equal # to the oracle message's OP_FROMALTSTACK # Get the timestamp from # alt stack OP_CHECKLOCKTIMEVERIFY # Check that timestamp we # need has passed OP_DROP # Drop timestamp from stack OP_BIN2NUM # Convert value from # oracle to compact number 50000 OP_GREATERTHANOREQUAL # Check if the value from # oracle is => 50000 OP_IF # Bob can spend using his signature and pubkey # if the price is >=50000 OP_DUP OP_HASH160 <bob_pubkeyhash> OP_EQUALVERIFY OP_CHECKSIG OP_ELSE # Alice can spend using his signature and # pubkey if the price is <50000 OP_DUP OP_HASH160 <alice_pubkeyhash> OP_EQUALVERIFY OP_CHECKSIG OP_ENDIF
This script uses a number of new opcodes not included in Bitcoin Core (and some not even in Bitcoin 0.1). First is OP_CHECKLOCKTIMEVERIFY introduced by the Core developers, secondly we have OP_SPLIT and OP_BIN2NUM that was introduced in May 2018. The final new opcode is OP_CHECKDATASIG that will be activated on mainnet on November 15th, 2018.
You may wonder what is the function of OP_BIN2NUM. The bitcoin scripting language handles all numbers as compact numbers, that means that any zeroes that are not needed must be removed. For example, if you try to do any kind of math with numbers (big endian in this example) 0x00000001 and 0x00000002, the script interpreter will execute to the end with true as the last stack element, but still end up with the infamous “non-mandatory-script-verify-flag (unknown error)”.
This poses a problem for oracles. Since the users of oracle data needs predictability, they need to know exactly how future oracle data will be formed so their spend scripts can parse it properly. Otherwise they risk building a script that turns out to be unspendable. In our case, the BCH price published will always be 4 bytes, so for some time, it will use less than 4 bytes. If today’s BCH price is 440 USD, we would represent it as 44000 for the two decimals (that’s 0xABE0 in hex big endian). The oracle would publish it as 0x0000ABE0, which means that when you feed that into the bitcoind script interpreter, you will get an unknown error. By using OP_BIN2NUM, 0x0000ABE0 will be shortened down to 0xABE0 and the script will pass. Another important note to keep in mind is that the variable is signed, so if you are using one byte, you can only use numbers up to 128, as each additional 128 requires another byte.
The escrow example
Let’s try another more complicated script. The message being verified is in the case <0 or 1> <contract_id>. It’s a byte array where the first byte is the verdict, affirmed or denied. The rest is 4 bytes with the contract id.
The scenario is that an escrow creates a P2SH address, the buyer sends BCH to this address, and the seller will send the physical item to the buyer. In this case, there are two escrows the buyer and the escrow service. In most cases, the buyer will release the payment and confirm delivery by signing a message. If the buyer does not do this, the escrow service can step in and sign the message after reaching arbitration. OP_CHECKDATASIG returns true for correct signatures, and false if the signature is zero. This means that even if the escrow goes offline, we can spend this script by inserting 0 as the signature. In this example, the buyer signs it, and the escrow doesn’t need to do anything.
ScriptSig: <seller_tx_signature> <seller_pubkey> [empty array] <contract_message> <buyer_contract_signature> <contract_message> Script: OP_DUP # Duplicate the message OP_TOALTSTACK # Push it to the alt stack <buyerKey> OP_CHECKDATASIG # Check sig with message OP_IF # If the message is signed # by the pubkey OP_FROMALTSTACK # Get the message from altstack OP_1 OP_SPLIT # Split the last 4 bytes OP_BIN2NUM # Convert to compact number <contractIdBytes> OP_EQUAL # Check contract id OP_IF OP_BIN2NUM # Convert to compact number OP_1 OP_GREATERTHANOREQUAL # If the number is > 1 OP_IF OP_1 OP_TOALTSTACK # Push number 1 to altstack OP_ENDIF OP_ENDIF OP_ELSE # Else, push zero to the altstack OP_FROMALTSTACK # (we can’t push OP_0) OP_1 OP_1SUB OP_TOALTSTACK OP_ENDIF OP_DUP # Copy next contract message OP_TOALTSTACK # Push it to the alt stack <escrowKey> OP_CHECKDATASIG # Check the signature OP_IF OP_FROMALTSTACK # Get the message from alt stack OP_4 OP_SPLIT # Split out contract id and verdict OP_BIN2NUM # Make it a compact number <contractIdBytes> OP_EQUAL # Check contract id OP_IF OP_BIN2NUM # Make it a compact number OP_1 OP_GREATERTHANOREQUAL OP_IF # If the verdict is 1, get # current variable from altstack OP_FROMALTSTACK OP_1 OP_ADD # Add 1 OP_TOALTSTACK # Push it back to # the alt stack OP_ENDIF OP_ENDIF OP_ELSE # If the signature is wrong OP_FROMALTSTACK # Remove the message from altstack OP_DROP # and drop it. OP_ENDIF OP_FROMALTSTACK # Get the verdicts from the altstack OP_1 OP_GREATERTHANOREQUAL # Check if it’s >= 1 OP_IF # Then the seller can spend this OP_DUP OP_HASH160 <sellerAddress> OP_EQUALVERIFY OP_CHECKSIG OP_ELSE # Otherwise, the buyer can spend this after the time has run out <timeLock> OP_CHECKLOCKTIMEVERIFY OP_DROP OP_DUP OP_HASH160 <buyerAddress> OP_EQUALVERIFY OP_CHECKSIG OP_ENDIF
This is a quite large script compared to most scripts on the bitcoin network. We could make it even longer by adding more escrows (just copy paste the operations from the second escrow). The larger we make our scripts, the more errors and bugs we risk introducing. We are fortunate that OP_CHECKDATASIG can be carried out with one op-code. It’s still unclear if it’s even possible to do the exact same thing with just opcodes alone, but even if it is, we are talking about an extremely large script.
Please note that the scriptSig have an empty array pushed, which if using bitcoinj’s ScriptBuilder would be .data(new byte). It’s worth mentioning that the altstack is used as extra memory. A CPU does have register and access to RAM, but when doing bitcoin scripting, we only have the ordinary stack and altstack. In this case, for each escrow we add 1 to the variable on the altstack. At the end of the script, we can check if the value stored on the alt stack is greater or equal to zero. If we add more escrows, we could require a higher value.
Difference between OP_CHECKDATASIG and OP_CHECKSIG
These two opcodes are actually very similar and nearly perform the same function. OP_CHECKDATASIG don’t use any scripthash and is just a plain EC key signature. While OP_CHECKSIG uses data from the transaction itself as the message, OP_CHECKDATASIG needs to get the message. This means that after signing a transaction that is supposed to be validated by OP_CHECKSIG, you can’t change the transaction without making it invalid. With OP_CHECKDATASIG, the transaction can be modified however you like without invalidating it. These two opcodes, despite being so similar, gives us two very different use cases that needs to be used appropriately. When making your script, you will need to combine both OP_CHECKSIG and OP_CHECKDATASIG to make sure that transactions can’t be modified on the fly. In our second example, the script ends with either making the seller or the buyer able to spend it after running the OP_CHECKDATASIG operations to validate the escrow data. The benefit is that the seller can spend without anyone stopping them. The seller doesn’t need to disclose where they send the transaction next to the escrow before signing. The escrow simply just signs the contract with a verdict and nothing else.
This is just scratching the surface
These are just two examples, there are many other uses cases of OP_CHECKDATASIG. There is Pay To Identity , Zero Conf Forfeit  already. We are sure this opcode will inspire a lot of innovation for permissionless payments and smarter contracts on-chain.
3 of 3 reviewers say it's worth paying for
0 of 3 reviewers say it's not worth paying for