Skip to content

Commit 6b3e136

Browse files
committed
Split in two, add unit test
1 parent 9e7c5ce commit 6b3e136

File tree

1 file changed

+87
-24
lines changed

1 file changed

+87
-24
lines changed

lightning/src/ln/channel.rs

Lines changed: 87 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1748,7 +1748,7 @@ pub(super) trait InteractivelyFunded<SP: Deref> where SP::Target: SignerProvider
17481748

17491749
maybe_add_funding_change_output(signer_provider, self.is_initiator(), self.dual_funding_context().our_funding_satoshis,
17501750
&funding_inputs_prev_outputs, &mut funding_outputs, self.dual_funding_context().funding_feerate_sat_per_1000_weight,
1751-
total_input_satoshis, self.context().holder_dust_limit_satoshis, self.context().channel_keys_id).map_err(
1751+
self.context().holder_dust_limit_satoshis, self.context().channel_keys_id).map_err(
17521752
|_| APIError::APIMisuseError { err: "Could not create change output".to_string() })?;
17531753

17541754
let constructor_args = InteractiveTxConstructorArgs {
@@ -4253,13 +4253,11 @@ fn get_v2_channel_reserve_satoshis(channel_value_satoshis: u64, dust_limit_satos
42534253
}
42544254

42554255
#[allow(dead_code)] // TODO(dual_funding): Remove once begin_interactive_funding_tx_construction() is used
4256-
pub(super) fn maybe_add_funding_change_output<SP: Deref>(signer_provider: &SP, is_initiator: bool,
4256+
fn need_to_add_funding_change_output(is_initiator: bool,
42574257
our_funding_satoshis: u64, funding_inputs_prev_outputs: &Vec<TxOut>,
4258-
funding_outputs: &mut Vec<OutputOwned>, funding_feerate_sat_per_1000_weight: u32,
4259-
total_input_satoshis: u64, holder_dust_limit_satoshis: u64, channel_keys_id: [u8; 32],
4260-
) -> Result<Option<TxOut>, ChannelError> where
4261-
SP::Target: SignerProvider,
4262-
{
4258+
funding_outputs: &Vec<OutputOwned>, funding_feerate_sat_per_1000_weight: u32,
4259+
holder_dust_limit_satoshis: u64,
4260+
) -> Result<Option<u64>, ChannelError> {
42634261
let our_funding_inputs_weight = funding_inputs_prev_outputs.iter().fold(0u64, |weight, prev_output| {
42644262
weight.saturating_add(estimate_input_weight(prev_output).to_wu())
42654263
});
@@ -4275,32 +4273,54 @@ pub(super) fn maybe_add_funding_change_output<SP: Deref>(signer_provider: &SP, i
42754273
fees_sats = fees_sats.saturating_add(common_fees);
42764274
}
42774275

4276+
let total_input_satoshis: u64 = funding_inputs_prev_outputs.iter().map(|out| out.value.to_sat()).sum();
4277+
42784278
let remaining_value = total_input_satoshis
42794279
.saturating_sub(our_funding_satoshis)
42804280
.saturating_sub(fees_sats);
42814281

42824282
if remaining_value < holder_dust_limit_satoshis {
42834283
Ok(None)
42844284
} else {
4285-
let change_script = signer_provider.get_destination_script(channel_keys_id).map_err(
4286-
|_| ChannelError::Close((
4287-
"Failed to get change script as new destination script".to_owned(),
4288-
ClosureReason::ProcessingError { err: "Failed to get change script as new destination script".to_owned() }
4289-
))
4290-
)?;
4291-
let mut change_output = TxOut {
4292-
value: Amount::from_sat(remaining_value),
4293-
script_pubkey: change_script,
4294-
};
4295-
let change_output_weight = get_output_weight(&change_output.script_pubkey).to_wu();
4296-
4297-
let change_output_fee = fee_for_weight(funding_feerate_sat_per_1000_weight, change_output_weight);
4298-
change_output.value = Amount::from_sat(remaining_value.saturating_sub(change_output_fee));
4299-
funding_outputs.push(OutputOwned::Single(change_output.clone()));
4300-
Ok(Some(change_output))
4285+
Ok(Some(remaining_value))
43014286
}
43024287
}
43034288

4289+
#[allow(dead_code)] // TODO(dual_funding): Remove once begin_interactive_funding_tx_construction() is used
4290+
fn maybe_add_funding_change_output<SP: Deref>(signer_provider: &SP, is_initiator: bool,
4291+
our_funding_satoshis: u64, funding_inputs_prev_outputs: &Vec<TxOut>,
4292+
funding_outputs: &mut Vec<OutputOwned>, funding_feerate_sat_per_1000_weight: u32,
4293+
holder_dust_limit_satoshis: u64, channel_keys_id: [u8; 32],
4294+
) -> Result<Option<TxOut>, ChannelError> where SP::Target: SignerProvider {
4295+
let remaining_value = match need_to_add_funding_change_output(
4296+
is_initiator, our_funding_satoshis, funding_inputs_prev_outputs,
4297+
funding_outputs, funding_feerate_sat_per_1000_weight, holder_dust_limit_satoshis
4298+
)? {
4299+
None => {
4300+
// No need to add
4301+
return Ok(None);
4302+
}
4303+
Some(remaining_value) => remaining_value,
4304+
};
4305+
4306+
let change_script = signer_provider.get_destination_script(channel_keys_id).map_err(
4307+
|_| ChannelError::Close((
4308+
"Failed to get change script as new destination script".to_owned(),
4309+
ClosureReason::ProcessingError { err: "Failed to get change script as new destination script".to_owned() }
4310+
))
4311+
)?;
4312+
let mut change_output = TxOut {
4313+
value: Amount::from_sat(remaining_value),
4314+
script_pubkey: change_script,
4315+
};
4316+
let change_output_weight = get_output_weight(&change_output.script_pubkey).to_wu();
4317+
4318+
let change_output_fee = fee_for_weight(funding_feerate_sat_per_1000_weight, change_output_weight);
4319+
change_output.value = Amount::from_sat(remaining_value.saturating_sub(change_output_fee));
4320+
funding_outputs.push(OutputOwned::Single(change_output.clone()));
4321+
Ok(Some(change_output))
4322+
}
4323+
43044324
pub(super) fn calculate_our_funding_satoshis(
43054325
is_initiator: bool, funding_inputs: &[(TxIn, TransactionU16LenLimited)],
43064326
total_witness_weight: Weight, funding_feerate_sat_per_1000_weight: u32,
@@ -10307,8 +10327,9 @@ mod tests {
1030710327
use crate::ln::channel_keys::{RevocationKey, RevocationBasepoint};
1030810328
use crate::ln::channelmanager::{self, HTLCSource, PaymentId};
1030910329
use crate::ln::channel::InitFeatures;
10310-
use crate::ln::channel::{AwaitingChannelReadyFlags, Channel, ChannelState, InboundHTLCOutput, OutboundV1Channel, InboundV1Channel, OutboundHTLCOutput, InboundHTLCState, OutboundHTLCState, HTLCCandidate, HTLCInitiator, HTLCUpdateAwaitingACK, commit_tx_fee_sat};
10330+
use crate::ln::channel::{AwaitingChannelReadyFlags, Channel, ChannelState, InboundHTLCOutput, OutboundV1Channel, InboundV1Channel, OutboundHTLCOutput, InboundHTLCState, OutboundHTLCState, HTLCCandidate, HTLCInitiator, HTLCUpdateAwaitingACK, commit_tx_fee_sat, need_to_add_funding_change_output};
1031110331
use crate::ln::channel::{MAX_FUNDING_SATOSHIS_NO_WUMBO, TOTAL_BITCOIN_SUPPLY_SATOSHIS, MIN_THEIR_CHAN_RESERVE_SATOSHIS};
10332+
use crate::ln::interactivetxs::{OutputOwned, SharedOwnedOutput};
1031210333
use crate::types::features::{ChannelFeatures, ChannelTypeFeatures, NodeFeatures};
1031310334
use crate::ln::msgs;
1031410335
use crate::ln::msgs::{ChannelUpdate, DecodeError, UnsignedChannelUpdate, MAX_VALUE_MSAT};
@@ -12071,4 +12092,46 @@ mod tests {
1207112092
assert_eq!(node_a_chan.context.channel_state, ChannelState::AwaitingChannelReady(AwaitingChannelReadyFlags::THEIR_CHANNEL_READY));
1207212093
assert!(node_a_chan.check_get_channel_ready(0, &&logger).is_some());
1207312094
}
12095+
12096+
#[test]
12097+
fn test_need_to_add_funding_change_output() {
12098+
let prevouts = vec![
12099+
TxOut { value: Amount::from_sat(70_000), script_pubkey: ScriptBuf::new()},
12100+
TxOut { value: Amount::from_sat(60_000), script_pubkey: ScriptBuf::new()},
12101+
];
12102+
let txout = TxOut { value: Amount::from_sat(125_000), script_pubkey: ScriptBuf::new()};
12103+
let outputs = vec![OutputOwned::Shared(SharedOwnedOutput::new(txout, 105_000))];
12104+
let our_contributed = 110_000;
12105+
let funding_feerate_sat_per_1000_weight = 3000;
12106+
12107+
let total_inputs: u64 = prevouts.iter().map(|o| o.value.to_sat()).sum();
12108+
let gross_change = total_inputs - our_contributed;
12109+
let fees = 1746;
12110+
let common_fees = 126;
12111+
{
12112+
// There is leftover for change
12113+
let res = need_to_add_funding_change_output(true, our_contributed, &prevouts, &outputs, funding_feerate_sat_per_1000_weight, 300);
12114+
assert_eq!(res.unwrap().unwrap(), gross_change - fees - common_fees);
12115+
}
12116+
{
12117+
// There is leftover for change, without common fees
12118+
let res = need_to_add_funding_change_output(false, our_contributed, &prevouts, &outputs, funding_feerate_sat_per_1000_weight, 300);
12119+
assert_eq!(res.unwrap().unwrap(), gross_change - fees);
12120+
}
12121+
{
12122+
// Insufficient inputs, no leftover
12123+
let res = need_to_add_funding_change_output(false, 130_000, &prevouts, &outputs, funding_feerate_sat_per_1000_weight, 300);
12124+
assert!(res.unwrap().is_none());
12125+
}
12126+
{
12127+
// Very small leftover
12128+
let res = need_to_add_funding_change_output(false, 128_100, &prevouts, &outputs, funding_feerate_sat_per_1000_weight, 300);
12129+
assert!(res.unwrap().is_none());
12130+
}
12131+
{
12132+
// Small leftover, but not dust
12133+
let res = need_to_add_funding_change_output(false, 128_100, &prevouts, &outputs, funding_feerate_sat_per_1000_weight, 100);
12134+
assert_eq!(res.unwrap().unwrap(), 154);
12135+
}
12136+
}
1207412137
}

0 commit comments

Comments
 (0)