package local import ( "context" "encoding/json" "fmt" "strings" "testing" fieldparams "github.com/prysmaticlabs/prysm/config/fieldparams" "github.com/prysmaticlabs/prysm/encoding/bytesutil" ethpbservice "github.com/prysmaticlabs/prysm/proto/eth/service" "github.com/prysmaticlabs/prysm/testing/require" mock "github.com/prysmaticlabs/prysm/validator/accounts/testing" "github.com/prysmaticlabs/prysm/validator/keymanager" logTest "github.com/sirupsen/logrus/hooks/test" keystorev4 "github.com/wealdtech/go-eth2-wallet-encryptor-keystorev4" ) func TestLocalKeymanager_DeleteKeystores(t *testing.T) { hook := logTest.NewGlobal() wallet := &mock.Wallet{ Files: make(map[string]map[string][]byte), WalletPassword: password, } dr := &Keymanager{ wallet: wallet, accountsStore: &accountStore{}, } numAccounts := 5 ctx := context.Background() keystores := make([]*keymanager.Keystore, numAccounts) passwords := make([]string, numAccounts) for i := 0; i < numAccounts; i++ { keystores[i] = createRandomKeystore(t, password) passwords[i] = password } _, err := dr.ImportKeystores(ctx, keystores, passwords) require.NoError(t, err) accounts, err := dr.FetchValidatingPublicKeys(ctx) require.NoError(t, err) require.Equal(t, numAccounts, len(accounts)) t.Run("keys not found", func(t *testing.T) { notFoundPubKey := [fieldparams.BLSPubkeyLength]byte{1, 2, 3} notFoundPubKey2 := [fieldparams.BLSPubkeyLength]byte{4, 5, 6} statuses, err := dr.DeleteKeystores(ctx, [][]byte{notFoundPubKey[:], notFoundPubKey2[:]}) require.NoError(t, err) require.Equal(t, 2, len(statuses)) require.Equal(t, ethpbservice.DeletedKeystoreStatus_NOT_FOUND, statuses[0].Status) require.Equal(t, ethpbservice.DeletedKeystoreStatus_NOT_FOUND, statuses[1].Status) }) t.Run("deletes properly", func(t *testing.T) { accountToRemove := uint64(2) accountPubKey := accounts[accountToRemove] statuses, err := dr.DeleteKeystores(ctx, [][]byte{accountPubKey[:]}) require.NoError(t, err) require.Equal(t, 1, len(statuses)) require.Equal(t, ethpbservice.DeletedKeystoreStatus_DELETED, statuses[0].Status) // Ensure the keystore file was written to the wallet // and ensure we can decrypt it using the EIP-2335 standard. var encodedKeystore []byte for k, v := range wallet.Files[AccountsPath] { if strings.Contains(k, "keystore") { encodedKeystore = v } } require.NotNil(t, encodedKeystore, "could not find keystore file") keystoreFile := &keymanager.Keystore{} require.NoError(t, json.Unmarshal(encodedKeystore, keystoreFile)) // We extract the accounts from the keystore. decryptor := keystorev4.New() encodedAccounts, err := decryptor.Decrypt(keystoreFile.Crypto, password) require.NoError(t, err, "Could not decrypt validator accounts") store := &accountStore{} require.NoError(t, json.Unmarshal(encodedAccounts, store)) require.Equal(t, numAccounts-1, len(store.PublicKeys)) require.Equal(t, numAccounts-1, len(store.PrivateKeys)) require.LogsContain(t, hook, fmt.Sprintf("%#x", bytesutil.Trunc(accountPubKey[:]))) require.LogsContain(t, hook, "Successfully deleted validator key(s)") }) t.Run("returns NOT_ACTIVE status for duplicate public key in request", func(t *testing.T) { accountToRemove := uint64(3) accountPubKey := accounts[accountToRemove] statuses, err := dr.DeleteKeystores(ctx, [][]byte{ accountPubKey[:], accountPubKey[:], // Add in the same key a few more times. accountPubKey[:], accountPubKey[:], }) require.NoError(t, err) require.Equal(t, 4, len(statuses)) for i, st := range statuses { if i == 0 { require.Equal(t, ethpbservice.DeletedKeystoreStatus_DELETED, st.Status) } else { require.Equal(t, ethpbservice.DeletedKeystoreStatus_NOT_ACTIVE, st.Status) } } // Ensure the keystore file was written to the wallet // and ensure we can decrypt it using the EIP-2335 standard. var encodedKeystore []byte for k, v := range wallet.Files[AccountsPath] { if strings.Contains(k, "keystore") { encodedKeystore = v } } require.NotNil(t, encodedKeystore, "could not find keystore file") keystoreFile := &keymanager.Keystore{} require.NoError(t, json.Unmarshal(encodedKeystore, keystoreFile)) // We extract the accounts from the keystore. decryptor := keystorev4.New() encodedAccounts, err := decryptor.Decrypt(keystoreFile.Crypto, password) require.NoError(t, err, "Could not decrypt validator accounts") store := &accountStore{} require.NoError(t, json.Unmarshal(encodedAccounts, store)) require.Equal(t, numAccounts-2, len(store.PublicKeys)) require.Equal(t, numAccounts-2, len(store.PrivateKeys)) require.LogsContain(t, hook, fmt.Sprintf("%#x", bytesutil.Trunc(accountPubKey[:]))) require.LogsContain(t, hook, "Successfully deleted validator key(s)") }) }