diff --git a/discovery/chan_series.go b/discovery/chan_series.go index ddea5104..ffb59b4e 100644 --- a/discovery/chan_series.go +++ b/discovery/chan_series.go @@ -6,6 +6,7 @@ import ( "github.com/btcsuite/btcd/chaincfg/chainhash" "github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/lnwire" + "github.com/lightningnetwork/lnd/netann" "github.com/lightningnetwork/lnd/routing/route" ) @@ -119,7 +120,7 @@ func (c *ChanSeries) UpdatesInHorizon(chain chainhash.Hash, continue } - chanAnn, edge1, edge2, err := CreateChanAnnouncement( + chanAnn, edge1, edge2, err := netann.CreateChanAnnouncement( channel.Info.AuthProof, channel.Info, channel.Policy1, channel.Policy2, ) @@ -258,7 +259,7 @@ func (c *ChanSeries) FetchChanAnns(chain chainhash.Hash, continue } - chanAnn, edge1, edge2, err := CreateChanAnnouncement( + chanAnn, edge1, edge2, err := netann.CreateChanAnnouncement( channel.Info.AuthProof, channel.Info, channel.Policy1, channel.Policy2, ) @@ -323,20 +324,7 @@ func (c *ChanSeries) FetchChanUpdates(chain chainhash.Hash, chanUpdates := make([]*lnwire.ChannelUpdate, 0, 2) if e1 != nil { - chanUpdate := &lnwire.ChannelUpdate{ - ChainHash: chanInfo.ChainHash, - ShortChannelID: shortChanID, - Timestamp: uint32(e1.LastUpdate.Unix()), - MessageFlags: e1.MessageFlags, - ChannelFlags: e1.ChannelFlags, - TimeLockDelta: e1.TimeLockDelta, - HtlcMinimumMsat: e1.MinHTLC, - HtlcMaximumMsat: e1.MaxHTLC, - BaseFee: uint32(e1.FeeBaseMSat), - FeeRate: uint32(e1.FeeProportionalMillionths), - ExtraOpaqueData: e1.ExtraOpaqueData, - } - chanUpdate.Signature, err = lnwire.NewSigFromRawSignature(e1.SigBytes) + chanUpdate, err := netann.ChannelUpdateFromEdge(chanInfo, e1) if err != nil { return nil, err } @@ -344,20 +332,7 @@ func (c *ChanSeries) FetchChanUpdates(chain chainhash.Hash, chanUpdates = append(chanUpdates, chanUpdate) } if e2 != nil { - chanUpdate := &lnwire.ChannelUpdate{ - ChainHash: chanInfo.ChainHash, - ShortChannelID: shortChanID, - Timestamp: uint32(e2.LastUpdate.Unix()), - MessageFlags: e2.MessageFlags, - ChannelFlags: e2.ChannelFlags, - TimeLockDelta: e2.TimeLockDelta, - HtlcMinimumMsat: e2.MinHTLC, - HtlcMaximumMsat: e2.MaxHTLC, - BaseFee: uint32(e2.FeeBaseMSat), - FeeRate: uint32(e2.FeeProportionalMillionths), - ExtraOpaqueData: e2.ExtraOpaqueData, - } - chanUpdate.Signature, err = lnwire.NewSigFromRawSignature(e2.SigBytes) + chanUpdate, err := netann.ChannelUpdateFromEdge(chanInfo, e2) if err != nil { return nil, err } diff --git a/discovery/gossiper.go b/discovery/gossiper.go index 8c9ed6e5..03139412 100644 --- a/discovery/gossiper.go +++ b/discovery/gossiper.go @@ -20,6 +20,7 @@ import ( "github.com/lightningnetwork/lnd/lnwallet" "github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/multimutex" + "github.com/lightningnetwork/lnd/netann" "github.com/lightningnetwork/lnd/routing" "github.com/lightningnetwork/lnd/routing/route" "github.com/lightningnetwork/lnd/ticker" @@ -350,15 +351,6 @@ func New(cfg Config, selfKey *btcec.PublicKey) *AuthenticatedGossiper { return gossiper } -// updatedChanPolicies is a set of channel policies that have been successfully -// updated and written to disk, or an error if the policy update failed. This -// struct's map field is intended to be used for updating channel policies on -// the link layer. -type updatedChanPolicies struct { - chanPolicies map[wire.OutPoint]*channeldb.ChannelEdgePolicy - err error -} - // EdgeWithInfo contains the information that is required to update an edge. type EdgeWithInfo struct { // Info describes the channel. @@ -1364,6 +1356,22 @@ func (d *AuthenticatedGossiper) processChanPolicyUpdate( return chanUpdates, nil } +// remotePubFromChanInfo returns the public key of the remote peer given a +// ChannelEdgeInfo that describe a channel we have with them. +func remotePubFromChanInfo(chanInfo *channeldb.ChannelEdgeInfo, + chanFlags lnwire.ChanUpdateChanFlags) [33]byte { + + var remotePubKey [33]byte + switch { + case chanFlags&lnwire.ChanUpdateDirection == 0: + remotePubKey = chanInfo.NodeKey2Bytes + case chanFlags&lnwire.ChanUpdateDirection == 1: + remotePubKey = chanInfo.NodeKey1Bytes + } + + return remotePubKey +} + // processRejectedEdge examines a rejected edge to see if we can extract any // new announcements from it. An edge will get rejected if we already added // the same edge without AuthProof to the graph. If the received announcement @@ -1400,7 +1408,7 @@ func (d *AuthenticatedGossiper) processRejectedEdge( // We'll then create then validate the new fully assembled // announcement. - chanAnn, e1Ann, e2Ann, err := CreateChanAnnouncement( + chanAnn, e1Ann, e2Ann, err := netann.CreateChanAnnouncement( proof, chanInfo, e1, e2, ) if err != nil { @@ -2157,7 +2165,7 @@ func (d *AuthenticatedGossiper) processNetworkAnnouncement( msg.ChannelID, peerID) - chanAnn, _, _, err := CreateChanAnnouncement( + chanAnn, _, _, err := netann.CreateChanAnnouncement( chanInfo.AuthProof, chanInfo, e1, e2, ) @@ -2240,7 +2248,7 @@ func (d *AuthenticatedGossiper) processNetworkAnnouncement( dbProof.BitcoinSig1Bytes = oppositeProof.BitcoinSignature.ToSignatureBytes() dbProof.BitcoinSig2Bytes = msg.BitcoinSignature.ToSignatureBytes() } - chanAnn, e1Ann, e2Ann, err := CreateChanAnnouncement( + chanAnn, e1Ann, e2Ann, err := netann.CreateChanAnnouncement( &dbProof, chanInfo, e1, e2, ) if err != nil { @@ -2445,42 +2453,23 @@ func (d *AuthenticatedGossiper) updateChannel(info *channeldb.ChannelEdgeInfo, edge *channeldb.ChannelEdgePolicy) (*lnwire.ChannelAnnouncement, *lnwire.ChannelUpdate, error) { - // Make sure timestamp is always increased, such that our update gets - // propagated. - timestamp := time.Now().Unix() - if timestamp <= edge.LastUpdate.Unix() { - timestamp = edge.LastUpdate.Unix() + 1 - } - edge.LastUpdate = time.Unix(timestamp, 0) + // Parse the unsigned edge into a channel update. + chanUpdate := netann.UnsignedChannelUpdateFromEdge(info, edge) - chanUpdate := &lnwire.ChannelUpdate{ - ChainHash: info.ChainHash, - ShortChannelID: lnwire.NewShortChanIDFromInt(edge.ChannelID), - Timestamp: uint32(timestamp), - MessageFlags: edge.MessageFlags, - ChannelFlags: edge.ChannelFlags, - TimeLockDelta: edge.TimeLockDelta, - HtlcMinimumMsat: edge.MinHTLC, - HtlcMaximumMsat: edge.MaxHTLC, - BaseFee: uint32(edge.FeeBaseMSat), - FeeRate: uint32(edge.FeeProportionalMillionths), - ExtraOpaqueData: edge.ExtraOpaqueData, - } - - // With the update applied, we'll generate a new signature over a - // digest of the channel announcement itself. - sig, err := SignAnnouncement(d.cfg.AnnSigner, d.selfKey, chanUpdate) + // We'll generate a new signature over a digest of the channel + // announcement itself and update the timestamp to ensure it propagate. + err := netann.SignChannelUpdate( + d.cfg.AnnSigner, d.selfKey, chanUpdate, + netann.ChanUpdSetTimestamp, + ) if err != nil { return nil, nil, err } // Next, we'll set the new signature in place, and update the reference // in the backing slice. - edge.SetSigBytes(sig.Serialize()) - chanUpdate.Signature, err = lnwire.NewSigFromSignature(sig) - if err != nil { - return nil, nil, err - } + edge.LastUpdate = time.Unix(int64(chanUpdate.Timestamp), 0) + edge.SigBytes = chanUpdate.Signature.ToSignatureBytes() // To ensure that our signature is valid, we'll verify it ourself // before committing it to the slice returned. diff --git a/discovery/gossiper_test.go b/discovery/gossiper_test.go index 4252397c..f927a960 100644 --- a/discovery/gossiper_test.go +++ b/discovery/gossiper_test.go @@ -26,6 +26,7 @@ import ( "github.com/lightningnetwork/lnd/lnpeer" "github.com/lightningnetwork/lnd/lntest/wait" "github.com/lightningnetwork/lnd/lnwire" + "github.com/lightningnetwork/lnd/netann" "github.com/lightningnetwork/lnd/routing" "github.com/lightningnetwork/lnd/routing/route" "github.com/lightningnetwork/lnd/ticker" @@ -550,7 +551,7 @@ func createNodeAnnouncement(priv *btcec.PrivateKey, } signer := mockSigner{priv} - sig, err := SignAnnouncement(&signer, priv.PubKey(), a) + sig, err := netann.SignAnnouncement(&signer, priv.PubKey(), a) if err != nil { return nil, err } @@ -602,7 +603,7 @@ func createUpdateAnnouncement(blockHeight uint32, func signUpdate(nodeKey *btcec.PrivateKey, a *lnwire.ChannelUpdate) error { pub := nodeKey.PubKey() signer := mockSigner{nodeKey} - sig, err := SignAnnouncement(&signer, pub, a) + sig, err := netann.SignAnnouncement(&signer, pub, a) if err != nil { return err } @@ -644,7 +645,7 @@ func createRemoteChannelAnnouncement(blockHeight uint32, pub := nodeKeyPriv1.PubKey() signer := mockSigner{nodeKeyPriv1} - sig, err := SignAnnouncement(&signer, pub, a) + sig, err := netann.SignAnnouncement(&signer, pub, a) if err != nil { return nil, err } @@ -655,7 +656,7 @@ func createRemoteChannelAnnouncement(blockHeight uint32, pub = nodeKeyPriv2.PubKey() signer = mockSigner{nodeKeyPriv2} - sig, err = SignAnnouncement(&signer, pub, a) + sig, err = netann.SignAnnouncement(&signer, pub, a) if err != nil { return nil, err } @@ -666,7 +667,7 @@ func createRemoteChannelAnnouncement(blockHeight uint32, pub = bitcoinKeyPriv1.PubKey() signer = mockSigner{bitcoinKeyPriv1} - sig, err = SignAnnouncement(&signer, pub, a) + sig, err = netann.SignAnnouncement(&signer, pub, a) if err != nil { return nil, err } @@ -677,7 +678,7 @@ func createRemoteChannelAnnouncement(blockHeight uint32, pub = bitcoinKeyPriv2.PubKey() signer = mockSigner{bitcoinKeyPriv2} - sig, err = SignAnnouncement(&signer, pub, a) + sig, err = netann.SignAnnouncement(&signer, pub, a) if err != nil { return nil, err } diff --git a/discovery/utils.go b/discovery/utils.go deleted file mode 100644 index ce211493..00000000 --- a/discovery/utils.go +++ /dev/null @@ -1,154 +0,0 @@ -package discovery - -import ( - "github.com/btcsuite/btcd/btcec" - "github.com/go-errors/errors" - "github.com/lightningnetwork/lnd/channeldb" - "github.com/lightningnetwork/lnd/lnwallet" - "github.com/lightningnetwork/lnd/lnwire" -) - -// CreateChanAnnouncement is a helper function which creates all channel -// announcements given the necessary channel related database items. This -// function is used to transform out database structs into the corresponding wire -// structs for announcing new channels to other peers, or simply syncing up a -// peer's initial routing table upon connect. -func CreateChanAnnouncement(chanProof *channeldb.ChannelAuthProof, - chanInfo *channeldb.ChannelEdgeInfo, - e1, e2 *channeldb.ChannelEdgePolicy) (*lnwire.ChannelAnnouncement, - *lnwire.ChannelUpdate, *lnwire.ChannelUpdate, error) { - - // First, using the parameters of the channel, along with the channel - // authentication chanProof, we'll create re-create the original - // authenticated channel announcement. - chanID := lnwire.NewShortChanIDFromInt(chanInfo.ChannelID) - chanAnn := &lnwire.ChannelAnnouncement{ - ShortChannelID: chanID, - NodeID1: chanInfo.NodeKey1Bytes, - NodeID2: chanInfo.NodeKey2Bytes, - ChainHash: chanInfo.ChainHash, - BitcoinKey1: chanInfo.BitcoinKey1Bytes, - BitcoinKey2: chanInfo.BitcoinKey2Bytes, - Features: lnwire.NewRawFeatureVector(), - ExtraOpaqueData: chanInfo.ExtraOpaqueData, - } - - var err error - chanAnn.BitcoinSig1, err = lnwire.NewSigFromRawSignature( - chanProof.BitcoinSig1Bytes, - ) - if err != nil { - return nil, nil, nil, err - } - chanAnn.BitcoinSig2, err = lnwire.NewSigFromRawSignature( - chanProof.BitcoinSig2Bytes, - ) - if err != nil { - return nil, nil, nil, err - } - chanAnn.NodeSig1, err = lnwire.NewSigFromRawSignature( - chanProof.NodeSig1Bytes, - ) - if err != nil { - return nil, nil, nil, err - } - chanAnn.NodeSig2, err = lnwire.NewSigFromRawSignature( - chanProof.NodeSig2Bytes, - ) - if err != nil { - return nil, nil, nil, err - } - - // We'll unconditionally queue the channel's existence chanProof as it - // will need to be processed before either of the channel update - // networkMsgs. - - // 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 - if e1 != nil { - edge1Ann = &lnwire.ChannelUpdate{ - ChainHash: chanInfo.ChainHash, - ShortChannelID: chanID, - Timestamp: uint32(e1.LastUpdate.Unix()), - MessageFlags: e1.MessageFlags, - ChannelFlags: e1.ChannelFlags, - TimeLockDelta: e1.TimeLockDelta, - HtlcMinimumMsat: e1.MinHTLC, - HtlcMaximumMsat: e1.MaxHTLC, - BaseFee: uint32(e1.FeeBaseMSat), - FeeRate: uint32(e1.FeeProportionalMillionths), - ExtraOpaqueData: e1.ExtraOpaqueData, - } - edge1Ann.Signature, err = lnwire.NewSigFromRawSignature(e1.SigBytes) - if err != nil { - return nil, nil, nil, err - } - } - if e2 != nil { - edge2Ann = &lnwire.ChannelUpdate{ - ChainHash: chanInfo.ChainHash, - ShortChannelID: chanID, - Timestamp: uint32(e2.LastUpdate.Unix()), - MessageFlags: e2.MessageFlags, - ChannelFlags: e2.ChannelFlags, - TimeLockDelta: e2.TimeLockDelta, - HtlcMinimumMsat: e2.MinHTLC, - HtlcMaximumMsat: e2.MaxHTLC, - BaseFee: uint32(e2.FeeBaseMSat), - FeeRate: uint32(e2.FeeProportionalMillionths), - ExtraOpaqueData: e2.ExtraOpaqueData, - } - edge2Ann.Signature, err = lnwire.NewSigFromRawSignature(e2.SigBytes) - if err != nil { - return nil, nil, nil, err - } - } - - return chanAnn, edge1Ann, edge2Ann, nil -} - -// SignAnnouncement is a helper function which is used to sign any outgoing -// channel node node announcement messages. -func SignAnnouncement(signer lnwallet.MessageSigner, pubKey *btcec.PublicKey, - msg lnwire.Message) (*btcec.Signature, error) { - - var ( - data []byte - err error - ) - - switch m := msg.(type) { - case *lnwire.ChannelAnnouncement: - data, err = m.DataToSign() - case *lnwire.ChannelUpdate: - data, err = m.DataToSign() - case *lnwire.NodeAnnouncement: - data, err = m.DataToSign() - default: - return nil, errors.New("can't sign message " + - "of this format") - } - if err != nil { - return nil, errors.Errorf("unable to get data to sign: %v", err) - } - - return signer.SignMessage(pubKey, data) -} - -// remotePubFromChanInfo returns the public key of the remote peer given a -// ChannelEdgeInfo that describe a channel we have with them. -func remotePubFromChanInfo(chanInfo *channeldb.ChannelEdgeInfo, - chanFlags lnwire.ChanUpdateChanFlags) [33]byte { - - var remotePubKey [33]byte - switch { - case chanFlags&lnwire.ChanUpdateDirection == 0: - remotePubKey = chanInfo.NodeKey2Bytes - case chanFlags&lnwire.ChanUpdateDirection == 1: - remotePubKey = chanInfo.NodeKey1Bytes - } - - return remotePubKey -} diff --git a/lnwire/node_announcement.go b/lnwire/node_announcement.go index 4ea64fa6..f0d897bc 100644 --- a/lnwire/node_announcement.go +++ b/lnwire/node_announcement.go @@ -101,14 +101,6 @@ type NodeAnnouncement struct { ExtraOpaqueData []byte } -// UpdateNodeAnnAddrs is a functional option that allows updating the addresses -// of the given node announcement. -func UpdateNodeAnnAddrs(addrs []net.Addr) func(*NodeAnnouncement) { - return func(nodeAnn *NodeAnnouncement) { - nodeAnn.Addresses = addrs - } -} - // A compile time check to ensure NodeAnnouncement implements the // lnwire.Message interface. var _ Message = (*NodeAnnouncement)(nil) diff --git a/netann/chan_status_manager.go b/netann/chan_status_manager.go index 146fef47..51797c87 100644 --- a/netann/chan_status_manager.go +++ b/netann/chan_status_manager.go @@ -527,7 +527,7 @@ func (m *ChanStatusManager) signAndSendNextUpdate(outpoint wire.OutPoint, err = SignChannelUpdate( m.cfg.MessageSigner, m.cfg.OurPubKey, chanUpdate, - ChannelUpdateSetDisable(disabled), + ChanUpdSetDisable(disabled), ChanUpdSetTimestamp, ) if err != nil { return err diff --git a/netann/channel_announcement.go b/netann/channel_announcement.go new file mode 100644 index 00000000..99f909e2 --- /dev/null +++ b/netann/channel_announcement.go @@ -0,0 +1,81 @@ +package netann + +import ( + "github.com/lightningnetwork/lnd/channeldb" + "github.com/lightningnetwork/lnd/lnwire" +) + +// CreateChanAnnouncement is a helper function which creates all channel +// announcements given the necessary channel related database items. This +// function is used to transform out database structs into the corresponding wire +// structs for announcing new channels to other peers, or simply syncing up a +// peer's initial routing table upon connect. +func CreateChanAnnouncement(chanProof *channeldb.ChannelAuthProof, + chanInfo *channeldb.ChannelEdgeInfo, + e1, e2 *channeldb.ChannelEdgePolicy) (*lnwire.ChannelAnnouncement, + *lnwire.ChannelUpdate, *lnwire.ChannelUpdate, error) { + + // First, using the parameters of the channel, along with the channel + // authentication chanProof, we'll create re-create the original + // authenticated channel announcement. + chanID := lnwire.NewShortChanIDFromInt(chanInfo.ChannelID) + chanAnn := &lnwire.ChannelAnnouncement{ + ShortChannelID: chanID, + NodeID1: chanInfo.NodeKey1Bytes, + NodeID2: chanInfo.NodeKey2Bytes, + ChainHash: chanInfo.ChainHash, + BitcoinKey1: chanInfo.BitcoinKey1Bytes, + BitcoinKey2: chanInfo.BitcoinKey2Bytes, + Features: lnwire.NewRawFeatureVector(), + ExtraOpaqueData: chanInfo.ExtraOpaqueData, + } + + var err error + chanAnn.BitcoinSig1, err = lnwire.NewSigFromRawSignature( + chanProof.BitcoinSig1Bytes, + ) + if err != nil { + return nil, nil, nil, err + } + chanAnn.BitcoinSig2, err = lnwire.NewSigFromRawSignature( + chanProof.BitcoinSig2Bytes, + ) + if err != nil { + return nil, nil, nil, err + } + chanAnn.NodeSig1, err = lnwire.NewSigFromRawSignature( + chanProof.NodeSig1Bytes, + ) + if err != nil { + return nil, nil, nil, err + } + chanAnn.NodeSig2, err = lnwire.NewSigFromRawSignature( + chanProof.NodeSig2Bytes, + ) + if err != nil { + return nil, nil, nil, err + } + + // We'll unconditionally queue the channel's existence chanProof as it + // will need to be processed before either of the channel update + // networkMsgs. + + // 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 + if e1 != nil { + edge1Ann, err = ChannelUpdateFromEdge(chanInfo, e1) + if err != nil { + return nil, nil, nil, err + } + } + if e2 != nil { + edge2Ann, err = ChannelUpdateFromEdge(chanInfo, e2) + if err != nil { + return nil, nil, nil, err + } + } + + return chanAnn, edge1Ann, edge2Ann, nil +} diff --git a/netann/channel_update.go b/netann/channel_update.go index c405a0c6..4423f4ec 100644 --- a/netann/channel_update.go +++ b/netann/channel_update.go @@ -15,9 +15,9 @@ import ( // lnwire.ChannelUpdate. type ChannelUpdateModifier func(*lnwire.ChannelUpdate) -// ChannelUpdateSetDisable sets the disabled channel flag if disabled is true, -// and clears the bit otherwise. -func ChannelUpdateSetDisable(disabled bool) ChannelUpdateModifier { +// 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) { if disabled { // Set the bit responsible for marking a channel as @@ -31,6 +31,20 @@ func ChannelUpdateSetDisable(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) { + newTimestamp := uint32(time.Now().Unix()) + if newTimestamp <= update.Timestamp { + // Increment the prior value to ensure the timestamp + // monotonically increases, otherwise the update won't + // propagate. + newTimestamp = update.Timestamp + 1 + } + update.Timestamp = newTimestamp +} + // SignChannelUpdate applies the given modifiers to the passed // lnwire.ChannelUpdate, then signs the resulting update. The provided update // should be the most recent, valid update, otherwise the timestamp may not @@ -45,23 +59,8 @@ func SignChannelUpdate(signer lnwallet.MessageSigner, pubKey *btcec.PublicKey, modifier(update) } - // Update the message's timestamp to the current time. If the update's - // current time is already in the future, we increment the prior value - // to ensure the timestamps monotonically increase, otherwise the - // update won't propagate. - newTimestamp := uint32(time.Now().Unix()) - if newTimestamp <= update.Timestamp { - newTimestamp = update.Timestamp + 1 - } - update.Timestamp = newTimestamp - - chanUpdateMsg, err := update.DataToSign() - if err != nil { - return err - } - // Create the DER-encoded ECDSA signature over the message digest. - sig, err := signer.SignMessage(pubKey, chanUpdateMsg) + sig, err := SignAnnouncement(signer, pubKey, update) if err != nil { return err } @@ -112,12 +111,12 @@ func ExtractChannelUpdate(ownerPubKey []byte, info.ChannelPoint) } -// ChannelUpdateFromEdge reconstructs a signed ChannelUpdate from the given edge -// info and policy. -func ChannelUpdateFromEdge(info *channeldb.ChannelEdgeInfo, - policy *channeldb.ChannelEdgePolicy) (*lnwire.ChannelUpdate, error) { +// UnsignedChannelUpdateFromEdge reconstructs an unsigned ChannelUpdate from the +// given edge info and policy. +func UnsignedChannelUpdateFromEdge(info *channeldb.ChannelEdgeInfo, + policy *channeldb.ChannelEdgePolicy) *lnwire.ChannelUpdate { - update := &lnwire.ChannelUpdate{ + return &lnwire.ChannelUpdate{ ChainHash: info.ChainHash, ShortChannelID: lnwire.NewShortChanIDFromInt(policy.ChannelID), Timestamp: uint32(policy.LastUpdate.Unix()), @@ -130,6 +129,14 @@ func ChannelUpdateFromEdge(info *channeldb.ChannelEdgeInfo, FeeRate: uint32(policy.FeeProportionalMillionths), ExtraOpaqueData: policy.ExtraOpaqueData, } +} + +// ChannelUpdateFromEdge reconstructs a signed ChannelUpdate from the given edge +// info and policy. +func ChannelUpdateFromEdge(info *channeldb.ChannelEdgeInfo, + policy *channeldb.ChannelEdgePolicy) (*lnwire.ChannelUpdate, error) { + + update := UnsignedChannelUpdateFromEdge(info, policy) var err error update.Signature, err = lnwire.NewSigFromRawSignature(policy.SigBytes) diff --git a/netann/channel_update_test.go b/netann/channel_update_test.go index 4ed019a8..0bc48e5c 100644 --- a/netann/channel_update_test.go +++ b/netann/channel_update_test.go @@ -103,6 +103,7 @@ func TestUpdateDisableFlag(t *testing.T) { t.Parallel() for _, tc := range updateDisableTests { + tc := tc t.Run(tc.name, func(t *testing.T) { // Create the initial update, the only fields we are // concerned with in this test are the timestamp and the @@ -127,7 +128,8 @@ func TestUpdateDisableFlag(t *testing.T) { // disabled or enabled as prescribed in the test case. err := netann.SignChannelUpdate( tc.signer, pubKey, newUpdate, - netann.ChannelUpdateSetDisable(tc.disable), + netann.ChanUpdSetDisable(tc.disable), + netann.ChanUpdSetTimestamp, ) var fail bool diff --git a/netann/node_announcement.go b/netann/node_announcement.go new file mode 100644 index 00000000..84a26105 --- /dev/null +++ b/netann/node_announcement.go @@ -0,0 +1,60 @@ +package netann + +import ( + "net" + "time" + + "github.com/btcsuite/btcd/btcec" + "github.com/lightningnetwork/lnd/lnwallet" + "github.com/lightningnetwork/lnd/lnwire" +) + +// NodeAnnModifier is a closure that makes in-place modifications to an +// lnwire.NodeAnnouncement. +type NodeAnnModifier func(*lnwire.NodeAnnouncement) + +// NodeAnnSetAddrs is a functional option that allows updating the addresses of +// the given node announcement. +func NodeAnnSetAddrs(addrs []net.Addr) func(*lnwire.NodeAnnouncement) { + return func(nodeAnn *lnwire.NodeAnnouncement) { + nodeAnn.Addresses = addrs + } +} + +// NodeAnnSetTimestamp is a functional option that sets the timestamp of the +// announcement to the current time, or increments it if the timestamp is +// already in the future. +func NodeAnnSetTimestamp(nodeAnn *lnwire.NodeAnnouncement) { + newTimestamp := uint32(time.Now().Unix()) + if newTimestamp <= nodeAnn.Timestamp { + // Increment the prior value to ensure the timestamp + // monotonically increases, otherwise the announcement won't + // propagate. + newTimestamp = nodeAnn.Timestamp + 1 + } + nodeAnn.Timestamp = newTimestamp +} + +// SignNodeAnnouncement applies the given modifies to the passed +// lnwire.NodeAnnouncement, then signs the resulting announcement. The provided +// update should be the most recent, valid update, otherwise the timestamp may +// not monotonically increase from the prior. +func SignNodeAnnouncement(signer lnwallet.MessageSigner, + pubKey *btcec.PublicKey, nodeAnn *lnwire.NodeAnnouncement, + mods ...NodeAnnModifier) error { + + // Apply the requested changes to the node announcement. + for _, modifier := range mods { + modifier(nodeAnn) + } + + // Create the DER-encoded ECDSA signature over the message digest. + sig, err := SignAnnouncement(signer, pubKey, nodeAnn) + if err != nil { + return err + } + + // Parse the DER-encoded signature into a fixed-size 64-byte array. + nodeAnn.Signature, err = lnwire.NewSigFromSignature(sig) + return err +} diff --git a/netann/sign.go b/netann/sign.go new file mode 100644 index 00000000..5a669620 --- /dev/null +++ b/netann/sign.go @@ -0,0 +1,36 @@ +package netann + +import ( + "fmt" + + "github.com/btcsuite/btcd/btcec" + "github.com/lightningnetwork/lnd/lnwallet" + "github.com/lightningnetwork/lnd/lnwire" +) + +// SignAnnouncement signs any type of gossip message that is announced on the +// network. +func SignAnnouncement(signer lnwallet.MessageSigner, pubKey *btcec.PublicKey, + msg lnwire.Message) (*btcec.Signature, error) { + + var ( + data []byte + err error + ) + + switch m := msg.(type) { + case *lnwire.ChannelAnnouncement: + data, err = m.DataToSign() + case *lnwire.ChannelUpdate: + data, err = m.DataToSign() + case *lnwire.NodeAnnouncement: + data, err = m.DataToSign() + default: + return nil, fmt.Errorf("can't sign %T message", m) + } + if err != nil { + return nil, fmt.Errorf("unable to get data to sign: %v", err) + } + + return signer.SignMessage(pubKey, data) +} diff --git a/server.go b/server.go index 6792981c..b9a628e3 100644 --- a/server.go +++ b/server.go @@ -651,7 +651,7 @@ func newServer(listenAddrs []net.Addr, chanDB *channeldb.DB, // With the announcement generated, we'll sign it to properly // authenticate the message on the network. - authSig, err := discovery.SignAnnouncement( + authSig, err := netann.SignAnnouncement( s.nodeSigner, s.identityPriv.PubKey(), nodeAnn, ) if err != nil { @@ -1657,7 +1657,7 @@ out: // announcement with the updated addresses and broadcast // it to our peers. newNodeAnn, err := s.genNodeAnnouncement( - true, lnwire.UpdateNodeAnnAddrs(newAddrs), + true, netann.NodeAnnSetAddrs(newAddrs), ) if err != nil { srvrLog.Debugf("Unable to generate new node "+ @@ -2039,7 +2039,7 @@ func (s *server) initTorController() error { // announcement. If refresh is true, then the time stamp of the announcement // will be updated in order to ensure it propagates through the network. func (s *server) genNodeAnnouncement(refresh bool, - updates ...func(*lnwire.NodeAnnouncement)) (lnwire.NodeAnnouncement, error) { + modifiers ...netann.NodeAnnModifier) (lnwire.NodeAnnouncement, error) { s.mu.Lock() defer s.mu.Unlock() @@ -2050,34 +2050,19 @@ func (s *server) genNodeAnnouncement(refresh bool, return *s.currentNodeAnn, nil } - // Now that we know we need to update our copy, we'll apply all the - // function updates that'll mutate the current version of our node - // announcement. - for _, update := range updates { - update(s.currentNodeAnn) - } + // Always update the timestamp when refreshing to ensure the update + // propagates. + modifiers = append(modifiers, netann.NodeAnnSetTimestamp) - // We'll now update the timestamp, ensuring that with each update, the - // timestamp monotonically increases. - newStamp := uint32(time.Now().Unix()) - if newStamp <= s.currentNodeAnn.Timestamp { - newStamp = s.currentNodeAnn.Timestamp + 1 - } - s.currentNodeAnn.Timestamp = newStamp - - // Now that the announcement is fully updated, we'll generate a new - // signature over the announcement to ensure nodes on the network - // accepted the new authenticated announcement. - sig, err := discovery.SignAnnouncement( + // Otherwise, we'll sign a new update after applying all of the passed + // modifiers. + err := netann.SignNodeAnnouncement( s.nodeSigner, s.identityPriv.PubKey(), s.currentNodeAnn, + modifiers..., ) if err != nil { return lnwire.NodeAnnouncement{}, err } - s.currentNodeAnn.Signature, err = lnwire.NewSigFromSignature(sig) - if err != nil { - return lnwire.NodeAnnouncement{}, err - } return *s.currentNodeAnn, nil }