-
Notifications
You must be signed in to change notification settings - Fork 417
Support client_trusts_lsp on LSPS2 #3838
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Support client_trusts_lsp on LSPS2 #3838
Conversation
👋 Thanks for assigning @TheBlueMatt as a reviewer! |
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #3838 +/- ##
==========================================
+ Coverage 88.73% 88.77% +0.04%
==========================================
Files 176 176
Lines 129042 129298 +256
Branches 129042 129298 +256
==========================================
+ Hits 114501 114781 +280
+ Misses 11939 11917 -22
+ Partials 2602 2600 -2
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
5d8508d
to
a038ab6
Compare
A few extra concerns: HTLCs routed over 0-conf channels might hit CLTV timeouts if the channel should be confirmed but isn’t yet, so if the user takes forever to claim the payment, then there could be some trouble there. Also, I'm not sure if it would be better to have new possible states to explicitly model the idea of having sent the channel_ready but the funding_tx is not broadcasted yet. Also sharing the trust_model state between 4 of the 5 possible states is not something I'm convinced. I have a few different options for this, but I'm open to comments and suggestions |
🔔 1st Reminder Hey @tnull! This PR has been waiting for your review. |
🔔 2nd Reminder Hey @tnull! This PR has been waiting for your review. |
🔔 3rd Reminder Hey @tnull! This PR has been waiting for your review. |
🔔 4th Reminder Hey @tnull! This PR has been waiting for your review. |
🔔 5th Reminder Hey @tnull! This PR has been waiting for your review. |
🔔 6th Reminder Hey @tnull! This PR has been waiting for your review. |
🔔 7th Reminder Hey @tnull! This PR has been waiting for your review. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for looking into this.
This generally makes sense to me, although we should really work out how we'd deal with operational channels for which we withhold the funding transaction broadcast.
HTLCs routed over 0-conf channels might hit CLTV timeouts if the channel should be > confirmed but isn’t yet, so if the user takes forever to claim the payment, then there
could be some trouble there.
Yes. IMO this means that we need to introduce proper (read: clean API, and tested) support for 'hosted channels', i.e., channels that are operational even though the funding transaction hasn't been confirmed yet. Not sure if @TheBlueMatt has an opinion here?
Also, I'm not sure if it would be better to have new possible states to explicitly model > the idea of having sent the channel_ready but the funding_tx is not broadcasted yet. > Also sharing the trust_model state between 4 of the 5 possible states is not > something I'm convinced. I have a few different options for this, but I'm open to > comments and suggestions
Yeah, as mentioned in the comments, it would def. be preferable if we could avoid the many clone
s. Also it's overall a lot of boilerplate for just three fields handed through, maybe there is a simpler approach?
@@ -11,6 +11,7 @@ | |||
|
|||
use alloc::string::{String, ToString}; | |||
use alloc::vec::Vec; | |||
use bitcoin::Transaction; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: Let's move this down to the other bitcoin
types.
TrustModel::ClientTrustsLsp { funding_tx_broadcast_safe, htlc_claimed, funding_tx } => { | ||
*funding_tx_broadcast_safe && *htlc_claimed && funding_tx.is_some() | ||
}, | ||
TrustModel::LspTrustsClient => false, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't this always be true
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
actually the method name is confusing. this should be false because in lsp-trusts-client, the broadcast is automatic, so we should return false
to avoid doing a manual broadcast
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixup commit changes this but will revert in a future commit, that will also include an e2e test
|
||
fn new(client_trusts_lsp: bool) -> Self { | ||
if client_trusts_lsp { | ||
return TrustModel::ClientTrustsLsp { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: Please avoid these explicit return
s here.
if client_trusts_lsp { | ||
return TrustModel::ClientTrustsLsp { | ||
funding_tx_broadcast_safe: false, | ||
htlc_claimed: false, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: Maybe payment_claimed
to align with the event type?
let mut payment_queue = core::mem::take(payment_queue); | ||
payment_queue.add_htlc(htlc); | ||
*self = OutboundJITChannelState::PendingChannelOpen { | ||
payment_queue, | ||
opening_fee_msat: *opening_fee_msat, | ||
trust_model: trust_model.clone(), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would be good if we could find a way to avoid these clone
s. Given that these are just two bools and the transaction option, I also wonder if it's indeed worth all the boilerplate, or if it might suffice to have these fields live on the state/channel objects directly.
Ok(()) | ||
} | ||
|
||
/// Called by ldk-node when the funding transaction is safe to broadcast. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note that in this context we don't know where this will be used, so we shouldn't assume LDK Node is the only consumer of this API.
lightning/src/ln/channelmanager.rs
Outdated
/// broadcast it manually. | ||
/// | ||
/// Used in LSPS2 on a client_trusts_lsp model | ||
CheckedManualBroadcast(Transaction), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: Let's move to the second positition here and elsewhere.
lightning/src/ln/channelmanager.rs
Outdated
/// # Warning | ||
/// Improper use of this method could lead to channel state inconsistencies. | ||
/// Ensure the transaction being broadcast is valid and expected by LDK. | ||
pub fn unsafe_broadcast_transaction(&self, tx: &Transaction) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, I'm not sure if this would qualify for the unsafe_
prefix and the Warning
. It's really just the normal flow, just that we leave broadcasting to the user instead of using the BroadcasterInterface
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we leave broadcasting to the user instead of using the BroadcasterInterface
actually, I think it would be good to emit an event ClientPaidSoPleaseBroadcastTransactionNow instead of doing it automatically, or even better, reuse the LdkEvent::FundingTxBroadcastSafe, which is literally the event used to let the user know they should manually broadcast a transaction. I will investigate if that's possible
lightning/src/routing/router.rs
Outdated
@@ -1075,7 +1075,7 @@ impl PaymentParameters { | |||
} | |||
|
|||
/// A struct for configuring parameters for routing the payment. | |||
#[derive(Clone, Copy)] | |||
#[derive(Clone, Copy, Debug)] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems unrelated?
@@ -35,7 +35,7 @@ lightning = { version = "0.2.0", path = "../lightning", default-features = false | |||
lightning-macros = { version = "0.2", path = "../lightning-macros", default-features = false } | |||
bitcoin = { version = "0.32.2", default-features = false } | |||
futures = { version = "0.3", optional = true } | |||
esplora-client = { version = "0.12", default-features = false, optional = true } | |||
esplora-client = { version = "0.11", default-features = false, optional = true } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please don't include unrelated changes here.
👋 The first review has been submitted! Do you think this PR is ready for a second reviewer? If so, click here to assign a second reviewer. |
a038ab6
to
eb5f42f
Compare
This needs a rebase now that #3662 landed. |
8d7da60
to
82a4bc1
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please let me know if/when you deem this ready for review!
I'm close, I have a half baked functional test. I want to finish that before putting this as ready for review |
fb259f4
to
6412945
Compare
ok, this should be ready now @tnull all comments are addressed in fixup commits. also I wrote a full end to end test that covers the client_trusts_lsp flow (directly in this repo, not in ldk-node, which was not possible before! 😄 ) . also added some documentation that explains how the client_trusts_lsp flow works. thanks! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Didn't review the integration tests.
lightning/src/ln/channelmanager.rs
Outdated
@@ -11546,6 +11568,15 @@ This indicates a bug inside LDK. Please report this error at https://github.com/ | |||
} | |||
} | |||
|
|||
/// Manually broadcast a transaction using the internal transaction broadcaster. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't really see the point of a method that just calls back into a user-provided interface? The caller in lightning-liquidity should probably just have a reference to the broadcaster.
lightning/src/ln/channelmanager.rs
Outdated
@@ -5767,6 +5775,18 @@ where | |||
self.batch_funding_transaction_generated_intern(temporary_chans, funding_type) | |||
} | |||
|
|||
/// Same as batch_funding_transaction_generated but it does not automatically broadcast the funding transaction |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Isn't this the same as funding_transaction_generated
? Also, this should be a link and at least link to Event::FundingTxBroadcastSafe
ClientTrustsLsp { | ||
funding_tx_broadcast_safe: bool, | ||
payment_claimed: bool, | ||
funding_tx: Option<Arc<Transaction>>, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't see a reason this should be in an Arc.
@@ -414,6 +500,9 @@ impl OutboundJITChannel { | |||
|
|||
fn payment_forwarded(&mut self) -> Result<Option<ForwardHTLCsAction>, LightningError> { | |||
let action = self.state.payment_forwarded()?; | |||
if action.is_some() { | |||
self.trust_model.set_payment_claimed(true); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think we currently test whether the HTLC that caused us to open the channel was forwarded or just any channel. I imagine someone could get enough in pending HTLCs to get a channel, then route a single sat to themselves, claim that, and get the funding broadcasted (this should probably be explicitly tested).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what I will do here is track an outstanding_opening_fee_msat
(this is the amount of fees that the client promised to pay), and as the PaymentForward
s come, I will substract the skimmed fees from it, until it's <= 0
once it's <= 0, it means that it's now safe for the LSP to proceed and broadcast the funding tx.
I did it this way because the PaymentForward does not tell what htlc was forwarded (I guess that's intentional?), so I cannot tell what HTLC exactly the client is claiming. it just tells me the channel and the skimmed fees.
I have the code ready, I'm improving the tests to cover some edge cases.
@TheBlueMatt please let me know if this makes sense conceptually or if I'd need to do something else
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I actually went with a simple approach without a paid fees accumulator FYI
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please correct me if I’m missing something, but I don’t think this attack is actually possible.
someone could get enough in pending HTLCs to get a channel, then route a single sat to themselves, claim that, and get the funding broadcasted
If the client already has a PaymentClaimable that would cover the opening fee once claimed but instead routes a 1 sat payment to themselves through the service, the service will intercept that 1 sat HTLC and do nothing (won’t forward it) because the state machine is in PendingPaymentForward waiting for the earlier payment to resolve.
If the client fails the earlier HTLC instead of claiming it and then routes a 1 sat payment to themselves, the state machine will be in PendingPayment. When the service intercepts the 1 sat payment, it checks
whether the queued HTLCs meet opening_fee_msat. A single sat doesn’t, so nothing is forwarded.
The only real risk is if the caller feeds incorrect PaymentForwarded events to the payment_forwarded function, but I don't think we should worry about this right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If the client already has a PaymentClaimable that would cover the opening fee once claimed but instead routes a 1 sat payment to themselves through the service, the service will intercept that 1 sat HTLC and do nothing (won’t forward it) because the state machine is in PendingPaymentForward waiting for the earlier payment to resolve.
Currently, because LDK doesn't support full HTLC interception, the client can bypass this with an HTLC using the chanel's SCID alias rather than the forwarding SCID.
} | ||
|
||
/// Called when the funding transaction is safe to broadcast. | ||
/// This marks the funding_tx_broadcast_safe flag as true for the given user_channel_id. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
funding_tx_broadcast_safe
this isnt a public thing so shouldnt appear in docs.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I will improve the name and docs of this function, but I'm still confused with what you mean by "funding_tx_broadcast_safe is not a public thing"
I'm making it necessary to call funding_tx_broadcast_safe()
BEFORE being able to broadcast the funding tx. is this incorrect?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry, I meant "the funding_tx_broadcast_safe flag", which isn't public, specifically.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the new method funding_transaction_generated_manual_broadcast
is following the same guidance as unsafe_manual_funding_transaction_generated
for the broadcast logic.
from the unsafe_manual_funding_transaction_generated
docs:
/// Note that if this method is called successfully, the funding transaction won't be
/// broadcasted and you are expected to broadcast it manually when receiving the
/// [`Event::FundingTxBroadcastSafe`] event.
so I made it mandatory that the user needs to call the LSPS2/service when they receive the FundingTxBroadcastSafe event, as a pre-requisite for broadcasting the funding tx.
I would love to get rid of this, but I don't know if it's possible. I will explore if it is though
Ok(()) | ||
} | ||
|
||
/// Called when the funding transaction is safe to broadcast. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This needs a discussion of what "safe to broadcast" means, cause its not actually "safe to broadcast" in the context of LSPS, only in the context of lightning. Should at least link to the lightning
Event
.
7fd8c8c
to
644cec2
Compare
@@ -631,7 +631,7 @@ use futures_util::{dummy_waker, Joiner, OptionalSelector, Selector, SelectorOutp | |||
/// # type P2PGossipSync<UL> = lightning::routing::gossip::P2PGossipSync<Arc<NetworkGraph>, Arc<UL>, Arc<Logger>>; | |||
/// # type ChannelManager<B, F, FE> = lightning::ln::channelmanager::SimpleArcChannelManager<ChainMonitor<B, F, FE>, B, FE, Logger>; | |||
/// # type OnionMessenger<B, F, FE> = lightning::onion_message::messenger::OnionMessenger<Arc<lightning::sign::KeysManager>, Arc<lightning::sign::KeysManager>, Arc<Logger>, Arc<ChannelManager<B, F, FE>>, Arc<lightning::onion_message::messenger::DefaultMessageRouter<Arc<NetworkGraph>, Arc<Logger>, Arc<lightning::sign::KeysManager>>>, Arc<ChannelManager<B, F, FE>>, lightning::ln::peer_handler::IgnoringMessageHandler, lightning::ln::peer_handler::IgnoringMessageHandler, lightning::ln::peer_handler::IgnoringMessageHandler>; | |||
/// # type LiquidityManager<B, F, FE> = lightning_liquidity::LiquidityManager<Arc<lightning::sign::KeysManager>, Arc<lightning::sign::KeysManager>, Arc<ChannelManager<B, F, FE>>, Arc<F>, Arc<DefaultTimeProvider>>; | |||
/// # type LiquidityManager<B, F, FE> = lightning_liquidity::LiquidityManager<Arc<lightning::sign::KeysManager>, Arc<lightning::sign::KeysManager>, Arc<ChannelManager<B, F, FE>>, Arc<F>, Arc<DefaultTimeProvider>, Arc<lightning::chain::chaininterface::BroadcasterInterface>>; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Seems this makes the doctests CI unhappy.
208351c
to
37066e4
Compare
alright, comments are addressed in fixup commits and CI is passing. let me know what you think, thanks! @TheBlueMatt @tnull |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Took a ~high level pass. Have yet to fully review all the details (esp. in conjunction with the LDK Node counterpart), but here are some comments.
In particular, I think there is a misconception: LSP-trusts-client doesn't mean we don't verify the fees paid, it's only that we broadcast immediately instead of waiting for the client to claim sufficient parts to pay for the channel open.
return Ok(None); | ||
} | ||
|
||
if skimmed_fee_msat.unwrap() >= *opening_fee_msat { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Never unwrap
on something like this in production code. Please see the API of LSPS2ServiceHandler::payment_forwarded
, where we decidedly didn't make next_channel_id
an Option
, as it requires that the user unwrap
s if they are certain that their LDK version supports it.
let forward_htlcs = ForwardHTLCsAction(*channel_id, payment_queue.clear()); | ||
*self = OutboundJITChannelState::PaymentForwarded { channel_id: *channel_id }; | ||
Ok(Some(forward_htlcs)) | ||
if !client_trusts_lsp { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, even for LSP-trusts-client we only want to forward once the client has paid for the channel open.
return Ok(Some(forward_htlcs)); | ||
} | ||
|
||
if skimmed_fee_msat.is_none() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do we need this if
here, if we're duplicating it below?
/// `skimmed_fee_msat` in [`Event::PaymentForwarded`]. | ||
/// and future HTLCs for the SCID of the initial invoice. | ||
/// | ||
/// If client_trusts_lsp=true, this method will verify the `skimmed_fee_msat` in [`Event::PaymentForwarded`]. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, as mentioned above, we should always verify the skimmed fee before forwarding.
@@ -2,38 +2,59 @@ | |||
|
|||
mod common; | |||
|
|||
use common::{create_service_and_client_nodes, get_lsps_message, LSPSNodes, LiquidityNode}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not quite sure how a sudden reordering of imports relates to this issue? If we want to change the import order we usually apply, let's make it a different PR, and a systematic crate-wide change.
Ok(()) | ||
} | ||
|
||
/// This should be called when the event [`Event::FundingTxBroadcastSafe`] is received. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe we intended the lightning-liquidity
to support non-LDK backends, which would imply that we should describe what this means (the "that channel has gotten signatures for the funding tx and the lightning state machine is ready to broadcast, though the ultimately broadcast has to be done by the LSPS service to ensure we have received payment first").
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
37066e4
to
7cfa0b4
Compare
thanks for the comments! just pushed 2 fixup commits addressing the latest comments: the skimmed_fee validation now applies to both lsp_trusts_client and client_trusts_lsp, so on both flows we forward all payments after the client claims the payment and pays the fee, but only on client_trusts_lsp it triggers the broadcast_funding_tx also improved set_funding_tx_broadcast_safe docs, but there is still an open question related to that #3838 (comment) @TheBlueMatt |
🔔 1st Reminder Hey @TheBlueMatt! This PR has been waiting for your review. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Basically LGTM if we just drop the new broadcast event. Feel free to squash, in any case.
pub fn payment_forwarded(&self, next_channel_id: ChannelId) -> Result<(), APIError> { | ||
/// [`Event::BroadcastFundingTransaction`]: crate::lsps2::event::LSPS2ServiceEvent::BroadcastFundingTransaction | ||
pub fn payment_forwarded( | ||
&self, next_channel_id: ChannelId, skimmed_fee_msat: Option<u64>, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IMO skimmed_fee_msat
shouldn't be an Option
. That implies to a user that they can skip it. Its only an Option
in the corresponding Event
for serialization backwards compatibility, but we should push the user to simmed_fee_msat.unwrap_or(0)
.
/// | ||
/// This method should only be used when manual control over transaction | ||
/// broadcast timing is required (e.g. client_trusts_lsp=true) | ||
pub fn broadcast_transaction(&self, tx: &Transaction) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm really not sure what the point of this is. We're already holding a reference to the BroadcasterInterface
(now), so why are we creating an Event
with the sole purpose of making the user call a method back on the code that generated the event? Why can't we just do the broadcast directly and remove the event entirely? And, if we keep the event, what's the point of having a BroadcasterInterface
reference is its just used for a public method that proxies it.
push the user to simmed_fee_msat.unwrap_or(0)
7cfa0b4
to
eb77ece
Compare
thanks for the review @TheBlueMatt ! all fixups are squashed, and also addressed the latest comments in new fixup commits thanks!! |
…r broadcasts funding tx
eb77ece
to
90ea0f8
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixups LGTM, I'll leave it to @tnull to look at this one last time and request remaining fixups be squashed.
The feature
lightningdevkit/ldk-node#479
Currently, our LSPS2 service implementation only supports the lsp_trusts_client model, which means that "If the client does not claim the payment, and the funding transaction confirms, then the LSP will have its funds locked in a channel that was not paid for."
On a client_trusts_lsp model, the LSP will NOT broadcast the funding transaction until the client claims the payment.
The plan:
(This plan was validated and acknowledged by @tnull in private). There are differences between the plan and the implementation, but it roughly describes the approach.
LSPS2 Process & Events: These are handled the same way as before. No changes here.
When the OpenChannel event is emitted, ldk-node calls create_channel as usual. The key difference is: If client_trusts_lsp = true, after emitting the OpenChannel event, we start tracking the HTLC that our client will eventually claim.
Funding Transaction Broadcast Logic: The batch_funding_transaction_generated_intern function decides whether the funding transaction should be broadcast automatically. There are two existing funding types:
I will introduce a third type:
With this:
lsps2_service on ldk-node will now interact with lsps2_service on rust-lightning in two new key moments:
Changes:
funding_transaction_generated_manual_broadcast
on channel_manager. Uses FundingType::CheckedManualBroadcast, which validates but does not automatically broadcastchannel_needs_manual_broadcast
. This is used by ldk-node to know if funding_transaction_generated or funding_transaction_generated_manual_broadcast should be called when FundingGenerationReady event is triggeredstore_funding_transaction
. This is used by ldk-node when the funding transaction is created. We need to store it because the broadcast of the funding transaction may be deferred.funding_tx_broadcast_safe
. This is used by ldk-node when the FundingTxBroadcastSafe event is triggeredbroadcast_transaction_if_applies
from the lsps2/serviceLDK Node integration
In this PR lightningdevkit/ldk-node#572 on ldk-node, you can see that 2 tests are created that demonstrates the funcionality described above.
client_trusts_lsp=true
In the test, receive_via_jit_channel_manual_claim is called, the mempool is checked to assert that the funding transaction was not broadcasted yet (it should not because client_trusts_lsp=true, and the client has not claimed the htlc yet).
Then the client calls
claim_for_hash
, and the mempool is checked again, and now the funding transaction should be thereclient_trusts_lsp=false
In the test, receive_via_jit_channel_manual_claim is called, the mempool is checked to assert that the funding transaction was broadcasted (because the LSP trusts the client), even though the client has not claimed the htlc yet. In this case, the LSP was tricked, and it will have its funds locked in a channel that was not paid for.
Side note: for the tests to work I had to create a new
receive_via_jit_channel_manual_claim
so the client can manually claim the htlc using theclaim_for_hash
.