DEV Community

Oghenovo Usiwoma
Oghenovo Usiwoma

Posted on

The PSBT Standard

PSBT is short for Partially Signed Bitcoin Transaction. It is a standard for defining transactions that can be signed by multiple signers across different clients. It contains the necessary information for a signer to produce a signature for a transaction. This is specifically beneficial for offline signers as they cannot fetch additional information about a transaction. The original PSBT format was specified in BIP 174 and a Version 2 in progress in BIP 370.

The PSBT Binary format

The PSBT format consists of key-value maps separated by a 0x00 byte. It supports the following maps:

  • The Global Map contains key-value pairs that apply to the entire transaction
  • The Input Map contains key-value pairs that apply to individual inputs
  • The Output Map contains key-value pairs that apply to individual outputs

Key-value pairs in each map all follow this format:

<keylen> <keytype> <keydata> <valuelen> <valuedata>
Enter fullscreen mode Exit fullscreen mode

The keylen, keytype and valuelen are specified as a compact size unsigned integer which ensures values that can be specified in one byte will be specified in one byte, for example, the decimal number ten is specified as 0x0a instead of 0x000a.

keydata is not always available. Some key types like PSBT_GLOBAL_UNSIGNED_TX = 0x00 do not have keydata.

To see the full list of key types supported, go to BIP 174.

Let's look at a few examples:

We'll start by breaking down this PSBT which is an unsigned tx with 0 inputs and 0 outputs.

70736274ff01000a0000000000000000000000
Enter fullscreen mode Exit fullscreen mode
<magic> 0x70736274ff // used to identify PSBT data
<global-map> // contains global transaction data
<keylen> 0x01 // Key length is one byte
<keytype> 0x00 // PSBT_GLOBAL_UNSIGNED_TX
<valuelen> 0x0a // 10 bytes
<valuedata> 0x00000000000000000000 // The Tx hex
<separator> 0x00
Enter fullscreen mode Exit fullscreen mode

Let's breakdown another simple unsigned PSBT data with 1 input and 1 output

0x70736274ff0100550200000001eb63366191dbaf74d30c6de8cbb7208de3fb65ad266b41c56990a5a7e6a2eac90000000000ffffffff010017a804000000001976a914c1752bf5bffbd320ab2ab625b32b9fe48337dce488ac00000000000000
Enter fullscreen mode Exit fullscreen mode
<magic> 0x70736274ff
<global-map>
<keylen> 0x01 // Key length is one byte
<keytype> 0x00 // PSBT_GLOBAL_UNSIGNED_TX
<valuelen> 0x55 // 85 bytes
<valuedata> 0x0200000001eb63366191dbaf74d30c6de8cbb7208de3fb65ad266b41c56990a5a7e6a2eac90000000000ffffffff010017a804000000001976a914c1752bf5bffbd320ab2ab625b32b9fe48337dce488ac00000000 // The Unsigned Tx hex
<separator> 0x00
<input-map>
<separator> 0x00
<output-map>
<separator> 0x00
Enter fullscreen mode Exit fullscreen mode

Signing a PSBT

The Signer uses the UTXOs provided in the PSBT to produce signatures for inputs. The signer performs some checks before signing.

  • For non-witness inputs, the signer verifies that the txid of the non-witness utxo matches the txid specified in the unsigned transaction.
  • For witness-inputs, the signer verifies that the witnessScript (if provided) matches the hash specified in the UTXO or the redeemScript, and the redeemScript (if provided) matches the hash in the UTXO

Any signatures created by the Signer must be added as a PSBT_IN_PARTIAL_SIG = 0x02 key-value pair for the respective input it relates to. If a Signer cannot sign a transaction, it must not add a Partial Signature.

Our previous PSBT spends from a non-witness utxo. Before we sign it, we must add the PSBT_IN_NON_WITNESS_UTXO = 0x00 key-value pair for that input:

0100b6020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0402bd0300ffffffff02c817a804000000002321035b4a568361af71783c22a6c9d9a13e3d5f32d9a7278c0e8325e4bc29b0090825ac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf9012000000000000000000000000000000000000000000000000000000000000000000000000000
Enter fullscreen mode Exit fullscreen mode

The global map is unchanged but new data has been added to the input map:

<input map>
<keylen> 0x01 // 1 byte
<keytype> 0x00 // PSBT_IN_NON_WITNESS_UTXO, requires no keydata
<valuelen> 0xb6 // 182 bytes
<valuedata> // the transaction in network serialization format the current input spends from
0x020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0402bd0300ffffffff02c817a804000000002321035b4a568361af71783c22a6c9d9a13e3d5f32d9a7278c0e8325e4bc29b0090825ac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000
<separator> 0x00
Enter fullscreen mode Exit fullscreen mode

To sign, we add the following PSBT_IN_PARTIAL_SIG = 0x02 key-value pair to the input pair:

<keylen> 0x22 //
<keytype> 0x02 // PSBT_IN_PARTIAL_SIG keytype
<keydata> 0x035b4a568361af71783c22a6c9d9a13e3d5f32d9a7278c0e8325e4bc29b0090825
<valuelen> 0x47
<valuedata>
3044022063179cb0f91d2b1d2c45d8112a527e4491ec771befb62c5717afac69f42345d9022035b3c00d8cb2f6309e0c0f6df03f54ae3e0bd6c4cae68061a714b57cf4f8cd8c01
Enter fullscreen mode Exit fullscreen mode

Finalizing a PSBT

The Finalizer validates each input, removes the partial sigs and constructs the PSBT_IN_FINAL_SCRIPTSIG = 0x07 and PSBT_IN_FINAL_SCRIPTWITNESS = 0x08 for each input and places them in the Input Map.

Our finalized PSBT looks like this:

0x70736274ff0100550200000001eb63366191dbaf74d30c6de8cbb7208de3fb65ad266b41c56990a5a7e6a2eac90000000000ffffffff010017a804000000001976a914c1752bf5bffbd320ab2ab625b32b9fe48337dce488ac0000000000
// Global map is unchanged

0100b6020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0402bd0300ffffffff02c817a804000000002321035b4a568361af71783c22a6c9d9a13e3d5f32d9a7278c0e8325e4bc29b0090825ac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000
// PSBT_IN_NON_WITNESS_UTXO is unchanged

// PSBT_IN_PARTIAL_SIG has been removed

<keylen> 0x01
<keyvalue> 0x07 // PSBT_IN_FINAL_SCRIPT_SIG
<valuelen> 0x48
<value> 0x473044022063179cb0f91d2b1d2c45d8112a527e4491ec771befb62c5717afac69f42345d9022035b3c00d8cb2f6309e0c0f6df03f54ae3e0bd6c4cae68061a714b57cf4f8cd8c01
0000
Enter fullscreen mode Exit fullscreen mode

Extracting a serialized transaction

The Extractor uses the unsigned transactions and signatures provided in the PSBT to construct a serialized transaction.

Conclusion

The PSBT standard is a key innovation in the world of Bitcoin transactions. We have only scratched its surface, if you want to read the full specification, go to BIP 174.

Top comments (0)