@@ -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,66 @@ 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 ) (err error ) {
269
+ keyRing := & lnd.HDKeyRing {
270
+ ExtendedKey : extendedKey ,
271
+ ChainParams : chainParams ,
272
+ }
273
+ var backups []chanbackup.Single
274
+ if singleBackup != "" || singleFile != "" {
275
+ if singleBackup != "" && singleFile != "" {
276
+ return fmt .Errorf ("must not pass --single_backup and --single_file together" )
277
+ }
278
+ var singleBackupBytes []byte
279
+ if singleBackup != "" {
280
+ singleBackupBytes , err = hex .DecodeString (singleBackup )
281
+ } else if singleFile != "" {
282
+ singleBackupBytes , err = os .ReadFile (singleFile )
283
+ }
284
+ if err != nil {
285
+ return fmt .Errorf ("failed to get single backup: %w" , err )
286
+ }
287
+ var s chanbackup.Single
288
+ if err := s .UnpackFromReader (bytes .NewReader (singleBackupBytes ), keyRing ); err != nil {
289
+ return fmt .Errorf ("failed to unpack single backup: %w" , err )
290
+ }
291
+ backups = append (backups , s )
292
+ }
293
+ if multiBackup != "" || multiFile != "" {
294
+ if len (backups ) != 0 {
295
+ return fmt .Errorf ("must not pass single and multi backups together" )
296
+ }
297
+ if multiBackup != "" && multiFile != "" {
298
+ return fmt .Errorf ("must not pass --multi_backup and --multi_file together" )
299
+ }
300
+ var multiBackupBytes []byte
301
+ if multiBackup != "" {
302
+ multiBackupBytes , err = hex .DecodeString (multiBackup )
303
+ } else if multiFile != "" {
304
+ multiBackupBytes , err = os .ReadFile (multiFile )
305
+ }
306
+ if err != nil {
307
+ return fmt .Errorf ("failed to get multi backup: %w" , err )
308
+ }
309
+ var m chanbackup.Multi
310
+ if err := m .UnpackFromReader (bytes .NewReader (multiBackupBytes ), keyRing ); err != nil {
311
+ return fmt .Errorf ("failed to unpack multi backup: %w" , err )
312
+ }
313
+ backups = append (backups , m .StaticBackups ... )
314
+ }
315
+
316
+ fmt .Println ()
317
+ fmt .Println ("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@" )
318
+ fmt .Println (forceCloseWarning )
319
+ fmt .Println ("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@" )
320
+ fmt .Println ()
321
+
322
+ for _ , s := range backups {
323
+ fmt .Println (s .FundingOutpoint )
324
+ fmt .Println (hex .EncodeToString (s .CloseTx ))
325
+ fmt .Println ()
326
+ }
327
+
328
+ return nil
329
+ }
0 commit comments