Skip to content

Commit a13262f

Browse files
committed
sweeptimelockmanual: allow using channel backup file
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.
1 parent dee18ed commit a13262f

File tree

1 file changed

+94
-2
lines changed

1 file changed

+94
-2
lines changed

cmd/chantools/sweeptimelockmanual.go

Lines changed: 94 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,9 @@ type sweepTimeLockManualCommand struct {
3737
MaxNumChannelsTotal uint16
3838
MaxNumChanUpdates uint64
3939

40+
ChannelBackup string
41+
ChannelPoint string
42+
4043
rootKey *rootKey
4144
inputs *inputFlags
4245
cmd *cobra.Command
@@ -56,6 +59,9 @@ and only the channel.backup file is available.
5659
To get the value for --remoterevbasepoint you must use the dumpbackup command,
5760
then look up the value for RemoteChanCfg -> RevocationBasePoint -> PubKey.
5861
62+
Alternatively you can directly use the --frombackup and --channelpoint flags to
63+
pull the required information from the given channel.backup file automatically.
64+
5965
To get the value for --timelockaddr you must look up the channel's funding
6066
output on chain, then follow it to the force close output. The time locked
6167
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).`,
6470
--timelockaddr bc1q............ \
6571
--remoterevbasepoint 03xxxxxxx \
6672
--feerate 10 \
73+
--publish
74+
75+
chantools sweeptimelockmanual \
76+
--sweepaddr bc1q..... \
77+
--timelockaddr bc1q............ \
78+
--frombackup channel.backup \
79+
--channelpoint f39310xxxxxxxxxx:1 \
80+
--feerate 10 \
6781
--publish`,
6882
RunE: cc.Execute,
6983
}
@@ -105,6 +119,16 @@ address is always the one that's longer (because it's P2WSH and not P2PKH).`,
105119
"remote node's revocation base point, can be found "+
106120
"in a channel.backup file",
107121
)
122+
cc.cmd.Flags().StringVar(
123+
&cc.ChannelBackup, "frombackup", "", "channel backup file to "+
124+
"read the channel information from",
125+
)
126+
cc.cmd.Flags().StringVar(
127+
&cc.ChannelPoint, "channelpoint", "", "channel point to use "+
128+
"for locating the channel in the channel backup file "+
129+
"specified in the --frombackup flag, "+
130+
"format: txid:index",
131+
)
108132

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

153+
var (
154+
startCsvLimit uint16
155+
maxCsvLimit = c.MaxCsvLimit
156+
startNumChannelsTotal uint16
157+
maxNumChannelsTotal = c.MaxNumChannelsTotal
158+
remoteRevocationBasePoint = c.RemoteRevocationBasePoint
159+
)
160+
161+
// We either support specifying the remote revocation base point
162+
// manually, in which case the CSV limit and number of channels are not
163+
// known, or we can use the channel backup file to get the required
164+
// information from there directly.
165+
switch {
166+
case c.RemoteRevocationBasePoint != "":
167+
// Nothing to do here but continue below with the info provided
168+
// by the user.
169+
170+
case c.ChannelBackup != "":
171+
if c.ChannelPoint == "" {
172+
return fmt.Errorf("channel point is required with " +
173+
"--frombackup")
174+
}
175+
176+
backupChan, err := lnd.ExtractChannel(
177+
extendedKey, chainParams, c.ChannelBackup,
178+
c.ChannelPoint,
179+
)
180+
if err != nil {
181+
return fmt.Errorf("error extracting channel: %w", err)
182+
}
183+
184+
remoteCfg := backupChan.RemoteChanCfg
185+
remoteRevocationBasePoint = remoteCfg.RevocationBasePoint.PubKey
186+
187+
startCsvLimit = remoteCfg.CsvDelay
188+
maxCsvLimit = startCsvLimit + 1
189+
190+
delayPath, err := lnd.ParsePath(
191+
backupChan.LocalChanCfg.DelayBasePoint.Path,
192+
)
193+
if err != nil {
194+
return fmt.Errorf("error parsing delay path: %w", err)
195+
}
196+
if len(delayPath) != 5 {
197+
return fmt.Errorf("invalid delay path '%v'", delayPath)
198+
}
199+
200+
startNumChannelsTotal = uint16(delayPath[4])
201+
maxNumChannelsTotal = startNumChannelsTotal + 1
202+
203+
case c.ChannelBackup != "" && c.RemoteRevocationBasePoint != "":
204+
return fmt.Errorf("cannot use both --frombackup and " +
205+
"--remoterevbasepoint at the same time")
206+
207+
default:
208+
return fmt.Errorf("either --frombackup or " +
209+
"--remoterevbasepoint is required")
210+
}
211+
129212
// The remote revocation base point must also be set and a valid EC
130213
// point.
131-
remoteRevPoint, err := pubKeyFromHex(c.RemoteRevocationBasePoint)
214+
remoteRevPoint, err := pubKeyFromHex(remoteRevocationBasePoint)
132215
if err != nil {
133216
return fmt.Errorf("invalid remote revocation base point: %w",
134217
err)
135218
}
136219

137220
return sweepTimeLockManual(
138221
extendedKey, c.APIURL, c.SweepAddr, c.TimeLockAddr,
139-
remoteRevPoint, 0, c.MaxCsvLimit, 0, c.MaxNumChannelsTotal,
222+
remoteRevPoint, startCsvLimit, maxCsvLimit,
223+
startNumChannelsTotal, maxNumChannelsTotal,
140224
c.MaxNumChanUpdates, c.Publish, c.FeeRate,
141225
)
142226
}
@@ -146,6 +230,14 @@ func sweepTimeLockManual(extendedKey *hdkeychain.ExtendedKey, apiURL string,
146230
startCsvTimeout, maxCsvTimeout, startNumChannels, maxNumChannels uint16,
147231
maxNumChanUpdates uint64, publish bool, feeRate uint32) error {
148232

233+
log.Debugf("Starting to brute force the time lock script, using: "+
234+
"remote_rev_base_point=%x, start_csv_limit=%d, "+
235+
"max_csv_limit=%d, start_num_channels=%d, "+
236+
"max_num_channels=%d, max_num_chan_updates=%d",
237+
remoteRevPoint.SerializeCompressed(), startCsvTimeout,
238+
maxCsvTimeout, startNumChannels, maxNumChannels,
239+
maxNumChanUpdates)
240+
149241
// First of all, we need to parse the lock addr and make sure we can
150242
// brute force the script with the information we have. If not, we can't
151243
// continue anyway.

0 commit comments

Comments
 (0)