Skip to content

Commit 31b20eb

Browse files
committed
forceclose: extract close tx from backups
1 parent 9943344 commit 31b20eb

File tree

1 file changed

+104
-8
lines changed

1 file changed

+104
-8
lines changed

cmd/chantools/forceclose.go

Lines changed: 104 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,15 @@ import (
77
"fmt"
88
"io"
99
"io/ioutil"
10+
"os"
1011
"time"
1112

1213
"github.com/btcsuite/btcd/btcutil/hdkeychain"
1314
"github.com/btcsuite/btcd/txscript"
1415
"github.com/lightninglabs/chantools/btc"
1516
"github.com/lightninglabs/chantools/dataformat"
1617
"github.com/lightninglabs/chantools/lnd"
18+
"github.com/lightningnetwork/lnd/chanbackup"
1719
"github.com/lightningnetwork/lnd/channeldb"
1820
"github.com/lightningnetwork/lnd/input"
1921
"github.com/spf13/cobra"
@@ -24,18 +26,18 @@ type forceCloseCommand struct {
2426
ChannelDB string
2527
Publish bool
2628

29+
// channel.backup.
30+
SingleBackup string
31+
SingleFile string
32+
MultiBackup string
33+
MultiFile string
34+
2735
rootKey *rootKey
2836
inputs *inputFlags
2937
cmd *cobra.Command
3038
}
3139

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
3941
you've tried SCB!) and a channel is still open, you can use this method to
4042
force-close your latest state that you have in your channel.db.
4143
@@ -46,7 +48,15 @@ the remote node *could* punish you by taking the whole channel amount *if* they
4648
come online before you can sweep the funds from the time locked (144 - 2000
4749
blocks) transaction *or* they have a watch tower looking out for them.
4850
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,
5060
Example: `chantools forceclose \
5161
--fromsummary results/summary-xxxx-yyyy.json
5262
--channeldb ~/.lnd/data/graph/mainnet/channel.db \
@@ -61,6 +71,24 @@ blocks) transaction *or* they have a watch tower looking out for them.
6171
&cc.ChannelDB, "channeldb", "", "lnd channel.db file to use "+
6272
"for force-closing channels",
6373
)
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+
6492
cc.cmd.Flags().BoolVar(
6593
&cc.Publish, "publish", false, "publish force-closing TX to "+
6694
"the chain API instead of just printing the TX",
@@ -78,6 +106,10 @@ func (c *forceCloseCommand) Execute(_ *cobra.Command, _ []string) error {
78106
return fmt.Errorf("error reading root key: %w", err)
79107
}
80108

109+
if c.SingleBackup != "" || c.MultiBackup != "" || c.SingleFile != "" || c.MultiFile != "" {
110+
return useChanBackup(extendedKey, c.SingleBackup, c.MultiBackup, c.SingleFile, c.MultiFile)
111+
}
112+
81113
// Check that we have a channel DB.
82114
if c.ChannelDB == "" {
83115
return fmt.Errorf("rescue DB is required")
@@ -232,3 +264,67 @@ func forceCloseChannels(apiURL string, extendedKey *hdkeychain.ExtendedKey,
232264
log.Infof("Writing result to %s", fileName)
233265
return ioutil.WriteFile(fileName, summaryBytes, 0644)
234266
}
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

Comments
 (0)