This post was first published on Medium.
Smart Contracts on Bitcoin have been shown to be more powerful and versatile than previously thought. Yet, there are still two grave limitations:
- A contract cannot support new features once deployed. For example, we could deploy a token contract, only to find out later it cannot be integrated into some new exchange or voting applications, due to lack of certain features. This greatly hinders its interoperability with third-party applications and thus wide adoption.
- All supported features must be included in the token contract, implied by 1. Even if we would foresee all features the token needs to support when it is first conceived, only a few features are frequently used and most of them are rarely, if not never, used. This causes contract size bloating.
Pay to Contract Hash
We propose a novel approach to address both limitations simultaneously, called Pay to Contract Hash (P2CH). We use NFT as an example to demonstrate how it works. It is also applicable to other crypto assets such as native bitcoins and fungible tokens.
In our previous NFT contract, we maintain a table mapping each token to its owner’s address. The owner possesses a private key that can authorize the transfer of the token.
Figure 1: NFT 3 is Owned by a Contract
Under P2CH, the owner can also be a contract’s address, defined as the hash of its script. The hash acts as the unique ID of the contract and is used to reference the contract.
Specifically, hash160 is used, the same hashing algorithm used to hash a bitcoin public key into an address. A contract hash appears the same as a regular bitcoin address, since they are both 20 bytes. The new NFT contract looks like the following:
This is the same NFT contract as before, except for the else branch. It handles the transfer when a token is controlled by a contract, instead of a user’s private key.
We need to ensure the contract is unlocked in another input of the same transaction, as we have done in inter-contract call.
In an example as Figure 2 shows, the token referenced in input 0 needs to validate input 1 is referencing the expected contract in the same transaction tx2.
Line 20-22 get the id of the transaction containing the contract (tx1 in the diagram) and the output containing it (output 0 in tx1).
Line 24 validates the full transaction matches the txid.
Line 26 parses the contract transaction to get the contract’s full script and hashes it and matches it against the owner “address” at Line 27.
Caption: Figure 2: Transfer Token Controlled by a Contract
A Token Sale Contract Example
One example contract could be the following:
It can be spent if and only if a given address is paid a specific amount. Using this contract, we can sell a token at a given price atomically in three steps:
- Deploy a TokenSale contract.
- Transfer a token to the hash of the above contract, i.e., hash160 of its locking script. Now the token is under the control of a contract, not a user’s private key. This is similar to a contract account in Ethereum, which does not have a corresponding private key, unlike an externally owned account (EOA).
- Unlock both the token and TokenSale contract in a single transaction, as in Figure 2. The buyer will transfer the token from the contract to himself.
A full code example can be found here.
It is worth noting the contract can be arbitrary and can be developed by a third party independently. The contract does not have to be known in advance. This means the token is infinitely extensible (limitation 1). Also, it is compact and only handles P2CH that applies to any contract (limitation 2).
In addition, they are alternative ways to pattern match a contract. We use a hash of the full contract script. Parts of the script can also be used, e.g., the code part of a stateful contract.
Watch: CoinGeek New York presentation, Smart Contracts & Computation on Bitcoin
New to Bitcoin? Check out CoinGeek’s Bitcoin for Beginners section, the ultimate resource guide to learn more about Bitcoin—as originally envisioned by Satoshi Nakamoto—and blockchain.