rescueclosed: add manual brute force method

pull/17/head
Oliver Gugger 3 years ago
parent e36bf5e463
commit 863a5e7da2
No known key found for this signature in database
GPG Key ID: 8E4256593F177720

@ -2,6 +2,7 @@ package main
import (
"crypto/subtle"
"encoding/hex"
"encoding/json"
"errors"
"fmt"
@ -32,7 +33,9 @@ type cacheEntry struct {
}
type rescueClosedCommand struct {
ChannelDB string
ChannelDB string
Addr string
CommitPoint string
rootKey *rootKey
inputs *inputFlags
@ -51,16 +54,34 @@ output that belongs to our side. This can only be used if we have a channel DB
that contains the latest commit point. Normally you would use SCB to get the
funds from those channels. But this method can help if the other node doesn't
know about the channels any more but we still have the channel.db from the
moment they force-closed.`,
moment they force-closed.
The alternative use case for this command is if you got the commit point by
running the fund-recovery branch of my guggero/lnd fork in combination with the
fakechanbackup command. Then you need to specify the --commit_point and
--force_close_addr flags instead of the --channeldb and --fromsummary flags.`,
Example: `chantools rescueclosed --rootkey xprvxxxxxxxxxx \
--fromsummary results/summary-xxxxxx.json \
--channeldb ~/.lnd/data/graph/mainnet/channel.db`,
--channeldb ~/.lnd/data/graph/mainnet/channel.db
chantools rescueclosed --rootkey xprvxxxxxxxxxx \
--force_close_addr bc1q... \
--commit_point 03xxxx`,
RunE: cc.Execute,
}
cc.cmd.Flags().StringVar(
&cc.ChannelDB, "channeldb", "", "lnd channel.db file to use "+
"for rescuing force-closed channels",
)
cc.cmd.Flags().StringVar(
&cc.Addr, "force_close_addr", "", "the address the channel "+
"was force closed to",
)
cc.cmd.Flags().StringVar(
&cc.CommitPoint, "commit_point", "", "the commit point that "+
"was obtained from the logs after running the "+
"fund-recovery branch of guggero/lnd",
)
cc.rootKey = newRootKey(cc.cmd, "decrypting the backup")
cc.inputs = newInputFlags(cc.cmd)
@ -74,21 +95,49 @@ func (c *rescueClosedCommand) Execute(_ *cobra.Command, _ []string) error {
return fmt.Errorf("error reading root key: %v", err)
}
// Check that we have a channel DB.
if c.ChannelDB == "" {
return fmt.Errorf("rescue DB is required")
}
db, err := lnd.OpenDB(c.ChannelDB, true)
if err != nil {
return fmt.Errorf("error opening rescue DB: %v", err)
}
// What way of recovery has the user chosen? From summary and DB or from
// address and commit point?
switch {
case c.ChannelDB != "":
db, err := lnd.OpenDB(c.ChannelDB, true)
if err != nil {
return fmt.Errorf("error opening rescue DB: %v", err)
}
// Parse channel entries from any of the possible input files.
entries, err := c.inputs.parseInputType()
if err != nil {
return err
// Parse channel entries from any of the possible input files.
entries, err := c.inputs.parseInputType()
if err != nil {
return err
}
return rescueClosedChannels(extendedKey, entries, db)
case c.Addr != "":
// First parse address to get targetPubKeyHash from it later.
targetAddr, err := btcutil.DecodeAddress(c.Addr, chainParams)
if err != nil {
return fmt.Errorf("error parsing addr: %v", err)
}
// Now parse the commit point.
commitPointRaw, err := hex.DecodeString(c.CommitPoint)
if err != nil {
return fmt.Errorf("error decoding commit point: %v",
err)
}
commitPoint, err := btcec.ParsePubKey(
commitPointRaw, btcec.S256(),
)
if err != nil {
return fmt.Errorf("error parsing commit point: %v", err)
}
return rescueClosedChannel(extendedKey, targetAddr, commitPoint)
default:
return fmt.Errorf("you either need to specify --channeldb and " +
"--fromsummary or --force_close_addr and " +
"--commit_point but not a mixture of them")
}
return rescueClosedChannels(extendedKey, entries, db)
}
func rescueClosedChannels(extendedKey *hdkeychain.ExtendedKey,
@ -168,6 +217,47 @@ func rescueClosedChannels(extendedKey *hdkeychain.ExtendedKey,
return ioutil.WriteFile(fileName, summaryBytes, 0644)
}
func rescueClosedChannel(extendedKey *hdkeychain.ExtendedKey,
addr btcutil.Address, commitPoint *btcec.PublicKey) error {
// Make the check on the decoded address according to the active
// network (testnet or mainnet only).
if !addr.IsForNet(chainParams) {
return fmt.Errorf("address: %v is not valid for this network: "+
"%v", addr, chainParams.Name)
}
// Must be a bech32 native SegWit address.
switch addr.(type) {
case *btcutil.AddressWitnessPubKeyHash:
log.Infof("Brute forcing private key for tweaked public key "+
"hash %x\n", addr.ScriptAddress())
default:
return fmt.Errorf("address: must be a bech32 P2WPKH address")
}
err := fillCache(extendedKey)
if err != nil {
return err
}
wif, err := addrInCache(addr.String(), commitPoint)
switch {
case err == nil:
log.Infof("Found private key %s for address %v!", wif, addr)
return nil
case err == errAddrNotFound:
return fmt.Errorf("did not find private key for address %v",
addr)
default:
return err
}
}
func addrInCache(addr string, perCommitPoint *btcec.PublicKey) (string, error) {
targetPubKeyHash, scriptHash, err := lnd.DecodeAddressHash(
addr, chainParams,

Loading…
Cancel
Save