-
Notifications
You must be signed in to change notification settings - Fork 23
/
Copy pathkeys.go
427 lines (362 loc) · 13.7 KB
/
keys.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
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
package cosmosutils
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"os"
"os/exec"
"path/filepath"
"regexp"
"strings"
"github.com/initia-labs/weave/common"
"github.com/initia-labs/weave/crypto"
"github.com/initia-labs/weave/io"
)
type KeyInfo struct {
Name string `json:"name"`
Type string `json:"type"`
Address string `json:"address"`
PubKey string `json:"pubkey"`
Mnemonic string `json:"mnemonic"`
}
func UnmarshalKeyInfo(rawJson string) (KeyInfo, error) {
var account KeyInfo
err := json.Unmarshal([]byte(rawJson), &account)
if err != nil {
return KeyInfo{}, fmt.Errorf("failed to unmarshal JSON: %v", err)
}
return account, nil
}
// AddOrReplace adds or replaces a key using `initiad keys add <keyname> --keyring-backend test` with 'y' confirmation
func AddOrReplace(appName, keyname string) (string, error) {
// Command to add the key: echo 'y' | initiad keys add <keyname> --keyring-backend test
cmd := exec.Command(appName, "keys", "add", keyname, "--coin-type", "118", "--key-type", "secp256k1", "--keyring-backend", "test", "--output", "json")
// Simulate pressing 'y' for confirmation
cmd.Stdin = bytes.NewBufferString("y\n")
// Run the command and capture the output
outputBytes, err := cmd.CombinedOutput()
if err != nil {
return "", fmt.Errorf("failed to add key for %s: %v, output: %s", keyname, err, string(outputBytes))
}
return string(outputBytes), nil
}
func DeleteKey(appName, keyname string) error {
cmd := exec.Command(appName, "keys", "delete", keyname, "--keyring-backend", "test", "-y")
return cmd.Run()
}
// KeyExists checks if a key with the given keyName exists using `initiad keys show`
func KeyExists(appName, keyname string) bool {
cmd := exec.Command(appName, "keys", "show", keyname, "--keyring-backend", "test")
// Run the command and capture the output or error
err := cmd.Run()
return err == nil
}
// RecoverKeyFromMnemonic recovers or replaces a key using a mnemonic phrase
// If the key already exists, it will replace the key and confirm with 'y' before adding the mnemonic
func RecoverKeyFromMnemonic(appName, keyname, mnemonic string) (string, error) {
// Check if the key already exists
exists := KeyExists(appName, keyname)
var inputBuffer bytes.Buffer
if exists {
// Simulate pressing 'y' for confirmation
inputBuffer.WriteString("y\n")
}
// Add the mnemonic input after the confirmation (if any)
inputBuffer.WriteString(mnemonic + "\n")
// Command to recover (or replace) the key: initiad keys add <keyname> --recover --keyring-backend test
cmd := exec.Command(appName, "keys", "add", keyname, "--coin-type", "118", "--key-type", "secp256k1", "--recover", "--keyring-backend", "test", "--output", "json")
// Pass the combined confirmation and mnemonic as input to the command
cmd.Stdin = &inputBuffer
// Run the command and capture the output
outputBytes, err := cmd.CombinedOutput()
if err != nil {
return "", fmt.Errorf("failed to recover or replace key for %s: %v, output: %s", keyname, err, string(outputBytes))
}
// Return the command output if successful
return string(outputBytes), nil
}
func GenerateNewKeyInfo(appName, keyname string) (KeyInfo, error) {
rawKey, err := AddOrReplace(appName, keyname)
if err != nil {
return KeyInfo{}, err
}
if err = DeleteKey(appName, keyname); err != nil {
return KeyInfo{}, err
}
return UnmarshalKeyInfo(rawKey)
}
func GetAddressFromMnemonic(appName, mnemonic string) (string, error) {
keyname := "weave.DummyKey"
rawKey, err := RecoverKeyFromMnemonic(appName, keyname, mnemonic)
if err != nil {
return "", err
}
if err := DeleteKey(appName, keyname); err != nil {
return "", err
}
key, err := UnmarshalKeyInfo(rawKey)
if err != nil {
return "", err
}
return key.Address, nil
}
// OPInitRecoverKeyFromMnemonic recovers or replaces a key using a mnemonic phrase
// If the key already exists, it will replace the key and confirm with 'y' before adding the mnemonic
func OPInitRecoverKeyFromMnemonic(appName, keyname, mnemonic string, isCelestia bool, opInitHome string) (string, error) {
// Check if the key already exists
exists := OPInitKeyExist(appName, keyname, opInitHome)
{
var cmd *exec.Cmd
var inputBuffer bytes.Buffer
if exists {
// Simulate pressing 'y' for confirmation
inputBuffer.WriteString("y\n")
cmd = exec.Command(appName, "keys", "delete", "weave-dummy", keyname, "--home", opInitHome)
// Run the command and capture the output
outputBytes, err := cmd.CombinedOutput()
if err != nil {
return "", fmt.Errorf("failed to delete key for %s: %v, output: %s", keyname, err, string(outputBytes))
}
}
}
var cmd *exec.Cmd
var inputBuffer bytes.Buffer
// Add the mnemonic input after the confirmation (if any)
inputBuffer.WriteString(mnemonic + "\n")
if isCelestia {
cmd = exec.Command(appName, "keys", "add", "weave-dummy", keyname, "--recover", "--bech32", "celestia", "--home", opInitHome)
} else {
cmd = exec.Command(appName, "keys", "add", "weave-dummy", keyname, "--recover", "--home", opInitHome)
}
// Pass the combined confirmation and mnemonic as input to the command
cmd.Stdin = &inputBuffer
// Run the command and capture the output
outputBytes, err := cmd.CombinedOutput()
if err != nil {
return "", fmt.Errorf("failed to recover or replace key for %s: %v, output: %s", keyname, err, string(outputBytes))
}
// Return the command output if successful
return string(outputBytes), nil
}
func OPInitKeyExist(appName, keyname, opInitHome string) bool {
cmd := exec.Command(appName, "keys", "show", "weave-dummy", keyname, "--home", opInitHome)
// Run the command and capture the output or error
err := cmd.Run()
return err == nil
}
// OPInitGetAddressForKey retrieves the address for a given key using opinitd.
func OPInitGetAddressForKey(appName, keyname, opInitHome string) (string, error) {
cmd := exec.Command(appName, "keys", "show", "weave-dummy", keyname, "--home", opInitHome)
outputBytes, err := cmd.CombinedOutput()
if err != nil {
return "", fmt.Errorf("failed to get address for key %s: %v, output: %s", keyname, err, string(outputBytes))
}
// Parse the output to extract the address
address, err := extractAddress(string(outputBytes), keyname)
if err != nil {
return "", fmt.Errorf("failed to extract address for %s: %v", keyname, err)
}
return address, nil
}
// OPInitGrantOracle grants oracle permissions to a specific address.
func OPInitGrantOracle(appName, address, opInitHome string) error {
cmd := exec.Command(appName, "tx", "grant-oracle", address, "--home", opInitHome)
if output, err := cmd.CombinedOutput(); err != nil {
outputStr := string(output)
if strings.Contains(outputStr, "fee allowance already exists") {
return nil
}
return fmt.Errorf("failed to grant oracle to address %s: %v (output: %s)", address, err, outputStr)
}
return nil
}
// extractAddress parses the command output to extract the key address.
func extractAddress(output, keyname string) (string, error) {
lines := strings.Split(output, "\n")
for _, line := range lines {
if strings.HasPrefix(line, keyname+":") {
parts := strings.Split(line, ":")
if len(parts) != 2 {
return "", errors.New("invalid format for key address")
}
return strings.TrimSpace(parts[1]), nil
}
}
return "", errors.New("key address not found in output")
}
// OPInitAddOrReplace adds or replaces a key using `opinitd keys add <keyname> --keyring-backend test`
// with 'y' confirmation
func OPInitAddOrReplace(appName, keyname string, isCelestia bool, opInitHome string) (string, error) {
// Check if the key already exists
exists := OPInitKeyExist(appName, keyname, opInitHome)
{
var cmd *exec.Cmd
var inputBuffer bytes.Buffer
if exists {
// Simulate pressing 'y' for confirmation
inputBuffer.WriteString("y\n")
cmd = exec.Command(appName, "keys", "delete", "weave-dummy", keyname, "--home", opInitHome)
// Run the command and capture the output
outputBytes, err := cmd.CombinedOutput()
if err != nil {
return "", fmt.Errorf("failed to delete key for %s: %v, output: %s", keyname, err, string(outputBytes))
}
}
}
var cmd *exec.Cmd
if isCelestia {
cmd = exec.Command(appName, "keys", "add", "weave-dummy", keyname, "--bech32", "celestia", "--home", opInitHome)
} else {
cmd = exec.Command(appName, "keys", "add", "weave-dummy", keyname, "--home", opInitHome)
}
// Simulate pressing 'y' for confirmation
cmd.Stdin = bytes.NewBufferString("y\n")
// Run the command and capture the output
outputBytes, err := cmd.CombinedOutput()
if err != nil {
return "", fmt.Errorf("failed to add key for %s: %v, output: %s", keyname, err, string(outputBytes))
}
return string(outputBytes), nil
}
func GetBinaryVersion(appName string) (string, error) {
var cmd *exec.Cmd
var inputBuffer bytes.Buffer
// Simulate pressing 'y' for confirmation
inputBuffer.WriteString("y\n")
cmd = exec.Command(appName, "version")
// Run the command and capture the output
outputBytes, err := cmd.CombinedOutput()
if err != nil {
return "", fmt.Errorf("failed to get binary version of %s: %v, output: %s", appName, err, string(outputBytes))
}
return strings.Trim(string(outputBytes), "\n"), nil
}
// SetSymlink sets a symbolic link in the parent directory pointing to the target binary.
func SetSymlink(targetPath string) error {
// Resolve an absolute path for clarity
absTargetPath, err := filepath.Abs(targetPath)
if err != nil {
return fmt.Errorf("failed to get absolute path of target: %v", err)
}
// Extract the base name of the target binary to create the symlink name automatically
// Example: if the target is "~/.weave/data/[email protected]/opinitd", the symlink name will be "opinitd".
binaryName := filepath.Base(absTargetPath)
// Define the symlink path in the parent directory of the versioned directory
symlinkPath := filepath.Join(filepath.Dir(filepath.Dir(absTargetPath)), binaryName)
// Check if the symlink or file already exists
if fileInfo, err := os.Lstat(symlinkPath); err == nil {
// If the path exists and is a symlink
if fileInfo.Mode()&os.ModeSymlink != 0 {
existingTarget, err := os.Readlink(symlinkPath)
if err != nil {
return fmt.Errorf("failed to read existing symlink: %v", err)
}
// If the symlink points to a different target, remove it
if existingTarget != absTargetPath {
if err := os.Remove(symlinkPath); err != nil {
return fmt.Errorf("failed to remove existing symlink: %v", err)
}
} else {
return nil
}
} else {
// If the path is not a symlink (file or directory), remove it
if err := os.Remove(symlinkPath); err != nil {
return fmt.Errorf("failed to remove existing file or directory: %v", err)
}
}
} else if !os.IsNotExist(err) {
// If there's an error other than "not exist", return it
return fmt.Errorf("failed to check existing file or directory: %v", err)
}
// Create the symlink
if err := os.Symlink(absTargetPath, symlinkPath); err != nil {
return fmt.Errorf("failed to create symlink: %v", err)
}
return nil
}
func GetHermesRelayerAddress(appName, chainId string) (string, bool) {
cmd := exec.Command(appName, "keys", "list", "--chain", chainId)
var out bytes.Buffer
cmd.Stdout = &out
cmd.Stderr = &out
err := cmd.Run()
if err != nil {
return "", false
}
output := out.String()
lines := strings.Split(output, "\n")
if len(lines) < 2 {
return "", false
}
secondLine := strings.TrimSpace(lines[1])
re := regexp.MustCompile(`weave-relayer' \(([^)]+)\)`)
match := re.FindStringSubmatch(secondLine)
if len(match) != 2 {
return "", false
}
keyName := match[1]
if keyName != "weave-relayer" {
return "", false
}
relayerAddress := match[2]
return relayerAddress, true
}
func DeleteWeaveKeyFromHermes(appName, chainId string) error {
cmd := exec.Command(appName, "keys", "delete", "--chain", chainId, "--key-name", "weave-relayer")
if output, err := cmd.CombinedOutput(); err != nil {
return fmt.Errorf("failed to delete key from hermes for network: %s: %v (output: %s)", chainId, err, string(output))
}
return nil
}
func addNewKeyToHermes(appName, chainId, mnemonic string) (*KeyInfo, error) {
userHome, err := os.UserHomeDir()
if err != nil {
return nil, fmt.Errorf("failed to get user home: %v", err)
}
tempMnemonicPath := filepath.Join(userHome, common.WeaveDataDirectory, common.HermesTempMnemonicFilename)
if err = io.WriteFile(tempMnemonicPath, mnemonic); err != nil {
return nil, fmt.Errorf("failed to write raw tx file: %v", err)
}
defer func() {
if err := io.DeleteFile(tempMnemonicPath); err != nil {
fmt.Printf("failed to delete temp mnemonic file: %v", err)
}
}()
cmd := exec.Command(appName, "keys", "add", "--chain", chainId, "--mnemonic-file", tempMnemonicPath)
var out bytes.Buffer
cmd.Stdout = &out
cmd.Stderr = &out
if err = cmd.Run(); err != nil {
return nil, fmt.Errorf("failed to run hermes keys add: %v (output: %s)", err, out.String())
}
output := out.String()
re := regexp.MustCompile(`weave-relayer' \(([^)]+)\)`)
match := re.FindStringSubmatch(output)
if len(match) < 2 {
return nil, fmt.Errorf("failed to parse address from command output: %s", output)
}
return &KeyInfo{
Address: match[1],
Mnemonic: mnemonic,
}, nil
}
func GenerateAndAddNewHermesKey(appName, chainId string) (*KeyInfo, error) {
mnemonic, err := crypto.GenerateMnemonic()
if err != nil {
return nil, err
}
return addNewKeyToHermes(appName, chainId, mnemonic)
}
func RecoverNewHermesKey(appName, chainId, mnemonic string) (*KeyInfo, error) {
return addNewKeyToHermes(appName, chainId, mnemonic)
}
func GenerateAndReplaceHermesKey(appName, chainId string) (*KeyInfo, error) {
_ = DeleteWeaveKeyFromHermes(appName, chainId)
return GenerateAndAddNewHermesKey(appName, chainId)
}
func RecoverAndReplaceHermesKey(appName, chainId, mnemonic string) (*KeyInfo, error) {
_ = DeleteWeaveKeyFromHermes(appName, chainId)
return RecoverNewHermesKey(appName, chainId, mnemonic)
}