Add filterbackup command

pull/3/head
Oliver Gugger 4 years ago
parent 98075a7965
commit 22420b9aca
No known key found for this signature in database
GPG Key ID: 8E4256593F177720

@ -7,6 +7,7 @@
* [Commands](#commands) * [Commands](#commands)
+ [dumpbackup](#dumpbackup) + [dumpbackup](#dumpbackup)
+ [dumpchannels](#dumpchannels) + [dumpchannels](#dumpchannels)
+ [filterbackup](#filterbackup)
+ [forceclose](#forceclose) + [forceclose](#forceclose)
+ [rescueclosed](#rescueclosed) + [rescueclosed](#rescueclosed)
+ [showrootkey](#showrootkey) + [showrootkey](#showrootkey)
@ -54,6 +55,7 @@ Available commands:
derivekey Derive a key with a specific derivation path from the BIP32 HD root key. derivekey Derive a key with a specific derivation path from the BIP32 HD root key.
dumpbackup Dump the content of a channel.backup file. dumpbackup Dump the content of a channel.backup file.
dumpchannels Dump all channel information from lnd's channel database. dumpchannels Dump all channel information from lnd's channel database.
filterbackup Filter an lnd channel.backup file and remove certain channels.
forceclose Force-close the last state that is in the channel.db provided. forceclose Force-close the last state that is in the channel.db provided.
rescueclosed Try finding the private keys for funds that are in outputs of remotely force-closed channels. rescueclosed Try finding the private keys for funds that are in outputs of remotely force-closed channels.
showrootkey Extract and show the BIP32 HD root key from the 24 word lnd aezeed. showrootkey Extract and show the BIP32 HD root key from the 24 word lnd aezeed.
@ -126,6 +128,29 @@ Example command:
chantools dumpchannels --channeldb ~/.lnd/data/graph/mainnet/channel.db chantools dumpchannels --channeldb ~/.lnd/data/graph/mainnet/channel.db
``` ```
### filterbackup
```text
Usage:
chantools [OPTIONS] filterbackup [filterbackup-OPTIONS]
[filterbackup command options]
--rootkey= BIP32 HD root key of the wallet that was used to create the backup. Leave empty to prompt for lnd 24 word aezeed.
--multi_file= The lnd channel.backup file to filter.
--discard= A comma separated list of channel funding outpoints (format <fundingTXID>:<index>) to remove from the backup file.
```
Filter an lnd `channel.backup` file by removing certain channels (identified by
their funding transaction outpoints).
Example command:
```bash
chantools filterbackup --rootkey xprvxxxxxxxxxx \
--multi_file ~/.lnd/data/chain/bitcoin/mainnet/channel.backup \
--discard 2abcdef2b2bffaaa...db0abadd:1,4abcdef2b2bffaaa...db8abadd:0
```
### forceclose ### forceclose
```text ```text

@ -8,6 +8,7 @@ import (
"github.com/guggero/chantools/btc" "github.com/guggero/chantools/btc"
"github.com/guggero/chantools/dump" "github.com/guggero/chantools/dump"
"github.com/lightningnetwork/lnd/chanbackup" "github.com/lightningnetwork/lnd/chanbackup"
"github.com/lightningnetwork/lnd/keychain"
) )
type dumpBackupCommand struct { type dumpBackupCommand struct {
@ -40,17 +41,20 @@ func (c *dumpBackupCommand) Execute(_ []string) error {
return fmt.Errorf("backup file is required") return fmt.Errorf("backup file is required")
} }
multiFile := chanbackup.NewMultiFile(c.MultiFile) multiFile := chanbackup.NewMultiFile(c.MultiFile)
multi, err := multiFile.ExtractMulti(&btc.ChannelBackupEncryptionRing{ keyRing := &btc.ChannelBackupEncryptionRing{
ExtendedKey: extendedKey, ExtendedKey: extendedKey,
ChainParams: chainParams, ChainParams: chainParams,
})
if err != nil {
return fmt.Errorf("could not extract multi file: %v", err)
} }
return dumpChannelBackup(multi) return dumpChannelBackup(multiFile, keyRing)
} }
func dumpChannelBackup(multi *chanbackup.Multi) error { func dumpChannelBackup(multiFile *chanbackup.MultiFile,
ring keychain.KeyRing) error {
multi, err := multiFile.ExtractMulti(ring)
if err != nil {
return fmt.Errorf("could not extract multi file: %v", err)
}
dumpSingles := make([]dump.BackupSingle, len(multi.StaticBackups)) dumpSingles := make([]dump.BackupSingle, len(multi.StaticBackups))
for idx, single := range multi.StaticBackups { for idx, single := range multi.StaticBackups {
dumpSingles[idx] = dump.BackupSingle{ dumpSingles[idx] = dump.BackupSingle{

@ -0,0 +1,92 @@
package main
import (
"fmt"
"os"
"strings"
"time"
"github.com/btcsuite/btcutil/hdkeychain"
"github.com/guggero/chantools/btc"
"github.com/lightningnetwork/lnd/chanbackup"
"github.com/lightningnetwork/lnd/keychain"
)
type filterBackupCommand struct {
RootKey string `long:"rootkey" description:"BIP32 HD root key of the wallet that was used to create the backup. Leave empty to prompt for lnd 24 word aezeed."`
MultiFile string `long:"multi_file" description:"The lnd channel.backup file to filter."`
Discard string `long:"discard" description:"A comma separated list of channel funding outpoints (format <fundingTXID>:<index>) to remove from the backup file."`
}
func (c *filterBackupCommand) Execute(_ []string) error {
setupChainParams(cfg)
var (
extendedKey *hdkeychain.ExtendedKey
err error
)
// Check that root key is valid or fall back to console input.
switch {
case c.RootKey != "":
extendedKey, err = hdkeychain.NewKeyFromString(c.RootKey)
default:
extendedKey, err = rootKeyFromConsole()
}
if err != nil {
return fmt.Errorf("error reading root key: %v", err)
}
// Parse discard filter.
discard := strings.Split(c.Discard, ",")
// Check that we have a backup file.
if c.MultiFile == "" {
return fmt.Errorf("backup file is required")
}
multiFile := chanbackup.NewMultiFile(c.MultiFile)
keyRing := &btc.ChannelBackupEncryptionRing{
ExtendedKey: extendedKey,
ChainParams: chainParams,
}
return filterChannelBackup(multiFile, keyRing, discard)
}
func filterChannelBackup(multiFile *chanbackup.MultiFile, ring keychain.KeyRing,
discard []string) error {
multi, err := multiFile.ExtractMulti(ring)
if err != nil {
return fmt.Errorf("could not extract multi file: %v", err)
}
keep := make([]chanbackup.Single, 0, len(multi.StaticBackups))
for _, single := range multi.StaticBackups {
found := false
for _, discardChanPoint := range discard {
if single.FundingOutpoint.String() == discardChanPoint {
found = true
}
}
if found {
continue
}
keep = append(keep, single)
}
multi.StaticBackups = keep
fileName := fmt.Sprintf("results/backup-filtered-%s.backup",
time.Now().Format("2006-01-02-15-04-05"))
log.Infof("Writing result to %s", fileName)
f, err := os.OpenFile(fileName, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644)
if err != nil {
return err
}
err = multi.PackToWriter(f, ring)
_ = f.Close()
if err != nil {
return err
}
return nil
}

@ -93,6 +93,10 @@ func runCommandParser() error {
"derivekey", "Derive a key with a specific derivation path "+ "derivekey", "Derive a key with a specific derivation path "+
"from the BIP32 HD root key.", "", &deriveKeyCommand{}, "from the BIP32 HD root key.", "", &deriveKeyCommand{},
) )
_, _ = parser.AddCommand(
"filterbackup", "Filter an lnd channel.backup file and " +
"remove certain channels.", "", &filterBackupCommand{},
)
_, err := parser.Parse() _, err := parser.Parse()
return err return err

@ -61,8 +61,8 @@ func (c *ListChannelsChannel) AsSummaryEntry() *SummaryEntry {
return &SummaryEntry{ return &SummaryEntry{
RemotePubkey: c.RemotePubkey, RemotePubkey: c.RemotePubkey,
ChannelPoint: c.ChannelPoint, ChannelPoint: c.ChannelPoint,
FundingTXID: fundingTXID(c.ChannelPoint), FundingTXID: FundingTXID(c.ChannelPoint),
FundingTXIndex: fundingTXIndex(c.ChannelPoint), FundingTXIndex: FundingTXIndex(c.ChannelPoint),
Capacity: uint64(c.Capacity), Capacity: uint64(c.Capacity),
Initiator: c.Initiator, Initiator: c.Initiator,
LocalBalance: uint64(c.LocalBalance), LocalBalance: uint64(c.LocalBalance),
@ -115,8 +115,8 @@ func (c *PendingChannelsChannel) AsSummaryEntry() *SummaryEntry {
return &SummaryEntry{ return &SummaryEntry{
RemotePubkey: c.Channel.RemotePubkey, RemotePubkey: c.Channel.RemotePubkey,
ChannelPoint: c.Channel.ChannelPoint, ChannelPoint: c.Channel.ChannelPoint,
FundingTXID: fundingTXID(c.Channel.ChannelPoint), FundingTXID: FundingTXID(c.Channel.ChannelPoint),
FundingTXIndex: fundingTXIndex(c.Channel.ChannelPoint), FundingTXIndex: FundingTXIndex(c.Channel.ChannelPoint),
Capacity: uint64(c.Channel.Capacity), Capacity: uint64(c.Channel.Capacity),
Initiator: false, Initiator: false,
LocalBalance: uint64(c.Channel.LocalBalance), LocalBalance: uint64(c.Channel.LocalBalance),
@ -159,7 +159,7 @@ func (f *SummaryEntryFile) AsSummaryEntries() ([]*SummaryEntry, error) {
return f.Channels, nil return f.Channels, nil
} }
func fundingTXID(chanPoint string) string { func FundingTXID(chanPoint string) string {
parts := strings.Split(chanPoint, ":") parts := strings.Split(chanPoint, ":")
if len(parts) != 2 { if len(parts) != 2 {
panic(fmt.Errorf("channel point not in format <txid>:<idx>: %s", panic(fmt.Errorf("channel point not in format <txid>:<idx>: %s",
@ -168,7 +168,7 @@ func fundingTXID(chanPoint string) string {
return parts[0] return parts[0]
} }
func fundingTXIndex(chanPoint string) uint32 { func FundingTXIndex(chanPoint string) uint32 {
parts := strings.Split(chanPoint, ":") parts := strings.Split(chanPoint, ":")
if len(parts) != 2 { if len(parts) != 2 {
panic(fmt.Errorf("channel point %s not in format <txid>:<idx>", panic(fmt.Errorf("channel point %s not in format <txid>:<idx>",

Loading…
Cancel
Save