• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

lightningnetwork / lnd / 16983297902

15 Aug 2025 04:58AM UTC coverage: 66.777% (+0.02%) from 66.758%
16983297902

Pull #10140

github

web-flow
Merge 33b9b55dc into 3841d5554
Pull Request #10140: [2/3] lnwire: fix encoding customized TLV records

178 of 191 new or added lines in 8 files covered. (93.19%)

79 existing lines in 13 files now uncovered.

136014 of 203685 relevant lines covered (66.78%)

21511.77 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

80.81
/lnwire/channel_reestablish.go
1
package lnwire
2

3
import (
4
        "bytes"
5
        "io"
6

7
        "github.com/btcsuite/btcd/btcec/v2"
8
        "github.com/lightningnetwork/lnd/fn/v2"
9
        "github.com/lightningnetwork/lnd/tlv"
10
)
11

12
const (
13
        CRDynHeight tlv.Type = 20
14
)
15

16
// DynHeight is a newtype wrapper to get the proper RecordProducer instance
17
// to smoothly integrate with the ChannelReestablish Message instance.
18
type DynHeight uint64
19

20
// Record implements the RecordProducer interface, allowing a full tlv.Record
21
// object to be constructed from a DynHeight.
22
func (d *DynHeight) Record() tlv.Record {
344✔
23
        return tlv.MakePrimitiveRecord(CRDynHeight, (*uint64)(d))
344✔
24
}
344✔
25

26
// ChannelReestablish is a message sent between peers that have an existing
27
// open channel upon connection reestablishment. This message allows both sides
28
// to report their local state, and their current knowledge of the state of the
29
// remote commitment chain. If a deviation is detected and can be recovered
30
// from, then the necessary messages will be retransmitted. If the level of
31
// desynchronization is irreconcilable, then the channel will be force closed.
32
type ChannelReestablish struct {
33
        // ChanID is the channel ID of the channel state we're attempting to
34
        // synchronize with the remote party.
35
        ChanID ChannelID
36

37
        // NextLocalCommitHeight is the next local commitment height of the
38
        // sending party. If the height of the sender's commitment chain from
39
        // the receiver's Pov is one less that this number, then the sender
40
        // should re-send the *exact* same proposed commitment.
41
        //
42
        // In other words, the receiver should re-send their last sent
43
        // commitment iff:
44
        //
45
        //  * NextLocalCommitHeight == remoteCommitChain.Height
46
        //
47
        // This covers the case of a lost commitment which was sent by the
48
        // sender of this message, but never received by the receiver of this
49
        // message.
50
        NextLocalCommitHeight uint64
51

52
        // RemoteCommitTailHeight is the height of the receiving party's
53
        // unrevoked commitment from the PoV of the sender of this message. If
54
        // the height of the receiver's commitment is *one more* than this
55
        // value, then their prior RevokeAndAck message should be
56
        // retransmitted.
57
        //
58
        // In other words, the receiver should re-send their last sent
59
        // RevokeAndAck message iff:
60
        //
61
        //  * localCommitChain.tail().Height == RemoteCommitTailHeight + 1
62
        //
63
        // This covers the case of a lost revocation, wherein the receiver of
64
        // the message sent a revocation for a prior state, but the sender of
65
        // the message never fully processed it.
66
        RemoteCommitTailHeight uint64
67

68
        // LastRemoteCommitSecret is the last commitment secret that the
69
        // receiving node has sent to the sending party. This will be the
70
        // secret of the last revoked commitment transaction. Including this
71
        // provides proof that the sending node at least knows of this state,
72
        // as they couldn't have produced it if it wasn't sent, as the value
73
        // can be authenticated by querying the shachain or the receiving
74
        // party.
75
        LastRemoteCommitSecret [32]byte
76

77
        // LocalUnrevokedCommitPoint is the commitment point used in the
78
        // current un-revoked commitment transaction of the sending party.
79
        LocalUnrevokedCommitPoint *btcec.PublicKey
80

81
        // NOTE: The following fields are TLV records.
82
        //
83
        // LocalNonce is an optional field that stores a local musig2 nonce.
84
        // This will only be populated if the simple taproot channels type was
85
        // negotiated.
86
        //
87
        LocalNonce OptMusig2NonceTLV
88

89
        // DynHeight is an optional field that stores the dynamic commitment
90
        // negotiation height that is incremented upon successful completion of
91
        // a dynamic commitment negotiation
92
        DynHeight fn.Option[DynHeight]
93

94
        // ExtraData is the set of data that was appended to this message to
95
        // fill out the full maximum transport message size. These fields can
96
        // be used to specify optional data such as custom TLV fields.
97
        ExtraData ExtraOpaqueData
98
}
99

100
// A compile time check to ensure ChannelReestablish implements the
101
// lnwire.Message interface.
102
var _ Message = (*ChannelReestablish)(nil)
103

104
// A compile time check to ensure ChannelReestablish implements the
105
// lnwire.SizeableMessage interface.
106
var _ SizeableMessage = (*ChannelReestablish)(nil)
107

108
// Encode serializes the target ChannelReestablish into the passed io.Writer
109
// observing the protocol version specified.
110
//
111
// This is part of the lnwire.Message interface.
112
func (a *ChannelReestablish) Encode(w *bytes.Buffer, pver uint32) error {
118✔
113
        if err := WriteChannelID(w, a.ChanID); err != nil {
118✔
114
                return err
×
115
        }
×
116

117
        if err := WriteUint64(w, a.NextLocalCommitHeight); err != nil {
118✔
118
                return err
×
119
        }
×
120

121
        if err := WriteUint64(w, a.RemoteCommitTailHeight); err != nil {
118✔
122
                return err
×
123
        }
×
124

125
        // If the commit point wasn't sent, then we won't write out any of the
126
        // remaining fields as they're optional.
127
        if a.LocalUnrevokedCommitPoint == nil {
119✔
128
                // However, we'll still write out the extra data if it's
1✔
129
                // present.
1✔
130
                //
1✔
131
                // NOTE: This is here primarily for the quickcheck tests, in
1✔
132
                // practice, we'll always populate this field.
1✔
133
                return WriteBytes(w, a.ExtraData)
1✔
134
        }
1✔
135

136
        // Otherwise, we'll write out the remaining elements.
137
        if err := WriteBytes(w, a.LastRemoteCommitSecret[:]); err != nil {
117✔
138
                return err
×
139
        }
×
140

141
        if err := WritePublicKey(w, a.LocalUnrevokedCommitPoint); err != nil {
117✔
142
                return err
×
143
        }
×
144

145
        // Get producers from extra data.
146
        producers, err := a.ExtraData.RecordProducers()
117✔
147
        if err != nil {
117✔
NEW
148
                return err
×
NEW
149
        }
×
150

151
        a.LocalNonce.WhenSome(func(localNonce Musig2NonceTLV) {
176✔
152
                producers = append(producers, &localNonce)
59✔
153
        })
59✔
154
        a.DynHeight.WhenSome(func(h DynHeight) {
170✔
155
                producers = append(producers, &h)
53✔
156
        })
53✔
157

158
        // Pack all records into a new TLV stream.
159
        var tlvData ExtraOpaqueData
117✔
160
        err = tlvData.PackRecords(producers...)
117✔
161
        if err != nil {
117✔
162
                return err
×
163
        }
×
164

165
        return WriteBytes(w, tlvData)
117✔
166
}
167

168
// Decode deserializes a serialized ChannelReestablish stored in the passed
169
// io.Reader observing the specified protocol version.
170
//
171
// This is part of the lnwire.Message interface.
172
func (a *ChannelReestablish) Decode(r io.Reader, pver uint32) error {
180✔
173
        err := ReadElements(r,
180✔
174
                &a.ChanID,
180✔
175
                &a.NextLocalCommitHeight,
180✔
176
                &a.RemoteCommitTailHeight,
180✔
177
        )
180✔
178
        if err != nil {
185✔
179
                return err
5✔
180
        }
5✔
181

182
        // This message has currently defined optional fields. As a result,
183
        // we'll only proceed if there's still bytes remaining within the
184
        // reader.
185
        //
186
        // We'll manually parse out the optional fields in order to be able to
187
        // still utilize the io.Reader interface.
188

189
        // We'll first attempt to read the optional commit secret, if we're at
190
        // the EOF, then this means the field wasn't included so we can exit
191
        // early.
192
        var buf [32]byte
175✔
193
        _, err = io.ReadFull(r, buf[:32])
175✔
194
        if err == io.EOF {
177✔
195
                // If there aren't any more bytes, then we'll emplace an empty
2✔
196
                // extra data to make our quickcheck tests happy.
2✔
197
                a.ExtraData = make([]byte, 0)
2✔
198
                return nil
2✔
199
        } else if err != nil {
176✔
200
                return err
1✔
201
        }
1✔
202

203
        // If the field is present, then we'll copy it over and proceed.
204
        copy(a.LastRemoteCommitSecret[:], buf[:])
172✔
205

172✔
206
        // We'll conclude by parsing out the commitment point. We don't check
172✔
207
        // the error in this case, as it has included the commit secret, then
172✔
208
        // they MUST also include the commit point.
172✔
209
        if err = ReadElement(r, &a.LocalUnrevokedCommitPoint); err != nil {
177✔
210
                return err
5✔
211
        }
5✔
212

213
        var tlvRecords ExtraOpaqueData
167✔
214
        if err := ReadElements(r, &tlvRecords); err != nil {
167✔
215
                return err
×
216
        }
×
217

218
        var (
167✔
219
                dynHeight  DynHeight
167✔
220
                localNonce = a.LocalNonce.Zero()
167✔
221
        )
167✔
222
        knownRecords, extraData, err := ParseAndExtractExtraData(
167✔
223
                tlvRecords, &localNonce, &dynHeight,
167✔
224
        )
167✔
225
        if err != nil {
207✔
226
                return err
40✔
227
        }
40✔
228

229
        if _, ok := knownRecords[a.LocalNonce.TlvType()]; ok {
187✔
230
                a.LocalNonce = tlv.SomeRecordT(localNonce)
60✔
231
        }
60✔
232
        if _, ok := knownRecords[CRDynHeight]; ok {
181✔
233
                a.DynHeight = fn.Some(dynHeight)
54✔
234
        }
54✔
235

236
        a.ExtraData = extraData
127✔
237

127✔
238
        return nil
127✔
239
}
240

241
// MsgType returns the integer uniquely identifying this message type on the
242
// wire.
243
//
244
// This is part of the lnwire.Message interface.
245
func (a *ChannelReestablish) MsgType() MessageType {
322✔
246
        return MsgChannelReestablish
322✔
247
}
322✔
248

249
// SerializedSize returns the serialized size of the message in bytes.
250
//
251
// This is part of the lnwire.SizeableMessage interface.
252
func (a *ChannelReestablish) SerializedSize() (uint32, error) {
×
253
        return MessageSerializedSize(a)
×
254
}
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc