btclib.psbt package

Submodules

btclib.psbt.silent_payments module

The BIP375 roles: sending a silent payment through a psbt.

https://github.com/bitcoin/bips/blob/master/bip-0375.mediawiki

btclib.psbt.psbt_in and btclib.psbt.psbt_out carry BIP375’s six fields; this is what the two roles BIP375 adds do with them, and it is a module rather than a function for the reason btclib.psbt.musig2 is: a role is a sequence of steps different parties take at different times.

Why it matters more than a signature does. A silent payment output script is derived, not signed: get it wrong and the transaction is still consensus-valid, so it confirms and the money is gone. The shares and the BIP374 proofs are what make that derivation checkable by somebody holding none of the keys – and the Transaction Extractor is where the check has to happen, being the last party before the bytes go on the wire.

The Signer’s side, in the order BIP375 puts it:

  • set_input_share writes an ECDH share and its proof for one input the Signer holds the key of; set_global_share writes the one pair that stands for every eligible input, which a Signer holding all the keys may do instead.

  • assert_shares_as_valid is what a Signer does with the shares it did not write: every proof verified against the key of the input it covers, or against the sum of them for a global one.

  • set_output_scripts computes what the recipients are paid, once every eligible input is covered, and clears the two modifiable flags – the scripts depend on the input set, so nothing may be added afterwards.

assert_as_valid is the Extractor’s, and it is the four checks BIP375’s own validator publishes, in its order: the fields, then the share coverage and the proofs, then which inputs are allowed to be there at all, then the output scripts recomputed and compared.

What a psbt input’s public key is, and why it needs its own reader. btclib.silent_payments.pub_key_from_input reads it off a signed input, from the witness or the scriptSig. An unsigned input has neither, and BIP375 says where to look instead: the Updater “should add a PSBT_IN_BIP32_DERIVATION for any p2wpkh, p2sh-p2wpkh, or p2pkh input so the public key is available for creating the ecdh_shared_secret when the private key is not known”. input_pub_key is that reader, and it answers None for an input BIP352 does not count – a taproot NUMS internal key, a p2sh wrapping anything but p2wpkh, a script type off the list.

The share is not the shared secret, which is the thing in BIP375 easiest to get wrong: a*B_scan carries no input hash, and BIP352’s shared secret is input_hash*a*B_scan. So the Extractor multiplies the share by the input hash, and the input hash needs the sum of the public keys of the eligible inputs – which is why reading those keys is the first thing here and not an aside.

The k of an output is BIP375’s own rule and not BIP352’s ordering: it counts per scan key, over the outputs in index order. That is what the BIP’s vectors and its reference validator do and not what its prose says, which asks for the codes to be sorted lexicographically; _ordered_sp_outputs is where the discrepancy is documented, and tests/psbt/silent_payments_test.py says which of the vectors pins it.

btclib.psbt.silent_payments.assert_as_valid(psbt: Psbt) None[source]

Raise unless the psbt satisfies BIP375, the roles included.

Psbt.assert_valid is the format; this is the protocol on top of it, and it is what a Transaction Extractor owes a silent payment before it hands the bytes over. The four checks in BIP375’s own order, each naming what failed: the fields, the shares and their proofs, which inputs may be present, and the output scripts.

A psbt with no silent payment output passes everything here, there being nothing to derive.

btclib.psbt.silent_payments.assert_eligibility_as_valid(psbt: Psbt) None[source]

Raise unless every input may be there at all, silent payments present.

BIP375’s third check, and the two rules are BIP352’s reasons in a psbt’s terms. An input spending a witness program above version 1 is one this protocol version has no derivation rule for, so BIP352 skips the whole transaction – which makes building one a way to pay an address nobody will scan. And a sighash type other than SIGHASH_ALL lets the inputs or the outputs change after the scripts were derived from them: BIP352 permits NONE and SINGLE, BIP375 does not, because here the scripts are computed from the number and the position of the codes.

btclib.psbt.silent_payments.assert_output_scripts_as_valid(psbt: Psbt) None[source]

Raise unless every silent payment script is the one derived.

BIP375’s fourth check and the Extractor’s reason to exist: this is the error a signature cannot catch, a wrong output script being consensus-valid. An output that carries no script yet is passed over – that is a psbt still being built – and one that carries a script without the shares to derive it is not.

btclib.psbt.silent_payments.assert_shares_as_valid(psbt: Psbt) None[source]

Raise unless every ECDH share the psbt carries is proved.

BIP375’s second check, and the one that makes a share worth reading: a proof is verified against the public key of what it covers – the sum of the eligible inputs’ keys for a global share, that one input’s key for a per-input one – so a share can be trusted by a party holding none of the private keys.

btclib.psbt.silent_payments.eligible_pub_keys(psbt: Psbt) dict[int, tuple[int, int]][source]

Return the public key of every input BIP352 counts, by index.

The index is kept because the per-input shares are filed per input: a coverage rule that answered “how many” rather than “which” could not name the input whose share is missing.

btclib.psbt.silent_payments.input_pub_key(psbt_in: PsbtIn) tuple[int, int] | None[source]

Return the public key of one psbt input, or None if it does not count.

A taproot input’s key is the output key the script_pub_key carries: it is what the recipient sums, script path or not, and the psbt need not say anything for it to be readable. Every other eligible kind keeps its key in the key data of PSBT_IN_BIP32_DERIVATION, which is what BIP375 asks an Updater to add for exactly this.

The lowest such key where there is more than one, rather than an arbitrary first: a dict’s order is the order the psbt happened to be parsed in, and an answer that depends on it is an answer two readers of one psbt could disagree about. An eligible input has one key anyway – p2pkh, p2wpkh and p2sh-p2wpkh each commit to a single hash – so this decides nothing that a correct psbt leaves open.

btclib.psbt.silent_payments.output_scripts(psbt: Psbt) dict[int, bytes][source]

Return the script every silent payment output should pay, by index.

An output whose scan key has no share is absent from the answer rather than raising: a psbt under construction is allowed to have one, which is the “in progress” half of BIP375’s own vectors, and what refuses the ones that are not allowed is assert_output_scripts_as_valid.

btclib.psbt.silent_payments.set_global_share(psbt: Psbt, prv_keys: Sequence[int | bytes | str | bytearray | memoryview | BIP32KeyData], aux: bytes | str | bytearray | memoryview | None = None) None[source]

Write the one ECDH share standing for every eligible input.

What a Signer holding every eligible input’s key may do instead of one share each: the sum of those keys, once, with one proof against the sum of their public keys. Fewer bytes in the psbt and one verification for every reader of it.

The keys are given in the order of the eligible inputs, and the sum is checked against the sum of their public keys before anything is written: a global share proved against the wrong sum is a share that fails for every recipient at once.

btclib.psbt.silent_payments.set_input_share(psbt: Psbt, vin_i: int, prv_key: int | bytes | str | bytearray | memoryview | BIP32KeyData, aux: bytes | str | bytearray | memoryview | None = None) None[source]

Write the ECDH share and proof of one input, for every recipient.

What a Signer holding one input’s key does: one share per scan key the psbt pays, each with the BIP374 proof that it was computed with the private key of this input’s public key – which is what lets the other signers check it without holding that key.

The input must be one BIP352 counts, and the key must be its own: a share proved against a public key the input does not have is a share every verifier rejects, so it is refused here instead of written.

btclib.psbt.silent_payments.set_output_scripts(psbt: Psbt) None[source]

Derive every silent payment output script, and freeze the psbt.

The Signer’s last step before it signs: BIP375 forbids a signature while an output has no script, and requires the two modifiable flags cleared once one is written – the script is a function of the input set, so a psbt that still invites inputs invites its own scripts to become wrong.

Every silent payment output must be derivable, or nothing is written: a psbt half-derived is one whose recipients each need the other’s signer to have finished.

btclib.psbt.silent_payments.shared_secret_from_share(psbt: Psbt, share: bytes | str | bytearray | memoryview, A_sum: tuple[int, int]) tuple[int, int][source]

Return BIP352’s shared secret from a BIP375 share.

The step the two BIPs do not share a name for, and the one worth spelling out: the psbt carries a*B_scan, with no input hash in it, where BIP352’s secret is input_hash*a*B_scan. So the share is multiplied by the input hash here, and the input hash is what binds the derivation to this transaction’s smallest outpoint – which is why the psbt is an argument and the share alone would not do.

btclib.psbt.psbt module

Partially Signed Bitcoin Transaction (Psbt) dataclass and functions.

https://github.com/bitcoin/bips/blob/master/bip-0174.mediawiki https://github.com/bitcoin/bips/blob/master/bip-0370.mediawiki

Both versions are held the same way, and it is BIP370’s way: the fields of the transaction being built – its version, each input’s outpoint and sequence, each output’s amount and script – live in the psbt, and the unsigned transaction is computed from them (Psbt.tx). Version 0, where that transaction is the field and those are computed, is then a conversion at the two edges, parse taking it apart and serialize putting it back together, and nothing between the two has to ask which version it is holding.

The other way round – keeping BIP174’s transaction and shadowing the BIP370 fields beside it – costs the same conversion and leaves the two able to disagree, and BIP370 needs a second transaction anyway: the one that identifies a psbt has every sequence zeroed (Psbt.unique_id), so one stored transaction could not be both.

class btclib.psbt.psbt.KeyManager(*args, **kwargs)[source]

Bases: Protocol

A signature over a hash, by public key or origin: what sign asks.

sign has no key of its own; a KeyManager is where the keys are, and each method answers what sign cannot – is this key one you hold, and if so, what does it sign msg_hash with. None answers “not mine” rather than raising, which is what lets one signer of an m-of-n answer for its own key alone: an input none of its keys can answer for is somebody else’s turn, not an error.

Both pub_key and origin travel on every call, in the order a psbt itself gives them precedence: a key match is a fact, an origin only a claim about a four-byte fingerprint, which collides. The claim is what a watch-only-shaped manager – one xprv, many children it has never derived – needs to answer at all, having no way to recognize a child key it has not yet computed.

What comes back is the bare signature – DER for ECDSA, 64 bytes r||s for schnorr – with no sig_hash type appended. sign appends it, being the one that fixed the hash and therefore the type the signature answers for; the secret stays inside the manager, which is what lets a hardware backend implement this same contract without sign ever holding what signs for it.

sign_ecdsa(pub_key: bytes, origin: BIP32KeyOrigin | None, msg_hash: bytes) bytes | None[source]

Return the DER signature of msg_hash by pub_key, or None.

sign_schnorr(pub_key: bytes, origin: BIP32KeyOrigin | None, msg_hash: bytes, merkle_root: bytes) bytes | None[source]

Return the BIP340 signature of msg_hash by pub_key, or None.

pub_key is the taproot internal key, x-only and untweaked, and the signature has to be the tweaked output key’s: merkle_root is PSBT_IN_TAP_MERKLE_ROOT, empty for a key-path-only output, and tweaking by it is the manager’s to do, sign never holding what tweaking a private key needs.

sign_schnorr_script_path(pub_key: bytes, origin: BIP32KeyOrigin | None, msg_hash: bytes, leaf_hash: bytes) bytes | None[source]

Return the BIP342 signature of msg_hash by pub_key, or None.

The other half of a taproot spend, and the one method here whose key signs as it is: pub_key is a key of the leaf script, x-only, and a script path proves the leaf rather than the output key – the tweak is what the control block carries, so there is nothing for the manager to apply and no merkle root to apply it by.

leaf_hash is which leaf asked. One key can sit in more than one, each leaf is a different message and a different entry of PSBT_IN_TAP_SCRIPT_SIG, and a manager with a policy about which conditions it signs under has only this to recognize them by.

class btclib.psbt.psbt.Psbt(tx_version: int, inputs: Sequence[PsbtIn], outputs: Sequence[PsbtOut], version: int, hd_key_paths: Mapping[bytes | str | bytearray | memoryview, BIP32KeyOrigin], unknown: Mapping[bytes | str | bytearray | memoryview, bytes | str | bytearray | memoryview] | None = None, fallback_lock_time: int | None = None, tx_modifiable: int | None = None, signed_message: bytes | str | bytearray | memoryview | None = None, sp_ecdh_shares: Mapping[bytes | str | bytearray | memoryview, bytes | str | bytearray | memoryview] | None = None, sp_dleq_proofs: Mapping[bytes | str | bytearray | memoryview, bytes | str | bytearray | memoryview] | None = None, *, check_validity: bool = True)[source]

Bases: object

A partially signed bitcoin transaction, BIP174 and BIP370.

Both versions are held BIP370’s way – the transaction’s fields live in the psbt and tx computes the unsigned transaction; the module docstring says why. The global fields are here, each input’s and output’s in its PsbtIn or PsbtOut; the wire form is serialize and parse, the customary text form b64encode and b64decode.

assert_signable() None[source]

Assert that every input carries what a Signer needs.

Valid and signable are different questions, and BIP174 answers only the first: it lists two psbts with no inputs as valid, and assert_valid accepts them. This one is the Signer’s pre-flight, so it answers the second, and a psbt with nothing to sign is not signable.

The check has to be explicit because every check below is per input: without it an empty vin passes the loop vacuously, and a caller doing assert_signable() and then looping over the inputs signs none of them and is told nothing.

assert_valid() None[source]

Assert logical self-consistency.

Two questions, and the version answers the first: which fields this psbt must have and which it must not, BIP370 giving each of its twelve a “Versions Requiring Inclusion” and a “Versions Requiring Exclusion”. The second is what the fields hold, which is the same question in both versions – an outpoint is an outpoint whether it was read from an input map or from an unsigned transaction.

classmethod b64decode(psbt_str: bytes | str | bytearray | memoryview, *, check_validity: bool = True) Psbt[source]

Build a Psbt from its base64 text, stripping whitespace.

The coercion before the strip, as bms.Sig.b64decode does it and for the reason issue #814 gives: without it, what is neither text nor bytes reaches base64.b64decode untouched, and is left facing its own “argument should be a bytes-like object or ASCII string” – a complaint about a builtin rather than about the psbt that was passed.

b64encode(*, check_validity: bool = True) str[source]

Return the serialization as base64 text, BIP174’s file form.

property estimated_vsize: int

Return the virtual size the transaction will have once signed.

The name Bitcoin Core’s analyzepsbt reports it under, and the Tx.vsize arithmetic: a quarter of the weight, rounded up.

property estimated_weight: int

Return the weight the transaction will have once signed.

A signature is assumed to be 72 bytes, the largest a low-s one can be with its sig_hash byte, so the answer is an upper bound; an input whose type the psbt does not determine has no estimate and raises, naming itself. Both rules are psbt_size’s, and why each is what it is, is there.

Tx.weight is what the placeholders below are handed to: a signature is bytes wherever it goes, and how many of them a transaction is once they are in place is one arithmetic, written once, in the class whose serialization it is.

classmethod from_dict(dict_: Mapping[str, Any], *, check_validity: bool = True) Psbt[source]

Build a Psbt from the dict shape to_dict writes.

classmethod from_tx(tx: Tx, inputs: Sequence[PsbtIn] | None = None, outputs: Sequence[PsbtOut] | None = None, *, check_validity: bool = True) Psbt[source]

Return the version 0 psbt of a transaction, Creator-style.

The transaction is taken apart into the fields the psbt holds, which is the same conversion parse makes: one input map per input and one output map per output, each carrying what the transaction said about it.

inputs and outputs are the maps to fill, for a caller who already has them – a Combiner or an Updater – and empty ones otherwise, which is what a Creator starts from.

property has_sig_hash_single: bool

Return whether a SIGHASH_SINGLE signature pins input to output.

Bit 2 of PSBT_GLOBAL_TX_MODIFIABLE. Such a signature commits to the output at the signed input’s own index, so the pairing is positional: adding, removing or reordering either side breaks it, whatever the two modifiable bits say.

property inputs_modifiable: bool

Return whether a Constructor may add or remove an input.

Bit 0 of PSBT_GLOBAL_TX_MODIFIABLE. A version 2 psbt with no such field says no: “A Constructor may choose to declare that no further inputs and outputs can be added to the transaction by setting the appropriate bits … to 0 or by removing the field entirely”. Version 0 has no field to consult and no Constructor role either, so it answers yes and nothing changes for it.

property lock_time: int

Return the lock time of the transaction being built.

Computed, never stored: BIP370 makes it the answer to the inputs’ required lock times, with the fallback for a psbt whose inputs require none – which is every version 0 psbt, its unsigned transaction’s nLockTime being read into the fallback. _lock_time is the algorithm.

property outputs_modifiable: bool

Return whether a Constructor may add or remove an output.

Bit 1 of PSBT_GLOBAL_TX_MODIFIABLE; inputs_modifiable says what an absent field and a version 0 psbt answer.

classmethod parse(data: BytesIO | bytes | str | bytearray | memoryview, *, check_validity: bool = True) Psbt[source]

Return a Psbt by parsing binary data.

A psbt ends at the separator of its last map. A stream is left right there, what follows in it being the caller’s, so a psbt can be read out of a stream that carries more than the psbt; octets are the whole of one, so anything after it is refused. Bitcoin Core splits the two the same way, between PSBTInput::Unserialize and DecodeRawPSBT’s “extra data after PSBT”.

serialize(*, check_validity: bool = True) bytes[source]

Return the psbt as the bytes of the version it declares.

Version 0 writes the unsigned transaction its fields make and nothing else of BIP370; version 2 writes those fields and no transaction. to_v0 and to_v2 are the conversions between the two, and neither is done here: what a psbt is written as is what it says it is.

sort_inputs(ordering_func: Callable[[PsbtIn], int] | None = None) None[source]

Sort psbt inputs.

sorting logic is ordering_func if present, shuffle otherwise.

A version 2 psbt is asked first: reordering the inputs is a change to the transaction every signature commits to, so it is one the Inputs Modifiable flag has to allow. _assert_modifiable is where the two flags are read.

sort_outputs(ordering_func: Callable[[PsbtOut], int] | None = None) None[source]

Sort psbt outputs.

sorting logic is ordering_func if present, shuffle otherwise.

The Outputs Modifiable flag is what allows it in a version 2 psbt, as the Inputs Modifiable one allows sort_inputs.

to_dict(*, check_validity: bool = True) dict[str, Any][source]

Return the psbt as a dict of json-friendly values.

The “tx” entry is derived for the reader and ignored by from_dict, the comment on it saying why; everything else round-trips.

to_v0() Psbt[source]

Return this psbt as the version 0 psbt of the same transaction.

What version 0 cannot say is dropped, and the transaction is unchanged by the dropping: the computed lock time becomes the fallback, which is where a version 0 psbt keeps its nLockTime, so the inputs’ required lock times go with nothing lost from the transaction – only the record of which input required what. The modifiable flags go too, version 0 having no Constructor to obey them.

A psbt whose inputs require both kinds of lock time has no transaction to be the version 0 psbt of, and raises here as it does anywhere else its lock time is asked for.

to_v2() Psbt[source]

Return this psbt as the version 2 psbt of the same transaction.

Nothing but the version number: every field version 2 writes is already held, this being how btclib holds a psbt of either version, so the conversion the other way is the one with work to do. What was the unsigned transaction’s nLockTime is written as the fallback, which is what it is – no input of a version 0 psbt requires a lock time, none being able to say so.

property tx: Tx

Return the unsigned transaction this psbt is of.

Computed from the fields each time, so it is a copy and not the psbt: what is written into it is written into nothing, and an outpoint or a sequence is changed on the input that holds it. The transaction is the psbt’s serialization in version 0 and nowhere at all in version 2, which is why it cannot be the field the rest hangs off.

property unique_id: bytes

Return the identifier BIP370 gives this psbt.

The txid of the unsigned transaction with every sequence set to 0: an Updater may set PSBT_IN_SEQUENCE, so two psbts of one transaction can disagree about it, and the identifier must not. It is what a Combiner compares – combine does – rather than tx.id, which for a version 2 psbt would call the same transaction two.

A silent payment output enters it as its address rather than as its script, which BIP375 adds for the same reason BIP370 zeroes the sequences: the script is computed later, so a psbt before and after that computation would otherwise be two. _identifying_script is the substitution.

vsize_estimate(sizer: Callable[[PsbtIn, TxIn], list[int] | None] | None = None) int[source]

Return the virtual size once signed, asking a sizer where needed.

estimated_vsize over weight_estimate, so that a fee computed from a caller’s own solution sizes is the same arithmetic as one computed from this library’s.

weight_estimate(sizer: Callable[[PsbtIn, TxIn], list[int] | None] | None = None) int[source]

Return the weight once signed, asking a sizer where needed.

What estimated_weight is, with the one thing a property cannot take: a psbt_size.SolutionSizer, for the inputs this library refuses to estimate because what they will push is knowledge only the caller has – a script of no standard type, a taproot script path. Without one this is that property exactly.

Validated first, as every other method that reads this psbt’s data is: an estimate off an incoherent psbt is a number, and a number is what a caller sizes a fee with.

btclib.psbt.psbt.assert_signatures_only(request: Psbt, returned: Psbt) None[source]

Raise unless returned is request with signatures added, and no more.

What a caller has to know before merging an answer from somebody else – an external signer, a hardware device, a cosigner – because combine will not tell them: BIP174’s Combiner takes the union of what it is given and may resolve a conflict by picking either side, so it compares only the psbt’s identifier and merges the rest. Which leaves three ways for an answer to change what was sent: adding a field the request left empty, overwriting one entry of a field that is a map, and – in a version 2 psbt, whose identifier zeroes every sequence – changing a sequence.

The rule here is one sentence. Everything that is not a signature comes back as it was sent; the signature fields may only gain entries; every signature that arrived is verified before anything is merged. _SIGNATURE_FIELDS is the second clause, _assert_unchanged the first, and both walk the fields a map declares rather than a list kept beside them.

The transaction being signed is compared whole, Psbt.tx being computed from the fields of either version: that is what catches the changed sequence of a version 2 psbt, which unique_id cannot see, and it makes the input and output counts equal without a check of their own.

tx_modifiable is the one field an answer may legitimately change, a Signer clearing a bit when it adds a signature that a change would break. What it may not do is loosen one, and the rule for that is _combined_tx_modifiable’s already: an answer no more permissive than the request is one the two combine into unchanged.

The two musig2 maps are permitted additions and are not verified here. A BIP327 partial signature is checked against the session’s aggregate nonce, which needs every participant’s nonce, and a psbt mid-session need not carry them yet; musig2.partial_sigs_agg refuses an aggregate that does not verify, which is the check that can be made once the session is complete.

btclib.psbt.psbt.assert_signed(psbt: Psbt, *, allow_partial: bool = False) None[source]

Raise unless every input is signed and every signature verifies.

The question between the two roles that read a signature and answer something else. assert_signatures_only holds an answer to the request it came from, and a request nobody signed comes back unchanged and passes; finalize builds a spend out of whatever satisfies the script, and reads a signature it cannot verify as one that is not there. So a caller storing a psbt as complete, or about to finalize one, has this to ask: is every signature this psbt carries a signature of this transaction by the key it is filed under, and does every input carry one.

Both halves matter and neither implies the other. A signature that does not verify is a psbt built, merged or transcribed wrong, whatever the count says; an input with no signature at all is a spend that will not relay, however good the signatures beside it are. allow_partial keeps the first half and drops the second, for a psbt of a signing session still going round: a device holding keys for some inputs only – one psbt spanning several wallets – leaves the others untouched, and which of the two a psbt is is the caller’s to say and not this function’s to guess. It is refused rather than read for its truth: its True is the permissive value, so the misreading a non-bool always makes – allow_partial=”false” out of a configuration file – is the one that stores as complete a psbt with an input nobody signed.

What neither half asks is whether an input is satisfied: one signature of a 2-of-2 is an input signed, and whether a spend can be built out of what the input holds is finalize’s answer, over the script and not over a count.

Verification is _assert_ecdsa_sigs_verify’s and _assert_taproot_sigs_verify’s, so it covers every input kind either signature kind belongs to, and the sig_hash type each commits to must be the type the input asks for. An input that does not say what was signed – no utxo, no redeem script, no witness script – is refused here rather than skipped: the Finalizer’s leniency is for a role that has the finalized scripts to fall back on, while a caller asking this is asking about the signatures themselves.

Two things are deliberately not a signature here. A BIP373 musig2 partial signature is not one until the session’s are added up, which is musig2.partial_sigs_agg and which writes the taproot signature this then checks – so an input holding a session mid-round is unsigned, and saying so is the point. And a finalized input carries no signature at all, BIP174 having the Finalizer clear them: it is refused with that said rather than reported unsigned, what it now carries being a spend for the script engine to verify.

btclib.psbt.psbt.combine(psbts: Sequence[Psbt]) Psbt[source]

Merge the data of several psbts of one transaction: the Combiner.

BIP174’s Combiner role, whose ordinary use is merging the partial signatures different signers added to copies of one psbt.

Every field a psbt map holds is merged, and the four left out are left out for one reason: amount, script_pub_key, previous_tx_id and output_index are part of what identifies the psbt, so the psbt being merged into carries them already and two psbts disagreeing about one of them are two transactions, refused above.

Which psbts are of one transaction is a question the two versions answer differently, and each is asked its own: a version 0 psbt is identified by the txid of the unsigned transaction every copy of it carries, so two copies whose sequences differ are two transactions; a version 2 psbt is identified as BIP370 says, by the txid of that transaction with every sequence zeroed, the sequence being a field an Updater may set. Comparing tx.id there would refuse two psbts of one transaction, which is what the identifier exists to prevent.

The versions must match, and are not converted here: to_v0 and to_v2 are that, and doing it silently would decide for the caller which of the two the combined psbt is – and, from v0 to v2, hand back a psbt whose lock time comes from the fallback rather than from the unsigned transaction the caller wrote it into.

The psbt handed back shares nothing with the ones handed in, which is finalize’s rule and extract_tx’s stated one. It matters more here than anywhere: without the copy this is psbts[0], merged into in place, so the copy a coordinator keeps to check the next signer’s answer against is the copy the last answer went into – and a check against it would then pass whatever came back. The whole sequence is copied and not only the first, _combine_field assigning the objects it takes rather than copying them: a witness_utxo or a leaf script map that came from psbts[1] would otherwise be the very object psbts[1] still holds.

btclib.psbt.psbt.ecdsa_sig_hash(psbt: Psbt, vin_i: int, *, hash_type: int | None = None) bytes[source]

Return the hash an ECDSA spend of one input signs.

What a Signer puts in PSBT_IN_PARTIAL_SIG is a signature of this, with the hash type appended; taproot_sig_hash is the same question for the schnorr signatures of a taproot input, and the two are the split finalize dispatches on.

hash_type defaults to the type the input asks for, and to SIGHASH_ALL when it asks for none. An input asking for SIGHASH_DEFAULT is refused: 0 is a taproot type, no ECDSA signature carries it, and a psbt asking for it is one no partial signature can finalize – which is what _assert_sig_hash_type says from the Finalizer’s end.

Every kind a partial signature can belong to is covered, the wrapped ones included: _sig_hash_from_psbt_in is the dispatch and says how. Where it answers None this raises, and the difference is the caller: a Finalizer checking a signature it was handed learns nothing from a psbt that does not say what was signed, while a Signer about to make one has to stop. Where it raises – a non-witness spend described by a witness utxo alone – every caller stops, that being an input no role may sign, verify or finalize.

btclib.psbt.psbt.extract_tx(psbt: Psbt, *, check_validity: bool = True) Tx[source]

Extract the Tx fro the Psbt.

The Transaction Extractor must only accept a PSBT. It checks whether all inputs have complete scriptSigs and scriptWitnesses by checking for the presence of 0x07 Finalized scriptSig and 0x08 Finalized scriptWitness typed records.

If they do, the Transaction Extractor should construct complete scriptSigs and scriptWitnesses and encode them into network serialized transactions. Otherwise the Extractor must not modify the PSBT.

The Extractor should produce a fully valid, network serialized transaction if all inputs are complete.

Extracting needs no script interpretation; an Extractor that can interpret scripts may also validate the transaction it extracts, as BIP174 allows.

btclib.psbt.psbt.finalize(psbt: Psbt, *, solver: Callable[[Psbt, int], tuple[bytes, Witness] | None] | None = None) Psbt[source]

Finalize the Psbt.

The Input Finalizer must only accept a PSBT.

For each input, the Input Finalizer determines if the input has enough data to pass validation. If it does, it must construct the 0x07 Finalized scriptSig and 0x08 Finalized scriptWitness and place them into the input key-value map.

All other data except the UTXO and unknown fields in the input key- value map should be cleared from the PSBT. The UTXO should be kept to allow Transaction Extractors to verify the final network serialized transaction. _FINALIZED_KEEPS is that list, and one list for both kinds of input is what keeps the two from drifting apart.

Deciding that an input has enough data is two checks beyond the presence of a signature, and both are per input: the sighash type each signature commits to is the one the input asks for, and each signature verifies against the key it is filed under.

What is then built is the spend the input’s own kind asks for, which is what _finalized_input dispatches on: a witness script alone does not say, being absent from every single-key segwit input.

An input that is already finalized is left alone rather than refused, so finalizing twice is finalizing once. “The Input Finalizer determines if the input has enough data” and one carrying its final scripts has more than enough; Bitcoin Core’s SignPSBTInput skips it too. Refusing it would mean a psbt whose signer finalized one input could not be finalized at all – the rest of it would raise “missing signatures” for the input that is already done.

A solver answers for the inputs whose spend is the caller’s to know. It is asked before this function builds anything, and not only where this function refuses, which the sizer of psbt_size is: two of the shapes below are refusals – a taproot input with more than one script path signature, and a leaf that is not a single-key one – but a witness script of no standard kind is not. That one is built from the signatures and the script, which is the satisfaction of a multisig and a guess for anything else, so a caller with a script of their own has to be able to answer over it rather than after it. descriptors.miniscript_solver is that answer wherever the witness script is a BIP379 miniscript, and it is a solver rather than a branch of this function for a reason of layering: descriptors imports this module and nothing here imports back.

What the solver does not take over is the bookkeeping: the clearing BIP174 asks for, what is kept, and the verification of whatever signatures the input does carry are this function’s either way.

btclib.psbt.psbt.join(psbts: Sequence[Psbt], enforce_same_tx_version: bool, enforce_same_tx_lock_time: bool, shuffle_inp: bool, shuffle_out: bool, sort_inp: Callable[[PsbtIn], int] | None = None, sort_out: Callable[[PsbtOut], int] | None = None) Psbt[source]

Join multiple psbts into a single one by merging inputs and outputs.

inputs/outputs are shuffled by default. If shuffle_{in|out}=False, they are concatenated in the same order as psbts are specified. A specific ordering can be specified via sort_{inp|out}, which overwrite shuffle when present.

Outputs are concatenated and never merged, and there is no parameter asking for it: coalescing two outputs that pay the same script is a change to the output set, so every signature already made over the old one stops verifying – and, after the shuffle or sort above, the result would depend on the order the merge ran in. A caller who wants one output where there were two builds it that way before signing, which is the only point at which it is safe.

Joining is a Constructor adding inputs and outputs to each of the psbts at once, so a version 2 psbt has to allow both: every psbt joined is asked for its two modifiable flags, and the joined psbt carries what all of them still allow. The versions must be the same, for the reason combine gives.

The joined psbt shares nothing with the ones joined, for the reason combine copies: the input and output maps below are taken from every psbt in the sequence, so without the copy the joined psbt’s inputs are theirs, and an Updater filling one in afterwards fills in a psbt somebody else is still holding.

A signed message is not carried over, and that is not an omission: it says which challenge this transaction answers, and joining builds a transaction that is not it – BIP322 binds the message to the first input’s outpoint, which the join can move. A caller building a proof of funds this way sets the field on the result, where what it names is a transaction that exists.

btclib.psbt.psbt.leaf_script(psbt_in: PsbtIn, leaf_hash: bytes | str | bytearray | memoryview) tuple[bytes, bytes][source]

Return the leaf script of a tapleaf hash, and its control block.

PSBT_IN_TAP_LEAF_SCRIPT is keyed by control block and holds the script and its leaf version, while every other taproot field names a leaf by its BIP341 hash: this is that lookup, and it computes the hashes rather than trusting a second index of them.

btclib.psbt.psbt.new_signers(request: Psbt, returned: Psbt) set[bytes][source]

Return the master fingerprints returned adds the signatures of.

Which wallets answered, read off an answer before it is merged. Every signature names the key that made it – as the key data it is filed under, in BIP174’s PSBT_IN_PARTIAL_SIG and BIP371’s and BIP373’s fields alike – and the psbt names the origin of that key, whose master fingerprint is the wallet it was derived from. Asked before combine, because a Combiner takes the union of what it is given and records nothing of which side each entry came from: afterwards there is nothing left to tell apart.

A fingerprint and not a key, four bytes of hash160 of a master public key: it is what a psbt states about a key’s provenance and all a psbt states, so a device that answered for several keys of several inputs is one signer here, and two devices sharing a seed are not distinguishable at all. It is not evidence either – a psbt is a file and the origin fields in it are whatever was written there – so what this answers is “which wallet does this psbt say signed”, which is the question a caller filing an answer, or refusing one that came from a device other than the one it asked, actually has.

The signature kinds are the ones a signer adds: ECDSA partial signatures, attributed through PSBT_IN_BIP32_DERIVATION; a taproot key path signature, through the internal key’s PSBT_IN_TAP_BIP32_DERIVATION, that being what says who holds the key the output commits to; a taproot script path signature, through the same field entry for the x-only key its key data names; and the two musig2 rounds, whose participant key is the first 33 bytes of their key data and whose origin is a plain key’s, in PSBT_IN_BIP32_DERIVATION. A BIP373 round is included because it is what that participant’s answer is, as assert_signatures_only counts the two maps among the signature fields for the same reason.

Two of those attributions are honest but weaker than they look, and both are the psbt’s doing rather than this function’s. A musig2 session whose internal key is BIP328-derived from the aggregate key files that derivation under a synthetic fingerprint – hash160 of the aggregate key – so the key path signature partial_sigs_agg writes is attributed to the session rather than to any one participant, which is exactly what it came from. And a psbt stating no origin for a key it holds a signature of is refused: see _master_fingerprint.

Only the input counts are compared. That the two psbts are otherwise the same request is assert_signatures_only’s question, which is the call that belongs beside this one, and the order of the two is the caller’s: it is the merge that must not happen before both have answered.

btclib.psbt.psbt.prevouts(psbt: Psbt) list[TxOut][source]

Return the output each input of the psbt spends.

A taproot signature commits to the amount and script of every input (BIP341’s sha_amounts and sha_scriptpubkeys), not only of the one being signed, so a single missing utxo leaves the whole transaction unsignable rather than one input of it – which is why this raises where _prev_out answers None.

btclib.psbt.psbt.sign(psbt: Psbt, key_manager: KeyManager) tuple[Psbt, list[int]][source]

Run the Signer role over every input key_manager answers for.

Per input the candidates are what the psbt itself names: hd_key_paths for an ECDSA spend, the taproot internal key and the taproot hd_key_paths entries for a taproot one. None from key_manager skips the key rather than raising – one signer of an m-of-n holds one key, and an input it cannot answer for is not an error but somebody else’s turn. What comes back besides the copy is which inputs got a new signature, since a caller collecting a quorum needs to tell “there was nothing for me” from “done”.

A taproot input is offered both of its paths, the key path and every leaf the psbt carries a script for, and one input may come back with signatures for both: which of the two is spent is the Finalizer’s choice, and a signer that holds keys for both has no reason to be asked twice. Which leaf a key belongs to is not sign’s guess either – PSBT_IN_TAP_BIP32_DERIVATION says it, and MuSig2’s own Signer is btclib.psbt.musig2 for the aggregate case. Every other kind is whatever _finalized_input can close over – p2pk, p2pkh, p2wpkh, p2sh-p2wpkh, p2wsh, bare and wrapped multisig.

Raises where the psbt cannot be signed at all – assert_signable’s question – and where a candidate’s own hash cannot be computed, which is ecdsa_sig_hash refusing to guess at a caller’s stop. A key key_manager has nothing to say about is a different question and does not raise.

btclib.psbt.psbt.single_leaf_key(script: bytes) bytes[source]

Return the key of a <32-byte key> OP_CHECKSIG leaf script.

What a Finalizer has to know to build the witness of a script path spend is what the leaf script pops, and that is a property of the script rather than of the psbt: one signature for this shape, and for a leaf that asks for more – a threshold of CHECKSIGADD, a hash preimage – the psbt says nothing about what else goes on the stack.

btclib.psbt.psbt.taproot_sig_hash(psbt: Psbt, vin_i: int, *, leaf_hash: bytes | str | bytearray | memoryview = b'', hash_type: int | None = None) bytes[source]

Return the hash a taproot spend of one input signs.

BIP341 for a key path spend, BIP342 for a script path one, and the tapleaf hash is what tells the two apart: given one, the message carries it along with the key version and the codesep position, as the script engine’s own OP_CHECKSIG builds them.

hash_type defaults to the type the input asks for, and to SIGHASH_DEFAULT when it asks for none. Passing it is what a Finalizer does: a taproot signature carries its own type appended, so the hash to check it against is the one it committed to.

The annex is empty: BIP341 leaves it undefined, no psbt field carries one, and a signer cannot invent what the spender will put on the stack.

btclib.psbt.musig2 module

The three roles BIP373 defines, over btclib.ecc.musig2.

https://github.com/bitcoin/bips/blob/master/bip-0373.mediawiki

btclib.psbt.psbt_in and psbt_out carry the four MuSig2 fields; what is here is what they mean. One function per role, and the roles are BIP373’s own:

  • Updater: add_participant_pub_keys aggregates a list of keys and files it under the aggregate key it makes, on an input or an output;

  • Signer: nonce_gen writes the public nonce of round 1 and partial_sign the partial signature of round 2;

  • Finalizer: partial_sigs_agg adds the partial signatures up into the BIP340 signature the spend needs, writes it to PSBT_IN_TAP_KEY_SIG or PSBT_IN_TAP_SCRIPT_SIG, and drops the session.

Where the secret nonce lives is the caller’s business, and this module holds nothing. nonce_gen hands back the bytearray that btclib.ecc.musig2.sign consumes, and partial_sign takes it back; between the two rounds it is in the caller’s hands and never in the psbt, which travels. That is not squeamishness about serialization: a secnonce that signs twice hands out the private key by elementary algebra, and a psbt is a file that gets copied, combined and re-read. A session object owned by btclib would have to be as careful as the bytearray already is, with a lifetime the library cannot see the end of – so the decision is to own no state at all, which is also the answer the libsecp256k1 musig module gives (an opaque secnonce its own API invalidates after use) and the shape an interactive threshold scheme needs (issue #257).

What a psbt says, and what it therefore need not be told, is how the aggregate key reaches the output being spent. Four ways, and BIP373 publishes a vector for each:

  • the aggregate key is the taproot output key: nothing to tweak;

  • the aggregate key is the internal key: one x-only tweak, the BIP341 commitment to the merkle root the input carries;

  • the aggregate key is a key in a leaf script: nothing to tweak, and the message is BIP342’s rather than BIP341’s – which is why every function here takes the tapleaf hash the key data carries;

  • the internal key is derived from the aggregate key: BIP328 derivation, i.e. one plain tweak per step of the path in PSBT_IN_TAP_BIP32_DERIVATION, then the x-only one.

session_context is where those four are read off the psbt, and it is public because a signer needs it for what BIP327 asks beyond signing: btclib.ecc.musig2.partial_sig_verify_ against another signer’s nonce. It returns the KeyAggContext it built the session on, alongside the session itself: partial_sigs_agg is a caller that needs the tweaked key, and the alternative – aggregating the participants over again to get it – is the quadratic shape issue #1046 fixed.

class btclib.psbt.musig2.Session(context: musig2.SessionContext, key_agg_ctx: musig2.KeyAggContext)[source]

Bases: NamedTuple

A BIP327 session, and the KeyAggContext it was built on.

context is what btclib.ecc.musig2.sign and partial_sig_verify_ take. key_agg_ctx is the aggregation _session_parts already did to reach it – handed back rather than left for a caller that needs the tweaked key, x_only_pub_key included, to aggregate the participants a second time.

context: SessionContext

Alias for field number 0

key_agg_ctx: KeyAggContext

Alias for field number 1

btclib.psbt.musig2.add_participant_pub_keys(psbt_map: PsbtIn | PsbtOut, participant_pub_keys: Sequence[bytes | str | bytearray | memoryview], *, sort: bool = False) bytes[source]

Add the participants of one aggregate key, and return that key.

The Updater role, and the aggregate key is not a parameter because it is not a choice: KeyAgg computes it from the list, so a caller handing in both could file a list under a key it does not aggregate to – which is exactly the field’s meaning gone, and no reader could tell.

sort=True aggregates the sorted list, as BIP327’s KeySort sorts it and as BIP373 requires whenever sorting was used at all: the order is part of the key, so it is stored in the order it was aggregated in.

btclib.psbt.musig2.assert_valid_participants(psbt_map: PsbtIn | PsbtOut) None[source]

Raise unless every participant list aggregates to the key it is under.

The check assert_valid cannot make: it is KeyAgg over the list, which is the aggregation this module exists to do, and a codec that depended on it would depend on a signing scheme. What it catches is the one way the field can be wrong while every length is right – a list that is not the aggregate key’s – which no signer can use and no Updater should have written.

btclib.psbt.musig2.nonce_gen(psbt: Psbt, vin_i: int, prv_key: int | bytes | str | bytearray | memoryview | BIP32KeyData, aggregate_pub_key: bytes | str | bytearray | memoryview, *, leaf_hash: bytes | str | bytearray | memoryview = b'', extra_in: bytes | str | bytearray | memoryview | None = None) bytearray[source]

Write the public nonce of round 1, and return the secret one.

The Signer role, first half. The returned bytearray is what partial_sign consumes and what must not be copied, written down or put in the psbt: the module docstring says why the decision is to hand it back rather than keep it.

The nonce is bound to everything BIP327 lets it be bound to – the signer’s key, the aggregate key of the session and the message – because all three are in the psbt, and a nonce derived from fewer of them is one a faulty random source can repeat across sessions.

btclib.psbt.musig2.partial_sig_verify(psbt: Psbt, vin_i: int, participant_pub_key: bytes | str | bytearray | memoryview, aggregate_pub_key: bytes | str | bytearray | memoryview, *, leaf_hash: bytes | str | bytearray | memoryview = b'') bool[source]

Verify one participant’s partial signature, as the psbt holds it.

What BIP327 asks every signer to do before aggregating, over the psbt the partial signature arrived in: the nonce it is checked against is the one the same input carries for the same participant, so a signature that was made against another session answers False here rather than at aggregation time, where the only news is that the total does not verify.

btclib.psbt.musig2.partial_sign(psbt: Psbt, vin_i: int, sec_nonce: bytearray, prv_key: int | bytes | str | bytearray | memoryview | BIP32KeyData, aggregate_pub_key: bytes | str | bytearray | memoryview, *, leaf_hash: bytes | str | bytearray | memoryview = b'') bytes[source]

Write the partial signature of round 2, and return it.

The Signer role, second half. The secnonce is consumed by btclib.ecc.musig2.sign, which zeroes it: this function cannot be called twice with one nonce, and that is the point.

The signature is verified before it is written, against the session the psbt describes: a signer that publishes a partial signature of a session it got wrong has published a number the others cannot use and cannot make it un-published.

btclib.psbt.musig2.partial_sigs_agg(psbt: Psbt, vin_i: int, aggregate_pub_key: bytes | str | bytearray | memoryview, *, leaf_hash: bytes | str | bytearray | memoryview = b'') Sig[source]

Aggregate the session’s partial signatures, and drop the session.

The Finalizer role. The BIP340 signature goes where the spend reads it – PSBT_IN_TAP_KEY_SIG for a key path spend, and PSBT_IN_TAP_SCRIPT_SIG under the key and tapleaf hash for a script path one – with the sig_hash type appended when the input asks for one other than the default, as BIP341 appends it.

Every participant must have signed: MuSig2 is n-of-n, so a missing partial signature is not a smaller quorum but an aggregate signature that verifies under nothing.

The three MuSig2 fields of that session are then removed. What replaces them is the signature itself, and a nonce that has been used is worse than useless: keeping it invites a second session with the same nonce, which is the one thing that hands out a private key.

btclib.psbt.musig2.session_context(psbt: Psbt, vin_i: int, aggregate_pub_key: bytes | str | bytearray | memoryview, *, leaf_hash: bytes | str | bytearray | memoryview = b'') Session[source]

Return the BIP327 session the psbt describes, and its key aggregation.

The aggregate nonce is the sum of the public nonces the input carries for this session, so every signer that has published one is in it: a context built before the last nonce arrives is a different context, and the partial signatures made against the two do not add up. btclib.ecc.musig2.partial_sig_verify_ is what catches that, and this is what it takes.

btclib.psbt.psbt_in module

Partially Signed Bitcoin Transaction Input (PsbtIn) dataclass and functions.

https://github.com/bitcoin/bips/blob/master/bip-0174.mediawiki

class btclib.psbt.psbt_in.PsbtIn(non_witness_utxo: Tx | None = None, witness_utxo: TxOut | None = None, partial_sigs: Mapping[bytes | str | bytearray | memoryview, bytes | str | bytearray | memoryview] | None = None, sig_hash_type: Literal[0, 1, 2, 3, 129, 130, 131] | None = None, redeem_script: bytes | str | bytearray | memoryview = b'', witness_script: bytes | str | bytearray | memoryview = b'', hd_key_paths: Mapping[bytes | str | bytearray | memoryview, BIP32KeyOrigin] | None = None, final_script_sig: bytes | str | bytearray | memoryview = b'', final_script_witness: Witness | None = None, ripemd160_preimages: Mapping[bytes | str | bytearray | memoryview, bytes | str | bytearray | memoryview] | None = None, sha256_preimages: Mapping[bytes | str | bytearray | memoryview, bytes | str | bytearray | memoryview] | None = None, hash160_preimages: Mapping[bytes | str | bytearray | memoryview, bytes | str | bytearray | memoryview] | None = None, hash256_preimages: Mapping[bytes | str | bytearray | memoryview, bytes | str | bytearray | memoryview] | None = None, taproot_key_spend_signature: bytes | str | bytearray | memoryview = b'', taproot_script_spend_signatures: Mapping[bytes | str | bytearray | memoryview, bytes | str | bytearray | memoryview] | None = None, taproot_leaf_scripts: Mapping[bytes | str | bytearray | memoryview, tuple[bytes | str | bytearray | memoryview, int]] | None = None, taproot_hd_key_paths: Mapping[bytes | str | bytearray | memoryview, tuple[list[bytes | str | bytearray | memoryview], BIP32KeyOrigin]] | None = None, taproot_internal_key: bytes | str | bytearray | memoryview = b'', taproot_merkle_root: bytes | str | bytearray | memoryview = b'', unknown: Mapping[bytes | str | bytearray | memoryview, bytes | str | bytearray | memoryview] | None = None, previous_tx_id: bytes | str | bytearray | memoryview = b'', output_index: int | None = None, sequence: int | None = None, required_time_lock_time: int | None = None, required_height_lock_time: int | None = None, musig2_participant_pub_keys: Mapping[bytes | str | bytearray | memoryview, Sequence[bytes | str | bytearray | memoryview]] | None = None, musig2_pub_nonces: Mapping[bytes | str | bytearray | memoryview, bytes | str | bytearray | memoryview] | None = None, musig2_partial_sigs: Mapping[bytes | str | bytearray | memoryview, bytes | str | bytearray | memoryview] | None = None, sp_ecdh_shares: Mapping[bytes | str | bytearray | memoryview, bytes | str | bytearray | memoryview] | None = None, sp_dleq_proofs: Mapping[bytes | str | bytearray | memoryview, bytes | str | bytearray | memoryview] | None = None, *, check_validity: bool = True)[source]

Bases: object

The per-input map of a psbt: one field per BIP174/BIP370 key type.

What each role fills in for one input on its way to a signature – the spent output or the transaction holding it, scripts, hd paths, partial and taproot signatures, preimages, the finalized script or witness – with what no key type names kept in unknown. A field a psbt does not carry is None, or empty for the collection types.

assert_valid() None[source]

Assert logical self-consistency.

The BIP370 fields are checked for what they hold and not for whether they are there: which of them an input must carry is the psbt’s version, which an input on its own does not know, so Psbt.assert_valid asks that question and this one answers what an input can be asked alone.

classmethod from_dict(dict_: Mapping[str, Any], *, check_validity: bool = True) PsbtIn[source]

Build a PsbtIn from the dict shape to_dict writes.

classmethod parse(data: BytesIO | bytes | str | bytearray | memoryview, *, psbt_version: int = 0, check_validity: bool = True) PsbtIn[source]

Return a PsbtIn by parsing binary data.

One map is read, its terminator included, which leaves the stream on the input after this one.

psbt_version is the version of the psbt the map belongs to, which decides whether a BIP370 type byte is a field of this input or one this version must not carry; an input read on its own is read as version 0, the version BIP174 defines. Asked for as serialize asks for it, and for the same reason.

Octets are one whole input and a stream is the caller’s, as they are for the psbt these maps make: Psbt.parse threads one stream through the inputs and the outputs, and what follows an input in it is the next one.

property prev_out: OutPoint

Return the outpoint this input spends.

The two fields as the one value every other btclib caller takes, TxIn.prev_out included; an input that does not carry both is an input Psbt.assert_valid refuses, and OutPoint says so here.

serialize(*, psbt_version: int = 0, check_validity: bool = True) bytes[source]

Return the binary representation of the input map.

psbt_version is the version of the psbt the map belongs to, and it decides whether the BIP370 fields are written here or folded into the psbt’s unsigned transaction; an input serialized on its own is written as version 0, the version BIP174 defines. It is asked for, and not read for its truth: every version that is not 0 wrote the BIP370 fields, so a None or a 3 wrote a version 2 input and said nothing.

property sig_hash: int

Return the sig_hash as int.

For compatibility with PartiallySignedInput.

to_dict(*, check_validity: bool = True) dict[str, Any][source]

Return the input map as a dict of json-friendly values.

Keys are hex, hd paths are BIP174’s bip32_derivs shape; from_dict reads the same shape back.

btclib.psbt.psbt_out module

Partially Signed Bitcoin Transaction Output (PsbtOut).

Dataclass and functions. https://github.com/bitcoin/bips/blob/master/bip-0174.mediawiki

class btclib.psbt.psbt_out.PsbtOut(redeem_script: bytes | str | bytearray | memoryview = b'', witness_script: bytes | str | bytearray | memoryview = b'', hd_key_paths: Mapping[bytes | str | bytearray | memoryview, BIP32KeyOrigin] | None = None, taproot_internal_key: bytes | str | bytearray | memoryview = b'', taproot_tree: Sequence[tuple[int, int, bytes | str | bytearray | memoryview]] | None = None, taproot_hd_key_paths: Mapping[bytes | str | bytearray | memoryview, tuple[list[bytes], BIP32KeyOrigin]] | None = None, unknown: Mapping[bytes | str | bytearray | memoryview, bytes | str | bytearray | memoryview] | None = None, amount: int | None = None, script_pub_key: bytes | str | bytearray | memoryview = b'', musig2_participant_pub_keys: Mapping[bytes | str | bytearray | memoryview, Sequence[bytes | str | bytearray | memoryview]] | None = None, sp_v0_info: bytes | str | bytearray | memoryview = b'', sp_v0_label: int | None = None, *, check_validity: bool = True)[source]

Bases: object

The per-output map of a psbt: one field per BIP174/BIP370 key type.

The scripts and hd paths that let a wallet recognize an output as its own, BIP370’s amount and script_pub_key of the output being built, and what no key type names in unknown. A field a psbt does not carry is None, or empty for the collection types.

assert_valid() None[source]

Assert logical self-consistency.

The two BIP370 fields are checked for what they hold and not for whether they are there: which of them an output must carry is the psbt’s version, which an output on its own does not know, so Psbt.assert_valid asks that question (PsbtIn.assert_valid says the same of the five fields of an input).

classmethod from_dict(dict_: Mapping[str, Any], *, check_validity: bool = True) PsbtOut[source]

Build a PsbtOut from the dict shape to_dict writes.

classmethod parse(data: BytesIO | bytes | str | bytearray | memoryview, *, psbt_version: int = 0, check_validity: bool = True) PsbtOut[source]

Return a PsbtOut by parsing binary data.

One map is read, its terminator included, which leaves the stream on the output after this one.

psbt_version is the version of the psbt the map belongs to, which decides whether a BIP370 type byte is a field of this output or one this version must not carry; an output read on its own is read as version 0, the version BIP174 defines. Asked for as serialize asks for it, and for the same reason.

Octets are one whole output and a stream is the caller’s, as they are for the psbt these maps make: Psbt.parse threads one stream through the inputs and the outputs, and what follows an output in it is the next one.

serialize(*, psbt_version: int = 0, check_validity: bool = True) bytes[source]

Return the binary representation of the output map.

psbt_version is the version of the psbt the map belongs to, and it decides whether the two BIP370 fields are written here or folded into the psbt’s unsigned transaction; an output serialized on its own is written as version 0, the version BIP174 defines. It is asked for, and not read for its truth: every version that is not 0 wrote the BIP370 fields, so a None or a 3 wrote a version 2 output and said nothing.

to_dict(*, check_validity: bool = True) dict[str, Any][source]

Return the output map as a dict of json-friendly values.

Keys are hex, hd paths are BIP174’s bip32_derivs shape; from_dict reads the same shape back.

btclib.psbt.psbt_size module

How large the inputs of a Psbt will be once they are signed.

Tx.size, Tx.vsize and Tx.weight are read off a serialization, so all three need the signatures a Psbt does not have yet. What a psbt does have is, per input, the output being spent and the scripts that unlock it, and those say how many bytes the missing signatures will take – which is what a fee has to be computed from, before there is anything to sign.

Two rules are what make the answer honest rather than merely available.

A signature is 72 bytes, the sig_hash byte that follows it in the script included. A DER signature is 30 <len> 02 <r len> r 02 <s len> s, so 71 bytes when the 32-byte r needs the leading zero its high bit calls for and 70 when it does not: with the sig_hash byte, 72 or 71. Low s is exactly the rule that keeps s from ever needing that zero, which is why 72 is the largest and not merely the likeliest. Assuming the shorter form would underpay the intended fee rate one transaction in two, so the worst case is what is assumed here and every estimate is an upper bound.

An input whose type cannot be read raises. The utxo, the redeem script, the witness script and the derivation data are what the type is read from, and an input that carries too little of them has no estimate: guessing costs bytes in one direction only, and a fee computed from a guess is a transaction that does not relay.

A caller who does not have to guess says so, with a SolutionSizer. descriptors.miniscript_sizer is one for every input whose witness script is a BIP379 miniscript, and it is a sizer rather than a branch of this module for a reason of layering: descriptors imports this one and nothing here imports back. Two inputs are refused above not for want of data but for want of knowledge nobody but the caller has – a script of no standard type, and a taproot script path, where which leaf will be spent is not in the psbt – and a sizer is asked exactly there, never in place of an answer this file can work out. What it returns is the whole of what the input will push, the witness script of a p2wsh and the control block of a taproot leaf included: a caller who knows the solution holds those too, and one rule with no exceptions is what keeps the two sides from each appending the other’s element.

btclib.psbt.psbt_size.estimated_input_sizes(psbt_in: PsbtIn, tx_in: TxIn, *, sizer: Callable[[PsbtIn, TxIn], list[int] | None] | None = None) tuple[int, list[int]][source]

Return the script_sig size and the witness stack of a signed input.

The second element is the size of each element the witness stack will hold, and not the size of its serialization: the count and the length prefixes are the transaction’s layout, which Tx.serialize is the one place that knows.

A signature is assumed to be 72 bytes; an input whose type cannot be read raises, unless sizer answers for it. All three rules, and why, are in the module docstring.

btclib.psbt.psbt_utils module

Partially Signed Bitcoin Transaction (Psbt) helper functions.

https://github.com/bitcoin/bips/blob/master/bip-0174.mediawiki

btclib.psbt.psbt_utils.assert_not_a_v2_field(key_type: bytes, psbt_version: int, v2_fields: Mapping[bytes, str]) None[source]

Refuse a BIP370 field in a psbt whose version excludes it.

BIP370 lists 0 under “Versions Requiring Exclusion” for each of the twelve fields it defines, so a version 0 psbt carrying one of them is invalid rather than merely odd: the file says it is not version 2 and then carries what only version 2 has.

Filing the key under unknown instead accepts it and round-trips it byte for byte, which is the right answer for a type byte nobody has defined and the wrong one for a type byte this BIP defines and forbids here.

Version 0 alone is refused: in a version 2 psbt these type bytes are the fields the parse is looking for, and the tables are what names them there too.

btclib.psbt.psbt_utils.assert_valid_leaf_scripts(leaf_scripts: dict[bytes, tuple[bytes, int]]) None[source]

Fails when the control blocks have not the correct length.

btclib.psbt.psbt_utils.assert_valid_musig2_participant_pub_keys(participants: Mapping[bytes, Sequence[bytes]]) None[source]

Raise unless every key of a musig2_participant_pub_keys is one.

The aggregate key is the key data and the participants are the value, which is the whole of what this field can be checked for on its own: that the participants aggregate to the key they are filed under is KeyAgg’s answer, and asking it here would make a codec depend on a signing session.

btclib.psbt.psbt_utils.assert_valid_musig2_pub_key(pub_key: bytes, what: str) None[source]

Raise unless the octets are a compressed secp256k1 public key.

The length is what the four x-only vectors of BIP373 fail on. The point parse is what Bitcoin Core asks beyond it – IsFullyValid on every key of every one of these fields – and it is what makes 33 octets a key rather than 33 octets: a psbt naming an aggregate key that is not on the curve names a session no signer can join.

btclib.psbt.psbt_utils.assert_valid_musig2_session_data(session_data: Mapping[bytes, bytes], value_size: int, what: str) None[source]

Raise unless every entry of a nonce or partial signature map fits.

One rule for the two fields because BIP373 gives them one key – the participant’s key, the aggregate key, and the tapleaf hash or nothing – and they differ only in the size of what a round produced.

btclib.psbt.psbt_utils.assert_valid_psbt_version(version: Any) None[source]

Refuse a psbt version that is not one of the two there are.

Here rather than in psbt.py, where the psbt is: the version is as much an argument of PsbtIn.serialize and PsbtOut.parse, which decide by it whether the BIP370 fields belong to the map or to the unsigned transaction, and those two modules are underneath psbt.py and cannot import from it.

The type before the range, as Tx.assert_valid checks its own two int fields: a comparison against a value of no integer type raises from underneath the library, and a bool passes every one of them as one or zero – to_dict/from_dict being a json boundary, where true would be version 0 rather than a schema error.

Then the two versions there are, which is a narrower rule than “a version btclib does not know”: a psbt claiming version 3 is not a psbt of a later BIP, no such BIP being written. Version 1 is not one of them and never will be – BIP370 skipped the number because version 0 had been colloquially called version 1 while it was being designed.

btclib.psbt.psbt_utils.assert_valid_redeem_script(redeem_script: bytes) None[source]

Raise an exception if the dataclass element is not valid.

btclib.psbt.psbt_utils.assert_valid_sp_scan_key_map(map_: Mapping[bytes, bytes], value_size: int, what: str) None[source]

Raise unless every entry is a scan key against a value of its size.

One rule for the four BIP375 fields keyed that way – the global and per-input ECDH share, and the BIP374 proof of each – because they differ in nothing but the size of the value: a share is a point and a proof is two scalars.

The scan key is parsed and not merely measured, as the musig2 keys above are: a psbt filing a share under 33 octets that are no point names a recipient no address ever published, and the ECDH the value claims to be could not have been computed against it.

What is not checked here is that the value is the share it claims to be. That is BIP374’s answer, btclib.ecc.dleq.verify_proof over the input keys, and asking it here would make a codec verify a proof.

btclib.psbt.psbt_utils.assert_valid_sp_v0_info(info: bytes) None[source]

Raise unless the octets are a silent payment address’s two keys.

The scan key and the spend key of the address being paid, in the compressed form the address itself carries them in, and both parsed: an output whose keys are not points is an output no Signer can derive a script for, and the field is what a Signer derives it from.

btclib.psbt.psbt_utils.assert_valid_taproot_bip32_derivation(derivations: dict[bytes, tuple[list[bytes], BIP32KeyOrigin]]) None[source]

Fails when the public keys have not the correct length.

btclib.psbt.psbt_utils.assert_valid_taproot_internal_key(key: bytes) None[source]

Fails when the internal pubkey has not the correct length.

btclib.psbt.psbt_utils.assert_valid_taproot_script_keys(keys: list[bytes], err_msg: str) None[source]

Fails when the keys have not the correct length.

Each key is the sum of a 32byte pubkey and a 32 byte leaf hash.

btclib.psbt.psbt_utils.assert_valid_taproot_signatures(signatures: list[bytes], what: str) None[source]

Fails when a signature is not a BIP340 signature and its hash type.

BIP341 spends a taproot output with 64 bytes of signature, or 65 when the sig_hash type is not the default one: the extra byte is that type, appended. BIP371 says “64 or 65 bytes” for both PSBT_IN_TAP_KEY_SIG and PSBT_IN_TAP_SCRIPT_SIG, and the script engine’s get_hashtype already reads them that way – requiring 64 alone here would keep a signature Bitcoin Core accepts out of a psbt (issue #122).

0x00 is refused as the appended byte, as BIP341 refuses it and the engine does: SIGHASH_DEFAULT is what the 64-byte form means, so spelling it out is a second encoding of one signature.

btclib.psbt.psbt_utils.assert_valid_unknown(data: Mapping[bytes, bytes]) None[source]

Raise an exception if the dataclass element is not valid.

btclib.psbt.psbt_utils.assert_valid_witness_script(witness_script: bytes) None[source]

Raise an exception if the dataclass element is not valid.

btclib.psbt.psbt_utils.decode_dict_bytes_bytes(map_: Mapping[bytes | str | bytearray | memoryview, bytes | str | bytearray | memoryview] | None) dict[bytes, bytes][source]

Return the dataclass element from its json representation.

btclib.psbt.psbt_utils.decode_leaf_scripts(map_: Mapping[bytes | str | bytearray | memoryview, tuple[bytes | str | bytearray | memoryview, int]] | None) dict[bytes, tuple[bytes, int]][source]

Return a tap_leaf_script from its json representation.

btclib.psbt.psbt_utils.decode_musig2_participant_pub_keys(dict_: Mapping[bytes | str | bytearray | memoryview, Sequence[bytes | str | bytearray | memoryview]] | None) dict[bytes, list[bytes]][source]

Parse correctly the musig2_participant_pub_keys init argument.

btclib.psbt.psbt_utils.decode_taproot_bip32(dict_: Mapping[bytes | str | bytearray | memoryview, tuple[Sequence[bytes | str | bytearray | memoryview], BIP32KeyOrigin]] | None) dict[bytes, tuple[list[bytes], BIP32KeyOrigin]][source]

Parse correctly the tap_bip32_derivation init arguments.

btclib.psbt.psbt_utils.decode_taproot_tree(list_: Sequence[tuple[int, int, bytes | str | bytearray | memoryview]] | None) list[tuple[int, int, bytes]][source]

Return a tap_tree from its json representation.

btclib.psbt.psbt_utils.deserialize_bytes(k: bytes, v: bytes, type_: str) bytes[source]

Return the dataclass element from its binary representation.

btclib.psbt.psbt_utils.deserialize_count(k: bytes, v: bytes, type_: str) int[source]

Return the count a compact size uint value holds.

The two BIP370 counts are compact size, not fixed width, so the size check of deserialize_sized_int does not apply; what takes its place is that the whole value has to be the number. var_int.parse refuses a non-canonical encoding on its own, and octets left after it would be the same malleability by another route.

btclib.psbt.psbt_utils.deserialize_map(data: BytesIO | bytes | str | bytearray | memoryview) dict[bytes, bytes][source]

Return one map, read from the stream up to its 0x00 separator.

The separator is consumed, so the stream is left on whatever comes after the map: threading one stream through is what reads the maps of a psbt in order. The map alone is handed back, the stream not being the function’s to give – a caller passing a stream already holds it, and one passing octets has nothing left to read from anyway.

btclib.psbt.psbt_utils.deserialize_sized_int(k: bytes, v: bytes, type_: str, size: int, *, signed: bool = False) int[source]

Return the int of a little-endian value of exactly size octets.

The size is what makes the value one encoding of one number, and every fixed-width field of BIP174 and BIP370 needs it: a four-byte field written in five octets, or in one, deserializes to the same integer and serializes back to four, which is one psbt with several encodings – the malleability read_exactly refuses a level down, where the length is the map’s rather than the field’s.

There is no unsized counterpart, and that is deliberate: the BIPs define no psbt integer field without a width, the two counts of BIP370 being compact size and having deserialize_count. A helper reading a value of any length is a field boundary left to whoever writes the bytes.

signed=True for an output’s amount, the one field btclib reads as the signed integer the BIPs define. BIP370 defines the transaction version as signed too, and btclib.psbt.psbt reads it unsigned, with the reason at the two sites that do.

btclib.psbt.psbt_utils.deserialize_tx(k: bytes, v: bytes, type_: str, include_witness: bool = True, *, unsigned_template: bool = False) Tx[source]

Return the dataclass element from its binary representation.

unsigned_template=True for a PSBT’s global unsigned transaction, which is incomplete by construction and so cannot satisfy the “at least one input” and “at least one output” of Tx.assert_valid; see the docstring there. The other caller is a non-witness utxo, which is a complete transaction and gets the full check.

The parse itself is unvalidated and assert_valid called afterwards: validating on the way in would report what is wrong with the transaction where what is wrong is the value. A witness serialization of a transaction with no outputs is such a value – to be refused for the encoding this could not write back, where a validating parse answers “Missing outputs”, which is true of it but not the fault.

btclib.psbt.psbt_utils.encode_dict_bytes_bytes(dict_: Mapping[bytes, bytes]) dict[str, str][source]

Return the json representation of the dataclass element.

btclib.psbt.psbt_utils.encode_leaf_scripts(dict_: Mapping[bytes, tuple[bytes, int]]) dict[str, tuple[str, int]][source]

Return the json representation of a tap_leaf_script.

A tap_leaf_script has a control block as key, and a taproot script and leaf version as value.

btclib.psbt.psbt_utils.encode_musig2_participant_pub_keys(dict_: Mapping[bytes, Sequence[bytes]]) dict[str, list[str]][source]

Return the json representation of the musig2_participant_pub_keys.

btclib.psbt.psbt_utils.encode_taproot_tree(list_: list[tuple[int, int, bytes]]) list[tuple[int, int, str]][source]

Return the json representation of a tap_tree.

A tapree is a list of depth, leaf version, and taproot script.

btclib.psbt.psbt_utils.parse_leaf_script(v: bytes) tuple[bytes, int][source]

Split the script and the leaf version.

BIP371 writes the value of a PSBT_IN_TAP_LEAF_SCRIPT as the script followed by the one byte of its leaf version, so an empty value is not a zero-length script: it is a record without the only field it is required to carry, which v[-1] would answer with an IndexError.

btclib.psbt.psbt_utils.parse_musig2_participant_pub_keys(v: bytes) list[bytes][source]

Return the participants of one aggregate key, in aggregation order.

BIP373 writes the value as the participants’ compressed keys concatenated, so a value that is not a whole number of keys is not a list of them, and an empty one is a field naming an aggregate key with no participants – which KeyAgg has no answer for.

A list and not a set: the order is the order aggregation was done in, which decides the aggregate key, and BIP327’s KeyAgg is where a reordering stops being the same key.

btclib.psbt.psbt_utils.parse_taproot_bip32(v: bytes) tuple[list[bytes], BIP32KeyOrigin][source]

Return a tap_bip32_derivation from its bytes representation.

btclib.psbt.psbt_utils.parse_taproot_tree(v: bytes) list[tuple[int, int, bytes]][source]

Return a tap_tree from its bytes representation.

btclib.psbt.psbt_utils.serialize_bytes(type_: bytes, value: bytes) bytes[source]

Return the binary representation of the dataclass element.

btclib.psbt.psbt_utils.serialize_count(type_: bytes, count: int) bytes[source]

Return the binary representation of a compact size uint field.

btclib.psbt.psbt_utils.serialize_dict_bytes_bytes(type_: bytes, dictionary: Mapping[bytes, bytes]) bytes[source]

Return the binary representation of the dataclass element.

btclib.psbt.psbt_utils.serialize_hd_key_paths(type_: bytes, hd_key_paths: Mapping[bytes, BIP32KeyOrigin]) bytes[source]

Return the binary representation of the dataclass element.

btclib.psbt.psbt_utils.serialize_leaf_scripts(type_: bytes, dictionary: dict[bytes, tuple[bytes, int]]) bytes[source]

Return the binary representation of the tap_leaf_script.

btclib.psbt.psbt_utils.serialize_musig2_participant_pub_keys(type_: bytes, dict_: Mapping[bytes, Sequence[bytes]]) bytes[source]

Return the binary representation of the musig2_participant_pub_keys.

btclib.psbt.psbt_utils.serialize_sized_int(type_: bytes, value: int, size: int, *, signed: bool = False) bytes[source]

Return the binary representation of a fixed-size integer field.

btclib.psbt.psbt_utils.serialize_taproot_bip32(type_: bytes, dict_: dict[bytes, tuple[list[bytes], BIP32KeyOrigin]]) bytes[source]

Return the binary representation of the tap_bip32_derivation.

btclib.psbt.psbt_utils.serialize_taproot_tree(type_: bytes, list_: list[tuple[int, int, bytes]]) bytes[source]

Return the binary representation of the tap_tree.

btclib.psbt.psbt_utils.taproot_bip32_from_dict(taproot_hd_key_paths: list[dict[str, str]], *, check_validity: bool = True) dict[bytes, tuple[list[bytes], BIP32KeyOrigin]][source]

Return a tap_bip32_derivation from its json representation.

btclib.psbt.psbt_utils.taproot_bip32_to_dict(taproot_hd_key_paths: dict[bytes, tuple[list[bytes], BIP32KeyOrigin]]) list[dict[str, Any]][source]

Return the json representation of a tap_bip32_derivation.

A tap_bip32_derivation is a list of leaf_hashes, master fingerprint, derivation path.

btclib.psbt.psbt_view module

A psbt read from a stream one map at a time, for a signer with no room.

Psbt is the whole object: Psbt.parse reads every map before anything can be inspected or signed, so a psbt whose inputs carry a previous transaction each costs all of them at once. A PsbtView reads the same psbt out of a seekable stream and keeps no map at all – it walks the stream once to learn where each one begins, and reads one when it is asked for it. diybitcoinhardware/embit calls the same idea psbtview.PSBTView and states its audience, which is this one: hardware wallets and airgapped signers, where the psbt can be larger than the memory available to hold it (issue #647).

This is a reader, beside Psbt and not instead of it. Most callers want the object model – combining, finalizing, extracting and sign all rewrite a psbt, and none of that is here. What is here is what a Signer needs before it signs: the global fields, one input or output map at a time, and the two messages a signature commits to. The signer’s answer is its own to assemble, one PsbtIn per input it signed and PsbtIn.serialize how each is written, the stream this reads from being read-only.

What it holds, exactly. At construction: the global map, which is the transaction version and the counts, the xpubs, and in a version 0 psbt the unsigned transaction; and one integer per input and output map, being where in the stream it starts. Asked for a sig_hash it builds two more things and keeps them, because BIP143 and BIP341 commit every input to them: the unsigned transaction, and the output each input spends – read one input at a time, so that the previous transaction a non-witness utxo carries is dropped as soon as the one output being spent is out of it. With them it keeps sig_hash.PrecomputedTxData, the five 32-byte hashes BIP341 commits to, so that signing N inputs hashes the whole transaction once instead of N times (issue #164 for the whole-object half of the same arithmetic).

What it therefore never holds is two input maps at once, which is where a psbt’s size is: a witness utxo is an amount and a script, a non-witness utxo is a whole transaction, and the derivation paths, leaf scripts and signatures of every input are each as large as the wallet that wrote them.

The stream must not change while the view is alive. A view answers from the bytes that are there when it reads them, and it reads on request: nothing re-reads a map to check that it says what it said, and nothing invalidates what has already been read. So a stream that changes under a view is a view that gives two answers to one question – and, if what changed is an amount or a script, a sig_hash committing to a transaction the signer was never shown, which is a time-of-check to time-of-use attack and not merely a stale read. embit’s own docstring gives the same warning about an SD card, whose controller answers each read separately. A caller reading a psbt from removable or untrusted storage copies it into memory it controls first; a caller whose stream has legitimately changed builds a new view, there being no way to refresh one.

The stream is any seekable binary one – a file object as much as a BytesIO – which is wider than alias.BinaryData, and deliberately: a parse consumes what it is given, so a BytesIO is all it can want, while a view over a BytesIO holds the whole psbt in memory and answers the question this module exists for with “buy more memory”. Octets are taken too, and read as one whole psbt: what follows the last map in them is refused, where what follows in a caller’s stream is the caller’s.

Not a parse classmethod, for the same reason it is not a dataclass: what this returns is not the psbt those bytes encode but a handle on the stream holding it, and the two are not interchangeable – the second is only valid while the first is unchanged. The properties Psbt.parse owes its caller are still checked where they are about the bytes: the walk at construction refuses a truncated psbt, one map at a time, and PsbtIn.parse reads each map from exactly the octets the walk bounded.

class btclib.psbt.psbt_view.PsbtView(data: BinaryIO | bytes | str | bytearray | memoryview)[source]

Bases: object

A read-only view of one psbt in a seekable stream.

The global fields are attributes, being the whole of what is read eagerly; input and output read one map from the stream each time they are called, tx and prevouts what a transaction is built out of, and ecdsa_sig_hash and taproot_sig_hash the two messages a Signer signs. The module docstring says what is kept between calls, and what a stream that changes underneath costs.

ecdsa_sig_hash(vin_i: int, *, hash_type: int | None = None) bytes[source]

Return the hash an ECDSA spend of one input signs.

psbt.ecdsa_sig_hash over a stream, and the same rules: the type the input asks for by default, SIGHASH_ALL when it asks for none, and SIGHASH_DEFAULT refused. The input is read now and the transaction is the one built once, so nothing is held past the answer but what the module docstring lists.

input(i: int, *, check_validity: bool = True) PsbtIn[source]

Return input i, read from the stream now and held by nothing.

Complete whatever the version: a version 0 psbt keeps the outpoint and the sequence of every input in its unsigned transaction, so this input’s are written into the map that does not carry them, exactly as Psbt.parse does for all of them at once.

check_validity asks of the map what Psbt.assert_valid asks of every input – the fields this version requires of it, and the previous transaction being the one the outpoint names – which are the questions about one input alone. What it cannot ask is the rest: this is one map, and a psbt is valid or not as a whole.

property lock_time: int

Return the lock time of the transaction being built.

BIP370’s, which _lock_time is the algorithm of: the inputs are streamed past it, one at a time, being read for the two fields it asks about and dropped. A version 0 psbt states it once, in its unsigned transaction, and none of its inputs may require one – _assert_valid_input_fields is where such an input is refused, when the map it is in is read.

output(i: int, *, check_validity: bool = True) PsbtOut[source]

Return output i, read from the stream now and held by nothing.

The output maps follow the input maps in a psbt, so this seeks past every input; input says what check_validity covers and what it cannot.

property prevouts: list[TxOut]

Return the output each input spends, psbt.prevouts over a stream.

A copy, for the reason tx is one. An input carrying no utxo raises: a taproot signature commits to the amount and script of every input, so one missing utxo leaves the whole transaction unsignable rather than one input of it.

taproot_sig_hash(vin_i: int, *, leaf_hash: bytes | str | bytearray | memoryview = b'', hash_type: int | None = None) bytes[source]

Return the hash a taproot spend of one input signs.

psbt.taproot_sig_hash over a stream, and the same rules: BIP341 for a key path spend, BIP342 for the script path a tapleaf hash names, the type the input asks for by default and SIGHASH_DEFAULT when it asks for none, and an empty annex. What the stream buys is the precomputation: the whole-transaction hashes are the same for every input, so they are built once and this is where they are handed over.

property tx: Tx

Return the unsigned transaction this psbt is of.

A copy, as Psbt.tx is one: what is written into it is written into nothing – not into the stream, which this cannot write, and not into what the next sig_hash commits to.

Module contents

Partially signed bitcoin transactions and the BIP174/BIP370 roles.

What this package exports is the format and the roles: the three maps a psbt is made of, the Combiner, the Finalizer, the Extractor, the outputs an unsigned psbt spends, the two messages a Signer signs and sign which plays the role over a KeyManager’s answers, and the size estimation a fee rate is applied to. musig2 is named as a module, being the BIP373 role rather than one function, the way btclib.ecc names dsa; so is silent_payments, BIP375’s two roles over the fields BIP375 adds – what a Signer writes into a psbt paying a silent payment address, and what a Transaction Extractor has to recompute before it hands the bytes over.

PsbtView is the same psbt read a map at a time out of a stream, for a signer with less memory than the psbt takes (issue #647). It is beside Psbt and not another way to spell it: it reads, where every role above rewrites, and what it hands back is the psbt’s maps one by one rather than the psbt. Its module docstring states what it holds between calls and what a stream that changes underneath it costs.

assert_signatures_only is the check that belongs before combine when the psbt being merged came from somebody else. BIP174 gives the Combiner no such role – it takes the union of what it is given and may resolve a conflict by picking either side – so a caller merging an external signer’s answer has to hold it to the request first. new_signers is what the same caller reads off that answer before merging it: which wallets it adds the signatures of, which the union no longer says.

assert_signed is the other question about a signature, and about the psbt rather than about an answer to a request: every signature it carries verifies, and every input carries one. Neither of the two roles that read a signature answers it – a request nobody signed passes assert_signatures_only unchanged, and finalize reads a signature it cannot verify as one that is not there.

ecdsa_sig_hash and taproot_sig_hash are here for the same reason prevouts is: sign needs the message before it needs anything else, and a caller playing the Signer by hand – writing a signature into a psbt without going through sign – needs it too, KeyManager not being the only way to hold a key.

btclib.psbt.psbt_utils is not exported, and this is where that decision is recorded: serialize_bytes, deserialize_map, deserialize_tx, the encode/decode_dict_bytes_bytes pair, serialize_dict_bytes_bytes, serialize_hd_key_paths and assert_valid_unknown are how one field of one map is written and read. They are called by psbt_in, psbt_out and psbt itself and by nothing outside this package, and there were more of them in __all__ than there were names for the format – so a caller reading btclib.psbt was offered the plumbing of a file format ahead of the psbt. Each is importable from the module that defines it, which is where the test suite takes the other half of that module from already.

class btclib.psbt.KeyManager(*args, **kwargs)[source]

Bases: Protocol

A signature over a hash, by public key or origin: what sign asks.

sign has no key of its own; a KeyManager is where the keys are, and each method answers what sign cannot – is this key one you hold, and if so, what does it sign msg_hash with. None answers “not mine” rather than raising, which is what lets one signer of an m-of-n answer for its own key alone: an input none of its keys can answer for is somebody else’s turn, not an error.

Both pub_key and origin travel on every call, in the order a psbt itself gives them precedence: a key match is a fact, an origin only a claim about a four-byte fingerprint, which collides. The claim is what a watch-only-shaped manager – one xprv, many children it has never derived – needs to answer at all, having no way to recognize a child key it has not yet computed.

What comes back is the bare signature – DER for ECDSA, 64 bytes r||s for schnorr – with no sig_hash type appended. sign appends it, being the one that fixed the hash and therefore the type the signature answers for; the secret stays inside the manager, which is what lets a hardware backend implement this same contract without sign ever holding what signs for it.

sign_ecdsa(pub_key: bytes, origin: BIP32KeyOrigin | None, msg_hash: bytes) bytes | None[source]

Return the DER signature of msg_hash by pub_key, or None.

sign_schnorr(pub_key: bytes, origin: BIP32KeyOrigin | None, msg_hash: bytes, merkle_root: bytes) bytes | None[source]

Return the BIP340 signature of msg_hash by pub_key, or None.

pub_key is the taproot internal key, x-only and untweaked, and the signature has to be the tweaked output key’s: merkle_root is PSBT_IN_TAP_MERKLE_ROOT, empty for a key-path-only output, and tweaking by it is the manager’s to do, sign never holding what tweaking a private key needs.

sign_schnorr_script_path(pub_key: bytes, origin: BIP32KeyOrigin | None, msg_hash: bytes, leaf_hash: bytes) bytes | None[source]

Return the BIP342 signature of msg_hash by pub_key, or None.

The other half of a taproot spend, and the one method here whose key signs as it is: pub_key is a key of the leaf script, x-only, and a script path proves the leaf rather than the output key – the tweak is what the control block carries, so there is nothing for the manager to apply and no merkle root to apply it by.

leaf_hash is which leaf asked. One key can sit in more than one, each leaf is a different message and a different entry of PSBT_IN_TAP_SCRIPT_SIG, and a manager with a policy about which conditions it signs under has only this to recognize them by.

class btclib.psbt.Psbt(tx_version: int, inputs: Sequence[PsbtIn], outputs: Sequence[PsbtOut], version: int, hd_key_paths: Mapping[bytes | str | bytearray | memoryview, BIP32KeyOrigin], unknown: Mapping[bytes | str | bytearray | memoryview, bytes | str | bytearray | memoryview] | None = None, fallback_lock_time: int | None = None, tx_modifiable: int | None = None, signed_message: bytes | str | bytearray | memoryview | None = None, sp_ecdh_shares: Mapping[bytes | str | bytearray | memoryview, bytes | str | bytearray | memoryview] | None = None, sp_dleq_proofs: Mapping[bytes | str | bytearray | memoryview, bytes | str | bytearray | memoryview] | None = None, *, check_validity: bool = True)[source]

Bases: object

A partially signed bitcoin transaction, BIP174 and BIP370.

Both versions are held BIP370’s way – the transaction’s fields live in the psbt and tx computes the unsigned transaction; the module docstring says why. The global fields are here, each input’s and output’s in its PsbtIn or PsbtOut; the wire form is serialize and parse, the customary text form b64encode and b64decode.

assert_signable() None[source]

Assert that every input carries what a Signer needs.

Valid and signable are different questions, and BIP174 answers only the first: it lists two psbts with no inputs as valid, and assert_valid accepts them. This one is the Signer’s pre-flight, so it answers the second, and a psbt with nothing to sign is not signable.

The check has to be explicit because every check below is per input: without it an empty vin passes the loop vacuously, and a caller doing assert_signable() and then looping over the inputs signs none of them and is told nothing.

assert_valid() None[source]

Assert logical self-consistency.

Two questions, and the version answers the first: which fields this psbt must have and which it must not, BIP370 giving each of its twelve a “Versions Requiring Inclusion” and a “Versions Requiring Exclusion”. The second is what the fields hold, which is the same question in both versions – an outpoint is an outpoint whether it was read from an input map or from an unsigned transaction.

classmethod b64decode(psbt_str: bytes | str | bytearray | memoryview, *, check_validity: bool = True) Psbt[source]

Build a Psbt from its base64 text, stripping whitespace.

The coercion before the strip, as bms.Sig.b64decode does it and for the reason issue #814 gives: without it, what is neither text nor bytes reaches base64.b64decode untouched, and is left facing its own “argument should be a bytes-like object or ASCII string” – a complaint about a builtin rather than about the psbt that was passed.

b64encode(*, check_validity: bool = True) str[source]

Return the serialization as base64 text, BIP174’s file form.

property estimated_vsize: int

Return the virtual size the transaction will have once signed.

The name Bitcoin Core’s analyzepsbt reports it under, and the Tx.vsize arithmetic: a quarter of the weight, rounded up.

property estimated_weight: int

Return the weight the transaction will have once signed.

A signature is assumed to be 72 bytes, the largest a low-s one can be with its sig_hash byte, so the answer is an upper bound; an input whose type the psbt does not determine has no estimate and raises, naming itself. Both rules are psbt_size’s, and why each is what it is, is there.

Tx.weight is what the placeholders below are handed to: a signature is bytes wherever it goes, and how many of them a transaction is once they are in place is one arithmetic, written once, in the class whose serialization it is.

classmethod from_dict(dict_: Mapping[str, Any], *, check_validity: bool = True) Psbt[source]

Build a Psbt from the dict shape to_dict writes.

classmethod from_tx(tx: Tx, inputs: Sequence[PsbtIn] | None = None, outputs: Sequence[PsbtOut] | None = None, *, check_validity: bool = True) Psbt[source]

Return the version 0 psbt of a transaction, Creator-style.

The transaction is taken apart into the fields the psbt holds, which is the same conversion parse makes: one input map per input and one output map per output, each carrying what the transaction said about it.

inputs and outputs are the maps to fill, for a caller who already has them – a Combiner or an Updater – and empty ones otherwise, which is what a Creator starts from.

property has_sig_hash_single: bool

Return whether a SIGHASH_SINGLE signature pins input to output.

Bit 2 of PSBT_GLOBAL_TX_MODIFIABLE. Such a signature commits to the output at the signed input’s own index, so the pairing is positional: adding, removing or reordering either side breaks it, whatever the two modifiable bits say.

property inputs_modifiable: bool

Return whether a Constructor may add or remove an input.

Bit 0 of PSBT_GLOBAL_TX_MODIFIABLE. A version 2 psbt with no such field says no: “A Constructor may choose to declare that no further inputs and outputs can be added to the transaction by setting the appropriate bits … to 0 or by removing the field entirely”. Version 0 has no field to consult and no Constructor role either, so it answers yes and nothing changes for it.

property lock_time: int

Return the lock time of the transaction being built.

Computed, never stored: BIP370 makes it the answer to the inputs’ required lock times, with the fallback for a psbt whose inputs require none – which is every version 0 psbt, its unsigned transaction’s nLockTime being read into the fallback. _lock_time is the algorithm.

property outputs_modifiable: bool

Return whether a Constructor may add or remove an output.

Bit 1 of PSBT_GLOBAL_TX_MODIFIABLE; inputs_modifiable says what an absent field and a version 0 psbt answer.

classmethod parse(data: BytesIO | bytes | str | bytearray | memoryview, *, check_validity: bool = True) Psbt[source]

Return a Psbt by parsing binary data.

A psbt ends at the separator of its last map. A stream is left right there, what follows in it being the caller’s, so a psbt can be read out of a stream that carries more than the psbt; octets are the whole of one, so anything after it is refused. Bitcoin Core splits the two the same way, between PSBTInput::Unserialize and DecodeRawPSBT’s “extra data after PSBT”.

serialize(*, check_validity: bool = True) bytes[source]

Return the psbt as the bytes of the version it declares.

Version 0 writes the unsigned transaction its fields make and nothing else of BIP370; version 2 writes those fields and no transaction. to_v0 and to_v2 are the conversions between the two, and neither is done here: what a psbt is written as is what it says it is.

sort_inputs(ordering_func: Callable[[PsbtIn], int] | None = None) None[source]

Sort psbt inputs.

sorting logic is ordering_func if present, shuffle otherwise.

A version 2 psbt is asked first: reordering the inputs is a change to the transaction every signature commits to, so it is one the Inputs Modifiable flag has to allow. _assert_modifiable is where the two flags are read.

sort_outputs(ordering_func: Callable[[PsbtOut], int] | None = None) None[source]

Sort psbt outputs.

sorting logic is ordering_func if present, shuffle otherwise.

The Outputs Modifiable flag is what allows it in a version 2 psbt, as the Inputs Modifiable one allows sort_inputs.

to_dict(*, check_validity: bool = True) dict[str, Any][source]

Return the psbt as a dict of json-friendly values.

The “tx” entry is derived for the reader and ignored by from_dict, the comment on it saying why; everything else round-trips.

to_v0() Psbt[source]

Return this psbt as the version 0 psbt of the same transaction.

What version 0 cannot say is dropped, and the transaction is unchanged by the dropping: the computed lock time becomes the fallback, which is where a version 0 psbt keeps its nLockTime, so the inputs’ required lock times go with nothing lost from the transaction – only the record of which input required what. The modifiable flags go too, version 0 having no Constructor to obey them.

A psbt whose inputs require both kinds of lock time has no transaction to be the version 0 psbt of, and raises here as it does anywhere else its lock time is asked for.

to_v2() Psbt[source]

Return this psbt as the version 2 psbt of the same transaction.

Nothing but the version number: every field version 2 writes is already held, this being how btclib holds a psbt of either version, so the conversion the other way is the one with work to do. What was the unsigned transaction’s nLockTime is written as the fallback, which is what it is – no input of a version 0 psbt requires a lock time, none being able to say so.

property tx: Tx

Return the unsigned transaction this psbt is of.

Computed from the fields each time, so it is a copy and not the psbt: what is written into it is written into nothing, and an outpoint or a sequence is changed on the input that holds it. The transaction is the psbt’s serialization in version 0 and nowhere at all in version 2, which is why it cannot be the field the rest hangs off.

property unique_id: bytes

Return the identifier BIP370 gives this psbt.

The txid of the unsigned transaction with every sequence set to 0: an Updater may set PSBT_IN_SEQUENCE, so two psbts of one transaction can disagree about it, and the identifier must not. It is what a Combiner compares – combine does – rather than tx.id, which for a version 2 psbt would call the same transaction two.

A silent payment output enters it as its address rather than as its script, which BIP375 adds for the same reason BIP370 zeroes the sequences: the script is computed later, so a psbt before and after that computation would otherwise be two. _identifying_script is the substitution.

vsize_estimate(sizer: Callable[[PsbtIn, TxIn], list[int] | None] | None = None) int[source]

Return the virtual size once signed, asking a sizer where needed.

estimated_vsize over weight_estimate, so that a fee computed from a caller’s own solution sizes is the same arithmetic as one computed from this library’s.

weight_estimate(sizer: Callable[[PsbtIn, TxIn], list[int] | None] | None = None) int[source]

Return the weight once signed, asking a sizer where needed.

What estimated_weight is, with the one thing a property cannot take: a psbt_size.SolutionSizer, for the inputs this library refuses to estimate because what they will push is knowledge only the caller has – a script of no standard type, a taproot script path. Without one this is that property exactly.

Validated first, as every other method that reads this psbt’s data is: an estimate off an incoherent psbt is a number, and a number is what a caller sizes a fee with.

class btclib.psbt.PsbtIn(non_witness_utxo: Tx | None = None, witness_utxo: TxOut | None = None, partial_sigs: Mapping[bytes | str | bytearray | memoryview, bytes | str | bytearray | memoryview] | None = None, sig_hash_type: Literal[0, 1, 2, 3, 129, 130, 131] | None = None, redeem_script: bytes | str | bytearray | memoryview = b'', witness_script: bytes | str | bytearray | memoryview = b'', hd_key_paths: Mapping[bytes | str | bytearray | memoryview, BIP32KeyOrigin] | None = None, final_script_sig: bytes | str | bytearray | memoryview = b'', final_script_witness: Witness | None = None, ripemd160_preimages: Mapping[bytes | str | bytearray | memoryview, bytes | str | bytearray | memoryview] | None = None, sha256_preimages: Mapping[bytes | str | bytearray | memoryview, bytes | str | bytearray | memoryview] | None = None, hash160_preimages: Mapping[bytes | str | bytearray | memoryview, bytes | str | bytearray | memoryview] | None = None, hash256_preimages: Mapping[bytes | str | bytearray | memoryview, bytes | str | bytearray | memoryview] | None = None, taproot_key_spend_signature: bytes | str | bytearray | memoryview = b'', taproot_script_spend_signatures: Mapping[bytes | str | bytearray | memoryview, bytes | str | bytearray | memoryview] | None = None, taproot_leaf_scripts: Mapping[bytes | str | bytearray | memoryview, tuple[bytes | str | bytearray | memoryview, int]] | None = None, taproot_hd_key_paths: Mapping[bytes | str | bytearray | memoryview, tuple[list[bytes | str | bytearray | memoryview], BIP32KeyOrigin]] | None = None, taproot_internal_key: bytes | str | bytearray | memoryview = b'', taproot_merkle_root: bytes | str | bytearray | memoryview = b'', unknown: Mapping[bytes | str | bytearray | memoryview, bytes | str | bytearray | memoryview] | None = None, previous_tx_id: bytes | str | bytearray | memoryview = b'', output_index: int | None = None, sequence: int | None = None, required_time_lock_time: int | None = None, required_height_lock_time: int | None = None, musig2_participant_pub_keys: Mapping[bytes | str | bytearray | memoryview, Sequence[bytes | str | bytearray | memoryview]] | None = None, musig2_pub_nonces: Mapping[bytes | str | bytearray | memoryview, bytes | str | bytearray | memoryview] | None = None, musig2_partial_sigs: Mapping[bytes | str | bytearray | memoryview, bytes | str | bytearray | memoryview] | None = None, sp_ecdh_shares: Mapping[bytes | str | bytearray | memoryview, bytes | str | bytearray | memoryview] | None = None, sp_dleq_proofs: Mapping[bytes | str | bytearray | memoryview, bytes | str | bytearray | memoryview] | None = None, *, check_validity: bool = True)[source]

Bases: object

The per-input map of a psbt: one field per BIP174/BIP370 key type.

What each role fills in for one input on its way to a signature – the spent output or the transaction holding it, scripts, hd paths, partial and taproot signatures, preimages, the finalized script or witness – with what no key type names kept in unknown. A field a psbt does not carry is None, or empty for the collection types.

assert_valid() None[source]

Assert logical self-consistency.

The BIP370 fields are checked for what they hold and not for whether they are there: which of them an input must carry is the psbt’s version, which an input on its own does not know, so Psbt.assert_valid asks that question and this one answers what an input can be asked alone.

classmethod from_dict(dict_: Mapping[str, Any], *, check_validity: bool = True) PsbtIn[source]

Build a PsbtIn from the dict shape to_dict writes.

classmethod parse(data: BytesIO | bytes | str | bytearray | memoryview, *, psbt_version: int = 0, check_validity: bool = True) PsbtIn[source]

Return a PsbtIn by parsing binary data.

One map is read, its terminator included, which leaves the stream on the input after this one.

psbt_version is the version of the psbt the map belongs to, which decides whether a BIP370 type byte is a field of this input or one this version must not carry; an input read on its own is read as version 0, the version BIP174 defines. Asked for as serialize asks for it, and for the same reason.

Octets are one whole input and a stream is the caller’s, as they are for the psbt these maps make: Psbt.parse threads one stream through the inputs and the outputs, and what follows an input in it is the next one.

property prev_out: OutPoint

Return the outpoint this input spends.

The two fields as the one value every other btclib caller takes, TxIn.prev_out included; an input that does not carry both is an input Psbt.assert_valid refuses, and OutPoint says so here.

serialize(*, psbt_version: int = 0, check_validity: bool = True) bytes[source]

Return the binary representation of the input map.

psbt_version is the version of the psbt the map belongs to, and it decides whether the BIP370 fields are written here or folded into the psbt’s unsigned transaction; an input serialized on its own is written as version 0, the version BIP174 defines. It is asked for, and not read for its truth: every version that is not 0 wrote the BIP370 fields, so a None or a 3 wrote a version 2 input and said nothing.

property sig_hash: int

Return the sig_hash as int.

For compatibility with PartiallySignedInput.

to_dict(*, check_validity: bool = True) dict[str, Any][source]

Return the input map as a dict of json-friendly values.

Keys are hex, hd paths are BIP174’s bip32_derivs shape; from_dict reads the same shape back.

class btclib.psbt.PsbtOut(redeem_script: bytes | str | bytearray | memoryview = b'', witness_script: bytes | str | bytearray | memoryview = b'', hd_key_paths: Mapping[bytes | str | bytearray | memoryview, BIP32KeyOrigin] | None = None, taproot_internal_key: bytes | str | bytearray | memoryview = b'', taproot_tree: Sequence[tuple[int, int, bytes | str | bytearray | memoryview]] | None = None, taproot_hd_key_paths: Mapping[bytes | str | bytearray | memoryview, tuple[list[bytes], BIP32KeyOrigin]] | None = None, unknown: Mapping[bytes | str | bytearray | memoryview, bytes | str | bytearray | memoryview] | None = None, amount: int | None = None, script_pub_key: bytes | str | bytearray | memoryview = b'', musig2_participant_pub_keys: Mapping[bytes | str | bytearray | memoryview, Sequence[bytes | str | bytearray | memoryview]] | None = None, sp_v0_info: bytes | str | bytearray | memoryview = b'', sp_v0_label: int | None = None, *, check_validity: bool = True)[source]

Bases: object

The per-output map of a psbt: one field per BIP174/BIP370 key type.

The scripts and hd paths that let a wallet recognize an output as its own, BIP370’s amount and script_pub_key of the output being built, and what no key type names in unknown. A field a psbt does not carry is None, or empty for the collection types.

assert_valid() None[source]

Assert logical self-consistency.

The two BIP370 fields are checked for what they hold and not for whether they are there: which of them an output must carry is the psbt’s version, which an output on its own does not know, so Psbt.assert_valid asks that question (PsbtIn.assert_valid says the same of the five fields of an input).

classmethod from_dict(dict_: Mapping[str, Any], *, check_validity: bool = True) PsbtOut[source]

Build a PsbtOut from the dict shape to_dict writes.

classmethod parse(data: BytesIO | bytes | str | bytearray | memoryview, *, psbt_version: int = 0, check_validity: bool = True) PsbtOut[source]

Return a PsbtOut by parsing binary data.

One map is read, its terminator included, which leaves the stream on the output after this one.

psbt_version is the version of the psbt the map belongs to, which decides whether a BIP370 type byte is a field of this output or one this version must not carry; an output read on its own is read as version 0, the version BIP174 defines. Asked for as serialize asks for it, and for the same reason.

Octets are one whole output and a stream is the caller’s, as they are for the psbt these maps make: Psbt.parse threads one stream through the inputs and the outputs, and what follows an output in it is the next one.

serialize(*, psbt_version: int = 0, check_validity: bool = True) bytes[source]

Return the binary representation of the output map.

psbt_version is the version of the psbt the map belongs to, and it decides whether the two BIP370 fields are written here or folded into the psbt’s unsigned transaction; an output serialized on its own is written as version 0, the version BIP174 defines. It is asked for, and not read for its truth: every version that is not 0 wrote the BIP370 fields, so a None or a 3 wrote a version 2 output and said nothing.

to_dict(*, check_validity: bool = True) dict[str, Any][source]

Return the output map as a dict of json-friendly values.

Keys are hex, hd paths are BIP174’s bip32_derivs shape; from_dict reads the same shape back.

class btclib.psbt.PsbtView(data: BinaryIO | bytes | str | bytearray | memoryview)[source]

Bases: object

A read-only view of one psbt in a seekable stream.

The global fields are attributes, being the whole of what is read eagerly; input and output read one map from the stream each time they are called, tx and prevouts what a transaction is built out of, and ecdsa_sig_hash and taproot_sig_hash the two messages a Signer signs. The module docstring says what is kept between calls, and what a stream that changes underneath costs.

ecdsa_sig_hash(vin_i: int, *, hash_type: int | None = None) bytes[source]

Return the hash an ECDSA spend of one input signs.

psbt.ecdsa_sig_hash over a stream, and the same rules: the type the input asks for by default, SIGHASH_ALL when it asks for none, and SIGHASH_DEFAULT refused. The input is read now and the transaction is the one built once, so nothing is held past the answer but what the module docstring lists.

input(i: int, *, check_validity: bool = True) PsbtIn[source]

Return input i, read from the stream now and held by nothing.

Complete whatever the version: a version 0 psbt keeps the outpoint and the sequence of every input in its unsigned transaction, so this input’s are written into the map that does not carry them, exactly as Psbt.parse does for all of them at once.

check_validity asks of the map what Psbt.assert_valid asks of every input – the fields this version requires of it, and the previous transaction being the one the outpoint names – which are the questions about one input alone. What it cannot ask is the rest: this is one map, and a psbt is valid or not as a whole.

property lock_time: int

Return the lock time of the transaction being built.

BIP370’s, which _lock_time is the algorithm of: the inputs are streamed past it, one at a time, being read for the two fields it asks about and dropped. A version 0 psbt states it once, in its unsigned transaction, and none of its inputs may require one – _assert_valid_input_fields is where such an input is refused, when the map it is in is read.

output(i: int, *, check_validity: bool = True) PsbtOut[source]

Return output i, read from the stream now and held by nothing.

The output maps follow the input maps in a psbt, so this seeks past every input; input says what check_validity covers and what it cannot.

property prevouts: list[TxOut]

Return the output each input spends, psbt.prevouts over a stream.

A copy, for the reason tx is one. An input carrying no utxo raises: a taproot signature commits to the amount and script of every input, so one missing utxo leaves the whole transaction unsignable rather than one input of it.

taproot_sig_hash(vin_i: int, *, leaf_hash: bytes | str | bytearray | memoryview = b'', hash_type: int | None = None) bytes[source]

Return the hash a taproot spend of one input signs.

psbt.taproot_sig_hash over a stream, and the same rules: BIP341 for a key path spend, BIP342 for the script path a tapleaf hash names, the type the input asks for by default and SIGHASH_DEFAULT when it asks for none, and an empty annex. What the stream buys is the precomputation: the whole-transaction hashes are the same for every input, so they are built once and this is where they are handed over.

property tx: Tx

Return the unsigned transaction this psbt is of.

A copy, as Psbt.tx is one: what is written into it is written into nothing – not into the stream, which this cannot write, and not into what the next sig_hash commits to.

btclib.psbt.assert_signatures_only(request: Psbt, returned: Psbt) None[source]

Raise unless returned is request with signatures added, and no more.

What a caller has to know before merging an answer from somebody else – an external signer, a hardware device, a cosigner – because combine will not tell them: BIP174’s Combiner takes the union of what it is given and may resolve a conflict by picking either side, so it compares only the psbt’s identifier and merges the rest. Which leaves three ways for an answer to change what was sent: adding a field the request left empty, overwriting one entry of a field that is a map, and – in a version 2 psbt, whose identifier zeroes every sequence – changing a sequence.

The rule here is one sentence. Everything that is not a signature comes back as it was sent; the signature fields may only gain entries; every signature that arrived is verified before anything is merged. _SIGNATURE_FIELDS is the second clause, _assert_unchanged the first, and both walk the fields a map declares rather than a list kept beside them.

The transaction being signed is compared whole, Psbt.tx being computed from the fields of either version: that is what catches the changed sequence of a version 2 psbt, which unique_id cannot see, and it makes the input and output counts equal without a check of their own.

tx_modifiable is the one field an answer may legitimately change, a Signer clearing a bit when it adds a signature that a change would break. What it may not do is loosen one, and the rule for that is _combined_tx_modifiable’s already: an answer no more permissive than the request is one the two combine into unchanged.

The two musig2 maps are permitted additions and are not verified here. A BIP327 partial signature is checked against the session’s aggregate nonce, which needs every participant’s nonce, and a psbt mid-session need not carry them yet; musig2.partial_sigs_agg refuses an aggregate that does not verify, which is the check that can be made once the session is complete.

btclib.psbt.assert_signed(psbt: Psbt, *, allow_partial: bool = False) None[source]

Raise unless every input is signed and every signature verifies.

The question between the two roles that read a signature and answer something else. assert_signatures_only holds an answer to the request it came from, and a request nobody signed comes back unchanged and passes; finalize builds a spend out of whatever satisfies the script, and reads a signature it cannot verify as one that is not there. So a caller storing a psbt as complete, or about to finalize one, has this to ask: is every signature this psbt carries a signature of this transaction by the key it is filed under, and does every input carry one.

Both halves matter and neither implies the other. A signature that does not verify is a psbt built, merged or transcribed wrong, whatever the count says; an input with no signature at all is a spend that will not relay, however good the signatures beside it are. allow_partial keeps the first half and drops the second, for a psbt of a signing session still going round: a device holding keys for some inputs only – one psbt spanning several wallets – leaves the others untouched, and which of the two a psbt is is the caller’s to say and not this function’s to guess. It is refused rather than read for its truth: its True is the permissive value, so the misreading a non-bool always makes – allow_partial=”false” out of a configuration file – is the one that stores as complete a psbt with an input nobody signed.

What neither half asks is whether an input is satisfied: one signature of a 2-of-2 is an input signed, and whether a spend can be built out of what the input holds is finalize’s answer, over the script and not over a count.

Verification is _assert_ecdsa_sigs_verify’s and _assert_taproot_sigs_verify’s, so it covers every input kind either signature kind belongs to, and the sig_hash type each commits to must be the type the input asks for. An input that does not say what was signed – no utxo, no redeem script, no witness script – is refused here rather than skipped: the Finalizer’s leniency is for a role that has the finalized scripts to fall back on, while a caller asking this is asking about the signatures themselves.

Two things are deliberately not a signature here. A BIP373 musig2 partial signature is not one until the session’s are added up, which is musig2.partial_sigs_agg and which writes the taproot signature this then checks – so an input holding a session mid-round is unsigned, and saying so is the point. And a finalized input carries no signature at all, BIP174 having the Finalizer clear them: it is refused with that said rather than reported unsigned, what it now carries being a spend for the script engine to verify.

btclib.psbt.combine(psbts: Sequence[Psbt]) Psbt[source]

Merge the data of several psbts of one transaction: the Combiner.

BIP174’s Combiner role, whose ordinary use is merging the partial signatures different signers added to copies of one psbt.

Every field a psbt map holds is merged, and the four left out are left out for one reason: amount, script_pub_key, previous_tx_id and output_index are part of what identifies the psbt, so the psbt being merged into carries them already and two psbts disagreeing about one of them are two transactions, refused above.

Which psbts are of one transaction is a question the two versions answer differently, and each is asked its own: a version 0 psbt is identified by the txid of the unsigned transaction every copy of it carries, so two copies whose sequences differ are two transactions; a version 2 psbt is identified as BIP370 says, by the txid of that transaction with every sequence zeroed, the sequence being a field an Updater may set. Comparing tx.id there would refuse two psbts of one transaction, which is what the identifier exists to prevent.

The versions must match, and are not converted here: to_v0 and to_v2 are that, and doing it silently would decide for the caller which of the two the combined psbt is – and, from v0 to v2, hand back a psbt whose lock time comes from the fallback rather than from the unsigned transaction the caller wrote it into.

The psbt handed back shares nothing with the ones handed in, which is finalize’s rule and extract_tx’s stated one. It matters more here than anywhere: without the copy this is psbts[0], merged into in place, so the copy a coordinator keeps to check the next signer’s answer against is the copy the last answer went into – and a check against it would then pass whatever came back. The whole sequence is copied and not only the first, _combine_field assigning the objects it takes rather than copying them: a witness_utxo or a leaf script map that came from psbts[1] would otherwise be the very object psbts[1] still holds.

btclib.psbt.ecdsa_sig_hash(psbt: Psbt, vin_i: int, *, hash_type: int | None = None) bytes[source]

Return the hash an ECDSA spend of one input signs.

What a Signer puts in PSBT_IN_PARTIAL_SIG is a signature of this, with the hash type appended; taproot_sig_hash is the same question for the schnorr signatures of a taproot input, and the two are the split finalize dispatches on.

hash_type defaults to the type the input asks for, and to SIGHASH_ALL when it asks for none. An input asking for SIGHASH_DEFAULT is refused: 0 is a taproot type, no ECDSA signature carries it, and a psbt asking for it is one no partial signature can finalize – which is what _assert_sig_hash_type says from the Finalizer’s end.

Every kind a partial signature can belong to is covered, the wrapped ones included: _sig_hash_from_psbt_in is the dispatch and says how. Where it answers None this raises, and the difference is the caller: a Finalizer checking a signature it was handed learns nothing from a psbt that does not say what was signed, while a Signer about to make one has to stop. Where it raises – a non-witness spend described by a witness utxo alone – every caller stops, that being an input no role may sign, verify or finalize.

btclib.psbt.estimated_input_sizes(psbt_in: PsbtIn, tx_in: TxIn, *, sizer: Callable[[PsbtIn, TxIn], list[int] | None] | None = None) tuple[int, list[int]][source]

Return the script_sig size and the witness stack of a signed input.

The second element is the size of each element the witness stack will hold, and not the size of its serialization: the count and the length prefixes are the transaction’s layout, which Tx.serialize is the one place that knows.

A signature is assumed to be 72 bytes; an input whose type cannot be read raises, unless sizer answers for it. All three rules, and why, are in the module docstring.

btclib.psbt.extract_tx(psbt: Psbt, *, check_validity: bool = True) Tx[source]

Extract the Tx fro the Psbt.

The Transaction Extractor must only accept a PSBT. It checks whether all inputs have complete scriptSigs and scriptWitnesses by checking for the presence of 0x07 Finalized scriptSig and 0x08 Finalized scriptWitness typed records.

If they do, the Transaction Extractor should construct complete scriptSigs and scriptWitnesses and encode them into network serialized transactions. Otherwise the Extractor must not modify the PSBT.

The Extractor should produce a fully valid, network serialized transaction if all inputs are complete.

Extracting needs no script interpretation; an Extractor that can interpret scripts may also validate the transaction it extracts, as BIP174 allows.

btclib.psbt.finalize(psbt: Psbt, *, solver: Callable[[Psbt, int], tuple[bytes, Witness] | None] | None = None) Psbt[source]

Finalize the Psbt.

The Input Finalizer must only accept a PSBT.

For each input, the Input Finalizer determines if the input has enough data to pass validation. If it does, it must construct the 0x07 Finalized scriptSig and 0x08 Finalized scriptWitness and place them into the input key-value map.

All other data except the UTXO and unknown fields in the input key- value map should be cleared from the PSBT. The UTXO should be kept to allow Transaction Extractors to verify the final network serialized transaction. _FINALIZED_KEEPS is that list, and one list for both kinds of input is what keeps the two from drifting apart.

Deciding that an input has enough data is two checks beyond the presence of a signature, and both are per input: the sighash type each signature commits to is the one the input asks for, and each signature verifies against the key it is filed under.

What is then built is the spend the input’s own kind asks for, which is what _finalized_input dispatches on: a witness script alone does not say, being absent from every single-key segwit input.

An input that is already finalized is left alone rather than refused, so finalizing twice is finalizing once. “The Input Finalizer determines if the input has enough data” and one carrying its final scripts has more than enough; Bitcoin Core’s SignPSBTInput skips it too. Refusing it would mean a psbt whose signer finalized one input could not be finalized at all – the rest of it would raise “missing signatures” for the input that is already done.

A solver answers for the inputs whose spend is the caller’s to know. It is asked before this function builds anything, and not only where this function refuses, which the sizer of psbt_size is: two of the shapes below are refusals – a taproot input with more than one script path signature, and a leaf that is not a single-key one – but a witness script of no standard kind is not. That one is built from the signatures and the script, which is the satisfaction of a multisig and a guess for anything else, so a caller with a script of their own has to be able to answer over it rather than after it. descriptors.miniscript_solver is that answer wherever the witness script is a BIP379 miniscript, and it is a solver rather than a branch of this function for a reason of layering: descriptors imports this module and nothing here imports back.

What the solver does not take over is the bookkeeping: the clearing BIP174 asks for, what is kept, and the verification of whatever signatures the input does carry are this function’s either way.

btclib.psbt.join(psbts: Sequence[Psbt], enforce_same_tx_version: bool, enforce_same_tx_lock_time: bool, shuffle_inp: bool, shuffle_out: bool, sort_inp: Callable[[PsbtIn], int] | None = None, sort_out: Callable[[PsbtOut], int] | None = None) Psbt[source]

Join multiple psbts into a single one by merging inputs and outputs.

inputs/outputs are shuffled by default. If shuffle_{in|out}=False, they are concatenated in the same order as psbts are specified. A specific ordering can be specified via sort_{inp|out}, which overwrite shuffle when present.

Outputs are concatenated and never merged, and there is no parameter asking for it: coalescing two outputs that pay the same script is a change to the output set, so every signature already made over the old one stops verifying – and, after the shuffle or sort above, the result would depend on the order the merge ran in. A caller who wants one output where there were two builds it that way before signing, which is the only point at which it is safe.

Joining is a Constructor adding inputs and outputs to each of the psbts at once, so a version 2 psbt has to allow both: every psbt joined is asked for its two modifiable flags, and the joined psbt carries what all of them still allow. The versions must be the same, for the reason combine gives.

The joined psbt shares nothing with the ones joined, for the reason combine copies: the input and output maps below are taken from every psbt in the sequence, so without the copy the joined psbt’s inputs are theirs, and an Updater filling one in afterwards fills in a psbt somebody else is still holding.

A signed message is not carried over, and that is not an omission: it says which challenge this transaction answers, and joining builds a transaction that is not it – BIP322 binds the message to the first input’s outpoint, which the join can move. A caller building a proof of funds this way sets the field on the result, where what it names is a transaction that exists.

btclib.psbt.new_signers(request: Psbt, returned: Psbt) set[bytes][source]

Return the master fingerprints returned adds the signatures of.

Which wallets answered, read off an answer before it is merged. Every signature names the key that made it – as the key data it is filed under, in BIP174’s PSBT_IN_PARTIAL_SIG and BIP371’s and BIP373’s fields alike – and the psbt names the origin of that key, whose master fingerprint is the wallet it was derived from. Asked before combine, because a Combiner takes the union of what it is given and records nothing of which side each entry came from: afterwards there is nothing left to tell apart.

A fingerprint and not a key, four bytes of hash160 of a master public key: it is what a psbt states about a key’s provenance and all a psbt states, so a device that answered for several keys of several inputs is one signer here, and two devices sharing a seed are not distinguishable at all. It is not evidence either – a psbt is a file and the origin fields in it are whatever was written there – so what this answers is “which wallet does this psbt say signed”, which is the question a caller filing an answer, or refusing one that came from a device other than the one it asked, actually has.

The signature kinds are the ones a signer adds: ECDSA partial signatures, attributed through PSBT_IN_BIP32_DERIVATION; a taproot key path signature, through the internal key’s PSBT_IN_TAP_BIP32_DERIVATION, that being what says who holds the key the output commits to; a taproot script path signature, through the same field entry for the x-only key its key data names; and the two musig2 rounds, whose participant key is the first 33 bytes of their key data and whose origin is a plain key’s, in PSBT_IN_BIP32_DERIVATION. A BIP373 round is included because it is what that participant’s answer is, as assert_signatures_only counts the two maps among the signature fields for the same reason.

Two of those attributions are honest but weaker than they look, and both are the psbt’s doing rather than this function’s. A musig2 session whose internal key is BIP328-derived from the aggregate key files that derivation under a synthetic fingerprint – hash160 of the aggregate key – so the key path signature partial_sigs_agg writes is attributed to the session rather than to any one participant, which is exactly what it came from. And a psbt stating no origin for a key it holds a signature of is refused: see _master_fingerprint.

Only the input counts are compared. That the two psbts are otherwise the same request is assert_signatures_only’s question, which is the call that belongs beside this one, and the order of the two is the caller’s: it is the merge that must not happen before both have answered.

btclib.psbt.prevouts(psbt: Psbt) list[TxOut][source]

Return the output each input of the psbt spends.

A taproot signature commits to the amount and script of every input (BIP341’s sha_amounts and sha_scriptpubkeys), not only of the one being signed, so a single missing utxo leaves the whole transaction unsignable rather than one input of it – which is why this raises where _prev_out answers None.

btclib.psbt.sign(psbt: Psbt, key_manager: KeyManager) tuple[Psbt, list[int]][source]

Run the Signer role over every input key_manager answers for.

Per input the candidates are what the psbt itself names: hd_key_paths for an ECDSA spend, the taproot internal key and the taproot hd_key_paths entries for a taproot one. None from key_manager skips the key rather than raising – one signer of an m-of-n holds one key, and an input it cannot answer for is not an error but somebody else’s turn. What comes back besides the copy is which inputs got a new signature, since a caller collecting a quorum needs to tell “there was nothing for me” from “done”.

A taproot input is offered both of its paths, the key path and every leaf the psbt carries a script for, and one input may come back with signatures for both: which of the two is spent is the Finalizer’s choice, and a signer that holds keys for both has no reason to be asked twice. Which leaf a key belongs to is not sign’s guess either – PSBT_IN_TAP_BIP32_DERIVATION says it, and MuSig2’s own Signer is btclib.psbt.musig2 for the aggregate case. Every other kind is whatever _finalized_input can close over – p2pk, p2pkh, p2wpkh, p2sh-p2wpkh, p2wsh, bare and wrapped multisig.

Raises where the psbt cannot be signed at all – assert_signable’s question – and where a candidate’s own hash cannot be computed, which is ecdsa_sig_hash refusing to guess at a caller’s stop. A key key_manager has nothing to say about is a different question and does not raise.

btclib.psbt.taproot_sig_hash(psbt: Psbt, vin_i: int, *, leaf_hash: bytes | str | bytearray | memoryview = b'', hash_type: int | None = None) bytes[source]

Return the hash a taproot spend of one input signs.

BIP341 for a key path spend, BIP342 for a script path one, and the tapleaf hash is what tells the two apart: given one, the message carries it along with the key version and the codesep position, as the script engine’s own OP_CHECKSIG builds them.

hash_type defaults to the type the input asks for, and to SIGHASH_DEFAULT when it asks for none. Passing it is what a Finalizer does: a taproot signature carries its own type appended, so the hash to check it against is the one it committed to.

The annex is empty: BIP341 leaves it undefined, no psbt field carries one, and a signer cannot invent what the spender will put on the stack.