Skip to content
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

Refactor ChannelTransactionParameters into FundingScope #3604

Open
wants to merge 20 commits into
base: main
Choose a base branch
from

Conversation

jkczyz
Copy link
Contributor

@jkczyz jkczyz commented Feb 14, 2025

Continues the work of #3592 by moving fields related to the funding transaction into FundingScope. This includes fields from ChannelContext for the funding transaction and ChannelTransactionParameters for the funding outpoint and pubkeys.

This primarily involves moving the entire ChannelTransactionParameters into FundingScope and channel_value_satoshis into ChannelTransactionParameters. The latter result in less API churn at the cost of duplicating parameters that don't change across each FundingScope. To that end, this PR also updates the EcdsaChannelSigner to take ChannelTransactionParameters which removes the need for ChannelSigner::provide_channel_parameters.

Since ChannelTransactionParameters contains channel_value_satoshis, implementation like InMemorySigner no longer need it. Thus, the NodeSigner trait is updated to no longer take the value when deriving signers, which is a breaking change.

@jkczyz
Copy link
Contributor Author

jkczyz commented Feb 14, 2025

@wpaulino This primarily moves fields from ChannelContext and duplicates the funding outpoint pubkeys from ChannelTransactionParameters into FundingScope. I skimmed through sign.rs to see how this would be affected. Thinking on it a bit, I'm wondering if instead of duplicating those fields we instead move the entire ChannelTransactionParameters from ChannelContext into FundingScope.

The drawback is we'd be duplicating fields that wouldn't change. But this seems better than duplicating fields that would change and risk having inconsistent data. Also, it could prevent us from needing to worry about changing other uses of ChannelTransactionParameters (e.g., by ChannelMonitor and OnchainTxHandler), outside needing to pass the appropriate ChannelTransactionParameters. Maybe we could reduce the duplication when persisting ChannelMonitor, though.

What are your thoughts?

@wpaulino
Copy link
Contributor

I was thinking we could deprecate the funding scope related fields from ChannelTransactionParameters and eventually remove them, so there wouldn't be any duplicate data in the long run.

@jkczyz
Copy link
Contributor Author

jkczyz commented Feb 18, 2025

I was thinking we could deprecate the funding scope related fields from ChannelTransactionParameters and eventually remove them, so there wouldn't be any duplicate data in the long run.

Yeah, this PR takes that approach. But when removing the fields we'd need to update all uses to take both ChannelTransactionParameters and FundingScope anywhere ChannelTransactionParameters is currently needed. So I'm wondering more based on the current uses if it makes sense to include the entire ChannelTransactionParameters in FundingScope?

For instance, writing an OnchainTxHandler writes ChannelTransactionParameters. So removing the deprecated fields would require a custom serialization to include the removed fields from FundingScope. And presumably OnchainTxHandler wouldn't need the other FundingScope fields since it currently doesn't.

There's also DirectedChannelTransactionParameters which has broadcaster_pubkeys and countersignatory_pubkeys methods where the returned ChannelPublicKeys needs to include the funding_pubkey. So we'd need to have AnchorDescriptor hold a FundingScope, for instance, and have a similar Directed variation. But all those additional fields in FundingScope aren't relevant.

I haven't looked exhaustively to see all usages, but it seems simpler to keep ChannelTransactionParameters as it is.

@wpaulino
Copy link
Contributor

Hm, I'm not opposed to always writing ChannelTransactionParameters, especially given the name already conveys it's tied to a specific channel transaction. It would also cover for any other currently static parameters changing due to some future protocol feature/upgrade. We would still be breaking the API somewhat since they are currently intended to be static throughout the channel's lifetime, but we can think of something there.

As for OnchainTxHandler, it will already need to know of the scope (or a subset of its fields) somehow to carry out signing requests. I'd love to avoid all of that completely by having the auxiliary data come from the claim request being handled, but that seems like quite the big change.

@jkczyz
Copy link
Contributor Author

jkczyz commented Feb 19, 2025

@wpaulino
Copy link
Contributor

Here's the alternative approach: https://github.com/jkczyz/rust-lightning/tree/2025-02-channel-funding-pubkeys-alt

Yeah this looks pretty simple, and would also allow us to handle transitioning to new channel types in a splice since it already tracks the channel type features. I don't think the duplicate data is all that bad here once we have multiple scopes since it will eventually go away when the splice confirms. We should at least be able to avoid persisting the duplicate data, if we choose to, by cloning it from the "main" (currently confirmed) scope when reading.

Thoughts on either of these approaches @TheBlueMatt?

@jkczyz
Copy link
Contributor Author

jkczyz commented Feb 20, 2025

@TheBlueMatt Added some more commits to the alternative branch after pairing with @wpaulino yesterday. They move channel_value_satoshis from FundingScope to ChannelTransactionParameters, which will be passed to EcdsaChannelSigner methods in lieu of calling provide_channel_parameters. This is done so far for sign_counterparty_commitment. The goal would be to get rid channel_value_satoshis and channel_parameters from InMemorySigner and instead access them through the passed ChannelTransactionParameters.

@TheBlueMatt
Copy link
Collaborator

Discussed this being supersceded by an alternative that tries to keep signer operations (across many splice versions) together as one call.

Rather than moving relevant fields of ChannelTransactionParameters to
FundingScope, move the entire struct there instead. This prevents API
churn wherever ChannelTransactionParameters is used, which otherwise
would need a FundingScope in addition.
Since the funding transactions changes for each new FudningScope,
include it there instead of ChannelContext.
@jkczyz jkczyz force-pushed the 2025-02-channel-funding-pubkeys branch from df840cb to aff70ae Compare February 25, 2025 00:56
@jkczyz jkczyz changed the title Refactor funding-tx-related fields into FundingScope Refactor ChannelTransactionParameters into FundingScope Feb 25, 2025
Copy link

codecov bot commented Feb 25, 2025

Codecov Report

Attention: Patch coverage is 75.09579% with 65 lines in your changes missing coverage. Please review.

Project coverage is 88.59%. Comparing base (c9fd3a5) to head (aff70ae).

Files with missing lines Patch % Lines
lightning/src/util/test_channel_signer.rs 51.51% 9 Missing and 23 partials ⚠️
lightning/src/sign/mod.rs 76.13% 11 Missing and 10 partials ⚠️
lightning/src/ln/channelmanager.rs 85.29% 2 Missing and 3 partials ⚠️
lightning/src/util/test_utils.rs 50.00% 4 Missing ⚠️
lightning/src/chain/channelmonitor.rs 92.30% 0 Missing and 1 partial ⚠️
lightning/src/chain/onchaintx.rs 85.71% 0 Missing and 1 partial ⚠️
lightning/src/events/bump_transaction.rs 75.00% 0 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #3604      +/-   ##
==========================================
- Coverage   88.60%   88.59%   -0.02%     
==========================================
  Files         151      151              
  Lines      118409   118404       -5     
  Branches   118409   118404       -5     
==========================================
- Hits       104918   104900      -18     
+ Misses      10972    10968       -4     
- Partials     2519     2536      +17     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@jkczyz
Copy link
Contributor Author

jkczyz commented Feb 25, 2025

Updated the branch to use the alternative version. See updated PR description for details.

@jkczyz jkczyz marked this pull request as ready for review February 25, 2025 01:05
@jkczyz jkczyz added the weekly goal Someone wants to land this this week label Feb 25, 2025
@jkczyz jkczyz force-pushed the 2025-02-channel-funding-pubkeys branch 3 times, most recently from 339e0ca to a55149f Compare February 26, 2025 01:24
let keys_data = keys_data.ok_or(DecodeError::InvalidValue)?;
let holder_signer = signer_provider.read_chan_signer(&keys_data)?;
(holder_signer.channel_keys_id(), holder_signer)
return Err(DecodeError::InvalidValue);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This needs a pending release note that we no longer support upgrading from versions of LDK prior to 0.0.113 (Dec 16, 2022). Once we include that, we should probably open an issue because there's certainly a lot of fields we can start making required now 🎉.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pending changelog added and opened #3625.

@@ -993,6 +1010,7 @@ impl Readable for ChannelTransactionParameters {
(8, funding_outpoint, option),
(10, _legacy_deserialization_prevention_marker, option),
(11, channel_type_features, option),
(13, channel_value_satoshis, (default_value, 0)),
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bleh, having a spurious 0 in these seems like it'll cause issues. Shouldn't we make it an Option so that we can check accesses for correctness?

Copy link
Contributor Author

@jkczyz jkczyz Feb 26, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, so currently this needs to be set by the caller. We could have ChannelTransactionParameters use ReadableArgs and pass it in, checking as you described, though.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd prefer the ReadableArgs over having to deal with the option everywhere

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed to use ReadableArgs in a fixup. This required updating the serialization macros since other structs hold ChannelTransactionParameters. It was a small change, but please check to see if how I'm using it is sane.

@@ -882,7 +882,9 @@ pub struct ChannelTransactionParameters {
pub funding_outpoint: Option<chain::transaction::OutPoint>,
/// This channel's type, as negotiated during channel open. For old objects where this field
/// wasn't serialized, it will default to static_remote_key at deserialization.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since we're providing these to the signer on every call now (and there's so much redundant data) I wonder if (I guess it could happen when we do splicing) we shouldn't have some partial-comparator for these that just checks that two of them are equal for the fields that shouldn't change across a splice?

@wpaulino
Copy link
Contributor

CI is sad

@jkczyz
Copy link
Contributor Author

jkczyz commented Feb 26, 2025

CI is sad

Fixed now, I think. Will make add pending CHANGELOG item in a bit. For now, PTAL.

@jkczyz jkczyz force-pushed the 2025-02-channel-funding-pubkeys branch from 888e658 to 38aad8e Compare February 26, 2025 20:57
@TheBlueMatt
Copy link
Collaborator

Changes LGTM, but CI is still quite sad.

Comment on lines 10298 to 10308
let mut _keys_data = None;
if ver <= 2 {
// Read the serialize signer bytes. We'll choose to deserialize them or not based on whether
// the `channel_keys_id` TLV is present below.
// Read the serialize signer bytes. These are no longer used as of version 0.2.0.
let keys_len: u32 = Readable::read(reader)?;
keys_data = Some(Vec::with_capacity(cmp::min(keys_len as usize, MAX_ALLOC_SIZE)));
while keys_data.as_ref().unwrap().len() != keys_len as usize {
_keys_data = Some(Vec::with_capacity(cmp::min(keys_len as usize, MAX_ALLOC_SIZE)));
while _keys_data.as_ref().unwrap().len() != keys_len as usize {
// Read 1KB at a time to avoid accidentally allocating 4GB on corrupted channel keys
let mut data = [0; 1024];
let read_slice = &mut data[0..cmp::min(1024, keys_len as usize - keys_data.as_ref().unwrap().len())];
let read_slice = &mut data[0..cmp::min(1024, keys_len as usize - _keys_data.as_ref().unwrap().len())];
reader.read_exact(read_slice)?;
keys_data.as_mut().unwrap().extend_from_slice(read_slice);
_keys_data.as_mut().unwrap().extend_from_slice(read_slice);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should be able to remove all of this now, no?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, done.

@wpaulino
Copy link
Contributor

Feel free to squash

@jkczyz jkczyz force-pushed the 2025-02-channel-funding-pubkeys branch from 1682e84 to f8877ff Compare February 27, 2025 00:42
InMemorySigner no longer holds channel_value_satoshis and
channel_parameters. Instead of writing 0 and None, respectively, drop
(de-)serialization support entirely since InMemorySigner hasn't been
serialized since SERIALIZATION_VERSION 2.
Since channel_value_satoshis is needed by the signer and may change for
each new FundingScope, included it in ChannelTransactionParameters so it
can be passed to each signer call in the upcoming commits.
Now that ChannelTransactionParameters are in FundingScope,
channel_value_satoshis can be dropped from the latter.
Now that channel_value_satoshis has been moved to
ChannelTransactionParameters, pass the entire parameters when calling
each method on EcdsaChannelSigner. This will remove the need for
ChannelSigner::provide_channel_parameters. Instead, the parameters from
the FundingScope will be passed in to each method. This simplifies the
interaction with a ChannelSigner when needing to be called for more than
one FundingScope, which will be the case for pending splices and RBF
attempts.
Now that channel_value_satoshis has been moved to
ChannelTransactionParameters, pass the entire parameters when calling
each method on EcdsaChannelSigner. This will remove the need for
ChannelSigner::provide_channel_parameters. Instead, the parameters from
the FundingScope will be passed in to each method. This simplifies the
interaction with a ChannelSigner when needing to be called for more than
one FundingScope, which will be the case for pending splices and RBF
attempts.
Now that channel_value_satoshis has been moved to
ChannelTransactionParameters, pass the entire parameters when calling
each method on EcdsaChannelSigner. This will remove the need for
ChannelSigner::provide_channel_parameters. Instead, the parameters from
the FundingScope will be passed in to each method. This simplifies the
interaction with a ChannelSigner when needing to be called for more than
one FundingScope, which will be the case for pending splices and RBF
attempts.
Now that channel_value_satoshis has been moved to
ChannelTransactionParameters, pass the entire parameters when calling
each method on EcdsaChannelSigner. This will remove the need for
ChannelSigner::provide_channel_parameters. Instead, the parameters from
the FundingScope will be passed in to each method. This simplifies the
interaction with a ChannelSigner when needing to be called for more than
one FundingScope, which will be the case for pending splices and RBF
attempts.
Now that channel_value_satoshis has been moved to
ChannelTransactionParameters, pass the entire parameters when calling
each method on EcdsaChannelSigner. This will remove the need for
ChannelSigner::provide_channel_parameters. Instead, the parameters from
the FundingScope will be passed in to each method. This simplifies the
interaction with a ChannelSigner when needing to be called for more than
one FundingScope, which will be the case for pending splices and RBF
attempts.
Now that channel_value_satoshis has been moved to
ChannelTransactionParameters, pass the entire parameters when calling
each method on EcdsaChannelSigner. This will remove the need for
ChannelSigner::provide_channel_parameters. Instead, the parameters from
the FundingScope will be passed in to each method. This simplifies the
interaction with a ChannelSigner when needing to be called for more than
one FundingScope, which will be the case for pending splices and RBF
attempts.
Now that channel_value_satoshis has been moved to
ChannelTransactionParameters, pass the entire parameters when calling
each method on EcdsaChannelSigner. This will remove the need for
ChannelSigner::provide_channel_parameters. Instead, the parameters from
the FundingScope will be passed in to each method. This simplifies the
interaction with a ChannelSigner when needing to be called for more than
one FundingScope, which will be the case for pending splices and RBF
attempts.
Now that channel_value_satoshis has been moved to
ChannelTransactionParameters, pass the entire parameters when calling
each method on EcdsaChannelSigner. This will remove the need for
ChannelSigner::provide_channel_parameters. Instead, the parameters from
the FundingScope will be passed in to each method. This simplifies the
interaction with a ChannelSigner when needing to be called for more than
one FundingScope, which will be the case for pending splices and RBF
attempts.
Now that channel_value_satoshis has been moved to
ChannelTransactionParameters, InMemorySigner no longer needs a copy.
Remove uses of the copy from sign_counterparty_payment_input.
Now that channel_value_satoshis has been moved to
ChannelTransactionParameters, InMemorySigner no longer needs a copy.
Remove indirect uses of the copy from TestChannelSigner.
Now that the copy of ChannelTransactionParameters is no longer used by
InMemorySigner, remove it and all remaining uses. Since it was set by
ChannelSigner::provide_channel_parameters, this method is no longer
needed and can also be removed.
Now that channel_value_satoshis has been moved to
ChannelTransactionParameters, it no longer needs to be used when
deriving a signer. This is a breaking API change, though InMemorySigner
did not make use of channel_value_satoshis when being derived.
The custom derive_channel_signer methods on AnchorDescriptor and
HTLCDescriptor simply delegate to the passed SignerProvider now that
providing ChannelTransactionParameters is no longer needed. Drop these
methods and replace them with the method bodies at the call sites.
@jkczyz jkczyz force-pushed the 2025-02-channel-funding-pubkeys branch from f8877ff to 39bcc54 Compare February 27, 2025 00:43
@jkczyz
Copy link
Contributor Author

jkczyz commented Feb 27, 2025

Squashed with the following changes:

diff --git a/lightning/src/ln/channel.rs b/lightning/src/ln/channel.rs
index cef3a990c..87f5cff1b 100644
--- a/lightning/src/ln/channel.rs
+++ b/lightning/src/ln/channel.rs
@@ -10260,7 +10260,6 @@ impl<SP: Deref> Writeable for FundedChannel<SP> where SP::Target: SignerProvider
        }
 }
 
-const MAX_ALLOC_SIZE: usize = 64*1024;
 impl<'a, 'b, 'c, ES: Deref, SP: Deref> ReadableArgs<(&'a ES, &'b SP, u32, &'c ChannelTypeFeatures)> for FundedChannel<SP>
                where
                        ES::Target: EntropySource,
@@ -10293,20 +10292,6 @@ impl<'a, 'b, 'c, ES: Deref, SP: Deref> ReadableArgs<(&'a ES, &'b SP, u32, &'c Ch
 
                let latest_monitor_update_id = Readable::read(reader)?;
 
-               let mut _keys_data = None;
-               if ver <= 2 {
-                       // Read the serialize signer bytes. These are no longer used as of version 0.2.0.
-                       let keys_len: u32 = Readable::read(reader)?;
-                       _keys_data = Some(Vec::with_capacity(cmp::min(keys_len as usize, MAX_ALLOC_SIZE)));
-                       while _keys_data.as_ref().unwrap().len() != keys_len as usize {
-                               // Read 1KB at a time to avoid accidentally allocating 4GB on corrupted channel keys
-                               let mut data = [0; 1024];
-                               let read_slice = &mut data[0..cmp::min(1024, keys_len as usize - _keys_data.as_ref().unwrap().len())];
-                               reader.read_exact(read_slice)?;
-                               _keys_data.as_mut().unwrap().extend_from_slice(read_slice);
-                       }
-               }
-
                // Read the old serialization for shutdown_pubkey, preferring the TLV field later if set.
                let mut shutdown_scriptpubkey = match <PublicKey as Readable>::read(reader) {
                        Ok(pubkey) => Some(ShutdownScript::new_p2wpkh_from_pubkey(pubkey)),

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
weekly goal Someone wants to land this this week
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants