diff --git a/channeldb/waitingproof.go b/channeldb/waitingproof.go index c6b2b9df5..faefc620c 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 1a00c829e..d7113d9e7 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/chan_series.go b/discovery/chan_series.go index 8cbca1277..b7b9af989 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" @@ -61,7 +60,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 @@ -136,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) @@ -145,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) @@ -326,7 +326,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 +335,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 84fae767f..a0d3d0d23 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 @@ -322,7 +326,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. @@ -849,9 +853,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 +1015,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) @@ -1035,7 +1039,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 +1051,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 @@ -1416,7 +1428,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, ) @@ -1582,10 +1594,10 @@ 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.ChannelAnnouncement: + case *lnwire.ChannelAnnouncement1: scid = m.ShortChannelID.ToUint64() default: @@ -1844,7 +1856,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 @@ -1878,7 +1890,7 @@ func (d *AuthenticatedGossiper) processRejectedEdge( if err != nil { return nil, err } - err = graph.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", @@ -1922,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, @@ -2029,19 +2048,19 @@ 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 // 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 // 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: @@ -2058,7 +2077,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. @@ -2081,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) @@ -2129,7 +2148,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, ) @@ -2151,7 +2170,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 @@ -2195,8 +2214,8 @@ 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, - *lnwire.ChannelUpdate, error) { + edge *models.ChannelEdgePolicy) (*lnwire.ChannelAnnouncement1, + *lnwire.ChannelUpdate1, error) { // Parse the unsigned edge into a channel update. chanUpdate := netann.UnsignedChannelUpdateFromEdge(info, edge) @@ -2218,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 { @@ -2234,10 +2253,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, @@ -2284,7 +2303,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. @@ -2409,18 +2428,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()) @@ -2521,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 := graph.ValidateChannelAnn(ann); err != nil { + err := netann.ValidateChannelAnn(ann, d.fetchPKScript) + if err != nil { err := fmt.Errorf("unable to validate announcement: "+ "%v", err) @@ -2753,7 +2773,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()) @@ -2788,7 +2808,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 @@ -2796,7 +2816,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, ", @@ -3009,7 +3029,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", @@ -3200,7 +3220,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 @@ -3415,7 +3435,8 @@ 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 { + 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/discovery/gossiper_test.go b/discovery/gossiper_test.go index c7cb149cf..db632cdaf 100644 --- a/discovery/gossiper_test.go +++ b/discovery/gossiper_test.go @@ -476,13 +476,13 @@ type annBatch struct { nodeAnn1 *lnwire.NodeAnnouncement nodeAnn2 *lnwire.NodeAnnouncement - chanAnn *lnwire.ChannelAnnouncement + chanAnn *lnwire.ChannelAnnouncement1 - chanUpdAnn1 *lnwire.ChannelUpdate - chanUpdAnn2 *lnwire.ChannelUpdate + chanUpdAnn1 *lnwire.ChannelUpdate1 + chanUpdAnn2 *lnwire.ChannelUpdate1 - 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, }, @@ -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 { @@ -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...) @@ -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 @@ -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 } @@ -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") @@ -1867,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, @@ -2811,9 +2812,9 @@ 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: + case *lnwire.ChannelUpdate1: chanUpd++ case *lnwire.NodeAnnouncement: nodeAnn++ @@ -3246,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 { @@ -3300,9 +3301,9 @@ func TestSendChannelUpdateReliably(t *testing.T) { } switch msg := msg.(type) { - case *lnwire.ChannelUpdate: + case *lnwire.ChannelUpdate1: assertMessage(t, staleChannelUpdate, msg) - case *lnwire.AnnounceSignatures: + case *lnwire.AnnounceSignatures1: assertMessage(t, batch.localProofAnn, msg) default: t.Fatalf("send unexpected %v message", msg.MsgType()) @@ -3504,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) @@ -3528,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) @@ -3552,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)) @@ -3884,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 10fa51623..025a89d67 100644 --- a/discovery/message_store.go +++ b/discovery/message_store.go @@ -83,9 +83,9 @@ 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: + 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 e812c3f1a..36c082e36 100644 --- a/discovery/message_store_test.go +++ b/discovery/message_store_test.go @@ -52,15 +52,15 @@ 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), } } -func randChannelUpdate() *lnwire.ChannelUpdate { - return &lnwire.ChannelUpdate{ +func randChannelUpdate() *lnwire.ChannelUpdate1 { + return &lnwire.ChannelUpdate1{ ShortChannelID: lnwire.NewShortChanIDFromInt(rand.Uint64()), ExtraOpaqueData: make([]byte, 0), } @@ -116,9 +116,9 @@ 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: + 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 cdb041d99..3043b39cd 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 } @@ -1447,7 +1449,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] @@ -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 bb6aec590..0ee635a0f 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 @@ -306,36 +306,36 @@ func TestGossipSyncerFilterGossipMsgsAllInMemory(t *testing.T) { }, { // Ann tuple below horizon. - msg: &lnwire.ChannelAnnouncement{ + msg: &lnwire.ChannelAnnouncement1{ ShortChannelID: lnwire.NewShortChanIDFromInt(10), }, }, { - msg: &lnwire.ChannelUpdate{ + msg: &lnwire.ChannelUpdate1{ ShortChannelID: lnwire.NewShortChanIDFromInt(10), Timestamp: unixStamp(5), }, }, { // Ann tuple above horizon. - msg: &lnwire.ChannelAnnouncement{ + msg: &lnwire.ChannelAnnouncement1{ ShortChannelID: lnwire.NewShortChanIDFromInt(15), }, }, { - msg: &lnwire.ChannelUpdate{ + msg: &lnwire.ChannelUpdate1{ ShortChannelID: lnwire.NewShortChanIDFromInt(15), Timestamp: unixStamp(25002), }, }, { // Ann tuple beyond horizon. - msg: &lnwire.ChannelAnnouncement{ + msg: &lnwire.ChannelAnnouncement1{ ShortChannelID: lnwire.NewShortChanIDFromInt(20), }, }, { - msg: &lnwire.ChannelUpdate{ + msg: &lnwire.ChannelUpdate1{ ShortChannelID: lnwire.NewShortChanIDFromInt(20), Timestamp: unixStamp(999999), }, @@ -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), }, }, @@ -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), }, @@ -706,10 +706,10 @@ func TestGossipSyncerReplyShortChanIDs(t *testing.T) { } queryReply := []lnwire.Message{ - &lnwire.ChannelAnnouncement{ + &lnwire.ChannelAnnouncement1{ ShortChannelID: lnwire.NewShortChanIDFromInt(20), }, - &lnwire.ChannelUpdate{ + &lnwire.ChannelUpdate1{ ShortChannelID: lnwire.NewShortChanIDFromInt(20), Timestamp: unixStamp(999999), }, diff --git a/docs/release-notes/release-notes-0.19.0.md b/docs/release-notes/release-notes-0.19.0.md index bda488095..8d3a908fd 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 diff --git a/funding/manager.go b/funding/manager.go index fb36bd990..3af8ad104 100644 --- a/funding/manager.go +++ b/funding/manager.go @@ -4143,9 +4143,9 @@ 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 - chanUpdateAnn *lnwire.ChannelUpdate - chanProof *lnwire.AnnounceSignatures + chanAnn *lnwire.ChannelAnnouncement1 + chanUpdateAnn *lnwire.ChannelUpdate1 + chanProof *lnwire.AnnounceSignatures1 } // newChanAnnouncement creates the authenticated channel announcement messages @@ -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, @@ -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()), @@ -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 d1aa9ec14..dce8475ad 100644 --- a/funding/manager_test.go +++ b/funding/manager_test.go @@ -1208,9 +1208,9 @@ 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: + case *lnwire.ChannelUpdate1: // The channel update sent by the node should // advertise the MinHTLC value required by the @@ -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/go.mod b/go.mod index 65a4f991f..4aeea3808 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 3936b4652..3c93d06e5 100644 --- a/graph/ann_validation.go +++ b/graph/ann_validation.go @@ -2,89 +2,13 @@ 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" ) -// 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 { - // 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. @@ -121,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.ChannelUpdate) 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.ChannelUpdate, - 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.ChannelUpdate) 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 717d3e5ad..8b8b40d64 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" @@ -1231,7 +1232,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 +1451,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 { @@ -1521,7 +1461,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) @@ -1545,7 +1485,7 @@ func (b *Builder) ApplyChannelUpdate(msg *lnwire.ChannelUpdate) 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/graph/validation_barrier.go b/graph/validation_barrier.go index 731852d75..98d910d89 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 @@ -146,14 +146,14 @@ 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 return case *channeldb.LightningNode: return - case *lnwire.AnnounceSignatures: + case *lnwire.AnnounceSignatures1: // TODO(roasbeef): need to wait on chan ann? return } @@ -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", @@ -216,10 +216,10 @@ 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: + 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 { @@ -295,13 +295,13 @@ 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) delete(v.chanEdgeDependencies, shortID) - case *lnwire.AnnounceSignatures: + case *lnwire.AnnounceSignatures1: return } } diff --git a/graph/validation_barrier_test.go b/graph/validation_barrier_test.go index da404443f..38fc7a087 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)), @@ -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 4e6384b97..ca653a326 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 83495f357..5e5bc8dc2 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 53a084209..aa5fd6e4e 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 6d27a5be9..6123c9010 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 01596ade0..4ff2746a7 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 0bc0df2d4..825ee6c65 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 a71577ef2..abd48e806 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 6694e8b4f..1a428e5bd 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/lnwallet/interface.go b/lnwallet/interface.go index d3b123509..2a4462462 100644 --- a/lnwallet/interface.go +++ b/lnwallet/interface.go @@ -21,6 +21,8 @@ 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" ) const ( @@ -671,3 +673,91 @@ 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 +} + +// 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/lnwire/announcement_signatures.go b/lnwire/announcement_signatures.go index 49b610611..a2bc21f39 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,19 @@ 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 +// 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 *AnnounceSignatures) Decode(r io.Reader, pver uint32) error { +func (a *AnnounceSignatures1) Decode(r io.Reader, _ uint32) error { return ReadElements(r, &a.ChannelID, &a.ShortChannelID, @@ -61,11 +65,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, _ uint32) error { if err := WriteChannelID(w, a.ChannelID); err != nil { return err } @@ -89,6 +93,20 @@ 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 } + +// 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/announcement_signatures_2.go b/lnwire/announcement_signatures_2.go new file mode 100644 index 000000000..a10447032 --- /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/channel_announcement.go b/lnwire/channel_announcement.go index 2b34c0f99..ed4c5b97e 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) @@ -184,3 +184,38 @@ func (a *ChannelAnnouncement) 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/channel_announcement_2.go b/lnwire/channel_announcement_2.go new file mode 100644 index 000000000..074e7d084 --- /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/channel_ready.go b/lnwire/channel_ready.go index bdcb95ce8..912a068bd 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/channel_update.go b/lnwire/channel_update.go index 7f42a58b4..33cc3bff0 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) @@ -279,3 +279,91 @@ func (a *ChannelUpdate) 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/channel_update_2.go b/lnwire/channel_update_2.go new file mode 100644 index 000000000..79a76aad6 --- /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/features.go b/lnwire/features.go index 437a50dc2..c5ae014f7 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 diff --git a/lnwire/gossip_timestamp_range.go b/lnwire/gossip_timestamp_range.go index d2dbddecb..7b628752a 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/interfaces.go b/lnwire/interfaces.go new file mode 100644 index 000000000..3a8b7cbdf --- /dev/null +++ b/lnwire/interfaces.go @@ -0,0 +1,121 @@ +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 { + // SCID returns the ShortChannelID of the channel. + SCID() ShortChannelID + + // ChanID returns the ChannelID identifying the channel. + ChanID() ChannelID + + 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 +} + +// 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 +} diff --git a/lnwire/lnwire.go b/lnwire/lnwire.go index 8ab082b0b..4d2b814e5 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 7eb434f45..a5a754806 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) + shaHash1Bytes, _ = hex.DecodeString("e3b0c44298fc1c149afbf4c8996fb" + + "92427ae41e4649b934ca495991b7852b855") - 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) + 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) { @@ -730,18 +737,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 +753,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) { @@ -979,8 +999,14 @@ func TestLightningWireProtocol(t *testing.T) { }, MsgChannelAnnouncement: func(v []reflect.Value, r *rand.Rand) { var err error - req := ChannelAnnouncement{ - ShortChannelID: NewShortChanIDFromInt(uint64(r.Int63())), + req := ChannelAnnouncement1{ + ShortChannelID: NewShortChanIDFromInt( + uint64(r.Int63()), + ), + NodeID1: randRawKey(t), + NodeID2: randRawKey(t), + BitcoinKey1: randRawKey(t), + BitcoinKey2: randRawKey(t), Features: randRawFeatureVector(r), ExtraOpaqueData: make([]byte, 0), } @@ -1005,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 @@ -1046,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), @@ -1062,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) @@ -1100,8 +1101,10 @@ func TestLightningWireProtocol(t *testing.T) { maxHtlc = 0 } - req := ChannelUpdate{ - ShortChannelID: NewShortChanIDFromInt(uint64(r.Int63())), + req := ChannelUpdate1{ + ShortChannelID: NewShortChanIDFromInt( + uint64(r.Int63()), + ), Timestamp: uint32(r.Int31()), MessageFlags: msgFlags, ChannelFlags: ChanUpdateChanFlags(r.Int31()), @@ -1138,7 +1141,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), } @@ -1202,6 +1205,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), @@ -1467,6 +1499,176 @@ 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) + }, + 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) + }, + 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) + }, } // With the above types defined, we'll now generate a slice of @@ -1631,7 +1833,7 @@ func TestLightningWireProtocol(t *testing.T) { }, { msgType: MsgChannelAnnouncement, - scenario: func(m ChannelAnnouncement) bool { + scenario: func(m ChannelAnnouncement1) bool { return mainScenario(&m) }, }, @@ -1643,13 +1845,13 @@ func TestLightningWireProtocol(t *testing.T) { }, { msgType: MsgChannelUpdate, - scenario: func(m ChannelUpdate) bool { + scenario: func(m ChannelUpdate1) bool { return mainScenario(&m) }, }, { msgType: MsgAnnounceSignatures, - scenario: func(m AnnounceSignatures) bool { + scenario: func(m AnnounceSignatures1) bool { return mainScenario(&m) }, }, @@ -1695,6 +1897,24 @@ func TestLightningWireProtocol(t *testing.T) { return mainScenario(&m) }, }, + { + msgType: MsgAnnounceSignatures2, + scenario: func(m AnnounceSignatures2) bool { + return mainScenario(&m) + }, + }, + { + msgType: MsgChannelAnnouncement2, + scenario: func(m ChannelAnnouncement2) bool { + 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 2bf64a313..a758db000 100644 --- a/lnwire/message.go +++ b/lnwire/message.go @@ -52,11 +52,14 @@ const ( MsgNodeAnnouncement = 257 MsgChannelUpdate = 258 MsgAnnounceSignatures = 259 + MsgAnnounceSignatures2 = 260 MsgQueryShortChanIDs = 261 MsgReplyShortChanIDsEnd = 262 MsgQueryChannelRange = 263 MsgReplyChannelRange = 264 MsgGossipTimestampRange = 265 + MsgChannelAnnouncement2 = 267 + MsgChannelUpdate2 = 271 MsgKickoffSig = 777 ) @@ -155,6 +158,12 @@ func (t MessageType) String() string { return "ClosingComplete" case MsgClosingSig: return "ClosingSig" + case MsgAnnounceSignatures2: + return "MsgAnnounceSignatures2" + case MsgChannelAnnouncement2: + return "ChannelAnnouncement2" + case MsgChannelUpdate2: + return "ChannelUpdate2" default: return "" } @@ -259,15 +268,15 @@ func makeEmptyMessage(msgType MessageType) (Message, error) { case MsgError: msg = &Error{} case MsgChannelAnnouncement: - msg = &ChannelAnnouncement{} + msg = &ChannelAnnouncement1{} case MsgChannelUpdate: - msg = &ChannelUpdate{} + msg = &ChannelUpdate1{} case MsgNodeAnnouncement: msg = &NodeAnnouncement{} case MsgPing: msg = &Ping{} case MsgAnnounceSignatures: - msg = &AnnounceSignatures{} + msg = &AnnounceSignatures1{} case MsgPong: msg = &Pong{} case MsgQueryShortChanIDs: @@ -284,6 +293,12 @@ func makeEmptyMessage(msgType MessageType) (Message, error) { msg = &ClosingComplete{} case MsgClosingSig: msg = &ClosingSig{} + case MsgAnnounceSignatures2: + 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/message_test.go b/lnwire/message_test.go index bbb434785..d42d57911 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), @@ -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, @@ -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/lnwire/msat.go b/lnwire/msat.go index 7473d72c8..2966e5ddb 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) +} diff --git a/lnwire/onion_error.go b/lnwire/onion_error.go index f8a8bf069..8d54a9895 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 bc14d5d42..9c39be6d5 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 f1e6aa578..c4db4009d 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 1e64a53f8..c709a95f2 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 8fc040f6f..9644a523f 100644 --- a/netann/channel_announcement.go +++ b/netann/channel_announcement.go @@ -2,9 +2,30 @@ package netann import ( "bytes" + "errors" + "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 @@ -14,14 +35,14 @@ import ( // peer's initial routing table upon connect. func CreateChanAnnouncement(chanProof *models.ChannelAuthProof, chanInfo *models.ChannelEdgeInfo, - e1, e2 *models.ChannelEdgePolicy) (*lnwire.ChannelAnnouncement, - *lnwire.ChannelUpdate, *lnwire.ChannelUpdate, error) { + e1, e2 *models.ChannelEdgePolicy) (*lnwire.ChannelAnnouncement1, + *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 // authenticated channel announcement. chanID := lnwire.NewShortChanIDFromInt(chanInfo.ChannelID) - chanAnn := &lnwire.ChannelAnnouncement{ + chanAnn := &lnwire.ChannelAnnouncement1{ ShortChannelID: chanID, NodeID1: chanInfo.NodeKey1Bytes, NodeID2: chanInfo.NodeKey2Bytes, @@ -68,7 +89,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 { @@ -84,3 +105,187 @@ func CreateChanAnnouncement(chanProof *models.ChannelAuthProof, return chanAnn, edge1Ann, edge2Ann, nil } + +// 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 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) + } +} + +// 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 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 + // 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 +} + +// 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 92a6c74ce..61db16b16 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" ) @@ -24,7 +29,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, @@ -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 +} diff --git a/netann/channel_update.go b/netann/channel_update.go index b93deb1d0..af91abdd2 100644 --- a/netann/channel_update.go +++ b/netann/channel_update.go @@ -6,10 +6,28 @@ 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" +) + +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 @@ -18,12 +36,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 +57,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 +75,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 +104,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 +136,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 +156,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) @@ -152,3 +170,165 @@ 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.ChannelUpdate) 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.ChannelUpdate, + pubKey *btcec.PublicKey) error { + + 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) + } +} + +// 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() + 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 +} + +// 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, + msg lnwire.ChannelUpdate) error { + + 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) + } +} + +// 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. + 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 +} + +// 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 +} diff --git a/netann/channel_update_test.go b/netann/channel_update_test.go index 7af51effc..2a619e062 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" @@ -111,7 +110,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 +121,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, } @@ -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/netann/msg_hash.go b/netann/msg_hash.go new file mode 100644 index 000000000..3c4806f9b --- /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) +} diff --git a/netann/sign.go b/netann/sign.go index 429533136..0c7612eac 100644 --- a/netann/sign.go +++ b/netann/sign.go @@ -20,9 +20,9 @@ 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: + 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 3223e7f4b..5669193a2 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,10 +1963,10 @@ out: nextMsg.MsgType()) } - case *lnwire.ChannelUpdate, - *lnwire.ChannelAnnouncement, + case *lnwire.ChannelUpdate1, + *lnwire.ChannelAnnouncement1, *lnwire.NodeAnnouncement, - *lnwire.AnnounceSignatures, + *lnwire.AnnounceSignatures1, *lnwire.GossipTimestampRange, *lnwire.QueryShortChanIDs, *lnwire.QueryChannelRange, @@ -2225,15 +2225,15 @@ 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()) - case *lnwire.ChannelAnnouncement: + case *lnwire.ChannelAnnouncement1: 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 e0ae29be8..0575acca5 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 27391d53e..4a0f73871 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 28464a378..32f83420f 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 5e6a468fe..b47f6ee0c 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" ) @@ -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,11 +436,11 @@ 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. - 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, ) diff --git a/routing/payment_session_test.go b/routing/payment_session_test.go index 34f835682..d510f77f9 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 bf7d6d3ed..68b527e5a 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 db3fbc857..40b7ecb49 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 7726091fc..62b015abb 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 0f50794e7..695375400 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, @@ -1753,7 +1754,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 +4827,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 +4845,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 (