diff --git a/.gitignore b/.gitignore index 1e498b0cf28..11c67fe65c4 100644 --- a/.gitignore +++ b/.gitignore @@ -82,3 +82,6 @@ coverage.txt /lnd-*/ .aider* + +# All test data generated from rapid. +*/testdata diff --git a/lnwire/dyn_ack.go b/lnwire/dyn_ack.go index 1cc57e955a9..1e25e16759c 100644 --- a/lnwire/dyn_ack.go +++ b/lnwire/dyn_ack.go @@ -24,6 +24,10 @@ type DynAck struct { // a dynamic commitment negotiation ChanID ChannelID + // Sig is a signature that acknowledges and approves the parameters + // that were requested in the DynPropose + Sig Sig + // LocalNonce is an optional field that is transmitted when accepting // a dynamic commitment upgrade to Taproot Channels. This nonce will be // used to verify the first commitment transaction signature. This will @@ -54,6 +58,10 @@ func (da *DynAck) Encode(w *bytes.Buffer, _ uint32) error { return err } + if err := WriteSig(w, da.Sig); err != nil { + return err + } + var tlvRecords []tlv.Record da.LocalNonce.WhenSome(func(nonce Musig2Nonce) { tlvRecords = append( @@ -88,7 +96,7 @@ func (da *DynAck) Encode(w *bytes.Buffer, _ uint32) error { // This is a part of the lnwire.Message interface. func (da *DynAck) Decode(r io.Reader, _ uint32) error { // Parse out main message. - if err := ReadElements(r, &da.ChanID); err != nil { + if err := ReadElements(r, &da.ChanID, &da.Sig); err != nil { return err } diff --git a/lnwire/dyn_commit.go b/lnwire/dyn_commit.go new file mode 100644 index 00000000000..083da162609 --- /dev/null +++ b/lnwire/dyn_commit.go @@ -0,0 +1,146 @@ +package lnwire + +import ( + "bytes" + "io" + + "github.com/btcsuite/btcd/btcutil" + "github.com/lightningnetwork/lnd/tlv" +) + +// DynCommit is a composite message that is used to irrefutably execute a +// dynamic commitment update. +type DynCommit struct { + // DynPropose is an embedded version of the original DynPropose message + // that initiated this negotiation. + DynPropose + + // DynAck is an embedded version of the original DynAck message that + // countersigned this negotiation. + DynAck + + // ExtraData is the set of data that was appended to this message to + // fill out the full maximum transport message size. These fields can + // be used to specify optional data such as custom TLV fields. + ExtraData ExtraOpaqueData +} + +// A compile time check to ensure DynCommit implements the lnwire.Message +// interface. +var _ Message = (*DynCommit)(nil) + +// A compile time check to ensure DynCommit implements the +// lnwire.SizeableMessage interface. +var _ SizeableMessage = (*DynCommit)(nil) + +// Encode serializes the target DynAck into the passed io.Writer. Serialization +// will observe the rules defined by the passed protocol version. +// +// This is a part of the lnwire.Message interface. +func (dc *DynCommit) Encode(w *bytes.Buffer, _ uint32) error { + if err := WriteChannelID(w, dc.DynPropose.ChanID); err != nil { + return err + } + + if err := WriteSig(w, dc.Sig); err != nil { + return err + } + + var extra ExtraOpaqueData + err := extra.PackRecords(dynProposeRecords(&dc.DynPropose)...) + if err != nil { + return err + } + dc.ExtraData = extra + + return WriteBytes(w, dc.ExtraData) +} + +// Decode deserializes the serialized DynCommit stored in the passed io.Reader +// into the target DynAck using the deserialization rules defined by the passed +// protocol version. +// +// This is a part of the lnwire.Message interface. +func (dc *DynCommit) Decode(r io.Reader, _ uint32) error { + // Parse out main message. + if err := ReadElements(r, &dc.DynPropose.ChanID, &dc.Sig); err != nil { + return err + } + dc.DynAck.ChanID = dc.DynPropose.ChanID + + // Parse out TLV records. + var tlvRecords ExtraOpaqueData + if err := ReadElement(r, &tlvRecords); err != nil { + return err + } + + // Prepare receiving buffers to be filled by TLV extraction. + var dustLimit tlv.RecordT[tlv.TlvType0, uint64] + var maxValue tlv.RecordT[tlv.TlvType2, uint64] + var htlcMin tlv.RecordT[tlv.TlvType4, uint64] + var reserve tlv.RecordT[tlv.TlvType6, uint64] + csvDelay := dc.CsvDelay.Zero() + maxHtlcs := dc.MaxAcceptedHTLCs.Zero() + chanType := dc.ChannelType.Zero() + + typeMap, err := tlvRecords.ExtractRecords( + &dustLimit, &maxValue, &htlcMin, &reserve, &csvDelay, &maxHtlcs, + &chanType, + ) + if err != nil { + return err + } + + // Check the results of the TLV Stream decoding and appropriately set + // message fields. + if val, ok := typeMap[dc.DustLimit.TlvType()]; ok && val == nil { + var rec tlv.RecordT[tlv.TlvType0, btcutil.Amount] + rec.Val = btcutil.Amount(dustLimit.Val) + dc.DustLimit = tlv.SomeRecordT(rec) + } + if val, ok := typeMap[dc.MaxValueInFlight.TlvType()]; ok && val == nil { + var rec tlv.RecordT[tlv.TlvType2, MilliSatoshi] + rec.Val = MilliSatoshi(maxValue.Val) + dc.MaxValueInFlight = tlv.SomeRecordT(rec) + } + if val, ok := typeMap[dc.HtlcMinimum.TlvType()]; ok && val == nil { + var rec tlv.RecordT[tlv.TlvType4, MilliSatoshi] + rec.Val = MilliSatoshi(htlcMin.Val) + dc.HtlcMinimum = tlv.SomeRecordT(rec) + } + if val, ok := typeMap[dc.ChannelReserve.TlvType()]; ok && val == nil { + var rec tlv.RecordT[tlv.TlvType6, btcutil.Amount] + rec.Val = btcutil.Amount(reserve.Val) + dc.ChannelReserve = tlv.SomeRecordT(rec) + } + if val, ok := typeMap[dc.CsvDelay.TlvType()]; ok && val == nil { + dc.CsvDelay = tlv.SomeRecordT(csvDelay) + } + if val, ok := typeMap[dc.MaxAcceptedHTLCs.TlvType()]; ok && val == nil { + dc.MaxAcceptedHTLCs = tlv.SomeRecordT(maxHtlcs) + } + if val, ok := typeMap[dc.ChannelType.TlvType()]; ok && val == nil { + dc.ChannelType = tlv.SomeRecordT(chanType) + } + + if len(tlvRecords) != 0 { + dc.ExtraData = tlvRecords + } + + return nil +} + +// MsgType returns the MessageType code which uniquely identifies this message +// as a DynCommit on the wire. +// +// This is part of the lnwire.Message interface. +func (dc *DynCommit) MsgType() MessageType { + return MsgDynCommit +} + +// SerializedSize returns the serialized size of the message in bytes. +// +// This is part of the lnwire.SizeableMessage interface. +func (dc *DynCommit) SerializedSize() (uint32, error) { + return MessageSerializedSize(dc) +} diff --git a/lnwire/dyn_propose.go b/lnwire/dyn_propose.go index cc19ec394f4..21a7ce524b7 100644 --- a/lnwire/dyn_propose.go +++ b/lnwire/dyn_propose.go @@ -4,47 +4,10 @@ import ( "bytes" "io" - "github.com/btcsuite/btcd/btcec/v2" "github.com/btcsuite/btcd/btcutil" - "github.com/lightningnetwork/lnd/fn/v2" - "github.com/lightningnetwork/lnd/lnwallet/chainfee" "github.com/lightningnetwork/lnd/tlv" ) -const ( - // DPDustLimitSatoshis is the TLV type number that identifies the record - // for DynPropose.DustLimit. - DPDustLimitSatoshis tlv.Type = 0 - - // DPMaxHtlcValueInFlightMsat is the TLV type number that identifies the - // record for DynPropose.MaxValueInFlight. - DPMaxHtlcValueInFlightMsat tlv.Type = 1 - - // DPChannelReserveSatoshis is the TLV type number that identifies the - // for DynPropose.ChannelReserve. - DPChannelReserveSatoshis tlv.Type = 2 - - // DPToSelfDelay is the TLV type number that identifies the record for - // DynPropose.CsvDelay. - DPToSelfDelay tlv.Type = 3 - - // DPMaxAcceptedHtlcs is the TLV type number that identifies the record - // for DynPropose.MaxAcceptedHTLCs. - DPMaxAcceptedHtlcs tlv.Type = 4 - - // DPFundingPubkey is the TLV type number that identifies the record for - // DynPropose.FundingKey. - DPFundingPubkey tlv.Type = 5 - - // DPChannelType is the TLV type number that identifies the record for - // DynPropose.ChannelType. - DPChannelType tlv.Type = 6 - - // DPKickoffFeerate is the TLV type number that identifies the record - // for DynPropose.KickoffFeerate. - DPKickoffFeerate tlv.Type = 7 -) - // DynPropose is a message that is sent during a dynamic commitments negotiation // process. It is sent by both parties to propose new channel parameters. type DynPropose struct { @@ -52,44 +15,33 @@ type DynPropose struct { // re-negotiate. ChanID ChannelID - // Initiator is a byte that identifies whether this message was sent as - // the initiator of a dynamic commitment negotiation or the responder - // of a dynamic commitment negotiation. bool true indicates it is the - // initiator - Initiator bool - // DustLimit, if not nil, proposes a change to the dust_limit_satoshis // for the sender's commitment transaction. - DustLimit fn.Option[btcutil.Amount] + DustLimit tlv.OptionalRecordT[tlv.TlvType0, btcutil.Amount] // MaxValueInFlight, if not nil, proposes a change to the // max_htlc_value_in_flight_msat limit of the sender. - MaxValueInFlight fn.Option[MilliSatoshi] + MaxValueInFlight tlv.OptionalRecordT[tlv.TlvType2, MilliSatoshi] + + // HtlcMinimum, if not nil, proposes a change to the htlc_minimum_msat + // floor of the sender. + HtlcMinimum tlv.OptionalRecordT[tlv.TlvType4, MilliSatoshi] // ChannelReserve, if not nil, proposes a change to the // channel_reserve_satoshis requirement of the recipient. - ChannelReserve fn.Option[btcutil.Amount] + ChannelReserve tlv.OptionalRecordT[tlv.TlvType6, btcutil.Amount] // CsvDelay, if not nil, proposes a change to the to_self_delay // requirement of the recipient. - CsvDelay fn.Option[uint16] + CsvDelay tlv.OptionalRecordT[tlv.TlvType8, uint16] // MaxAcceptedHTLCs, if not nil, proposes a change to the // max_accepted_htlcs limit of the sender. - MaxAcceptedHTLCs fn.Option[uint16] - - // FundingKey, if not nil, proposes a change to the funding_pubkey - // parameter of the sender. - FundingKey fn.Option[btcec.PublicKey] + MaxAcceptedHTLCs tlv.OptionalRecordT[tlv.TlvType10, uint16] // ChannelType, if not nil, proposes a change to the channel_type // parameter. - ChannelType fn.Option[ChannelType] - - // KickoffFeerate proposes the fee rate in satoshis per kw that it - // is offering for a ChannelType conversion that requires a kickoff - // transaction. - KickoffFeerate fn.Option[chainfee.SatPerKWeight] + ChannelType tlv.OptionalRecordT[tlv.TlvType12, ChannelType] // ExtraData is the set of data that was appended to this message to // fill out the full maximum transport message size. These fields can @@ -114,88 +66,14 @@ var _ SizeableMessage = (*DynPropose)(nil) // // This is a part of the lnwire.Message interface. func (dp *DynPropose) Encode(w *bytes.Buffer, _ uint32) error { - var tlvRecords []tlv.Record - dp.DustLimit.WhenSome(func(dl btcutil.Amount) { - protoSats := uint64(dl) - tlvRecords = append( - tlvRecords, tlv.MakePrimitiveRecord( - DPDustLimitSatoshis, &protoSats, - ), - ) - }) - dp.MaxValueInFlight.WhenSome(func(max MilliSatoshi) { - protoSats := uint64(max) - tlvRecords = append( - tlvRecords, tlv.MakePrimitiveRecord( - DPMaxHtlcValueInFlightMsat, &protoSats, - ), - ) - }) - dp.ChannelReserve.WhenSome(func(min btcutil.Amount) { - channelReserve := uint64(min) - tlvRecords = append( - tlvRecords, tlv.MakePrimitiveRecord( - DPChannelReserveSatoshis, &channelReserve, - ), - ) - }) - dp.CsvDelay.WhenSome(func(wait uint16) { - tlvRecords = append( - tlvRecords, tlv.MakePrimitiveRecord( - DPToSelfDelay, &wait, - ), - ) - }) - dp.MaxAcceptedHTLCs.WhenSome(func(max uint16) { - tlvRecords = append( - tlvRecords, tlv.MakePrimitiveRecord( - DPMaxAcceptedHtlcs, &max, - ), - ) - }) - dp.FundingKey.WhenSome(func(key btcec.PublicKey) { - keyScratch := &key - tlvRecords = append( - tlvRecords, tlv.MakePrimitiveRecord( - DPFundingPubkey, &keyScratch, - ), - ) - }) - dp.ChannelType.WhenSome(func(ty ChannelType) { - tlvRecords = append( - tlvRecords, tlv.MakeDynamicRecord( - DPChannelType, &ty, - ty.featureBitLen, - channelTypeEncoder, channelTypeDecoder, - ), - ) - }) - dp.KickoffFeerate.WhenSome(func(kickoffFeerate chainfee.SatPerKWeight) { - protoSats := uint32(kickoffFeerate) - tlvRecords = append( - tlvRecords, tlv.MakePrimitiveRecord( - DPKickoffFeerate, &protoSats, - ), - ) - }) - tlv.SortRecords(tlvRecords) - - tlvStream, err := tlv.NewStream(tlvRecords...) - if err != nil { - return err - } - - var extraBytesWriter bytes.Buffer - if err := tlvStream.Encode(&extraBytesWriter); err != nil { - return err - } - dp.ExtraData = ExtraOpaqueData(extraBytesWriter.Bytes()) - if err := WriteChannelID(w, dp.ChanID); err != nil { return err } - if err := WriteBool(w, dp.Initiator); err != nil { + producers := dynProposeRecords(dp) + + err := EncodeMessageExtraData(&dp.ExtraData, producers...) + if err != nil { return err } @@ -209,7 +87,7 @@ func (dp *DynPropose) Encode(w *bytes.Buffer, _ uint32) error { // This is a part of the lnwire.Message interface. func (dp *DynPropose) Decode(r io.Reader, _ uint32) error { // Parse out the only required field. - if err := ReadElements(r, &dp.ChanID, &dp.Initiator); err != nil { + if err := ReadElements(r, &dp.ChanID); err != nil { return err } @@ -220,91 +98,52 @@ func (dp *DynPropose) Decode(r io.Reader, _ uint32) error { } // Prepare receiving buffers to be filled by TLV extraction. - var dustLimitScratch uint64 - dustLimit := tlv.MakePrimitiveRecord( - DPDustLimitSatoshis, &dustLimitScratch, - ) - - var maxValueScratch uint64 - maxValue := tlv.MakePrimitiveRecord( - DPMaxHtlcValueInFlightMsat, &maxValueScratch, - ) - - var reserveScratch uint64 - reserve := tlv.MakePrimitiveRecord( - DPChannelReserveSatoshis, &reserveScratch, - ) - - var csvDelayScratch uint16 - csvDelay := tlv.MakePrimitiveRecord(DPToSelfDelay, &csvDelayScratch) - - var maxHtlcsScratch uint16 - maxHtlcs := tlv.MakePrimitiveRecord( - DPMaxAcceptedHtlcs, &maxHtlcsScratch, - ) - - var fundingKeyScratch *btcec.PublicKey - fundingKey := tlv.MakePrimitiveRecord( - DPFundingPubkey, &fundingKeyScratch, - ) - - var chanTypeScratch ChannelType - chanType := tlv.MakeDynamicRecord( - DPChannelType, &chanTypeScratch, chanTypeScratch.featureBitLen, - channelTypeEncoder, channelTypeDecoder, - ) - - var kickoffFeerateScratch uint32 - kickoffFeerate := tlv.MakePrimitiveRecord( - DPKickoffFeerate, &kickoffFeerateScratch, + var dustLimit tlv.RecordT[tlv.TlvType0, uint64] + var maxValue tlv.RecordT[tlv.TlvType2, uint64] + var htlcMin tlv.RecordT[tlv.TlvType4, uint64] + var reserve tlv.RecordT[tlv.TlvType6, uint64] + csvDelay := dp.CsvDelay.Zero() + maxHtlcs := dp.MaxAcceptedHTLCs.Zero() + chanType := dp.ChannelType.Zero() + + typeMap, err := tlvRecords.ExtractRecords( + &dustLimit, &maxValue, &htlcMin, &reserve, &csvDelay, &maxHtlcs, + &chanType, ) - - // Create set of Records to read TLV bytestream into. - records := []tlv.Record{ - dustLimit, maxValue, reserve, csvDelay, maxHtlcs, fundingKey, - chanType, kickoffFeerate, - } - tlv.SortRecords(records) - - // Read TLV stream into record set. - extraBytesReader := bytes.NewReader(tlvRecords) - tlvStream, err := tlv.NewStream(records...) - if err != nil { - return err - } - - typeMap, err := tlvStream.DecodeWithParsedTypesP2P(extraBytesReader) if err != nil { return err } // Check the results of the TLV Stream decoding and appropriately set // message fields. - if val, ok := typeMap[DPDustLimitSatoshis]; ok && val == nil { - dp.DustLimit = fn.Some(btcutil.Amount(dustLimitScratch)) + if val, ok := typeMap[dp.DustLimit.TlvType()]; ok && val == nil { + var rec tlv.RecordT[tlv.TlvType0, btcutil.Amount] + rec.Val = btcutil.Amount(dustLimit.Val) + dp.DustLimit = tlv.SomeRecordT(rec) } - if val, ok := typeMap[DPMaxHtlcValueInFlightMsat]; ok && val == nil { - dp.MaxValueInFlight = fn.Some(MilliSatoshi(maxValueScratch)) + if val, ok := typeMap[dp.MaxValueInFlight.TlvType()]; ok && val == nil { + var rec tlv.RecordT[tlv.TlvType2, MilliSatoshi] + rec.Val = MilliSatoshi(maxValue.Val) + dp.MaxValueInFlight = tlv.SomeRecordT(rec) } - if val, ok := typeMap[DPChannelReserveSatoshis]; ok && val == nil { - dp.ChannelReserve = fn.Some(btcutil.Amount(reserveScratch)) + if val, ok := typeMap[dp.HtlcMinimum.TlvType()]; ok && val == nil { + var rec tlv.RecordT[tlv.TlvType4, MilliSatoshi] + rec.Val = MilliSatoshi(htlcMin.Val) + dp.HtlcMinimum = tlv.SomeRecordT(rec) } - if val, ok := typeMap[DPToSelfDelay]; ok && val == nil { - dp.CsvDelay = fn.Some(csvDelayScratch) + if val, ok := typeMap[dp.ChannelReserve.TlvType()]; ok && val == nil { + var rec tlv.RecordT[tlv.TlvType6, btcutil.Amount] + rec.Val = btcutil.Amount(reserve.Val) + dp.ChannelReserve = tlv.SomeRecordT(rec) } - if val, ok := typeMap[DPMaxAcceptedHtlcs]; ok && val == nil { - dp.MaxAcceptedHTLCs = fn.Some(maxHtlcsScratch) + if val, ok := typeMap[dp.CsvDelay.TlvType()]; ok && val == nil { + dp.CsvDelay = tlv.SomeRecordT(csvDelay) } - if val, ok := typeMap[DPFundingPubkey]; ok && val == nil { - dp.FundingKey = fn.Some(*fundingKeyScratch) + if val, ok := typeMap[dp.MaxAcceptedHTLCs.TlvType()]; ok && val == nil { + dp.MaxAcceptedHTLCs = tlv.SomeRecordT(maxHtlcs) } - if val, ok := typeMap[DPChannelType]; ok && val == nil { - dp.ChannelType = fn.Some(chanTypeScratch) - } - if val, ok := typeMap[DPKickoffFeerate]; ok && val == nil { - dp.KickoffFeerate = fn.Some( - chainfee.SatPerKWeight(kickoffFeerateScratch), - ) + if val, ok := typeMap[dp.ChannelType.TlvType()]; ok && val == nil { + dp.ChannelType = tlv.SomeRecordT(chanType) } if len(tlvRecords) != 0 { @@ -328,3 +167,72 @@ func (dp *DynPropose) MsgType() MessageType { func (dp *DynPropose) SerializedSize() (uint32, error) { return MessageSerializedSize(dp) } + +// SerializeTlvData takes just the TLV data of DynPropose (which covers all of +// the parameters on deck for changing) and serializes just this component. The +// main purpose of this is to make it easier to validate the DynAck signature. +func (dp *DynPropose) SerializeTlvData() ([]byte, error) { + producers := dynProposeRecords(dp) + + var extra ExtraOpaqueData + err := extra.PackRecords(producers...) + if err != nil { + return nil, err + } + + return extra, nil +} + +func dynProposeRecords(dp *DynPropose) []tlv.RecordProducer { + recordProducers := make([]tlv.RecordProducer, 0, 7) + + dp.DustLimit.WhenSome( + func(dl tlv.RecordT[tlv.TlvType0, btcutil.Amount]) { + rec := tlv.NewPrimitiveRecord[tlv.TlvType0]( + uint64(dl.Val), + ) + recordProducers = append(recordProducers, &rec) + }, + ) + dp.MaxValueInFlight.WhenSome( + func(mvif tlv.RecordT[tlv.TlvType2, MilliSatoshi]) { + rec := tlv.NewPrimitiveRecord[tlv.TlvType2]( + uint64(mvif.Val), + ) + recordProducers = append(recordProducers, &rec) + }, + ) + dp.HtlcMinimum.WhenSome( + func(hm tlv.RecordT[tlv.TlvType4, MilliSatoshi]) { + rec := tlv.NewPrimitiveRecord[tlv.TlvType4]( + uint64(hm.Val), + ) + recordProducers = append(recordProducers, &rec) + }, + ) + dp.ChannelReserve.WhenSome( + func(reserve tlv.RecordT[tlv.TlvType6, btcutil.Amount]) { + rec := tlv.NewPrimitiveRecord[tlv.TlvType6]( + uint64(reserve.Val), + ) + recordProducers = append(recordProducers, &rec) + }, + ) + dp.CsvDelay.WhenSome( + func(wait tlv.RecordT[tlv.TlvType8, uint16]) { + recordProducers = append(recordProducers, &wait) + }, + ) + dp.MaxAcceptedHTLCs.WhenSome( + func(mah tlv.RecordT[tlv.TlvType10, uint16]) { + recordProducers = append(recordProducers, &mah) + }, + ) + dp.ChannelType.WhenSome( + func(ty tlv.RecordT[tlv.TlvType12, ChannelType]) { + recordProducers = append(recordProducers, &ty) + }, + ) + + return recordProducers +} diff --git a/lnwire/fuzz_test.go b/lnwire/fuzz_test.go index e608eb9b0bc..054af585cf5 100644 --- a/lnwire/fuzz_test.go +++ b/lnwire/fuzz_test.go @@ -452,6 +452,12 @@ func FuzzDynAck(f *testing.F) { }) } +func FuzzDynCommit(f *testing.F) { + f.Fuzz(func(t *testing.T, data []byte) { + wireMsgHarness(t, data, MsgDynCommit) + }) +} + func FuzzKickoffSig(f *testing.F) { f.Fuzz(func(t *testing.T, data []byte) { wireMsgHarness(t, data, MsgKickoffSig) diff --git a/lnwire/message.go b/lnwire/message.go index ea480075a1a..a36359f0bc3 100644 --- a/lnwire/message.go +++ b/lnwire/message.go @@ -44,6 +44,7 @@ const ( MsgDynPropose = 111 MsgDynAck = 113 MsgDynReject = 115 + MsgDynCommit = 117 MsgUpdateAddHTLC = 128 MsgUpdateFulfillHTLC = 130 MsgUpdateFailHTLC = 131 @@ -140,6 +141,8 @@ func (t MessageType) String() string { return "DynAck" case MsgDynReject: return "DynReject" + case MsgDynCommit: + return "DynCommit" case MsgKickoffSig: return "KickoffSig" case MsgUpdateAddHTLC: @@ -300,6 +303,8 @@ func makeEmptyMessage(msgType MessageType) (Message, error) { msg = &DynAck{} case MsgDynReject: msg = &DynReject{} + case MsgDynCommit: + msg = &DynCommit{} case MsgKickoffSig: msg = &KickoffSig{} case MsgUpdateAddHTLC: diff --git a/lnwire/test_message.go b/lnwire/test_message.go index 9d086c8688a..ec04ae16364 100644 --- a/lnwire/test_message.go +++ b/lnwire/test_message.go @@ -10,7 +10,6 @@ import ( "github.com/btcsuite/btcd/btcutil" "github.com/btcsuite/btcd/chaincfg/chainhash" "github.com/lightningnetwork/lnd/fn/v2" - "github.com/lightningnetwork/lnd/lnwallet/chainfee" "github.com/lightningnetwork/lnd/tlv" "github.com/stretchr/testify/require" "pgregory.net/rapid" @@ -817,7 +816,6 @@ var _ TestMessage = (*DynPropose)(nil) func (dp *DynPropose) RandTestMessage(t *rapid.T) Message { msg := &DynPropose{ ChanID: RandChannelID(t), - Initiator: rapid.Bool().Draw(t, "initiator"), ExtraData: RandExtraOpaqueData(t, nil), } @@ -831,49 +829,47 @@ func (dp *DynPropose) RandTestMessage(t *rapid.T) Message { includeMaxAcceptedHTLCs := rapid.Bool().Draw( t, "includeMaxAcceptedHTLCs", ) - includeFundingKey := rapid.Bool().Draw(t, "includeFundingKey") includeChannelType := rapid.Bool().Draw(t, "includeChannelType") - includeKickoffFeerate := rapid.Bool().Draw(t, "includeKickoffFeerate") // Generate random values for each included field if includeDustLimit { - dl := btcutil.Amount(rapid.Uint32().Draw(t, "dustLimit")) - msg.DustLimit = fn.Some(dl) + var rec tlv.RecordT[tlv.TlvType0, btcutil.Amount] + val := btcutil.Amount(rapid.Uint32().Draw(t, "dustLimit")) + rec.Val = val + msg.DustLimit = tlv.SomeRecordT(rec) } if includeMaxValueInFlight { - mvif := MilliSatoshi(rapid.Uint64().Draw(t, "maxValueInFlight")) - msg.MaxValueInFlight = fn.Some(mvif) + var rec tlv.RecordT[tlv.TlvType2, MilliSatoshi] + val := MilliSatoshi(rapid.Uint64().Draw(t, "maxValueInFlight")) + rec.Val = val + msg.MaxValueInFlight = tlv.SomeRecordT(rec) } if includeChannelReserve { - cr := btcutil.Amount(rapid.Uint32().Draw(t, "channelReserve")) - msg.ChannelReserve = fn.Some(cr) + var rec tlv.RecordT[tlv.TlvType6, btcutil.Amount] + val := btcutil.Amount(rapid.Uint32().Draw(t, "channelReserve")) + rec.Val = val + msg.ChannelReserve = tlv.SomeRecordT(rec) } if includeCsvDelay { - cd := rapid.Uint16().Draw(t, "csvDelay") - msg.CsvDelay = fn.Some(cd) + csvDelay := msg.CsvDelay.Zero() + val := rapid.Uint16().Draw(t, "csvDelay") + csvDelay.Val = val + msg.CsvDelay = tlv.SomeRecordT(csvDelay) } if includeMaxAcceptedHTLCs { - mah := rapid.Uint16().Draw(t, "maxAcceptedHTLCs") - msg.MaxAcceptedHTLCs = fn.Some(mah) - } - - if includeFundingKey { - msg.FundingKey = fn.Some(*RandPubKey(t)) + maxHtlcs := msg.MaxAcceptedHTLCs.Zero() + maxHtlcs.Val = rapid.Uint16().Draw(t, "maxAcceptedHTLCs") + msg.MaxAcceptedHTLCs = tlv.SomeRecordT(maxHtlcs) } if includeChannelType { - msg.ChannelType = fn.Some(*RandChannelType(t)) - } - - if includeKickoffFeerate { - kf := chainfee.SatPerKWeight(rapid.Uint32().Draw( - t, "kickoffFeerate"), - ) - msg.KickoffFeerate = fn.Some(kf) + chanType := msg.ChannelType.Zero() + chanType.Val = *RandChannelType(t) + msg.ChannelType = tlv.SomeRecordT(chanType) } return msg @@ -913,6 +909,91 @@ func (dr *DynReject) RandTestMessage(t *rapid.T) Message { } } +// A compile time check to ensure DynCommit implements the lnwire.TestMessage +// interface. +var _ TestMessage = (*DynCommit)(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 (dc *DynCommit) RandTestMessage(t *rapid.T) Message { + chanID := RandChannelID(t) + + da := &DynAck{ + ChanID: chanID, + } + + dp := &DynPropose{ + ChanID: chanID, + } + + // Randomly decide which optional fields to include + includeDustLimit := rapid.Bool().Draw(t, "includeDustLimit") + includeMaxValueInFlight := rapid.Bool().Draw( + t, "includeMaxValueInFlight", + ) + includeChannelReserve := rapid.Bool().Draw(t, "includeChannelReserve") + includeCsvDelay := rapid.Bool().Draw(t, "includeCsvDelay") + includeMaxAcceptedHTLCs := rapid.Bool().Draw( + t, "includeMaxAcceptedHTLCs", + ) + includeChannelType := rapid.Bool().Draw(t, "includeChannelType") + + // Generate random values for each included field + if includeDustLimit { + var rec tlv.RecordT[tlv.TlvType0, btcutil.Amount] + val := btcutil.Amount(rapid.Uint32().Draw(t, "dustLimit")) + rec.Val = val + dp.DustLimit = tlv.SomeRecordT(rec) + } + + if includeMaxValueInFlight { + var rec tlv.RecordT[tlv.TlvType2, MilliSatoshi] + val := MilliSatoshi(rapid.Uint64().Draw(t, "maxValueInFlight")) + rec.Val = val + dp.MaxValueInFlight = tlv.SomeRecordT(rec) + } + + if includeChannelReserve { + var rec tlv.RecordT[tlv.TlvType6, btcutil.Amount] + val := btcutil.Amount(rapid.Uint32().Draw(t, "channelReserve")) + rec.Val = val + dp.ChannelReserve = tlv.SomeRecordT(rec) + } + + if includeCsvDelay { + csvDelay := dp.CsvDelay.Zero() + val := rapid.Uint16().Draw(t, "csvDelay") + csvDelay.Val = val + dp.CsvDelay = tlv.SomeRecordT(csvDelay) + } + + if includeMaxAcceptedHTLCs { + maxHtlcs := dp.MaxAcceptedHTLCs.Zero() + maxHtlcs.Val = rapid.Uint16().Draw(t, "maxAcceptedHTLCs") + dp.MaxAcceptedHTLCs = tlv.SomeRecordT(maxHtlcs) + } + + if includeChannelType { + chanType := dp.ChannelType.Zero() + chanType.Val = *RandChannelType(t) + dp.ChannelType = tlv.SomeRecordT(chanType) + } + + var extraData ExtraOpaqueData + randData := RandExtraOpaqueData(t, nil) + if len(randData) > 0 { + extraData = randData + } + + return &DynCommit{ + DynPropose: *dp, + DynAck: *da, + ExtraData: extraData, + } +} + // A compile time check to ensure FundingCreated implements the TestMessage // interface. var _ TestMessage = (*FundingCreated)(nil)