• 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

75.74
/chanbackup/single.go
1
package chanbackup
2

3
import (
4
        "bytes"
5
        "fmt"
6
        "io"
7
        "net"
8

9
        "github.com/btcsuite/btcd/btcec/v2"
10
        "github.com/btcsuite/btcd/btcutil"
11
        "github.com/btcsuite/btcd/chaincfg/chainhash"
12
        "github.com/btcsuite/btcd/wire"
13
        "github.com/lightningnetwork/lnd/channeldb"
14
        "github.com/lightningnetwork/lnd/keychain"
15
        "github.com/lightningnetwork/lnd/lnencrypt"
16
        "github.com/lightningnetwork/lnd/lnwire"
17
)
18

19
// SingleBackupVersion denotes the version of the single static channel backup.
20
// Based on this version, we know how to pack/unpack serialized versions of the
21
// backup.
22
type SingleBackupVersion byte
23

24
const (
25
        // DefaultSingleVersion is the default version of the single channel
26
        // backup. The serialized version of this static channel backup is
27
        // simply: version || SCB. Where SCB is the known format of the
28
        // version.
29
        DefaultSingleVersion = 0
30

31
        // TweaklessCommitVersion is the second SCB version. This version
32
        // implicitly denotes that this channel uses the new tweakless commit
33
        // format.
34
        TweaklessCommitVersion = 1
35

36
        // AnchorsCommitVersion is the third SCB version. This version
37
        // implicitly denotes that this channel uses the new anchor commitment
38
        // format.
39
        AnchorsCommitVersion = 2
40

41
        // AnchorsZeroFeeHtlcTxCommitVersion is a version that denotes this
42
        // channel is using the zero-fee second-level anchor commitment format.
43
        AnchorsZeroFeeHtlcTxCommitVersion = 3
44

45
        // ScriptEnforcedLeaseVersion is a version that denotes this channel is
46
        // using the zero-fee second-level anchor commitment format along with
47
        // an additional CLTV requirement of the channel lease maturity on any
48
        // commitment and HTLC outputs that pay directly to the channel
49
        // initiator.
50
        ScriptEnforcedLeaseVersion = 4
51

52
        // SimpleTaprootVersion is a version that denotes this channel is using
53
        // the musig2 based taproot commitment format.
54
        SimpleTaprootVersion = 5
55
)
56

57
// Single is a static description of an existing channel that can be used for
58
// the purposes of backing up. The fields in this struct allow a node to
59
// recover the settled funds within a channel in the case of partial or
60
// complete data loss. We provide the network address that we last used to
61
// connect to the peer as well, in case the node stops advertising the IP on
62
// the network for whatever reason.
63
//
64
// TODO(roasbeef): suffix version into struct?
65
type Single struct {
66
        // Version is the version that should be observed when attempting to
67
        // pack the single backup.
68
        Version SingleBackupVersion
69

70
        // IsInitiator is true if we were the initiator of the channel, and
71
        // false otherwise. We'll need to know this information in order to
72
        // properly re-derive the state hint information.
73
        IsInitiator bool
74

75
        // ChainHash is a hash which represents the blockchain that this
76
        // channel will be opened within. This value is typically the genesis
77
        // hash. In the case that the original chain went through a contentious
78
        // hard-fork, then this value will be tweaked using the unique fork
79
        // point on each branch.
80
        ChainHash chainhash.Hash
81

82
        // FundingOutpoint is the outpoint of the final funding transaction.
83
        // This value uniquely and globally identities the channel within the
84
        // target blockchain as specified by the chain hash parameter.
85
        FundingOutpoint wire.OutPoint
86

87
        // ShortChannelID encodes the exact location in the chain in which the
88
        // channel was initially confirmed. This includes: the block height,
89
        // transaction index, and the output within the target transaction.
90
        // Channels that were not confirmed at the time of backup creation will
91
        // have the funding TX broadcast height set as their block height in
92
        // the ShortChannelID.
93
        ShortChannelID lnwire.ShortChannelID
94

95
        // RemoteNodePub is the identity public key of the remote node this
96
        // channel has been established with.
97
        RemoteNodePub *btcec.PublicKey
98

99
        // Addresses is a list of IP address in which either we were able to
100
        // reach the node over in the past, OR we received an incoming
101
        // authenticated connection for the stored identity public key.
102
        Addresses []net.Addr
103

104
        // Capacity is the size of the original channel.
105
        Capacity btcutil.Amount
106

107
        // LocalChanCfg is our local channel configuration. It contains all the
108
        // information we need to re-derive the keys we used within the
109
        // channel. Most importantly, it allows to derive the base public
110
        // that's used to deriving the key used within the non-delayed
111
        // pay-to-self output on the commitment transaction for a node. With
112
        // this information, we can re-derive the private key needed to sweep
113
        // the funds on-chain.
114
        //
115
        // NOTE: Of the items in the ChannelConstraints, we only write the CSV
116
        // delay.
117
        LocalChanCfg channeldb.ChannelConfig
118

119
        // RemoteChanCfg is the remote channel confirmation. We store this as
120
        // well since we'll need some of their keys to re-derive things like
121
        // the state hint obfuscator which will allow us to recognize the state
122
        // their broadcast on chain.
123
        //
124
        // NOTE: Of the items in the ChannelConstraints, we only write the CSV
125
        // delay.
126
        RemoteChanCfg channeldb.ChannelConfig
127

128
        // ShaChainRootDesc describes how to derive the private key that was
129
        // used as the shachain root for this channel.
130
        ShaChainRootDesc keychain.KeyDescriptor
131

132
        // LeaseExpiry represents the absolute expiration as a height of the
133
        // chain of a channel lease that is applied to every output that pays
134
        // directly to the channel initiator in addition to the usual CSV
135
        // requirement.
136
        //
137
        // NOTE: This field will only be present for the following versions:
138
        //
139
        // - ScriptEnforcedLeaseVersion
140
        LeaseExpiry uint32
141
}
142

143
// NewSingle creates a new static channel backup based on an existing open
144
// channel. We also pass in the set of addresses that we used in the past to
145
// connect to the channel peer.
146
func NewSingle(channel *channeldb.OpenChannel,
147
        nodeAddrs []net.Addr) Single {
65✔
148

65✔
149
        var shaChainRootDesc keychain.KeyDescriptor
65✔
150

65✔
151
        // If the channel has a populated RevocationKeyLocator, then we can
65✔
152
        // just store that instead of the public key.
65✔
153
        if channel.RevocationKeyLocator.Family == keychain.KeyFamilyRevocationRoot {
65✔
UNCOV
154
                shaChainRootDesc = keychain.KeyDescriptor{
×
UNCOV
155
                        KeyLocator: channel.RevocationKeyLocator,
×
UNCOV
156
                }
×
157
        } else {
65✔
158
                // If the RevocationKeyLocator is not populated, then we'll need
65✔
159
                // to obtain a public point for the shachain root and store that.
65✔
160
                // This is the legacy scheme.
65✔
161
                var b bytes.Buffer
65✔
162
                _ = channel.RevocationProducer.Encode(&b) // Can't return an error.
65✔
163

65✔
164
                // Once we have the root, we'll make a public key from it, such that
65✔
165
                // the backups plaintext don't carry any private information. When
65✔
166
                // we go to recover, we'll present this in order to derive the
65✔
167
                // private key.
65✔
168
                _, shaChainPoint := btcec.PrivKeyFromBytes(b.Bytes())
65✔
169

65✔
170
                shaChainRootDesc = keychain.KeyDescriptor{
65✔
171
                        PubKey: shaChainPoint,
65✔
172
                        KeyLocator: keychain.KeyLocator{
65✔
173
                                Family: keychain.KeyFamilyRevocationRoot,
65✔
174
                        },
65✔
175
                }
65✔
176
        }
65✔
177

178
        // If a channel is unconfirmed, the block height of the ShortChannelID
179
        // is zero. This will lead to problems when trying to restore that
180
        // channel as the spend notifier would get a height hint of zero.
181
        // To work around that problem, we add the channel broadcast height
182
        // to the channel ID so we can use that as height hint on restore.
183
        chanID := channel.ShortChanID()
65✔
184
        if chanID.BlockHeight == 0 {
66✔
185
                chanID.BlockHeight = channel.BroadcastHeight()
1✔
186
        }
1✔
187

188
        // If this is a zero-conf channel, we'll need to have separate logic
189
        // depending on whether it's confirmed or not. This is because the
190
        // ShortChanID is an alias.
191
        if channel.IsZeroConf() {
65✔
UNCOV
192
                // If the channel is confirmed, we'll use the confirmed SCID.
×
UNCOV
193
                if channel.ZeroConfConfirmed() {
×
UNCOV
194
                        chanID = channel.ZeroConfRealScid()
×
UNCOV
195
                } else {
×
UNCOV
196
                        // Else if the zero-conf channel is unconfirmed, we'll
×
UNCOV
197
                        // need to use the broadcast height and zero out the
×
UNCOV
198
                        // TxIndex and TxPosition fields. This is so
×
UNCOV
199
                        // openChannelShell works properly.
×
UNCOV
200
                        chanID.BlockHeight = channel.BroadcastHeight()
×
UNCOV
201
                        chanID.TxIndex = 0
×
UNCOV
202
                        chanID.TxPosition = 0
×
UNCOV
203
                }
×
204
        }
205

206
        single := Single{
65✔
207
                IsInitiator:      channel.IsInitiator,
65✔
208
                ChainHash:        channel.ChainHash,
65✔
209
                FundingOutpoint:  channel.FundingOutpoint,
65✔
210
                ShortChannelID:   chanID,
65✔
211
                RemoteNodePub:    channel.IdentityPub,
65✔
212
                Addresses:        nodeAddrs,
65✔
213
                Capacity:         channel.Capacity,
65✔
214
                LocalChanCfg:     channel.LocalChanCfg,
65✔
215
                RemoteChanCfg:    channel.RemoteChanCfg,
65✔
216
                ShaChainRootDesc: shaChainRootDesc,
65✔
217
        }
65✔
218

65✔
219
        switch {
65✔
UNCOV
220
        case channel.ChanType.IsTaproot():
×
UNCOV
221
                single.Version = SimpleTaprootVersion
×
222

UNCOV
223
        case channel.ChanType.HasLeaseExpiration():
×
UNCOV
224
                single.Version = ScriptEnforcedLeaseVersion
×
UNCOV
225
                single.LeaseExpiry = channel.ThawHeight
×
226

UNCOV
227
        case channel.ChanType.ZeroHtlcTxFee():
×
UNCOV
228
                single.Version = AnchorsZeroFeeHtlcTxCommitVersion
×
229

230
        case channel.ChanType.HasAnchors():
×
231
                single.Version = AnchorsCommitVersion
×
232

233
        case channel.ChanType.IsTweakless():
29✔
234
                single.Version = TweaklessCommitVersion
29✔
235

236
        default:
36✔
237
                single.Version = DefaultSingleVersion
36✔
238
        }
239

240
        return single
65✔
241
}
242

243
// Serialize attempts to write out the serialized version of the target
244
// StaticChannelBackup into the passed io.Writer.
245
func (s *Single) Serialize(w io.Writer) error {
79✔
246
        // Check to ensure that we'll only attempt to serialize a version that
79✔
247
        // we're aware of.
79✔
248
        switch s.Version {
79✔
249
        case DefaultSingleVersion:
41✔
250
        case TweaklessCommitVersion:
33✔
251
        case AnchorsCommitVersion:
2✔
UNCOV
252
        case AnchorsZeroFeeHtlcTxCommitVersion:
×
253
        case ScriptEnforcedLeaseVersion:
2✔
UNCOV
254
        case SimpleTaprootVersion:
×
255
        default:
1✔
256
                return fmt.Errorf("unable to serialize w/ unknown "+
1✔
257
                        "version: %v", s.Version)
1✔
258
        }
259

260
        // If the sha chain root has specified a public key (which is
261
        // optional), then we'll encode it now.
262
        var shaChainPub [33]byte
78✔
263
        if s.ShaChainRootDesc.PubKey != nil {
156✔
264
                copy(
78✔
265
                        shaChainPub[:],
78✔
266
                        s.ShaChainRootDesc.PubKey.SerializeCompressed(),
78✔
267
                )
78✔
268
        }
78✔
269

270
        // First we gather the SCB as is into a temporary buffer so we can
271
        // determine the total length. Before we write out the serialized SCB,
272
        // we write the length which allows us to skip any Singles that we
273
        // don't know of when decoding a multi.
274
        var singleBytes bytes.Buffer
78✔
275
        if err := lnwire.WriteElements(
78✔
276
                &singleBytes,
78✔
277
                s.IsInitiator,
78✔
278
                s.ChainHash[:],
78✔
279
                s.FundingOutpoint,
78✔
280
                s.ShortChannelID,
78✔
281
                s.RemoteNodePub,
78✔
282
                s.Addresses,
78✔
283
                s.Capacity,
78✔
284

78✔
285
                s.LocalChanCfg.CsvDelay,
78✔
286

78✔
287
                // We only need to write out the KeyLocator portion of the
78✔
288
                // local channel config.
78✔
289
                uint32(s.LocalChanCfg.MultiSigKey.Family),
78✔
290
                s.LocalChanCfg.MultiSigKey.Index,
78✔
291
                uint32(s.LocalChanCfg.RevocationBasePoint.Family),
78✔
292
                s.LocalChanCfg.RevocationBasePoint.Index,
78✔
293
                uint32(s.LocalChanCfg.PaymentBasePoint.Family),
78✔
294
                s.LocalChanCfg.PaymentBasePoint.Index,
78✔
295
                uint32(s.LocalChanCfg.DelayBasePoint.Family),
78✔
296
                s.LocalChanCfg.DelayBasePoint.Index,
78✔
297
                uint32(s.LocalChanCfg.HtlcBasePoint.Family),
78✔
298
                s.LocalChanCfg.HtlcBasePoint.Index,
78✔
299

78✔
300
                s.RemoteChanCfg.CsvDelay,
78✔
301

78✔
302
                // We only need to write out the raw pubkey for the remote
78✔
303
                // channel config.
78✔
304
                s.RemoteChanCfg.MultiSigKey.PubKey,
78✔
305
                s.RemoteChanCfg.RevocationBasePoint.PubKey,
78✔
306
                s.RemoteChanCfg.PaymentBasePoint.PubKey,
78✔
307
                s.RemoteChanCfg.DelayBasePoint.PubKey,
78✔
308
                s.RemoteChanCfg.HtlcBasePoint.PubKey,
78✔
309

78✔
310
                shaChainPub[:],
78✔
311
                uint32(s.ShaChainRootDesc.KeyLocator.Family),
78✔
312
                s.ShaChainRootDesc.KeyLocator.Index,
78✔
313
        ); err != nil {
78✔
314
                return err
×
315
        }
×
316
        if s.Version == ScriptEnforcedLeaseVersion {
80✔
317
                err := lnwire.WriteElements(&singleBytes, s.LeaseExpiry)
2✔
318
                if err != nil {
2✔
319
                        return err
×
320
                }
×
321
        }
322

323
        // TODO(yy): remove the type assertion when we finished refactoring db
324
        // into using write buffer.
325
        buf, ok := w.(*bytes.Buffer)
78✔
326
        if !ok {
78✔
327
                return fmt.Errorf("expect io.Writer to be *bytes.Buffer")
×
328
        }
×
329

330
        return lnwire.WriteElements(
78✔
331
                buf,
78✔
332
                byte(s.Version),
78✔
333
                uint16(len(singleBytes.Bytes())),
78✔
334
                singleBytes.Bytes(),
78✔
335
        )
78✔
336
}
337

338
// PackToWriter is similar to the Serialize method, but takes the operation a
339
// step further by encryption the raw bytes of the static channel back up. For
340
// encryption we use the chacah20poly1305 AEAD cipher with a 24 byte nonce and
341
// 32-byte key size. We use a 24-byte nonce, as we can't ensure that we have a
342
// global counter to use as a sequence number for nonces, and want to ensure
343
// that we're able to decrypt these blobs without any additional context. We
344
// derive the key that we use for encryption via a SHA2 operation of the with
345
// the golden keychain.KeyFamilyBaseEncryption base encryption key.  We then
346
// take the serialized resulting shared secret point, and hash it using sha256
347
// to obtain the key that we'll use for encryption. When using the AEAD, we
348
// pass the nonce as associated data such that we'll be able to package the two
349
// together for storage. Before writing out the encrypted payload, we prepend
350
// the nonce to the final blob.
351
func (s *Single) PackToWriter(w io.Writer, keyRing keychain.KeyRing) error {
37✔
352
        // First, we'll serialize the SCB (StaticChannelBackup) into a
37✔
353
        // temporary buffer so we can store it in a temporary place before we
37✔
354
        // go to encrypt the entire thing.
37✔
355
        var rawBytes bytes.Buffer
37✔
356
        if err := s.Serialize(&rawBytes); err != nil {
38✔
357
                return err
1✔
358
        }
1✔
359

360
        // Finally, we'll encrypt the raw serialized SCB (using the nonce as
361
        // associated data), and write out the ciphertext prepend with the
362
        // nonce that we used to the passed io.Reader.
363
        e, err := lnencrypt.KeyRingEncrypter(keyRing)
36✔
364
        if err != nil {
37✔
365
                return fmt.Errorf("unable to generate encrypt key %w", err)
1✔
366
        }
1✔
367
        return e.EncryptPayloadToWriter(rawBytes.Bytes(), w)
35✔
368
}
369

370
// readLocalKeyDesc reads a KeyDescriptor encoded within an unpacked Single.
371
// For local KeyDescs, we only write out the KeyLocator information as we can
372
// re-derive the pubkey from it.
373
func readLocalKeyDesc(r io.Reader) (keychain.KeyDescriptor, error) {
650✔
374
        var keyDesc keychain.KeyDescriptor
650✔
375

650✔
376
        var keyFam uint32
650✔
377
        if err := lnwire.ReadElements(r, &keyFam); err != nil {
650✔
378
                return keyDesc, err
×
379
        }
×
380
        keyDesc.Family = keychain.KeyFamily(keyFam)
650✔
381

650✔
382
        if err := lnwire.ReadElements(r, &keyDesc.Index); err != nil {
650✔
383
                return keyDesc, err
×
384
        }
×
385

386
        return keyDesc, nil
650✔
387
}
388

389
// readRemoteKeyDesc reads a remote KeyDescriptor encoded within an unpacked
390
// Single. For remote KeyDescs, we write out only the PubKey since we don't
391
// actually have the KeyLocator data.
392
func readRemoteKeyDesc(r io.Reader) (keychain.KeyDescriptor, error) {
650✔
393
        var (
650✔
394
                keyDesc keychain.KeyDescriptor
650✔
395
                pub     [33]byte
650✔
396
        )
650✔
397

650✔
398
        _, err := io.ReadFull(r, pub[:])
650✔
399
        if err != nil {
650✔
400
                return keychain.KeyDescriptor{}, err
×
401
        }
×
402

403
        keyDesc.PubKey, err = btcec.ParsePubKey(pub[:])
650✔
404
        if err != nil {
650✔
405
                return keychain.KeyDescriptor{}, err
×
406
        }
×
407

408
        return keyDesc, nil
650✔
409
}
410

411
// Deserialize attempts to read the raw plaintext serialized SCB from the
412
// passed io.Reader. If the method is successful, then the target
413
// StaticChannelBackup will be fully populated.
414
func (s *Single) Deserialize(r io.Reader) error {
134✔
415
        // First, we'll need to read the version of this single-back up so we
134✔
416
        // can know how to unpack each of the SCB.
134✔
417
        var version byte
134✔
418
        err := lnwire.ReadElements(r, &version)
134✔
419
        if err != nil {
134✔
420
                return err
×
421
        }
×
422

423
        s.Version = SingleBackupVersion(version)
134✔
424

134✔
425
        switch s.Version {
134✔
426
        case DefaultSingleVersion:
73✔
427
        case TweaklessCommitVersion:
55✔
428
        case AnchorsCommitVersion:
1✔
UNCOV
429
        case AnchorsZeroFeeHtlcTxCommitVersion:
×
430
        case ScriptEnforcedLeaseVersion:
1✔
UNCOV
431
        case SimpleTaprootVersion:
×
432
        default:
4✔
433
                return fmt.Errorf("unable to de-serialize w/ unknown "+
4✔
434
                        "version: %v", s.Version)
4✔
435
        }
436

437
        var length uint16
130✔
438
        if err := lnwire.ReadElements(r, &length); err != nil {
130✔
439
                return err
×
440
        }
×
441

442
        err = lnwire.ReadElements(
130✔
443
                r, &s.IsInitiator, s.ChainHash[:], &s.FundingOutpoint,
130✔
444
                &s.ShortChannelID, &s.RemoteNodePub, &s.Addresses, &s.Capacity,
130✔
445
        )
130✔
446
        if err != nil {
130✔
447
                return err
×
448
        }
×
449

450
        err = lnwire.ReadElements(r, &s.LocalChanCfg.CsvDelay)
130✔
451
        if err != nil {
130✔
452
                return err
×
453
        }
×
454
        s.LocalChanCfg.MultiSigKey, err = readLocalKeyDesc(r)
130✔
455
        if err != nil {
130✔
456
                return err
×
457
        }
×
458
        s.LocalChanCfg.RevocationBasePoint, err = readLocalKeyDesc(r)
130✔
459
        if err != nil {
130✔
460
                return err
×
461
        }
×
462
        s.LocalChanCfg.PaymentBasePoint, err = readLocalKeyDesc(r)
130✔
463
        if err != nil {
130✔
464
                return err
×
465
        }
×
466
        s.LocalChanCfg.DelayBasePoint, err = readLocalKeyDesc(r)
130✔
467
        if err != nil {
130✔
468
                return err
×
469
        }
×
470
        s.LocalChanCfg.HtlcBasePoint, err = readLocalKeyDesc(r)
130✔
471
        if err != nil {
130✔
472
                return err
×
473
        }
×
474

475
        err = lnwire.ReadElements(r, &s.RemoteChanCfg.CsvDelay)
130✔
476
        if err != nil {
130✔
477
                return err
×
478
        }
×
479
        s.RemoteChanCfg.MultiSigKey, err = readRemoteKeyDesc(r)
130✔
480
        if err != nil {
130✔
481
                return err
×
482
        }
×
483
        s.RemoteChanCfg.RevocationBasePoint, err = readRemoteKeyDesc(r)
130✔
484
        if err != nil {
130✔
485
                return err
×
486
        }
×
487
        s.RemoteChanCfg.PaymentBasePoint, err = readRemoteKeyDesc(r)
130✔
488
        if err != nil {
130✔
489
                return err
×
490
        }
×
491
        s.RemoteChanCfg.DelayBasePoint, err = readRemoteKeyDesc(r)
130✔
492
        if err != nil {
130✔
493
                return err
×
494
        }
×
495
        s.RemoteChanCfg.HtlcBasePoint, err = readRemoteKeyDesc(r)
130✔
496
        if err != nil {
130✔
497
                return err
×
498
        }
×
499

500
        // Finally, we'll parse out the ShaChainRootDesc.
501
        var (
130✔
502
                shaChainPub [33]byte
130✔
503
                zeroPub     [33]byte
130✔
504
        )
130✔
505
        if err := lnwire.ReadElements(r, shaChainPub[:]); err != nil {
130✔
506
                return err
×
507
        }
×
508

509
        // Since this field is optional, we'll check to see if the pubkey has
510
        // been specified or not.
511
        if !bytes.Equal(shaChainPub[:], zeroPub[:]) {
260✔
512
                s.ShaChainRootDesc.PubKey, err = btcec.ParsePubKey(
130✔
513
                        shaChainPub[:],
130✔
514
                )
130✔
515
                if err != nil {
130✔
516
                        return err
×
517
                }
×
518
        }
519

520
        var shaKeyFam uint32
130✔
521
        if err := lnwire.ReadElements(r, &shaKeyFam); err != nil {
130✔
522
                return err
×
523
        }
×
524
        s.ShaChainRootDesc.KeyLocator.Family = keychain.KeyFamily(shaKeyFam)
130✔
525
        err = lnwire.ReadElements(r, &s.ShaChainRootDesc.KeyLocator.Index)
130✔
526
        if err != nil {
130✔
527
                return err
×
528
        }
×
529

530
        if s.Version == ScriptEnforcedLeaseVersion {
131✔
531
                if err := lnwire.ReadElement(r, &s.LeaseExpiry); err != nil {
1✔
532
                        return err
×
533
                }
×
534
        }
535

536
        return nil
130✔
537
}
538

539
// UnpackFromReader is similar to Deserialize method, but it expects the passed
540
// io.Reader to contain an encrypt SCB. Refer to the SerializeAndEncrypt method
541
// for details w.r.t the encryption scheme used. If we're unable to decrypt the
542
// payload for whatever reason (wrong key, wrong nonce, etc), then this method
543
// will return an error.
544
func (s *Single) UnpackFromReader(r io.Reader, keyRing keychain.KeyRing) error {
57✔
545
        e, err := lnencrypt.KeyRingEncrypter(keyRing)
57✔
546
        if err != nil {
58✔
547
                return fmt.Errorf("unable to generate key decrypter %w", err)
1✔
548
        }
1✔
549
        plaintext, err := e.DecryptPayloadFromReader(r)
56✔
550
        if err != nil {
57✔
551
                return err
1✔
552
        }
1✔
553

554
        // Finally, we'll pack the bytes into a reader to we can deserialize
555
        // the plaintext bytes of the SCB.
556
        backupReader := bytes.NewReader(plaintext)
55✔
557
        return s.Deserialize(backupReader)
55✔
558
}
559

560
// PackStaticChanBackups accepts a set of existing open channels, and a
561
// keychain.KeyRing, and returns a map of outpoints to the serialized+encrypted
562
// static channel backups. The passed keyRing should be backed by the users
563
// root HD seed in order to ensure full determinism.
564
func PackStaticChanBackups(backups []Single,
565
        keyRing keychain.KeyRing) (map[wire.OutPoint][]byte, error) {
2✔
566

2✔
567
        packedBackups := make(map[wire.OutPoint][]byte)
2✔
568
        for _, chanBackup := range backups {
13✔
569
                chanPoint := chanBackup.FundingOutpoint
11✔
570

11✔
571
                var b bytes.Buffer
11✔
572
                err := chanBackup.PackToWriter(&b, keyRing)
11✔
573
                if err != nil {
12✔
574
                        return nil, fmt.Errorf("unable to pack chan backup "+
1✔
575
                                "for %v: %v", chanPoint, err)
1✔
576
                }
1✔
577

578
                packedBackups[chanPoint] = b.Bytes()
10✔
579
        }
580

581
        return packedBackups, nil
1✔
582
}
583

584
// PackedSingles represents a series of fully packed SCBs. This may be the
585
// combination of a series of individual SCBs in order to batch their
586
// unpacking.
587
type PackedSingles [][]byte
588

589
// Unpack attempts to decrypt the passed set of encrypted SCBs and deserialize
590
// each one into a new SCB struct. The passed keyRing should be backed by the
591
// same HD seed as was used to encrypt the set of backups in the first place.
592
// If we're unable to decrypt any of the back ups, then we'll return an error.
593
func (p PackedSingles) Unpack(keyRing keychain.KeyRing) ([]Single, error) {
6✔
594

6✔
595
        backups := make([]Single, len(p))
6✔
596
        for i, encryptedBackup := range p {
48✔
597
                var backup Single
42✔
598

42✔
599
                backupReader := bytes.NewReader(encryptedBackup)
42✔
600
                err := backup.UnpackFromReader(backupReader, keyRing)
42✔
601
                if err != nil {
44✔
602
                        return nil, err
2✔
603
                }
2✔
604

605
                backups[i] = backup
40✔
606
        }
607

608
        return backups, nil
4✔
609
}
610

611
// TODO(roasbeef): make codec package?
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