@@ -9,9 +9,12 @@ import (
9
9
"os"
10
10
"time"
11
11
12
+ "github.com/btcsuite/btcd/btcec/v2"
12
13
"github.com/btcsuite/btcd/btcutil"
13
14
"github.com/btcsuite/btcd/wire"
15
+ "github.com/lightninglabs/chantools/cln"
14
16
"github.com/lightninglabs/chantools/lnd"
17
+ "github.com/lightningnetwork/lnd/keychain"
15
18
"github.com/spf13/cobra"
16
19
)
17
20
@@ -25,6 +28,8 @@ type zombieRecoveryPrepareKeysCommand struct {
25
28
26
29
NumKeys uint32
27
30
31
+ HsmSecret string
32
+
28
33
rootKey * rootKey
29
34
cmd * cobra.Command
30
35
}
@@ -58,6 +63,12 @@ correct ones for the matched channels.`,
58
63
& cc .NumKeys , "num_keys" , numMultisigKeys , "the number of " +
59
64
"multisig keys to derive" ,
60
65
)
66
+ cc .cmd .Flags ().StringVar (
67
+ & cc .HsmSecret , "hsm_secret" , "" , "the hex encoded HSM secret " +
68
+ "to use for deriving the multisig keys for a CLN " +
69
+ "node; obtain by running 'xxd -p -c32 " +
70
+ "~/.lightning/bitcoin/hsm_secret'" ,
71
+ )
61
72
62
73
cc .rootKey = newRootKey (cc .cmd , "deriving the multisig keys" )
63
74
@@ -67,12 +78,7 @@ correct ones for the matched channels.`,
67
78
func (c * zombieRecoveryPrepareKeysCommand ) Execute (_ * cobra.Command ,
68
79
_ []string ) error {
69
80
70
- extendedKey , err := c .rootKey .read ()
71
- if err != nil {
72
- return fmt .Errorf ("error reading root key: %w" , err )
73
- }
74
-
75
- err = lnd .CheckAddress (
81
+ err := lnd .CheckAddress (
76
82
c .PayoutAddr , chainParams , false , "payout" , lnd .AddrTypeP2WKH ,
77
83
lnd .AddrTypeP2TR ,
78
84
)
@@ -98,26 +104,68 @@ func (c *zombieRecoveryPrepareKeysCommand) Execute(_ *cobra.Command,
98
104
return errors .New ("invalid match file, node info missing" )
99
105
}
100
106
101
- _ , pubKey , _ , err := lnd .DeriveKey (
102
- extendedKey , lnd .IdentityPath (chainParams ), chainParams ,
103
- )
107
+ // Derive the keys for the node type, depending on the input flags.
108
+ var signer lnd.ChannelSigner
109
+ switch {
110
+ case c .HsmSecret != "" :
111
+ secretBytes , err := hex .DecodeString (c .HsmSecret )
112
+ if err != nil {
113
+ return fmt .Errorf ("error decoding HSM secret: %w" , err )
114
+ }
115
+
116
+ var hsmSecret [32 ]byte
117
+ copy (hsmSecret [:], secretBytes )
118
+
119
+ signer = & cln.Signer {
120
+ HsmSecret : hsmSecret ,
121
+ }
122
+
123
+ default :
124
+ extendedKey , err := c .rootKey .read ()
125
+ if err != nil {
126
+ return fmt .Errorf ("error reading root key: %w" , err )
127
+ }
128
+ signer = & lnd.Signer {
129
+ ExtendedKey : extendedKey ,
130
+ ChainParams : chainParams ,
131
+ }
132
+ }
133
+
134
+ nodePrivKey , err := signer .FetchPrivateKey (& keychain.KeyDescriptor {
135
+ KeyLocator : keychain.KeyLocator {
136
+ Family : keychain .KeyFamilyNodeKey ,
137
+ },
138
+ })
104
139
if err != nil {
105
- return fmt .Errorf ("error deriving identity pubkey: %w" , err )
140
+ return fmt .Errorf ("error deriving identity private key: %w" ,
141
+ err )
106
142
}
107
143
144
+ pubKey := nodePrivKey .PubKey ()
108
145
pubKeyStr := hex .EncodeToString (pubKey .SerializeCompressed ())
109
- var nodeInfo * nodeInfo
146
+ var ourNodeInfo , theirNodeInfo * nodeInfo
110
147
switch {
111
148
case match .Node1 .PubKey != pubKeyStr && match .Node2 .PubKey != pubKeyStr :
112
- return fmt .Errorf ("derived pubkey %s from seed but that key " +
113
- "was not found in the match file %s" , pubKeyStr ,
149
+ return fmt .Errorf ("derived pubkey %s from seed but that " +
150
+ "key was not found in the match file %s" , pubKeyStr ,
114
151
c .MatchFile )
115
152
116
153
case match .Node1 .PubKey == pubKeyStr :
117
- nodeInfo = match .Node1
154
+ ourNodeInfo = match .Node1
155
+ theirNodeInfo = match .Node2
118
156
119
157
default :
120
- nodeInfo = match .Node2
158
+ ourNodeInfo = match .Node2
159
+ theirNodeInfo = match .Node1
160
+ }
161
+
162
+ theirNodeKeyBytes , err := hex .DecodeString (theirNodeInfo .PubKey )
163
+ if err != nil {
164
+ return fmt .Errorf ("error decoding peer pubkey: %w" , err )
165
+ }
166
+ theirNodeKey , err := btcec .ParsePubKey (theirNodeKeyBytes )
167
+ if err != nil {
168
+ return fmt .Errorf ("error parsing peer pubkey: %w" , err )
121
169
}
122
170
123
171
// If there are any Simple Taproot channels, we need to generate some
@@ -132,6 +180,12 @@ func (c *zombieRecoveryPrepareKeysCommand) Execute(_ *cobra.Command,
132
180
133
181
_ , isP2TR := addr .(* btcutil.AddressTaproot )
134
182
if isP2TR {
183
+ lndSigner , ok := signer .(* lnd.Signer )
184
+ if ! ok {
185
+ return errors .New ("taproot channels not " +
186
+ "supported for CLN " )
187
+ }
188
+
135
189
chanPoint , err := wire .NewOutPointFromString (
136
190
matchChannel .ChanPoint ,
137
191
)
@@ -147,12 +201,13 @@ func (c *zombieRecoveryPrepareKeysCommand) Execute(_ *cobra.Command,
147
201
}
148
202
149
203
nonces , err := lnd .GenerateMuSig2Nonces (
150
- extendedKey , randomness , chanPoint , chainParams ,
204
+ lndSigner .ExtendedKey , randomness , chanPoint ,
205
+ chainParams ,
151
206
nil ,
152
207
)
153
208
if err != nil {
154
- return fmt .Errorf ("error generating MuSig2 " +
155
- "nonces: %w" , err )
209
+ return fmt .Errorf ("error generating " +
210
+ "MuSig2 nonces: %w" , err )
156
211
}
157
212
158
213
matchChannel .MuSig2NonceRandomness = hex .EncodeToString (
@@ -166,21 +221,25 @@ func (c *zombieRecoveryPrepareKeysCommand) Execute(_ *cobra.Command,
166
221
167
222
// Derive all 2500 keys now, this might take a while.
168
223
for index := range c .NumKeys {
169
- _ , pubKey , _ , err := lnd .DeriveKey (
170
- extendedKey , lnd .MultisigPath (chainParams , int (index )),
171
- chainParams ,
172
- )
224
+ privKey , err := signer .FetchPrivateKey (& keychain.KeyDescriptor {
225
+ PubKey : theirNodeKey ,
226
+ KeyLocator : keychain.KeyLocator {
227
+ Family : keychain .KeyFamilyMultiSig ,
228
+ Index : index ,
229
+ },
230
+ })
173
231
if err != nil {
174
- return fmt .Errorf ("error deriving multisig pubkey: %w" ,
175
- err )
232
+ return fmt .Errorf ("error deriving funding private " +
233
+ "key: %w" , err )
176
234
}
177
235
178
- nodeInfo .MultisigKeys = append (
179
- nodeInfo .MultisigKeys ,
180
- hex .EncodeToString (pubKey .SerializeCompressed ()),
236
+ fundingPubKey := privKey .PubKey ()
237
+ ourNodeInfo .MultisigKeys = append (
238
+ ourNodeInfo .MultisigKeys ,
239
+ hex .EncodeToString (fundingPubKey .SerializeCompressed ()),
181
240
)
182
241
}
183
- nodeInfo .PayoutAddr = c .PayoutAddr
242
+ ourNodeInfo .PayoutAddr = c .PayoutAddr
184
243
185
244
// Write the result back into a new file.
186
245
matchBytes , err := json .MarshalIndent (match , "" , " " )
0 commit comments