Skip to content

Commit

Permalink
sweeptimelockmanual: allow using channel backup file
Browse files Browse the repository at this point in the history
Instead of needing to manually dump the channel backup file, look for
the remote revocation base point and then have the CSV delay and channel
derivation index being brute forced, we can extract all that info
directly from the channel backup file.
  • Loading branch information
guggero committed Nov 8, 2023
1 parent dee18ed commit a13262f
Showing 1 changed file with 94 additions and 2 deletions.
96 changes: 94 additions & 2 deletions cmd/chantools/sweeptimelockmanual.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@ type sweepTimeLockManualCommand struct {
MaxNumChannelsTotal uint16
MaxNumChanUpdates uint64

ChannelBackup string
ChannelPoint string

rootKey *rootKey
inputs *inputFlags
cmd *cobra.Command
Expand All @@ -56,6 +59,9 @@ and only the channel.backup file is available.
To get the value for --remoterevbasepoint you must use the dumpbackup command,
then look up the value for RemoteChanCfg -> RevocationBasePoint -> PubKey.
Alternatively you can directly use the --frombackup and --channelpoint flags to
pull the required information from the given channel.backup file automatically.
To get the value for --timelockaddr you must look up the channel's funding
output on chain, then follow it to the force close output. The time locked
address is always the one that's longer (because it's P2WSH and not P2PKH).`,
Expand All @@ -64,6 +70,14 @@ address is always the one that's longer (because it's P2WSH and not P2PKH).`,
--timelockaddr bc1q............ \
--remoterevbasepoint 03xxxxxxx \
--feerate 10 \
--publish
chantools sweeptimelockmanual \
--sweepaddr bc1q..... \
--timelockaddr bc1q............ \
--frombackup channel.backup \
--channelpoint f39310xxxxxxxxxx:1 \
--feerate 10 \
--publish`,
RunE: cc.Execute,
}
Expand Down Expand Up @@ -105,6 +119,16 @@ address is always the one that's longer (because it's P2WSH and not P2PKH).`,
"remote node's revocation base point, can be found "+
"in a channel.backup file",
)
cc.cmd.Flags().StringVar(
&cc.ChannelBackup, "frombackup", "", "channel backup file to "+
"read the channel information from",
)
cc.cmd.Flags().StringVar(
&cc.ChannelPoint, "channelpoint", "", "channel point to use "+
"for locating the channel in the channel backup file "+
"specified in the --frombackup flag, "+
"format: txid:index",
)

cc.rootKey = newRootKey(cc.cmd, "deriving keys")
cc.inputs = newInputFlags(cc.cmd)
Expand All @@ -126,17 +150,77 @@ func (c *sweepTimeLockManualCommand) Execute(_ *cobra.Command, _ []string) error
return fmt.Errorf("time lock addr is required")
}

var (
startCsvLimit uint16
maxCsvLimit = c.MaxCsvLimit
startNumChannelsTotal uint16
maxNumChannelsTotal = c.MaxNumChannelsTotal
remoteRevocationBasePoint = c.RemoteRevocationBasePoint
)

// We either support specifying the remote revocation base point
// manually, in which case the CSV limit and number of channels are not
// known, or we can use the channel backup file to get the required
// information from there directly.
switch {
case c.RemoteRevocationBasePoint != "":
// Nothing to do here but continue below with the info provided
// by the user.

case c.ChannelBackup != "":
if c.ChannelPoint == "" {
return fmt.Errorf("channel point is required with " +
"--frombackup")
}

backupChan, err := lnd.ExtractChannel(
extendedKey, chainParams, c.ChannelBackup,
c.ChannelPoint,
)
if err != nil {
return fmt.Errorf("error extracting channel: %w", err)
}

remoteCfg := backupChan.RemoteChanCfg
remoteRevocationBasePoint = remoteCfg.RevocationBasePoint.PubKey

startCsvLimit = remoteCfg.CsvDelay
maxCsvLimit = startCsvLimit + 1

delayPath, err := lnd.ParsePath(
backupChan.LocalChanCfg.DelayBasePoint.Path,
)
if err != nil {
return fmt.Errorf("error parsing delay path: %w", err)
}
if len(delayPath) != 5 {
return fmt.Errorf("invalid delay path '%v'", delayPath)
}

startNumChannelsTotal = uint16(delayPath[4])
maxNumChannelsTotal = startNumChannelsTotal + 1

case c.ChannelBackup != "" && c.RemoteRevocationBasePoint != "":
return fmt.Errorf("cannot use both --frombackup and " +
"--remoterevbasepoint at the same time")

default:
return fmt.Errorf("either --frombackup or " +
"--remoterevbasepoint is required")
}

// The remote revocation base point must also be set and a valid EC
// point.
remoteRevPoint, err := pubKeyFromHex(c.RemoteRevocationBasePoint)
remoteRevPoint, err := pubKeyFromHex(remoteRevocationBasePoint)
if err != nil {
return fmt.Errorf("invalid remote revocation base point: %w",
err)
}

return sweepTimeLockManual(
extendedKey, c.APIURL, c.SweepAddr, c.TimeLockAddr,
remoteRevPoint, 0, c.MaxCsvLimit, 0, c.MaxNumChannelsTotal,
remoteRevPoint, startCsvLimit, maxCsvLimit,
startNumChannelsTotal, maxNumChannelsTotal,
c.MaxNumChanUpdates, c.Publish, c.FeeRate,
)
}
Expand All @@ -146,6 +230,14 @@ func sweepTimeLockManual(extendedKey *hdkeychain.ExtendedKey, apiURL string,
startCsvTimeout, maxCsvTimeout, startNumChannels, maxNumChannels uint16,
maxNumChanUpdates uint64, publish bool, feeRate uint32) error {

log.Debugf("Starting to brute force the time lock script, using: "+
"remote_rev_base_point=%x, start_csv_limit=%d, "+
"max_csv_limit=%d, start_num_channels=%d, "+
"max_num_channels=%d, max_num_chan_updates=%d",
remoteRevPoint.SerializeCompressed(), startCsvTimeout,
maxCsvTimeout, startNumChannels, maxNumChannels,
maxNumChanUpdates)

// First of all, we need to parse the lock addr and make sure we can
// brute force the script with the information we have. If not, we can't
// continue anyway.
Expand Down

0 comments on commit a13262f

Please sign in to comment.