Zero-fee commitments support#660
Conversation
|
👋 Thanks for assigning @tnull as a reviewer! |
| /// `option_anchor_zero_fee_commitments`. All the caveats and warnings in | ||
| /// [`AnchorChannelsConfig`] still apply. | ||
| /// [`AnchorChannelsConfig`]: Config::anchor_channels_config | ||
| pub enable_zero_fee_commitments: bool, |
There was a problem hiding this comment.
I don't think we'll wan to add a new flag here that's probably hard to understand for the user? Rather, shouldn't we enable this for the user based on our current 'trust model settings' here?
Also, from these docs it's very unclear what this setting even does, when the user would want to enable it, what drawbacks it has, etc
There was a problem hiding this comment.
FWIW, thinking about it again it seems that we should never set negotiate_anchor_zero_fee_commitments until we're positive our chain sources support submitpackage/TRUC, no? And once we are positive, we would always set it?
There was a problem hiding this comment.
Rather, shouldn't we enable this for the user based on our current 'trust model settings' here?
Don't quite follow here could you expand ? I think 0FC channels merit an explicit setting somewhere rather than derived from trust model settings.
Also, from these docs it's very unclear what this setting even does, when the user would want to enable it, what drawbacks it has, etc
Yes will expand
There was a problem hiding this comment.
Don't quite follow here could you expand ? I think 0FC channels merit an explicit setting somewhere rather than derived from trust model settings.
Why, what do they fundamentally change for the user compared to our three current modes (fully trusted/keep 0-reserve, still try to claim/keep X reserve, try to claim)? Keep in mind that communicating these three modes to the user is already very hard, they always have a very hard time understanding what this means. Now, how would we communicate any changed assumptions for 0FC here? If we already trust our counterparty already, wouldn't we always want to enable 0FC for the UX improvements?
There was a problem hiding this comment.
Why, what do they fundamentally change for the user compared to our three current modes (fully trusted/keep 0-reserve, still try to claim/keep X reserve, try to claim)?
Let me see I don't think they change anything ? Whether to enable or disable 0FC channels is orthogonal to these modes ie trusted_peers_no_reserve and per_channel_reserve_sats should have no influence on whether we enable 0FC channels (only that per_channel_reserve_sats should be set to some value). I suspect you don't agree :)
If we already trust our counterparty already, wouldn't we always want to enable 0FC for the UX improvements?
It seems to me trusting our counterparty -> keeping 0 reserve is orthogonal to whether the user wants to enable 0FC channels ? for example a user trusts their counterparty, but wants to wait for greater adoption of Core v29+ before using 0FC channels.
cb1cdf9 to
c874049
Compare
|
Marked as draft: I think we should wait for electrum and esplora submit package support before merging this PR. |
c874049 to
ef3ba7a
Compare
|
Successfully opened some 0FC channels, made payments, and force closed them with the esplora diff in this branch. https://mutinynet.com/tx/508a954d85f5b7daf224a2fdc54ea6de9a26c0f62f7d58284bf61c3cdfd346e6 |
ef3ba7a to
3ebd017
Compare
AnchorChannelsConfig::enable_zero_fee_commitments3ebd017 to
eda13d4
Compare
e136f33 to
d4a2a04
Compare
771f45b to
a7c7911
Compare
a8779ef to
fcf54fd
Compare
|
Rebased, squashed the previous fixups, added a few more |
| } | ||
|
|
||
| pub(super) async fn validate_zero_fee_commitments_support(&self) -> Result<(), Error> { | ||
| self.esplora_client.submit_package(&super::dummy_package(), None, None).await.map_err( |
There was a problem hiding this comment.
Especially if we already submitted this package before, couldn't the backend return an different error that we'd misinterpret as 'doesn't support submitpackage'? E.g., typical transaction broadcast errors are 'already known' or 'missing-inputs'. Why can't we ever hit them here?
There was a problem hiding this comment.
I've looked down the stack, and also had codex review the mempool-electrs and blockstream-electrs code.
already-known and missing-inputs errors get placed in the package_msg and error fields of the response, but still yield status 200.
Non 200 error codes are for transport-level errors, not for errors returned in the fields of the submitpackage response. As long as Bitcoin Core RPC returns some valid response for submitpackage, we get back 200.
There was a problem hiding this comment.
Hmm, if we always get back a 200 if there's no connectivity error, then I'm still confused why we interpret any error here as Esplora server does not support submitpackage? Shouldn't we only match on 404 (assuming that's what's returned if the endpoint isn't known?)
There was a problem hiding this comment.
Also want to note that this way of checking support for Electrum/Esplora is error prone as it will pass if the service runs bitcoind v26+ I believe, while we require v29+ in reality?
There was a problem hiding this comment.
Shouldn't we only match on 404 (assuming that's what's returned if the endpoint isn't known?)
Sounds good yes I will be more precise.
Also want to note that this way of checking support for Electrum/Esplora is error prone as it will pass if the service runs bitcoind v26+ I believe, while we require v29+ in reality?
Yes agreed this is not a guarantee we are safe. Do you see another way to do this?
There was a problem hiding this comment.
Added some more prevision below based on the error returned across both electrum and esplora.
There was a problem hiding this comment.
Also want to note that this way of checking support for Electrum/Esplora is error prone as it will pass if the service runs bitcoind v26+ I believe, while we require v29+ in reality?
Thought about it further, I'm open to all options here.
Is it too early / reckless to ship 0FC channels against electrum / esplora given that they offer no way to assert that the backend Bitcoind is v29+ ?
Should we wait for a way to assert more cleanly that some esplora server supports submitpackage? You have a PR to do this for the electrum client thank you !
Should we drop the validate_zero_fee_commitments_support startup roundtrip in electrum and esplora given that it doesn't even guarantee that the backend bitcoind will relay ephemeral dust?
| .init_features; | ||
| let anchor_channel = init_features.requires_anchors_zero_fee_htlc_tx(); | ||
| let anchor_channel = init_features.requires_anchors_zero_fee_htlc_tx() | ||
| || init_features.requires_anchor_zero_fee_commitments(); |
There was a problem hiding this comment.
Hmm, pre-existing, but it seems for init features we should be using supports_?
There was a problem hiding this comment.
Yes @febyeji had a similar question further above in this comment: #660 (comment)
Can we revisit these checks in a standalone PR ?
There was a problem hiding this comment.
Can we revisit these checks in a standalone PR ?
Well, if we touch it here we should at least not add incorrect code if we think it's incorrect. If you prefer a separate PR, feel free to open an base this one on top, but could also make it a prefactor commit.
There was a problem hiding this comment.
ok will add a prefactor commit then
There was a problem hiding this comment.
Done, added two commits below
|
Let me know if I can squash |
| } | ||
|
|
||
| pub(crate) async fn process_broadcast_package(&self, txs: TransactionBroadcast) { | ||
| fn log_broadcast_error(&self, e: impl core::fmt::Display, txids: &[Txid], txs: &[Transaction]) { |
There was a problem hiding this comment.
Please make the DRYing up a freestanding prefactor commit and then use it in a fixup to your code (to not touch unrelated preexisting code in your commits)
There was a problem hiding this comment.
Yes that's the plan!
There was a problem hiding this comment.
Done, see commit below
| let spawn_fut = | ||
| self.runtime.spawn_blocking(move || electrum_client.transaction_broadcast(&tx)); | ||
| let spawn_fut = self.runtime.spawn_blocking({ | ||
| let tx = tx.clone(); |
There was a problem hiding this comment.
Why do we need this clone now?
There was a problem hiding this comment.
the log helpers further below take a &[Transaction] now, will rework things to remove it.
| let txids: Vec<_> = package.iter().map(|tx| tx.compute_txid()).collect(); | ||
|
|
||
| let spawn_fut = self.runtime.spawn_blocking({ | ||
| let package = package.clone(); |
There was a problem hiding this comment.
Can we avoid cloning the whole package? It seems it's only required because we need the reference later for logging, but previously we intentionally avoided the allocations. Can we do the same still here and above?
There was a problem hiding this comment.
Sounds good will remove these clones thank you
| total_anchor_channels_reserve_sats(&self.channel_manager, &self.config); | ||
| let spendable_amount_sats = | ||
| self.wallet.get_spendable_amount_sats(cur_anchor_reserve_sats).unwrap_or(0); | ||
| let anchor_channel = init_features.requires_anchors_zero_fee_htlc_tx() |
There was a problem hiding this comment.
Claude:
- /home/tnull/workspace/ldk-node-pr-660/src/liquidity/service/lsps2.rs:455: git diff --check upstream/main...HEAD fails because added lines contain trailing \r whitespace at lines 455, 456, and 459.
There was a problem hiding this comment.
I removed all carriage returns across all rust files in a prefactor commit below.
| } | ||
|
|
||
| pub(super) async fn validate_zero_fee_commitments_support(&self) -> Result<(), Error> { | ||
| self.esplora_client.submit_package(&super::dummy_package(), None, None).await.map_err( |
There was a problem hiding this comment.
Also want to note that this way of checking support for Electrum/Esplora is error prone as it will pass if the service runs bitcoind v26+ I believe, while we require v29+ in reality?
|
|
||
| const BCAST_PACKAGE_QUEUE_SIZE: usize = 50; | ||
|
|
||
| pub(crate) struct TransactionBroadcast(Vec<Transaction>); |
There was a problem hiding this comment.
Yea happy to rebase myself in case that's needed no problem
We only do this for rust files, and leave bat files untouched. Use git show --ignore-cr-at-eol to check that this commit has no other edits.
`BroadcasterInterface::broadcast_transactions` requires that any passed vector containing multiple transactions must be a single child together with its parents. We will lean on this contract in upcoming commits, so here we fix a case where we broke this contract.
In an upcoming commit, we will fix `check_sufficient_funds_for_channel` to check that we have on-chain funds to cover the anchor reserve for an additional anchor channel in the validation of outbound channel opens. Before we do this, we stop using this function to check that any splice-ins leave enough on-chain anchor reserves. This function keeps an anchor reserve for an additional anchor channel on top of the existing set of anchor channels, but after splice-ins, our anchor reserve only needs to cover the existing set of anchor channels.
When we are preparing to open a channel to a peer, we should reserve onchain funds for an anchor channel when the peer's init features signals anchor channels as optional, as channel negotiation with such a peer can result in an anchor channel.
AI assistance: generated with OpenAI Codex.
AI-Generated-By: OpenAI Codex
AI-Generated-By: OpenAI Codex
Implementations of `BroadcasterInterface` cannot assume any topological ordering on the transactions received, so here we order the received transactions before adding them to the broadcast queue. Any consumers of the queue can now assume all transactions received to be topologically sorted. Codex wrote the tests.
The From trait is usually reserved for cheap and straightforward conversions which was not a good fit here.
The patch adds support for the `broadcast_package` method added in electrum protocol v1.6. Upcoming commits will require this patch to pass CI.
The mempool/electrs docker image used in those tests only supports submitpackage via the esplora interface, not the electrum interface.
We bump the Bitcoin Core version used in kotlin and python tests to support ephemeral dust. This is required for 0FC channels.
Do this roundtrip at the same time we make a roundtrip to retrieve the feerates to keep startup as fast as possible.
These will be useful when we add support for broadcasting packages in an upcoming commit.
This allows the thread that broadcasts and the thread that logs to share ownership of the transaction, and avoids cloning or encoding the transaction unnecessarily.
We rely on the `BroadcasterInterface` contract whereby any multi-transaction vector must be a single child and its parents, and must be broadcasted together as a package using `submitpackage`. In a prior commit, we added the guarantee that any packages received from the broadcast queue are already topologically sorted, and hence can be passed directly to the `submit_package` Bitcoin Core RPC.
This allows the thread that broadcasts and the thread that logs to share ownership of the package, and avoids cloning or encoding the package unnecessarily.
fcf54fd to
5eab461
Compare
No description provided.