There are currently three different ways to do this: raw transactions, PSBT in 0.17, and PSBT in 0.18. I recommend that you use PSBT in 0.18 as it is the least hassle, but I will describe all three here for you.
Traditionally, you would do this with createrawtransaction
, fundrawtransaction
, and signrawtransactionwithkey
/signrawtransactionwithwallet
. One person would have to know all of the inputs that were being used in the transaction or have all of those stored in their wallet (i.e. they have imported everyone else's public keys and/or redeemScripts into their wallet) and then they can use createrawtransaction
or fundrawtransaction
to create the raw transaction.
createrawtransaction
just takes inputs and outputs that are specified by the user and gives the resulting transaction as is (so you need to be careful about fees and change). fundrawtransaction
can choose the inputs to use for you using your wallet and it will handle fees and change outputs itself. However fundrawtransaction
needs to know all of the information necessary to construct the final transaction (except for the private key, it can just use a dummy signature) in order to correctly estimate the transaction fees, which is why you need to import the public keys and scripts into your wallet.
Once you have the raw transaction, you then send it to each of the other people involved in the transaction. They then sign it using signrawtransactionwithwallet
(if the keys and scripts are in their wallet) or signrawtransactionwithkey
(if their keys and scripts are not in their wallet). Most people will use signrawtransactionwithwallet
. They then send the result back to you where you have to combine all of the signed transactions into the final transaction using combinerawtransaction
. Then you can broadcast it with sendrawtransaction
.
Instead of sending the transactions out to everyone to sign at the same time, you could send to one person who signs, who then sends to the next person and so on. This would not require combinerawtransaction
. The last person would sign and then use sendrawtransaction
to broadcast.
This process is kind of bulky and requires that one person knows some (or all) of the information for the inputs being used (except for the private keys).
Since Bitcoin Core 0.17, you can use the various *psbt
RPCs to achieve the same thing. With 0.17, the flow is fairly similar to the the raw transaction flow described earlier. You would have to have all of the information for creating the final transaction in your wallet (except for private keys) so that fee estimation can be properly done for coin selection, or you need to know which inputs to use. Either way, you use walletcreatefundedpsbt
and specify the inputs you want and the outputs you want. Bitcoin Core will then automatically choose additional inputs if there is not enough to cover the output amount and create a change output for you. Because it needs to also conver transaction fees, it needs to have the same information that you need for fundrawtransaction
to be able to estimate fees for the inputs it chooses.
Then you would send the transaction to everyone else who would add their signatures (and other metadata) using walletprocesspsbt
. You can also do walletprocesspsbt
first before sending the transaction to everyone so that various metadata needed for signing (like the UTXO that is being spent for each input) is added beforehand. Once signed, everyone would send the transaction back to you and you combine them together using combinepsbts
Then you create the final transaction for broadcast using finalizepsbt
and send the result using sendrawtransaction.
Of course using PSBTs you can do the same thing described above where each person sends the transaction to the next person to be signed instead of sending back to you to be combined.
Again, this process is kind of bulky and requires that one person knows some (or all) of information for the inputs being used (except for the private keys). But it is slightly less likely to result in an error than the raw transaction method. PSBTs also allows you to use other non-Bitcoin Core wallets that support PSBT, and you can use this method with wallets which are not connected to the internet or otherwise do not have the blockchain or the UTXOs that are being spent in the transaction.
However, Bitcoin Core 0.18 actually makes this whole process a lot easier. Bitcoin Core 0.18 introduces a couple of new *psbt
RPCs which make it so that one person does not need to know all of the inputs being used and all of the information for each input. The only caveat is that this method assumes that each person has their own outputs that they want to create instead of multiple people pooling together their Bitcoin to make one large output.
With 0.18, everyone would create their own PSBT using walletcreatefundedpsbt
. They specify any of their own inputs that they want to use and all of their outputs. Any change outputs will be added, and additional inputs from the wallet will be added to cover the output amount and transaction fee if not enough were specified by the user. Since every user does this with their own wallet, no one person needs to know anything about what the other users have in their wallets.
Then each person would send the PSBT to a designated coordinator who uses joinpsbts
to join every PSBT into one large PSBT. This PSBT will have all of the inputs and outputs that each person wants to spend and create. So instead of having many individual PSBTs with their own inputs and outputs, there is now one large PSBT with everyone's inputs and outputs.
This PSBT is then sent back to each person who uses walletprocesspsbt
to update the PSBT and add their input information to the PSBT and signs it. Even if a user has an airgapped setup where their private keys are somewhere that does not have access to the blockchain, they can update the PSBT from an online watching only wallet and send the PSBT to the offline machine to be signed.
Once everyone has signed the PSBT, it is sent back to the coordinator who uses combinepsbt
to combine all of the signatures and input information into the same PSBT, then creates the final network transaction using finalizepsbt
and broadcasts it using sendrawtransaction
.
Again, this can be done serially like the other methods described earlier and thus omit the need for combinepsbt
.