github.com/prysmaticlabs/prysm@v1.4.4/validator/keymanager/imported/keymanager.go (about) 1 package imported 2 3 import ( 4 "bytes" 5 "context" 6 "encoding/json" 7 "fmt" 8 "strings" 9 "sync" 10 11 "github.com/google/uuid" 12 "github.com/pkg/errors" 13 validatorpb "github.com/prysmaticlabs/prysm/proto/validator/accounts/v2" 14 "github.com/prysmaticlabs/prysm/shared/bls" 15 "github.com/prysmaticlabs/prysm/shared/bytesutil" 16 "github.com/prysmaticlabs/prysm/shared/event" 17 "github.com/prysmaticlabs/prysm/shared/interop" 18 "github.com/prysmaticlabs/prysm/shared/petnames" 19 "github.com/prysmaticlabs/prysm/validator/accounts/iface" 20 "github.com/sirupsen/logrus" 21 keystorev4 "github.com/wealdtech/go-eth2-wallet-encryptor-keystorev4" 22 "go.opencensus.io/trace" 23 ) 24 25 var ( 26 lock sync.RWMutex 27 orderedPublicKeys = make([][48]byte, 0) 28 secretKeysCache = make(map[[48]byte]bls.SecretKey) 29 ) 30 31 const ( 32 // KeystoreFileNameFormat exposes the filename the keystore should be formatted in. 33 KeystoreFileNameFormat = "keystore-%d.json" 34 // AccountsPath where all imported keymanager keystores are kept. 35 AccountsPath = "accounts" 36 // AccountsKeystoreFileName exposes the name of the keystore file. 37 AccountsKeystoreFileName = "all-accounts.keystore.json" 38 ) 39 40 // Keymanager implementation for imported keystores utilizing EIP-2335. 41 type Keymanager struct { 42 wallet iface.Wallet 43 accountsStore *accountStore 44 accountsChangedFeed *event.Feed 45 } 46 47 // SetupConfig includes configuration values for initializing 48 // a keymanager, such as passwords, the wallet, and more. 49 type SetupConfig struct { 50 Wallet iface.Wallet 51 ListenForChanges bool 52 } 53 54 // Defines a struct containing 1-to-1 corresponding 55 // private keys and public keys for Ethereum validators. 56 type accountStore struct { 57 PrivateKeys [][]byte `json:"private_keys"` 58 PublicKeys [][]byte `json:"public_keys"` 59 } 60 61 // AccountsKeystoreRepresentation defines an internal Prysm representation 62 // of validator accounts, encrypted according to the EIP-2334 standard. 63 type AccountsKeystoreRepresentation struct { 64 Crypto map[string]interface{} `json:"crypto"` 65 ID string `json:"uuid"` 66 Version uint `json:"version"` 67 Name string `json:"name"` 68 } 69 70 // ResetCaches for the keymanager. 71 func ResetCaches() { 72 lock.Lock() 73 orderedPublicKeys = make([][48]byte, 0) 74 secretKeysCache = make(map[[48]byte]bls.SecretKey) 75 lock.Unlock() 76 } 77 78 // NewKeymanager instantiates a new imported keymanager from configuration options. 79 func NewKeymanager(ctx context.Context, cfg *SetupConfig) (*Keymanager, error) { 80 k := &Keymanager{ 81 wallet: cfg.Wallet, 82 accountsStore: &accountStore{}, 83 accountsChangedFeed: new(event.Feed), 84 } 85 86 if err := k.initializeAccountKeystore(ctx); err != nil { 87 return nil, errors.Wrap(err, "failed to initialize account store") 88 } 89 90 if cfg.ListenForChanges { 91 // We begin a goroutine to listen for file changes to our 92 // all-accounts.keystore.json file in the wallet directory. 93 go k.listenForAccountChanges(ctx) 94 } 95 return k, nil 96 } 97 98 // NewInteropKeymanager instantiates a new imported keymanager with the deterministically generated interop keys. 99 func NewInteropKeymanager(_ context.Context, offset, numValidatorKeys uint64) (*Keymanager, error) { 100 k := &Keymanager{ 101 accountsChangedFeed: new(event.Feed), 102 } 103 if numValidatorKeys == 0 { 104 return k, nil 105 } 106 secretKeys, publicKeys, err := interop.DeterministicallyGenerateKeys(offset, numValidatorKeys) 107 if err != nil { 108 return nil, errors.Wrap(err, "could not generate interop keys") 109 } 110 lock.Lock() 111 pubKeys := make([][48]byte, numValidatorKeys) 112 for i := uint64(0); i < numValidatorKeys; i++ { 113 publicKey := bytesutil.ToBytes48(publicKeys[i].Marshal()) 114 pubKeys[i] = publicKey 115 secretKeysCache[publicKey] = secretKeys[i] 116 } 117 orderedPublicKeys = pubKeys 118 lock.Unlock() 119 return k, nil 120 } 121 122 // SubscribeAccountChanges creates an event subscription for a channel 123 // to listen for public key changes at runtime, such as when new validator accounts 124 // are imported into the keymanager while the validator process is running. 125 func (km *Keymanager) SubscribeAccountChanges(pubKeysChan chan [][48]byte) event.Subscription { 126 return km.accountsChangedFeed.Subscribe(pubKeysChan) 127 } 128 129 // ValidatingAccountNames for a imported keymanager. 130 func (km *Keymanager) ValidatingAccountNames() ([]string, error) { 131 lock.RLock() 132 names := make([]string, len(orderedPublicKeys)) 133 for i, pubKey := range orderedPublicKeys { 134 names[i] = petnames.DeterministicName(bytesutil.FromBytes48(pubKey), "-") 135 } 136 lock.RUnlock() 137 return names, nil 138 } 139 140 // Initialize public and secret key caches that are used to speed up the functions 141 // FetchValidatingPublicKeys and Sign 142 func (km *Keymanager) initializeKeysCachesFromKeystore() error { 143 lock.Lock() 144 defer lock.Unlock() 145 count := len(km.accountsStore.PrivateKeys) 146 orderedPublicKeys = make([][48]byte, count) 147 secretKeysCache = make(map[[48]byte]bls.SecretKey, count) 148 for i, publicKey := range km.accountsStore.PublicKeys { 149 publicKey48 := bytesutil.ToBytes48(publicKey) 150 orderedPublicKeys[i] = publicKey48 151 secretKey, err := bls.SecretKeyFromBytes(km.accountsStore.PrivateKeys[i]) 152 if err != nil { 153 return errors.Wrap(err, "failed to initialize keys caches from account keystore") 154 } 155 secretKeysCache[publicKey48] = secretKey 156 } 157 return nil 158 } 159 160 // DeleteAccounts takes in public keys and removes the accounts entirely. This includes their disk keystore and cached keystore. 161 func (km *Keymanager) DeleteAccounts(ctx context.Context, publicKeys [][]byte) error { 162 for _, publicKey := range publicKeys { 163 var index int 164 var found bool 165 for i, pubKey := range km.accountsStore.PublicKeys { 166 if bytes.Equal(pubKey, publicKey) { 167 index = i 168 found = true 169 break 170 } 171 } 172 if !found { 173 return fmt.Errorf("could not find public key %#x", publicKey) 174 } 175 deletedPublicKey := km.accountsStore.PublicKeys[index] 176 accountName := petnames.DeterministicName(deletedPublicKey, "-") 177 km.accountsStore.PrivateKeys = append(km.accountsStore.PrivateKeys[:index], km.accountsStore.PrivateKeys[index+1:]...) 178 km.accountsStore.PublicKeys = append(km.accountsStore.PublicKeys[:index], km.accountsStore.PublicKeys[index+1:]...) 179 180 newStore, err := km.CreateAccountsKeystore(ctx, km.accountsStore.PrivateKeys, km.accountsStore.PublicKeys) 181 if err != nil { 182 return errors.Wrap(err, "could not rewrite accounts keystore") 183 } 184 185 // Write the encoded keystore. 186 encoded, err := json.MarshalIndent(newStore, "", "\t") 187 if err != nil { 188 return err 189 } 190 if err := km.wallet.WriteFileAtPath(ctx, AccountsPath, AccountsKeystoreFileName, encoded); err != nil { 191 return errors.Wrap(err, "could not write keystore file for accounts") 192 } 193 194 log.WithFields(logrus.Fields{ 195 "name": accountName, 196 "publicKey": fmt.Sprintf("%#x", bytesutil.Trunc(deletedPublicKey)), 197 }).Info("Successfully deleted validator account") 198 err = km.initializeKeysCachesFromKeystore() 199 if err != nil { 200 return errors.Wrap(err, "failed to initialize keys caches") 201 } 202 } 203 return nil 204 } 205 206 // FetchValidatingPublicKeys fetches the list of active public keys from the imported account keystores. 207 func (km *Keymanager) FetchValidatingPublicKeys(ctx context.Context) ([][48]byte, error) { 208 ctx, span := trace.StartSpan(ctx, "keymanager.FetchValidatingPublicKeys") 209 defer span.End() 210 211 lock.RLock() 212 keys := orderedPublicKeys 213 result := make([][48]byte, len(keys)) 214 copy(result, keys) 215 lock.RUnlock() 216 return result, nil 217 } 218 219 // FetchValidatingPrivateKeys fetches the list of private keys from the secret keys cache 220 func (km *Keymanager) FetchValidatingPrivateKeys(ctx context.Context) ([][32]byte, error) { 221 lock.RLock() 222 defer lock.RUnlock() 223 privKeys := make([][32]byte, len(secretKeysCache)) 224 pubKeys, err := km.FetchValidatingPublicKeys(ctx) 225 if err != nil { 226 return nil, errors.Wrap(err, "could not retrieve public keys") 227 } 228 for i, pk := range pubKeys { 229 seckey, ok := secretKeysCache[pk] 230 if !ok { 231 return nil, errors.New("Could not fetch private key") 232 } 233 privKeys[i] = bytesutil.ToBytes32(seckey.Marshal()) 234 } 235 return privKeys, nil 236 } 237 238 // Sign signs a message using a validator key. 239 func (km *Keymanager) Sign(ctx context.Context, req *validatorpb.SignRequest) (bls.Signature, error) { 240 ctx, span := trace.StartSpan(ctx, "keymanager.Sign") 241 defer span.End() 242 243 publicKey := req.PublicKey 244 if publicKey == nil { 245 return nil, errors.New("nil public key in request") 246 } 247 lock.RLock() 248 secretKey, ok := secretKeysCache[bytesutil.ToBytes48(publicKey)] 249 lock.RUnlock() 250 if !ok { 251 return nil, errors.New("no signing key found in keys cache") 252 } 253 return secretKey.Sign(req.SigningRoot), nil 254 } 255 256 func (km *Keymanager) initializeAccountKeystore(ctx context.Context) error { 257 encoded, err := km.wallet.ReadFileAtPath(ctx, AccountsPath, AccountsKeystoreFileName) 258 if err != nil && strings.Contains(err.Error(), "no files found") { 259 // If there are no keys to initialize at all, just exit. 260 return nil 261 } else if err != nil { 262 return errors.Wrapf(err, "could not read keystore file for accounts %s", AccountsKeystoreFileName) 263 } 264 keystoreFile := &AccountsKeystoreRepresentation{} 265 if err := json.Unmarshal(encoded, keystoreFile); err != nil { 266 return errors.Wrapf(err, "could not decode keystore file for accounts %s", AccountsKeystoreFileName) 267 } 268 // We extract the validator signing private key from the keystore 269 // by utilizing the password and initialize a new BLS secret key from 270 // its raw bytes. 271 password := km.wallet.Password() 272 decryptor := keystorev4.New() 273 enc, err := decryptor.Decrypt(keystoreFile.Crypto, password) 274 if err != nil && strings.Contains(err.Error(), "invalid checksum") { 275 return errors.Wrap(err, "wrong password for wallet entered") 276 } else if err != nil { 277 return errors.Wrap(err, "could not decrypt keystore") 278 } 279 280 store := &accountStore{} 281 if err := json.Unmarshal(enc, store); err != nil { 282 return err 283 } 284 if len(store.PublicKeys) != len(store.PrivateKeys) { 285 return errors.New("unequal number of public keys and private keys") 286 } 287 if len(store.PublicKeys) == 0 { 288 return nil 289 } 290 km.accountsStore = store 291 err = km.initializeKeysCachesFromKeystore() 292 if err != nil { 293 return errors.Wrap(err, "failed to initialize keys caches") 294 } 295 return err 296 } 297 298 // CreateAccountsKeystore creates a new keystore holding the provided keys. 299 func (km *Keymanager) CreateAccountsKeystore( 300 _ context.Context, 301 privateKeys, publicKeys [][]byte, 302 ) (*AccountsKeystoreRepresentation, error) { 303 encryptor := keystorev4.New() 304 id, err := uuid.NewRandom() 305 if err != nil { 306 return nil, err 307 } 308 if len(privateKeys) != len(publicKeys) { 309 return nil, fmt.Errorf( 310 "number of private keys and public keys is not equal: %d != %d", len(privateKeys), len(publicKeys), 311 ) 312 } 313 if km.accountsStore == nil { 314 km.accountsStore = &accountStore{ 315 PrivateKeys: privateKeys, 316 PublicKeys: publicKeys, 317 } 318 } else { 319 existingPubKeys := make(map[string]bool) 320 existingPrivKeys := make(map[string]bool) 321 for i := 0; i < len(km.accountsStore.PrivateKeys); i++ { 322 existingPrivKeys[string(km.accountsStore.PrivateKeys[i])] = true 323 existingPubKeys[string(km.accountsStore.PublicKeys[i])] = true 324 } 325 // We append to the accounts store keys only 326 // if the private/secret key do not already exist, to prevent duplicates. 327 for i := 0; i < len(privateKeys); i++ { 328 sk := privateKeys[i] 329 pk := publicKeys[i] 330 _, privKeyExists := existingPrivKeys[string(sk)] 331 _, pubKeyExists := existingPubKeys[string(pk)] 332 if privKeyExists || pubKeyExists { 333 continue 334 } 335 km.accountsStore.PublicKeys = append(km.accountsStore.PublicKeys, pk) 336 km.accountsStore.PrivateKeys = append(km.accountsStore.PrivateKeys, sk) 337 } 338 } 339 err = km.initializeKeysCachesFromKeystore() 340 if err != nil { 341 return nil, errors.Wrap(err, "failed to initialize keys caches") 342 } 343 encodedStore, err := json.MarshalIndent(km.accountsStore, "", "\t") 344 if err != nil { 345 return nil, err 346 } 347 cryptoFields, err := encryptor.Encrypt(encodedStore, km.wallet.Password()) 348 if err != nil { 349 return nil, errors.Wrap(err, "could not encrypt accounts") 350 } 351 return &AccountsKeystoreRepresentation{ 352 Crypto: cryptoFields, 353 ID: id.String(), 354 Version: encryptor.Version(), 355 Name: encryptor.Name(), 356 }, nil 357 }