@@ -60,7 +60,8 @@ use crate::ln::funding::{FundingTxInput, SpliceContribution};
60
60
use crate::ln::interactivetxs::{
61
61
calculate_change_output_value, get_output_weight, AbortReason, HandleTxCompleteValue,
62
62
InteractiveTxConstructor, InteractiveTxConstructorArgs, InteractiveTxMessageSend,
63
- InteractiveTxSigningSession, SharedOwnedInput, SharedOwnedOutput, TX_COMMON_FIELDS_WEIGHT,
63
+ InteractiveTxSigningSession, NegotiationError, SharedOwnedInput, SharedOwnedOutput,
64
+ TX_COMMON_FIELDS_WEIGHT,
64
65
};
65
66
use crate::ln::msgs;
66
67
use crate::ln::msgs::{ClosingSigned, ClosingSignedFeeRange, DecodeError, OnionErrorPacket};
@@ -1712,12 +1713,13 @@ where
1712
1713
}
1713
1714
1714
1715
fn fail_interactive_tx_negotiation<L: Deref>(
1715
- &mut self, reason: AbortReason , logger: &L,
1716
+ &mut self, error: NegotiationError , logger: &L,
1716
1717
) -> msgs::TxAbort
1717
1718
where
1718
1719
L::Target: Logger,
1719
1720
{
1720
1721
let logger = WithChannelContext::from(logger, &self.context(), None);
1722
+ let NegotiationError { reason, .. } = error;
1721
1723
log_info!(logger, "Failed interactive transaction negotiation: {reason}");
1722
1724
1723
1725
let _interactive_tx_constructor = match &mut self.phase {
@@ -1754,11 +1756,15 @@ where
1754
1756
{
1755
1757
match self.interactive_tx_constructor_mut() {
1756
1758
Some(interactive_tx_constructor) => interactive_tx_constructor.handle_tx_add_input(msg),
1757
- None => Err(AbortReason::InternalError(
1758
- "Received unexpected interactive transaction negotiation message",
1759
- )),
1759
+ None => Err(NegotiationError {
1760
+ reason: AbortReason::InternalError(
1761
+ "Received unexpected interactive transaction negotiation message",
1762
+ ),
1763
+ contributed_inputs: Vec::new(),
1764
+ contributed_outputs: Vec::new(),
1765
+ }),
1760
1766
}
1761
- .map_err(|abort_reason | self.fail_interactive_tx_negotiation(abort_reason , logger))
1767
+ .map_err(|e | self.fail_interactive_tx_negotiation(e , logger))
1762
1768
}
1763
1769
1764
1770
pub fn tx_add_output<L: Deref>(
@@ -1771,11 +1777,15 @@ where
1771
1777
Some(interactive_tx_constructor) => {
1772
1778
interactive_tx_constructor.handle_tx_add_output(msg)
1773
1779
},
1774
- None => Err(AbortReason::InternalError(
1775
- "Received unexpected interactive transaction negotiation message",
1776
- )),
1780
+ None => Err(NegotiationError {
1781
+ reason: AbortReason::InternalError(
1782
+ "Received unexpected interactive transaction negotiation message",
1783
+ ),
1784
+ contributed_inputs: Vec::new(),
1785
+ contributed_outputs: Vec::new(),
1786
+ }),
1777
1787
}
1778
- .map_err(|abort_reason | self.fail_interactive_tx_negotiation(abort_reason , logger))
1788
+ .map_err(|e | self.fail_interactive_tx_negotiation(e , logger))
1779
1789
}
1780
1790
1781
1791
pub fn tx_remove_input<L: Deref>(
@@ -1788,11 +1798,15 @@ where
1788
1798
Some(interactive_tx_constructor) => {
1789
1799
interactive_tx_constructor.handle_tx_remove_input(msg)
1790
1800
},
1791
- None => Err(AbortReason::InternalError(
1792
- "Received unexpected interactive transaction negotiation message",
1793
- )),
1801
+ None => Err(NegotiationError {
1802
+ reason: AbortReason::InternalError(
1803
+ "Received unexpected interactive transaction negotiation message",
1804
+ ),
1805
+ contributed_inputs: Vec::new(),
1806
+ contributed_outputs: Vec::new(),
1807
+ }),
1794
1808
}
1795
- .map_err(|abort_reason | self.fail_interactive_tx_negotiation(abort_reason , logger))
1809
+ .map_err(|e | self.fail_interactive_tx_negotiation(e , logger))
1796
1810
}
1797
1811
1798
1812
pub fn tx_remove_output<L: Deref>(
@@ -1805,11 +1819,15 @@ where
1805
1819
Some(interactive_tx_constructor) => {
1806
1820
interactive_tx_constructor.handle_tx_remove_output(msg)
1807
1821
},
1808
- None => Err(AbortReason::InternalError(
1809
- "Received unexpected interactive transaction negotiation message",
1810
- )),
1822
+ None => Err(NegotiationError {
1823
+ reason: AbortReason::InternalError(
1824
+ "Received unexpected interactive transaction negotiation message",
1825
+ ),
1826
+ contributed_inputs: Vec::new(),
1827
+ contributed_outputs: Vec::new(),
1828
+ }),
1811
1829
}
1812
- .map_err(|abort_reason | self.fail_interactive_tx_negotiation(abort_reason , logger))
1830
+ .map_err(|e | self.fail_interactive_tx_negotiation(e , logger))
1813
1831
}
1814
1832
1815
1833
pub fn tx_complete<L: Deref>(
@@ -1820,11 +1838,15 @@ where
1820
1838
{
1821
1839
let tx_complete_action = match self.interactive_tx_constructor_mut() {
1822
1840
Some(interactive_tx_constructor) => interactive_tx_constructor.handle_tx_complete(msg),
1823
- None => Err(AbortReason::InternalError(
1824
- "Received unexpected interactive transaction negotiation message",
1825
- )),
1841
+ None => Err(NegotiationError {
1842
+ reason: AbortReason::InternalError(
1843
+ "Received unexpected interactive transaction negotiation message",
1844
+ ),
1845
+ contributed_inputs: Vec::new(),
1846
+ contributed_outputs: Vec::new(),
1847
+ }),
1826
1848
}
1827
- .map_err(|abort_reason | self.fail_interactive_tx_negotiation(abort_reason , logger))?;
1849
+ .map_err(|e | self.fail_interactive_tx_negotiation(e , logger))?;
1828
1850
1829
1851
let (interactive_tx_msg_send, negotiation_complete) = match tx_complete_action {
1830
1852
HandleTxCompleteValue::SendTxMessage(interactive_tx_msg_send) => {
@@ -1842,7 +1864,7 @@ where
1842
1864
1843
1865
let commitment_signed = self
1844
1866
.funding_tx_constructed(logger)
1845
- .map_err(|abort_reason | self.fail_interactive_tx_negotiation(abort_reason , logger))?;
1867
+ .map_err(|e | self.fail_interactive_tx_negotiation(e , logger))?;
1846
1868
Ok((interactive_tx_msg_send, Some(commitment_signed)))
1847
1869
}
1848
1870
@@ -1925,7 +1947,7 @@ where
1925
1947
1926
1948
fn funding_tx_constructed<L: Deref>(
1927
1949
&mut self, logger: &L,
1928
- ) -> Result<msgs::CommitmentSigned, AbortReason >
1950
+ ) -> Result<msgs::CommitmentSigned, NegotiationError >
1929
1951
where
1930
1952
L::Target: Logger,
1931
1953
{
@@ -1983,15 +2005,23 @@ where
1983
2005
}
1984
2006
}
1985
2007
1986
- return Err(AbortReason::InternalError(
1987
- "Got a tx_complete message in an invalid state",
1988
- ));
2008
+ return Err(NegotiationError {
2009
+ reason: AbortReason::InternalError(
2010
+ "Got a tx_complete message in an invalid state",
2011
+ ),
2012
+ contributed_inputs: Vec::new(),
2013
+ contributed_outputs: Vec::new(),
2014
+ });
1989
2015
},
1990
2016
_ => {
1991
2017
debug_assert!(false);
1992
- return Err(AbortReason::InternalError(
1993
- "Got a tx_complete message in an invalid phase",
1994
- ));
2018
+ return Err(NegotiationError {
2019
+ reason: AbortReason::InternalError(
2020
+ "Got a tx_complete message in an invalid phase",
2021
+ ),
2022
+ contributed_inputs: Vec::new(),
2023
+ contributed_outputs: Vec::new(),
2024
+ });
1995
2025
},
1996
2026
}
1997
2027
}
@@ -6102,7 +6132,7 @@ where
6102
6132
fn funding_tx_constructed<L: Deref>(
6103
6133
&mut self, funding: &mut FundingScope, signing_session: InteractiveTxSigningSession,
6104
6134
is_splice: bool, holder_commitment_transaction_number: u64, logger: &L
6105
- ) -> Result<msgs::CommitmentSigned, AbortReason >
6135
+ ) -> Result<msgs::CommitmentSigned, NegotiationError >
6106
6136
where
6107
6137
L::Target: Logger
6108
6138
{
@@ -6111,15 +6141,17 @@ where
6111
6141
for (idx, outp) in signing_session.unsigned_tx().tx().output.iter().enumerate() {
6112
6142
if outp.script_pubkey == expected_spk && outp.value.to_sat() == funding.get_value_satoshis() {
6113
6143
if output_index.is_some() {
6114
- return Err(AbortReason::DuplicateFundingOutput);
6144
+ let reason = AbortReason::DuplicateFundingOutput;
6145
+ return Err(signing_session.into_negotiation_error(reason));
6115
6146
}
6116
6147
output_index = Some(idx as u16);
6117
6148
}
6118
6149
}
6119
6150
let outpoint = if let Some(output_index) = output_index {
6120
6151
OutPoint { txid: signing_session.unsigned_tx().compute_txid(), index: output_index }
6121
6152
} else {
6122
- return Err(AbortReason::MissingFundingOutput);
6153
+ let reason = AbortReason::MissingFundingOutput;
6154
+ return Err(signing_session.into_negotiation_error(reason));
6123
6155
};
6124
6156
funding
6125
6157
.channel_transaction_parameters.funding_outpoint = Some(outpoint);
@@ -6131,7 +6163,9 @@ where
6131
6163
self.counterparty_next_commitment_transaction_number,
6132
6164
);
6133
6165
// TODO(splicing) Forced error, as the use case is not complete
6134
- return Err(AbortReason::InternalError("Splicing not yet supported"));
6166
+ let signing_session = self.interactive_tx_signing_session.take().unwrap();
6167
+ let reason = AbortReason::InternalError("Splicing not yet supported");
6168
+ return Err(signing_session.into_negotiation_error(reason));
6135
6169
} else {
6136
6170
self.assert_no_commitment_advancement(holder_commitment_transaction_number, "initial commitment_signed");
6137
6171
self.channel_state = ChannelState::FundingNegotiated(FundingNegotiatedFlags::new());
@@ -6143,7 +6177,9 @@ where
6143
6177
// TODO(splicing): Support async signing
6144
6178
None => {
6145
6179
funding.channel_transaction_parameters.funding_outpoint = None;
6146
- return Err(AbortReason::InternalError("Failed to compute commitment_signed signatures"));
6180
+ let signing_session = self.interactive_tx_signing_session.take().unwrap();
6181
+ let reason = AbortReason::InternalError("Failed to compute commitment_signed signatures");
6182
+ return Err(signing_session.into_negotiation_error(reason));
6147
6183
},
6148
6184
};
6149
6185
@@ -6556,9 +6592,9 @@ impl FundingNegotiationContext {
6556
6592
/// Prepare and start interactive transaction negotiation.
6557
6593
/// If error occurs, it is caused by our side, not the counterparty.
6558
6594
fn into_interactive_tx_constructor<SP: Deref, ES: Deref>(
6559
- self, context: &ChannelContext<SP>, funding: &FundingScope, signer_provider: &SP,
6595
+ mut self, context: &ChannelContext<SP>, funding: &FundingScope, signer_provider: &SP,
6560
6596
entropy_source: &ES, holder_node_id: PublicKey,
6561
- ) -> Result<InteractiveTxConstructor, AbortReason >
6597
+ ) -> Result<InteractiveTxConstructor, NegotiationError >
6562
6598
where
6563
6599
SP::Target: SignerProvider,
6564
6600
ES::Target: EntropySource,
@@ -6584,25 +6620,32 @@ impl FundingNegotiationContext {
6584
6620
6585
6621
// Optionally add change output
6586
6622
let change_value_opt = if self.our_funding_contribution > SignedAmount::ZERO {
6587
- calculate_change_output_value(
6623
+ match calculate_change_output_value(
6588
6624
&self,
6589
6625
self.shared_funding_input.is_some(),
6590
6626
&shared_funding_output.script_pubkey,
6591
6627
context.holder_dust_limit_satoshis,
6592
- )?
6628
+ ) {
6629
+ Ok(change_value_opt) => change_value_opt,
6630
+ Err(reason) => {
6631
+ return Err(self.into_negotiation_error(reason));
6632
+ },
6633
+ }
6593
6634
} else {
6594
6635
None
6595
6636
};
6596
6637
6597
- let mut funding_outputs = self.our_funding_outputs;
6598
-
6599
6638
if let Some(change_value) = change_value_opt {
6600
6639
let change_script = if let Some(script) = self.change_script {
6601
6640
script
6602
6641
} else {
6603
- signer_provider
6604
- .get_destination_script(context.channel_keys_id)
6605
- .map_err(|_err| AbortReason::InternalError("Error getting change script"))?
6642
+ match signer_provider.get_destination_script(context.channel_keys_id) {
6643
+ Ok(script) => script,
6644
+ Err(_) => {
6645
+ let reason = AbortReason::InternalError("Error getting change script");
6646
+ return Err(self.into_negotiation_error(reason));
6647
+ },
6648
+ }
6606
6649
};
6607
6650
let mut change_output =
6608
6651
TxOut { value: Amount::from_sat(change_value), script_pubkey: change_script };
@@ -6613,7 +6656,7 @@ impl FundingNegotiationContext {
6613
6656
// Check dust limit again
6614
6657
if change_value_decreased_with_fee > context.holder_dust_limit_satoshis {
6615
6658
change_output.value = Amount::from_sat(change_value_decreased_with_fee);
6616
- funding_outputs .push(change_output);
6659
+ self.our_funding_outputs .push(change_output);
6617
6660
}
6618
6661
}
6619
6662
@@ -6631,10 +6674,22 @@ impl FundingNegotiationContext {
6631
6674
shared_funding_output,
6632
6675
funding.value_to_self_msat / 1000,
6633
6676
),
6634
- outputs_to_contribute: funding_outputs ,
6677
+ outputs_to_contribute: self.our_funding_outputs ,
6635
6678
};
6636
6679
InteractiveTxConstructor::new(constructor_args)
6637
6680
}
6681
+
6682
+ fn into_negotiation_error(self, reason: AbortReason) -> NegotiationError {
6683
+ let contributed_inputs = self
6684
+ .our_funding_inputs
6685
+ .into_iter()
6686
+ .map(|input| input.utxo.outpoint)
6687
+ .collect();
6688
+
6689
+ let contributed_outputs = self.our_funding_outputs;
6690
+
6691
+ NegotiationError { reason, contributed_inputs, contributed_outputs }
6692
+ }
6638
6693
}
6639
6694
6640
6695
// Holder designates channel data owned for the benefit of the user client.
@@ -13721,8 +13776,8 @@ where
13721
13776
outputs_to_contribute: funding_negotiation_context.our_funding_outputs.clone(),
13722
13777
}
13723
13778
).map_err(|err| {
13724
- let reason = ClosureReason::ProcessingError { err: err.to_string() };
13725
- ChannelError::Close((err.to_string(), reason))
13779
+ let reason = ClosureReason::ProcessingError { err: err.reason. to_string() };
13780
+ ChannelError::Close((err.reason. to_string(), reason))
13726
13781
})?);
13727
13782
13728
13783
let unfunded_context = UnfundedChannelContext {
0 commit comments