github.com/prysmaticlabs/prysm@v1.4.4/validator/keymanager/imported/keymanager_test.go (about) 1 package imported 2 3 import ( 4 "context" 5 "encoding/json" 6 "fmt" 7 "strings" 8 "testing" 9 10 validatorpb "github.com/prysmaticlabs/prysm/proto/validator/accounts/v2" 11 "github.com/prysmaticlabs/prysm/shared/bls" 12 "github.com/prysmaticlabs/prysm/shared/bytesutil" 13 "github.com/prysmaticlabs/prysm/shared/testutil/assert" 14 "github.com/prysmaticlabs/prysm/shared/testutil/require" 15 mock "github.com/prysmaticlabs/prysm/validator/accounts/testing" 16 "github.com/prysmaticlabs/prysm/validator/keymanager" 17 logTest "github.com/sirupsen/logrus/hooks/test" 18 keystorev4 "github.com/wealdtech/go-eth2-wallet-encryptor-keystorev4" 19 ) 20 21 func TestImportedKeymanager_RemoveAccounts(t *testing.T) { 22 hook := logTest.NewGlobal() 23 wallet := &mock.Wallet{ 24 Files: make(map[string]map[string][]byte), 25 WalletPassword: password, 26 } 27 dr := &Keymanager{ 28 wallet: wallet, 29 accountsStore: &accountStore{}, 30 } 31 numAccounts := 5 32 ctx := context.Background() 33 keystores := make([]*keymanager.Keystore, numAccounts) 34 for i := 0; i < numAccounts; i++ { 35 keystores[i] = createRandomKeystore(t, password) 36 } 37 require.NoError(t, dr.ImportKeystores(ctx, keystores, password)) 38 accounts, err := dr.FetchValidatingPublicKeys(ctx) 39 require.NoError(t, err) 40 require.Equal(t, numAccounts, len(accounts)) 41 42 accountToRemove := uint64(2) 43 accountPubKey := accounts[accountToRemove] 44 // Remove an account from the keystore. 45 require.NoError(t, dr.DeleteAccounts(ctx, [][]byte{accountPubKey[:]})) 46 // Ensure the keystore file was written to the wallet 47 // and ensure we can decrypt it using the EIP-2335 standard. 48 var encodedKeystore []byte 49 for k, v := range wallet.Files[AccountsPath] { 50 if strings.Contains(k, "keystore") { 51 encodedKeystore = v 52 } 53 } 54 require.NotNil(t, encodedKeystore, "could not find keystore file") 55 keystoreFile := &keymanager.Keystore{} 56 require.NoError(t, json.Unmarshal(encodedKeystore, keystoreFile)) 57 58 // We extract the accounts from the keystore. 59 decryptor := keystorev4.New() 60 encodedAccounts, err := decryptor.Decrypt(keystoreFile.Crypto, password) 61 require.NoError(t, err, "Could not decrypt validator accounts") 62 store := &accountStore{} 63 require.NoError(t, json.Unmarshal(encodedAccounts, store)) 64 65 require.Equal(t, numAccounts-1, len(store.PublicKeys)) 66 require.Equal(t, numAccounts-1, len(store.PrivateKeys)) 67 require.LogsContain(t, hook, fmt.Sprintf("%#x", bytesutil.Trunc(accountPubKey[:]))) 68 require.LogsContain(t, hook, "Successfully deleted validator account") 69 } 70 71 func TestImportedKeymanager_FetchValidatingPublicKeys(t *testing.T) { 72 wallet := &mock.Wallet{ 73 Files: make(map[string]map[string][]byte), 74 WalletPassword: password, 75 } 76 dr := &Keymanager{ 77 wallet: wallet, 78 accountsStore: &accountStore{}, 79 } 80 // First, generate accounts and their keystore.json files. 81 ctx := context.Background() 82 numAccounts := 10 83 wantedPubKeys := make([][48]byte, 0) 84 for i := 0; i < numAccounts; i++ { 85 privKey, err := bls.RandKey() 86 require.NoError(t, err) 87 pubKey := bytesutil.ToBytes48(privKey.PublicKey().Marshal()) 88 wantedPubKeys = append(wantedPubKeys, pubKey) 89 dr.accountsStore.PublicKeys = append(dr.accountsStore.PublicKeys, pubKey[:]) 90 dr.accountsStore.PrivateKeys = append(dr.accountsStore.PrivateKeys, privKey.Marshal()) 91 } 92 require.NoError(t, dr.initializeKeysCachesFromKeystore()) 93 publicKeys, err := dr.FetchValidatingPublicKeys(ctx) 94 require.NoError(t, err) 95 assert.Equal(t, numAccounts, len(publicKeys)) 96 // FetchValidatingPublicKeys is also used in generating the output of account list 97 // therefore the results must be in the same order as the order in which the accounts were derived 98 for i, key := range wantedPubKeys { 99 assert.Equal(t, key, publicKeys[i]) 100 } 101 } 102 103 func TestImportedKeymanager_FetchValidatingPrivateKeys(t *testing.T) { 104 wallet := &mock.Wallet{ 105 Files: make(map[string]map[string][]byte), 106 WalletPassword: password, 107 } 108 dr := &Keymanager{ 109 wallet: wallet, 110 accountsStore: &accountStore{}, 111 } 112 // First, generate accounts and their keystore.json files. 113 ctx := context.Background() 114 numAccounts := 10 115 wantedPrivateKeys := make([][32]byte, numAccounts) 116 for i := 0; i < numAccounts; i++ { 117 privKey, err := bls.RandKey() 118 require.NoError(t, err) 119 privKeyData := privKey.Marshal() 120 pubKey := bytesutil.ToBytes48(privKey.PublicKey().Marshal()) 121 wantedPrivateKeys[i] = bytesutil.ToBytes32(privKeyData) 122 dr.accountsStore.PublicKeys = append(dr.accountsStore.PublicKeys, pubKey[:]) 123 dr.accountsStore.PrivateKeys = append(dr.accountsStore.PrivateKeys, privKeyData) 124 } 125 require.NoError(t, dr.initializeKeysCachesFromKeystore()) 126 privateKeys, err := dr.FetchValidatingPrivateKeys(ctx) 127 require.NoError(t, err) 128 assert.Equal(t, numAccounts, len(privateKeys)) 129 // FetchValidatingPrivateKeys is also used in generating the output of account list 130 // therefore the results must be in the same order as the order in which the accounts were created 131 for i, key := range wantedPrivateKeys { 132 assert.Equal(t, key, privateKeys[i]) 133 } 134 } 135 136 func TestImportedKeymanager_Sign(t *testing.T) { 137 wallet := &mock.Wallet{ 138 Files: make(map[string]map[string][]byte), 139 AccountPasswords: make(map[string]string), 140 WalletPassword: password, 141 } 142 dr := &Keymanager{ 143 wallet: wallet, 144 accountsStore: &accountStore{}, 145 } 146 147 // First, generate accounts and their keystore.json files. 148 ctx := context.Background() 149 numAccounts := 10 150 keystores := make([]*keymanager.Keystore, numAccounts) 151 for i := 0; i < numAccounts; i++ { 152 keystores[i] = createRandomKeystore(t, password) 153 } 154 require.NoError(t, dr.ImportKeystores(ctx, keystores, password)) 155 156 var encodedKeystore []byte 157 for k, v := range wallet.Files[AccountsPath] { 158 if strings.Contains(k, "keystore") { 159 encodedKeystore = v 160 } 161 } 162 keystoreFile := &keymanager.Keystore{} 163 require.NoError(t, json.Unmarshal(encodedKeystore, keystoreFile)) 164 165 // We extract the validator signing private key from the keystore 166 // by utilizing the password and initialize a new BLS secret key from 167 // its raw bytes. 168 decryptor := keystorev4.New() 169 enc, err := decryptor.Decrypt(keystoreFile.Crypto, dr.wallet.Password()) 170 require.NoError(t, err) 171 store := &accountStore{} 172 require.NoError(t, json.Unmarshal(enc, store)) 173 require.Equal(t, len(store.PublicKeys), len(store.PrivateKeys)) 174 require.NotEqual(t, 0, len(store.PublicKeys)) 175 dr.accountsStore = store 176 require.NoError(t, dr.initializeKeysCachesFromKeystore()) 177 publicKeys, err := dr.FetchValidatingPublicKeys(ctx) 178 require.NoError(t, err) 179 require.Equal(t, len(publicKeys), len(store.PublicKeys)) 180 181 // We prepare naive data to sign. 182 data := []byte("hello world") 183 signRequest := &validatorpb.SignRequest{ 184 PublicKey: publicKeys[0][:], 185 SigningRoot: data, 186 } 187 sig, err := dr.Sign(ctx, signRequest) 188 require.NoError(t, err) 189 pubKey, err := bls.PublicKeyFromBytes(publicKeys[0][:]) 190 require.NoError(t, err) 191 wrongPubKey, err := bls.PublicKeyFromBytes(publicKeys[1][:]) 192 require.NoError(t, err) 193 if !sig.Verify(pubKey, data) { 194 t.Fatalf("Expected sig to verify for pubkey %#x and data %v", pubKey.Marshal(), data) 195 } 196 if sig.Verify(wrongPubKey, data) { 197 t.Fatalf("Expected sig not to verify for pubkey %#x and data %v", wrongPubKey.Marshal(), data) 198 } 199 } 200 201 func TestImportedKeymanager_Sign_NoPublicKeySpecified(t *testing.T) { 202 req := &validatorpb.SignRequest{ 203 PublicKey: nil, 204 } 205 dr := &Keymanager{} 206 _, err := dr.Sign(context.Background(), req) 207 assert.ErrorContains(t, "nil public key", err) 208 } 209 210 func TestImportedKeymanager_Sign_NoPublicKeyInCache(t *testing.T) { 211 req := &validatorpb.SignRequest{ 212 PublicKey: []byte("hello world"), 213 } 214 secretKeysCache = make(map[[48]byte]bls.SecretKey) 215 dr := &Keymanager{} 216 _, err := dr.Sign(context.Background(), req) 217 assert.ErrorContains(t, "no signing key found in keys cache", err) 218 }