Fadi Barbàra's Brand

Journey of a P2SH transaction - Part 3

We left the ball in the hands of Bob, who must now spend the P2SH transaction sent by Alice. Bob also has a node and uses the same library as Alice.

The first thing Bob does is to verify that the transaction has arrived. In theory, Alice could send him the transaction id, but that doesn't scale if she plans to create a protocol constantly used by thousands of people. Furthermore, such an exchange requires too much interactivity and that's not good: if Alice buys a good from Bob, Bob can send Alice the script (or a process can generate it automatically). But requiring Alice to send the transaction id back to Bob means that one too many unnecessary response remains in the system. If Bob's servers are compromised one day, the less information there is, the better.

Another thing to be careful of is that the P2SH transaction will not appear in Bob's node as "unspent transacion": technically it is not possible to know who owns this kind of transaction because everything that now appears in the blockchain is a hash of the script we called "known script". This is also a way to increase your privacy in the Bitcoin protocol, we will see how.

But then: how will Bob know if he has received a transaction? Fortunately for them, the address the money goes to is generated only by the "known script". So, knowing that, it is possible to monitor the blockchain to know the amount of funds available.

Bob generates the address

Bob has the known script obviously, so it is very easy to generate the address for him. Here's the complete script, but basically the important part is this:

redeem_script = Script(["OP_SHA256", "eb2e10773d403f9972939ede70382fc05c43260b1beb0d5163f3b39dbc8f964b", "OP_EQUAL"])
print(addr.to_string() )

you see the redeem_script we created last time and the addr variable that transforms the script in a P2SH address. The script outputs the address: 2N9dQC4mkYeMw1rEYomehB9VTBXtF4FXqAB.

Monitoring the address

Now that Bob has the address, he has to check if there are coins in it. As we said before, the transaction doesn't appear in the listunspent RPC call, so he has to manually check the Bitcoin blockchain. To do that he can do two things

  1. Check his own node
  2. Check somebody else's node

Of couse the first option is better, but unfortunately it requires Bob to have the txindex flag and also he should have a complete blockchain and not a pruned one. Those two options require a lot of space on his hard disk, and while the mainnet does worth it, Bob thinks the testnet isn't worth it (he is a little bit of a freerider in this sense). The second option is less secure (you trust somebody else) but more practical.

Bob decides to use the Blockcypher API and continuing to script in Python, even if a simple curl call would suffice. That's because he thinks it would better integrate in the previous script when he'll write a simple library. You'll find the script here, but the important lines are:

x=requests.get(api + address).json()

which gives Bob the amount of the address in satoshis and the txid of the input transaction: that's why Bob doesn't need Alice to tell him this. So basically, Bob can now monitor the chain. When he sees all the balance has been confirmed (unconfirmed_balance==0) he can spend that amount.

Spending the P2SH transaction

Now we come to the crucial part of this step: Bob spends the P2SH transaction Alice sent to him. This corresponds to the example that you can find here. We had a different known script, so we'll need less variables. Also, we'll depart a little from the example beacuse it lends itself to optimizations that are easy to understand.

First thing: what are we spending? from which transaction?

txin = TxInput(txin, txin_n_out)

See? we used the previously selected variables. Now, what's the unknown script, the one Bob knows, but Alice doesn't?

# hexadecimal obtained from bash with (the .. are important):
# echo "obase=16; 389274691823"|bc|tac -rs ..

Now, where does Bob want to send the transaction to? Bob's a novice too, he sends it to a P2PKH address:

txout = TxOutput(to_satoshis(0.0137), to_addr.to_script_pub_key() )

Note that the amount is a little less than the one Alice gave Bob because of fees (Bob put a random fee; again, it could still be too high...). OK, we have all the information. Now Bob has to package that:

tx = Transaction([txin], [txout])
txin.script_sig = Script([unknown_script, redeem_script.to_hex()])
signed_tx = tx.serialize()

As in the previous post we add some printing:

print("\nRaw signed transaction:\n" + signed_tx)
print("\nTxId:", tx.get_txid())

you can see the transaction here and the whole script needed to generate the transaction here.


This concludes the journey of a P2SH transaction: in the following post we will analyze the blockchain footprint to see if P2SH transactions have some shot at being a better privacy preserving way to transact. The two transactions can be viewed in any block explorer, or on your own node. However, there are some considerations to make.

The first one is that Alice and Bob have used a library that is not ready to be used in production yet. So as instructive as this blog series is, it is not advisable to use this library in real life (i.e. on mainnet). The second consideration is that this process does not scale right now: the proposed scripts still require a lot of manual work by both Bob and Alice. In the future, Alice or Bob will probably improve these scripts, not only to automate the system, but also to support different types of transactions. For example here we have performed a transaction from a P2PKH address to a P2SH one and then from the latter to a P2PKH. How would it change if we only used P2SH? The last consideration is that the known script we used is very unsecure: never use it that way. You see that Bob doesn't need to sign anything here: isn't it a little strange? Well, yes it is. As we said in the first post of the series, anybody who knows the secret is able to spend this transaction, and when Bob send's his transaction to miners, they will know the secret (we'll see how in the next post). The fix is easy, and it involves signing of course