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

lightningnetwork / lnd / 15838907453

24 Jun 2025 01:26AM UTC coverage: 57.079% (-11.1%) from 68.172%
15838907453

Pull #9982

github

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

103 of 167 new or added lines in 5 files covered. (61.68%)

30191 existing lines in 463 files now uncovered.

96331 of 168768 relevant lines covered (57.08%)

0.6 hits per line

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

59.0
/lnwire/local_nonces.go
1
package lnwire
2

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

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

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

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

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

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

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

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

61
        var numEntries uint16
1✔
62
        var sortedEntries []LocalNonceEntry
1✔
63

1✔
64
        if lnd.NoncesMap != nil && len(lnd.NoncesMap) > 0 {
2✔
65
                sortedEntries = make([]LocalNonceEntry, 0, len(lnd.NoncesMap))
1✔
66
                for txid, nonce := range lnd.NoncesMap {
2✔
67
                        sortedEntries = append(sortedEntries, LocalNonceEntry{TXID: txid, Nonce: nonce})
1✔
68
                }
1✔
69

70
                sort.Slice(sortedEntries, func(i, j int) bool {
1✔
NEW
71
                        return bytes.Compare(sortedEntries[i].TXID[:], sortedEntries[j].TXID[:]) < 0
×
NEW
72
                })
×
73
                numEntries = uint16(len(sortedEntries))
1✔
74
        }
75

76
        // Write numEntries
77
        var uint16Bytes [2]byte
1✔
78
        binary.BigEndian.PutUint16(uint16Bytes[:], numEntries)
1✔
79
        if _, err := w.Write(uint16Bytes[:]); err != nil {
1✔
NEW
80
                return err
×
NEW
81
        }
×
82

83
        // Write actual entries
84
        for _, entry := range sortedEntries {
2✔
85
                if _, err := w.Write(entry.TXID[:]); err != nil {
1✔
NEW
86
                        return err
×
NEW
87
                }
×
88
                if _, err := w.Write(entry.Nonce[:]); err != nil {
1✔
NEW
89
                        return err
×
NEW
90
                }
×
91
        }
92
        return nil
1✔
93
}
94

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

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

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

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

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

141
        // If numEntries is 0, the map should be empty.
142
        if numEntries == 0 {
1✔
NEW
143
                if len(lnd.NoncesMap) > 0 { // Clear if it had previous entries
×
NEW
144
                                lnd.NoncesMap = make(map[chainhash.Hash]Musig2Nonce)
×
NEW
145
                        }
×
NEW
146
                        return nil
×
147
        }
148

149
        // Prepare the map for new entries. Using 'make' here also clears any
150
        // existing entries if the LocalNoncesData instance is being reused.
151
        lnd.NoncesMap = make(map[chainhash.Hash]Musig2Nonce, numEntries)
1✔
152

1✔
153
        for i := uint16(0); i < numEntries; i++ {
2✔
154
                var txid chainhash.Hash
1✔
155
                var nonce Musig2Nonce
1✔
156

1✔
157
                if _, err := io.ReadFull(r, txid[:]); err != nil {
1✔
NEW
158
                        return err // Should be UnexpectedEOF if recordLen was miscalculated or stream ends early
×
NEW
159
                }
×
160
                if _, err := io.ReadFull(r, nonce[:]); err != nil {
1✔
NEW
161
                        return err // Similar to above
×
NEW
162
                }
×
163
                lnd.NoncesMap[txid] = nonce
1✔
164
        }
165

166
        return nil
1✔
167
}
168

169
// Compile-time checks to ensure LocalNoncesData implements the tlv.RecordProducer interface.
170
var _ tlv.RecordProducer = (*LocalNoncesData)(nil)
171

172
// OptLocalNonces is a type alias for the optional TLV structure.
173
type OptLocalNonces = fn.Option[LocalNoncesData]
174

175
// SomeLocalNonces is a helper function to create an fn.Option[LocalNoncesData]
176
// with the given data.
177
func SomeLocalNonces(data LocalNoncesData) OptLocalNonces {
1✔
178
        return fn.Some(data)
1✔
179
}
1✔
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