|
|
|
@ -6,7 +6,6 @@ import (
|
|
|
|
|
"encoding/hex"
|
|
|
|
|
"encoding/json"
|
|
|
|
|
"fmt"
|
|
|
|
|
"io/ioutil"
|
|
|
|
|
"os"
|
|
|
|
|
"strconv"
|
|
|
|
|
"strings"
|
|
|
|
@ -28,6 +27,8 @@ type zombieRecoveryMakeOfferCommand struct {
|
|
|
|
|
Node2 string
|
|
|
|
|
FeeRate uint32
|
|
|
|
|
|
|
|
|
|
MatchOnly bool
|
|
|
|
|
|
|
|
|
|
rootKey *rootKey
|
|
|
|
|
cmd *cobra.Command
|
|
|
|
|
}
|
|
|
|
@ -64,6 +65,10 @@ a counter offer.`,
|
|
|
|
|
&cc.FeeRate, "feerate", defaultFeeSatPerVByte, "fee rate to "+
|
|
|
|
|
"use for the sweep transaction in sat/vByte",
|
|
|
|
|
)
|
|
|
|
|
cc.cmd.Flags().BoolVar(
|
|
|
|
|
&cc.MatchOnly, "matchonly", false, "only match the keys, "+
|
|
|
|
|
"don't create an offer",
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
cc.rootKey = newRootKey(cc.cmd, "signing the offer")
|
|
|
|
|
|
|
|
|
@ -82,12 +87,12 @@ func (c *zombieRecoveryMakeOfferCommand) Execute(_ *cobra.Command,
|
|
|
|
|
c.FeeRate = defaultFeeSatPerVByte
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
node1Bytes, err := ioutil.ReadFile(c.Node1)
|
|
|
|
|
node1Bytes, err := os.ReadFile(c.Node1)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return fmt.Errorf("error reading node1 key file %s: %w",
|
|
|
|
|
c.Node1, err)
|
|
|
|
|
}
|
|
|
|
|
node2Bytes, err := ioutil.ReadFile(c.Node2)
|
|
|
|
|
node2Bytes, err := os.ReadFile(c.Node2)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return fmt.Errorf("error reading node2 key file %s: %w",
|
|
|
|
|
c.Node2, err)
|
|
|
|
@ -153,6 +158,22 @@ func (c *zombieRecoveryMakeOfferCommand) Execute(_ *cobra.Command,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// If we're only matching, we can stop here.
|
|
|
|
|
if c.MatchOnly {
|
|
|
|
|
ourPubKeys, err := parseKeys(keys1.Node1.MultisigKeys)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return fmt.Errorf("error parsing their keys: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
theirPubKeys, err := parseKeys(keys2.Node2.MultisigKeys)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return fmt.Errorf("error parsing our keys: %w", err)
|
|
|
|
|
}
|
|
|
|
|
return matchKeys(
|
|
|
|
|
keys1.Channels, ourPubKeys, theirPubKeys, chainParams,
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Make sure one of the nodes is ours.
|
|
|
|
|
_, pubKey, _, err := lnd.DeriveKey(
|
|
|
|
|
extendedKey, lnd.IdentityPath(chainParams), chainParams,
|
|
|
|
@ -206,52 +227,19 @@ func (c *zombieRecoveryMakeOfferCommand) Execute(_ *cobra.Command,
|
|
|
|
|
return fmt.Errorf("payout address missing")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ourPubKeys := make([]*btcec.PublicKey, len(ourKeys))
|
|
|
|
|
theirPubKeys := make([]*btcec.PublicKey, len(theirKeys))
|
|
|
|
|
for idx, pubKeyHex := range ourKeys {
|
|
|
|
|
ourPubKeys[idx], err = pubKeyFromHex(pubKeyHex)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return fmt.Errorf("error parsing our pubKey: %w", err)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
for idx, pubKeyHex := range theirKeys {
|
|
|
|
|
theirPubKeys[idx], err = pubKeyFromHex(pubKeyHex)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return fmt.Errorf("error parsing their pubKey: %w", err)
|
|
|
|
|
}
|
|
|
|
|
ourPubKeys, err := parseKeys(ourKeys)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return fmt.Errorf("error parsing their keys: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Loop through all channels and all keys now, this will definitely take
|
|
|
|
|
// a while.
|
|
|
|
|
channelLoop:
|
|
|
|
|
for _, channel := range keys1.Channels {
|
|
|
|
|
for ourKeyIndex, ourKey := range ourPubKeys {
|
|
|
|
|
for _, theirKey := range theirPubKeys {
|
|
|
|
|
match, witnessScript, err := matchScript(
|
|
|
|
|
channel.Address, ourKey, theirKey,
|
|
|
|
|
chainParams,
|
|
|
|
|
)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return fmt.Errorf("error matching "+
|
|
|
|
|
"keys to script: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if match {
|
|
|
|
|
channel.ourKeyIndex = uint32(ourKeyIndex)
|
|
|
|
|
channel.ourKey = ourKey
|
|
|
|
|
channel.theirKey = theirKey
|
|
|
|
|
channel.witnessScript = witnessScript
|
|
|
|
|
|
|
|
|
|
log.Infof("Found keys for channel %s",
|
|
|
|
|
channel.ChanPoint)
|
|
|
|
|
|
|
|
|
|
continue channelLoop
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
theirPubKeys, err := parseKeys(theirKeys)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return fmt.Errorf("error parsing our keys: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return fmt.Errorf("didn't find matching multisig keys for "+
|
|
|
|
|
"channel %s", channel.ChanPoint)
|
|
|
|
|
err = matchKeys(keys1.Channels, ourPubKeys, theirPubKeys, chainParams)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Let's now sum up the tally of how much of the rescued funds should
|
|
|
|
@ -444,6 +432,64 @@ channelLoop:
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// parseKeys parses a list of string keys into public keys.
|
|
|
|
|
func parseKeys(keys []string) ([]*btcec.PublicKey, error) {
|
|
|
|
|
pubKeys := make([]*btcec.PublicKey, 0, len(keys))
|
|
|
|
|
for _, key := range keys {
|
|
|
|
|
pubKey, err := pubKeyFromHex(key)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
pubKeys = append(pubKeys, pubKey)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return pubKeys, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// matchKeys tries to match the keys from the two nodes. It updates the channels
|
|
|
|
|
// with the correct keys and witness scripts.
|
|
|
|
|
func matchKeys(channels []*channel, ourPubKeys, theirPubKeys []*btcec.PublicKey,
|
|
|
|
|
chainParams *chaincfg.Params) error {
|
|
|
|
|
|
|
|
|
|
// Loop through all channels and all keys now, this will definitely take
|
|
|
|
|
// a while.
|
|
|
|
|
channelLoop:
|
|
|
|
|
for _, channel := range channels {
|
|
|
|
|
for ourKeyIndex, ourKey := range ourPubKeys {
|
|
|
|
|
for _, theirKey := range theirPubKeys {
|
|
|
|
|
match, witnessScript, err := matchScript(
|
|
|
|
|
channel.Address, ourKey, theirKey,
|
|
|
|
|
chainParams,
|
|
|
|
|
)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return fmt.Errorf("error matching "+
|
|
|
|
|
"keys to script: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if match {
|
|
|
|
|
channel.ourKeyIndex = uint32(ourKeyIndex)
|
|
|
|
|
channel.ourKey = ourKey
|
|
|
|
|
channel.theirKey = theirKey
|
|
|
|
|
channel.witnessScript = witnessScript
|
|
|
|
|
|
|
|
|
|
log.Infof("Found keys for channel %s: "+
|
|
|
|
|
"our key %x, their key %x",
|
|
|
|
|
channel.ChanPoint,
|
|
|
|
|
ourKey.SerializeCompressed(),
|
|
|
|
|
theirKey.SerializeCompressed())
|
|
|
|
|
|
|
|
|
|
continue channelLoop
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return fmt.Errorf("didn't find matching multisig keys for "+
|
|
|
|
|
"channel %s", channel.ChanPoint)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func matchScript(address string, key1, key2 *btcec.PublicKey,
|
|
|
|
|
params *chaincfg.Params) (bool, []byte, error) {
|
|
|
|
|
|
|
|
|
|