Skip to content

Commit 208351c

Browse files
fixup: don't broadcast funding tx if fee unpaid
Previously a client could trigger funding broadcast by claiming any payment unrelated to the HTLC that paid the negotiated opening fee. We removed the payment_claimed flag and gate manual broadcast on state == PaymentForwarded. When client_trusts_lsp is true we now require skimmed_fee_msat >= opening_fee_msat for the PendingPaymentForward -> PaymentForwarded transition, else we stay pending. Added tests for partial, non-paying and full fee.
1 parent 6a28644 commit 208351c

File tree

2 files changed

+532
-74
lines changed

2 files changed

+532
-74
lines changed

lightning-liquidity/src/lsps2/service.rs

Lines changed: 166 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -111,34 +111,24 @@ struct ForwardHTLCsAction(ChannelId, Vec<InterceptedHTLC>);
111111

112112
#[derive(Debug, Clone)]
113113
enum TrustModel {
114-
ClientTrustsLsp {
115-
funding_tx_broadcast_safe: bool,
116-
payment_claimed: bool,
117-
funding_tx: Option<Transaction>,
118-
},
114+
ClientTrustsLsp { funding_tx_broadcast_safe: bool, funding_tx: Option<Transaction> },
119115
LspTrustsClient,
120116
}
121117

122118
impl TrustModel {
123-
fn should_manually_broadcast(&self) -> bool {
119+
fn should_manually_broadcast(&self, state_is_payment_forward: bool) -> bool {
124120
match self {
125-
TrustModel::ClientTrustsLsp {
126-
funding_tx_broadcast_safe,
127-
payment_claimed,
128-
funding_tx,
129-
} => *funding_tx_broadcast_safe && *payment_claimed && funding_tx.is_some(),
121+
TrustModel::ClientTrustsLsp { funding_tx_broadcast_safe, funding_tx } => {
122+
*funding_tx_broadcast_safe && state_is_payment_forward && funding_tx.is_some()
123+
},
130124
// in lsp-trusts-client, the broadcast is automatic, so we never need to manually broadcast.
131125
TrustModel::LspTrustsClient => false,
132126
}
133127
}
134128

135129
fn new(client_trusts_lsp: bool) -> Self {
136130
if client_trusts_lsp {
137-
TrustModel::ClientTrustsLsp {
138-
funding_tx_broadcast_safe: false,
139-
payment_claimed: false,
140-
funding_tx: None,
141-
}
131+
TrustModel::ClientTrustsLsp { funding_tx_broadcast_safe: false, funding_tx: None }
142132
} else {
143133
TrustModel::LspTrustsClient
144134
}
@@ -166,17 +156,6 @@ impl TrustModel {
166156
}
167157
}
168158

169-
fn set_payment_claimed(&mut self, payment_claimed: bool) {
170-
match self {
171-
TrustModel::ClientTrustsLsp { payment_claimed: claimed, .. } => {
172-
*claimed = payment_claimed;
173-
},
174-
TrustModel::LspTrustsClient => {
175-
// No-op
176-
},
177-
}
178-
}
179-
180159
fn get_funding_tx(&self) -> Option<Transaction> {
181160
match self {
182161
TrustModel::ClientTrustsLsp { funding_tx, .. } => funding_tx.clone(),
@@ -435,15 +414,44 @@ impl OutboundJITChannelState {
435414
}
436415
}
437416

438-
fn payment_forwarded(&mut self) -> Result<Option<ForwardHTLCsAction>, ChannelStateError> {
417+
fn payment_forwarded(
418+
&mut self, skimmed_fee_msat: Option<u64>, client_trusts_lsp: bool,
419+
) -> Result<Option<ForwardHTLCsAction>, ChannelStateError> {
439420
match self {
440421
OutboundJITChannelState::PendingPaymentForward {
441-
payment_queue, channel_id, ..
422+
payment_queue,
423+
channel_id,
424+
opening_fee_msat,
442425
} => {
443-
let mut payment_queue = core::mem::take(payment_queue);
444-
let forward_htlcs = ForwardHTLCsAction(*channel_id, payment_queue.clear());
445-
*self = OutboundJITChannelState::PaymentForwarded { channel_id: *channel_id };
446-
Ok(Some(forward_htlcs))
426+
if !client_trusts_lsp {
427+
let mut pq = core::mem::take(payment_queue);
428+
let forward_htlcs = ForwardHTLCsAction(*channel_id, pq.clear());
429+
*self = OutboundJITChannelState::PaymentForwarded { channel_id: *channel_id };
430+
return Ok(Some(forward_htlcs));
431+
}
432+
433+
if skimmed_fee_msat.is_none() {
434+
*self = OutboundJITChannelState::PendingPaymentForward {
435+
payment_queue: core::mem::take(payment_queue),
436+
opening_fee_msat: *opening_fee_msat,
437+
channel_id: *channel_id,
438+
};
439+
return Ok(None);
440+
}
441+
442+
if skimmed_fee_msat.unwrap() >= *opening_fee_msat {
443+
let mut pq = core::mem::take(payment_queue);
444+
let forward_htlcs = ForwardHTLCsAction(*channel_id, pq.clear());
445+
*self = OutboundJITChannelState::PaymentForwarded { channel_id: *channel_id };
446+
Ok(Some(forward_htlcs))
447+
} else {
448+
*self = OutboundJITChannelState::PendingPaymentForward {
449+
payment_queue: core::mem::take(payment_queue),
450+
opening_fee_msat: *opening_fee_msat,
451+
channel_id: *channel_id,
452+
};
453+
Ok(None)
454+
}
447455
},
448456
OutboundJITChannelState::PaymentForwarded { channel_id } => {
449457
*self = OutboundJITChannelState::PaymentForwarded { channel_id: *channel_id };
@@ -499,11 +507,10 @@ impl OutboundJITChannel {
499507
Ok(action)
500508
}
501509

502-
fn payment_forwarded(&mut self) -> Result<Option<ForwardHTLCsAction>, LightningError> {
503-
let action = self.state.payment_forwarded()?;
504-
if action.is_some() {
505-
self.trust_model.set_payment_claimed(true);
506-
}
510+
fn payment_forwarded(
511+
&mut self, skimmed_fee_msat: Option<u64>,
512+
) -> Result<Option<ForwardHTLCsAction>, LightningError> {
513+
let action = self.state.payment_forwarded(skimmed_fee_msat, self.is_client_trusts_lsp())?;
507514
Ok(action)
508515
}
509516

@@ -527,14 +534,17 @@ impl OutboundJITChannel {
527534
}
528535

529536
fn should_broadcast_funding_transaction(&self) -> bool {
530-
self.trust_model.should_manually_broadcast()
537+
self.trust_model.should_manually_broadcast(matches!(
538+
self.state,
539+
OutboundJITChannelState::PaymentForwarded { .. }
540+
))
531541
}
532542

533543
fn get_funding_tx(&self) -> Option<Transaction> {
534544
self.trust_model.get_funding_tx()
535545
}
536546

537-
fn client_trusts_lsp(&self) -> bool {
547+
fn is_client_trusts_lsp(&self) -> bool {
538548
self.trust_model.is_client_trusts_lsp()
539549
}
540550
}
@@ -1004,15 +1014,22 @@ where
10041014
/// Forward [`Event::PaymentForwarded`] event parameter into this function.
10051015
///
10061016
/// Will register the forwarded payment as having paid the JIT channel fee, and forward any held
1007-
/// and future HTLCs for the SCID of the initial invoice. In the future, this will verify the
1008-
/// `skimmed_fee_msat` in [`Event::PaymentForwarded`].
1017+
/// and future HTLCs for the SCID of the initial invoice.
1018+
///
1019+
/// If client_trusts_lsp=true, this method will verify the `skimmed_fee_msat` in [`Event::PaymentForwarded`].
1020+
/// If the skimmed_fee_msat covers the opening_fee_msat, that means that the client has claimed the funds and
1021+
/// paid the promised fee, so the funding transaction becomes eligible for broadcast, and the
1022+
/// [`Event::BroadcastFundingTransaction`] is emitted.
10091023
///
10101024
/// Note that `next_channel_id` is required to be provided. Therefore, the corresponding
10111025
/// [`Event::PaymentForwarded`] events need to be generated and serialized by LDK versions
10121026
/// greater or equal to 0.0.107.
10131027
///
10141028
/// [`Event::PaymentForwarded`]: lightning::events::Event::PaymentForwarded
1015-
pub fn payment_forwarded(&self, next_channel_id: ChannelId) -> Result<(), APIError> {
1029+
/// [`Event::BroadcastFundingTransaction`]: crate::lsps2::event::LSPS2ServiceEvent::BroadcastFundingTransaction
1030+
pub fn payment_forwarded(
1031+
&self, next_channel_id: ChannelId, skimmed_fee_msat: Option<u64>,
1032+
) -> Result<(), APIError> {
10161033
if let Some(counterparty_node_id) =
10171034
self.peer_by_channel_id.read().unwrap().get(&next_channel_id)
10181035
{
@@ -1026,7 +1043,7 @@ where
10261043
if let Some(jit_channel) =
10271044
peer_state.outbound_channels_by_intercept_scid.get_mut(&intercept_scid)
10281045
{
1029-
match jit_channel.payment_forwarded() {
1046+
match jit_channel.payment_forwarded(skimmed_fee_msat) {
10301047
Ok(Some(ForwardHTLCsAction(channel_id, htlcs))) => {
10311048
for htlc in htlcs {
10321049
self.channel_manager.get_cm().forward_intercepted_htlc(
@@ -1569,7 +1586,7 @@ where
15691586
),
15701587
})?;
15711588

1572-
Ok(jit_channel.client_trusts_lsp())
1589+
Ok(jit_channel.is_client_trusts_lsp())
15731590
}
15741591

15751592
/// Called to store the funding transaction for a JIT channel.
@@ -1751,6 +1768,7 @@ mod tests {
17511768

17521769
use proptest::prelude::*;
17531770

1771+
use bitcoin::{absolute::LockTime, transaction::Version};
17541772
use core::str::FromStr;
17551773

17561774
const MAX_VALUE_MSAT: u64 = 21_000_000_0000_0000_000;
@@ -1987,7 +2005,7 @@ mod tests {
19872005
}
19882006
// Payment completes, queued payments get forwarded.
19892007
{
1990-
let action = state.payment_forwarded().unwrap();
2008+
let action = state.payment_forwarded(None, false).unwrap();
19912009
assert!(matches!(state, OutboundJITChannelState::PaymentForwarded { .. }));
19922010
match action {
19932011
Some(ForwardHTLCsAction(channel_id, htlcs)) => {
@@ -2122,7 +2140,7 @@ mod tests {
21222140
}
21232141
// Payment completes, queued payments get forwarded.
21242142
{
2125-
let action = state.payment_forwarded().unwrap();
2143+
let action = state.payment_forwarded(None, false).unwrap();
21262144
assert!(matches!(state, OutboundJITChannelState::PaymentForwarded { .. }));
21272145
match action {
21282146
Some(ForwardHTLCsAction(channel_id, htlcs)) => {
@@ -2158,4 +2176,105 @@ mod tests {
21582176
);
21592177
}
21602178
}
2179+
2180+
#[test]
2181+
fn broadcast_not_allowed_after_non_paying_fee_payment_claimed() {
2182+
let min_fee_msat: u64 = 12345;
2183+
let opening_fee_params = LSPS2OpeningFeeParams {
2184+
min_fee_msat,
2185+
proportional: 0,
2186+
valid_until: LSPSDateTime::from_str("2035-05-20T08:30:45Z").unwrap(),
2187+
min_lifetime: 144,
2188+
max_client_to_self_delay: 128,
2189+
min_payment_size_msat: 1,
2190+
max_payment_size_msat: 10_000_000_000,
2191+
promise: "ignore".to_string(),
2192+
};
2193+
2194+
let payment_size_msat = Some(1_000_000);
2195+
let user_channel_id = 4242u128;
2196+
let mut jit_channel = OutboundJITChannel::new(
2197+
payment_size_msat,
2198+
opening_fee_params.clone(),
2199+
user_channel_id,
2200+
true,
2201+
);
2202+
2203+
let opening_payment_hash = PaymentHash([42; 32]);
2204+
let htlcs_for_opening = [
2205+
InterceptedHTLC {
2206+
intercept_id: InterceptId([0; 32]),
2207+
expected_outbound_amount_msat: 400_000,
2208+
payment_hash: opening_payment_hash,
2209+
},
2210+
InterceptedHTLC {
2211+
intercept_id: InterceptId([1; 32]),
2212+
expected_outbound_amount_msat: 600_000,
2213+
payment_hash: opening_payment_hash,
2214+
},
2215+
];
2216+
2217+
assert!(jit_channel.htlc_intercepted(htlcs_for_opening[0].clone()).unwrap().is_none());
2218+
let action = jit_channel.htlc_intercepted(htlcs_for_opening[1].clone()).unwrap();
2219+
match action {
2220+
Some(HTLCInterceptedAction::OpenChannel(_)) => {},
2221+
other => panic!("Expected OpenChannel action, got {:?}", other),
2222+
}
2223+
2224+
let channel_id = ChannelId([7; 32]);
2225+
let ForwardPaymentAction(_, fee_payment) = jit_channel.channel_ready(channel_id).unwrap();
2226+
assert_eq!(fee_payment.opening_fee_msat, min_fee_msat);
2227+
2228+
let followup = jit_channel.htlc_handling_failed().unwrap();
2229+
assert!(followup.is_none());
2230+
2231+
let dummy_tx = Transaction {
2232+
version: Version(2),
2233+
lock_time: LockTime::ZERO,
2234+
input: vec![],
2235+
output: vec![],
2236+
};
2237+
jit_channel.set_funding_tx(dummy_tx);
2238+
jit_channel.set_funding_tx_broadcast_safe(true);
2239+
assert!(
2240+
!jit_channel.should_broadcast_funding_transaction(),
2241+
"Should not broadcast before any successful payment is claimed"
2242+
);
2243+
2244+
let second_payment_hash = PaymentHash([99; 32]);
2245+
let second_htlc = InterceptedHTLC {
2246+
intercept_id: InterceptId([2; 32]),
2247+
expected_outbound_amount_msat: min_fee_msat,
2248+
payment_hash: second_payment_hash,
2249+
};
2250+
let action2 = jit_channel.htlc_intercepted(second_htlc).unwrap();
2251+
let (forwarded_channel_id, fee_payment2) = match action2 {
2252+
Some(HTLCInterceptedAction::ForwardPayment(cid, fp)) => (cid, fp),
2253+
other => panic!("Expected ForwardPayment for second HTLC, got {:?}", other),
2254+
};
2255+
assert_eq!(forwarded_channel_id, channel_id);
2256+
assert_eq!(fee_payment2.opening_fee_msat, min_fee_msat);
2257+
2258+
assert!(
2259+
!jit_channel.should_broadcast_funding_transaction(),
2260+
"Should not broadcast before any successful payment is claimed"
2261+
);
2262+
2263+
// Forward a payment that is not enough to cover the fees
2264+
let _ = jit_channel.payment_forwarded(Some(min_fee_msat - 1)).unwrap();
2265+
2266+
assert!(
2267+
!jit_channel.should_broadcast_funding_transaction(),
2268+
"Should not broadcast before all the fees are collected"
2269+
);
2270+
2271+
let _ = jit_channel.payment_forwarded(Some(min_fee_msat)).unwrap();
2272+
2273+
let broadcast_allowed = jit_channel.should_broadcast_funding_transaction();
2274+
2275+
assert!(
2276+
broadcast_allowed,
2277+
"Broadcast was not allowed even though all the skimmed fees were collected"
2278+
);
2279+
}
21612280
}

0 commit comments

Comments
 (0)