Build, Sign and Broadcast PSBTs in Rust— Part 1
Exploring the technical side of Partially Signed Bitcoin Transactions by simulating a CoinJoin with separate wallets in Rust.
Hey guys! In this guide, we’ll go through what a Partially Signed Bitcoin Transaction (PSBT) is and demonstrate how to perform manual Coinjoins using PSBTs on the regtest network. This article is in two parts, this first part focused mainly on laying the foundation (explaining the concepts) while the second part followed a pragmatic approach to the concepts explained here. You can checkout the second part here.
Before moving forward, let’s understand what a PSBT is, when it was introduced to the Bitcoin Network and how it works.
The What, When and How of PSBTs on the Bitcoin Protocol:
PSBTs are standard data format for unsigned transactions proposed in BIP 0174 making it easier for multiple wallet softwares or nodes to exchange information about an unsigned bitcoin transaction, and can sign the same transaction independently.
PSBTs has been widely adopted by many wallet softwares and nodes because it offers great benefits, especially in Multi-Sig transactions and CoinJoin transactions. This essentially means PSBTs enhances interoperability in the Bitcoin Network because transactions can now be sign by multiple parties across multiple devices (including cold storage devices).
Now that we’ve understood what PSBTs are, let’s talk about how PSBTs work in practice using a CoinJoin transaction example between Alice, Bob, and Carol. Let’s make Alice the coordinator for this transaction.
Firstly, all the participating users will each create their own PSBT, specifying the inputs and the output of the transaction from their wallet.
Afterwards, the PSBTs created by Bob and Carol will be sent to the coordinator, Alice (who created his own PSBT as well), to join the PSBTs into one large single PSBT. This single PSBT contains the PSBT of the other parties, and will be sent to each of them to update the PSBT, add their input information and sign the transaction.
After all the participating users have signed the PSBT, the coordinator receives it back and combines all the updated input informations and signatures into the same PSBT, finalizes the PSBT by creating a fully signed network transaction and finally broadcasting the transaction to the network.
This may seem like Alice knows the input information of the participants and can cheat them, but in reality the coordinator cannot steal the funds.
I know, so let’s move to the interesting part!
Just before we move on, I’ll like to mention that you’ll need the following to follow along:
- Bitcoin Core Setup — I’ll be interacting with Bitcoin Core via RPC and you can find updated RPC API reference here. You can clone Bitcoin Core from here and try to complete the build processes.
- Rust Installation — You can follow the official website url to install and setup Rust on your machine.
- Experience fighting the Rust compiler.
That’s all so let’s dive in!
Open up your terminal and run the following commands to setup the project folder in your pwd and cd into the newly created directory:
mkdir psbt_guide && cd psbt_guide
Initialize a new cargo package in the current directory with the command below:
cargo init
If successful, you’ll have a folder structure similar to what we have in the image below.
At this point, go ahead and start the Bitcoin daemon on your machine by running bitcoind
on your terminal or whatever you set up as the alias to start the daemon. We’ll be using the regtest network for this guide, so ensure your bitcoin.conf
file is pointing to regtest. Also feel free to follow along on testnet or the signet network (the concept is the same on mainnet as well, but always test and verify on any of the different test networks available before going to mainnet unless you have enough satoshis to test with).
You can verify that Bitcoin Core is running by typing in bitcoin-cli -getinfo
in to the terminal and expect a response like I have in the image below. Also, you’ll notice I used bitstein-cli
rather than bitcoin-cli
, it’s because I have multiple instances of Core on my machine (pointing to different networks), so I added an alias for this instance to differentiate it from the others.
Because we’re trying to build and sign a PSBT, we’ll simulate the different CoinJoin participants by creating two PSBTs from two seperate wallet.dat
files. So we’ll go ahead and create/load two different wallets, create an address from each of the wallets and get funds into them by mining blocks ourselves.
Open your terminal and run:
# Run this command to create a new wallet on Core
bitcoin-cli createwallet <wallet_name>
# Run this command to load an existing wallet from Core
bitcoin-cli loadwallet <wallet_name>
Either of the above command will create or load an existing wallet. Remember we need two wallet.dat
files to simulate two CoinJoin participants and create two seperate PSBTs, so go ahead and create or load two different wallets.
Note: Using the
createwallet
rpc command creates and loads the wallet automatically, so you don’t need to run theloadwallet
command again to get the wallet loaded.
Now, let’s get some test funds into the wallets by mining blocks. Load each of the wallet and mine some blocks to get funds into them:
bitcoin-cli -rpcwallet=<wallet_name> -generate 101
Because we created or loaded two wallets (also applies if you have more than one wallet locally and want to perform some wallet related actions like we’re doing now), then you will need to specify the wallet you want to generate some test funds on, and that’s why we’re using the-rpcwallet
flag to specify the wallet name. If you’re unsure about the wallet names, then run bitcoin-cli listwallets
to show the wallets you’ve loaded.
That’s all we need for this first part. In the second part we’ll start with connecting to Core via RPC, then move to creating, signing and broadcasting our PSBT to the network.
Conclusion
We have setup a good foundation for ourselves by understanding how PSBTs work and how CoinJoins are good candidates for PSBTs. We also got Bitcoin Core running successfully, created and loaded two different wallets to simulate two CoinJoin participants, and mined some blocks to get the wallets funded for our transaction. We’ll get to the more interesting part in the second part of the article.
Feedback is a blessing! If this was helpful, please do give a clap and don’t forget to share your thoughts in the comment section. Have any questions or corrections to give? You can drop it in the comment section or shoot my DM on X and I will respond asap!