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

lightningnetwork / lnd / 11170835610

03 Oct 2024 10:41PM UTC coverage: 49.188% (-9.6%) from 58.738%
11170835610

push

github

web-flow
Merge pull request #9154 from ziggie1984/master

multi: bump btcd version.

3 of 6 new or added lines in 6 files covered. (50.0%)

26110 existing lines in 428 files now uncovered.

97359 of 197934 relevant lines covered (49.19%)

1.04 hits per line

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

71.89
/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 {
2✔
148

2✔
149
        var shaChainRootDesc keychain.KeyDescriptor
2✔
150

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

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

2✔
170
                shaChainRootDesc = keychain.KeyDescriptor{
2✔
171
                        PubKey: shaChainPoint,
2✔
172
                        KeyLocator: keychain.KeyLocator{
2✔
173
                                Family: keychain.KeyFamilyRevocationRoot,
2✔
174
                        },
2✔
175
                }
2✔
176
        }
2✔
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()
2✔
184
        if chanID.BlockHeight == 0 {
4✔
185
                chanID.BlockHeight = channel.BroadcastHeight()
2✔
186
        }
2✔
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() {
4✔
192
                // If the channel is confirmed, we'll use the confirmed SCID.
2✔
193
                if channel.ZeroConfConfirmed() {
4✔
194
                        chanID = channel.ZeroConfRealScid()
2✔
195
                } else {
4✔
196
                        // Else if the zero-conf channel is unconfirmed, we'll
2✔
197
                        // need to use the broadcast height and zero out the
2✔
198
                        // TxIndex and TxPosition fields. This is so
2✔
199
                        // openChannelShell works properly.
2✔
200
                        chanID.BlockHeight = channel.BroadcastHeight()
2✔
201
                        chanID.TxIndex = 0
2✔
202
                        chanID.TxPosition = 0
2✔
203
                }
2✔
204
        }
205

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

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

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

227
        case channel.ChanType.ZeroHtlcTxFee():
2✔
228
                single.Version = AnchorsZeroFeeHtlcTxCommitVersion
2✔
229

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

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

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

240
        return single
2✔
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 {
2✔
246
        // Check to ensure that we'll only attempt to serialize a version that
2✔
247
        // we're aware of.
2✔
248
        switch s.Version {
2✔
249
        case DefaultSingleVersion:
2✔
250
        case TweaklessCommitVersion:
2✔
UNCOV
251
        case AnchorsCommitVersion:
×
252
        case AnchorsZeroFeeHtlcTxCommitVersion:
2✔
253
        case ScriptEnforcedLeaseVersion:
2✔
254
        case SimpleTaprootVersion:
2✔
UNCOV
255
        default:
×
UNCOV
256
                return fmt.Errorf("unable to serialize w/ unknown "+
×
UNCOV
257
                        "version: %v", s.Version)
×
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
2✔
263
        if s.ShaChainRootDesc.PubKey != nil {
4✔
264
                copy(
2✔
265
                        shaChainPub[:],
2✔
266
                        s.ShaChainRootDesc.PubKey.SerializeCompressed(),
2✔
267
                )
2✔
268
        }
2✔
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
2✔
275
        if err := lnwire.WriteElements(
2✔
276
                &singleBytes,
2✔
277
                s.IsInitiator,
2✔
278
                s.ChainHash[:],
2✔
279
                s.FundingOutpoint,
2✔
280
                s.ShortChannelID,
2✔
281
                s.RemoteNodePub,
2✔
282
                s.Addresses,
2✔
283
                s.Capacity,
2✔
284

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

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

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

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

2✔
310
                shaChainPub[:],
2✔
311
                uint32(s.ShaChainRootDesc.KeyLocator.Family),
2✔
312
                s.ShaChainRootDesc.KeyLocator.Index,
2✔
313
        ); err != nil {
2✔
314
                return err
×
315
        }
×
316
        if s.Version == ScriptEnforcedLeaseVersion {
4✔
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)
2✔
326
        if !ok {
2✔
327
                return fmt.Errorf("expect io.Writer to be *bytes.Buffer")
×
328
        }
×
329

330
        return lnwire.WriteElements(
2✔
331
                buf,
2✔
332
                byte(s.Version),
2✔
333
                uint16(len(singleBytes.Bytes())),
2✔
334
                singleBytes.Bytes(),
2✔
335
        )
2✔
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 {
2✔
352
        // First, we'll serialize the SCB (StaticChannelBackup) into a
2✔
353
        // temporary buffer so we can store it in a temporary place before we
2✔
354
        // go to encrypt the entire thing.
2✔
355
        var rawBytes bytes.Buffer
2✔
356
        if err := s.Serialize(&rawBytes); err != nil {
2✔
UNCOV
357
                return err
×
UNCOV
358
        }
×
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)
2✔
364
        if err != nil {
2✔
UNCOV
365
                return fmt.Errorf("unable to generate encrypt key %w", err)
×
UNCOV
366
        }
×
367
        return e.EncryptPayloadToWriter(rawBytes.Bytes(), w)
2✔
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) {
2✔
374
        var keyDesc keychain.KeyDescriptor
2✔
375

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

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

386
        return keyDesc, nil
2✔
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) {
2✔
393
        var (
2✔
394
                keyDesc keychain.KeyDescriptor
2✔
395
                pub     [33]byte
2✔
396
        )
2✔
397

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

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

408
        return keyDesc, nil
2✔
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 {
2✔
415
        // First, we'll need to read the version of this single-back up so we
2✔
416
        // can know how to unpack each of the SCB.
2✔
417
        var version byte
2✔
418
        err := lnwire.ReadElements(r, &version)
2✔
419
        if err != nil {
2✔
420
                return err
×
421
        }
×
422

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

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

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

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

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

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

500
        // Finally, we'll parse out the ShaChainRootDesc.
501
        var (
2✔
502
                shaChainPub [33]byte
2✔
503
                zeroPub     [33]byte
2✔
504
        )
2✔
505
        if err := lnwire.ReadElements(r, shaChainPub[:]); err != nil {
2✔
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[:]) {
4✔
512
                s.ShaChainRootDesc.PubKey, err = btcec.ParsePubKey(
2✔
513
                        shaChainPub[:],
2✔
514
                )
2✔
515
                if err != nil {
2✔
516
                        return err
×
517
                }
×
518
        }
519

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

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

536
        return nil
2✔
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.
UNCOV
544
func (s *Single) UnpackFromReader(r io.Reader, keyRing keychain.KeyRing) error {
×
UNCOV
545
        e, err := lnencrypt.KeyRingEncrypter(keyRing)
×
UNCOV
546
        if err != nil {
×
UNCOV
547
                return fmt.Errorf("unable to generate key decrypter %w", err)
×
UNCOV
548
        }
×
UNCOV
549
        plaintext, err := e.DecryptPayloadFromReader(r)
×
UNCOV
550
        if err != nil {
×
UNCOV
551
                return err
×
UNCOV
552
        }
×
553

554
        // Finally, we'll pack the bytes into a reader to we can deserialize
555
        // the plaintext bytes of the SCB.
UNCOV
556
        backupReader := bytes.NewReader(plaintext)
×
UNCOV
557
        return s.Deserialize(backupReader)
×
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 {
4✔
569
                chanPoint := chanBackup.FundingOutpoint
2✔
570

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

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

581
        return packedBackups, nil
2✔
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.
UNCOV
593
func (p PackedSingles) Unpack(keyRing keychain.KeyRing) ([]Single, error) {
×
UNCOV
594

×
UNCOV
595
        backups := make([]Single, len(p))
×
UNCOV
596
        for i, encryptedBackup := range p {
×
UNCOV
597
                var backup Single
×
UNCOV
598

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

UNCOV
605
                backups[i] = backup
×
606
        }
607

UNCOV
608
        return backups, nil
×
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