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
21pub const MAX_VALUE_MSAT: u64 = 21_000_000_0000_0000_000;
23
24pub const MIN_DUST_LIMIT_SATOSHIS: u64 = 330;
27pub const MIN_CHAN_DUST_LIMIT_SATOSHIS: u64 = 354;
30
31pub(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
41pub(crate) fn mutual_close_tx_weight(unsigned_tx: &Transaction) -> usize {
43 const EXPECTED_MUTUAL_CLOSE_WITNESS_WEIGHT: usize = 2 + 1 + 4 + 72 + 72 + 1 + 1 + 33 + 1 + 33 + 1 + 1; unsigned_tx.weight().to_wu() as usize + EXPECTED_MUTUAL_CLOSE_WITNESS_WEIGHT
52}
53
54pub 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 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 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 weight_with_change += (VarInt(tx.output.len() as u64 + 1).size()
86 - VarInt(tx.output.len() as u64).size()) as i64
87 * 4;
88 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 return Err(());
103 }
104
105 Ok(())
106}
107
108pub(crate) fn estimate_feerate_per_kw(total_fee: u64, weight: u64) -> u32 {
110 (((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
149pub 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 = ¶ms.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 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 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 for (idx, output) in tx.output.iter().enumerate() {
226 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 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
248pub fn decode_commitment_number(
250 tx: &Transaction,
251 params: &ChannelTransactionParameters,
252) -> Option<u64> {
253 let holder_pubkeys = ¶ms.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 tx.input.len() != 1 {
266 return None;
267 }
268
269 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 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
283pub 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, ¶ms).unwrap();
419 assert_eq!(parsed_commitment_number, commitment_number);
420 let (parsed_main, htlcs) =
421 decode_commitment_tx(&holder_tx, &per_commitment_point, &None, ¶ms, &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 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, ¶ms).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 ¶ms,
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 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 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 + 1 + 36 + 1 + 4 + 1 + 4 )*4 + ((8+1) + spk as u64) * 4; assert_eq!(expected_tx_weight, tx_weight.to_wu());
529 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}