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  }