Skip to main content

ddk_dlc/
dlc_input.rs

1//! Module for working with DLC inputs
2
3use bitcoin::{Amount, EcdsaSighashType, OutPoint, ScriptBuf, Transaction, Witness};
4use secp256k1_zkp::{ecdsa::Signature, PublicKey, Secp256k1, SecretKey, Signing, Verification};
5
6use crate::{util::finalize_sig, Error, TxInputInfo};
7
8#[derive(Clone, Debug, PartialEq, Eq)]
9#[cfg_attr(feature = "use-serde", derive(serde::Serialize, serde::Deserialize))]
10/// Contains information about a DLC input to be used in a funding transaction.
11pub struct DlcInputInfo {
12    /// The transaction of the funding transaction.
13    pub fund_tx: Transaction,
14    /// The output index of the funding transaction.
15    pub fund_vout: u32,
16    /// The local funding public key.
17    pub local_fund_pubkey: PublicKey,
18    /// The remote funding public key.
19    pub remote_fund_pubkey: PublicKey,
20    /// The amount of the funding transaction.
21    pub fund_amount: Amount,
22    /// The maximum witness length of the funding transaction.
23    pub max_witness_len: usize,
24    /// The serial id of the funding transaction.
25    pub input_serial_id: u64,
26    /// The contract id referenced
27    pub contract_id: [u8; 32],
28}
29
30impl From<&DlcInputInfo> for TxInputInfo {
31    fn from(val: &DlcInputInfo) -> Self {
32        TxInputInfo {
33            outpoint: OutPoint::new(val.fund_tx.compute_txid(), val.fund_vout),
34            max_witness_len: val.max_witness_len,
35            redeem_script: ScriptBuf::new(),
36            serial_id: val.input_serial_id,
37        }
38    }
39}
40
41/// Calculate weight of DLC inputs for fee estimation
42pub fn get_dlc_inputs_weight(dlc_inputs: &[DlcInputInfo]) -> usize {
43    dlc_inputs
44        .iter()
45        .map(|dlc_input| {
46            // P2WSH 2-of-2 multisig weight
47            36 * 4 + 4 + 4 * 4 + dlc_input.max_witness_len
48        })
49        .sum()
50}
51
52/// Calculate total amount from DLC inputs
53pub fn calculate_total_dlc_input_amount(dlc_inputs: &[DlcInputInfo]) -> Amount {
54    dlc_inputs.iter().map(|input| input.fund_amount).sum()
55}
56
57/// Create the funding script for a DLC input
58pub fn create_dlc_input_funding_script(dlc_input: &DlcInputInfo) -> ScriptBuf {
59    crate::make_funding_redeemscript(&dlc_input.local_fund_pubkey, &dlc_input.remote_fund_pubkey)
60}
61
62/// Create a signature for a DLC funding input
63pub fn create_dlc_funding_input_signature<C: Signing>(
64    secp: &Secp256k1<C>,
65    fund_transaction: &Transaction,
66    input_index: usize,
67    dlc_input: &DlcInputInfo,
68    privkey: &SecretKey,
69) -> Result<Vec<u8>, Error> {
70    let funding_script = create_dlc_input_funding_script(dlc_input);
71    let sig_hash_msg = super::util::get_sig_hash_msg(
72        fund_transaction,
73        input_index,
74        &funding_script,
75        dlc_input.fund_amount,
76    )?;
77    let signature = secp.sign_ecdsa_low_r(&sig_hash_msg, privkey);
78    Ok(finalize_sig(&signature, EcdsaSighashType::All))
79}
80
81/// Verify a DLC funding input signature
82pub fn verify_dlc_funding_input_signature<V: Verification>(
83    secp: &Secp256k1<V>,
84    fund_transaction: &Transaction,
85    input_index: usize,
86    dlc_input: &DlcInputInfo,
87    signature: Vec<u8>,
88    pubkey: &PublicKey,
89) -> Result<(), Error> {
90    let funding_script = create_dlc_input_funding_script(dlc_input);
91
92    // Parse DER signature instead of compact
93    let signature = if signature.len() == 64 {
94        Signature::from_compact(&signature)?
95    } else {
96        // Remove sighash type byte and parse DER
97        let sig_bytes = &signature[..signature.len() - 1];
98        Signature::from_der(sig_bytes)?
99    };
100
101    super::verify_tx_input_sig(
102        secp,
103        &signature,
104        fund_transaction,
105        input_index,
106        &funding_script,
107        dlc_input.fund_amount,
108        pubkey,
109    )
110}
111
112/// Combine both parties' signatures for a DLC input
113pub fn combine_dlc_input_signatures(
114    dlc_input: &DlcInputInfo,
115    my_signature: &Vec<u8>,
116    other_signature: &Vec<u8>,
117    my_pubkey: &PublicKey,
118    other_pubkey: &PublicKey,
119) -> Witness {
120    let funding_script = create_dlc_input_funding_script(dlc_input);
121
122    // Order signatures based on pubkey order
123    let (first_sig, second_sig) = if my_pubkey <= other_pubkey {
124        (my_signature, other_signature)
125    } else {
126        (other_signature, my_signature)
127    };
128
129    let mut witness = Witness::new();
130    witness.push([]);
131    witness.push(first_sig);
132    witness.push(second_sig);
133    witness.push(funding_script.to_bytes());
134
135    witness
136}