-
Notifications
You must be signed in to change notification settings - Fork 31
/
zombierecovery_preparekeys.go
146 lines (122 loc) · 3.75 KB
/
zombierecovery_preparekeys.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
package main
import (
"bytes"
"encoding/hex"
"encoding/json"
"fmt"
"io/ioutil"
"os"
"time"
"github.com/lightninglabs/chantools/lnd"
"github.com/spf13/cobra"
)
const (
numMultisigKeys = 2500
)
type zombieRecoveryPrepareKeysCommand struct {
MatchFile string
PayoutAddr string
NumKeys uint32
rootKey *rootKey
cmd *cobra.Command
}
func newZombieRecoveryPrepareKeysCommand() *cobra.Command {
cc := &zombieRecoveryPrepareKeysCommand{}
cc.cmd = &cobra.Command{
Use: "preparekeys",
Short: "[1/3] Prepare all public keys for a recovery attempt",
Long: `Takes a match file, validates it against the seed and
then adds the first 2500 multisig pubkeys to it.
This must be run by both parties of a channel for a successful recovery. The
next step (makeoffer) takes two such key enriched files and tries to find the
correct ones for the matched channels.`,
Example: `chantools zombierecovery preparekeys \
--match_file match-xxxx-xx-xx-<pubkey1>-<pubkey2>.json \
--payout_addr bc1q...`,
RunE: cc.Execute,
}
cc.cmd.Flags().StringVar(
&cc.MatchFile, "match_file", "", "the match JSON file that "+
"was sent to both nodes by the match maker",
)
cc.cmd.Flags().StringVar(
&cc.PayoutAddr, "payout_addr", "", "the address where this "+
"node's rescued funds should be sent to, must be a "+
"P2WPKH (native SegWit) address",
)
cc.cmd.Flags().Uint32Var(
&cc.NumKeys, "num_keys", numMultisigKeys, "the number of "+
"multisig keys to derive",
)
cc.rootKey = newRootKey(cc.cmd, "deriving the multisig keys")
return cc.cmd
}
func (c *zombieRecoveryPrepareKeysCommand) Execute(_ *cobra.Command,
_ []string) error {
extendedKey, err := c.rootKey.read()
if err != nil {
return fmt.Errorf("error reading root key: %w", err)
}
_, err = lnd.GetP2WPKHScript(c.PayoutAddr, chainParams)
if err != nil {
return fmt.Errorf("invalid payout address, must be P2WPKH")
}
matchFileBytes, err := ioutil.ReadFile(c.MatchFile)
if err != nil {
return fmt.Errorf("error reading match file %s: %w",
c.MatchFile, err)
}
decoder := json.NewDecoder(bytes.NewReader(matchFileBytes))
match := &match{}
if err := decoder.Decode(&match); err != nil {
return fmt.Errorf("error decoding match file %s: %w",
c.MatchFile, err)
}
// Make sure the match file was filled correctly.
if match.Node1 == nil || match.Node2 == nil {
return fmt.Errorf("invalid match file, node info missing")
}
_, pubKey, _, err := lnd.DeriveKey(
extendedKey, lnd.IdentityPath(chainParams), chainParams,
)
if err != nil {
return fmt.Errorf("error deriving identity pubkey: %w", err)
}
pubKeyStr := hex.EncodeToString(pubKey.SerializeCompressed())
var nodeInfo *nodeInfo
switch {
case match.Node1.PubKey != pubKeyStr && match.Node2.PubKey != pubKeyStr:
return fmt.Errorf("derived pubkey %s from seed but that key "+
"was not found in the match file %s", pubKeyStr,
c.MatchFile)
case match.Node1.PubKey == pubKeyStr:
nodeInfo = match.Node1
default:
nodeInfo = match.Node2
}
// Derive all 2500 keys now, this might take a while.
for index := uint32(0); index < c.NumKeys; index++ {
_, pubKey, _, err := lnd.DeriveKey(
extendedKey, lnd.MultisigPath(chainParams, int(index)),
chainParams,
)
if err != nil {
return fmt.Errorf("error deriving multisig pubkey: %w",
err)
}
nodeInfo.MultisigKeys = append(
nodeInfo.MultisigKeys,
hex.EncodeToString(pubKey.SerializeCompressed()),
)
}
nodeInfo.PayoutAddr = c.PayoutAddr
// Write the result back into a new file.
matchBytes, err := json.MarshalIndent(match, "", " ")
if err != nil {
return err
}
fileName := fmt.Sprintf("results/preparedkeys-%s-%s.json",
time.Now().Format("2006-01-02"), pubKeyStr)
log.Infof("Writing result to %s", fileName)
return os.WriteFile(fileName, matchBytes, 0644)
}