rescueclosed: allow reading commit_points from log file

pull/20/head
Oliver Gugger 3 years ago
parent 53f886cf43
commit e8e7c641dc
No known key found for this signature in database
GPG Key ID: 8E4256593F177720

@ -7,6 +7,7 @@ import (
"errors"
"fmt"
"io/ioutil"
"regexp"
"time"
"github.com/btcsuite/btcd/btcec"
@ -25,6 +26,8 @@ var (
cache []*cacheEntry
errAddrNotFound = errors.New("addr not found")
patternCommitPoint = regexp.MustCompile(`commit_point=([0-9a-f]{66})`)
)
type cacheEntry struct {
@ -36,6 +39,7 @@ type rescueClosedCommand struct {
ChannelDB string
Addr string
CommitPoint string
LndLog string
rootKey *rootKey
inputs *inputFlags
@ -59,14 +63,24 @@ 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.`,
--force_close_addr flags instead of the --channeldb and --fromsummary flags.
If you need to rescue a whole bunch of channels all at once, you can also
specify the --fromsummary and --lnd_log flags to automatically look for force
close addresses in the summary and the corresponding commit points in the
lnd log file. This only works if lnd is running the fund-recovery branch of my
guggero/lnd fork.`,
Example: `chantools rescueclosed --rootkey xprvxxxxxxxxxx \
--fromsummary results/summary-xxxxxx.json \
--channeldb ~/.lnd/data/graph/mainnet/channel.db
chantools rescueclosed --rootkey xprvxxxxxxxxxx \
--force_close_addr bc1q... \
--commit_point 03xxxx`,
--commit_point 03xxxx
chantools rescueclosed --rootkey xprvxxxxxxxxxx \
--fromsummary results/summary-xxxxxx.json \
--lnd_log ~/.lnd/logs/bitcoin/mainnet/lnd.log`,
RunE: cc.Execute,
}
cc.cmd.Flags().StringVar(
@ -82,7 +96,10 @@ chantools rescueclosed --rootkey xprvxxxxxxxxxx \
"was obtained from the logs after running the "+
"fund-recovery branch of guggero/lnd",
)
cc.cmd.Flags().StringVar(
&cc.LndLog, "lnd_log", "", "the lnd log file to read to get "+
"the commit_point values when rescuing multiple "+
"channels at the same time")
cc.rootKey = newRootKey(cc.cmd, "decrypting the backup")
cc.inputs = newInputFlags(cc.cmd)
@ -109,7 +126,13 @@ func (c *rescueClosedCommand) Execute(_ *cobra.Command, _ []string) error {
if err != nil {
return err
}
return rescueClosedChannels(extendedKey, entries, db)
commitPoints, err := commitPointsFromDB(db)
if err != nil {
return fmt.Errorf("error reading commit points from "+
"db: %v", err)
}
return rescueClosedChannels(extendedKey, entries, commitPoints)
case c.Addr != "":
// First parse address to get targetPubKeyHash from it later.
@ -133,6 +156,20 @@ func (c *rescueClosedCommand) Execute(_ *cobra.Command, _ []string) error {
return rescueClosedChannel(extendedKey, targetAddr, commitPoint)
case c.LndLog != "":
// Parse channel entries from any of the possible input files.
entries, err := c.inputs.parseInputType()
if err != nil {
return err
}
commitPoints, err := commitPointsFromLogFile(c.LndLog)
if err != nil {
return fmt.Errorf("error parsing commit points from "+
"log file: %v", err)
}
return rescueClosedChannels(extendedKey, entries, commitPoints)
default:
return fmt.Errorf("you either need to specify --channeldb and " +
"--fromsummary or --force_close_addr and " +
@ -140,62 +177,103 @@ func (c *rescueClosedCommand) Execute(_ *cobra.Command, _ []string) error {
}
}
func rescueClosedChannels(extendedKey *hdkeychain.ExtendedKey,
entries []*dataformat.SummaryEntry, chanDb *channeldb.DB) error {
err := fillCache(extendedKey)
if err != nil {
return err
}
func commitPointsFromDB(chanDb *channeldb.DB) ([]*btcec.PublicKey, error) {
var result []*btcec.PublicKey
channels, err := chanDb.FetchAllChannels()
if err != nil {
return err
return nil, err
}
// Try naive/lucky guess with information from channel DB.
for _, channel := range channels {
channelPoint := channel.FundingOutpoint.String()
var channelEntry *dataformat.SummaryEntry
for _, entry := range entries {
if entry.ChannelPoint == channelPoint {
channelEntry = entry
}
if channel.RemoteNextRevocation != nil {
result = append(result, channel.RemoteNextRevocation)
}
if channel.RemoteCurrentRevocation != nil {
result = append(result, channel.RemoteCurrentRevocation)
}
}
return result, nil
}
func commitPointsFromLogFile(lndLog string) ([]*btcec.PublicKey, error) {
logFileBytes, err := ioutil.ReadFile(lndLog)
if err != nil {
return nil, fmt.Errorf("error reading log file %s: %v", lndLog,
err)
}
allMatches := patternCommitPoint.FindAllStringSubmatch(
string(logFileBytes), -1,
)
dedupMap := make(map[string]*btcec.PublicKey, len(allMatches))
for _, groups := range allMatches {
commitPointBytes, err := hex.DecodeString(groups[1])
if err != nil {
return nil, fmt.Errorf("error parsing commit point "+
"hex: %v", err)
}
commitPoint, err := btcec.ParsePubKey(
commitPointBytes, btcec.S256(),
)
if err != nil {
return nil, fmt.Errorf("error parsing commit point: %v",
err)
}
dedupMap[groups[1]] = commitPoint
}
var result []*btcec.PublicKey
for _, commitPoint := range dedupMap {
result = append(result, commitPoint)
}
log.Infof("Extracted %d commit points from log file %s", len(result),
lndLog)
return result, nil
}
func rescueClosedChannels(extendedKey *hdkeychain.ExtendedKey,
entries []*dataformat.SummaryEntry,
possibleCommitPoints []*btcec.PublicKey) error {
err := fillCache(extendedKey)
if err != nil {
return err
}
// Try naive/lucky guess by trying out all combinations.
outer:
for _, entry := range entries {
// Don't try anything with open channels, fully closed channels
// or channels where we already have the private key.
if channelEntry == nil || channelEntry.ClosingTX == nil ||
channelEntry.ClosingTX.AllOutsSpent ||
channelEntry.ClosingTX.OurAddr == "" ||
channelEntry.ClosingTX.SweepPrivkey != "" {
if entry.ClosingTX == nil ||
entry.ClosingTX.AllOutsSpent ||
(entry.ClosingTX.OurAddr == "" &&
entry.ClosingTX.ToRemoteAddr == "") ||
entry.ClosingTX.SweepPrivkey != "" {
continue
}
if channel.RemoteNextRevocation != nil {
wif, err := addrInCache(
channelEntry.ClosingTX.OurAddr,
channel.RemoteNextRevocation,
)
switch {
case err == nil:
channelEntry.ClosingTX.SweepPrivkey = wif
case err == errAddrNotFound:
default:
return err
// Try with every possible commit point now.
for _, commitPoint := range possibleCommitPoints {
addr := entry.ClosingTX.OurAddr
if addr == "" {
addr = entry.ClosingTX.ToRemoteAddr
}
}
if channel.RemoteCurrentRevocation != nil {
wif, err := addrInCache(
channelEntry.ClosingTX.OurAddr,
channel.RemoteCurrentRevocation,
)
wif, err := addrInCache(addr, commitPoint)
switch {
case err == nil:
channelEntry.ClosingTX.SweepPrivkey = wif
entry.ClosingTX.SweepPrivkey = wif
continue outer
case err == errAddrNotFound:

@ -17,6 +17,12 @@ 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.
If you need to rescue a whole bunch of channels all at once, you can also
specify the --fromsummary and --lnd_log flags to automatically look for force
close addresses in the summary and the corresponding commit points in the
lnd log file. This only works if lnd is running the fund-recovery branch of my
guggero/lnd fork.
```
chantools rescueclosed [flags]
```
@ -31,6 +37,10 @@ chantools rescueclosed --rootkey xprvxxxxxxxxxx \
chantools rescueclosed --rootkey xprvxxxxxxxxxx \
--force_close_addr bc1q... \
--commit_point 03xxxx
chantools rescueclosed --rootkey xprvxxxxxxxxxx \
--fromsummary results/summary-xxxxxx.json \
--lnd_log ~/.lnd/logs/bitcoin/mainnet/lnd.log
```
### Options
@ -44,6 +54,7 @@ chantools rescueclosed --rootkey xprvxxxxxxxxxx \
--fromsummary string channel input is in the format of chantool's channel summary; specify '-' to read from stdin
-h, --help help for rescueclosed
--listchannels string channel input is in the format of lncli's listchannels format; specify '-' to read from stdin
--lnd_log string the lnd log file to read to get the commit_point values when rescuing multiple channels at the same time
--pendingchannels string channel input is in the format of lncli's pendingchannels format; specify '-' to read from stdin
--rootkey string BIP32 HD root key of the wallet to use for decrypting the backup; leave empty to prompt for lnd 24 word aezeed
```

Loading…
Cancel
Save