The goal of this series of posts is to explain how to create a non-standard transaction and embed it in a P2SH transaction in Bitcoin. P2SH transactions can be considered "the smart contracts of Bitcoin", although there are obvious differences: the Bitcoin language (called Script) is not Turing complete (e.g. it does not have the concept of for loops) and therefore the number of possible procedures is very narrow. This has some disadvantages, such as the fact that you cannot program a complicated script, but it has the advantage of being more secure, one reason being that you always have a finite number of steps. So there is no need of Gas.
Another substantial difference is that the Bitcoin smart contract is not visible to the public, while the Ethereum-type smart contract is. In fact, only a hash of the smart contract appears in the Bitcoin blockchain. This process uniquely identifies the smart contract (thanks to the hash's second-preimage-resistance property) and at the same time this hashing procedure hides it from the public (thanks to the hash's preimage-resistance property). The Ethereum smart contract, on the other hand, is "ABI encoded" and it is possible to trace back from the encoding to the source code. P2SH transactions are introduced in BIP 16 and BIP 12.
There are a lot of medium post on how to make an Ethereum smart contract, but there is not much blogging about Bitcoin smart contracts. There is the marvelous Learning Bitcoin from the Command Line tutorial started by Christopher Allen with many contributions, but it's based on Bitcoin Core and as the tutorial says Bitcoin Core has no API to create P2SH:
Each of those steps of course takes some work on its own, and some of them can be pretty intricate. The good news is that you don't really have to worry about them, because they're sufficiently complex that you'll usually have an API take care of it all for you. Unfortunately, this is another place where you're going to need to fall back to APIs, in large part because bitcoin-cli
doesn't provide any support for creating P2SH transactions. (It can redeem them just fine.)
In this series of posts I present one way to build, test, evaluate, re-test and then send P2SH transactions. Redeeming them is easier and we will see how to do it. I assume you already know how basically Bitcoin transactions (e.g. of the P2PKH type) work: if that is not the case, many blog posts on the Internet want to explain it to you.
I need this part just to put some contest. As seen in BIP 16, the P2SH transaction outputScript is
OP_HASH160 [20-byte-hash-value] OP_EQUAL
where the 20 bytes hash value is the hash of one part of the script/smart contract.
To be clear: the script in a P2SH transaction must be split in two: a part we call "known script" and one we call "unknown script". The 20 bytes hash is the hash of the
s
and performs some function f
on it, so that he has S=f(s)
S
to Alice and the script needed to evaluate itSo because the part of the script with S
in it is known to both Alice and Bob, I call that part the
To validate a P2SH transaction, the script behind the hash combined with the unkown part (i.e. the smart contract) must return true
(or 01
) on the stack. Since we want to make a transaction, we also want the script to be a little complex, or at least to need specific knowledge that is not public: if they were public, anyone could redeem these funds.
In this sense, therefore, a script that given two numbers verifies that the sum gives 3 is not very safe (all opcodes should have "OP_" before them, but I am lazy). In fact if Alice receives this script from Bob:
2 ADD 3 EQUAL
then it's pretty easy for Alice to understand that Bob's secret is s=1
.
Arguably, a script for which you have to provide a number such that its hash is equal to eb2e10773d403f9972939ede70382fc05c43260b1beb0d5163f3b39dbc8f964b is a little more secure (but it is not, see final section):
SHA256 eb2e10773d403f9972939ede70382fc05c43260b1beb0d5163f3b39dbc8f964b EQUAL
The number that give this hash is 389274691823
, so to use the formalism mentioned above, Bob creates s=389274691823
and then he performs f=SHA256
on s
with result S=f(s)=eb2e10773d...
. Bob then gives the above known script to Alice.
Obviously, creating hashes is not the only possible smart contract on Bitcoin, but it is still a common operation and it is worth learning to build puzzles that are based on this type of secret. Suppose the secret number is num=389274691823
. Then this bash one-liner
echo "obase=16; $num"|bc|tac -rs ..|xxd -r -p| sha256sum|cut -d" " -f1
gives Bob the hash he needs to give Alice, i.e. S=eb2e10773d403...
. Commands in the bash script are (as per their respective man page title)
Obviously things aren't so simple. This bash
one liner does work only is you use a positive number less than 2147483647
. Here you find a better (and longer) script capable of handling bigger numbers and negative numbers.
Bob needs to know if everything will end well btcdeb
program (github link).
The easiest way to evaluate a Bitcoin script is by doing btcdeb '[SCRIPT]'
. In our case that means
btcdeb '[389274691823 SHA256 eb2e10773d403f9972939ede70382fc05c43260b1beb0d5163f3b39dbc8f964b EQUAL]'
Note that I put both the unknown part (i.e the secret s
) and the known part of the script. Then write step
on the btcdeb
console all the way down and see how the stack evolves.
btcdeb> step
First of all, scripts similar to the one I proposed above are not secure. Even if Alice or people in general do not know the secret, miners or transaction relayers do. In fact, if Bob doesn't mine transactions himself, then he has to broadcast them to some miner. So the miner see's Bob transaction
The problem with the script is simply that there is no control of any signature. The fix is easy: just add Bob's public key and the CHECKSIG opcode efter the EQUAL one. We see the details in the future.
Right now we just created a script and we evaluated that it resolves to true
(01
) in the stack. So we end the journey of the P2SH transaction with Bob sending the known part of the script to Alice. Next time Alice will put funds in the transaction and broadcast it.