From 932c5f1a7bc2ec0f85fef6eac44b30be54264650 Mon Sep 17 00:00:00 2001 From: eugene Date: Fri, 18 Feb 2022 14:34:52 -0500 Subject: [PATCH] discovery: move ann sig handling to handleAnnSig --- discovery/gossiper.go | 628 +++++++++++++++++++++--------------------- 1 file changed, 307 insertions(+), 321 deletions(-) diff --git a/discovery/gossiper.go b/discovery/gossiper.go index ebf1ca918..18681843f 100644 --- a/discovery/gossiper.go +++ b/discovery/gossiper.go @@ -1758,10 +1758,7 @@ func (d *AuthenticatedGossiper) processNetworkAnnouncement( schedulerOp = append(schedulerOp, batch.LazyAdd()) } - var announcements []networkMsg - switch msg := nMsg.msg.(type) { - // A new node announcement has arrived which either presents new // information about a node in one of the channels we know about, or a // updating previously advertised information. @@ -1785,324 +1782,7 @@ func (d *AuthenticatedGossiper) processNetworkAnnouncement( // willingness of nodes involved in the funding of a channel to // announce this new channel to the rest of the world. case *lnwire.AnnounceSignatures: - needBlockHeight := msg.ShortChannelID.BlockHeight + - d.cfg.ProofMatureDelta - shortChanID := msg.ShortChannelID.ToUint64() - - prefix := "local" - if nMsg.isRemote { - prefix = "remote" - } - - log.Infof("Received new %v channel announcement for %v", prefix, - msg.ShortChannelID) - - // By the specification, channel announcement proofs should be - // sent after some number of confirmations after channel was - // registered in bitcoin blockchain. Therefore, we check if the - // proof is premature. - d.Lock() - premature := d.isPremature( - msg.ShortChannelID, d.cfg.ProofMatureDelta, nMsg, - ) - if premature { - log.Warnf("Premature proof announcement, current "+ - "block height lower than needed: %v < %v", - d.bestHeight, needBlockHeight) - d.Unlock() - nMsg.err <- nil - return nil, false - } - d.Unlock() - - // Ensure that we know of a channel with the target channel ID - // before proceeding further. - // - // We must acquire the mutex for this channel ID before getting - // the channel from the database, to ensure what we read does - // not change before we call AddProof() later. - d.channelMtx.Lock(msg.ShortChannelID.ToUint64()) - defer d.channelMtx.Unlock(msg.ShortChannelID.ToUint64()) - - chanInfo, e1, e2, err := d.cfg.Router.GetChannelByID( - msg.ShortChannelID) - if err != nil { - // TODO(andrew.shvv) this is dangerous because remote - // node might rewrite the waiting proof. - proof := channeldb.NewWaitingProof(nMsg.isRemote, msg) - err := d.cfg.WaitingProofStore.Add(proof) - if err != nil { - err := fmt.Errorf("unable to store "+ - "the proof for short_chan_id=%v: %v", - shortChanID, err) - log.Error(err) - nMsg.err <- err - return nil, false - } - - log.Infof("Orphan %v proof announcement with "+ - "short_chan_id=%v, adding "+ - "to waiting batch", prefix, shortChanID) - nMsg.err <- nil - return nil, false - } - - nodeID := nMsg.source.SerializeCompressed() - isFirstNode := bytes.Equal(nodeID, chanInfo.NodeKey1Bytes[:]) - isSecondNode := bytes.Equal(nodeID, chanInfo.NodeKey2Bytes[:]) - - // Ensure that channel that was retrieved belongs to the peer - // which sent the proof announcement. - if !(isFirstNode || isSecondNode) { - err := fmt.Errorf("channel that was received not "+ - "belongs to the peer which sent the proof, "+ - "short_chan_id=%v", shortChanID) - log.Error(err) - nMsg.err <- err - return nil, false - } - - // If proof was sent by a local sub-system, then we'll - // send the announcement signature to the remote node - // so they can also reconstruct the full channel - // announcement. - if !nMsg.isRemote { - var remotePubKey [33]byte - if isFirstNode { - remotePubKey = chanInfo.NodeKey2Bytes - } else { - remotePubKey = chanInfo.NodeKey1Bytes - } - // Since the remote peer might not be online - // we'll call a method that will attempt to - // deliver the proof when it comes online. - err := d.reliableSender.sendMessage(msg, remotePubKey) - if err != nil { - err := fmt.Errorf("unable to reliably send %v "+ - "for channel=%v to peer=%x: %v", - msg.MsgType(), msg.ShortChannelID, - remotePubKey, err) - nMsg.err <- err - return nil, false - } - } - - // Check if we already have the full proof for this channel. - if chanInfo.AuthProof != nil { - // If we already have the fully assembled proof, then - // the peer sending us their proof has probably not - // received our local proof yet. So be kind and send - // them the full proof. - if nMsg.isRemote { - peerID := nMsg.source.SerializeCompressed() - log.Debugf("Got AnnounceSignatures for " + - "channel with full proof.") - - d.wg.Add(1) - go func() { - defer d.wg.Done() - log.Debugf("Received half proof for "+ - "channel %v with existing "+ - "full proof. Sending full "+ - "proof to peer=%x", - msg.ChannelID, - peerID) - - chanAnn, _, _, err := netann.CreateChanAnnouncement( - chanInfo.AuthProof, chanInfo, - e1, e2, - ) - if err != nil { - log.Errorf("unable to gen "+ - "ann: %v", err) - return - } - err = nMsg.peer.SendMessage( - false, chanAnn, - ) - if err != nil { - log.Errorf("Failed sending "+ - "full proof to "+ - "peer=%x: %v", - peerID, err) - return - } - log.Debugf("Full proof sent to peer=%x"+ - " for chanID=%v", peerID, - msg.ChannelID) - }() - } - - log.Debugf("Already have proof for channel "+ - "with chanID=%v", msg.ChannelID) - nMsg.err <- nil - return nil, true - } - - // Check that we received the opposite proof. If so, then we're - // now able to construct the full proof, and create the channel - // announcement. If we didn't receive the opposite half of the - // proof than we should store it this one, and wait for - // opposite to be received. - proof := channeldb.NewWaitingProof(nMsg.isRemote, msg) - oppositeProof, err := d.cfg.WaitingProofStore.Get( - proof.OppositeKey(), - ) - if err != nil && err != channeldb.ErrWaitingProofNotFound { - err := fmt.Errorf("unable to get "+ - "the opposite proof for short_chan_id=%v: %v", - shortChanID, err) - log.Error(err) - nMsg.err <- err - return nil, false - } - - if err == channeldb.ErrWaitingProofNotFound { - err := d.cfg.WaitingProofStore.Add(proof) - if err != nil { - err := fmt.Errorf("unable to store "+ - "the proof for short_chan_id=%v: %v", - shortChanID, err) - log.Error(err) - nMsg.err <- err - return nil, false - } - - log.Infof("1/2 of channel ann proof received for "+ - "short_chan_id=%v, waiting for other half", - shortChanID) - - nMsg.err <- nil - return nil, false - } - - // We now have both halves of the channel announcement proof, - // then we'll reconstruct the initial announcement so we can - // validate it shortly below. - var dbProof channeldb.ChannelAuthProof - if isFirstNode { - dbProof.NodeSig1Bytes = msg.NodeSignature.ToSignatureBytes() - dbProof.NodeSig2Bytes = oppositeProof.NodeSignature.ToSignatureBytes() - dbProof.BitcoinSig1Bytes = msg.BitcoinSignature.ToSignatureBytes() - dbProof.BitcoinSig2Bytes = oppositeProof.BitcoinSignature.ToSignatureBytes() - } else { - dbProof.NodeSig1Bytes = oppositeProof.NodeSignature.ToSignatureBytes() - dbProof.NodeSig2Bytes = msg.NodeSignature.ToSignatureBytes() - dbProof.BitcoinSig1Bytes = oppositeProof.BitcoinSignature.ToSignatureBytes() - dbProof.BitcoinSig2Bytes = msg.BitcoinSignature.ToSignatureBytes() - } - chanAnn, e1Ann, e2Ann, err := netann.CreateChanAnnouncement( - &dbProof, chanInfo, e1, e2, - ) - if err != nil { - log.Error(err) - nMsg.err <- err - return nil, false - } - - // With all the necessary components assembled validate the - // full channel announcement proof. - if err := routing.ValidateChannelAnn(chanAnn); err != nil { - err := fmt.Errorf("channel announcement proof "+ - "for short_chan_id=%v isn't valid: %v", - shortChanID, err) - - log.Error(err) - nMsg.err <- err - return nil, false - } - - // If the channel was returned by the router it means that - // existence of funding point and inclusion of nodes bitcoin - // keys in it already checked by the router. In this stage we - // should check that node keys are attest to the bitcoin keys - // by validating the signatures of announcement. If proof is - // valid then we'll populate the channel edge with it, so we - // can announce it on peer connect. - err = d.cfg.Router.AddProof(msg.ShortChannelID, &dbProof) - if err != nil { - err := fmt.Errorf("unable add proof to the "+ - "channel chanID=%v: %v", msg.ChannelID, err) - log.Error(err) - nMsg.err <- err - return nil, false - } - - err = d.cfg.WaitingProofStore.Remove(proof.OppositeKey()) - if err != nil { - err := fmt.Errorf("unable remove opposite proof "+ - "for the channel with chanID=%v: %v", - msg.ChannelID, err) - log.Error(err) - nMsg.err <- err - return nil, false - } - - // Proof was successfully created and now can announce the - // channel to the remain network. - log.Infof("Fully valid channel proof for short_chan_id=%v "+ - "constructed, adding to next ann batch", - shortChanID) - - // Assemble the necessary announcements to add to the next - // broadcasting batch. - announcements = append(announcements, networkMsg{ - peer: nMsg.peer, - source: nMsg.source, - msg: chanAnn, - }) - if src, err := chanInfo.NodeKey1(); err == nil && e1Ann != nil { - announcements = append(announcements, networkMsg{ - peer: nMsg.peer, - source: src, - msg: e1Ann, - }) - } - if src, err := chanInfo.NodeKey2(); err == nil && e2Ann != nil { - announcements = append(announcements, networkMsg{ - peer: nMsg.peer, - source: src, - msg: e2Ann, - }) - } - - // We'll also send along the node announcements for each channel - // participant if we know of them. To ensure our node - // announcement propagates to our channel counterparty, we'll - // set the source for each announcement to the node it belongs - // to, otherwise we won't send it since the source gets skipped. - // This isn't necessary for channel updates and announcement - // signatures since we send those directly to our channel - // counterparty through the gossiper's reliable sender. - node1Ann, err := d.fetchNodeAnn(chanInfo.NodeKey1Bytes) - if err != nil { - log.Debugf("Unable to fetch node announcement for "+ - "%x: %v", chanInfo.NodeKey1Bytes, err) - } else { - if nodeKey1, err := chanInfo.NodeKey1(); err == nil { - announcements = append(announcements, networkMsg{ - peer: nMsg.peer, - source: nodeKey1, - msg: node1Ann, - }) - } - } - node2Ann, err := d.fetchNodeAnn(chanInfo.NodeKey2Bytes) - if err != nil { - log.Debugf("Unable to fetch node announcement for "+ - "%x: %v", chanInfo.NodeKey2Bytes, err) - } else { - if nodeKey2, err := chanInfo.NodeKey2(); err == nil { - announcements = append(announcements, networkMsg{ - peer: nMsg.peer, - source: nodeKey2, - msg: node2Ann, - }) - } - } - - nMsg.err <- nil - return announcements, true + return d.handleAnnSig(nMsg, msg) default: err := errors.New("wrong type of the announcement") @@ -2978,3 +2658,309 @@ func (d *AuthenticatedGossiper) handleChanUpdate(nMsg *networkMsg, nMsg.err <- nil return announcements, true } + +// handleAnnSig processes a new announcement signatures message. +func (d *AuthenticatedGossiper) handleAnnSig(nMsg *networkMsg, + ann *lnwire.AnnounceSignatures) ([]networkMsg, bool) { + + needBlockHeight := ann.ShortChannelID.BlockHeight + + d.cfg.ProofMatureDelta + shortChanID := ann.ShortChannelID.ToUint64() + + prefix := "local" + if nMsg.isRemote { + prefix = "remote" + } + + log.Infof("Received new %v channel announcement for %v", prefix, + ann.ShortChannelID) + + // By the specification, channel announcement proofs should be sent + // after some number of confirmations after channel was registered in + // bitcoin blockchain. Therefore, we check if the proof is mature. + d.Lock() + premature := d.isPremature( + ann.ShortChannelID, d.cfg.ProofMatureDelta, nMsg, + ) + if premature { + log.Warnf("Premature proof announcement, current block height"+ + "lower than needed: %v < %v", d.bestHeight, + needBlockHeight) + d.Unlock() + nMsg.err <- nil + return nil, false + } + d.Unlock() + + // Ensure that we know of a channel with the target channel ID before + // proceeding further. + // + // We must acquire the mutex for this channel ID before getting the + // channel from the database, to ensure what we read does not change + // before we call AddProof() later. + d.channelMtx.Lock(ann.ShortChannelID.ToUint64()) + defer d.channelMtx.Unlock(ann.ShortChannelID.ToUint64()) + + chanInfo, e1, e2, err := d.cfg.Router.GetChannelByID( + ann.ShortChannelID, + ) + + if err != nil { + proof := channeldb.NewWaitingProof(nMsg.isRemote, ann) + err := d.cfg.WaitingProofStore.Add(proof) + if err != nil { + err := fmt.Errorf("unable to store the proof for "+ + "short_chan_id=%v: %v", shortChanID, err) + log.Error(err) + nMsg.err <- err + return nil, false + } + + log.Infof("Orphan %v proof announcement with short_chan_id=%v"+ + ", adding to waiting batch", prefix, shortChanID) + nMsg.err <- nil + return nil, false + } + + nodeID := nMsg.source.SerializeCompressed() + isFirstNode := bytes.Equal(nodeID, chanInfo.NodeKey1Bytes[:]) + isSecondNode := bytes.Equal(nodeID, chanInfo.NodeKey2Bytes[:]) + + // Ensure that channel that was retrieved belongs to the peer which + // sent the proof announcement. + if !(isFirstNode || isSecondNode) { + err := fmt.Errorf("channel that was received doesn't belong "+ + "to the peer which sent the proof, short_chan_id=%v", + shortChanID) + log.Error(err) + nMsg.err <- err + return nil, false + } + + // If proof was sent by a local sub-system, then we'll send the + // announcement signature to the remote node so they can also + // reconstruct the full channel announcement. + if !nMsg.isRemote { + var remotePubKey [33]byte + if isFirstNode { + remotePubKey = chanInfo.NodeKey2Bytes + } else { + remotePubKey = chanInfo.NodeKey1Bytes + } + // Since the remote peer might not be online we'll call a + // method that will attempt to deliver the proof when it comes + // online. + err := d.reliableSender.sendMessage(ann, remotePubKey) + if err != nil { + err := fmt.Errorf("unable to reliably send %v for "+ + "channel=%v to peer=%x: %v", ann.MsgType(), + ann.ShortChannelID, remotePubKey, err) + nMsg.err <- err + return nil, false + } + } + + // Check if we already have the full proof for this channel. + if chanInfo.AuthProof != nil { + // If we already have the fully assembled proof, then the peer + // sending us their proof has probably not received our local + // proof yet. So be kind and send them the full proof. + if nMsg.isRemote { + peerID := nMsg.source.SerializeCompressed() + log.Debugf("Got AnnounceSignatures for channel with " + + "full proof.") + + d.wg.Add(1) + go func() { + defer d.wg.Done() + log.Debugf("Received half proof for channel "+ + "%v with existing full proof. Sending"+ + " full proof to peer=%x", + ann.ChannelID, peerID) + + ca, _, _, err := netann.CreateChanAnnouncement( + chanInfo.AuthProof, chanInfo, e1, e2, + ) + if err != nil { + log.Errorf("unable to gen ann: %v", + err) + return + } + + err = nMsg.peer.SendMessage(false, ca) + if err != nil { + log.Errorf("Failed sending full proof"+ + " to peer=%x: %v", peerID, err) + return + } + + log.Debugf("Full proof sent to peer=%x for "+ + "chanID=%v", peerID, ann.ChannelID) + }() + } + + log.Debugf("Already have proof for channel with chanID=%v", + ann.ChannelID) + nMsg.err <- nil + return nil, true + } + + // Check that we received the opposite proof. If so, then we're now + // able to construct the full proof, and create the channel + // announcement. If we didn't receive the opposite half of the proof + // then we should store this one, and wait for the opposite to be + // received. + proof := channeldb.NewWaitingProof(nMsg.isRemote, ann) + oppProof, err := d.cfg.WaitingProofStore.Get(proof.OppositeKey()) + if err != nil && err != channeldb.ErrWaitingProofNotFound { + err := fmt.Errorf("unable to get the opposite proof for "+ + "short_chan_id=%v: %v", shortChanID, err) + log.Error(err) + nMsg.err <- err + return nil, false + } + + if err == channeldb.ErrWaitingProofNotFound { + err := d.cfg.WaitingProofStore.Add(proof) + if err != nil { + err := fmt.Errorf("unable to store the proof for "+ + "short_chan_id=%v: %v", shortChanID, err) + log.Error(err) + nMsg.err <- err + return nil, false + } + + log.Infof("1/2 of channel ann proof received for "+ + "short_chan_id=%v, waiting for other half", + shortChanID) + + nMsg.err <- nil + return nil, false + } + + // We now have both halves of the channel announcement proof, then + // we'll reconstruct the initial announcement so we can validate it + // shortly below. + var dbProof channeldb.ChannelAuthProof + if isFirstNode { + dbProof.NodeSig1Bytes = ann.NodeSignature.ToSignatureBytes() + dbProof.NodeSig2Bytes = oppProof.NodeSignature.ToSignatureBytes() + dbProof.BitcoinSig1Bytes = ann.BitcoinSignature.ToSignatureBytes() + dbProof.BitcoinSig2Bytes = oppProof.BitcoinSignature.ToSignatureBytes() + } else { + dbProof.NodeSig1Bytes = oppProof.NodeSignature.ToSignatureBytes() + dbProof.NodeSig2Bytes = ann.NodeSignature.ToSignatureBytes() + dbProof.BitcoinSig1Bytes = oppProof.BitcoinSignature.ToSignatureBytes() + dbProof.BitcoinSig2Bytes = ann.BitcoinSignature.ToSignatureBytes() + } + + chanAnn, e1Ann, e2Ann, err := netann.CreateChanAnnouncement( + &dbProof, chanInfo, e1, e2, + ) + if err != nil { + log.Error(err) + nMsg.err <- err + return nil, false + } + + // With all the necessary components assembled validate the full + // channel announcement proof. + if err := routing.ValidateChannelAnn(chanAnn); err != nil { + err := fmt.Errorf("channel announcement proof for "+ + "short_chan_id=%v isn't valid: %v", shortChanID, err) + + log.Error(err) + nMsg.err <- err + return nil, false + } + + // If the channel was returned by the router it means that existence of + // funding point and inclusion of nodes bitcoin keys in it already + // checked by the router. In this stage we should check that node keys + // attest to the bitcoin keys by validating the signatures of + // announcement. If proof is valid then we'll populate the channel edge + // with it, so we can announce it on peer connect. + err = d.cfg.Router.AddProof(ann.ShortChannelID, &dbProof) + if err != nil { + err := fmt.Errorf("unable add proof to the channel chanID=%v:"+ + " %v", ann.ChannelID, err) + log.Error(err) + nMsg.err <- err + return nil, false + } + + err = d.cfg.WaitingProofStore.Remove(proof.OppositeKey()) + if err != nil { + err := fmt.Errorf("unable to remove opposite proof for the "+ + "channel with chanID=%v: %v", ann.ChannelID, err) + log.Error(err) + nMsg.err <- err + return nil, false + } + + // Proof was successfully created and now can announce the channel to + // the remain network. + log.Infof("Fully valid channel proof for short_chan_id=%v constructed"+ + ", adding to next ann batch", shortChanID) + + // Assemble the necessary announcements to add to the next broadcasting + // batch. + var announcements []networkMsg + announcements = append(announcements, networkMsg{ + peer: nMsg.peer, + source: nMsg.source, + msg: chanAnn, + }) + if src, err := chanInfo.NodeKey1(); err == nil && e1Ann != nil { + announcements = append(announcements, networkMsg{ + peer: nMsg.peer, + source: src, + msg: e1Ann, + }) + } + if src, err := chanInfo.NodeKey2(); err == nil && e2Ann != nil { + announcements = append(announcements, networkMsg{ + peer: nMsg.peer, + source: src, + msg: e2Ann, + }) + } + + // We'll also send along the node announcements for each channel + // participant if we know of them. To ensure our node announcement + // propagates to our channel counterparty, we'll set the source for + // each announcement to the node it belongs to, otherwise we won't send + // it since the source gets skipped. This isn't necessary for channel + // updates and announcement signatures since we send those directly to + // our channel counterparty through the gossiper's reliable sender. + node1Ann, err := d.fetchNodeAnn(chanInfo.NodeKey1Bytes) + if err != nil { + log.Debugf("Unable to fetch node announcement for %x: %v", + chanInfo.NodeKey1Bytes, err) + } else { + if nodeKey1, err := chanInfo.NodeKey1(); err == nil { + announcements = append(announcements, networkMsg{ + peer: nMsg.peer, + source: nodeKey1, + msg: node1Ann, + }) + } + } + + node2Ann, err := d.fetchNodeAnn(chanInfo.NodeKey2Bytes) + if err != nil { + log.Debugf("Unable to fetch node announcement for %x: %v", + chanInfo.NodeKey2Bytes, err) + } else { + if nodeKey2, err := chanInfo.NodeKey2(); err == nil { + announcements = append(announcements, networkMsg{ + peer: nMsg.peer, + source: nodeKey2, + msg: node2Ann, + }) + } + } + + nMsg.err <- nil + return announcements, true +}