• 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

0.0
/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/lightningnetwork/lnd/chanbackup"
12
        "github.com/lightningnetwork/lnd/channeldb"
13
        "github.com/lightningnetwork/lnd/contractcourt"
14
        "github.com/lightningnetwork/lnd/keychain"
15
        "github.com/lightningnetwork/lnd/lnwire"
16
        "github.com/lightningnetwork/lnd/shachain"
17
)
18

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

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

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

39
        secretKeys keychain.SecretKeyRing
40

41
        chainArb *contractcourt.ChainArbitrator
42
}
43

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

×
UNCOV
51
        var err error
×
UNCOV
52

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

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

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

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

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

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

UNCOV
130
                ch := chainhash.Hash(ecdh)
×
UNCOV
131
                revRoot = &ch
×
132
        }
133

UNCOV
134
        shaChainProducer := shachain.NewRevocationProducer(*revRoot)
×
UNCOV
135

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

UNCOV
141
        case chanbackup.TweaklessCommitVersion:
×
UNCOV
142
                chanType = channeldb.SingleFunderTweaklessBit
×
143

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

UNCOV
148
        case chanbackup.AnchorsZeroFeeHtlcTxCommitVersion:
×
UNCOV
149
                chanType = channeldb.ZeroHtlcTxFeeBit
×
UNCOV
150
                chanType |= channeldb.AnchorOutputsBit
×
UNCOV
151
                chanType |= channeldb.SingleFunderTweaklessBit
×
152

UNCOV
153
        case chanbackup.ScriptEnforcedLeaseVersion:
×
UNCOV
154
                chanType = channeldb.LeaseExpirationBit
×
UNCOV
155
                chanType |= channeldb.ZeroHtlcTxFeeBit
×
UNCOV
156
                chanType |= channeldb.AnchorOutputsBit
×
UNCOV
157
                chanType |= channeldb.SingleFunderTweaklessBit
×
158

UNCOV
159
        case chanbackup.SimpleTaprootVersion:
×
UNCOV
160
                chanType = channeldb.ZeroHtlcTxFeeBit
×
UNCOV
161
                chanType |= channeldb.AnchorOutputsBit
×
UNCOV
162
                chanType |= channeldb.SingleFunderTweaklessBit
×
UNCOV
163
                chanType |= channeldb.SimpleTaprootFeatureBit
×
164

165
        default:
×
166
                return nil, fmt.Errorf("unknown Single version: %w", err)
×
167
        }
168

UNCOV
169
        ltndLog.Infof("SCB Recovery: created channel shell for ChannelPoint"+
×
UNCOV
170
                "(%v), chan_type=%v", backup.FundingOutpoint, chanType)
×
UNCOV
171

×
UNCOV
172
        chanShell := channeldb.ChannelShell{
×
UNCOV
173
                NodeAddrs: backup.Addresses,
×
UNCOV
174
                Chan: &channeldb.OpenChannel{
×
UNCOV
175
                        ChanType:                chanType,
×
UNCOV
176
                        ChainHash:               backup.ChainHash,
×
UNCOV
177
                        IsInitiator:             backup.IsInitiator,
×
UNCOV
178
                        Capacity:                backup.Capacity,
×
UNCOV
179
                        FundingOutpoint:         backup.FundingOutpoint,
×
UNCOV
180
                        ShortChannelID:          backup.ShortChannelID,
×
UNCOV
181
                        IdentityPub:             backup.RemoteNodePub,
×
UNCOV
182
                        IsPending:               false,
×
UNCOV
183
                        LocalChanCfg:            backup.LocalChanCfg,
×
UNCOV
184
                        RemoteChanCfg:           backup.RemoteChanCfg,
×
UNCOV
185
                        RemoteCurrentRevocation: backup.RemoteNodePub,
×
UNCOV
186
                        RevocationStore:         shachain.NewRevocationStore(),
×
UNCOV
187
                        RevocationProducer:      shaChainProducer,
×
UNCOV
188
                        ThawHeight:              backup.LeaseExpiry,
×
UNCOV
189
                },
×
UNCOV
190
        }
×
UNCOV
191

×
UNCOV
192
        return &chanShell, nil
×
193
}
194

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

210
                // Find the block height of the earliest channel in this backup.
UNCOV
211
                chanHeight := chanShell.Chan.ShortChanID().BlockHeight
×
UNCOV
212
                if chanHeight != 0 && chanHeight < firstChanHeight {
×
UNCOV
213
                        firstChanHeight = chanHeight
×
UNCOV
214
                }
×
215

UNCOV
216
                channelShells = append(channelShells, chanShell)
×
217
        }
218

219
        // In case there were only unconfirmed channels, we will have to scan
220
        // the chain beginning from the launch date of SCBs.
UNCOV
221
        if firstChanHeight == math.MaxUint32 {
×
222
                chainHash := channelShells[0].Chan.ChainHash
×
223
                switch {
×
224
                case chainHash.IsEqual(chaincfg.MainNetParams.GenesisHash):
×
225
                        firstChanHeight = mainnetSCBLaunchBlock
×
226

227
                case chainHash.IsEqual(chaincfg.TestNet3Params.GenesisHash):
×
228
                        firstChanHeight = testnetSCBLaunchBlock
×
229

230
                default:
×
231
                        // Worst case: We have no height hint and start at
×
232
                        // block 1. Should only happen for SCBs in regtest
×
233
                        // and simnet.
×
234
                        firstChanHeight = 1
×
235
                }
236
        }
237

238
        // If there were channels in the backup that were not confirmed at the
239
        // time of the backup creation, they won't have a block height in the
240
        // ShortChanID which would lead to an error in the chain watcher.
241
        // We want to at least set the funding broadcast height that the chain
242
        // watcher can use instead. We have two possible fallback values for
243
        // the broadcast height that we are going to try here.
UNCOV
244
        for _, chanShell := range channelShells {
×
UNCOV
245
                channel := chanShell.Chan
×
UNCOV
246

×
UNCOV
247
                switch {
×
248
                // Fallback case 1: This is an unconfirmed channel from an old
249
                // backup file where we didn't have any workaround in place and
250
                // the short channel ID is 0:0:0. Best we can do here is set the
251
                // funding broadcast height to a reasonable value that we
252
                // determined earlier.
253
                case channel.ShortChanID().BlockHeight == 0:
×
254
                        channel.SetBroadcastHeight(firstChanHeight)
×
255

256
                // Fallback case 2: It is extremely unlikely at this point that
257
                // a channel we are trying to restore has a coinbase funding TX.
258
                // Therefore we can be quite certain that if the TxIndex is
259
                // zero but the block height wasn't, it was an unconfirmed
260
                // channel where we used the BlockHeight to encode the funding
261
                // TX broadcast height. To not end up with an invalid short
262
                // channel ID that looks valid, we restore the "original"
263
                // unconfirmed one here.
UNCOV
264
                case channel.ShortChannelID.TxIndex == 0:
×
UNCOV
265
                        broadcastHeight := channel.ShortChannelID.BlockHeight
×
UNCOV
266
                        channel.SetBroadcastHeight(broadcastHeight)
×
UNCOV
267
                        channel.ShortChannelID.BlockHeight = 0
×
268
                }
269
        }
270

UNCOV
271
        ltndLog.Infof("Inserting %v SCB channel shells into DB",
×
UNCOV
272
                len(channelShells))
×
UNCOV
273

×
UNCOV
274
        // Now that we have all the backups mapped into a series of Singles,
×
UNCOV
275
        // we'll insert them all into the database.
×
UNCOV
276
        if err := c.db.RestoreChannelShells(channelShells...); err != nil {
×
UNCOV
277
                return err
×
UNCOV
278
        }
×
279

UNCOV
280
        ltndLog.Infof("Informing chain watchers of new restored channels")
×
UNCOV
281

×
UNCOV
282
        // Finally, we'll need to inform the chain arbitrator of these new
×
UNCOV
283
        // channels so we'll properly watch for their ultimate closure on chain
×
UNCOV
284
        // and sweep them via the DLP.
×
UNCOV
285
        for _, restoredChannel := range channelShells {
×
UNCOV
286
                err := c.chainArb.WatchNewChannel(restoredChannel.Chan)
×
UNCOV
287
                if err != nil {
×
288
                        return err
×
289
                }
×
290
        }
291

UNCOV
292
        return nil
×
293
}
294

295
// A compile-time constraint to ensure chanDBRestorer implements
296
// chanbackup.ChannelRestorer.
297
var _ chanbackup.ChannelRestorer = (*chanDBRestorer)(nil)
298

299
// ConnectPeer attempts to connect to the target node at the set of available
300
// addresses. Once this method returns with a non-nil error, the connector
301
// should attempt to persistently connect to the target peer in the background
302
// as a persistent attempt.
303
//
304
// NOTE: Part of the chanbackup.PeerConnector interface.
UNCOV
305
func (s *server) ConnectPeer(nodePub *btcec.PublicKey, addrs []net.Addr) error {
×
UNCOV
306
        // Before we connect to the remote peer, we'll remove any connections
×
UNCOV
307
        // to ensure the new connection is created after this new link/channel
×
UNCOV
308
        // is known.
×
UNCOV
309
        if err := s.DisconnectPeer(nodePub); err != nil {
×
UNCOV
310
                ltndLog.Infof("Peer(%v) is already connected, proceeding "+
×
UNCOV
311
                        "with chan restore", nodePub.SerializeCompressed())
×
UNCOV
312
        }
×
313

314
        // For each of the known addresses, we'll attempt to launch a
315
        // persistent connection to the (pub, addr) pair. In the event that any
316
        // of them connect, all the other stale requests will be canceled.
UNCOV
317
        for _, addr := range addrs {
×
UNCOV
318
                netAddr := &lnwire.NetAddress{
×
UNCOV
319
                        IdentityKey: nodePub,
×
UNCOV
320
                        Address:     addr,
×
UNCOV
321
                }
×
UNCOV
322

×
UNCOV
323
                ltndLog.Infof("Attempting to connect to %v for SCB restore "+
×
UNCOV
324
                        "DLP", netAddr)
×
UNCOV
325

×
UNCOV
326
                // Attempt to connect to the peer using this full address. If
×
UNCOV
327
                // we're unable to connect to them, then we'll try the next
×
UNCOV
328
                // address in place of it.
×
UNCOV
329
                err := s.ConnectToPeer(netAddr, true, s.cfg.ConnectionTimeout)
×
UNCOV
330

×
UNCOV
331
                // If we're already connected to this peer, then we don't
×
UNCOV
332
                // consider this an error, so we'll exit here.
×
UNCOV
333
                if _, ok := err.(*errPeerAlreadyConnected); ok {
×
334
                        return nil
×
335

×
UNCOV
336
                } else if err != nil {
×
337
                        // Otherwise, something else happened, so we'll try the
×
338
                        // next address.
×
339
                        ltndLog.Errorf("unable to connect to %v to "+
×
340
                                "complete SCB restore: %v", netAddr, err)
×
341
                        continue
×
342
                }
343

344
                // If we connected no problem, then we can exit early as our
345
                // job here is done.
UNCOV
346
                return nil
×
347
        }
348

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