1 // This file is Copyright its original authors, visible in version control
4 // This file is licensed under the Apache License, Version 2.0 <LICENSE-APACHE
5 // or http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
6 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your option.
7 // You may not use this file except in accordance with one or both of these
10 use crate::io_extras::sink;
11 use crate::prelude::*;
13 use bitcoin::absolute::LockTime as AbsoluteLockTime;
14 use bitcoin::amount::Amount;
15 use bitcoin::blockdata::constants::WITNESS_SCALE_FACTOR;
16 use bitcoin::consensus::Encodable;
17 use bitcoin::policy::MAX_STANDARD_TX_WEIGHT;
18 use bitcoin::transaction::Version;
19 use bitcoin::{OutPoint, ScriptBuf, Sequence, Transaction, TxIn, TxOut, Weight};
21 use crate::chain::chaininterface::fee_for_weight;
22 use crate::events::bump_transaction::{BASE_INPUT_WEIGHT, EMPTY_SCRIPT_SIG_WEIGHT};
23 use crate::ln::channel::TOTAL_BITCOIN_SUPPLY_SATOSHIS;
25 use crate::ln::msgs::SerialId;
26 use crate::ln::types::ChannelId;
27 use crate::sign::{EntropySource, P2TR_KEY_PATH_WITNESS_WEIGHT, P2WPKH_WITNESS_WEIGHT};
28 use crate::util::ser::TransactionU16LenLimited;
32 /// The number of received `tx_add_input` messages during a negotiation at which point the
33 /// negotiation MUST be failed.
34 const MAX_RECEIVED_TX_ADD_INPUT_COUNT: u16 = 4096;
36 /// The number of received `tx_add_output` messages during a negotiation at which point the
37 /// negotiation MUST be failed.
38 const MAX_RECEIVED_TX_ADD_OUTPUT_COUNT: u16 = 4096;
40 /// The number of inputs or outputs that the state machine can have, before it MUST fail the
42 const MAX_INPUTS_OUTPUTS_COUNT: usize = 252;
44 /// The total weight of the common fields whose fee is paid by the initiator of the interactive
45 /// transaction construction protocol.
46 const TX_COMMON_FIELDS_WEIGHT: u64 = (4 /* version */ + 4 /* locktime */ + 1 /* input count */ +
47 1 /* output count */) * WITNESS_SCALE_FACTOR as u64 + 2 /* segwit marker + flag */;
49 // BOLT 3 - Lower bounds for input weights
51 /// Lower bound for P2WPKH input weight
52 pub(crate) const P2WPKH_INPUT_WEIGHT_LOWER_BOUND: u64 =
53 BASE_INPUT_WEIGHT + EMPTY_SCRIPT_SIG_WEIGHT + P2WPKH_WITNESS_WEIGHT;
55 /// Lower bound for P2WSH input weight is chosen as same as P2WPKH input weight in BOLT 3
56 pub(crate) const P2WSH_INPUT_WEIGHT_LOWER_BOUND: u64 = P2WPKH_INPUT_WEIGHT_LOWER_BOUND;
58 /// Lower bound for P2TR input weight is chosen as the key spend path.
59 /// Not specified in BOLT 3, but a reasonable lower bound.
60 pub(crate) const P2TR_INPUT_WEIGHT_LOWER_BOUND: u64 =
61 BASE_INPUT_WEIGHT + EMPTY_SCRIPT_SIG_WEIGHT + P2TR_KEY_PATH_WITNESS_WEIGHT;
63 /// Lower bound for unknown segwit version input weight is chosen the same as P2WPKH in BOLT 3
64 pub(crate) const UNKNOWN_SEGWIT_VERSION_INPUT_WEIGHT_LOWER_BOUND: u64 =
65 P2WPKH_INPUT_WEIGHT_LOWER_BOUND;
68 fn is_for_initiator(&self) -> bool;
69 fn is_for_non_initiator(&self) -> bool;
72 impl SerialIdExt for SerialId {
73 fn is_for_initiator(&self) -> bool {
77 fn is_for_non_initiator(&self) -> bool {
78 !self.is_for_initiator()
82 #[derive(Debug, Clone, PartialEq)]
83 pub(crate) enum AbortReason {
84 InvalidStateTransition,
85 UnexpectedCounterpartyMessage,
86 ReceivedTooManyTxAddInputs,
87 ReceivedTooManyTxAddOutputs,
88 IncorrectInputSequenceValue,
89 IncorrectSerialIdParity,
93 ExceededMaximumSatsAllowed,
94 ExceededNumberOfInputsOrOutputs,
99 OutputsValueExceedsInputsValue,
101 /// No funding (shared) output found.
102 MissingFundingOutput,
103 /// More than one funding (shared) output found.
104 DuplicateFundingOutput,
105 /// The intended local part of the funding output is higher than the actual shared funding output,
106 /// if funding output is provided by the peer this is an interop error,
107 /// if provided by the same node than internal input consistency error.
108 InvalidLowFundingOutputValue,
111 #[derive(Debug, Clone, PartialEq, Eq)]
112 pub(crate) struct ConstructedTransaction {
113 holder_is_initiator: bool,
115 inputs: Vec<InteractiveTxInput>,
116 outputs: Vec<InteractiveTxOutput>,
118 local_inputs_value_satoshis: u64,
119 local_outputs_value_satoshis: u64,
121 remote_inputs_value_satoshis: u64,
122 remote_outputs_value_satoshis: u64,
124 lock_time: AbsoluteLockTime,
127 impl ConstructedTransaction {
128 fn new(context: NegotiationContext) -> Self {
129 let local_inputs_value_satoshis = context
132 .fold(0u64, |value, (_, input)| value.saturating_add(input.local_value()));
134 let local_outputs_value_satoshis = context
137 .fold(0u64, |value, (_, output)| value.saturating_add(output.local_value()));
140 holder_is_initiator: context.holder_is_initiator,
142 local_inputs_value_satoshis,
143 local_outputs_value_satoshis,
145 remote_inputs_value_satoshis: context.remote_inputs_value(),
146 remote_outputs_value_satoshis: context.remote_outputs_value(),
148 inputs: context.inputs.into_values().collect(),
149 outputs: context.outputs.into_values().collect(),
151 lock_time: context.tx_locktime,
155 pub fn weight(&self) -> Weight {
156 let inputs_weight = self.inputs.iter().fold(Weight::from_wu(0), |weight, input| {
157 weight.checked_add(estimate_input_weight(input.prev_output())).unwrap_or(Weight::MAX)
159 let outputs_weight = self.outputs.iter().fold(Weight::from_wu(0), |weight, output| {
160 weight.checked_add(get_output_weight(&output.script_pubkey())).unwrap_or(Weight::MAX)
162 Weight::from_wu(TX_COMMON_FIELDS_WEIGHT)
163 .checked_add(inputs_weight)
164 .and_then(|weight| weight.checked_add(outputs_weight))
165 .unwrap_or(Weight::MAX)
168 pub fn into_unsigned_tx(self) -> Transaction {
169 // Inputs and outputs must be sorted by serial_id
170 let ConstructedTransaction { mut inputs, mut outputs, .. } = self;
172 inputs.sort_unstable_by_key(|input| input.serial_id());
173 outputs.sort_unstable_by_key(|output| output.serial_id);
175 let input: Vec<TxIn> = inputs.into_iter().map(|input| input.txin().clone()).collect();
176 let output: Vec<TxOut> =
177 outputs.into_iter().map(|output| output.tx_out().clone()).collect();
179 Transaction { version: Version::TWO, lock_time: self.lock_time, input, output }
184 struct NegotiationContext {
185 holder_is_initiator: bool,
186 received_tx_add_input_count: u16,
187 received_tx_add_output_count: u16,
188 inputs: HashMap<SerialId, InteractiveTxInput>,
189 /// The output script intended to be the new funding output script.
190 /// The script pubkey is used to determine which output is the funding output.
191 /// When an output with the same script pubkey is added by any of the nodes, it will be
192 /// treated as the shared output.
193 /// The value is the holder's intended contribution to the shared funding output.
194 /// The rest is the counterparty's contribution.
195 /// When the funding output is added (recognized by its output script pubkey), it will be marked
196 /// as shared, and split between the peers according to the local value.
197 /// If the local value is found to be larger than the actual funding output, an error is generated.
198 expected_shared_funding_output: (ScriptBuf, u64),
199 /// The actual new funding output, set only after the output has actually been added.
200 /// NOTE: this output is also included in `outputs`.
201 actual_new_funding_output: Option<SharedOwnedOutput>,
202 prevtx_outpoints: HashSet<OutPoint>,
203 /// The outputs added so far.
204 outputs: HashMap<SerialId, InteractiveTxOutput>,
205 /// The locktime of the funding transaction.
206 tx_locktime: AbsoluteLockTime,
207 /// The fee rate used for the transaction
208 feerate_sat_per_kw: u32,
211 pub(crate) fn estimate_input_weight(prev_output: &TxOut) -> Weight {
212 Weight::from_wu(if prev_output.script_pubkey.is_p2wpkh() {
213 P2WPKH_INPUT_WEIGHT_LOWER_BOUND
214 } else if prev_output.script_pubkey.is_p2wsh() {
215 P2WSH_INPUT_WEIGHT_LOWER_BOUND
216 } else if prev_output.script_pubkey.is_p2tr() {
217 P2TR_INPUT_WEIGHT_LOWER_BOUND
219 UNKNOWN_SEGWIT_VERSION_INPUT_WEIGHT_LOWER_BOUND
223 pub(crate) fn get_output_weight(script_pubkey: &ScriptBuf) -> Weight {
225 (8 /* value */ + script_pubkey.consensus_encode(&mut sink()).unwrap() as u64)
226 * WITNESS_SCALE_FACTOR as u64,
230 fn is_serial_id_valid_for_counterparty(holder_is_initiator: bool, serial_id: &SerialId) -> bool {
231 // A received `SerialId`'s parity must match the role of the counterparty.
232 holder_is_initiator == serial_id.is_for_non_initiator()
235 impl NegotiationContext {
237 holder_is_initiator: bool, expected_shared_funding_output: (ScriptBuf, u64),
238 tx_locktime: AbsoluteLockTime, feerate_sat_per_kw: u32,
242 received_tx_add_input_count: 0,
243 received_tx_add_output_count: 0,
244 inputs: new_hash_map(),
245 expected_shared_funding_output,
246 actual_new_funding_output: None,
247 prevtx_outpoints: new_hash_set(),
248 outputs: new_hash_map(),
254 fn set_actual_new_funding_output(
255 &mut self, tx_out: TxOut,
256 ) -> Result<SharedOwnedOutput, AbortReason> {
257 if self.actual_new_funding_output.is_some() {
258 return Err(AbortReason::DuplicateFundingOutput);
260 let value = tx_out.value.to_sat();
261 let local_owned = self.expected_shared_funding_output.1;
263 if local_owned > value {
264 return Err(AbortReason::InvalidLowFundingOutputValue);
266 let shared_output = SharedOwnedOutput::new(tx_out, local_owned);
267 self.actual_new_funding_output = Some(shared_output.clone());
271 fn is_serial_id_valid_for_counterparty(&self, serial_id: &SerialId) -> bool {
272 is_serial_id_valid_for_counterparty(self.holder_is_initiator, serial_id)
275 fn remote_inputs_value(&self) -> u64 {
276 self.inputs.iter().fold(0u64, |acc, (_, input)| acc.saturating_add(input.remote_value()))
279 fn remote_outputs_value(&self) -> u64 {
280 self.outputs.iter().fold(0u64, |acc, (_, output)| acc.saturating_add(output.remote_value()))
283 fn remote_inputs_weight(&self) -> Weight {
287 .filter(|(serial_id, _)| self.is_serial_id_valid_for_counterparty(serial_id))
288 .fold(0u64, |weight, (_, input)| {
289 weight.saturating_add(estimate_input_weight(input.prev_output()).to_wu())
294 fn remote_outputs_weight(&self) -> Weight {
298 .filter(|(serial_id, _)| self.is_serial_id_valid_for_counterparty(serial_id))
299 .fold(0u64, |weight, (_, output)| {
300 weight.saturating_add(get_output_weight(&output.script_pubkey()).to_wu())
305 fn received_tx_add_input(&mut self, msg: &msgs::TxAddInput) -> Result<(), AbortReason> {
306 // The interactive-txs spec calls for us to fail negotiation if the `prevtx` we receive is
307 // invalid. However, we would not need to account for this explicit negotiation failure
308 // mode here since `PeerManager` would already disconnect the peer if the `prevtx` is
309 // invalid; implicitly ending the negotiation.
311 if !self.is_serial_id_valid_for_counterparty(&msg.serial_id) {
312 // The receiving node:
313 // - MUST fail the negotiation if:
314 // - the `serial_id` has the wrong parity
315 return Err(AbortReason::IncorrectSerialIdParity);
318 self.received_tx_add_input_count += 1;
319 if self.received_tx_add_input_count > MAX_RECEIVED_TX_ADD_INPUT_COUNT {
320 // The receiving node:
321 // - MUST fail the negotiation if:
322 // - if has received 4096 `tx_add_input` messages during this negotiation
323 return Err(AbortReason::ReceivedTooManyTxAddInputs);
326 if msg.sequence >= 0xFFFFFFFE {
327 // The receiving node:
328 // - MUST fail the negotiation if:
329 // - `sequence` is set to `0xFFFFFFFE` or `0xFFFFFFFF`
330 return Err(AbortReason::IncorrectInputSequenceValue);
333 let transaction = msg.prevtx.as_transaction();
334 let txid = transaction.txid();
336 if let Some(tx_out) = transaction.output.get(msg.prevtx_out as usize) {
337 if !tx_out.script_pubkey.is_witness_program() {
338 // The receiving node:
339 // - MUST fail the negotiation if:
340 // - the `scriptPubKey` is not a witness program
341 return Err(AbortReason::PrevTxOutInvalid);
344 if !self.prevtx_outpoints.insert(OutPoint { txid, vout: msg.prevtx_out }) {
345 // The receiving node:
346 // - MUST fail the negotiation if:
347 // - the `prevtx` and `prevtx_vout` are identical to a previously added
348 // (and not removed) input's
349 return Err(AbortReason::PrevTxOutInvalid);
352 // The receiving node:
353 // - MUST fail the negotiation if:
354 // - `prevtx_vout` is greater or equal to the number of outputs on `prevtx`
355 return Err(AbortReason::PrevTxOutInvalid);
358 let prev_out = if let Some(prev_out) = transaction.output.get(msg.prevtx_out as usize) {
361 return Err(AbortReason::PrevTxOutInvalid);
363 match self.inputs.entry(msg.serial_id) {
364 hash_map::Entry::Occupied(_) => {
365 // The receiving node:
366 // - MUST fail the negotiation if:
367 // - the `serial_id` is already included in the transaction
368 Err(AbortReason::DuplicateSerialId)
370 hash_map::Entry::Vacant(entry) => {
371 let prev_outpoint = OutPoint { txid, vout: msg.prevtx_out };
372 entry.insert(InteractiveTxInput::Remote(LocalOrRemoteInput {
373 serial_id: msg.serial_id,
375 previous_output: prev_outpoint,
376 sequence: Sequence(msg.sequence),
379 prev_output: prev_out,
381 self.prevtx_outpoints.insert(prev_outpoint);
387 fn received_tx_remove_input(&mut self, msg: &msgs::TxRemoveInput) -> Result<(), AbortReason> {
388 if !self.is_serial_id_valid_for_counterparty(&msg.serial_id) {
389 return Err(AbortReason::IncorrectSerialIdParity);
393 .remove(&msg.serial_id)
394 // The receiving node:
395 // - MUST fail the negotiation if:
396 // - the input or output identified by the `serial_id` was not added by the sender
397 // - the `serial_id` does not correspond to a currently added input
398 .ok_or(AbortReason::SerialIdUnknown)
402 fn received_tx_add_output(&mut self, msg: &msgs::TxAddOutput) -> Result<(), AbortReason> {
403 // The receiving node:
404 // - MUST fail the negotiation if:
405 // - the serial_id has the wrong parity
406 if !self.is_serial_id_valid_for_counterparty(&msg.serial_id) {
407 return Err(AbortReason::IncorrectSerialIdParity);
410 self.received_tx_add_output_count += 1;
411 if self.received_tx_add_output_count > MAX_RECEIVED_TX_ADD_OUTPUT_COUNT {
412 // The receiving node:
413 // - MUST fail the negotiation if:
414 // - if has received 4096 `tx_add_output` messages during this negotiation
415 return Err(AbortReason::ReceivedTooManyTxAddOutputs);
418 if msg.sats < msg.script.dust_value().to_sat() {
419 // The receiving node:
420 // - MUST fail the negotiation if:
421 // - the sats amount is less than the dust_limit
422 return Err(AbortReason::BelowDustLimit);
425 // Check that adding this output would not cause the total output value to exceed the total
427 let mut outputs_value: u64 = 0;
428 for output in self.outputs.iter() {
429 outputs_value = outputs_value.saturating_add(output.1.value());
431 if outputs_value.saturating_add(msg.sats) > TOTAL_BITCOIN_SUPPLY_SATOSHIS {
432 // The receiving node:
433 // - MUST fail the negotiation if:
434 // - the sats amount is greater than 2,100,000,000,000,000 (TOTAL_BITCOIN_SUPPLY_SATOSHIS)
435 return Err(AbortReason::ExceededMaximumSatsAllowed);
438 // The receiving node:
439 // - MUST accept P2WSH, P2WPKH, P2TR scripts
440 // - MAY fail the negotiation if script is non-standard
442 // We can actually be a bit looser than the above as only witness version 0 has special
443 // length-based standardness constraints to match similar consensus rules. All witness scripts
444 // with witness versions V1 and up are always considered standard. Yes, the scripts can be
445 // anyone-can-spend-able, but if our counterparty wants to add an output like that then it's none
446 // of our concern really ¯\_(ツ)_/¯
448 // TODO: The last check would be simplified when https://github.com/rust-bitcoin/rust-bitcoin/commit/1656e1a09a1959230e20af90d20789a4a8f0a31b
449 // hits the next release of rust-bitcoin.
450 if !(msg.script.is_p2wpkh()
451 || msg.script.is_p2wsh()
452 || (msg.script.is_witness_program()
453 && msg.script.witness_version().map(|v| v.to_num() >= 1).unwrap_or(false)))
455 return Err(AbortReason::InvalidOutputScript);
458 let txout = TxOut { value: Amount::from_sat(msg.sats), script_pubkey: msg.script.clone() };
459 let is_shared = msg.script == self.expected_shared_funding_output.0;
460 let output = if is_shared {
461 // this is a shared funding output
462 let shared_output = self.set_actual_new_funding_output(txout)?;
463 InteractiveTxOutput {
464 serial_id: msg.serial_id,
465 added_by: AddingRole::Remote,
466 output: OutputOwned::Shared(shared_output),
469 InteractiveTxOutput {
470 serial_id: msg.serial_id,
471 added_by: AddingRole::Remote,
472 output: OutputOwned::Single(txout),
475 match self.outputs.entry(msg.serial_id) {
476 hash_map::Entry::Occupied(_) => {
477 // The receiving node:
478 // - MUST fail the negotiation if:
479 // - the `serial_id` is already included in the transaction
480 Err(AbortReason::DuplicateSerialId)
482 hash_map::Entry::Vacant(entry) => {
483 entry.insert(output);
489 fn received_tx_remove_output(&mut self, msg: &msgs::TxRemoveOutput) -> Result<(), AbortReason> {
490 if !self.is_serial_id_valid_for_counterparty(&msg.serial_id) {
491 return Err(AbortReason::IncorrectSerialIdParity);
493 if self.outputs.remove(&msg.serial_id).is_some() {
496 // The receiving node:
497 // - MUST fail the negotiation if:
498 // - the input or output identified by the `serial_id` was not added by the sender
499 // - the `serial_id` does not correspond to a currently added input
500 Err(AbortReason::SerialIdUnknown)
504 fn sent_tx_add_input(&mut self, msg: &msgs::TxAddInput) -> Result<(), AbortReason> {
505 let tx = msg.prevtx.as_transaction();
507 previous_output: OutPoint { txid: tx.txid(), vout: msg.prevtx_out },
508 sequence: Sequence(msg.sequence),
511 if !self.prevtx_outpoints.insert(txin.previous_output.clone()) {
512 // We have added an input that already exists
513 return Err(AbortReason::PrevTxOutInvalid);
515 let vout = txin.previous_output.vout as usize;
516 let prev_output = tx.output.get(vout).ok_or(AbortReason::PrevTxOutInvalid)?.clone();
517 let input = InteractiveTxInput::Local(LocalOrRemoteInput {
518 serial_id: msg.serial_id,
522 self.inputs.insert(msg.serial_id, input);
526 fn sent_tx_add_output(&mut self, msg: &msgs::TxAddOutput) -> Result<(), AbortReason> {
527 let txout = TxOut { value: Amount::from_sat(msg.sats), script_pubkey: msg.script.clone() };
528 let is_shared = msg.script == self.expected_shared_funding_output.0;
529 let output = if is_shared {
530 // this is a shared funding output
531 let shared_output = self.set_actual_new_funding_output(txout)?;
532 InteractiveTxOutput {
533 serial_id: msg.serial_id,
534 added_by: AddingRole::Local,
535 output: OutputOwned::Shared(shared_output),
538 InteractiveTxOutput {
539 serial_id: msg.serial_id,
540 added_by: AddingRole::Local,
541 output: OutputOwned::Single(txout),
544 self.outputs.insert(msg.serial_id, output);
548 fn sent_tx_remove_input(&mut self, msg: &msgs::TxRemoveInput) -> Result<(), AbortReason> {
549 self.inputs.remove(&msg.serial_id);
553 fn sent_tx_remove_output(&mut self, msg: &msgs::TxRemoveOutput) -> Result<(), AbortReason> {
554 self.outputs.remove(&msg.serial_id);
558 fn check_counterparty_fees(
559 &self, counterparty_fees_contributed: u64,
560 ) -> Result<(), AbortReason> {
561 let counterparty_weight_contributed = self
562 .remote_inputs_weight()
564 .saturating_add(self.remote_outputs_weight().to_wu());
565 let mut required_counterparty_contribution_fee =
566 fee_for_weight(self.feerate_sat_per_kw, counterparty_weight_contributed);
567 if !self.holder_is_initiator {
568 // if is the non-initiator:
569 // - the initiator's fees do not cover the common fields (version, segwit marker + flag,
570 // input count, output count, locktime)
571 let tx_common_fields_fee =
572 fee_for_weight(self.feerate_sat_per_kw, TX_COMMON_FIELDS_WEIGHT);
573 required_counterparty_contribution_fee += tx_common_fields_fee;
575 if counterparty_fees_contributed < required_counterparty_contribution_fee {
576 return Err(AbortReason::InsufficientFees);
581 fn validate_tx(self) -> Result<ConstructedTransaction, AbortReason> {
582 // The receiving node:
583 // MUST fail the negotiation if:
585 // - the peer's total input satoshis is less than their outputs
586 let remote_inputs_value = self.remote_inputs_value();
587 let remote_outputs_value = self.remote_outputs_value();
588 if remote_inputs_value < remote_outputs_value {
589 return Err(AbortReason::OutputsValueExceedsInputsValue);
592 // - there are more than 252 inputs
593 // - there are more than 252 outputs
594 if self.inputs.len() > MAX_INPUTS_OUTPUTS_COUNT
595 || self.outputs.len() > MAX_INPUTS_OUTPUTS_COUNT
597 return Err(AbortReason::ExceededNumberOfInputsOrOutputs);
600 if self.actual_new_funding_output.is_none() {
601 return Err(AbortReason::MissingFundingOutput);
604 // - the peer's paid feerate does not meet or exceed the agreed feerate (based on the minimum fee).
605 self.check_counterparty_fees(remote_inputs_value.saturating_sub(remote_outputs_value))?;
607 let constructed_tx = ConstructedTransaction::new(self);
609 if constructed_tx.weight().to_wu() > MAX_STANDARD_TX_WEIGHT as u64 {
610 return Err(AbortReason::TransactionTooLarge);
617 // The interactive transaction construction protocol allows two peers to collaboratively build a
618 // transaction for broadcast.
620 // The protocol is turn-based, so we define different states here that we store depending on whose
621 // turn it is to send the next message. The states are defined so that their types ensure we only
622 // perform actions (only send messages) via defined state transitions that do not violate the
625 // An example of a full negotiation and associated states follows:
627 // +------------+ +------------------+---- Holder state after message sent/received ----+
628 // | |--(1)- tx_add_input ---->| | SentChangeMsg +
629 // | |<-(2)- tx_complete ------| | ReceivedTxComplete +
630 // | |--(3)- tx_add_output --->| | SentChangeMsg +
631 // | |<-(4)- tx_complete ------| | ReceivedTxComplete +
632 // | |--(5)- tx_add_input ---->| | SentChangeMsg +
633 // | Holder |<-(6)- tx_add_input -----| Counterparty | ReceivedChangeMsg +
634 // | |--(7)- tx_remove_output >| | SentChangeMsg +
635 // | |<-(8)- tx_add_output ----| | ReceivedChangeMsg +
636 // | |--(9)- tx_complete ----->| | SentTxComplete +
637 // | |<-(10) tx_complete ------| | NegotiationComplete +
638 // +------------+ +------------------+--------------------------------------------------+
640 /// Negotiation states that can send & receive `tx_(add|remove)_(input|output)` and `tx_complete`
643 /// Category of states where we have sent some message to the counterparty, and we are waiting for
645 trait SentMsgState: State {
646 fn into_negotiation_context(self) -> NegotiationContext;
649 /// Category of states that our counterparty has put us in after we receive a message from them.
650 trait ReceivedMsgState: State {
651 fn into_negotiation_context(self) -> NegotiationContext;
654 // This macro is a helper for implementing the above state traits for various states subsequently
655 // defined below the macro.
656 macro_rules! define_state {
657 (SENT_MSG_STATE, $state: ident, $doc: expr) => {
658 define_state!($state, NegotiationContext, $doc);
659 impl SentMsgState for $state {
660 fn into_negotiation_context(self) -> NegotiationContext {
665 (RECEIVED_MSG_STATE, $state: ident, $doc: expr) => {
666 define_state!($state, NegotiationContext, $doc);
667 impl ReceivedMsgState for $state {
668 fn into_negotiation_context(self) -> NegotiationContext {
673 ($state: ident, $inner: ident, $doc: expr) => {
676 struct $state($inner);
677 impl State for $state {}
684 "We have sent a message to the counterparty that has affected our negotiation state."
689 "We have sent a `tx_complete` message and are awaiting the counterparty's."
694 "We have received a message from the counterparty that has affected our negotiation state."
699 "We have received a `tx_complete` message and the counterparty is awaiting ours."
701 define_state!(NegotiationComplete, ConstructedTransaction, "We have exchanged consecutive `tx_complete` messages with the counterparty and the transaction negotiation is complete.");
705 "The negotiation has failed and cannot be continued."
708 type StateTransitionResult<S> = Result<S, AbortReason>;
710 trait StateTransition<NewState: State, TransitionData> {
711 fn transition(self, data: TransitionData) -> StateTransitionResult<NewState>;
714 // This macro helps define the legal transitions between the states above by implementing
715 // the `StateTransition` trait for each of the states that follow this declaration.
716 macro_rules! define_state_transitions {
717 (SENT_MSG_STATE, [$(DATA $data: ty, TRANSITION $transition: ident),+]) => {
719 impl<S: SentMsgState> StateTransition<ReceivedChangeMsg, $data> for S {
720 fn transition(self, data: $data) -> StateTransitionResult<ReceivedChangeMsg> {
721 let mut context = self.into_negotiation_context();
722 context.$transition(data)?;
723 Ok(ReceivedChangeMsg(context))
728 (RECEIVED_MSG_STATE, [$(DATA $data: ty, TRANSITION $transition: ident),+]) => {
730 impl<S: ReceivedMsgState> StateTransition<SentChangeMsg, $data> for S {
731 fn transition(self, data: $data) -> StateTransitionResult<SentChangeMsg> {
732 let mut context = self.into_negotiation_context();
733 context.$transition(data)?;
734 Ok(SentChangeMsg(context))
739 (TX_COMPLETE, $from_state: ident, $tx_complete_state: ident) => {
740 impl StateTransition<NegotiationComplete, &msgs::TxComplete> for $tx_complete_state {
741 fn transition(self, _data: &msgs::TxComplete) -> StateTransitionResult<NegotiationComplete> {
742 let context = self.into_negotiation_context();
743 let tx = context.validate_tx()?;
744 Ok(NegotiationComplete(tx))
748 impl StateTransition<$tx_complete_state, &msgs::TxComplete> for $from_state {
749 fn transition(self, _data: &msgs::TxComplete) -> StateTransitionResult<$tx_complete_state> {
750 Ok($tx_complete_state(self.into_negotiation_context()))
756 // State transitions when we have sent our counterparty some messages and are waiting for them
758 define_state_transitions!(SENT_MSG_STATE, [
759 DATA &msgs::TxAddInput, TRANSITION received_tx_add_input,
760 DATA &msgs::TxRemoveInput, TRANSITION received_tx_remove_input,
761 DATA &msgs::TxAddOutput, TRANSITION received_tx_add_output,
762 DATA &msgs::TxRemoveOutput, TRANSITION received_tx_remove_output
764 // State transitions when we have received some messages from our counterparty and we should
766 define_state_transitions!(RECEIVED_MSG_STATE, [
767 DATA &msgs::TxAddInput, TRANSITION sent_tx_add_input,
768 DATA &msgs::TxRemoveInput, TRANSITION sent_tx_remove_input,
769 DATA &msgs::TxAddOutput, TRANSITION sent_tx_add_output,
770 DATA &msgs::TxRemoveOutput, TRANSITION sent_tx_remove_output
772 define_state_transitions!(TX_COMPLETE, SentChangeMsg, ReceivedTxComplete);
773 define_state_transitions!(TX_COMPLETE, ReceivedChangeMsg, SentTxComplete);
778 SentChangeMsg(SentChangeMsg),
779 ReceivedChangeMsg(ReceivedChangeMsg),
780 SentTxComplete(SentTxComplete),
781 ReceivedTxComplete(ReceivedTxComplete),
782 NegotiationComplete(NegotiationComplete),
783 NegotiationAborted(NegotiationAborted),
786 impl Default for StateMachine {
787 fn default() -> Self {
792 // The `StateMachine` internally executes the actual transition between two states and keeps
793 // track of the current state. This macro defines _how_ those state transitions happen to
794 // update the internal state.
795 macro_rules! define_state_machine_transitions {
796 ($transition: ident, $msg: ty, [$(FROM $from_state: ident, TO $to_state: ident),+]) => {
797 fn $transition(self, msg: $msg) -> StateMachine {
800 Self::$from_state(s) => match s.transition(msg) {
801 Ok(new_state) => StateMachine::$to_state(new_state),
802 Err(abort_reason) => StateMachine::NegotiationAborted(NegotiationAborted(abort_reason)),
805 _ => StateMachine::NegotiationAborted(NegotiationAborted(AbortReason::UnexpectedCounterpartyMessage)),
813 feerate_sat_per_kw: u32, is_initiator: bool, tx_locktime: AbsoluteLockTime,
814 expected_shared_funding_output: (ScriptBuf, u64),
816 let context = NegotiationContext::new(
818 expected_shared_funding_output,
823 Self::ReceivedChangeMsg(ReceivedChangeMsg(context))
825 Self::SentChangeMsg(SentChangeMsg(context))
830 define_state_machine_transitions!(sent_tx_add_input, &msgs::TxAddInput, [
831 FROM ReceivedChangeMsg, TO SentChangeMsg,
832 FROM ReceivedTxComplete, TO SentChangeMsg
834 define_state_machine_transitions!(received_tx_add_input, &msgs::TxAddInput, [
835 FROM SentChangeMsg, TO ReceivedChangeMsg,
836 FROM SentTxComplete, TO ReceivedChangeMsg
840 define_state_machine_transitions!(sent_tx_add_output, &msgs::TxAddOutput, [
841 FROM ReceivedChangeMsg, TO SentChangeMsg,
842 FROM ReceivedTxComplete, TO SentChangeMsg
844 define_state_machine_transitions!(received_tx_add_output, &msgs::TxAddOutput, [
845 FROM SentChangeMsg, TO ReceivedChangeMsg,
846 FROM SentTxComplete, TO ReceivedChangeMsg
850 define_state_machine_transitions!(sent_tx_remove_input, &msgs::TxRemoveInput, [
851 FROM ReceivedChangeMsg, TO SentChangeMsg,
852 FROM ReceivedTxComplete, TO SentChangeMsg
854 define_state_machine_transitions!(received_tx_remove_input, &msgs::TxRemoveInput, [
855 FROM SentChangeMsg, TO ReceivedChangeMsg,
856 FROM SentTxComplete, TO ReceivedChangeMsg
860 define_state_machine_transitions!(sent_tx_remove_output, &msgs::TxRemoveOutput, [
861 FROM ReceivedChangeMsg, TO SentChangeMsg,
862 FROM ReceivedTxComplete, TO SentChangeMsg
864 define_state_machine_transitions!(received_tx_remove_output, &msgs::TxRemoveOutput, [
865 FROM SentChangeMsg, TO ReceivedChangeMsg,
866 FROM SentTxComplete, TO ReceivedChangeMsg
870 define_state_machine_transitions!(sent_tx_complete, &msgs::TxComplete, [
871 FROM ReceivedChangeMsg, TO SentTxComplete,
872 FROM ReceivedTxComplete, TO NegotiationComplete
874 define_state_machine_transitions!(received_tx_complete, &msgs::TxComplete, [
875 FROM SentChangeMsg, TO ReceivedTxComplete,
876 FROM SentTxComplete, TO NegotiationComplete
880 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
881 pub enum AddingRole {
886 /// Represents an input -- local or remote (both have the same fields)
887 #[derive(Clone, Debug, Eq, PartialEq)]
888 pub struct LocalOrRemoteInput {
894 #[derive(Clone, Debug, Eq, PartialEq)]
895 pub enum InteractiveTxInput {
896 Local(LocalOrRemoteInput),
897 Remote(LocalOrRemoteInput),
898 // TODO(splicing) SharedInput should be added
901 #[derive(Clone, Debug, Eq, PartialEq)]
902 pub struct SharedOwnedOutput {
907 impl SharedOwnedOutput {
908 fn new(tx_out: TxOut, local_owned: u64) -> SharedOwnedOutput {
910 local_owned <= tx_out.value.to_sat(),
911 "SharedOwnedOutput: Inconsistent local_owned value {}, larger than output value {}",
915 SharedOwnedOutput { tx_out, local_owned }
918 fn remote_owned(&self) -> u64 {
919 self.tx_out.value.to_sat().saturating_sub(self.local_owned)
923 /// Represents an output, with information about
924 /// its control -- exclusive by the adder or shared --, and
925 /// its ownership -- value fully owned by the adder or jointly
926 #[derive(Clone, Debug, Eq, PartialEq)]
927 pub enum OutputOwned {
928 /// Belongs to local node -- controlled exclusively and fully belonging to local node
930 /// Output with shared control, but fully belonging to local node
931 SharedControlFullyOwned(TxOut),
932 /// Output with shared control and joint ownership
933 Shared(SharedOwnedOutput),
937 fn tx_out(&self) -> &TxOut {
939 OutputOwned::Single(tx_out) | OutputOwned::SharedControlFullyOwned(tx_out) => tx_out,
940 OutputOwned::Shared(output) => &output.tx_out,
944 fn value(&self) -> u64 {
945 self.tx_out().value.to_sat()
948 fn is_shared(&self) -> bool {
950 OutputOwned::Single(_) => false,
951 OutputOwned::SharedControlFullyOwned(_) => true,
952 OutputOwned::Shared(_) => true,
956 fn local_value(&self, local_role: AddingRole) -> u64 {
958 OutputOwned::Single(tx_out) | OutputOwned::SharedControlFullyOwned(tx_out) => {
960 AddingRole::Local => tx_out.value.to_sat(),
961 AddingRole::Remote => 0,
964 OutputOwned::Shared(output) => output.local_owned,
968 fn remote_value(&self, local_role: AddingRole) -> u64 {
970 OutputOwned::Single(tx_out) | OutputOwned::SharedControlFullyOwned(tx_out) => {
972 AddingRole::Local => 0,
973 AddingRole::Remote => tx_out.value.to_sat(),
976 OutputOwned::Shared(output) => output.remote_owned(),
981 #[derive(Clone, Debug, Eq, PartialEq)]
982 pub struct InteractiveTxOutput {
984 added_by: AddingRole,
988 impl InteractiveTxOutput {
989 fn tx_out(&self) -> &TxOut {
993 fn value(&self) -> u64 {
994 self.tx_out().value.to_sat()
997 fn local_value(&self) -> u64 {
998 self.output.local_value(self.added_by)
1001 fn remote_value(&self) -> u64 {
1002 self.output.remote_value(self.added_by)
1005 fn script_pubkey(&self) -> &ScriptBuf {
1006 &self.output.tx_out().script_pubkey
1010 impl InteractiveTxInput {
1011 pub fn serial_id(&self) -> SerialId {
1013 InteractiveTxInput::Local(input) => input.serial_id,
1014 InteractiveTxInput::Remote(input) => input.serial_id,
1018 pub fn txin(&self) -> &TxIn {
1020 InteractiveTxInput::Local(input) => &input.input,
1021 InteractiveTxInput::Remote(input) => &input.input,
1025 pub fn prev_output(&self) -> &TxOut {
1027 InteractiveTxInput::Local(input) => &input.prev_output,
1028 InteractiveTxInput::Remote(input) => &input.prev_output,
1032 pub fn value(&self) -> u64 {
1033 self.prev_output().value.to_sat()
1036 pub fn local_value(&self) -> u64 {
1038 InteractiveTxInput::Local(input) => input.prev_output.value.to_sat(),
1039 InteractiveTxInput::Remote(_input) => 0,
1043 pub fn remote_value(&self) -> u64 {
1045 InteractiveTxInput::Local(_input) => 0,
1046 InteractiveTxInput::Remote(input) => input.prev_output.value.to_sat(),
1051 pub(crate) struct InteractiveTxConstructor {
1052 state_machine: StateMachine,
1053 channel_id: ChannelId,
1054 inputs_to_contribute: Vec<(SerialId, TxIn, TransactionU16LenLimited)>,
1055 outputs_to_contribute: Vec<(SerialId, OutputOwned)>,
1058 pub(crate) enum InteractiveTxMessageSend {
1059 TxAddInput(msgs::TxAddInput),
1060 TxAddOutput(msgs::TxAddOutput),
1061 TxComplete(msgs::TxComplete),
1064 // This macro executes a state machine transition based on a provided action.
1065 macro_rules! do_state_transition {
1066 ($self: ident, $transition: ident, $msg: expr) => {{
1067 let state_machine = core::mem::take(&mut $self.state_machine);
1068 $self.state_machine = state_machine.$transition($msg);
1069 match &$self.state_machine {
1070 StateMachine::NegotiationAborted(state) => Err(state.0.clone()),
1076 fn generate_holder_serial_id<ES: Deref>(entropy_source: &ES, is_initiator: bool) -> SerialId
1078 ES::Target: EntropySource,
1080 let rand_bytes = entropy_source.get_secure_random_bytes();
1081 let mut serial_id_bytes = [0u8; 8];
1082 serial_id_bytes.copy_from_slice(&rand_bytes[..8]);
1083 let mut serial_id = u64::from_be_bytes(serial_id_bytes);
1084 if serial_id.is_for_initiator() != is_initiator {
1090 pub(crate) enum HandleTxCompleteValue {
1091 SendTxMessage(InteractiveTxMessageSend),
1092 SendTxComplete(InteractiveTxMessageSend, ConstructedTransaction),
1093 NegotiationComplete(ConstructedTransaction),
1096 impl InteractiveTxConstructor {
1097 /// Instantiates a new `InteractiveTxConstructor`.
1099 /// `expected_remote_shared_funding_output`: In the case when the local node doesn't
1100 /// add a shared output, but it expects a shared output to be added by the remote node,
1101 /// it has to specify the script pubkey, used to determine the shared output,
1102 /// and its (local) contribution from the shared output:
1103 /// 0 when the whole value belongs to the remote node, or
1104 /// positive if owned also by local.
1105 /// Note: The local value cannot be larger that the actual shared output.
1107 /// A tuple is returned containing the newly instantiate `InteractiveTxConstructor` and optionally
1108 /// an initial wrapped `Tx_` message which the holder needs to send to the counterparty.
1109 pub fn new<ES: Deref>(
1110 entropy_source: &ES, channel_id: ChannelId, feerate_sat_per_kw: u32, is_initiator: bool,
1111 funding_tx_locktime: AbsoluteLockTime,
1112 inputs_to_contribute: Vec<(TxIn, TransactionU16LenLimited)>,
1113 outputs_to_contribute: Vec<OutputOwned>,
1114 expected_remote_shared_funding_output: Option<(ScriptBuf, u64)>,
1115 ) -> Result<(Self, Option<InteractiveTxMessageSend>), AbortReason>
1117 ES::Target: EntropySource,
1119 // Sanity check: There can be at most one shared output, local-added or remote-added
1120 let mut expected_shared_funding_output: Option<(ScriptBuf, u64)> = None;
1121 for output in &outputs_to_contribute {
1122 let new_output = match output {
1123 OutputOwned::Single(_tx_out) => None,
1124 OutputOwned::SharedControlFullyOwned(tx_out) => {
1125 Some((tx_out.script_pubkey.clone(), tx_out.value.to_sat()))
1127 OutputOwned::Shared(output) => {
1129 if output.local_owned > output.tx_out.value.to_sat() {
1130 return Err(AbortReason::InvalidLowFundingOutputValue);
1132 Some((output.tx_out.script_pubkey.clone(), output.local_owned))
1135 if new_output.is_some() {
1136 if expected_shared_funding_output.is_some()
1137 || expected_remote_shared_funding_output.is_some()
1139 // more than one local-added shared output or
1140 // one local-added and one remote-expected shared output
1141 return Err(AbortReason::DuplicateFundingOutput);
1143 expected_shared_funding_output = new_output;
1146 if let Some(expected_remote_shared_funding_output) = expected_remote_shared_funding_output {
1147 expected_shared_funding_output = Some(expected_remote_shared_funding_output);
1149 if let Some(expected_shared_funding_output) = expected_shared_funding_output {
1150 let state_machine = StateMachine::new(
1153 funding_tx_locktime,
1154 expected_shared_funding_output,
1156 let mut inputs_to_contribute: Vec<(SerialId, TxIn, TransactionU16LenLimited)> =
1157 inputs_to_contribute
1159 .map(|(input, tx)| {
1160 let serial_id = generate_holder_serial_id(entropy_source, is_initiator);
1161 (serial_id, input, tx)
1164 // We'll sort by the randomly generated serial IDs, effectively shuffling the order of the inputs
1165 // as the user passed them to us to avoid leaking any potential categorization of transactions
1166 // before we pass any of the inputs to the counterparty.
1167 inputs_to_contribute.sort_unstable_by_key(|(serial_id, _, _)| *serial_id);
1168 let mut outputs_to_contribute: Vec<_> = outputs_to_contribute
1171 let serial_id = generate_holder_serial_id(entropy_source, is_initiator);
1175 // In the same manner and for the same rationale as the inputs above, we'll shuffle the outputs.
1176 outputs_to_contribute.sort_unstable_by_key(|(serial_id, _)| *serial_id);
1177 let mut constructor =
1178 Self { state_machine, channel_id, inputs_to_contribute, outputs_to_contribute };
1179 let message_send = if is_initiator {
1180 match constructor.maybe_send_message() {
1181 Ok(msg_send) => Some(msg_send),
1185 "We should always be able to start our state machine successfully"
1193 Ok((constructor, message_send))
1195 Err(AbortReason::MissingFundingOutput)
1199 fn maybe_send_message(&mut self) -> Result<InteractiveTxMessageSend, AbortReason> {
1200 // We first attempt to send inputs we want to add, then outputs. Once we are done sending
1201 // them both, then we always send tx_complete.
1202 if let Some((serial_id, input, prevtx)) = self.inputs_to_contribute.pop() {
1203 let msg = msgs::TxAddInput {
1204 channel_id: self.channel_id,
1207 prevtx_out: input.previous_output.vout,
1208 sequence: input.sequence.to_consensus_u32(),
1210 do_state_transition!(self, sent_tx_add_input, &msg)?;
1211 Ok(InteractiveTxMessageSend::TxAddInput(msg))
1212 } else if let Some((serial_id, output)) = self.outputs_to_contribute.pop() {
1213 let msg = msgs::TxAddOutput {
1214 channel_id: self.channel_id,
1216 sats: output.tx_out().value.to_sat(),
1217 script: output.tx_out().script_pubkey.clone(),
1219 do_state_transition!(self, sent_tx_add_output, &msg)?;
1220 Ok(InteractiveTxMessageSend::TxAddOutput(msg))
1222 let msg = msgs::TxComplete { channel_id: self.channel_id };
1223 do_state_transition!(self, sent_tx_complete, &msg)?;
1224 Ok(InteractiveTxMessageSend::TxComplete(msg))
1228 pub fn handle_tx_add_input(
1229 &mut self, msg: &msgs::TxAddInput,
1230 ) -> Result<InteractiveTxMessageSend, AbortReason> {
1231 do_state_transition!(self, received_tx_add_input, msg)?;
1232 self.maybe_send_message()
1235 pub fn handle_tx_remove_input(
1236 &mut self, msg: &msgs::TxRemoveInput,
1237 ) -> Result<InteractiveTxMessageSend, AbortReason> {
1238 do_state_transition!(self, received_tx_remove_input, msg)?;
1239 self.maybe_send_message()
1242 pub fn handle_tx_add_output(
1243 &mut self, msg: &msgs::TxAddOutput,
1244 ) -> Result<InteractiveTxMessageSend, AbortReason> {
1245 do_state_transition!(self, received_tx_add_output, msg)?;
1246 self.maybe_send_message()
1249 pub fn handle_tx_remove_output(
1250 &mut self, msg: &msgs::TxRemoveOutput,
1251 ) -> Result<InteractiveTxMessageSend, AbortReason> {
1252 do_state_transition!(self, received_tx_remove_output, msg)?;
1253 self.maybe_send_message()
1256 pub fn handle_tx_complete(
1257 &mut self, msg: &msgs::TxComplete,
1258 ) -> Result<HandleTxCompleteValue, AbortReason> {
1259 do_state_transition!(self, received_tx_complete, msg)?;
1260 match &self.state_machine {
1261 StateMachine::ReceivedTxComplete(_) => {
1262 let msg_send = self.maybe_send_message()?;
1263 match &self.state_machine {
1264 StateMachine::NegotiationComplete(s) => {
1265 Ok(HandleTxCompleteValue::SendTxComplete(msg_send, s.0.clone()))
1267 StateMachine::SentChangeMsg(_) => {
1268 Ok(HandleTxCompleteValue::SendTxMessage(msg_send))
1269 }, // We either had an input or output to contribute.
1271 debug_assert!(false, "We cannot transition to any other states after receiving `tx_complete` and responding");
1272 Err(AbortReason::InvalidStateTransition)
1276 StateMachine::NegotiationComplete(s) => {
1277 Ok(HandleTxCompleteValue::NegotiationComplete(s.0.clone()))
1282 "We cannot transition to any other states after receiving `tx_complete`"
1284 Err(AbortReason::InvalidStateTransition)
1292 use crate::chain::chaininterface::{fee_for_weight, FEERATE_FLOOR_SATS_PER_KW};
1293 use crate::ln::channel::TOTAL_BITCOIN_SUPPLY_SATOSHIS;
1294 use crate::ln::interactivetxs::{
1295 generate_holder_serial_id, AbortReason, HandleTxCompleteValue, InteractiveTxConstructor,
1296 InteractiveTxMessageSend, MAX_INPUTS_OUTPUTS_COUNT, MAX_RECEIVED_TX_ADD_INPUT_COUNT,
1297 MAX_RECEIVED_TX_ADD_OUTPUT_COUNT,
1299 use crate::ln::types::ChannelId;
1300 use crate::sign::EntropySource;
1301 use crate::util::atomic_counter::AtomicCounter;
1302 use crate::util::ser::TransactionU16LenLimited;
1303 use bitcoin::absolute::LockTime as AbsoluteLockTime;
1304 use bitcoin::amount::Amount;
1305 use bitcoin::blockdata::opcodes;
1306 use bitcoin::blockdata::script::Builder;
1307 use bitcoin::hashes::Hash;
1308 use bitcoin::key::UntweakedPublicKey;
1309 use bitcoin::secp256k1::{Keypair, Secp256k1};
1310 use bitcoin::transaction::Version;
1312 OutPoint, PubkeyHash, ScriptBuf, Sequence, Transaction, TxIn, TxOut, WPubkeyHash,
1314 use core::ops::Deref;
1317 get_output_weight, AddingRole, OutputOwned, SharedOwnedOutput,
1318 P2TR_INPUT_WEIGHT_LOWER_BOUND, P2WPKH_INPUT_WEIGHT_LOWER_BOUND,
1319 P2WSH_INPUT_WEIGHT_LOWER_BOUND, TX_COMMON_FIELDS_WEIGHT,
1322 const TEST_FEERATE_SATS_PER_KW: u32 = FEERATE_FLOOR_SATS_PER_KW * 10;
1324 // A simple entropy source that works based on an atomic counter.
1325 struct TestEntropySource(AtomicCounter);
1326 impl EntropySource for TestEntropySource {
1327 fn get_secure_random_bytes(&self) -> [u8; 32] {
1328 let mut res = [0u8; 32];
1329 let increment = self.0.get_increment();
1331 // Rotate the increment value by 'i' bits to the right, to avoid clashes
1332 // when `generate_local_serial_id` does a parity flip on consecutive calls for the
1334 let rotated_increment = increment.rotate_right(i as u32);
1335 res[i] = (rotated_increment & 0xff) as u8;
1341 // An entropy source that deliberately returns you the same seed every time. We use this
1342 // to test if the constructor would catch inputs/outputs that are attempting to be added
1343 // with duplicate serial ids.
1344 struct DuplicateEntropySource;
1345 impl EntropySource for DuplicateEntropySource {
1346 fn get_secure_random_bytes(&self) -> [u8; 32] {
1347 let mut res = [0u8; 32];
1349 res[0..8].copy_from_slice(&count.to_be_bytes());
1354 #[derive(Debug, PartialEq, Eq)]
1358 // Some error values are only checked at the end of the negotiation and are not easy to attribute
1359 // to a particular party. Both parties would indicate an `AbortReason` in this case.
1360 // e.g. Exceeded max inputs and outputs after negotiation.
1364 struct TestSession {
1365 description: &'static str,
1366 inputs_a: Vec<(TxIn, TransactionU16LenLimited)>,
1367 outputs_a: Vec<OutputOwned>,
1368 inputs_b: Vec<(TxIn, TransactionU16LenLimited)>,
1369 outputs_b: Vec<OutputOwned>,
1370 expect_error: Option<(AbortReason, ErrorCulprit)>,
1371 /// A node adds no shared output, but expects the peer to add one, with the specific script pubkey, and local contribution
1372 a_expected_remote_shared_output: Option<(ScriptBuf, u64)>,
1373 /// B node adds no shared output, but expects the peer to add one, with the specific script pubkey, and local contribution
1374 b_expected_remote_shared_output: Option<(ScriptBuf, u64)>,
1377 fn do_test_interactive_tx_constructor(session: TestSession) {
1378 let entropy_source = TestEntropySource(AtomicCounter::new());
1379 do_test_interactive_tx_constructor_internal(session, &&entropy_source);
1382 fn do_test_interactive_tx_constructor_with_entropy_source<ES: Deref>(
1383 session: TestSession, entropy_source: ES,
1385 ES::Target: EntropySource,
1387 do_test_interactive_tx_constructor_internal(session, &entropy_source);
1390 fn do_test_interactive_tx_constructor_internal<ES: Deref>(
1391 session: TestSession, entropy_source: &ES,
1393 ES::Target: EntropySource,
1395 let channel_id = ChannelId(entropy_source.get_secure_random_bytes());
1396 let tx_locktime = AbsoluteLockTime::from_height(1337).unwrap();
1398 // funding output sanity check
1399 let shared_outputs_by_a: Vec<_> =
1400 session.outputs_a.iter().filter(|o| o.is_shared()).collect();
1401 if shared_outputs_by_a.len() > 1 {
1402 println!("Test warning: Expected at most one shared output. NodeA");
1404 let shared_output_by_a = if shared_outputs_by_a.len() >= 1 {
1405 Some(shared_outputs_by_a[0].value())
1409 let shared_outputs_by_b: Vec<_> =
1410 session.outputs_b.iter().filter(|o| o.is_shared()).collect();
1411 if shared_outputs_by_b.len() > 1 {
1412 println!("Test warning: Expected at most one shared output. NodeB");
1414 let shared_output_by_b = if shared_outputs_by_b.len() >= 1 {
1415 Some(shared_outputs_by_b[0].value())
1419 if session.a_expected_remote_shared_output.is_some()
1420 || session.b_expected_remote_shared_output.is_some()
1422 let expected_by_a = if let Some(a_expected_remote_shared_output) =
1423 &session.a_expected_remote_shared_output
1425 a_expected_remote_shared_output.1
1427 if shared_outputs_by_a.len() >= 1 {
1428 shared_outputs_by_a[0].local_value(AddingRole::Local)
1433 let expected_by_b = if let Some(b_expected_remote_shared_output) =
1434 &session.b_expected_remote_shared_output
1436 b_expected_remote_shared_output.1
1438 if shared_outputs_by_b.len() >= 1 {
1439 shared_outputs_by_b[0].local_value(AddingRole::Local)
1445 let expected_sum = expected_by_a + expected_by_b;
1446 let actual_shared_output =
1447 shared_output_by_a.unwrap_or(shared_output_by_b.unwrap_or(0));
1448 if expected_sum != actual_shared_output {
1449 println!("Test warning: Sum of expected shared output values does not match actual shared output value, {} {} {} {} {} {}", expected_sum, actual_shared_output, expected_by_a, expected_by_b, shared_output_by_a.unwrap_or(0), shared_output_by_b.unwrap_or(0));
1453 let (mut constructor_a, first_message_a) = match InteractiveTxConstructor::new(
1456 TEST_FEERATE_SATS_PER_KW,
1460 session.outputs_a.iter().map(|o| o.clone()).collect(),
1461 session.a_expected_remote_shared_output,
1464 Err(abort_reason) => {
1466 Some((abort_reason, ErrorCulprit::NodeA)),
1467 session.expect_error,
1474 let (mut constructor_b, first_message_b) = match InteractiveTxConstructor::new(
1477 TEST_FEERATE_SATS_PER_KW,
1481 session.outputs_b.iter().map(|o| o.clone()).collect(),
1482 session.b_expected_remote_shared_output,
1485 Err(abort_reason) => {
1487 Some((abort_reason, ErrorCulprit::NodeB)),
1488 session.expect_error,
1496 let handle_message_send =
1497 |msg: InteractiveTxMessageSend, for_constructor: &mut InteractiveTxConstructor| {
1499 InteractiveTxMessageSend::TxAddInput(msg) => for_constructor
1500 .handle_tx_add_input(&msg)
1501 .map(|msg_send| (Some(msg_send), None)),
1502 InteractiveTxMessageSend::TxAddOutput(msg) => for_constructor
1503 .handle_tx_add_output(&msg)
1504 .map(|msg_send| (Some(msg_send), None)),
1505 InteractiveTxMessageSend::TxComplete(msg) => {
1506 for_constructor.handle_tx_complete(&msg).map(|value| match value {
1507 HandleTxCompleteValue::SendTxMessage(msg_send) => {
1508 (Some(msg_send), None)
1510 HandleTxCompleteValue::SendTxComplete(msg_send, tx) => {
1511 (Some(msg_send), Some(tx))
1513 HandleTxCompleteValue::NegotiationComplete(tx) => (None, Some(tx)),
1519 assert!(first_message_b.is_none());
1520 let mut message_send_a = first_message_a;
1521 let mut message_send_b = None;
1522 let mut final_tx_a = None;
1523 let mut final_tx_b = None;
1524 while final_tx_a.is_none() || final_tx_b.is_none() {
1525 if let Some(message_send_a) = message_send_a.take() {
1526 match handle_message_send(message_send_a, &mut constructor_b) {
1527 Ok((msg_send, final_tx)) => {
1528 message_send_b = msg_send;
1529 final_tx_b = final_tx;
1531 Err(abort_reason) => {
1532 let error_culprit = match abort_reason {
1533 AbortReason::ExceededNumberOfInputsOrOutputs => {
1534 ErrorCulprit::Indeterminate
1536 _ => ErrorCulprit::NodeA,
1539 Some((abort_reason, error_culprit)),
1540 session.expect_error,
1544 assert!(message_send_b.is_none(), "Test: {}", session.description);
1549 if let Some(message_send_b) = message_send_b.take() {
1550 match handle_message_send(message_send_b, &mut constructor_a) {
1551 Ok((msg_send, final_tx)) => {
1552 message_send_a = msg_send;
1553 final_tx_a = final_tx;
1555 Err(abort_reason) => {
1556 let error_culprit = match abort_reason {
1557 AbortReason::ExceededNumberOfInputsOrOutputs => {
1558 ErrorCulprit::Indeterminate
1560 _ => ErrorCulprit::NodeB,
1563 Some((abort_reason, error_culprit)),
1564 session.expect_error,
1568 assert!(message_send_a.is_none(), "Test: {}", session.description);
1574 assert!(message_send_a.is_none());
1575 assert!(message_send_b.is_none());
1576 assert_eq!(final_tx_a.unwrap().into_unsigned_tx(), final_tx_b.unwrap().into_unsigned_tx());
1578 session.expect_error.is_none(),
1579 "Missing expected error {:?}, Test: {}",
1580 session.expect_error,
1581 session.description,
1585 #[derive(Debug, Clone, Copy)]
1588 /// P2WSH, but with the specific script used for the funding output
1591 // Non-witness type to test rejection.
1595 fn generate_tx(outputs: &[TestOutput]) -> Transaction {
1596 generate_tx_with_locktime(outputs, 1337)
1599 fn generate_txout(output: &TestOutput) -> TxOut {
1600 let secp_ctx = Secp256k1::new();
1601 let (value, script_pubkey) = match output {
1602 TestOutput::P2WPKH(value) => (*value, generate_p2wpkh_script_pubkey()),
1603 TestOutput::P2WSH(value) => (*value, generate_funding_script_pubkey()),
1604 TestOutput::P2TR(value) => (
1606 ScriptBuf::new_p2tr(
1608 UntweakedPublicKey::from_keypair(
1609 &Keypair::from_seckey_slice(&secp_ctx, &[3; 32]).unwrap(),
1615 TestOutput::P2PKH(value) => {
1616 (*value, ScriptBuf::new_p2pkh(&PubkeyHash::from_slice(&[4; 20]).unwrap()))
1620 TxOut { value: Amount::from_sat(value), script_pubkey }
1623 fn generate_tx_with_locktime(outputs: &[TestOutput], locktime: u32) -> Transaction {
1625 version: Version::TWO,
1626 lock_time: AbsoluteLockTime::from_height(locktime).unwrap(),
1627 input: vec![TxIn { ..Default::default() }],
1628 output: outputs.iter().map(generate_txout).collect(),
1632 fn generate_inputs(outputs: &[TestOutput]) -> Vec<(TxIn, TransactionU16LenLimited)> {
1633 let tx = generate_tx(outputs);
1634 let txid = tx.txid();
1640 previous_output: OutPoint { txid, vout: idx as u32 },
1641 script_sig: Default::default(),
1642 sequence: Sequence::ENABLE_RBF_NO_LOCKTIME,
1643 witness: Default::default(),
1645 (input, TransactionU16LenLimited::new(tx.clone()).unwrap())
1650 fn generate_p2wsh_script_pubkey() -> ScriptBuf {
1651 Builder::new().push_opcode(opcodes::OP_TRUE).into_script().to_p2wsh()
1654 fn generate_p2wpkh_script_pubkey() -> ScriptBuf {
1655 ScriptBuf::new_p2wpkh(&WPubkeyHash::from_slice(&[1; 20]).unwrap())
1658 fn generate_funding_script_pubkey() -> ScriptBuf {
1659 Builder::new().push_int(33).into_script().to_p2wsh()
1662 fn generate_output_nonfunding_one(output: &TestOutput) -> OutputOwned {
1663 OutputOwned::Single(generate_txout(output))
1666 fn generate_outputs(outputs: &[TestOutput]) -> Vec<OutputOwned> {
1667 outputs.iter().map(|o| generate_output_nonfunding_one(o)).collect()
1670 /// Generate a single output that is the funding output
1671 fn generate_output(output: &TestOutput) -> Vec<OutputOwned> {
1672 vec![OutputOwned::SharedControlFullyOwned(generate_txout(output))]
1675 /// Generate a single P2WSH output that is the funding output
1676 fn generate_funding_output(value: u64) -> Vec<OutputOwned> {
1677 generate_output(&TestOutput::P2WSH(value))
1680 /// Generate a single P2WSH output with shared contribution that is the funding output
1681 fn generate_shared_funding_output_one(value: u64, local_value: u64) -> OutputOwned {
1682 OutputOwned::Shared(SharedOwnedOutput {
1683 tx_out: generate_txout(&TestOutput::P2WSH(value)),
1684 local_owned: local_value,
1688 /// Generate a single P2WSH output with shared contribution that is the funding output
1689 fn generate_shared_funding_output(value: u64, local_value: u64) -> Vec<OutputOwned> {
1690 vec![generate_shared_funding_output_one(value, local_value)]
1693 fn generate_fixed_number_of_inputs(count: u16) -> Vec<(TxIn, TransactionU16LenLimited)> {
1694 // Generate transactions with a total `count` number of outputs such that no transaction has a
1695 // serialized length greater than u16::MAX.
1696 let max_outputs_per_prevtx = 1_500;
1697 let mut remaining = count;
1698 let mut inputs: Vec<(TxIn, TransactionU16LenLimited)> = Vec::with_capacity(count as usize);
1700 while remaining > 0 {
1701 let tx_output_count = remaining.min(max_outputs_per_prevtx);
1702 remaining -= tx_output_count;
1704 // Use unique locktime for each tx so outpoints are different across transactions
1705 let tx = generate_tx_with_locktime(
1706 &vec![TestOutput::P2WPKH(1_000_000); tx_output_count as usize],
1707 (1337 + remaining).into(),
1709 let txid = tx.txid();
1711 let mut temp: Vec<(TxIn, TransactionU16LenLimited)> = tx
1717 previous_output: OutPoint { txid, vout: idx as u32 },
1718 script_sig: Default::default(),
1719 sequence: Sequence::ENABLE_RBF_NO_LOCKTIME,
1720 witness: Default::default(),
1722 (input, TransactionU16LenLimited::new(tx.clone()).unwrap())
1726 inputs.append(&mut temp);
1732 fn generate_fixed_number_of_outputs(count: u16) -> Vec<OutputOwned> {
1733 // Set a constant value for each TxOut
1734 generate_outputs(&vec![TestOutput::P2WPKH(1_000_000); count as usize])
1737 fn generate_p2sh_script_pubkey() -> ScriptBuf {
1738 Builder::new().push_opcode(opcodes::OP_TRUE).into_script().to_p2sh()
1741 fn generate_non_witness_output(value: u64) -> OutputOwned {
1742 OutputOwned::Single(TxOut {
1743 value: Amount::from_sat(value),
1744 script_pubkey: generate_p2sh_script_pubkey(),
1749 fn test_interactive_tx_constructor() {
1750 do_test_interactive_tx_constructor(TestSession {
1751 description: "No contributions",
1756 expect_error: Some((AbortReason::MissingFundingOutput, ErrorCulprit::NodeA)),
1757 a_expected_remote_shared_output: None,
1758 b_expected_remote_shared_output: None,
1760 do_test_interactive_tx_constructor(TestSession {
1761 description: "Single contribution, no initiator inputs",
1763 outputs_a: generate_output(&TestOutput::P2WPKH(1_000_000)),
1766 expect_error: Some((AbortReason::OutputsValueExceedsInputsValue, ErrorCulprit::NodeA)),
1767 a_expected_remote_shared_output: None,
1768 b_expected_remote_shared_output: Some((generate_p2wpkh_script_pubkey(), 0)),
1770 do_test_interactive_tx_constructor(TestSession {
1771 description: "Single contribution, no initiator outputs",
1772 inputs_a: generate_inputs(&[TestOutput::P2WPKH(1_000_000)]),
1776 expect_error: Some((AbortReason::MissingFundingOutput, ErrorCulprit::NodeA)),
1777 a_expected_remote_shared_output: None,
1778 b_expected_remote_shared_output: None,
1780 do_test_interactive_tx_constructor(TestSession {
1781 description: "Single contribution, no fees",
1782 inputs_a: generate_inputs(&[TestOutput::P2WPKH(1_000_000)]),
1783 outputs_a: generate_output(&TestOutput::P2WPKH(1_000_000)),
1786 expect_error: Some((AbortReason::InsufficientFees, ErrorCulprit::NodeA)),
1787 a_expected_remote_shared_output: None,
1788 b_expected_remote_shared_output: Some((generate_p2wpkh_script_pubkey(), 0)),
1790 let p2wpkh_fee = fee_for_weight(TEST_FEERATE_SATS_PER_KW, P2WPKH_INPUT_WEIGHT_LOWER_BOUND);
1791 let outputs_fee = fee_for_weight(
1792 TEST_FEERATE_SATS_PER_KW,
1793 get_output_weight(&generate_p2wpkh_script_pubkey()).to_wu(),
1795 let tx_common_fields_fee =
1796 fee_for_weight(TEST_FEERATE_SATS_PER_KW, TX_COMMON_FIELDS_WEIGHT);
1798 let amount_adjusted_with_p2wpkh_fee =
1799 1_000_000 - p2wpkh_fee - outputs_fee - tx_common_fields_fee;
1800 do_test_interactive_tx_constructor(TestSession {
1801 description: "Single contribution, with P2WPKH input, insufficient fees",
1802 inputs_a: generate_inputs(&[TestOutput::P2WPKH(1_000_000)]),
1803 outputs_a: generate_output(&TestOutput::P2WPKH(
1804 amount_adjusted_with_p2wpkh_fee + 1, /* makes fees insuffcient for initiator */
1808 expect_error: Some((AbortReason::InsufficientFees, ErrorCulprit::NodeA)),
1809 a_expected_remote_shared_output: None,
1810 b_expected_remote_shared_output: Some((generate_p2wpkh_script_pubkey(), 0)),
1812 do_test_interactive_tx_constructor(TestSession {
1813 description: "Single contribution with P2WPKH input, sufficient fees",
1814 inputs_a: generate_inputs(&[TestOutput::P2WPKH(1_000_000)]),
1815 outputs_a: generate_output(&TestOutput::P2WPKH(amount_adjusted_with_p2wpkh_fee)),
1819 a_expected_remote_shared_output: None,
1820 b_expected_remote_shared_output: Some((generate_p2wpkh_script_pubkey(), 0)),
1822 let p2wsh_fee = fee_for_weight(TEST_FEERATE_SATS_PER_KW, P2WSH_INPUT_WEIGHT_LOWER_BOUND);
1823 let amount_adjusted_with_p2wsh_fee =
1824 1_000_000 - p2wsh_fee - outputs_fee - tx_common_fields_fee;
1825 do_test_interactive_tx_constructor(TestSession {
1826 description: "Single contribution, with P2WSH input, insufficient fees",
1827 inputs_a: generate_inputs(&[TestOutput::P2WSH(1_000_000)]),
1828 outputs_a: generate_output(&TestOutput::P2WPKH(
1829 amount_adjusted_with_p2wsh_fee + 1, /* makes fees insuffcient for initiator */
1833 expect_error: Some((AbortReason::InsufficientFees, ErrorCulprit::NodeA)),
1834 a_expected_remote_shared_output: None,
1835 b_expected_remote_shared_output: Some((generate_p2wpkh_script_pubkey(), 0)),
1837 do_test_interactive_tx_constructor(TestSession {
1838 description: "Single contribution with P2WSH input, sufficient fees",
1839 inputs_a: generate_inputs(&[TestOutput::P2WSH(1_000_000)]),
1840 outputs_a: generate_output(&TestOutput::P2WPKH(amount_adjusted_with_p2wsh_fee)),
1844 a_expected_remote_shared_output: None,
1845 b_expected_remote_shared_output: Some((generate_p2wpkh_script_pubkey(), 0)),
1847 let p2tr_fee = fee_for_weight(TEST_FEERATE_SATS_PER_KW, P2TR_INPUT_WEIGHT_LOWER_BOUND);
1848 let amount_adjusted_with_p2tr_fee =
1849 1_000_000 - p2tr_fee - outputs_fee - tx_common_fields_fee;
1850 do_test_interactive_tx_constructor(TestSession {
1851 description: "Single contribution, with P2TR input, insufficient fees",
1852 inputs_a: generate_inputs(&[TestOutput::P2TR(1_000_000)]),
1853 outputs_a: generate_output(&TestOutput::P2WPKH(
1854 amount_adjusted_with_p2tr_fee + 1, /* makes fees insuffcient for initiator */
1858 expect_error: Some((AbortReason::InsufficientFees, ErrorCulprit::NodeA)),
1859 a_expected_remote_shared_output: None,
1860 b_expected_remote_shared_output: Some((generate_p2wpkh_script_pubkey(), 0)),
1862 do_test_interactive_tx_constructor(TestSession {
1863 description: "Single contribution with P2TR input, sufficient fees",
1864 inputs_a: generate_inputs(&[TestOutput::P2TR(1_000_000)]),
1865 outputs_a: generate_output(&TestOutput::P2WPKH(amount_adjusted_with_p2tr_fee)),
1869 a_expected_remote_shared_output: None,
1870 b_expected_remote_shared_output: Some((generate_p2wpkh_script_pubkey(), 0)),
1872 do_test_interactive_tx_constructor(TestSession {
1873 description: "Initiator contributes sufficient fees, but non-initiator does not",
1874 inputs_a: generate_inputs(&[TestOutput::P2WPKH(1_000_000)]),
1876 inputs_b: generate_inputs(&[TestOutput::P2WPKH(100_000)]),
1877 outputs_b: generate_output(&TestOutput::P2WPKH(100_000)),
1878 expect_error: Some((AbortReason::InsufficientFees, ErrorCulprit::NodeB)),
1879 a_expected_remote_shared_output: Some((generate_p2wpkh_script_pubkey(), 0)),
1880 b_expected_remote_shared_output: None,
1882 do_test_interactive_tx_constructor(TestSession {
1883 description: "Multi-input-output contributions from both sides",
1884 inputs_a: generate_inputs(&[TestOutput::P2WPKH(1_000_000); 2]),
1886 generate_shared_funding_output_one(1_000_000, 200_000),
1887 generate_output_nonfunding_one(&TestOutput::P2WPKH(200_000)),
1889 inputs_b: generate_inputs(&[
1890 TestOutput::P2WPKH(1_000_000),
1891 TestOutput::P2WPKH(500_000),
1893 outputs_b: vec![generate_output_nonfunding_one(&TestOutput::P2WPKH(400_000))],
1895 a_expected_remote_shared_output: None,
1896 b_expected_remote_shared_output: Some((generate_funding_script_pubkey(), 800_000)),
1899 do_test_interactive_tx_constructor(TestSession {
1900 description: "Prevout from initiator is not a witness program",
1901 inputs_a: generate_inputs(&[TestOutput::P2PKH(1_000_000)]),
1905 expect_error: Some((AbortReason::PrevTxOutInvalid, ErrorCulprit::NodeA)),
1906 a_expected_remote_shared_output: Some((generate_funding_script_pubkey(), 0)),
1907 b_expected_remote_shared_output: Some((generate_funding_script_pubkey(), 0)),
1911 TransactionU16LenLimited::new(generate_tx(&[TestOutput::P2WPKH(1_000_000)])).unwrap();
1912 let invalid_sequence_input = TxIn {
1913 previous_output: OutPoint { txid: tx.as_transaction().txid(), vout: 0 },
1914 ..Default::default()
1916 do_test_interactive_tx_constructor(TestSession {
1917 description: "Invalid input sequence from initiator",
1918 inputs_a: vec![(invalid_sequence_input, tx.clone())],
1919 outputs_a: generate_output(&TestOutput::P2WPKH(1_000_000)),
1922 expect_error: Some((AbortReason::IncorrectInputSequenceValue, ErrorCulprit::NodeA)),
1923 a_expected_remote_shared_output: None,
1924 b_expected_remote_shared_output: Some((generate_p2wpkh_script_pubkey(), 0)),
1926 let duplicate_input = TxIn {
1927 previous_output: OutPoint { txid: tx.as_transaction().txid(), vout: 0 },
1928 sequence: Sequence::ENABLE_RBF_NO_LOCKTIME,
1929 ..Default::default()
1931 do_test_interactive_tx_constructor(TestSession {
1932 description: "Duplicate prevout from initiator",
1933 inputs_a: vec![(duplicate_input.clone(), tx.clone()), (duplicate_input, tx.clone())],
1934 outputs_a: generate_output(&TestOutput::P2WPKH(1_000_000)),
1937 expect_error: Some((AbortReason::PrevTxOutInvalid, ErrorCulprit::NodeB)),
1938 a_expected_remote_shared_output: None,
1939 b_expected_remote_shared_output: Some((generate_p2wpkh_script_pubkey(), 0)),
1941 // Non-initiator uses same prevout as initiator.
1942 let duplicate_input = TxIn {
1943 previous_output: OutPoint { txid: tx.as_transaction().txid(), vout: 0 },
1944 sequence: Sequence::ENABLE_RBF_NO_LOCKTIME,
1945 ..Default::default()
1947 do_test_interactive_tx_constructor(TestSession {
1948 description: "Non-initiator uses same prevout as initiator",
1949 inputs_a: vec![(duplicate_input.clone(), tx.clone())],
1950 outputs_a: generate_shared_funding_output(1_000_000, 905_000),
1951 inputs_b: vec![(duplicate_input.clone(), tx.clone())],
1953 expect_error: Some((AbortReason::PrevTxOutInvalid, ErrorCulprit::NodeA)),
1954 a_expected_remote_shared_output: None,
1955 b_expected_remote_shared_output: Some((generate_funding_script_pubkey(), 95_000)),
1957 let duplicate_input = TxIn {
1958 previous_output: OutPoint { txid: tx.as_transaction().txid(), vout: 0 },
1959 sequence: Sequence::ENABLE_RBF_NO_LOCKTIME,
1960 ..Default::default()
1962 do_test_interactive_tx_constructor(TestSession {
1963 description: "Non-initiator uses same prevout as initiator",
1964 inputs_a: vec![(duplicate_input.clone(), tx.clone())],
1965 outputs_a: generate_output(&TestOutput::P2WPKH(1_000_000)),
1966 inputs_b: vec![(duplicate_input.clone(), tx.clone())],
1968 expect_error: Some((AbortReason::PrevTxOutInvalid, ErrorCulprit::NodeA)),
1969 a_expected_remote_shared_output: None,
1970 b_expected_remote_shared_output: Some((generate_p2wpkh_script_pubkey(), 0)),
1972 do_test_interactive_tx_constructor(TestSession {
1973 description: "Initiator sends too many TxAddInputs",
1974 inputs_a: generate_fixed_number_of_inputs(MAX_RECEIVED_TX_ADD_INPUT_COUNT + 1),
1978 expect_error: Some((AbortReason::ReceivedTooManyTxAddInputs, ErrorCulprit::NodeA)),
1979 a_expected_remote_shared_output: Some((generate_funding_script_pubkey(), 0)),
1980 b_expected_remote_shared_output: Some((generate_funding_script_pubkey(), 0)),
1982 do_test_interactive_tx_constructor_with_entropy_source(
1984 // We use a deliberately bad entropy source, `DuplicateEntropySource` to simulate this.
1985 description: "Attempt to queue up two inputs with duplicate serial ids",
1986 inputs_a: generate_fixed_number_of_inputs(2),
1990 expect_error: Some((AbortReason::DuplicateSerialId, ErrorCulprit::NodeA)),
1991 a_expected_remote_shared_output: Some((generate_funding_script_pubkey(), 0)),
1992 b_expected_remote_shared_output: Some((generate_funding_script_pubkey(), 0)),
1994 &DuplicateEntropySource,
1996 do_test_interactive_tx_constructor(TestSession {
1997 description: "Initiator sends too many TxAddOutputs",
1999 outputs_a: generate_fixed_number_of_outputs(MAX_RECEIVED_TX_ADD_OUTPUT_COUNT + 1),
2002 expect_error: Some((AbortReason::ReceivedTooManyTxAddOutputs, ErrorCulprit::NodeA)),
2003 a_expected_remote_shared_output: Some((generate_funding_script_pubkey(), 0)),
2004 b_expected_remote_shared_output: Some((generate_funding_script_pubkey(), 0)),
2006 do_test_interactive_tx_constructor(TestSession {
2007 description: "Initiator sends an output below dust value",
2009 outputs_a: generate_funding_output(
2010 generate_p2wsh_script_pubkey().dust_value().to_sat() - 1,
2014 expect_error: Some((AbortReason::BelowDustLimit, ErrorCulprit::NodeA)),
2015 a_expected_remote_shared_output: None,
2016 b_expected_remote_shared_output: Some((generate_funding_script_pubkey(), 0)),
2018 do_test_interactive_tx_constructor(TestSession {
2019 description: "Initiator sends an output above maximum sats allowed",
2021 outputs_a: generate_output(&TestOutput::P2WPKH(TOTAL_BITCOIN_SUPPLY_SATOSHIS + 1)),
2024 expect_error: Some((AbortReason::ExceededMaximumSatsAllowed, ErrorCulprit::NodeA)),
2025 a_expected_remote_shared_output: None,
2026 b_expected_remote_shared_output: Some((generate_funding_script_pubkey(), 0)),
2028 do_test_interactive_tx_constructor(TestSession {
2029 description: "Initiator sends an output without a witness program",
2031 outputs_a: vec![generate_non_witness_output(1_000_000)],
2034 expect_error: Some((AbortReason::InvalidOutputScript, ErrorCulprit::NodeA)),
2035 a_expected_remote_shared_output: Some((generate_funding_script_pubkey(), 0)),
2036 b_expected_remote_shared_output: Some((generate_funding_script_pubkey(), 0)),
2038 do_test_interactive_tx_constructor_with_entropy_source(
2040 // We use a deliberately bad entropy source, `DuplicateEntropySource` to simulate this.
2041 description: "Attempt to queue up two outputs with duplicate serial ids",
2043 outputs_a: generate_fixed_number_of_outputs(2),
2046 expect_error: Some((AbortReason::DuplicateSerialId, ErrorCulprit::NodeA)),
2047 a_expected_remote_shared_output: Some((generate_funding_script_pubkey(), 0)),
2048 b_expected_remote_shared_output: Some((generate_funding_script_pubkey(), 0)),
2050 &DuplicateEntropySource,
2053 do_test_interactive_tx_constructor(TestSession {
2054 description: "Peer contributed more output value than inputs",
2055 inputs_a: generate_inputs(&[TestOutput::P2WPKH(100_000)]),
2056 outputs_a: generate_output(&TestOutput::P2WPKH(1_000_000)),
2059 expect_error: Some((AbortReason::OutputsValueExceedsInputsValue, ErrorCulprit::NodeA)),
2060 a_expected_remote_shared_output: None,
2061 b_expected_remote_shared_output: Some((generate_funding_script_pubkey(), 0)),
2064 do_test_interactive_tx_constructor(TestSession {
2065 description: "Peer contributed more than allowed number of inputs",
2066 inputs_a: generate_fixed_number_of_inputs(MAX_INPUTS_OUTPUTS_COUNT as u16 + 1),
2070 expect_error: Some((
2071 AbortReason::ExceededNumberOfInputsOrOutputs,
2072 ErrorCulprit::Indeterminate,
2074 a_expected_remote_shared_output: Some((generate_funding_script_pubkey(), 0)),
2075 b_expected_remote_shared_output: Some((generate_funding_script_pubkey(), 0)),
2077 do_test_interactive_tx_constructor(TestSession {
2078 description: "Peer contributed more than allowed number of outputs",
2079 inputs_a: generate_inputs(&[TestOutput::P2WPKH(TOTAL_BITCOIN_SUPPLY_SATOSHIS)]),
2080 outputs_a: generate_fixed_number_of_outputs(MAX_INPUTS_OUTPUTS_COUNT as u16 + 1),
2083 expect_error: Some((
2084 AbortReason::ExceededNumberOfInputsOrOutputs,
2085 ErrorCulprit::Indeterminate,
2087 a_expected_remote_shared_output: Some((generate_funding_script_pubkey(), 0)),
2088 b_expected_remote_shared_output: Some((generate_funding_script_pubkey(), 0)),
2091 // Adding multiple outputs to the funding output pubkey is an error
2092 do_test_interactive_tx_constructor(TestSession {
2093 description: "Adding two outputs to the funding output pubkey",
2094 inputs_a: generate_inputs(&[TestOutput::P2WPKH(1_000_000)]),
2095 outputs_a: generate_funding_output(100_000),
2096 inputs_b: generate_inputs(&[TestOutput::P2WPKH(1_001_000)]),
2097 outputs_b: generate_funding_output(100_000),
2098 expect_error: Some((AbortReason::DuplicateFundingOutput, ErrorCulprit::NodeA)),
2099 a_expected_remote_shared_output: None,
2100 b_expected_remote_shared_output: None,
2103 // We add the funding output, but we contribute a little
2104 do_test_interactive_tx_constructor(TestSession {
2105 description: "Funding output by us, small contribution",
2106 inputs_a: generate_inputs(&[TestOutput::P2WPKH(12_000)]),
2107 outputs_a: generate_shared_funding_output(1_000_000, 10_000),
2108 inputs_b: generate_inputs(&[TestOutput::P2WPKH(992_000)]),
2111 a_expected_remote_shared_output: None,
2112 b_expected_remote_shared_output: Some((generate_funding_script_pubkey(), 990_000)),
2115 // They add the funding output, and we contribute a little
2116 do_test_interactive_tx_constructor(TestSession {
2117 description: "Funding output by them, small contribution",
2118 inputs_a: generate_inputs(&[TestOutput::P2WPKH(12_000)]),
2120 inputs_b: generate_inputs(&[TestOutput::P2WPKH(992_000)]),
2121 outputs_b: generate_shared_funding_output(1_000_000, 990_000),
2123 a_expected_remote_shared_output: Some((generate_funding_script_pubkey(), 10_000)),
2124 b_expected_remote_shared_output: None,
2127 // We add the funding output, and we contribute most
2128 do_test_interactive_tx_constructor(TestSession {
2129 description: "Funding output by us, large contribution",
2130 inputs_a: generate_inputs(&[TestOutput::P2WPKH(992_000)]),
2131 outputs_a: generate_shared_funding_output(1_000_000, 990_000),
2132 inputs_b: generate_inputs(&[TestOutput::P2WPKH(12_000)]),
2135 a_expected_remote_shared_output: None,
2136 b_expected_remote_shared_output: Some((generate_funding_script_pubkey(), 10_000)),
2139 // They add the funding output, but we contribute most
2140 do_test_interactive_tx_constructor(TestSession {
2141 description: "Funding output by them, large contribution",
2142 inputs_a: generate_inputs(&[TestOutput::P2WPKH(992_000)]),
2144 inputs_b: generate_inputs(&[TestOutput::P2WPKH(12_000)]),
2145 outputs_b: generate_shared_funding_output(1_000_000, 10_000),
2147 a_expected_remote_shared_output: Some((generate_funding_script_pubkey(), 990_000)),
2148 b_expected_remote_shared_output: None,
2151 // During a splice-out, with peer providing more output value than input value
2152 // but still pays enough fees due to their to_remote_value_satoshis portion in
2153 // the shared input.
2154 do_test_interactive_tx_constructor(TestSession {
2155 description: "Splice out with sufficient initiator balance",
2156 inputs_a: generate_inputs(&[TestOutput::P2WPKH(100_000), TestOutput::P2WPKH(50_000)]),
2157 outputs_a: generate_funding_output(120_000),
2158 inputs_b: generate_inputs(&[TestOutput::P2WPKH(50_000)]),
2161 a_expected_remote_shared_output: None,
2162 b_expected_remote_shared_output: Some((generate_funding_script_pubkey(), 0)),
2165 // During a splice-out, with peer providing more output value than input value
2166 // and the to_remote_value_satoshis portion in
2167 // the shared input cannot cover fees
2168 do_test_interactive_tx_constructor(TestSession {
2169 description: "Splice out with insufficient initiator balance",
2170 inputs_a: generate_inputs(&[TestOutput::P2WPKH(100_000), TestOutput::P2WPKH(15_000)]),
2171 outputs_a: generate_funding_output(120_000),
2172 inputs_b: generate_inputs(&[TestOutput::P2WPKH(85_000)]),
2174 expect_error: Some((AbortReason::OutputsValueExceedsInputsValue, ErrorCulprit::NodeA)),
2175 a_expected_remote_shared_output: None,
2176 b_expected_remote_shared_output: Some((generate_funding_script_pubkey(), 0)),
2179 // The actual funding output value is lower than the intended local contribution by the same node
2180 do_test_interactive_tx_constructor(TestSession {
2181 description: "Splice in, invalid intended local contribution",
2182 inputs_a: generate_inputs(&[TestOutput::P2WPKH(100_000), TestOutput::P2WPKH(15_000)]),
2183 outputs_a: generate_shared_funding_output(100_000, 120_000), // local value is higher than the output value
2184 inputs_b: generate_inputs(&[TestOutput::P2WPKH(85_000)]),
2186 expect_error: Some((AbortReason::InvalidLowFundingOutputValue, ErrorCulprit::NodeA)),
2187 a_expected_remote_shared_output: None,
2188 b_expected_remote_shared_output: Some((generate_funding_script_pubkey(), 20_000)),
2191 // The actual funding output value is lower than the intended local contribution of the other node
2192 do_test_interactive_tx_constructor(TestSession {
2193 description: "Splice in, invalid intended local contribution",
2194 inputs_a: generate_inputs(&[TestOutput::P2WPKH(100_000), TestOutput::P2WPKH(15_000)]),
2196 inputs_b: generate_inputs(&[TestOutput::P2WPKH(85_000)]),
2197 outputs_b: generate_funding_output(100_000),
2198 // The error is caused by NodeA, it occurs when nodeA prepares the message to be sent to NodeB, that's why here it shows up as NodeB
2199 expect_error: Some((AbortReason::InvalidLowFundingOutputValue, ErrorCulprit::NodeB)),
2200 a_expected_remote_shared_output: Some((generate_funding_script_pubkey(), 120_000)), // this is higher than the actual output value
2201 b_expected_remote_shared_output: None,
2206 fn test_generate_local_serial_id() {
2207 let entropy_source = TestEntropySource(AtomicCounter::new());
2209 // Initiators should have even serial id, non-initiators should have odd serial id.
2210 assert_eq!(generate_holder_serial_id(&&entropy_source, true) % 2, 0);
2211 assert_eq!(generate_holder_serial_id(&&entropy_source, false) % 2, 1)