Skip to content

Commit

Permalink
Moved need_to_add_funding_change_output() to interactivetxs.rs
Browse files Browse the repository at this point in the history
  • Loading branch information
optout21 committed Dec 6, 2024
1 parent acf9853 commit 4f7e41b
Show file tree
Hide file tree
Showing 2 changed files with 118 additions and 119 deletions.
120 changes: 2 additions & 118 deletions lightning/src/ln/channel.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ use crate::ln::types::ChannelId;
use crate::types::payment::{PaymentPreimage, PaymentHash};
use crate::types::features::{ChannelTypeFeatures, InitFeatures};
use crate::ln::interactivetxs::{
estimate_input_weight, get_output_weight, HandleTxCompleteValue, HandleTxCompleteResult, InteractiveTxConstructor,
get_output_weight, need_to_add_funding_change_output, HandleTxCompleteValue, HandleTxCompleteResult, InteractiveTxConstructor,
InteractiveTxConstructorArgs, InteractiveTxMessageSend, InteractiveTxSigningSession, InteractiveTxMessageSendResult,
OutputOwned, SharedOwnedOutput, TX_COMMON_FIELDS_WEIGHT,
};
Expand Down Expand Up @@ -4252,42 +4252,6 @@ fn get_v2_channel_reserve_satoshis(channel_value_satoshis: u64, dust_limit_satos
cmp::min(channel_value_satoshis, cmp::max(q, dust_limit_satoshis))
}

/// Determine whether with our given inputs, outputs, and intended contribution
/// a change output should be added or not, and if so, of waht size
#[allow(dead_code)] // TODO(dual_funding): Remove once begin_interactive_funding_tx_construction() is used
fn need_to_add_funding_change_output(is_initiator: bool,
our_contribution: u64,
funding_inputs_prev_outputs: &Vec<TxOut>, funding_outputs: &Vec<OutputOwned>,
funding_feerate_sat_per_1000_weight: u32, holder_dust_limit_satoshis: u64,
) -> Option<u64> {
let our_funding_inputs_weight = funding_inputs_prev_outputs.iter().fold(0u64, |weight, prev_output| {
weight.saturating_add(estimate_input_weight(prev_output).to_wu())
});
let our_funding_outputs_weight = funding_outputs.iter().fold(0u64, |weight, out| {
weight.saturating_add(get_output_weight(&out.tx_out().script_pubkey).to_wu())
});
let our_contributed_weight = our_funding_outputs_weight.saturating_add(our_funding_inputs_weight);
let mut fees_sats = fee_for_weight(funding_feerate_sat_per_1000_weight, our_contributed_weight);

// If we are the initiator, we must pay for weight of all common fields in the funding transaction.
if is_initiator {
let common_fees = fee_for_weight(funding_feerate_sat_per_1000_weight, TX_COMMON_FIELDS_WEIGHT);
fees_sats = fees_sats.saturating_add(common_fees);
}

let total_input_satoshis: u64 = funding_inputs_prev_outputs.iter().map(|out| out.value.to_sat()).sum();

let remaining_value = total_input_satoshis
.saturating_sub(our_contribution)
.saturating_sub(fees_sats);

if remaining_value <= holder_dust_limit_satoshis {
None
} else {
Some(remaining_value)
}
}

#[allow(dead_code)] // TODO(dual_funding): Remove once begin_interactive_funding_tx_construction() is used
fn maybe_add_funding_change_output<SP: Deref>(signer_provider: &SP, is_initiator: bool,
our_funding_satoshis: u64, funding_inputs_prev_outputs: &Vec<TxOut>,
Expand Down Expand Up @@ -10329,9 +10293,8 @@ mod tests {
use crate::ln::channel_keys::{RevocationKey, RevocationBasepoint};
use crate::ln::channelmanager::{self, HTLCSource, PaymentId};
use crate::ln::channel::InitFeatures;
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};
use crate::ln::channel::{AwaitingChannelReadyFlags, Channel, ChannelState, InboundHTLCOutput, OutboundV1Channel, InboundV1Channel, OutboundHTLCOutput, InboundHTLCState, OutboundHTLCState, HTLCCandidate, HTLCInitiator, HTLCUpdateAwaitingACK, commit_tx_fee_sat};
use crate::ln::channel::{MAX_FUNDING_SATOSHIS_NO_WUMBO, TOTAL_BITCOIN_SUPPLY_SATOSHIS, MIN_THEIR_CHAN_RESERVE_SATOSHIS};
use crate::ln::interactivetxs::{OutputOwned, SharedOwnedOutput};
use crate::types::features::{ChannelFeatures, ChannelTypeFeatures, NodeFeatures};
use crate::ln::msgs;
use crate::ln::msgs::{ChannelUpdate, DecodeError, UnsignedChannelUpdate, MAX_VALUE_MSAT};
Expand Down Expand Up @@ -12094,83 +12057,4 @@ mod tests {
assert_eq!(node_a_chan.context.channel_state, ChannelState::AwaitingChannelReady(AwaitingChannelReadyFlags::THEIR_CHANNEL_READY));
assert!(node_a_chan.check_get_channel_ready(0, &&logger).is_some());
}

#[test]
fn test_need_to_add_funding_change_output_open() {
let input_prevouts = vec![
TxOut { value: Amount::from_sat(70_000), script_pubkey: ScriptBuf::new()},
TxOut { value: Amount::from_sat(60_000), script_pubkey: ScriptBuf::new()},
];
let our_contributed = 110_000;
let txout = TxOut { value: Amount::from_sat(128_000), script_pubkey: ScriptBuf::new()};
let outputs = vec![OutputOwned::SharedControlFullyOwned(txout)];
let funding_feerate_sat_per_1000_weight = 3000;

let total_inputs: u64 = input_prevouts.iter().map(|o| o.value.to_sat()).sum();
let gross_change = total_inputs - our_contributed;
let fees = 1746;
let common_fees = 126;
{
// There is leftover for change
let res = need_to_add_funding_change_output(true, our_contributed, &input_prevouts, &outputs, funding_feerate_sat_per_1000_weight, 300);
assert_eq!(res.unwrap(), gross_change - fees - common_fees);
}
{
// There is leftover for change, without common fees
let res = need_to_add_funding_change_output(false, our_contributed, &input_prevouts, &outputs, funding_feerate_sat_per_1000_weight, 300);
assert_eq!(res.unwrap(), gross_change - fees);
}
{
// Larger fee, smaller change
let res = need_to_add_funding_change_output(true, our_contributed, &input_prevouts, &outputs, 9000, 300);
assert_eq!(res.unwrap(), 14384);
}
{
// Insufficient inputs, no leftover
let res = need_to_add_funding_change_output(false, 130_000, &input_prevouts, &outputs, funding_feerate_sat_per_1000_weight, 300);
assert!(res.is_none());
}
{
// Very small leftover
let res = need_to_add_funding_change_output(false, 128_100, &input_prevouts, &outputs, funding_feerate_sat_per_1000_weight, 300);
assert!(res.is_none());
}
{
// Small leftover, but not dust
let res = need_to_add_funding_change_output(false, 128_100, &input_prevouts, &outputs, funding_feerate_sat_per_1000_weight, 100);
assert_eq!(res.unwrap(), 154);
}
}

#[test]
fn test_need_to_add_funding_change_output_splice() {
let input_prevouts = vec![
TxOut { value: Amount::from_sat(70_000), script_pubkey: ScriptBuf::new()},
TxOut { value: Amount::from_sat(60_000), script_pubkey: ScriptBuf::new()},
];
let our_contributed = 110_000;
let txout = TxOut { value: Amount::from_sat(148_000), script_pubkey: ScriptBuf::new()};
let outputs = vec![OutputOwned::Shared(SharedOwnedOutput::new(txout, our_contributed))];
let funding_feerate_sat_per_1000_weight = 3000;

let total_inputs: u64 = input_prevouts.iter().map(|o| o.value.to_sat()).sum();
let gross_change = total_inputs - our_contributed;
let fees = 1746;
let common_fees = 126;
{
// There is leftover for change
let res = need_to_add_funding_change_output(true, our_contributed, &input_prevouts, &outputs, funding_feerate_sat_per_1000_weight, 300);
assert_eq!(res.unwrap(), gross_change - fees - common_fees);
}
{
// Very small leftover
let res = need_to_add_funding_change_output(false, 128_100, &input_prevouts, &outputs, funding_feerate_sat_per_1000_weight, 300);
assert!(res.is_none());
}
{
// Small leftover, but not dust
let res = need_to_add_funding_change_output(false, 128_100, &input_prevouts, &outputs, funding_feerate_sat_per_1000_weight, 100);
assert_eq!(res.unwrap(), 154);
}
}
}
117 changes: 116 additions & 1 deletion lightning/src/ln/interactivetxs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1662,12 +1662,48 @@ impl InteractiveTxConstructor {
}
}

/// Determine whether with our given inputs, outputs, and intended contribution
/// a change output should be added or not, and if so, of waht size
#[allow(dead_code)] // TODO(dual_funding): Remove once begin_interactive_funding_tx_construction() is used
pub(super) fn need_to_add_funding_change_output(is_initiator: bool,
our_contribution: u64,
funding_inputs_prev_outputs: &Vec<TxOut>, funding_outputs: &Vec<OutputOwned>,
funding_feerate_sat_per_1000_weight: u32, holder_dust_limit_satoshis: u64,
) -> Option<u64> {
let our_funding_inputs_weight = funding_inputs_prev_outputs.iter().fold(0u64, |weight, prev_output| {
weight.saturating_add(estimate_input_weight(prev_output).to_wu())
});
let our_funding_outputs_weight = funding_outputs.iter().fold(0u64, |weight, out| {
weight.saturating_add(get_output_weight(&out.tx_out().script_pubkey).to_wu())
});
let our_contributed_weight = our_funding_outputs_weight.saturating_add(our_funding_inputs_weight);
let mut fees_sats = fee_for_weight(funding_feerate_sat_per_1000_weight, our_contributed_weight);

// If we are the initiator, we must pay for weight of all common fields in the funding transaction.
if is_initiator {
let common_fees = fee_for_weight(funding_feerate_sat_per_1000_weight, TX_COMMON_FIELDS_WEIGHT);
fees_sats = fees_sats.saturating_add(common_fees);
}

let total_input_satoshis: u64 = funding_inputs_prev_outputs.iter().map(|out| out.value.to_sat()).sum();

let remaining_value = total_input_satoshis
.saturating_sub(our_contribution)
.saturating_sub(fees_sats);

if remaining_value <= holder_dust_limit_satoshis {
None
} else {
Some(remaining_value)
}
}

#[cfg(test)]
mod tests {
use crate::chain::chaininterface::{fee_for_weight, FEERATE_FLOOR_SATS_PER_KW};
use crate::ln::channel::TOTAL_BITCOIN_SUPPLY_SATOSHIS;
use crate::ln::interactivetxs::{
generate_holder_serial_id, AbortReason, HandleTxCompleteValue, InteractiveTxConstructor,
generate_holder_serial_id, need_to_add_funding_change_output, AbortReason, HandleTxCompleteValue, InteractiveTxConstructor,
InteractiveTxConstructorArgs, InteractiveTxMessageSend, MAX_INPUTS_OUTPUTS_COUNT,
MAX_RECEIVED_TX_ADD_INPUT_COUNT, MAX_RECEIVED_TX_ADD_OUTPUT_COUNT,
};
Expand Down Expand Up @@ -2594,4 +2630,83 @@ mod tests {
assert_eq!(generate_holder_serial_id(&&entropy_source, true) % 2, 0);
assert_eq!(generate_holder_serial_id(&&entropy_source, false) % 2, 1)
}

#[test]
fn test_need_to_add_funding_change_output_open() {
let input_prevouts = vec![
TxOut { value: Amount::from_sat(70_000), script_pubkey: ScriptBuf::new()},
TxOut { value: Amount::from_sat(60_000), script_pubkey: ScriptBuf::new()},
];
let our_contributed = 110_000;
let txout = TxOut { value: Amount::from_sat(128_000), script_pubkey: ScriptBuf::new()};
let outputs = vec![OutputOwned::SharedControlFullyOwned(txout)];
let funding_feerate_sat_per_1000_weight = 3000;

let total_inputs: u64 = input_prevouts.iter().map(|o| o.value.to_sat()).sum();
let gross_change = total_inputs - our_contributed;
let fees = 1746;
let common_fees = 126;
{
// There is leftover for change
let res = need_to_add_funding_change_output(true, our_contributed, &input_prevouts, &outputs, funding_feerate_sat_per_1000_weight, 300);
assert_eq!(res.unwrap(), gross_change - fees - common_fees);
}
{
// There is leftover for change, without common fees
let res = need_to_add_funding_change_output(false, our_contributed, &input_prevouts, &outputs, funding_feerate_sat_per_1000_weight, 300);
assert_eq!(res.unwrap(), gross_change - fees);
}
{
// Larger fee, smaller change
let res = need_to_add_funding_change_output(true, our_contributed, &input_prevouts, &outputs, 9000, 300);
assert_eq!(res.unwrap(), 14384);
}
{
// Insufficient inputs, no leftover
let res = need_to_add_funding_change_output(false, 130_000, &input_prevouts, &outputs, funding_feerate_sat_per_1000_weight, 300);
assert!(res.is_none());
}
{
// Very small leftover
let res = need_to_add_funding_change_output(false, 128_100, &input_prevouts, &outputs, funding_feerate_sat_per_1000_weight, 300);
assert!(res.is_none());
}
{
// Small leftover, but not dust
let res = need_to_add_funding_change_output(false, 128_100, &input_prevouts, &outputs, funding_feerate_sat_per_1000_weight, 100);
assert_eq!(res.unwrap(), 154);
}
}

#[test]
fn test_need_to_add_funding_change_output_splice() {
let input_prevouts = vec![
TxOut { value: Amount::from_sat(70_000), script_pubkey: ScriptBuf::new()},
TxOut { value: Amount::from_sat(60_000), script_pubkey: ScriptBuf::new()},
];
let our_contributed = 110_000;
let txout = TxOut { value: Amount::from_sat(148_000), script_pubkey: ScriptBuf::new()};
let outputs = vec![OutputOwned::Shared(SharedOwnedOutput::new(txout, our_contributed))];
let funding_feerate_sat_per_1000_weight = 3000;

let total_inputs: u64 = input_prevouts.iter().map(|o| o.value.to_sat()).sum();
let gross_change = total_inputs - our_contributed;
let fees = 1746;
let common_fees = 126;
{
// There is leftover for change
let res = need_to_add_funding_change_output(true, our_contributed, &input_prevouts, &outputs, funding_feerate_sat_per_1000_weight, 300);
assert_eq!(res.unwrap(), gross_change - fees - common_fees);
}
{
// Very small leftover
let res = need_to_add_funding_change_output(false, 128_100, &input_prevouts, &outputs, funding_feerate_sat_per_1000_weight, 300);
assert!(res.is_none());
}
{
// Small leftover, but not dust
let res = need_to_add_funding_change_output(false, 128_100, &input_prevouts, &outputs, funding_feerate_sat_per_1000_weight, 100);
assert_eq!(res.unwrap(), 154);
}
}
}

0 comments on commit 4f7e41b

Please sign in to comment.