-
Notifications
You must be signed in to change notification settings - Fork 113
Add anchor support #141
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
Add anchor support #141
Conversation
340cdd5 to
405dcec
Compare
|
Rebased on #151. |
405dcec to
08ba6e0
Compare
|
Rebased on current main. Still need to expose a good API. |
08ba6e0 to
3bc7c2d
Compare
3bc7c2d to
0387783
Compare
0387783 to
1fecf85
Compare
a6bdd44 to
8a01a2e
Compare
2741cf9 to
1510751
Compare
|
@jkczyz Let me know if I can squash. |
SGTM |
bb689e0 to
893172c
Compare
Squashed fixups without further changes. |
src/lib.rs
Outdated
| let spendable_amount_sats = | ||
| self.wallet.get_balances(cur_anchor_reserve_sats).map(|(_, s)| s).unwrap_or(0); |
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.
For readability and DRY, could we add a get_spendable_amount to Wallet?
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, now added a fixup to that end, but personally no big fan of one-liner helpers that IMO often tend to obfuscate what actually happens.
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.
Yeah, ideally Wallet would just be aware of the anchors reserve (instead of passing it) and return an adjusted Balance, though that involves introducing dependencies to the Wallet. Even if it were aware, the reserve concept can't be applied to one of the Balance components without affecting the total, unfortunately.
What about defining our own internal OnchainBalance as:
struct OnchainBalance {
pub anchor_reserve_sats: u64,
pub spendable_amount_sats: u64,
pub total_amount_sats: u64,
}Where Wallet::get_balances takes ChannelManager and Config to compute the anchor reserve internally instead of computing it externally at each call site and passing it in? That would keep the calculations in one place, including the one that adjusts the reserve based on the total balance.
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.
Hm, I'd really like to keep the objects as separate as possible for the time being, which is why I went this way:
a) Currently every wallet operation tends to block on the sync which lives in its own thread. So rather than integrating it further, I'd rather move the opposite direction and isolate Wallet more where possible. I had considered before to fully rewrite it based on an Actor pattern and only communicating via channels from/to the wallet. I just didn't go that way yet as the upcoming update might mitigate a lot of the issues.
b) we're about to essentially rewrite the wallet integration with the BDK 1.0 more or less entirely
c) We might still consider introducing a Wallet trait to let users bring/integrate their own wallet. This is undecided, but at least has been requested many times in the past.
So if we end up really deciding against c), I'd at least would hold off on it until we upgrade or after the BDK upgrade, especially as it will complicate the balance-caching logic even more than it needs to be.
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.
Sure, happy to wait if you prefer. Just seems if we always need to call total_anchor_channels_reserve_sats before calling Wallet::get_balance then we might as well merge them.
|
|
||
| let cur_balance = self.wallet.get_balance()?; | ||
| if cur_balance.get_spendable() < amount_sats { | ||
| log_error!(self.logger, "Unable to send payment due to insufficient funds."); | ||
| let cur_anchor_reserve_sats = | ||
| crate::total_anchor_channels_reserve_sats(&self.channel_manager, &self.config); | ||
| let spendable_amount_sats = | ||
| self.wallet.get_balances(cur_anchor_reserve_sats).map(|(_, s)| s).unwrap_or(0); | ||
|
|
||
| if spendable_amount_sats < amount_sats { | ||
| log_error!(self.logger, | ||
| "Unable to send payment due to insufficient funds. Available: {}sats, Required: {}sats", | ||
| spendable_amount_sats, amount_sats | ||
| ); | ||
| return Err(Error::InsufficientFunds); |
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.
Technically, this is a behavioral change when get_balances returns Err (i.e., a different variant than before will be returned). Maybe it doesn't matter?
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.
Yeah, that's true, but I think it shouldn't matter all too much. We might need to revisit it though as part of the BDK 1.0 upgrade, which will entail switching BDK's persistence backend to our KVStore, meaning failure to access the wallet might happen more often, e.g., in a VSS setting. But essentially we'll have to thoroughly revisit and review all BDK connected code then, and before that I'll have to read some BDK internal code to learn what's actually going on under the hood and how we can deal with persistence failures best.
FYI, for context: We will also add a cache for retrieving the on-chain balances in the #281 follow-up as the Wallet mutex is blocked for the entire duration of any on-chain syncing task. This means that operations like these might (depending on the setting and how unreliable/rate-limited the configured Esplora server is) block upwards of a minute to retrieve the balance. This is painful here but unacceptable in event handling as it would bring the node essentially to a standstill. This will be properly fixed as part of the BDK 1.0 upgrade which will allow us to decouple retrieving the changes-to-sync and actually applying them, i.e., afterwards we won't need to hold the Wallet's mutex guard for the entire time, but before caching the balances is a requirement.
TLDR: Blocking on the wallet mutex bad, we'll work-around a few of the consequential issues in #281.
src/wallet.rs
Outdated
| let locked_wallet = self.inner.lock().unwrap(); | ||
| let address_info = locked_wallet.get_address(AddressIndex::LastUnused).map_err(|e| { | ||
| log_error!(self.logger, "Failed to retrieve new address from wallet: {}", e); | ||
| })?; | ||
| let address_info = | ||
| locked_wallet.get_internal_address(AddressIndex::LastUnused).map_err(|e| { | ||
| log_error!(self.logger, "Failed to retrieve new address from wallet: {}", e); | ||
| })?; |
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 we use get_new_internal_address instead?
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 we use
get_new_internal_addressinstead?
Hmm, it could, butget_new_internal_address is essentially just a helper API for WalletKeysManager. While they are currently doing the same thing, it's not unthinkable the behavior might diverge in the future. Generally I'd prefer methods on Wallet to use self.inner and everything else to use the Wallet API.
56f3040 to
caf0aca
Compare
jkczyz
left a comment
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.
Other than the earlier comment on balance computation, just some minor comments and I think we're good.
caf0aca to
a93fd5d
Compare
Addressed the remaining doc comments. See my response to the balance/reserve above. Let me know if I can squash the fixups. |
Yup, go ahead and squash. |
.. allowing to configure the per-channel emergency reserve as well as some trusted peers for which we won't maintain any reserve.
a93fd5d to
e7c9824
Compare
Squashed fixups without further changes. |
When Anchor outputs need to be spent LDK will generate `BumpTransactionEvent`s. Here, we add the corresponding event-handling and PSBT-signing support.
.. because they will be the new default. Note the upcoming CLN 24.02 release will make Anchors default, too, but for now we have to set the `experimental-anchors` config option.
.. which we somehow so far ommitted exposing in the API. We now introduce a `force_close` method and broadcast if the counterparty is not trusted.
.. i.e., without bumping.
e7c9824 to
436f2e4
Compare
|
Force-pushed with the indentation fixes: > git diff-tree -U2 e7c9824 436f2e4
diff --git a/src/event.rs b/src/event.rs
index 19ecfbd..36769c0 100644
--- a/src/event.rs
+++ b/src/event.rs
@@ -850,8 +850,8 @@ where
if spendable_amount_sats < required_amount_sats {
log_error!(
- self.logger,
- "Rejecting inbound Anchor channel from peer {} due to insufficient available on-chain reserves.",
- counterparty_node_id,
- );
+ self.logger,
+ "Rejecting inbound Anchor channel from peer {} due to insufficient available on-chain reserves.",
+ counterparty_node_id,
+ );
self.channel_manager
.force_close_without_broadcasting_txn(
@@ -1147,7 +1147,7 @@ where
{
log_debug!(self.logger,
- "Ignoring BumpTransactionEvent for channel {} due to trusted counterparty {}",
- channel_id, counterparty_node_id
- );
+ "Ignoring BumpTransactionEvent for channel {} due to trusted counterparty {}",
+ channel_id, counterparty_node_id
+ );
return;
} |
Based on
#243,#250, #253.