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

lightningnetwork / lnd / 15839155990

24 Jun 2025 01:45AM UTC coverage: 58.125%. First build
15839155990

Pull #9982

github

web-flow
Merge 1b95798fc into 45c15646c
Pull Request #9982: lnwire+lnwallet: add LocalNonces field for splice nonce coordination w/ taproot channels

106 of 172 new or added lines in 5 files covered. (61.63%)

98017 of 168630 relevant lines covered (58.13%)

1.8 hits per line

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

59.05
/lnwire/local_nonces.go
1
package lnwire
2

3
import (
4
        "bytes"
5
        // Added for direct binary operations
6
        "encoding/binary"
7
        "io"
8
        "sort"
9

10
        "github.com/btcsuite/btcd/btcec/v2/schnorr/musig2"
11
        "github.com/btcsuite/btcd/chaincfg/chainhash"
12
        "github.com/lightningnetwork/lnd/fn/v2"
13
        "github.com/lightningnetwork/lnd/tlv"
14
)
15

16
// LocalNoncesRecordTypeDef is the concrete TLV record type for LocalNoncesData.
17
// We'll use TlvType22 as it's available and even.
18
type LocalNoncesRecordTypeDef = tlv.TlvType22
19

20
// LocalNonceEntry holds a single TXID -> Musig2Nonce mapping.
21
type LocalNonceEntry struct {
22
        TXID  chainhash.Hash
23
        // Musig2Nonce is [musig2.PubNonceSize]byte
24
        Nonce Musig2Nonce
25
}
26

27
// LocalNoncesData is the core data structure holding the map of nonces.
28
type LocalNoncesData struct {
29
        NoncesMap map[chainhash.Hash]Musig2Nonce
30
}
31

32
// NewLocalNoncesData creates a new LocalNoncesData with an initialized map.
NEW
33
func NewLocalNoncesData() *LocalNoncesData {
×
NEW
34
        return &LocalNoncesData{
×
NEW
35
                NoncesMap: make(map[chainhash.Hash]Musig2Nonce),
×
NEW
36
        }
×
NEW
37
}
×
38

39
// Record implements the tlv.RecordProducer interface.
40
func (lnd *LocalNoncesData) Record() tlv.Record {
3✔
41
        return tlv.MakeStaticRecord(
3✔
42
                (LocalNoncesRecordTypeDef)(nil).TypeVal(),
3✔
43
                lnd,
3✔
44
                // Length function
3✔
45
                func() uint64 {
6✔
46
                        if lnd.NoncesMap == nil || len(lnd.NoncesMap) == 0 {
6✔
47
                                // Just space for numEntries (uint16)
3✔
48
                                return 2
3✔
49
                        }
3✔
50
                        numEntries := len(lnd.NoncesMap)
3✔
51
                        return uint64(2 + numEntries*(chainhash.HashSize+musig2.PubNonceSize))
3✔
52
                }(),
53
                encodeLocalNoncesData,
54
                decodeLocalNoncesData,
55
        )
56
}
57

58
// encodeLocalNoncesData implements the tlv.Encoder for LocalNoncesData.
59
func encodeLocalNoncesData(w io.Writer, val interface{}, _ *[8]byte) error {
3✔
60
        lnd, ok := val.(*LocalNoncesData)
3✔
61
        if !ok {
3✔
NEW
62
                return tlv.NewTypeForEncodingErr(val, "*lnwire.LocalNoncesData")
×
NEW
63
        }
×
64

65
        var numEntries uint16
3✔
66
        var sortedEntries []LocalNonceEntry
3✔
67

3✔
68
        if lnd.NoncesMap != nil && len(lnd.NoncesMap) > 0 {
6✔
69
                sortedEntries = make([]LocalNonceEntry, 0, len(lnd.NoncesMap))
3✔
70
                for txid, nonce := range lnd.NoncesMap {
6✔
71
                        sortedEntries = append(sortedEntries, LocalNonceEntry{TXID: txid, Nonce: nonce})
3✔
72
                }
3✔
73

74
                sort.Slice(sortedEntries, func(i, j int) bool {
3✔
NEW
75
                        return bytes.Compare(sortedEntries[i].TXID[:], sortedEntries[j].TXID[:]) < 0
×
NEW
76
                })
×
77
                numEntries = uint16(len(sortedEntries))
3✔
78
        }
79

80
        // Write numEntries
81
        var uint16Bytes [2]byte
3✔
82
        binary.BigEndian.PutUint16(uint16Bytes[:], numEntries)
3✔
83
        if _, err := w.Write(uint16Bytes[:]); err != nil {
3✔
NEW
84
                return err
×
NEW
85
        }
×
86

87
        // Write actual entries
88
        for _, entry := range sortedEntries {
6✔
89
                if _, err := w.Write(entry.TXID[:]); err != nil {
3✔
NEW
90
                        return err
×
NEW
91
                }
×
92
                if _, err := w.Write(entry.Nonce[:]); err != nil {
3✔
NEW
93
                        return err
×
NEW
94
                }
×
95
        }
96
        return nil
3✔
97
}
98

99
// decodeLocalNoncesData implements the tlv.Decoder for LocalNoncesData.
100
// decodeLocalNoncesData implements the tlv.Decoder for LocalNoncesData.
101
func decodeLocalNoncesData(r io.Reader, val interface{}, _ *[8]byte, recordLen uint64) error {
3✔
102
        lnd, ok := val.(*LocalNoncesData)
3✔
103
        if !ok {
3✔
NEW
104
                return tlv.NewTypeForDecodingErr(val, "*lnwire.LocalNoncesData", recordLen, 0)
×
NEW
105
        }
×
106

107
        // Ensure the map is initialized. This handles cases where an uninitialized
108
        // LocalNoncesData might be passed, or if we want to ensure it's fresh.
109
        // If NewLocalNoncesData was used, NoncesMap would already be non-nil.
110
        // For decoding, we want to populate the passed 'val'.
111
        if lnd.NoncesMap == nil {
6✔
112
                lnd.NoncesMap = make(map[chainhash.Hash]Musig2Nonce)
3✔
113
        }
3✔
114

115
        if recordLen < 2 {
3✔
NEW
116
                // If recordLen is 0, it means an empty TLV value, which is valid for 0 entries.
×
NEW
117
                // Ensure the map is empty in this case.
×
NEW
118
                if recordLen == 0 {
×
NEW
119
                        // Clear if it had previous entries
×
NEW
120
                if len(lnd.NoncesMap) > 0 {
×
NEW
121
                                lnd.NoncesMap = make(map[chainhash.Hash]Musig2Nonce)
×
NEW
122
                        }
×
NEW
123
                        return nil
×
124
                }
125
                // Otherwise, it's too short to even read numEntries.
NEW
126
                return tlv.NewTypeForDecodingErr(lnd, "lnwire.LocalNoncesData (record too short for numEntries)", recordLen, 2)
×
127
        }
128

129
        var numEntriesBytes [2]byte
3✔
130
        if _, err := io.ReadFull(r, numEntriesBytes[:]); err != nil {
3✔
NEW
131
                // This could be io.EOF if recordLen was exactly 0 or 1, which is handled by the check above.
×
NEW
132
                // If recordLen >= 2, io.EOF here would be unexpected.
×
NEW
133
                return err
×
NEW
134
        }
×
135
        numEntries := binary.BigEndian.Uint16(numEntriesBytes[:])
3✔
136

3✔
137
        // Validate overall length against what numEntries implies.
3✔
138
        // The total record length must be 2 (for numEntries) + numEntries * (size_of_entry).
3✔
139
        expectedTotalRecordLength := uint64(2) + (uint64(numEntries) * (chainhash.HashSize + musig2.PubNonceSize))
3✔
140
        if recordLen != expectedTotalRecordLength {
3✔
NEW
141
                return tlv.NewTypeForDecodingErr(
×
NEW
142
                        lnd, "lnwire.LocalNoncesData (record length mismatch)", recordLen, expectedTotalRecordLength,
×
NEW
143
                )
×
NEW
144
        }
×
145

146
        // If numEntries is 0, the map should be empty.
147
        if numEntries == 0 {
3✔
NEW
148
                // Clear if it had previous entries
×
NEW
149
                if len(lnd.NoncesMap) > 0 {
×
NEW
150
                        lnd.NoncesMap = make(map[chainhash.Hash]Musig2Nonce)
×
NEW
151
                }
×
NEW
152
                return nil
×
153
        }
154

155
        // Prepare the map for new entries. Using 'make' here also clears any
156
        // existing entries if the LocalNoncesData instance is being reused.
157
        lnd.NoncesMap = make(map[chainhash.Hash]Musig2Nonce, numEntries)
3✔
158

3✔
159
        for i := uint16(0); i < numEntries; i++ {
6✔
160
                var txid chainhash.Hash
3✔
161
                var nonce Musig2Nonce
3✔
162

3✔
163
                // Should be UnexpectedEOF if recordLen was miscalculated or stream ends early
3✔
164
                if _, err := io.ReadFull(r, txid[:]); err != nil {
3✔
NEW
165
                        return err
×
NEW
166
                }
×
167

168
                // Similar to above
169
                if _, err := io.ReadFull(r, nonce[:]); err != nil {
3✔
NEW
170
                        return err
×
NEW
171
                }
×
172
                lnd.NoncesMap[txid] = nonce
3✔
173
        }
174

175
        return nil
3✔
176
}
177

178
// Compile-time checks to ensure LocalNoncesData implements the tlv.RecordProducer interface.
179
var _ tlv.RecordProducer = (*LocalNoncesData)(nil)
180

181
// OptLocalNonces is a type alias for the optional TLV structure.
182
type OptLocalNonces = fn.Option[LocalNoncesData]
183

184
// SomeLocalNonces is a helper function to create an fn.Option[LocalNoncesData]
185
// with the given data.
186
func SomeLocalNonces(data LocalNoncesData) OptLocalNonces {
3✔
187
        return fn.Some(data)
3✔
188
}
3✔
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