PSBT
Partially Signed Bitcoin Transaction

A PSBT (Partially Signed Bitcoin Transaction) is a data format for passing transactions around for signing.
This format essentially contains raw unsigned transaction data, along with extra data required to sign the inputs.
So if you export an unsigned transaction from your bitcoin wallet, it will most likely be in the PSBT format. Most modern wallets support the exporting and signing of PSBTs, for example:
- Electrum (Desktop Wallet)
- Sparrow (Desktop Wallet)
- Trezor (Hardware Wallet)
- Coldcard (Hardware Wallet)
Purpose
Why do we need PSBTs?

To sign a bitcoin transaction, you actually need some additional information that isn't included within the raw unsigned transaction data itself.
For example, if you want to create a signature to unlock a P2WPKH input, you need the following extra data:
- The
ScriptPubKey
of the output being spent. - The
amount
of the output being spent.
This information is stored on the previous transaction that created the output (UTXO), but not on the current transaction spending it as an input.
Now, this isn't a problem if you're using a wallet with an Internet connection (i.e. a "hot wallet"), as this information can be retrieved from the blockchain by searching for the previous transaction that created the UTXO you're spending.
However, if you're using an offline wallet to sign your transaction (e.g. a hardware device or "cold wallet"), it will not have an up-to-date copy of the blockchain, so even though it has the private key to sign for the input, it does not have access to the extra information needed to actually create the signature.
Therefore, the PSBT format allows you to pass all the data required to sign the transaction on a separate wallet/device.
Benefits
Why was the PSBT format created?

A PSBT provides a standard format for passing transactions around for signing.
Before PSBTs, wallets would implement their own format for exporting the extra data needed to create the signatures for a transaction. This worked, but it meant their unsigned transaction format was probably not compatible with other wallets and devices. Therefore, the person signing the PSBT would need to use the same software as the person who created it.
However, with PSBTs, an unsigned transaction can be exported and signed by different wallets and devices.
For example, you could construct a transaction that spends a multisig output (e.g. P2MS) requiring signatures from 3 different people to unlock it. By using the PSBT format, you can send each signer the unsigned transaction data, and each signer can use whichever wallet/device they prefer to add their signature, without having to worry about whether their software understands the unsigned transaction format.
These separate PSBTs can then be combined to create the final signed transaction.
So having a common format for unsigned (or partially signed) transactions enables compatibility between different bitcoin wallets and devices.
Structure
What does a PSBT look like?
The PSBT format is a series of key-value pairs contained within separate maps.

Header
A PSBT always starts with some header bytes to identify the data as a PSBT:
Header
- The magic bytes70736274
('psbt' in ASCII) followed by a 1-byteff
separator.
Maps
After the header, the PSBT contains three map types (in the following order):
-
Global Map
— General information about the unsigned transaction.- This typically contains the unsigned transaction data.
- There is only one global map.
-
Input Map(s)
— Data related to each input in the unsigned transaction.- This typically contains the extra data required to create signatures for each input.
- There can be multiple input maps (one for each input in the unsigned transaction).
-
Output Map(s)
— Data related to each output in the unsigned transaction.- This typically contains supplementary information about the transaction's outputs. For example, this can be used to help identify change outputs.
- There can be multiple output maps (one for each output in the unsigned transaction).
The end of each map is signified by a 1-byte 00
separator.
The input map(s)
and output map(s)
must be in order corresponding to the inputs and outputs in the unsigned transaction. Each input and output must have its own map. If you do not need to include any key-value pairs for an input or output, just use the 00
separator to indicate the end of the map.
You need to extract the input count and output count of the unsigned transaction from the global map
to determine the number of input map(s)
and output map(s)
in the PSBT.
Key-Value
Within each map is a series of key-value pairs.
Each key-value pair has the following structure:
-
Key:
Length
— A compact size field indicating the size of the upcomingtype
anddata
fields.Type
— A compact size field indicating the type of data stored (see key types).Data
— Some extra data related to thetype
. This field does not always contain data, but when it does, it allows multiple keys of the sametype
in a map without having duplicates.
-
Value:
Length
— A compact size field indicating the size of the upcomingdata
field.Data
— The main data associated with the key.
These key-value pairs are repeated until the end of the map is reached.
Code
Here are some code simple snippets for decoding and encoding raw PSBTs.
These code snippets do not implement all the steps required for processing PSBTs (that's your job), nor do they detect any errors, but they should be helpful for getting the hang of deconstructing and building PSBTs for debugging and testing.
Decode
# ----
# PSBT
# ----
# example psbt in base64
base64 = "cHNidP8BAHcCAAAAAfdOPcmRewY2v88bQwUxucGsxSuXH0uEkKSNE80NzyiB
AAAAAAD9////AjU+AAAAAAAAGXapFD8wj0I2S+BXFbGa4wd7u8o36zxniKy2
WQAAAAAAABl2qRRjuoLSTMaSnO/tORKfAZa2RonLgYisf8kNAAABAL8CAAAA
AV4xZeLXJkrfKXk+fL6JBWhcMD/l0EPa78AWP1Pr4UpyAAAAAGpHMEQCIBS7
ZFI/kRlbC4oeDirf3uVy1EWPiNqSHhcwmkkiSwQ9AiAitN+1CQUHNCeY9dsc
ruF1U+B4Y+XtR5wXd83/YNy8ugEhAqAoKT0yVpnlt6sXX1Qz6eC2kO49L16M
A34ipmgeGZtl/f///wHNmAAAAAAAABl2qRSFdGbzr4xiIfOuLmEpSa4n414z
LYisf8kNACIGAxWaFXJLMzktDDpTbQAFCIllf+xM8nxjA86XolMXHogOGJwO
R5IsAACAAAAAgAAAAIAAAAAAAAAAAAAiAgKh3fjioSvCMeCiWW2YHs/jG7yf
7Ld4HzMyz/wF8fcKehicDkeSLAAAgAAAAIAAAACAAQAAAAAAAAAAAA=="
# convert base64 to hex string
require 'base64'
hex = Base64.decode64(base64).unpack("H*")[0]
# tip: if you want to decode from hex instead of base64, uncomment the line below and set the hex string there
# hex = '70736274ff0100770200000001f74e3dc9917b0636bfcf1b430531b9c1acc52b971f4b8490a48d13cd0dcf28810000000000fdffffff02353e0000000000001976a9143f308f42364be05715b19ae3077bbbca37eb3c6788acb6590000000000001976a91463ba82d24cc6929cefed39129f0196b64689cb8188ac7fc90d00000100bf02000000015e3165e2d7264adf29793e7cbe8905685c303fe5d043daefc0163f53ebe14a72000000006a473044022014bb64523f91195b0b8a1e0e2adfdee572d4458f88da921e17309a49224b043d022022b4dfb5090507342798f5db1caee17553e07863e5ed479c1777cdff60dcbcba012102a028293d325699e5b7ab175f5433e9e0b690ee3d2f5e8c037e22a6681e199b65fdffffff01cd980000000000001976a914857466f3af8c6221f3ae2e612949ae27e35e332d88ac7fc90d00220603159a15724b33392d0c3a536d00050889657fec4cf27c6303ce97a253171e880e189c0e47922c0000800000008000000080000000000000000000220202a1ddf8e2a12bc231e0a2596d981ecfe31bbc9fecb7781f3332cffc05f1f70a7a189c0e47922c000080000000800000008001000000000000000000'
# show the PSBT in hex format
puts hex
puts
# ---------
# Functions
# ---------
# Utility functions to help with decoding
def read_compact_size(buffer)
# read the first byte
byte = buffer.read(1)
# convert byte to integer value
int = byte.unpack("C")[0] # C = integer [8 bit] (unsigned)
# get the value held by the compact size field
if (int <= 0xFC)
value = int # this byte
elsif (int == 0xFD)
value = buffer.read(2).unpack("v")[0] # next 2 bytes
elsif (int == 0xFE)
value = buffer.read(4).unpack("V")[0] # next 4 bytes
elsif (int == 0xFF)
value = buffer.read(8).unpack("Q")[0] # next 8 bytes
end
# return value
return value
end
# ---------
# Key Types
# ---------
# An array of all the key types, useful for displaying the name of the key type
types = {
'global' => {
0 => 'PSBT_GLOBAL_UNSIGNED_TX',
1 => 'PSBT_GLOBAL_XPUB',
2 => 'PSBT_GLOBAL_TX_VERSION',
3 => 'PSBT_GLOBAL_FALLBACK_LOCKTIME',
4 => 'PSBT_GLOBAL_INPUT_COUNT',
5 => 'PSBT_GLOBAL_OUTPUT_COUNT',
6 => 'PSBT_GLOBAL_TX_MODIFIABLE',
7 => 'PSBT_GLOBAL_SP_ECDH_SHARE',
8 => 'PSBT_GLOBAL_SP_DLEQ',
251 => 'PSBT_GLOBAL_VERSION',
252 => 'PSBT_GLOBAL_PROPRIETARY'
},
'input' => {
0 => 'PSBT_IN_NON_WITNESS_UTXO',
1 => 'PSBT_IN_WITNESS_UTXO',
2 => 'PSBT_IN_PARTIAL_SIG',
3 => 'PSBT_IN_SIGHASH_TYPE',
4 => 'PSBT_IN_REDEEM_SCRIPT',
5 => 'PSBT_IN_WITNESS_SCRIPT',
6 => 'PSBT_IN_BIP32_DERIVATION',
7 => 'PSBT_IN_FINAL_SCRIPTSIG',
8 => 'PSBT_IN_FINAL_SCRIPTWITNESS',
9 => 'PSBT_IN_POR_COMMITMENT',
10 => 'PSBT_IN_RIPEMD160',
11 => 'PSBT_IN_SHA256',
12 => 'PSBT_IN_HASH160',
13 => 'PSBT_IN_HASH256',
14 => 'PSBT_IN_PREVIOUS_TXID',
15 => 'PSBT_IN_OUTPUT_INDEX',
16 => 'PSBT_IN_SEQUENCE',
17 => 'PSBT_IN_REQUIRED_TIME_LOCKTIME',
18 => 'PSBT_IN_REQUIRED_HEIGHT_LOCKTIME',
19 => 'PSBT_IN_TAP_KEY_SIG',
20 => 'PSBT_IN_TAP_SCRIPT_SIG',
21 => 'PSBT_IN_TAP_LEAF_SCRIPT',
22 => 'PSBT_IN_TAP_BIP32_DERIVATION',
23 => 'PSBT_IN_TAP_INTERNAL_KEY',
24 => 'PSBT_IN_TAP_MERKLE_ROOT',
26 => 'PSBT_IN_MUSIG2_PARTICIPANT_PUBKEYS',
27 => 'PSBT_IN_MUSIG2_PUB_NONCE',
28 => 'PSBT_IN_MUSIG2_PARTIAL_SIG',
29 => 'PSBT_IN_SP_ECDH_SHARE',
30 => 'PSBT_IN_SP_DLEQ',
252 => 'PSBT_IN_PROPRIETARY'
},
'output' => {
0 => 'PSBT_OUT_REDEEM_SCRIPT',
1 => 'WPSBT_OUT_WITNESS_SCRIPT',
2 => 'PSBT_OUT_BIP32_DERIVATION',
3 => 'PSBT_OUT_AMOUNT',
4 => 'PSBT_OUT_SCRIPT',
5 => 'PSBT_OUT_TAP_INTERNAL_KEY',
6 => 'PSBT_OUT_TAP_TREE',
7 => 'PSBT_OUT_TAP_BIP32_DERIVATION',
8 => 'PSBT_OUT_MUSIG2_PARTICIPANT_PUBKEYS',
9 => 'PSBT_OUT_SP_V0_INFO',
10 => 'PSBT_OUT_SP_V0_LABEL',
53 => 'PSBT_OUT_DNSSEC_PROOF',
252 => 'PSBT_OUT_PROPRIETARY'
}
}
# ------
# Decode
# ------
# Decode the PSBT
# these variables will be updated when reading through the global map, and are required to be able to read the following input map and output map
inputcount = 0
outputcount = 0
# convert psbt hex string to bytes
bytes = [hex].pack("H*")
# use StringIO to read through the bytes as we go
require "stringio"
buffer = StringIO.new(bytes)
# Header
magic_bytes = buffer.read(4)
separator = buffer.read(1)
# Global Map
puts "GLOBAL MAP:"
puts
while true do
# keylen
keylen = read_compact_size(buffer)
# if keylen is 0x00, we have hit the separator byte and are at the end of the global map
if (keylen == 0)
break
end
# use keylen to read keytype and keydata
keytype_and_keydata = StringIO.new(buffer.read(keylen)) # create another StringIO from the buffer so we can read bytes from it
# read the keytype
keytype = read_compact_size(keytype_and_keydata)
# read the keydata, which is the all the bytes left after the keytype
keydata = keytype_and_keydata.read
# valuelen
valuelen = read_compact_size(buffer)
# valuedata
valuedata = buffer.read(valuelen)
# if keytype = 0x00 (TX), extract the inputcount and outputcount from the raw transaction data given
if (keytype == 0)
# convert raw transaction data to StringIO so we can read bytes from it
tx = StringIO.new(valuedata)
version = tx.read(4) # ignore
# input count field
inputcount = read_compact_size(tx)
# skip past the inputs to get to the outputcount field
inputcount.times do
txid_vout = tx.read(36)
scriptsig_size = read_compact_size(tx)
scriptsig = tx.read(scriptsig_size)
sequence = tx.read(4)
end
# output count field
outputcount = read_compact_size(tx)
end
# if keytype = 0x04, the inputcount is explicitly provided
if (keytype == 4)
inputcount = read_compact_size(StringIO.new(valuedata)) # convert to StringIO to read compact size field
end
# if keytype = 0x05, the outputcount is explicitly provided
if (keytype == 5)
outputcount = read_compact_size(StringIO.new(valuedata)) # convert to StringIO to read compact size field
end
# show keypair data
puts " key: #{keytype} (#{types['global'][keytype]}) #{keydata.unpack('H*')[0]}"
puts " value: #{valuedata.unpack('H*')[0]}"
puts
end
# Input Map(s)
inputcount.times do |i|
# run through each input
puts "INPUT #{i} MAP:"
puts
# get the keypairs for each input
while true do
# keylen
keylen = read_compact_size(buffer)
# if keylen is 0x00, we have hit the separator byte and are at the end of the keypairs for this input
if (keylen == 0)
break
end
# use keylen to read keytype and keydata
keytype_and_keydata = StringIO.new(buffer.read(keylen)) # create another StringIO from the buffer so we can read bytes from it
# read the keytype
keytype = read_compact_size(keytype_and_keydata)
# read the keydata, which is the all the bytes left after the keytype
keydata = keytype_and_keydata.read
# valuelen
valuelen = read_compact_size(buffer)
# valuedata
valuedata = buffer.read(valuelen)
# show keypair data
puts " key: #{keytype} (#{types['input'][keytype]}) #{keydata.unpack('H*')[0]}"
puts " value: #{valuedata.unpack('H*')[0]}"
puts
end
end
# Output Map(s)
outputcount.times do |i|
# run through each output
puts "OUTPUT #{i} MAP:"
puts
# get the keypairs for each output
while true do
# keylen
keylen = read_compact_size(buffer)
# if keylen is 0x00, we have hit the separator byte and are at the end of the keypairs for this output
if (keylen == 0)
break
end
# use keylen to read keytype and keydata
keytype_and_keydata = StringIO.new(buffer.read(keylen)) # create another StringIO from the buffer so we can read bytes from it
# read the keytype
keytype = read_compact_size(keytype_and_keydata)
# read the keydata, which is the all the bytes left after the keytype
keydata = keytype_and_keydata.read
# valuelen
valuelen = read_compact_size(buffer)
# valuedata
valuedata = buffer.read(valuelen)
# show keypair data
puts " key: #{keytype} (#{types['output'][keytype]}) #{keydata.unpack('H*')[0]}"
puts " value: #{valuedata.unpack('H*')[0]}"
puts
end
end
Encode
# ---------
# Constants
# ---------
# Define constants for global key types
PSBT_GLOBAL_UNSIGNED_TX = 0x00
PSBT_GLOBAL_XPUB = 0x01
PSBT_GLOBAL_TX_VERSION = 0x02
PSBT_GLOBAL_FALLBACK_LOCKTIME = 0x03
PSBT_GLOBAL_INPUT_COUNT = 0x04
PSBT_GLOBAL_OUTPUT_COUNT = 0x05
PSBT_GLOBAL_TX_MODIFIABLE = 0x06
PSBT_GLOBAL_SP_ECDH_SHARE = 0x07
PSBT_GLOBAL_SP_DLEQ = 0x08
PSBT_GLOBAL_VERSION = 0xFB
PSBT_GLOBAL_PROPRIETARY = 0xFC
# Define constants for input key types
PSBT_IN_NON_WITNESS_UTXO = 0x00
PSBT_IN_WITNESS_UTXO = 0x01
PSBT_IN_PARTIAL_SIG = 0x02
PSBT_IN_SIGHASH_TYPE = 0x03
PSBT_IN_REDEEM_SCRIPT = 0x04
PSBT_IN_WITNESS_SCRIPT = 0x05
PSBT_IN_BIP32_DERIVATION = 0x06
PSBT_IN_FINAL_SCRIPTSIG = 0x07
PSBT_IN_FINAL_SCRIPTWITNESS = 0x08
PSBT_IN_POR_COMMITMENT = 0x09
PSBT_IN_RIPEMD160 = 0x0A
PSBT_IN_SHA256 = 0x0B
PSBT_IN_HASH160 = 0x0C
PSBT_IN_HASH256 = 0x0D
PSBT_IN_PREVIOUS_TXID = 0x0E
PSBT_IN_OUTPUT_INDEX = 0x0F
PSBT_IN_SEQUENCE = 0x10
PSBT_IN_REQUIRED_TIME_LOCKTIME = 0x11
PSBT_IN_REQUIRED_HEIGHT_LOCKTIME = 0x12
PSBT_IN_TAP_KEY_SIG = 0x13
PSBT_IN_TAP_SCRIPT_SIG = 0x14
PSBT_IN_TAP_LEAF_SCRIPT = 0x15
PSBT_IN_TAP_BIP32_DERIVATION = 0x16
PSBT_IN_TAP_INTERNAL_KEY = 0x17
PSBT_IN_TAP_MERKLE_ROOT = 0x18
PSBT_IN_MUSIG2_PARTICIPANT_PUBKEYS = 0x1A
PSBT_IN_MUSIG2_PUB_NONCE = 0x1B
PSBT_IN_MUSIG2_PARTIAL_SIG = 0x1C
PSBT_IN_SP_ECDH_SHARE = 0x1D
PSBT_IN_SP_DLEQ = 0x1E
PSBT_IN_PROPRIETARY = 0xFC
# Define constants for output key types
PSBT_OUT_REDEEM_SCRIPT = 0x00
PSBT_OUT_WITNESS_SCRIPT = 0x01
PSBT_OUT_BIP32_DERIVATION = 0x02
PSBT_OUT_AMOUNT = 0x03
PSBT_OUT_SCRIPT = 0x04
PSBT_OUT_TAP_INTERNAL_KEY = 0x05
PSBT_OUT_TAP_TREE = 0x06
PSBT_OUT_TAP_BIP32_DERIVATION = 0x07
PSBT_OUT_MUSIG2_PARTICIPANT_PUBKEYS = 0x08
PSBT_OUT_SP_V0_INFO = 0x09
PSBT_OUT_SP_V0_LABEL = 0x0A
PSBT_OUT_DNSSEC_PROOF = 0x35
PSBT_OUT_PROPRIETARY = 0xFC
# ---------
# Functions
# ---------
# Utility functions to help with decoding
# Convert an integer to a compact size field
def compact_size(i)
if ( i <= 252) then compactsize = [i].pack("C").unpack("H*")[0]
elsif (i > 252 && i <= 65535) then compactsize = 'fd' + [i].pack("S<").unpack("H*")[0]
elsif (i > 65535 && i <= 4294967295) then compactsize = 'fe' + [i].pack("L<").unpack("H*")[0]
elsif (i > 4294967295 && i <= 18446744073709551615) then compactsize = 'ff' + [i].pack("Q<").unpack("H*")[0]
end
return compactsize
end
# ----
# PSBT
# ----
# Construct an array of maps and key-value pairs to convert to a hex PSBT
psbt = {
global: {
0 => [
{
key: {type: PSBT_GLOBAL_UNSIGNED_TX, data: ''},
value: '0200000001f74e3dc9917b0636bfcf1b430531b9c1acc52b971f4b8490a48d13cd0dcf28810000000000fdffffff02353e0000000000001976a9143f308f42364be05715b19ae3077bbbca37eb3c6788acb6590000000000001976a91463ba82d24cc6929cefed39129f0196b64689cb8188ac7fc90d00', # 1 input, 1 output
},
]
},
inputs: {
0 => [
{
key: {type: PSBT_IN_NON_WITNESS_UTXO, data: ''},
value: '02000000015e3165e2d7264adf29793e7cbe8905685c303fe5d043daefc0163f53ebe14a72000000006a473044022014bb64523f91195b0b8a1e0e2adfdee572d4458f88da921e17309a49224b043d022022b4dfb5090507342798f5db1caee17553e07863e5ed479c1777cdff60dcbcba012102a028293d325699e5b7ab175f5433e9e0b690ee3d2f5e8c037e22a6681e199b65fdffffff01cd980000000000001976a914857466f3af8c6221f3ae2e612949ae27e35e332d88ac7fc90d00',
},
{
key: {type: PSBT_IN_BIP32_DERIVATION, data: '03159a15724b33392d0c3a536d00050889657fec4cf27c6303ce97a253171e880e'},
value: '9c0e47922c00008000000080000000800000000000000000',
},
],
},
outputs: {
0 => [
{
key: {type: PSBT_OUT_BIP32_DERIVATION, data: '02a1ddf8e2a12bc231e0a2596d981ecfe31bbc9fecb7781f3332cffc05f1f70a7a'},
value: '9c0e47922c00008000000080000000800100000000000000',
},
],
1 => [], # use empty array if an input/output map is expected but no key pairs are needed for it
},
}
# ------
# Encode
# ------
# buffer
hex = ""
# header
hex += "70736274ff" # magic bytes + separator
# run through each map section (global, input, output)
psbt.each do |mapname, maplist|
# run through the individual maps in each section (one for global, can be multiple input and output maps)
maplist.each do |map_index, map|
# run through all the keypairs in a map
map.each do |keypair|
# extract the fields from the array
keytype = keypair[:key][:type] # integer (will need to convert this to a compact size field)
keydata = keypair[:key][:data] # bytes
valuedata = keypair[:value] # bytes
# encode key
keytype = compact_size(keytype)
keydata = keydata
keylen = compact_size((keytype.length / 2) + (keydata.length / 2)) # length of keytype+keydata in bytes
# note: working with hex _strings_, so need to divide their length by 2 to get their length in _bytes_
# note: ideally you should be working with the data in raw bytes rather than hex strings
# encode value
valuelen = compact_size(valuedata.length / 2) # length of valuedata in bytes
valuedata = valuedata
# add key+value to buffer
hex += keylen + keytype + keydata + valuelen + valuedata
end # end of keypairs
# add separator at end of each map
hex += '00'
end # end of map
end
# ------
# Result
# ------
# show original data array
require 'json'
puts JSON.pretty_generate(psbt) # encode to json to pretty print the original array
puts
# show PSBT in hex
puts "hex:"
puts hex
puts
# show PSBT in base64
require 'base64'
base64 = Base64.encode64([hex].pack('H*')) # need to convert hex string to raw bytes before converting to base64
puts "base64:"
puts base64
The only tricky part about decoding PSBTs is making sure you grab the correct number of inputs and outputs from the global map (via the PSBT_GLOBAL_UNSIGNED_TX
, or the PSBT_GLOBAL_INPUT_COUNT
and PSBT_GLOBAL_OUTPUT_COUNT
keys). This is needed so that you can correctly determine whether the upcoming maps are either input or output maps.
Make sure you interpret the keytype
as a compact size field instead of a single byte. It's usually only 1-byte, but it's technically a compact size field that can expand to include more key types in the future. So you want to account for that.
Key Types
Each map within a PSBT uses a variety of key types
to store data.
These key types
are represented by integers, and indicate the type of data stored in the key data
and value data
fields for the key-value pair.
Below is a list of all the different key types available for each map, along with descriptions of the data stored.
The keys within each map do not need to be in numerical order. However, it's useful to put them in numerical order if you can to help compare the structure of PSBTs as they get updated during usage.
The key type
integers are specific to a map type. Different maps may use the same integers, but they represent different key types depending on which map you're in. Therefore, you cannot determine the exact key type from the integer alone, as you also need to know the map you're using it in.
The descriptions below are taken directly from BIP 174
Global Map (11)
-
00x00Unsigned TransactionPSBT_GLOBAL_UNSIGNED_TXv0: Requiredv2: Disallowed
Key Data empty
Value Data <bytes transaction>
The transaction in network serialization. The scriptSigs and witnesses for each input must be empty. The transaction must be in the old serialization format (without witnesses).
-
10x01Extended Public KeyPSBT_GLOBAL_XPUB
Key Data <bytes xpub>
The 78 byte serialized extended public key as defined by BIP 32. Extended public keys are those that can be used to derive public keys used in the inputs and outputs of this transaction. It should be the public key at the highest hardened derivation index so that the unhardened child keys used in the transaction can be derived.
Value Data <4 byte fingerprint> <32-bit little endian uint path element>*
The master key fingerprint as defined by BIP 32 concatenated with the derivation path of the public key. The derivation path is represented as 32-bit little endian unsigned integer indexes concatenated with each other. The number of 32 bit unsigned integer indexes must match the depth provided in the extended public key.
-
20x02Transaction VersionPSBT_GLOBAL_TX_VERSIONv0: Disallowedv2: Required
Key Data empty
Value Data <32-bit little endian int version>
The 32-bit little endian signed integer representing the version number of the transaction being created. Note that this is not the same as the PSBT version number specified by the
PSBT_GLOBAL_VERSION
field. -
30x03Fallback LocktimePSBT_GLOBAL_FALLBACK_LOCKTIMEv0: Disallowed
Key Data empty
Value Data <32-bit little endian uint locktime>
The 32-bit little endian unsigned integer representing the transaction locktime to use if no inputs specify a required locktime.
-
40x04Input CountPSBT_GLOBAL_INPUT_COUNTv0: Disallowedv2: Required
Key Data empty
Value Data <compact size uint input count>
Compact size unsigned integer representing the number of inputs in this PSBT.
-
50x05Output CountPSBT_GLOBAL_OUTPUT_COUNTv0: Disallowedv2: Required
Key Data empty
Value Data <compact size uint input count>
Compact size unsigned integer representing the number of outputs in this PSBT.
-
60x06Transaction Modifiable FlagsPSBT_GLOBAL_TX_MODIFIABLEv0: Disallowed
Key Data empty
Value Data <8-bit uint flags>
An 8 bit little endian unsigned integer as a bitfield for various transaction modification flags. Bit 0 is the Inputs Modifiable Flag and indicates whether inputs can be modified. Bit 1 is the Outputs Modifiable Flag and indicates whether outputs can be modified. Bit 2 is the Has SIGHASH_SINGLE flag and indicates whether the transaction has a SIGHASH_SINGLE signature who's input and output pairing must be preserved. Bit 2 essentially indicates that the Constructor must iterate the inputs to determine whether and how to add an input.
-
70x07Silent Payment Global ECDH SharePSBT_GLOBAL_SP_ECDH_SHAREv0: Disallowed
Key Data <33 byte scan key>
The scan key that this ECDH share is for.
Value Data <33 byte share>
An ECDH share for a scan key. The ECDH shared is computed with
a * B_scan
, wherea
is the sum of all private keys of all eligible inputs, andB_scan
is the scan key of a recipient. -
80x08Silent Payment Global DLEQ ProofPSBT_GLOBAL_SP_DLEQv0: Disallowed
Key Data <33 byte scan key>
The scan key that this proof covers.
Value Data <64-byte proof>
A BIP 374 DLEQ proof computed for the matching ECDH share.
-
2510xFBPSBT Version NumberPSBT_GLOBAL_VERSION
Key Data empty
Value Data <32-bit little endian uint version>
The 32-bit little endian unsigned integer representing the version number of this PSBT. If omitted, the version number is 0.
-
2520xFCProprietary Use TypePSBT_GLOBAL_PROPRIETARY
Key Data <compact size uint identifier length> <bytes identifier> <compact size uint subtype> <bytes subkeydata>
Compact size unsigned integer of the length of the identifier, followed by identifier prefix, followed by a compact size unsigned integer subtype, followed by the key data itself.
Value Data <bytes data>
Any value data as defined by the proprietary type user.
Input Map (31)
-
00x00Non-Witness UTXOPSBT_IN_NON_WITNESS_UTXO
Key Data empty
Value Data <bytes transaction>
The transaction in network serialization format the current input spends from. This should be present for inputs that spend non-segwit outputs and can be present for inputs that spend segwit outputs. An input can have both
PSBT_IN_NON_WITNESS_UTXO
andPSBT_IN_WITNESS_UTXO
. -
10x01Witness UTXOPSBT_IN_WITNESS_UTXO
Key Data empty
Value Data <64-bit little endian int amount> <compact size uint scriptPubKeylen> <bytes scriptPubKey>
The entire transaction output in network serialization which the current input spends from. This should only be present for inputs which spend segwit outputs, including P2SH embedded ones. An input can have both
PSBT_IN_NON_WITNESS_UTXO
andPSBT_IN_WITNESS_UTXO
-
20x02Partial SignaturePSBT_IN_PARTIAL_SIG
Key Data <bytes pubkey>
The public key which corresponds to this signature.
Value Data <bytes signature>
The signature as would be pushed to the stack from a scriptSig or witness. The signature should be a valid ECDSA signature corresponding to the pubkey that would return true when verified and not a value that would return false or be invalid otherwise (such as a NULLDUMMY).
-
30x03Sighash TypePSBT_IN_SIGHASH_TYPE
Key Data empty
Value Data <32-bit little endian uint sighash type>
The 32-bit unsigned integer specifying the sighash type to be used for this input. Signatures for this input must use the sighash type, finalizers must fail to finalize inputs which have signatures that do not match the specified sighash type. Signers who cannot produce signatures with the sighash type must not provide a signature.
-
40x04Redeem ScriptPSBT_IN_REDEEM_SCRIPT
Key Data empty
Value Data <bytes redeemScript>
The redeemScript for this input if it has one.
-
50x05Witness ScriptPSBT_IN_WITNESS_SCRIPT
Key Data empty
Value Data <bytes witnessScript>
The witnessScript for this input if it has one.
-
60x06BIP 32 Derivation PathPSBT_IN_BIP32_DERIVATION
Key Data <bytes pubkey>
The public key
Value Data <4 byte fingerprint> <32-bit little endian uint path element>*
The master key fingerprint as defined by BIP 32 concatenated with the derivation path of the public key. The derivation path is represented as 32 bit unsigned integer indexes concatenated with each other. Public keys are those that will be needed to sign this input.
-
70x07Finalized scriptSigPSBT_IN_FINAL_SCRIPTSIG
Key Data empty
Value Data <bytes scriptSig>
The Finalized scriptSig contains a fully constructed scriptSig with signatures and any other scripts necessary for the input to pass validation.
-
80x08Finalized scriptWitnessPSBT_IN_FINAL_SCRIPTWITNESS
Key Data empty
Value Data <bytes scriptWitness>
The Finalized scriptWitness contains a fully constructed scriptWitness with signatures and any other scripts necessary for the input to pass validation.
-
90x09Proof-of-reserves commitmentPSBT_IN_POR_COMMITMENT
Key Data empty
Value Data <bytes porCommitment>
The UTF-8 encoded commitment message string for the proof-of-reserves. See BIP 127 for more information.
-
100x0ARIPEMD160 preimagePSBT_IN_RIPEMD160
Key Data <20-byte hash>
The resulting hash of the preimage
Value Data <bytes preimage>
The hash preimage, encoded as a byte vector, which must equal the key when run through the RIPEMD160 algorithm
-
110x0BSHA256 preimagePSBT_IN_SHA256
Key Data <32-byte hash>
The resulting hash of the preimage
Value Data <bytes preimage>
The hash preimage, encoded as a byte vector, which must equal the key when run through the SHA256 algorithm
-
120x0CHASH160 preimagePSBT_IN_HASH160
Key Data <20-byte hash>
The resulting hash of the preimage
Value Data <bytes preimage>
The hash preimage, encoded as a byte vector, which must equal the key when run through the SHA256 algorithm followed by the RIPEMD160 algorithm (HASH160)
-
130x0DHASH256 preimagePSBT_IN_HASH256
Key Data <32-byte hash>
The resulting hash of the preimage
Value Data <bytes preimage>
The hash preimage, encoded as a byte vector, which must equal the key when run through the SHA256 algorithm twice (HASH256)
-
140x0EPrevious TXIDPSBT_IN_PREVIOUS_TXIDv0: Disallowedv2: Required
Key Data empty
Value Data <32 byte txid>
32 byte txid of the previous transaction whose output at
PSBT_IN_OUTPUT_INDEX
is being spent. -
150x0FSpent Output IndexPSBT_IN_OUTPUT_INDEXv0: Disallowedv2: Required
Key Data empty
Value Data <32-bit little endian uint index>
32 bit little endian integer representing the index of the output being spent in the transaction with the txid of
PSBT_IN_PREVIOUS_TXID
. -
160x10Sequence NumberPSBT_IN_SEQUENCEv0: Disallowed
Key Data empty
Value Data <32-bit little endian uint sequence>
The 32 bit unsigned little endian integer for the sequence number of this input. If omitted, the sequence number is assumed to be the final sequence number (0xffffffff).
-
170x11Required Time-based LocktimePSBT_IN_REQUIRED_TIME_LOCKTIMEv0: Disallowed
Key Data empty
Value Data <32-bit little endian uint locktime>
32 bit unsigned little endian integer greater than or equal to 500000000 representing the minimum Unix timestamp that this input requires to be set as the transaction's lock time.
-
180x12Required Height-based LocktimePSBT_IN_REQUIRED_HEIGHT_LOCKTIMEv0: Disallowed
Key Data empty
Value Data <32-bit uint locktime>
32 bit unsigned little endian integer less than 500000000 representing the minimum block height that this input requires to be set as the transaction's lock time.
-
190x13Taproot Key Spend SignaturePSBT_IN_TAP_KEY_SIG
Key Data empty
Value Data <64 or 65 byte signature>
The 64 or 65 byte Schnorr signature for key path spending a Taproot output. Finalizers should remove this field after
PSBT_IN_FINAL_SCRIPTWITNESS
is constructed. -
200x14Taproot Script Spend SignaturePSBT_IN_TAP_SCRIPT_SIG
Key Data <32 byte xonlypubkey> <leafhash>
A 32 byte X-only public key involved in a leaf script concatenated with the 32 byte hash of the leaf it is part of.
Value Data <64 or 65 byte signature>
The 64 or 65 byte Schnorr signature for this pubkey and leaf combination. Finalizers should remove this field after
PSBT_IN_FINAL_SCRIPTWITNESS
is constructed. -
210x15Taproot Leaf ScriptPSBT_IN_TAP_LEAF_SCRIPT
Key Data <bytes control block>
The control block for this leaf as specified in BIP 341. The control block contains the merkle tree path to this leaf.
Value Data <bytes script> <8-bit uint leaf version>
The script for this leaf as would be provided in the witness stack followed by the single byte leaf version. Note that the leaves included in this field should be those that the signers of this input are expected to be able to sign for. Finalizers should remove this field after
PSBT_IN_FINAL_SCRIPTWITNESS
is constructed. -
220x16Taproot Key BIP 32 Derivation PathPSBT_IN_TAP_BIP32_DERIVATION
Key Data <32 byte xonlypubkey>
A 32 byte X-only public key involved in this input. It may be the output key, the internal key, or a key present in a leaf script.
Value Data <compact size uint number of hashes> <32 byte leaf hash>* <4 byte fingerprint> <32-bit little endian uint path element>*
A compact size unsigned integer representing the number of leaf hashes, followed by a list of leaf hashes, followed by the 4 byte master key fingerprint concatenated with the derivation path of the public key. The derivation path is represented as 32-bit little endian unsigned integer indexes concatenated with each other. Public keys are those needed to spend this output. The leaf hashes are of the leaves which involve this public key. The internal key does not have leaf hashes, so can be indicated with a hashes len of 0. Finalizers should remove this field after
PSBT_IN_FINAL_SCRIPTWITNESS
is constructed. -
230x17Taproot Internal KeyPSBT_IN_TAP_INTERNAL_KEY
Key Data empty
Value Data <32 byte xonlypubkey>
The X-only pubkey used as the internal key in this output. Finalizers should remove this field after
PSBT_IN_FINAL_SCRIPTWITNESS
is constructed. -
240x18Taproot Merkle RootPSBT_IN_TAP_MERKLE_ROOT
Key Data empty
Value Data <32-byte hash>
The 32 byte Merkle root hash. Finalizers should remove this field after
PSBT_IN_FINAL_SCRIPTWITNESS
is constructed. -
260x1AMuSig2 Participant Public KeysPSBT_IN_MUSIG2_PARTICIPANT_PUBKEYS
Key Data <33 byte plain aggregate pubkey>
The MuSig2 aggregate plain public key from the KeyAgg algorithm. This key may or may not be in the script directly (as x-only). It may instead be a parent public key from which the public keys in the script were derived.
Value Data <33 byte compressed pubkey>*
A list of the compressed public keys of the participants in the MuSig2 aggregate key in the order required for aggregation. If sorting was done, then the keys must be in the sorted order.
-
270x1BMuSig2 Public NoncePSBT_IN_MUSIG2_PUB_NONCE
Key Data <33 byte compressed pubkey> <33 byte plain pubkey> <32 byte hash or omitted>
The compressed public key of the participant providing this nonce, followed by the plain public key the participant is providing the nonce for, followed by the BIP 341 tapleaf hash of the Taproot leaf script that will be signed. If the aggregate key is the taproot internal key or the taproot output key, then the tapleaf hash must be omitted. The plain public key must be the key found in the script and not the aggregate public key that it was derived from, if it was derived from an aggregate key.
Value Data <66 byte public nonce>
The public nonce produced by the NonceGen algorithm.
-
280x1CMuSig2 Participant Partial SignaturePSBT_IN_MUSIG2_PARTIAL_SIG
Key Data <33 byte compressed pubkey> <33 byte plain pubkey> <32 byte hash or omitted>
The compressed public key of the participant providing this partial signature, followed by the plain public key the participant is providing the signature for, followed by the BIP 341 tapleaf hash of the Taproot leaf script that will be signed. If the aggregate key is the taproot internal key or the taproot output key, then the tapleaf hash must be omitted. Note that the plain public key must be the key found in the script and not the aggregate public key that it was derived from, if it was derived from an aggregate key.
Value Data <32 byte partial signature>
The partial signature produced by the Sign algorithm.
-
290x1DSilent Payment Input ECDH SharePSBT_IN_SP_ECDH_SHAREv0: Disallowed
Key Data <33 byte scan key>
The scan key that this ECDH share is for.
Value Data <33 byte share>
An ECDH share for a scan key. The ECDH shared is computed with a * B_scan, where a is the private key of the corresponding prevout public key, and B_scan is the scan key of a recipient.
-
300x1ESilent Payment Input DLEQ ProofPSBT_IN_SP_DLEQv0: Disallowed
Key Data <33 byte scan key>
The scan key that this proof covers.
Value Data <64-byte proof>
A BIP 374 DLEQ proof computed for the matching ECDH share.
-
2520xFCProprietary Use TypePSBT_IN_PROPRIETARY
Key Data <compact size uint identifier length> <bytes identifier> <compact size uint subtype> <bytes subkeydata>
Compact size unsigned integer of the length of the identifier, followed by identifier prefix, followed by a compact size unsigned integer subtype, followed by the key data itself.
Value Data <bytes data>
Any value data as defined by the proprietary type user.
Output Map (13)
-
00x00Redeem ScriptPSBT_OUT_REDEEM_SCRIPT
Key Data empty
Value Data <bytes redeemScript>
The redeemScript for this output if it has one.
-
10x01Witness ScriptPSBT_OUT_WITNESS_SCRIPT
Key Data empty
Value Data <bytes witnessScript>
The witnessScript for this output if it has one.
-
20x02BIP 32 Derivation PathPSBT_OUT_BIP32_DERIVATION
Key Data <bytes public key>
The public key
Value Data <4 byte fingerprint> <32-bit little endian uint path element>*
The master key fingerprint concatenated with the derivation path of the public key. The derivation path is represented as 32-bit little endian unsigned integer indexes concatenated with each other. Public keys are those needed to spend this output.
-
30x03Output AmountPSBT_OUT_AMOUNTv0: Disallowedv2: Required
Key Data empty
Value Data <64-bit int amount>
64 bit signed little endian integer representing the output's amount in satoshis.
-
40x04Output ScriptPSBT_OUT_SCRIPTv0: Disallowed
Key Data empty
Value Data <bytes script>
The script for this output, also known as the scriptPubKey. Must be omitted in PSBTv0. Must be provided in PSBTv2 if not sending to a BIP 352 silent payment address, otherwise may be omitted.
-
50x05Taproot Internal KeyPSBT_OUT_TAP_INTERNAL_KEY
Key Data empty
Value Data <32 byte xonlypubkey>
The X-only pubkey used as the internal key in this output.
-
60x06Taproot TreePSBT_OUT_TAP_TREE
Key Data empty
Value Data {<8-bit uint depth> <8-bit uint leaf version> <compact size uint scriptlen> <bytes script>}*
One or more tuples representing the depth, leaf version, and script for a leaf in the Taproot tree, allowing the entire tree to be reconstructed. The tuples must be in depth first search order so that the tree is correctly reconstructed. Each tuple is an 8-bit unsigned integer representing the depth in the Taproot tree for this script, an 8-bit unsigned integer representing the leaf version, the length of the script as a compact size unsigned integer, and the script itself.
-
70x07Taproot Key BIP 32 Derivation PathPSBT_OUT_TAP_BIP32_DERIVATION
Key Data <32 byte xonlypubkey>
A 32 byte X-only public key involved in this output. It may be the output key, the internal key, or a key present in a leaf script.
Value Data <compact size uint number of hashes> <32 byte leaf hash>* <4 byte fingerprint> <32-bit little endian uint path element>*
A compact size unsigned integer representing the number of leaf hashes, followed by a list of leaf hashes, followed by the 4 byte master key fingerprint concatenated with the derivation path of the public key. The derivation path is represented as 32-bit little endian unsigned integer indexes concatenated with each other. Public keys are those needed to spend this output. The leaf hashes are of the leaves which involve this public key. The internal key does not have leaf hashes, so can be indicated with a hashes len of 0. Finalizers should remove this field after
PSBT_IN_FINAL_SCRIPTWITNESS
is constructed. -
80x08MuSig2 Participant Public KeysPSBT_OUT_MUSIG2_PARTICIPANT_PUBKEYS
Key Data <33 byte plain aggregate pubkey>
The MuSig2 aggregate plain public key from the KeyAgg algorithm. This key may or may not be in the script directly. It may instead be a parent public key from which the public keys in the script were derived.
Value Data <33 byte compressed pubkey>*
A list of the compressed public keys of the participants in the MuSig2 aggregate key in the order required for aggregation. If sorting was done, then the keys must be in the sorted order.
-
90x09Silent Payment DataPSBT_OUT_SP_V0_INFOv0: Disallowed
Key Data empty
Value Data <33 byte scan key> <33 byte spend key>
The scan and spend public keys from the silent payments address.
-
100x0ASilent Payment LabelPSBT_OUT_SP_V0_LABELv0: Disallowed
Key Data empty
Value Data <32-bit little endian uint label>
The label to use to compute the spend key of the silent payments address to verify change.
-
530x35BIP 353 DNSSEC proofPSBT_OUT_DNSSEC_PROOF
Key Data empty
Value Data <1-byte-length-prefixed BIP 353 human-readable name><RFC 9102-formatted AuthenticationChain DNSSEC Proof>
A BIP 353 human-readable name (without the ₿ prefix), prefixed by a 1-byte length. Followed by an RFC 9102 DNSSEC AuthenticationChain (i.e. a series of DNS Resource Records in no particular order) providing a DNSSEC proof to a BIP 353 DNS TXT record.
-
2520xFCProprietary Use TypePSBT_OUT_PROPRIETARY
Key Data <compact size uint identifier length> <bytes identifier> <compact size uint subtype> <bytes subkeydata>
Compact size unsigned integer of the length of the identifier, followed by identifier prefix, followed by a compact size unsigned integer subtype, followed by the key data itself.
Value Data <bytes data>
Any value data as defined by the proprietary type user.
Usage
How do you use PSBTs?
A PSBT gets processed through multiple steps before being converted into the final signed transaction.
You'd think that it would just be a 2-step process, like this:
- Create — Create the PSBT and add all the extra data needed for signing.
- Sign — Sign the inputs using the extra data and return the signed transaction.
However, these two steps can be broken down in to further steps (or "roles"), where each role has a specific job for updating the state of the PSBT:
- Creator — Create the initial PSBT with the unsigned transaction data.
- Constructor — An additional role for v2 PSBTs, used for adding inputs and outputs to the unsigned transaction data.
- Updater — Add the extra data required to sign the inputs.
- Signer — Sign the inputs using the extra data and add the raw signatures to the PSBT.
- Input Finalizer — Use the raw signatures to construct the complete unlocking code for the inputs.
- Transaction Extractor — Convert the PSBT into the raw signed transaction data ready to broadcast to the network.
There is also a helpful Combiner role, which can be used at any intermediate step:
- Combiner — Combine multiple PSBTs into one.
The above steps do not have to be executed in a single sequence. For example, you could Update/Sign/Finalize one input for a PSBT, then send it to someone else who can go back and Update/Sign/Finalize another input.
Most of the time a single wallet/entity will perform multiple roles at once. For example, when using a hardware device to sign a v0 PSBT, the watch-only wallet will be the Creator/Updater, and the hardware device will be the Signer/Input Finalizer/Transaction Extractor (the Combiner role is not needed).
Nonetheless, each individual role is specialized, and these roles allow you to split the work between multiple wallets/entities for maximum flexibility when processing PSBTs.
Below is a list of the roles for both version 0 and version 2 PSBTs. There are slight variations, but the overall process is generally the same.
Version 0

1. Creator
A Creator creates a new PSBT.
It constructs the initial unsigned transaction data, and stores it in the following key:
This unsigned transaction data should be a serialized transaction with empty ScriptSig and Witness fields.
- The
input map(s)
andoutput map(s)
must be empty. PSBT_GLOBAL_VERSION
can optionally be set to 0. However, it is 0 by default if not included.
2. Updater
An Updater adds the extra data needed to create signatures for the unsigned transaction data.
This almost always involves including the required data from the previous transaction(s) for each input via the following keys:
Optionally, the Updater can also specify a SIGHASH type for each input:
If an input is a P2SH or P2WSH, it should also include the custom script that was used (i.e. Redeem Script for a P2SH, or Witness Script for a P2WSH) to create the Script Hash placed in the ScriptPubKey on the output.
These original custom scripts are required to be able to unlock P2SH and P2WSH inputs.
These custom scripts should have been stored by the wallet that created the address for receiving payments.
Lastly, an Updater can also add useful information about the public keys (and derivation paths) used for the inputs and outputs.
The above keys help wallets to identify which inputs they can sign for, as well as which outputs are used for change. A wallet can identify both of these things without these keys, but including them helps a wallet to identify them faster.
Hardware wallets may rely on these keys to identify the inputs they can sign for, as they may not have the processing power to do so without them.
In summary, an Updater should add any extra data to the PSBT that it has access to. The main goal of an Updater is to add all the relevant data to the input maps so that a Signer has everything they need to sign the inputs.
A PSBT can be passed between multiple Updaters to get all the extra data needed for signing (e.g. if a single Updater does not have access to all the information required).
3. Signer
A Signer uses the data already provided in the PSBT to create signatures for the inputs.
The resulting signature is placed within the following key for each input map:
If an input requires multiple signatures (e.g. a P2MS style locking script), the input maps will end up with multiple PSBT_IN_PARTIAL_SIG
keys.
The PSBT_IN_PARTIAL_SIG
key data field contains the public key for the corresponding signature. This prevents duplicate keys if there are multiple signatures in a single input map.
Signers should sign as many inputs in the PSBT as they can, assuming they have the private keys to sign for them.
A PSBT can be passed between multiple Signers if multiple signatures are required from different people, with each adding PSBT_IN_PARTIAL_SIG
keys to the inputs as they go.
- A Signer should check the transaction to make sure they agree with it before adding their signature.
- A Signer can use the
PSBT_IN_BIP32_DERIVATION
keys to detect which inputs to sign, and thePSBT_OUT_BIP32_DERIVATION
keys to help detect the change outputs. - Detecting change outputs can be useful when displaying the transaction details to the wallet user before signing.
Checks
A Signer should perform checks before signing an input.
These checks depend on the data provided in the input map:
PSBT_IN_NON_WITNESS_UTXO
— The TXID of the data provided (previous transaction) must match the TXID specified in the prevout in the unsigned transactionPSBT_IN_WITNESS_UTXO
— No non-witness signature may be created if this key is provided.
PSBT_IN_REDEEM_SCRIPT
— The ScriptPubKey must be for that Redeem Script (i.e. matches the hash in the UTXO).PSBT_IN_WITNESS_SCRIPT
— the ScriptPubKey or the Redeem Script must be for that Witness Script (i.e. matches the hash in the UTXO or Redeem Script)
PSBT_IN_SIGHASH_TYPE
- If provided, the signer must check that the sighash is acceptable. If unacceptable, they must fail.
- If not provided, the signer should sign using SIGHASH_ALL, but may use any sighash type they wish.
4. Input Finalizer
An Input Finalizer takes the signatures from the PSBT_IN_PARTIAL_SIG
keys (added by the Signer(s)) and uses them to construct the complete unlocking code for each input.
The resulting unlocking scripts are placed within either of the following keys:
The key you use depends on whether the input is unlocked via the ScriptSig field (e.g. P2PK, P2PKH, P2SH) or the Witness field (e.g. P2WPKH, P2WSH, P2TR).
All other keys (except for PSBT_IN_NON_WITNESS_UTXO
and PSBT_IN_WITNESS_UTXO
) should be cleared from the input map after the unlocking code has been added.
The UTXO keys are kept so that the Transaction Extractor can verify that the final transaction is correct.
If an Input Finalizer does not yet have enough data to create the code to unlock an input (e.g. not enough signatures to unlock a multisig), the input map should not be updated.
5. Transaction Extractor
A Transaction Extractor converts the PSBT into the final signed transaction.
It takes the data from the PSBT_IN_FINAL_SCRIPTSIG
and PSBT_IN_FINAL_SCRIPTWITNESS
keys for each input, and inserts it into the raw unsigned transaction data in PSBT_GLOBAL_UNSIGNED_TX
.
The result is a serialized transaction that is ready to be broadcast to the network.
Every input map must have a PSBT_IN_FINAL_SCRIPTSIG
or PSBT_IN_FINAL_SCRIPTWITNESS
to be able to construct the final signed transaction.
Combiner
A Combiner merges multiple PSBTs into a single PSBT.
The resulting PSBT must contain all of the key-value pairs from each of the individual PSBTs. Any duplicates must be removed.
If there are key-value pairs with the same key but different values, the Combiner can choose which key-value pair to keep. However, the Combiner can refuse to combine the PSBTs if there are likely to be inconsistencies or conflicts in the resulting PSBT.
PSBTs should only be combined if they are for the same unsigned transaction. The unique identifier for a v0 PSBT is the unsigned transaction data in PSBT_GLOBAL_UNSIGNED_TX
.
Version 2
A v2 PSBT splits up the unsigned transaction data and stores in separate keys across all three maps.
This design change makes it easier to add and remove inputs from the PSBT before signing.
To accommodate for this change, a new role has been added (Constructor), and some of the existing roles have been modified slightly. But there's not a massive difference overall, and the general process for working with PSBTs remains the same.

1. Creator
A v2 Creator creates a new PSBT.
However, in v2, the Creator does not include the unsigned transaction data just yet. Instead, it simply adds the basic settings for the PSBT in the global map.
Firstly, the PSBT version number must be set to 2 to indicate that you're using PSBT v2:
Next, set the version number for the transaction, and set an optional fallback value for the transaction's locktime:
Lastly, the modifiable field should be set to allow for inputs and outputs to be added to the PSBT:
However, this last field can be omitted if the Creator is also a Constructor.
2. Constructor
A v2 Constructor adds the inputs and outputs to be included in the unsigned transaction data.
For each new input, the following keys must be added to the input's map:
These keys allow you to reference the outputs you want to use as inputs to the transaction.
In addition, each input can specify a locktime (if needed):
You can add one, both, or neither.
If you're adding an input with just one of these locktime key types, you must check that it does not conflict with the locktime key types on the other input maps. For example, if one input already has PSBT_IN_REQUIRED_TIME_LOCKTIME
only, you cannot add an input with PSBT_IN_REQUIRED_HEIGHT_LOCKTIME
only (and vice versa).
Furthermore, if the PSBT has already been signed, you cannot add an input that specifies a locktime that will change the entire transaction's final locktime value.
See the Locktime Calculation section in the Transaction Extractor role for details.
For each new output, the following keys must be added to the output's map:
After adding a new input or output to the PSBT, the count fields in the global map must be incremented:
Lastly, if no more inputs or outputs should be added to the transaction, this should be declared by updating or removing the modifiable key in the global map:
If the PSBT_GLOBAL_TX_MODIFIABLE
key has the Has SIGHASH_SINGLE flag (bit 2) set to True, you must iterate through the inputs and find the inputs which have signatures that use SIGHASH_SINGLE
. The same number of inputs and outputs must be added before those inputs and their corresponding outputs.
3. Updater
A v2 Updater works the same as a v0 Updater.
However, it can also set the sequence number for each input:
If this key is not set, the sequence number defaults to 0xFFFFFFFF
.
4. Signer
A v2 Signer is the same as a v0 Signer.
However, after signing an input, a v2 Signer should update the modifiable key to reflect the state of the PSBT:
This field must be updated based on the SIGHASH type the Signer has added for the input:
- If the signature does not use
SIGHASH_ANYONECANPAY
, the Input Modifiable flag (bit 0) must be set to False. - If the signature does not use
SIGHASH_NONE
, the Outputs Modifiable flag (bit 1) must be set to False. - If the signature uses
SIGHASH_SINGLE
, the Has SIGHASH_SINGLE flag (bit 2) must be set to True.
5. Input Finalizer
A v2 Input Finalizer works the same as a v0 Input Finalizer.
However, after adding the relevant PSBT_IN_FINAL_SCRIPTSIG
or PSBT_IN_FINAL_SCRIPTWITNESS
, the following v2 keys should not be removed from the input maps (if present):
PSBT_IN_PREVIOUS_TXID
PSBT_IN_OUTPUT_INDEX
PSBT_IN_SEQUENCE
PSBT_IN_REQUIRED_TIME_LOCKTIME
PSBT_IN_REQUIRED_HEIGHT_LOCKTIME
These keys are needed by the Transaction Extractor to construct the final signed transaction.
6. Transaction Extractor
A v2 Transaction Extractor converts the PSBT into a raw signed transaction that can be broadcast to the network.
However, in v2 this step is a little more involved as you first need to build the unsigned transaction data from the individual keys added to the PSBT by the v2 Creator and v2 Constructor.
You will also need to calculate the locktime field for the transaction by looking at the relevant locktime keys across the global map
and input map(s)
.
Lastly, the PSBT_IN_FINAL_SCRIPTSIG
and PSBT_IN_FINAL_SCRIPTWITNESS
fields (added by the Input Finalizer) are merged in to the unsigned transaction data to form the final signed transaction.
In v0, the complete unsigned transaction data is already prepared in the PSBT_GLOBAL_UNSIGNED_TX
. In v2, the unsigned transaction data needs to be built from the data stored in the individual keys across the global map
, input map(s)
, and output map(s)
.
Locktime Calculation
To calculate the locktime field for the transaction, you need to run through each input map to check the individual locktime values that may have been set for each input.
The basic algorithm is as follows:
- If one or more inputs has a specified locktime value, use the highest locktime value present across the inputs.*
- If no input has a specified locktime value, use the fallback value in the global map (
PSBT_GLOBAL_FALLBACK_LOCKTIME
).- If a fallback locktime is not set, use the default locktime value of 0 (i.e. no locktime).
- If no input has a specified locktime value, use the fallback value in the global map (
*For each input there are actually two different locktime key types that can be specified:
PSBT_IN_REQUIRED_TIME_LOCKTIME
— Specifies the desired transaction locktime in seconds (Unix Time).PSBT_IN_REQUIRED_HEIGHT_LOCKTIME
— Specifies the desired transaction locktime as a block height.
To determine which locktime type to use, you need to check which type(s) are supported across all of the inputs:
- If an input map specifies both types (by including both keys), then both types remain supported for the transaction.
- If an input map specifies neither type (by not including either key), then both types remain supported for the transaction.
- If an input map specifies one type only, then only that one type is supported for the transaction.
If both types are supported across all inputs (i.e. you have a choice between the two), you must use the highest value PSBT_IN_REQUIRED_HEIGHT_LOCKTIME
. This is because it takes precedence over PSBT_IN_REQUIRED_TIME_LOCKTIME
(even if it has a higher value overall).
However, if there is a conflict where one or more input maps support one locktime type only, and one or more inputs support the other locktime type only, the transaction locktime field cannot be calculated.
Ideally a Constructor will not add an input with a conflicting locktime key. But a Transaction Extractor still needs to check for conflicts when determining the final value for the transaction's locktime field.
Combiner
A v2 Combiner works the same as a v0 Combiner.
Format
What data format do PSBTs use?
There are two "official" formats for PSBTs:
- Binary (File) — Used for passing a PSBT around as a file (using the file extension
.psbt
). - Base64 (Text) — Used for copying/pasting, or storing a PSBT as text.
So if you export a PSBT from a wallet, you should either get a binary file or a base64 string. Either is fine, and they both represent the same underlying data, so you can convert directly between both formats:
# convert binary file to base64 string
base64 -w0 file.psbt
# note: -w0 prevents line breaks, which can cause problems when decoding
# convert base64 string to binary file
echo "base64psbtgoeshere" | base64 -d > file.psbt
But sometimes it's easier to inspect PSBTs in hex format, so it may be useful to convert a PSBT from a binary/base64 to hex format if needed:
# convert binary file to hex string (without formatting)
xxd -p file.psbt | tr -d '\n' && echo ''
# convert base64 string to hex string (without formatting)
echo "base64psbtgoeshere" | base64 -d | xxd -p | tr -d '\n' && echo ''
Hex Format. I've displayed PSBT data in hex format throughout this page, as that's generally easier to use when testing/debugging. But it's not technically a format that PSBTs should be passed around in, and most wallets won't support exporting/importing a PSBT in hex format.
History
When were PSBTs first introduced?
The original idea of having a standard format for exporting unsigned transactions was proposed by Pieter Wuille (who also came up with the name "Partially Signed Bitcoin Transactions"). The actual PSBT format was then designed and implemented by Ava Chow.
The first version (PSBTv0) was introduced in 2017:
- 17 Jul 2017 — BIP 174 published
- 03 Oct 2018 — Support for PSBTv0 added to Bitcoin Core v0.17.0
The second version (PSBTv2) was introduced in 2021:
- 28 Jan 2021 — BIP 370 published
- 23 Feb 2021 — Submitted pull request for adding PSBTv2 support to Bitcoin Core
This second version uses the same PSBT data structure as before, but introduces new key types and modifies some roles to allow for inputs and outputs to be added to the unsigned transaction data before signing. So it's basically a slightly more flexible version compared to PSBTv0.
Bitcoin Core does not yet offer functionality to support PSBTv2, but it is currently supported by other third-party wallets such as Sparrow Wallet and Coldcard.
There is no PSBTv1. The first version was called v0, and seeing as this was commonly referred to as the "first version", the second version was called v2 instead of v1 to help prevent confusion (except for the confusion about why there's no "v1", of course).
Summary
A PBST is just a transaction that includes all the extra data needed to sign it.
Most modern wallets understand the PSBT format, so if you're creating some software that can export/import unsigned transactions, you're going to want to get the hang of handling PSBTs.
They may seem intimidating at first due to all the available key types, but you don't need to understand all of them to take advantage of PSBTs — you only need the ones that are relevant to your software's needs. The rest are just there if you need them.
So if you're getting started with PSBTs, start by figuring out how to decode and encode them. From there, you can just use the keys you need for signing the specific input types that your wallet needs to handle.
Most wallets only implement the functionality to handle a subset of all the possible key types. For example, to process a v0 PSBT for signing a simple input type like P2PKH, you only need to work with the following key types:
PSBT_IN_BIP32_DERIVATION
(optional, but helpful)PSBT_OUT_BIP32_DERIVATION
(optional, but helpful)
So as always, just start with the basic functionality and go from there. You can figure out how to use the other key types as you go.
Have fun.
Resources
- A. Chow: Partially Signed Bitcoin Transactions
- PSBT Decoder
- PSBT Howto for Bitcoin Core
- Bitcoin Dev Mailing List Discussion: BIP 174 thoughts
- Bitcoin Optech — Partially signed bitcoin transactions
Thanks
- Thanks to Craig Raw (creator of Sparrow Wallet) for explaining the benefits of including the
BIP32_DERIVATION
keys in a PSBT.