@@ -7,13 +7,15 @@ import (
7
7
"fmt"
8
8
"io"
9
9
"io/ioutil"
10
+ "os"
10
11
"time"
11
12
12
13
"github.com/btcsuite/btcd/btcutil/hdkeychain"
13
14
"github.com/btcsuite/btcd/txscript"
14
15
"github.com/lightninglabs/chantools/btc"
15
16
"github.com/lightninglabs/chantools/dataformat"
16
17
"github.com/lightninglabs/chantools/lnd"
18
+ "github.com/lightningnetwork/lnd/chanbackup"
17
19
"github.com/lightningnetwork/lnd/channeldb"
18
20
"github.com/lightningnetwork/lnd/input"
19
21
"github.com/spf13/cobra"
@@ -24,18 +26,18 @@ type forceCloseCommand struct {
24
26
ChannelDB string
25
27
Publish bool
26
28
29
+ // channel.backup.
30
+ SingleBackup string
31
+ SingleFile string
32
+ MultiBackup string
33
+ MultiFile string
34
+
27
35
rootKey * rootKey
28
36
inputs * inputFlags
29
37
cmd * cobra.Command
30
38
}
31
39
32
- func newForceCloseCommand () * cobra.Command {
33
- cc := & forceCloseCommand {}
34
- cc .cmd = & cobra.Command {
35
- Use : "forceclose" ,
36
- Short : "Force-close the last state that is in the channel.db " +
37
- "provided" ,
38
- Long : `If you are certain that a node is offline for good (AFTER
40
+ const forceCloseWarning = `If you are certain that a node is offline for good (AFTER
39
41
you've tried SCB!) and a channel is still open, you can use this method to
40
42
force-close your latest state that you have in your channel.db.
41
43
@@ -46,7 +48,15 @@ the remote node *could* punish you by taking the whole channel amount *if* they
46
48
come online before you can sweep the funds from the time locked (144 - 2000
47
49
blocks) transaction *or* they have a watch tower looking out for them.
48
50
49
- **This should absolutely be the last resort and you have been warned!**` ,
51
+ **This should absolutely be the last resort and you have been warned!**`
52
+
53
+ func newForceCloseCommand () * cobra.Command {
54
+ cc := & forceCloseCommand {}
55
+ cc .cmd = & cobra.Command {
56
+ Use : "forceclose" ,
57
+ Short : "Force-close the last state that is in the channel.db " +
58
+ "provided" ,
59
+ Long : forceCloseWarning ,
50
60
Example : `chantools forceclose \
51
61
--fromsummary results/summary-xxxx-yyyy.json
52
62
--channeldb ~/.lnd/data/graph/mainnet/channel.db \
@@ -61,6 +71,24 @@ blocks) transaction *or* they have a watch tower looking out for them.
61
71
& cc .ChannelDB , "channeldb" , "" , "lnd channel.db file to use " +
62
72
"for force-closing channels" ,
63
73
)
74
+
75
+ cc .cmd .Flags ().StringVar (
76
+ & cc .SingleBackup , "single_backup" , "" , "a hex encoded single channel " +
77
+ "backup obtained from exportchanbackup for force-closing channels" ,
78
+ )
79
+ cc .cmd .Flags ().StringVar (
80
+ & cc .MultiBackup , "multi_backup" , "" , "a hex encoded multi-channel " +
81
+ "backup obtained from exportchanbackup for force-closing channels" ,
82
+ )
83
+ cc .cmd .Flags ().StringVar (
84
+ & cc .SingleFile , "single_file" , "" , "the path to a single-channel " +
85
+ "backup file" ,
86
+ )
87
+ cc .cmd .Flags ().StringVar (
88
+ & cc .MultiFile , "multi_file" , "" , "the path to a single-channel " +
89
+ "backup file (channel.backup)" ,
90
+ )
91
+
64
92
cc .cmd .Flags ().BoolVar (
65
93
& cc .Publish , "publish" , false , "publish force-closing TX to " +
66
94
"the chain API instead of just printing the TX" ,
@@ -78,6 +106,10 @@ func (c *forceCloseCommand) Execute(_ *cobra.Command, _ []string) error {
78
106
return fmt .Errorf ("error reading root key: %w" , err )
79
107
}
80
108
109
+ if c .SingleBackup != "" || c .MultiBackup != "" || c .SingleFile != "" || c .MultiFile != "" {
110
+ return useChanBackup (extendedKey , c .SingleBackup , c .MultiBackup , c .SingleFile , c .MultiFile )
111
+ }
112
+
81
113
// Check that we have a channel DB.
82
114
if c .ChannelDB == "" {
83
115
return fmt .Errorf ("rescue DB is required" )
@@ -232,3 +264,67 @@ func forceCloseChannels(apiURL string, extendedKey *hdkeychain.ExtendedKey,
232
264
log .Infof ("Writing result to %s" , fileName )
233
265
return ioutil .WriteFile (fileName , summaryBytes , 0644 )
234
266
}
267
+
268
+ func useChanBackup (extendedKey * hdkeychain.ExtendedKey , singleBackup , multiBackup , singleFile , multiFile string ) error {
269
+ keyRing := & lnd.HDKeyRing {
270
+ ExtendedKey : extendedKey ,
271
+ ChainParams : chainParams ,
272
+ }
273
+ var backups []chanbackup.Single
274
+ var err error
275
+ if singleBackup != "" || singleFile != "" {
276
+ if singleBackup != "" && singleFile != "" {
277
+ return fmt .Errorf ("must not pass --single_backup and --single_file together" )
278
+ }
279
+ var singleBackupBytes []byte
280
+ if singleBackup != "" {
281
+ singleBackupBytes , err = hex .DecodeString (singleBackup )
282
+ } else if singleFile != "" {
283
+ singleBackupBytes , err = os .ReadFile (singleFile )
284
+ }
285
+ if err != nil {
286
+ return fmt .Errorf ("failed to get single backup: %w" , err )
287
+ }
288
+ var s chanbackup.Single
289
+ if err := s .UnpackFromReader (bytes .NewReader (singleBackupBytes ), keyRing ); err != nil {
290
+ return fmt .Errorf ("failed to unpack single backup: %w" , err )
291
+ }
292
+ backups = append (backups , s )
293
+ }
294
+ if multiBackup != "" || multiFile != "" {
295
+ if len (backups ) != 0 {
296
+ return fmt .Errorf ("must not pass single and multi backups together" )
297
+ }
298
+ if multiBackup != "" && multiFile != "" {
299
+ return fmt .Errorf ("must not pass --multi_backup and --multi_file together" )
300
+ }
301
+ var multiBackupBytes []byte
302
+ if multiBackup != "" {
303
+ multiBackupBytes , err = hex .DecodeString (multiBackup )
304
+ } else if multiFile != "" {
305
+ multiBackupBytes , err = os .ReadFile (multiFile )
306
+ }
307
+ if err != nil {
308
+ return fmt .Errorf ("failed to get multi backup: %w" , err )
309
+ }
310
+ var m chanbackup.Multi
311
+ if err := m .UnpackFromReader (bytes .NewReader (multiBackupBytes ), keyRing ); err != nil {
312
+ return fmt .Errorf ("failed to unpack multi backup: %w" , err )
313
+ }
314
+ backups = append (backups , m .StaticBackups ... )
315
+ }
316
+
317
+ fmt .Println ()
318
+ fmt .Println ("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@" )
319
+ fmt .Println (forceCloseWarning )
320
+ fmt .Println ("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@" )
321
+ fmt .Println ()
322
+
323
+ for _ , s := range backups {
324
+ fmt .Println (s .FundingOutpoint )
325
+ fmt .Println (hex .EncodeToString (s .CloseTx ))
326
+ fmt .Println ()
327
+ }
328
+
329
+ return nil
330
+ }
0 commit comments