From 05d76b696dafb8235d74c5d418e3a6d8d076f4e0 Mon Sep 17 00:00:00 2001 From: Elle Mouton Date: Wed, 21 Aug 2024 08:34:57 +0200 Subject: [PATCH 01/22] multi: rename AnnounceSignatures to AnnounceSignatures1 In preparation for adding a new message, AnnounceSignatures2 along with an AnnounceSignatures interface, we rename the existing message to AnnounceSignatures1. --- channeldb/waitingproof.go | 17 ++++++++++------- channeldb/waitingproof_test.go | 2 +- discovery/gossiper.go | 8 ++++---- discovery/gossiper_test.go | 12 ++++++------ discovery/message_store.go | 2 +- discovery/message_store_test.go | 6 +++--- funding/manager.go | 4 ++-- funding/manager_test.go | 2 +- graph/validation_barrier.go | 6 +++--- lnwire/announcement_signatures.go | 18 +++++++++--------- lnwire/lnwire_test.go | 4 ++-- lnwire/message.go | 2 +- lnwire/message_test.go | 4 ++-- peer/brontide.go | 4 ++-- 14 files changed, 47 insertions(+), 44 deletions(-) diff --git a/channeldb/waitingproof.go b/channeldb/waitingproof.go index c6b2b9df52..faefc620ce 100644 --- a/channeldb/waitingproof.go +++ b/channeldb/waitingproof.go @@ -191,15 +191,17 @@ type WaitingProofKey [9]byte // needed to make channel proof exchange persistent, so that after client // restart we may receive remote/local half proof and process it. type WaitingProof struct { - *lnwire.AnnounceSignatures + *lnwire.AnnounceSignatures1 isRemote bool } // NewWaitingProof constructs a new waiting prof instance. -func NewWaitingProof(isRemote bool, proof *lnwire.AnnounceSignatures) *WaitingProof { +func NewWaitingProof(isRemote bool, + proof *lnwire.AnnounceSignatures1) *WaitingProof { + return &WaitingProof{ - AnnounceSignatures: proof, - isRemote: isRemote, + AnnounceSignatures1: proof, + isRemote: isRemote, } } @@ -238,7 +240,7 @@ func (p *WaitingProof) Encode(w io.Writer) error { return fmt.Errorf("expect io.Writer to be *bytes.Buffer") } - if err := p.AnnounceSignatures.Encode(buf, 0); err != nil { + if err := p.AnnounceSignatures1.Encode(buf, 0); err != nil { return err } @@ -252,11 +254,12 @@ func (p *WaitingProof) Decode(r io.Reader) error { return err } - msg := &lnwire.AnnounceSignatures{} + msg := &lnwire.AnnounceSignatures1{} if err := msg.Decode(r, 0); err != nil { return err } - (*p).AnnounceSignatures = msg + p.AnnounceSignatures1 = msg + return nil } diff --git a/channeldb/waitingproof_test.go b/channeldb/waitingproof_test.go index 1a00c829e0..d7113d9e75 100644 --- a/channeldb/waitingproof_test.go +++ b/channeldb/waitingproof_test.go @@ -18,7 +18,7 @@ func TestWaitingProofStore(t *testing.T) { db, err := MakeTestDB(t) require.NoError(t, err, "failed to make test database") - proof1 := NewWaitingProof(true, &lnwire.AnnounceSignatures{ + proof1 := NewWaitingProof(true, &lnwire.AnnounceSignatures1{ NodeSignature: wireSig, BitcoinSignature: wireSig, ExtraOpaqueData: make([]byte, 0), diff --git a/discovery/gossiper.go b/discovery/gossiper.go index 84fae767f0..80a304e772 100644 --- a/discovery/gossiper.go +++ b/discovery/gossiper.go @@ -1416,7 +1416,7 @@ func (d *AuthenticatedGossiper) networkHandler() { switch announcement.msg.(type) { // Channel announcement signatures are amongst the only // messages that we'll process serially. - case *lnwire.AnnounceSignatures: + case *lnwire.AnnounceSignatures1: emittedAnnouncements, _ := d.processNetworkAnnouncement( announcement, ) @@ -2041,7 +2041,7 @@ func (d *AuthenticatedGossiper) processNetworkAnnouncement( // A new signature announcement has been received. This indicates // willingness of nodes involved in the funding of a channel to // announce this new channel to the rest of the world. - case *lnwire.AnnounceSignatures: + case *lnwire.AnnounceSignatures1: return d.handleAnnSig(nMsg, msg) default: @@ -2129,7 +2129,7 @@ func (d *AuthenticatedGossiper) fetchNodeAnn( // MessageStore is seen as stale by the current graph. func (d *AuthenticatedGossiper) isMsgStale(msg lnwire.Message) bool { switch msg := msg.(type) { - case *lnwire.AnnounceSignatures: + case *lnwire.AnnounceSignatures1: chanInfo, _, _, err := d.cfg.Graph.GetChannelByID( msg.ShortChannelID, ) @@ -3200,7 +3200,7 @@ func (d *AuthenticatedGossiper) handleChanUpdate(nMsg *networkMsg, // handleAnnSig processes a new announcement signatures message. func (d *AuthenticatedGossiper) handleAnnSig(nMsg *networkMsg, - ann *lnwire.AnnounceSignatures) ([]networkMsg, bool) { + ann *lnwire.AnnounceSignatures1) ([]networkMsg, bool) { needBlockHeight := ann.ShortChannelID.BlockHeight + d.cfg.ProofMatureDelta diff --git a/discovery/gossiper_test.go b/discovery/gossiper_test.go index c7cb149cfe..f15b34d1d7 100644 --- a/discovery/gossiper_test.go +++ b/discovery/gossiper_test.go @@ -481,8 +481,8 @@ type annBatch struct { chanUpdAnn1 *lnwire.ChannelUpdate chanUpdAnn2 *lnwire.ChannelUpdate - localProofAnn *lnwire.AnnounceSignatures - remoteProofAnn *lnwire.AnnounceSignatures + localProofAnn *lnwire.AnnounceSignatures1 + remoteProofAnn *lnwire.AnnounceSignatures1 } func createLocalAnnouncements(blockHeight uint32) (*annBatch, error) { @@ -513,7 +513,7 @@ func createAnnouncements(blockHeight uint32, key1, key2 *btcec.PrivateKey) (*ann return nil, err } - batch.remoteProofAnn = &lnwire.AnnounceSignatures{ + batch.remoteProofAnn = &lnwire.AnnounceSignatures1{ ShortChannelID: lnwire.ShortChannelID{ BlockHeight: blockHeight, }, @@ -521,7 +521,7 @@ func createAnnouncements(blockHeight uint32, key1, key2 *btcec.PrivateKey) (*ann BitcoinSignature: batch.chanAnn.BitcoinSig2, } - batch.localProofAnn = &lnwire.AnnounceSignatures{ + batch.localProofAnn = &lnwire.AnnounceSignatures1{ ShortChannelID: lnwire.ShortChannelID{ BlockHeight: blockHeight, }, @@ -1540,7 +1540,7 @@ out: case msg := <-sentToPeer: // Since the ChannelUpdate will also be resent as it is // sent reliably, we'll need to filter it out. - if _, ok := msg.(*lnwire.AnnounceSignatures); !ok { + if _, ok := msg.(*lnwire.AnnounceSignatures1); !ok { continue } @@ -3302,7 +3302,7 @@ func TestSendChannelUpdateReliably(t *testing.T) { switch msg := msg.(type) { case *lnwire.ChannelUpdate: assertMessage(t, staleChannelUpdate, msg) - case *lnwire.AnnounceSignatures: + case *lnwire.AnnounceSignatures1: assertMessage(t, batch.localProofAnn, msg) default: t.Fatalf("send unexpected %v message", msg.MsgType()) diff --git a/discovery/message_store.go b/discovery/message_store.go index 10fa51623a..80970677e3 100644 --- a/discovery/message_store.go +++ b/discovery/message_store.go @@ -83,7 +83,7 @@ func NewMessageStore(db kvdb.Backend) (*MessageStore, error) { func msgShortChanID(msg lnwire.Message) (lnwire.ShortChannelID, error) { var shortChanID lnwire.ShortChannelID switch msg := msg.(type) { - case *lnwire.AnnounceSignatures: + case *lnwire.AnnounceSignatures1: shortChanID = msg.ShortChannelID case *lnwire.ChannelUpdate: shortChanID = msg.ShortChannelID diff --git a/discovery/message_store_test.go b/discovery/message_store_test.go index e812c3f1a3..54b3670645 100644 --- a/discovery/message_store_test.go +++ b/discovery/message_store_test.go @@ -52,8 +52,8 @@ func randCompressedPubKey(t *testing.T) [33]byte { return compressedPubKey } -func randAnnounceSignatures() *lnwire.AnnounceSignatures { - return &lnwire.AnnounceSignatures{ +func randAnnounceSignatures() *lnwire.AnnounceSignatures1 { + return &lnwire.AnnounceSignatures1{ ShortChannelID: lnwire.NewShortChanIDFromInt(rand.Uint64()), ExtraOpaqueData: make([]byte, 0), } @@ -116,7 +116,7 @@ func TestMessageStoreMessages(t *testing.T) { for _, msg := range peerMsgs { var shortChanID uint64 switch msg := msg.(type) { - case *lnwire.AnnounceSignatures: + case *lnwire.AnnounceSignatures1: shortChanID = msg.ShortChannelID.ToUint64() case *lnwire.ChannelUpdate: shortChanID = msg.ShortChannelID.ToUint64() diff --git a/funding/manager.go b/funding/manager.go index fb36bd9903..77a2ac0831 100644 --- a/funding/manager.go +++ b/funding/manager.go @@ -4145,7 +4145,7 @@ func (f *Manager) ensureInitialForwardingPolicy(chanID lnwire.ChannelID, type chanAnnouncement struct { chanAnn *lnwire.ChannelAnnouncement chanUpdateAnn *lnwire.ChannelUpdate - chanProof *lnwire.AnnounceSignatures + chanProof *lnwire.AnnounceSignatures1 } // newChanAnnouncement creates the authenticated channel announcement messages @@ -4337,7 +4337,7 @@ func (f *Manager) newChanAnnouncement(localPubKey, // Finally, we'll generate the announcement proof which we'll use to // provide the other side with the necessary signatures required to // allow them to reconstruct the full channel announcement. - proof := &lnwire.AnnounceSignatures{ + proof := &lnwire.AnnounceSignatures1{ ChannelID: chanID, ShortChannelID: shortChanID, } diff --git a/funding/manager_test.go b/funding/manager_test.go index d1aa9ec146..bef6cc0262 100644 --- a/funding/manager_test.go +++ b/funding/manager_test.go @@ -1297,7 +1297,7 @@ func assertAnnouncementSignatures(t *testing.T, alice, bob *testNode) { gotNodeAnnouncement := false for _, msg := range announcements { switch msg.(type) { - case *lnwire.AnnounceSignatures: + case *lnwire.AnnounceSignatures1: gotAnnounceSignatures = true case *lnwire.NodeAnnouncement: gotNodeAnnouncement = true diff --git a/graph/validation_barrier.go b/graph/validation_barrier.go index 731852d753..0622d89451 100644 --- a/graph/validation_barrier.go +++ b/graph/validation_barrier.go @@ -153,7 +153,7 @@ func (v *ValidationBarrier) InitJobDependencies(job interface{}) { return case *channeldb.LightningNode: return - case *lnwire.AnnounceSignatures: + case *lnwire.AnnounceSignatures1: // TODO(roasbeef): need to wait on chan ann? return } @@ -216,7 +216,7 @@ func (v *ValidationBarrier) WaitForDependants(job interface{}) error { // Other types of jobs can be executed immediately, so we'll just // return directly. - case *lnwire.AnnounceSignatures: + case *lnwire.AnnounceSignatures1: // TODO(roasbeef): need to wait on chan ann? case *models.ChannelEdgeInfo: case *lnwire.ChannelAnnouncement: @@ -301,7 +301,7 @@ func (v *ValidationBarrier) SignalDependants(job interface{}, allow bool) { shortID := lnwire.NewShortChanIDFromInt(msg.ChannelID) delete(v.chanEdgeDependencies, shortID) - case *lnwire.AnnounceSignatures: + case *lnwire.AnnounceSignatures1: return } } diff --git a/lnwire/announcement_signatures.go b/lnwire/announcement_signatures.go index 49b6106119..3f0e72803d 100644 --- a/lnwire/announcement_signatures.go +++ b/lnwire/announcement_signatures.go @@ -5,11 +5,11 @@ import ( "io" ) -// AnnounceSignatures is a direct message between two endpoints of a +// AnnounceSignatures1 is a direct message between two endpoints of a // channel and serves as an opt-in mechanism to allow the announcement of // the channel to the rest of the network. It contains the necessary // signatures by the sender to construct the channel announcement message. -type AnnounceSignatures struct { +type AnnounceSignatures1 struct { // ChannelID is the unique description of the funding transaction. // Channel id is better for users and debugging and short channel id is // used for quick test on existence of the particular utxo inside the @@ -43,15 +43,15 @@ type AnnounceSignatures struct { ExtraOpaqueData ExtraOpaqueData } -// A compile time check to ensure AnnounceSignatures implements the +// A compile time check to ensure AnnounceSignatures1 implements the // lnwire.Message interface. -var _ Message = (*AnnounceSignatures)(nil) +var _ Message = (*AnnounceSignatures1)(nil) -// Decode deserializes a serialized AnnounceSignatures stored in the passed +// Decode deserializes a serialized AnnounceSignatures1 stored in the passed // io.Reader observing the specified protocol version. // // This is part of the lnwire.Message interface. -func (a *AnnounceSignatures) Decode(r io.Reader, pver uint32) error { +func (a *AnnounceSignatures1) Decode(r io.Reader, pver uint32) error { return ReadElements(r, &a.ChannelID, &a.ShortChannelID, @@ -61,11 +61,11 @@ func (a *AnnounceSignatures) Decode(r io.Reader, pver uint32) error { ) } -// Encode serializes the target AnnounceSignatures into the passed io.Writer +// Encode serializes the target AnnounceSignatures1 into the passed io.Writer // observing the protocol version specified. // // This is part of the lnwire.Message interface. -func (a *AnnounceSignatures) Encode(w *bytes.Buffer, pver uint32) error { +func (a *AnnounceSignatures1) Encode(w *bytes.Buffer, pver uint32) error { if err := WriteChannelID(w, a.ChannelID); err != nil { return err } @@ -89,6 +89,6 @@ func (a *AnnounceSignatures) Encode(w *bytes.Buffer, pver uint32) error { // wire. // // This is part of the lnwire.Message interface. -func (a *AnnounceSignatures) MsgType() MessageType { +func (a *AnnounceSignatures1) MsgType() MessageType { return MsgAnnounceSignatures } diff --git a/lnwire/lnwire_test.go b/lnwire/lnwire_test.go index 7eb434f454..3b2d6fe149 100644 --- a/lnwire/lnwire_test.go +++ b/lnwire/lnwire_test.go @@ -1138,7 +1138,7 @@ func TestLightningWireProtocol(t *testing.T) { }, MsgAnnounceSignatures: func(v []reflect.Value, r *rand.Rand) { var err error - req := AnnounceSignatures{ + req := AnnounceSignatures1{ ShortChannelID: NewShortChanIDFromInt(uint64(r.Int63())), ExtraOpaqueData: make([]byte, 0), } @@ -1649,7 +1649,7 @@ func TestLightningWireProtocol(t *testing.T) { }, { msgType: MsgAnnounceSignatures, - scenario: func(m AnnounceSignatures) bool { + scenario: func(m AnnounceSignatures1) bool { return mainScenario(&m) }, }, diff --git a/lnwire/message.go b/lnwire/message.go index 2bf64a3131..1efb0368a6 100644 --- a/lnwire/message.go +++ b/lnwire/message.go @@ -267,7 +267,7 @@ func makeEmptyMessage(msgType MessageType) (Message, error) { case MsgPing: msg = &Ping{} case MsgAnnounceSignatures: - msg = &AnnounceSignatures{} + msg = &AnnounceSignatures1{} case MsgPong: msg = &Pong{} case MsgQueryShortChanIDs: diff --git a/lnwire/message_test.go b/lnwire/message_test.go index bbb434785f..1a3c61ac21 100644 --- a/lnwire/message_test.go +++ b/lnwire/message_test.go @@ -727,11 +727,11 @@ func newMsgChannelUpdate(t testing.TB, r *rand.Rand) *lnwire.ChannelUpdate { } func newMsgAnnounceSignatures(t testing.TB, - r *rand.Rand) *lnwire.AnnounceSignatures { + r *rand.Rand) *lnwire.AnnounceSignatures1 { t.Helper() - msg := &lnwire.AnnounceSignatures{ + msg := &lnwire.AnnounceSignatures1{ ShortChannelID: lnwire.NewShortChanIDFromInt( uint64(r.Int63()), ), diff --git a/peer/brontide.go b/peer/brontide.go index 3223e7f4b7..7a7576f464 100644 --- a/peer/brontide.go +++ b/peer/brontide.go @@ -1966,7 +1966,7 @@ out: case *lnwire.ChannelUpdate, *lnwire.ChannelAnnouncement, *lnwire.NodeAnnouncement, - *lnwire.AnnounceSignatures, + *lnwire.AnnounceSignatures1, *lnwire.GossipTimestampRange, *lnwire.QueryShortChanIDs, *lnwire.QueryChannelRange, @@ -2225,7 +2225,7 @@ func messageSummary(msg lnwire.Message) string { case *lnwire.Error: return fmt.Sprintf("%v", msg.Error()) - case *lnwire.AnnounceSignatures: + case *lnwire.AnnounceSignatures1: return fmt.Sprintf("chan_id=%v, short_chan_id=%v", msg.ChannelID, msg.ShortChannelID.ToUint64()) From bb44efa21f87cf0af4620840404dfcaf98a4a44f Mon Sep 17 00:00:00 2001 From: Elle Mouton Date: Wed, 21 Aug 2024 08:37:50 +0200 Subject: [PATCH 02/22] multi: rename ChannelAnnouncement to ChannelAnnouncment1 In preparation for adding the new ChannelAnnouncement2 message along with a ChannelAnnouncement interface, we rename the existing message to ChannelAnnouncement1. --- discovery/gossiper.go | 26 +++++++++++++------------- discovery/gossiper_test.go | 17 +++++++++-------- discovery/syncer.go | 2 +- discovery/syncer_test.go | 10 +++++----- funding/manager.go | 4 ++-- funding/manager_test.go | 2 +- graph/ann_validation.go | 2 +- graph/validation_barrier.go | 6 +++--- graph/validation_barrier_test.go | 4 ++-- lnwire/channel_announcement.go | 14 +++++++------- lnwire/lnwire_test.go | 4 ++-- lnwire/message.go | 2 +- lnwire/message_test.go | 4 ++-- netann/channel_announcement.go | 4 ++-- netann/channel_announcement_test.go | 2 +- netann/sign.go | 2 +- peer/brontide.go | 4 ++-- 17 files changed, 55 insertions(+), 54 deletions(-) diff --git a/discovery/gossiper.go b/discovery/gossiper.go index 80a304e772..ca7e088d04 100644 --- a/discovery/gossiper.go +++ b/discovery/gossiper.go @@ -849,9 +849,9 @@ func (d *AuthenticatedGossiper) ProcessRemoteAnnouncement(msg lnwire.Message, // To avoid inserting edges in the graph for our own channels that we // have already closed, we ignore such channel announcements coming // from the remote. - case *lnwire.ChannelAnnouncement: + case *lnwire.ChannelAnnouncement1: ownKey := d.selfKey.SerializeCompressed() - ownErr := fmt.Errorf("ignoring remote ChannelAnnouncement " + + ownErr := fmt.Errorf("ignoring remote ChannelAnnouncement1 " + "for own channel") if bytes.Equal(m.NodeID1[:], ownKey) || @@ -1011,7 +1011,7 @@ func (d *deDupedAnnouncements) addMsg(message networkMsg) { switch msg := message.msg.(type) { // Channel announcements are identified by the short channel id field. - case *lnwire.ChannelAnnouncement: + case *lnwire.ChannelAnnouncement1: deDupKey := msg.ShortChannelID sender := route.NewVertex(message.source) @@ -1585,7 +1585,7 @@ func (d *AuthenticatedGossiper) isRecentlyRejectedMsg(msg lnwire.Message, case *lnwire.ChannelUpdate: scid = m.ShortChannelID.ToUint64() - case *lnwire.ChannelAnnouncement: + case *lnwire.ChannelAnnouncement1: scid = m.ShortChannelID.ToUint64() default: @@ -1844,7 +1844,7 @@ func remotePubFromChanInfo(chanInfo *models.ChannelEdgeInfo, // to receive the remote peer's proof, while the remote peer is able to fully // assemble the proof and craft the ChannelAnnouncement. func (d *AuthenticatedGossiper) processRejectedEdge( - chanAnnMsg *lnwire.ChannelAnnouncement, + chanAnnMsg *lnwire.ChannelAnnouncement1, proof *models.ChannelAuthProof) ([]networkMsg, error) { // First, we'll fetch the state of the channel as we know if from the @@ -2029,7 +2029,7 @@ func (d *AuthenticatedGossiper) processNetworkAnnouncement( // *creation* of a new channel within the network. This only advertises // the existence of a channel and not yet the routing policies in // either direction of the channel. - case *lnwire.ChannelAnnouncement: + case *lnwire.ChannelAnnouncement1: return d.handleChanAnnouncement(nMsg, msg, schedulerOp) // A new authenticated channel edge update has arrived. This indicates @@ -2195,7 +2195,7 @@ func (d *AuthenticatedGossiper) isMsgStale(msg lnwire.Message) bool { // updateChannel creates a new fully signed update for the channel, and updates // the underlying graph with the new state. func (d *AuthenticatedGossiper) updateChannel(info *models.ChannelEdgeInfo, - edge *models.ChannelEdgePolicy) (*lnwire.ChannelAnnouncement, + edge *models.ChannelEdgePolicy) (*lnwire.ChannelAnnouncement1, *lnwire.ChannelUpdate, error) { // Parse the unsigned edge into a channel update. @@ -2234,10 +2234,10 @@ func (d *AuthenticatedGossiper) updateChannel(info *models.ChannelEdgeInfo, // We'll also create the original channel announcement so the two can // be broadcast along side each other (if necessary), but only if we // have a full channel announcement for this channel. - var chanAnn *lnwire.ChannelAnnouncement + var chanAnn *lnwire.ChannelAnnouncement1 if info.AuthProof != nil { chanID := lnwire.NewShortChanIDFromInt(info.ChannelID) - chanAnn = &lnwire.ChannelAnnouncement{ + chanAnn = &lnwire.ChannelAnnouncement1{ ShortChannelID: chanID, NodeID1: info.NodeKey1Bytes, NodeID2: info.NodeKey2Bytes, @@ -2409,18 +2409,18 @@ func (d *AuthenticatedGossiper) handleNodeAnnouncement(nMsg *networkMsg, // handleChanAnnouncement processes a new channel announcement. func (d *AuthenticatedGossiper) handleChanAnnouncement(nMsg *networkMsg, - ann *lnwire.ChannelAnnouncement, + ann *lnwire.ChannelAnnouncement1, ops []batch.SchedulerOption) ([]networkMsg, bool) { scid := ann.ShortChannelID - log.Debugf("Processing ChannelAnnouncement: peer=%v, short_chan_id=%v", + log.Debugf("Processing ChannelAnnouncement1: peer=%v, short_chan_id=%v", nMsg.peer, scid.ToUint64()) // We'll ignore any channel announcements that target any chain other // than the set of chains we know of. if !bytes.Equal(ann.ChainHash[:], d.cfg.ChainHash[:]) { - err := fmt.Errorf("ignoring ChannelAnnouncement from chain=%v"+ + err := fmt.Errorf("ignoring ChannelAnnouncement1 from chain=%v"+ ", gossiper on chain=%v", ann.ChainHash, d.cfg.ChainHash) log.Errorf(err.Error()) @@ -2788,7 +2788,7 @@ func (d *AuthenticatedGossiper) handleChanAnnouncement(nMsg *networkMsg, nMsg.err <- nil - log.Debugf("Processed ChannelAnnouncement: peer=%v, short_chan_id=%v", + log.Debugf("Processed ChannelAnnouncement1: peer=%v, short_chan_id=%v", nMsg.peer, scid.ToUint64()) return announcements, true diff --git a/discovery/gossiper_test.go b/discovery/gossiper_test.go index f15b34d1d7..eaffc19e13 100644 --- a/discovery/gossiper_test.go +++ b/discovery/gossiper_test.go @@ -476,7 +476,7 @@ type annBatch struct { nodeAnn1 *lnwire.NodeAnnouncement nodeAnn2 *lnwire.NodeAnnouncement - chanAnn *lnwire.ChannelAnnouncement + chanAnn *lnwire.ChannelAnnouncement1 chanUpdAnn1 *lnwire.ChannelUpdate chanUpdAnn2 *lnwire.ChannelUpdate @@ -635,9 +635,9 @@ func signUpdate(nodeKey *btcec.PrivateKey, a *lnwire.ChannelUpdate) error { func createAnnouncementWithoutProof(blockHeight uint32, key1, key2 *btcec.PublicKey, - extraBytes ...[]byte) *lnwire.ChannelAnnouncement { + extraBytes ...[]byte) *lnwire.ChannelAnnouncement1 { - a := &lnwire.ChannelAnnouncement{ + a := &lnwire.ChannelAnnouncement1{ ShortChannelID: lnwire.ShortChannelID{ BlockHeight: blockHeight, TxIndex: 0, @@ -657,13 +657,13 @@ func createAnnouncementWithoutProof(blockHeight uint32, } func createRemoteChannelAnnouncement(blockHeight uint32, - extraBytes ...[]byte) (*lnwire.ChannelAnnouncement, error) { + extraBytes ...[]byte) (*lnwire.ChannelAnnouncement1, error) { return createChannelAnnouncement(blockHeight, remoteKeyPriv1, remoteKeyPriv2, extraBytes...) } func createChannelAnnouncement(blockHeight uint32, key1, key2 *btcec.PrivateKey, - extraBytes ...[]byte) (*lnwire.ChannelAnnouncement, error) { + extraBytes ...[]byte) (*lnwire.ChannelAnnouncement1, error) { a := createAnnouncementWithoutProof(blockHeight, key1.PubKey(), key2.PubKey(), extraBytes...) @@ -1768,9 +1768,10 @@ func TestSignatureAnnouncementFullProofWhenRemoteProof(t *testing.T) { // We expect the gossiper to send this message to the remote peer. select { case msg := <-sentToPeer: - _, ok := msg.(*lnwire.ChannelAnnouncement) + _, ok := msg.(*lnwire.ChannelAnnouncement1) if !ok { - t.Fatalf("expected ChannelAnnouncement, instead got %T", msg) + t.Fatalf("expected ChannelAnnouncement1, instead got "+ + "%T", msg) } case <-time.After(2 * time.Second): t.Fatal("did not send local proof to peer") @@ -2811,7 +2812,7 @@ func TestRetransmit(t *testing.T) { var chanAnn, chanUpd, nodeAnn int for _, msg := range anns { switch msg.(type) { - case *lnwire.ChannelAnnouncement: + case *lnwire.ChannelAnnouncement1: chanAnn++ case *lnwire.ChannelUpdate: chanUpd++ diff --git a/discovery/syncer.go b/discovery/syncer.go index cdb041d99b..e74b2b6bb2 100644 --- a/discovery/syncer.go +++ b/discovery/syncer.go @@ -1447,7 +1447,7 @@ func (g *GossipSyncer) FilterGossipMsgs(msgs ...msgWithSenders) { // For each channel announcement message, we'll only send this // message if the channel updates for the channel are between // our time range. - case *lnwire.ChannelAnnouncement: + case *lnwire.ChannelAnnouncement1: // First, we'll check if the channel updates are in // this message batch. chanUpdates, ok := chanUpdateIndex[msg.ShortChannelID] diff --git a/discovery/syncer_test.go b/discovery/syncer_test.go index bb6aec5907..7d95baa82f 100644 --- a/discovery/syncer_test.go +++ b/discovery/syncer_test.go @@ -306,7 +306,7 @@ func TestGossipSyncerFilterGossipMsgsAllInMemory(t *testing.T) { }, { // Ann tuple below horizon. - msg: &lnwire.ChannelAnnouncement{ + msg: &lnwire.ChannelAnnouncement1{ ShortChannelID: lnwire.NewShortChanIDFromInt(10), }, }, @@ -318,7 +318,7 @@ func TestGossipSyncerFilterGossipMsgsAllInMemory(t *testing.T) { }, { // Ann tuple above horizon. - msg: &lnwire.ChannelAnnouncement{ + msg: &lnwire.ChannelAnnouncement1{ ShortChannelID: lnwire.NewShortChanIDFromInt(15), }, }, @@ -330,7 +330,7 @@ func TestGossipSyncerFilterGossipMsgsAllInMemory(t *testing.T) { }, { // Ann tuple beyond horizon. - msg: &lnwire.ChannelAnnouncement{ + msg: &lnwire.ChannelAnnouncement1{ ShortChannelID: lnwire.NewShortChanIDFromInt(20), }, }, @@ -343,7 +343,7 @@ func TestGossipSyncerFilterGossipMsgsAllInMemory(t *testing.T) { { // Ann w/o an update at all, the update in the DB will // be below the horizon. - msg: &lnwire.ChannelAnnouncement{ + msg: &lnwire.ChannelAnnouncement1{ ShortChannelID: lnwire.NewShortChanIDFromInt(25), }, }, @@ -706,7 +706,7 @@ func TestGossipSyncerReplyShortChanIDs(t *testing.T) { } queryReply := []lnwire.Message{ - &lnwire.ChannelAnnouncement{ + &lnwire.ChannelAnnouncement1{ ShortChannelID: lnwire.NewShortChanIDFromInt(20), }, &lnwire.ChannelUpdate{ diff --git a/funding/manager.go b/funding/manager.go index 77a2ac0831..6eb0b78c5c 100644 --- a/funding/manager.go +++ b/funding/manager.go @@ -4143,7 +4143,7 @@ func (f *Manager) ensureInitialForwardingPolicy(chanID lnwire.ChannelID, // chanAnnouncement encapsulates the two authenticated announcements that we // send out to the network after a new channel has been created locally. type chanAnnouncement struct { - chanAnn *lnwire.ChannelAnnouncement + chanAnn *lnwire.ChannelAnnouncement1 chanUpdateAnn *lnwire.ChannelUpdate chanProof *lnwire.AnnounceSignatures1 } @@ -4168,7 +4168,7 @@ func (f *Manager) newChanAnnouncement(localPubKey, // The unconditional section of the announcement is the ShortChannelID // itself which compactly encodes the location of the funding output // within the blockchain. - chanAnn := &lnwire.ChannelAnnouncement{ + chanAnn := &lnwire.ChannelAnnouncement1{ ShortChannelID: shortChanID, Features: lnwire.NewRawFeatureVector(), ChainHash: chainHash, diff --git a/funding/manager_test.go b/funding/manager_test.go index bef6cc0262..f863ccd9fb 100644 --- a/funding/manager_test.go +++ b/funding/manager_test.go @@ -1208,7 +1208,7 @@ func assertChannelAnnouncements(t *testing.T, alice, bob *testNode, gotChannelUpdate := false for _, msg := range announcements { switch m := msg.(type) { - case *lnwire.ChannelAnnouncement: + case *lnwire.ChannelAnnouncement1: gotChannelAnnouncement = true case *lnwire.ChannelUpdate: diff --git a/graph/ann_validation.go b/graph/ann_validation.go index 3936b4652f..bdc439e706 100644 --- a/graph/ann_validation.go +++ b/graph/ann_validation.go @@ -15,7 +15,7 @@ import ( // ValidateChannelAnn validates the channel announcement message and checks // that node signatures covers the announcement message, and that the bitcoin // signatures covers the node keys. -func ValidateChannelAnn(a *lnwire.ChannelAnnouncement) error { +func ValidateChannelAnn(a *lnwire.ChannelAnnouncement1) error { // First, we'll compute the digest (h) which is to be signed by each of // the keys included within the node announcement message. This hash // digest includes all the keys, so the (up to 4 signatures) will diff --git a/graph/validation_barrier.go b/graph/validation_barrier.go index 0622d89451..14a54357c9 100644 --- a/graph/validation_barrier.go +++ b/graph/validation_barrier.go @@ -102,7 +102,7 @@ func (v *ValidationBarrier) InitJobDependencies(job interface{}) { // ChannelUpdates for the same channel, or NodeAnnouncements of nodes // that are involved in this channel. This goes for both the wire // type,s and also the types that we use within the database. - case *lnwire.ChannelAnnouncement: + case *lnwire.ChannelAnnouncement1: // We ensure that we only create a new announcement signal iff, // one doesn't already exist, as there may be duplicate @@ -219,7 +219,7 @@ func (v *ValidationBarrier) WaitForDependants(job interface{}) error { case *lnwire.AnnounceSignatures1: // TODO(roasbeef): need to wait on chan ann? case *models.ChannelEdgeInfo: - case *lnwire.ChannelAnnouncement: + case *lnwire.ChannelAnnouncement1: } // Release the lock once the above read is finished. @@ -275,7 +275,7 @@ func (v *ValidationBarrier) SignalDependants(job interface{}, allow bool) { } delete(v.chanAnnFinSignal, shortID) } - case *lnwire.ChannelAnnouncement: + case *lnwire.ChannelAnnouncement1: finSignals, ok := v.chanAnnFinSignal[msg.ShortChannelID] if ok { if allow { diff --git a/graph/validation_barrier_test.go b/graph/validation_barrier_test.go index da404443f5..bf0d73ee66 100644 --- a/graph/validation_barrier_test.go +++ b/graph/validation_barrier_test.go @@ -73,9 +73,9 @@ func TestValidationBarrierQuit(t *testing.T) { // Create a set of unique channel announcements that we will prep for // validation. - anns := make([]*lnwire.ChannelAnnouncement, 0, numTasks) + anns := make([]*lnwire.ChannelAnnouncement1, 0, numTasks) for i := 0; i < numTasks; i++ { - anns = append(anns, &lnwire.ChannelAnnouncement{ + anns = append(anns, &lnwire.ChannelAnnouncement1{ ShortChannelID: lnwire.NewShortChanIDFromInt(uint64(i)), NodeID1: nodeIDFromInt(uint64(2 * i)), NodeID2: nodeIDFromInt(uint64(2*i + 1)), diff --git a/lnwire/channel_announcement.go b/lnwire/channel_announcement.go index 2b34c0f990..f9dee24b41 100644 --- a/lnwire/channel_announcement.go +++ b/lnwire/channel_announcement.go @@ -7,10 +7,10 @@ import ( "github.com/btcsuite/btcd/chaincfg/chainhash" ) -// ChannelAnnouncement message is used to announce the existence of a channel +// ChannelAnnouncement1 message is used to announce the existence of a channel // between two peers in the overlay, which is propagated by the discovery // service over broadcast handler. -type ChannelAnnouncement struct { +type ChannelAnnouncement1 struct { // This signatures are used by nodes in order to create cross // references between node's channel and node. Requiring both nodes // to sign indicates they are both willing to route other payments via @@ -60,13 +60,13 @@ type ChannelAnnouncement struct { // A compile time check to ensure ChannelAnnouncement implements the // lnwire.Message interface. -var _ Message = (*ChannelAnnouncement)(nil) +var _ Message = (*ChannelAnnouncement1)(nil) // Decode deserializes a serialized ChannelAnnouncement stored in the passed // io.Reader observing the specified protocol version. // // This is part of the lnwire.Message interface. -func (a *ChannelAnnouncement) Decode(r io.Reader, pver uint32) error { +func (a *ChannelAnnouncement1) Decode(r io.Reader, pver uint32) error { return ReadElements(r, &a.NodeSig1, &a.NodeSig2, @@ -87,7 +87,7 @@ func (a *ChannelAnnouncement) Decode(r io.Reader, pver uint32) error { // observing the protocol version specified. // // This is part of the lnwire.Message interface. -func (a *ChannelAnnouncement) Encode(w *bytes.Buffer, pver uint32) error { +func (a *ChannelAnnouncement1) Encode(w *bytes.Buffer, pver uint32) error { if err := WriteSig(w, a.NodeSig1); err != nil { return err } @@ -139,13 +139,13 @@ func (a *ChannelAnnouncement) Encode(w *bytes.Buffer, pver uint32) error { // wire. // // This is part of the lnwire.Message interface. -func (a *ChannelAnnouncement) MsgType() MessageType { +func (a *ChannelAnnouncement1) MsgType() MessageType { return MsgChannelAnnouncement } // DataToSign is used to retrieve part of the announcement message which should // be signed. -func (a *ChannelAnnouncement) DataToSign() ([]byte, error) { +func (a *ChannelAnnouncement1) DataToSign() ([]byte, error) { // We should not include the signatures itself. b := make([]byte, 0, MaxMsgBody) buf := bytes.NewBuffer(b) diff --git a/lnwire/lnwire_test.go b/lnwire/lnwire_test.go index 3b2d6fe149..2ad6f358e1 100644 --- a/lnwire/lnwire_test.go +++ b/lnwire/lnwire_test.go @@ -979,7 +979,7 @@ func TestLightningWireProtocol(t *testing.T) { }, MsgChannelAnnouncement: func(v []reflect.Value, r *rand.Rand) { var err error - req := ChannelAnnouncement{ + req := ChannelAnnouncement1{ ShortChannelID: NewShortChanIDFromInt(uint64(r.Int63())), Features: randRawFeatureVector(r), ExtraOpaqueData: make([]byte, 0), @@ -1631,7 +1631,7 @@ func TestLightningWireProtocol(t *testing.T) { }, { msgType: MsgChannelAnnouncement, - scenario: func(m ChannelAnnouncement) bool { + scenario: func(m ChannelAnnouncement1) bool { return mainScenario(&m) }, }, diff --git a/lnwire/message.go b/lnwire/message.go index 1efb0368a6..ef1a3628d2 100644 --- a/lnwire/message.go +++ b/lnwire/message.go @@ -259,7 +259,7 @@ func makeEmptyMessage(msgType MessageType) (Message, error) { case MsgError: msg = &Error{} case MsgChannelAnnouncement: - msg = &ChannelAnnouncement{} + msg = &ChannelAnnouncement1{} case MsgChannelUpdate: msg = &ChannelUpdate{} case MsgNodeAnnouncement: diff --git a/lnwire/message_test.go b/lnwire/message_test.go index 1a3c61ac21..4c0ebdd45f 100644 --- a/lnwire/message_test.go +++ b/lnwire/message_test.go @@ -645,11 +645,11 @@ func newMsgChannelReestablish(t testing.TB, } func newMsgChannelAnnouncement(t testing.TB, - r *rand.Rand) *lnwire.ChannelAnnouncement { + r *rand.Rand) *lnwire.ChannelAnnouncement1 { t.Helper() - msg := &lnwire.ChannelAnnouncement{ + msg := &lnwire.ChannelAnnouncement1{ ShortChannelID: lnwire.NewShortChanIDFromInt(uint64(r.Int63())), Features: rawFeatureVector(), NodeID1: randRawKey(t), diff --git a/netann/channel_announcement.go b/netann/channel_announcement.go index 8fc040f6f3..64e8a0c52a 100644 --- a/netann/channel_announcement.go +++ b/netann/channel_announcement.go @@ -14,14 +14,14 @@ import ( // peer's initial routing table upon connect. func CreateChanAnnouncement(chanProof *models.ChannelAuthProof, chanInfo *models.ChannelEdgeInfo, - e1, e2 *models.ChannelEdgePolicy) (*lnwire.ChannelAnnouncement, + e1, e2 *models.ChannelEdgePolicy) (*lnwire.ChannelAnnouncement1, *lnwire.ChannelUpdate, *lnwire.ChannelUpdate, error) { // First, using the parameters of the channel, along with the channel // authentication chanProof, we'll create re-create the original // authenticated channel announcement. chanID := lnwire.NewShortChanIDFromInt(chanInfo.ChannelID) - chanAnn := &lnwire.ChannelAnnouncement{ + chanAnn := &lnwire.ChannelAnnouncement1{ ShortChannelID: chanID, NodeID1: chanInfo.NodeKey1Bytes, NodeID2: chanInfo.NodeKey2Bytes, diff --git a/netann/channel_announcement_test.go b/netann/channel_announcement_test.go index 92a6c74ce0..0a5b0f048b 100644 --- a/netann/channel_announcement_test.go +++ b/netann/channel_announcement_test.go @@ -24,7 +24,7 @@ func TestCreateChanAnnouncement(t *testing.T) { t.Fatalf("unable to encode features: %v", err) } - expChanAnn := &lnwire.ChannelAnnouncement{ + expChanAnn := &lnwire.ChannelAnnouncement1{ ChainHash: chainhash.Hash{0x1}, ShortChannelID: lnwire.ShortChannelID{BlockHeight: 1}, NodeID1: key, diff --git a/netann/sign.go b/netann/sign.go index 4295331360..f66df00a5e 100644 --- a/netann/sign.go +++ b/netann/sign.go @@ -20,7 +20,7 @@ func SignAnnouncement(signer lnwallet.MessageSigner, keyLoc keychain.KeyLocator, ) switch m := msg.(type) { - case *lnwire.ChannelAnnouncement: + case *lnwire.ChannelAnnouncement1: data, err = m.DataToSign() case *lnwire.ChannelUpdate: data, err = m.DataToSign() diff --git a/peer/brontide.go b/peer/brontide.go index 7a7576f464..35340d8c87 100644 --- a/peer/brontide.go +++ b/peer/brontide.go @@ -1964,7 +1964,7 @@ out: } case *lnwire.ChannelUpdate, - *lnwire.ChannelAnnouncement, + *lnwire.ChannelAnnouncement1, *lnwire.NodeAnnouncement, *lnwire.AnnounceSignatures1, *lnwire.GossipTimestampRange, @@ -2229,7 +2229,7 @@ func messageSummary(msg lnwire.Message) string { return fmt.Sprintf("chan_id=%v, short_chan_id=%v", msg.ChannelID, msg.ShortChannelID.ToUint64()) - case *lnwire.ChannelAnnouncement: + case *lnwire.ChannelAnnouncement1: return fmt.Sprintf("chain_hash=%v, short_chan_id=%v", msg.ChainHash, msg.ShortChannelID.ToUint64()) From 60f331edb1087857ab4add2817fdd75b55a15077 Mon Sep 17 00:00:00 2001 From: Elle Mouton Date: Wed, 21 Aug 2024 08:39:37 +0200 Subject: [PATCH 03/22] multi: rename ChannelUpdate to ChannelUpdate1 In preparation for adding a new ChannelUpdate2 message and a ChannelUpdate interface, we rename the existing message to ChannelUpdate1. --- discovery/chan_series.go | 7 +++-- discovery/gossiper.go | 30 +++++++++++------- discovery/gossiper_test.go | 30 +++++++++--------- discovery/message_store.go | 12 ++++++-- discovery/message_store_test.go | 6 ++-- discovery/syncer.go | 8 +++-- discovery/syncer_test.go | 18 +++++------ funding/manager.go | 4 +-- funding/manager_test.go | 2 +- graph/ann_validation.go | 6 ++-- graph/builder.go | 2 +- graph/validation_barrier.go | 6 ++-- graph/validation_barrier_test.go | 4 +-- htlcswitch/interfaces.go | 2 +- htlcswitch/link.go | 23 +++++++------- htlcswitch/link_test.go | 6 ++-- htlcswitch/mock.go | 10 +++--- htlcswitch/switch.go | 7 +++-- htlcswitch/switch_test.go | 2 +- htlcswitch/test_utils.go | 6 ++-- lnrpc/routerrpc/router_backend.go | 2 +- lnwire/channel_update.go | 14 ++++----- lnwire/lnwire_test.go | 4 +-- lnwire/message.go | 2 +- lnwire/message_test.go | 4 +-- lnwire/onion_error.go | 44 +++++++++++++++------------ lnwire/onion_error_test.go | 6 ++-- netann/chan_status_manager.go | 4 +-- netann/chan_status_manager_test.go | 6 ++-- netann/channel_announcement.go | 4 +-- netann/channel_update.go | 16 +++++----- netann/channel_update_test.go | 4 +-- netann/sign.go | 2 +- peer/brontide.go | 6 ++-- peer/test_utils.go | 6 ++-- routing/missioncontrol_test.go | 6 ++-- routing/mock_test.go | 4 +-- routing/payment_session.go | 6 ++-- routing/payment_session_test.go | 2 +- routing/result_interpretation_test.go | 7 +++-- routing/router.go | 6 ++-- routing/router_test.go | 16 +++++----- server.go | 8 ++--- 43 files changed, 198 insertions(+), 172 deletions(-) diff --git a/discovery/chan_series.go b/discovery/chan_series.go index 8cbca1277d..ede116683f 100644 --- a/discovery/chan_series.go +++ b/discovery/chan_series.go @@ -61,7 +61,8 @@ type ChannelGraphTimeSeries interface { // specified short channel ID. If no channel updates are known for the // channel, then an empty slice will be returned. FetchChanUpdates(chain chainhash.Hash, - shortChanID lnwire.ShortChannelID) ([]*lnwire.ChannelUpdate, error) + shortChanID lnwire.ShortChannelID) ([]*lnwire.ChannelUpdate1, + error) } // ChanSeries is an implementation of the ChannelGraphTimeSeries @@ -326,7 +327,7 @@ func (c *ChanSeries) FetchChanAnns(chain chainhash.Hash, // // NOTE: This is part of the ChannelGraphTimeSeries interface. func (c *ChanSeries) FetchChanUpdates(chain chainhash.Hash, - shortChanID lnwire.ShortChannelID) ([]*lnwire.ChannelUpdate, error) { + shortChanID lnwire.ShortChannelID) ([]*lnwire.ChannelUpdate1, error) { chanInfo, e1, e2, err := c.graph.FetchChannelEdgesByID( shortChanID.ToUint64(), @@ -335,7 +336,7 @@ func (c *ChanSeries) FetchChanUpdates(chain chainhash.Hash, return nil, err } - chanUpdates := make([]*lnwire.ChannelUpdate, 0, 2) + chanUpdates := make([]*lnwire.ChannelUpdate1, 0, 2) if e1 != nil { chanUpdate, err := netann.ChannelUpdateFromEdge(chanInfo, e1) if err != nil { diff --git a/discovery/gossiper.go b/discovery/gossiper.go index ca7e088d04..a9ce801a06 100644 --- a/discovery/gossiper.go +++ b/discovery/gossiper.go @@ -322,7 +322,7 @@ type Config struct { // SignAliasUpdate is used to re-sign a channel update using the // remote's alias if the option-scid-alias feature bit was negotiated. - SignAliasUpdate func(u *lnwire.ChannelUpdate) (*ecdsa.Signature, + SignAliasUpdate func(u *lnwire.ChannelUpdate1) (*ecdsa.Signature, error) // FindBaseByAlias finds the SCID stored in the graph by an alias SCID. @@ -1035,7 +1035,7 @@ func (d *deDupedAnnouncements) addMsg(message networkMsg) { // Channel updates are identified by the (short channel id, // channelflags) tuple. - case *lnwire.ChannelUpdate: + case *lnwire.ChannelUpdate1: sender := route.NewVertex(message.source) deDupKey := channelUpdateID{ msg.ShortChannelID, @@ -1047,7 +1047,15 @@ func (d *deDupedAnnouncements) addMsg(message networkMsg) { if ok { // If we already have seen this message, record its // timestamp. - oldTimestamp = mws.msg.(*lnwire.ChannelUpdate).Timestamp + update, ok := mws.msg.(*lnwire.ChannelUpdate1) + if !ok { + log.Errorf("Expected *lnwire.ChannelUpdate1, "+ + "got: %T", mws.msg) + + return + } + + oldTimestamp = update.Timestamp } // If we already had this message with a strictly newer @@ -1582,7 +1590,7 @@ func (d *AuthenticatedGossiper) isRecentlyRejectedMsg(msg lnwire.Message, var scid uint64 switch m := msg.(type) { - case *lnwire.ChannelUpdate: + case *lnwire.ChannelUpdate1: scid = m.ShortChannelID.ToUint64() case *lnwire.ChannelAnnouncement1: @@ -2035,7 +2043,7 @@ func (d *AuthenticatedGossiper) processNetworkAnnouncement( // A new authenticated channel edge update has arrived. This indicates // that the directional information for an already known channel has // been updated. - case *lnwire.ChannelUpdate: + case *lnwire.ChannelUpdate1: return d.handleChanUpdate(nMsg, msg, schedulerOp) // A new signature announcement has been received. This indicates @@ -2058,7 +2066,7 @@ func (d *AuthenticatedGossiper) processNetworkAnnouncement( // should be inspected. func (d *AuthenticatedGossiper) processZombieUpdate( chanInfo *models.ChannelEdgeInfo, scid lnwire.ShortChannelID, - msg *lnwire.ChannelUpdate) error { + msg *lnwire.ChannelUpdate1) error { // The least-significant bit in the flag on the channel update tells us // which edge is being updated. @@ -2151,7 +2159,7 @@ func (d *AuthenticatedGossiper) isMsgStale(msg lnwire.Message) bool { // can safely delete the local proof from the database. return chanInfo.AuthProof != nil - case *lnwire.ChannelUpdate: + case *lnwire.ChannelUpdate1: _, p1, p2, err := d.cfg.Graph.GetChannelByID(msg.ShortChannelID) // If the channel cannot be found, it is most likely a leftover @@ -2196,7 +2204,7 @@ func (d *AuthenticatedGossiper) isMsgStale(msg lnwire.Message) bool { // the underlying graph with the new state. func (d *AuthenticatedGossiper) updateChannel(info *models.ChannelEdgeInfo, edge *models.ChannelEdgePolicy) (*lnwire.ChannelAnnouncement1, - *lnwire.ChannelUpdate, error) { + *lnwire.ChannelUpdate1, error) { // Parse the unsigned edge into a channel update. chanUpdate := netann.UnsignedChannelUpdateFromEdge(info, edge) @@ -2284,7 +2292,7 @@ func (d *AuthenticatedGossiper) SyncManager() *SyncManager { // IsKeepAliveUpdate determines whether this channel update is considered a // keep-alive update based on the previous channel update processed for the same // direction. -func IsKeepAliveUpdate(update *lnwire.ChannelUpdate, +func IsKeepAliveUpdate(update *lnwire.ChannelUpdate1, prev *models.ChannelEdgePolicy) bool { // Both updates should be from the same direction. @@ -2753,7 +2761,7 @@ func (d *AuthenticatedGossiper) handleChanAnnouncement(nMsg *networkMsg, // Reprocess the message, making sure we return an // error to the original caller in case the gossiper // shuts down. - case *lnwire.ChannelUpdate: + case *lnwire.ChannelUpdate1: log.Debugf("Reprocessing ChannelUpdate for "+ "shortChanID=%v", scid.ToUint64()) @@ -2796,7 +2804,7 @@ func (d *AuthenticatedGossiper) handleChanAnnouncement(nMsg *networkMsg, // handleChanUpdate processes a new channel update. func (d *AuthenticatedGossiper) handleChanUpdate(nMsg *networkMsg, - upd *lnwire.ChannelUpdate, + upd *lnwire.ChannelUpdate1, ops []batch.SchedulerOption) ([]networkMsg, bool) { log.Debugf("Processing ChannelUpdate: peer=%v, short_chan_id=%v, ", diff --git a/discovery/gossiper_test.go b/discovery/gossiper_test.go index eaffc19e13..db632cdafe 100644 --- a/discovery/gossiper_test.go +++ b/discovery/gossiper_test.go @@ -478,8 +478,8 @@ type annBatch struct { chanAnn *lnwire.ChannelAnnouncement1 - chanUpdAnn1 *lnwire.ChannelUpdate - chanUpdAnn2 *lnwire.ChannelUpdate + chanUpdAnn1 *lnwire.ChannelUpdate1 + chanUpdAnn2 *lnwire.ChannelUpdate1 localProofAnn *lnwire.AnnounceSignatures1 remoteProofAnn *lnwire.AnnounceSignatures1 @@ -585,12 +585,12 @@ func createNodeAnnouncement(priv *btcec.PrivateKey, func createUpdateAnnouncement(blockHeight uint32, flags lnwire.ChanUpdateChanFlags, nodeKey *btcec.PrivateKey, timestamp uint32, - extraBytes ...[]byte) (*lnwire.ChannelUpdate, error) { + extraBytes ...[]byte) (*lnwire.ChannelUpdate1, error) { var err error htlcMinMsat := lnwire.MilliSatoshi(prand.Int63()) - a := &lnwire.ChannelUpdate{ + a := &lnwire.ChannelUpdate1{ ShortChannelID: lnwire.ShortChannelID{ BlockHeight: blockHeight, }, @@ -618,7 +618,7 @@ func createUpdateAnnouncement(blockHeight uint32, return a, nil } -func signUpdate(nodeKey *btcec.PrivateKey, a *lnwire.ChannelUpdate) error { +func signUpdate(nodeKey *btcec.PrivateKey, a *lnwire.ChannelUpdate1) error { signer := mock.SingleSigner{Privkey: nodeKey} sig, err := netann.SignAnnouncement(&signer, testKeyLoc, a) if err != nil { @@ -749,7 +749,7 @@ func createTestCtx(t *testing.T, startHeight uint32, isChanPeer bool) ( return false } - signAliasUpdate := func(*lnwire.ChannelUpdate) (*ecdsa.Signature, + signAliasUpdate := func(*lnwire.ChannelUpdate1) (*ecdsa.Signature, error) { return nil, nil @@ -1462,7 +1462,7 @@ func TestSignatureAnnouncementRetryAtStartup(t *testing.T) { return false } - signAliasUpdate := func(*lnwire.ChannelUpdate) (*ecdsa.Signature, + signAliasUpdate := func(*lnwire.ChannelUpdate1) (*ecdsa.Signature, error) { return nil, nil @@ -1868,7 +1868,7 @@ func TestDeDuplicatedAnnouncements(t *testing.T) { t.Fatal("channel update not replaced in batch") } - assertChannelUpdate := func(channelUpdate *lnwire.ChannelUpdate) { + assertChannelUpdate := func(channelUpdate *lnwire.ChannelUpdate1) { channelKey := channelUpdateID{ ua3.ShortChannelID, ua3.ChannelFlags, @@ -2814,7 +2814,7 @@ func TestRetransmit(t *testing.T) { switch msg.(type) { case *lnwire.ChannelAnnouncement1: chanAnn++ - case *lnwire.ChannelUpdate: + case *lnwire.ChannelUpdate1: chanUpd++ case *lnwire.NodeAnnouncement: nodeAnn++ @@ -3247,7 +3247,7 @@ func TestSendChannelUpdateReliably(t *testing.T) { // already been announced. We'll keep track of the old message that is // now stale to use later on. staleChannelUpdate := batch.chanUpdAnn1 - newChannelUpdate := &lnwire.ChannelUpdate{} + newChannelUpdate := &lnwire.ChannelUpdate1{} *newChannelUpdate = *staleChannelUpdate newChannelUpdate.Timestamp++ if err := signUpdate(selfKeyPriv, newChannelUpdate); err != nil { @@ -3301,7 +3301,7 @@ func TestSendChannelUpdateReliably(t *testing.T) { } switch msg := msg.(type) { - case *lnwire.ChannelUpdate: + case *lnwire.ChannelUpdate1: assertMessage(t, staleChannelUpdate, msg) case *lnwire.AnnounceSignatures1: assertMessage(t, batch.localProofAnn, msg) @@ -3505,7 +3505,7 @@ out: // being the channel our first private channel. for i := 0; i < numChannels-1; i++ { assertBroadcastMsg(t, ctx, func(msg lnwire.Message) error { - upd, ok := msg.(*lnwire.ChannelUpdate) + upd, ok := msg.(*lnwire.ChannelUpdate1) if !ok { return fmt.Errorf("channel update not "+ "broadcast, instead %T was", msg) @@ -3529,7 +3529,7 @@ out: // remote peer via the reliable sender. select { case msg := <-sentMsgs: - upd, ok := msg.(*lnwire.ChannelUpdate) + upd, ok := msg.(*lnwire.ChannelUpdate1) if !ok { t.Fatalf("channel update not "+ "broadcast, instead %T was", msg) @@ -3553,7 +3553,7 @@ out: for { select { case msg := <-ctx.broadcastedMessage: - if upd, ok := msg.msg.(*lnwire.ChannelUpdate); ok { + if upd, ok := msg.msg.(*lnwire.ChannelUpdate1); ok { if upd.ShortChannelID == firstChanID { t.Fatalf("chan update msg received: %v", spew.Sdump(msg)) @@ -3885,7 +3885,7 @@ func TestRateLimitChannelUpdates(t *testing.T) { // We'll define a helper to assert whether updates should be rate // limited or not depending on their contents. - assertRateLimit := func(update *lnwire.ChannelUpdate, peer lnpeer.Peer, + assertRateLimit := func(update *lnwire.ChannelUpdate1, peer lnpeer.Peer, shouldRateLimit bool) { t.Helper() diff --git a/discovery/message_store.go b/discovery/message_store.go index 80970677e3..025a89d67a 100644 --- a/discovery/message_store.go +++ b/discovery/message_store.go @@ -85,7 +85,7 @@ func msgShortChanID(msg lnwire.Message) (lnwire.ShortChannelID, error) { switch msg := msg.(type) { case *lnwire.AnnounceSignatures1: shortChanID = msg.ShortChannelID - case *lnwire.ChannelUpdate: + case *lnwire.ChannelUpdate1: shortChanID = msg.ShortChannelID default: return shortChanID, ErrUnsupportedMessage @@ -160,7 +160,7 @@ func (s *MessageStore) DeleteMessage(msg lnwire.Message, // In the event that we're attempting to delete a ChannelUpdate // from the store, we'll make sure that we're actually deleting // the correct one as it can be overwritten. - if msg, ok := msg.(*lnwire.ChannelUpdate); ok { + if msg, ok := msg.(*lnwire.ChannelUpdate1); ok { // Deleting a value from a bucket that doesn't exist // acts as a NOP, so we'll return if a message doesn't // exist under this key. @@ -176,7 +176,13 @@ func (s *MessageStore) DeleteMessage(msg lnwire.Message, // If the timestamps don't match, then the update stored // should be the latest one, so we'll avoid deleting it. - if msg.Timestamp != dbMsg.(*lnwire.ChannelUpdate).Timestamp { + m, ok := dbMsg.(*lnwire.ChannelUpdate1) + if !ok { + return fmt.Errorf("expected "+ + "*lnwire.ChannelUpdate1, got: %T", + dbMsg) + } + if msg.Timestamp != m.Timestamp { return nil } } diff --git a/discovery/message_store_test.go b/discovery/message_store_test.go index 54b3670645..36c082e36f 100644 --- a/discovery/message_store_test.go +++ b/discovery/message_store_test.go @@ -59,8 +59,8 @@ func randAnnounceSignatures() *lnwire.AnnounceSignatures1 { } } -func randChannelUpdate() *lnwire.ChannelUpdate { - return &lnwire.ChannelUpdate{ +func randChannelUpdate() *lnwire.ChannelUpdate1 { + return &lnwire.ChannelUpdate1{ ShortChannelID: lnwire.NewShortChanIDFromInt(rand.Uint64()), ExtraOpaqueData: make([]byte, 0), } @@ -118,7 +118,7 @@ func TestMessageStoreMessages(t *testing.T) { switch msg := msg.(type) { case *lnwire.AnnounceSignatures1: shortChanID = msg.ShortChannelID.ToUint64() - case *lnwire.ChannelUpdate: + case *lnwire.ChannelUpdate1: shortChanID = msg.ShortChannelID.ToUint64() default: t.Fatalf("found unexpected message type %T", msg) diff --git a/discovery/syncer.go b/discovery/syncer.go index e74b2b6bb2..3043b39cd6 100644 --- a/discovery/syncer.go +++ b/discovery/syncer.go @@ -1406,9 +1406,11 @@ func (g *GossipSyncer) FilterGossipMsgs(msgs ...msgWithSenders) { // set of channel announcements and channel updates. This will allow us // to quickly check if we should forward a chan ann, based on the known // channel updates for a channel. - chanUpdateIndex := make(map[lnwire.ShortChannelID][]*lnwire.ChannelUpdate) + chanUpdateIndex := make( + map[lnwire.ShortChannelID][]*lnwire.ChannelUpdate1, + ) for _, msg := range msgs { - chanUpdate, ok := msg.msg.(*lnwire.ChannelUpdate) + chanUpdate, ok := msg.msg.(*lnwire.ChannelUpdate1) if !ok { continue } @@ -1478,7 +1480,7 @@ func (g *GossipSyncer) FilterGossipMsgs(msgs ...msgWithSenders) { // For each channel update, we'll only send if it the timestamp // is between our time range. - case *lnwire.ChannelUpdate: + case *lnwire.ChannelUpdate1: if passesFilter(msg.Timestamp) { msgsToSend = append(msgsToSend, msg) } diff --git a/discovery/syncer_test.go b/discovery/syncer_test.go index 7d95baa82f..0ee635a0f2 100644 --- a/discovery/syncer_test.go +++ b/discovery/syncer_test.go @@ -52,7 +52,7 @@ type mockChannelGraphTimeSeries struct { annResp chan []lnwire.Message updateReq chan lnwire.ShortChannelID - updateResp chan []*lnwire.ChannelUpdate + updateResp chan []*lnwire.ChannelUpdate1 } func newMockChannelGraphTimeSeries( @@ -74,7 +74,7 @@ func newMockChannelGraphTimeSeries( annResp: make(chan []lnwire.Message, 1), updateReq: make(chan lnwire.ShortChannelID, 1), - updateResp: make(chan []*lnwire.ChannelUpdate, 1), + updateResp: make(chan []*lnwire.ChannelUpdate1, 1), } } @@ -149,7 +149,7 @@ func (m *mockChannelGraphTimeSeries) FetchChanAnns(chain chainhash.Hash, return <-m.annResp, nil } func (m *mockChannelGraphTimeSeries) FetchChanUpdates(chain chainhash.Hash, - shortChanID lnwire.ShortChannelID) ([]*lnwire.ChannelUpdate, error) { + shortChanID lnwire.ShortChannelID) ([]*lnwire.ChannelUpdate1, error) { m.updateReq <- shortChanID @@ -311,7 +311,7 @@ func TestGossipSyncerFilterGossipMsgsAllInMemory(t *testing.T) { }, }, { - msg: &lnwire.ChannelUpdate{ + msg: &lnwire.ChannelUpdate1{ ShortChannelID: lnwire.NewShortChanIDFromInt(10), Timestamp: unixStamp(5), }, @@ -323,7 +323,7 @@ func TestGossipSyncerFilterGossipMsgsAllInMemory(t *testing.T) { }, }, { - msg: &lnwire.ChannelUpdate{ + msg: &lnwire.ChannelUpdate1{ ShortChannelID: lnwire.NewShortChanIDFromInt(15), Timestamp: unixStamp(25002), }, @@ -335,7 +335,7 @@ func TestGossipSyncerFilterGossipMsgsAllInMemory(t *testing.T) { }, }, { - msg: &lnwire.ChannelUpdate{ + msg: &lnwire.ChannelUpdate1{ ShortChannelID: lnwire.NewShortChanIDFromInt(20), Timestamp: unixStamp(999999), }, @@ -369,7 +369,7 @@ func TestGossipSyncerFilterGossipMsgsAllInMemory(t *testing.T) { } // If so, then we'll send back the missing update. - chanSeries.updateResp <- []*lnwire.ChannelUpdate{ + chanSeries.updateResp <- []*lnwire.ChannelUpdate1{ { ShortChannelID: lnwire.NewShortChanIDFromInt(25), Timestamp: unixStamp(5), @@ -551,7 +551,7 @@ func TestGossipSyncerApplyGossipFilter(t *testing.T) { // For this first response, we'll send back a proper // set of messages that should be echoed back. chanSeries.horizonResp <- []lnwire.Message{ - &lnwire.ChannelUpdate{ + &lnwire.ChannelUpdate1{ ShortChannelID: lnwire.NewShortChanIDFromInt(25), Timestamp: unixStamp(5), }, @@ -709,7 +709,7 @@ func TestGossipSyncerReplyShortChanIDs(t *testing.T) { &lnwire.ChannelAnnouncement1{ ShortChannelID: lnwire.NewShortChanIDFromInt(20), }, - &lnwire.ChannelUpdate{ + &lnwire.ChannelUpdate1{ ShortChannelID: lnwire.NewShortChanIDFromInt(20), Timestamp: unixStamp(999999), }, diff --git a/funding/manager.go b/funding/manager.go index 6eb0b78c5c..3af8ad1049 100644 --- a/funding/manager.go +++ b/funding/manager.go @@ -4144,7 +4144,7 @@ func (f *Manager) ensureInitialForwardingPolicy(chanID lnwire.ChannelID, // send out to the network after a new channel has been created locally. type chanAnnouncement struct { chanAnn *lnwire.ChannelAnnouncement1 - chanUpdateAnn *lnwire.ChannelUpdate + chanUpdateAnn *lnwire.ChannelUpdate1 chanProof *lnwire.AnnounceSignatures1 } @@ -4238,7 +4238,7 @@ func (f *Manager) newChanAnnouncement(localPubKey, // We announce the channel with the default values. Some of // these values can later be changed by crafting a new ChannelUpdate. - chanUpdateAnn := &lnwire.ChannelUpdate{ + chanUpdateAnn := &lnwire.ChannelUpdate1{ ShortChannelID: shortChanID, ChainHash: chainHash, Timestamp: uint32(time.Now().Unix()), diff --git a/funding/manager_test.go b/funding/manager_test.go index f863ccd9fb..dce8475adf 100644 --- a/funding/manager_test.go +++ b/funding/manager_test.go @@ -1210,7 +1210,7 @@ func assertChannelAnnouncements(t *testing.T, alice, bob *testNode, switch m := msg.(type) { case *lnwire.ChannelAnnouncement1: gotChannelAnnouncement = true - case *lnwire.ChannelUpdate: + case *lnwire.ChannelUpdate1: // The channel update sent by the node should // advertise the MinHTLC value required by the diff --git a/graph/ann_validation.go b/graph/ann_validation.go index bdc439e706..d586ff3ae8 100644 --- a/graph/ann_validation.go +++ b/graph/ann_validation.go @@ -127,7 +127,7 @@ func ValidateNodeAnn(a *lnwire.NodeAnnouncement) error { // signed by the node's private key, and (2) that the announcement's message // flags and optional fields are sane. func ValidateChannelUpdateAnn(pubKey *btcec.PublicKey, capacity btcutil.Amount, - a *lnwire.ChannelUpdate) error { + a *lnwire.ChannelUpdate1) error { if err := ValidateChannelUpdateFields(capacity, a); err != nil { return err @@ -138,7 +138,7 @@ func ValidateChannelUpdateAnn(pubKey *btcec.PublicKey, capacity btcutil.Amount, // VerifyChannelUpdateSignature verifies that the channel update message was // signed by the party with the given node public key. -func VerifyChannelUpdateSignature(msg *lnwire.ChannelUpdate, +func VerifyChannelUpdateSignature(msg *lnwire.ChannelUpdate1, pubKey *btcec.PublicKey) error { data, err := msg.DataToSign() @@ -163,7 +163,7 @@ func VerifyChannelUpdateSignature(msg *lnwire.ChannelUpdate, // ValidateChannelUpdateFields validates a channel update's message flags and // corresponding update fields. func ValidateChannelUpdateFields(capacity btcutil.Amount, - msg *lnwire.ChannelUpdate) error { + msg *lnwire.ChannelUpdate1) error { // The maxHTLC flag is mandatory. if !msg.MessageFlags.HasMaxHtlc() { diff --git a/graph/builder.go b/graph/builder.go index 717d3e5ad8..0997250688 100644 --- a/graph/builder.go +++ b/graph/builder.go @@ -1521,7 +1521,7 @@ type routingMsg struct { // ApplyChannelUpdate validates a channel update and if valid, applies it to the // database. It returns a bool indicating whether the updates were successful. -func (b *Builder) ApplyChannelUpdate(msg *lnwire.ChannelUpdate) bool { +func (b *Builder) ApplyChannelUpdate(msg *lnwire.ChannelUpdate1) bool { ch, _, _, err := b.GetChannelByID(msg.ShortChannelID) if err != nil { log.Errorf("Unable to retrieve channel by id: %v", err) diff --git a/graph/validation_barrier.go b/graph/validation_barrier.go index 14a54357c9..98d910d899 100644 --- a/graph/validation_barrier.go +++ b/graph/validation_barrier.go @@ -146,7 +146,7 @@ func (v *ValidationBarrier) InitJobDependencies(job interface{}) { // initialization needs to be done beyond just occupying a job slot. case *models.ChannelEdgePolicy: return - case *lnwire.ChannelUpdate: + case *lnwire.ChannelUpdate1: return case *lnwire.NodeAnnouncement: // TODO(roasbeef): node ann needs to wait on existing channel updates @@ -202,7 +202,7 @@ func (v *ValidationBarrier) WaitForDependants(job interface{}) error { jobDesc = fmt.Sprintf("job=channeldb.LightningNode, pub=%s", vertex) - case *lnwire.ChannelUpdate: + case *lnwire.ChannelUpdate1: signals, ok = v.chanEdgeDependencies[msg.ShortChannelID] jobDesc = fmt.Sprintf("job=lnwire.ChannelUpdate, scid=%v", @@ -295,7 +295,7 @@ func (v *ValidationBarrier) SignalDependants(job interface{}, allow bool) { delete(v.nodeAnnDependencies, route.Vertex(msg.PubKeyBytes)) case *lnwire.NodeAnnouncement: delete(v.nodeAnnDependencies, route.Vertex(msg.NodeID)) - case *lnwire.ChannelUpdate: + case *lnwire.ChannelUpdate1: delete(v.chanEdgeDependencies, msg.ShortChannelID) case *models.ChannelEdgePolicy: shortID := lnwire.NewShortChanIDFromInt(msg.ChannelID) diff --git a/graph/validation_barrier_test.go b/graph/validation_barrier_test.go index bf0d73ee66..38fc7a0870 100644 --- a/graph/validation_barrier_test.go +++ b/graph/validation_barrier_test.go @@ -85,9 +85,9 @@ func TestValidationBarrierQuit(t *testing.T) { // Create a set of channel updates, that must wait until their // associated channel announcement has been verified. - chanUpds := make([]*lnwire.ChannelUpdate, 0, numTasks) + chanUpds := make([]*lnwire.ChannelUpdate1, 0, numTasks) for i := 0; i < numTasks; i++ { - chanUpds = append(chanUpds, &lnwire.ChannelUpdate{ + chanUpds = append(chanUpds, &lnwire.ChannelUpdate1{ ShortChannelID: lnwire.NewShortChanIDFromInt(uint64(i)), }) barrier.InitJobDependencies(chanUpds[i]) diff --git a/htlcswitch/interfaces.go b/htlcswitch/interfaces.go index 4e6384b978..ca653a3265 100644 --- a/htlcswitch/interfaces.go +++ b/htlcswitch/interfaces.go @@ -86,7 +86,7 @@ type scidAliasHandler interface { // HTLCs on option_scid_alias channels. attachFailAliasUpdate(failClosure func( sid lnwire.ShortChannelID, - incoming bool) *lnwire.ChannelUpdate) + incoming bool) *lnwire.ChannelUpdate1) // getAliases fetches the link's underlying aliases. This is used by // the Switch to determine whether to forward an HTLC and where to diff --git a/htlcswitch/link.go b/htlcswitch/link.go index 83495f357b..5e5bc8dc22 100644 --- a/htlcswitch/link.go +++ b/htlcswitch/link.go @@ -120,7 +120,8 @@ type ChannelLinkConfig struct { // specified when we receive an incoming HTLC. This will be used to // provide payment senders our latest policy when sending encrypted // error messages. - FetchLastChannelUpdate func(lnwire.ShortChannelID) (*lnwire.ChannelUpdate, error) + FetchLastChannelUpdate func(lnwire.ShortChannelID) ( + *lnwire.ChannelUpdate1, error) // Peer is a lightning network node with which we have the channel link // opened. @@ -262,7 +263,7 @@ type ChannelLinkConfig struct { // FailAliasUpdate is a function used to fail an HTLC for an // option_scid_alias channel. FailAliasUpdate func(sid lnwire.ShortChannelID, - incoming bool) *lnwire.ChannelUpdate + incoming bool) *lnwire.ChannelUpdate1 // GetAliases is used by the link and switch to fetch the set of // aliases for a given link. @@ -765,7 +766,7 @@ func shouldAdjustCommitFee(netFee, chanFee, } // failCb is used to cut down on the argument verbosity. -type failCb func(update *lnwire.ChannelUpdate) lnwire.FailureMessage +type failCb func(update *lnwire.ChannelUpdate1) lnwire.FailureMessage // createFailureWithUpdate creates a ChannelUpdate when failing an incoming or // outgoing HTLC. It may return a FailureMessage that references a channel's @@ -2949,7 +2950,7 @@ func (l *channelLink) getAliases() []lnwire.ShortChannelID { // // Part of the scidAliasHandler interface. func (l *channelLink) attachFailAliasUpdate(closure func( - sid lnwire.ShortChannelID, incoming bool) *lnwire.ChannelUpdate) { + sid lnwire.ShortChannelID, incoming bool) *lnwire.ChannelUpdate1) { l.Lock() l.cfg.FailAliasUpdate = closure @@ -3041,7 +3042,7 @@ func (l *channelLink) CheckHtlcForward(payHash [32]byte, // As part of the returned error, we'll send our latest routing // policy so the sending node obtains the most up to date data. - cb := func(upd *lnwire.ChannelUpdate) lnwire.FailureMessage { + cb := func(upd *lnwire.ChannelUpdate1) lnwire.FailureMessage { return lnwire.NewFeeInsufficient(amtToForward, *upd) } failure := l.createFailureWithUpdate(false, originalScid, cb) @@ -3069,7 +3070,7 @@ func (l *channelLink) CheckHtlcForward(payHash [32]byte, // Grab the latest routing policy so the sending node is up to // date with our current policy. - cb := func(upd *lnwire.ChannelUpdate) lnwire.FailureMessage { + cb := func(upd *lnwire.ChannelUpdate1) lnwire.FailureMessage { return lnwire.NewIncorrectCltvExpiry( incomingTimeout, *upd, ) @@ -3118,7 +3119,7 @@ func (l *channelLink) canSendHtlc(policy models.ForwardingPolicy, // As part of the returned error, we'll send our latest routing // policy so the sending node obtains the most up to date data. - cb := func(upd *lnwire.ChannelUpdate) lnwire.FailureMessage { + cb := func(upd *lnwire.ChannelUpdate1) lnwire.FailureMessage { return lnwire.NewAmountBelowMinimum(amt, *upd) } failure := l.createFailureWithUpdate(false, originalScid, cb) @@ -3133,7 +3134,7 @@ func (l *channelLink) canSendHtlc(policy models.ForwardingPolicy, // As part of the returned error, we'll send our latest routing // policy so the sending node obtains the most up-to-date data. - cb := func(upd *lnwire.ChannelUpdate) lnwire.FailureMessage { + cb := func(upd *lnwire.ChannelUpdate1) lnwire.FailureMessage { return lnwire.NewTemporaryChannelFailure(upd) } failure := l.createFailureWithUpdate(false, originalScid, cb) @@ -3148,7 +3149,7 @@ func (l *channelLink) canSendHtlc(policy models.ForwardingPolicy, "outgoing_expiry=%v, best_height=%v", payHash[:], timeout, heightNow) - cb := func(upd *lnwire.ChannelUpdate) lnwire.FailureMessage { + cb := func(upd *lnwire.ChannelUpdate1) lnwire.FailureMessage { return lnwire.NewExpiryTooSoon(*upd) } failure := l.createFailureWithUpdate(false, originalScid, cb) @@ -3168,7 +3169,7 @@ func (l *channelLink) canSendHtlc(policy models.ForwardingPolicy, if amt > l.Bandwidth() { l.log.Warnf("insufficient bandwidth to route htlc: %v is "+ "larger than %v", amt, l.Bandwidth()) - cb := func(upd *lnwire.ChannelUpdate) lnwire.FailureMessage { + cb := func(upd *lnwire.ChannelUpdate1) lnwire.FailureMessage { return lnwire.NewTemporaryChannelFailure(upd) } failure := l.createFailureWithUpdate(false, originalScid, cb) @@ -3680,7 +3681,7 @@ func (l *channelLink) processRemoteAdds(fwdPkg *channeldb.FwdPkg) { l.log.Errorf("unable to encode the "+ "remaining route %v", err) - cb := func(upd *lnwire.ChannelUpdate) lnwire.FailureMessage { + cb := func(upd *lnwire.ChannelUpdate1) lnwire.FailureMessage { //nolint:lll return lnwire.NewTemporaryChannelFailure(upd) } diff --git a/htlcswitch/link_test.go b/htlcswitch/link_test.go index 53a084209e..aa5fd6e4e7 100644 --- a/htlcswitch/link_test.go +++ b/htlcswitch/link_test.go @@ -6166,13 +6166,13 @@ func TestForwardingAsymmetricTimeLockPolicies(t *testing.T) { // forwarding policy. func TestCheckHtlcForward(t *testing.T) { fetchLastChannelUpdate := func(lnwire.ShortChannelID) ( - *lnwire.ChannelUpdate, error) { + *lnwire.ChannelUpdate1, error) { - return &lnwire.ChannelUpdate{}, nil + return &lnwire.ChannelUpdate1{}, nil } failAliasUpdate := func(sid lnwire.ShortChannelID, - incoming bool) *lnwire.ChannelUpdate { + incoming bool) *lnwire.ChannelUpdate1 { return nil } diff --git a/htlcswitch/mock.go b/htlcswitch/mock.go index 6d27a5be99..6123c9010d 100644 --- a/htlcswitch/mock.go +++ b/htlcswitch/mock.go @@ -167,7 +167,7 @@ type mockServer struct { var _ lnpeer.Peer = (*mockServer)(nil) func initSwitchWithDB(startingHeight uint32, db *channeldb.DB) (*Switch, error) { - signAliasUpdate := func(u *lnwire.ChannelUpdate) (*ecdsa.Signature, + signAliasUpdate := func(u *lnwire.ChannelUpdate1) (*ecdsa.Signature, error) { return testSig, nil @@ -183,9 +183,9 @@ func initSwitchWithDB(startingHeight uint32, db *channeldb.DB) (*Switch, error) events: make(map[time.Time]channeldb.ForwardingEvent), }, FetchLastChannelUpdate: func(scid lnwire.ShortChannelID) ( - *lnwire.ChannelUpdate, error) { + *lnwire.ChannelUpdate1, error) { - return &lnwire.ChannelUpdate{ + return &lnwire.ChannelUpdate1{ ShortChannelID: scid, }, nil }, @@ -735,7 +735,7 @@ type mockChannelLink struct { checkHtlcForwardResult *LinkError failAliasUpdate func(sid lnwire.ShortChannelID, - incoming bool) *lnwire.ChannelUpdate + incoming bool) *lnwire.ChannelUpdate1 confirmedZC bool } @@ -870,7 +870,7 @@ func (f *mockChannelLink) AttachMailBox(mailBox MailBox) { } func (f *mockChannelLink) attachFailAliasUpdate(closure func( - sid lnwire.ShortChannelID, incoming bool) *lnwire.ChannelUpdate) { + sid lnwire.ShortChannelID, incoming bool) *lnwire.ChannelUpdate1) { f.failAliasUpdate = closure } diff --git a/htlcswitch/switch.go b/htlcswitch/switch.go index 01596ade02..4ff2746a73 100644 --- a/htlcswitch/switch.go +++ b/htlcswitch/switch.go @@ -173,7 +173,8 @@ type Config struct { // specified when we receive an incoming HTLC. This will be used to // provide payment senders our latest policy when sending encrypted // error messages. - FetchLastChannelUpdate func(lnwire.ShortChannelID) (*lnwire.ChannelUpdate, error) + FetchLastChannelUpdate func(lnwire.ShortChannelID) ( + *lnwire.ChannelUpdate1, error) // Notifier is an instance of a chain notifier that we'll use to signal // the switch when a new block has arrived. @@ -220,7 +221,7 @@ type Config struct { // option_scid_alias channels. This avoids a potential privacy leak by // replacing the public, confirmed SCID with the alias in the // ChannelUpdate. - SignAliasUpdate func(u *lnwire.ChannelUpdate) (*ecdsa.Signature, + SignAliasUpdate func(u *lnwire.ChannelUpdate1) (*ecdsa.Signature, error) // IsAlias returns whether or not a given SCID is an alias. @@ -2640,7 +2641,7 @@ func (s *Switch) failMailboxUpdate(outgoingScid, // and the caller is expected to handle this properly. In this case, a return // to the original non-alias behavior is expected. func (s *Switch) failAliasUpdate(scid lnwire.ShortChannelID, - incoming bool) *lnwire.ChannelUpdate { + incoming bool) *lnwire.ChannelUpdate1 { // This function does not defer the unlocking because of the database // lookups for ChannelUpdate. diff --git a/htlcswitch/switch_test.go b/htlcswitch/switch_test.go index 0bc0df2d46..825ee6c652 100644 --- a/htlcswitch/switch_test.go +++ b/htlcswitch/switch_test.go @@ -3951,7 +3951,7 @@ func TestSwitchHoldForward(t *testing.T) { // Simulate an error during the composition of the failure message. currentCallback := c.s.cfg.FetchLastChannelUpdate c.s.cfg.FetchLastChannelUpdate = func( - lnwire.ShortChannelID) (*lnwire.ChannelUpdate, error) { + lnwire.ShortChannelID) (*lnwire.ChannelUpdate1, error) { return nil, errors.New("cannot fetch update") } diff --git a/htlcswitch/test_utils.go b/htlcswitch/test_utils.go index a71577ef2b..abd48e806d 100644 --- a/htlcswitch/test_utils.go +++ b/htlcswitch/test_utils.go @@ -91,8 +91,10 @@ func genIDs() (lnwire.ChannelID, lnwire.ChannelID, lnwire.ShortChannelID, // mockGetChanUpdateMessage helper function which returns topology update of // the channel -func mockGetChanUpdateMessage(cid lnwire.ShortChannelID) (*lnwire.ChannelUpdate, error) { - return &lnwire.ChannelUpdate{ +func mockGetChanUpdateMessage(_ lnwire.ShortChannelID) (*lnwire.ChannelUpdate1, + error) { + + return &lnwire.ChannelUpdate1{ Signature: wireSig, }, nil } diff --git a/lnrpc/routerrpc/router_backend.go b/lnrpc/routerrpc/router_backend.go index 6694e8b4f6..1a428e5bd4 100644 --- a/lnrpc/routerrpc/router_backend.go +++ b/lnrpc/routerrpc/router_backend.go @@ -1626,7 +1626,7 @@ func marshallWireError(msg lnwire.FailureMessage, // marshallChannelUpdate marshalls a channel update as received over the wire to // the router rpc format. -func marshallChannelUpdate(update *lnwire.ChannelUpdate) *lnrpc.ChannelUpdate { +func marshallChannelUpdate(update *lnwire.ChannelUpdate1) *lnrpc.ChannelUpdate { if update == nil { return nil } diff --git a/lnwire/channel_update.go b/lnwire/channel_update.go index 7f42a58b46..c322c51639 100644 --- a/lnwire/channel_update.go +++ b/lnwire/channel_update.go @@ -56,11 +56,11 @@ func (c ChanUpdateChanFlags) String() string { return fmt.Sprintf("%08b", c) } -// ChannelUpdate message is used after channel has been initially announced. +// ChannelUpdate1 message is used after channel has been initially announced. // Each side independently announces its fees and minimum expiry for HTLCs and // other parameters. Also this message is used to redeclare initially set // channel parameters. -type ChannelUpdate struct { +type ChannelUpdate1 struct { // Signature is used to validate the announced data and prove the // ownership of node id. Signature Sig @@ -122,13 +122,13 @@ type ChannelUpdate struct { // A compile time check to ensure ChannelUpdate implements the lnwire.Message // interface. -var _ Message = (*ChannelUpdate)(nil) +var _ Message = (*ChannelUpdate1)(nil) // Decode deserializes a serialized ChannelUpdate stored in the passed // io.Reader observing the specified protocol version. // // This is part of the lnwire.Message interface. -func (a *ChannelUpdate) Decode(r io.Reader, pver uint32) error { +func (a *ChannelUpdate1) Decode(r io.Reader, pver uint32) error { err := ReadElements(r, &a.Signature, a.ChainHash[:], @@ -159,7 +159,7 @@ func (a *ChannelUpdate) Decode(r io.Reader, pver uint32) error { // observing the protocol version specified. // // This is part of the lnwire.Message interface. -func (a *ChannelUpdate) Encode(w *bytes.Buffer, pver uint32) error { +func (a *ChannelUpdate1) Encode(w *bytes.Buffer, pver uint32) error { if err := WriteSig(w, a.Signature); err != nil { return err } @@ -217,13 +217,13 @@ func (a *ChannelUpdate) Encode(w *bytes.Buffer, pver uint32) error { // wire. // // This is part of the lnwire.Message interface. -func (a *ChannelUpdate) MsgType() MessageType { +func (a *ChannelUpdate1) MsgType() MessageType { return MsgChannelUpdate } // DataToSign is used to retrieve part of the announcement message which should // be signed. -func (a *ChannelUpdate) DataToSign() ([]byte, error) { +func (a *ChannelUpdate1) DataToSign() ([]byte, error) { // We should not include the signatures itself. b := make([]byte, 0, MaxMsgBody) buf := bytes.NewBuffer(b) diff --git a/lnwire/lnwire_test.go b/lnwire/lnwire_test.go index 2ad6f358e1..22f26bd5b1 100644 --- a/lnwire/lnwire_test.go +++ b/lnwire/lnwire_test.go @@ -1100,7 +1100,7 @@ func TestLightningWireProtocol(t *testing.T) { maxHtlc = 0 } - req := ChannelUpdate{ + req := ChannelUpdate1{ ShortChannelID: NewShortChanIDFromInt(uint64(r.Int63())), Timestamp: uint32(r.Int31()), MessageFlags: msgFlags, @@ -1643,7 +1643,7 @@ func TestLightningWireProtocol(t *testing.T) { }, { msgType: MsgChannelUpdate, - scenario: func(m ChannelUpdate) bool { + scenario: func(m ChannelUpdate1) bool { return mainScenario(&m) }, }, diff --git a/lnwire/message.go b/lnwire/message.go index ef1a3628d2..1d93a2a26c 100644 --- a/lnwire/message.go +++ b/lnwire/message.go @@ -261,7 +261,7 @@ func makeEmptyMessage(msgType MessageType) (Message, error) { case MsgChannelAnnouncement: msg = &ChannelAnnouncement1{} case MsgChannelUpdate: - msg = &ChannelUpdate{} + msg = &ChannelUpdate1{} case MsgNodeAnnouncement: msg = &NodeAnnouncement{} case MsgPing: diff --git a/lnwire/message_test.go b/lnwire/message_test.go index 4c0ebdd45f..d42d579115 100644 --- a/lnwire/message_test.go +++ b/lnwire/message_test.go @@ -692,7 +692,7 @@ func newMsgNodeAnnouncement(t testing.TB, return msg } -func newMsgChannelUpdate(t testing.TB, r *rand.Rand) *lnwire.ChannelUpdate { +func newMsgChannelUpdate(t testing.TB, r *rand.Rand) *lnwire.ChannelUpdate1 { t.Helper() msgFlags := lnwire.ChanUpdateMsgFlags(r.Int31()) @@ -706,7 +706,7 @@ func newMsgChannelUpdate(t testing.TB, r *rand.Rand) *lnwire.ChannelUpdate { maxHtlc = 0 } - msg := &lnwire.ChannelUpdate{ + msg := &lnwire.ChannelUpdate1{ ShortChannelID: lnwire.NewShortChanIDFromInt(r.Uint64()), Timestamp: uint32(r.Int31()), MessageFlags: msgFlags, diff --git a/lnwire/onion_error.go b/lnwire/onion_error.go index f8a8bf0698..8d54a98951 100644 --- a/lnwire/onion_error.go +++ b/lnwire/onion_error.go @@ -601,7 +601,7 @@ func (f *FailInvalidOnionKey) Error() string { // unable to pull out a fully valid version, then we'll fall back to the // regular parsing mechanism which includes the length prefix an NO type byte. func parseChannelUpdateCompatibilityMode(reader io.Reader, length uint16, - chanUpdate *ChannelUpdate, pver uint32) error { + chanUpdate *ChannelUpdate1, pver uint32) error { // Instantiate a LimitReader because there may be additional data // present after the channel update. Without limiting the stream, the @@ -648,11 +648,13 @@ type FailTemporaryChannelFailure struct { // which caused the failure. // // NOTE: This field is optional. - Update *ChannelUpdate + Update *ChannelUpdate1 } // NewTemporaryChannelFailure creates new instance of the FailTemporaryChannelFailure. -func NewTemporaryChannelFailure(update *ChannelUpdate) *FailTemporaryChannelFailure { +func NewTemporaryChannelFailure( + update *ChannelUpdate1) *FailTemporaryChannelFailure { + return &FailTemporaryChannelFailure{Update: update} } @@ -686,7 +688,7 @@ func (f *FailTemporaryChannelFailure) Decode(r io.Reader, pver uint32) error { } if length != 0 { - f.Update = &ChannelUpdate{} + f.Update = &ChannelUpdate1{} return parseChannelUpdateCompatibilityMode( r, length, f.Update, pver, @@ -721,12 +723,12 @@ type FailAmountBelowMinimum struct { // Update is used to update information about state of the channel // which caused the failure. - Update ChannelUpdate + Update ChannelUpdate1 } // NewAmountBelowMinimum creates new instance of the FailAmountBelowMinimum. func NewAmountBelowMinimum(htlcMsat MilliSatoshi, - update ChannelUpdate) *FailAmountBelowMinimum { + update ChannelUpdate1) *FailAmountBelowMinimum { return &FailAmountBelowMinimum{ HtlcMsat: htlcMsat, @@ -762,7 +764,7 @@ func (f *FailAmountBelowMinimum) Decode(r io.Reader, pver uint32) error { return err } - f.Update = ChannelUpdate{} + f.Update = ChannelUpdate1{} return parseChannelUpdateCompatibilityMode( r, length, &f.Update, pver, @@ -791,12 +793,12 @@ type FailFeeInsufficient struct { // Update is used to update information about state of the channel // which caused the failure. - Update ChannelUpdate + Update ChannelUpdate1 } // NewFeeInsufficient creates new instance of the FailFeeInsufficient. func NewFeeInsufficient(htlcMsat MilliSatoshi, - update ChannelUpdate) *FailFeeInsufficient { + update ChannelUpdate1) *FailFeeInsufficient { return &FailFeeInsufficient{ HtlcMsat: htlcMsat, Update: update, @@ -831,7 +833,7 @@ func (f *FailFeeInsufficient) Decode(r io.Reader, pver uint32) error { return err } - f.Update = ChannelUpdate{} + f.Update = ChannelUpdate1{} return parseChannelUpdateCompatibilityMode( r, length, &f.Update, pver, @@ -862,12 +864,12 @@ type FailIncorrectCltvExpiry struct { // Update is used to update information about state of the channel // which caused the failure. - Update ChannelUpdate + Update ChannelUpdate1 } // NewIncorrectCltvExpiry creates new instance of the FailIncorrectCltvExpiry. func NewIncorrectCltvExpiry(cltvExpiry uint32, - update ChannelUpdate) *FailIncorrectCltvExpiry { + update ChannelUpdate1) *FailIncorrectCltvExpiry { return &FailIncorrectCltvExpiry{ CltvExpiry: cltvExpiry, @@ -900,7 +902,7 @@ func (f *FailIncorrectCltvExpiry) Decode(r io.Reader, pver uint32) error { return err } - f.Update = ChannelUpdate{} + f.Update = ChannelUpdate1{} return parseChannelUpdateCompatibilityMode( r, length, &f.Update, pver, @@ -925,11 +927,11 @@ func (f *FailIncorrectCltvExpiry) Encode(w *bytes.Buffer, pver uint32) error { type FailExpiryTooSoon struct { // Update is used to update information about state of the channel // which caused the failure. - Update ChannelUpdate + Update ChannelUpdate1 } // NewExpiryTooSoon creates new instance of the FailExpiryTooSoon. -func NewExpiryTooSoon(update ChannelUpdate) *FailExpiryTooSoon { +func NewExpiryTooSoon(update ChannelUpdate1) *FailExpiryTooSoon { return &FailExpiryTooSoon{ Update: update, } @@ -958,7 +960,7 @@ func (f *FailExpiryTooSoon) Decode(r io.Reader, pver uint32) error { return err } - f.Update = ChannelUpdate{} + f.Update = ChannelUpdate1{} return parseChannelUpdateCompatibilityMode( r, length, &f.Update, pver, @@ -984,11 +986,13 @@ type FailChannelDisabled struct { // Update is used to update information about state of the channel // which caused the failure. - Update ChannelUpdate + Update ChannelUpdate1 } // NewChannelDisabled creates new instance of the FailChannelDisabled. -func NewChannelDisabled(flags uint16, update ChannelUpdate) *FailChannelDisabled { +func NewChannelDisabled(flags uint16, + update ChannelUpdate1) *FailChannelDisabled { + return &FailChannelDisabled{ Flags: flags, Update: update, @@ -1023,7 +1027,7 @@ func (f *FailChannelDisabled) Decode(r io.Reader, pver uint32) error { return err } - f.Update = ChannelUpdate{} + f.Update = ChannelUpdate1{} return parseChannelUpdateCompatibilityMode( r, length, &f.Update, pver, @@ -1516,7 +1520,7 @@ func makeEmptyOnionError(code FailCode) (FailureMessage, error) { // writeOnionErrorChanUpdate writes out a ChannelUpdate using the onion error // format. The format is that we first write out the true serialized length of // the channel update, followed by the serialized channel update itself. -func writeOnionErrorChanUpdate(w *bytes.Buffer, chanUpdate *ChannelUpdate, +func writeOnionErrorChanUpdate(w *bytes.Buffer, chanUpdate *ChannelUpdate1, pver uint32) error { // First, we encode the channel update in a temporary buffer in order diff --git a/lnwire/onion_error_test.go b/lnwire/onion_error_test.go index bc14d5d420..9c39be6d5c 100644 --- a/lnwire/onion_error_test.go +++ b/lnwire/onion_error_test.go @@ -21,7 +21,7 @@ var ( testType = uint64(3) testOffset = uint16(24) sig, _ = NewSigFromSignature(testSig) - testChannelUpdate = ChannelUpdate{ + testChannelUpdate = ChannelUpdate1{ Signature: sig, ShortChannelID: NewShortChanIDFromInt(1), Timestamp: 1, @@ -138,7 +138,7 @@ func TestChannelUpdateCompatibilityParsing(t *testing.T) { // Now that we have the set of bytes encoded, we'll ensure that we're // able to decode it using our compatibility method, as it's a regular // encoded channel update message. - var newChanUpdate ChannelUpdate + var newChanUpdate ChannelUpdate1 err := parseChannelUpdateCompatibilityMode( &b, uint16(b.Len()), &newChanUpdate, 0, ) @@ -165,7 +165,7 @@ func TestChannelUpdateCompatibilityParsing(t *testing.T) { // We should be able to properly parse the encoded channel update // message even with the extra two bytes. - var newChanUpdate2 ChannelUpdate + var newChanUpdate2 ChannelUpdate1 err = parseChannelUpdateCompatibilityMode( &b, uint16(b.Len()), &newChanUpdate2, 0, ) diff --git a/netann/chan_status_manager.go b/netann/chan_status_manager.go index f1e6aa578a..c4db4009dc 100644 --- a/netann/chan_status_manager.go +++ b/netann/chan_status_manager.go @@ -60,7 +60,7 @@ type ChanStatusConfig struct { // ApplyChannelUpdate processes new ChannelUpdates signed by our node by // updating our local routing table and broadcasting the update to our // peers. - ApplyChannelUpdate func(*lnwire.ChannelUpdate, *wire.OutPoint, + ApplyChannelUpdate func(*lnwire.ChannelUpdate1, *wire.OutPoint, bool) error // DB stores the set of channels that are to be monitored. @@ -650,7 +650,7 @@ func (m *ChanStatusManager) signAndSendNextUpdate(outpoint wire.OutPoint, // in case our ChannelEdgePolicy is not found in the database. Also returns if // the channel is private by checking AuthProof for nil. func (m *ChanStatusManager) fetchLastChanUpdateByOutPoint(op wire.OutPoint) ( - *lnwire.ChannelUpdate, bool, error) { + *lnwire.ChannelUpdate1, bool, error) { // Get the edge info and policies for this channel from the graph. info, edge1, edge2, err := m.cfg.Graph.FetchChannelEdgesByOutpoint(&op) diff --git a/netann/chan_status_manager_test.go b/netann/chan_status_manager_test.go index 1e64a53f8c..c709a95f27 100644 --- a/netann/chan_status_manager_test.go +++ b/netann/chan_status_manager_test.go @@ -126,7 +126,7 @@ type mockGraph struct { chanPols2 map[wire.OutPoint]*models.ChannelEdgePolicy sidToCid map[lnwire.ShortChannelID]wire.OutPoint - updates chan *lnwire.ChannelUpdate + updates chan *lnwire.ChannelUpdate1 } func newMockGraph(t *testing.T, numChannels int, @@ -138,7 +138,7 @@ func newMockGraph(t *testing.T, numChannels int, chanPols1: make(map[wire.OutPoint]*models.ChannelEdgePolicy), chanPols2: make(map[wire.OutPoint]*models.ChannelEdgePolicy), sidToCid: make(map[lnwire.ShortChannelID]wire.OutPoint), - updates: make(chan *lnwire.ChannelUpdate, 2*numChannels), + updates: make(chan *lnwire.ChannelUpdate1, 2*numChannels), } for i := 0; i < numChannels; i++ { @@ -177,7 +177,7 @@ func (g *mockGraph) FetchChannelEdgesByOutpoint( return info, pol1, pol2, nil } -func (g *mockGraph) ApplyChannelUpdate(update *lnwire.ChannelUpdate, +func (g *mockGraph) ApplyChannelUpdate(update *lnwire.ChannelUpdate1, op *wire.OutPoint, private bool) error { g.mu.Lock() diff --git a/netann/channel_announcement.go b/netann/channel_announcement.go index 64e8a0c52a..4bc7669d2e 100644 --- a/netann/channel_announcement.go +++ b/netann/channel_announcement.go @@ -15,7 +15,7 @@ import ( func CreateChanAnnouncement(chanProof *models.ChannelAuthProof, chanInfo *models.ChannelEdgeInfo, e1, e2 *models.ChannelEdgePolicy) (*lnwire.ChannelAnnouncement1, - *lnwire.ChannelUpdate, *lnwire.ChannelUpdate, error) { + *lnwire.ChannelUpdate1, *lnwire.ChannelUpdate1, error) { // First, using the parameters of the channel, along with the channel // authentication chanProof, we'll create re-create the original @@ -68,7 +68,7 @@ func CreateChanAnnouncement(chanProof *models.ChannelAuthProof, // Since it's up to a node's policy as to whether they advertise the // edge in a direction, we don't create an advertisement if the edge is // nil. - var edge1Ann, edge2Ann *lnwire.ChannelUpdate + var edge1Ann, edge2Ann *lnwire.ChannelUpdate1 if e1 != nil { edge1Ann, err = ChannelUpdateFromEdge(chanInfo, e1) if err != nil { diff --git a/netann/channel_update.go b/netann/channel_update.go index b93deb1d0c..c8992ccc69 100644 --- a/netann/channel_update.go +++ b/netann/channel_update.go @@ -18,12 +18,12 @@ var ErrUnableToExtractChanUpdate = fmt.Errorf("unable to extract ChannelUpdate") // ChannelUpdateModifier is a closure that makes in-place modifications to an // lnwire.ChannelUpdate. -type ChannelUpdateModifier func(*lnwire.ChannelUpdate) +type ChannelUpdateModifier func(*lnwire.ChannelUpdate1) // ChanUpdSetDisable is a functional option that sets the disabled channel flag // if disabled is true, and clears the bit otherwise. func ChanUpdSetDisable(disabled bool) ChannelUpdateModifier { - return func(update *lnwire.ChannelUpdate) { + return func(update *lnwire.ChannelUpdate1) { if disabled { // Set the bit responsible for marking a channel as // disabled. @@ -39,7 +39,7 @@ func ChanUpdSetDisable(disabled bool) ChannelUpdateModifier { // ChanUpdSetTimestamp is a functional option that sets the timestamp of the // update to the current time, or increments it if the timestamp is already in // the future. -func ChanUpdSetTimestamp(update *lnwire.ChannelUpdate) { +func ChanUpdSetTimestamp(update *lnwire.ChannelUpdate1) { newTimestamp := uint32(time.Now().Unix()) if newTimestamp <= update.Timestamp { // Increment the prior value to ensure the timestamp @@ -57,7 +57,7 @@ func ChanUpdSetTimestamp(update *lnwire.ChannelUpdate) { // // NOTE: This method modifies the given update. func SignChannelUpdate(signer lnwallet.MessageSigner, keyLoc keychain.KeyLocator, - update *lnwire.ChannelUpdate, mods ...ChannelUpdateModifier) error { + update *lnwire.ChannelUpdate1, mods ...ChannelUpdateModifier) error { // Apply the requested changes to the channel update. for _, modifier := range mods { @@ -86,7 +86,7 @@ func SignChannelUpdate(signer lnwallet.MessageSigner, keyLoc keychain.KeyLocator func ExtractChannelUpdate(ownerPubKey []byte, info *models.ChannelEdgeInfo, policies ...*models.ChannelEdgePolicy) ( - *lnwire.ChannelUpdate, error) { + *lnwire.ChannelUpdate1, error) { // Helper function to extract the owner of the given policy. owner := func(edge *models.ChannelEdgePolicy) []byte { @@ -118,9 +118,9 @@ func ExtractChannelUpdate(ownerPubKey []byte, // UnsignedChannelUpdateFromEdge reconstructs an unsigned ChannelUpdate from the // given edge info and policy. func UnsignedChannelUpdateFromEdge(info *models.ChannelEdgeInfo, - policy *models.ChannelEdgePolicy) *lnwire.ChannelUpdate { + policy *models.ChannelEdgePolicy) *lnwire.ChannelUpdate1 { - return &lnwire.ChannelUpdate{ + return &lnwire.ChannelUpdate1{ ChainHash: info.ChainHash, ShortChannelID: lnwire.NewShortChanIDFromInt(policy.ChannelID), Timestamp: uint32(policy.LastUpdate.Unix()), @@ -138,7 +138,7 @@ func UnsignedChannelUpdateFromEdge(info *models.ChannelEdgeInfo, // ChannelUpdateFromEdge reconstructs a signed ChannelUpdate from the given edge // info and policy. func ChannelUpdateFromEdge(info *models.ChannelEdgeInfo, - policy *models.ChannelEdgePolicy) (*lnwire.ChannelUpdate, error) { + policy *models.ChannelEdgePolicy) (*lnwire.ChannelUpdate1, error) { update := UnsignedChannelUpdateFromEdge(info, policy) diff --git a/netann/channel_update_test.go b/netann/channel_update_test.go index 7af51effc0..2a9ed2e98e 100644 --- a/netann/channel_update_test.go +++ b/netann/channel_update_test.go @@ -111,7 +111,7 @@ func TestUpdateDisableFlag(t *testing.T) { // Create the initial update, the only fields we are // concerned with in this test are the timestamp and the // channel flags. - ogUpdate := &lnwire.ChannelUpdate{ + ogUpdate := &lnwire.ChannelUpdate1{ Timestamp: uint32(tc.startTime.Unix()), } if !tc.startEnabled { @@ -122,7 +122,7 @@ func TestUpdateDisableFlag(t *testing.T) { // the original. UpdateDisableFlag will mutate the // passed channel update, so we keep the old one to test // against. - newUpdate := &lnwire.ChannelUpdate{ + newUpdate := &lnwire.ChannelUpdate1{ Timestamp: ogUpdate.Timestamp, ChannelFlags: ogUpdate.ChannelFlags, } diff --git a/netann/sign.go b/netann/sign.go index f66df00a5e..0c7612eac4 100644 --- a/netann/sign.go +++ b/netann/sign.go @@ -22,7 +22,7 @@ func SignAnnouncement(signer lnwallet.MessageSigner, keyLoc keychain.KeyLocator, switch m := msg.(type) { case *lnwire.ChannelAnnouncement1: data, err = m.DataToSign() - case *lnwire.ChannelUpdate: + case *lnwire.ChannelUpdate1: data, err = m.DataToSign() case *lnwire.NodeAnnouncement: data, err = m.DataToSign() diff --git a/peer/brontide.go b/peer/brontide.go index 35340d8c87..5669193a24 100644 --- a/peer/brontide.go +++ b/peer/brontide.go @@ -301,7 +301,7 @@ type Config struct { // FetchLastChanUpdate fetches our latest channel update for a target // channel. - FetchLastChanUpdate func(lnwire.ShortChannelID) (*lnwire.ChannelUpdate, + FetchLastChanUpdate func(lnwire.ShortChannelID) (*lnwire.ChannelUpdate1, error) // FundingManager is an implementation of the funding.Controller interface. @@ -1963,7 +1963,7 @@ out: nextMsg.MsgType()) } - case *lnwire.ChannelUpdate, + case *lnwire.ChannelUpdate1, *lnwire.ChannelAnnouncement1, *lnwire.NodeAnnouncement, *lnwire.AnnounceSignatures1, @@ -2233,7 +2233,7 @@ func messageSummary(msg lnwire.Message) string { return fmt.Sprintf("chain_hash=%v, short_chan_id=%v", msg.ChainHash, msg.ShortChannelID.ToUint64()) - case *lnwire.ChannelUpdate: + case *lnwire.ChannelUpdate1: return fmt.Sprintf("chain_hash=%v, short_chan_id=%v, "+ "mflags=%v, cflags=%v, update_time=%v", msg.ChainHash, msg.ShortChannelID.ToUint64(), msg.MessageFlags, diff --git a/peer/test_utils.go b/peer/test_utils.go index e0ae29be8b..0575acca55 100644 --- a/peer/test_utils.go +++ b/peer/test_utils.go @@ -611,7 +611,7 @@ func createTestPeer(t *testing.T) *peerTestCtx { IsChannelActive: func(lnwire.ChannelID) bool { return true }, - ApplyChannelUpdate: func(*lnwire.ChannelUpdate, + ApplyChannelUpdate: func(*lnwire.ChannelUpdate1, *wire.OutPoint, bool) error { return nil @@ -719,9 +719,9 @@ func createTestPeer(t *testing.T) *peerTestCtx { }, PongBuf: make([]byte, lnwire.MaxPongBytes), FetchLastChanUpdate: func(chanID lnwire.ShortChannelID, - ) (*lnwire.ChannelUpdate, error) { + ) (*lnwire.ChannelUpdate1, error) { - return &lnwire.ChannelUpdate{}, nil + return &lnwire.ChannelUpdate1{}, nil }, } diff --git a/routing/missioncontrol_test.go b/routing/missioncontrol_test.go index 27391d53e2..4a0f738715 100644 --- a/routing/missioncontrol_test.go +++ b/routing/missioncontrol_test.go @@ -197,7 +197,7 @@ func TestMissionControl(t *testing.T) { // A node level failure should bring probability of all known channels // back to zero. - ctx.reportFailure(0, lnwire.NewExpiryTooSoon(lnwire.ChannelUpdate{})) + ctx.reportFailure(0, lnwire.NewExpiryTooSoon(lnwire.ChannelUpdate1{})) ctx.expectP(1000, 0) // Check whether history snapshot looks sane. @@ -219,14 +219,14 @@ func TestMissionControlChannelUpdate(t *testing.T) { // Report a policy related failure. Because it is the first, we don't // expect a penalty. ctx.reportFailure( - 0, lnwire.NewFeeInsufficient(0, lnwire.ChannelUpdate{}), + 0, lnwire.NewFeeInsufficient(0, lnwire.ChannelUpdate1{}), ) ctx.expectP(100, testAprioriHopProbability) // Report another failure for the same channel. We expect it to be // pruned. ctx.reportFailure( - 0, lnwire.NewFeeInsufficient(0, lnwire.ChannelUpdate{}), + 0, lnwire.NewFeeInsufficient(0, lnwire.ChannelUpdate1{}), ) ctx.expectP(100, 0) } diff --git a/routing/mock_test.go b/routing/mock_test.go index 28464a378a..32f83420fc 100644 --- a/routing/mock_test.go +++ b/routing/mock_test.go @@ -186,7 +186,7 @@ func (m *mockPaymentSessionOld) RequestRoute(_, _ lnwire.MilliSatoshi, return r, nil } -func (m *mockPaymentSessionOld) UpdateAdditionalEdge(_ *lnwire.ChannelUpdate, +func (m *mockPaymentSessionOld) UpdateAdditionalEdge(_ *lnwire.ChannelUpdate1, _ *btcec.PublicKey, _ *models.CachedEdgePolicy) bool { return false @@ -710,7 +710,7 @@ func (m *mockPaymentSession) RequestRoute(maxAmt, feeLimit lnwire.MilliSatoshi, return args.Get(0).(*route.Route), args.Error(1) } -func (m *mockPaymentSession) UpdateAdditionalEdge(msg *lnwire.ChannelUpdate, +func (m *mockPaymentSession) UpdateAdditionalEdge(msg *lnwire.ChannelUpdate1, pubKey *btcec.PublicKey, policy *models.CachedEdgePolicy) bool { args := m.Called(msg, pubKey, policy) diff --git a/routing/payment_session.go b/routing/payment_session.go index 5e6a468fe1..e07de73b7a 100644 --- a/routing/payment_session.go +++ b/routing/payment_session.go @@ -147,8 +147,8 @@ type PaymentSession interface { // (private channels) and applies the update from the message. Returns // a boolean to indicate whether the update has been applied without // error. - UpdateAdditionalEdge(msg *lnwire.ChannelUpdate, pubKey *btcec.PublicKey, - policy *models.CachedEdgePolicy) bool + UpdateAdditionalEdge(msg *lnwire.ChannelUpdate1, + pubKey *btcec.PublicKey, policy *models.CachedEdgePolicy) bool // GetAdditionalEdgePolicy uses the public key and channel ID to query // the ephemeral channel edge policy for additional edges. Returns a nil @@ -436,7 +436,7 @@ func (p *paymentSession) RequestRoute(maxAmt, feeLimit lnwire.MilliSatoshi, // validates the message signature and checks it's up to date, then applies the // updates to the supplied policy. It returns a boolean to indicate whether // there's an error when applying the updates. -func (p *paymentSession) UpdateAdditionalEdge(msg *lnwire.ChannelUpdate, +func (p *paymentSession) UpdateAdditionalEdge(msg *lnwire.ChannelUpdate1, pubKey *btcec.PublicKey, policy *models.CachedEdgePolicy) bool { // Validate the message signature. diff --git a/routing/payment_session_test.go b/routing/payment_session_test.go index 34f8356820..d510f77f96 100644 --- a/routing/payment_session_test.go +++ b/routing/payment_session_test.go @@ -147,7 +147,7 @@ func TestUpdateAdditionalEdge(t *testing.T) { ) // Create the channel update message and sign. - msg := &lnwire.ChannelUpdate{ + msg := &lnwire.ChannelUpdate1{ ShortChannelID: lnwire.NewShortChanIDFromInt(testChannelID), Timestamp: uint32(time.Now().Unix()), BaseFee: newFeeBaseMSat, diff --git a/routing/result_interpretation_test.go b/routing/result_interpretation_test.go index bf7d6d3edd..68b527e5a9 100644 --- a/routing/result_interpretation_test.go +++ b/routing/result_interpretation_test.go @@ -164,7 +164,7 @@ var resultTestCases = []resultTestCase{ name: "fail expiry too soon", route: &routeFourHop, failureSrcIdx: 3, - failure: lnwire.NewExpiryTooSoon(lnwire.ChannelUpdate{}), + failure: lnwire.NewExpiryTooSoon(lnwire.ChannelUpdate1{}), expectedResult: &interpretedResult{ pairResults: map[DirectedNodePair]pairResult{ @@ -266,8 +266,9 @@ var resultTestCases = []resultTestCase{ name: "fail fee insufficient intermediate", route: &routeFourHop, failureSrcIdx: 2, - failure: lnwire.NewFeeInsufficient(0, lnwire.ChannelUpdate{}), - + failure: lnwire.NewFeeInsufficient( + 0, lnwire.ChannelUpdate1{}, + ), expectedResult: &interpretedResult{ pairResults: map[DirectedNodePair]pairResult{ getTestPair(0, 1): { diff --git a/routing/router.go b/routing/router.go index db3fbc8573..40b7ecb49c 100644 --- a/routing/router.go +++ b/routing/router.go @@ -288,7 +288,7 @@ type Config struct { // ApplyChannelUpdate can be called to apply a new channel update to the // graph that we received from a payment failure. - ApplyChannelUpdate func(msg *lnwire.ChannelUpdate) bool + ApplyChannelUpdate func(msg *lnwire.ChannelUpdate1) bool // ClosedSCIDs is used by the router to fetch closed channels. // @@ -1329,9 +1329,9 @@ func (r *ChannelRouter) sendPayment(ctx context.Context, // extractChannelUpdate examines the error and extracts the channel update. func (r *ChannelRouter) extractChannelUpdate( - failure lnwire.FailureMessage) *lnwire.ChannelUpdate { + failure lnwire.FailureMessage) *lnwire.ChannelUpdate1 { - var update *lnwire.ChannelUpdate + var update *lnwire.ChannelUpdate1 switch onionErr := failure.(type) { case *lnwire.FailExpiryTooSoon: update = &onionErr.Update diff --git a/routing/router_test.go b/routing/router_test.go index 7726091fcd..62b015abb9 100644 --- a/routing/router_test.go +++ b/routing/router_test.go @@ -223,7 +223,7 @@ func createTestCtxFromFile(t *testing.T, // Add valid signature to channel update simulated as error received from the // network. func signErrChanUpdate(t *testing.T, key *btcec.PrivateKey, - errChanUpdate *lnwire.ChannelUpdate) { + errChanUpdate *lnwire.ChannelUpdate1) { chanUpdateMsg, err := errChanUpdate.DataToSign() require.NoError(t, err, "failed to retrieve data to sign") @@ -488,7 +488,7 @@ func TestChannelUpdateValidation(t *testing.T) { // Set up a channel update message with an invalid signature to be // returned to the sender. var invalidSignature lnwire.Sig - errChanUpdate := lnwire.ChannelUpdate{ + errChanUpdate := lnwire.ChannelUpdate1{ Signature: invalidSignature, FeeRate: 500, ShortChannelID: lnwire.NewShortChanIDFromInt(1), @@ -593,7 +593,7 @@ func TestSendPaymentErrorRepeatedFeeInsufficient(t *testing.T) { ) require.NoError(t, err, "unable to fetch chan id") - errChanUpdate := lnwire.ChannelUpdate{ + errChanUpdate := lnwire.ChannelUpdate1{ ShortChannelID: lnwire.NewShortChanIDFromInt( songokuSophonChanID, ), @@ -712,7 +712,7 @@ func TestSendPaymentErrorFeeInsufficientPrivateEdge(t *testing.T) { // Prepare an error update for the private channel, with twice the // original fee. updatedFeeBaseMSat := feeBaseMSat * 2 - errChanUpdate := lnwire.ChannelUpdate{ + errChanUpdate := lnwire.ChannelUpdate1{ ShortChannelID: lnwire.NewShortChanIDFromInt(privateChannelID), Timestamp: uint32(testTime.Add(time.Minute).Unix()), BaseFee: updatedFeeBaseMSat, @@ -838,7 +838,7 @@ func TestSendPaymentPrivateEdgeUpdateFeeExceedsLimit(t *testing.T) { // Prepare an error update for the private channel. The updated fee // will exceeds the feeLimit. updatedFeeBaseMSat := feeBaseMSat + uint32(feeLimit) - errChanUpdate := lnwire.ChannelUpdate{ + errChanUpdate := lnwire.ChannelUpdate1{ ShortChannelID: lnwire.NewShortChanIDFromInt(privateChannelID), Timestamp: uint32(testTime.Add(time.Minute).Unix()), BaseFee: updatedFeeBaseMSat, @@ -939,7 +939,7 @@ func TestSendPaymentErrorNonFinalTimeLockErrors(t *testing.T) { _, _, edgeUpdateToFail, err := ctx.graph.FetchChannelEdgesByID(chanID) require.NoError(t, err, "unable to fetch chan id") - errChanUpdate := lnwire.ChannelUpdate{ + errChanUpdate := lnwire.ChannelUpdate1{ ShortChannelID: lnwire.NewShortChanIDFromInt(chanID), Timestamp: uint32(edgeUpdateToFail.LastUpdate.Unix()), MessageFlags: edgeUpdateToFail.MessageFlags, @@ -1402,7 +1402,7 @@ func TestSendToRouteStructuredError(t *testing.T) { testCases := map[int]lnwire.FailureMessage{ finalHopIndex: lnwire.NewFailIncorrectDetails(payAmt, 100), 1: &lnwire.FailFeeInsufficient{ - Update: lnwire.ChannelUpdate{}, + Update: lnwire.ChannelUpdate1{}, }, } @@ -2967,7 +2967,7 @@ func (m *mockGraphBuilder) setNextReject(reject bool) { m.rejectUpdate = reject } -func (m *mockGraphBuilder) ApplyChannelUpdate(msg *lnwire.ChannelUpdate) bool { +func (m *mockGraphBuilder) ApplyChannelUpdate(msg *lnwire.ChannelUpdate1) bool { if m.rejectUpdate { return false } diff --git a/server.go b/server.go index 0f50794e74..a188f62696 100644 --- a/server.go +++ b/server.go @@ -1753,7 +1753,7 @@ func (s *server) UpdateRoutingConfig(cfg *routing.MissionControlConfig) { // signAliasUpdate takes a ChannelUpdate and returns the signature. This is // used for option_scid_alias channels where the ChannelUpdate to be sent back // may differ from what is on disk. -func (s *server) signAliasUpdate(u *lnwire.ChannelUpdate) (*ecdsa.Signature, +func (s *server) signAliasUpdate(u *lnwire.ChannelUpdate1) (*ecdsa.Signature, error) { data, err := u.DataToSign() @@ -4826,10 +4826,10 @@ func (s *server) fetchNodeAdvertisedAddrs(pub *btcec.PublicKey) ([]net.Addr, err // fetchLastChanUpdate returns a function which is able to retrieve our latest // channel update for a target channel. func (s *server) fetchLastChanUpdate() func(lnwire.ShortChannelID) ( - *lnwire.ChannelUpdate, error) { + *lnwire.ChannelUpdate1, error) { ourPubKey := s.identityECDH.PubKey().SerializeCompressed() - return func(cid lnwire.ShortChannelID) (*lnwire.ChannelUpdate, error) { + return func(cid lnwire.ShortChannelID) (*lnwire.ChannelUpdate1, error) { info, edge1, edge2, err := s.graphBuilder.GetChannelByID(cid) if err != nil { return nil, err @@ -4844,7 +4844,7 @@ func (s *server) fetchLastChanUpdate() func(lnwire.ShortChannelID) ( // applyChannelUpdate applies the channel update to the different sub-systems of // the server. The useAlias boolean denotes whether or not to send an alias in // place of the real SCID. -func (s *server) applyChannelUpdate(update *lnwire.ChannelUpdate, +func (s *server) applyChannelUpdate(update *lnwire.ChannelUpdate1, op *wire.OutPoint, useAlias bool) error { var ( From 60b0e46c3697fd6b33e3edfd359ed70a1af04a52 Mon Sep 17 00:00:00 2001 From: Elle Mouton Date: Wed, 21 Aug 2024 08:46:23 +0200 Subject: [PATCH 04/22] lnwire: add btc and node announcement nonces to channel_ready In preparation for Gossip 1.75, we add new TLV's to the `ChannelReady` message. Namely: `AnnouncementBitcoinNonce` and `AnnouncementNodeNonce`. These will be used to exchange nones required for producing the partial signature to be send in the `AnnouncementSignatures2` message. The type numbers for these new fields are even because if they are set, then a peer is expecting its peer to understand gossip 1.75 and the new fields. --- lnwire/channel_ready.go | 35 +++++++++++++++++++++++++++++++++-- lnwire/lnwire_test.go | 31 ++++++++++++++++++++++--------- 2 files changed, 55 insertions(+), 11 deletions(-) diff --git a/lnwire/channel_ready.go b/lnwire/channel_ready.go index bdcb95ce8d..912a068bde 100644 --- a/lnwire/channel_ready.go +++ b/lnwire/channel_ready.go @@ -33,6 +33,16 @@ type ChannelReady struct { // to accept a new commitment state transition. NextLocalNonce OptMusig2NonceTLV + // AnnouncementNodeNonce is an optional field that stores a public + // nonce that will be used along with the node's ID key during signing + // of the ChannelAnnouncement2 message. + AnnouncementNodeNonce tlv.OptionalRecordT[tlv.TlvType0, Musig2Nonce] + + // AnnouncementBitcoinNonce is an optional field that stores a public + // nonce that will be used along with the node's bitcoin key during + // signing of the ChannelAnnouncement2 message. + AnnouncementBitcoinNonce tlv.OptionalRecordT[tlv.TlvType2, Musig2Nonce] + // ExtraData is the set of data that was appended to this message to // fill out the full maximum transport message size. These fields can // be used to specify optional data such as custom TLV fields. @@ -78,9 +88,11 @@ func (c *ChannelReady) Decode(r io.Reader, _ uint32) error { var ( aliasScid ShortChannelID localNonce = c.NextLocalNonce.Zero() + nodeNonce = tlv.ZeroRecordT[tlv.TlvType0, Musig2Nonce]() + btcNonce = tlv.ZeroRecordT[tlv.TlvType2, Musig2Nonce]() ) typeMap, err := tlvRecords.ExtractRecords( - &aliasScid, &localNonce, + &btcNonce, &aliasScid, &nodeNonce, &localNonce, ) if err != nil { return err @@ -94,6 +106,14 @@ func (c *ChannelReady) Decode(r io.Reader, _ uint32) error { if val, ok := typeMap[c.NextLocalNonce.TlvType()]; ok && val == nil { c.NextLocalNonce = tlv.SomeRecordT(localNonce) } + val, ok := typeMap[c.AnnouncementBitcoinNonce.TlvType()] + if ok && val == nil { + c.AnnouncementBitcoinNonce = tlv.SomeRecordT(btcNonce) + } + val, ok = typeMap[c.AnnouncementNodeNonce.TlvType()] + if ok && val == nil { + c.AnnouncementNodeNonce = tlv.SomeRecordT(nodeNonce) + } if len(tlvRecords) != 0 { c.ExtraData = tlvRecords @@ -117,13 +137,24 @@ func (c *ChannelReady) Encode(w *bytes.Buffer, _ uint32) error { } // We'll only encode the AliasScid in a TLV segment if it exists. - recordProducers := make([]tlv.RecordProducer, 0, 2) + recordProducers := make([]tlv.RecordProducer, 0, 4) if c.AliasScid != nil { recordProducers = append(recordProducers, c.AliasScid) } c.NextLocalNonce.WhenSome(func(localNonce Musig2NonceTLV) { recordProducers = append(recordProducers, &localNonce) }) + c.AnnouncementBitcoinNonce.WhenSome( + func(nonce tlv.RecordT[tlv.TlvType2, Musig2Nonce]) { + recordProducers = append(recordProducers, &nonce) + }, + ) + c.AnnouncementNodeNonce.WhenSome( + func(nonce tlv.RecordT[tlv.TlvType0, Musig2Nonce]) { + recordProducers = append(recordProducers, &nonce) + }, + ) + err := EncodeMessageExtraData(&c.ExtraData, recordProducers...) if err != nil { return err diff --git a/lnwire/lnwire_test.go b/lnwire/lnwire_test.go index 22f26bd5b1..d76c57a4b0 100644 --- a/lnwire/lnwire_test.go +++ b/lnwire/lnwire_test.go @@ -730,18 +730,13 @@ func TestLightningWireProtocol(t *testing.T) { }, MsgChannelReady: func(v []reflect.Value, r *rand.Rand) { var c [32]byte - if _, err := r.Read(c[:]); err != nil { - t.Fatalf("unable to generate chan id: %v", err) - return - } + _, err := r.Read(c[:]) + require.NoError(t, err) pubKey, err := randPubKey() - if err != nil { - t.Fatalf("unable to generate key: %v", err) - return - } + require.NoError(t, err) - req := NewChannelReady(ChannelID(c), pubKey) + req := NewChannelReady(c, pubKey) if r.Int31()%2 == 0 { scid := NewShortChanIDFromInt(uint64(r.Int63())) @@ -751,6 +746,24 @@ func TestLightningWireProtocol(t *testing.T) { req.NextLocalNonce = someLocalNonce[NonceRecordTypeT](r) } + if r.Int31()%2 == 0 { + nodeNonce := tlv.ZeroRecordT[ + tlv.TlvType0, Musig2Nonce, + ]() + nodeNonce.Val = randLocalNonce(r) + req.AnnouncementNodeNonce = tlv.SomeRecordT( + nodeNonce, + ) + + btcNonce := tlv.ZeroRecordT[ + tlv.TlvType2, Musig2Nonce, + ]() + btcNonce.Val = randLocalNonce(r) + req.AnnouncementBitcoinNonce = tlv.SomeRecordT( + btcNonce, + ) + } + v[0] = reflect.ValueOf(*req) }, MsgShutdown: func(v []reflect.Value, r *rand.Rand) { From df65b7cad95ac341082753532503c0d188d3cd41 Mon Sep 17 00:00:00 2001 From: Elle Mouton Date: Mon, 8 Jan 2024 16:57:06 +0200 Subject: [PATCH 05/22] lnwire: add FirstBlock and BlockRange to GossipTimestampRange Add new FirstBlockHeight and BlockRange TLV fields to the GossipTimestampRange message. This will be used to query for Gossip 1.75 messages which are timestamped using block height instead of Unix timestamps. --- lnwire/gossip_timestamp_range.go | 72 +++++++++++++++++++++++++++++--- lnwire/lnwire_test.go | 29 +++++++++++++ 2 files changed, 96 insertions(+), 5 deletions(-) diff --git a/lnwire/gossip_timestamp_range.go b/lnwire/gossip_timestamp_range.go index d2dbddecbc..7b628752a0 100644 --- a/lnwire/gossip_timestamp_range.go +++ b/lnwire/gossip_timestamp_range.go @@ -5,6 +5,7 @@ import ( "io" "github.com/btcsuite/btcd/chaincfg/chainhash" + "github.com/lightningnetwork/lnd/tlv" ) // GossipTimestampRange is a message that allows the sender to restrict the set @@ -17,15 +18,31 @@ type GossipTimestampRange struct { ChainHash chainhash.Hash // FirstTimestamp is the timestamp of the earliest announcement message - // that should be sent by the receiver. + // that should be sent by the receiver. This is only to be used for + // querying message of gossip 1.0 which are timestamped using Unix + // timestamps. FirstBlockHeight and BlockRange should be used to + // query for announcement messages timestamped using block heights. FirstTimestamp uint32 // TimestampRange is the horizon beyond the FirstTimestamp that any // announcement messages should be sent for. The receiving node MUST // NOT send any announcements that have a timestamp greater than - // FirstTimestamp + TimestampRange. + // FirstTimestamp + TimestampRange. This is used together with + // FirstTimestamp to query for gossip 1.0 messages timestamped with + // Unix timestamps. TimestampRange uint32 + // FirstBlockHeight is the height of earliest announcement message that + // should be sent by the receiver. This is used only for querying + // announcement messages that use block heights as a timestamp. + FirstBlockHeight tlv.OptionalRecordT[tlv.TlvType2, uint32] + + // BlockRange is the horizon beyond FirstBlockHeight that any + // announcement messages should be sent for. The receiving node MUST NOT + // send any announcements that have a timestamp greater than + // FirstBlockHeight + BlockRange. + BlockRange tlv.OptionalRecordT[tlv.TlvType4, uint32] + // ExtraData is the set of data that was appended to this message to // fill out the full maximum transport message size. These fields can // be used to specify optional data such as custom TLV fields. @@ -45,13 +62,42 @@ var _ Message = (*GossipTimestampRange)(nil) // passed io.Reader observing the specified protocol version. // // This is part of the lnwire.Message interface. -func (g *GossipTimestampRange) Decode(r io.Reader, pver uint32) error { - return ReadElements(r, +func (g *GossipTimestampRange) Decode(r io.Reader, _ uint32) error { + err := ReadElements(r, g.ChainHash[:], &g.FirstTimestamp, &g.TimestampRange, - &g.ExtraData, ) + if err != nil { + return err + } + + var tlvRecords ExtraOpaqueData + if err := ReadElements(r, &tlvRecords); err != nil { + return err + } + + var ( + firstBlock = tlv.ZeroRecordT[tlv.TlvType2, uint32]() + blockRange = tlv.ZeroRecordT[tlv.TlvType4, uint32]() + ) + typeMap, err := tlvRecords.ExtractRecords(&firstBlock, &blockRange) + if err != nil { + return err + } + + if val, ok := typeMap[g.FirstBlockHeight.TlvType()]; ok && val == nil { + g.FirstBlockHeight = tlv.SomeRecordT(firstBlock) + } + if val, ok := typeMap[g.BlockRange.TlvType()]; ok && val == nil { + g.BlockRange = tlv.SomeRecordT(blockRange) + } + + if len(tlvRecords) != 0 { + g.ExtraData = tlvRecords + } + + return nil } // Encode serializes the target GossipTimestampRange into the passed io.Writer @@ -71,6 +117,22 @@ func (g *GossipTimestampRange) Encode(w *bytes.Buffer, pver uint32) error { return err } + recordProducers := make([]tlv.RecordProducer, 0, 2) + g.FirstBlockHeight.WhenSome( + func(height tlv.RecordT[tlv.TlvType2, uint32]) { + recordProducers = append(recordProducers, &height) + }, + ) + g.BlockRange.WhenSome( + func(blockRange tlv.RecordT[tlv.TlvType4, uint32]) { + recordProducers = append(recordProducers, &blockRange) + }, + ) + err := EncodeMessageExtraData(&g.ExtraData, recordProducers...) + if err != nil { + return err + } + return WriteBytes(w, g.ExtraData) } diff --git a/lnwire/lnwire_test.go b/lnwire/lnwire_test.go index d76c57a4b0..90d338d881 100644 --- a/lnwire/lnwire_test.go +++ b/lnwire/lnwire_test.go @@ -1215,6 +1215,35 @@ func TestLightningWireProtocol(t *testing.T) { v[0] = reflect.ValueOf(req) }, + MsgGossipTimestampRange: func(v []reflect.Value, r *rand.Rand) { + req := GossipTimestampRange{ + FirstTimestamp: rand.Uint32(), + TimestampRange: rand.Uint32(), + ExtraData: make([]byte, 0), + } + + _, err := rand.Read(req.ChainHash[:]) + require.NoError(t, err) + + // Sometimes add a block range. + if r.Int31()%2 == 0 { + firstBlock := tlv.ZeroRecordT[ + tlv.TlvType2, uint32, + ]() + firstBlock.Val = rand.Uint32() + req.FirstBlockHeight = tlv.SomeRecordT( + firstBlock, + ) + + blockRange := tlv.ZeroRecordT[ + tlv.TlvType4, uint32, + ]() + blockRange.Val = rand.Uint32() + req.BlockRange = tlv.SomeRecordT(blockRange) + } + + v[0] = reflect.ValueOf(req) + }, MsgQueryShortChanIDs: func(v []reflect.Value, r *rand.Rand) { req := QueryShortChanIDs{ ExtraData: make([]byte, 0), From f230e2c5745ed8205e7f3ae5b7f2a2ac84b7ac93 Mon Sep 17 00:00:00 2001 From: Elle Mouton Date: Thu, 26 Oct 2023 12:17:12 +0200 Subject: [PATCH 06/22] lnwire: add AnnounceSignatures interface Add a AnnounceSignatures interface and ensure that AnnounceSignatures1 implements it. --- lnwire/announcement_signatures.go | 22 ++++++++++++++++++++-- lnwire/interfaces.go | 13 +++++++++++++ 2 files changed, 33 insertions(+), 2 deletions(-) create mode 100644 lnwire/interfaces.go diff --git a/lnwire/announcement_signatures.go b/lnwire/announcement_signatures.go index 3f0e72803d..a2bc21f39d 100644 --- a/lnwire/announcement_signatures.go +++ b/lnwire/announcement_signatures.go @@ -47,11 +47,15 @@ type AnnounceSignatures1 struct { // lnwire.Message interface. var _ Message = (*AnnounceSignatures1)(nil) +// A compile time check to ensure AnnounceSignatures1 implements the +// lnwire.AnnounceSignatures interface. +var _ AnnounceSignatures = (*AnnounceSignatures1)(nil) + // Decode deserializes a serialized AnnounceSignatures1 stored in the passed // io.Reader observing the specified protocol version. // // This is part of the lnwire.Message interface. -func (a *AnnounceSignatures1) Decode(r io.Reader, pver uint32) error { +func (a *AnnounceSignatures1) Decode(r io.Reader, _ uint32) error { return ReadElements(r, &a.ChannelID, &a.ShortChannelID, @@ -65,7 +69,7 @@ func (a *AnnounceSignatures1) Decode(r io.Reader, pver uint32) error { // observing the protocol version specified. // // This is part of the lnwire.Message interface. -func (a *AnnounceSignatures1) Encode(w *bytes.Buffer, pver uint32) error { +func (a *AnnounceSignatures1) Encode(w *bytes.Buffer, _ uint32) error { if err := WriteChannelID(w, a.ChannelID); err != nil { return err } @@ -92,3 +96,17 @@ func (a *AnnounceSignatures1) Encode(w *bytes.Buffer, pver uint32) error { func (a *AnnounceSignatures1) MsgType() MessageType { return MsgAnnounceSignatures } + +// SCID returns the ShortChannelID of the channel. +// +// This is part of the lnwire.AnnounceSignatures interface. +func (a *AnnounceSignatures1) SCID() ShortChannelID { + return a.ShortChannelID +} + +// ChanID returns the ChannelID identifying the channel. +// +// This is part of the lnwire.AnnounceSignatures interface. +func (a *AnnounceSignatures1) ChanID() ChannelID { + return a.ChannelID +} diff --git a/lnwire/interfaces.go b/lnwire/interfaces.go new file mode 100644 index 0000000000..f8e4373c38 --- /dev/null +++ b/lnwire/interfaces.go @@ -0,0 +1,13 @@ +package lnwire + +// AnnounceSignatures is an interface that represents a message used to +// exchange signatures of a ChannelAnnouncment message during the funding flow. +type AnnounceSignatures interface { + // SCID returns the ShortChannelID of the channel. + SCID() ShortChannelID + + // ChanID returns the ChannelID identifying the channel. + ChanID() ChannelID + + Message +} From 9be84c1bdc59c62a1f7f78be5d6ec4dfba241085 Mon Sep 17 00:00:00 2001 From: Elle Mouton Date: Mon, 2 Sep 2024 12:05:34 +0200 Subject: [PATCH 07/22] graph+lnwallet: move FetchTx logic to lnwallet So that it can be re-used elsewhere. --- graph/builder.go | 67 ++----------------------------------------- lnwallet/interface.go | 66 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 69 insertions(+), 64 deletions(-) diff --git a/graph/builder.go b/graph/builder.go index 0997250688..d629523f47 100644 --- a/graph/builder.go +++ b/graph/builder.go @@ -1231,7 +1231,9 @@ func (b *Builder) processUpdate(msg interface{}, // to obtain the full funding outpoint that's encoded within // the channel ID. channelID := lnwire.NewShortChanIDFromInt(msg.ChannelID) - fundingTx, err := b.fetchFundingTxWrapper(&channelID) + fundingTx, err := lnwallet.FetchFundingTxWrapper( + b.cfg.Chain, &channelID, b.quit, + ) if err != nil { //nolint:lll // @@ -1448,69 +1450,6 @@ func (b *Builder) processUpdate(msg interface{}, return nil } -// fetchFundingTxWrapper is a wrapper around fetchFundingTx, except that it -// will exit if the router has stopped. -func (b *Builder) fetchFundingTxWrapper(chanID *lnwire.ShortChannelID) ( - *wire.MsgTx, error) { - - txChan := make(chan *wire.MsgTx, 1) - errChan := make(chan error, 1) - - go func() { - tx, err := b.fetchFundingTx(chanID) - if err != nil { - errChan <- err - return - } - - txChan <- tx - }() - - select { - case tx := <-txChan: - return tx, nil - - case err := <-errChan: - return nil, err - - case <-b.quit: - return nil, ErrGraphBuilderShuttingDown - } -} - -// fetchFundingTx returns the funding transaction identified by the passed -// short channel ID. -// -// TODO(roasbeef): replace with call to GetBlockTransaction? (would allow to -// later use getblocktxn). -func (b *Builder) fetchFundingTx( - chanID *lnwire.ShortChannelID) (*wire.MsgTx, error) { - - // First fetch the block hash by the block number encoded, then use - // that hash to fetch the block itself. - blockNum := int64(chanID.BlockHeight) - blockHash, err := b.cfg.Chain.GetBlockHash(blockNum) - if err != nil { - return nil, err - } - fundingBlock, err := b.cfg.Chain.GetBlock(blockHash) - if err != nil { - return nil, err - } - - // As a sanity check, ensure that the advertised transaction index is - // within the bounds of the total number of transactions within a - // block. - numTxns := uint32(len(fundingBlock.Transactions)) - if chanID.TxIndex > numTxns-1 { - return nil, fmt.Errorf("tx_index=#%v "+ - "is out of range (max_index=%v), network_chan_id=%v", - chanID.TxIndex, numTxns-1, chanID) - } - - return fundingBlock.Transactions[chanID.TxIndex].Copy(), nil -} - // routingMsg couples a routing related routing topology update to the // error channel. type routingMsg struct { diff --git a/lnwallet/interface.go b/lnwallet/interface.go index d3b123509b..3c59313f78 100644 --- a/lnwallet/interface.go +++ b/lnwallet/interface.go @@ -21,6 +21,7 @@ import ( "github.com/lightningnetwork/lnd/fn" "github.com/lightningnetwork/lnd/keychain" "github.com/lightningnetwork/lnd/lnwallet/chainfee" + "github.com/lightningnetwork/lnd/lnwire" ) const ( @@ -671,3 +672,68 @@ func SupportedWallets() []string { return supportedWallets } + +// FetchFundingTxWrapper is a wrapper around FetchFundingTx, except that it will +// exit when the supplied quit channel is closed. +func FetchFundingTxWrapper(chain BlockChainIO, chanID *lnwire.ShortChannelID, + quit chan struct{}) (*wire.MsgTx, error) { + + txChan := make(chan *wire.MsgTx, 1) + errChan := make(chan error, 1) + + go func() { + tx, err := FetchFundingTx(chain, chanID) + if err != nil { + errChan <- err + return + } + + txChan <- tx + }() + + select { + case tx := <-txChan: + return tx, nil + + case err := <-errChan: + return nil, err + + case <-quit: + return nil, fmt.Errorf("quit channel passed to " + + "lnwallet.FetchFundingTxWrapper has been closed") + } +} + +// FetchFundingTx uses the given BlockChainIO to fetch and return the funding +// transaction identified by the passed short channel ID. +// +// TODO(roasbeef): replace with call to GetBlockTransaction? (would allow to +// later use getblocktxn). +func FetchFundingTx(chain BlockChainIO, + chanID *lnwire.ShortChannelID) (*wire.MsgTx, error) { + + // First fetch the block hash by the block number encoded, then use + // that hash to fetch the block itself. + blockNum := int64(chanID.BlockHeight) + blockHash, err := chain.GetBlockHash(blockNum) + if err != nil { + return nil, err + } + + fundingBlock, err := chain.GetBlock(blockHash) + if err != nil { + return nil, err + } + + // As a sanity check, ensure that the advertised transaction index is + // within the bounds of the total number of transactions within a + // block. + numTxns := uint32(len(fundingBlock.Transactions)) + if chanID.TxIndex > numTxns-1 { + return nil, fmt.Errorf("tx_index=#%v "+ + "is out of range (max_index=%v), network_chan_id=%v", + chanID.TxIndex, numTxns-1, chanID) + } + + return fundingBlock.Transactions[chanID.TxIndex].Copy(), nil +} From a6bf76a0b7c9d3e6a3cff0251c24698f98b8d922 Mon Sep 17 00:00:00 2001 From: Elle Mouton Date: Mon, 2 Sep 2024 12:10:13 +0200 Subject: [PATCH 08/22] discovery+lnwallet: add fetchPKScript helper to gossiper This commit makes an lnwallet.BlockChainIO available to the gossiper and uses it to construct a helper that can be used to fetch the pk script for a given SCID. This will be used for channel announcement verification in an upcoming commit. --- discovery/gossiper.go | 11 +++++++++++ lnwallet/interface.go | 24 ++++++++++++++++++++++++ server.go | 1 + 3 files changed, 36 insertions(+) diff --git a/discovery/gossiper.go b/discovery/gossiper.go index a9ce801a06..a2a215d0d7 100644 --- a/discovery/gossiper.go +++ b/discovery/gossiper.go @@ -171,6 +171,10 @@ type Config struct { // order to be included in the LN graph. Graph graph.ChannelGraphSource + // ChainIO represents an abstraction over a source that can query the + // blockchain. + ChainIO lnwallet.BlockChainIO + // ChanSeries is an interfaces that provides access to a time series // view of the current known channel graph. Each GossipSyncer enabled // peer will utilize this in order to create and respond to channel @@ -1930,6 +1934,13 @@ func (d *AuthenticatedGossiper) processRejectedEdge( return announcements, nil } +// fetchPKScript fetches the output script for the given SCID. +func (d *AuthenticatedGossiper) fetchPKScript(chanID *lnwire.ShortChannelID) ( + []byte, error) { + + return lnwallet.FetchPKScriptWithQuit(d.cfg.ChainIO, chanID, d.quit) +} + // addNode processes the given node announcement, and adds it to our channel // graph. func (d *AuthenticatedGossiper) addNode(msg *lnwire.NodeAnnouncement, diff --git a/lnwallet/interface.go b/lnwallet/interface.go index 3c59313f78..2a44624625 100644 --- a/lnwallet/interface.go +++ b/lnwallet/interface.go @@ -21,6 +21,7 @@ import ( "github.com/lightningnetwork/lnd/fn" "github.com/lightningnetwork/lnd/keychain" "github.com/lightningnetwork/lnd/lnwallet/chainfee" + "github.com/lightningnetwork/lnd/lnwallet/chanvalidate" "github.com/lightningnetwork/lnd/lnwire" ) @@ -737,3 +738,26 @@ func FetchFundingTx(chain BlockChainIO, return fundingBlock.Transactions[chanID.TxIndex].Copy(), nil } + +// FetchPKScriptWithQuit fetches the output script for the given SCID and exits +// early with an error if the provided quit channel is closed before +// completion. +func FetchPKScriptWithQuit(chain BlockChainIO, chanID *lnwire.ShortChannelID, + quit chan struct{}) ([]byte, error) { + + tx, err := FetchFundingTxWrapper(chain, chanID, quit) + if err != nil { + return nil, err + } + + outputLocator := chanvalidate.ShortChanIDChanLocator{ + ID: *chanID, + } + + output, _, err := outputLocator.Locate(tx) + if err != nil { + return nil, err + } + + return output.PkScript, nil +} diff --git a/server.go b/server.go index a188f62696..6953754006 100644 --- a/server.go +++ b/server.go @@ -1042,6 +1042,7 @@ func newServer(cfg *Config, listenAddrs []net.Addr, s.authGossiper = discovery.New(discovery.Config{ Graph: s.graphBuilder, + ChainIO: s.cc.ChainIO, Notifier: s.cc.ChainNotifier, ChainHash: *s.cfg.ActiveNetParams.GenesisHash, Broadcast: s.BroadcastMessage, From 615db1fc2e698c564ece603bfcf6fb610a1d2be2 Mon Sep 17 00:00:00 2001 From: Elle Mouton Date: Wed, 11 Sep 2024 12:15:44 +0200 Subject: [PATCH 09/22] multi: move channel announcement validation to netann from the graph package. --- discovery/gossiper.go | 6 +-- graph/ann_validation.go | 73 --------------------------------- netann/channel_announcement.go | 75 ++++++++++++++++++++++++++++++++++ 3 files changed, 78 insertions(+), 76 deletions(-) diff --git a/discovery/gossiper.go b/discovery/gossiper.go index a2a215d0d7..300a880b04 100644 --- a/discovery/gossiper.go +++ b/discovery/gossiper.go @@ -1890,7 +1890,7 @@ func (d *AuthenticatedGossiper) processRejectedEdge( if err != nil { return nil, err } - err = graph.ValidateChannelAnn(chanAnn) + err = netann.ValidateChannelAnn(chanAnn) if err != nil { err := fmt.Errorf("assembled channel announcement proof "+ "for shortChanID=%v isn't valid: %v", @@ -2540,7 +2540,7 @@ func (d *AuthenticatedGossiper) handleChanAnnouncement(nMsg *networkMsg, // the signatures within the proof as it should be well formed. var proof *models.ChannelAuthProof if nMsg.isRemote { - if err := graph.ValidateChannelAnn(ann); err != nil { + if err := netann.ValidateChannelAnn(ann); err != nil { err := fmt.Errorf("unable to validate announcement: "+ "%v", err) @@ -3434,7 +3434,7 @@ func (d *AuthenticatedGossiper) handleAnnSig(nMsg *networkMsg, // With all the necessary components assembled validate the full // channel announcement proof. - if err := graph.ValidateChannelAnn(chanAnn); err != nil { + if err := netann.ValidateChannelAnn(chanAnn); err != nil { err := fmt.Errorf("channel announcement proof for "+ "short_chan_id=%v isn't valid: %v", shortChanID, err) diff --git a/graph/ann_validation.go b/graph/ann_validation.go index d586ff3ae8..4cbf3737b1 100644 --- a/graph/ann_validation.go +++ b/graph/ann_validation.go @@ -12,79 +12,6 @@ import ( "github.com/lightningnetwork/lnd/lnwire" ) -// ValidateChannelAnn validates the channel announcement message and checks -// that node signatures covers the announcement message, and that the bitcoin -// signatures covers the node keys. -func ValidateChannelAnn(a *lnwire.ChannelAnnouncement1) error { - // First, we'll compute the digest (h) which is to be signed by each of - // the keys included within the node announcement message. This hash - // digest includes all the keys, so the (up to 4 signatures) will - // attest to the validity of each of the keys. - data, err := a.DataToSign() - if err != nil { - return err - } - dataHash := chainhash.DoubleHashB(data) - - // First we'll verify that the passed bitcoin key signature is indeed a - // signature over the computed hash digest. - bitcoinSig1, err := a.BitcoinSig1.ToSignature() - if err != nil { - return err - } - bitcoinKey1, err := btcec.ParsePubKey(a.BitcoinKey1[:]) - if err != nil { - return err - } - if !bitcoinSig1.Verify(dataHash, bitcoinKey1) { - return errors.New("can't verify first bitcoin signature") - } - - // If that checks out, then we'll verify that the second bitcoin - // signature is a valid signature of the bitcoin public key over hash - // digest as well. - bitcoinSig2, err := a.BitcoinSig2.ToSignature() - if err != nil { - return err - } - bitcoinKey2, err := btcec.ParsePubKey(a.BitcoinKey2[:]) - if err != nil { - return err - } - if !bitcoinSig2.Verify(dataHash, bitcoinKey2) { - return errors.New("can't verify second bitcoin signature") - } - - // Both node signatures attached should indeed be a valid signature - // over the selected digest of the channel announcement signature. - nodeSig1, err := a.NodeSig1.ToSignature() - if err != nil { - return err - } - nodeKey1, err := btcec.ParsePubKey(a.NodeID1[:]) - if err != nil { - return err - } - if !nodeSig1.Verify(dataHash, nodeKey1) { - return errors.New("can't verify data in first node signature") - } - - nodeSig2, err := a.NodeSig2.ToSignature() - if err != nil { - return err - } - nodeKey2, err := btcec.ParsePubKey(a.NodeID2[:]) - if err != nil { - return err - } - if !nodeSig2.Verify(dataHash, nodeKey2) { - return errors.New("can't verify data in second node signature") - } - - return nil - -} - // ValidateNodeAnn validates the node announcement by ensuring that the // attached signature is needed a signature of the node announcement under the // specified node public key. diff --git a/netann/channel_announcement.go b/netann/channel_announcement.go index 4bc7669d2e..3731f0b248 100644 --- a/netann/channel_announcement.go +++ b/netann/channel_announcement.go @@ -2,7 +2,10 @@ package netann import ( "bytes" + "errors" + "github.com/btcsuite/btcd/btcec/v2" + "github.com/btcsuite/btcd/chaincfg/chainhash" "github.com/lightningnetwork/lnd/channeldb/models" "github.com/lightningnetwork/lnd/lnwire" ) @@ -84,3 +87,75 @@ func CreateChanAnnouncement(chanProof *models.ChannelAuthProof, return chanAnn, edge1Ann, edge2Ann, nil } + +// ValidateChannelAnn validates the channel announcement message and checks +// that node signatures covers the announcement message, and that the bitcoin +// signatures covers the node keys. +func ValidateChannelAnn(a *lnwire.ChannelAnnouncement1) error { + // First, we'll compute the digest (h) which is to be signed by each of + // the keys included within the node announcement message. This hash + // digest includes all the keys, so the (up to 4 signatures) will + // attest to the validity of each of the keys. + data, err := a.DataToSign() + if err != nil { + return err + } + dataHash := chainhash.DoubleHashB(data) + + // First we'll verify that the passed bitcoin key signature is indeed a + // signature over the computed hash digest. + bitcoinSig1, err := a.BitcoinSig1.ToSignature() + if err != nil { + return err + } + bitcoinKey1, err := btcec.ParsePubKey(a.BitcoinKey1[:]) + if err != nil { + return err + } + if !bitcoinSig1.Verify(dataHash, bitcoinKey1) { + return errors.New("can't verify first bitcoin signature") + } + + // If that checks out, then we'll verify that the second bitcoin + // signature is a valid signature of the bitcoin public key over hash + // digest as well. + bitcoinSig2, err := a.BitcoinSig2.ToSignature() + if err != nil { + return err + } + bitcoinKey2, err := btcec.ParsePubKey(a.BitcoinKey2[:]) + if err != nil { + return err + } + if !bitcoinSig2.Verify(dataHash, bitcoinKey2) { + return errors.New("can't verify second bitcoin signature") + } + + // Both node signatures attached should indeed be a valid signature + // over the selected digest of the channel announcement signature. + nodeSig1, err := a.NodeSig1.ToSignature() + if err != nil { + return err + } + nodeKey1, err := btcec.ParsePubKey(a.NodeID1[:]) + if err != nil { + return err + } + if !nodeSig1.Verify(dataHash, nodeKey1) { + return errors.New("can't verify data in first node signature") + } + + nodeSig2, err := a.NodeSig2.ToSignature() + if err != nil { + return err + } + nodeKey2, err := btcec.ParsePubKey(a.NodeID2[:]) + if err != nil { + return err + } + if !nodeSig2.Verify(dataHash, nodeKey2) { + return errors.New("can't verify data in second node signature") + } + + return nil +} From e07d23567cbc85ea67111085623281d251b39b19 Mon Sep 17 00:00:00 2001 From: Elle Mouton Date: Wed, 11 Sep 2024 12:21:54 +0200 Subject: [PATCH 10/22] lnwire: add a ChannelAnnouncement interface Add a new ChannelAnnouncement interface and ensure that ChannelAnnouncement1 implements it. --- lnwire/channel_announcement.go | 35 ++++++++++++++++++++++++++++++++++ lnwire/interfaces.go | 23 ++++++++++++++++++++++ 2 files changed, 58 insertions(+) diff --git a/lnwire/channel_announcement.go b/lnwire/channel_announcement.go index f9dee24b41..ed4c5b97ea 100644 --- a/lnwire/channel_announcement.go +++ b/lnwire/channel_announcement.go @@ -184,3 +184,38 @@ func (a *ChannelAnnouncement1) DataToSign() ([]byte, error) { return buf.Bytes(), nil } + +// Node1KeyBytes returns the bytes representing the public key of node 1 in the +// channel. +// +// NOTE: This is part of the ChannelAnnouncement interface. +func (a *ChannelAnnouncement1) Node1KeyBytes() [33]byte { + return a.NodeID1 +} + +// Node2KeyBytes returns the bytes representing the public key of node 2 in the +// channel. +// +// NOTE: This is part of the ChannelAnnouncement interface. +func (a *ChannelAnnouncement1) Node2KeyBytes() [33]byte { + return a.NodeID2 +} + +// GetChainHash returns the hash of the chain which this channel's funding +// transaction is confirmed in. +// +// NOTE: This is part of the ChannelAnnouncement interface. +func (a *ChannelAnnouncement1) GetChainHash() chainhash.Hash { + return a.ChainHash +} + +// SCID returns the short channel ID of the channel. +// +// NOTE: This is part of the ChannelAnnouncement interface. +func (a *ChannelAnnouncement1) SCID() ShortChannelID { + return a.ShortChannelID +} + +// A compile-time check to ensure that ChannelAnnouncement1 implements the +// ChannelAnnouncement interface. +var _ ChannelAnnouncement = (*ChannelAnnouncement1)(nil) diff --git a/lnwire/interfaces.go b/lnwire/interfaces.go index f8e4373c38..693c6ca719 100644 --- a/lnwire/interfaces.go +++ b/lnwire/interfaces.go @@ -1,5 +1,7 @@ package lnwire +import "github.com/btcsuite/btcd/chaincfg/chainhash" + // AnnounceSignatures is an interface that represents a message used to // exchange signatures of a ChannelAnnouncment message during the funding flow. type AnnounceSignatures interface { @@ -11,3 +13,24 @@ type AnnounceSignatures interface { Message } + +// ChannelAnnouncement is an interface that must be satisfied by any message +// used to announce and prove the existence of a channel. +type ChannelAnnouncement interface { + // SCID returns the short channel ID of the channel. + SCID() ShortChannelID + + // GetChainHash returns the hash of the chain which this channel's + // funding transaction is confirmed in. + GetChainHash() chainhash.Hash + + // Node1KeyBytes returns the bytes representing the public key of node + // 1 in the channel. + Node1KeyBytes() [33]byte + + // Node2KeyBytes returns the bytes representing the public key of node + // 2 in the channel. + Node2KeyBytes() [33]byte + + Message +} From 7bbf89625fd98fc912289c99bf9428d31e2fe3d1 Mon Sep 17 00:00:00 2001 From: Elle Mouton Date: Wed, 11 Sep 2024 12:27:14 +0200 Subject: [PATCH 11/22] multi: move ChannelUpdate validate methods to netann --- discovery/chan_series.go | 5 +-- discovery/gossiper.go | 6 +-- go.mod | 2 +- graph/ann_validation.go | 70 ---------------------------------- graph/builder.go | 3 +- netann/channel_update.go | 71 +++++++++++++++++++++++++++++++++++ netann/channel_update_test.go | 3 +- routing/payment_session.go | 4 +- 8 files changed, 82 insertions(+), 82 deletions(-) diff --git a/discovery/chan_series.go b/discovery/chan_series.go index ede116683f..b7b9af9890 100644 --- a/discovery/chan_series.go +++ b/discovery/chan_series.go @@ -5,7 +5,6 @@ import ( "github.com/btcsuite/btcd/chaincfg/chainhash" "github.com/lightningnetwork/lnd/channeldb" - "github.com/lightningnetwork/lnd/graph" "github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/netann" "github.com/lightningnetwork/lnd/routing/route" @@ -137,7 +136,7 @@ func (c *ChanSeries) UpdatesInHorizon(chain chainhash.Hash, if edge1 != nil { // We don't want to send channel updates that don't // conform to the spec (anymore). - err := graph.ValidateChannelUpdateFields(0, edge1) + err := netann.ValidateChannelUpdateFields(0, edge1) if err != nil { log.Errorf("not sending invalid channel "+ "update %v: %v", edge1, err) @@ -146,7 +145,7 @@ func (c *ChanSeries) UpdatesInHorizon(chain chainhash.Hash, } } if edge2 != nil { - err := graph.ValidateChannelUpdateFields(0, edge2) + err := netann.ValidateChannelUpdateFields(0, edge2) if err != nil { log.Errorf("not sending invalid channel "+ "update %v: %v", edge2, err) diff --git a/discovery/gossiper.go b/discovery/gossiper.go index 300a880b04..ee1e2916b5 100644 --- a/discovery/gossiper.go +++ b/discovery/gossiper.go @@ -2100,7 +2100,7 @@ func (d *AuthenticatedGossiper) processZombieUpdate( "with chan_id=%v", msg.ShortChannelID) } - err := graph.VerifyChannelUpdateSignature(msg, pubKey) + err := netann.VerifyChannelUpdateSignature(msg, pubKey) if err != nil { return fmt.Errorf("unable to verify channel "+ "update signature: %v", err) @@ -2237,7 +2237,7 @@ func (d *AuthenticatedGossiper) updateChannel(info *models.ChannelEdgeInfo, // To ensure that our signature is valid, we'll verify it ourself // before committing it to the slice returned. - err = graph.ValidateChannelUpdateAnn( + err = netann.ValidateChannelUpdateAnn( d.selfKey, info.Capacity, chanUpdate, ) if err != nil { @@ -3028,7 +3028,7 @@ func (d *AuthenticatedGossiper) handleChanUpdate(nMsg *networkMsg, // Validate the channel announcement with the expected public key and // channel capacity. In the case of an invalid channel update, we'll // return an error to the caller and exit early. - err = graph.ValidateChannelUpdateAnn(pubKey, chanInfo.Capacity, upd) + err = netann.ValidateChannelUpdateAnn(pubKey, chanInfo.Capacity, upd) if err != nil { rErr := fmt.Errorf("unable to validate channel update "+ "announcement for short_chan_id=%v: %v", diff --git a/go.mod b/go.mod index 65a4f991fc..4aeea38086 100644 --- a/go.mod +++ b/go.mod @@ -45,6 +45,7 @@ require ( github.com/lightningnetwork/lnd/tor v1.1.2 github.com/ltcsuite/ltcd v0.0.0-20190101042124-f37f8bf35796 github.com/miekg/dns v1.1.43 + github.com/pkg/errors v0.9.1 github.com/prometheus/client_golang v1.11.1 github.com/stretchr/testify v1.9.0 github.com/tv42/zbase32 v0.0.0-20160707012821-501572607d02 @@ -135,7 +136,6 @@ require ( github.com/opencontainers/image-spec v1.0.2 // indirect github.com/opencontainers/runc v1.1.12 // indirect github.com/ory/dockertest/v3 v3.10.0 // indirect - github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/prometheus/client_model v0.2.0 // indirect github.com/prometheus/common v0.26.0 // indirect diff --git a/graph/ann_validation.go b/graph/ann_validation.go index 4cbf3737b1..3c93d06e52 100644 --- a/graph/ann_validation.go +++ b/graph/ann_validation.go @@ -2,12 +2,9 @@ package graph import ( "bytes" - "fmt" "github.com/btcsuite/btcd/btcec/v2" - "github.com/btcsuite/btcd/btcutil" "github.com/btcsuite/btcd/chaincfg/chainhash" - "github.com/davecgh/go-spew/spew" "github.com/go-errors/errors" "github.com/lightningnetwork/lnd/lnwire" ) @@ -48,70 +45,3 @@ func ValidateNodeAnn(a *lnwire.NodeAnnouncement) error { return nil } - -// ValidateChannelUpdateAnn validates the channel update announcement by -// checking (1) that the included signature covers the announcement and has been -// signed by the node's private key, and (2) that the announcement's message -// flags and optional fields are sane. -func ValidateChannelUpdateAnn(pubKey *btcec.PublicKey, capacity btcutil.Amount, - a *lnwire.ChannelUpdate1) error { - - if err := ValidateChannelUpdateFields(capacity, a); err != nil { - return err - } - - return VerifyChannelUpdateSignature(a, pubKey) -} - -// VerifyChannelUpdateSignature verifies that the channel update message was -// signed by the party with the given node public key. -func VerifyChannelUpdateSignature(msg *lnwire.ChannelUpdate1, - pubKey *btcec.PublicKey) error { - - data, err := msg.DataToSign() - if err != nil { - return fmt.Errorf("unable to reconstruct message data: %w", err) - } - dataHash := chainhash.DoubleHashB(data) - - nodeSig, err := msg.Signature.ToSignature() - if err != nil { - return err - } - - if !nodeSig.Verify(dataHash, pubKey) { - return fmt.Errorf("invalid signature for channel update %v", - spew.Sdump(msg)) - } - - return nil -} - -// ValidateChannelUpdateFields validates a channel update's message flags and -// corresponding update fields. -func ValidateChannelUpdateFields(capacity btcutil.Amount, - msg *lnwire.ChannelUpdate1) error { - - // The maxHTLC flag is mandatory. - if !msg.MessageFlags.HasMaxHtlc() { - return errors.Errorf("max htlc flag not set for channel "+ - "update %v", spew.Sdump(msg)) - } - - maxHtlc := msg.HtlcMaximumMsat - if maxHtlc == 0 || maxHtlc < msg.HtlcMinimumMsat { - return errors.Errorf("invalid max htlc for channel "+ - "update %v", spew.Sdump(msg)) - } - - // For light clients, the capacity will not be set so we'll skip - // checking whether the MaxHTLC value respects the channel's - // capacity. - capacityMsat := lnwire.NewMSatFromSatoshis(capacity) - if capacityMsat != 0 && maxHtlc > capacityMsat { - return errors.Errorf("max_htlc (%v) for channel update "+ - "greater than capacity (%v)", maxHtlc, capacityMsat) - } - - return nil -} diff --git a/graph/builder.go b/graph/builder.go index d629523f47..8b8b40d640 100644 --- a/graph/builder.go +++ b/graph/builder.go @@ -27,6 +27,7 @@ import ( "github.com/lightningnetwork/lnd/lnwallet/chanvalidate" "github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/multimutex" + "github.com/lightningnetwork/lnd/netann" "github.com/lightningnetwork/lnd/routing/chainview" "github.com/lightningnetwork/lnd/routing/route" "github.com/lightningnetwork/lnd/ticker" @@ -1484,7 +1485,7 @@ func (b *Builder) ApplyChannelUpdate(msg *lnwire.ChannelUpdate1) bool { return false } - err = ValidateChannelUpdateAnn(pubKey, ch.Capacity, msg) + err = netann.ValidateChannelUpdateAnn(pubKey, ch.Capacity, msg) if err != nil { log.Errorf("Unable to validate channel update: %v", err) return false diff --git a/netann/channel_update.go b/netann/channel_update.go index c8992ccc69..609a26a1ee 100644 --- a/netann/channel_update.go +++ b/netann/channel_update.go @@ -6,10 +6,14 @@ import ( "time" "github.com/btcsuite/btcd/btcec/v2" + "github.com/btcsuite/btcd/btcutil" + "github.com/btcsuite/btcd/chaincfg/chainhash" + "github.com/davecgh/go-spew/spew" "github.com/lightningnetwork/lnd/channeldb/models" "github.com/lightningnetwork/lnd/keychain" "github.com/lightningnetwork/lnd/lnwallet" "github.com/lightningnetwork/lnd/lnwire" + "github.com/pkg/errors" ) // ErrUnableToExtractChanUpdate is returned when a channel update cannot be @@ -152,3 +156,70 @@ func ChannelUpdateFromEdge(info *models.ChannelEdgeInfo, return update, nil } + +// ValidateChannelUpdateAnn validates the channel update announcement by +// checking (1) that the included signature covers the announcement and has been +// signed by the node's private key, and (2) that the announcement's message +// flags and optional fields are sane. +func ValidateChannelUpdateAnn(pubKey *btcec.PublicKey, capacity btcutil.Amount, + a *lnwire.ChannelUpdate1) error { + + if err := ValidateChannelUpdateFields(capacity, a); err != nil { + return err + } + + return VerifyChannelUpdateSignature(a, pubKey) +} + +// VerifyChannelUpdateSignature verifies that the channel update message was +// signed by the party with the given node public key. +func VerifyChannelUpdateSignature(msg *lnwire.ChannelUpdate1, + pubKey *btcec.PublicKey) error { + + data, err := msg.DataToSign() + if err != nil { + return fmt.Errorf("unable to reconstruct message data: %w", err) + } + dataHash := chainhash.DoubleHashB(data) + + nodeSig, err := msg.Signature.ToSignature() + if err != nil { + return err + } + + if !nodeSig.Verify(dataHash, pubKey) { + return fmt.Errorf("invalid signature for channel update %v", + spew.Sdump(msg)) + } + + return nil +} + +// ValidateChannelUpdateFields validates a channel update's message flags and +// corresponding update fields. +func ValidateChannelUpdateFields(capacity btcutil.Amount, + msg *lnwire.ChannelUpdate1) error { + + // The maxHTLC flag is mandatory. + if !msg.MessageFlags.HasMaxHtlc() { + return errors.Errorf("max htlc flag not set for channel "+ + "update %v", spew.Sdump(msg)) + } + + maxHtlc := msg.HtlcMaximumMsat + if maxHtlc == 0 || maxHtlc < msg.HtlcMinimumMsat { + return errors.Errorf("invalid max htlc for channel "+ + "update %v", spew.Sdump(msg)) + } + + // For light clients, the capacity will not be set so we'll skip + // checking whether the MaxHTLC value respects the channel's + // capacity. + capacityMsat := lnwire.NewMSatFromSatoshis(capacity) + if capacityMsat != 0 && maxHtlc > capacityMsat { + return errors.Errorf("max_htlc (%v) for channel update "+ + "greater than capacity (%v)", maxHtlc, capacityMsat) + } + + return nil +} diff --git a/netann/channel_update_test.go b/netann/channel_update_test.go index 2a9ed2e98e..2a619e062e 100644 --- a/netann/channel_update_test.go +++ b/netann/channel_update_test.go @@ -7,7 +7,6 @@ import ( "github.com/btcsuite/btcd/btcec/v2" "github.com/btcsuite/btcd/btcec/v2/ecdsa" - "github.com/lightningnetwork/lnd/graph" "github.com/lightningnetwork/lnd/keychain" "github.com/lightningnetwork/lnd/lnwallet" "github.com/lightningnetwork/lnd/lnwire" @@ -182,7 +181,7 @@ func TestUpdateDisableFlag(t *testing.T) { // Finally, validate the signature using the router's // verification logic. - err = graph.VerifyChannelUpdateSignature( + err = netann.VerifyChannelUpdateSignature( newUpdate, pubKey, ) if err != nil { diff --git a/routing/payment_session.go b/routing/payment_session.go index e07de73b7a..b47f6ee0ca 100644 --- a/routing/payment_session.go +++ b/routing/payment_session.go @@ -8,9 +8,9 @@ import ( "github.com/lightningnetwork/lnd/build" "github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/channeldb/models" - "github.com/lightningnetwork/lnd/graph" "github.com/lightningnetwork/lnd/lnutils" "github.com/lightningnetwork/lnd/lnwire" + "github.com/lightningnetwork/lnd/netann" "github.com/lightningnetwork/lnd/routing/route" ) @@ -440,7 +440,7 @@ func (p *paymentSession) UpdateAdditionalEdge(msg *lnwire.ChannelUpdate1, pubKey *btcec.PublicKey, policy *models.CachedEdgePolicy) bool { // Validate the message signature. - if err := graph.VerifyChannelUpdateSignature(msg, pubKey); err != nil { + if err := netann.VerifyChannelUpdateSignature(msg, pubKey); err != nil { log.Errorf( "Unable to validate channel update signature: %v", err, ) From 34e9ee1ee535f9b711e34fa736024f32b6920e8f Mon Sep 17 00:00:00 2001 From: Elle Mouton Date: Wed, 11 Sep 2024 12:30:29 +0200 Subject: [PATCH 12/22] lnwire: lnwire: add a ChannelUpdate interface In this commit, a new ChannelUpdate interface is added and ChannelUpdate1 is made to implement the new interface. --- lnwire/channel_update.go | 88 ++++++++++++++++++++++++++++++++++++++++ lnwire/interfaces.go | 85 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 173 insertions(+) diff --git a/lnwire/channel_update.go b/lnwire/channel_update.go index c322c51639..33cc3bff03 100644 --- a/lnwire/channel_update.go +++ b/lnwire/channel_update.go @@ -279,3 +279,91 @@ func (a *ChannelUpdate1) DataToSign() ([]byte, error) { return buf.Bytes(), nil } + +// SCID returns the ShortChannelID of the channel that the update applies to. +// +// NOTE: this is part of the ChannelUpdate interface. +func (a *ChannelUpdate1) SCID() ShortChannelID { + return a.ShortChannelID +} + +// IsNode1 is true if the update was produced by node 1 of the channel peers. +// Node 1 is the node with the lexicographically smaller public key. +// +// NOTE: this is part of the ChannelUpdate interface. +func (a *ChannelUpdate1) IsNode1() bool { + return a.ChannelFlags&ChanUpdateDirection == 0 +} + +// IsDisabled is true if the update is announcing that the channel should be +// considered disabled. +// +// NOTE: this is part of the ChannelUpdate interface. +func (a *ChannelUpdate1) IsDisabled() bool { + return a.ChannelFlags&ChanUpdateDisabled == ChanUpdateDisabled +} + +// GetChainHash returns the hash of the chain that the message is referring to. +// +// NOTE: this is part of the ChannelUpdate interface. +func (a *ChannelUpdate1) GetChainHash() chainhash.Hash { + return a.ChainHash +} + +// ForwardingPolicy returns the set of forwarding constraints of the update. +// +// NOTE: this is part of the ChannelUpdate interface. +func (a *ChannelUpdate1) ForwardingPolicy() *ForwardingPolicy { + return &ForwardingPolicy{ + TimeLockDelta: a.TimeLockDelta, + BaseFee: MilliSatoshi(a.BaseFee), + FeeRate: MilliSatoshi(a.FeeRate), + MinHTLC: a.HtlcMinimumMsat, + HasMaxHTLC: a.MessageFlags.HasMaxHtlc(), + MaxHTLC: a.HtlcMaximumMsat, + } +} + +// CmpAge can be used to determine if the update is older or newer than the +// passed update. It returns 1 if this update is newer, -1 if it is older, and +// 0 if they are the same age. +// +// NOTE: this is part of the ChannelUpdate interface. +func (a *ChannelUpdate1) CmpAge(update ChannelUpdate) (CompareResult, error) { + other, ok := update.(*ChannelUpdate1) + if !ok { + return 0, fmt.Errorf("expected *ChannelUpdate1, got: %T", + update) + } + + switch { + case a.Timestamp > other.Timestamp: + return GreaterThan, nil + case a.Timestamp < other.Timestamp: + return LessThan, nil + default: + return EqualTo, nil + } +} + +// SetDisabledFlag can be used to adjust the disabled flag of an update. +// +// NOTE: this is part of the ChannelUpdate interface. +func (a *ChannelUpdate1) SetDisabledFlag(disabled bool) { + if disabled { + a.ChannelFlags |= ChanUpdateDisabled + } else { + a.ChannelFlags &= ^ChanUpdateDisabled + } +} + +// SetSCID can be used to overwrite the SCID of the update. +// +// NOTE: this is part of the ChannelUpdate interface. +func (a *ChannelUpdate1) SetSCID(scid ShortChannelID) { + a.ShortChannelID = scid +} + +// A compile time assertion to ensure ChannelUpdate1 implements the +// ChannelUpdate interface. +var _ ChannelUpdate = (*ChannelUpdate1)(nil) diff --git a/lnwire/interfaces.go b/lnwire/interfaces.go index 693c6ca719..3a8b7cbdfc 100644 --- a/lnwire/interfaces.go +++ b/lnwire/interfaces.go @@ -34,3 +34,88 @@ type ChannelAnnouncement interface { Message } + +// CompareResult represents the result after comparing two things. +type CompareResult uint8 + +const ( + // LessThan indicates that base object is less than the object it was + // compared to. + LessThan CompareResult = iota + + // EqualTo indicates that the base object is equal to the object it was + // compared to. + EqualTo + + // GreaterThan indicates that base object is greater than the object it + // was compared to. + GreaterThan +) + +// ChannelUpdate is an interface that describes a message used to update the +// forwarding rules of a channel. +type ChannelUpdate interface { + // SCID returns the ShortChannelID of the channel that the update + // applies to. + SCID() ShortChannelID + + // IsNode1 is true if the update was produced by node 1 of the channel + // peers. Node 1 is the node with the lexicographically smaller public + // key. + IsNode1() bool + + // IsDisabled is true if the update is announcing that the channel + // should be considered disabled. + IsDisabled() bool + + // GetChainHash returns the hash of the chain that the message is + // referring to. + GetChainHash() chainhash.Hash + + // ForwardingPolicy returns the set of forwarding constraints of the + // update. + ForwardingPolicy() *ForwardingPolicy + + // CmpAge can be used to determine if the update is older or newer than + // the passed update. It returns LessThan if this update is older than + // the passed update, GreaterThan if it is newer and EqualTo if they are + // the same age. + CmpAge(update ChannelUpdate) (CompareResult, error) + + // SetDisabledFlag can be used to adjust the disabled flag of an update. + SetDisabledFlag(bool) + + // SetSCID can be used to overwrite the SCID of the update. + SetSCID(scid ShortChannelID) + + Message +} + +// ForwardingPolicy defines the set of forwarding constraints advertised in a +// ChannelUpdate message. +type ForwardingPolicy struct { + // TimeLockDelta is the minimum number of blocks that the node requires + // to be added to the expiry of HTLCs. This is a security parameter + // determined by the node operator. This value represents the required + // gap between the time locks of the incoming and outgoing HTLC's set + // to this node. + TimeLockDelta uint16 + + // BaseFee is the base fee that must be used for incoming HTLC's to + // this particular channel. This value will be tacked onto the required + // for a payment independent of the size of the payment. + BaseFee MilliSatoshi + + // FeeRate is the fee rate that will be charged per millionth of a + // satoshi. + FeeRate MilliSatoshi + + // HtlcMinimumMsat is the minimum HTLC value which will be accepted. + MinHTLC MilliSatoshi + + // HasMaxHTLC is true if the MaxHTLC field is provided in the update. + HasMaxHTLC bool + + // HtlcMaximumMsat is the maximum HTLC value which will be accepted. + MaxHTLC MilliSatoshi +} From 35d0c61c1264bf7bb1b775fd244ca80c579a9e3b Mon Sep 17 00:00:00 2001 From: Elle Mouton Date: Wed, 11 Sep 2024 12:37:23 +0200 Subject: [PATCH 13/22] netann: let ValidateChannelAnn take the new interface --- discovery/gossiper.go | 8 +++++--- netann/channel_announcement.go | 20 ++++++++++++++++++-- 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/discovery/gossiper.go b/discovery/gossiper.go index ee1e2916b5..a0d3d0d232 100644 --- a/discovery/gossiper.go +++ b/discovery/gossiper.go @@ -1890,7 +1890,7 @@ func (d *AuthenticatedGossiper) processRejectedEdge( if err != nil { return nil, err } - err = netann.ValidateChannelAnn(chanAnn) + err = netann.ValidateChannelAnn(chanAnn, d.fetchPKScript) if err != nil { err := fmt.Errorf("assembled channel announcement proof "+ "for shortChanID=%v isn't valid: %v", @@ -2540,7 +2540,8 @@ func (d *AuthenticatedGossiper) handleChanAnnouncement(nMsg *networkMsg, // the signatures within the proof as it should be well formed. var proof *models.ChannelAuthProof if nMsg.isRemote { - if err := netann.ValidateChannelAnn(ann); err != nil { + err := netann.ValidateChannelAnn(ann, d.fetchPKScript) + if err != nil { err := fmt.Errorf("unable to validate announcement: "+ "%v", err) @@ -3434,7 +3435,8 @@ func (d *AuthenticatedGossiper) handleAnnSig(nMsg *networkMsg, // With all the necessary components assembled validate the full // channel announcement proof. - if err := netann.ValidateChannelAnn(chanAnn); err != nil { + err = netann.ValidateChannelAnn(chanAnn, d.fetchPKScript) + if err != nil { err := fmt.Errorf("channel announcement proof for "+ "short_chan_id=%v isn't valid: %v", shortChanID, err) diff --git a/netann/channel_announcement.go b/netann/channel_announcement.go index 3731f0b248..9de7960ea9 100644 --- a/netann/channel_announcement.go +++ b/netann/channel_announcement.go @@ -3,6 +3,7 @@ package netann import ( "bytes" "errors" + "fmt" "github.com/btcsuite/btcd/btcec/v2" "github.com/btcsuite/btcd/chaincfg/chainhash" @@ -88,10 +89,25 @@ func CreateChanAnnouncement(chanProof *models.ChannelAuthProof, return chanAnn, edge1Ann, edge2Ann, nil } -// ValidateChannelAnn validates the channel announcement message and checks +// FetchPkScript defines a function that can be used to fetch the output script +// for the transaction with the given SCID. +type FetchPkScript func(*lnwire.ShortChannelID) ([]byte, error) + +// ValidateChannelAnn validates the channel announcement. +func ValidateChannelAnn(a lnwire.ChannelAnnouncement, _ FetchPkScript) error { + switch ann := a.(type) { + case *lnwire.ChannelAnnouncement1: + return validateChannelAnn1(ann) + default: + return fmt.Errorf("unhandled implementation of "+ + "lnwire.ChannelAnnouncement: %T", a) + } +} + +// validateChannelAnn1 validates the channel announcement message and checks // that node signatures covers the announcement message, and that the bitcoin // signatures covers the node keys. -func ValidateChannelAnn(a *lnwire.ChannelAnnouncement1) error { +func validateChannelAnn1(a *lnwire.ChannelAnnouncement1) error { // First, we'll compute the digest (h) which is to be signed by each of // the keys included within the node announcement message. This hash // digest includes all the keys, so the (up to 4 signatures) will From ced88a997812ede915c5fd2f64dd473343abb706 Mon Sep 17 00:00:00 2001 From: Elle Mouton Date: Wed, 11 Sep 2024 12:48:26 +0200 Subject: [PATCH 14/22] netann: let ChannelUpdate validate methods take in the new interface --- netann/channel_update.go | 32 ++++++++++++++++++++++++++++++-- 1 file changed, 30 insertions(+), 2 deletions(-) diff --git a/netann/channel_update.go b/netann/channel_update.go index 609a26a1ee..5495bc227b 100644 --- a/netann/channel_update.go +++ b/netann/channel_update.go @@ -162,7 +162,7 @@ func ChannelUpdateFromEdge(info *models.ChannelEdgeInfo, // signed by the node's private key, and (2) that the announcement's message // flags and optional fields are sane. func ValidateChannelUpdateAnn(pubKey *btcec.PublicKey, capacity btcutil.Amount, - a *lnwire.ChannelUpdate1) error { + a lnwire.ChannelUpdate) error { if err := ValidateChannelUpdateFields(capacity, a); err != nil { return err @@ -173,7 +173,21 @@ func ValidateChannelUpdateAnn(pubKey *btcec.PublicKey, capacity btcutil.Amount, // VerifyChannelUpdateSignature verifies that the channel update message was // signed by the party with the given node public key. -func VerifyChannelUpdateSignature(msg *lnwire.ChannelUpdate1, +func VerifyChannelUpdateSignature(msg lnwire.ChannelUpdate, + pubKey *btcec.PublicKey) error { + + switch u := msg.(type) { + case *lnwire.ChannelUpdate1: + return verifyChannelUpdate1Signature(u, pubKey) + default: + return fmt.Errorf("unhandled implementation of "+ + "lnwire.ChannelUpdate: %T", msg) + } +} + +// verifyChannelUpdateSignature1 verifies that the channel update message was +// signed by the party with the given node public key. +func verifyChannelUpdate1Signature(msg *lnwire.ChannelUpdate1, pubKey *btcec.PublicKey) error { data, err := msg.DataToSign() @@ -198,6 +212,20 @@ func VerifyChannelUpdateSignature(msg *lnwire.ChannelUpdate1, // ValidateChannelUpdateFields validates a channel update's message flags and // corresponding update fields. func ValidateChannelUpdateFields(capacity btcutil.Amount, + msg lnwire.ChannelUpdate) error { + + switch u := msg.(type) { + case *lnwire.ChannelUpdate1: + return validateChannelUpdate1Fields(capacity, u) + default: + return fmt.Errorf("unhandled implementation of "+ + "lnwire.ChannelUpdate: %T", msg) + } +} + +// validateChannelUpdate1Fields validates a channel update's message flags and +// corresponding update fields. +func validateChannelUpdate1Fields(capacity btcutil.Amount, msg *lnwire.ChannelUpdate1) error { // The maxHTLC flag is mandatory. From 0b4e5a0d83508052f83dee3c06227e798ba7f6ea Mon Sep 17 00:00:00 2001 From: Elle Mouton Date: Fri, 29 Sep 2023 10:22:25 +0200 Subject: [PATCH 15/22] lnwire: add AnnounceSignatures2 message And ensure that it implements the AnnounceSignatures interface. --- lnwire/announcement_signatures_2.go | 97 +++++++++++++++++++++++++++++ lnwire/lnwire.go | 12 ++++ lnwire/lnwire_test.go | 35 +++++++++++ lnwire/message.go | 5 ++ 4 files changed, 149 insertions(+) create mode 100644 lnwire/announcement_signatures_2.go diff --git a/lnwire/announcement_signatures_2.go b/lnwire/announcement_signatures_2.go new file mode 100644 index 0000000000..a104470321 --- /dev/null +++ b/lnwire/announcement_signatures_2.go @@ -0,0 +1,97 @@ +package lnwire + +import ( + "bytes" + "io" +) + +// AnnounceSignatures2 is a direct message between two endpoints of a +// channel and serves as an opt-in mechanism to allow the announcement of +// a taproot channel to the rest of the network. It contains the necessary +// signatures by the sender to construct the channel_announcement_2 message. +type AnnounceSignatures2 struct { + // ChannelID is the unique description of the funding transaction. + // Channel id is better for users and debugging and short channel id is + // used for quick test on existence of the particular utxo inside the + // blockchain, because it contains information about block. + ChannelID ChannelID + + // ShortChannelID is the unique description of the funding transaction. + // It is constructed with the most significant 3 bytes as the block + // height, the next 3 bytes indicating the transaction index within the + // block, and the least significant two bytes indicating the output + // index which pays to the channel. + ShortChannelID ShortChannelID + + // PartialSignature is the combination of the partial Schnorr signature + // created for the node's bitcoin key with the partial signature created + // for the node's node ID key. + PartialSignature PartialSig + + // ExtraOpaqueData is the set of data that was appended to this + // message, some of which we may not actually know how to iterate or + // parse. By holding onto this data, we ensure that we're able to + // properly validate the set of signatures that cover these new fields, + // and ensure we're able to make upgrades to the network in a forwards + // compatible manner. + ExtraOpaqueData ExtraOpaqueData +} + +// A compile time check to ensure AnnounceSignatures2 implements the +// lnwire.Message interface. +var _ Message = (*AnnounceSignatures2)(nil) + +// Decode deserializes a serialized AnnounceSignatures2 stored in the passed +// io.Reader observing the specified protocol version. +// +// This is part of the lnwire.Message interface. +func (a *AnnounceSignatures2) Decode(r io.Reader, _ uint32) error { + return ReadElements(r, + &a.ChannelID, + &a.ShortChannelID, + &a.PartialSignature, + &a.ExtraOpaqueData, + ) +} + +// Encode serializes the target AnnounceSignatures2 into the passed io.Writer +// observing the protocol version specified. +// +// This is part of the lnwire.Message interface. +func (a *AnnounceSignatures2) Encode(w *bytes.Buffer, _ uint32) error { + if err := WriteChannelID(w, a.ChannelID); err != nil { + return err + } + + if err := WriteShortChannelID(w, a.ShortChannelID); err != nil { + return err + } + + if err := WriteElement(w, a.PartialSignature); err != nil { + return err + } + + return WriteBytes(w, a.ExtraOpaqueData) +} + +// MsgType returns the integer uniquely identifying this message type on the +// wire. +// +// This is part of the lnwire.Message interface. +func (a *AnnounceSignatures2) MsgType() MessageType { + return MsgAnnounceSignatures2 +} + +// SCID returns the ShortChannelID of the channel. +// +// NOTE: this is part of the AnnounceSignatures interface. +func (a *AnnounceSignatures2) SCID() ShortChannelID { + return a.ShortChannelID +} + +// ChanID returns the ChannelID identifying the channel. +// +// NOTE: this is part of the AnnounceSignatures interface. +func (a *AnnounceSignatures2) ChanID() ChannelID { + return a.ChannelID +} diff --git a/lnwire/lnwire.go b/lnwire/lnwire.go index 8ab082b0bd..4d2b814e5d 100644 --- a/lnwire/lnwire.go +++ b/lnwire/lnwire.go @@ -187,6 +187,11 @@ func WriteElement(w *bytes.Buffer, element interface{}) error { return err } + case PartialSig: + if err := e.Encode(w); err != nil { + return err + } + case PingPayload: var l [2]byte binary.BigEndian.PutUint16(l[:], uint16(len(e))) @@ -936,6 +941,13 @@ func ReadElement(r io.Reader, element interface{}) error { } *e = addrBytes[:length] + case *PartialSig: + var sig PartialSig + if err = sig.Decode(r); err != nil { + return err + } + *e = sig + case *ExtraOpaqueData: return e.Decode(r) diff --git a/lnwire/lnwire_test.go b/lnwire/lnwire_test.go index 90d338d881..e4ab753f7d 100644 --- a/lnwire/lnwire_test.go +++ b/lnwire/lnwire_test.go @@ -1509,6 +1509,35 @@ func TestLightningWireProtocol(t *testing.T) { v[0] = reflect.ValueOf(*req) }, + MsgAnnounceSignatures2: func(v []reflect.Value, + r *rand.Rand) { + + req := AnnounceSignatures2{ + ShortChannelID: NewShortChanIDFromInt( + uint64(r.Int63()), + ), + ExtraOpaqueData: make([]byte, 0), + } + + _, err := r.Read(req.ChannelID[:]) + require.NoError(t, err) + + partialSig, err := randPartialSig(r) + require.NoError(t, err) + + req.PartialSignature = *partialSig + + numExtraBytes := r.Int31n(1000) + if numExtraBytes > 0 { + req.ExtraOpaqueData = make( + []byte, numExtraBytes, + ) + _, err := r.Read(req.ExtraOpaqueData[:]) + require.NoError(t, err) + } + + v[0] = reflect.ValueOf(req) + }, } // With the above types defined, we'll now generate a slice of @@ -1737,6 +1766,12 @@ func TestLightningWireProtocol(t *testing.T) { return mainScenario(&m) }, }, + { + msgType: MsgAnnounceSignatures2, + scenario: func(m AnnounceSignatures2) bool { + return mainScenario(&m) + }, + }, } for _, test := range tests { var config *quick.Config diff --git a/lnwire/message.go b/lnwire/message.go index 1d93a2a26c..e933572773 100644 --- a/lnwire/message.go +++ b/lnwire/message.go @@ -52,6 +52,7 @@ const ( MsgNodeAnnouncement = 257 MsgChannelUpdate = 258 MsgAnnounceSignatures = 259 + MsgAnnounceSignatures2 = 260 MsgQueryShortChanIDs = 261 MsgReplyShortChanIDsEnd = 262 MsgQueryChannelRange = 263 @@ -155,6 +156,8 @@ func (t MessageType) String() string { return "ClosingComplete" case MsgClosingSig: return "ClosingSig" + case MsgAnnounceSignatures2: + return "MsgAnnounceSignatures2" default: return "" } @@ -284,6 +287,8 @@ func makeEmptyMessage(msgType MessageType) (Message, error) { msg = &ClosingComplete{} case MsgClosingSig: msg = &ClosingSig{} + case MsgAnnounceSignatures2: + msg = &AnnounceSignatures2{} default: // If the message is not within our custom range and has not // specifically been overridden, return an unknown message. From 66302ceb29672bb6454d14ae8c713226f30fd077 Mon Sep 17 00:00:00 2001 From: Elle Mouton Date: Wed, 21 Aug 2024 16:48:09 +0200 Subject: [PATCH 16/22] lnwire: make RawFeatureVector a Record producer --- lnwire/features.go | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/lnwire/features.go b/lnwire/features.go index 437a50dc21..c5ae014f79 100644 --- a/lnwire/features.go +++ b/lnwire/features.go @@ -642,31 +642,39 @@ func (fv *RawFeatureVector) sizeFunc() uint64 { // Record returns a TLV record that can be used to encode/decode raw feature // vectors. Note that the length of the feature vector is not included, because // it is covered by the TLV record's length field. -func (fv *RawFeatureVector) Record(recordType tlv.Type) tlv.Record { +func (fv *RawFeatureVector) Record() tlv.Record { return tlv.MakeDynamicRecord( - recordType, fv, fv.sizeFunc, rawFeatureEncoder, - rawFeatureDecoder, + 0, fv, fv.sizeFunc, rawFeatureEncoder, rawFeatureDecoder, ) } // rawFeatureEncoder is a custom TLV encoder for raw feature vectors. func rawFeatureEncoder(w io.Writer, val interface{}, _ *[8]byte) error { - if f, ok := val.(*RawFeatureVector); ok { - return f.encode(w, f.SerializeSize(), 8) + if v, ok := val.(*RawFeatureVector); ok { + // Encode the feature bits as a byte slice without its length + // prepended, as that's already taken care of by the TLV record. + fv := *v + return fv.encode(w, fv.SerializeSize(), 8) } - return tlv.NewTypeForEncodingErr(val, "*lnwire.RawFeatureVector") + return tlv.NewTypeForEncodingErr(val, "lnwire.RawFeatureVector") } // rawFeatureDecoder is a custom TLV decoder for raw feature vectors. func rawFeatureDecoder(r io.Reader, val interface{}, _ *[8]byte, l uint64) error { - if f, ok := val.(*RawFeatureVector); ok { - return f.decode(r, int(l), 8) + if v, ok := val.(*RawFeatureVector); ok { + fv := NewRawFeatureVector() + if err := fv.decode(r, int(l), 8); err != nil { + return err + } + *v = *fv + + return nil } - return tlv.NewTypeForEncodingErr(val, "*lnwire.RawFeatureVector") + return tlv.NewTypeForEncodingErr(val, "lnwire.RawFeatureVector") } // FeatureVector represents a set of enabled features. The set stores From a438eb3af329250ae4f04f8b2b2110fc136c37ba Mon Sep 17 00:00:00 2001 From: Elle Mouton Date: Fri, 29 Sep 2023 10:33:24 +0200 Subject: [PATCH 17/22] lnwire: add ChannelAnnouncement2 message And ensure that it implements the ChannelAnnouncement interface. --- lnwire/channel_announcement_2.go | 234 +++++++++++++++++++++++++++++++ lnwire/lnwire_test.go | 148 +++++++++++++------ lnwire/message.go | 5 + 3 files changed, 343 insertions(+), 44 deletions(-) create mode 100644 lnwire/channel_announcement_2.go diff --git a/lnwire/channel_announcement_2.go b/lnwire/channel_announcement_2.go new file mode 100644 index 0000000000..074e7d0842 --- /dev/null +++ b/lnwire/channel_announcement_2.go @@ -0,0 +1,234 @@ +package lnwire + +import ( + "bytes" + "io" + + "github.com/btcsuite/btcd/chaincfg" + "github.com/btcsuite/btcd/chaincfg/chainhash" + "github.com/lightningnetwork/lnd/tlv" +) + +// ChannelAnnouncement2 message is used to announce the existence of a taproot +// channel between two peers in the network. +type ChannelAnnouncement2 struct { + // Signature is a Schnorr signature over the TLV stream of the message. + Signature Sig + + // ChainHash denotes the target chain that this channel was opened + // within. This value should be the genesis hash of the target chain. + ChainHash tlv.RecordT[tlv.TlvType0, chainhash.Hash] + + // Features is the feature vector that encodes the features supported + // by the target node. This field can be used to signal the type of the + // channel, or modifications to the fields that would normally follow + // this vector. + Features tlv.RecordT[tlv.TlvType2, RawFeatureVector] + + // ShortChannelID is the unique description of the funding transaction, + // or where exactly it's located within the target blockchain. + ShortChannelID tlv.RecordT[tlv.TlvType4, ShortChannelID] + + // Capacity is the number of satoshis of the capacity of this channel. + // It must be less than or equal to the value of the on-chain funding + // output. + Capacity tlv.RecordT[tlv.TlvType6, uint64] + + // NodeID1 is the numerically-lesser public key ID of one of the channel + // operators. + NodeID1 tlv.RecordT[tlv.TlvType8, [33]byte] + + // NodeID2 is the numerically-greater public key ID of one of the + // channel operators. + NodeID2 tlv.RecordT[tlv.TlvType10, [33]byte] + + // BitcoinKey1 is the public key of the key used by Node1 in the + // construction of the on-chain funding transaction. This is an optional + // field and only needs to be set if the 4-of-4 MuSig construction was + // used in the creation of the message signature. + BitcoinKey1 tlv.OptionalRecordT[tlv.TlvType12, [33]byte] + + // BitcoinKey2 is the public key of the key used by Node2 in the + // construction of the on-chain funding transaction. This is an optional + // field and only needs to be set if the 4-of-4 MuSig construction was + // used in the creation of the message signature. + BitcoinKey2 tlv.OptionalRecordT[tlv.TlvType14, [33]byte] + + // MerkleRootHash is the hash used to create the optional tweak in the + // funding output. If this is not set but the bitcoin keys are, then + // the funding output is a pure 2-of-2 MuSig aggregate public key. + MerkleRootHash tlv.OptionalRecordT[tlv.TlvType16, [32]byte] + + // ExtraOpaqueData is the set of data that was appended to this + // message, some of which we may not actually know how to iterate or + // parse. By holding onto this data, we ensure that we're able to + // properly validate the set of signatures that cover these new fields, + // and ensure we're able to make upgrades to the network in a forwards + // compatible manner. + ExtraOpaqueData ExtraOpaqueData +} + +// Decode deserializes a serialized AnnounceSignatures1 stored in the passed +// io.Reader observing the specified protocol version. +// +// This is part of the lnwire.Message interface. +func (c *ChannelAnnouncement2) Decode(r io.Reader, _ uint32) error { + err := ReadElement(r, &c.Signature) + if err != nil { + return err + } + c.Signature.ForceSchnorr() + + return c.DecodeTLVRecords(r) +} + +// DecodeTLVRecords decodes only the TLV section of the message. +func (c *ChannelAnnouncement2) DecodeTLVRecords(r io.Reader) error { + // First extract into extra opaque data. + var tlvRecords ExtraOpaqueData + if err := ReadElements(r, &tlvRecords); err != nil { + return err + } + + var ( + chainHash = tlv.ZeroRecordT[tlv.TlvType0, [32]byte]() + btcKey1 = tlv.ZeroRecordT[tlv.TlvType12, [33]byte]() + btcKey2 = tlv.ZeroRecordT[tlv.TlvType14, [33]byte]() + merkleRootHash = tlv.ZeroRecordT[tlv.TlvType16, [32]byte]() + ) + typeMap, err := tlvRecords.ExtractRecords( + &chainHash, &c.Features, &c.ShortChannelID, &c.Capacity, + &c.NodeID1, &c.NodeID2, &btcKey1, &btcKey2, &merkleRootHash, + ) + if err != nil { + return err + } + + // By default, the chain-hash is the bitcoin mainnet genesis block hash. + c.ChainHash.Val = *chaincfg.MainNetParams.GenesisHash + if _, ok := typeMap[c.ChainHash.TlvType()]; ok { + c.ChainHash.Val = chainHash.Val + } + + if _, ok := typeMap[c.BitcoinKey1.TlvType()]; ok { + c.BitcoinKey1 = tlv.SomeRecordT(btcKey1) + } + + if _, ok := typeMap[c.BitcoinKey2.TlvType()]; ok { + c.BitcoinKey2 = tlv.SomeRecordT(btcKey2) + } + + if _, ok := typeMap[c.MerkleRootHash.TlvType()]; ok { + c.MerkleRootHash = tlv.SomeRecordT(merkleRootHash) + } + + if len(tlvRecords) != 0 { + c.ExtraOpaqueData = tlvRecords + } + + return nil +} + +// Encode serializes the target AnnounceSignatures1 into the passed io.Writer +// observing the protocol version specified. +// +// This is part of the lnwire.Message interface. +func (c *ChannelAnnouncement2) Encode(w *bytes.Buffer, _ uint32) error { + _, err := w.Write(c.Signature.RawBytes()) + if err != nil { + return err + } + _, err = c.DataToSign() + if err != nil { + return err + } + + return WriteBytes(w, c.ExtraOpaqueData) +} + +// DataToSign encodes the data to be signed into the ExtraOpaqueData member and +// returns it. +func (c *ChannelAnnouncement2) DataToSign() ([]byte, error) { + // The chain-hash record is only included if it is _not_ equal to the + // bitcoin mainnet genisis block hash. + var recordProducers []tlv.RecordProducer + if !c.ChainHash.Val.IsEqual(chaincfg.MainNetParams.GenesisHash) { + hash := tlv.ZeroRecordT[tlv.TlvType0, [32]byte]() + hash.Val = c.ChainHash.Val + + recordProducers = append(recordProducers, &hash) + } + + recordProducers = append(recordProducers, + &c.Features, &c.ShortChannelID, &c.Capacity, &c.NodeID1, + &c.NodeID2, + ) + + c.BitcoinKey1.WhenSome(func(key tlv.RecordT[tlv.TlvType12, [33]byte]) { + recordProducers = append(recordProducers, &key) + }) + + c.BitcoinKey2.WhenSome(func(key tlv.RecordT[tlv.TlvType14, [33]byte]) { + recordProducers = append(recordProducers, &key) + }) + + c.MerkleRootHash.WhenSome( + func(hash tlv.RecordT[tlv.TlvType16, [32]byte]) { + recordProducers = append(recordProducers, &hash) + }, + ) + + err := EncodeMessageExtraData(&c.ExtraOpaqueData, recordProducers...) + if err != nil { + return nil, err + } + + return c.ExtraOpaqueData, nil +} + +// MsgType returns the integer uniquely identifying this message type on the +// wire. +// +// This is part of the lnwire.Message interface. +func (c *ChannelAnnouncement2) MsgType() MessageType { + return MsgChannelAnnouncement2 +} + +// A compile time check to ensure ChannelAnnouncement2 implements the +// lnwire.Message interface. +var _ Message = (*ChannelAnnouncement2)(nil) + +// Node1KeyBytes returns the bytes representing the public key of node 1 in the +// channel. +// +// NOTE: This is part of the ChannelAnnouncement interface. +func (c *ChannelAnnouncement2) Node1KeyBytes() [33]byte { + return c.NodeID1.Val +} + +// Node2KeyBytes returns the bytes representing the public key of node 2 in the +// channel. +// +// NOTE: This is part of the ChannelAnnouncement interface. +func (c *ChannelAnnouncement2) Node2KeyBytes() [33]byte { + return c.NodeID2.Val +} + +// GetChainHash returns the hash of the chain which this channel's funding +// transaction is confirmed in. +// +// NOTE: This is part of the ChannelAnnouncement interface. +func (c *ChannelAnnouncement2) GetChainHash() chainhash.Hash { + return c.ChainHash.Val +} + +// SCID returns the short channel ID of the channel. +// +// NOTE: This is part of the ChannelAnnouncement interface. +func (c *ChannelAnnouncement2) SCID() ShortChannelID { + return c.ShortChannelID.Val +} + +// A compile-time check to ensure that ChannelAnnouncement2 implements the +// ChannelAnnouncement interface. +var _ ChannelAnnouncement = (*ChannelAnnouncement2)(nil) diff --git a/lnwire/lnwire_test.go b/lnwire/lnwire_test.go index e4ab753f7d..2e74853e47 100644 --- a/lnwire/lnwire_test.go +++ b/lnwire/lnwire_test.go @@ -19,6 +19,7 @@ import ( "github.com/btcsuite/btcd/btcec/v2" "github.com/btcsuite/btcd/btcec/v2/ecdsa" "github.com/btcsuite/btcd/btcutil" + "github.com/btcsuite/btcd/chaincfg" "github.com/btcsuite/btcd/chaincfg/chainhash" "github.com/btcsuite/btcd/wire" "github.com/lightningnetwork/lnd/fn" @@ -30,17 +31,25 @@ import ( ) var ( - shaHash1Bytes, _ = hex.DecodeString("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855") - shaHash1, _ = chainhash.NewHash(shaHash1Bytes) - outpoint1 = wire.NewOutPoint(shaHash1, 0) - - testRBytes, _ = hex.DecodeString("8ce2bc69281ce27da07e6683571319d18e949ddfa2965fb6caa1bf0314f882d7") - testSBytes, _ = hex.DecodeString("299105481d63e0f4bc2a88121167221b6700d72a0ead154c03be696a292d24ae") - testRScalar = new(btcec.ModNScalar) - testSScalar = new(btcec.ModNScalar) - _ = testRScalar.SetByteSlice(testRBytes) - _ = testSScalar.SetByteSlice(testSBytes) - testSig = ecdsa.NewSignature(testRScalar, testSScalar) + shaHash1Bytes, _ = hex.DecodeString("e3b0c44298fc1c149afbf4c8996fb" + + "92427ae41e4649b934ca495991b7852b855") + + shaHash1, _ = chainhash.NewHash(shaHash1Bytes) + outpoint1 = wire.NewOutPoint(shaHash1, 0) + + testRBytes, _ = hex.DecodeString("8ce2bc69281ce27da07e6683571" + + "319d18e949ddfa2965fb6caa1bf0314f882d7") + testSBytes, _ = hex.DecodeString("299105481d63e0f4bc2a" + + "88121167221b6700d72a0ead154c03be696a292d24ae") + testRScalar = new(btcec.ModNScalar) + testSScalar = new(btcec.ModNScalar) + _ = testRScalar.SetByteSlice(testRBytes) + _ = testSScalar.SetByteSlice(testSBytes) + testSig = ecdsa.NewSignature(testRScalar, testSScalar) + testSchnorrSigStr, _ = hex.DecodeString("04E7F9037658A92AFEB4F2" + + "5BAE5339E3DDCA81A353493827D26F16D92308E49E2A25E9220867" + + "8A2DF86970DA91B03A8AF8815A8A60498B358DAF560B347AA557") + testSchnorrSig, _ = NewSigFromSchnorrRawSignature(testSchnorrSigStr) ) const letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" @@ -156,17 +165,15 @@ func generateRandomBytes(n int) ([]byte, error) { return b, nil } -func randRawKey() ([33]byte, error) { +func randRawKey(t *testing.T) [33]byte { var n [33]byte priv, err := btcec.NewPrivateKey() - if err != nil { - return n, err - } + require.NoError(t, err) copy(n[:], priv.PubKey().SerializeCompressed()) - return n, nil + return n } func randDeliveryAddress(r *rand.Rand) (DeliveryAddress, error) { @@ -993,7 +1000,13 @@ func TestLightningWireProtocol(t *testing.T) { MsgChannelAnnouncement: func(v []reflect.Value, r *rand.Rand) { var err error req := ChannelAnnouncement1{ - ShortChannelID: NewShortChanIDFromInt(uint64(r.Int63())), + ShortChannelID: NewShortChanIDFromInt( + uint64(r.Int63()), + ), + NodeID1: randRawKey(t), + NodeID2: randRawKey(t), + BitcoinKey1: randRawKey(t), + BitcoinKey2: randRawKey(t), Features: randRawFeatureVector(r), ExtraOpaqueData: make([]byte, 0), } @@ -1018,26 +1031,6 @@ func TestLightningWireProtocol(t *testing.T) { return } - req.NodeID1, err = randRawKey() - if err != nil { - t.Fatalf("unable to generate key: %v", err) - return - } - req.NodeID2, err = randRawKey() - if err != nil { - t.Fatalf("unable to generate key: %v", err) - return - } - req.BitcoinKey1, err = randRawKey() - if err != nil { - t.Fatalf("unable to generate key: %v", err) - return - } - req.BitcoinKey2, err = randRawKey() - if err != nil { - t.Fatalf("unable to generate key: %v", err) - return - } if _, err := r.Read(req.ChainHash[:]); err != nil { t.Fatalf("unable to generate chain hash: %v", err) return @@ -1059,6 +1052,7 @@ func TestLightningWireProtocol(t *testing.T) { MsgNodeAnnouncement: func(v []reflect.Value, r *rand.Rand) { var err error req := NodeAnnouncement{ + NodeID: randRawKey(t), Features: randRawFeatureVector(r), Timestamp: uint32(r.Int31()), Alias: randAlias(r), @@ -1075,12 +1069,6 @@ func TestLightningWireProtocol(t *testing.T) { return } - req.NodeID, err = randRawKey() - if err != nil { - t.Fatalf("unable to generate key: %v", err) - return - } - req.Addresses, err = randAddrs(r) if err != nil { t.Fatalf("unable to generate addresses: %v", err) @@ -1114,7 +1102,9 @@ func TestLightningWireProtocol(t *testing.T) { } req := ChannelUpdate1{ - ShortChannelID: NewShortChanIDFromInt(uint64(r.Int63())), + ShortChannelID: NewShortChanIDFromInt( + uint64(r.Int63()), + ), Timestamp: uint32(r.Int31()), MessageFlags: msgFlags, ChannelFlags: ChanUpdateChanFlags(r.Int31()), @@ -1536,6 +1526,70 @@ func TestLightningWireProtocol(t *testing.T) { require.NoError(t, err) } + v[0] = reflect.ValueOf(req) + }, + MsgChannelAnnouncement2: func(v []reflect.Value, r *rand.Rand) { + req := ChannelAnnouncement2{ + Signature: testSchnorrSig, + ExtraOpaqueData: make([]byte, 0), + } + + req.ShortChannelID.Val = NewShortChanIDFromInt( + uint64(r.Int63()), + ) + req.Capacity.Val = rand.Uint64() + + req.Features.Val = *randRawFeatureVector(r) + + req.NodeID1.Val = randRawKey(t) + req.NodeID2.Val = randRawKey(t) + + // Sometimes set chain hash to bitcoin mainnet genesis + // hash. + req.ChainHash.Val = *chaincfg.MainNetParams.GenesisHash + if r.Int31()%2 == 0 { + _, err := r.Read(req.ChainHash.Val[:]) + require.NoError(t, err) + } + + // Sometimes set the bitcoin keys. + if r.Int31()%2 == 0 { + btcKey1 := tlv.ZeroRecordT[ + tlv.TlvType12, [33]byte, + ]() + btcKey1.Val = randRawKey(t) + req.BitcoinKey1 = tlv.SomeRecordT(btcKey1) + + btcKey2 := tlv.ZeroRecordT[ + tlv.TlvType14, [33]byte, + ]() + btcKey2.Val = randRawKey(t) + req.BitcoinKey2 = tlv.SomeRecordT(btcKey2) + + // Occasionally also set the merkle root hash. + if r.Int31()%2 == 0 { + hash := tlv.ZeroRecordT[ + tlv.TlvType16, [32]byte, + ]() + + _, err := r.Read(hash.Val[:]) + require.NoError(t, err) + + req.MerkleRootHash = tlv.SomeRecordT( + hash, + ) + } + } + + numExtraBytes := r.Int31n(1000) + if numExtraBytes > 0 { + req.ExtraOpaqueData = make( + []byte, numExtraBytes, + ) + _, err := r.Read(req.ExtraOpaqueData[:]) + require.NoError(t, err) + } + v[0] = reflect.ValueOf(req) }, } @@ -1772,6 +1826,12 @@ func TestLightningWireProtocol(t *testing.T) { return mainScenario(&m) }, }, + { + msgType: MsgChannelAnnouncement2, + scenario: func(m ChannelAnnouncement2) bool { + return mainScenario(&m) + }, + }, } for _, test := range tests { var config *quick.Config diff --git a/lnwire/message.go b/lnwire/message.go index e933572773..b91db06792 100644 --- a/lnwire/message.go +++ b/lnwire/message.go @@ -58,6 +58,7 @@ const ( MsgQueryChannelRange = 263 MsgReplyChannelRange = 264 MsgGossipTimestampRange = 265 + MsgChannelAnnouncement2 = 267 MsgKickoffSig = 777 ) @@ -158,6 +159,8 @@ func (t MessageType) String() string { return "ClosingSig" case MsgAnnounceSignatures2: return "MsgAnnounceSignatures2" + case MsgChannelAnnouncement2: + return "ChannelAnnouncement2" default: return "" } @@ -289,6 +292,8 @@ func makeEmptyMessage(msgType MessageType) (Message, error) { msg = &ClosingSig{} case MsgAnnounceSignatures2: msg = &AnnounceSignatures2{} + case MsgChannelAnnouncement2: + msg = &ChannelAnnouncement2{} default: // If the message is not within our custom range and has not // specifically been overridden, return an unknown message. From 5fc1da3abe770fe95308059afb1ab070363cfdad Mon Sep 17 00:00:00 2001 From: Elle Mouton Date: Thu, 26 Oct 2023 14:06:39 +0200 Subject: [PATCH 18/22] netann: add MsgHash helper This commit adds the MsgHash helper function which can be used to calculate the digest of a message to be signed using schnorr signatures. --- netann/msg_hash.go | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 netann/msg_hash.go diff --git a/netann/msg_hash.go b/netann/msg_hash.go new file mode 100644 index 0000000000..3c4806f9b4 --- /dev/null +++ b/netann/msg_hash.go @@ -0,0 +1,30 @@ +package netann + +import ( + "github.com/btcsuite/btcd/chaincfg/chainhash" +) + +// MsgHashTag will prefix the message name and the field name in order to +// construct the message tag. +const MsgHashTag = "lightning" + +// MsgTag computes the full tag that will be used to prefix a message before +// calculating the tagged hash. The tag is constructed as follows: +// +// tag = "lightning"||"msg_name"||"field_name" +func MsgTag(msgName, fieldName string) []byte { + tag := []byte(MsgHashTag) + tag = append(tag, []byte(msgName)...) + + return append(tag, []byte(fieldName)...) +} + +// MsgHash computes the tagged hash of the given message as follows: +// +// tag = "lightning"||"msg_name"||"field_name" +// hash = sha256(sha246(tag) || sha256(tag) || msg) +func MsgHash(msgName, fieldName string, msg []byte) *chainhash.Hash { + tag := MsgTag(msgName, fieldName) + + return chainhash.TaggedHash(tag, msg) +} From 21c9ef89048ded8603adb545dc0c6309058946e3 Mon Sep 17 00:00:00 2001 From: Elle Mouton Date: Wed, 11 Sep 2024 13:54:55 +0200 Subject: [PATCH 19/22] netann: validation and verification funcs for ChannelAnnouncement2 --- netann/channel_announcement.go | 116 ++++++++++- netann/channel_announcement_test.go | 293 ++++++++++++++++++++++++++++ 2 files changed, 408 insertions(+), 1 deletion(-) diff --git a/netann/channel_announcement.go b/netann/channel_announcement.go index 9de7960ea9..9644a523ff 100644 --- a/netann/channel_announcement.go +++ b/netann/channel_announcement.go @@ -6,9 +6,26 @@ import ( "fmt" "github.com/btcsuite/btcd/btcec/v2" + "github.com/btcsuite/btcd/btcec/v2/schnorr" + "github.com/btcsuite/btcd/btcec/v2/schnorr/musig2" "github.com/btcsuite/btcd/chaincfg/chainhash" "github.com/lightningnetwork/lnd/channeldb/models" "github.com/lightningnetwork/lnd/lnwire" + "github.com/lightningnetwork/lnd/tlv" +) + +const ( + // chanAnn2MsgName is a string representing the name of the + // ChannelAnnouncement2 message. This string will be used during the + // construction of the tagged hash message to be signed when producing + // the signature for the ChannelAnnouncement2 message. + chanAnn2MsgName = "channel_announcement_2" + + // chanAnn2SigFieldName is the name of the signature field of the + // ChannelAnnouncement2 message. This string will be used during the + // construction of the tagged hash message to be signed when producing + // the signature for the ChannelAnnouncement2 message. + chanAnn2SigFieldName = "signature" ) // CreateChanAnnouncement is a helper function which creates all channel @@ -94,10 +111,14 @@ func CreateChanAnnouncement(chanProof *models.ChannelAuthProof, type FetchPkScript func(*lnwire.ShortChannelID) ([]byte, error) // ValidateChannelAnn validates the channel announcement. -func ValidateChannelAnn(a lnwire.ChannelAnnouncement, _ FetchPkScript) error { +func ValidateChannelAnn(a lnwire.ChannelAnnouncement, + fetchPkScript FetchPkScript) error { + switch ann := a.(type) { case *lnwire.ChannelAnnouncement1: return validateChannelAnn1(ann) + case *lnwire.ChannelAnnouncement2: + return validateChannelAnn2(ann, fetchPkScript) default: return fmt.Errorf("unhandled implementation of "+ "lnwire.ChannelAnnouncement: %T", a) @@ -175,3 +196,96 @@ func validateChannelAnn1(a *lnwire.ChannelAnnouncement1) error { return nil } + +// validateChannelAnn2 validates the channel announcement message and checks +// that message signature covers the announcement message. +func validateChannelAnn2(a *lnwire.ChannelAnnouncement2, + fetchPkScript FetchPkScript) error { + + dataHash, err := ChanAnn2DigestToSign(a) + if err != nil { + return err + } + + sig, err := a.Signature.ToSignature() + if err != nil { + return err + } + + nodeKey1, err := btcec.ParsePubKey(a.NodeID1.Val[:]) + if err != nil { + return err + } + + nodeKey2, err := btcec.ParsePubKey(a.NodeID2.Val[:]) + if err != nil { + return err + } + + keys := []*btcec.PublicKey{ + nodeKey1, nodeKey2, + } + + // If the bitcoin keys are provided in the announcement, then it is + // assumed that the signature of the announcement is a 4-of-4 MuSig2 + // over the bitcoin keys and node ID keys. + if a.BitcoinKey1.IsSome() && a.BitcoinKey2.IsSome() { + var ( + btcKey1 tlv.RecordT[tlv.TlvType12, [33]byte] + btcKey2 tlv.RecordT[tlv.TlvType14, [33]byte] + ) + + btcKey1 = a.BitcoinKey1.UnwrapOr(btcKey1) + btcKey2 = a.BitcoinKey2.UnwrapOr(btcKey2) + + bitcoinKey1, err := btcec.ParsePubKey(btcKey1.Val[:]) + if err != nil { + return err + } + + bitcoinKey2, err := btcec.ParsePubKey(btcKey2.Val[:]) + if err != nil { + return err + } + + keys = append(keys, bitcoinKey1, bitcoinKey2) + } else { + // If bitcoin keys are not provided, then we need to get the + // on-chain output key since this will be the 3rd key in the + // 3-of-3 MuSig2 signature. + pkScript, err := fetchPkScript(&a.ShortChannelID.Val) + if err != nil { + return err + } + + outputKey, err := schnorr.ParsePubKey(pkScript[2:]) + if err != nil { + return err + } + + keys = append(keys, outputKey) + } + + aggKey, _, _, err := musig2.AggregateKeys(keys, true) + if err != nil { + return err + } + + if !sig.Verify(dataHash.CloneBytes(), aggKey.FinalKey) { + return fmt.Errorf("invalid sig") + } + + return nil +} + +// ChanAnn2DigestToSign computes the digest of the message to be signed. +func ChanAnn2DigestToSign(a *lnwire.ChannelAnnouncement2) (*chainhash.Hash, + error) { + + data, err := a.DataToSign() + if err != nil { + return nil, err + } + + return MsgHash(chanAnn2MsgName, chanAnn2SigFieldName, data), nil +} diff --git a/netann/channel_announcement_test.go b/netann/channel_announcement_test.go index 0a5b0f048b..61db16b16e 100644 --- a/netann/channel_announcement_test.go +++ b/netann/channel_announcement_test.go @@ -4,11 +4,16 @@ import ( "bytes" "testing" + "github.com/btcsuite/btcd/btcec/v2" + "github.com/btcsuite/btcd/btcec/v2/schnorr/musig2" "github.com/btcsuite/btcd/btcutil" + "github.com/btcsuite/btcd/chaincfg" "github.com/btcsuite/btcd/chaincfg/chainhash" "github.com/btcsuite/btcd/wire" "github.com/lightningnetwork/lnd/channeldb/models" + "github.com/lightningnetwork/lnd/input" "github.com/lightningnetwork/lnd/lnwire" + "github.com/lightningnetwork/lnd/tlv" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -64,3 +69,291 @@ func TestCreateChanAnnouncement(t *testing.T) { assert.Equal(t, chanAnn, expChanAnn) } + +// TestChanAnnounce2Validation checks that the various forms of the +// channel_announcement_2 message are validated correctly. +func TestChanAnnounce2Validation(t *testing.T) { + t.Parallel() + + t.Run( + "test 4-of-4 MuSig2 channel announcement", + test4of4MuSig2ChanAnnouncement, + ) + + t.Run( + "test 3-of-3 MuSig2 channel announcement", + test3of3MuSig2ChanAnnouncement, + ) +} + +// test4of4MuSig2ChanAnnouncement covers the case where both bitcoin keys are +// present in the channel announcement. In this case, the signature should be +// a 4-of-4 MuSig2. +func test4of4MuSig2ChanAnnouncement(t *testing.T) { + t.Parallel() + + // Generate the keys for node 1 and node2. + node1, node2 := genChanAnnKeys(t) + + // Build the unsigned channel announcement. + ann := buildUnsignedChanAnnouncement(node1, node2, true) + + // Serialise the bytes that need to be signed. + msg, err := ChanAnn2DigestToSign(ann) + require.NoError(t, err) + + var msgBytes [32]byte + copy(msgBytes[:], msg.CloneBytes()) + + // Generate the 4 nonces required for producing the signature. + var ( + node1NodeNonce = genNonceForPubKey(t, node1.nodePub) + node1BtcNonce = genNonceForPubKey(t, node1.btcPub) + node2NodeNonce = genNonceForPubKey(t, node2.nodePub) + node2BtcNonce = genNonceForPubKey(t, node2.btcPub) + ) + + nonceAgg, err := musig2.AggregateNonces([][66]byte{ + node1NodeNonce.PubNonce, + node1BtcNonce.PubNonce, + node2NodeNonce.PubNonce, + node2BtcNonce.PubNonce, + }) + require.NoError(t, err) + + pubKeys := []*btcec.PublicKey{ + node1.nodePub, node2.nodePub, node1.btcPub, node2.btcPub, + } + + // Let Node1 sign the announcement message with its node key. + psA1, err := musig2.Sign( + node1NodeNonce.SecNonce, node1.nodePriv, nonceAgg, pubKeys, + msgBytes, musig2.WithSortedKeys(), + ) + require.NoError(t, err) + + // Let Node1 sign the announcement message with its bitcoin key. + psA2, err := musig2.Sign( + node1BtcNonce.SecNonce, node1.btcPriv, nonceAgg, pubKeys, + msgBytes, musig2.WithSortedKeys(), + ) + require.NoError(t, err) + + // Let Node2 sign the announcement message with its node key. + psB1, err := musig2.Sign( + node2NodeNonce.SecNonce, node2.nodePriv, nonceAgg, pubKeys, + msgBytes, musig2.WithSortedKeys(), + ) + require.NoError(t, err) + + // Let Node2 sign the announcement message with its bitcoin key. + psB2, err := musig2.Sign( + node2BtcNonce.SecNonce, node2.btcPriv, nonceAgg, pubKeys, + msgBytes, musig2.WithSortedKeys(), + ) + require.NoError(t, err) + + // Finally, combine the partial signatures from Node1 and Node2 and add + // the signature to the announcement message. + s := musig2.CombineSigs(psA1.R, []*musig2.PartialSignature{ + psA1, psA2, psB1, psB2, + }) + + sig, err := lnwire.NewSigFromSignature(s) + require.NoError(t, err) + + ann.Signature = sig + + // Validate the announcement. + require.NoError(t, ValidateChannelAnn(ann, nil)) +} + +// test3of3MuSig2ChanAnnouncement covers the case where no bitcoin keys are +// present in the channel announcement. In this case, the signature should be +// a 3-of-3 MuSig2 where the keys making up the pub key are: node1 ID, node2 ID +// and the output key found on-chain in the funding transaction. As the +// verifier, we don't care about the construction of the output key. We only +// care that the channel peers were able to sign for the output key. In reality, +// this key will likely be constructed from at least 1 key from each peer and +// the partial signature for it will be constructed via nested MuSig2 but for +// the sake of the test, we will just have it be backed by a single key. +func test3of3MuSig2ChanAnnouncement(t *testing.T) { + // Generate the keys for node 1 and node 2. + node1, node2 := genChanAnnKeys(t) + + // Build the unsigned channel announcement. + ann := buildUnsignedChanAnnouncement(node1, node2, false) + + // Serialise the bytes that need to be signed. + msg, err := ChanAnn2DigestToSign(ann) + require.NoError(t, err) + + var msgBytes [32]byte + copy(msgBytes[:], msg.CloneBytes()) + + // Create a random 3rd key to be used for the output key. + outputKeyPriv, err := btcec.NewPrivateKey() + require.NoError(t, err) + + outputKey := outputKeyPriv.PubKey() + + // Ensure that the output key has an even Y by negating the private key + // if required. + if outputKey.SerializeCompressed()[0] == + input.PubKeyFormatCompressedOdd { + + outputKeyPriv.Key.Negate() + outputKey = outputKeyPriv.PubKey() + } + + // Generate the nonces required for producing the partial signatures. + var ( + node1NodeNonce = genNonceForPubKey(t, node1.nodePub) + node2NodeNonce = genNonceForPubKey(t, node2.nodePub) + outputKeyNonce = genNonceForPubKey(t, outputKey) + ) + + nonceAgg, err := musig2.AggregateNonces([][66]byte{ + node1NodeNonce.PubNonce, + node2NodeNonce.PubNonce, + outputKeyNonce.PubNonce, + }) + require.NoError(t, err) + + pkScript, err := input.PayToTaprootScript(outputKey) + require.NoError(t, err) + + // We'll pass in a mock tx fetcher that will return the funding output + // containing this key. This is needed since the output key can not be + // determined from the channel announcement itself. + fetchTx := func(chanID *lnwire.ShortChannelID) ([]byte, error) { + return pkScript, nil + } + + pubKeys := []*btcec.PublicKey{node1.nodePub, node2.nodePub, outputKey} + + // Let Node1 sign the announcement message with its node key. + psA, err := musig2.Sign( + node1NodeNonce.SecNonce, node1.nodePriv, nonceAgg, pubKeys, + msgBytes, musig2.WithSortedKeys(), + ) + require.NoError(t, err) + + // Let Node2 sign the announcement message with its node key. + psB, err := musig2.Sign( + node2NodeNonce.SecNonce, node2.nodePriv, nonceAgg, pubKeys, + msgBytes, musig2.WithSortedKeys(), + ) + require.NoError(t, err) + + // Create a partial sig for the output key. + psO, err := musig2.Sign( + outputKeyNonce.SecNonce, outputKeyPriv, nonceAgg, pubKeys, + msgBytes, musig2.WithSortedKeys(), + ) + require.NoError(t, err) + + // Finally, combine the partial signatures from Node1 and Node2 and add + // the signature to the announcement message. + s := musig2.CombineSigs(psA.R, []*musig2.PartialSignature{ + psA, psB, psO, + }) + + sig, err := lnwire.NewSigFromSignature(s) + require.NoError(t, err) + + ann.Signature = sig + + // Validate the announcement. + require.NoError(t, ValidateChannelAnn(ann, fetchTx)) +} + +func genNonceForPubKey(t *testing.T, pub *btcec.PublicKey) *musig2.Nonces { + nonce, err := musig2.GenNonces(musig2.WithPublicKey(pub)) + require.NoError(t, err) + + return nonce +} + +type keyRing struct { + nodePriv *btcec.PrivateKey + nodePub *btcec.PublicKey + btcPriv *btcec.PrivateKey + btcPub *btcec.PublicKey +} + +func genChanAnnKeys(t *testing.T) (*keyRing, *keyRing) { + // Let Alice and Bob derive the various keys they need. + aliceNodePrivKey, err := btcec.NewPrivateKey() + require.NoError(t, err) + + aliceNodeID := aliceNodePrivKey.PubKey() + + aliceBtcPrivKey, err := btcec.NewPrivateKey() + require.NoError(t, err) + + bobNodePrivKey, err := btcec.NewPrivateKey() + require.NoError(t, err) + + bobNodeID := bobNodePrivKey.PubKey() + + bobBtcPrivKey, err := btcec.NewPrivateKey() + require.NoError(t, err) + + alice := &keyRing{ + nodePriv: aliceNodePrivKey, + nodePub: aliceNodePrivKey.PubKey(), + btcPriv: aliceBtcPrivKey, + btcPub: aliceBtcPrivKey.PubKey(), + } + + bob := &keyRing{ + nodePriv: bobNodePrivKey, + nodePub: bobNodePrivKey.PubKey(), + btcPriv: bobBtcPrivKey, + btcPub: bobBtcPrivKey.PubKey(), + } + + if bytes.Compare( + aliceNodeID.SerializeCompressed(), + bobNodeID.SerializeCompressed(), + ) != -1 { + + return bob, alice + } + + return alice, bob +} + +func buildUnsignedChanAnnouncement(node1, node2 *keyRing, + withBtcKeys bool) *lnwire.ChannelAnnouncement2 { + + var ann lnwire.ChannelAnnouncement2 + ann.ChainHash.Val = *chaincfg.MainNetParams.GenesisHash + features := lnwire.NewRawFeatureVector() + ann.Features.Val = *features + ann.ShortChannelID.Val = lnwire.ShortChannelID{ + BlockHeight: 1000, + TxIndex: 100, + TxPosition: 0, + } + ann.Capacity.Val = 100000 + + copy(ann.NodeID1.Val[:], node1.nodePub.SerializeCompressed()) + copy(ann.NodeID2.Val[:], node2.nodePub.SerializeCompressed()) + + if !withBtcKeys { + return &ann + } + + btcKey1Bytes := tlv.ZeroRecordT[tlv.TlvType12, [33]byte]() + btcKey2Bytes := tlv.ZeroRecordT[tlv.TlvType14, [33]byte]() + + copy(btcKey1Bytes.Val[:], node1.btcPub.SerializeCompressed()) + copy(btcKey2Bytes.Val[:], node2.btcPub.SerializeCompressed()) + + ann.BitcoinKey1 = tlv.SomeRecordT(btcKey1Bytes) + ann.BitcoinKey2 = tlv.SomeRecordT(btcKey2Bytes) + + return &ann +} From 580c10477f333b167d0732894eb0bf97f42fbcfd Mon Sep 17 00:00:00 2001 From: Elle Mouton Date: Fri, 29 Sep 2023 11:37:39 +0200 Subject: [PATCH 20/22] lnwire: add ChannelUpdate2 Add the new ChannelUpdate2 message and ensure that it implements the ChannelUpdate interface. --- lnwire/channel_update_2.go | 441 +++++++++++++++++++++++++++++++++++++ lnwire/lnwire_test.go | 83 +++++++ lnwire/message.go | 5 + lnwire/msat.go | 39 ++++ 4 files changed, 568 insertions(+) create mode 100644 lnwire/channel_update_2.go diff --git a/lnwire/channel_update_2.go b/lnwire/channel_update_2.go new file mode 100644 index 0000000000..79a76aad61 --- /dev/null +++ b/lnwire/channel_update_2.go @@ -0,0 +1,441 @@ +package lnwire + +import ( + "bytes" + "fmt" + "io" + + "github.com/btcsuite/btcd/chaincfg" + "github.com/btcsuite/btcd/chaincfg/chainhash" + "github.com/lightningnetwork/lnd/tlv" +) + +const ( + defaultCltvExpiryDelta = uint16(80) + defaultHtlcMinMsat = MilliSatoshi(1) + defaultFeeBaseMsat = uint32(1000) + defaultFeeProportionalMillionths = uint32(1) +) + +// ChannelUpdate2 message is used after taproot channel has been initially +// announced. Each side independently announces its fees and minimum expiry for +// HTLCs and other parameters. This message is also used to redeclare initially +// set channel parameters. +type ChannelUpdate2 struct { + // Signature is used to validate the announced data and prove the + // ownership of node id. + Signature Sig + + // ChainHash denotes the target chain that this channel was opened + // within. This value should be the genesis hash of the target chain. + // Along with the short channel ID, this uniquely identifies the + // channel globally in a blockchain. + ChainHash tlv.RecordT[tlv.TlvType0, chainhash.Hash] + + // ShortChannelID is the unique description of the funding transaction. + ShortChannelID tlv.RecordT[tlv.TlvType2, ShortChannelID] + + // BlockHeight allows ordering in the case of multiple announcements. We + // should ignore the message if block height is not greater than the + // last-received. The block height must always be greater or equal to + // the block height that the channel funding transaction was confirmed + // in. + BlockHeight tlv.RecordT[tlv.TlvType4, uint32] + + // DisabledFlags is an optional bitfield that describes various reasons + // that the node is communicating that the channel should be considered + // disabled. + DisabledFlags tlv.RecordT[tlv.TlvType6, ChanUpdateDisableFlags] + + // SecondPeer is used to indicate which node the channel node has + // created and signed this message. If this field is present, it was + // node 2 otherwise it was node 1. + SecondPeer tlv.OptionalRecordT[tlv.TlvType8, TrueBoolean] + + // CLTVExpiryDelta is the minimum number of blocks this node requires to + // be added to the expiry of HTLCs. This is a security parameter + // determined by the node operator. This value represents the required + // gap between the time locks of the incoming and outgoing HTLC's set + // to this node. + CLTVExpiryDelta tlv.RecordT[tlv.TlvType10, uint16] + + // HTLCMinimumMsat is the minimum HTLC value which will be accepted. + HTLCMinimumMsat tlv.RecordT[tlv.TlvType12, MilliSatoshi] + + // HtlcMaximumMsat is the maximum HTLC value which will be accepted. + HTLCMaximumMsat tlv.RecordT[tlv.TlvType14, MilliSatoshi] + + // FeeBaseMsat is the base fee that must be used for incoming HTLC's to + // this particular channel. This value will be tacked onto the required + // for a payment independent of the size of the payment. + FeeBaseMsat tlv.RecordT[tlv.TlvType16, uint32] + + // FeeProportionalMillionths is the fee rate that will be charged per + // millionth of a satoshi. + FeeProportionalMillionths tlv.RecordT[tlv.TlvType18, uint32] + + // ExtraOpaqueData is the set of data that was appended to this message + // to fill out the full maximum transport message size. These fields can + // be used to specify optional data such as custom TLV fields. + ExtraOpaqueData ExtraOpaqueData +} + +// Decode deserializes a serialized ChannelUpdate2 stored in the passed +// io.Reader observing the specified protocol version. +// +// This is part of the lnwire.Message interface. +func (c *ChannelUpdate2) Decode(r io.Reader, _ uint32) error { + err := ReadElement(r, &c.Signature) + if err != nil { + return err + } + c.Signature.ForceSchnorr() + + return c.DecodeTLVRecords(r) +} + +// DecodeTLVRecords decodes only the TLV section of the message. +func (c *ChannelUpdate2) DecodeTLVRecords(r io.Reader) error { + // First extract into extra opaque data. + var tlvRecords ExtraOpaqueData + if err := ReadElements(r, &tlvRecords); err != nil { + return err + } + + var ( + chainHash = tlv.ZeroRecordT[tlv.TlvType0, [32]byte]() + secondPeer = tlv.ZeroRecordT[tlv.TlvType8, TrueBoolean]() + ) + typeMap, err := tlvRecords.ExtractRecords( + &chainHash, &c.ShortChannelID, &c.BlockHeight, &c.DisabledFlags, + &secondPeer, &c.CLTVExpiryDelta, &c.HTLCMinimumMsat, + &c.HTLCMaximumMsat, &c.FeeBaseMsat, + &c.FeeProportionalMillionths, + ) + if err != nil { + return err + } + + // By default, the chain-hash is the bitcoin mainnet genesis block hash. + c.ChainHash.Val = *chaincfg.MainNetParams.GenesisHash + if _, ok := typeMap[c.ChainHash.TlvType()]; ok { + c.ChainHash.Val = chainHash.Val + } + + // The presence of the second_peer tlv type indicates "true". + if _, ok := typeMap[c.SecondPeer.TlvType()]; ok { + c.SecondPeer = tlv.SomeRecordT(secondPeer) + } + + // If the CLTV expiry delta was not encoded, then set it to the default + // value. + if _, ok := typeMap[c.CLTVExpiryDelta.TlvType()]; !ok { + c.CLTVExpiryDelta.Val = defaultCltvExpiryDelta + } + + // If the HTLC Minimum msat was not encoded, then set it to the default + // value. + if _, ok := typeMap[c.HTLCMinimumMsat.TlvType()]; !ok { + c.HTLCMinimumMsat.Val = defaultHtlcMinMsat + } + + // If the base fee was not encoded, then set it to the default value. + if _, ok := typeMap[c.FeeBaseMsat.TlvType()]; !ok { + c.FeeBaseMsat.Val = defaultFeeBaseMsat + } + + // If the proportional fee was not encoded, then set it to the default + // value. + if _, ok := typeMap[c.FeeProportionalMillionths.TlvType()]; !ok { + c.FeeProportionalMillionths.Val = defaultFeeProportionalMillionths //nolint:lll + } + + if len(tlvRecords) != 0 { + c.ExtraOpaqueData = tlvRecords + } + + return nil +} + +// Encode serializes the target ChannelUpdate2 into the passed io.Writer +// observing the protocol version specified. +// +// This is part of the lnwire.Message interface. +func (c *ChannelUpdate2) Encode(w *bytes.Buffer, _ uint32) error { + _, err := w.Write(c.Signature.RawBytes()) + if err != nil { + return err + } + + _, err = c.DataToSign() + if err != nil { + return err + } + + return WriteBytes(w, c.ExtraOpaqueData) +} + +// DataToSign is used to retrieve part of the announcement message which should +// be signed. For the ChannelUpdate2 message, this includes the serialised TLV +// records. +func (c *ChannelUpdate2) DataToSign() ([]byte, error) { + // The chain-hash record is only included if it is _not_ equal to the + // bitcoin mainnet genisis block hash. + var recordProducers []tlv.RecordProducer + if !c.ChainHash.Val.IsEqual(chaincfg.MainNetParams.GenesisHash) { + hash := tlv.ZeroRecordT[tlv.TlvType0, [32]byte]() + hash.Val = c.ChainHash.Val + + recordProducers = append(recordProducers, &hash) + } + + recordProducers = append(recordProducers, + &c.ShortChannelID, &c.BlockHeight, + ) + + // Only include the disable flags if any bit is set. + if !c.DisabledFlags.Val.IsEnabled() { + recordProducers = append(recordProducers, &c.DisabledFlags) + } + + // We only need to encode the second peer boolean if it is true + c.SecondPeer.WhenSome(func(r tlv.RecordT[tlv.TlvType8, TrueBoolean]) { + recordProducers = append(recordProducers, &r) + }) + + // We only encode the cltv expiry delta if it is not equal to the + // default. + if c.CLTVExpiryDelta.Val != defaultCltvExpiryDelta { + recordProducers = append(recordProducers, &c.CLTVExpiryDelta) + } + + if c.HTLCMinimumMsat.Val != defaultHtlcMinMsat { + recordProducers = append(recordProducers, &c.HTLCMinimumMsat) + } + + recordProducers = append(recordProducers, &c.HTLCMaximumMsat) + + if c.FeeBaseMsat.Val != defaultFeeBaseMsat { + recordProducers = append(recordProducers, &c.FeeBaseMsat) + } + + if c.FeeProportionalMillionths.Val != defaultFeeProportionalMillionths { + recordProducers = append( + recordProducers, &c.FeeProportionalMillionths, + ) + } + + err := EncodeMessageExtraData(&c.ExtraOpaqueData, recordProducers...) + if err != nil { + return nil, err + } + + return c.ExtraOpaqueData, nil +} + +// MsgType returns the integer uniquely identifying this message type on the +// wire. +// +// This is part of the lnwire.Message interface. +func (c *ChannelUpdate2) MsgType() MessageType { + return MsgChannelUpdate2 +} + +func (c *ChannelUpdate2) ExtraData() ExtraOpaqueData { + return c.ExtraOpaqueData +} + +// A compile time check to ensure ChannelUpdate2 implements the +// lnwire.Message interface. +var _ Message = (*ChannelUpdate2)(nil) + +// SCID returns the ShortChannelID of the channel that the update applies to. +// +// NOTE: this is part of the ChannelUpdate interface. +func (c *ChannelUpdate2) SCID() ShortChannelID { + return c.ShortChannelID.Val +} + +// IsNode1 is true if the update was produced by node 1 of the channel peers. +// Node 1 is the node with the lexicographically smaller public key. +// +// NOTE: this is part of the ChannelUpdate interface. +func (c *ChannelUpdate2) IsNode1() bool { + return c.SecondPeer.IsNone() +} + +// IsDisabled is true if the update is announcing that the channel should be +// considered disabled. +// +// NOTE: this is part of the ChannelUpdate interface. +func (c *ChannelUpdate2) IsDisabled() bool { + return !c.DisabledFlags.Val.IsEnabled() +} + +// GetChainHash returns the hash of the chain that the message is referring to. +// +// NOTE: this is part of the ChannelUpdate interface. +func (c *ChannelUpdate2) GetChainHash() chainhash.Hash { + return c.ChainHash.Val +} + +// ForwardingPolicy returns the set of forwarding constraints of the update. +// +// NOTE: this is part of the ChannelUpdate interface. +func (c *ChannelUpdate2) ForwardingPolicy() *ForwardingPolicy { + return &ForwardingPolicy{ + TimeLockDelta: c.CLTVExpiryDelta.Val, + BaseFee: MilliSatoshi(c.FeeBaseMsat.Val), + FeeRate: MilliSatoshi(c.FeeProportionalMillionths.Val), + MinHTLC: c.HTLCMinimumMsat.Val, + HasMaxHTLC: true, + MaxHTLC: c.HTLCMaximumMsat.Val, + } +} + +// CmpAge can be used to determine if the update is older or newer than the +// passed update. It returns 1 if this update is newer, -1 if it is older, and +// 0 if they are the same age. +// +// NOTE: this is part of the ChannelUpdate interface. +func (c *ChannelUpdate2) CmpAge(update ChannelUpdate) (CompareResult, error) { + other, ok := update.(*ChannelUpdate2) + if !ok { + return 0, fmt.Errorf("expected *ChannelUpdate2, got: %T", + update) + } + + switch { + case c.BlockHeight.Val > other.BlockHeight.Val: + return GreaterThan, nil + case c.BlockHeight.Val < other.BlockHeight.Val: + return LessThan, nil + default: + return EqualTo, nil + } +} + +// SetDisabledFlag can be used to adjust the disabled flag of an update. +// +// NOTE: this is part of the ChannelUpdate interface. +func (c *ChannelUpdate2) SetDisabledFlag(disabled bool) { + if disabled { + c.DisabledFlags.Val |= ChanUpdateDisableIncoming + c.DisabledFlags.Val |= ChanUpdateDisableOutgoing + } else { + c.DisabledFlags.Val &^= ChanUpdateDisableIncoming + c.DisabledFlags.Val &^= ChanUpdateDisableOutgoing + } +} + +// SetSCID can be used to overwrite the SCID of the update. +// +// NOTE: this is part of the ChannelUpdate interface. +func (c *ChannelUpdate2) SetSCID(scid ShortChannelID) { + c.ShortChannelID.Val = scid +} + +// A compile time check to ensure ChannelUpdate2 implements the +// lnwire.ChannelUpdate interface. +var _ ChannelUpdate = (*ChannelUpdate2)(nil) + +// ChanUpdateDisableFlags is a bit vector that can be used to indicate various +// reasons for the channel being marked as disabled. +type ChanUpdateDisableFlags uint8 + +const ( + // ChanUpdateDisableIncoming is a bit indicates that a channel is + // disabled in the inbound direction meaning that the node broadcasting + // the update is communicating that they cannot receive funds. + ChanUpdateDisableIncoming ChanUpdateDisableFlags = 1 << iota + + // ChanUpdateDisableOutgoing is a bit indicates that a channel is + // disabled in the outbound direction meaning that the node broadcasting + // the update is communicating that they cannot send or route funds. + ChanUpdateDisableOutgoing = 2 +) + +// IncomingDisabled returns true if the ChanUpdateDisableIncoming bit is set. +func (c ChanUpdateDisableFlags) IncomingDisabled() bool { + return c&ChanUpdateDisableIncoming == ChanUpdateDisableIncoming +} + +// OutgoingDisabled returns true if the ChanUpdateDisableOutgoing bit is set. +func (c ChanUpdateDisableFlags) OutgoingDisabled() bool { + return c&ChanUpdateDisableOutgoing == ChanUpdateDisableOutgoing +} + +// IsEnabled returns true if none of the disable bits are set. +func (c ChanUpdateDisableFlags) IsEnabled() bool { + return c == 0 +} + +// String returns the bitfield flags as a string. +func (c ChanUpdateDisableFlags) String() string { + return fmt.Sprintf("%08b", c) +} + +// Record returns the tlv record for the disable flags. +func (c *ChanUpdateDisableFlags) Record() tlv.Record { + return tlv.MakeStaticRecord(0, c, 1, encodeDisableFlags, + decodeDisableFlags) +} + +func encodeDisableFlags(w io.Writer, val interface{}, buf *[8]byte) error { + if v, ok := val.(*ChanUpdateDisableFlags); ok { + flagsInt := uint8(*v) + + return tlv.EUint8(w, &flagsInt, buf) + } + + return tlv.NewTypeForEncodingErr(val, "lnwire.ChanUpdateDisableFlags") +} + +func decodeDisableFlags(r io.Reader, val interface{}, buf *[8]byte, + l uint64) error { + + if v, ok := val.(*ChanUpdateDisableFlags); ok { + var flagsInt uint8 + err := tlv.DUint8(r, &flagsInt, buf, l) + if err != nil { + return err + } + + *v = ChanUpdateDisableFlags(flagsInt) + + return nil + } + + return tlv.NewTypeForDecodingErr(val, "lnwire.ChanUpdateDisableFlags", + l, l) +} + +// TrueBoolean is a record that indicates true or false using the presence of +// the record. If the record is absent, it indicates false. If it is presence, +// it indicates true. +type TrueBoolean struct{} + +// Record returns the tlv record for the boolean entry. +func (b *TrueBoolean) Record() tlv.Record { + return tlv.MakeStaticRecord( + 0, b, 0, booleanEncoder, booleanDecoder, + ) +} + +func booleanEncoder(_ io.Writer, val interface{}, _ *[8]byte) error { + if _, ok := val.(*TrueBoolean); ok { + return nil + } + + return tlv.NewTypeForEncodingErr(val, "TrueBoolean") +} + +func booleanDecoder(_ io.Reader, val interface{}, _ *[8]byte, + l uint64) error { + + if _, ok := val.(*TrueBoolean); ok && (l == 0 || l == 1) { + return nil + } + + return tlv.NewTypeForEncodingErr(val, "TrueBoolean") +} diff --git a/lnwire/lnwire_test.go b/lnwire/lnwire_test.go index 2e74853e47..a5a7548068 100644 --- a/lnwire/lnwire_test.go +++ b/lnwire/lnwire_test.go @@ -1590,6 +1590,83 @@ func TestLightningWireProtocol(t *testing.T) { require.NoError(t, err) } + v[0] = reflect.ValueOf(req) + }, + MsgChannelUpdate2: func(v []reflect.Value, r *rand.Rand) { + req := ChannelUpdate2{ + Signature: testSchnorrSig, + ExtraOpaqueData: make([]byte, 0), + } + + req.ShortChannelID.Val = NewShortChanIDFromInt( + uint64(r.Int63()), + ) + req.BlockHeight.Val = r.Uint32() + req.HTLCMaximumMsat.Val = MilliSatoshi(r.Uint64()) + + // Sometimes set chain hash to bitcoin mainnet genesis + // hash. + req.ChainHash.Val = *chaincfg.MainNetParams.GenesisHash + if r.Int31()%2 == 0 { + _, err := r.Read(req.ChainHash.Val[:]) + require.NoError(t, err) + } + + // Sometimes use default htlc min msat. + req.HTLCMinimumMsat.Val = defaultHtlcMinMsat + if r.Int31()%2 == 0 { + req.HTLCMinimumMsat.Val = MilliSatoshi( + r.Uint64(), + ) + } + + // Sometimes set the cltv expiry delta to the default. + req.CLTVExpiryDelta.Val = defaultCltvExpiryDelta + if r.Int31()%2 == 0 { + req.CLTVExpiryDelta.Val = uint16(r.Int31()) + } + + // Sometimes use default fee base. + req.FeeBaseMsat.Val = defaultFeeBaseMsat + if r.Int31()%2 == 0 { + req.FeeBaseMsat.Val = r.Uint32() + } + + // Sometimes use default proportional fee. + req.FeeProportionalMillionths.Val = + defaultFeeProportionalMillionths + if r.Int31()%2 == 0 { + req.FeeProportionalMillionths.Val = r.Uint32() + } + + // Alternate between the two direction possibilities. + if r.Int31()%2 == 0 { + req.SecondPeer = tlv.SomeRecordT( + tlv.ZeroRecordT[tlv.TlvType8, TrueBoolean](), //nolint:lll + ) + } + + // Sometimes set the incoming disabled flag. + if r.Int31()%2 == 0 { + req.DisabledFlags.Val |= + ChanUpdateDisableIncoming + } + + // Sometimes set the outgoing disabled flag. + if r.Int31()%2 == 0 { + req.DisabledFlags.Val |= + ChanUpdateDisableOutgoing + } + + numExtraBytes := r.Int31n(1000) + if numExtraBytes > 0 { + req.ExtraOpaqueData = make( + []byte, numExtraBytes, + ) + _, err := r.Read(req.ExtraOpaqueData[:]) + require.NoError(t, err) + } + v[0] = reflect.ValueOf(req) }, } @@ -1832,6 +1909,12 @@ func TestLightningWireProtocol(t *testing.T) { return mainScenario(&m) }, }, + { + msgType: MsgChannelUpdate2, + scenario: func(m ChannelUpdate2) bool { + return mainScenario(&m) + }, + }, } for _, test := range tests { var config *quick.Config diff --git a/lnwire/message.go b/lnwire/message.go index b91db06792..a758db000d 100644 --- a/lnwire/message.go +++ b/lnwire/message.go @@ -59,6 +59,7 @@ const ( MsgReplyChannelRange = 264 MsgGossipTimestampRange = 265 MsgChannelAnnouncement2 = 267 + MsgChannelUpdate2 = 271 MsgKickoffSig = 777 ) @@ -161,6 +162,8 @@ func (t MessageType) String() string { return "MsgAnnounceSignatures2" case MsgChannelAnnouncement2: return "ChannelAnnouncement2" + case MsgChannelUpdate2: + return "ChannelUpdate2" default: return "" } @@ -294,6 +297,8 @@ func makeEmptyMessage(msgType MessageType) (Message, error) { msg = &AnnounceSignatures2{} case MsgChannelAnnouncement2: msg = &ChannelAnnouncement2{} + case MsgChannelUpdate2: + msg = &ChannelUpdate2{} default: // If the message is not within our custom range and has not // specifically been overridden, return an unknown message. diff --git a/lnwire/msat.go b/lnwire/msat.go index 7473d72c82..2966e5ddb2 100644 --- a/lnwire/msat.go +++ b/lnwire/msat.go @@ -2,8 +2,10 @@ package lnwire import ( "fmt" + "io" "github.com/btcsuite/btcd/btcutil" + "github.com/lightningnetwork/lnd/tlv" ) const ( @@ -49,3 +51,40 @@ func (m MilliSatoshi) String() string { } // TODO(roasbeef): extend with arithmetic operations? + +// Record returns a TLV record that can be used to encode/decode a MilliSatoshi +// to/from a TLV stream. +func (m *MilliSatoshi) Record() tlv.Record { + return tlv.MakeDynamicRecord( + 0, m, tlv.SizeBigSize(m), encodeMilliSatoshis, + decodeMilliSatoshis, + ) +} + +func encodeMilliSatoshis(w io.Writer, val interface{}, buf *[8]byte) error { + if v, ok := val.(*MilliSatoshi); ok { + bigSize := uint64(*v) + + return tlv.EBigSize(w, &bigSize, buf) + } + + return tlv.NewTypeForEncodingErr(val, "lnwire.MilliSatoshi") +} + +func decodeMilliSatoshis(r io.Reader, val interface{}, buf *[8]byte, + l uint64) error { + + if v, ok := val.(*MilliSatoshi); ok { + var bigSize uint64 + err := tlv.DBigSize(r, &bigSize, buf, l) + if err != nil { + return err + } + + *v = MilliSatoshi(bigSize) + + return nil + } + + return tlv.NewTypeForDecodingErr(val, "lnwire.MilliSatoshi", l, l) +} From a62201b61d055a2c46fe4998be4e816c7dec9024 Mon Sep 17 00:00:00 2001 From: Elle Mouton Date: Wed, 11 Sep 2024 14:05:33 +0200 Subject: [PATCH 21/22] netann: add chan update 2 validate and verify funcs --- netann/channel_update.go | 81 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 81 insertions(+) diff --git a/netann/channel_update.go b/netann/channel_update.go index 5495bc227b..af91abdd24 100644 --- a/netann/channel_update.go +++ b/netann/channel_update.go @@ -16,6 +16,20 @@ import ( "github.com/pkg/errors" ) +const ( + // chanUpdate2MsgName is a string representing the name of the + // ChannelUpdate2 message. This string will be used during the + // construction of the tagged hash message to be signed when producing + // the signature for the ChannelUpdate2 message. + chanUpdate2MsgName = "channel_update_2" + + // chanUpdate2SigField is the name of the signature field of the + // ChannelUpdate2 message. This string will be used during the + // construction of the tagged hash message to be signed when producing + // the signature for the ChannelUpdate2 message. + chanUpdate2SigField = "signature" +) + // ErrUnableToExtractChanUpdate is returned when a channel update cannot be // found for one of our active channels. var ErrUnableToExtractChanUpdate = fmt.Errorf("unable to extract ChannelUpdate") @@ -179,6 +193,8 @@ func VerifyChannelUpdateSignature(msg lnwire.ChannelUpdate, switch u := msg.(type) { case *lnwire.ChannelUpdate1: return verifyChannelUpdate1Signature(u, pubKey) + case *lnwire.ChannelUpdate2: + return verifyChannelUpdate2Signature(u, pubKey) default: return fmt.Errorf("unhandled implementation of "+ "lnwire.ChannelUpdate: %T", msg) @@ -209,6 +225,29 @@ func verifyChannelUpdate1Signature(msg *lnwire.ChannelUpdate1, return nil } +// verifyChannelUpdateSignature2 verifies that the channel update message was +// signed by the party with the given node public key. +func verifyChannelUpdate2Signature(c *lnwire.ChannelUpdate2, + pubKey *btcec.PublicKey) error { + + digest, err := chanUpdate2DigestToSign(c) + if err != nil { + return fmt.Errorf("unable to reconstruct message data: %w", err) + } + + nodeSig, err := c.Signature.ToSignature() + if err != nil { + return err + } + + if !nodeSig.Verify(digest, pubKey) { + return fmt.Errorf("invalid signature for channel update %v", + spew.Sdump(c)) + } + + return nil +} + // ValidateChannelUpdateFields validates a channel update's message flags and // corresponding update fields. func ValidateChannelUpdateFields(capacity btcutil.Amount, @@ -217,6 +256,8 @@ func ValidateChannelUpdateFields(capacity btcutil.Amount, switch u := msg.(type) { case *lnwire.ChannelUpdate1: return validateChannelUpdate1Fields(capacity, u) + case *lnwire.ChannelUpdate2: + return validateChannelUpdate2Fields(capacity, u) default: return fmt.Errorf("unhandled implementation of "+ "lnwire.ChannelUpdate: %T", msg) @@ -251,3 +292,43 @@ func validateChannelUpdate1Fields(capacity btcutil.Amount, return nil } + +// validateChannelUpdate2Fields validates a channel update's message flags and +// corresponding update fields. +func validateChannelUpdate2Fields(capacity btcutil.Amount, + c *lnwire.ChannelUpdate2) error { + + maxHtlc := c.HTLCMaximumMsat.Val + if maxHtlc == 0 || maxHtlc < c.HTLCMinimumMsat.Val { + return fmt.Errorf("invalid max htlc for channel update %v", + spew.Sdump(c)) + } + + // Checking whether the MaxHTLC value respects the channel's capacity. + capacityMsat := lnwire.NewMSatFromSatoshis(capacity) + if maxHtlc > capacityMsat { + return fmt.Errorf("max_htlc (%v) for channel update greater "+ + "than capacity (%v)", maxHtlc, capacityMsat) + } + + return nil +} + +// ChanUpdate2DigestTag returns the tag to be used when signing the digest of +// a channel_update_2 message. +func ChanUpdate2DigestTag() []byte { + return MsgTag(chanUpdate2MsgName, chanUpdate2SigField) +} + +// chanUpdate2DigestToSign computes the digest of the ChannelUpdate2 message to +// be signed. +func chanUpdate2DigestToSign(c *lnwire.ChannelUpdate2) ([]byte, error) { + data, err := c.DataToSign() + if err != nil { + return nil, err + } + + hash := MsgHash(chanUpdate2MsgName, chanUpdate2SigField, data) + + return hash[:], nil +} From 077273e66e79a4b710ff8136c5cbbcc35a1bbe54 Mon Sep 17 00:00:00 2001 From: Elle Mouton Date: Wed, 21 Aug 2024 09:50:46 +0200 Subject: [PATCH 22/22] docs: update release notes --- docs/release-notes/release-notes-0.19.0.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/release-notes/release-notes-0.19.0.md b/docs/release-notes/release-notes-0.19.0.md index bda488095a..8d3a908fd3 100644 --- a/docs/release-notes/release-notes-0.19.0.md +++ b/docs/release-notes/release-notes-0.19.0.md @@ -52,6 +52,9 @@ # Technical and Architectural Updates ## BOLT Spec Updates +* Add new [lnwire](https://github.com/lightningnetwork/lnd/pull/8044) messages + for the Gossip 1.75 protocol. + ## Testing ## Database