Introduce FundingContributionBuilder API#4516
Introduce FundingContributionBuilder API#4516wpaulino wants to merge 2 commits intolightningdevkit:mainfrom
Conversation
|
👋 Thanks for assigning @jkczyz as a reviewer! |
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #4516 +/- ##
==========================================
+ Coverage 86.14% 86.26% +0.11%
==========================================
Files 160 160
Lines 108046 109045 +999
Branches 108046 109045 +999
==========================================
+ Hits 93080 94071 +991
+ Misses 12346 12332 -14
- Partials 2620 2642 +22
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:
|
lightning/src/ln/funding.rs
Outdated
| return Ok(contribution); | ||
| } | ||
|
|
||
| Err(FundingContributionError::MissingCoinSelectionSource) |
There was a problem hiding this comment.
Could we have the compiler enforce this by requiring a call to with_coin_selection_source yielding a type with compatible methods? I'd include the convenience methods like splice_in assuming we reuse FundingTemplate instead of adding another type.
There was a problem hiding this comment.
Or maybe we just drop the convenience methods?
There was a problem hiding this comment.
Discussed offline: we enforce this at compile-time already for add/remove_value, but we cannot do it elsewhere as it's possible that an RBF builder without an attached wallet may require coin selection if the new feerate is too large for the prior contribution's input set.
0968836 to
2da45ef
Compare
|
Leaving the explicit input support for a follow-up as this PR is large enough already. |
| .await | ||
| .map_err(|_| FundingContributionError::CoinSelectionFailed)?; | ||
|
|
||
| return Ok(FundingContribution::new( |
There was a problem hiding this comment.
Nit: unnecessary return in final expression position. Same on line 740 for the sync variant.
| return Ok(FundingContribution::new( | |
| Ok(FundingContribution::new( |
Review SummaryNew Issues Found (this pass)
Previously Flagged Issues (still present)
Inline Comments Posted
|
| let FundingTemplate { shared_input, min_rbf_feerate, .. } = self; | ||
| build_funding_contribution!(value_added, vec![], shared_input, min_rbf_feerate, min_feerate, max_feerate, false, wallet, await) | ||
|
|
||
| Err(FundingContributionError::MissingCoinSelectionSource) |
There was a problem hiding this comment.
Can we get this when it should be InvalidSpliceValue? For example, with zero added value and no outputs?
There was a problem hiding this comment.
That should have already been checked in validate_contribution_parameters above.
| for output in outputs { | ||
| builder = builder.add_output(output); | ||
| } |
There was a problem hiding this comment.
Could we make a private add_outputs that simply moves the input Vec?
|
|
||
| /// Creates a [`FundingContribution`] for both adding and removing funds from a channel using | ||
| /// `wallet` to perform coin selection. | ||
| impl FundingTemplate { |
There was a problem hiding this comment.
Hmm... I think the diff may be saner if these methods were kept in the original impl block.
There was a problem hiding this comment.
The methods haven't moved, rather the diffing logic isn't happy with FundingBuilder being added above FundingTemplate.
lightning/src/ln/funding.rs
Outdated
| struct FundingBuilderContents { | ||
| shared_input: Option<Input>, | ||
| min_rbf_feerate: Option<FeeRate>, | ||
| prior_contribution: Option<PriorContribution>, | ||
| value_added: Amount, | ||
| outputs: Vec<TxOut>, | ||
| feerate: FeeRate, | ||
| max_feerate: FeeRate, | ||
| } |
There was a problem hiding this comment.
Is this struct necessary? Seems having all this on FundingBuilder could simplify things. Plus, you may be able to simplify error handling if value_added was only for builder types having a CoinSelectionSource. Then value_added could be a field on the parameter type.
There was a problem hiding this comment.
Removed FundingBuilderContents. value_added will need to remain here as we plan to add explicit input support.
TheBlueMatt
left a comment
There was a problem hiding this comment.
I didn't look too deeply at the tests but basically LGTM.
lightning/src/ln/funding.rs
Outdated
| } | ||
|
|
||
| #[doc(hidden)] | ||
| pub mod builder_states { |
There was a problem hiding this comment.
Rather than using a generic, it seems like the code would be basically just as clean, but bindings would be happier, if we kept the internal struct and just exposed three separate builder structs.
There was a problem hiding this comment.
I think what I pushed should work, let me know otherwise.
It does not require coin selection, so the wallet argument is not necessary.
This commit introduces a `FundingBuilder`, following the builder pattern, to iteratively construct a `FundingContribution`. It supports adding/removing value (which may trigger a coin selection attempt), adding/remove outputs, and amending prior contributions (which was previously not possible). A `FundingBuilder` may be obtained in two ways: via `FundingTemplate::contribution_builder` (which resumes from the prior contribution to amend if available) or `FundingTemplate::without_prior_contribution_builder` to start from scratch and fully replace the prior contribution. When `FundingBuilder::build` is called, we attempt to build the contribution iteratively such that coin selection is our last resort. The splice-related convenience methods on `FundingTemplate` now map to the `FundingBuilder` under the hood.
2da45ef to
19b230b
Compare
|
Had to rebase due to a small import conflict. |
| return Some(adjusted); | ||
| } | ||
| } | ||
| return None; |
There was a problem hiding this comment.
Bug: When the request matches the prior exactly but holder_balance is None, this early return at line 418 skips amend_without_coin_selection entirely. For input-backed contributions, amend_without_coin_selection does not need holder_balance (lines 1157-1211 don't use it), so it could succeed. This means rbf_prior_contribution_sync(None, max_feerate, wallet) unnecessarily re-runs coin selection for input-backed prior contributions when holder_balance is None — potentially yielding a different input set than the prior had.
This is covered by the test at line 2890 (test_rbf_sync_holder_balance_none_falls_through_to_coin_selection), which expects coin selection, so this appears intentional. But worth noting that the amend_without_coin_selection path could handle this case without requiring a wallet.
| } | ||
|
|
||
| if self.value_added == Amount::ZERO && self.outputs.is_empty() { | ||
| return Err(FundingContributionError::InvalidSpliceValue); |
There was a problem hiding this comment.
The validation here catches value_added == 0 && outputs.is_empty(), which blocks the RBF "same request" path for splice-out prior contributions that had value_added == 0 and outputs == []. While this shouldn't arise in normal use (a prior contribution with both zero is meaningless), there's a subtle interaction: if someone calls with_prior_contribution(...) on a fresh template (no prior) without calling any mutation methods, they'd hit this error. The error message "Invalid splice value" doesn't hint that they need to call add_value or add_output first. Consider a more descriptive variant like EmptyContribution or adding a hint to the InvalidSpliceValue display message.
Looking for initial feedback on the design, still needs to be cleaned up a good bit.