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

lightningnetwork / lnd / 12430538171

20 Dec 2024 11:21AM UTC coverage: 58.716% (+0.1%) from 58.576%
12430538171

push

github

web-flow
Merge pull request #9381 from yyforyongyu/fix-no-space-left

workflows: fix no space left error

135265 of 230373 relevant lines covered (58.72%)

19174.52 hits per line

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

74.25
/chanrestore.go
1
package lnd
2

3
import (
4
        "fmt"
5
        "math"
6
        "net"
7

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

20
const (
21
        // mainnetSCBLaunchBlock is the approximate block height of the bitcoin
22
        // mainnet chain of the date when SCBs first were released in lnd
23
        // (v0.6.0-beta). The block date is 4/15/2019, 10:54 PM UTC.
24
        mainnetSCBLaunchBlock = 571800
25

26
        // testnetSCBLaunchBlock is the approximate block height of the bitcoin
27
        // testnet3 chain of the date when SCBs first were released in lnd
28
        // (v0.6.0-beta). The block date is 4/16/2019, 08:04 AM UTC.
29
        testnetSCBLaunchBlock = 1489300
30
)
31

32
// chanDBRestorer is an implementation of the chanbackup.ChannelRestorer
33
// interface that is able to properly map a Single backup, into a
34
// channeldb.ChannelShell which is required to fully restore a channel. We also
35
// need the secret key chain in order obtain the prior shachain root so we can
36
// verify the DLP protocol as initiated by the remote node.
37
type chanDBRestorer struct {
38
        db *channeldb.ChannelStateDB
39

40
        secretKeys keychain.SecretKeyRing
41

42
        chainArb *contractcourt.ChainArbitrator
43
}
44

45
// openChannelShell maps the static channel back up into an open channel
46
// "shell". We say shell as this doesn't include all the information required
47
// to continue to use the channel, only the minimal amount of information to
48
// insert this shell channel back into the database.
49
func (c *chanDBRestorer) openChannelShell(backup chanbackup.Single) (
50
        *channeldb.ChannelShell, error) {
3✔
51

3✔
52
        var err error
3✔
53

3✔
54
        // Each of the keys in our local channel config only have their
3✔
55
        // locators populate, so we'll re-derive the raw key now as we'll need
3✔
56
        // it in order to carry out the DLP protocol.
3✔
57
        backup.LocalChanCfg.MultiSigKey, err = c.secretKeys.DeriveKey(
3✔
58
                backup.LocalChanCfg.MultiSigKey.KeyLocator,
3✔
59
        )
3✔
60
        if err != nil {
3✔
61
                return nil, fmt.Errorf("unable to derive multi sig key: %w",
×
62
                        err)
×
63
        }
×
64
        backup.LocalChanCfg.RevocationBasePoint, err = c.secretKeys.DeriveKey(
3✔
65
                backup.LocalChanCfg.RevocationBasePoint.KeyLocator,
3✔
66
        )
3✔
67
        if err != nil {
3✔
68
                return nil, fmt.Errorf("unable to derive revocation key: %w",
×
69
                        err)
×
70
        }
×
71
        backup.LocalChanCfg.PaymentBasePoint, err = c.secretKeys.DeriveKey(
3✔
72
                backup.LocalChanCfg.PaymentBasePoint.KeyLocator,
3✔
73
        )
3✔
74
        if err != nil {
3✔
75
                return nil, fmt.Errorf("unable to derive payment key: %w", err)
×
76
        }
×
77
        backup.LocalChanCfg.DelayBasePoint, err = c.secretKeys.DeriveKey(
3✔
78
                backup.LocalChanCfg.DelayBasePoint.KeyLocator,
3✔
79
        )
3✔
80
        if err != nil {
3✔
81
                return nil, fmt.Errorf("unable to derive delay key: %w", err)
×
82
        }
×
83
        backup.LocalChanCfg.HtlcBasePoint, err = c.secretKeys.DeriveKey(
3✔
84
                backup.LocalChanCfg.HtlcBasePoint.KeyLocator,
3✔
85
        )
3✔
86
        if err != nil {
3✔
87
                return nil, fmt.Errorf("unable to derive htlc key: %w", err)
×
88
        }
×
89

90
        // The shachain root that seeds RevocationProducer for this channel.
91
        // It currently has two possible formats.
92
        var revRoot *chainhash.Hash
3✔
93

3✔
94
        // If the PubKey field is non-nil, then this shachain root is using the
3✔
95
        // legacy non-ECDH scheme.
3✔
96
        if backup.ShaChainRootDesc.PubKey != nil {
6✔
97
                ltndLog.Debugf("Using legacy revocation producer format for "+
3✔
98
                        "channel point %v", backup.FundingOutpoint)
3✔
99

3✔
100
                // Obtain the private key for the shachain root from the
3✔
101
                // encoded public key.
3✔
102
                privKey, err := c.secretKeys.DerivePrivKey(
3✔
103
                        backup.ShaChainRootDesc,
3✔
104
                )
3✔
105
                if err != nil {
3✔
106
                        return nil, fmt.Errorf("could not derive private key "+
×
107
                                "for legacy channel revocation root format: "+
×
108
                                "%v", err)
×
109
                }
×
110

111
                revRoot, err = chainhash.NewHash(privKey.Serialize())
3✔
112
                if err != nil {
3✔
113
                        return nil, err
×
114
                }
×
115
        } else {
3✔
116
                ltndLog.Debugf("Using new ECDH revocation producer format "+
3✔
117
                        "for channel point %v", backup.FundingOutpoint)
3✔
118

3✔
119
                // This is the scheme in which the shachain root is derived via
3✔
120
                // an ECDH operation on the private key of ShaChainRootDesc and
3✔
121
                // our public multisig key.
3✔
122
                ecdh, err := c.secretKeys.ECDH(
3✔
123
                        backup.ShaChainRootDesc,
3✔
124
                        backup.LocalChanCfg.MultiSigKey.PubKey,
3✔
125
                )
3✔
126
                if err != nil {
3✔
127
                        return nil, fmt.Errorf("unable to derive shachain "+
×
128
                                "root: %v", err)
×
129
                }
×
130

131
                ch := chainhash.Hash(ecdh)
3✔
132
                revRoot = &ch
3✔
133
        }
134

135
        shaChainProducer := shachain.NewRevocationProducer(*revRoot)
3✔
136

3✔
137
        var chanType channeldb.ChannelType
3✔
138
        switch backup.Version {
3✔
139
        case chanbackup.DefaultSingleVersion:
×
140
                chanType = channeldb.SingleFunderBit
×
141

142
        case chanbackup.TweaklessCommitVersion:
3✔
143
                chanType = channeldb.SingleFunderTweaklessBit
3✔
144

145
        case chanbackup.AnchorsCommitVersion:
×
146
                chanType = channeldb.AnchorOutputsBit
×
147
                chanType |= channeldb.SingleFunderTweaklessBit
×
148

149
        case chanbackup.AnchorsZeroFeeHtlcTxCommitVersion:
3✔
150
                chanType = channeldb.ZeroHtlcTxFeeBit
3✔
151
                chanType |= channeldb.AnchorOutputsBit
3✔
152
                chanType |= channeldb.SingleFunderTweaklessBit
3✔
153

154
        case chanbackup.ScriptEnforcedLeaseVersion:
3✔
155
                chanType = channeldb.LeaseExpirationBit
3✔
156
                chanType |= channeldb.ZeroHtlcTxFeeBit
3✔
157
                chanType |= channeldb.AnchorOutputsBit
3✔
158
                chanType |= channeldb.SingleFunderTweaklessBit
3✔
159

160
        case chanbackup.SimpleTaprootVersion:
3✔
161
                chanType = channeldb.ZeroHtlcTxFeeBit
3✔
162
                chanType |= channeldb.AnchorOutputsBit
3✔
163
                chanType |= channeldb.SingleFunderTweaklessBit
3✔
164
                chanType |= channeldb.SimpleTaprootFeatureBit
3✔
165

166
        case chanbackup.TapscriptRootVersion:
×
167
                chanType = channeldb.ZeroHtlcTxFeeBit
×
168
                chanType |= channeldb.AnchorOutputsBit
×
169
                chanType |= channeldb.SingleFunderTweaklessBit
×
170
                chanType |= channeldb.SimpleTaprootFeatureBit
×
171
                chanType |= channeldb.TapscriptRootBit
×
172

173
        default:
×
174
                return nil, fmt.Errorf("unknown Single version: %w", err)
×
175
        }
176

177
        ltndLog.Infof("SCB Recovery: created channel shell for ChannelPoint"+
3✔
178
                "(%v), chan_type=%v", backup.FundingOutpoint, chanType)
3✔
179

3✔
180
        chanShell := channeldb.ChannelShell{
3✔
181
                NodeAddrs: backup.Addresses,
3✔
182
                Chan: &channeldb.OpenChannel{
3✔
183
                        ChanType:                chanType,
3✔
184
                        ChainHash:               backup.ChainHash,
3✔
185
                        IsInitiator:             backup.IsInitiator,
3✔
186
                        Capacity:                backup.Capacity,
3✔
187
                        FundingOutpoint:         backup.FundingOutpoint,
3✔
188
                        ShortChannelID:          backup.ShortChannelID,
3✔
189
                        IdentityPub:             backup.RemoteNodePub,
3✔
190
                        IsPending:               false,
3✔
191
                        LocalChanCfg:            backup.LocalChanCfg,
3✔
192
                        RemoteChanCfg:           backup.RemoteChanCfg,
3✔
193
                        RemoteCurrentRevocation: backup.RemoteNodePub,
3✔
194
                        RevocationStore:         shachain.NewRevocationStore(),
3✔
195
                        RevocationProducer:      shaChainProducer,
3✔
196
                        ThawHeight:              backup.LeaseExpiry,
3✔
197
                },
3✔
198
        }
3✔
199

3✔
200
        return &chanShell, nil
3✔
201
}
202

203
// RestoreChansFromSingles attempts to map the set of single channel backups to
204
// channel shells that will be stored persistently. Once these shells have been
205
// stored on disk, we'll be able to connect to the channel peer an execute the
206
// data loss recovery protocol.
207
//
208
// NOTE: Part of the chanbackup.ChannelRestorer interface.
209
func (c *chanDBRestorer) RestoreChansFromSingles(backups ...chanbackup.Single) error {
3✔
210
        channelShells := make([]*channeldb.ChannelShell, 0, len(backups))
3✔
211
        firstChanHeight := uint32(math.MaxUint32)
3✔
212
        for _, backup := range backups {
6✔
213
                chanShell, err := c.openChannelShell(backup)
3✔
214
                if err != nil {
3✔
215
                        return err
×
216
                }
×
217

218
                // Find the block height of the earliest channel in this backup.
219
                chanHeight := chanShell.Chan.ShortChanID().BlockHeight
3✔
220
                if chanHeight != 0 && chanHeight < firstChanHeight {
6✔
221
                        firstChanHeight = chanHeight
3✔
222
                }
3✔
223

224
                channelShells = append(channelShells, chanShell)
3✔
225
        }
226

227
        // In case there were only unconfirmed channels, we will have to scan
228
        // the chain beginning from the launch date of SCBs.
229
        if firstChanHeight == math.MaxUint32 {
3✔
230
                chainHash := channelShells[0].Chan.ChainHash
×
231
                switch {
×
232
                case chainHash.IsEqual(chaincfg.MainNetParams.GenesisHash):
×
233
                        firstChanHeight = mainnetSCBLaunchBlock
×
234

235
                case chainHash.IsEqual(chaincfg.TestNet3Params.GenesisHash):
×
236
                        firstChanHeight = testnetSCBLaunchBlock
×
237

238
                default:
×
239
                        // Worst case: We have no height hint and start at
×
240
                        // block 1. Should only happen for SCBs in regtest
×
241
                        // and simnet.
×
242
                        firstChanHeight = 1
×
243
                }
244
        }
245

246
        // If there were channels in the backup that were not confirmed at the
247
        // time of the backup creation, they won't have a block height in the
248
        // ShortChanID which would lead to an error in the chain watcher.
249
        // We want to at least set the funding broadcast height that the chain
250
        // watcher can use instead. We have two possible fallback values for
251
        // the broadcast height that we are going to try here.
252
        for _, chanShell := range channelShells {
6✔
253
                channel := chanShell.Chan
3✔
254

3✔
255
                switch {
3✔
256
                // Fallback case 1: This is an unconfirmed channel from an old
257
                // backup file where we didn't have any workaround in place and
258
                // the short channel ID is 0:0:0. Best we can do here is set the
259
                // funding broadcast height to a reasonable value that we
260
                // determined earlier.
261
                case channel.ShortChanID().BlockHeight == 0:
×
262
                        channel.SetBroadcastHeight(firstChanHeight)
×
263

264
                // Fallback case 2: It is extremely unlikely at this point that
265
                // a channel we are trying to restore has a coinbase funding TX.
266
                // Therefore we can be quite certain that if the TxIndex is
267
                // zero but the block height wasn't, it was an unconfirmed
268
                // channel where we used the BlockHeight to encode the funding
269
                // TX broadcast height. To not end up with an invalid short
270
                // channel ID that looks valid, we restore the "original"
271
                // unconfirmed one here.
272
                case channel.ShortChannelID.TxIndex == 0:
3✔
273
                        broadcastHeight := channel.ShortChannelID.BlockHeight
3✔
274
                        channel.SetBroadcastHeight(broadcastHeight)
3✔
275
                        channel.ShortChannelID.BlockHeight = 0
3✔
276
                }
277
        }
278

279
        ltndLog.Infof("Inserting %v SCB channel shells into DB",
3✔
280
                len(channelShells))
3✔
281

3✔
282
        // Now that we have all the backups mapped into a series of Singles,
3✔
283
        // we'll insert them all into the database.
3✔
284
        if err := c.db.RestoreChannelShells(channelShells...); err != nil {
6✔
285
                return err
3✔
286
        }
3✔
287

288
        ltndLog.Infof("Informing chain watchers of new restored channels")
3✔
289

3✔
290
        // Create a slice of channel points.
3✔
291
        chanPoints := make([]wire.OutPoint, 0, len(channelShells))
3✔
292

3✔
293
        // Finally, we'll need to inform the chain arbitrator of these new
3✔
294
        // channels so we'll properly watch for their ultimate closure on chain
3✔
295
        // and sweep them via the DLP.
3✔
296
        for _, restoredChannel := range channelShells {
6✔
297
                err := c.chainArb.WatchNewChannel(restoredChannel.Chan)
3✔
298
                if err != nil {
3✔
299
                        return err
×
300
                }
×
301

302
                chanPoints = append(
3✔
303
                        chanPoints, restoredChannel.Chan.FundingOutpoint,
3✔
304
                )
3✔
305
        }
306

307
        // With all the channels restored, we'll now re-send the blockbeat.
308
        c.chainArb.RedispatchBlockbeat(chanPoints)
3✔
309

3✔
310
        return nil
3✔
311
}
312

313
// A compile-time constraint to ensure chanDBRestorer implements
314
// chanbackup.ChannelRestorer.
315
var _ chanbackup.ChannelRestorer = (*chanDBRestorer)(nil)
316

317
// ConnectPeer attempts to connect to the target node at the set of available
318
// addresses. Once this method returns with a non-nil error, the connector
319
// should attempt to persistently connect to the target peer in the background
320
// as a persistent attempt.
321
//
322
// NOTE: Part of the chanbackup.PeerConnector interface.
323
func (s *server) ConnectPeer(nodePub *btcec.PublicKey, addrs []net.Addr) error {
3✔
324
        // Before we connect to the remote peer, we'll remove any connections
3✔
325
        // to ensure the new connection is created after this new link/channel
3✔
326
        // is known.
3✔
327
        if err := s.DisconnectPeer(nodePub); err != nil {
6✔
328
                ltndLog.Infof("Peer(%x) is already connected, proceeding "+
3✔
329
                        "with chan restore", nodePub.SerializeCompressed())
3✔
330
        }
3✔
331

332
        // For each of the known addresses, we'll attempt to launch a
333
        // persistent connection to the (pub, addr) pair. In the event that any
334
        // of them connect, all the other stale requests will be canceled.
335
        for _, addr := range addrs {
6✔
336
                netAddr := &lnwire.NetAddress{
3✔
337
                        IdentityKey: nodePub,
3✔
338
                        Address:     addr,
3✔
339
                }
3✔
340

3✔
341
                ltndLog.Infof("Attempting to connect to %v for SCB restore "+
3✔
342
                        "DLP", netAddr)
3✔
343

3✔
344
                // Attempt to connect to the peer using this full address. If
3✔
345
                // we're unable to connect to them, then we'll try the next
3✔
346
                // address in place of it.
3✔
347
                err := s.ConnectToPeer(netAddr, true, s.cfg.ConnectionTimeout)
3✔
348

3✔
349
                // If we're already connected to this peer, then we don't
3✔
350
                // consider this an error, so we'll exit here.
3✔
351
                if _, ok := err.(*errPeerAlreadyConnected); ok {
3✔
352
                        return nil
×
353

×
354
                } else if err != nil {
3✔
355
                        // Otherwise, something else happened, so we'll try the
×
356
                        // next address.
×
357
                        ltndLog.Errorf("unable to connect to %v to "+
×
358
                                "complete SCB restore: %v", netAddr, err)
×
359
                        continue
×
360
                }
361

362
                // If we connected no problem, then we can exit early as our
363
                // job here is done.
364
                return nil
3✔
365
        }
366

367
        return fmt.Errorf("unable to connect to peer %x for SCB restore",
×
368
                nodePub.SerializeCompressed())
×
369
}
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