github.com/prysmaticlabs/prysm@v1.4.4/validator/keymanager/imported/refresh.go (about) 1 package imported 2 3 import ( 4 "context" 5 "encoding/json" 6 "io/ioutil" 7 "path/filepath" 8 9 "github.com/fsnotify/fsnotify" 10 "github.com/pkg/errors" 11 "github.com/prysmaticlabs/prysm/shared/asyncutil" 12 "github.com/prysmaticlabs/prysm/shared/bls" 13 "github.com/prysmaticlabs/prysm/shared/bytesutil" 14 "github.com/prysmaticlabs/prysm/shared/featureconfig" 15 "github.com/prysmaticlabs/prysm/shared/fileutil" 16 "github.com/prysmaticlabs/prysm/validator/keymanager" 17 keystorev4 "github.com/wealdtech/go-eth2-wallet-encryptor-keystorev4" 18 ) 19 20 // Listen for changes to the all-accounts.keystore.json file in our wallet 21 // to load in new keys we observe into our keymanager. This uses the fsnotify 22 // library to listen for file-system changes and debounces these events to 23 // ensure we can handle thousands of events fired in a short time-span. 24 func (km *Keymanager) listenForAccountChanges(ctx context.Context) { 25 debounceFileChangesInterval := featureconfig.Get().KeystoreImportDebounceInterval 26 accountsFilePath := filepath.Join(km.wallet.AccountsDir(), AccountsPath, AccountsKeystoreFileName) 27 if !fileutil.FileExists(accountsFilePath) { 28 return 29 } 30 watcher, err := fsnotify.NewWatcher() 31 if err != nil { 32 log.WithError(err).Error("Could not initialize file watcher") 33 return 34 } 35 defer func() { 36 if err := watcher.Close(); err != nil { 37 log.WithError(err).Error("Could not close file watcher") 38 } 39 }() 40 if err := watcher.Add(accountsFilePath); err != nil { 41 log.WithError(err).Errorf("Could not add file %s to file watcher", accountsFilePath) 42 return 43 } 44 ctx, cancel := context.WithCancel(ctx) 45 defer cancel() 46 fileChangesChan := make(chan interface{}, 100) 47 defer close(fileChangesChan) 48 49 // We debounce events sent over the file changes channel by an interval 50 // to ensure we are not overwhelmed by a ton of events fired over the channel in 51 // a short span of time. 52 go asyncutil.Debounce(ctx, debounceFileChangesInterval, fileChangesChan, func(event interface{}) { 53 ev, ok := event.(fsnotify.Event) 54 if !ok { 55 log.Errorf("Type %T is not a valid file system event", event) 56 return 57 } 58 fileBytes, err := ioutil.ReadFile(ev.Name) 59 if err != nil { 60 log.WithError(err).Errorf("Could not read file at path: %s", ev.Name) 61 return 62 } 63 if fileBytes == nil { 64 log.WithError(err).Errorf("Loaded in an empty file: %s", ev.Name) 65 return 66 } 67 accountsKeystore := &AccountsKeystoreRepresentation{} 68 if err := json.Unmarshal(fileBytes, accountsKeystore); err != nil { 69 log.WithError( 70 err, 71 ).Errorf("Could not read valid, EIP-2335 keystore json file at path: %s", ev.Name) 72 return 73 } 74 if err := km.reloadAccountsFromKeystore(accountsKeystore); err != nil { 75 log.WithError( 76 err, 77 ).Error("Could not replace the accounts store from keystore file") 78 } 79 }) 80 for { 81 select { 82 case event := <-watcher.Events: 83 // If a file was modified, we attempt to read that file 84 // and parse it into our accounts store. 85 fileChangesChan <- event 86 case err := <-watcher.Errors: 87 log.WithError(err).Errorf("Could not watch for file changes for: %s", accountsFilePath) 88 case <-ctx.Done(): 89 return 90 } 91 } 92 } 93 94 // Replaces the accounts store struct in the imported keymanager with 95 // the contents of a keystore file by decrypting it with the accounts password. 96 func (km *Keymanager) reloadAccountsFromKeystore(keystore *AccountsKeystoreRepresentation) error { 97 decryptor := keystorev4.New() 98 encodedAccounts, err := decryptor.Decrypt(keystore.Crypto, km.wallet.Password()) 99 if err != nil { 100 return errors.Wrap(err, "could not decrypt keystore file") 101 } 102 newAccountsStore := &accountStore{} 103 if err := json.Unmarshal(encodedAccounts, newAccountsStore); err != nil { 104 return err 105 } 106 if len(newAccountsStore.PublicKeys) != len(newAccountsStore.PrivateKeys) { 107 return errors.New("number of public and private keys in keystore do not match") 108 } 109 pubKeys := make([][48]byte, len(newAccountsStore.PublicKeys)) 110 for i := 0; i < len(newAccountsStore.PrivateKeys); i++ { 111 privKey, err := bls.SecretKeyFromBytes(newAccountsStore.PrivateKeys[i]) 112 if err != nil { 113 return errors.Wrap(err, "could not initialize private key") 114 } 115 pubKeyBytes := privKey.PublicKey().Marshal() 116 pubKeys[i] = bytesutil.ToBytes48(pubKeyBytes) 117 } 118 km.accountsStore = newAccountsStore 119 if err := km.initializeKeysCachesFromKeystore(); err != nil { 120 return err 121 } 122 log.Info(keymanager.KeysReloaded) 123 km.accountsChangedFeed.Send(pubKeys) 124 return nil 125 }