lightning_signer/util/
transaction_utils.rs

1use crate::prelude::*;
2use crate::tx::script::ANCHOR_OUTPUT_VALUE_SATOSHI;
3use anyhow::anyhow;
4use bitcoin::consensus::Encodable;
5use bitcoin::io::sink;
6use bitcoin::secp256k1::ecdsa::Signature;
7use bitcoin::secp256k1::{All, PublicKey, Secp256k1};
8use bitcoin::sighash::EcdsaSighashType;
9use bitcoin::transaction::Version;
10use bitcoin::{Amount, CompressedPublicKey, ScriptBuf, Sequence, TxIn, Witness, WitnessProgram};
11use bitcoin::{Transaction, TxOut, VarInt};
12use lightning::ln::chan_utils::{
13    get_commitment_transaction_number_obscure_factor, get_revokeable_redeemscript,
14    get_to_countersignatory_with_anchors_redeemscript, make_funding_redeemscript,
15    ChannelTransactionParameters, TxCreationKeys,
16};
17use lightning::sign::{
18    DelayedPaymentOutputDescriptor, SpendableOutputDescriptor, StaticPaymentOutputDescriptor,
19};
20
21/// The maximum value of an input or output in milli satoshi
22pub const MAX_VALUE_MSAT: u64 = 21_000_000_0000_0000_000;
23
24/// The minimum value of the dust limit in satoshis - for p2wsh outputs
25/// (such as anchors)
26pub const MIN_DUST_LIMIT_SATOSHIS: u64 = 330;
27/// The minimum value of the dust limit in satoshis - for segwit in general
28/// This is also the minimum negotiated dust limit
29pub const MIN_CHAN_DUST_LIMIT_SATOSHIS: u64 = 354;
30
31/// The expected weight of a commitment transaction
32pub(crate) fn expected_commitment_tx_weight(opt_anchors: bool, num_untrimmed_htlc: usize) -> usize {
33    const COMMITMENT_TX_BASE_WEIGHT: usize = 724;
34    const COMMITMENT_TX_BASE_ANCHOR_WEIGHT: usize = 1124;
35    const COMMITMENT_TX_WEIGHT_PER_HTLC: usize = 172;
36    let base_weight =
37        if opt_anchors { COMMITMENT_TX_BASE_ANCHOR_WEIGHT } else { COMMITMENT_TX_BASE_WEIGHT };
38    base_weight + num_untrimmed_htlc * COMMITMENT_TX_WEIGHT_PER_HTLC
39}
40
41/// The weight of a mutual close transaction.
42pub(crate) fn mutual_close_tx_weight(unsigned_tx: &Transaction) -> usize {
43    // NOTE related to issue 165 - we use 72 here because we might as well assume low-S
44    // for the signature, and some node implementations use that.
45    // However, nodes may use 73 to be consistent with BOLT-3.
46    // That's OK because we will be more lenient on the fee.
47    const EXPECTED_MUTUAL_CLOSE_WITNESS_WEIGHT: usize = //
48        2 + 1 + 4 + // witness-marker-and-flag witness-element-count 4-element-lengths
49        72 + 72 + // <signature_for_pubkey1> <signature_for_pubkey2>
50        1 + 1 + 33 + 1 + 33 + 1 + 1; // 2 <pubkey1> <pubkey2> 2 OP_CHECKMULTISIG
51    unsigned_tx.weight().to_wu() as usize + EXPECTED_MUTUAL_CLOSE_WITNESS_WEIGHT
52}
53
54/// Possibly adds a change output to the given transaction, always doing so if there are excess
55/// funds available beyond the requested feerate.
56/// Assumes at least one input will have a witness (ie spends a segwit output).
57/// Returns an Err(()) if the requested feerate cannot be met.
58pub fn maybe_add_change_output(
59    tx: &mut Transaction,
60    input_value: u64,
61    witness_max_weight: u64,
62    feerate_sat_per_1000_weight: u32,
63    change_destination_script: ScriptBuf,
64) -> Result<(), ()> {
65    if input_value > MAX_VALUE_MSAT / 1000 {
66        //bail!("Input value is greater than max satoshis");
67        return Err(());
68    }
69
70    let mut output_value = Amount::ZERO;
71    for output in tx.output.iter() {
72        output_value = output_value.checked_add(output.value).unwrap();
73        if output_value.to_sat() >= input_value {
74            // bail!("Ouput value equals or exceeds input value");
75            return Err(());
76        }
77    }
78
79    let dust_value = change_destination_script.minimal_non_dust();
80    let mut change_output = TxOut { script_pubkey: change_destination_script, value: Amount::ZERO };
81    let change_len = change_output.consensus_encode(&mut sink()).map_err(|_| ())?;
82    let mut weight_with_change: i64 =
83        tx.weight().to_wu() as i64 + 2 + witness_max_weight as i64 + change_len as i64 * 4;
84    // Include any extra bytes required to push an extra output.
85    weight_with_change += (VarInt(tx.output.len() as u64 + 1).size()
86        - VarInt(tx.output.len() as u64).size()) as i64
87        * 4;
88    // When calculating weight, add two for the flag bytes
89    let difference = input_value.checked_sub(output_value.to_sat()).ok_or(())?;
90    let change_value: i64 =
91        difference as i64 - weight_with_change * feerate_sat_per_1000_weight as i64 / 1000;
92    if change_value >= dust_value.to_sat() as i64 {
93        change_output.value = Amount::from_sat(change_value as u64);
94        tx.output.push(change_output);
95    } else if difference as i64
96        - (tx.weight().to_wu() as i64 + 2 + witness_max_weight as i64)
97            * feerate_sat_per_1000_weight as i64
98            / 1000
99        < 0
100    {
101        // bail!("Requested fee rate cannot be met");
102        return Err(());
103    }
104
105    Ok(())
106}
107
108/// Estimate the feerate for an HTLC transaction
109pub(crate) fn estimate_feerate_per_kw(total_fee: u64, weight: u64) -> u32 {
110    // we want the highest feerate that can give rise to this total fee
111    (((total_fee * 1000) + 999) / weight) as u32
112}
113
114pub(crate) fn add_holder_sig(
115    tx: &mut Transaction,
116    holder_sig: Signature,
117    counterparty_sig: Signature,
118    holder_funding_key: &PublicKey,
119    counterparty_funding_key: &PublicKey,
120) {
121    let funding_redeemscript =
122        make_funding_redeemscript(&holder_funding_key, &counterparty_funding_key);
123
124    tx.input[0].witness.push(Vec::new());
125    let mut ser_holder_sig = holder_sig.serialize_der().to_vec();
126    ser_holder_sig.push(EcdsaSighashType::All as u8);
127    let mut ser_cp_sig = counterparty_sig.serialize_der().to_vec();
128    ser_cp_sig.push(EcdsaSighashType::All as u8);
129
130    let holder_sig_first =
131        holder_funding_key.serialize()[..] < counterparty_funding_key.serialize()[..];
132
133    if holder_sig_first {
134        tx.input[0].witness.push(ser_holder_sig);
135        tx.input[0].witness.push(ser_cp_sig);
136    } else {
137        tx.input[0].witness.push(ser_cp_sig);
138        tx.input[0].witness.push(ser_holder_sig);
139    }
140
141    tx.input[0].witness.push(funding_redeemscript.as_bytes().to_vec());
142}
143
144pub(crate) fn is_tx_non_malleable(tx: &Transaction, segwit_flags: &[bool]) -> bool {
145    assert_eq!(tx.input.len(), segwit_flags.len(), "tx and segwit_flags must have same length");
146    segwit_flags.iter().all(|flag| *flag)
147}
148
149/// Decode a commitment transaction and return the outputs that we need to watch.
150/// Our main output index and any HTLC output indexes are returned.
151///
152/// `cp_per_commitment_point` is filled in if known, otherwise None.  It might
153/// not be known if the signer is old, before we started collecting counterparty secrets.
154/// If it is None, then we won't be able to tell the difference between a counterparty
155/// to-self output and an HTLC output.
156///
157/// The counterparty parameters must be populated.
158pub fn decode_commitment_tx(
159    tx: &Transaction,
160    holder_per_commitment_point: &PublicKey,
161    cp_per_commitment_point: &Option<PublicKey>,
162    params: &ChannelTransactionParameters,
163    secp_ctx: &Secp256k1<All>,
164) -> (Option<u32>, Vec<u32>) {
165    let cp_params = params.counterparty_parameters.as_ref().unwrap();
166
167    let opt_anchors = params.channel_type_features.supports_anchors_nonzero_fee_htlc_tx()
168        || params.channel_type_features.supports_anchors_zero_fee_htlc_tx();
169    let holder_pubkeys = &params.holder_pubkeys;
170    let cp_pubkeys = &cp_params.pubkeys;
171
172    let holder_non_delayed_script = if opt_anchors {
173        get_to_countersignatory_with_anchors_redeemscript(&holder_pubkeys.payment_point).to_p2wsh()
174    } else {
175        let bitcoin_key =
176            CompressedPublicKey::from_slice(&holder_pubkeys.payment_point.serialize()).unwrap();
177        let prog = WitnessProgram::p2wpkh(&bitcoin_key);
178        ScriptBuf::new_witness_program(&prog)
179    };
180
181    // compute the transaction keys we would have used if this is a holder commitment
182    let holder_tx_keys = TxCreationKeys::derive_new(
183        secp_ctx,
184        &holder_per_commitment_point,
185        &holder_pubkeys.delayed_payment_basepoint,
186        &holder_pubkeys.htlc_basepoint,
187        &cp_pubkeys.revocation_basepoint,
188        &cp_pubkeys.htlc_basepoint,
189    );
190
191    let holder_delayed_redeem_script = get_revokeable_redeemscript(
192        &holder_tx_keys.revocation_key,
193        cp_params.selected_contest_delay,
194        &holder_tx_keys.broadcaster_delayed_payment_key,
195    );
196
197    let holder_delayed_script = holder_delayed_redeem_script.to_p2wsh();
198
199    let cp_delayed_script = if let Some(cp_per_commitment_point) = cp_per_commitment_point {
200        // compute the transaction keys we would have used if this is a holder commitment
201        let cp_tx_keys = TxCreationKeys::derive_new(
202            secp_ctx,
203            &cp_per_commitment_point,
204            &cp_pubkeys.delayed_payment_basepoint,
205            &cp_pubkeys.htlc_basepoint,
206            &holder_pubkeys.revocation_basepoint,
207            &holder_pubkeys.htlc_basepoint,
208        );
209
210        let cp_delayed_redeem_script = get_revokeable_redeemscript(
211            &cp_tx_keys.revocation_key,
212            params.holder_selected_contest_delay,
213            &cp_tx_keys.broadcaster_delayed_payment_key,
214        );
215
216        Some(cp_delayed_redeem_script.to_p2wsh())
217    } else {
218        None
219    };
220
221    let mut htlcs = Vec::new();
222    let mut main_output_index = None;
223
224    // find the output that pays to us, if any
225    for (idx, output) in tx.output.iter().enumerate() {
226        // we don't track anchors
227        if output.value == ANCHOR_OUTPUT_VALUE_SATOSHI {
228            continue;
229        }
230
231        if Some(&output.script_pubkey) == cp_delayed_script.as_ref() {
232            continue;
233        }
234
235        // look for our main output, either when broadcast by us or by our counterparty
236        if output.script_pubkey == holder_non_delayed_script
237            || output.script_pubkey == holder_delayed_script
238        {
239            main_output_index = Some(idx as u32);
240        } else if output.script_pubkey.is_p2wsh() {
241            htlcs.push(idx as u32);
242        }
243    }
244
245    (main_output_index, htlcs)
246}
247
248/// Decode a commitment transaction and return the commitment number if it is a commitment tx
249pub fn decode_commitment_number(
250    tx: &Transaction,
251    params: &ChannelTransactionParameters,
252) -> Option<u64> {
253    let holder_pubkeys = &params.holder_pubkeys;
254    let cp_params = params.counterparty_parameters.as_ref().unwrap();
255    let cp_pubkeys = &cp_params.pubkeys;
256
257    let obscure_factor = get_commitment_transaction_number_obscure_factor(
258        &holder_pubkeys.payment_point,
259        &cp_pubkeys.payment_point,
260        params.is_outbound_from_holder,
261    );
262
263    // if the tx has more than one input, it's not a standard closing tx,
264    // so we bail
265    if tx.input.len() != 1 {
266        return None;
267    }
268
269    // check if the input sequence and locktime are set to standard commitment tx values
270    if (tx.input[0].sequence.0 >> 8 * 3) as u8 != 0x80
271        || (tx.lock_time.to_consensus_u32() >> 8 * 3) as u8 != 0x20
272    {
273        return None;
274    }
275
276    // forward counting
277    let commitment_number = (((tx.input[0].sequence.0 as u64 & 0xffffff) << 3 * 8)
278        | (tx.lock_time.to_consensus_u32() as u64 & 0xffffff))
279        ^ obscure_factor;
280    Some(commitment_number)
281}
282
283/// Create a spending transaction, helper function used in [`KeysManagerClient::spend_spendable_outputs`].
284///
285/// [`KeysManagerClient::spend_spendable_outputsspend_spendable_outputs`] vls_protocol_client::KeysManagerClient::spend_spendable_outputsspend_spendable_outputs
286pub fn create_spending_transaction(
287    descriptors: &[&SpendableOutputDescriptor],
288    outputs: Vec<TxOut>,
289    change_destination_script: ScriptBuf,
290    feerate_sats_per_1000_weight: u32,
291) -> anyhow::Result<Transaction> {
292    let mut input = Vec::new();
293    let mut input_value = Amount::ZERO;
294    let mut witness_weight = 0;
295    let mut output_set = UnorderedSet::with_capacity(descriptors.len());
296    for outp in descriptors {
297        match outp {
298            SpendableOutputDescriptor::StaticPaymentOutput(descriptor) => {
299                input.push(TxIn {
300                    previous_output: descriptor.outpoint.into_bitcoin_outpoint(),
301                    script_sig: ScriptBuf::new(),
302                    sequence: Sequence(1),
303                    witness: Witness::new(),
304                });
305                witness_weight += StaticPaymentOutputDescriptor::max_witness_length(descriptor);
306                input_value = input_value.checked_add(descriptor.output.value).unwrap();
307                if !output_set.insert(descriptor.outpoint) {
308                    return Err(anyhow!("duplicate"));
309                }
310            }
311            SpendableOutputDescriptor::DelayedPaymentOutput(descriptor) => {
312                input.push(TxIn {
313                    previous_output: descriptor.outpoint.into_bitcoin_outpoint(),
314                    script_sig: ScriptBuf::new(),
315                    sequence: Sequence(descriptor.to_self_delay as u32),
316                    witness: Witness::new(),
317                });
318                witness_weight += DelayedPaymentOutputDescriptor::MAX_WITNESS_LENGTH;
319                input_value = input_value.checked_add(descriptor.output.value).unwrap();
320                if !output_set.insert(descriptor.outpoint) {
321                    return Err(anyhow!("duplicate"));
322                }
323            }
324
325            SpendableOutputDescriptor::StaticOutput {
326                ref outpoint,
327                ref output,
328                channel_keys_id: _,
329            } => {
330                input.push(TxIn {
331                    previous_output: outpoint.into_bitcoin_outpoint(),
332                    script_sig: ScriptBuf::new(),
333                    sequence: Sequence::ZERO,
334                    witness: Witness::default(),
335                });
336                witness_weight += 1 + 73 + 34;
337                input_value = input_value.checked_add(output.value).unwrap();
338                if !output_set.insert(*outpoint) {
339                    return Err(anyhow!("duplicate"));
340                }
341            }
342        }
343
344        if input_value.to_sat() > MAX_VALUE_MSAT / 1000 {
345            return Err(anyhow!("overflow"));
346        }
347    }
348
349    let mut spend_tx = Transaction {
350        version: Version::TWO,
351        lock_time: bitcoin::absolute::LockTime::ZERO,
352        input,
353        output: outputs,
354    };
355    maybe_add_change_output(
356        &mut spend_tx,
357        input_value.to_sat(),
358        witness_weight,
359        feerate_sats_per_1000_weight,
360        change_destination_script,
361    )
362    .map_err(|()| anyhow!("could not add or change"))?;
363    Ok(spend_tx)
364}
365
366#[cfg(test)]
367mod tests {
368    use super::*;
369    use crate::channel::ChannelBase;
370    use crate::util::test_utils::{
371        init_node_and_channel, make_test_channel_setup, TEST_NODE_CONFIG, TEST_SEED,
372    };
373    use bitcoin::consensus::deserialize;
374    use bitcoin::hashes::hex::FromHex;
375    use bitcoin::secp256k1::SecretKey;
376    use lightning::ln::chan_utils::{htlc_success_tx_weight, htlc_timeout_tx_weight};
377    use lightning::types::features::ChannelTypeFeatures;
378
379    use bitcoin::blockdata::constants::genesis_block;
380    use bitcoin::blockdata::transaction::TxIn;
381    use bitcoin::hashes::Hash;
382    use bitcoin::secp256k1::Secp256k1;
383    use bitcoin::Network;
384    use bitcoin::{Amount, ScriptBuf, Transaction, TxOut, Witness};
385    use lightning::chain::transaction::OutPoint as LightningOutPoint;
386    use lightning::sign::{SpendableOutputDescriptor, StaticPaymentOutputDescriptor};
387
388    #[test]
389    fn test_parse_closing_tx_holder() {
390        let secp_ctx = Secp256k1::new();
391        let commitment_number = 0;
392        let (node, channel_id) =
393            init_node_and_channel(TEST_NODE_CONFIG, TEST_SEED[0], make_test_channel_setup());
394        let params = node
395            .with_channel(&channel_id, |channel| Ok(channel.make_channel_parameters()))
396            .unwrap();
397
398        let (holder_commitment, per_commitment_point) = node
399            .with_channel(&channel_id, |channel| {
400                let per_commitment_point =
401                    channel.get_per_commitment_point(commitment_number).unwrap();
402                let keys = channel.make_holder_tx_keys(&per_commitment_point);
403                let per_commitment_point = channel.get_per_commitment_point(commitment_number)?;
404                Ok((
405                    channel.make_holder_commitment_tx(
406                        commitment_number,
407                        &keys,
408                        123,
409                        1000,
410                        100,
411                        Vec::new(),
412                    ),
413                    per_commitment_point,
414                ))
415            })
416            .unwrap();
417        let holder_tx = holder_commitment.trust().built_transaction().transaction.clone();
418        let parsed_commitment_number = decode_commitment_number(&holder_tx, &params).unwrap();
419        assert_eq!(parsed_commitment_number, commitment_number);
420        let (parsed_main, htlcs) =
421            decode_commitment_tx(&holder_tx, &per_commitment_point, &None, &params, &secp_ctx);
422        let our_main =
423            holder_tx.output.iter().position(|txout| txout.script_pubkey.is_p2wsh()).unwrap();
424        assert_eq!(parsed_main, Some(our_main as u32));
425        assert!(htlcs.is_empty());
426    }
427
428    #[test]
429    fn test_parse_closing_tx_counterparty() {
430        let secp_ctx = Secp256k1::new();
431        let commitment_number = 0;
432        let (node, channel_id) =
433            init_node_and_channel(TEST_NODE_CONFIG, TEST_SEED[0], make_test_channel_setup());
434        let params = node
435            .with_channel(&channel_id, |channel| Ok(channel.make_channel_parameters()))
436            .unwrap();
437
438        let cp_per_commitment_secret = SecretKey::from_slice(&[2; 32]).unwrap();
439        let cp_per_commitment_point =
440            PublicKey::from_secret_key(&secp_ctx, &cp_per_commitment_secret);
441        let (cp_commitment, holder_per_commitment_point) = node
442            .with_channel(&channel_id, |channel| {
443                // this is not used in the test because we are parsing a counterparty commitment,
444                // but we need to set it to something different than the counterparty one
445                let holder_per_commitment_point =
446                    channel.get_per_commitment_point(commitment_number)?;
447                Ok((
448                    channel.make_counterparty_commitment_tx(
449                        &cp_per_commitment_point,
450                        commitment_number,
451                        123,
452                        1000,
453                        100,
454                        Vec::new(),
455                    ),
456                    holder_per_commitment_point,
457                ))
458            })
459            .unwrap();
460        let cp_tx = cp_commitment.trust().built_transaction().transaction.clone();
461        let parsed_commit_number = decode_commitment_number(&cp_tx, &params).unwrap();
462        assert_eq!(parsed_commit_number, commitment_number);
463        let (parsed_main, htlcs) = decode_commitment_tx(
464            &cp_tx,
465            &holder_per_commitment_point,
466            &Some(cp_per_commitment_point),
467            &params,
468            &secp_ctx,
469        );
470        let our_main =
471            cp_tx.output.iter().position(|txout| txout.script_pubkey.is_p2wpkh()).unwrap();
472        assert_eq!(parsed_main, Some(our_main as u32));
473        println!("htlcs: {:?}", htlcs);
474        assert!(htlcs.is_empty());
475    }
476
477    #[test]
478    fn test_estimate_feerate() {
479        let non_anchor_features = ChannelTypeFeatures::empty();
480        let mut anchor_features = ChannelTypeFeatures::empty();
481        anchor_features.set_anchors_zero_fee_htlc_tx_optional();
482        let weights = vec![
483            htlc_timeout_tx_weight(&non_anchor_features),
484            htlc_timeout_tx_weight(&anchor_features),
485            htlc_success_tx_weight(&non_anchor_features),
486            htlc_success_tx_weight(&anchor_features),
487        ];
488
489        // make sure the feerate is not lower than 253 at the low end,
490        // so as not to fail policy check
491        let feerate = 253;
492        for weight in &weights {
493            let total_fee = (feerate as u64 * *weight) / 1000;
494            let estimated_feerate = super::estimate_feerate_per_kw(total_fee, *weight);
495            assert!(estimated_feerate >= 253);
496        }
497
498        // make sure that the total tx fee stays the same after estimating the rate and recomputing the fee
499        // so as to recreate an identical transaction
500        for feerate in (300..5000).step_by(10) {
501            for weight in &weights {
502                let total_fee = (feerate as u64 * *weight) / 1000;
503                let estimated_feerate = super::estimate_feerate_per_kw(total_fee, *weight);
504                let recovered_total_fee = (estimated_feerate as u64 * *weight) / 1000;
505                assert_eq!(total_fee, recovered_total_fee);
506            }
507        }
508    }
509
510    #[test]
511    fn test_issue_165() {
512        let tx: Transaction = deserialize(&Vec::from_hex("0200000001b78e0523c17f8ac709eec54654cc849529c05584bfda6e04c92a3b670476f2a20000000000ffffffff017d4417000000000016001476168b09afc66bd3956efb25cd8b83650bda0c5f00000000").unwrap()).unwrap();
513        let tx_weight = tx.weight();
514        let spk = tx.output[0].script_pubkey.len();
515        let weight = super::mutual_close_tx_weight(&tx);
516        let fee = 1524999 - tx.output[0].value.to_sat();
517        let estimated_feerate = super::estimate_feerate_per_kw(fee, weight as u64);
518        let expected_tx_weight = (4 +                                           // version
519            1 +                                           // input count
520            36 +                                          // prevout
521            1 +                                           // script length (0)
522            4 +                                           // sequence
523            1 +                                           // output count
524            4                                             // lock time
525        )*4 +                                         // * 4 for non-witness parts
526            ((8+1) +                            // output values and script length
527                spk as u64) * 4; // scriptpubkey and witness multiplier
528        assert_eq!(expected_tx_weight, tx_weight.to_wu());
529        // CLN was actually missing the pubkey length byte, so the feerate is genuinely too low
530        assert_eq!(estimated_feerate, 252);
531    }
532
533    fn create_mock_transaction(outputs: Vec<TxOut>) -> Transaction {
534        Transaction {
535            version: Version::TWO,
536            lock_time: bitcoin::absolute::LockTime::ZERO,
537            input: vec![TxIn {
538                previous_output: bitcoin::OutPoint::new(
539                    genesis_block(Network::Bitcoin).txdata[0].compute_txid(),
540                    0,
541                ),
542                script_sig: ScriptBuf::new(),
543                sequence: Sequence::ZERO,
544                witness: Witness::new(),
545            }],
546            output: outputs,
547        }
548    }
549
550    fn create_mock_script() -> ScriptBuf {
551        let hash = bitcoin::WPubkeyHash::from_slice(&[
552            1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
553        ])
554        .unwrap();
555        ScriptBuf::new_p2wpkh(&hash)
556    }
557
558    #[test]
559    fn test_maybe_add_change_output_success() {
560        let mut tx = create_mock_transaction(vec![TxOut {
561            value: Amount::from_sat(100_000),
562            script_pubkey: create_mock_script(),
563        }]);
564        let input_value = 150_000;
565        let witness_max_weight = 100;
566        let feerate_sat_per_1000_weight = 1000;
567        let change_destination_script = create_mock_script();
568
569        let result = maybe_add_change_output(
570            &mut tx,
571            input_value,
572            witness_max_weight,
573            feerate_sat_per_1000_weight,
574            change_destination_script.clone(),
575        );
576
577        assert!(result.is_ok());
578        assert_eq!(tx.output.len(), 2);
579        assert_eq!(
580            tx.output[1].script_pubkey.to_string(),
581            "OP_0 OP_PUSHBYTES_20 0102030405060708090a0b0c0d0e0f1011121314"
582        );
583        assert_eq!(tx.output[1].value.to_sat(), 49446);
584    }
585
586    #[test]
587    fn test_maybe_add_change_output_input_too_large() {
588        let mut tx = create_mock_transaction(vec![TxOut {
589            value: Amount::from_sat(100_000),
590            script_pubkey: create_mock_script(),
591        }]);
592        let input_value = MAX_VALUE_MSAT / 1000 + 1;
593        let witness_max_weight = 100;
594        let feerate_sat_per_1000_weight = 1000;
595        let change_destination_script = create_mock_script();
596
597        let result = maybe_add_change_output(
598            &mut tx,
599            input_value,
600            witness_max_weight,
601            feerate_sat_per_1000_weight,
602            change_destination_script,
603        );
604
605        assert!(result.is_err());
606    }
607
608    #[test]
609    fn test_maybe_add_change_output_output_exceeds_input() {
610        let mut tx = create_mock_transaction(vec![TxOut {
611            value: Amount::from_sat(200_000),
612            script_pubkey: create_mock_script(),
613        }]);
614        let input_value = 100_000;
615        let witness_max_weight = 100;
616        let feerate_sat_per_1000_weight = 1000;
617        let change_destination_script = create_mock_script();
618
619        let result = maybe_add_change_output(
620            &mut tx,
621            input_value,
622            witness_max_weight,
623            feerate_sat_per_1000_weight,
624            change_destination_script,
625        );
626
627        assert!(result.is_err());
628    }
629
630    #[test]
631    fn test_maybe_add_change_output_insufficient_for_fee() {
632        let mut tx = create_mock_transaction(vec![TxOut {
633            value: Amount::from_sat(100_000),
634            script_pubkey: create_mock_script(),
635        }]);
636        let input_value = 100_100;
637        let witness_max_weight = 100;
638        let feerate_sat_per_1000_weight = 1000;
639        let change_destination_script = create_mock_script();
640
641        let result = maybe_add_change_output(
642            &mut tx,
643            input_value,
644            witness_max_weight,
645            feerate_sat_per_1000_weight,
646            change_destination_script,
647        );
648
649        assert!(result.is_err());
650    }
651
652    #[test]
653    fn test_create_spending_transaction_static_payment() {
654        let _secp = Secp256k1::new();
655        let bitcoin_outpoint =
656            bitcoin::OutPoint::new(genesis_block(Network::Bitcoin).txdata[0].compute_txid(), 0);
657        let outpoint =
658            LightningOutPoint { txid: bitcoin_outpoint.txid, index: bitcoin_outpoint.vout as u16 };
659        let descriptor =
660            SpendableOutputDescriptor::StaticPaymentOutput(StaticPaymentOutputDescriptor {
661                outpoint,
662                output: TxOut {
663                    value: Amount::from_sat(100_000),
664                    script_pubkey: create_mock_script(),
665                },
666                channel_keys_id: [0u8; 32],
667                channel_value_satoshis: 100_000,
668                channel_transaction_parameters: None,
669            });
670        let outputs =
671            vec![TxOut { value: Amount::from_sat(50_000), script_pubkey: create_mock_script() }];
672        let change_destination_script = create_mock_script();
673        let feerate_sats_per_1000_weight = 1000;
674
675        let result = create_spending_transaction(
676            &[&descriptor],
677            outputs.clone(),
678            change_destination_script.clone(),
679            feerate_sats_per_1000_weight,
680        );
681
682        assert!(result.is_ok());
683        let tx = result.unwrap();
684        assert_eq!(tx.input.len(), 1);
685        assert_eq!(
686            tx.input[0].previous_output.to_string(),
687            "4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b:0"
688        );
689        assert_eq!(tx.input[0].sequence, Sequence(1));
690        assert_eq!(tx.output.len(), 2);
691        assert_eq!(tx.output[0], outputs[0]);
692        assert_eq!(
693            tx.output[1].script_pubkey.to_string(),
694            "OP_0 OP_PUSHBYTES_20 0102030405060708090a0b0c0d0e0f1011121314"
695        );
696    }
697
698    #[test]
699    fn test_create_spending_transaction_duplicate_outpoint() {
700        let _secp = Secp256k1::new();
701        let bitcoin_outpoint =
702            bitcoin::OutPoint::new(genesis_block(Network::Bitcoin).txdata[0].compute_txid(), 0);
703        let outpoint =
704            LightningOutPoint { txid: bitcoin_outpoint.txid, index: bitcoin_outpoint.vout as u16 };
705        let descriptor =
706            SpendableOutputDescriptor::StaticPaymentOutput(StaticPaymentOutputDescriptor {
707                outpoint,
708                output: TxOut {
709                    value: Amount::from_sat(100_000),
710                    script_pubkey: create_mock_script(),
711                },
712                channel_keys_id: [0u8; 32],
713                channel_value_satoshis: 100_000,
714                channel_transaction_parameters: None,
715            });
716        let outputs =
717            vec![TxOut { value: Amount::from_sat(50_000), script_pubkey: create_mock_script() }];
718        let change_destination_script = create_mock_script();
719        let feerate_sats_per_1000_weight = 1000;
720
721        let result = create_spending_transaction(
722            &[&descriptor, &descriptor],
723            outputs,
724            change_destination_script,
725            feerate_sats_per_1000_weight,
726        );
727
728        assert!(result.is_err());
729        assert_eq!(result.unwrap_err().to_string(), "duplicate");
730    }
731
732    #[test]
733    fn test_create_spending_transaction_value_overflow() {
734        let _secp = Secp256k1::new();
735        let bitcoin_outpoint =
736            bitcoin::OutPoint::new(genesis_block(Network::Bitcoin).txdata[0].compute_txid(), 0);
737        let outpoint =
738            LightningOutPoint { txid: bitcoin_outpoint.txid, index: bitcoin_outpoint.vout as u16 };
739        let descriptor =
740            SpendableOutputDescriptor::StaticPaymentOutput(StaticPaymentOutputDescriptor {
741                outpoint,
742                output: TxOut {
743                    value: Amount::from_sat(MAX_VALUE_MSAT / 1000 + 1),
744                    script_pubkey: create_mock_script(),
745                },
746                channel_keys_id: [0u8; 32],
747                channel_value_satoshis: MAX_VALUE_MSAT / 1000 + 1,
748                channel_transaction_parameters: None,
749            });
750        let outputs =
751            vec![TxOut { value: Amount::from_sat(50_000), script_pubkey: create_mock_script() }];
752        let change_destination_script = create_mock_script();
753        let feerate_sats_per_1000_weight = 1000;
754
755        let result = create_spending_transaction(
756            &[&descriptor],
757            outputs,
758            change_destination_script,
759            feerate_sats_per_1000_weight,
760        );
761
762        assert!(result.is_err());
763        assert_eq!(result.unwrap_err().to_string(), "overflow");
764    }
765}