btclib_wallet.dlc package¶
Submodules¶
btclib_wallet.dlc.oracle module¶
DLC oracle announcements and attestations, parsed and checked offline.
https://github.com/discreetlogcontracts/dlcspecs/blob/master/Oracle.md https://github.com/discreetlogcontracts/dlcspecs/blob/master/Messaging.md
An oracle announces an event before it happens: the nonces it will sign with, when it will attest, and what the outcomes are. After the event it attests with one BIP340 signature per nonce. Nothing here asks the oracle anything: OracleAnnouncement.parse and OracleAttestation.parse read the TLVs, and verify_announcement and verify_attestation check them.
An announcement’s signature has two schemes, named by the caller:
TAGGED, the default, is the specification’s: a BIP340 signature over the tagged hash DLC/oracle/announcement/v0 of the serialized oracle_event, header included, which is what bitcoin-s does.
PLAIN_SHA256 is the plain sha256 of the oracle_event’s value, header excluded, which is what every announcement of the dlcspecs vectors carries.
A signature under one scheme is not valid under the other.
The caller pins the oracle’s key. verify_announcement checks the signature against the key the announcement carries, and verify_attestation against that same key, so an event announced under anyone’s key, with nonces of their own, verifies and is attested. A caller compares announcement.public_key with the oracle’s key it already trusts, and checks the announcement with verify_announcement before checking an attestation against it: verify_attestation does not check the announcement’s signature.
An attestation signs each outcome as a BIP340 signature, with the nonce the announcement committed to, of the tagged hash DLC/oracle/attestation/v0 of the outcome’s UTF-8 text. The signatures and the outcomes are in the order of the nonces. A signed digit decomposition has the sign, + or -, first, and then each digit in base 10, however large the base.
The base of a digit decomposition is a `u16` on the wire, where Messaging.md says bigsize: the dlcspecs vectors and bitcoin-s write two octets.
Strings are NFC UTF-8, and a string that is not is refused: that is Messaging.md’s string type, and Oracle.md has each outcome normalized before it is signed, so that one outcome is not signed under two spellings.
An enum’s outcomes are distinct. An outcome is signed by its text, so a repeated one names no second outcome: the specification is silent, and this package refuses it.
Every class checks itself in assert_valid. parse and serialize call it unless check_validity is false; the signing and verifying functions always call it on the objects they are given. parse checks how long each value is, and where it ends, whatever check_validity says.
A nonce signs one message. sign_attestation takes the nonces’ secret keys, and two signatures with one nonce reveal the oracle’s key. A refusal never prints a key or a nonce.
- class btclib_wallet.dlc.oracle.DigitDecompositionEventDescriptor(*, base: int, is_signed: bool, unit: str, precision: int, nb_digits: int)[source]¶
Bases:
objectAn event with a number, signed digit by digit.
- class btclib_wallet.dlc.oracle.EnumEventDescriptor(*, outcomes: tuple[str, ...])[source]¶
Bases:
objectAn event with a list of outcomes, one of which the oracle signs.
- class btclib_wallet.dlc.oracle.OracleAnnouncement(*, signature: bytes, public_key: bytes, event: OracleEvent)[source]¶
Bases:
objectAn oracle’s event, with its key and the signature of that event.
Whether the signature is valid is verify_announcement’s to say, under the scheme the caller names. Whose key it is, is the caller’s to pin: see the module docstring.
- classmethod parse(data: BytesIO | bytes | str | bytearray | memoryview, *, check_validity: bool = True) OracleAnnouncement[source]¶
Return the announcement of its TLV.
Octets are one announcement and bytes after it are refused. A stream is left on the byte after the announcement.
- class btclib_wallet.dlc.oracle.OracleAttestation(*, event_id: str, public_key: bytes, signatures: tuple[bytes, ...], outcomes: tuple[str, ...])[source]¶
Bases:
objectThe signatures with which an oracle attests the outcome of an event.
- classmethod parse(data: BytesIO | bytes | str | bytearray | memoryview, *, check_validity: bool = True) OracleAttestation[source]¶
Return the attestation of its TLV.
Octets are one attestation and bytes after it are refused. A stream is left on the byte after the attestation.
- class btclib_wallet.dlc.oracle.OracleEvent(*, nonces: tuple[bytes, ...], maturity_epoch: int, descriptor: EnumEventDescriptor | DigitDecompositionEventDescriptor, event_id: str)[source]¶
Bases:
objectWhat an oracle commits to before an event: its nonces and outcomes.
- classmethod parse(data: BytesIO | bytes | str | bytearray | memoryview, *, check_validity: bool = True) OracleEvent[source]¶
Return the event of its TLV.
Octets are one event and bytes after it are refused. A stream is left on the byte after the event.
- btclib_wallet.dlc.oracle.outcome_message(outcome: str) bytes[source]¶
Return the 32 octets the oracle signs to attest an outcome.
- btclib_wallet.dlc.oracle.sign_announcement(event: OracleEvent, prv_key: bytes | str | bytearray | memoryview | int, *, scheme: Literal['tagged', 'sha256'] = 'tagged') OracleAnnouncement[source]¶
Return the announcement of event, signed by the oracle’s key.
- btclib_wallet.dlc.oracle.sign_attestation(announcement: OracleAnnouncement, outcomes: tuple[str, ...], prv_key: bytes | str | bytearray | memoryview | int, nonces: tuple[bytes | str | bytearray | memoryview | int, ...]) OracleAttestation[source]¶
Return the attestation of outcomes, signed with the committed nonces.
nonces are the secret keys of the announcement’s nonces, in their order.
- btclib_wallet.dlc.oracle.verify_announcement(announcement: OracleAnnouncement, *, scheme: Literal['tagged', 'sha256'] = 'tagged') bool[source]¶
Return whether the announcement’s signature is valid under the scheme.
It is checked against announcement.public_key, which the caller pins as the module docstring says.
- btclib_wallet.dlc.oracle.verify_attestation(announcement: OracleAnnouncement, attestation: OracleAttestation) bool[source]¶
Return whether attestation attests an outcome of the announced event.
It must come from the announced oracle and event, sign with the committed nonces in their order, name outcomes the event allows, and carry a valid signature of each. The announcement is taken as given: the caller has had verify_announcement accept it and has pinned its key, as the module docstring says.
btclib_wallet.dlc.signing module¶
The signatures of a DLC: adaptor signatures on CETs, and the witnesses.
https://github.com/discreetlogcontracts/dlcspecs/blob/master/ECDSA-adaptor.md
Each party gives the other an adaptor signature of every CET, encrypted under the outcome’s signature point (ecc.ecc.ecdsa_adaptor), so whoever holds the oracle’s attestation decrypts it into an ordinary signature. The refund transaction is signed outright. All of them spend the funding output with SIGHASH_ALL.
Only the agreed transactions are signed. sig_hash, sign and adaptor_sign take the Dlc and the unsigned transaction, and refuse any transaction but those btclib_wallet.dlc.transactions builds from those terms: the refund, or a CET of one of their payouts. verify_adaptor answers False for any other. The script code and the funding value come from the terms.
Funding inputs are not signed here. The wallet’s signer signs them, whatever the script; complete_funding_tx only places the witnesses it produced.
Adaptor signatures encrypt a secret the oracle knows. The scheme leaks a Diffie-Hellman key per signature, so it is for a signature point of an oracle’s attestation and for nothing else: see ecc.ecc.ecdsa_adaptor. A refusal never prints a key or a scalar.
- btclib_wallet.dlc.signing.adaptor_sign(dlc: Dlc, tx: Tx, prv_key: bytes | str | bytearray | memoryview | int, sig_point: tuple[int, int], aux: bytes | str | bytearray | memoryview | None = None) bytes[source]¶
Return the adaptor signature of tx under the outcome’s signature point.
aux is 32 octets of randomness, fresh when absent. Fixing it makes the signature repeatable: do it only to check a vector.
- btclib_wallet.dlc.signing.complete_cet(cet: Tx, *, local_pub_key: bytes, local_sig: bytes, remote_pub_key: bytes, remote_adaptor_sig: bytes | str | bytearray | memoryview, decryption_key: bytes | str | bytearray | memoryview | int) Tx[source]¶
Return the CET with both signatures placed in its witness.
The remote signature is the remote adaptor signature decrypted with the sum of the oracle signatures’ scalars: the decryption key of the outcome’s signature point. Neither signature is verified here: call verify_adaptor on the remote adaptor signature first.
- btclib_wallet.dlc.signing.complete_funding_tx(funding_tx: Tx, witnesses: Mapping[OutPoint, Witness]) Tx[source]¶
Return the funding transaction with each input’s witness in place.
- btclib_wallet.dlc.signing.complete_spend(tx: Tx, sigs: Mapping[bytes, bytes]) Tx[source]¶
Return tx with the witness of the funding output’s 2-of-2 spend.
sigs maps each of the two funding keys to its DER and SIGHASH_ALL signature, in either order: the witness orders them as the script does. No signature is verified here: a caller verifies the completed transaction with btclib.script.engine.verify_transaction, and Protocol.md rejects the contract when the counterparty’s refund signature fails.
- btclib_wallet.dlc.signing.sig_hash(dlc: Dlc, tx: Tx) bytes[source]¶
Return the BIP143 SIGHASH_ALL hash of the funding input of tx.
tx is a CET of the terms’ payouts or their refund, as built.
- btclib_wallet.dlc.signing.sign(dlc: Dlc, tx: Tx, prv_key: bytes | str | bytearray | memoryview | int) bytes[source]¶
Return the signature of tx’s funding input, DER and SIGHASH_ALL.
- btclib_wallet.dlc.signing.verify_adaptor(dlc: Dlc, tx: Tx, adaptor_sig: bytes | str | bytearray | memoryview, pub_key: bytes | str | bytearray | memoryview, sig_point: tuple[int, int]) bool[source]¶
Return whether adaptor_sig decrypts to pub_key’s signature of tx.
The adaptor signature is encrypted under sig_point. A transaction the terms do not agree on answers False: it is a wrong value, not a malformed argument.
btclib_wallet.dlc.transactions module¶
The transactions of a DLC: funding, contract execution and refund.
https://github.com/discreetlogcontracts/dlcspecs/blob/master/Transactions.md
The funding transaction pays both parties’ collateral, and the fee of the transaction that will later spend it, to a 2-of-2 P2WSH. A contract execution transaction (CET) per outcome, and the refund transaction, each spend that output to the parties’ payout scripts. Two parties who build these from the same terms get the same bytes, which is what lets each sign the other’s.
Inputs are sorted by serial id. So are the outputs of the funding transaction (the funding output and the two change outputs) and those of a CET or of the refund (the two payouts). A payout under 1000 satoshis is not produced.
Fees follow the specification’s expected weights, a rate in satoshis per virtual byte. The funding transaction’s fee is taken from each party’s change, and so is the fee of the CET that will spend the funding output, which is added to the funding output: a CET pays its outcome’s payouts in full and carries no fee of its own to subtract.
The unsigned transactions carry the funding witness script as the only witness item of the input of a CET or of the refund, which is how the specification’s vectors write them; btclib_wallet.dlc.signing completes the stack.
The funding output’s index is where the serial ids put it. The specification’s text says 0, but it sorts the outputs by serial id, and bitcoin-s follows the sorting. The dlcspecs vectors put the funding output first.
A change under the dust limit is refused. The specification asks the party to add funding until it is not, and bitcoin-s drops the output instead, which gives the value to the miners. No vector has such a change.
A collateral under the dust limit is accepted. Protocol.md has the sender of an offer set offer_collateral_satoshis to at least 1000, and lets the receiver reject an offer under it. Dlc holds terms that both parties have agreed, and the receiver builds them too, so a rule that binds the sender is not enforced; terms that cannot be built are refused by the dust checks.
The refund opens after the CETs, in the same unit. Protocol.md requires the CET lock time to be less than the refund’s, and both to be heights or both timestamps. A refund that opens no later lets the loser take the collateral back before the oracle attests, and a height cannot be compared with a timestamp.
- class btclib_wallet.dlc.transactions.Dlc(*, offer: DlcParty, accept: DlcParty, fund_output_serial_id: int, fee_rate: int, offer_payouts: tuple[int, ...], cet_locktime: int, refund_locktime: int)[source]¶
Bases:
objectThe terms two parties agree on, from which every transaction follows.
offer_payouts[i] is what the offerer gets in the CET of outcome i; the accepter gets the rest of the two collaterals.
- class btclib_wallet.dlc.transactions.DlcParty(*, funding_pub_key: bytes, payout_spk: bytes, payout_serial_id: int, change_spk: bytes, change_serial_id: int, collateral: int, inputs: tuple[FundingInput, ...])[source]¶
Bases:
objectWhat one party of a DLC puts in and takes out.
funding_pub_key is the compressed key of the party’s half of the funding output. The party pays collateral and the fees of its own part from inputs, and gets the rest back at change_spk.
- class btclib_wallet.dlc.transactions.DlcTransactions(*, funding_tx: Tx, funding_vout: int, cets: tuple[Tx, ...], refund_tx: Tx)[source]¶
Bases:
objectThe unsigned transactions of a DLC, and where the funding output is.
- class btclib_wallet.dlc.transactions.FundingInput(*, serial_id: int, prev_tx: Tx, prev_vout: int, max_witness_len: int, redeem_script: bytes = b'', sequence: int = 4294967295)[source]¶
Bases:
objectAn output of prev_tx that a party spends into the funding transaction.
It is native SegWit, or P2SH wrapping a SegWit v0 script whose redeem_script is then given. max_witness_len is the size of the serialized witness the spend will carry, its item count included, as the specification’s example counts it.
- btclib_wallet.dlc.transactions.build_cet(dlc: Dlc, funding_tx: Tx, funding_vout: int, offer_payout: int) Tx[source]¶
Return the unsigned CET that pays the offerer offer_payout.
- btclib_wallet.dlc.transactions.build_funding_tx(dlc: Dlc) tuple[Tx, int][source]¶
Return the unsigned funding transaction, and its funding output’s index.
A party whose change is under the dust limit is refused: the specification asks it to add funding.
- btclib_wallet.dlc.transactions.build_refund_tx(dlc: Dlc, funding_tx: Tx, funding_vout: int) Tx[source]¶
Return the unsigned refund: each party gets its collateral.
- btclib_wallet.dlc.transactions.build_transactions(dlc: Dlc) DlcTransactions[source]¶
Return the unsigned funding transaction, every CET and the refund.
- btclib_wallet.dlc.transactions.closing_fee(party: DlcParty, fee_rate: int) int[source]¶
Return the fee of the party’s share of a CET or of the refund.
The share is half of the shared fields and the party’s payout output: 249 weight units, plus 4 per byte of the payout script, the specification’s.
- btclib_wallet.dlc.transactions.funding_fee(party: DlcParty, fee_rate: int) int[source]¶
Return the fee of the party’s share of the funding transaction.
The share is the party’s inputs, its change output and half of the shared fields: 107 weight units, the specification’s.
Module contents¶
Discreet log contracts: transactions, signatures and oracle data.
https://github.com/discreetlogcontracts/dlcspecs
oracle, transactions and signing are the modules: each says what it holds.
The flat surface is this package’s: the names are read off btclib_wallet.dlc, the modules being where they are written.
- class btclib_wallet.dlc.DigitDecompositionEventDescriptor(*, base: int, is_signed: bool, unit: str, precision: int, nb_digits: int)[source]¶
Bases:
objectAn event with a number, signed digit by digit.
- class btclib_wallet.dlc.Dlc(*, offer: DlcParty, accept: DlcParty, fund_output_serial_id: int, fee_rate: int, offer_payouts: tuple[int, ...], cet_locktime: int, refund_locktime: int)[source]¶
Bases:
objectThe terms two parties agree on, from which every transaction follows.
offer_payouts[i] is what the offerer gets in the CET of outcome i; the accepter gets the rest of the two collaterals.
- class btclib_wallet.dlc.DlcParty(*, funding_pub_key: bytes, payout_spk: bytes, payout_serial_id: int, change_spk: bytes, change_serial_id: int, collateral: int, inputs: tuple[FundingInput, ...])[source]¶
Bases:
objectWhat one party of a DLC puts in and takes out.
funding_pub_key is the compressed key of the party’s half of the funding output. The party pays collateral and the fees of its own part from inputs, and gets the rest back at change_spk.
- class btclib_wallet.dlc.DlcTransactions(*, funding_tx: Tx, funding_vout: int, cets: tuple[Tx, ...], refund_tx: Tx)[source]¶
Bases:
objectThe unsigned transactions of a DLC, and where the funding output is.
- class btclib_wallet.dlc.EnumEventDescriptor(*, outcomes: tuple[str, ...])[source]¶
Bases:
objectAn event with a list of outcomes, one of which the oracle signs.
- class btclib_wallet.dlc.FundingInput(*, serial_id: int, prev_tx: Tx, prev_vout: int, max_witness_len: int, redeem_script: bytes = b'', sequence: int = 4294967295)[source]¶
Bases:
objectAn output of prev_tx that a party spends into the funding transaction.
It is native SegWit, or P2SH wrapping a SegWit v0 script whose redeem_script is then given. max_witness_len is the size of the serialized witness the spend will carry, its item count included, as the specification’s example counts it.
- class btclib_wallet.dlc.OracleAnnouncement(*, signature: bytes, public_key: bytes, event: OracleEvent)[source]¶
Bases:
objectAn oracle’s event, with its key and the signature of that event.
Whether the signature is valid is verify_announcement’s to say, under the scheme the caller names. Whose key it is, is the caller’s to pin: see the module docstring.
- classmethod parse(data: BytesIO | bytes | str | bytearray | memoryview, *, check_validity: bool = True) OracleAnnouncement[source]¶
Return the announcement of its TLV.
Octets are one announcement and bytes after it are refused. A stream is left on the byte after the announcement.
- class btclib_wallet.dlc.OracleAttestation(*, event_id: str, public_key: bytes, signatures: tuple[bytes, ...], outcomes: tuple[str, ...])[source]¶
Bases:
objectThe signatures with which an oracle attests the outcome of an event.
- classmethod parse(data: BytesIO | bytes | str | bytearray | memoryview, *, check_validity: bool = True) OracleAttestation[source]¶
Return the attestation of its TLV.
Octets are one attestation and bytes after it are refused. A stream is left on the byte after the attestation.
- class btclib_wallet.dlc.OracleEvent(*, nonces: tuple[bytes, ...], maturity_epoch: int, descriptor: EnumEventDescriptor | DigitDecompositionEventDescriptor, event_id: str)[source]¶
Bases:
objectWhat an oracle commits to before an event: its nonces and outcomes.
- classmethod parse(data: BytesIO | bytes | str | bytearray | memoryview, *, check_validity: bool = True) OracleEvent[source]¶
Return the event of its TLV.
Octets are one event and bytes after it are refused. A stream is left on the byte after the event.
- btclib_wallet.dlc.adaptor_sign(dlc: Dlc, tx: Tx, prv_key: bytes | str | bytearray | memoryview | int, sig_point: tuple[int, int], aux: bytes | str | bytearray | memoryview | None = None) bytes[source]¶
Return the adaptor signature of tx under the outcome’s signature point.
aux is 32 octets of randomness, fresh when absent. Fixing it makes the signature repeatable: do it only to check a vector.
- btclib_wallet.dlc.build_cet(dlc: Dlc, funding_tx: Tx, funding_vout: int, offer_payout: int) Tx[source]¶
Return the unsigned CET that pays the offerer offer_payout.
- btclib_wallet.dlc.build_funding_tx(dlc: Dlc) tuple[Tx, int][source]¶
Return the unsigned funding transaction, and its funding output’s index.
A party whose change is under the dust limit is refused: the specification asks it to add funding.
- btclib_wallet.dlc.build_refund_tx(dlc: Dlc, funding_tx: Tx, funding_vout: int) Tx[source]¶
Return the unsigned refund: each party gets its collateral.
- btclib_wallet.dlc.build_transactions(dlc: Dlc) DlcTransactions[source]¶
Return the unsigned funding transaction, every CET and the refund.
- btclib_wallet.dlc.closing_fee(party: DlcParty, fee_rate: int) int[source]¶
Return the fee of the party’s share of a CET or of the refund.
The share is half of the shared fields and the party’s payout output: 249 weight units, plus 4 per byte of the payout script, the specification’s.
- btclib_wallet.dlc.complete_cet(cet: Tx, *, local_pub_key: bytes, local_sig: bytes, remote_pub_key: bytes, remote_adaptor_sig: bytes | str | bytearray | memoryview, decryption_key: bytes | str | bytearray | memoryview | int) Tx[source]¶
Return the CET with both signatures placed in its witness.
The remote signature is the remote adaptor signature decrypted with the sum of the oracle signatures’ scalars: the decryption key of the outcome’s signature point. Neither signature is verified here: call verify_adaptor on the remote adaptor signature first.
- btclib_wallet.dlc.complete_funding_tx(funding_tx: Tx, witnesses: Mapping[OutPoint, Witness]) Tx[source]¶
Return the funding transaction with each input’s witness in place.
- btclib_wallet.dlc.complete_spend(tx: Tx, sigs: Mapping[bytes, bytes]) Tx[source]¶
Return tx with the witness of the funding output’s 2-of-2 spend.
sigs maps each of the two funding keys to its DER and SIGHASH_ALL signature, in either order: the witness orders them as the script does. No signature is verified here: a caller verifies the completed transaction with btclib.script.engine.verify_transaction, and Protocol.md rejects the contract when the counterparty’s refund signature fails.
- btclib_wallet.dlc.funding_fee(party: DlcParty, fee_rate: int) int[source]¶
Return the fee of the party’s share of the funding transaction.
The share is the party’s inputs, its change output and half of the shared fields: 107 weight units, the specification’s.
- btclib_wallet.dlc.funding_script(offer_key: bytes, accept_key: bytes) bytes[source]¶
Return the 2-of-2 witness script of the funding output.
The keys are in lexicographic order, as BIP67 asks.
- btclib_wallet.dlc.funding_script_pub_key(offer_key: bytes, accept_key: bytes) bytes[source]¶
Return the P2WSH script_pub_key of the funding output.
- btclib_wallet.dlc.outcome_message(outcome: str) bytes[source]¶
Return the 32 octets the oracle signs to attest an outcome.
- btclib_wallet.dlc.sig_hash(dlc: Dlc, tx: Tx) bytes[source]¶
Return the BIP143 SIGHASH_ALL hash of the funding input of tx.
tx is a CET of the terms’ payouts or their refund, as built.
- btclib_wallet.dlc.sign(dlc: Dlc, tx: Tx, prv_key: bytes | str | bytearray | memoryview | int) bytes[source]¶
Return the signature of tx’s funding input, DER and SIGHASH_ALL.
- btclib_wallet.dlc.sign_announcement(event: OracleEvent, prv_key: bytes | str | bytearray | memoryview | int, *, scheme: Literal['tagged', 'sha256'] = 'tagged') OracleAnnouncement[source]¶
Return the announcement of event, signed by the oracle’s key.
- btclib_wallet.dlc.sign_attestation(announcement: OracleAnnouncement, outcomes: tuple[str, ...], prv_key: bytes | str | bytearray | memoryview | int, nonces: tuple[bytes | str | bytearray | memoryview | int, ...]) OracleAttestation[source]¶
Return the attestation of outcomes, signed with the committed nonces.
nonces are the secret keys of the announcement’s nonces, in their order.
- btclib_wallet.dlc.verify_adaptor(dlc: Dlc, tx: Tx, adaptor_sig: bytes | str | bytearray | memoryview, pub_key: bytes | str | bytearray | memoryview, sig_point: tuple[int, int]) bool[source]¶
Return whether adaptor_sig decrypts to pub_key’s signature of tx.
The adaptor signature is encrypted under sig_point. A transaction the terms do not agree on answers False: it is a wrong value, not a malformed argument.
- btclib_wallet.dlc.verify_announcement(announcement: OracleAnnouncement, *, scheme: Literal['tagged', 'sha256'] = 'tagged') bool[source]¶
Return whether the announcement’s signature is valid under the scheme.
It is checked against announcement.public_key, which the caller pins as the module docstring says.
- btclib_wallet.dlc.verify_attestation(announcement: OracleAnnouncement, attestation: OracleAttestation) bool[source]¶
Return whether attestation attests an outcome of the announced event.
It must come from the announced oracle and event, sign with the committed nonces in their order, name outcomes the event allows, and carry a valid signature of each. The announcement is taken as given: the caller has had verify_announcement accept it and has pinned its key, as the module docstring says.