Skip to content

Commit 7150f71

Browse files
committed
sweepremoteclosed: implement ancient channel lookup
1 parent 74b748e commit 7150f71

File tree

4 files changed

+297
-13
lines changed

4 files changed

+297
-13
lines changed

cmd/chantools/rescueclosed.go

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ var (
3232

3333
type cacheEntry struct {
3434
privKey *btcec.PrivateKey
35-
pubKey *btcec.PublicKey
35+
keyDesc *keychain.KeyDescriptor
3636
}
3737

3838
type rescueClosedCommand struct {
@@ -403,7 +403,7 @@ func addrInCache(numKeys uint32, addr string,
403403
for i := range numKeys {
404404
cacheEntry := cache[i]
405405
hashedPubKey := btcutil.Hash160(
406-
cacheEntry.pubKey.SerializeCompressed(),
406+
cacheEntry.keyDesc.PubKey.SerializeCompressed(),
407407
)
408408
equal := subtle.ConstantTimeCompare(
409409
targetPubKeyHash, hashedPubKey,
@@ -431,7 +431,7 @@ func addrInCache(numKeys uint32, addr string,
431431
// corresponds to the target pubKeyHash of the given address.
432432
for i := range numKeys {
433433
cacheEntry := cache[i]
434-
basePoint := cacheEntry.pubKey
434+
basePoint := cacheEntry.keyDesc.PubKey
435435
tweakedPubKey := input.TweakPubKey(basePoint, perCommitPoint)
436436
tweakBytes := input.SingleTweakBytes(perCommitPoint, basePoint)
437437
tweakedPrivKey := input.TweakPrivKey(
@@ -460,6 +460,29 @@ func addrInCache(numKeys uint32, addr string,
460460
return "", errAddrNotFound
461461
}
462462

463+
func keyInCache(numKeys uint32, targetPubKeyHash []byte,
464+
perCommitPoint *btcec.PublicKey) (*keychain.KeyDescriptor, []byte,
465+
error) {
466+
467+
for i := range numKeys {
468+
cacheEntry := cache[i]
469+
basePoint := cacheEntry.keyDesc.PubKey
470+
tweakedPubKey := input.TweakPubKey(basePoint, perCommitPoint)
471+
tweakBytes := input.SingleTweakBytes(perCommitPoint, basePoint)
472+
hashedPubKey := btcutil.Hash160(
473+
tweakedPubKey.SerializeCompressed(),
474+
)
475+
equal := subtle.ConstantTimeCompare(
476+
targetPubKeyHash, hashedPubKey,
477+
)
478+
if equal == 1 {
479+
return cacheEntry.keyDesc, tweakBytes, nil
480+
}
481+
}
482+
483+
return nil, nil, errAddrNotFound
484+
}
485+
463486
func fillCache(numKeys uint32, extendedKey *hdkeychain.ExtendedKey) error {
464487
cache = make([]*cacheEntry, numKeys)
465488

@@ -484,7 +507,13 @@ func fillCache(numKeys uint32, extendedKey *hdkeychain.ExtendedKey) error {
484507
}
485508
cache[i] = &cacheEntry{
486509
privKey: privKey,
487-
pubKey: pubKey,
510+
keyDesc: &keychain.KeyDescriptor{
511+
KeyLocator: keychain.KeyLocator{
512+
Family: keychain.KeyFamilyPaymentBase,
513+
Index: i,
514+
},
515+
PubKey: pubKey,
516+
},
488517
}
489518

490519
if i > 0 && i%10000 == 0 {

cmd/chantools/sweepremoteclosed.go

Lines changed: 148 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,16 @@ package main
22

33
import (
44
"bytes"
5+
_ "embed"
56
"encoding/hex"
7+
"encoding/json"
8+
"errors"
69
"fmt"
710

811
"github.com/btcsuite/btcd/btcec/v2"
912
"github.com/btcsuite/btcd/btcutil"
1013
"github.com/btcsuite/btcd/btcutil/hdkeychain"
14+
"github.com/btcsuite/btcd/chaincfg"
1115
"github.com/btcsuite/btcd/chaincfg/chainhash"
1216
"github.com/btcsuite/btcd/txscript"
1317
"github.com/btcsuite/btcd/wire"
@@ -19,6 +23,9 @@ import (
1923
"github.com/spf13/cobra"
2024
)
2125

26+
//go:embed sweepremoteclosed_ancient.json
27+
var ancientChannelPoints []byte
28+
2229
const (
2330
sweepRemoteClosedDefaultRecoveryWindow = 200
2431
sweepDustLimit = 600
@@ -121,9 +128,8 @@ func (c *sweepRemoteClosedCommand) Execute(_ *cobra.Command, _ []string) error {
121128

122129
type targetAddr struct {
123130
addr btcutil.Address
124-
pubKey *btcec.PublicKey
125-
path string
126131
keyDesc *keychain.KeyDescriptor
132+
tweak []byte
127133
vouts []*btc.Vout
128134
script []byte
129135
scriptTree *input.CommitScriptTree
@@ -154,9 +160,7 @@ func sweepRemoteClosed(extendedKey *hdkeychain.ExtendedKey, apiURL,
154160
return fmt.Errorf("error parsing path: %w", err)
155161
}
156162

157-
hdKey, err := lnd.DeriveChildren(
158-
extendedKey, parsedPath,
159-
)
163+
hdKey, err := lnd.DeriveChildren(extendedKey, parsedPath)
160164
if err != nil {
161165
return fmt.Errorf("eror deriving children: %w", err)
162166
}
@@ -168,7 +172,7 @@ func sweepRemoteClosed(extendedKey *hdkeychain.ExtendedKey, apiURL,
168172
}
169173

170174
foundTargets, err := queryAddressBalances(
171-
privKey.PubKey(), path, &keychain.KeyDescriptor{
175+
privKey.PubKey(), &keychain.KeyDescriptor{
172176
PubKey: privKey.PubKey(),
173177
KeyLocator: keychain.KeyLocator{
174178
Family: keychain.KeyFamilyPaymentBase,
@@ -183,6 +187,20 @@ func sweepRemoteClosed(extendedKey *hdkeychain.ExtendedKey, apiURL,
183187
targets = append(targets, foundTargets...)
184188
}
185189

190+
// Also check if there are any funds in channels with the initial,
191+
// tweaked channel type that requires a channel point.
192+
ancientChannelTargets, err := checkAncientChannelPoints(
193+
api, recoveryWindow, extendedKey,
194+
)
195+
if err != nil && !errors.Is(err, errAddrNotFound) {
196+
return fmt.Errorf("could not check ancient channel points: %w",
197+
err)
198+
}
199+
200+
if len(ancientChannelTargets) > 0 {
201+
targets = append(targets, ancientChannelTargets...)
202+
}
203+
186204
// Create estimator and transaction template.
187205
var (
188206
signDescs []*input.SignDescriptor
@@ -235,6 +253,7 @@ func sweepRemoteClosed(extendedKey *hdkeychain.ExtendedKey, apiURL,
235253
signDesc = &input.SignDescriptor{
236254
KeyDesc: *target.keyDesc,
237255
WitnessScript: target.script,
256+
SingleTweak: target.tweak,
238257
Output: prevTxOut,
239258
HashType: txscript.SigHashAll,
240259
PrevOutputFetcher: prevOutFetcher,
@@ -383,7 +402,7 @@ func sweepRemoteClosed(extendedKey *hdkeychain.ExtendedKey, apiURL,
383402
return nil
384403
}
385404

386-
func queryAddressBalances(pubKey *btcec.PublicKey, path string,
405+
func queryAddressBalances(pubKey *btcec.PublicKey,
387406
keyDesc *keychain.KeyDescriptor, api *btc.ExplorerAPI) ([]*targetAddr,
388407
error) {
389408

@@ -401,8 +420,6 @@ func queryAddressBalances(pubKey *btcec.PublicKey, path string,
401420
len(unspent), address.EncodeAddress())
402421
targets = append(targets, &targetAddr{
403422
addr: address,
404-
pubKey: pubKey,
405-
path: path,
406423
keyDesc: keyDesc,
407424
vouts: unspent,
408425
script: script,
@@ -439,3 +456,125 @@ func queryAddressBalances(pubKey *btcec.PublicKey, path string,
439456

440457
return targets, nil
441458
}
459+
460+
type ancientChannel struct {
461+
OP string `json:"close_outpoint"`
462+
Addr string `json:"close_addr"`
463+
CP string `json:"commit_point"`
464+
}
465+
466+
func findAncientChannels(channels []ancientChannel, numKeys uint32,
467+
key *hdkeychain.ExtendedKey) ([]ancientChannel, error) {
468+
469+
if err := fillCache(numKeys, key); err != nil {
470+
return nil, err
471+
}
472+
473+
var foundChannels []ancientChannel
474+
for _, channel := range channels {
475+
// Decode the commit point.
476+
commitPointBytes, err := hex.DecodeString(channel.CP)
477+
if err != nil {
478+
return nil, fmt.Errorf("unable to decode commit "+
479+
"point: %w", err)
480+
}
481+
commitPoint, err := btcec.ParsePubKey(commitPointBytes)
482+
if err != nil {
483+
return nil, fmt.Errorf("unable to parse commit "+
484+
"point: %w", err)
485+
}
486+
487+
// Create the address for the commit key. The addresses in the
488+
// file are always for mainnet.
489+
targetPubKeyHash, _, err := lnd.DecodeAddressHash(
490+
channel.Addr, &chaincfg.MainNetParams,
491+
)
492+
if err != nil {
493+
return nil, fmt.Errorf("error parsing addr: %w", err)
494+
}
495+
496+
_, _, err = keyInCache(numKeys, targetPubKeyHash, commitPoint)
497+
switch {
498+
case err == nil:
499+
foundChannels = append(foundChannels, channel)
500+
501+
case errors.Is(err, errAddrNotFound):
502+
// Try next address.
503+
504+
default:
505+
return nil, err
506+
}
507+
}
508+
509+
return foundChannels, nil
510+
}
511+
512+
func checkAncientChannelPoints(api *btc.ExplorerAPI, numKeys uint32,
513+
key *hdkeychain.ExtendedKey) ([]*targetAddr, error) {
514+
515+
var channels []ancientChannel
516+
err := json.Unmarshal(ancientChannelPoints, &channels)
517+
if err != nil {
518+
return nil, err
519+
}
520+
521+
ancientChannels, err := findAncientChannels(channels, numKeys, key)
522+
if err != nil {
523+
return nil, err
524+
}
525+
526+
targets := make([]*targetAddr, 0, len(ancientChannels))
527+
for _, ancientChannel := range ancientChannels {
528+
// Decode the commit point.
529+
commitPointBytes, err := hex.DecodeString(ancientChannel.CP)
530+
if err != nil {
531+
return nil, fmt.Errorf("unable to decode commit "+
532+
"point: %w", err)
533+
}
534+
commitPoint, err := btcec.ParsePubKey(commitPointBytes)
535+
if err != nil {
536+
return nil, fmt.Errorf("unable to parse commit point: "+
537+
"%w", err)
538+
}
539+
540+
// Create the address for the commit key. The addresses in the
541+
// file are always for mainnet.
542+
targetPubKeyHash, _, err := lnd.DecodeAddressHash(
543+
ancientChannel.Addr, &chaincfg.MainNetParams,
544+
)
545+
if err != nil {
546+
return nil, fmt.Errorf("error parsing addr: %w", err)
547+
}
548+
addr, err := lnd.ParseAddress(
549+
ancientChannel.Addr, &chaincfg.MainNetParams,
550+
)
551+
if err != nil {
552+
return nil, fmt.Errorf("error parsing addr: %w", err)
553+
}
554+
555+
log.Infof("Found private key for address %v in list of "+
556+
"ancient channels!", addr)
557+
558+
unspent, err := api.Unspent(addr.EncodeAddress())
559+
if err != nil {
560+
return nil, fmt.Errorf("could not query unspent: %w",
561+
err)
562+
}
563+
564+
keyDesc, tweak, err := keyInCache(
565+
numKeys, targetPubKeyHash, commitPoint,
566+
)
567+
if err != nil {
568+
return nil, err
569+
}
570+
571+
targets = append(targets, &targetAddr{
572+
addr: addr,
573+
keyDesc: keyDesc,
574+
tweak: tweak,
575+
vouts: unspent,
576+
})
577+
}
578+
579+
return targets, nil
580+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
[
2+
{
3+
"close_outpoint": "35e5aafcc03a4eacaed492d44815fea553f5434b881ecb5d9edcd094c899c074:0",
4+
"close_addr": "bc1q43tqzm23a8ld9qdpfp7feut5yc20rtewhtpz40",
5+
"commit_point": "023fc323c1768102c214f550551a102f88c0f261a4b8e7cc998c0dce28b6998d3c"
6+
}
7+
]

0 commit comments

Comments
 (0)