Fadi Barbàra's Brand

Journey of a P2SH transaction - Part 1

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.

How a P2SH transaction works

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 known script. I call it known because I assume a scenario where Alice wants to pay Bob, therefore:

  1. Bob creates a secret s and performs some function f on it, so that he has S=f(s)
  2. Bob sends S to Alice and the script needed to evaluate it
  3. Alice can create the P2SH transaction with the script given by Bob

So because the part of the script with S in it is known to both Alice and Bob, I call that part the known script.

Create the script

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.

Creating the hash

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.

Evaluate the smart contract

Bob needs to know if everything will end well before he gives the know script to Alice. How can he evaluate it? Luckly there is the 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
            

Conclusions

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 before it is included in some block of the blockchain. So the miner sees Bob's secret: and why shouldn't the miner take advantage of this by creating a transaction similar to Bob's, but sending the money to an address controlled by the miner?

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.