Skip to content

Commit 42089b1

Browse files
committed
lnwire: add DynCommit message to match spec
1 parent 60887ad commit 42089b1

File tree

3 files changed

+258
-0
lines changed

3 files changed

+258
-0
lines changed

lnwire/dyn_commit.go

Lines changed: 247 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,247 @@
1+
package lnwire
2+
3+
import (
4+
"bytes"
5+
"io"
6+
7+
"github.com/btcsuite/btcd/btcec/v2"
8+
"github.com/btcsuite/btcd/btcutil"
9+
"github.com/lightningnetwork/lnd/fn/v2"
10+
"github.com/lightningnetwork/lnd/lnwallet/chainfee"
11+
"github.com/lightningnetwork/lnd/tlv"
12+
)
13+
14+
// DynCommit is a composite message that is used to irrefutably execute a
15+
// dynamic commitment update.
16+
type DynCommit struct {
17+
// DynPropose is an embedded version of the original DynPropose message
18+
// that initiated this negotiation.
19+
DynPropose
20+
21+
// DynAck is an embedded version of the original DynAck message that
22+
// countersigned this negotiation.
23+
DynAck
24+
25+
// ExtraData is the set of data that was appended to this message to
26+
// fill out the full maximum transport message size. These fields can
27+
// be used to specify optional data such as custom TLV fields.
28+
ExtraData ExtraOpaqueData
29+
}
30+
31+
// A compile time check to ensure DynAck implements the lnwire.Message
32+
// interface.
33+
var _ Message = (*DynCommit)(nil)
34+
35+
// Encode serializes the target DynAck into the passed io.Writer. Serialization
36+
// will observe the rules defined by the passed protocol version.
37+
//
38+
// This is a part of the lnwire.Message interface.
39+
func (dc *DynCommit) Encode(w *bytes.Buffer, _ uint32) error {
40+
if err := WriteChannelID(w, dc.DynPropose.ChanID); err != nil {
41+
return err
42+
}
43+
44+
if err := WriteSig(w, dc.Sig); err != nil {
45+
return err
46+
}
47+
48+
var tlvRecords []tlv.Record
49+
dc.DustLimit.WhenSome(func(dl btcutil.Amount) {
50+
protoSats := uint64(dl)
51+
tlvRecords = append(
52+
tlvRecords, tlv.MakePrimitiveRecord(
53+
DPDustLimitSatoshis, &protoSats,
54+
),
55+
)
56+
})
57+
dc.MaxValueInFlight.WhenSome(func(max MilliSatoshi) {
58+
protoSats := uint64(max)
59+
tlvRecords = append(
60+
tlvRecords, tlv.MakePrimitiveRecord(
61+
DPMaxHtlcValueInFlightMsat, &protoSats,
62+
),
63+
)
64+
})
65+
dc.ChannelReserve.WhenSome(func(min btcutil.Amount) {
66+
channelReserve := uint64(min)
67+
tlvRecords = append(
68+
tlvRecords, tlv.MakePrimitiveRecord(
69+
DPChannelReserveSatoshis, &channelReserve,
70+
),
71+
)
72+
})
73+
dc.CsvDelay.WhenSome(func(wait uint16) {
74+
tlvRecords = append(
75+
tlvRecords, tlv.MakePrimitiveRecord(
76+
DPToSelfDelay, &wait,
77+
),
78+
)
79+
})
80+
dc.MaxAcceptedHTLCs.WhenSome(func(max uint16) {
81+
tlvRecords = append(
82+
tlvRecords, tlv.MakePrimitiveRecord(
83+
DPMaxAcceptedHtlcs, &max,
84+
),
85+
)
86+
})
87+
dc.FundingKey.WhenSome(func(key btcec.PublicKey) {
88+
keyScratch := &key
89+
tlvRecords = append(
90+
tlvRecords, tlv.MakePrimitiveRecord(
91+
DPFundingPubkey, &keyScratch,
92+
),
93+
)
94+
})
95+
dc.ChannelType.WhenSome(func(ty ChannelType) {
96+
tlvRecords = append(
97+
tlvRecords, tlv.MakeDynamicRecord(
98+
DPChannelType, &ty,
99+
ty.featureBitLen,
100+
channelTypeEncoder, channelTypeDecoder,
101+
),
102+
)
103+
})
104+
dc.KickoffFeerate.WhenSome(func(kickoffFeerate chainfee.SatPerKWeight) {
105+
protoSats := uint32(kickoffFeerate)
106+
tlvRecords = append(
107+
tlvRecords, tlv.MakePrimitiveRecord(
108+
DPKickoffFeerate, &protoSats,
109+
),
110+
)
111+
})
112+
tlv.SortRecords(tlvRecords)
113+
114+
tlvStream, err := tlv.NewStream(tlvRecords...)
115+
if err != nil {
116+
return err
117+
}
118+
119+
var extraBytesWriter bytes.Buffer
120+
if err := tlvStream.Encode(&extraBytesWriter); err != nil {
121+
return err
122+
}
123+
124+
dc.ExtraData = ExtraOpaqueData(extraBytesWriter.Bytes())
125+
126+
return WriteBytes(w, dc.ExtraData)
127+
}
128+
129+
// Decode deserializes the serialized DynCommit stored in the passed io.Reader
130+
// into the target DynAck using the deserialization rules defined by the passed
131+
// protocol version.
132+
//
133+
// This is a part of the lnwire.Message interface.
134+
func (dc *DynCommit) Decode(r io.Reader, _ uint32) error {
135+
// Parse out main message.
136+
if err := ReadElements(r, &dc.DynPropose.ChanID, &dc.Sig); err != nil {
137+
return err
138+
}
139+
dc.DynAck.ChanID = dc.DynPropose.ChanID
140+
141+
// Parse out TLV records.
142+
var tlvRecords ExtraOpaqueData
143+
if err := ReadElement(r, &tlvRecords); err != nil {
144+
return err
145+
}
146+
147+
// Prepare receiving buffers to be filled by TLV extraction.
148+
var dustLimitScratch uint64
149+
dustLimit := tlv.MakePrimitiveRecord(
150+
DPDustLimitSatoshis, &dustLimitScratch,
151+
)
152+
153+
var maxValueScratch uint64
154+
maxValue := tlv.MakePrimitiveRecord(
155+
DPMaxHtlcValueInFlightMsat, &maxValueScratch,
156+
)
157+
158+
var reserveScratch uint64
159+
reserve := tlv.MakePrimitiveRecord(
160+
DPChannelReserveSatoshis, &reserveScratch,
161+
)
162+
163+
var csvDelayScratch uint16
164+
csvDelay := tlv.MakePrimitiveRecord(DPToSelfDelay, &csvDelayScratch)
165+
166+
var maxHtlcsScratch uint16
167+
maxHtlcs := tlv.MakePrimitiveRecord(
168+
DPMaxAcceptedHtlcs, &maxHtlcsScratch,
169+
)
170+
171+
var fundingKeyScratch *btcec.PublicKey
172+
fundingKey := tlv.MakePrimitiveRecord(
173+
DPFundingPubkey, &fundingKeyScratch,
174+
)
175+
176+
var chanTypeScratch ChannelType
177+
chanType := tlv.MakeDynamicRecord(
178+
DPChannelType, &chanTypeScratch, chanTypeScratch.featureBitLen,
179+
channelTypeEncoder, channelTypeDecoder,
180+
)
181+
182+
var kickoffFeerateScratch uint32
183+
kickoffFeerate := tlv.MakePrimitiveRecord(
184+
DPKickoffFeerate, &kickoffFeerateScratch,
185+
)
186+
187+
// Create set of Records to read TLV bytestream into.
188+
records := []tlv.Record{
189+
dustLimit, maxValue, reserve, csvDelay, maxHtlcs, fundingKey,
190+
chanType, kickoffFeerate,
191+
}
192+
tlv.SortRecords(records)
193+
194+
// Read TLV stream into record set.
195+
extraBytesReader := bytes.NewReader(tlvRecords)
196+
tlvStream, err := tlv.NewStream(records...)
197+
if err != nil {
198+
return err
199+
}
200+
typeMap, err := tlvStream.DecodeWithParsedTypesP2P(extraBytesReader)
201+
if err != nil {
202+
return err
203+
}
204+
205+
// Check the results of the TLV Stream decoding and appropriately set
206+
// message fields.
207+
if val, ok := typeMap[DPDustLimitSatoshis]; ok && val == nil {
208+
dc.DustLimit = fn.Some(btcutil.Amount(dustLimitScratch))
209+
}
210+
if val, ok := typeMap[DPMaxHtlcValueInFlightMsat]; ok && val == nil {
211+
dc.MaxValueInFlight = fn.Some(MilliSatoshi(maxValueScratch))
212+
}
213+
if val, ok := typeMap[DPChannelReserveSatoshis]; ok && val == nil {
214+
dc.ChannelReserve = fn.Some(btcutil.Amount(reserveScratch))
215+
}
216+
if val, ok := typeMap[DPToSelfDelay]; ok && val == nil {
217+
dc.CsvDelay = fn.Some(csvDelayScratch)
218+
}
219+
if val, ok := typeMap[DPMaxAcceptedHtlcs]; ok && val == nil {
220+
dc.MaxAcceptedHTLCs = fn.Some(maxHtlcsScratch)
221+
}
222+
if val, ok := typeMap[DPFundingPubkey]; ok && val == nil {
223+
dc.FundingKey = fn.Some(*fundingKeyScratch)
224+
}
225+
if val, ok := typeMap[DPChannelType]; ok && val == nil {
226+
dc.ChannelType = fn.Some(chanTypeScratch)
227+
}
228+
if val, ok := typeMap[DPKickoffFeerate]; ok && val == nil {
229+
dc.KickoffFeerate = fn.Some(
230+
chainfee.SatPerKWeight(kickoffFeerateScratch),
231+
)
232+
}
233+
234+
if len(tlvRecords) != 0 {
235+
dc.ExtraData = tlvRecords
236+
}
237+
238+
return nil
239+
}
240+
241+
// MsgType returns the MessageType code which uniquely identifies this message
242+
// as a DynCommit on the wire.
243+
//
244+
// This is part of the lnwire.Message interface.
245+
func (dc *DynCommit) MsgType() MessageType {
246+
return MsgDynCommit
247+
}

lnwire/fuzz_test.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -452,6 +452,12 @@ func FuzzDynAck(f *testing.F) {
452452
})
453453
}
454454

455+
func FuzzDynCommit(f *testing.F) {
456+
f.Fuzz(func(t *testing.T, data []byte) {
457+
wireMsgHarness(t, data, MsgDynCommit)
458+
})
459+
}
460+
455461
func FuzzKickoffSig(f *testing.F) {
456462
f.Fuzz(func(t *testing.T, data []byte) {
457463
wireMsgHarness(t, data, MsgKickoffSig)

lnwire/message.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ const (
4444
MsgDynPropose = 111
4545
MsgDynAck = 113
4646
MsgDynReject = 115
47+
MsgDynCommit = 117
4748
MsgUpdateAddHTLC = 128
4849
MsgUpdateFulfillHTLC = 130
4950
MsgUpdateFailHTLC = 131
@@ -140,6 +141,8 @@ func (t MessageType) String() string {
140141
return "DynAck"
141142
case MsgDynReject:
142143
return "DynReject"
144+
case MsgDynCommit:
145+
return "DynCommit"
143146
case MsgKickoffSig:
144147
return "KickoffSig"
145148
case MsgUpdateAddHTLC:
@@ -300,6 +303,8 @@ func makeEmptyMessage(msgType MessageType) (Message, error) {
300303
msg = &DynAck{}
301304
case MsgDynReject:
302305
msg = &DynReject{}
306+
case MsgDynCommit:
307+
msg = &DynCommit{}
303308
case MsgKickoffSig:
304309
msg = &KickoffSig{}
305310
case MsgUpdateAddHTLC:

0 commit comments

Comments
 (0)