-
Notifications
You must be signed in to change notification settings - Fork 2.2k
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
multi: make reassignment of alias channel edge atomic #8777
base: master
Are you sure you want to change the base?
Changes from all commits
01c09ec
0c76e29
ce27b34
48f7842
55a4ab6
fc0a477
115451c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -530,9 +530,12 @@ type Config struct { | |
// the initiator for channels of the anchor type. | ||
MaxAnchorsCommitFeeRate chainfee.SatPerKWeight | ||
|
||
// DeleteAliasEdge allows the Manager to delete an alias channel edge | ||
// from the graph. It also returns our local to-be-deleted policy. | ||
DeleteAliasEdge func(scid lnwire.ShortChannelID) ( | ||
// ReAssignSCID allows the Manager to assign a new SCID to an | ||
// option-scid channel being part of the underlying graph. This is | ||
// necessary because option-scid channels change their scid during their | ||
// lifetime (public zeroconf channels for example) so we need to make | ||
// sure to update the underlying graph. | ||
ReAssignSCID func(aliasScID, newScID lnwire.ShortChannelID) ( | ||
*models.ChannelEdgePolicy, error) | ||
|
||
// AliasManager is an implementation of the aliasHandler interface that | ||
|
@@ -3719,19 +3722,29 @@ func (f *Manager) annAfterSixConfs(completeChan *channeldb.OpenChannel, | |
"maps: %v", err) | ||
} | ||
|
||
// We'll delete the edge and add it again via | ||
// addToGraph. This is because the peer may have | ||
// sent us a ChannelUpdate with an alias and we don't | ||
// want to relay this. | ||
ourPolicy, err := f.cfg.DeleteAliasEdge(baseScid) | ||
// We reassign the same scid to the graph db. This will | ||
// trigger a deletion of the current edge data and | ||
// reinsert the channel with the same edge info and | ||
// policy. This is done to guarantee that potential | ||
// ChannelUpdates using the alias as the scid are | ||
// removed and not relayed to the broader network | ||
// because the alias is not a verifiable channel id. | ||
ourPolicy, err := f.cfg.ReAssignSCID( | ||
baseScid, baseScid, | ||
) | ||
if err != nil { | ||
return fmt.Errorf("failed deleting real edge "+ | ||
"for alias channel from graph: %v", | ||
err) | ||
return fmt.Errorf("unable to reassign alias "+ | ||
"edge in graph: %w", err) | ||
} | ||
|
||
err = f.addToGraph( | ||
completeChan, &baseScid, nil, ourPolicy, | ||
log.Infof("Successfully reassigned alias edge in "+ | ||
ziggie1984 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
"graph(non-zeroconf): %v(%d) -> %v(%d)", | ||
baseScid, baseScid.ToUint64(), | ||
baseScid, baseScid.ToUint64()) | ||
|
||
// We send the rassigned ChannelUpdate to the peer. | ||
err = f.sendChanUpdate( | ||
completeChan, &baseScid, ourPolicy, | ||
Comment on lines
+3746
to
+3747
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The unit tests pass even with this call commented out. I propose to cover this case of announcement sending in unit tests as well. Another idea. DeleteSixConfs, ReAssignSCID, and sendChanUpdate is a common sequence in both cases where they are used. Does it make sense to create a method doing DeleteSixConfs, ReAssignSCID, and sendChanUpdate and reuse it in both cases? It case there will be a third case in the future, there will be less chances to use incorrectly (e.g. to forget to call sendChanUpdate). |
||
) | ||
if err != nil { | ||
return fmt.Errorf("failed to re-add to "+ | ||
|
@@ -3808,23 +3821,33 @@ func (f *Manager) waitForZeroConfChannel(c *channeldb.OpenChannel) error { | |
"six confirmations: %v", err) | ||
} | ||
|
||
// TODO: Make this atomic! | ||
ourPolicy, err := f.cfg.DeleteAliasEdge(c.ShortChanID()) | ||
// The underlying graph entry for this channel id needs to be | ||
// reassigned with the new confirmed scid. Moreover channel | ||
// updates with the alias scid are removed so that we do not | ||
// relay them to the broader network. | ||
ourPolicy, err := f.cfg.ReAssignSCID( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think this fixes the issue mentioned in the PR description re the possible race. We can still receive an There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I described the problem in detail here: ef6cd7a Looking back I think this issue could have been solved by deleting the rejectCache before adding the edge via the |
||
c.ShortChanID(), confChan.shortChanID, | ||
) | ||
if err != nil { | ||
return fmt.Errorf("unable to delete alias edge from "+ | ||
"graph: %v", err) | ||
return fmt.Errorf("unable to reassign alias edge in "+ | ||
"graph: %w", err) | ||
} | ||
|
||
// We'll need to update the graph with the new ShortChannelID | ||
// via an addToGraph call. We don't pass in the peer's | ||
// alias since we'll be using the confirmed SCID from now on | ||
// regardless if it's public or not. | ||
err = f.addToGraph( | ||
starius marked this conversation as resolved.
Show resolved
Hide resolved
|
||
c, &confChan.shortChanID, nil, ourPolicy, | ||
aliasScid := c.ShortChanID() | ||
confirmedScid := confChan.shortChanID | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The variables can be used above in the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. looks like this is not addressed |
||
|
||
log.Infof("Successfully reassigned alias edge in "+ | ||
"graph(zeroconf): %v(%d) -> %v(%d)", | ||
aliasScid, aliasScid.ToUint64(), | ||
confirmedScid, confirmedScid.ToUint64()) | ||
|
||
// Send the ChannelUpdate with the confirmed scid to the peer. | ||
err = f.sendChanUpdate( | ||
c, &confChan.shortChanID, ourPolicy, | ||
) | ||
if err != nil { | ||
return fmt.Errorf("failed adding confirmed zero-conf "+ | ||
"SCID to graph: %v", err) | ||
return fmt.Errorf("failed to send ChannelUpdate to "+ | ||
"gossiper: %v", err) | ||
} | ||
} | ||
|
||
|
@@ -4590,6 +4613,52 @@ func (f *Manager) announceChannel(localIDKey, remoteIDKey *btcec.PublicKey, | |
return nil | ||
} | ||
|
||
// sendChanUpdate sends a ChannelUpdate to the gossiper which is as a | ||
// consequence sent to the peer. | ||
// | ||
// TODO(ziggie): Refactor the gossip msgs so that not always all msgs have | ||
// to be created but only the ones which are needed. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. think we have a de-dup logic when sending the msgs tho There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I was more referring to the creation of these messages, we always require all messages to be created although only single ones are used, that's why I propose splitting them, and it will also come handy in the follow-up PR which sets the dont_forward bit. |
||
func (f *Manager) sendChanUpdate(completeChan *channeldb.OpenChannel, | ||
shortChanID *lnwire.ShortChannelID, | ||
ziggie1984 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
ourPolicy *models.ChannelEdgePolicy) error { | ||
|
||
chanID := lnwire.NewChanIDFromOutPoint(completeChan.FundingOutpoint) | ||
|
||
fwdMinHTLC, fwdMaxHTLC := f.extractAnnounceParams(completeChan) | ||
|
||
ann, err := f.newChanAnnouncement( | ||
f.cfg.IDKey, completeChan.IdentityPub, | ||
&completeChan.LocalChanCfg.MultiSigKey, | ||
completeChan.RemoteChanCfg.MultiSigKey.PubKey, *shortChanID, | ||
chanID, fwdMinHTLC, fwdMaxHTLC, ourPolicy, | ||
completeChan.ChanType, | ||
) | ||
if err != nil { | ||
return fmt.Errorf("error generating channel "+ | ||
"announcement: %v", err) | ||
} | ||
|
||
errChan := f.cfg.SendAnnouncement(ann.chanUpdateAnn) | ||
select { | ||
case err := <-errChan: | ||
if err != nil { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. can be simplified to, if graph.IsError(err, graph.ErrOutdated, graph.ErrIgnored) {
log.Debugf("Graph rejected ChannelUpdate: %v", err)
return nil
}
if err != nil {
return fmt.Errorf("error sending channel update: %v",
err)
} or just, if graph.IsError(err, graph.ErrOutdated, graph.ErrIgnored) {
log.Debugf("Graph rejected ChannelUpdate: %v", err)
return nil
}
return err since all the returned error from There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nice! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. not addressed |
||
if graph.IsError(err, graph.ErrOutdated, | ||
graph.ErrIgnored) { | ||
|
||
log.Debugf("Graph rejected "+ | ||
"ChannelUpdate: %v", err) | ||
} else { | ||
return fmt.Errorf("error sending channel "+ | ||
"update: %v", err) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
} | ||
} | ||
case <-f.quit: | ||
ziggie1984 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
return ErrFundingManagerShuttingDown | ||
} | ||
|
||
return nil | ||
} | ||
|
||
// InitFundingWorkflow sends a message to the funding manager instructing it | ||
// to initiate a single funder workflow with the source peer. | ||
func (f *Manager) InitFundingWorkflow(msg *InitFundingMsg) { | ||
|
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.
I think the actually issue is we call
DeleteSixConfs
too early - if we only call it once the edge is recreated, we can fix the case where the peer sends achannel_update
using alias?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 I don't think so, we don't want this ChanUpdate with the Alias so we need to delete the mapping earlier so we do not risk adding the Alias ChanUpdate after we readded the Edge ?