Skip to content

Conversation

wpaulino
Copy link
Contributor

This also addresses follow-ups from #3889.

The `handle_channel_resumption` path is reachable from both channel
reestablish and monitor update completion. Since we only want to sign
once we know the monitor update has completed, it's possible we could
have unintentionally attempted to sign if we were still pending the
monitor update but had a channel reestablish occur.
This is reachable if the event doesn't get handled and a channel
reestablish occurs.
This commit tracks all data related to the shared input of a splice,
such that a valid witness can be formed upon the splice transaction
finalization.
We get async for free since the user already has to call back into LDK
via `funding_transaction_signed`. If the signer fails to sign, they can
simply retry the call once it is available.
@wpaulino wpaulino requested review from TheBlueMatt and jkczyz August 21, 2025 17:02
@wpaulino wpaulino self-assigned this Aug 21, 2025
@ldk-reviews-bot
Copy link

ldk-reviews-bot commented Aug 21, 2025

👋 Thanks for assigning @jkczyz as a reviewer!
I'll wait for their review and will help manage the review process.
Once they submit their review, I'll check if a second reviewer would be helpful.

Copy link
Collaborator

@TheBlueMatt TheBlueMatt left a comment

Choose a reason for hiding this comment

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

LGTM, aside from the last commit.

&self.context.secp_ctx,
)
.map_err(|_| APIError::ChannelUnavailable {
err: "Signer unavailable".to_string(),
Copy link
Collaborator

Choose a reason for hiding this comment

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

Hmm, so the user can't just call signer_available (which is the usual async-signing API) they have to cache their funding tx sigs and call signed again? I don't think this is the right API and would prefer to just wait to do splice-async-signing until we can do it right.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Well we have to handle the error here anyway so I figured why not. They will have to cache the transaction in the interim, though we can drop that requirement once we do "proper" async.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Honestly I figure we should just not? Doing it this way means we need to go document it, and users might try to implement it and then change it again later? Its not that much work to cache the local sigs, so if you want to go for it, sure, but doing it wrong seems worse than not at all.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Caching the sigs on our end seems worse, they would call funding_transaction_signed with empty witnesses? If you don't like this then either we remove the Result from the method signature or we try to do it the right way now.

Copy link
Collaborator

Choose a reason for hiding this comment

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

My point is that if a signing method fails, the user needs to be able to call signer_unblocked (and only signer_unblocked) to kick the channel into gear. Making them call funding_transaction_signed is pretty annoying (the async signing API is already a bit annoying to use by having to call signer_unblocked, having to now also track exactly which method they need to call to kick the channel seems like we're just asking for trouble).

@TheBlueMatt
Copy link
Collaborator

Also CI is sad.

@@ -7969,9 +7981,9 @@ where
}

pub fn funding_transaction_signed(
&mut self, witnesses: Vec<Witness>,
&mut self, funding_txid_signed: Txid, witnesses: Vec<Witness>,
Copy link
Contributor

Choose a reason for hiding this comment

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

CI is failing because this parameter is passed in the first commit before later being added here.

.zip(witnesses)
.for_each(|(input, witness)| input.witness = witness);
}

pub fn holder_is_initiator(&self) -> bool {
self.holder_is_initiator
}

pub fn shared_input_index(&self) -> Option<u32> {
self.shared_input_index.clone()
Copy link
Contributor

Choose a reason for hiding this comment

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

clone is unnecessary

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
Status: No status
Development

Successfully merging this pull request may close these issues.

4 participants