-
Notifications
You must be signed in to change notification settings - Fork 80
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
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.
src/payment/onchain.rs
Outdated
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_address
instead?
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
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.