Smart Contracts on Bitcoin
Contrary to popular belief, Bitcoin¹ comes with smart contracting capability since its inception, with a native stack-based programming language called Script. Each Bitcoin transaction consists of inputs and outputs. Each output locks some bitcoins with a script that dictates obligations of a contract. If an input comes with a script that fulfills the contract of an output, it unlocks bitcoins² in that output and moves them into new outputs. This is how bitcoin ownership transfer works.
Script is generally regarded as extremely limited and thus incapable of sophisticated smart contracting. One often cited deficiency of Bitcoin smart contracting is its lack of state. It is one major limitation Ethereum supposes to overcome that justifies its existence.
Some contracts are inherently stateful in that they require contracting parties to interact in multiple stages and depend on time-varying states, such as onchain voting and gaming. We show a general mechanism to maintain state in Bitcoin smart contracts. We also implement a stateful contract in sCrypt, a high-level language that compiles to native script.
Before we look at how to maintain state in Bitcoin smart contracts, we introduce a powerful technique called OP_PUSH_TX. It can be regarded as a pseudo opcode³ that pushes the current transaction into the stack, which can then be inspected at runtime. More precisely, it enables inspection of the preimage used in signature verification defined in BIP143. The format of the preimage is as follows:
Stateful Contract Implementation
Once we can inspect the transaction context of a contract, we can place arbitrary constraints on its inputs and outputs.
One way to implement state in contract is dividing the contract in the locking script into two parts: data and code. Data part is the state. Code part contains the business logic of a contract that encodes rules for state transition. Data is passively appended to code as OP_RETURN <data> or OP_PUSHDATA <data> OP_DROP. Even though it is not evaluated, it still affects validity of a contract since the preceding code part validates it.
Using OP_PUSH_TX, we can get the locking script of the output being spent from part 5 and that of the new output from part 8. To maintain state, we demand the code part of the locking script must not change and data/state changes must comply with state transition rules in the code part.
This is analogous to the concept of object in Object Oriented Programming, with code being methods and data being member variables of an object. Methods are immutable⁴. Member variables are encapsulated and can only be mutated through methods⁵. Methods are called from the unlocking script, encoding which method to call and their arguments.
An Example Contract: Counter
Let us look at a simple example of a stateful contract: a counter contract tracking how many times its function increment() has been called. Its code is shown below with comments inline.
Line 3 and 4 ensure the preimage is from the current transaction. We get the previous locking script in Line 9, also known as scriptCode in part 5 of the preimage. The previous counter state is extracted from scriptCode in Line 12, after which it is incremented and put in the new locking script in Line 14. Note that the counter is the only part that changes in the locking script. The rest ensures the output contains the new locking script.
Here is code to deploy the contract and repeatedly call its function increment(). An instance of the contract with counter increment from 0 to 9 can be found: 0 -> 1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 7 -> 8 -> 9. Note that the counter state is at the end of the script of the first transaction output.
This is part of a series we are writing to demonstrate what Bitcoin smart contracts can do and how to implement them. Many supposed limitations of script are due to failure to realize its potential. As we explain and demonstrate more on what script can achieve, people will find it is extremely extensible, versatile, and future-oriented. We will show that Bitcoin, without artificial limits, can run any smart contracts other blockchains can run, while being able to scale unboundedly. It can be leveraged to make many applications across industries more efficient and secure via economic incentives.
Special thanks go to nChain for providing the original idea of OP_PUSH_TX. We also thank George Papageorgiou, James Belding, Brenton Gunning, and Joel Dalais for valuable suggestions on an earlier draft of this article.
: In the article, we refer to Bitcoin SV as Bitcoin as it follows the original protocol design.
: There is an exception that some outputs contain zero bitcoins, marked by, e.g., OP_RETURN.
: An opcode is the basic unit of script.
: There is some exception to this general rule in some dynamic OOP languages.
: There is also some exception to this general rule.