btclib.script package¶
Subpackages¶
- btclib.script.engine package
- Submodules
- btclib.script.engine.flags module
- btclib.script.engine.script module
- btclib.script.engine.script_op_codes module
assert_balanced_if()assert_minimal_push()assert_stack_size()op_0notequal()op_1add()op_1negate()op_1sub()op_2drop()op_2dup()op_2over()op_2rot()op_2swap()op_3dup()op_abs()op_add()op_booland()op_boolor()op_checklocktimeverify()op_checkmultisigverify()op_checksequenceverify()op_checksigverify()op_depth()op_drop()op_dup()op_else()op_endif()op_equal()op_equalverify()op_fromaltstack()op_greaterthan()op_greaterthanorequal()op_hash160()op_hash256()op_if()op_ifdup()op_lessthan()op_lessthanorequal()op_max()op_min()op_negate()op_nip()op_nop()op_not()op_notif()op_numequal()op_numequalverify()op_numnotequal()op_over()op_pick()op_return()op_ripemd160()op_roll()op_rot()op_sha1()op_sha256()op_size()op_sub()op_swap()op_toaltstack()op_tuck()op_verify()op_within()read_push_data()unknown_op_code()
- btclib.script.engine.tapscript module
- Module contents
Submodules¶
btclib.script.limits module¶
The consensus limits on a script, with Bitcoin Core’s names.
The five caps at the top of Core’s script/script.h, in one place because the same number is read from more than one module: the element size bounds a witness stack element in the engine and a tapscript push in taproot.parse, and the code that reads it in one place cannot see the copy in the other.
A module of its own rather than the top of script.py, because of what script.py is: the encoding – the tables, parse, serialize – while every limit here is a rule about executing one. Reading them from the decoder is what let a 1443-byte push be refused as unparsable when it is merely unspendable (issue #123), and a module the decoder need not import is what says so.
LOCKTIME_THRESHOLD is deliberately not here, though script.h declares it in the same block: it is not a limit but the value that tells a lock time read as a block height from one read as a timestamp, and it means the same in a transaction as in a script. It is btclib.tx.limits’s, and OP_CHECKLOCKTIMEVERIFY imports it from there.
btclib.script.op_codes_tapscript module¶
The tapscript op code tables, per BIP342.
OP_VERIF (0x65) and OP_VERNOTIF (0x66) are named here although no script that executes them can be spent: BIP342 leaves both out of OP_SUCCESS, and the engine rejects them whether or not the branch they sit in is taken. Naming them is not endorsing them, and it is what Core does as well – GetOpName has a case for each, GetOp reads them, and only the interpreter decides. Dropping them from the tables would go further than Core and reject a spendable script: OP_VERIF OP_SUCCESS80 is valid, Core’s pre-scan returning success at the first OP_SUCCESS whatever precedes it, while parse would raise on the byte before ever reaching it (issue #182).
btclib.script.script module¶
Bitcoin Script.
https://en.bitcoin.it/wiki/Script
Scripts are represented by list[Command], where Command = int | str | bytes
an ascii string is an op code name (e.g. ‘OP_HASH160’, ‘OP_1NEGATE’)
a hex-string or bytes (i.e., Octets) are data
The tables name op codes no valid script can execute, marked # disabled below: the fifteen splice, bitwise and multiplication op codes Satoshi switched off for CVE-2010-5137. Naming them is what lets the engine reject them by name – a byte with no name reaches the interpreter as “unknown op code”, and one that parse cannot name at all cannot be serialized back either, which the engine’s FindAndDelete relies on. Core draws the same line: script.h defines them, GetOpName names them, GetOp reads them, and only the interpreter refuses – there, before it even asks whether the branch executes. OP_RESERVED, OP_VER, OP_VERIF, OP_VERNOTIF, OP_RESERVED1 and OP_RESERVED2 are named for the same reason and refused by their own rules.
- class btclib.script.script.Script(script: bytes | str | bytearray | memoryview = b'', *, check_validity: bool = True)[source]¶
Bases:
objectA Bitcoin script, held as its bytes.
The bytes are the script – assert_valid asks nothing else of them – and asm is their parse, computed on first read and cached. Immutable, so a Script can be shared and concatenated (+) without aliasing surprises; ScriptPubKey extends it with a network.
- property asm: list[int | str | bytes | bytearray | memoryview][source]¶
The parsed script, parsed once.
A plain property would parse self.script again on every read, for a value that cannot change; cached, a second read is an attribute lookup.
Nothing warms the cache: construction does not parse, and __init__ filling the cache in would be the wrong trade – measured on a 16.5 kB script, an instance holding the parse costs 55.4 kB against 0.2 kB without it, 277 times the script’s own bytes, and nothing inside the library reads .asm.
- assert_valid() None[source]¶
Assert that the script is bytes, which is all a script is.
There is no other question to ask: Bitcoin Core has no validity notion for a script either – a CScript is a vector of bytes, and the only script-level predicate it offers is IsUnspendable(), for pruning the UTXO set. Whether a script can be executed is the interpreter’s answer, given by executing it, and the limits it enforces depend on the sigversion the script is spent under: in tapscript an OP_SUCCESSx makes a script valid however malformed the rest of it is, so no predicate on the bytes alone could answer for both.
Not a parse, refusing what the parse refuses – a push over 520 bytes, a truncated push, an op code no table names: five transactions in blocks 251718 to 299571 carry such scripts, so that predicate leaves Tx.parse unable to read them and .asm raising for the scripts an explorer prints (issue #123).
The coercion is the check, as it is in Witness.assert_valid: a Script built through __init__ has been through bytes_from_octets already, and this is what answers for one reached any other way.
- btclib.script.script.op_code_spans(script: bytes) Iterator[tuple[int, int, int]][source]¶
Walk a script op code by op code: (op code, first byte, one past last).
The walk stops where read_op_code returns None, so the bytes from the last yielded stop to the end of the script are whatever could not be read as an op code — Core’s GetOp loops end the same way, and keep that tail.
- btclib.script.script.op_int(i: int) str[source]¶
Name the one-byte op code that pushes the number i, -1 to 16.
OP_0..OP_16 and OP_1NEGATE are the only numbers with an op code of their own; any other integer is refused, a caller wanting it pushed passing the integer itself to serialize.
- btclib.script.script.parse(stream: BytesIO | bytes | str | bytearray | memoryview) list[int | str | bytes | bytearray | memoryview][source]¶
Decode a script, as Bitcoin Core decodes one.
Which is to say: whatever the bytes are. Core’s only decoder is GetScriptOp, it reads one instruction at a time, and the sole thing it refuses is a push running past the end of the script – where the walk stops, and ERROR_COMMAND is appended in the place Core’s ScriptToAsmStr writes the same “[error]”. Every other question, the element limit and the op codes no valid script may execute among them, belongs to the interpreter and is asked there; a script it would refuse still decodes, exactly as one that is in a block must (issue #123).
- btclib.script.script.push_int(i: int) str[source]¶
Return the shortest command that pushes the number i.
The op code where the number has one – op_int, -1 to 16 – and the CScriptNum encoding of it otherwise, as the hex a data push is written as. Both halves are here already; this is the choice between them, which is what a caller assembling a script by hand writes out every time it puts a number in one: a threshold, a relative timelock, a size. Which half applies is a property of the value and not of what the number means, so a script template holding 16 and one holding 17 are written the same way and serialize differently.
serialize reaches the same bytes from the integer itself for everything above 16, and warns for -1 to 16 that the op code is one byte shorter: this is that op code, so the warning is the caller being told to write this instead. Minimal because the consumers are: miniscript.from_script refuses a push written the long way, and the interpreter refuses it too under MINIMALDATA, so a number pushed with a byte to spare is a script that reads as nothing and may not spend.
- btclib.script.script.read_op_code(script: bytes, start: int) tuple[int, int] | None[source]¶
Read one op code: (the op code, the offset one past it and its data).
This is Bitcoin Core’s CScript::GetOp, and it exists because a script code is a slice of the script’s own bytes: consensus commits to the bytes as they were written, and serialize(parse(script)) does not give them back. A non-minimal push is legal and comes back minimal — 4c01ff as 01ff — so a script code recovered by re-serializing part of a parse is a different preimage than the one Core signs (issue #176). Walking the bytes is the only way to find an op code boundary without moving the bytes on either side of it.
None where nothing whole can be read: the end of the script, or a push whose data or length runs past it. Core’s GetOp returns false in the same two cases, and its callers stop and keep the rest verbatim. Truncation is all this refuses: the 520-byte push limit is a rule about what reaches the stack, enforced where a push is executed, and GetOp does not know it either.
- btclib.script.script.script_from_dict(value: Mapping[str, str] | bytes | str | bytearray | memoryview) bytes[source]¶
Read back what script_to_dict wrote: the hex, and only it.
asm is derived from hex, so there is nothing in it to read. It is still not ignored: a dict carrying an asm that the hex does not produce is refused, naming both. Ignoring it would let a hand-edited asm sit in a stored dict describing a script that is not the one the bytes hold, and every consumer that reads the human-readable field – a diff, a review, an explorer – would then be reading a lie, silently. Believing the asm instead is not on offer: it is lossy (a non-minimal push comes back minimal, [error] comes back not at all), so it cannot name every script hex can.
A bare hex string is accepted as well, which is the shape to_dict emitted before it emitted this one. Every constructor downstream already takes Octets, so accepting it costs one branch and keeps a dict stored by an older btclib readable – the emission is what changed, and a reader that refused the old spelling would break round trips that never had an asm to disagree with.
- btclib.script.script.script_to_dict(script: bytes) dict[str, str][source]¶
Render a script as Bitcoin Core’s RPC renders one: asm and hex.
The two renderings of the same bytes, which is what getrawtransaction and decodepsbt hand back for every script they report. hex is the script; asm is parse joined by spaces, i.e. a reading aid, and the only thing script_from_dict will believe is the hex.
Not Core’s asm byte for byte, and it cannot be: btclib prints a push as upper-case hex where Core prints one under 5 bytes as a decimal number, and neither spelling is invertible – see ERROR_COMMAND above. What this is, exactly, is Script.asm with a space between its commands.
- btclib.script.script.serialize(script: Sequence[int | str | bytes | bytearray | memoryview]) bytes[source]¶
Serialize a script from its commands.
An integer is encoded as the number it pushes – with a warning where a one-byte op code means the same, and a refusal outside the int64 a script number is – a string is an op code name, an UNKNOWN_OP_CODE_n byte, or hex data, and bytes are data; data is always the minimal push operator, per BIP62. What parse returns round-trips, ERROR_COMMAND excepted, that marker being a place in the bytes rather than an instruction.
The minimal operator and not the minimal command: data is data, so the bytes 0x01 are pushed with a length of one and not with OP_1, and the same goes for an integer command, which is the number’s bytes and is warned about for exactly this. push_int is what writes a number the shortest way there is, and what every caller in this library that means one uses.
Zero is where the two coincide and the warning therefore stops: the empty vector is what encode_num writes for it, and the push of an empty vector is OP_0, so serialize([0]) is the op code – as Core’s CScript() << 0 is – with nothing shorter left to suggest.
A sequence of commands and not one command: Sequence[Command] accepts a str and a bytes, each of them being a sequence of Command as far as the type goes, so serialize(“OP_DUP”) was six one-character commands and a bytes handed here a script of one integer command per byte – the first refused for a character it could not read as an op code, the second accepted and wrong. What is not a sequence at all was “not iterable” from underneath the library.
btclib.script.script_pub_key module¶
ScriptPubKey and the classification of the standard script shapes.
Every standard shape has an assert_* function, raising BTClibValueError on bytes that are not it, and an is_* companion answering the same test as a bool: False means “not this shape”, while a caller error – None, a list, anything no bytes can be read from – raises through both, a TypeError not being an answer to “is this a p2sh”. The assert functions test shape, not spendability: any 20 bytes are a pub_key hash to assert_p2pkh, and only assert_p2pk parses its key, the script being the one place the key itself sits.
- class btclib.script.script_pub_key.ScriptPubKey(script: bytes | str | bytearray | memoryview, network: str = 'mainnet', *, check_validity: bool = True)[source]¶
Bases:
ScriptA Script with the network its addresses render on.
The script bytes and their validation are Script’s; the network enters address, addresses, the equality test and the hash, two ScriptPubKey being equal when their scripts match and their networks are of one type – mainnet against the rest – rather than of one name. Frozen and hashable on that same pair, so a ScriptPubKey can be a set member or a dict key. The classmethods build the standard shapes, one per type the classifier names.
- property address: str¶
Return the bech32/base58 address.
An address is a shortened notation for a particular script. As a transaction output contains exactly one script, it has at most one address (it is possible that the script does not correspond to a particular address, though).
- property addresses: list[str]¶
Return the address, if any, or the p2pkh addresses for p2ms.
Historically, a p2pkh address has been used to refer to a key. For a p2ms multisig script, the keys it pays to are returned, expressed in p2pkh-address notation.
https://bitcoin.stackexchange.com/questions/30442/multiple-addresses-in-one-utxo
- classmethod from_address(addr: bytes | str | bytearray | memoryview, *, check_validity: bool = True) ScriptPubKey[source]¶
Return the ScriptPubKey of the input bech32/base58 address.
- classmethod nulldata(data: bytes | str | bytearray | memoryview, *, check_validity: bool = True) ScriptPubKey[source]¶
Return the nulldata ScriptPubKey of the provided data.
- classmethod p2ms(m: int, keys: Sequence[int | bytes | str | bytearray | memoryview | BIP32KeyData | tuple[int, int] | PreparedPoint], network: str | None = None, compressed: bool | None = None, lexicographic_sorting: bool = True, *, check_validity: bool = True) ScriptPubKey[source]¶
Return the m-of-n multi-sig ScriptPubKey of the provided keys.
BIP67 endorses lexicographic sorting of compressed public keys.
Sorting uncompressed keys (leading 0x04 byte) would result in a different order. An uncompressed key is not refused here even when lexicographic_sorting is True: BIP67’s own compatibility note calls such a key a sign of a non-conforming counterparty, not something to reject, and Bitcoin Core’s sortedmulti() accepts the mix and sorts it the same way – see #299.
https://github.com/bitcoin/bips/blob/master/bip-0067.mediawiki
- classmethod p2pk(key: int | bytes | str | bytearray | memoryview | BIP32KeyData | tuple[int, int] | PreparedPoint, network: str | None = None, *, check_validity: bool = True) ScriptPubKey[source]¶
Return the p2pk ScriptPubKey of the provided Key.
- classmethod p2pkh(key: int | bytes | str | bytearray | memoryview | BIP32KeyData | tuple[int, int] | PreparedPoint, compressed: bool | None = None, network: str | None = None, *, check_validity: bool = True) ScriptPubKey[source]¶
Return the p2pkh ScriptPubKey of the provided key.
- classmethod p2sh(redeem_script: bytes | str | bytearray | memoryview, network: str = 'mainnet', *, check_validity: bool = True) ScriptPubKey[source]¶
Return the p2sh ScriptPubKey of the provided redeem script.
- classmethod p2tr(internal_key: int | bytes | str | bytearray | memoryview | BIP32KeyData | tuple[int, int] | PreparedPoint | None = None, script_path: list[tuple[int, list[int | str | bytes | bytearray | memoryview]] | list[tuple[int, list[int | str | bytes | bytearray | memoryview]] | TaprootScriptTree]] | None = None, network: str = 'mainnet', *, check_validity: bool = True) ScriptPubKey[source]¶
Return the p2tr ScriptPubKey of the provided script tree.
- classmethod p2wpkh(key: int | bytes | str | bytearray | memoryview | BIP32KeyData | tuple[int, int] | PreparedPoint, *, check_validity: bool = True) ScriptPubKey[source]¶
Return the p2wpkh ScriptPubKey of the provided key.
If the provided key is a public one, it must be compressed.
- classmethod p2wsh(redeem_script: bytes | str | bytearray | memoryview, network: str = 'mainnet', *, check_validity: bool = True) ScriptPubKey[source]¶
Return the p2wsh ScriptPubKey of the provided redeem script.
- btclib.script.script_pub_key.address(script_pub_key: bytes | str | bytearray | memoryview, network: str = 'mainnet') str[source]¶
Return the bech32/base58 address from a script_pub_key.
A witness program of version 2 or higher has an address as much as a p2tr one does – bech32m spells it, b32.address_from_witness writes it, and Bitcoin Core renders it, EncodeDestination having a WitnessUnknown case. Answering “” for one would be indistinguishable from “this script has no address”, which is the right answer for a nulldata output and the wrong one where btclib can read the address back into the very script it came from (issue #251).
- btclib.script.script_pub_key.addresses(script_pub_key: bytes | str | bytearray | memoryview, network: str = 'mainnet') list[str][source]¶
Return the p2pkh addresses of the pub_keys in a p2ms script_pub_key.
- btclib.script.script_pub_key.assert_nulldata(script_pub_key: bytes | str | bytearray | memoryview) None[source]¶
Assert the standard nulldata shape: OP_RETURN and one minimal push.
A policy, and narrower than Bitcoin Core’s classification on every count. Core’s Solver answers NULL_DATA for an OP_RETURN whose remaining bytes pass IsPushOnly, which refuses only an op code above OP_16: any number of pushes qualifies, OP_1..OP_16 among them, and so does the empty remainder of a bare OP_RETURN. The 83-byte bound is a third thing again, MAX_OP_RETURN_RELAY being relay policy tested by IsStandardTx and not by the classifier, and the length 78 refused below is the non-minimal push of 75 bytes through OP_PUSHDATA1, which consensus allows. So 6a, 6a51, 6a0101010102 and a nulldata of any size are NULL_DATA there and unknown here (issue #211).
The narrowness is what lets type_and_payload answer at all: it returns one payload, and OP_RETURN followed by push-only bytes has none for a bare OP_RETURN and two for two pushes. It is also the one shape ScriptPubKey.nulldata builds, so the classifier agrees with the constructor. A caller wanting Core’s answer wants a different function, returning a list of payloads.
6a00 is accepted, and by arithmetic rather than by Core’s rule: the 00 is read here as the length marker of a zero-length push, where Core reads it as OP_0, a push like any other.
- btclib.script.script_pub_key.assert_p2ms(script_pub_key: bytes | str | bytearray | memoryview) None[source]¶
Refuse bytes that are not p2ms: m, the pushed keys, n, OP_CHECKMULTISIG.
p2ms_m_and_keys is the whole of the check, and says what it is.
- btclib.script.script_pub_key.assert_p2pk(script_pub_key: bytes | str | bytearray | memoryview) None[source]¶
Refuse bytes that are not p2pk: a pushed pub_key, OP_CHECKSIG.
The key must parse as a curve point, so a well-shaped script pushing 33 or 65 bytes that are not one is refused too.
- btclib.script.script_pub_key.assert_p2pkh(script_pub_key: bytes | str | bytearray | memoryview) None[source]¶
Refuse bytes that are not p2pkh.
The shape is OP_DUP OP_HASH160, a 20-byte push, OP_EQUALVERIFY OP_CHECKSIG; the hash is any 20 bytes.
- btclib.script.script_pub_key.assert_p2sh(script_pub_key: bytes | str | bytearray | memoryview) None[source]¶
Refuse bytes that are not p2sh, per BIP16.
The shape is OP_HASH160, a 20-byte push, OP_EQUAL; the hash is any 20 bytes.
- btclib.script.script_pub_key.assert_p2tr(script_pub_key: bytes | str | bytearray | memoryview) None[source]¶
Refuse bytes that are not p2tr: OP_1, a 32-byte push, per BIP341.
The 32 bytes are not checked to be a valid x-only key: the shape is the classification, and an output key off the curve is found by the spender, not by the classifier.
- btclib.script.script_pub_key.assert_p2wpkh(script_pub_key: bytes | str | bytearray | memoryview) None[source]¶
Refuse bytes that are not p2wpkh: OP_0, a 20-byte push, per BIP141.
- btclib.script.script_pub_key.assert_p2wsh(script_pub_key: bytes | str | bytearray | memoryview) None[source]¶
Refuse bytes that are not p2wsh: OP_0, a 32-byte push, per BIP141.
- btclib.script.script_pub_key.assert_segwit(script_pub_key: bytes | str | bytearray | memoryview) None[source]¶
Refuse bytes that are not a witness program, per BIP141.
The shape every segwit output shares, whatever its version: one version op code – OP_0 or OP_1..OP_16 – and one push of 2 to 40 bytes. Shape only; the program is not interpreted.
- btclib.script.script_pub_key.is_nulldata(script_pub_key: bytes | str | bytearray | memoryview) bool[source]¶
Answer whether the bytes are a standard nulldata script_pub_key.
- btclib.script.script_pub_key.is_p2ms(script_pub_key: bytes | str | bytearray | memoryview) bool[source]¶
Answer whether the bytes are a p2ms script_pub_key.
- btclib.script.script_pub_key.is_p2pk(script_pub_key: bytes | str | bytearray | memoryview) bool[source]¶
Answer whether the bytes are a p2pk script_pub_key.
- btclib.script.script_pub_key.is_p2pkh(script_pub_key: bytes | str | bytearray | memoryview) bool[source]¶
Answer whether the bytes are a p2pkh script_pub_key.
- btclib.script.script_pub_key.is_p2sh(script_pub_key: bytes | str | bytearray | memoryview) bool[source]¶
Answer whether the bytes are a p2sh script_pub_key.
- btclib.script.script_pub_key.is_p2tr(script_pub_key: bytes | str | bytearray | memoryview) bool[source]¶
Answer whether the bytes are a p2tr script_pub_key.
- btclib.script.script_pub_key.is_p2wpkh(script_pub_key: bytes | str | bytearray | memoryview) bool[source]¶
Answer whether the bytes are a p2wpkh script_pub_key.
- btclib.script.script_pub_key.is_p2wsh(script_pub_key: bytes | str | bytearray | memoryview) bool[source]¶
Answer whether the bytes are a p2wsh script_pub_key.
- btclib.script.script_pub_key.is_segwit(script_pub_key: bytes | str | bytearray | memoryview) bool[source]¶
Answer whether the bytes are a witness program of any version.
- btclib.script.script_pub_key.p2ms_m_and_keys(script_pub_key: bytes | str | bytearray | memoryview) tuple[int, list[bytes]][source]¶
Return the threshold and the pub keys of a p2ms script_pub_key.
The bounds are checked – 0 < m <= n < 17 – and each key is read as a push of the declared length and then parsed as a public key: a push that is not one is what makes the bytes not a p2ms, which is the answer is_p2ms gives.
The keys come back as the script holds them, uncompressed ones included, and not as the parse normalized them: BIP174 keys a partial signature by the key “as it appears in the scriptPubKey or redeemScript”, so the Finalizer below matches these bytes.
Three callers ask this one question, which is why it is asked in one place: addresses wants a p2pkh address per key, assert_p2ms the exception alone, and the psbt Finalizer both halves of the answer – OP_CHECKMULTISIG takes m signatures, in the order the script lists the keys they belong to.
- btclib.script.script_pub_key.type_and_payload(script_pub_key: bytes | str | bytearray | memoryview) tuple[Literal['nulldata', 'p2ms', 'p2pk', 'p2pkh', 'p2sh', 'p2tr', 'p2wpkh', 'p2wsh', 'unknown', 'witness_unknown'], bytes][source]¶
Return (script_pub_key type, payload) from the input script_pub_key.
The returns here and in _witness_type_and_payload are the whole of ScriptType between them, mypy checking each one against it: an eleventh shape classified in either is a member added there.
btclib.script.sig_hash module¶
The hashes a transaction signature commits to, one per era.
Three preimages for one question – what does this input’s signature sign: legacy is Satoshi’s SignatureHash, segwit_v0 is BIP143’s, which adds the amount being spent, and taproot is BIP341’s SigMsg, which commits to every spent output. The hash types – ALL, NONE, SINGLE, each with or without ANYONECANPAY, and taproot’s DEFAULT – choose how much of the transaction each preimage covers, and from_tx dispatches an input to the preimage its script demands.
- class btclib.script.sig_hash.PrecomputedTxData(tx: Tx, prevouts: list[TxOut])[source]¶
Bases:
objectThe transaction-wide hashes every input of a transaction shares.
BIP143 and BIP341 commit each input to hashes of the whole transaction — its prevouts, its sequences, its outputs — and BIP341 to the amounts and script_pub_keys being spent as well. None of them depends on which input is being signed, so a transaction with N inputs needs them once and not N times: rebuilding them per input makes signing or verifying Θ(N²) in the number of inputs, and a consolidation transaction is the ordinary case there rather than a pathological one (issue #164). Bitcoin Core computes the same set into its PrecomputedTransactionData and passes it down.
The sha_ attributes are the BIP341 hashes, spelled as that BIP spells them but for script_pub_keys, which btclib does not write scriptpubkeys. The hash_ properties are the three BIP143 ones, and they are one further sha256 over the corresponding sha_ attribute rather than a second pass over the transaction: hash256 is sha256 twice, and the two BIPs hash the very same serializations.
Everything is computed here, once, because this must be a snapshot of the transaction and not a view onto it: Tx is mutable, and a hash computed lazily out of the caller’s transaction would be issue #140 again, a sig_hash that changed under the caller between two calls. Build one, use it for a loop over the inputs, and throw it away with the transaction it describes.
- btclib.script.sig_hash.assert_valid_hash_type(hash_type: int) None[source]¶
Refuse a hash type outside SIG_HASH_TYPES.
The set is the seven combinations the BIPs define; ANYONECANPAY with DEFAULT is not among them, BIP341 leaving 0x80 undefined.
- btclib.script.sig_hash.from_tx(prevouts: list[TxOut], tx: Tx, vin_i: int, hash_type: int, precomputed: PrecomputedTxData | None = None, *, codesep_index: int = 0) bytes[source]¶
Return the hash to be signed for one input of a transaction.
precomputed is what makes a loop over the N inputs of a transaction cost O(N) instead of Θ(N²): the transaction-wide hashes a segwit sig_hash commits to are the same for every input, so computing them once is the caller’s to do — PrecomputedTxData(tx, prevouts) before the loop, dropped with the transaction after it. It must describe this very tx, and nothing here can tell whether it does.
codesep_index is which OP_CODESEPARATOR the script code starts after: 0, the default, is the whole script, i.e. none executed, and k is the k-th occurrence in the script being signed for — the redeem script of a p2sh input, the witness script of a p2wsh one. Whether that occurrence is the one last executed when the signature is checked depends on which branches the input takes, which is the signer’s to know: a script OP_IF OP_CODESEPARATOR OP_ENDIF OP_CODESEPARATOR run down its false branch executes the second occurrence and not the first. A verifier does not need the parameter and does not have the problem — the interpreter advances Core’s pbegincodehash as it goes.
- btclib.script.sig_hash.legacy(script_code: bytes | str | bytearray | memoryview, tx: Tx, vin_i: int, hash_type: int) bytes[source]¶
Return the pre-segwit hash one input’s signature commits to.
Satoshi’s SignatureHash: the transaction is copied, every other script_sig blanked, the signed input’s replaced by the script code with its OP_CODESEPARATORs elided, and outputs and sequences dropped as NONE, SINGLE and ANYONECANPAY ask; hash256 of that serialization and the four hash-type bytes is the answer. The SINGLE bug is kept, being consensus: an input with no matching output signs the constant 1, not an error.
hash_type is Core’s int32_t nHashType, and every 32-bit word has a preimage: the seven defined types are what a signer picks, and an undefined one is what a signature may carry and this must still hash.
- btclib.script.sig_hash.redeem_script(script_sig: bytes | str | bytearray | memoryview, script_pub_key: bytes | str | bytearray | memoryview) bytes[source]¶
Return the redeem script of a p2sh input, checked against its hash.
BIP16 has it as the last command of the script_sig, and what the sig_hash must dispatch on is the redeem script itself, never the push that carries it: serialized, a p2sh-p2wpkh redeem script is 23 bytes where p2wpkh wants exactly 22, so every is_p2w* test on the script_sig is false and the input would silently take the legacy branch, signing a hash that does not commit to the amount.
The hash is checked here rather than left to the script engine: a script_sig disagreeing with the script_pub_key it spends can only give a sig_hash for a script no one will ever run.
- btclib.script.sig_hash.segwit_v0(script_code: bytes | str | bytearray | memoryview, tx: Tx, vin_i: int, hash_type: int, amount: int, precomputed: PrecomputedTxData | None = None) bytes[source]¶
Return the BIP143 hash one segwit v0 input’s signature commits to.
The preimage commits to the amount being spent – the point of BIP143 – and to the script code whole, OP_CODESEPARATORs included. precomputed, when given, must describe this very transaction; a single call leaves it None and hashes only what its hash type commits to.
hash_type is Core’s int32_t nHashType, as legacy’s is, and every 32-bit word has a preimage for the same reason.
- btclib.script.sig_hash.taproot(transaction: Tx, input_index: int, prevouts: list[TxOut], hashtype: int, ext_flag: int, annex: bytes | str | bytearray | memoryview, message_extension: bytes | str | bytearray | memoryview, precomputed: PrecomputedTxData | None = None) bytes[source]¶
Return the BIP341 hash one taproot input’s signature commits to.
BIP341’s SigMsg under the TapSighash tag: the whole-transaction hashes enter as their single-sha256 forms, the spent amounts and script_pub_keys are always committed to, and ext_flag with message_extension carry BIP342’s tapleaf commitment for a script path – empty for the key path. SIGHASH_SINGLE with no matching output is an error here, per BIP341, where legacy keeps the bug.
- btclib.script.sig_hash.taproot_annex_and_ext(tx: Tx, vin_i: int) tuple[bytes, bytes][source]¶
Read (annex, sig_hash extension) off one input’s witness stack.
What taproot needs beyond the transaction: the annex, per BIP341’s “last element whose first byte is 0x50”, and – for a stack that is a script path – BIP342’s message extension, the tapleaf hash with key version 0 and no OP_CODESEPARATOR executed. A signer past a separator computes its own extension; the caller’s transaction is never rewritten.
btclib.script.sig_ops module¶
The legacy signature check operation count of a script.
Bitcoin Core’s CScript::GetSigOpCount(false), which GetLegacySigOpCount sums over the input and output scripts of a transaction and CheckBlock sums again over the transactions of a block, to bound it by MAX_BLOCK_SIGOPS_COST. That is the one sigop rule a block can be held to from its own bytes, and it is why the count is here: Tx.sig_op_count and Block.sig_op_count are the two sums, each reading this.
The count underestimates, and Core’s comment where it is summed says so: the p2sh count needs the redeem script the input pushes, the witness count needs the script_pub_key being spent, and both are outputs of blocks that are not this one. fAccurate is not a parameter here for that same reason – accurate means OP_CHECKMULTISIG costing the number of keys pushed before it rather than the twenty of MAX_PUBKEYS_PER_MULTISIG, and Core only ever asks for it under p2sh and segwit, where the script counted comes from the UTXO set.
A module of its own rather than the bottom of script.py, which is the encoding and reads no limit: MAX_PUBKEYS_PER_MULTISIG is a rule about executing a script, and a decoder that need not import the limits is what script/limits.py exists to say. What it does share with the decoder is the walk – op_code_spans, i.e. Core’s GetOp – because the boundary between one op code and the next is the only thing the count depends on.
- btclib.script.sig_ops.sig_op_count(script: bytes | str | bytearray | memoryview) int[source]¶
Return the number of legacy signature checks a script announces.
One for OP_CHECKSIG and OP_CHECKSIGVERIFY, MAX_PUBKEYS_PER_MULTISIG for OP_CHECKMULTISIG and OP_CHECKMULTISIGVERIFY however many keys the script actually pushes, and nothing for the rest – the count is announced by the bytes and is not what executing them would do.
Where the script stops parsing the count stops too, and no exception is raised: Core’s loop break`s when `GetOp returns false, which is a push running past the end, and op_code_spans ends the same way. The coinbase output script of testnet block 987,876 is that case on chain – it ends …d8 3d 0aa68688ac, and the OP_CHECKSIG of that final ac is five bytes inside the 61-byte push 3d announces, so neither implementation ever reaches it and the answer for that script is zero.
btclib.script.taproot module¶
Taproot keys, script trees and control blocks, per BIP341.
The output side and the spend side of taproot, tapscript execution excepted: output_pubkey and output_prvkey build the tweaked keys, tree_helper and input_script_sig walk a script tree into leaves, merkle paths and control blocks, check_output_pubkey verifies one, and serialize/parse are the tapscript codec the engine reads.
- btclib.script.taproot.assert_valid_control_block(control_block: bytes | str | bytearray | memoryview) None[source]¶
Refuse a control block whose size no leaf depth can produce.
Size only, and only its residue: one leading byte plus a multiple of 32, which BIP341’s 33 + 32m sizes all satisfy. Proving the block against an output key is check_output_pubkey’s.
The octets first, as check_output_pubkey takes them: len of the text spelling counts characters, so “e” * 33 – 33 characters and the 66 octets of a hex string – was a size this accepted, and “é” * 33, 33 characters and 66 octets of UTF-8, was a size it accepted for no reason at all.
- btclib.script.taproot.check_output_pubkey(q: bytes | str | bytearray | memoryview, script: bytes | str | bytearray | memoryview, control: bytes | str | bytearray | memoryview) bool[source]¶
Answer whether the control block proves the script against the key.
BIP341’s control-block verification: the leaf hash is folded up the merkle path in the control block, and the internal key tweaked by the result must equal the output key q, parity included. A malformed control block or an internal key that is not a point is refused rather than answered False, either being no proof at all.
- btclib.script.taproot.input_script_sig(internal_pubkey: int | bytes | str | bytearray | memoryview | BIP32KeyData | tuple[int, int] | PreparedPoint | None, script_tree: list[tuple[int, list[int | str | bytes | bytearray | memoryview]] | TaprootScriptTree], script_num: int) tuple[list[int | str | bytes | bytearray | memoryview], bytes][source]¶
Return (leaf script, control block) for a script-path spend.
script_num picks the leaf in tree order, as tree_helper returns them; the control block is BIP341’s – parity bit plus leaf version, then the x-only internal key, then the merkle path – and a missing internal key is the unspendable point output_pubkey uses.
In tree order and counting from zero: Python would read -1 as the last leaf and hand back a control block that proves it, so a leaf named from the wrong end is refused rather than answered.
- btclib.script.taproot.leaf_hash(leaf_version: int, script: bytes) bytes[source]¶
Return the BIP341 tapleaf hash of a serialized leaf script.
What names a leaf everywhere but in the control block: the merkle path is built from these, a BIP342 signature commits to one, and BIP371’s psbt fields key their taproot data by one. script is the leaf script already serialized, which is the form all three of those hold it in.
- btclib.script.taproot.output_prvkey(prv_key: int | bytes | str | bytearray | memoryview | BIP32KeyData, script_tree: list[tuple[int, list[int | str | bytes | bytearray | memoryview]] | list[tuple[int, list[int | str | bytes | bytearray | memoryview]] | TaprootScriptTree]] | None = None) int[source]¶
Return the private key of the taproot output key, per BIP341.
The private counterpart of output_pubkey: the internal key is negated where its public point has an odd y, then tweaked by the script tree’s root hash, so its public point is the output key exactly.
- btclib.script.taproot.output_prvkey_from_merkle_root(prv_key: int | bytes | str | bytearray | memoryview | BIP32KeyData, merkle_root: bytes | str | bytearray | memoryview = b'') int[source]¶
Return the private key of a taproot output from a merkle root.
output_prvkey with the root already in hand, the shape PSBT_IN_TAP_MERKLE_ROOT carries it in: a KeyManager signing a taproot key path spend holds the internal private key and this field, never the script tree that produced the root, a psbt naming a script path by its leaf and control block instead.
- btclib.script.taproot.output_pubkey(internal_pubkey: int | bytes | str | bytearray | memoryview | BIP32KeyData | tuple[int, int] | PreparedPoint | None = None, script_tree: list[tuple[int, list[int | str | bytes | bytearray | memoryview]] | list[tuple[int, list[int | str | bytes | bytearray | memoryview]] | TaprootScriptTree]] | None = None) tuple[bytes, int][source]¶
Return a taproot output key and its parity, per BIP341.
The x-only internal key is tweaked by the script tree’s root hash, an empty tree contributing empty bytes – key path only – and a missing internal key replaced by BIP341’s unspendable point, script path only. The parity bit is the tweaked point’s, needed by the control block and never serialized in the output.
- btclib.script.taproot.output_pubkey_from_merkle_root(internal_pubkey: bytes | str | bytearray | memoryview, merkle_root: bytes | str | bytearray | memoryview = b'') tuple[bytes, int][source]¶
Return a taproot output key from a merkle root, per BIP341.
output_pubkey with the root already in hand, which is the shape a psbt has it in: BIP371’s PSBT_IN_TAP_MERKLE_ROOT is the root and not the tree that produced it, an input naming the branch it spends by its leaf script and control block instead – so a signer that takes the key path is told the root and nothing else about the tree. An empty root is key path only, as an empty tree is.
The internal key is x-only and 32 bytes, which is what BIP341 tweaks and what the psbt field holds; output_pubkey takes the wider Key because a caller building an output has the key in whatever form it reached them in.
- btclib.script.taproot.parse(stream: BytesIO | bytes | str | bytearray | memoryview, exit_on_op_success: bool = False) list[int | str | bytes | bytearray | memoryview][source]¶
Parse a tapscript into its commands, per BIP342.
An unknown op code is refused, data pushes come back as hex strings, and an OP_SUCCESSx ends the parse: what follows one is returned as raw bytes, BIP342 not requiring it to be a script – or, with exit_on_op_success, the whole answer is the single marker [“OP_SUCCESS”], which is Core’s pre-scan. An element over 520 bytes is refused only by a parse that meets no OP_SUCCESSx, one anywhere making the script valid.
A bool and nothing else, which is the line tests/built_object_contract_test.py draws: this flag decides what is computed rather than whether a check runs, so a value read for its truth answered the pre-scan’s marker where the commands were asked for – two different readings of the same bytes.
- btclib.script.taproot.serialize(script: list[int | str | bytes | bytearray | memoryview]) bytes[source]¶
Serialize a tapscript from its commands.
The tapscript twin of script.serialize, differing where BIP342 differs: the OP_SUCCESSx names exist here, and one must be followed by exactly one bytes command, appended raw – what follows an OP_SUCCESS need not be a script, so it round-trips unparsed.
- btclib.script.taproot.tree_helper(script_tree: list[tuple[int, list[int | str | bytes | bytearray | memoryview]] | TaprootScriptTree]) tuple[list[tuple[tuple[int, list[int | str | bytes | bytearray | memoryview]], bytes]], bytes][source]¶
Walk a script tree: (every leaf with its merkle path, root hash).
BIP341’s taproot_tree_helper: the leaves come back in tree order, each with the control-block path that proves it, and the root is what the output key commits to.
btclib.script.witness module¶
The Witness dataclass; the class docstring has the contract.
- class btclib.script.witness.Witness(stack: Sequence[bytes | str | bytearray | memoryview] | None = None, *, check_validity: bool = True)[source]¶
Bases:
objectThe witness stack of one transaction input, per BIP141.
A tuple of byte strings, bottom of the stack first, exactly as serialized in the transaction’s witness section after the outputs; a non-segwit input has an empty one. Immutable throughout, so a Witness can be shared and hashed; the script interpreter pops a list copy of its own.
- assert_valid() None[source]¶
Refuse a stack element that does not read as bytes.
Any elements are a valid witness – what they must mean is the spending script’s business – so readability is the whole check.
- classmethod from_dict(dict_: Mapping[str, Sequence[bytes | str | bytearray | memoryview]], *, check_validity: bool = True) Witness[source]¶
Build a Witness from the dict shape to_dict writes.
The stack is asked for as an array rather than left to the constructor, which reads stack or []: a None there is an empty witness, so a schema mistake was a witness of no elements instead of an error.
- classmethod parse(data: BytesIO | bytes | str | bytearray | memoryview, *, check_validity: bool = True) Witness[source]¶
Return a Witness by parsing binary data.
Octets are one whole witness and a stream is the caller’s: btclib/utils.py states the rule both halves of this contract read. The stream case is what a transaction is parsed with – one witness per input, out of the stream the transaction is read from – and the octet case is what a PSBT_IN_FINAL_SCRIPTWITNESS value is.
Module contents¶
Scripts: types, classification, sig hashes, taproot, the engine.
The flat surface is the script itself: the codec, the ScriptPubKey classification with both halves of every pair, the Witness, and the taproot output key a caller builds an address from.
Three submodules are named beside it, being the three subgroups a caller reaches by name – sig_hash for what a signature commits to, taproot for the tree and the control block, engine for the verifier – and docs/proposals/cli.md promises each as a command group. taproot is imported above for the four names re-exported flat; sig_hash and engine are imported on demand by the __getattr__ at the bottom of this file, and that is not a choice about speed: both reach the transaction stack – sig_hash needs btclib.tx, whose tx_in and tx_out import this package back – and btclib.script.engine.script asks this very package for sig_hash, so importing either from here at import time closes a cycle on a half-initialized btclib.script. That is issue #147’s shape, and tests/imports_test.py is what reports it.
The other submodules are not named: script, script_pub_key, sig_ops and witness are where the flat names above are defined, and limits and op_codes_tapscript are tables the engine reads. Each declares its own __all__ and is importable; none is a group.
- class btclib.script.Script(script: bytes | str | bytearray | memoryview = b'', *, check_validity: bool = True)[source]¶
Bases:
objectA Bitcoin script, held as its bytes.
The bytes are the script – assert_valid asks nothing else of them – and asm is their parse, computed on first read and cached. Immutable, so a Script can be shared and concatenated (+) without aliasing surprises; ScriptPubKey extends it with a network.
- property asm: list[int | str | bytes | bytearray | memoryview][source]¶
The parsed script, parsed once.
A plain property would parse self.script again on every read, for a value that cannot change; cached, a second read is an attribute lookup.
Nothing warms the cache: construction does not parse, and __init__ filling the cache in would be the wrong trade – measured on a 16.5 kB script, an instance holding the parse costs 55.4 kB against 0.2 kB without it, 277 times the script’s own bytes, and nothing inside the library reads .asm.
- assert_valid() None[source]¶
Assert that the script is bytes, which is all a script is.
There is no other question to ask: Bitcoin Core has no validity notion for a script either – a CScript is a vector of bytes, and the only script-level predicate it offers is IsUnspendable(), for pruning the UTXO set. Whether a script can be executed is the interpreter’s answer, given by executing it, and the limits it enforces depend on the sigversion the script is spent under: in tapscript an OP_SUCCESSx makes a script valid however malformed the rest of it is, so no predicate on the bytes alone could answer for both.
Not a parse, refusing what the parse refuses – a push over 520 bytes, a truncated push, an op code no table names: five transactions in blocks 251718 to 299571 carry such scripts, so that predicate leaves Tx.parse unable to read them and .asm raising for the scripts an explorer prints (issue #123).
The coercion is the check, as it is in Witness.assert_valid: a Script built through __init__ has been through bytes_from_octets already, and this is what answers for one reached any other way.
- class btclib.script.ScriptPubKey(script: bytes | str | bytearray | memoryview, network: str = 'mainnet', *, check_validity: bool = True)[source]¶
Bases:
ScriptA Script with the network its addresses render on.
The script bytes and their validation are Script’s; the network enters address, addresses, the equality test and the hash, two ScriptPubKey being equal when their scripts match and their networks are of one type – mainnet against the rest – rather than of one name. Frozen and hashable on that same pair, so a ScriptPubKey can be a set member or a dict key. The classmethods build the standard shapes, one per type the classifier names.
- property address: str¶
Return the bech32/base58 address.
An address is a shortened notation for a particular script. As a transaction output contains exactly one script, it has at most one address (it is possible that the script does not correspond to a particular address, though).
- property addresses: list[str]¶
Return the address, if any, or the p2pkh addresses for p2ms.
Historically, a p2pkh address has been used to refer to a key. For a p2ms multisig script, the keys it pays to are returned, expressed in p2pkh-address notation.
https://bitcoin.stackexchange.com/questions/30442/multiple-addresses-in-one-utxo
- classmethod from_address(addr: bytes | str | bytearray | memoryview, *, check_validity: bool = True) ScriptPubKey[source]¶
Return the ScriptPubKey of the input bech32/base58 address.
- classmethod nulldata(data: bytes | str | bytearray | memoryview, *, check_validity: bool = True) ScriptPubKey[source]¶
Return the nulldata ScriptPubKey of the provided data.
- classmethod p2ms(m: int, keys: Sequence[int | bytes | str | bytearray | memoryview | BIP32KeyData | tuple[int, int] | PreparedPoint], network: str | None = None, compressed: bool | None = None, lexicographic_sorting: bool = True, *, check_validity: bool = True) ScriptPubKey[source]¶
Return the m-of-n multi-sig ScriptPubKey of the provided keys.
BIP67 endorses lexicographic sorting of compressed public keys.
Sorting uncompressed keys (leading 0x04 byte) would result in a different order. An uncompressed key is not refused here even when lexicographic_sorting is True: BIP67’s own compatibility note calls such a key a sign of a non-conforming counterparty, not something to reject, and Bitcoin Core’s sortedmulti() accepts the mix and sorts it the same way – see #299.
https://github.com/bitcoin/bips/blob/master/bip-0067.mediawiki
- classmethod p2pk(key: int | bytes | str | bytearray | memoryview | BIP32KeyData | tuple[int, int] | PreparedPoint, network: str | None = None, *, check_validity: bool = True) ScriptPubKey[source]¶
Return the p2pk ScriptPubKey of the provided Key.
- classmethod p2pkh(key: int | bytes | str | bytearray | memoryview | BIP32KeyData | tuple[int, int] | PreparedPoint, compressed: bool | None = None, network: str | None = None, *, check_validity: bool = True) ScriptPubKey[source]¶
Return the p2pkh ScriptPubKey of the provided key.
- classmethod p2sh(redeem_script: bytes | str | bytearray | memoryview, network: str = 'mainnet', *, check_validity: bool = True) ScriptPubKey[source]¶
Return the p2sh ScriptPubKey of the provided redeem script.
- classmethod p2tr(internal_key: int | bytes | str | bytearray | memoryview | BIP32KeyData | tuple[int, int] | PreparedPoint | None = None, script_path: list[tuple[int, list[int | str | bytes | bytearray | memoryview]] | list[tuple[int, list[int | str | bytes | bytearray | memoryview]] | TaprootScriptTree]] | None = None, network: str = 'mainnet', *, check_validity: bool = True) ScriptPubKey[source]¶
Return the p2tr ScriptPubKey of the provided script tree.
- classmethod p2wpkh(key: int | bytes | str | bytearray | memoryview | BIP32KeyData | tuple[int, int] | PreparedPoint, *, check_validity: bool = True) ScriptPubKey[source]¶
Return the p2wpkh ScriptPubKey of the provided key.
If the provided key is a public one, it must be compressed.
- classmethod p2wsh(redeem_script: bytes | str | bytearray | memoryview, network: str = 'mainnet', *, check_validity: bool = True) ScriptPubKey[source]¶
Return the p2wsh ScriptPubKey of the provided redeem script.
- class btclib.script.Witness(stack: Sequence[bytes | str | bytearray | memoryview] | None = None, *, check_validity: bool = True)[source]¶
Bases:
objectThe witness stack of one transaction input, per BIP141.
A tuple of byte strings, bottom of the stack first, exactly as serialized in the transaction’s witness section after the outputs; a non-segwit input has an empty one. Immutable throughout, so a Witness can be shared and hashed; the script interpreter pops a list copy of its own.
- assert_valid() None[source]¶
Refuse a stack element that does not read as bytes.
Any elements are a valid witness – what they must mean is the spending script’s business – so readability is the whole check.
- classmethod from_dict(dict_: Mapping[str, Sequence[bytes | str | bytearray | memoryview]], *, check_validity: bool = True) Witness[source]¶
Build a Witness from the dict shape to_dict writes.
The stack is asked for as an array rather than left to the constructor, which reads stack or []: a None there is an empty witness, so a schema mistake was a witness of no elements instead of an error.
- classmethod parse(data: BytesIO | bytes | str | bytearray | memoryview, *, check_validity: bool = True) Witness[source]¶
Return a Witness by parsing binary data.
Octets are one whole witness and a stream is the caller’s: btclib/utils.py states the rule both halves of this contract read. The stream case is what a transaction is parsed with – one witness per input, out of the stream the transaction is read from – and the octet case is what a PSBT_IN_FINAL_SCRIPTWITNESS value is.
- btclib.script.address(script_pub_key: bytes | str | bytearray | memoryview, network: str = 'mainnet') str[source]¶
Return the bech32/base58 address from a script_pub_key.
A witness program of version 2 or higher has an address as much as a p2tr one does – bech32m spells it, b32.address_from_witness writes it, and Bitcoin Core renders it, EncodeDestination having a WitnessUnknown case. Answering “” for one would be indistinguishable from “this script has no address”, which is the right answer for a nulldata output and the wrong one where btclib can read the address back into the very script it came from (issue #251).
- btclib.script.addresses(script_pub_key: bytes | str | bytearray | memoryview, network: str = 'mainnet') list[str][source]¶
Return the p2pkh addresses of the pub_keys in a p2ms script_pub_key.
- btclib.script.assert_nulldata(script_pub_key: bytes | str | bytearray | memoryview) None[source]¶
Assert the standard nulldata shape: OP_RETURN and one minimal push.
A policy, and narrower than Bitcoin Core’s classification on every count. Core’s Solver answers NULL_DATA for an OP_RETURN whose remaining bytes pass IsPushOnly, which refuses only an op code above OP_16: any number of pushes qualifies, OP_1..OP_16 among them, and so does the empty remainder of a bare OP_RETURN. The 83-byte bound is a third thing again, MAX_OP_RETURN_RELAY being relay policy tested by IsStandardTx and not by the classifier, and the length 78 refused below is the non-minimal push of 75 bytes through OP_PUSHDATA1, which consensus allows. So 6a, 6a51, 6a0101010102 and a nulldata of any size are NULL_DATA there and unknown here (issue #211).
The narrowness is what lets type_and_payload answer at all: it returns one payload, and OP_RETURN followed by push-only bytes has none for a bare OP_RETURN and two for two pushes. It is also the one shape ScriptPubKey.nulldata builds, so the classifier agrees with the constructor. A caller wanting Core’s answer wants a different function, returning a list of payloads.
6a00 is accepted, and by arithmetic rather than by Core’s rule: the 00 is read here as the length marker of a zero-length push, where Core reads it as OP_0, a push like any other.
- btclib.script.assert_p2ms(script_pub_key: bytes | str | bytearray | memoryview) None[source]¶
Refuse bytes that are not p2ms: m, the pushed keys, n, OP_CHECKMULTISIG.
p2ms_m_and_keys is the whole of the check, and says what it is.
- btclib.script.assert_p2pk(script_pub_key: bytes | str | bytearray | memoryview) None[source]¶
Refuse bytes that are not p2pk: a pushed pub_key, OP_CHECKSIG.
The key must parse as a curve point, so a well-shaped script pushing 33 or 65 bytes that are not one is refused too.
- btclib.script.assert_p2pkh(script_pub_key: bytes | str | bytearray | memoryview) None[source]¶
Refuse bytes that are not p2pkh.
The shape is OP_DUP OP_HASH160, a 20-byte push, OP_EQUALVERIFY OP_CHECKSIG; the hash is any 20 bytes.
- btclib.script.assert_p2sh(script_pub_key: bytes | str | bytearray | memoryview) None[source]¶
Refuse bytes that are not p2sh, per BIP16.
The shape is OP_HASH160, a 20-byte push, OP_EQUAL; the hash is any 20 bytes.
- btclib.script.assert_p2tr(script_pub_key: bytes | str | bytearray | memoryview) None[source]¶
Refuse bytes that are not p2tr: OP_1, a 32-byte push, per BIP341.
The 32 bytes are not checked to be a valid x-only key: the shape is the classification, and an output key off the curve is found by the spender, not by the classifier.
- btclib.script.assert_p2wpkh(script_pub_key: bytes | str | bytearray | memoryview) None[source]¶
Refuse bytes that are not p2wpkh: OP_0, a 20-byte push, per BIP141.
- btclib.script.assert_p2wsh(script_pub_key: bytes | str | bytearray | memoryview) None[source]¶
Refuse bytes that are not p2wsh: OP_0, a 32-byte push, per BIP141.
- btclib.script.assert_segwit(script_pub_key: bytes | str | bytearray | memoryview) None[source]¶
Refuse bytes that are not a witness program, per BIP141.
The shape every segwit output shares, whatever its version: one version op code – OP_0 or OP_1..OP_16 – and one push of 2 to 40 bytes. Shape only; the program is not interpreted.
- btclib.script.check_output_pubkey(q: bytes | str | bytearray | memoryview, script: bytes | str | bytearray | memoryview, control: bytes | str | bytearray | memoryview) bool[source]¶
Answer whether the control block proves the script against the key.
BIP341’s control-block verification: the leaf hash is folded up the merkle path in the control block, and the internal key tweaked by the result must equal the output key q, parity included. A malformed control block or an internal key that is not a point is refused rather than answered False, either being no proof at all.
- btclib.script.input_script_sig(internal_pubkey: int | bytes | str | bytearray | memoryview | BIP32KeyData | tuple[int, int] | PreparedPoint | None, script_tree: list[tuple[int, list[int | str | bytes | bytearray | memoryview]] | TaprootScriptTree], script_num: int) tuple[list[int | str | bytes | bytearray | memoryview], bytes][source]¶
Return (leaf script, control block) for a script-path spend.
script_num picks the leaf in tree order, as tree_helper returns them; the control block is BIP341’s – parity bit plus leaf version, then the x-only internal key, then the merkle path – and a missing internal key is the unspendable point output_pubkey uses.
In tree order and counting from zero: Python would read -1 as the last leaf and hand back a control block that proves it, so a leaf named from the wrong end is refused rather than answered.
- btclib.script.is_nulldata(script_pub_key: bytes | str | bytearray | memoryview) bool[source]¶
Answer whether the bytes are a standard nulldata script_pub_key.
- btclib.script.is_p2ms(script_pub_key: bytes | str | bytearray | memoryview) bool[source]¶
Answer whether the bytes are a p2ms script_pub_key.
- btclib.script.is_p2pk(script_pub_key: bytes | str | bytearray | memoryview) bool[source]¶
Answer whether the bytes are a p2pk script_pub_key.
- btclib.script.is_p2pkh(script_pub_key: bytes | str | bytearray | memoryview) bool[source]¶
Answer whether the bytes are a p2pkh script_pub_key.
- btclib.script.is_p2sh(script_pub_key: bytes | str | bytearray | memoryview) bool[source]¶
Answer whether the bytes are a p2sh script_pub_key.
- btclib.script.is_p2tr(script_pub_key: bytes | str | bytearray | memoryview) bool[source]¶
Answer whether the bytes are a p2tr script_pub_key.
- btclib.script.is_p2wpkh(script_pub_key: bytes | str | bytearray | memoryview) bool[source]¶
Answer whether the bytes are a p2wpkh script_pub_key.
- btclib.script.is_p2wsh(script_pub_key: bytes | str | bytearray | memoryview) bool[source]¶
Answer whether the bytes are a p2wsh script_pub_key.
- btclib.script.is_segwit(script_pub_key: bytes | str | bytearray | memoryview) bool[source]¶
Answer whether the bytes are a witness program of any version.
- btclib.script.op_int(i: int) str[source]¶
Name the one-byte op code that pushes the number i, -1 to 16.
OP_0..OP_16 and OP_1NEGATE are the only numbers with an op code of their own; any other integer is refused, a caller wanting it pushed passing the integer itself to serialize.
- btclib.script.output_prvkey(prv_key: int | bytes | str | bytearray | memoryview | BIP32KeyData, script_tree: list[tuple[int, list[int | str | bytes | bytearray | memoryview]] | list[tuple[int, list[int | str | bytes | bytearray | memoryview]] | TaprootScriptTree]] | None = None) int[source]¶
Return the private key of the taproot output key, per BIP341.
The private counterpart of output_pubkey: the internal key is negated where its public point has an odd y, then tweaked by the script tree’s root hash, so its public point is the output key exactly.
- btclib.script.output_prvkey_from_merkle_root(prv_key: int | bytes | str | bytearray | memoryview | BIP32KeyData, merkle_root: bytes | str | bytearray | memoryview = b'') int[source]¶
Return the private key of a taproot output from a merkle root.
output_prvkey with the root already in hand, the shape PSBT_IN_TAP_MERKLE_ROOT carries it in: a KeyManager signing a taproot key path spend holds the internal private key and this field, never the script tree that produced the root, a psbt naming a script path by its leaf and control block instead.
- btclib.script.output_pubkey(internal_pubkey: int | bytes | str | bytearray | memoryview | BIP32KeyData | tuple[int, int] | PreparedPoint | None = None, script_tree: list[tuple[int, list[int | str | bytes | bytearray | memoryview]] | list[tuple[int, list[int | str | bytes | bytearray | memoryview]] | TaprootScriptTree]] | None = None) tuple[bytes, int][source]¶
Return a taproot output key and its parity, per BIP341.
The x-only internal key is tweaked by the script tree’s root hash, an empty tree contributing empty bytes – key path only – and a missing internal key replaced by BIP341’s unspendable point, script path only. The parity bit is the tweaked point’s, needed by the control block and never serialized in the output.
- btclib.script.output_pubkey_from_merkle_root(internal_pubkey: bytes | str | bytearray | memoryview, merkle_root: bytes | str | bytearray | memoryview = b'') tuple[bytes, int][source]¶
Return a taproot output key from a merkle root, per BIP341.
output_pubkey with the root already in hand, which is the shape a psbt has it in: BIP371’s PSBT_IN_TAP_MERKLE_ROOT is the root and not the tree that produced it, an input naming the branch it spends by its leaf script and control block instead – so a signer that takes the key path is told the root and nothing else about the tree. An empty root is key path only, as an empty tree is.
The internal key is x-only and 32 bytes, which is what BIP341 tweaks and what the psbt field holds; output_pubkey takes the wider Key because a caller building an output has the key in whatever form it reached them in.
- btclib.script.p2ms_m_and_keys(script_pub_key: bytes | str | bytearray | memoryview) tuple[int, list[bytes]][source]¶
Return the threshold and the pub keys of a p2ms script_pub_key.
The bounds are checked – 0 < m <= n < 17 – and each key is read as a push of the declared length and then parsed as a public key: a push that is not one is what makes the bytes not a p2ms, which is the answer is_p2ms gives.
The keys come back as the script holds them, uncompressed ones included, and not as the parse normalized them: BIP174 keys a partial signature by the key “as it appears in the scriptPubKey or redeemScript”, so the Finalizer below matches these bytes.
Three callers ask this one question, which is why it is asked in one place: addresses wants a p2pkh address per key, assert_p2ms the exception alone, and the psbt Finalizer both halves of the answer – OP_CHECKMULTISIG takes m signatures, in the order the script lists the keys they belong to.
- btclib.script.parse(stream: BytesIO | bytes | str | bytearray | memoryview) list[int | str | bytes | bytearray | memoryview][source]¶
Decode a script, as Bitcoin Core decodes one.
Which is to say: whatever the bytes are. Core’s only decoder is GetScriptOp, it reads one instruction at a time, and the sole thing it refuses is a push running past the end of the script – where the walk stops, and ERROR_COMMAND is appended in the place Core’s ScriptToAsmStr writes the same “[error]”. Every other question, the element limit and the op codes no valid script may execute among them, belongs to the interpreter and is asked there; a script it would refuse still decodes, exactly as one that is in a block must (issue #123).
- btclib.script.push_int(i: int) str[source]¶
Return the shortest command that pushes the number i.
The op code where the number has one – op_int, -1 to 16 – and the CScriptNum encoding of it otherwise, as the hex a data push is written as. Both halves are here already; this is the choice between them, which is what a caller assembling a script by hand writes out every time it puts a number in one: a threshold, a relative timelock, a size. Which half applies is a property of the value and not of what the number means, so a script template holding 16 and one holding 17 are written the same way and serialize differently.
serialize reaches the same bytes from the integer itself for everything above 16, and warns for -1 to 16 that the op code is one byte shorter: this is that op code, so the warning is the caller being told to write this instead. Minimal because the consumers are: miniscript.from_script refuses a push written the long way, and the interpreter refuses it too under MINIMALDATA, so a number pushed with a byte to spare is a script that reads as nothing and may not spend.
- btclib.script.script_from_dict(value: Mapping[str, str] | bytes | str | bytearray | memoryview) bytes[source]¶
Read back what script_to_dict wrote: the hex, and only it.
asm is derived from hex, so there is nothing in it to read. It is still not ignored: a dict carrying an asm that the hex does not produce is refused, naming both. Ignoring it would let a hand-edited asm sit in a stored dict describing a script that is not the one the bytes hold, and every consumer that reads the human-readable field – a diff, a review, an explorer – would then be reading a lie, silently. Believing the asm instead is not on offer: it is lossy (a non-minimal push comes back minimal, [error] comes back not at all), so it cannot name every script hex can.
A bare hex string is accepted as well, which is the shape to_dict emitted before it emitted this one. Every constructor downstream already takes Octets, so accepting it costs one branch and keeps a dict stored by an older btclib readable – the emission is what changed, and a reader that refused the old spelling would break round trips that never had an asm to disagree with.
- btclib.script.script_to_dict(script: bytes) dict[str, str][source]¶
Render a script as Bitcoin Core’s RPC renders one: asm and hex.
The two renderings of the same bytes, which is what getrawtransaction and decodepsbt hand back for every script they report. hex is the script; asm is parse joined by spaces, i.e. a reading aid, and the only thing script_from_dict will believe is the hex.
Not Core’s asm byte for byte, and it cannot be: btclib prints a push as upper-case hex where Core prints one under 5 bytes as a decimal number, and neither spelling is invertible – see ERROR_COMMAND above. What this is, exactly, is Script.asm with a space between its commands.
- btclib.script.serialize(script: Sequence[int | str | bytes | bytearray | memoryview]) bytes[source]¶
Serialize a script from its commands.
An integer is encoded as the number it pushes – with a warning where a one-byte op code means the same, and a refusal outside the int64 a script number is – a string is an op code name, an UNKNOWN_OP_CODE_n byte, or hex data, and bytes are data; data is always the minimal push operator, per BIP62. What parse returns round-trips, ERROR_COMMAND excepted, that marker being a place in the bytes rather than an instruction.
The minimal operator and not the minimal command: data is data, so the bytes 0x01 are pushed with a length of one and not with OP_1, and the same goes for an integer command, which is the number’s bytes and is warned about for exactly this. push_int is what writes a number the shortest way there is, and what every caller in this library that means one uses.
Zero is where the two coincide and the warning therefore stops: the empty vector is what encode_num writes for it, and the push of an empty vector is OP_0, so serialize([0]) is the op code – as Core’s CScript() << 0 is – with nothing shorter left to suggest.
A sequence of commands and not one command: Sequence[Command] accepts a str and a bytes, each of them being a sequence of Command as far as the type goes, so serialize(“OP_DUP”) was six one-character commands and a bytes handed here a script of one integer command per byte – the first refused for a character it could not read as an op code, the second accepted and wrong. What is not a sequence at all was “not iterable” from underneath the library.
- btclib.script.sig_op_count(script: bytes | str | bytearray | memoryview) int[source]¶
Return the number of legacy signature checks a script announces.
One for OP_CHECKSIG and OP_CHECKSIGVERIFY, MAX_PUBKEYS_PER_MULTISIG for OP_CHECKMULTISIG and OP_CHECKMULTISIGVERIFY however many keys the script actually pushes, and nothing for the rest – the count is announced by the bytes and is not what executing them would do.
Where the script stops parsing the count stops too, and no exception is raised: Core’s loop break`s when `GetOp returns false, which is a push running past the end, and op_code_spans ends the same way. The coinbase output script of testnet block 987,876 is that case on chain – it ends …d8 3d 0aa68688ac, and the OP_CHECKSIG of that final ac is five bytes inside the 61-byte push 3d announces, so neither implementation ever reaches it and the answer for that script is zero.
- btclib.script.type_and_payload(script_pub_key: bytes | str | bytearray | memoryview) tuple[Literal['nulldata', 'p2ms', 'p2pk', 'p2pkh', 'p2sh', 'p2tr', 'p2wpkh', 'p2wsh', 'unknown', 'witness_unknown'], bytes][source]¶
Return (script_pub_key type, payload) from the input script_pub_key.
The returns here and in _witness_type_and_payload are the whole of ScriptType between them, mypy checking each one against it: an eleventh shape classified in either is a member added there.