Split splice initiation into two phases#4290
Split splice initiation into two phases#4290TheBlueMatt merged 7 commits intolightningdevkit:mainfrom
Conversation
|
👋 Thanks for assigning @TheBlueMatt as a reviewer! |
f5933e5 to
854e9ca
Compare
|
@TheBlueMatt @wpaulino Looking for some high-level feedback on the API introduced in the last commit. In summary:
The same mechanism can be used later for contributing inputs for counterparty-initiated splices or v2 channel opens since Test code still needs to be fixed up, and |
wpaulino
left a comment
There was a problem hiding this comment.
The API design LGTM, though there's one issue with WalletSource. One thing users need to keep in mind now is that from the moment they receive FundingNeeded, they need to act quickly to ensure the counterparty doesn't disconnect due to quiescence taking too long.
| fn list_confirmed_utxos(&self) -> Result<Vec<Utxo>, ()>; | ||
|
|
||
| /// | ||
| fn select_confirmed_utxos( |
There was a problem hiding this comment.
Adding this here now requires implementers to satisfy this method when, in the context of anchor channels, WalletSource is only intended to be used such that we perform coin selection on behalf of the user. Ideally, we also give users the option between choosing WalletSource/CoinSelectionSource when funding channels.
There was a problem hiding this comment.
Right, I guess I'm a bit confused why we can't use select_confirmed_utxos as-is? Indeed the claim_id is annoying, but we can make that either an enum across a ClaimId and some unit value describing a splice or just make it an Option. Aside from that it seems to be basically what we want.
There was a problem hiding this comment.
Adding this here now requires implementers to satisfy this method when, in the context of anchor channels,
WalletSourceis only intended to be used such that we perform coin selection on behalf of the user. Ideally, we also give users the option between choosingWalletSource/CoinSelectionSourcewhen funding channels.
Hmm... I see. Would a separate trait be desirable? Also, see my reply to @TheBlueMatt below.
Right, I guess I'm a bit confused why we can't use
select_confirmed_utxosas-is? Indeed theclaim_idis annoying, but we can make that either anenumacross aClaimIdand some unit value describing a splice or just make it anOption. Aside from that it seems to be basically what we want.
The return value also isn't compatible. It contains Utxos but we also need the previous tx and sequence number as part of each FundingTxInput. Though its constructor will give a default sequence number.
We could change CoinSelection to use FundingTxInput instead of Utxo, but that would be odd for use with the anchor context.
There was a problem hiding this comment.
Honestly that seems fine to me? We expect ~all of our users to want to use splicing, which implies they need to support the "return coin selection with full transactions" interface. So what if anchors throw away some of that data?
If we feel strongly about it we can add a new trait method that does return the full transactions and provide a default implementation for the current method so that those that really want to avoid always fetching the transaction data can.
There was a problem hiding this comment.
Right, I don't have a strong opinion, but note that Wallet's implementation of CoinSelectionSource::select_confirmed_utxos delegates to WalletSource::list_confirmed_utxos. So it might be expensive to use that abstraction. @wpaulino WDYT?
There was a problem hiding this comment.
I was thinking of keeping
CoinSelectionSourcethe same (with the full transaction data in the response, or with the default-impl-method described above) but changingWalletSourceso that we don't have to fetch all previous-transactions at the start.
Yeah, Wallet wraps a WalletSource and implements CoinSelectionSource by listing all the UTXOs and selecting from them. So WalletSource's interface would remain unchanged while CoinSelection would use FundingTxInput instead of Utxo. Which I guess means FundingTemplate::build should actually take a CoinSelectionSource.
Seems reasonable to require a sequence number in the response for that as well, even for anchors?
Hmm... in WalletSource::list_confirmed_utxos by adding a field to Utxto (and removing it from FundingTxInput)? Or by having the CoinSelectionSource implementation fill it in on FundingTxInput?
There was a problem hiding this comment.
So WalletSource's interface would remain unchanged
Wouldn't we need a WalletSource::get_previous_transaction_for_utxo method to fetch the full tx data for the UTXOs we selected?
Hmm... in WalletSource::list_confirmed_utxos by adding a field to Utxto (and removing it from FundingTxInput)? Or by having the CoinSelectionSource implementation fill it in on FundingTxInput?
ISTM we should replace Utxo with FundingTxInput since FundingTxInput has strictly more fields (it contains a Utxo!) and we'd move to returning FundingTxInput from the trait.
There was a problem hiding this comment.
Wouldn't we need a
WalletSource::get_previous_transaction_for_utxomethod to fetch the full tx data for the UTXOs we selected?
Right, we need another method for that.
ISTM we should replace
UtxowithFundingTxInputsinceFundingTxInputhas strictly more fields (it contains aUtxo!) and we'd move to returningFundingTxInputfrom the trait.
The question is more what should be setting Sequence? Either:
(1) Move it to Utxo and have WalletSource::list_confirmed_utxos set it since it returns Vec<Utxo>.
(2) Have CoinSelectionSource::select_confirmed_utxos set it since CoinSelection would now contain Vec<FundingTxInput>
We just can't replace Utxo with FundingTxInput in WalletSource::list_confirmed_utxos since we don't want to return the previous tx there.
There was a problem hiding this comment.
(1) Move it to Utxo and have WalletSource::list_confirmed_utxos set it since it returns Vec.
Presumably this. No reason to want it to not be possible in WalletSource.
There was a problem hiding this comment.
Done as discussed here and offline. I'm in the middle of updating the tests, but I've pushed an update for now.
TheBlueMatt
left a comment
There was a problem hiding this comment.
Aside from the above which-interface question I think the API is good.
854e9ca to
6d78c3f
Compare
|
🔔 1st Reminder Hey @TheBlueMatt @wpaulino! This PR has been waiting for your review. |
1 similar comment
|
🔔 1st Reminder Hey @TheBlueMatt @wpaulino! This PR has been waiting for your review. |
6d78c3f to
94b1aa9
Compare
|
🔔 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. |
|
🔔 3rd Reminder Hey @TheBlueMatt @wpaulino! This PR has been waiting for your review. |
1 similar comment
|
🔔 3rd Reminder Hey @TheBlueMatt @wpaulino! This PR has been waiting for your review. |
94b1aa9 to
c3f3453
Compare
lightning/src/ln/funding.rs
Outdated
| // FIXME: Should claim_id be an Option? | ||
| let claim_id = ClaimId([0; 32]); |
There was a problem hiding this comment.
Regarding the CoinSelectionSource API, do we want to make claim_id an Option?
lightning/src/ln/splicing_tests.rs
Outdated
| Amount::from_sat(383) | ||
| Amount::from_sat(385) | ||
| } else { | ||
| Amount::from_sat(384) | ||
| Amount::from_sat(386) |
There was a problem hiding this comment.
Seems select_confirmed_utxos_internal might be off on the change calculation because it's using the weight of the change output to compute additional fees instead of re-computing the total fees using the total weight when including a change output.
c3f3453 to
3253a99
Compare
|
🔔 4th Reminder Hey @TheBlueMatt @wpaulino! This PR has been waiting for your review. |
1 similar comment
|
🔔 4th Reminder Hey @TheBlueMatt @wpaulino! This PR has been waiting for your review. |
f2d9fa5 to
fcccbac
Compare
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #4290 +/- ##
==========================================
- Coverage 86.09% 85.89% -0.20%
==========================================
Files 156 156
Lines 103623 103963 +340
Branches 103623 103963 +340
==========================================
+ Hits 89211 89304 +93
- Misses 11895 12132 +237
- Partials 2517 2527 +10
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:
|
TheBlueMatt
left a comment
There was a problem hiding this comment.
API looks good. some misc comments i noted while skimming it
lightning/src/events/mod.rs
Outdated
| /// Indicates that funding is needed for a channel splice or a dual-funded channel open. | ||
| /// | ||
| /// The client should build a [`FundingContribution`] from the provided [`FundingTemplate`] and | ||
| /// pass it to [`ChannelManager::funding_contributed`]. |
There was a problem hiding this comment.
Needs a call-out to what to do if you actually don't want to splice anymore (ie on failure)
There was a problem hiding this comment.
Also maybe a note that the channel is hung waiting on our response, so we need to respond quickly.
There was a problem hiding this comment.
Should they simply not handle the event? We'll be in quiescence and timeout after DISCONNECT_PEER_AWAITING_RESPONSE_TICKS. Though it seems this now inadvertently (but maybe expectedly) now applies to us sending splice_init. So maybe some renaming is in order?
Or should we expose ChannelManager::exit_quiescence?
There was a problem hiding this comment.
Now that the presence of this event in the pending events queue determines whether or not a splice can be initiated, no action is needed from the user if they no longer want to splice.
lightning/src/ln/funding.rs
Outdated
| // FIXME: Should claim_id be an Option? | ||
| let claim_id = ClaimId([0; 32]); |
lightning/src/ln/funding.rs
Outdated
|
|
||
| /// Creates a `FundingContribution` from the template by using `wallet` to perform coin | ||
| /// selection with the given fee rate. | ||
| pub fn build_sync<W: Deref>( |
There was a problem hiding this comment.
Can we also have a method for building with a provided set of inputs rather than going through the trait?
There was a problem hiding this comment.
We could... or probably a CoinSelection so that a change output can be given.
lightning/src/ln/channel.rs
Outdated
| if self.context.channel_state.is_quiescent() { | ||
| return Err(APIError::APIMisuseError { | ||
| err: format!( | ||
| "Channel {} cannot be spliced as it is already quiescent", |
There was a problem hiding this comment.
Don't we want to support queuing up the splice to do afterwards?
There was a problem hiding this comment.
Hmmm... this was to handle the time after we've generated FundingNeeded when we no longer have a quiescent_action but are still quiescent waiting on the user to call funding_contributed. But then we can't differentiate this from counterparty-initiated quiescence. Maybe we need to make a placeholder QuiescentAction for when we are waiting on the user to respond? Something like AwaitingFundingContribution, which could also be checked when funding_contributed is called.
There was a problem hiding this comment.
Regarding error handling, since the FundingContribution is taken by value we need to generate a SpliceFailed event to allow the user to unlock the UTXOs. However, some failure may be result of misuse (e.g., wrong channel / counterparty, unexpected funding) or bad timing (e.g., peer already disconnected timing out quiescence).
In those cases, it doesn't make sense to generate SpliceFailed. Maybe we need to use DiscardFunding for some of these cases? We'd need another FundingInfo variant, though.
Otherwise, we'd need to return the UTXOs back to the caller, which we wanted to avoid.
Thoughts?
There was a problem hiding this comment.
I'll leave this for a follow-up PR.
There was a problem hiding this comment.
Seems like the motivation for this is no longer relevant now that FundingNeeded comes before quiescence. We should definitely allow queueing the splice when quiescent, but only if we're not the initiator and/or we're attempting a different quiescent protocol (not possible yet). If we're already splicing, we should suggest doing an RBF once that's supported.
There was a problem hiding this comment.
Seems like the motivation for this is no longer relevant now that
FundingNeededcomes before quiescence.
The misuse cases still hold when calling funding_contributed. Maybe they are less likely though given it would essentially be a programmer error? Although, is it still possible when a channel is closed before calling funding_contributed?
We should definitely allow queueing the splice when quiescent, but only if we're not the initiator and/or we're attempting a different quiescent protocol (not possible yet). If we're already splicing, we should suggest doing an RBF once that's supported.
Ok, that should work, but note that once the splice is pending and we are no longer quiescent, we've lost whether we had been the initiator. So we'd need to allow queueing another splice then, which I think should be fine.
When implementing RBF, either side could include the contributions from the enqueued action.
lightning/src/ln/channel.rs
Outdated
| ) -> Result<msgs::SpliceInit, SpliceFundingFailed> | ||
| where | ||
| L::Target: Logger, | ||
| { |
There was a problem hiding this comment.
presumably we need to check that we're quiescent and its our turn to talk?
There was a problem hiding this comment.
Based on offline discussion, we are no longer waiting to enter quiescence to generate FundingNeeded. It will instead be initiated when funding_contributed is called.
|
Given that the new API is to be reused for RBF, I wrote up a proposal to consider with this PR. @TheBlueMatt @wpaulino Looking for feedback / alternative ideas before continuing that work. Observations
Proposal
Questions
|
88ab4ca to
3c86404
Compare
FundingNeeded event for splicing|
After some offline discussion, we decided to drop the |
A forthcoming commit will change CoinSelection to include FundingTxInput instead of Utxo, though the former will probably be renamed. This is so CoinSelectionSource can be used when funding a splice. Further updating WalletSource to use FundingTxInput is not desirable, however, as it would result in looking up each confirmed UTXOs previous transaction even if it is not selected. See Wallet's implementation of CoinSelectionSource, which delegates to WalletSource for listing all confirmed UTXOs. This commit moves FundingTxInput::sequence to Utxo, and thus the responsibility for setting it to WalletSource implementations. Doing so will allow Wallet's CoinSelectionSource implementation to delegate looking up previous transactions to WalletSource without having to explicitly set the sequence on any FundingTxInput.
3c86404 to
7e636ad
Compare
|
Rebased to fix build error from removal of |
7e636ad to
7abb73a
Compare
|
🔔 4th Reminder Hey @TheBlueMatt @wpaulino! This PR has been waiting for your review. |
7abb73a to
05067c5
Compare
|
🔔 2nd Reminder Hey @TheBlueMatt @wpaulino! This PR has been waiting for your review. |
|
🔔 5th Reminder Hey @TheBlueMatt @wpaulino! This PR has been waiting for your review. |
TheBlueMatt
left a comment
There was a problem hiding this comment.
A handful of comments that are worth following up on, but I'd much rather land this as-is so that various conflicts (eg chanmon_consistency updates) can be avoided and we can get this train moving.
| } | ||
|
|
||
| let prev_funding_input = self.funding.to_splice_funding_input(); | ||
| let is_initiator = contribution.is_initiator(); |
There was a problem hiding this comment.
We should always be the initiator at this point - we're in if is_holder_quiescence_initiator {. Probably we should debug-assert that here because otherwise we probably have the wrong funding.
There was a problem hiding this comment.
I don't think we'll ever need is_initiator in a QuiescentAction now that we won't use an event for acceptors to contribute. Instead, we'll set it later based on how the QuiescentAction ends up being used.
There was a problem hiding this comment.
FWIW, I don't think we have a good way to let the acceptor to not over pay fees generally. By the time we know who won quiescence, the coins have already been selected. We could try adjusting the change output once we know we've lost, but that assumes there is a change output to adjust.
| let value_added = self.value_added.to_signed().unwrap_or(SignedAmount::MAX); | ||
| /// spliced out than spliced in. Fees will be deducted from the expected splice-out amount | ||
| /// if no inputs were included. | ||
| pub fn net_value(&self) -> Result<SignedAmount, String> { |
There was a problem hiding this comment.
Certainly can be in a followup but imo this should be infallible and checked when constructing the contribution.
There was a problem hiding this comment.
The problem is that we can't perform some checks until after coin selection. But if we fail at that point, the user isn't able to reclaim their UTXOs.
There was a problem hiding this comment.
Ended up splitting the checks into two, some performed when building the FundingContribution while the others are in a separate validate method called in funding_contributed. This let's us make net_value infallible when handling stfu.
| &self, channel_id: &ChannelId, counterparty_node_id: &PublicKey, feerate: FeeRate, | ||
| ) -> Result<FundingTemplate, APIError> { | ||
| let mut res = Err(APIError::APIMisuseError { err: String::new() }); | ||
| PersistenceNotifierGuard::optionally_notify(self, || { |
There was a problem hiding this comment.
nit: we dont have to block persistence just reading channel fields now (and can presumably drop internal_splice_channel)
| shared_input: Option<Input>, | ||
|
|
||
| /// The fee rate to use for coin selection. | ||
| feerate: FeeRate, |
There was a problem hiding this comment.
Do we need this here or can we pass it when building the actual funding?
There was a problem hiding this comment.
When initiating an RBF later, we'd like the returned FundingTemplate to use a FeeRate that meets the spec requirements instead of asking the user to calculate it correctly.
There was a problem hiding this comment.
Wouldn't they still provide a feerate upfront in splice_channel? I guess if they provide something higher, we'd keep it the same, but bump it to the minimum required if it's lower.
There was a problem hiding this comment.
Right, though there are two scenarios. They call splice_channel as you said and we try to RBF to add their contribution. Or they call something like initiate_rbf because the splice isn't confirming and they need to run coin selection again with a higher fee rate.
There was a problem hiding this comment.
ISTM we could have a minimum-feerate in the template, but presumably even in the rbf case they might want to rbf to a higher feerate than some minimum required calculation so we'll want to enable that anyway?
There was a problem hiding this comment.
If we don't have them specify a fee rate in splice_channel, what should the resulting template use for the minimum fee rate? Something from the fee estimator? Presuming there is no outstanding counterparty-initiated splice.
| wallet, | ||
| ) | ||
| } | ||
| } |
There was a problem hiding this comment.
I noted it previously but in a followup we do need a "manual builder" here that doesn't rely on a coin selection.
|
|
||
| /// Creates a [`FundingContribution`] for both adding and removing funds from a channel using | ||
| /// `wallet` to perform coin selection. | ||
| pub fn splice_in_and_out_sync<W: Deref>( |
There was a problem hiding this comment.
We dropped the Deref pattern and should avoid adding it in new code.
There was a problem hiding this comment.
Having trouble making this work because of the 'a lifetime on the trait.
error[E0309]: the parameter type `T` may not live long enough
--> lightning/src/util/wallet_utils.rs:408:3
|
404 | fn select_confirmed_utxos<'a>(
| -- the parameter type `T` must be valid for the lifetime `'a` as defined here...
...
408 | self.deref().select_confirmed_utxos(claim_id, must_spend, must_pay_to, target_feerate_sat_per_1000_weight, max_tx_weight)
| ^^^^^^^^^^^^ ...so that the type `T` will meet its required lifetime bounds
|
help: consider adding an explicit lifetime bound
|
407 | ) -> impl Future<Output = Result<CoinSelection, ()>> + MaybeSend + 'a where T: 'a {
| +++++++++++
But the suggestion won't work because it causes the trait signature to no longer match. And there's no T in the trait to allow to match.
error[E0195]: lifetime parameters or bounds on method `select_confirmed_utxos` do not match the trait declaration
--> lightning/src/util/wallet_utils.rs:404:27
|
389 | fn select_confirmed_utxos<'a>(
| ---- lifetimes in impl do not match this method in trait
...
404 | fn select_confirmed_utxos<'a>(
| ^^^^ lifetimes do not match method in trait
...
407 | ) -> impl Future<Output = Result<CoinSelection, ()>> + MaybeSend + 'a where T: 'a {
| ----------- this `where` clause might not match the one in the trait
There was a problem hiding this comment.
Hmm, maybe an issue in some of your later PRs, but this builds fine on this PR:
diff --git a/lightning/src/events/bump_transaction/mod.rs b/lightning/src/events/bump_transaction/mod.rs
index c783e96381..909bf15fbe 100644
--- a/lightning/src/events/bump_transaction/mod.rs
+++ b/lightning/src/events/bump_transaction/mod.rs
@@ -443,6 +443,26 @@ pub trait CoinSelectionSource {
) -> impl Future<Output = Result<Transaction, ()>> + MaybeSend + 'a;
}
+impl<C: Deref> CoinSelectionSource for C
+where
+ C::Target: CoinSelectionSource,
+{
+ fn select_confirmed_utxos<'a>(
+ &'a self, claim_id: Option<ClaimId>, must_spend: Vec<Input>, must_pay_to: &'a [TxOut],
+ target_feerate: u32, max_weight: u64,
+ ) -> impl Future<Output = Result<CoinSelection, ()>> + MaybeSend + 'a {
+ self
+ .deref()
+ .select_confirmed_utxos(claim_id, must_spend, must_pay_to, target_feerate, max_weight)
+ }
+
+ fn sign_psbt<'a>(
+ &'a self, psbt: Psbt,
+ ) -> impl Future<Output = Result<Transaction, ()>> + MaybeSend + 'a {
+ self.deref().sign_psbt(psbt)
+ }
+}
+
/// An alternative to [`CoinSelectionSource`] that can be implemented and used along [`Wallet`] to
/// provide a default implementation to [`CoinSelectionSource`].
///
@@ -763,12 +783,10 @@ where
// Note that updates to documentation on this struct should be copied to the synchronous version.
pub struct BumpTransactionEventHandler<
B: BroadcasterInterface,
- C: Deref,
+ C: CoinSelectionSource,
SP: SignerProvider,
L: Logger,
-> where
- C::Target: CoinSelectionSource,
-{
+> {
broadcaster: B,
utxo_source: C,
signer_provider: SP,
@@ -776,10 +794,8 @@ pub struct BumpTransactionEventHandler<
secp: Secp256k1<secp256k1::All>,
}
-impl<B: BroadcasterInterface, C: Deref, SP: SignerProvider, L: Logger>
+impl<B: BroadcasterInterface, C: CoinSelectionSource, SP: SignerProvider, L: Logger>
BumpTransactionEventHandler<B, C, SP, L>
-where
- C::Target: CoinSelectionSource,
{
/// Returns a new instance capable of handling [`Event::BumpTransaction`] events.
///
diff --git a/lightning/src/events/bump_transaction/sync.rs b/lightning/src/events/bump_transaction/sync.rs
index 39088bb0e9..f800fc98e6 100644
--- a/lightning/src/events/bump_transaction/sync.rs
+++ b/lightning/src/events/bump_transaction/sync.rs
@@ -226,26 +226,27 @@ pub trait CoinSelectionSourceSync {
fn sign_psbt(&self, psbt: Psbt) -> Result<Transaction, ()>;
}
-struct CoinSelectionSourceSyncWrapper<T: Deref>(T)
+impl<C: Deref> CoinSelectionSourceSync for C
where
- T::Target: CoinSelectionSourceSync;
-
-// Implement `Deref` directly on CoinSelectionSourceSyncWrapper so that it can be used directly
-// below, rather than via a wrapper.
-impl<T: Deref> Deref for CoinSelectionSourceSyncWrapper<T>
-where
- T::Target: CoinSelectionSourceSync,
+ C::Target: CoinSelectionSourceSync,
{
- type Target = Self;
- fn deref(&self) -> &Self {
+ fn select_confirmed_utxos(
+ &self, claim_id: Option<ClaimId>, must_spend: Vec<Input>, must_pay_to: &[TxOut],
+ target_feerate: u32, max_weight: u64,
+ ) -> Result<CoinSelection, ()> {
self
+ .deref()
+ .select_confirmed_utxos(claim_id, must_spend, must_pay_to, target_feerate, max_weight)
+ }
+
+ fn sign_psbt(&self, psbt: Psbt) -> Result<Transaction, ()> {
+ self.deref().sign_psbt(psbt)
}
}
-impl<T: Deref> CoinSelectionSource for CoinSelectionSourceSyncWrapper<T>
-where
- T::Target: CoinSelectionSourceSync,
-{
+struct CoinSelectionSourceSyncWrapper<T: CoinSelectionSourceSync>(T);
+
+impl<T: CoinSelectionSourceSync> CoinSelectionSource for CoinSelectionSourceSyncWrapper<T> {
fn select_confirmed_utxos<'a>(
&'a self, claim_id: Option<ClaimId>, must_spend: Vec<Input>, must_pay_to: &'a [TxOut],
target_feerate_sat_per_1000_weight: u32, max_tx_weight: u64,
@@ -278,20 +279,16 @@ where
// Note that updates to documentation on this struct should be copied to the synchronous version.
pub struct BumpTransactionEventHandlerSync<
B: BroadcasterInterface,
- C: Deref,
+ C: CoinSelectionSourceSync,
SP: SignerProvider,
L: Logger,
-> where
- C::Target: CoinSelectionSourceSync,
-{
+> {
bump_transaction_event_handler:
BumpTransactionEventHandler<B, CoinSelectionSourceSyncWrapper<C>, SP, L>,
}
-impl<B: BroadcasterInterface, C: Deref, SP: SignerProvider, L: Logger>
+impl<B: BroadcasterInterface, C: CoinSelectionSourceSync, SP: SignerProvider, L: Logger>
BumpTransactionEventHandlerSync<B, C, SP, L>
-where
- C::Target: CoinSelectionSourceSync,
{
/// Constructs a new instance of [`BumpTransactionEventHandlerSync`].
pub fn new(broadcaster: B, utxo_source: C, signer_provider: SP, logger: L) -> Self {
diff --git a/lightning/src/ln/funding.rs b/lightning/src/ln/funding.rs
index 06b972d712..c3bbf661ca 100644
--- a/lightning/src/ln/funding.rs
+++ b/lightning/src/ln/funding.rs
@@ -202,12 +202,9 @@ impl FundingTemplate {
/// Creates a [`FundingContribution`] for both adding and removing funds from a channel using
/// `wallet` to perform coin selection.
- pub async fn splice_in_and_out<W: Deref + MaybeSend>(
+ pub async fn splice_in_and_out<W: CoinSelectionSource + MaybeSend>(
self, value_added: Amount, outputs: Vec<TxOut>, wallet: W,
- ) -> Result<FundingContribution, ()>
- where
- W::Target: CoinSelectionSource + MaybeSend,
- {
+ ) -> Result<FundingContribution, ()> {
if value_added == Amount::ZERO && outputs.is_empty() {
return Err(());
}
@@ -217,12 +214,9 @@ impl FundingTemplate {
/// Creates a [`FundingContribution`] for both adding and removing funds from a channel using
/// `wallet` to perform coin selection.
- pub fn splice_in_and_out_sync<W: Deref>(
+ pub fn splice_in_and_out_sync<W: CoinSelectionSourceSync>(
self, value_added: Amount, outputs: Vec<TxOut>, wallet: W,
- ) -> Result<FundingContribution, ()>
- where
- W::Target: CoinSelectionSourceSync,
- {
+ ) -> Result<FundingContribution, ()> {
if value_added == Amount::ZERO && outputs.is_empty() {
return Err(());
}There was a problem hiding this comment.
This works but it doesn't follow that pattern of NodeSigner:
rust-lightning/lightning/src/sign/mod.rs
Line 1008 in 0a20fa5
That pattern works for CoinSelectionSourceSync but not CoinSelectionSource. Though using a where clause for Target and adding ?Sized to the parameter requiring Deref instead of Target works:
impl<C: Deref + ?Sized> CoinSelectionSource for C
where
C::Target: CoinSelectionSource,
{Gemini tells me this approach is more likely correct. Do you agree?
There was a problem hiding this comment.
Ok, nevermind about that. I just had Claude do it and ask why it dropped the ?Sized constraint. This is what it said:
❯ Why does the ?Sized constraint need to be removed?
⏺ Looking at the sync blanket impl that already exists:
impl<T: CoinSelectionSourceSync + ?Sized, C: Deref<Target = T>> CoinSelectionSourceSync for C {
It uses ?Sized with a separate T parameter. The async version I wrote uses the C::Target associated type form instead:
impl<C: Deref> CoinSelectionSource for C
where
C::Target: CoinSelectionSource,
{
I switched to this form to work around the T may not live long enough error — with a separate T parameter, the compiler couldn't prove
T: 'a for the RPITIT return types. Using C::Target instead lets the compiler deduce the lifetime bound from &'a C (since C: 'a implies
C::Target: 'a through well-formedness of associated types).
But ?Sized doesn't actually need to be removed — Deref::Target is already ?Sized by default. The C::Target: CoinSelectionSource form
still allows unsized targets. So the two forms are equivalent in that regard; I just changed the spelling to work around the lifetime
inference issue.
|
Argh, |
In order to reuse CoinSelectionSource for splicing, the previous transaction of each UTXO is needed. Update CoinSelection to use FundingTxInput (renamed to ConfirmedUtxo) so that it is available. This requires adding a method to WalletSource to look up a previous transaction for a UTXO. Otherwise, Wallet's implementation of CoinSelectionSource would need WalletSource to include the previous transactions when listing confirmed UTXOs to select from. But this would be inefficient since only some UTXOs are selected.
CoinSelectionSource is used for anchor bumping where a ClaimId is passed in to avoid double spending other claims. To re-use this trait for funding a splice, the ClaimId must be optional. And, if None, then any locked UTXOs may be considered ineligible by an implementation.
Previously, splice_channel required callers to manually construct funding inputs and pass them directly, making coin selection the caller's responsibility. This made the API difficult to use and prevented reuse of the existing CoinSelectionSource trait. Introduce a two-phase API: splice_channel now returns a FundingTemplate that callers use to build a FundingContribution via wallet-backed splice methods (e.g., splice_in_sync, splice_out_sync), which handle coin selection automatically. The completed contribution is then passed to a new funding_contributed method to begin quiescence and negotiation. This also renames SpliceContribution to FundingContribution and moves fee estimation and input validation into the funding module, co-located with the types they operate on. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Now that CoinSelection is used to fund a splice funding transaction, use that for determining of a change output should be used. Previously, the initiator could either provide a change script upfront or let LDK generate one using SignerProvider::get_destination_script. Since older versions may have serialized a SpliceInstruction without a change script while waiting on quiescence, LDK must still generate a change output in this case.
Instead of logging both inside propose_quiescence and at the call site, only log inside it. This simplifies the return type.
05067c5 to
20916b7
Compare
Sorry, I was limiting what claude ran and missed this. Fixed now along with the |
TheBlueMatt
left a comment
There was a problem hiding this comment.
obv needs ldk node update now.
|
All follow-ups are in #4382 |
Previously,
splice_channelrequired callers to manually construct funding inputs and pass them directly, making coin selection the caller's responsibility. This made the API difficult to use and prevented reuse of the existingCoinSelectionSourcetrait.Introduce a two-phase API:
splice_channelnow returns aFundingTemplatethat callers use to build aFundingContributionvia wallet-backed splice methods (e.g.,splice_in_sync,splice_out_sync), which handle coin selection automatically. The completed contribution is then passed to a newfunding_contributedmethod to begin quiescence and negotiation.This also renames
SpliceContributiontoFundingContributionand moves fee estimation and input validation into the funding module, co-located with the types they operate on.