-
Notifications
You must be signed in to change notification settings - Fork 115
Channel splicing support #677
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?
Conversation
|
👋 Thanks for assigning @wpaulino as a reviewer! |
|
🔔 1st Reminder Hey @tnull @TheBlueMatt @wpaulino! This PR has been waiting for your review. |
2 similar comments
|
🔔 1st Reminder Hey @tnull @TheBlueMatt @wpaulino! This PR has been waiting for your review. |
|
🔔 1st Reminder Hey @tnull @TheBlueMatt @wpaulino! 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!
Looks mostly good, just a few comments. This will need bindings support, too, and unfortunately also need a rebase by now. Sorry!
| } else { | ||
| log_error!( | ||
| self.logger, | ||
| "Channel not found for user_channel_id: {:?} and counterparty: {}", |
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.
Pre-existing, but we should probably implement Display for UserChannelId, now opened #681
| Ok(txid) | ||
| } | ||
|
|
||
| pub(crate) fn select_confirmed_utxos( |
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 cool to be able to reuse this for impl CoinSelectionSource while dropping impl WalletSource going forward. What do we think the necessary changes would be that would allow for this? Probably just making claim_id optional?
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.
cc: @wpaulino
Hmm... are you saying have LDK Node's Wallet implement CoinSelectionSource directly? Would we implement UTXO locking there? Or are you assuming BDK's wallet will eventually implement this?
To re-use this code, yeah we can extract the Utxo from each FundingTxInput to use in CoinSelection. Though we need to expose that or have a conversion function. Currently, FundingTxInput::utxo is pub(super).
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... are you saying have LDK Node's
WalletimplementCoinSelectionSourcedirectly?
Yes, it might be a bit more efficient than the detour over WalletSource, and would allow us to reuse most code, IIUC.
Would we implement UTXO locking there? Or are you assuming BDK's wallet will eventually implement this?
We could, although I still prefer to wait until BDK ships it soon hopefully. Might be debatable if we need to implement it manually in the meantime. However, the UTXO locking discussion seems orthogonal to whether or not to use CoinSelectionSource, no?
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 could, although I still prefer to wait until BDK ships it soon hopefully. Might be debatable if we need to implement it manually in the meantime. However, the UTXO locking discussion seems orthogonal to whether or not to use
CoinSelectionSource, no?
IIUC, seems we'd replace the lightning::events::bump_transaction::Wallet parameterization in BumpTransactionEventHandler with LDK Node's Wallet. But the former has some sort of locking already. Or at least it prefers not to double spend when possible.
We may be able to maintain a list of OutPoints to pass to TxBuilder::unspendable (filtered by claim id), attempting with and without the list.
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 may be able to main a list of
OutPoints to pass toTxBuilder::unspendable(filtered by claim id), attempting with and without the list.
Yeah, though note that would require us to track a list of locked OutPoints whenever we generate any Transaction. We'd then need to drop entries whenever they are spent and re-add them in case of a reorg, etc. This might become very complicated real quick, if we want to do it 'properly'.
I think an alternative approach would be to just always act as if we already saw the transaction in the mempool, i.e., call Wallet::apply_unconfirmed_txs, and then the Wallet should do the right thing and eventually mark the transaction as evicted if it wouldn't end up in the mempool afterall.
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, maybe it would even make sense to go with the last described approach in this PR (also for non-splicing funding transactions). If we do, we might however finally need to handle DiscardFunding, too.
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 think an alternative approach would be to just always act as if we already saw the transaction in the mempool, i.e., call
Wallet::apply_unconfirmed_txs, and then theWalletshould do the right thing and eventually mark the transaction as evicted if it wouldn't end up in the mempool afterall.
Hmmm... would we do this when CoinSelectionSource::sign_psbt is called? We don't have the transaction in CoinSelectionSource::select_confirmed_utxos.
Hmm, maybe it would even make sense to go with the last described approach in this PR (also for non-splicing funding transactions). If we do, we might however finally need to handle
DiscardFunding, too.
Which approach?
src/config.rs
Outdated
| /// users should be aware of the backwards compatibility risk prior to using the functionality. | ||
| /// | ||
| /// [`Node`]: crate::Node | ||
| pub reject_inbound_splices: bool, |
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.
IIUC, contrary to the docs here and in LDK, this flag is necessary to still support downgrades, i.e., forwards, not backwards compatibility (we might want to fix this/make this more clear in the LDK docs).
However, we currently don't support downgrades in LDK Node anyways, so here we can just omit this flag.
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 think CI is still failing because reject_inbound_splices is still here even though it hasn't been added to the bindings.
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.
Right, I'm not sure how consistent we've been with using forward and backward compatibility correctly. For instance, IIUC, should this have said backward compatibility?
And these like reject_inbound_splices should have said forward compatibility?
https://github.com/lightningdevkit/rust-lightning/blob/17f7858f01f29ec904c5bddda799365f4f9ff71a/lightning/src/util/config.rs#L915
https://github.com/lightningdevkit/rust-lightning/blob/17f7858f01f29ec904c5bddda799365f4f9ff71a/lightning/src/util/config.rs#L941
Maybe it would be best to avoid "backward" / "forward" and just say "compatibility" when referencing an earlier version. Seems these could be confusing if it is unclear if the term is relative to older or newer version.
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 think CI is still failing because
reject_inbound_splicesis still here even though it hasn't been added to the bindings.
Yup, gonna drop the commit, so that will fix CI.
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.
should this have said backward compatibility
I think so, yes.
And these like
reject_inbound_splicesshould have said forward compatibility?
Yes, I think so, too.
Maybe it would be best to avoid "backward" / "forward" and just say "compatibility" when referencing an earlier version. Seems these could be confusing if it is unclear if the term is relative to older or newer version.
Yeah, that sounds reasonable. Seems there often is confusion around it and 'compatibility with versions prior' is much more precise anyways.
tests/integration_tests_rust.rs
Outdated
|
|
||
| #[test] | ||
| fn splice_channel() { | ||
| macro_rules! expect_splice_pending_event { |
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: Usually we'd add this to common/mod.rs.
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.
Since it wasn't being used there, having it there would give a build warning.
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.
That's a good point about the build warning.
To resolve that and utilize the macro more broadly, what do you think about adding a test for splice-in/splice-out assertions into do_channel_full_cycle? We could use the new expect_splice_pending_event! macro there, which would justify having it in common/mod.rs and make our full-cycle test more robust for splicing.
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.
Started looking into this. @tnull The channel funding transaction is recognized as an outbound on-chain payment for A -- presumably because we are spending from the wallet. If B were to splice out paying A (or whomever), should the splice funding transaction be considered an outbound on-chain payment for B? It does not currently since it spends from the funding txo.
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.
Started looking into this. @tnull The channel funding transaction is recognized as an outbound on-chain payment for A -- presumably because we are spending from the wallet. If B were to splice out paying A (or whomever), should the splice funding transaction be considered an outbound on-chain payment for B? It does not currently since it spends from the funding txo.
Hmm, yeah, unfortunately that will be the case prior to 0.3 when we get lightningdevkit/rust-lightning#3566. I think having splice-out counting as an outbound onchain transaction makes sense until then. Might be worth noting somewhere that this is the case though, and that PaymentKinds will change in the future. (Tbh. one reason why I think it would make sense to flag splicing support alpha/experimental, simply as the classification API is unstable currently).
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... so currently we don't have the Txid until we see an LdkEvent::FundingTransactionReadyForSigning, but we've lost the context of the transaction at that point. We can assume it is a splice (could be dual-funding later), but it might difficult to determine if it is a splice-out. Maybe simply seeing that none of the inputs belong to our wallet would be sufficient? We should only see the event if we have any contributions / are the initiator, IIRC.
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... so currently we don't have the
Txiduntil we see anLdkEvent::FundingTransactionReadyForSigning, but we've lost the context of the transaction at that point. We can assume it is a splice (could be dual-funding later), but it might difficult to determine if it is a splice-out. Maybe simply seeing that none of the inputs belong to our wallet would be sufficient? We should only see the event if we have any contributions / are the initiator, IIRC.
Okay. That makes me wonder even more if we should wait for LDK#3566 above to be able to classify them properly.
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, maybe let's just stick with adding splice-ins (which will be marked outbound payments) for now, while noting this behavior on the splice_in/splice_out methods under an 'Experimental API' section or similiar?
982ce34 to
57ad1c8
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.
Need to rebase still.
tests/integration_tests_rust.rs
Outdated
|
|
||
| #[test] | ||
| fn splice_channel() { | ||
| macro_rules! expect_splice_pending_event { |
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.
Since it wasn't being used there, having it there would give a build warning.
|
🔔 2nd Reminder Hey @TheBlueMatt @wpaulino! This PR has been waiting for your review. |
1 similar comment
|
🔔 2nd Reminder Hey @TheBlueMatt @wpaulino! This PR has been waiting for your review. |
When a channel is spliced, the existing funding transaction's output is spent and a new funding transaction output is formed. Once the splice is considered locked by both parties, LDK will emit a ChannelReady event which will include the new funding_txo. Additionally, the initial ChannelReady event now includes the original funding_txo. Include this data in LDK Node's ChannelReady event.
LDK introduced similar events with splicing. SplicePending is largely informational like ChannelPending. SpliceFailed indicates the used UTXOs can be reclaimed. This requires UTXO locking, which is not yet implemented.
When the interactive-tx construction protocol completes in LDK during splicing (and in the future dual-funding), LDK Node must provide signatures for any non-shared inputs belonging to its on-chain wallet. This commit implements this when handling the corresponding FundingTransactionReadyForSigning event.
Extract the funds availability checking logic from open_channel_inner into a separate method so that it can be reused for channel splicing.
Instead of closing and re-opening a channel when outbound liquidity is exhausted, splicing allows to adding more funds (splice-in) while keeping the channel operational. This commit implements splice-in using funds from the BDK on-chain wallet.
Instead of closing and re-opening a channel when on-chain funds are needed, splicing allows removing funds (splice-out) while keeping the channel operational. This commit implements splice-out sending funds to a user-provided on-chain address.
57ad1c8 to
281ebce
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.
I'm really excited to see Splicing being added to LDK-Node - this is a fantastic feature! I learned a lot reviewing this PR.
I've left two minor, non-blocking comments regarding the CI failure and a test organization suggestion (to utilize the new macro in do_channel_full_cycle).
Great work!
src/config.rs
Outdated
| /// users should be aware of the backwards compatibility risk prior to using the functionality. | ||
| /// | ||
| /// [`Node`]: crate::Node | ||
| pub reject_inbound_splices: bool, |
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 think CI is still failing because reject_inbound_splices is still here even though it hasn't been added to the bindings.
tests/integration_tests_rust.rs
Outdated
|
|
||
| #[test] | ||
| fn splice_channel() { | ||
| macro_rules! expect_splice_pending_event { |
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.
That's a good point about the build warning.
To resolve that and utilize the macro more broadly, what do you think about adding a test for splice-in/splice-out assertions into do_channel_full_cycle? We could use the new expect_splice_pending_event! macro there, which would justify having it in common/mod.rs and make our full-cycle test more robust for splicing.
Since LDK Node does not support downgrades, there's no need to have a Config parameter for accepting inbound splices. Instead, enable it by default.
281ebce to
adb59c8
Compare
LDK 0.2 added beta support for splicing. This PR exposes it in LDK Node as two new
Nodemethods:splice_inandsplice_out. Funds used for splicing in are taken from the on-chain BDK wallet. When splicing out, any on-chain address can be provided as the destination.Two new events are provided:
SplicePendingandSpliceFailed. The former is emitted once the new funding transaction has been broadcast. The latter indicates that any contributed inputs may be re-used, though this is not currently exposed. It should be used internally to unlock UTXOs that were intended to be spent.