Skip to content

Commit 1f71f14

Browse files
committed
lnwire+netann: update ChannelAnnouncement2 structure
Such that all fields are now TLV (including the signature).
1 parent 5e7ca54 commit 1f71f14

File tree

6 files changed

+279
-96
lines changed

6 files changed

+279
-96
lines changed

lnwire/channel_announcement_2.go

Lines changed: 134 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,6 @@ import (
1212
// ChannelAnnouncement2 message is used to announce the existence of a taproot
1313
// channel between two peers in the network.
1414
type ChannelAnnouncement2 struct {
15-
// Signature is a Schnorr signature over the TLV stream of the message.
16-
Signature Sig
17-
1815
// ChainHash denotes the target chain that this channel was opened
1916
// within. This value should be the genesis hash of the target chain.
2017
ChainHash tlv.RecordT[tlv.TlvType0, chainhash.Hash]
@@ -59,47 +56,107 @@ type ChannelAnnouncement2 struct {
5956
// the funding output is a pure 2-of-2 MuSig aggregate public key.
6057
MerkleRootHash tlv.OptionalRecordT[tlv.TlvType16, [32]byte]
6158

62-
// ExtraOpaqueData is the set of data that was appended to this
63-
// message, some of which we may not actually know how to iterate or
64-
// parse. By holding onto this data, we ensure that we're able to
65-
// properly validate the set of signatures that cover these new fields,
66-
// and ensure we're able to make upgrades to the network in a forwards
67-
// compatible manner.
68-
ExtraOpaqueData ExtraOpaqueData
59+
// Signature is a Schnorr signature over serialised signed-range TLV
60+
// stream of the message.
61+
Signature tlv.RecordT[tlv.TlvType160, Sig]
62+
63+
// Any extra fields in the signed range that we do not yet know about,
64+
// but we need to keep them for signature validation and to produce a
65+
// valid message.
66+
ExtraSignedFields
6967
}
7068

71-
// Decode deserializes a serialized AnnounceSignatures1 stored in the passed
72-
// io.Reader observing the specified protocol version.
69+
// Encode serializes the target AnnounceSignatures1 into the passed io.Writer
70+
// observing the protocol version specified.
7371
//
7472
// This is part of the lnwire.Message interface.
75-
func (c *ChannelAnnouncement2) Decode(r io.Reader, _ uint32) error {
76-
err := ReadElement(r, &c.Signature)
77-
if err != nil {
78-
return err
79-
}
80-
c.Signature.ForceSchnorr()
73+
func (c *ChannelAnnouncement2) Encode(w *bytes.Buffer, _ uint32) error {
74+
return EncodePureTLVMessage(c, w)
75+
}
76+
77+
// AllRecords returns all the TLV records for the message. This will include all
78+
// the records we know about along with any that we don't know about but that
79+
// fall in the signed TLV range.
80+
//
81+
// NOTE: this is part of the PureTLVMessage interface.
82+
func (c *ChannelAnnouncement2) AllRecords() []tlv.Record {
83+
recordProducers := append(
84+
c.allNonSignatureRecordProducers(), &c.Signature,
85+
)
8186

82-
return c.DecodeTLVRecords(r)
87+
return ProduceRecordsSorted(recordProducers...)
8388
}
8489

85-
// DecodeTLVRecords decodes only the TLV section of the message.
86-
func (c *ChannelAnnouncement2) DecodeTLVRecords(r io.Reader) error {
87-
// First extract into extra opaque data.
88-
var tlvRecords ExtraOpaqueData
89-
if err := ReadElements(r, &tlvRecords); err != nil {
90-
return err
90+
// allNonSignatureRecordProducers returns all the TLV record producers for the
91+
// message except the signature record producer.
92+
//
93+
//nolint:ll
94+
func (c *ChannelAnnouncement2) allNonSignatureRecordProducers() []tlv.RecordProducer {
95+
// The chain-hash record is only included if it is _not_ equal to the
96+
// bitcoin mainnet genisis block hash.
97+
var recordProducers []tlv.RecordProducer
98+
if !c.ChainHash.Val.IsEqual(chaincfg.MainNetParams.GenesisHash) {
99+
hash := tlv.ZeroRecordT[tlv.TlvType0, [32]byte]()
100+
hash.Val = c.ChainHash.Val
101+
102+
recordProducers = append(recordProducers, &hash)
91103
}
92104

105+
recordProducers = append(recordProducers,
106+
&c.Features, &c.ShortChannelID, &c.Capacity, &c.NodeID1,
107+
&c.NodeID2,
108+
)
109+
110+
c.BitcoinKey1.WhenSome(func(key tlv.RecordT[tlv.TlvType12, [33]byte]) {
111+
recordProducers = append(recordProducers, &key)
112+
})
113+
114+
c.BitcoinKey2.WhenSome(func(key tlv.RecordT[tlv.TlvType14, [33]byte]) {
115+
recordProducers = append(recordProducers, &key)
116+
})
117+
118+
c.MerkleRootHash.WhenSome(
119+
func(hash tlv.RecordT[tlv.TlvType16, [32]byte]) {
120+
recordProducers = append(recordProducers, &hash)
121+
},
122+
)
123+
124+
recordProducers = append(recordProducers, RecordsAsProducers(
125+
tlv.MapToRecords(c.ExtraSignedFields),
126+
)...)
127+
128+
return recordProducers
129+
}
130+
131+
// Decode deserializes a serialized AnnounceSignatures1 stored in the passed
132+
// io.Reader observing the specified protocol version.
133+
//
134+
// This is part of the lnwire.Message interface.
135+
func (c *ChannelAnnouncement2) Decode(r io.Reader, _ uint32) error {
93136
var (
94137
chainHash = tlv.ZeroRecordT[tlv.TlvType0, [32]byte]()
95138
btcKey1 = tlv.ZeroRecordT[tlv.TlvType12, [33]byte]()
96139
btcKey2 = tlv.ZeroRecordT[tlv.TlvType14, [33]byte]()
97140
merkleRootHash = tlv.ZeroRecordT[tlv.TlvType16, [32]byte]()
98141
)
99-
typeMap, err := tlvRecords.ExtractRecords(
100-
&chainHash, &c.Features, &c.ShortChannelID, &c.Capacity,
101-
&c.NodeID1, &c.NodeID2, &btcKey1, &btcKey2, &merkleRootHash,
102-
)
142+
stream, err := tlv.NewStream(ProduceRecordsSorted(
143+
&chainHash,
144+
&c.Features,
145+
&c.ShortChannelID,
146+
&c.Capacity,
147+
&c.NodeID1,
148+
&c.NodeID2,
149+
&btcKey1,
150+
&btcKey2,
151+
&merkleRootHash,
152+
&c.Signature,
153+
)...)
154+
if err != nil {
155+
return err
156+
}
157+
c.Signature.Val.ForceSchnorr()
158+
159+
typeMap, err := stream.DecodeWithParsedTypesP2P(r)
103160
if err != nil {
104161
return err
105162
}
@@ -122,68 +179,68 @@ func (c *ChannelAnnouncement2) DecodeTLVRecords(r io.Reader) error {
122179
c.MerkleRootHash = tlv.SomeRecordT(merkleRootHash)
123180
}
124181

125-
if len(tlvRecords) != 0 {
126-
c.ExtraOpaqueData = tlvRecords
127-
}
182+
c.ExtraSignedFields = ExtraSignedFieldsFromTypeMap(typeMap)
128183

129-
return c.ExtraOpaqueData.ValidateTLV()
184+
return nil
130185
}
131186

132-
// Encode serializes the target AnnounceSignatures1 into the passed io.Writer
133-
// observing the protocol version specified.
134-
//
135-
// This is part of the lnwire.Message interface.
136-
func (c *ChannelAnnouncement2) Encode(w *bytes.Buffer, _ uint32) error {
137-
_, err := w.Write(c.Signature.RawBytes())
187+
// DecodeNonSigTLVRecords decodes only the TLV section of the message.
188+
func (c *ChannelAnnouncement2) DecodeNonSigTLVRecords(r io.Reader) error {
189+
var (
190+
chainHash = tlv.ZeroRecordT[tlv.TlvType0, [32]byte]()
191+
btcKey1 = tlv.ZeroRecordT[tlv.TlvType12, [33]byte]()
192+
btcKey2 = tlv.ZeroRecordT[tlv.TlvType14, [33]byte]()
193+
merkleRootHash = tlv.ZeroRecordT[tlv.TlvType16, [32]byte]()
194+
)
195+
stream, err := tlv.NewStream(ProduceRecordsSorted(
196+
&chainHash,
197+
&c.Features,
198+
&c.ShortChannelID,
199+
&c.Capacity,
200+
&c.NodeID1,
201+
&c.NodeID2,
202+
&btcKey1,
203+
&btcKey2,
204+
&merkleRootHash,
205+
)...)
138206
if err != nil {
139207
return err
140208
}
141-
_, err = c.DataToSign()
209+
210+
typeMap, err := stream.DecodeWithParsedTypesP2P(r)
142211
if err != nil {
143212
return err
144213
}
145214

146-
return WriteBytes(w, c.ExtraOpaqueData)
147-
}
215+
// By default, the chain-hash is the bitcoin mainnet genesis block hash.
216+
c.ChainHash.Val = *chaincfg.MainNetParams.GenesisHash
217+
if _, ok := typeMap[c.ChainHash.TlvType()]; ok {
218+
c.ChainHash.Val = chainHash.Val
219+
}
148220

149-
// DataToSign encodes the data to be signed into the ExtraOpaqueData member and
150-
// returns it.
151-
func (c *ChannelAnnouncement2) DataToSign() ([]byte, error) {
152-
// The chain-hash record is only included if it is _not_ equal to the
153-
// bitcoin mainnet genisis block hash.
154-
var recordProducers []tlv.RecordProducer
155-
if !c.ChainHash.Val.IsEqual(chaincfg.MainNetParams.GenesisHash) {
156-
hash := tlv.ZeroRecordT[tlv.TlvType0, [32]byte]()
157-
hash.Val = c.ChainHash.Val
221+
if _, ok := typeMap[c.BitcoinKey1.TlvType()]; ok {
222+
c.BitcoinKey1 = tlv.SomeRecordT(btcKey1)
223+
}
158224

159-
recordProducers = append(recordProducers, &hash)
225+
if _, ok := typeMap[c.BitcoinKey2.TlvType()]; ok {
226+
c.BitcoinKey2 = tlv.SomeRecordT(btcKey2)
160227
}
161228

162-
recordProducers = append(recordProducers,
163-
&c.Features, &c.ShortChannelID, &c.Capacity, &c.NodeID1,
164-
&c.NodeID2,
165-
)
229+
if _, ok := typeMap[c.MerkleRootHash.TlvType()]; ok {
230+
c.MerkleRootHash = tlv.SomeRecordT(merkleRootHash)
231+
}
166232

167-
c.BitcoinKey1.WhenSome(func(key tlv.RecordT[tlv.TlvType12, [33]byte]) {
168-
recordProducers = append(recordProducers, &key)
169-
})
233+
c.ExtraSignedFields = ExtraSignedFieldsFromTypeMap(typeMap)
170234

171-
c.BitcoinKey2.WhenSome(func(key tlv.RecordT[tlv.TlvType14, [33]byte]) {
172-
recordProducers = append(recordProducers, &key)
173-
})
235+
return nil
236+
}
174237

175-
c.MerkleRootHash.WhenSome(
176-
func(hash tlv.RecordT[tlv.TlvType16, [32]byte]) {
177-
recordProducers = append(recordProducers, &hash)
178-
},
238+
// EncodeAllNonSigFields encodes the entire message to the given writer but
239+
// excludes the signature field.
240+
func (c *ChannelAnnouncement2) EncodeAllNonSigFields(w io.Writer) error {
241+
return EncodeRecordsTo(
242+
w, ProduceRecordsSorted(c.allNonSignatureRecordProducers()...),
179243
)
180-
181-
err := EncodeMessageExtraData(&c.ExtraOpaqueData, recordProducers...)
182-
if err != nil {
183-
return nil, err
184-
}
185-
186-
return c.ExtraOpaqueData, nil
187244
}
188245

189246
// MsgType returns the integer uniquely identifying this message type on the
@@ -209,6 +266,10 @@ var _ Message = (*ChannelAnnouncement2)(nil)
209266
// lnwire.SizeableMessage interface.
210267
var _ SizeableMessage = (*ChannelAnnouncement2)(nil)
211268

269+
// A compile time check to ensure ChannelAnnouncement2 implements the
270+
// lnwire.PureTLVMessage interface.
271+
var _ PureTLVMessage = (*ChannelAnnouncement2)(nil)
272+
212273
// Node1KeyBytes returns the bytes representing the public key of node 1 in the
213274
// channel.
214275
//
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
package lnwire
2+
3+
import (
4+
"bytes"
5+
"testing"
6+
7+
"github.com/lightningnetwork/lnd/tlv"
8+
"github.com/stretchr/testify/require"
9+
)
10+
11+
// TestChanAnn2EncodeDecode tests the encoding and decoding of the
12+
// ChannelAnnouncement2 message using hardcoded byte slices.
13+
func TestChanAnn2EncodeDecode(t *testing.T) {
14+
t.Parallel()
15+
16+
// We'll create a raw byte stream that represents a valid
17+
// ChannelAnnouncement2 message with various known and unknown fields in
18+
// the signed TLV ranges along with the signature in the unsigned range.
19+
rawBytes := []byte{
20+
// ChainHash record (optional, not mainnet).
21+
0x00, // type.
22+
0x20, // length.
23+
0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1,
24+
0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1,
25+
0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1,
26+
27+
// Features record.
28+
0x02, // type.
29+
0x02, // length.
30+
0x1, 0x2, // value.
31+
32+
// ShortChannelID record.
33+
0x04, // type.
34+
0x08, // length.
35+
0x0, 0x0, 0x1, 0x0, 0x0, 0x2, 0x0, 0x3, // value.
36+
37+
// Unknown TLV record.
38+
0x05, // type.
39+
0x02, // length.
40+
0xab, 0xcd, // value.
41+
42+
// Capacity record.
43+
0x06, // type.
44+
0x08, // length.
45+
0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x86, 0xa0, // value: 100000.
46+
47+
// NodeID1 record.
48+
0x08, // type.
49+
0x21, // length.
50+
0x2, 0x28, 0xf2, 0xaf, 0xa, 0xbe, 0x32, 0x24, 0x3, 0x48, 0xf,
51+
0xb3, 0xee, 0x17, 0x2f, 0x7f, 0x16, 0x1, 0xe6, 0x7d, 0x1d, 0xa6,
52+
0xca, 0xd4, 0xb, 0x54, 0xc4, 0x46, 0x8d, 0x48, 0x23, 0x6c, 0x39,
53+
54+
// NodeID2 record.
55+
0x0a, // type.
56+
0x21, // length.
57+
0x3, 0x28, 0xf2, 0xaf, 0xa, 0xbe, 0x32, 0x24, 0x3, 0x48, 0xf,
58+
0xb3, 0xee, 0x17, 0x2f, 0x7f, 0x16, 0x1, 0xe6, 0x7d, 0x1d, 0xa6,
59+
0xca, 0xd4, 0xb, 0x54, 0xc4, 0x46, 0x8d, 0x48, 0x23, 0x6c, 0x39,
60+
61+
// Unknown TLV record.
62+
0x6f, // type.
63+
0x2, // length.
64+
0x79, 0x79, // value.
65+
66+
// Signature.
67+
0xa0, // type.
68+
0x40, // length.
69+
0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb,
70+
0xc, 0xd, 0xe, 0xf, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
71+
0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
72+
0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a,
73+
0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34,
74+
0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e,
75+
0x3f, // value.
76+
}
77+
secondSignedRangeType := new(bytes.Buffer)
78+
var buf [8]byte
79+
err := tlv.WriteVarInt(
80+
secondSignedRangeType, pureTLVSignedSecondRangeStart+1, &buf,
81+
)
82+
require.NoError(t, err)
83+
rawBytes = append(rawBytes, secondSignedRangeType.Bytes()...) // type.
84+
rawBytes = append(rawBytes, []byte{
85+
0x02, // length.
86+
0x79, 0x79, // value.
87+
}...)
88+
89+
// Now, create a new empty message and decode the raw bytes into it.
90+
msg := &ChannelAnnouncement2{}
91+
r := bytes.NewReader(rawBytes)
92+
err = msg.Decode(r, 0)
93+
require.NoError(t, err)
94+
95+
// Next, encode the message back into a new byte buffer.
96+
var b bytes.Buffer
97+
err = msg.Encode(&b, 0)
98+
require.NoError(t, err)
99+
100+
// The re-encoded bytes should be exactly the same as the original raw
101+
// bytes.
102+
require.Equal(t, rawBytes, b.Bytes())
103+
}

0 commit comments

Comments
 (0)