March 16, 2018
Use Cases for Re-Enabled Op Codes
Several disabled op codes that were in the original version of bitcoin but subsequently disabled are proposed for reactivation in the May 15th Bitcoin Cash protocol upgrade.
As a co-author of the specification for enabling these old op codes, I admit to failing to understand the community’s appetite for a clear explanation of why this is proposed. Among developers it could be argued that the use cases for fundamental operations in a scripting language are so common as to not need stating. We use basic operations like arithmetic, string operations and bitwise logic many times every day and it’s almost inconceivable to get any useful code written without them.
However the Bitcoin Cash community is far more than just developers. And in fact that argument above, even if compelling to a developer, still doesn’t give a concrete example of how these op codes can be used on a Bitcoin Cash context. Nor does it explain why they were disabled or why it’s safe to re-enable them. These are explanations the Bitcoin Cash community has a right to hear so let’s try to remedy that.
History of disabled op codes
LSHIFT and RETURN bugs
On July 28 2010 two bugs were discovered and demonstrated on the test network. The first caused bitcoin to crash on some machines when processing a transaction containing an OP_LSHIFT. The second exploited another bug in the transaction handling code and allowed an attacker to spend coins that they did not own. Neither were exploited on the main network, and both were fixed by Bitcoin version 0.3.5.
After these bugs were discovered, many currently-unused script words were disabled for safety.
Essentially out of an abundance of caution and lack of time to fully explore and fix the edge cases that needed to be addressed, the decision was taken to simply disable any op codes around which there were doubts or even hints of doubts. In the early days of Bitcoin the focus was very much on getting the much simpler safe payments option right and thoroughly tested. The exploration of more complex uses was necessarily deferred until later.
Peter Todd on why OP_CAT (and others) were disabled (i.e. we panicked):
More on the history of op codes:
Video of same presentation is here:
Why re-enable them?
Bitcoin was created with a rich scripting language. If it’s use was intended to be limited to straight payments from one private key to another and even including multisig transactions there is no need for a script language at all. These functions could simply be hardcoded vastly simplifying the code. The fact that Bitcoin was built on top of a scripting language with a rich instruction set suggests that it was always intended to be a base platform that enabled much more complex rulesets to govern the transfer of funds. Restoring bitcoin to it’s originally intended design (with bugs fixed) is a reason in itself.
7 years have passed and the edge cases around these op codes are much better understood now. Additionally the decision to disable them was taken hastily and under duress. The BCH community now has had the luxury of time to address these issues thoroughly.
7 years also provides a lot of time for people to think up ways to use these op codes. And in fact they have already been enabled in various parts of the crypto eco-system (e.g. Elements Alpha) to unlock some of those use cases.
Why are only some proposed to be re-enabled?
Simply for the sake of continuing the cautious approach. Enabling only a few op codes for the May 15th protocol upgrade not only limits the risk and enables all involved developers to give each op code more attention. By keeping the scope of the change small it gives the BCH developer community a chance to refine the process of enabling such features. This process includes scheduling, developing specs, attracting peer review, refining, reaching agreement that a high quality threshold has been satisfied, building and testing an acceptance and release methodology and of course executing all of the above.
What are some use cases?
Following is a compilation of use cases that have been published or discussed over recent years. Many of these are referenced under more than one op code as they require more than one. The list is no doubt incomplete.
Multi party hash locks:
Securely and compactly verify many hashes and hash preimages. This would shrink offchain Tumblebit transactions significantly. For instance a transaction TxA which checks that a transaction TxB releases preimages x1,x2,…,x10 such that y1=H(x1), y2=H(x2),…,y10=H(x10).
Currently put y1,…y10 and check that the preimahes hash correctly.
With OP_CAT you only have to store one hash in TxA, yhash
ytotal = H(OP_CAT(H(OP_CAT(y1, y2)),y3)…y10)
TxA could then just hash all the preimages supplied by TxB and confirm they hash to TxA. This would reduce the size of TxA from approx 10*32B to 32+10*16B.
Simple merkle tree verification:
This enables multisig on a much larger scale. Theoretical limit with current script size limitations is ~ 1 of 4 billion. A more practical limit of 1 of 250000 can be achieved in 886 bytes of script which would take ~ 8mb without OP_CAT.
Weak hash with OP_LEFT:
A weak hash is just a smaller than usual hash. They are used in bitcoin addresses as checksums providing a degree of error checking (the chance of an undetected error for a 32 bit checksum is 1 in 2^32). They can be produced by taking a hash and splitting off the first (or last) n bytes.
Mini proof of work:
Ensure a scriptSig is computationally expensive for the recipient to produce with a mini-PoW. By requiring either a signature or hash to begin with a specific sequence of bits. This can be achieved for numbers less than 2^32 using OP_SPLIT and OP_LESSTHAN. OP_EQUALS can also be used with OP_SPLIT for larger numbers but is limited to difficulties of 2^n where n must be a multiple of 8 (you can only use whole bytes). For larger numbers a combination of OP_SPLIT and OP_AND allow an effective difficulty of any power of 2. E.g. if you want the PoW to take an average of 8 billion attempts to solve the script might require the last 33 bits be zeros:
<pow_hash> SIZE 5 SUB SPLIT 0x01FFFFFFFF AND IF RETURN ELSE //finish script
- SIZE 5 SUB – get length of pow_hash and subtract 5
- SPLIT – split last 5 bytes byte off using len(pow_hash)-5 as the split point
- 0x01FFFFFFFF AND – zero the leftmost 7 bits.
- IF RETURN – if any of the rightmost 33 bits are not 0, fail the script
- //finish script
Examination of parts of a byte array. e.g. Looking at the SIGHASH_FLAGS of a signature:
This is one example of enforcing limitations on the way funds can be spent.
<sig> <pubkey> | 1 PICK SIZE 1 SUB SPLIT 1 ROLL DROP
- 1 PICK – copy sig to top of stack
- SIZE 1 SUB – get length of sig and subtract one
- SPLIT – split last byte off using len(sig) -1 as the split point
- 1 ROLL DROP – move the left part of the split bytes to the top of stack and drop it leaving the last byte of the sig at the top of the stack.
0x80 AND VERIFY
- 0x80 – Push 0x80 onto stack (this is the ANYONECANPAY flag but could be others)
- AND – last byte of sig & 0x80 returns 0x80 of SIGHASH_ANYONECANPAY flag was set or 0x00 if not.
- VERIFY – fail the script is the flag wasn’t set.
- //do remainder of P2PKH script
Stack state is now: <sig> <pubkey> as is needed for a p2pkh. The full script would be:
<sig> <pubkey> | 1 PICK SIZE 1 SUB SPLIT 1 ROLL DROP 0x80 AND VERIFY DUP HASH160 <pubkeyhash> EQUALVERIFY CHECKSIG
Use of OP_SPLIT with OP_DATASIGVERIFY:
If an exchange were to produce signed data such as “BCHUSD:20180228:120000:132500:119500:130000” (BCHUSD market, 28th February 2018, open 120000 USDcents/BCH, high 132500, low 119500, close 130000). OP_SPLIT would be used to extract values from this data for decision making, after verification of signatures. Example: confirm that the data is for the BCHUSD market, that the date is 2018-02-28, that the close value was greater than 125000.
AND/OR: Setting and checking bit flags, a useful way to represent a large set of boolean values compactly.
If implementing any one of these operations the other two are trivial additions (the simplest implementation is a single case statement and a single line of code.) All other factors like restrictions on operand lengths and format are dealt with by implementing the first one.
Checking and clearing of bit flags
Require a signature to use specific sighash flags (see detailed description under OP_SPLIT).
Setting bit flags. Checking of unset bit flags.
Deterministic random number generation with OP_XOR:
Combining secret values from different parties.
Uses OP_XOR and OP_MOD to combine two coin tosses into one result. The majority of the paper is about the protocol for performing the coin toss between two parties with no trust, the final stage is that the random values the participants have chosen are combined into a result using xor and mod.
Similar described here https://github.com/jl2012/bips/blob/mastopcodes/bip-mastopcodes.mediawiki#Trustfree_betting
Fast deterministic random number generation using XOR:
– note: this also requires OP_LSHIFT which is not proposed for the May hard fork.
Any on chain calculation in a smart contract requires arithmetic. Higher order operators can currently be simulated using very large scripts. OP_MUL is a series of OP_ADDs. OP_DIV is a series of OP_SUBs whilst incrementing a counter and checking at each step if you’re result is less than the divisor. OP_MOD is a series of OP_SUBs and once you have a negative result, taking the OP_ABS value. There is a practical limit to this given that loops do not exist in script (and were never part of the original design) and scripts have a size limit. It also becomes much more complex when both of the values being operated on are variable (as opposed to when one of the values is a constant value).
A cautious approach is warranted which is why not all arithmetic operators are proposed for the same hard fork.
Crypto math – currently 32bit limit requires the use of OP_SPLIT to achieve this. However it’s inclusion is a prerequisite to increasing the valid bit length of arithmetic inputs.
http://www.cs.technion.ac.il/~idddo/cointossBitcoin.pdf – Also requires OP_XOR
No specific use case but it is a subset of OP_MOD behaviour and all edge cases applicable to DIV are already addressed by MOD.
Script, although it doesn’t have an explicit data typing system, does implicitly use two data types in practice. Numbers and byte arrays. Operators like arithmetic, string and bitwise are designed to use specific data types (number, bytes arrays and byte arrays respectively). In order to alleviate concerns raised during the peer review of the specification about accidental misuse of these op codes with incorrect data types, it was decided to introduce two new op codes to provide safe and explicit conversion of data between types. This is simply about making the script language safer and easier to use rather than adding specific use cases. This benefit is not only applicable to the op codes mentioned in this article but also to many existing op codes.
See “Script data types” section of specification here:
See “Script data types” section of specification here:
This list is not exhaustive. But it does illustrate that because these op codes are fundamental building blocks of the script language their utility is very broad. These are examples people have thought of in the context of believing they are never likely to see the light of day. Now that their use is a real possibility it is reasonable to expect that creative minds will turn a lot more attention toward unlocking their further potential for the Bitcoin Cash economy.