The PSBT Standard
Tags: node, bitcoinjs, web3, bitcoin
Published: 2023-05-29 06:27:57

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](https://github.com/bitcoin/bips/blob/master/bip-0174.mediawiki) and a Version 2 in progress in [BIP 370](https://github.com/bitcoin/bips/blob/master/bip-0370.mediawiki).  

## 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>
```
The `keylen`, `keytype` and `valuelen` are specified as a [compact size unsigned integer](https://en.bitcoin.it/wiki/Protocol_documentation#Variable_length_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](https://github.com/bitcoin/bips/blob/master/bip-0174.mediawiki).

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
```

```
<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
```

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

```
<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
```

## 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
```
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
```

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
```

## 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
```

## 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](https://github.com/bitcoin/bips/blob/master/bip-0174.mediawiki).