Merge pull request #10232 from ellemouton/g175MessageFollowup

lnwire: add missing Gossip 1.75 fields and message
This commit is contained in:
Elle authored and GitHub committed 2025-10-02 14:58:53 +02:00
commit 1c2ff4a7e7
32 files changed
+1435 -136

No files matched your search

+12 -11
View File
@@ -244,11 +244,11 @@ type Config struct {
// FetchSelfAnnouncement retrieves our current node announcement, for
// use when determining whether we should update our peers about our
// presence in the network.
FetchSelfAnnouncement func() lnwire.NodeAnnouncement
FetchSelfAnnouncement func() lnwire.NodeAnnouncement1
// UpdateSelfAnnouncement produces a new announcement for our node with
// an updated timestamp which can be broadcast to our peers.
UpdateSelfAnnouncement func() (lnwire.NodeAnnouncement, error)
UpdateSelfAnnouncement func() (lnwire.NodeAnnouncement1, error)
// ProofMatureDelta the number of confirmations which is needed before
// exchange the channel announcement proofs.
@@ -1186,7 +1186,7 @@ func (d *deDupedAnnouncements) addMsg(message networkMsg) {
// Node announcements are identified by the Vertex field. Use the
// NodeID to create the corresponding Vertex.
case *lnwire.NodeAnnouncement:
case *lnwire.NodeAnnouncement1:
sender := route.NewVertex(message.source)
deDupKey := route.Vertex(msg.NodeID)
@@ -1195,7 +1195,8 @@ func (d *deDupedAnnouncements) addMsg(message networkMsg) {
oldTimestamp := uint32(0)
mws, ok := d.nodeAnnouncements[deDupKey]
if ok {
oldTimestamp = mws.msg.(*lnwire.NodeAnnouncement).Timestamp
ann, _ := mws.msg.(*lnwire.NodeAnnouncement1)
oldTimestamp = ann.Timestamp
}
// Discard the message if it's old.
@@ -1806,7 +1807,7 @@ func (d *AuthenticatedGossiper) retransmitStaleAnns(ctx context.Context,
return nil
}
// We'll also check that our NodeAnnouncement is not too old.
// We'll also check that our NodeAnnouncement1 is not too old.
currentNodeAnn := d.cfg.FetchSelfAnnouncement()
timestamp := time.Unix(int64(currentNodeAnn.Timestamp), 0)
timeElapsed := now.Sub(timestamp)
@@ -2062,7 +2063,7 @@ func (d *AuthenticatedGossiper) fetchPKScript(chanID lnwire.ShortChannelID) (
// addNode processes the given node announcement, and adds it to our channel
// graph.
func (d *AuthenticatedGossiper) addNode(ctx context.Context,
msg *lnwire.NodeAnnouncement, op ...batch.SchedulerOption) error {
msg *lnwire.NodeAnnouncement1, op ...batch.SchedulerOption) error {
if err := netann.ValidateNodeAnn(msg); err != nil {
return fmt.Errorf("unable to validate node announcement: %w",
@@ -2153,7 +2154,7 @@ func (d *AuthenticatedGossiper) processNetworkAnnouncement(ctx context.Context,
// A new node announcement has arrived which either presents new
// information about a node in one of the channels we know about, or a
// updating previously advertised information.
case *lnwire.NodeAnnouncement:
case *lnwire.NodeAnnouncement1:
return d.handleNodeAnnouncement(ctx, nMsg, msg, schedulerOp)
// A new channel announcement has arrived, this indicates the
@@ -2248,7 +2249,7 @@ func (d *AuthenticatedGossiper) processZombieUpdate(_ context.Context,
// announcement fields and returns an error if they are invalid to prevent
// forwarding invalid node announcements to our peers.
func (d *AuthenticatedGossiper) fetchNodeAnn(ctx context.Context,
pubKey [33]byte) (*lnwire.NodeAnnouncement, error) {
pubKey [33]byte) (*lnwire.NodeAnnouncement1, error) {
node, err := d.cfg.Graph.FetchNode(ctx, pubKey)
if err != nil {
@@ -2478,12 +2479,12 @@ func (d *AuthenticatedGossiper) latestHeight() uint32 {
// handleNodeAnnouncement processes a new node announcement.
func (d *AuthenticatedGossiper) handleNodeAnnouncement(ctx context.Context,
nMsg *networkMsg, nodeAnn *lnwire.NodeAnnouncement,
nMsg *networkMsg, nodeAnn *lnwire.NodeAnnouncement1,
ops []batch.SchedulerOption) ([]networkMsg, bool) {
timestamp := time.Unix(int64(nodeAnn.Timestamp), 0)
log.Debugf("Processing NodeAnnouncement: peer=%v, timestamp=%v, "+
log.Debugf("Processing NodeAnnouncement1: peer=%v, timestamp=%v, "+
"node=%x, source=%x", nMsg.peer, timestamp, nodeAnn.NodeID,
nMsg.source.SerializeCompressed())
@@ -2542,7 +2543,7 @@ func (d *AuthenticatedGossiper) handleNodeAnnouncement(ctx context.Context,
nMsg.err <- nil
// TODO(roasbeef): get rid of the above
log.Debugf("Processed NodeAnnouncement: peer=%v, timestamp=%v, "+
log.Debugf("Processed NodeAnnouncement1: peer=%v, timestamp=%v, "+
"node=%x, source=%x", nMsg.peer, timestamp, nodeAnn.NodeID,
nMsg.source.SerializeCompressed())
+15 -14
View File
@@ -493,8 +493,8 @@ func (m *mockNotifier) Stop() error {
}
type annBatch struct {
nodeAnn1 *lnwire.NodeAnnouncement
nodeAnn2 *lnwire.NodeAnnouncement
nodeAnn1 *lnwire.NodeAnnouncement1
nodeAnn2 *lnwire.NodeAnnouncement1
chanAnn *lnwire.ChannelAnnouncement1
@@ -577,8 +577,8 @@ func (ctx *testCtx) createAnnouncements(blockHeight uint32, key1,
}
func createNodeAnnouncement(priv *btcec.PrivateKey,
timestamp uint32, extraBytes ...[]byte) (*lnwire.NodeAnnouncement, error) {
func createNodeAnnouncement(priv *btcec.PrivateKey, timestamp uint32,
extraBytes ...[]byte) (*lnwire.NodeAnnouncement1, error) {
var err error
k := hex.EncodeToString(priv.Serialize())
@@ -587,7 +587,7 @@ func createNodeAnnouncement(priv *btcec.PrivateKey,
return nil, err
}
a := &lnwire.NodeAnnouncement{
a := &lnwire.NodeAnnouncement1{
Timestamp: timestamp,
Addresses: testAddrs,
Alias: alias,
@@ -966,15 +966,15 @@ func createTestCtx(t *testing.T, startHeight uint32, isChanPeer bool) (
c := make(chan struct{})
return c
},
FetchSelfAnnouncement: func() lnwire.NodeAnnouncement {
return lnwire.NodeAnnouncement{
FetchSelfAnnouncement: func() lnwire.NodeAnnouncement1 {
return lnwire.NodeAnnouncement1{
Timestamp: testTimestamp,
}
},
UpdateSelfAnnouncement: func() (lnwire.NodeAnnouncement,
UpdateSelfAnnouncement: func() (lnwire.NodeAnnouncement1,
error) {
return lnwire.NodeAnnouncement{
return lnwire.NodeAnnouncement1{
Timestamp: testTimestamp,
}, nil
},
@@ -2285,7 +2285,7 @@ func TestForwardPrivateNodeAnnouncement(t *testing.T) {
case <-time.After(2 * trickleDelay):
}
// Now, we'll attempt to forward the NodeAnnouncement for the same node
// Now, we'll attempt to forward the NodeAnnouncement1 for the same node
// by opening a public channel on the network. We'll create a
// ChannelAnnouncement and hand it off to the gossiper in order to
// process it.
@@ -2312,8 +2312,9 @@ func TestForwardPrivateNodeAnnouncement(t *testing.T) {
t.Fatal("gossiper should have broadcast the channel announcement")
}
// We'll recreate the NodeAnnouncement with an updated timestamp to
// prevent a stale update. The NodeAnnouncement should now be forwarded.
// We'll recreate the NodeAnnouncement1 with an updated timestamp to
// prevent a stale update. The NodeAnnouncement1 should now be
// forwarded.
nodeAnn, err = createNodeAnnouncement(remoteKeyPriv1, timestamp+1)
require.NoError(t, err, "unable to create node announcement")
@@ -2897,7 +2898,7 @@ func TestExtraDataChannelUpdateValidation(t *testing.T) {
}
// TestExtraDataNodeAnnouncementValidation tests that we're able to properly
// validate a NodeAnnouncement that includes opaque bytes that we don't
// validate a NodeAnnouncement1 that includes opaque bytes that we don't
// currently know of.
func TestExtraDataNodeAnnouncementValidation(t *testing.T) {
t.Parallel()
@@ -3044,7 +3045,7 @@ func TestRetransmit(t *testing.T) {
chanAnn++
case *lnwire.ChannelUpdate1:
chanUpd++
case *lnwire.NodeAnnouncement:
case *lnwire.NodeAnnouncement1:
nodeAnn++
}
}
+1 -1
View File
@@ -1653,7 +1653,7 @@ func (g *GossipSyncer) FilterGossipMsgs(ctx context.Context,
// Similarly, we only send node announcements if the update
// timestamp ifs between our set gossip filter time range.
case *lnwire.NodeAnnouncement:
case *lnwire.NodeAnnouncement1:
if passesFilter(msg.Timestamp) {
msgsToSend = append(msgsToSend, msg)
}
+16 -6
View File
@@ -249,10 +249,14 @@ func TestGossipSyncerFilterGossipMsgsNoHorizon(t *testing.T) {
// through the gossiper to the target peer.
msgs := []msgWithSenders{
{
msg: &lnwire.NodeAnnouncement{Timestamp: uint32(time.Now().Unix())},
msg: &lnwire.NodeAnnouncement1{
Timestamp: uint32(time.Now().Unix()),
},
},
{
msg: &lnwire.NodeAnnouncement{Timestamp: uint32(time.Now().Unix())},
msg: &lnwire.NodeAnnouncement1{
Timestamp: uint32(time.Now().Unix()),
},
},
}
@@ -308,15 +312,21 @@ func TestGossipSyncerFilterGossipMsgsAllInMemory(t *testing.T) {
msgs := []msgWithSenders{
{
// Node ann above horizon.
msg: &lnwire.NodeAnnouncement{Timestamp: unixStamp(25001)},
msg: &lnwire.NodeAnnouncement1{
Timestamp: unixStamp(25001),
},
},
{
// Node ann below horizon.
msg: &lnwire.NodeAnnouncement{Timestamp: unixStamp(5)},
msg: &lnwire.NodeAnnouncement1{
Timestamp: unixStamp(5),
},
},
{
// Node ann above horizon.
msg: &lnwire.NodeAnnouncement{Timestamp: unixStamp(999999)},
msg: &lnwire.NodeAnnouncement1{
Timestamp: unixStamp(999999),
},
},
{
// Ann tuple below horizon.
@@ -737,7 +747,7 @@ func TestGossipSyncerReplyShortChanIDs(t *testing.T) {
ShortChannelID: lnwire.NewShortChanIDFromInt(20),
Timestamp: unixStamp(999999),
},
&lnwire.NodeAnnouncement{Timestamp: unixStamp(25001)},
&lnwire.NodeAnnouncement1{Timestamp: unixStamp(25001)},
}
// We'll then craft a reply to the upcoming query for all the matching
+5 -5
View File
@@ -188,7 +188,7 @@ func (v *ValidationBarrier) InitJobDependencies(job interface{}) (JobID,
populateDependencies(msg.ShortChannelID.String(), childJobID)
return childJobID, nil
case *lnwire.NodeAnnouncement:
case *lnwire.NodeAnnouncement1:
childJobID := JobID(v.idCtr.Add(1))
populateDependencies(
route.Vertex(msg.NodeID).String(), childJobID,
@@ -242,7 +242,7 @@ func (v *ValidationBarrier) WaitForParents(childJobID JobID,
v.Lock()
switch msg := job.(type) {
// Any ChannelUpdate or NodeAnnouncement jobs will need to wait on the
// Any ChannelUpdate or NodeAnnouncement1 jobs will need to wait on the
// completion of any active ChannelAnnouncement jobs related to them.
case *lnwire.ChannelUpdate1:
annID = msg.ShortChannelID.String()
@@ -258,7 +258,7 @@ func (v *ValidationBarrier) WaitForParents(childJobID JobID,
jobDesc = fmt.Sprintf("job=lnwire.ChannelUpdate, scid=%v",
msg.ShortChannelID.ToUint64())
case *lnwire.NodeAnnouncement:
case *lnwire.NodeAnnouncement1:
annID = route.Vertex(msg.NodeID).String()
parentJobIDs, ok = v.jobDependencies[childJobID]
@@ -269,7 +269,7 @@ func (v *ValidationBarrier) WaitForParents(childJobID JobID,
return nil
}
jobDesc = fmt.Sprintf("job=lnwire.NodeAnnouncement, pub=%s",
jobDesc = fmt.Sprintf("job=lnwire.NodeAnnouncement1, pub=%s",
route.Vertex(msg.NodeID))
// Other types of jobs can be executed immediately, so we'll just
@@ -446,7 +446,7 @@ func (v *ValidationBarrier) SignalDependents(job interface{}, id JobID) error {
return nil
case *lnwire.NodeAnnouncement:
case *lnwire.NodeAnnouncement1:
// Remove child job info.
return removeJob(route.Vertex(msg.NodeID).String(), id, true)
+3 -3
View File
@@ -186,7 +186,7 @@ func TestValidationBarrierQuit(t *testing.T) {
}
// TestValidationBarrierParentJobsClear tests that creating two parent jobs for
// ChannelUpdate / NodeAnnouncement will pause child jobs until the set of
// ChannelUpdate / NodeAnnouncement1 will pause child jobs until the set of
// parent jobs has cleared.
func TestValidationBarrierParentJobsClear(t *testing.T) {
t.Parallel()
@@ -230,14 +230,14 @@ func TestValidationBarrierParentJobsClear(t *testing.T) {
parentID3, err := barrier.InitJobDependencies(ann3)
require.NoError(t, err)
// Create the ChannelUpdate & NodeAnnouncement messages.
// Create the ChannelUpdate & NodeAnnouncement1 messages.
upd1 := &lnwire.ChannelUpdate1{
ShortChannelID: sharedScid,
}
childID1, err := barrier.InitJobDependencies(upd1)
require.NoError(t, err)
node1 := &lnwire.NodeAnnouncement{
node1 := &lnwire.NodeAnnouncement1{
NodeID: sharedNodeID,
}
childID2, err := barrier.InitJobDependencies(node1)
+5 -5
View File
@@ -398,7 +398,7 @@ type Config struct {
// CurrentNodeAnnouncement should return the latest, fully signed node
// announcement from the backing Lightning Network node with a fresh
// timestamp.
CurrentNodeAnnouncement func() (lnwire.NodeAnnouncement, error)
CurrentNodeAnnouncement func() (lnwire.NodeAnnouncement1, error)
// SendAnnouncement is used by the FundingManager to send announcement
// messages to the Gossiper to possibly broadcast to the greater
@@ -3757,8 +3757,8 @@ func (f *Manager) annAfterSixConfs(completeChan *channeldb.OpenChannel,
shortChanID *lnwire.ShortChannelID) error {
// If this channel is not meant to be announced to the greater network,
// we'll only send our NodeAnnouncement to our counterparty to ensure we
// don't leak any of our information.
// we'll only send our NodeAnnouncement1 to our counterparty to ensure
// we don't leak any of our information.
announceChan := completeChan.ChannelFlags&lnwire.FFAnnounceChannel != 0
if !announceChan {
log.Debugf("Will not announce private channel %v.",
@@ -3779,7 +3779,7 @@ func (f *Manager) annAfterSixConfs(completeChan *channeldb.OpenChannel,
completeChan.FundingOutpoint,
)
pubKey := peer.PubKey()
log.Debugf("Sending our NodeAnnouncement for "+
log.Debugf("Sending our NodeAnnouncement1 for "+
"ChannelID(%v) to %x", chanID, pubKey)
// TODO(halseth): make reliable. If the peer is not online this
@@ -4724,7 +4724,7 @@ func (f *Manager) announceChannel(localIDKey, remoteIDKey *btcec.PublicKey,
graph.ErrIgnored) {
log.Debugf("Graph rejected "+
"NodeAnnouncement: %v", err)
"NodeAnnouncement1: %v", err)
} else {
log.Errorf("Unable to send node "+
"announcement: %v", err)
+14 -13
View File
@@ -485,10 +485,10 @@ func createTestFundingManager(t *testing.T, privKey *btcec.PrivateKey,
}
return errChan
},
CurrentNodeAnnouncement: func() (lnwire.NodeAnnouncement,
CurrentNodeAnnouncement: func() (lnwire.NodeAnnouncement1,
error) {
return lnwire.NodeAnnouncement{}, nil
return lnwire.NodeAnnouncement1{}, nil
},
TempChanIDSeed: chanIDSeed,
FindChannel: func(node *btcec.PublicKey,
@@ -660,10 +660,10 @@ func recreateAliceFundingManager(t *testing.T, alice *testNode) {
}
return errChan
},
CurrentNodeAnnouncement: func() (lnwire.NodeAnnouncement,
CurrentNodeAnnouncement: func() (lnwire.NodeAnnouncement1,
error) {
return lnwire.NodeAnnouncement{}, nil
return lnwire.NodeAnnouncement1{}, nil
},
NotifyWhenOnline: func(peer [33]byte,
connectedChan chan<- lnpeer.Peer) {
@@ -1328,9 +1328,9 @@ func assertAnnouncementSignatures(t *testing.T, alice, bob *testNode) {
// by having the nodes exchange announcement signatures.
// Two distinct messages will be sent:
// 1) AnnouncementSignatures
// 2) NodeAnnouncement
// 2) NodeAnnouncement1
// These may arrive in no particular order.
// Note that sending the NodeAnnouncement at this point is an
// Note that sending the NodeAnnouncement1 at this point is an
// implementation detail, and not something required by the LN spec.
for j, node := range []*testNode{alice, bob} {
announcements := make([]lnwire.Message, 2)
@@ -1348,7 +1348,7 @@ func assertAnnouncementSignatures(t *testing.T, alice, bob *testNode) {
switch msg.(type) {
case *lnwire.AnnounceSignatures1:
gotAnnounceSignatures = true
case *lnwire.NodeAnnouncement:
case *lnwire.NodeAnnouncement1:
gotNodeAnnouncement = true
}
}
@@ -1358,7 +1358,8 @@ func assertAnnouncementSignatures(t *testing.T, alice, bob *testNode) {
j)
}
if !gotNodeAnnouncement {
t.Fatalf("did not get NodeAnnouncement from node %d", j)
t.Fatalf("did not get NodeAnnouncement1 from node %d",
j)
}
}
}
@@ -1378,7 +1379,7 @@ func assertNodeAnnSent(t *testing.T, alice, bob *testNode) {
node.msgChan, time.Second*5,
)
require.NoError(t, err)
assertType[*lnwire.NodeAnnouncement](t, *nodeAnn)
assertType[*lnwire.NodeAnnouncement1](t, *nodeAnn)
}
}
@@ -2959,7 +2960,7 @@ func TestFundingManagerPrivateChannel(t *testing.T) {
// We should however receive each side's node announcement.
select {
case msg := <-alice.msgChan:
if _, ok := msg.(*lnwire.NodeAnnouncement); !ok {
if _, ok := msg.(*lnwire.NodeAnnouncement1); !ok {
t.Fatalf("expected to receive node announcement")
}
case <-time.After(time.Second):
@@ -2968,7 +2969,7 @@ func TestFundingManagerPrivateChannel(t *testing.T) {
select {
case msg := <-bob.msgChan:
if _, ok := msg.(*lnwire.NodeAnnouncement); !ok {
if _, ok := msg.(*lnwire.NodeAnnouncement1); !ok {
t.Fatalf("expected to receive node announcement")
}
case <-time.After(time.Second):
@@ -3085,7 +3086,7 @@ func TestFundingManagerPrivateRestart(t *testing.T) {
// We should however receive each side's node announcement.
select {
case msg := <-alice.msgChan:
if _, ok := msg.(*lnwire.NodeAnnouncement); !ok {
if _, ok := msg.(*lnwire.NodeAnnouncement1); !ok {
t.Fatalf("expected to receive node announcement")
}
case <-time.After(time.Second):
@@ -3094,7 +3095,7 @@ func TestFundingManagerPrivateRestart(t *testing.T) {
select {
case msg := <-bob.msgChan:
if _, ok := msg.(*lnwire.NodeAnnouncement); !ok {
if _, ok := msg.(*lnwire.NodeAnnouncement1); !ok {
t.Fatalf("expected to receive node announcement")
}
case <-time.After(time.Second):
+2 -2
View File
@@ -4754,10 +4754,10 @@ func TestLightningNodePersistence(t *testing.T) {
require.NoError(t, err)
// Use the raw serialized node announcement message create an
// lnwire.NodeAnnouncement instance.
// lnwire.NodeAnnouncement1 instance.
msg, err := lnwire.ReadMessage(bytes.NewBuffer(nodeAnnBytes), 0)
require.NoError(t, err)
na, ok := msg.(*lnwire.NodeAnnouncement)
na, ok := msg.(*lnwire.NodeAnnouncement1)
require.True(t, ok)
// Convert the wire message to our internal node representation.
+4 -4
View File
@@ -96,7 +96,7 @@ func (l *Node) AddPubKey(key *btcec.PublicKey) {
}
// NodeAnnouncement retrieves the latest node announcement of the node.
func (l *Node) NodeAnnouncement(signed bool) (*lnwire.NodeAnnouncement,
func (l *Node) NodeAnnouncement(signed bool) (*lnwire.NodeAnnouncement1,
error) {
if !l.HaveNodeAnnouncement {
@@ -108,7 +108,7 @@ func (l *Node) NodeAnnouncement(signed bool) (*lnwire.NodeAnnouncement,
return nil, err
}
nodeAnn := &lnwire.NodeAnnouncement{
nodeAnn := &lnwire.NodeAnnouncement1{
Features: l.Features.RawFeatureVector,
NodeID: l.PubKeyBytes,
RGBColor: l.Color,
@@ -133,8 +133,8 @@ func (l *Node) NodeAnnouncement(signed bool) (*lnwire.NodeAnnouncement,
}
// NodeFromWireAnnouncement creates a Node instance from an
// lnwire.NodeAnnouncement message.
func NodeFromWireAnnouncement(msg *lnwire.NodeAnnouncement) *Node {
// lnwire.NodeAnnouncement1 message.
func NodeFromWireAnnouncement(msg *lnwire.NodeAnnouncement1) *Node {
timestamp := time.Unix(int64(msg.Timestamp), 0)
features := lnwire.NewFeatureVector(msg.Features, lnwire.Features)
+1 -1
View File
@@ -19,7 +19,7 @@ import (
type Config struct {
// GetNodeAnnouncement is used to send our retrieve the current
// node announcement information.
GetNodeAnnouncement func() lnwire.NodeAnnouncement
GetNodeAnnouncement func() lnwire.NodeAnnouncement1
// ParseAddr parses an address from its string format to a net.Addr.
ParseAddr func(addr string) (net.Addr, error)
+10 -4
View File
@@ -56,6 +56,9 @@ type ChannelAnnouncement2 struct {
// the funding output is a pure 2-of-2 MuSig aggregate public key.
MerkleRootHash tlv.OptionalRecordT[tlv.TlvType16, [32]byte]
// Outpoint is the outpoint of the funding transaction.
Outpoint tlv.RecordT[tlv.TlvType18, OutPoint]
// Signature is a Schnorr signature over serialised signed-range TLV
// stream of the message.
Signature tlv.RecordT[tlv.TlvType160, Sig]
@@ -81,17 +84,17 @@ func (c *ChannelAnnouncement2) Encode(w *bytes.Buffer, _ uint32) error {
// NOTE: this is part of the PureTLVMessage interface.
func (c *ChannelAnnouncement2) AllRecords() []tlv.Record {
recordProducers := append(
c.allNonSignatureRecordProducers(), &c.Signature,
c.nonSignatureRecordProducers(), &c.Signature,
)
return ProduceRecordsSorted(recordProducers...)
}
// allNonSignatureRecordProducers returns all the TLV record producers for the
// nonSignatureRecordProducers returns all the TLV record producers for the
// message except the signature record producer.
//
//nolint:ll
func (c *ChannelAnnouncement2) allNonSignatureRecordProducers() []tlv.RecordProducer {
func (c *ChannelAnnouncement2) nonSignatureRecordProducers() []tlv.RecordProducer {
// The chain-hash record is only included if it is _not_ equal to the
// bitcoin mainnet genisis block hash.
var recordProducers []tlv.RecordProducer
@@ -121,6 +124,7 @@ func (c *ChannelAnnouncement2) allNonSignatureRecordProducers() []tlv.RecordProd
},
)
recordProducers = append(recordProducers, &c.Outpoint)
recordProducers = append(recordProducers, RecordsAsProducers(
tlv.MapToRecords(c.ExtraSignedFields),
)...)
@@ -149,6 +153,7 @@ func (c *ChannelAnnouncement2) Decode(r io.Reader, _ uint32) error {
&btcKey1,
&btcKey2,
&merkleRootHash,
&c.Outpoint,
&c.Signature,
)...)
if err != nil {
@@ -202,6 +207,7 @@ func (c *ChannelAnnouncement2) DecodeNonSigTLVRecords(r io.Reader) error {
&btcKey1,
&btcKey2,
&merkleRootHash,
&c.Outpoint,
)...)
if err != nil {
return err
@@ -239,7 +245,7 @@ func (c *ChannelAnnouncement2) DecodeNonSigTLVRecords(r io.Reader) error {
// excludes the signature field.
func (c *ChannelAnnouncement2) EncodeAllNonSigFields(w io.Writer) error {
return EncodeRecordsTo(
w, ProduceRecordsSorted(c.allNonSignatureRecordProducers()...),
w, ProduceRecordsSorted(c.nonSignatureRecordProducers()...),
)
}
+11
View File
@@ -58,6 +58,17 @@ func TestChanAnn2EncodeDecode(t *testing.T) {
0xb3, 0xee, 0x17, 0x2f, 0x7f, 0x16, 0x1, 0xe6, 0x7d, 0x1d, 0xa6,
0xca, 0xd4, 0xb, 0x54, 0xc4, 0x46, 0x8d, 0x48, 0x23, 0x6c, 0x39,
// Outpoint record.
0x12, // type (18).
0x22, // length (34 bytes).
// Hash (32 bytes).
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10,
0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
// Index (2 bytes).
0x30, 0x39, // value: 12345.
// Unknown TLV record.
0x6f, // type.
0x2, // length.
+16 -1
View File
@@ -70,6 +70,11 @@ type ChannelUpdate2 struct {
// millionth of a satoshi.
FeeProportionalMillionths tlv.RecordT[tlv.TlvType18, uint32]
// InboundFee is an optional TLV record that contains the fee
// information for incoming HTLCs.
// TODO(elle): assign normal tlv type?
InboundFee tlv.OptionalRecordT[tlv.TlvType55555, Fee]
// Signature is used to validate the announced data and prove the
// ownership of node id.
Signature tlv.RecordT[tlv.TlvType160, Sig]
@@ -102,12 +107,13 @@ func (c *ChannelUpdate2) Decode(r io.Reader, _ uint32) error {
var (
chainHash = tlv.ZeroRecordT[tlv.TlvType0, [32]byte]()
secondPeer = tlv.ZeroRecordT[tlv.TlvType8, TrueBoolean]()
inboundFee = tlv.ZeroRecordT[tlv.TlvType55555, Fee]()
)
typeMap, err := tlvRecords.ExtractRecords(
&chainHash, &c.ShortChannelID, &c.BlockHeight, &c.DisabledFlags,
&secondPeer, &c.CLTVExpiryDelta, &c.HTLCMinimumMsat,
&c.HTLCMaximumMsat, &c.FeeBaseMsat,
&c.FeeProportionalMillionths,
&c.FeeProportionalMillionths, &inboundFee,
&c.Signature,
)
if err != nil {
@@ -149,6 +155,11 @@ func (c *ChannelUpdate2) Decode(r io.Reader, _ uint32) error {
c.FeeProportionalMillionths.Val = defaultFeeProportionalMillionths //nolint:ll
}
// If the inbound fee was encoded, set it.
if _, ok := typeMap[c.InboundFee.TlvType()]; ok {
c.InboundFee = tlv.SomeRecordT(inboundFee)
}
c.ExtraSignedFields = ExtraSignedFieldsFromTypeMap(typeMap)
return nil
@@ -207,6 +218,10 @@ func (c *ChannelUpdate2) AllRecords() []tlv.Record {
)
}
c.InboundFee.WhenSome(func(r tlv.RecordT[tlv.TlvType55555, Fee]) {
recordProducers = append(recordProducers, &r)
})
recordProducers = append(recordProducers, RecordsAsProducers(
tlv.MapToRecords(c.ExtraSignedFields),
)...)
+71
View File
@@ -1,10 +1,14 @@
package lnwire
import (
"bytes"
"errors"
"fmt"
"io"
"net"
"strconv"
"github.com/lightningnetwork/lnd/tlv"
)
var (
@@ -86,3 +90,70 @@ func ValidateDNSAddr(hostname string, port uint16) error {
return nil
}
// Record returns a TLV record that can be used to encode/decode the DNSAddress.
//
// NOTE: this is part of the tlv.RecordProducer interface.
func (d *DNSAddress) Record() tlv.Record {
sizeFunc := func() uint64 {
// Hostname length + 2 bytes for port.
return uint64(len(d.Hostname) + 2)
}
return tlv.MakeDynamicRecord(
0, d, sizeFunc, dnsAddressEncoder, dnsAddressDecoder,
)
}
// dnsAddressEncoder is a TLV encoder for DNSAddress.
func dnsAddressEncoder(w io.Writer, val any, _ *[8]byte) error {
if v, ok := val.(*DNSAddress); ok {
var buf bytes.Buffer
// Write the hostname as raw bytes (no length prefix for TLV).
if _, err := buf.WriteString(v.Hostname); err != nil {
return err
}
// Write the port as 2 bytes.
err := WriteUint16(&buf, v.Port)
if err != nil {
return err
}
_, err = w.Write(buf.Bytes())
return err
}
return tlv.NewTypeForEncodingErr(val, "DNSAddress")
}
// dnsAddressDecoder is a TLV decoder for DNSAddress.
func dnsAddressDecoder(r io.Reader, val any, _ *[8]byte,
l uint64) error {
if v, ok := val.(*DNSAddress); ok {
if l < 2 {
return fmt.Errorf("DNS address must be at least 2 " +
"bytes")
}
// Read hostname (all bytes except last 2).
hostnameLen := l - 2
hostnameBytes := make([]byte, hostnameLen)
if _, err := io.ReadFull(r, hostnameBytes); err != nil {
return err
}
v.Hostname = string(hostnameBytes)
// Read port (last 2 bytes).
if err := ReadElement(r, &v.Port); err != nil {
return err
}
return ValidateDNSAddr(v.Hostname, v.Port)
}
return tlv.NewTypeForDecodingErr(val, "DNSAddress", l, 0)
}
+112
View File
@@ -1,11 +1,14 @@
package lnwire
import (
"bytes"
"fmt"
"strings"
"testing"
"github.com/lightningnetwork/lnd/tlv"
"github.com/stretchr/testify/require"
"pgregory.net/rapid"
)
// TestValidateDNSAddr tests hostname and port validation per BOLT #7.
@@ -85,3 +88,112 @@ func TestValidateDNSAddr(t *testing.T) {
})
}
}
// TestDNSAddressTLVEncoding tests the TLV encoding and decoding of DNSAddress
// structs using the ExtraOpaqueData interface.
func TestDNSAddressTLVEncoding(t *testing.T) {
t.Parallel()
testDNSAddr := DNSAddress{
Hostname: "lightning.example.com",
Port: 9000,
}
var extraData ExtraOpaqueData
require.NoError(t, extraData.PackRecords(&testDNSAddr))
var decodedDNSAddr DNSAddress
tlvs, err := extraData.ExtractRecords(&decodedDNSAddr)
require.NoError(t, err)
require.Contains(t, tlvs, tlv.Type(0))
require.Equal(t, testDNSAddr, decodedDNSAddr)
}
// TestDNSAddressRecord tests the TLV Record interface of DNSAddress
// by directly encoding and decoding using the Record method.
func TestDNSAddressRecord(t *testing.T) {
t.Parallel()
testDNSAddr := DNSAddress{
Hostname: "lightning.example.com",
Port: 9000,
}
var buf bytes.Buffer
record := testDNSAddr.Record()
require.NoError(t, record.Encode(&buf))
var decodedDNSAddr DNSAddress
decodedRecord := decodedDNSAddr.Record()
require.NoError(t, decodedRecord.Decode(&buf, uint64(buf.Len())))
require.Equal(t, testDNSAddr, decodedDNSAddr)
}
// TestDNSAddressInvalidDecoding tests error cases during TLV decoding.
func TestDNSAddressInvalidDecoding(t *testing.T) {
t.Parallel()
testCases := []struct {
name string
data []byte
errMsg string
}{
{
name: "too short (only 1 byte)",
data: []byte{0x61},
errMsg: "DNS address must be at least 2 bytes",
},
{
name: "empty data",
data: []byte{},
errMsg: "DNS address must be at least 2 bytes",
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
var dnsAddr DNSAddress
record := dnsAddr.Record()
buf := bytes.NewReader(tc.data)
err := record.Decode(buf, uint64(len(tc.data)))
require.Error(t, err)
require.ErrorContains(t, err, tc.errMsg)
})
}
}
// TestDNSAddressProperty uses property-based testing to verify that DNSAddress
// TLV encoding and decoding is correct for random DNSAddress values.
func TestDNSAddressProperty(t *testing.T) {
t.Parallel()
scenario := func(t *rapid.T) {
// Generate a random valid hostname.
hostname := genValidHostname(t)
// Generate a random port (excluding 0 which is invalid).
port := rapid.Uint16Range(1, 65535).Draw(t, "port")
dnsAddr := DNSAddress{
Hostname: hostname,
Port: port,
}
var buf bytes.Buffer
record := dnsAddr.Record()
err := record.Encode(&buf)
require.NoError(t, err)
var decodedDNSAddr DNSAddress
decodedRecord := decodedDNSAddr.Record()
err = decodedRecord.Decode(&buf, uint64(buf.Len()))
require.NoError(t, err)
require.Equal(t, dnsAddr, decodedDNSAddr)
}
rapid.Check(t, scenario)
}
+28 -4
View File
@@ -220,10 +220,10 @@ func FuzzNodeAnnouncement(f *testing.F) {
// can be represented by different underlying bytes. Instead, we
// compare the normalized string representation of each address.
assertEq := func(t *testing.T, x, y any) {
require.IsType(t, &NodeAnnouncement{}, x)
first, _ := x.(*NodeAnnouncement)
require.IsType(t, &NodeAnnouncement{}, y)
second, _ := y.(*NodeAnnouncement)
require.IsType(t, &NodeAnnouncement1{}, x)
first, _ := x.(*NodeAnnouncement1)
require.IsType(t, &NodeAnnouncement1{}, y)
second, _ := y.(*NodeAnnouncement1)
require.Equal(
t, len(first.Addresses), len(second.Addresses),
@@ -244,6 +244,30 @@ func FuzzNodeAnnouncement(f *testing.F) {
})
}
func FuzzNodeAnnouncement2(f *testing.F) {
f.Fuzz(func(t *testing.T, data []byte) {
// We can't use require.Equal for Features, since we consider
// the empty map and nil to be equivalent.
assertEq := func(t *testing.T, x, y any) {
require.IsType(t, &NodeAnnouncement2{}, x)
first, _ := x.(*NodeAnnouncement2)
require.IsType(t, &NodeAnnouncement2{}, y)
second, _ := y.(*NodeAnnouncement2)
require.True(
t,
first.Features.Val.Equals(&second.Features.Val),
)
first.Features.Val = *NewRawFeatureVector()
second.Features.Val = *NewRawFeatureVector()
require.Equal(t, first, second)
}
wireMsgHarnessCustom(t, data, MsgNodeAnnouncement2, assertEq)
})
}
func FuzzOpenChannel(f *testing.F) {
f.Fuzz(func(t *testing.T, data []byte) {
// We can't use require.Equal for UpfrontShutdownScript, since
+7 -2
View File
@@ -64,6 +64,7 @@ const (
MsgReplyChannelRange = 264
MsgGossipTimestampRange = 265
MsgChannelAnnouncement2 = 267
MsgNodeAnnouncement2 = 269
MsgChannelUpdate2 = 271
MsgKickoffSig = 777
@@ -166,7 +167,7 @@ func (t MessageType) String() string {
case MsgChannelUpdate:
return "ChannelUpdate"
case MsgNodeAnnouncement:
return "NodeAnnouncement"
return "NodeAnnouncement1"
case MsgPing:
return "Ping"
case MsgAnnounceSignatures:
@@ -193,6 +194,8 @@ func (t MessageType) String() string {
return "MsgAnnounceSignatures2"
case MsgChannelAnnouncement2:
return "ChannelAnnouncement2"
case MsgNodeAnnouncement2:
return "NodeAnnouncement2"
case MsgChannelUpdate2:
return "ChannelUpdate2"
default:
@@ -330,7 +333,7 @@ func makeEmptyMessage(msgType MessageType) (Message, error) {
case MsgChannelUpdate:
msg = &ChannelUpdate1{}
case MsgNodeAnnouncement:
msg = &NodeAnnouncement{}
msg = &NodeAnnouncement1{}
case MsgPing:
msg = &Ping{}
case MsgAnnounceSignatures:
@@ -355,6 +358,8 @@ func makeEmptyMessage(msgType MessageType) (Message, error) {
msg = &AnnounceSignatures2{}
case MsgChannelAnnouncement2:
msg = &ChannelAnnouncement2{}
case MsgNodeAnnouncement2:
msg = &NodeAnnouncement2{}
case MsgChannelUpdate2:
msg = &ChannelUpdate2{}
default:
+2 -2
View File
@@ -670,11 +670,11 @@ func newMsgChannelAnnouncement(t testing.TB,
}
func newMsgNodeAnnouncement(t testing.TB,
r *rand.Rand) *lnwire.NodeAnnouncement {
r *rand.Rand) *lnwire.NodeAnnouncement1 {
t.Helper()
msg := &lnwire.NodeAnnouncement{
msg := &lnwire.NodeAnnouncement1{
Features: rawFeatureVector(),
Timestamp: uint32(r.Int31()),
Alias: randAlias(r),
+14 -15
View File
@@ -62,11 +62,11 @@ func (n NodeAlias) String() string {
return string(bytes.Trim(n[:], "\x00"))
}
// NodeAnnouncement message is used to announce the presence of a Lightning
// NodeAnnouncement1 message is used to announce the presence of a Lightning
// node and also to signal that the node is accepting incoming connections.
// Each NodeAnnouncement authenticating the advertised information within the
// Each NodeAnnouncement1 authenticating the advertised information within the
// announcement via a signature using the advertised node pubkey.
type NodeAnnouncement struct {
type NodeAnnouncement1 struct {
// Signature is used to prove the ownership of node id.
Signature Sig
@@ -100,19 +100,19 @@ type NodeAnnouncement struct {
ExtraOpaqueData ExtraOpaqueData
}
// A compile time check to ensure NodeAnnouncement implements the
// A compile time check to ensure NodeAnnouncement1 implements the
// lnwire.Message interface.
var _ Message = (*NodeAnnouncement)(nil)
var _ Message = (*NodeAnnouncement1)(nil)
// A compile time check to ensure NodeAnnouncement implements the
// A compile time check to ensure NodeAnnouncement1 implements the
// lnwire.SizeableMessage interface.
var _ SizeableMessage = (*NodeAnnouncement)(nil)
var _ SizeableMessage = (*NodeAnnouncement1)(nil)
// Decode deserializes a serialized NodeAnnouncement stored in the passed
// Decode deserializes a serialized NodeAnnouncement1 stored in the passed
// io.Reader observing the specified protocol version.
//
// This is part of the lnwire.Message interface.
func (a *NodeAnnouncement) Decode(r io.Reader, _ uint32) error {
func (a *NodeAnnouncement1) Decode(r io.Reader, _ uint32) error {
err := ReadElements(r,
&a.Signature,
&a.Features,
@@ -130,11 +130,11 @@ func (a *NodeAnnouncement) Decode(r io.Reader, _ uint32) error {
return a.ExtraOpaqueData.ValidateTLV()
}
// Encode serializes the target NodeAnnouncement into the passed io.Writer
// Encode serializes the target NodeAnnouncement1 into the passed io.Writer
// observing the protocol version specified.
//
// This is part of the lnwire.Message interface.
func (a *NodeAnnouncement) Encode(w *bytes.Buffer, pver uint32) error {
func (a *NodeAnnouncement1) Encode(w *bytes.Buffer, pver uint32) error {
if err := WriteSig(w, a.Signature); err != nil {
return err
}
@@ -170,13 +170,12 @@ func (a *NodeAnnouncement) Encode(w *bytes.Buffer, pver uint32) error {
// wire.
//
// This is part of the lnwire.Message interface.
func (a *NodeAnnouncement) MsgType() MessageType {
func (a *NodeAnnouncement1) MsgType() MessageType {
return MsgNodeAnnouncement
}
// DataToSign returns the part of the message that should be signed.
func (a *NodeAnnouncement) DataToSign() ([]byte, error) {
func (a *NodeAnnouncement1) DataToSign() ([]byte, error) {
// We should not include the signatures itself.
buffer := make([]byte, 0, MaxMsgBody)
buf := bytes.NewBuffer(buffer)
@@ -215,6 +214,6 @@ func (a *NodeAnnouncement) DataToSign() ([]byte, error) {
// SerializedSize returns the serialized size of the message in bytes.
//
// This is part of the lnwire.SizeableMessage interface.
func (a *NodeAnnouncement) SerializedSize() (uint32, error) {
func (a *NodeAnnouncement1) SerializedSize() (uint32, error) {
return MessageSerializedSize(a)
}
+581
View File
@@ -0,0 +1,581 @@
package lnwire
import (
"bytes"
"encoding/binary"
"errors"
"fmt"
"image/color"
"io"
"net"
"unicode/utf8"
"github.com/lightningnetwork/lnd/tlv"
"github.com/lightningnetwork/lnd/tor"
)
// NodeAnnouncement2 message is used to announce the presence of a Lightning
// node and also to signal that the node is accepting incoming connections.
// Each NodeAnnouncement2 authenticating the advertised information within the
// announcement via a signature using the advertised node pubkey.
type NodeAnnouncement2 struct {
// Features is the feature vector that encodes the features supported
// by the target node.
Features tlv.RecordT[tlv.TlvType0, RawFeatureVector]
// Color is an optional field used to customize a node's appearance in
// maps and graphs.
Color tlv.OptionalRecordT[tlv.TlvType1, Color]
// 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.TlvType2, uint32]
// Alias is used to customize their node's appearance in maps and
// graphs.
Alias tlv.OptionalRecordT[tlv.TlvType3, NodeAlias2]
// NodeID is the public key of the node creating the announcement.
NodeID tlv.RecordT[tlv.TlvType4, [33]byte]
// IPV4Addrs is an optional list of ipv4 addresses that the node is
// reachable at.
IPV4Addrs tlv.OptionalRecordT[tlv.TlvType5, IPV4Addrs]
// IPV6Addrs is an optional list of ipv6 addresses that the node is
// reachable at.
IPV6Addrs tlv.OptionalRecordT[tlv.TlvType7, IPV6Addrs]
// TorV3Addrs is an optional list of tor v3 addresses that the node is
// reachable at.
TorV3Addrs tlv.OptionalRecordT[tlv.TlvType9, TorV3Addrs]
// DNSHostName is an optional DNS hostname that the node is reachable
// at.
DNSHostName tlv.OptionalRecordT[tlv.TlvType11, DNSAddress]
// Signature is used to validate the announced data and prove the
// ownership of node id.
Signature tlv.RecordT[tlv.TlvType160, Sig]
// Any extra fields in the signed range that we do not yet know about,
// but we need to keep them for signature validation and to produce a
// valid message.
ExtraSignedFields
}
// SerializedSize returns the serialized size of the message in bytes.
//
// This is part of the lnwire.SizeableMessage interface.
func (n *NodeAnnouncement2) SerializedSize() (uint32, error) {
return MessageSerializedSize(n)
}
// AllRecords returns all the TLV records for the message. This will include all
// the records we know about along with any that we don't know about but that
// fall in the signed TLV range.
//
// NOTE: this is part of the PureTLVMessage interface.
func (n *NodeAnnouncement2) AllRecords() []tlv.Record {
recordProducers := []tlv.RecordProducer{
&n.Features,
&n.BlockHeight,
&n.NodeID,
&n.Signature,
}
n.Color.WhenSome(func(r tlv.RecordT[tlv.TlvType1, Color]) {
recordProducers = append(recordProducers, &r)
})
n.Alias.WhenSome(func(a tlv.RecordT[tlv.TlvType3, NodeAlias2]) {
recordProducers = append(recordProducers, &a)
})
n.IPV4Addrs.WhenSome(func(r tlv.RecordT[tlv.TlvType5, IPV4Addrs]) {
recordProducers = append(recordProducers, &r)
})
n.IPV6Addrs.WhenSome(func(r tlv.RecordT[tlv.TlvType7, IPV6Addrs]) {
recordProducers = append(recordProducers, &r)
})
n.TorV3Addrs.WhenSome(func(r tlv.RecordT[tlv.TlvType9, TorV3Addrs]) {
recordProducers = append(recordProducers, &r)
})
n.DNSHostName.WhenSome(func(r tlv.RecordT[tlv.TlvType11, DNSAddress]) {
recordProducers = append(recordProducers, &r)
})
recordProducers = append(recordProducers, RecordsAsProducers(
tlv.MapToRecords(n.ExtraSignedFields),
)...)
return ProduceRecordsSorted(recordProducers...)
}
// Decode deserializes a serialized NodeAnnouncement2 stored in the passed
// io.Reader observing the specified protocol version.
//
// This is part of the lnwire.Message interface.
func (n *NodeAnnouncement2) Decode(r io.Reader, _ uint32) error {
var (
color = tlv.ZeroRecordT[tlv.TlvType1, Color]()
alias = tlv.ZeroRecordT[tlv.TlvType3, NodeAlias2]()
ipv4 = tlv.ZeroRecordT[tlv.TlvType5, IPV4Addrs]()
ipv6 = tlv.ZeroRecordT[tlv.TlvType7, IPV6Addrs]()
torV3 = tlv.ZeroRecordT[tlv.TlvType9, TorV3Addrs]()
dns = tlv.ZeroRecordT[tlv.TlvType11, DNSAddress]()
)
stream, err := tlv.NewStream(ProduceRecordsSorted(
&n.Features,
&n.BlockHeight,
&n.NodeID,
&n.Signature,
&alias,
&color,
&ipv4,
&ipv6,
&torV3,
&dns,
)...)
if err != nil {
return err
}
n.Signature.Val.ForceSchnorr()
typeMap, err := stream.DecodeWithParsedTypesP2P(r)
if err != nil {
return err
}
if _, ok := typeMap[n.Alias.TlvType()]; ok {
n.Alias = tlv.SomeRecordT(alias)
}
if _, ok := typeMap[n.Color.TlvType()]; ok {
n.Color = tlv.SomeRecordT(color)
}
if _, ok := typeMap[n.IPV4Addrs.TlvType()]; ok {
n.IPV4Addrs = tlv.SomeRecordT(ipv4)
}
if _, ok := typeMap[n.IPV6Addrs.TlvType()]; ok {
n.IPV6Addrs = tlv.SomeRecordT(ipv6)
}
if _, ok := typeMap[n.TorV3Addrs.TlvType()]; ok {
n.TorV3Addrs = tlv.SomeRecordT(torV3)
}
if _, ok := typeMap[n.DNSHostName.TlvType()]; ok {
n.DNSHostName = tlv.SomeRecordT(dns)
}
n.ExtraSignedFields = ExtraSignedFieldsFromTypeMap(typeMap)
return nil
}
// Encode serializes the target NodeAnnouncement2 into the passed io.Writer
// observing the protocol version specified.
//
// This is part of the lnwire.Message interface.
func (n *NodeAnnouncement2) Encode(w *bytes.Buffer, _ uint32) error {
return EncodePureTLVMessage(n, w)
}
// MsgType returns the integer uniquely identifying this message type on the
// wire.
//
// This is part of the lnwire.Message interface.
func (n *NodeAnnouncement2) MsgType() MessageType {
return MsgNodeAnnouncement2
}
// A compile-time check to ensure NodeAnnouncement2 implements the Message
// interface.
var _ Message = (*NodeAnnouncement2)(nil)
// A compile-time check to ensure NodeAnnouncement2 implements the
// PureTLVMessage interface.
var _ PureTLVMessage = (*NodeAnnouncement2)(nil)
// A compile time check to ensure ChannelAnnouncement2 implements the
// lnwire.SizeableMessage interface.
var _ SizeableMessage = (*NodeAnnouncement2)(nil)
// NodeAlias2 represents a UTF-8 encoded node alias with a maximum length of 32
// bytes.
type NodeAlias2 []byte
// Record returns a TLV record for encoding/decoding NodeAlias2.
func (n *NodeAlias2) Record() tlv.Record {
sizeFunc := func() uint64 {
return uint64(len(*n))
}
return tlv.MakeDynamicRecord(
0, n, sizeFunc, nodeAlias2Encoder, nodeAlias2Decoder,
)
}
// ValidateNodeAlias2 validates that the node alias is valid UTF-8 and within
// the 32-byte limit.
func ValidateNodeAlias2(alias []byte) error {
if len(alias) == 0 {
return errors.New("node alias cannot be empty")
}
if len(alias) > 32 {
return fmt.Errorf("node alias exceeds 32 bytes: length %d",
len(alias))
}
if !utf8.Valid(alias) {
return errors.New("node alias contains invalid UTF-8")
}
return nil
}
// nodeAlias2Encoder encodes a NodeAlias2 as raw UTF-8 bytes.
func nodeAlias2Encoder(w io.Writer, val any, _ *[8]byte) error {
if v, ok := val.(*NodeAlias2); ok {
if err := ValidateNodeAlias2(*v); err != nil {
return err
}
_, err := w.Write(*v)
return err
}
return tlv.NewTypeForEncodingErr(val, "NodeAlias2")
}
// nodeAlias2Decoder decodes raw bytes into a NodeAlias2.
func nodeAlias2Decoder(r io.Reader, val any, _ *[8]byte, l uint64) error {
if v, ok := val.(*NodeAlias2); ok {
if l > 32 {
return fmt.Errorf("node alias exceeds 32 bytes: %d", l)
}
aliasBytes := make([]byte, l)
if _, err := io.ReadFull(r, aliasBytes); err != nil {
return err
}
if err := ValidateNodeAlias2(aliasBytes); err != nil {
return err
}
*v = aliasBytes
return nil
}
return tlv.NewTypeForDecodingErr(val, "NodeAlias2", l, 0)
}
// Color is a custom type to represent a color.RGBA that can be encoded/decoded
// as a TLV record.
type Color color.RGBA
// Record returns a TLV record for encoding/decoding Color.
func (c *Color) Record() tlv.Record {
return tlv.MakeStaticRecord(0, c, 3, rgbEncoder, rgbDecoder)
}
// rgbEncoder encodes a Color as RGB bytes.
func rgbEncoder(w io.Writer, val any, _ *[8]byte) error {
if v, ok := val.(*Color); ok {
buf := bytes.NewBuffer(nil)
err := WriteColorRGBA(buf, color.RGBA(*v))
if err != nil {
return err
}
_, err = w.Write(buf.Bytes())
return err
}
return tlv.NewTypeForEncodingErr(val, "Color")
}
// rgbDecoder decodes RGB bytes into a Color.
func rgbDecoder(r io.Reader, val any, _ *[8]byte, l uint64) error {
if v, ok := val.(*Color); ok {
return ReadElements(r, &v.R, &v.G, &v.B)
}
return tlv.NewTypeForDecodingErr(val, "Color", l, 3)
}
// ipv4AddrEncodedSize is the number of bytes required to encode a single ipv4
// address. Four bytes are used to encode the IP address and two bytes for the
// port number.
const ipv4AddrEncodedSize = 4 + 2
// IPV4Addrs is a list of ipv4 addresses that can be encoded as a TLV record.
type IPV4Addrs []*net.TCPAddr
// Record returns a Record that can be used to encode/decode a IPV4Addrs
// to/from a TLV stream.
func (a *IPV4Addrs) Record() tlv.Record {
return tlv.MakeDynamicRecord(
0, a, a.encodedSize, ipv4AddrsEncoder, ipv4AddrsDecoder,
)
}
// encodedSize returns the number of bytes required to encode an IPV4Addrs
// variable.
func (a *IPV4Addrs) encodedSize() uint64 {
return uint64(len(*a) * ipv4AddrEncodedSize)
}
// ipv4AddrsEncoder encodes IPv4 addresses as TLV bytes.
func ipv4AddrsEncoder(w io.Writer, val interface{}, _ *[8]byte) error {
if v, ok := val.(*IPV4Addrs); ok {
for _, ip := range *v {
_, err := w.Write(ip.IP.To4())
if err != nil {
return err
}
var port [2]byte
binary.BigEndian.PutUint16(port[:], uint16(ip.Port))
_, err = w.Write(port[:])
if err != nil {
return err
}
}
return nil
}
return tlv.NewTypeForEncodingErr(val, "lnwire.IPV4Addrs")
}
// ipv4AddrsDecoder decodes TLV bytes into IPv4 addresses.
func ipv4AddrsDecoder(r io.Reader, val interface{}, _ *[8]byte,
l uint64) error {
if v, ok := val.(*IPV4Addrs); ok {
if l%(ipv4AddrEncodedSize) != 0 {
return fmt.Errorf("invalid ipv4 list encoding")
}
var (
numAddrs = int(l / ipv4AddrEncodedSize)
addrs = make([]*net.TCPAddr, 0, numAddrs)
ip [4]byte
port [2]byte
)
for len(addrs) < numAddrs {
_, err := r.Read(ip[:])
if err != nil {
return err
}
_, err = r.Read(port[:])
if err != nil {
return err
}
addrs = append(addrs, &net.TCPAddr{
IP: ip[:],
Port: int(binary.BigEndian.Uint16(port[:])),
})
}
*v = addrs
return nil
}
return tlv.NewTypeForEncodingErr(val, "lnwire.IPV4Addrs")
}
// IPV6Addrs is a list of ipv6 addresses that can be encoded as a TLV record.
type IPV6Addrs []*net.TCPAddr
// Record returns a Record that can be used to encode/decode a IPV4Addrs
// to/from a TLV stream.
func (a *IPV6Addrs) Record() tlv.Record {
return tlv.MakeDynamicRecord(
0, a, a.encodedSize, ipv6AddrsEncoder, ipv6AddrsDecoder,
)
}
// ipv6AddrEncodedSize is the number of bytes required to encode a single ipv6
// address. Sixteen bytes are used to encode the IP address and two bytes for
// the port number.
const ipv6AddrEncodedSize = 16 + 2
// encodedSize returns the number of bytes required to encode an IPV6Addrs
// variable.
func (a *IPV6Addrs) encodedSize() uint64 {
return uint64(len(*a) * ipv6AddrEncodedSize)
}
// ipv6AddrsEncoder encodes IPv6 addresses as TLV bytes.
func ipv6AddrsEncoder(w io.Writer, val interface{}, _ *[8]byte) error {
if v, ok := val.(*IPV6Addrs); ok {
for _, ip := range *v {
_, err := w.Write(ip.IP.To16())
if err != nil {
return err
}
var port [2]byte
binary.BigEndian.PutUint16(port[:], uint16(ip.Port))
_, err = w.Write(port[:])
if err != nil {
return err
}
}
return nil
}
return tlv.NewTypeForEncodingErr(val, "lnwire.IPV6Addrs")
}
// ipv6AddrsDecoder decodes TLV bytes into IPv6 addresses.
func ipv6AddrsDecoder(r io.Reader, val interface{}, _ *[8]byte,
l uint64) error {
if v, ok := val.(*IPV6Addrs); ok {
if l%(ipv6AddrEncodedSize) != 0 {
return fmt.Errorf("invalid ipv6 list encoding")
}
var (
numAddrs = int(l / ipv6AddrEncodedSize)
addrs = make([]*net.TCPAddr, 0, numAddrs)
ip [16]byte
port [2]byte
)
for len(addrs) < numAddrs {
_, err := r.Read(ip[:])
if err != nil {
return err
}
_, err = r.Read(port[:])
if err != nil {
return err
}
addrs = append(addrs, &net.TCPAddr{
IP: ip[:],
Port: int(binary.BigEndian.Uint16(port[:])),
})
}
*v = addrs
return nil
}
return tlv.NewTypeForEncodingErr(val, "lnwire.IPV6Addrs")
}
// TorV3Addrs is a list of tor v3 addresses that can be encoded as a TLV record.
type TorV3Addrs []*tor.OnionAddr
// torV3AddrEncodedSize is the number of bytes required to encode a single tor
// v3 address.
const torV3AddrEncodedSize = tor.V3DecodedLen + 2
// encodedSize returns the number of bytes required to encode an TorV3Addrs
// variable.
func (a *TorV3Addrs) encodedSize() uint64 {
return uint64(len(*a) * torV3AddrEncodedSize)
}
// Record returns a Record that can be used to encode/decode a IPV4Addrs
// to/from a TLV stream.
func (a *TorV3Addrs) Record() tlv.Record {
return tlv.MakeDynamicRecord(
0, a, a.encodedSize, torV3AddrsEncoder, torV3AddrsDecoder,
)
}
// torV3AddrsEncoder encodes Tor v3 addresses as TLV bytes.
func torV3AddrsEncoder(w io.Writer, val interface{}, _ *[8]byte) error {
if v, ok := val.(*TorV3Addrs); ok {
for _, addr := range *v {
encodedHostLen := tor.V3Len - tor.OnionSuffixLen
host, err := tor.Base32Encoding.DecodeString(
addr.OnionService[:encodedHostLen],
)
if err != nil {
return err
}
if len(host) != tor.V3DecodedLen {
return fmt.Errorf("expected a tor v3 host "+
"length of %d, got: %d",
tor.V3DecodedLen, len(host))
}
if _, err = w.Write(host); err != nil {
return err
}
var port [2]byte
binary.BigEndian.PutUint16(port[:], uint16(addr.Port))
if _, err = w.Write(port[:]); err != nil {
return err
}
}
return nil
}
return tlv.NewTypeForEncodingErr(val, "lnwire.TorV3Addrs")
}
// torV3AddrsDecoder decodes TLV bytes into Tor v3 addresses.
func torV3AddrsDecoder(r io.Reader, val interface{}, _ *[8]byte,
l uint64) error {
if v, ok := val.(*TorV3Addrs); ok {
if l%torV3AddrEncodedSize != 0 {
return fmt.Errorf("invalid tor v3 list encoding")
}
var (
numAddrs = int(l / torV3AddrEncodedSize)
addrs = make([]*tor.OnionAddr, 0, numAddrs)
ip [tor.V3DecodedLen]byte
p [2]byte
)
for len(addrs) < numAddrs {
_, err := r.Read(ip[:])
if err != nil {
return err
}
_, err = r.Read(p[:])
if err != nil {
return err
}
onionService := tor.Base32Encoding.EncodeToString(ip[:])
onionService += tor.OnionSuffix
if len(onionService) != tor.V3Len {
return fmt.Errorf("expected a tor v3 onion "+
"service length of %d, got: %d",
tor.V3Len, len(onionService))
}
port := int(binary.BigEndian.Uint16(p[:]))
addrs = append(addrs, &tor.OnionAddr{
OnionService: onionService,
Port: port,
})
}
*v = addrs
return nil
}
return tlv.NewTypeForEncodingErr(val, "lnwire.TorV3Addrs")
}
+119
View File
@@ -0,0 +1,119 @@
package lnwire
import (
"bytes"
"testing"
"github.com/lightningnetwork/lnd/tlv"
"github.com/stretchr/testify/require"
)
// TestNodeAnn2EncodeDecode tests the encoding and decoding of the
// NodeAnnouncement2 message using hardcoded byte slices.
func TestNodeAnn2EncodeDecode(t *testing.T) {
t.Parallel()
// We'll create a raw byte stream that represents a valid
// NodeAnnouncement2 message with various known and unknown fields in
// the signed TLV ranges along with the signature in the unsigned range.
rawBytes := []byte{
// Features record.
0x00, // type.
0x02, // length.
0x1, 0x2, // value.
// Color record (optional).
0x01, // type.
0x03, // length.
0xff, 0x00, 0x80, // value: RGB(255, 0, 128).
// BlockHeight record.
0x02, // type.
0x04, // length.
0x00, 0x01, 0x86, 0xa0, // value: 100000.
// Alias record (optional).
0x03, // type.
0x20, // length (32).
0x4c, 0x69, 0x67, 0x68, 0x74, 0x6e, 0x69, 0x6e,
0x67, 0x20, 0x4e, 0x6f, 0x64, 0x65, 0x20, 0x54,
0x65, 0x73, 0x74, 0x20, 0x41, 0x6c, 0x69, 0x61,
0x73, 0x20, 0x4e, 0x61, 0x6d, 0x65, 0x21, 0x00, // value.
// NodeID record.
0x04, // type.
0x21, // length.
0x02, 0x28, 0xf2, 0xaf, 0xa, 0xbe, 0x32, 0x24, 0x3, 0x48, 0xf,
0xb3, 0xee, 0x17, 0x2f, 0x7f, 0x16, 0x1, 0xe6, 0x7d, 0x1d, 0xa6,
0xca, 0xd4, 0xb, 0x54, 0xc4, 0x46, 0x8d, 0x48, 0x23, 0x6c, 0x39,
// IPV4Addrs record (optional).
0x05, // type.
0x0c, // length (12 bytes).
0x0a, 0x00, 0x00, 0x01, 0x23, 0x28, // 10.0.0.1:9000
0x0a, 0x00, 0x00, 0x01, 0x1f, 0x90, // 10.0.0.1:8080
// IPV6Addrs record (optional).
0x07, // type.
0x12, // length.
0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00, // IPv6 address
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, // 2001:db8::1
0x1f, 0x40, // port 8000
// TorV3Addrs record (optional).
0x09, // type.
0x25, // length (37 bytes = 1 address).
// value: 35-byte tor v3 address + 2-byte port.
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10,
0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
0x21, 0x22, 0x23,
0x23, 0x28, // port 9000.
// DNSHostName record (optional).
0x0b, // type (11).
0x17, // length (23).
0x6c, 0x69, 0x67, 0x68, 0x74, 0x6e, 0x69, 0x6e,
0x67, 0x2e, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, // value.
0x65, 0x2e, 0x63, 0x6f, 0x6d,
0x23, 0x28, // port 9000.
// Signature.
0xa0, // type.
0x40, // length.
0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb,
0xc, 0xd, 0xe, 0xf, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a,
0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34,
0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e,
0x3f, // value.
}
secondSignedRangeType := new(bytes.Buffer)
var buf [8]byte
err := tlv.WriteVarInt(
secondSignedRangeType, pureTLVSignedSecondRangeStart+1, &buf,
)
require.NoError(t, err)
rawBytes = append(rawBytes, secondSignedRangeType.Bytes()...) // type.
rawBytes = append(rawBytes, []byte{
0x02, // length.
0x79, 0x79, // value.
}...)
// Now, create a new empty message and decode the raw bytes into it.
msg := &NodeAnnouncement2{}
r := bytes.NewReader(rawBytes)
err = msg.Decode(r, 0)
require.NoError(t, err)
// Next, encode the message back into a new byte buffer.
var b bytes.Buffer
err = msg.Encode(&b, 0)
require.NoError(t, err)
// The re-encoded bytes should be exactly the same as the original raw
// bytes.
require.Equal(t, rawBytes, b.Bytes())
}
+53
View File
@@ -0,0 +1,53 @@
package lnwire
import (
"bytes"
"io"
"github.com/btcsuite/btcd/wire"
"github.com/lightningnetwork/lnd/tlv"
)
// OutPoint describes an outpoint of a transaction via its transaction hash
// and the outpoint index. This is a thin wrapper around the wire.OutPoint to
// provide TLV encoding/decoding.
type OutPoint wire.OutPoint
// Record returns a TLV record that can be used to encode/decode the OutPoint.
//
// NOTE: this is part of the tlv.RecordProducer interface.
func (o *OutPoint) Record() tlv.Record {
return tlv.MakeStaticRecord(0, o, 34, outpointEncoder, outpointDecoder)
}
// outpointEncoder is a TLV encoder for OutPoint.
func outpointEncoder(w io.Writer, val any, _ *[8]byte) error {
if v, ok := val.(*OutPoint); ok {
buf := bytes.NewBuffer(nil)
err := WriteOutPoint(buf, wire.OutPoint(*v))
if err != nil {
return err
}
_, err = w.Write(buf.Bytes())
return err
}
return tlv.NewTypeForEncodingErr(val, "OutPoint")
}
// outpointDecoder is a TLV decoder for OutPoint.
func outpointDecoder(r io.Reader, val any, _ *[8]byte, l uint64) error {
if v, ok := val.(*OutPoint); ok {
var o wire.OutPoint
if err := ReadElement(r, &o); err != nil {
return err
}
*v = OutPoint(o)
return nil
}
return tlv.NewTypeForDecodingErr(val, "OutPoint", l, 34)
}
+107
View File
@@ -0,0 +1,107 @@
package lnwire
import (
"bytes"
"testing"
"github.com/btcsuite/btcd/chaincfg/chainhash"
"github.com/btcsuite/btcd/wire"
"github.com/lightningnetwork/lnd/tlv"
"github.com/stretchr/testify/require"
"pgregory.net/rapid"
)
// TestOutPointTLVEncoding tests the TLV encoding and decoding of OutPoint
// structs using the Record interface.
func TestOutPointTLVEncoding(t *testing.T) {
t.Parallel()
testOutPoint := OutPoint(wire.OutPoint{
Hash: chainhash.Hash{
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10,
0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
},
Index: 12345,
})
var extraData ExtraOpaqueData
require.NoError(t, extraData.PackRecords(&testOutPoint))
var decodedOutPoint OutPoint
tlvs, err := extraData.ExtractRecords(&decodedOutPoint)
require.NoError(t, err)
require.Contains(t, tlvs, tlv.Type(0))
require.Equal(t, testOutPoint, decodedOutPoint)
}
// TestOutPointRecord tests the TLV Record interface of OutPoint
// by directly encoding and decoding using the Record method.
func TestOutPointRecord(t *testing.T) {
t.Parallel()
testOutPoint := OutPoint(wire.OutPoint{
Hash: chainhash.Hash{
0xff, 0xfe, 0xfd, 0xfc, 0xfb, 0xfa, 0xf9, 0xf8,
0xf7, 0xf6, 0xf5, 0xf4, 0xf3, 0xf2, 0xf1, 0xf0,
0xef, 0xee, 0xed, 0xec, 0xeb, 0xea, 0xe9, 0xe8,
0xe7, 0xe6, 0xe5, 0xe4, 0xe3, 0xe2, 0xe1, 0xe0,
},
Index: 65535,
})
var buf bytes.Buffer
record := testOutPoint.Record()
require.NoError(t, record.Encode(&buf))
var decodedOutPoint OutPoint
decodedRecord := decodedOutPoint.Record()
require.NoError(t, decodedRecord.Decode(&buf, uint64(buf.Len())))
require.Equal(t, testOutPoint, decodedOutPoint)
}
// TestOutPointProperty uses property-based testing to verify that OutPoint
// TLV encoding and decoding is correct for random OutPoint values.
func TestOutPointProperty(t *testing.T) {
t.Parallel()
scenario := func(t *rapid.T) {
wireOutPoint := RandOutPoint(t)
lnOutPoint := OutPoint(wireOutPoint)
var buf bytes.Buffer
record := lnOutPoint.Record()
err := record.Encode(&buf)
require.NoError(t, err)
var decodedOutPoint OutPoint
decodedRecord := decodedOutPoint.Record()
err = decodedRecord.Decode(&buf, uint64(buf.Len()))
require.NoError(t, err)
require.Equal(t, lnOutPoint, decodedOutPoint)
require.Equal(t, wireOutPoint, wire.OutPoint(decodedOutPoint))
}
rapid.Check(t, scenario)
}
// TestOutPointZeroValues tests that OutPoint handles zero values correctly.
func TestOutPointZeroValues(t *testing.T) {
t.Parallel()
zeroOutPoint := OutPoint(wire.OutPoint{})
var buf bytes.Buffer
record := zeroOutPoint.Record()
require.NoError(t, record.Encode(&buf))
var decodedOutPoint OutPoint
decodedRecord := decodedOutPoint.Record()
require.NoError(t, decodedRecord.Decode(&buf, uint64(buf.Len())))
require.Equal(t, zeroOutPoint, decodedOutPoint)
}
+172 -4
View File
@@ -5,12 +5,14 @@ import (
"fmt"
"image/color"
"math"
"net"
"github.com/btcsuite/btcd/btcec/v2"
"github.com/btcsuite/btcd/btcutil"
"github.com/btcsuite/btcd/chaincfg/chainhash"
"github.com/lightningnetwork/lnd/fn/v2"
"github.com/lightningnetwork/lnd/tlv"
"github.com/lightningnetwork/lnd/tor"
"github.com/stretchr/testify/require"
"pgregory.net/rapid"
)
@@ -291,6 +293,10 @@ func (c *ChannelAnnouncement2) RandTestMessage(t *rapid.T) Message {
)
}
msg.Outpoint = tlv.NewRecordT[tlv.TlvType18, OutPoint](
OutPoint(RandOutPoint(t)),
)
return msg
}
@@ -565,6 +571,18 @@ func (c *ChannelUpdate2) RandTestMessage(t *rapid.T) Message {
ExtraSignedFields: make(map[uint64][]byte),
}
if rapid.Bool().Draw(t, "includeInboundFee") {
base := rapid.IntRange(-1000, 1000).Draw(t, "inFeeBase")
rate := rapid.IntRange(-1000, 1000).Draw(t, "inFeeProp")
fee := Fee{
BaseFee: int32(base),
FeeRate: int32(rate),
}
msg.InboundFee = tlv.SomeRecordT(
tlv.NewRecordT[tlv.TlvType55555](fee),
)
}
msg.Signature.Val = RandSignature(t)
msg.Signature.Val.ForceSchnorr()
@@ -1211,15 +1229,19 @@ func (ks *KickoffSig) RandTestMessage(t *rapid.T) Message {
}
}
// A compile time check to ensure NodeAnnouncement implements the
// A compile time check to ensure NodeAnnouncement1 implements the
// lnwire.TestMessage interface.
var _ TestMessage = (*NodeAnnouncement)(nil)
var _ TestMessage = (*NodeAnnouncement1)(nil)
// A compile time check to ensure NodeAnnouncement2 implements the
// lnwire.TestMessage interface.
var _ TestMessage = (*NodeAnnouncement2)(nil)
// RandTestMessage populates the message with random data suitable for testing.
// It uses the rapid testing framework to generate random values.
//
// This is part of the TestMessage interface.
func (a *NodeAnnouncement) RandTestMessage(t *rapid.T) Message {
func (a *NodeAnnouncement1) RandTestMessage(t *rapid.T) Message {
// Generate random compressed public key for node ID
pubKey := RandPubKey(t)
var nodeID [33]byte
@@ -1232,7 +1254,7 @@ func (a *NodeAnnouncement) RandTestMessage(t *rapid.T) Message {
B: uint8(rapid.IntRange(0, 255).Draw(t, "rgbB")),
}
return &NodeAnnouncement{
return &NodeAnnouncement1{
Signature: RandSignature(t),
Features: RandFeatureVector(t),
Timestamp: uint32(rapid.IntRange(0, 0x7FFFFFFF).Draw(
@@ -1246,6 +1268,131 @@ func (a *NodeAnnouncement) RandTestMessage(t *rapid.T) Message {
}
}
// RandTestMessage populates the message with random data suitable for testing.
// It uses the rapid testing framework to generate random values.
//
// This is part of the TestMessage interface.
func (n *NodeAnnouncement2) RandTestMessage(t *rapid.T) Message {
features := RandFeatureVector(t)
blockHeight := uint32(rapid.IntRange(0, 1000000).Draw(t, "blockHeight"))
// Generate random compressed public key for node ID
pubKey := RandPubKey(t)
var nodeID [33]byte
copy(nodeID[:], pubKey.SerializeCompressed())
msg := &NodeAnnouncement2{
Features: tlv.NewRecordT[tlv.TlvType0](*features),
BlockHeight: tlv.NewPrimitiveRecord[tlv.TlvType2](
blockHeight,
),
NodeID: tlv.NewPrimitiveRecord[tlv.TlvType4](nodeID),
ExtraSignedFields: make(map[uint64][]byte),
}
msg.Signature.Val = RandSignature(t)
msg.Signature.Val.ForceSchnorr()
// Test optional fields one by one
if rapid.Bool().Draw(t, "includeColor") {
rgbColor := Color{
R: uint8(rapid.IntRange(0, 255).Draw(t, "rgbR")),
G: uint8(rapid.IntRange(0, 255).Draw(t, "rgbG")),
B: uint8(rapid.IntRange(0, 255).Draw(t, "rgbB")),
}
colorRecord := tlv.ZeroRecordT[tlv.TlvType1, Color]()
colorRecord.Val = rgbColor
msg.Color = tlv.SomeRecordT(colorRecord)
}
if rapid.Bool().Draw(t, "includeAlias") {
aliasBytes := RandNodeAlias2(t)
aliasRecord := tlv.ZeroRecordT[tlv.TlvType3, NodeAlias2]()
aliasRecord.Val = aliasBytes
msg.Alias = tlv.SomeRecordT(aliasRecord)
}
if rapid.Bool().Draw(t, "includeIPV4Addrs") {
ipv4Addrs := make(IPV4Addrs, 1)
ip := make(net.IP, 4)
ip[0] = uint8(rapid.IntRange(1, 223).Draw(t, "ip4_0"))
ip[1] = uint8(rapid.IntRange(0, 255).Draw(t, "ip4_1"))
ip[2] = uint8(rapid.IntRange(0, 255).Draw(t, "ip4_2"))
ip[3] = uint8(rapid.IntRange(1, 254).Draw(t, "ip4_3"))
ipv4Addrs[0] = &net.TCPAddr{
IP: ip,
Port: rapid.IntRange(1, 65535).Draw(t, "port4"),
}
ipv4Record := tlv.ZeroRecordT[tlv.TlvType5, IPV4Addrs]()
ipv4Record.Val = ipv4Addrs
msg.IPV4Addrs = tlv.SomeRecordT(ipv4Record)
}
if rapid.Bool().Draw(t, "includeIPV6Addrs") {
ipv6Addrs := make(IPV6Addrs, 1)
ip := make(net.IP, 16)
// Generate random IPv6 address.
for j := 0; j < 16; j++ {
ip[j] = uint8(rapid.IntRange(0, 255).Draw(
t, fmt.Sprintf("ip6_%d", j)),
)
}
ipv6Addrs[0] = &net.TCPAddr{
IP: ip,
Port: rapid.IntRange(1, 65535).Draw(t, "port6"),
}
ipv6Record := tlv.ZeroRecordT[tlv.TlvType7, IPV6Addrs]()
ipv6Record.Val = ipv6Addrs
msg.IPV6Addrs = tlv.SomeRecordT(ipv6Record)
}
if rapid.Bool().Draw(t, "includeTorV3Addrs") {
torV3Addrs := make(TorV3Addrs, 1)
onionBytes := rapid.SliceOfN(rapid.Byte(), 35, 35).Draw(
t, "onion",
)
onionService := tor.Base32Encoding.EncodeToString(onionBytes) +
tor.OnionSuffix
torV3Addrs[0] = &tor.OnionAddr{
OnionService: onionService,
Port: rapid.IntRange(1, 65535).Draw(
t, "torPort",
),
}
torV3Record := tlv.ZeroRecordT[tlv.TlvType9, TorV3Addrs]()
torV3Record.Val = torV3Addrs
msg.TorV3Addrs = tlv.SomeRecordT(torV3Record)
}
if rapid.Bool().Draw(t, "includeDNSHostName") {
// Generate a valid DNS hostname.
hostname := genValidHostname(t)
port := rapid.Uint16Range(1, 65535).Draw(t, "dnsPort")
dnsAddr := DNSAddress{
Hostname: hostname,
Port: port,
}
dnsRecord := tlv.ZeroRecordT[tlv.TlvType11, DNSAddress]()
dnsRecord.Val = dnsAddr
msg.DNSHostName = tlv.SomeRecordT(dnsRecord)
}
randRecs, _ := RandSignedRangeRecords(t)
if len(randRecs) > 0 {
msg.ExtraSignedFields = ExtraSignedFields(randRecs)
}
return msg
}
// A compile time check to ensure OpenChannel implements the TestMessage
// interface.
var _ TestMessage = (*OpenChannel)(nil)
@@ -1851,3 +1998,24 @@ func (c *Error) RandTestMessage(t *rapid.T) Message {
return msg
}
// genValidHostname generates a random valid hostname according to BOLT #7
// rules.
func genValidHostname(t *rapid.T) string {
// Valid characters: a-z, A-Z, 0-9, -, .
validChars := "abcdefghijklmnopqrstuvwxyzABCDE" +
"FGHIJKLMNOPQRSTUVWXYZ0123456789-."
// Generate hostname length between 1 and 255 characters.
length := rapid.IntRange(1, 255).Draw(t, "hostname_length")
hostname := make([]byte, length)
for i := 0; i < length; i++ {
charIndex := rapid.IntRange(0, len(validChars)-1).Draw(
t, fmt.Sprintf("char_%d", i),
)
hostname[i] = validChars[charIndex]
}
return string(hostname)
}
+13
View File
@@ -152,6 +152,19 @@ func RandNodeAlias(t *rapid.T) NodeAlias {
return alias
}
// RandNodeAlias2 generates a random node alias that is compatible with
// NodeAlias2 validation (non-empty, max 32 bytes, valid UTF-8).
func RandNodeAlias2(t *rapid.T) NodeAlias2 {
// Ensure length is at least 1 to satisfy NodeAlias2 validation
aliasLength := rapid.IntRange(1, 32).Draw(t, "aliasLength")
aliasBytes := rapid.SliceOfN(
rapid.SampledFrom([]rune(charset)), aliasLength, aliasLength,
).Draw(t, "alias")
return []byte(string(aliasBytes))
}
// RandNetAddrs generates random network addresses.
func RandNetAddrs(t *rapid.T) []net.Addr {
numAddresses := rapid.IntRange(0, 5).Draw(t, "numAddresses")
+5 -5
View File
@@ -22,8 +22,8 @@ type HostAnnouncerConfig struct {
LookupHost func(string) (net.Addr, error)
// AdvertisedIPs is the set of IPs that we've already announced with
// our current NodeAnnouncement. This set will be constructed to avoid
// unnecessary node NodeAnnouncement updates.
// our current NodeAnnouncement1. This set will be constructed to avoid
// unnecessary node NodeAnnouncement1 updates.
AdvertisedIPs map[string]struct{}
// AnnounceNewIPs announces a new set of IP addresses for the backing
@@ -36,7 +36,7 @@ type HostAnnouncerConfig struct {
// HostAnnouncer is a sub-system that allows a user to specify a set of hosts
// for lnd that will be continually resolved to notice any IP address changes.
// If the target IP address for a host changes, then we'll generate a new
// NodeAnnouncement that includes these new IPs.
// NodeAnnouncement1 that includes these new IPs.
type HostAnnouncer struct {
cfg HostAnnouncerConfig
@@ -171,7 +171,7 @@ func (h *HostAnnouncer) hostWatcher() {
// announcement on disk. It returns the updated node announcement given a set
// of updates to be applied to the current node announcement.
type NodeAnnUpdater func(modifier ...NodeAnnModifier,
) (lnwire.NodeAnnouncement, error)
) (lnwire.NodeAnnouncement1, error)
// IPAnnouncer is a factory function that generates a new function that uses
// the passed annUpdater function to to announce new IP changes for a given
@@ -181,7 +181,7 @@ func IPAnnouncer(annUpdater NodeAnnUpdater) func([]net.Addr,
return func(newAddrs []net.Addr, oldAddrs map[string]struct{}) error {
_, err := annUpdater(func(
currentNodeAnn *lnwire.NodeAnnouncement) {
currentNodeAnn *lnwire.NodeAnnouncement1) {
// To ensure we don't duplicate any addresses, we'll
// filter out the same of addresses we should no longer
// advertise.
+19 -17
View File
@@ -16,37 +16,39 @@ import (
)
// NodeAnnModifier is a closure that makes in-place modifications to an
// lnwire.NodeAnnouncement.
type NodeAnnModifier func(*lnwire.NodeAnnouncement)
// lnwire.NodeAnnouncement1.
type NodeAnnModifier func(*lnwire.NodeAnnouncement1)
// NodeAnnSetAlias is a functional option that sets the alias of the
// given node announcement.
func NodeAnnSetAlias(alias lnwire.NodeAlias) func(*lnwire.NodeAnnouncement) {
return func(nodeAnn *lnwire.NodeAnnouncement) {
func NodeAnnSetAlias(alias lnwire.NodeAlias) func(*lnwire.NodeAnnouncement1) {
return func(nodeAnn *lnwire.NodeAnnouncement1) {
nodeAnn.Alias = alias
}
}
// 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) {
func NodeAnnSetAddrs(addrs []net.Addr) func(*lnwire.NodeAnnouncement1) {
return func(nodeAnn *lnwire.NodeAnnouncement1) {
nodeAnn.Addresses = addrs
}
}
// NodeAnnSetColor is a functional option that sets the color of the
// given node announcement.
func NodeAnnSetColor(newColor color.RGBA) func(*lnwire.NodeAnnouncement) {
return func(nodeAnn *lnwire.NodeAnnouncement) {
func NodeAnnSetColor(newColor color.RGBA) func(*lnwire.NodeAnnouncement1) {
return func(nodeAnn *lnwire.NodeAnnouncement1) {
nodeAnn.RGBColor = newColor
}
}
// NodeAnnSetFeatures is a functional option that allows updating the features of
// the given node announcement.
func NodeAnnSetFeatures(features *lnwire.RawFeatureVector) func(*lnwire.NodeAnnouncement) {
return func(nodeAnn *lnwire.NodeAnnouncement) {
func NodeAnnSetFeatures(
features *lnwire.RawFeatureVector) func(*lnwire.NodeAnnouncement1) {
return func(nodeAnn *lnwire.NodeAnnouncement1) {
nodeAnn.Features = features
}
}
@@ -54,7 +56,7 @@ func NodeAnnSetFeatures(features *lnwire.RawFeatureVector) func(*lnwire.NodeAnno
// 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) {
func NodeAnnSetTimestamp(nodeAnn *lnwire.NodeAnnouncement1) {
newTimestamp := uint32(time.Now().Unix())
if newTimestamp <= nodeAnn.Timestamp {
// Increment the prior value to ensure the timestamp
@@ -65,11 +67,11 @@ func NodeAnnSetTimestamp(nodeAnn *lnwire.NodeAnnouncement) {
nodeAnn.Timestamp = newTimestamp
}
// SignNodeAnnouncement signs the lnwire.NodeAnnouncement provided, which
// SignNodeAnnouncement signs the lnwire.NodeAnnouncement1 provided, which
// should be the most recent, valid update, otherwise the timestamp may not
// monotonically increase from the prior.
func SignNodeAnnouncement(signer lnwallet.MessageSigner,
keyLoc keychain.KeyLocator, nodeAnn *lnwire.NodeAnnouncement) error {
keyLoc keychain.KeyLocator, nodeAnn *lnwire.NodeAnnouncement1) error {
// Create the DER-encoded ECDSA signature over the message digest.
sig, err := SignAnnouncement(signer, keyLoc, nodeAnn)
@@ -83,7 +85,7 @@ func SignNodeAnnouncement(signer lnwallet.MessageSigner,
}
// ValidateNodeAnn validates the fields and signature of a node announcement.
func ValidateNodeAnn(a *lnwire.NodeAnnouncement) error {
func ValidateNodeAnn(a *lnwire.NodeAnnouncement1) error {
err := ValidateNodeAnnFields(a)
if err != nil {
return fmt.Errorf("invalid node announcement fields: %w", err)
@@ -93,7 +95,7 @@ func ValidateNodeAnn(a *lnwire.NodeAnnouncement) error {
}
// ValidateNodeAnnFields validates the fields of a node announcement.
func ValidateNodeAnnFields(a *lnwire.NodeAnnouncement) error {
func ValidateNodeAnnFields(a *lnwire.NodeAnnouncement1) error {
// Check that it only has at most one DNS address.
hasDNSAddr := false
for _, addr := range a.Addresses {
@@ -120,7 +122,7 @@ func ValidateNodeAnnFields(a *lnwire.NodeAnnouncement) error {
// ValidateNodeAnnSignature validates the node announcement by ensuring that the
// attached signature is needed a signature of the node announcement under the
// specified node public key.
func ValidateNodeAnnSignature(a *lnwire.NodeAnnouncement) error {
func ValidateNodeAnnSignature(a *lnwire.NodeAnnouncement1) error {
// Reconstruct the data of announcement which should be covered by the
// signature so we can verify the signature shortly below
data, err := a.DataToSign()
@@ -146,7 +148,7 @@ func ValidateNodeAnnSignature(a *lnwire.NodeAnnouncement) error {
return err
}
return fmt.Errorf("signature on NodeAnnouncement(%x) is "+
return fmt.Errorf("signature on NodeAnnouncement1(%x) is "+
"invalid: %x", nodeKey.SerializeCompressed(),
msgBuf.Bytes())
}
+1 -1
View File
@@ -24,7 +24,7 @@ func SignAnnouncement(signer lnwallet.MessageSigner, keyLoc keychain.KeyLocator,
data, err = m.DataToSign()
case *lnwire.ChannelUpdate1:
data, err = m.DataToSign()
case *lnwire.NodeAnnouncement:
case *lnwire.NodeAnnouncement1:
data, err = m.DataToSign()
default:
return nil, fmt.Errorf("can't sign %T message", m)
+4 -4
View File
@@ -329,7 +329,7 @@ type Config struct {
// GenNodeAnnouncement is used to send our node announcement to the remote
// on startup.
GenNodeAnnouncement func(...netann.NodeAnnModifier) (
lnwire.NodeAnnouncement, error)
lnwire.NodeAnnouncement1, error)
// PrunePersistentPeerConnection is used to remove all internal state
// related to this peer in the server.
@@ -2092,7 +2092,7 @@ out:
idleTimer.Reset(idleTimeout)
continue
// If the NodeAnnouncement has an invalid alias, then
// If the NodeAnnouncement1 has an invalid alias, then
// we'll log that error above and continue so we can
// continue to read messages from the peer. We do not
// store this error because it is of little debugging
@@ -2213,7 +2213,7 @@ out:
case *lnwire.ChannelUpdate1,
*lnwire.ChannelAnnouncement1,
*lnwire.NodeAnnouncement,
*lnwire.NodeAnnouncement1,
*lnwire.AnnounceSignatures1,
*lnwire.GossipTimestampRange,
*lnwire.QueryShortChanIDs,
@@ -2494,7 +2494,7 @@ func messageSummary(msg lnwire.Message) string {
msg.ShortChannelID.ToUint64(), msg.MessageFlags,
msg.ChannelFlags, time.Unix(int64(msg.Timestamp), 0))
case *lnwire.NodeAnnouncement:
case *lnwire.NodeAnnouncement1:
return fmt.Sprintf("node=%x, update_time=%v",
msg.NodeID, time.Unix(int64(msg.Timestamp), 0))
+11 -11
View File
@@ -400,7 +400,7 @@ type server struct {
// currentNodeAnn is the node announcement that has been broadcast to
// the network upon startup, if the attributes of the node (us) has
// changed since last start.
currentNodeAnn *lnwire.NodeAnnouncement
currentNodeAnn *lnwire.NodeAnnouncement1
// chansToRestore is the set of channels that upon starting, the server
// should attempt to restore/recover.
@@ -1069,7 +1069,7 @@ func newServer(ctx context.Context, cfg *Config, listenAddrs []net.Addr,
NotifyWhenOnline: s.NotifyWhenOnline,
NotifyWhenOffline: s.NotifyWhenOffline,
FetchSelfAnnouncement: s.getNodeAnnouncement,
UpdateSelfAnnouncement: func() (lnwire.NodeAnnouncement,
UpdateSelfAnnouncement: func() (lnwire.NodeAnnouncement1,
error) {
return s.genNodeAnnouncement(nil)
@@ -1466,7 +1466,7 @@ func newServer(ctx context.Context, cfg *Config, listenAddrs []net.Addr,
ChannelDB: s.chanStateDB,
FeeEstimator: cc.FeeEstimator,
SignMessage: cc.MsgSigner.SignMessage,
CurrentNodeAnnouncement: func() (lnwire.NodeAnnouncement,
CurrentNodeAnnouncement: func() (lnwire.NodeAnnouncement1,
error) {
return s.genNodeAnnouncement(nil)
@@ -1796,7 +1796,7 @@ func newServer(ctx context.Context, cfg *Config, listenAddrs []net.Addr,
AdvertisedIPs: advertisedIPs,
AnnounceNewIPs: netann.IPAnnouncer(
func(modifier ...netann.NodeAnnModifier) (
lnwire.NodeAnnouncement, error) {
lnwire.NodeAnnouncement1, error) {
return s.genNodeAnnouncement(
nil, modifier...,
@@ -3306,7 +3306,7 @@ func (s *server) createNewHiddenService(ctx context.Context) error {
// Now that the onion service has been created, we'll add the onion
// address it can be reached at to our list of advertised addresses.
newNodeAnn, err := s.genNodeAnnouncement(
nil, func(currentAnn *lnwire.NodeAnnouncement) {
nil, func(currentAnn *lnwire.NodeAnnouncement1) {
currentAnn.Addresses = append(currentAnn.Addresses, addr)
},
)
@@ -3357,7 +3357,7 @@ func (s *server) findChannel(node *btcec.PublicKey, chanID lnwire.ChannelID) (
}
// getNodeAnnouncement fetches the current, fully signed node announcement.
func (s *server) getNodeAnnouncement() lnwire.NodeAnnouncement {
func (s *server) getNodeAnnouncement() lnwire.NodeAnnouncement1 {
s.mu.Lock()
defer s.mu.Unlock()
@@ -3368,7 +3368,7 @@ func (s *server) getNodeAnnouncement() lnwire.NodeAnnouncement {
// announcement. The time stamp of the announcement will be updated in order
// to ensure it propagates through the network.
func (s *server) genNodeAnnouncement(features *lnwire.RawFeatureVector,
modifiers ...netann.NodeAnnModifier) (lnwire.NodeAnnouncement, error) {
modifiers ...netann.NodeAnnModifier) (lnwire.NodeAnnouncement1, error) {
s.mu.Lock()
defer s.mu.Unlock()
@@ -3386,7 +3386,7 @@ func (s *server) genNodeAnnouncement(features *lnwire.RawFeatureVector,
}
err := s.featureMgr.UpdateFeatureSets(proposedFeatures)
if err != nil {
return lnwire.NodeAnnouncement{}, err
return lnwire.NodeAnnouncement1{}, err
}
// If we could successfully update our feature manager, add
@@ -3411,7 +3411,7 @@ func (s *server) genNodeAnnouncement(features *lnwire.RawFeatureVector,
s.nodeSigner, s.identityKeyLoc, &newNodeAnn,
)
if err != nil {
return lnwire.NodeAnnouncement{}, err
return lnwire.NodeAnnouncement1{}, err
}
// If signing succeeds, update the current announcement.
@@ -3501,7 +3501,7 @@ func (s *server) establishPersistentConnections(ctx context.Context) error {
// After checking our previous connections for addresses to connect to,
// iterate through the nodes in our channel graph to find addresses
// that have been added via NodeAnnouncement messages.
// that have been added via NodeAnnouncement1 messages.
// TODO(roasbeef): instead iterate over link nodes and query graph for
// each of the nodes.
graphAddrs := make(map[string]*nodeAddresses)
@@ -4394,7 +4394,7 @@ func (s *server) peerConnected(conn net.Conn, connReq *connmgr.ConnReq,
TowerClient: towerClient,
DisconnectPeer: s.DisconnectPeer,
GenNodeAnnouncement: func(...netann.NodeAnnModifier) (
lnwire.NodeAnnouncement, error) {
lnwire.NodeAnnouncement1, error) {
return s.genNodeAnnouncement(nil)
},
+1 -1
View File
@@ -122,7 +122,7 @@ func (s *subRPCServerConfigs) PopulateDependencies(cfg *Config,
tcpResolver lncfg.TCPResolver,
genInvoiceFeatures func() *lnwire.FeatureVector,
genAmpInvoiceFeatures func() *lnwire.FeatureVector,
getNodeAnnouncement func() lnwire.NodeAnnouncement,
getNodeAnnouncement func() lnwire.NodeAnnouncement1,
updateNodeAnnouncement func(ctx context.Context,
features *lnwire.RawFeatureVector,
modifiers ...netann.NodeAnnModifier) error,