@ -37,6 +37,9 @@ type sweepTimeLockManualCommand struct {
MaxNumChannelsTotal uint16
MaxNumChanUpdates uint64
ChannelBackup string
ChannelPoint string
rootKey * rootKey
inputs * inputFlags
cmd * cobra . Command
@ -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 ) . ` ,
@ -64,6 +70,14 @@ address is always the one that's longer (because it's P2WSH and not P2PKH).`,
-- timelockaddr bc1q ... ... ... ... \
-- remoterevbasepoint 03 xxxxxxx \
-- feerate 10 \
-- publish
chantools sweeptimelockmanual \
-- sweepaddr bc1q ... . . \
-- timelockaddr bc1q ... ... ... ... \
-- frombackup channel . backup \
-- channelpoint f39310xxxxxxxxxx : 1 \
-- feerate 10 \
-- publish ` ,
RunE : cc . Execute ,
}
@ -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 )
@ -126,9 +150,68 @@ 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 ( r emoteRevocationBasePoint)
if err != nil {
return fmt . Errorf ( "invalid remote revocation base point: %w" ,
err )
@ -136,7 +219,8 @@ func (c *sweepTimeLockManualCommand) Execute(_ *cobra.Command, _ []string) error
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 ,
)
}
@ -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.