Skip to content

Commit 4530889

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

File tree

1 file changed

+103
-8
lines changed

1 file changed

+103
-8
lines changed

cmd/chantools/forceclose.go

Lines changed: 103 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,66 @@ 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) (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

Comments
 (0)