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(),
1209 shared_input_txid: None,
1211 do_state_transition!(self, sent_tx_add_input, &msg)?;
1212 Ok(InteractiveTxMessageSend::TxAddInput(msg))
1213 } else if let Some((serial_id, output)) = self.outputs_to_contribute.pop() {
1214 let msg = msgs::TxAddOutput {
1215 channel_id: self.channel_id,
1217 sats: output.tx_out().value.to_sat(),
1218 script: output.tx_out().script_pubkey.clone(),
1220 do_state_transition!(self, sent_tx_add_output, &msg)?;
1221 Ok(InteractiveTxMessageSend::TxAddOutput(msg))
1223 let msg = msgs::TxComplete { channel_id: self.channel_id };
1224 do_state_transition!(self, sent_tx_complete, &msg)?;
1225 Ok(InteractiveTxMessageSend::TxComplete(msg))
1229 pub fn handle_tx_add_input(
1230 &mut self, msg: &msgs::TxAddInput,
1231 ) -> Result<InteractiveTxMessageSend, AbortReason> {
1232 do_state_transition!(self, received_tx_add_input, msg)?;
1233 self.maybe_send_message()
1236 pub fn handle_tx_remove_input(
1237 &mut self, msg: &msgs::TxRemoveInput,
1238 ) -> Result<InteractiveTxMessageSend, AbortReason> {
1239 do_state_transition!(self, received_tx_remove_input, msg)?;
1240 self.maybe_send_message()
1243 pub fn handle_tx_add_output(
1244 &mut self, msg: &msgs::TxAddOutput,
1245 ) -> Result<InteractiveTxMessageSend, AbortReason> {
1246 do_state_transition!(self, received_tx_add_output, msg)?;
1247 self.maybe_send_message()
1250 pub fn handle_tx_remove_output(
1251 &mut self, msg: &msgs::TxRemoveOutput,
1252 ) -> Result<InteractiveTxMessageSend, AbortReason> {
1253 do_state_transition!(self, received_tx_remove_output, msg)?;
1254 self.maybe_send_message()
1257 pub fn handle_tx_complete(
1258 &mut self, msg: &msgs::TxComplete,
1259 ) -> Result<HandleTxCompleteValue, AbortReason> {
1260 do_state_transition!(self, received_tx_complete, msg)?;
1261 match &self.state_machine {
1262 StateMachine::ReceivedTxComplete(_) => {
1263 let msg_send = self.maybe_send_message()?;
1264 match &self.state_machine {
1265 StateMachine::NegotiationComplete(s) => {
1266 Ok(HandleTxCompleteValue::SendTxComplete(msg_send, s.0.clone()))
1268 StateMachine::SentChangeMsg(_) => {
1269 Ok(HandleTxCompleteValue::SendTxMessage(msg_send))
1270 }, // We either had an input or output to contribute.
1272 debug_assert!(false, "We cannot transition to any other states after receiving `tx_complete` and responding");
1273 Err(AbortReason::InvalidStateTransition)
1277 StateMachine::NegotiationComplete(s) => {
1278 Ok(HandleTxCompleteValue::NegotiationComplete(s.0.clone()))
1283 "We cannot transition to any other states after receiving `tx_complete`"
1285 Err(AbortReason::InvalidStateTransition)
1293 use crate::chain::chaininterface::{fee_for_weight, FEERATE_FLOOR_SATS_PER_KW};
1294 use crate::ln::channel::TOTAL_BITCOIN_SUPPLY_SATOSHIS;
1295 use crate::ln::interactivetxs::{
1296 generate_holder_serial_id, AbortReason, HandleTxCompleteValue, InteractiveTxConstructor,
1297 InteractiveTxMessageSend, MAX_INPUTS_OUTPUTS_COUNT, MAX_RECEIVED_TX_ADD_INPUT_COUNT,
1298 MAX_RECEIVED_TX_ADD_OUTPUT_COUNT,
1300 use crate::ln::types::ChannelId;
1301 use crate::sign::EntropySource;
1302 use crate::util::atomic_counter::AtomicCounter;
1303 use crate::util::ser::TransactionU16LenLimited;
1304 use bitcoin::absolute::LockTime as AbsoluteLockTime;
1305 use bitcoin::amount::Amount;
1306 use bitcoin::blockdata::opcodes;
1307 use bitcoin::blockdata::script::Builder;
1308 use bitcoin::hashes::Hash;
1309 use bitcoin::key::UntweakedPublicKey;
1310 use bitcoin::secp256k1::{Keypair, Secp256k1};
1311 use bitcoin::transaction::Version;
1313 OutPoint, PubkeyHash, ScriptBuf, Sequence, Transaction, TxIn, TxOut, WPubkeyHash,
1315 use core::ops::Deref;
1318 get_output_weight, AddingRole, OutputOwned, SharedOwnedOutput,
1319 P2TR_INPUT_WEIGHT_LOWER_BOUND, P2WPKH_INPUT_WEIGHT_LOWER_BOUND,
1320 P2WSH_INPUT_WEIGHT_LOWER_BOUND, TX_COMMON_FIELDS_WEIGHT,
1323 const TEST_FEERATE_SATS_PER_KW: u32 = FEERATE_FLOOR_SATS_PER_KW * 10;
1325 // A simple entropy source that works based on an atomic counter.
1326 struct TestEntropySource(AtomicCounter);
1327 impl EntropySource for TestEntropySource {
1328 fn get_secure_random_bytes(&self) -> [u8; 32] {
1329 let mut res = [0u8; 32];
1330 let increment = self.0.get_increment();
1332 // Rotate the increment value by 'i' bits to the right, to avoid clashes
1333 // when `generate_local_serial_id` does a parity flip on consecutive calls for the
1335 let rotated_increment = increment.rotate_right(i as u32);
1336 res[i] = (rotated_increment & 0xff) as u8;
1342 // An entropy source that deliberately returns you the same seed every time. We use this
1343 // to test if the constructor would catch inputs/outputs that are attempting to be added
1344 // with duplicate serial ids.
1345 struct DuplicateEntropySource;
1346 impl EntropySource for DuplicateEntropySource {
1347 fn get_secure_random_bytes(&self) -> [u8; 32] {
1348 let mut res = [0u8; 32];
1350 res[0..8].copy_from_slice(&count.to_be_bytes());
1355 #[derive(Debug, PartialEq, Eq)]
1359 // Some error values are only checked at the end of the negotiation and are not easy to attribute
1360 // to a particular party. Both parties would indicate an `AbortReason` in this case.
1361 // e.g. Exceeded max inputs and outputs after negotiation.
1365 struct TestSession {
1366 description: &'static str,
1367 inputs_a: Vec<(TxIn, TransactionU16LenLimited)>,
1368 outputs_a: Vec<OutputOwned>,
1369 inputs_b: Vec<(TxIn, TransactionU16LenLimited)>,
1370 outputs_b: Vec<OutputOwned>,
1371 expect_error: Option<(AbortReason, ErrorCulprit)>,
1372 /// A node adds no shared output, but expects the peer to add one, with the specific script pubkey, and local contribution
1373 a_expected_remote_shared_output: Option<(ScriptBuf, u64)>,
1374 /// B node adds no shared output, but expects the peer to add one, with the specific script pubkey, and local contribution
1375 b_expected_remote_shared_output: Option<(ScriptBuf, u64)>,
1378 fn do_test_interactive_tx_constructor(session: TestSession) {
1379 let entropy_source = TestEntropySource(AtomicCounter::new());
1380 do_test_interactive_tx_constructor_internal(session, &&entropy_source);
1383 fn do_test_interactive_tx_constructor_with_entropy_source<ES: Deref>(
1384 session: TestSession, entropy_source: ES,
1386 ES::Target: EntropySource,
1388 do_test_interactive_tx_constructor_internal(session, &entropy_source);
1391 fn do_test_interactive_tx_constructor_internal<ES: Deref>(
1392 session: TestSession, entropy_source: &ES,
1394 ES::Target: EntropySource,
1396 let channel_id = ChannelId(entropy_source.get_secure_random_bytes());
1397 let tx_locktime = AbsoluteLockTime::from_height(1337).unwrap();
1399 // funding output sanity check
1400 let shared_outputs_by_a: Vec<_> =
1401 session.outputs_a.iter().filter(|o| o.is_shared()).collect();
1402 if shared_outputs_by_a.len() > 1 {
1403 println!("Test warning: Expected at most one shared output. NodeA");
1405 let shared_output_by_a = if shared_outputs_by_a.len() >= 1 {
1406 Some(shared_outputs_by_a[0].value())
1410 let shared_outputs_by_b: Vec<_> =
1411 session.outputs_b.iter().filter(|o| o.is_shared()).collect();
1412 if shared_outputs_by_b.len() > 1 {
1413 println!("Test warning: Expected at most one shared output. NodeB");
1415 let shared_output_by_b = if shared_outputs_by_b.len() >= 1 {
1416 Some(shared_outputs_by_b[0].value())
1420 if session.a_expected_remote_shared_output.is_some()
1421 || session.b_expected_remote_shared_output.is_some()
1423 let expected_by_a = if let Some(a_expected_remote_shared_output) =
1424 &session.a_expected_remote_shared_output
1426 a_expected_remote_shared_output.1
1428 if shared_outputs_by_a.len() >= 1 {
1429 shared_outputs_by_a[0].local_value(AddingRole::Local)
1434 let expected_by_b = if let Some(b_expected_remote_shared_output) =
1435 &session.b_expected_remote_shared_output
1437 b_expected_remote_shared_output.1
1439 if shared_outputs_by_b.len() >= 1 {
1440 shared_outputs_by_b[0].local_value(AddingRole::Local)
1446 let expected_sum = expected_by_a + expected_by_b;
1447 let actual_shared_output =
1448 shared_output_by_a.unwrap_or(shared_output_by_b.unwrap_or(0));
1449 if expected_sum != actual_shared_output {
1450 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));
1454 let (mut constructor_a, first_message_a) = match InteractiveTxConstructor::new(
1457 TEST_FEERATE_SATS_PER_KW,
1461 session.outputs_a.iter().map(|o| o.clone()).collect(),
1462 session.a_expected_remote_shared_output,
1465 Err(abort_reason) => {
1467 Some((abort_reason, ErrorCulprit::NodeA)),
1468 session.expect_error,
1475 let (mut constructor_b, first_message_b) = match InteractiveTxConstructor::new(
1478 TEST_FEERATE_SATS_PER_KW,
1482 session.outputs_b.iter().map(|o| o.clone()).collect(),
1483 session.b_expected_remote_shared_output,
1486 Err(abort_reason) => {
1488 Some((abort_reason, ErrorCulprit::NodeB)),
1489 session.expect_error,
1497 let handle_message_send =
1498 |msg: InteractiveTxMessageSend, for_constructor: &mut InteractiveTxConstructor| {
1500 InteractiveTxMessageSend::TxAddInput(msg) => for_constructor
1501 .handle_tx_add_input(&msg)
1502 .map(|msg_send| (Some(msg_send), None)),
1503 InteractiveTxMessageSend::TxAddOutput(msg) => for_constructor
1504 .handle_tx_add_output(&msg)
1505 .map(|msg_send| (Some(msg_send), None)),
1506 InteractiveTxMessageSend::TxComplete(msg) => {
1507 for_constructor.handle_tx_complete(&msg).map(|value| match value {
1508 HandleTxCompleteValue::SendTxMessage(msg_send) => {
1509 (Some(msg_send), None)
1511 HandleTxCompleteValue::SendTxComplete(msg_send, tx) => {
1512 (Some(msg_send), Some(tx))
1514 HandleTxCompleteValue::NegotiationComplete(tx) => (None, Some(tx)),
1520 assert!(first_message_b.is_none());
1521 let mut message_send_a = first_message_a;
1522 let mut message_send_b = None;
1523 let mut final_tx_a = None;
1524 let mut final_tx_b = None;
1525 while final_tx_a.is_none() || final_tx_b.is_none() {
1526 if let Some(message_send_a) = message_send_a.take() {
1527 match handle_message_send(message_send_a, &mut constructor_b) {
1528 Ok((msg_send, final_tx)) => {
1529 message_send_b = msg_send;
1530 final_tx_b = final_tx;
1532 Err(abort_reason) => {
1533 let error_culprit = match abort_reason {
1534 AbortReason::ExceededNumberOfInputsOrOutputs => {
1535 ErrorCulprit::Indeterminate
1537 _ => ErrorCulprit::NodeA,
1540 Some((abort_reason, error_culprit)),
1541 session.expect_error,
1545 assert!(message_send_b.is_none(), "Test: {}", session.description);
1550 if let Some(message_send_b) = message_send_b.take() {
1551 match handle_message_send(message_send_b, &mut constructor_a) {
1552 Ok((msg_send, final_tx)) => {
1553 message_send_a = msg_send;
1554 final_tx_a = final_tx;
1556 Err(abort_reason) => {
1557 let error_culprit = match abort_reason {
1558 AbortReason::ExceededNumberOfInputsOrOutputs => {
1559 ErrorCulprit::Indeterminate
1561 _ => ErrorCulprit::NodeB,
1564 Some((abort_reason, error_culprit)),
1565 session.expect_error,
1569 assert!(message_send_a.is_none(), "Test: {}", session.description);
1575 assert!(message_send_a.is_none());
1576 assert!(message_send_b.is_none());
1577 assert_eq!(final_tx_a.unwrap().into_unsigned_tx(), final_tx_b.unwrap().into_unsigned_tx());
1579 session.expect_error.is_none(),
1580 "Missing expected error {:?}, Test: {}",
1581 session.expect_error,
1582 session.description,
1586 #[derive(Debug, Clone, Copy)]
1589 /// P2WSH, but with the specific script used for the funding output
1592 // Non-witness type to test rejection.
1596 fn generate_tx(outputs: &[TestOutput]) -> Transaction {
1597 generate_tx_with_locktime(outputs, 1337)
1600 fn generate_txout(output: &TestOutput) -> TxOut {
1601 let secp_ctx = Secp256k1::new();
1602 let (value, script_pubkey) = match output {
1603 TestOutput::P2WPKH(value) => (*value, generate_p2wpkh_script_pubkey()),
1604 TestOutput::P2WSH(value) => (*value, generate_funding_script_pubkey()),
1605 TestOutput::P2TR(value) => (
1607 ScriptBuf::new_p2tr(
1609 UntweakedPublicKey::from_keypair(
1610 &Keypair::from_seckey_slice(&secp_ctx, &[3; 32]).unwrap(),
1616 TestOutput::P2PKH(value) => {
1617 (*value, ScriptBuf::new_p2pkh(&PubkeyHash::from_slice(&[4; 20]).unwrap()))
1621 TxOut { value: Amount::from_sat(value), script_pubkey }
1624 fn generate_tx_with_locktime(outputs: &[TestOutput], locktime: u32) -> Transaction {
1626 version: Version::TWO,
1627 lock_time: AbsoluteLockTime::from_height(locktime).unwrap(),
1628 input: vec![TxIn { ..Default::default() }],
1629 output: outputs.iter().map(generate_txout).collect(),
1633 fn generate_inputs(outputs: &[TestOutput]) -> Vec<(TxIn, TransactionU16LenLimited)> {
1634 let tx = generate_tx(outputs);
1635 let txid = tx.txid();
1641 previous_output: OutPoint { txid, vout: idx as u32 },
1642 script_sig: Default::default(),
1643 sequence: Sequence::ENABLE_RBF_NO_LOCKTIME,
1644 witness: Default::default(),
1646 (input, TransactionU16LenLimited::new(tx.clone()).unwrap())
1651 fn generate_p2wsh_script_pubkey() -> ScriptBuf {
1652 Builder::new().push_opcode(opcodes::OP_TRUE).into_script().to_p2wsh()
1655 fn generate_p2wpkh_script_pubkey() -> ScriptBuf {
1656 ScriptBuf::new_p2wpkh(&WPubkeyHash::from_slice(&[1; 20]).unwrap())
1659 fn generate_funding_script_pubkey() -> ScriptBuf {
1660 Builder::new().push_int(33).into_script().to_p2wsh()
1663 fn generate_output_nonfunding_one(output: &TestOutput) -> OutputOwned {
1664 OutputOwned::Single(generate_txout(output))
1667 fn generate_outputs(outputs: &[TestOutput]) -> Vec<OutputOwned> {
1668 outputs.iter().map(|o| generate_output_nonfunding_one(o)).collect()
1671 /// Generate a single output that is the funding output
1672 fn generate_output(output: &TestOutput) -> Vec<OutputOwned> {
1673 vec![OutputOwned::SharedControlFullyOwned(generate_txout(output))]
1676 /// Generate a single P2WSH output that is the funding output
1677 fn generate_funding_output(value: u64) -> Vec<OutputOwned> {
1678 generate_output(&TestOutput::P2WSH(value))
1681 /// Generate a single P2WSH output with shared contribution that is the funding output
1682 fn generate_shared_funding_output_one(value: u64, local_value: u64) -> OutputOwned {
1683 OutputOwned::Shared(SharedOwnedOutput {
1684 tx_out: generate_txout(&TestOutput::P2WSH(value)),
1685 local_owned: local_value,
1689 /// Generate a single P2WSH output with shared contribution that is the funding output
1690 fn generate_shared_funding_output(value: u64, local_value: u64) -> Vec<OutputOwned> {
1691 vec![generate_shared_funding_output_one(value, local_value)]
1694 fn generate_fixed_number_of_inputs(count: u16) -> Vec<(TxIn, TransactionU16LenLimited)> {
1695 // Generate transactions with a total `count` number of outputs such that no transaction has a
1696 // serialized length greater than u16::MAX.
1697 let max_outputs_per_prevtx = 1_500;
1698 let mut remaining = count;
1699 let mut inputs: Vec<(TxIn, TransactionU16LenLimited)> = Vec::with_capacity(count as usize);
1701 while remaining > 0 {
1702 let tx_output_count = remaining.min(max_outputs_per_prevtx);
1703 remaining -= tx_output_count;
1705 // Use unique locktime for each tx so outpoints are different across transactions
1706 let tx = generate_tx_with_locktime(
1707 &vec![TestOutput::P2WPKH(1_000_000); tx_output_count as usize],
1708 (1337 + remaining).into(),
1710 let txid = tx.txid();
1712 let mut temp: Vec<(TxIn, TransactionU16LenLimited)> = tx
1718 previous_output: OutPoint { txid, vout: idx as u32 },
1719 script_sig: Default::default(),
1720 sequence: Sequence::ENABLE_RBF_NO_LOCKTIME,
1721 witness: Default::default(),
1723 (input, TransactionU16LenLimited::new(tx.clone()).unwrap())
1727 inputs.append(&mut temp);
1733 fn generate_fixed_number_of_outputs(count: u16) -> Vec<OutputOwned> {
1734 // Set a constant value for each TxOut
1735 generate_outputs(&vec![TestOutput::P2WPKH(1_000_000); count as usize])
1738 fn generate_p2sh_script_pubkey() -> ScriptBuf {
1739 Builder::new().push_opcode(opcodes::OP_TRUE).into_script().to_p2sh()
1742 fn generate_non_witness_output(value: u64) -> OutputOwned {
1743 OutputOwned::Single(TxOut {
1744 value: Amount::from_sat(value),
1745 script_pubkey: generate_p2sh_script_pubkey(),
1750 fn test_interactive_tx_constructor() {
1751 do_test_interactive_tx_constructor(TestSession {
1752 description: "No contributions",
1757 expect_error: Some((AbortReason::MissingFundingOutput, ErrorCulprit::NodeA)),
1758 a_expected_remote_shared_output: None,
1759 b_expected_remote_shared_output: None,
1761 do_test_interactive_tx_constructor(TestSession {
1762 description: "Single contribution, no initiator inputs",
1764 outputs_a: generate_output(&TestOutput::P2WPKH(1_000_000)),
1767 expect_error: Some((AbortReason::OutputsValueExceedsInputsValue, ErrorCulprit::NodeA)),
1768 a_expected_remote_shared_output: None,
1769 b_expected_remote_shared_output: Some((generate_p2wpkh_script_pubkey(), 0)),
1771 do_test_interactive_tx_constructor(TestSession {
1772 description: "Single contribution, no initiator outputs",
1773 inputs_a: generate_inputs(&[TestOutput::P2WPKH(1_000_000)]),
1777 expect_error: Some((AbortReason::MissingFundingOutput, ErrorCulprit::NodeA)),
1778 a_expected_remote_shared_output: None,
1779 b_expected_remote_shared_output: None,
1781 do_test_interactive_tx_constructor(TestSession {
1782 description: "Single contribution, no fees",
1783 inputs_a: generate_inputs(&[TestOutput::P2WPKH(1_000_000)]),
1784 outputs_a: generate_output(&TestOutput::P2WPKH(1_000_000)),
1787 expect_error: Some((AbortReason::InsufficientFees, ErrorCulprit::NodeA)),
1788 a_expected_remote_shared_output: None,
1789 b_expected_remote_shared_output: Some((generate_p2wpkh_script_pubkey(), 0)),
1791 let p2wpkh_fee = fee_for_weight(TEST_FEERATE_SATS_PER_KW, P2WPKH_INPUT_WEIGHT_LOWER_BOUND);
1792 let outputs_fee = fee_for_weight(
1793 TEST_FEERATE_SATS_PER_KW,
1794 get_output_weight(&generate_p2wpkh_script_pubkey()).to_wu(),
1796 let tx_common_fields_fee =
1797 fee_for_weight(TEST_FEERATE_SATS_PER_KW, TX_COMMON_FIELDS_WEIGHT);
1799 let amount_adjusted_with_p2wpkh_fee =
1800 1_000_000 - p2wpkh_fee - outputs_fee - tx_common_fields_fee;
1801 do_test_interactive_tx_constructor(TestSession {
1802 description: "Single contribution, with P2WPKH input, insufficient fees",
1803 inputs_a: generate_inputs(&[TestOutput::P2WPKH(1_000_000)]),
1804 outputs_a: generate_output(&TestOutput::P2WPKH(
1805 amount_adjusted_with_p2wpkh_fee + 1, /* makes fees insuffcient for initiator */
1809 expect_error: Some((AbortReason::InsufficientFees, ErrorCulprit::NodeA)),
1810 a_expected_remote_shared_output: None,
1811 b_expected_remote_shared_output: Some((generate_p2wpkh_script_pubkey(), 0)),
1813 do_test_interactive_tx_constructor(TestSession {
1814 description: "Single contribution with P2WPKH input, sufficient fees",
1815 inputs_a: generate_inputs(&[TestOutput::P2WPKH(1_000_000)]),
1816 outputs_a: generate_output(&TestOutput::P2WPKH(amount_adjusted_with_p2wpkh_fee)),
1820 a_expected_remote_shared_output: None,
1821 b_expected_remote_shared_output: Some((generate_p2wpkh_script_pubkey(), 0)),
1823 let p2wsh_fee = fee_for_weight(TEST_FEERATE_SATS_PER_KW, P2WSH_INPUT_WEIGHT_LOWER_BOUND);
1824 let amount_adjusted_with_p2wsh_fee =
1825 1_000_000 - p2wsh_fee - outputs_fee - tx_common_fields_fee;
1826 do_test_interactive_tx_constructor(TestSession {
1827 description: "Single contribution, with P2WSH input, insufficient fees",
1828 inputs_a: generate_inputs(&[TestOutput::P2WSH(1_000_000)]),
1829 outputs_a: generate_output(&TestOutput::P2WPKH(
1830 amount_adjusted_with_p2wsh_fee + 1, /* makes fees insuffcient for initiator */
1834 expect_error: Some((AbortReason::InsufficientFees, ErrorCulprit::NodeA)),
1835 a_expected_remote_shared_output: None,
1836 b_expected_remote_shared_output: Some((generate_p2wpkh_script_pubkey(), 0)),
1838 do_test_interactive_tx_constructor(TestSession {
1839 description: "Single contribution with P2WSH input, sufficient fees",
1840 inputs_a: generate_inputs(&[TestOutput::P2WSH(1_000_000)]),
1841 outputs_a: generate_output(&TestOutput::P2WPKH(amount_adjusted_with_p2wsh_fee)),
1845 a_expected_remote_shared_output: None,
1846 b_expected_remote_shared_output: Some((generate_p2wpkh_script_pubkey(), 0)),
1848 let p2tr_fee = fee_for_weight(TEST_FEERATE_SATS_PER_KW, P2TR_INPUT_WEIGHT_LOWER_BOUND);
1849 let amount_adjusted_with_p2tr_fee =
1850 1_000_000 - p2tr_fee - outputs_fee - tx_common_fields_fee;
1851 do_test_interactive_tx_constructor(TestSession {
1852 description: "Single contribution, with P2TR input, insufficient fees",
1853 inputs_a: generate_inputs(&[TestOutput::P2TR(1_000_000)]),
1854 outputs_a: generate_output(&TestOutput::P2WPKH(
1855 amount_adjusted_with_p2tr_fee + 1, /* makes fees insuffcient for initiator */
1859 expect_error: Some((AbortReason::InsufficientFees, ErrorCulprit::NodeA)),
1860 a_expected_remote_shared_output: None,
1861 b_expected_remote_shared_output: Some((generate_p2wpkh_script_pubkey(), 0)),
1863 do_test_interactive_tx_constructor(TestSession {
1864 description: "Single contribution with P2TR input, sufficient fees",
1865 inputs_a: generate_inputs(&[TestOutput::P2TR(1_000_000)]),
1866 outputs_a: generate_output(&TestOutput::P2WPKH(amount_adjusted_with_p2tr_fee)),
1870 a_expected_remote_shared_output: None,
1871 b_expected_remote_shared_output: Some((generate_p2wpkh_script_pubkey(), 0)),
1873 do_test_interactive_tx_constructor(TestSession {
1874 description: "Initiator contributes sufficient fees, but non-initiator does not",
1875 inputs_a: generate_inputs(&[TestOutput::P2WPKH(1_000_000)]),
1877 inputs_b: generate_inputs(&[TestOutput::P2WPKH(100_000)]),
1878 outputs_b: generate_output(&TestOutput::P2WPKH(100_000)),
1879 expect_error: Some((AbortReason::InsufficientFees, ErrorCulprit::NodeB)),
1880 a_expected_remote_shared_output: Some((generate_p2wpkh_script_pubkey(), 0)),
1881 b_expected_remote_shared_output: None,
1883 do_test_interactive_tx_constructor(TestSession {
1884 description: "Multi-input-output contributions from both sides",
1885 inputs_a: generate_inputs(&[TestOutput::P2WPKH(1_000_000); 2]),
1887 generate_shared_funding_output_one(1_000_000, 200_000),
1888 generate_output_nonfunding_one(&TestOutput::P2WPKH(200_000)),
1890 inputs_b: generate_inputs(&[
1891 TestOutput::P2WPKH(1_000_000),
1892 TestOutput::P2WPKH(500_000),
1894 outputs_b: vec![generate_output_nonfunding_one(&TestOutput::P2WPKH(400_000))],
1896 a_expected_remote_shared_output: None,
1897 b_expected_remote_shared_output: Some((generate_funding_script_pubkey(), 800_000)),
1900 do_test_interactive_tx_constructor(TestSession {
1901 description: "Prevout from initiator is not a witness program",
1902 inputs_a: generate_inputs(&[TestOutput::P2PKH(1_000_000)]),
1906 expect_error: Some((AbortReason::PrevTxOutInvalid, ErrorCulprit::NodeA)),
1907 a_expected_remote_shared_output: Some((generate_funding_script_pubkey(), 0)),
1908 b_expected_remote_shared_output: Some((generate_funding_script_pubkey(), 0)),
1912 TransactionU16LenLimited::new(generate_tx(&[TestOutput::P2WPKH(1_000_000)])).unwrap();
1913 let invalid_sequence_input = TxIn {
1914 previous_output: OutPoint { txid: tx.as_transaction().txid(), vout: 0 },
1915 ..Default::default()
1917 do_test_interactive_tx_constructor(TestSession {
1918 description: "Invalid input sequence from initiator",
1919 inputs_a: vec![(invalid_sequence_input, tx.clone())],
1920 outputs_a: generate_output(&TestOutput::P2WPKH(1_000_000)),
1923 expect_error: Some((AbortReason::IncorrectInputSequenceValue, ErrorCulprit::NodeA)),
1924 a_expected_remote_shared_output: None,
1925 b_expected_remote_shared_output: Some((generate_p2wpkh_script_pubkey(), 0)),
1927 let duplicate_input = TxIn {
1928 previous_output: OutPoint { txid: tx.as_transaction().txid(), vout: 0 },
1929 sequence: Sequence::ENABLE_RBF_NO_LOCKTIME,
1930 ..Default::default()
1932 do_test_interactive_tx_constructor(TestSession {
1933 description: "Duplicate prevout from initiator",
1934 inputs_a: vec![(duplicate_input.clone(), tx.clone()), (duplicate_input, tx.clone())],
1935 outputs_a: generate_output(&TestOutput::P2WPKH(1_000_000)),
1938 expect_error: Some((AbortReason::PrevTxOutInvalid, ErrorCulprit::NodeB)),
1939 a_expected_remote_shared_output: None,
1940 b_expected_remote_shared_output: Some((generate_p2wpkh_script_pubkey(), 0)),
1942 // Non-initiator uses same prevout as initiator.
1943 let duplicate_input = TxIn {
1944 previous_output: OutPoint { txid: tx.as_transaction().txid(), vout: 0 },
1945 sequence: Sequence::ENABLE_RBF_NO_LOCKTIME,
1946 ..Default::default()
1948 do_test_interactive_tx_constructor(TestSession {
1949 description: "Non-initiator uses same prevout as initiator",
1950 inputs_a: vec![(duplicate_input.clone(), tx.clone())],
1951 outputs_a: generate_shared_funding_output(1_000_000, 905_000),
1952 inputs_b: vec![(duplicate_input.clone(), tx.clone())],
1954 expect_error: Some((AbortReason::PrevTxOutInvalid, ErrorCulprit::NodeA)),
1955 a_expected_remote_shared_output: None,
1956 b_expected_remote_shared_output: Some((generate_funding_script_pubkey(), 95_000)),
1958 let duplicate_input = TxIn {
1959 previous_output: OutPoint { txid: tx.as_transaction().txid(), vout: 0 },
1960 sequence: Sequence::ENABLE_RBF_NO_LOCKTIME,
1961 ..Default::default()
1963 do_test_interactive_tx_constructor(TestSession {
1964 description: "Non-initiator uses same prevout as initiator",
1965 inputs_a: vec![(duplicate_input.clone(), tx.clone())],
1966 outputs_a: generate_output(&TestOutput::P2WPKH(1_000_000)),
1967 inputs_b: vec![(duplicate_input.clone(), tx.clone())],
1969 expect_error: Some((AbortReason::PrevTxOutInvalid, ErrorCulprit::NodeA)),
1970 a_expected_remote_shared_output: None,
1971 b_expected_remote_shared_output: Some((generate_p2wpkh_script_pubkey(), 0)),
1973 do_test_interactive_tx_constructor(TestSession {
1974 description: "Initiator sends too many TxAddInputs",
1975 inputs_a: generate_fixed_number_of_inputs(MAX_RECEIVED_TX_ADD_INPUT_COUNT + 1),
1979 expect_error: Some((AbortReason::ReceivedTooManyTxAddInputs, ErrorCulprit::NodeA)),
1980 a_expected_remote_shared_output: Some((generate_funding_script_pubkey(), 0)),
1981 b_expected_remote_shared_output: Some((generate_funding_script_pubkey(), 0)),
1983 do_test_interactive_tx_constructor_with_entropy_source(
1985 // We use a deliberately bad entropy source, `DuplicateEntropySource` to simulate this.
1986 description: "Attempt to queue up two inputs with duplicate serial ids",
1987 inputs_a: generate_fixed_number_of_inputs(2),
1991 expect_error: Some((AbortReason::DuplicateSerialId, ErrorCulprit::NodeA)),
1992 a_expected_remote_shared_output: Some((generate_funding_script_pubkey(), 0)),
1993 b_expected_remote_shared_output: Some((generate_funding_script_pubkey(), 0)),
1995 &DuplicateEntropySource,
1997 do_test_interactive_tx_constructor(TestSession {
1998 description: "Initiator sends too many TxAddOutputs",
2000 outputs_a: generate_fixed_number_of_outputs(MAX_RECEIVED_TX_ADD_OUTPUT_COUNT + 1),
2003 expect_error: Some((AbortReason::ReceivedTooManyTxAddOutputs, ErrorCulprit::NodeA)),
2004 a_expected_remote_shared_output: Some((generate_funding_script_pubkey(), 0)),
2005 b_expected_remote_shared_output: Some((generate_funding_script_pubkey(), 0)),
2007 do_test_interactive_tx_constructor(TestSession {
2008 description: "Initiator sends an output below dust value",
2010 outputs_a: generate_funding_output(
2011 generate_p2wsh_script_pubkey().dust_value().to_sat() - 1,
2015 expect_error: Some((AbortReason::BelowDustLimit, ErrorCulprit::NodeA)),
2016 a_expected_remote_shared_output: None,
2017 b_expected_remote_shared_output: Some((generate_funding_script_pubkey(), 0)),
2019 do_test_interactive_tx_constructor(TestSession {
2020 description: "Initiator sends an output above maximum sats allowed",
2022 outputs_a: generate_output(&TestOutput::P2WPKH(TOTAL_BITCOIN_SUPPLY_SATOSHIS + 1)),
2025 expect_error: Some((AbortReason::ExceededMaximumSatsAllowed, ErrorCulprit::NodeA)),
2026 a_expected_remote_shared_output: None,
2027 b_expected_remote_shared_output: Some((generate_funding_script_pubkey(), 0)),
2029 do_test_interactive_tx_constructor(TestSession {
2030 description: "Initiator sends an output without a witness program",
2032 outputs_a: vec![generate_non_witness_output(1_000_000)],
2035 expect_error: Some((AbortReason::InvalidOutputScript, ErrorCulprit::NodeA)),
2036 a_expected_remote_shared_output: Some((generate_funding_script_pubkey(), 0)),
2037 b_expected_remote_shared_output: Some((generate_funding_script_pubkey(), 0)),
2039 do_test_interactive_tx_constructor_with_entropy_source(
2041 // We use a deliberately bad entropy source, `DuplicateEntropySource` to simulate this.
2042 description: "Attempt to queue up two outputs with duplicate serial ids",
2044 outputs_a: generate_fixed_number_of_outputs(2),
2047 expect_error: Some((AbortReason::DuplicateSerialId, ErrorCulprit::NodeA)),
2048 a_expected_remote_shared_output: Some((generate_funding_script_pubkey(), 0)),
2049 b_expected_remote_shared_output: Some((generate_funding_script_pubkey(), 0)),
2051 &DuplicateEntropySource,
2054 do_test_interactive_tx_constructor(TestSession {
2055 description: "Peer contributed more output value than inputs",
2056 inputs_a: generate_inputs(&[TestOutput::P2WPKH(100_000)]),
2057 outputs_a: generate_output(&TestOutput::P2WPKH(1_000_000)),
2060 expect_error: Some((AbortReason::OutputsValueExceedsInputsValue, ErrorCulprit::NodeA)),
2061 a_expected_remote_shared_output: None,
2062 b_expected_remote_shared_output: Some((generate_funding_script_pubkey(), 0)),
2065 do_test_interactive_tx_constructor(TestSession {
2066 description: "Peer contributed more than allowed number of inputs",
2067 inputs_a: generate_fixed_number_of_inputs(MAX_INPUTS_OUTPUTS_COUNT as u16 + 1),
2071 expect_error: Some((
2072 AbortReason::ExceededNumberOfInputsOrOutputs,
2073 ErrorCulprit::Indeterminate,
2075 a_expected_remote_shared_output: Some((generate_funding_script_pubkey(), 0)),
2076 b_expected_remote_shared_output: Some((generate_funding_script_pubkey(), 0)),
2078 do_test_interactive_tx_constructor(TestSession {
2079 description: "Peer contributed more than allowed number of outputs",
2080 inputs_a: generate_inputs(&[TestOutput::P2WPKH(TOTAL_BITCOIN_SUPPLY_SATOSHIS)]),
2081 outputs_a: generate_fixed_number_of_outputs(MAX_INPUTS_OUTPUTS_COUNT as u16 + 1),
2084 expect_error: Some((
2085 AbortReason::ExceededNumberOfInputsOrOutputs,
2086 ErrorCulprit::Indeterminate,
2088 a_expected_remote_shared_output: Some((generate_funding_script_pubkey(), 0)),
2089 b_expected_remote_shared_output: Some((generate_funding_script_pubkey(), 0)),
2092 // Adding multiple outputs to the funding output pubkey is an error
2093 do_test_interactive_tx_constructor(TestSession {
2094 description: "Adding two outputs to the funding output pubkey",
2095 inputs_a: generate_inputs(&[TestOutput::P2WPKH(1_000_000)]),
2096 outputs_a: generate_funding_output(100_000),
2097 inputs_b: generate_inputs(&[TestOutput::P2WPKH(1_001_000)]),
2098 outputs_b: generate_funding_output(100_000),
2099 expect_error: Some((AbortReason::DuplicateFundingOutput, ErrorCulprit::NodeA)),
2100 a_expected_remote_shared_output: None,
2101 b_expected_remote_shared_output: None,
2104 // We add the funding output, but we contribute a little
2105 do_test_interactive_tx_constructor(TestSession {
2106 description: "Funding output by us, small contribution",
2107 inputs_a: generate_inputs(&[TestOutput::P2WPKH(12_000)]),
2108 outputs_a: generate_shared_funding_output(1_000_000, 10_000),
2109 inputs_b: generate_inputs(&[TestOutput::P2WPKH(992_000)]),
2112 a_expected_remote_shared_output: None,
2113 b_expected_remote_shared_output: Some((generate_funding_script_pubkey(), 990_000)),
2116 // They add the funding output, and we contribute a little
2117 do_test_interactive_tx_constructor(TestSession {
2118 description: "Funding output by them, small contribution",
2119 inputs_a: generate_inputs(&[TestOutput::P2WPKH(12_000)]),
2121 inputs_b: generate_inputs(&[TestOutput::P2WPKH(992_000)]),
2122 outputs_b: generate_shared_funding_output(1_000_000, 990_000),
2124 a_expected_remote_shared_output: Some((generate_funding_script_pubkey(), 10_000)),
2125 b_expected_remote_shared_output: None,
2128 // We add the funding output, and we contribute most
2129 do_test_interactive_tx_constructor(TestSession {
2130 description: "Funding output by us, large contribution",
2131 inputs_a: generate_inputs(&[TestOutput::P2WPKH(992_000)]),
2132 outputs_a: generate_shared_funding_output(1_000_000, 990_000),
2133 inputs_b: generate_inputs(&[TestOutput::P2WPKH(12_000)]),
2136 a_expected_remote_shared_output: None,
2137 b_expected_remote_shared_output: Some((generate_funding_script_pubkey(), 10_000)),
2140 // They add the funding output, but we contribute most
2141 do_test_interactive_tx_constructor(TestSession {
2142 description: "Funding output by them, large contribution",
2143 inputs_a: generate_inputs(&[TestOutput::P2WPKH(992_000)]),
2145 inputs_b: generate_inputs(&[TestOutput::P2WPKH(12_000)]),
2146 outputs_b: generate_shared_funding_output(1_000_000, 10_000),
2148 a_expected_remote_shared_output: Some((generate_funding_script_pubkey(), 990_000)),
2149 b_expected_remote_shared_output: None,
2152 // During a splice-out, with peer providing more output value than input value
2153 // but still pays enough fees due to their to_remote_value_satoshis portion in
2154 // the shared input.
2155 do_test_interactive_tx_constructor(TestSession {
2156 description: "Splice out with sufficient initiator balance",
2157 inputs_a: generate_inputs(&[TestOutput::P2WPKH(100_000), TestOutput::P2WPKH(50_000)]),
2158 outputs_a: generate_funding_output(120_000),
2159 inputs_b: generate_inputs(&[TestOutput::P2WPKH(50_000)]),
2162 a_expected_remote_shared_output: None,
2163 b_expected_remote_shared_output: Some((generate_funding_script_pubkey(), 0)),
2166 // During a splice-out, with peer providing more output value than input value
2167 // and the to_remote_value_satoshis portion in
2168 // the shared input cannot cover fees
2169 do_test_interactive_tx_constructor(TestSession {
2170 description: "Splice out with insufficient initiator balance",
2171 inputs_a: generate_inputs(&[TestOutput::P2WPKH(100_000), TestOutput::P2WPKH(15_000)]),
2172 outputs_a: generate_funding_output(120_000),
2173 inputs_b: generate_inputs(&[TestOutput::P2WPKH(85_000)]),
2175 expect_error: Some((AbortReason::OutputsValueExceedsInputsValue, ErrorCulprit::NodeA)),
2176 a_expected_remote_shared_output: None,
2177 b_expected_remote_shared_output: Some((generate_funding_script_pubkey(), 0)),
2180 // The actual funding output value is lower than the intended local contribution by the same node
2181 do_test_interactive_tx_constructor(TestSession {
2182 description: "Splice in, invalid intended local contribution",
2183 inputs_a: generate_inputs(&[TestOutput::P2WPKH(100_000), TestOutput::P2WPKH(15_000)]),
2184 outputs_a: generate_shared_funding_output(100_000, 120_000), // local value is higher than the output value
2185 inputs_b: generate_inputs(&[TestOutput::P2WPKH(85_000)]),
2187 expect_error: Some((AbortReason::InvalidLowFundingOutputValue, ErrorCulprit::NodeA)),
2188 a_expected_remote_shared_output: None,
2189 b_expected_remote_shared_output: Some((generate_funding_script_pubkey(), 20_000)),
2192 // The actual funding output value is lower than the intended local contribution of the other node
2193 do_test_interactive_tx_constructor(TestSession {
2194 description: "Splice in, invalid intended local contribution",
2195 inputs_a: generate_inputs(&[TestOutput::P2WPKH(100_000), TestOutput::P2WPKH(15_000)]),
2197 inputs_b: generate_inputs(&[TestOutput::P2WPKH(85_000)]),
2198 outputs_b: generate_funding_output(100_000),
2199 // 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
2200 expect_error: Some((AbortReason::InvalidLowFundingOutputValue, ErrorCulprit::NodeB)),
2201 a_expected_remote_shared_output: Some((generate_funding_script_pubkey(), 120_000)), // this is higher than the actual output value
2202 b_expected_remote_shared_output: None,
2207 fn test_generate_local_serial_id() {
2208 let entropy_source = TestEntropySource(AtomicCounter::new());
2210 // Initiators should have even serial id, non-initiators should have odd serial id.
2211 assert_eq!(generate_holder_serial_id(&&entropy_source, true) % 2, 0);
2212 assert_eq!(generate_holder_serial_id(&&entropy_source, false) % 2, 1)