Skip to content

Commit 5885853

Browse files
committed
lnwire: add convenience functions for protocol validation
1 parent dc92eee commit 5885853

File tree

2 files changed

+175
-0
lines changed

2 files changed

+175
-0
lines changed

lnwire/dyn_ack.go

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,12 @@ package lnwire
22

33
import (
44
"bytes"
5+
"fmt"
56
"io"
67

78
"github.com/btcsuite/btcd/btcec/v2/schnorr/musig2"
9+
"github.com/btcsuite/btcd/chaincfg/chainhash"
10+
"github.com/decred/dcrd/dcrec/secp256k1/v4"
811
"github.com/lightningnetwork/lnd/fn"
912
"github.com/lightningnetwork/lnd/tlv"
1013
)
@@ -144,3 +147,43 @@ func (da *DynAck) Decode(r io.Reader, _ uint32) error {
144147
func (da *DynAck) MsgType() MessageType {
145148
return MsgDynAck
146149
}
150+
151+
// Validate does DynAck signature validation for a given prior DynPropose
152+
// message.
153+
func (da *DynAck) Validate(propose DynPropose, nextHeight uint64,
154+
pubkey *secp256k1.PublicKey) error {
155+
156+
cSig, err := da.Sig.ToSignature()
157+
if err != nil {
158+
return err
159+
}
160+
161+
var msg bytes.Buffer
162+
err = WriteChannelID(&msg, da.ChanID)
163+
if err != nil {
164+
return err
165+
}
166+
167+
err = WriteElement(&msg, nextHeight)
168+
if err != nil {
169+
return err
170+
}
171+
172+
tlvData, err := propose.SerializeTlvData()
173+
if err != nil {
174+
return err
175+
}
176+
177+
msg.Write(tlvData)
178+
179+
digest := chainhash.DoubleHashB(msg.Bytes())
180+
181+
if !cSig.Verify(digest, pubkey) {
182+
return fmt.Errorf(
183+
"invalid signature for dyn_ack: %v @ %v by %v",
184+
propose, nextHeight, pubkey,
185+
)
186+
}
187+
188+
return nil
189+
}

lnwire/dyn_propose.go

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"github.com/btcsuite/btcd/btcec/v2"
88
"github.com/btcsuite/btcd/btcutil"
99
"github.com/lightningnetwork/lnd/fn"
10+
"github.com/lightningnetwork/lnd/keychain"
1011
"github.com/lightningnetwork/lnd/lnwallet/chainfee"
1112
"github.com/lightningnetwork/lnd/tlv"
1213
)
@@ -307,3 +308,134 @@ func (dp *DynPropose) Decode(r io.Reader, _ uint32) error {
307308
func (dp *DynPropose) MsgType() MessageType {
308309
return MsgDynPropose
309310
}
311+
312+
// SerializeTlvData takes just the TLV data of DynPropose (which covers all of
313+
// the parameters on deck for changing) and serializes just this component. The
314+
// main purpose of this is to make it easier to validate the DynAck signature.
315+
func (dp *DynPropose) SerializeTlvData() ([]byte, error) {
316+
var tlvRecords []tlv.Record
317+
dp.DustLimit.WhenSome(func(dl btcutil.Amount) {
318+
protoSats := uint64(dl)
319+
tlvRecords = append(
320+
tlvRecords, tlv.MakePrimitiveRecord(
321+
DPDustLimitSatoshis, &protoSats,
322+
),
323+
)
324+
})
325+
dp.MaxValueInFlight.WhenSome(func(max MilliSatoshi) {
326+
protoSats := uint64(max)
327+
tlvRecords = append(
328+
tlvRecords, tlv.MakePrimitiveRecord(
329+
DPMaxHtlcValueInFlightMsat, &protoSats,
330+
),
331+
)
332+
})
333+
dp.ChannelReserve.WhenSome(func(min btcutil.Amount) {
334+
channelReserve := uint64(min)
335+
tlvRecords = append(
336+
tlvRecords, tlv.MakePrimitiveRecord(
337+
DPChannelReserveSatoshis, &channelReserve,
338+
),
339+
)
340+
})
341+
dp.CsvDelay.WhenSome(func(wait uint16) {
342+
tlvRecords = append(
343+
tlvRecords, tlv.MakePrimitiveRecord(
344+
DPToSelfDelay, &wait,
345+
),
346+
)
347+
})
348+
dp.MaxAcceptedHTLCs.WhenSome(func(max uint16) {
349+
tlvRecords = append(
350+
tlvRecords, tlv.MakePrimitiveRecord(
351+
DPMaxAcceptedHtlcs, &max,
352+
),
353+
)
354+
})
355+
dp.FundingKey.WhenSome(func(key btcec.PublicKey) {
356+
keyScratch := &key
357+
tlvRecords = append(
358+
tlvRecords, tlv.MakePrimitiveRecord(
359+
DPFundingPubkey, &keyScratch,
360+
),
361+
)
362+
})
363+
dp.ChannelType.WhenSome(func(ty ChannelType) {
364+
tlvRecords = append(
365+
tlvRecords, tlv.MakeDynamicRecord(
366+
DPChannelType, &ty,
367+
ty.featureBitLen,
368+
channelTypeEncoder, channelTypeDecoder,
369+
),
370+
)
371+
})
372+
dp.KickoffFeerate.WhenSome(func(kickoffFeerate chainfee.SatPerKWeight) {
373+
protoSats := uint32(kickoffFeerate)
374+
tlvRecords = append(
375+
tlvRecords, tlv.MakePrimitiveRecord(
376+
DPKickoffFeerate, &protoSats,
377+
),
378+
)
379+
})
380+
tlv.SortRecords(tlvRecords)
381+
382+
tlvStream, err := tlv.NewStream(tlvRecords...)
383+
if err != nil {
384+
return nil, err
385+
}
386+
387+
var outBuf bytes.Buffer
388+
err = tlvStream.Encode(&outBuf)
389+
if err != nil {
390+
return nil, err
391+
}
392+
393+
return outBuf.Bytes(), nil
394+
}
395+
396+
// Accept provides a convenience method for taking a DynPropose and issuing a
397+
// corresponding DynAck using the provided MessageSignerRing.
398+
func (dp *DynPropose) Accept(nextHeight uint64,
399+
signer keychain.MessageSignerRing) (DynAck, error) {
400+
401+
var msg bytes.Buffer
402+
err := WriteChannelID(&msg, dp.ChanID)
403+
if err != nil {
404+
return DynAck{}, err
405+
}
406+
407+
err = WriteElement(&msg, nextHeight)
408+
if err != nil {
409+
return DynAck{}, err
410+
}
411+
412+
tlvData, err := dp.SerializeTlvData()
413+
if err != nil {
414+
return DynAck{}, err
415+
}
416+
417+
msg.Write(tlvData)
418+
419+
nodeKeyLoc := keychain.KeyLocator{
420+
Family: keychain.KeyFamilyNodeKey,
421+
Index: 0,
422+
}
423+
424+
rawSig, err := signer.SignMessageCompact(nodeKeyLoc, msg.Bytes(), false)
425+
if err != nil {
426+
return DynAck{}, err
427+
}
428+
429+
var sigFixed [64]byte
430+
copy(sigFixed[:], rawSig[0:64])
431+
432+
sig := Sig{
433+
bytes: sigFixed,
434+
sigType: sigTypeECDSA,
435+
}
436+
437+
return DynAck{
438+
ChanID: dp.ChanID,
439+
Sig: sig,
440+
}, nil
441+
}

0 commit comments

Comments
 (0)