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

funding: fix channel type negotiation bug #7177

Merged
merged 7 commits into from Aug 15, 2023

Conversation

morehouse
Copy link
Collaborator

The bug manifests when a nil ChannelType is passed to the funding manager in InitFundingMsg. A default value for ChannelType is selected and sent in the OpenChannel message. However, a nil ChannelType is stored in the reservation context. This causes our ChannelType checks in handleFundingAccept to be bypassed.

Usually this makes us end up in the "peer unexpectedly sent explicit ChannelType" case, where we can still recover by reselecting a default ChannelType and verifying it matches the one the peer sent. But if the peer sends a nil ChannelType, we miss it.

While fixing the bug, I also tried to simplify the negotiation logic, as the complexity is likely what hid the bug in the first place.

Now negotiateCommitmentType only returns the ChannelType to be used in OpenChannel/AcceptChannel and the CommitmentType to pass to the wallet. It will even return a nil ChannelType when we're supposed to use implicit negotiation, so we don't need to manually set it to nil for OpenChannel and AcceptChannel.

Copy link
Collaborator

@yyforyongyu yyforyongyu left a comment

Choose a reason for hiding this comment

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

Thanks for the PR and nice catch! I think the first commit can be split into two commits - one for refactoring and one for the actual fix, that way it'd be easier for the reviewers to follow the changes. Besides fixing the bug, the refactoring isn't so "pure" as it changes the behavior and we need to take a deeper look.

@@ -4131,7 +4124,7 @@ func (f *Manager) handleInitFundingMsg(msg *InitFundingMsg) {
remoteMaxHtlcs: maxHtlcs,
remoteChanReserve: chanReserve,
maxLocalCsv: maxCSV,
channelType: msg.ChannelType,
channelType: chanType,
Copy link
Collaborator

Choose a reason for hiding this comment

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

Looks like this is the only line needed for the fix? Since after implicitNegotiateCommitmentType, chanType won't be nil.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Unfortunately, it's not that simple.

Changing this value to chanType causes many other test cases to fail. It makes us expect the peer to use explicit negotiation in handleFundingAccept, even if the peer never signaled the necessary feature bit. And so we incorrectly fail the channel.

Looking at previous changes to the negotiation function, it seems to me that no one has really understood what the function is doing, and has instead applied bandaids to fix urgent bugs, while introducing new more subtle bugs.

I hope this PR helps fix the root problem once and for all.

// errUnsupportedExplicitNegotiation is an error returned when explicit
// channel commitment negotiation is attempted but either peer of the
// channel does not support it.
errUnsupportedExplicitNegotiation = errors.New("explicit channel " +
Copy link
Collaborator

Choose a reason for hiding this comment

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

Not sure about the removal of this error. In this case the sender will get less explicit error?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

The error only exists due to a previous bandaid-type fix: #6027.

After the fix in this PR, there seems to be little need for the error message. The only way to get non-default channel types is through explicit negotiation. If the peer doesn't support explicit negotiation it also won't support the desired channel type, so it seems just as accurate to return errUnsupportedChannelType.

The one case where it would potentially make sense to return errUnsupportedExplicitNegotiation is when the default channel type matches the desired channel type and the peer doesn't support explicit negotiation. But this PR makes handles that case by using implicit negotiation instead of failing like we did previously.

@morehouse
Copy link
Collaborator Author

Thanks for the PR and nice catch! I think the first commit can be split into two commits - one for refactoring and one for the actual fix, that way it'd be easier for the reviewers to follow the changes. Besides fixing the bug, the refactoring isn't so "pure" as it changes the behavior and we need to take a deeper look.

I'll see what I can do to split up the change. But negotiateCommitmentType really does need the complete rewrite in this PR. The current code is bug-prone and seems to have confused multiple people in the past.

Copy link
Collaborator

@yyforyongyu yyforyongyu left a comment

Choose a reason for hiding this comment

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

Sorry about the late review. Wanna bump this as it involves some nice refactors, and left some questions.

funding/manager.go Show resolved Hide resolved
funding/manager.go Outdated Show resolved Hide resolved
docs/release-notes/release-notes-0.16.0.md Outdated Show resolved Hide resolved
funding/commitment_type_negotiation.go Outdated Show resolved Hide resolved
This prevents us from confusing the channelType parameter with the
chanType local.
negotiateCommitmentType returns both a chanType and a commitType, so it
is really confusing when the commitType is called chanType instead.
We don't actually do the logging described in the first comment, and the
second comment block repeats much of the first comment block.
This allows us to expect chanType to be nil, which will be possible
after the next commit.
The bug manifests when a nil ChannelType is passed to the funding
manager in InitFundingMsg. A default value for ChannelType is selected
and sent in the OpenChannel message. However, a nil ChannelType is
stored in the reservation context. This causes our ChannelType checks in
handleFundingAccept to be bypassed.

Usually this makes us end up in the "peer unexpectedly sent explicit
ChannelType" case, where we can still recover by reselecting a default
ChannelType and verifying it matches the one the peer sent. But if the
peer sends a nil ChannelType, we miss it.

While fixing the bug, I also tried to simplify the negotiation logic, as
the complexity is likely what hid the bug in the first place.

Now negotiateCommitmentType only returns the ChannelType to be used in
OpenChannel/AcceptChannel and the CommitmentType to pass to the wallet.
It will even return a nil ChannelType when we're supposed to use implicit
negotiation, so we don't need to manually set it to nil for OpenChannel
and AcceptChannel.
@morehouse
Copy link
Collaborator Author

Rebased and updated.

@lightninglabs-deploy
Copy link

@yyforyongyu: review reminder

Copy link
Collaborator

@yyforyongyu yyforyongyu left a comment

Choose a reason for hiding this comment

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

LGTM🙏

funding/commitment_type_negotiation.go Outdated Show resolved Hide resolved
@@ -4070,20 +4063,23 @@ func (f *Manager) handleInitFundingMsg(msg *InitFundingMsg) {
scid bool
)

// Check if the returned chanType includes either the zero-conf or
// scid-alias bits.
featureVec := lnwire.RawFeatureVector(*chanType)
Copy link
Collaborator

Choose a reason for hiding this comment

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

Some notes for other reviewers: previously if the msg.ChannelType is nil, we'd implicitly negotiate a chanType. And when it's not nil, and ExplicitChannelTypeOptional is not supported, then we'd return an error so we won't get here.

@yyforyongyu yyforyongyu added this to the v0.17.0 milestone May 25, 2023
@Roasbeef Roasbeef added funding Related to the opening of new channels with funding transactions on the blockchain bug fix labels Aug 2, 2023
@carlaKC carlaKC self-requested a review August 4, 2023 17:27

// TestFundingManagerNoEchoChanType tests that the funding flow is aborted if
// the peer fails to echo back the channel type in AcceptChannel.
func TestFundingManagerNoEchoChanType(t *testing.T) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

structure nittynit: nice to have the test in a commit before the bug fix triggering the condition and then updated in the fix commit to show it's addressed.

Copy link
Collaborator

@carlaKC carlaKC left a comment

Choose a reason for hiding this comment

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

Nice fix to a gnarly piece of code. Only question here (so far) is whether we could go even simpler to save ourselves future headaches.

Not blocking, as I know 17 needs to get out of the door soon. Will take another pass on Monday.

@morehouse morehouse requested a review from carlaKC August 10, 2023 15:46
Copy link
Collaborator

@carlaKC carlaKC left a comment

Choose a reason for hiding this comment

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

Nice fix, I think it would be nice to switch to switch but that would need another round from @yyforyongyu. Happy with either iteration.

Copy link
Collaborator

@yyforyongyu yyforyongyu left a comment

Choose a reason for hiding this comment

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

ACK 81d8d31

funding/commitment_type_negotiation.go Show resolved Hide resolved
funding/commitment_type_negotiation.go Show resolved Hide resolved
- use a switch instead of nested ifs to improve readability
- improve some variable names
- reword comments
Copy link
Collaborator

@carlaKC carlaKC left a comment

Choose a reason for hiding this comment

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

Shouldn't e6a2167 be squashed with a8a50f3?

@saubyk
Copy link
Collaborator

saubyk commented Aug 14, 2023

Concept Ack. Thanks for identifying this and the fix.

Copy link
Member

@Roasbeef Roasbeef left a comment

Choose a reason for hiding this comment

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

LGTM 🌋

The clean ups here are much appreciated! While working in the final set of changes for the taproot channels, I realized that once we merge in that feature, implicit negotiation starts to break down as otherwise if we had that in place still, one would never be able to open a public anchors channel after upgrading. I brought this up in the past spec meeting, and ppl generally agreed that we'd start to set the chan type feature bit to required, then only use that going forward.

@@ -38,10 +38,10 @@ func negotiateCommitmentType(desiredChanType *lnwire.ChannelType, local,
local, remote, lnwire.ExplicitChannelTypeOptional,
) {

chanType, err := explicitNegotiateCommitmentType(
commitType, err := explicitNegotiateCommitmentType(
Copy link
Member

Choose a reason for hiding this comment

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

👍


// Explicitly signal the "implicit" negotiation commitment type
// as default when a desired channel type isn't specified.
chanType, commitType := implicitNegotiateCommitmentType(local,
Copy link
Member

Choose a reason for hiding this comment

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

Weird line folding, should be:

chanType, commitType := implicitNegotiateCommitmentType(
    local, remote, 
)

Copy link
Member

Choose a reason for hiding this comment

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

Fixed in the refactor commit.

@Roasbeef Roasbeef merged commit c6a68d1 into lightningnetwork:master Aug 15, 2023
21 of 24 checks passed
@morehouse morehouse deleted the fix_chantype_negotiation branch August 15, 2023 19:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug fix funding Related to the opening of new channels with funding transactions on the blockchain
Projects
No open projects
Status: Done
Development

Successfully merging this pull request may close these issues.

None yet

6 participants