-
Notifications
You must be signed in to change notification settings - Fork 5
/
import.go
180 lines (169 loc) · 6.16 KB
/
import.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
package local
import (
"context"
"encoding/hex"
"fmt"
"strings"
"github.com/k0kubun/go-ansi"
"github.com/pkg/errors"
"github.com/schollz/progressbar/v3"
"github.com/sirupsen/logrus"
keystorev4 "github.com/theQRL/go-zond-wallet-encryptor-keystore"
"github.com/theQRL/qrysm/v4/crypto/dilithium"
zondpbservice "github.com/theQRL/qrysm/v4/proto/zond/service"
"github.com/theQRL/qrysm/v4/validator/keymanager"
)
// ImportKeystores into the local keymanager from an external source.
// 1) Copy the in memory keystore
// 2) Update copied keystore with new keys
// 3) Save the copy to disk
// 4) Reinitialize account store and updating the keymanager
// 5) Return Statuses
func (km *Keymanager) ImportKeystores(
ctx context.Context,
keystores []*keymanager.Keystore,
passwords []string,
) ([]*zondpbservice.ImportedKeystoreStatus, error) {
if len(passwords) == 0 {
return nil, ErrNoPasswords
}
if len(passwords) != len(keystores) {
return nil, ErrMismatchedNumPasswords
}
enc := keystorev4.New()
bar := initializeProgressBar(len(keystores), "Importing accounts...")
keys := map[string]string{}
statuses := make([]*zondpbservice.ImportedKeystoreStatus, len(keystores))
var err error
// 1) Copy the in memory keystore
storeCopy := km.accountsStore.Copy()
importedKeys := make([][]byte, 0)
existingPubKeys := make(map[string]bool)
for i := 0; i < len(storeCopy.Seeds); i++ {
existingPubKeys[string(storeCopy.PublicKeys[i])] = true
}
for i := 0; i < len(keystores); i++ {
var privKeyBytes []byte
var pubKeyBytes []byte
privKeyBytes, pubKeyBytes, _, err = km.attemptDecryptKeystore(enc, keystores[i], passwords[i])
if err != nil {
statuses[i] = &zondpbservice.ImportedKeystoreStatus{
Status: zondpbservice.ImportedKeystoreStatus_ERROR,
Message: err.Error(),
}
continue
}
if err := bar.Add(1); err != nil {
log.Error(err)
}
// if key exists prior to being added then output log that duplicate key was found
_, isDuplicateInArray := keys[string(pubKeyBytes)]
_, isDuplicateInExisting := existingPubKeys[string(pubKeyBytes)]
if isDuplicateInArray || isDuplicateInExisting {
log.Warnf("Duplicate key in import will be ignored: %#x", pubKeyBytes)
statuses[i] = &zondpbservice.ImportedKeystoreStatus{
Status: zondpbservice.ImportedKeystoreStatus_DUPLICATE,
}
continue
}
keys[string(pubKeyBytes)] = string(privKeyBytes)
importedKeys = append(importedKeys, pubKeyBytes)
statuses[i] = &zondpbservice.ImportedKeystoreStatus{
Status: zondpbservice.ImportedKeystoreStatus_IMPORTED,
}
}
if len(importedKeys) == 0 {
log.Warn("no keys were imported")
return statuses, nil
}
// 2) Update copied keystore with new keys,clear duplicates in existing set
// duplicates,errored ones are already skipped
for pubKey, privKey := range keys {
storeCopy.PublicKeys = append(storeCopy.PublicKeys, []byte(pubKey))
storeCopy.Seeds = append(storeCopy.Seeds, []byte(privKey))
}
//3 & 4) save to disk and re-initializes keystore
if err := km.SaveStoreAndReInitialize(ctx, storeCopy); err != nil {
return nil, err
}
log.WithFields(logrus.Fields{
"publicKeys": CreatePrintoutOfKeys(importedKeys),
}).Info("Successfully imported validator key(s)")
// 5) Return Statuses
return statuses, nil
}
// ImportKeypairs directly into the keymanager.
func (km *Keymanager) ImportKeypairs(ctx context.Context, privKeys, pubKeys [][]byte) error {
if len(privKeys) != len(pubKeys) {
return fmt.Errorf(
"number of private keys and public keys is not equal: %d != %d", len(privKeys), len(pubKeys),
)
}
// 1) Copy the in memory keystore
storeCopy := km.accountsStore.Copy()
// 2) Update store and remove duplicates
updateAccountsStoreKeys(storeCopy, privKeys, pubKeys)
// 3 & 4) save to disk and re-initializes keystore
if err := km.SaveStoreAndReInitialize(ctx, storeCopy); err != nil {
return err
}
// 5) verify if store was not updated
if len(km.accountsStore.PublicKeys) < len(storeCopy.PublicKeys) {
return fmt.Errorf("keys were not imported successfully, expected %d got %d", len(storeCopy.PublicKeys), len(km.accountsStore.PublicKeys))
}
return nil
}
// Retrieves the private key and public key from an EIP-2335 keystore file
// by decrypting using a specified password. If the password fails,
// it prompts the user for the correct password until it confirms.
func (_ *Keymanager) attemptDecryptKeystore(
enc *keystorev4.Encryptor, keystore *keymanager.Keystore, password string,
) ([]byte, []byte, string, error) {
// Attempt to decrypt the keystore with the specifies password.
var privKeyBytes []byte
var err error
privKeyBytes, err = enc.Decrypt(keystore.Crypto, password)
doesNotDecrypt := err != nil && strings.Contains(err.Error(), keymanager.IncorrectPasswordErrMsg)
if doesNotDecrypt {
return nil, nil, "", fmt.Errorf(
"incorrect password for key 0x%s",
keystore.Pubkey,
)
}
if err != nil && !strings.Contains(err.Error(), keymanager.IncorrectPasswordErrMsg) {
return nil, nil, "", errors.Wrap(err, "could not decrypt keystore")
}
var pubKeyBytes []byte
// Attempt to use the pubkey present in the keystore itself as a field. If unavailable,
// then utilize the public key directly from the private key.
if keystore.Pubkey != "" {
pubKeyBytes, err = hex.DecodeString(strings.TrimPrefix(keystore.Pubkey, "0x"))
if err != nil {
return nil, nil, "", errors.Wrap(err, "could not decode pubkey from keystore")
}
} else {
privKey, err := dilithium.SecretKeyFromBytes(privKeyBytes)
if err != nil {
return nil, nil, "", errors.Wrap(err, "could not initialize private key from bytes")
}
pubKeyBytes = privKey.PublicKey().Marshal()
}
return privKeyBytes, pubKeyBytes, password, nil
}
func initializeProgressBar(numItems int, msg string) *progressbar.ProgressBar {
return progressbar.NewOptions(
numItems,
progressbar.OptionFullWidth(),
progressbar.OptionSetWriter(ansi.NewAnsiStdout()),
progressbar.OptionEnableColorCodes(true),
progressbar.OptionSetTheme(progressbar.Theme{
Saucer: "[green]=[reset]",
SaucerHead: "[green]>[reset]",
SaucerPadding: " ",
BarStart: "[",
BarEnd: "]",
}),
progressbar.OptionOnCompletion(func() { fmt.Println() }),
progressbar.OptionSetDescription(msg),
)
}