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

lightningnetwork / lnd / 11216766535

07 Oct 2024 01:37PM UTC coverage: 57.817% (-1.0%) from 58.817%
11216766535

Pull #9148

github

ProofOfKeags
lnwire: remove kickoff feerate from propose/commit
Pull Request #9148: DynComms [2/n]: lnwire: add authenticated wire messages for Dyn*

571 of 879 new or added lines in 16 files covered. (64.96%)

23253 existing lines in 251 files now uncovered.

99022 of 171268 relevant lines covered (57.82%)

38420.67 hits per line

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

79.63
/channeldb/reports.go
1
package channeldb
2

3
import (
4
        "bytes"
5
        "errors"
6
        "io"
7

8
        "github.com/btcsuite/btcd/btcutil"
9
        "github.com/btcsuite/btcd/chaincfg/chainhash"
10
        "github.com/btcsuite/btcd/wire"
11
        "github.com/lightningnetwork/lnd/kvdb"
12
        "github.com/lightningnetwork/lnd/tlv"
13
)
14

15
var (
16
        // closeSummaryBucket is a top level bucket which holds additional
17
        // information about channel closes. It nests channels by chainhash
18
        // and channel point.
19
        // [closeSummaryBucket]
20
        //        [chainHashBucket]
21
        //                [channelBucket]
22
        //                        [resolversBucket]
23
        closeSummaryBucket = []byte("close-summaries")
24

25
        // resolversBucket holds the outcome of a channel's resolvers. It is
26
        // nested under a channel and chainhash bucket in the close summaries
27
        // bucket.
28
        resolversBucket = []byte("resolvers-bucket")
29
)
30

31
var (
32
        // ErrNoChainHashBucket is returned when we have not created a bucket
33
        // for the current chain hash.
34
        ErrNoChainHashBucket = errors.New("no chain hash bucket")
35

36
        // ErrNoChannelSummaries is returned when a channel is not found in the
37
        // chain hash bucket.
38
        ErrNoChannelSummaries = errors.New("channel bucket not found")
39

40
        amountType    tlv.Type = 1
41
        resolverType  tlv.Type = 2
42
        outcomeType   tlv.Type = 3
43
        spendTxIDType tlv.Type = 4
44
)
45

46
// ResolverType indicates the type of resolver that was resolved on chain.
47
type ResolverType uint8
48

49
const (
50
        // ResolverTypeAnchor represents a resolver for an anchor output.
51
        ResolverTypeAnchor ResolverType = 0
52

53
        // ResolverTypeIncomingHtlc represents resolution of an incoming htlc.
54
        ResolverTypeIncomingHtlc ResolverType = 1
55

56
        // ResolverTypeOutgoingHtlc represents resolution of an outgoing htlc.
57
        ResolverTypeOutgoingHtlc ResolverType = 2
58

59
        // ResolverTypeCommit represents resolution of our time locked commit
60
        // when we force close.
61
        ResolverTypeCommit ResolverType = 3
62
)
63

64
// ResolverOutcome indicates the outcome for the resolver that that the contract
65
// court reached. This state is not necessarily final, since htlcs on our own
66
// commitment are resolved across two resolvers.
67
type ResolverOutcome uint8
68

69
const (
70
        // ResolverOutcomeClaimed indicates that funds were claimed on chain.
71
        ResolverOutcomeClaimed ResolverOutcome = 0
72

73
        // ResolverOutcomeUnclaimed indicates that we did not claim our funds on
74
        // chain. This may be the case for anchors that we did not sweep, or
75
        // outputs that were not economical to sweep.
76
        ResolverOutcomeUnclaimed ResolverOutcome = 1
77

78
        // ResolverOutcomeAbandoned indicates that we did not attempt to claim
79
        // an output on chain. This is the case for htlcs that we could not
80
        // decode to claim, or invoice which we fail when an attempt is made
81
        // to settle them on chain.
82
        ResolverOutcomeAbandoned ResolverOutcome = 2
83

84
        // ResolverOutcomeTimeout indicates that a contract was timed out on
85
        // chain.
86
        ResolverOutcomeTimeout ResolverOutcome = 3
87

88
        // ResolverOutcomeFirstStage indicates that a htlc had to be claimed
89
        // over two stages, with this outcome representing the confirmation
90
        // of our success/timeout tx.
91
        ResolverOutcomeFirstStage ResolverOutcome = 4
92
)
93

94
// ResolverReport provides an account of the outcome of a resolver. This differs
95
// from a ContractReport because it does not necessarily fully resolve the
96
// contract; each step of two stage htlc resolution is included.
97
type ResolverReport struct {
98
        // OutPoint is the on chain outpoint that was spent as a result of this
99
        // resolution. When an output is directly resolved (eg, commitment
100
        // sweeps and single stage htlcs on the remote party's output) this
101
        // is an output on the commitment tx that was broadcast. When we resolve
102
        // across two stages (eg, htlcs on our own force close commit), the
103
        // first stage outpoint is the output on our commitment and the second
104
        // stage output is the spend from our htlc success/timeout tx.
105
        OutPoint wire.OutPoint
106

107
        // Amount is the value of the output referenced above.
108
        Amount btcutil.Amount
109

110
        // ResolverType indicates the type of resolution that occurred.
111
        ResolverType
112

113
        // ResolverOutcome indicates the outcome of the resolver.
114
        ResolverOutcome
115

116
        // SpendTxID is the transaction ID of the spending transaction that
117
        // claimed the outpoint. This may be a sweep transaction, or a first
118
        // stage success/timeout transaction.
119
        SpendTxID *chainhash.Hash
120
}
121

122
// PutResolverReport creates and commits a transaction that is used to write a
123
// resolver report to disk.
124
func (d *DB) PutResolverReport(tx kvdb.RwTx, chainHash chainhash.Hash,
125
        channelOutpoint *wire.OutPoint, report *ResolverReport) error {
3✔
126

3✔
127
        putReportFunc := func(tx kvdb.RwTx) error {
6✔
128
                return putReport(tx, chainHash, channelOutpoint, report)
3✔
129
        }
3✔
130

131
        // If the transaction is nil, we'll create a new one.
132
        if tx == nil {
6✔
133
                return kvdb.Update(d, putReportFunc, func() {})
6✔
134
        }
135

136
        // Otherwise, we can write the report to disk using the existing
137
        // transaction.
UNCOV
138
        return putReportFunc(tx)
×
139
}
140

141
// putReport puts a report in the bucket provided, with its outpoint as its key.
142
func putReport(tx kvdb.RwTx, chainHash chainhash.Hash,
143
        channelOutpoint *wire.OutPoint, report *ResolverReport) error {
3✔
144

3✔
145
        channelBucket, err := fetchReportWriteBucket(
3✔
146
                tx, chainHash, channelOutpoint,
3✔
147
        )
3✔
148
        if err != nil {
3✔
149
                return err
×
150
        }
×
151

152
        // If the resolvers bucket does not exist yet, create it.
153
        resolvers, err := channelBucket.CreateBucketIfNotExists(
3✔
154
                resolversBucket,
3✔
155
        )
3✔
156
        if err != nil {
3✔
157
                return err
×
158
        }
×
159

160
        var valueBuf bytes.Buffer
3✔
161
        if err := serializeReport(&valueBuf, report); err != nil {
3✔
162
                return err
×
163
        }
×
164

165
        // Finally write our outpoint to be used as the key for this record.
166
        var keyBuf bytes.Buffer
3✔
167
        if err := writeOutpoint(&keyBuf, &report.OutPoint); err != nil {
3✔
168
                return err
×
169
        }
×
170

171
        return resolvers.Put(keyBuf.Bytes(), valueBuf.Bytes())
3✔
172
}
173

174
// serializeReport serialized a report using a TLV stream to allow for optional
175
// fields.
176
func serializeReport(w io.Writer, report *ResolverReport) error {
3✔
177
        amt := uint64(report.Amount)
3✔
178
        resolver := uint8(report.ResolverType)
3✔
179
        outcome := uint8(report.ResolverOutcome)
3✔
180

3✔
181
        // Create a set of TLV records for the values we know to be present.
3✔
182
        records := []tlv.Record{
3✔
183
                tlv.MakePrimitiveRecord(amountType, &amt),
3✔
184
                tlv.MakePrimitiveRecord(resolverType, &resolver),
3✔
185
                tlv.MakePrimitiveRecord(outcomeType, &outcome),
3✔
186
        }
3✔
187

3✔
188
        // If our spend txid is non-nil, we add a tlv entry for it.
3✔
189
        if report.SpendTxID != nil {
4✔
190
                var spendBuf bytes.Buffer
1✔
191
                err := WriteElement(&spendBuf, *report.SpendTxID)
1✔
192
                if err != nil {
1✔
193
                        return err
×
194
                }
×
195
                spendBytes := spendBuf.Bytes()
1✔
196

1✔
197
                records = append(records, tlv.MakePrimitiveRecord(
1✔
198
                        spendTxIDType, &spendBytes,
1✔
199
                ))
1✔
200
        }
201

202
        // Create our stream and encode it.
203
        tlvStream, err := tlv.NewStream(records...)
3✔
204
        if err != nil {
3✔
205
                return err
×
206
        }
×
207

208
        return tlvStream.Encode(w)
3✔
209
}
210

211
// FetchChannelReports fetches the set of reports for a channel.
212
func (d DB) FetchChannelReports(chainHash chainhash.Hash,
213
        outPoint *wire.OutPoint) ([]*ResolverReport, error) {
4✔
214

4✔
215
        var reports []*ResolverReport
4✔
216

4✔
217
        if err := kvdb.View(d.Backend, func(tx kvdb.RTx) error {
8✔
218
                chanBucket, err := fetchReportReadBucket(
4✔
219
                        tx, chainHash, outPoint,
4✔
220
                )
4✔
221
                if err != nil {
5✔
222
                        return err
1✔
223
                }
1✔
224

225
                // If there are no resolvers for this channel, we simply
226
                // return nil, because nothing has been persisted yet.
227
                resolvers := chanBucket.NestedReadBucket(resolversBucket)
3✔
228
                if resolvers == nil {
3✔
229
                        return nil
×
230
                }
×
231

232
                // Run through each resolution and add it to our set of
233
                // resolutions.
234
                return resolvers.ForEach(func(k, v []byte) error {
6✔
235
                        // Deserialize the contents of our field.
3✔
236
                        r := bytes.NewReader(v)
3✔
237
                        report, err := deserializeReport(r)
3✔
238
                        if err != nil {
3✔
239
                                return err
×
240
                        }
×
241

242
                        // Once we have read our values out, set the outpoint
243
                        // on the report using the key.
244
                        r = bytes.NewReader(k)
3✔
245
                        if err := ReadElement(r, &report.OutPoint); err != nil {
3✔
246
                                return err
×
247
                        }
×
248

249
                        reports = append(reports, report)
3✔
250

3✔
251
                        return nil
3✔
252
                })
253
        }, func() {
4✔
254
                reports = nil
4✔
255
        }); err != nil {
5✔
256
                return nil, err
1✔
257
        }
1✔
258

259
        return reports, nil
3✔
260
}
261

262
// deserializeReport gets a resolver report from a tlv stream. The outpoint on
263
// the resolver will not be set because we key reports by their outpoint, and
264
// this function reads only the values saved in the stream.
265
func deserializeReport(r io.Reader) (*ResolverReport, error) {
3✔
266
        var (
3✔
267
                resolver, outcome uint8
3✔
268
                amt               uint64
3✔
269
                spentTx           []byte
3✔
270
        )
3✔
271

3✔
272
        tlvStream, err := tlv.NewStream(
3✔
273
                tlv.MakePrimitiveRecord(amountType, &amt),
3✔
274
                tlv.MakePrimitiveRecord(resolverType, &resolver),
3✔
275
                tlv.MakePrimitiveRecord(outcomeType, &outcome),
3✔
276
                tlv.MakePrimitiveRecord(spendTxIDType, &spentTx),
3✔
277
        )
3✔
278
        if err != nil {
3✔
279
                return nil, err
×
280
        }
×
281

282
        if err := tlvStream.Decode(r); err != nil {
3✔
283
                return nil, err
×
284
        }
×
285

286
        report := &ResolverReport{
3✔
287
                Amount:          btcutil.Amount(amt),
3✔
288
                ResolverOutcome: ResolverOutcome(outcome),
3✔
289
                ResolverType:    ResolverType(resolver),
3✔
290
        }
3✔
291

3✔
292
        // If our spend tx is set, we set it on our report.
3✔
293
        if len(spentTx) != 0 {
4✔
294
                spendTx, err := chainhash.NewHash(spentTx)
1✔
295
                if err != nil {
1✔
296
                        return nil, err
×
297
                }
×
298
                report.SpendTxID = spendTx
1✔
299
        }
300

301
        return report, nil
3✔
302
}
303

304
// fetchReportWriteBucket returns a write channel bucket within the reports
305
// top level bucket. If the channel's bucket does not yet exist, it will be
306
// created.
307
func fetchReportWriteBucket(tx kvdb.RwTx, chainHash chainhash.Hash,
308
        outPoint *wire.OutPoint) (kvdb.RwBucket, error) {
7✔
309

7✔
310
        // Get the channel close summary bucket.
7✔
311
        closedBucket := tx.ReadWriteBucket(closeSummaryBucket)
7✔
312

7✔
313
        // Create the chain hash bucket if it does not exist.
7✔
314
        chainHashBkt, err := closedBucket.CreateBucketIfNotExists(chainHash[:])
7✔
315
        if err != nil {
7✔
316
                return nil, err
×
317
        }
×
318

319
        var chanPointBuf bytes.Buffer
7✔
320
        if err := writeOutpoint(&chanPointBuf, outPoint); err != nil {
7✔
321
                return nil, err
×
322
        }
×
323

324
        return chainHashBkt.CreateBucketIfNotExists(chanPointBuf.Bytes())
7✔
325
}
326

327
// fetchReportReadBucket returns a read channel bucket within the reports
328
// top level bucket. If any bucket along the way does not exist, it will error.
329
func fetchReportReadBucket(tx kvdb.RTx, chainHash chainhash.Hash,
330
        outPoint *wire.OutPoint) (kvdb.RBucket, error) {
4✔
331

4✔
332
        // First fetch the top level channel close summary bucket.
4✔
333
        closeBucket := tx.ReadBucket(closeSummaryBucket)
4✔
334

4✔
335
        // Next we get the chain hash bucket for our current chain.
4✔
336
        chainHashBucket := closeBucket.NestedReadBucket(chainHash[:])
4✔
337
        if chainHashBucket == nil {
5✔
338
                return nil, ErrNoChainHashBucket
1✔
339
        }
1✔
340

341
        // With the bucket for the node and chain fetched, we can now go down
342
        // another level, for the channel itself.
343
        var chanPointBuf bytes.Buffer
3✔
344
        if err := writeOutpoint(&chanPointBuf, outPoint); err != nil {
3✔
345
                return nil, err
×
346
        }
×
347

348
        chanBucket := chainHashBucket.NestedReadBucket(chanPointBuf.Bytes())
3✔
349
        if chanBucket == nil {
3✔
UNCOV
350
                return nil, ErrNoChannelSummaries
×
UNCOV
351
        }
×
352

353
        return chanBucket, nil
3✔
354
}
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