github.com/prysmaticlabs/prysm@v1.4.4/validator/keymanager/derived/keymanager_test.go (about)

     1  package derived
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"testing"
     7  
     8  	validatorpb "github.com/prysmaticlabs/prysm/proto/validator/accounts/v2"
     9  	"github.com/prysmaticlabs/prysm/shared/bls"
    10  	"github.com/prysmaticlabs/prysm/shared/rand"
    11  	"github.com/prysmaticlabs/prysm/shared/testutil/assert"
    12  	"github.com/prysmaticlabs/prysm/shared/testutil/require"
    13  	mock "github.com/prysmaticlabs/prysm/validator/accounts/testing"
    14  	constant "github.com/prysmaticlabs/prysm/validator/testing"
    15  	"github.com/tyler-smith/go-bip39"
    16  	util "github.com/wealdtech/go-eth2-util"
    17  )
    18  
    19  const (
    20  	password = "secretPassw0rd$1999"
    21  )
    22  
    23  // We test that using a '25th word' mnemonic passphrase leads to different
    24  // public keys derived than not specifying the passphrase.
    25  func TestDerivedKeymanager_MnemnonicPassphrase_DifferentResults(t *testing.T) {
    26  	ctx := context.Background()
    27  	wallet := &mock.Wallet{
    28  		Files:            make(map[string]map[string][]byte),
    29  		AccountPasswords: make(map[string]string),
    30  		WalletPassword:   password,
    31  	}
    32  	km, err := NewKeymanager(ctx, &SetupConfig{
    33  		Wallet:           wallet,
    34  		ListenForChanges: false,
    35  	})
    36  	require.NoError(t, err)
    37  	numAccounts := 5
    38  	err = km.RecoverAccountsFromMnemonic(ctx, constant.TestMnemonic, "mnemonicpass", numAccounts)
    39  	require.NoError(t, err)
    40  	without25thWord, err := km.FetchValidatingPublicKeys(ctx)
    41  	require.NoError(t, err)
    42  	wallet = &mock.Wallet{
    43  		Files:            make(map[string]map[string][]byte),
    44  		AccountPasswords: make(map[string]string),
    45  		WalletPassword:   password,
    46  	}
    47  	km, err = NewKeymanager(ctx, &SetupConfig{
    48  		Wallet:           wallet,
    49  		ListenForChanges: false,
    50  	})
    51  	require.NoError(t, err)
    52  	// No mnemonic passphrase this time.
    53  	err = km.RecoverAccountsFromMnemonic(ctx, constant.TestMnemonic, "", numAccounts)
    54  	require.NoError(t, err)
    55  	with25thWord, err := km.FetchValidatingPublicKeys(ctx)
    56  	require.NoError(t, err)
    57  	for i, k := range with25thWord {
    58  		without := without25thWord[i]
    59  		assert.DeepNotEqual(t, k, without)
    60  	}
    61  }
    62  
    63  func TestDerivedKeymanager_RecoverSeedRoundTrip(t *testing.T) {
    64  	mnemonicEntropy := make([]byte, 32)
    65  	n, err := rand.NewGenerator().Read(mnemonicEntropy)
    66  	require.NoError(t, err)
    67  	require.Equal(t, n, len(mnemonicEntropy))
    68  	mnemonic, err := bip39.NewMnemonic(mnemonicEntropy)
    69  	require.NoError(t, err)
    70  	wanted := bip39.NewSeed(mnemonic, "")
    71  
    72  	got, err := seedFromMnemonic(mnemonic, "" /* no passphrase */)
    73  	require.NoError(t, err)
    74  	// Ensure the derived seed matches.
    75  	assert.DeepEqual(t, wanted, got)
    76  }
    77  
    78  func TestDerivedKeymanager_FetchValidatingPublicKeys(t *testing.T) {
    79  	derivedSeed, err := seedFromMnemonic(constant.TestMnemonic, "")
    80  	require.NoError(t, err)
    81  	wallet := &mock.Wallet{
    82  		Files:            make(map[string]map[string][]byte),
    83  		AccountPasswords: make(map[string]string),
    84  		WalletPassword:   password,
    85  	}
    86  	ctx := context.Background()
    87  	dr, err := NewKeymanager(ctx, &SetupConfig{
    88  		Wallet:           wallet,
    89  		ListenForChanges: false,
    90  	})
    91  	require.NoError(t, err)
    92  	numAccounts := 5
    93  	err = dr.RecoverAccountsFromMnemonic(ctx, constant.TestMnemonic, "", numAccounts)
    94  	require.NoError(t, err)
    95  
    96  	// Fetch the public keys.
    97  	publicKeys, err := dr.FetchValidatingPublicKeys(ctx)
    98  	require.NoError(t, err)
    99  	require.Equal(t, numAccounts, len(publicKeys))
   100  
   101  	wantedPubKeys := make([][48]byte, numAccounts)
   102  	for i := 0; i < numAccounts; i++ {
   103  		privKey, err := util.PrivateKeyFromSeedAndPath(derivedSeed, fmt.Sprintf(ValidatingKeyDerivationPathTemplate, i))
   104  		require.NoError(t, err)
   105  		pubKey := [48]byte{}
   106  		copy(pubKey[:], privKey.PublicKey().Marshal())
   107  		wantedPubKeys[i] = pubKey
   108  	}
   109  
   110  	// FetchValidatingPublicKeys is also used in generating the output of account list
   111  	// therefore the results must be in the same order as the order in which the accounts were derived
   112  	for i, key := range wantedPubKeys {
   113  		assert.Equal(t, key, publicKeys[i])
   114  	}
   115  }
   116  
   117  func TestDerivedKeymanager_FetchValidatingPrivateKeys(t *testing.T) {
   118  	derivedSeed, err := seedFromMnemonic(constant.TestMnemonic, "")
   119  	require.NoError(t, err)
   120  	wallet := &mock.Wallet{
   121  		Files:            make(map[string]map[string][]byte),
   122  		AccountPasswords: make(map[string]string),
   123  		WalletPassword:   password,
   124  	}
   125  	ctx := context.Background()
   126  	dr, err := NewKeymanager(ctx, &SetupConfig{
   127  		Wallet:           wallet,
   128  		ListenForChanges: false,
   129  	})
   130  	require.NoError(t, err)
   131  	numAccounts := 5
   132  	err = dr.RecoverAccountsFromMnemonic(ctx, constant.TestMnemonic, "", numAccounts)
   133  	require.NoError(t, err)
   134  
   135  	// Fetch the private keys.
   136  	privateKeys, err := dr.FetchValidatingPrivateKeys(ctx)
   137  	require.NoError(t, err)
   138  	require.Equal(t, numAccounts, len(privateKeys))
   139  
   140  	wantedPrivKeys := make([][32]byte, numAccounts)
   141  	for i := 0; i < numAccounts; i++ {
   142  		privKey, err := util.PrivateKeyFromSeedAndPath(derivedSeed, fmt.Sprintf(ValidatingKeyDerivationPathTemplate, i))
   143  		require.NoError(t, err)
   144  		privKeyBytes := [32]byte{}
   145  		copy(privKeyBytes[:], privKey.Marshal())
   146  		wantedPrivKeys[i] = privKeyBytes
   147  	}
   148  
   149  	// FetchValidatingPrivateKeys is also used in generating the output of account list
   150  	// therefore the results must be in the same order as the order in which the accounts were derived
   151  	for i, key := range wantedPrivKeys {
   152  		assert.Equal(t, key, privateKeys[i])
   153  	}
   154  }
   155  
   156  func TestDerivedKeymanager_Sign(t *testing.T) {
   157  	wallet := &mock.Wallet{
   158  		Files:            make(map[string]map[string][]byte),
   159  		AccountPasswords: make(map[string]string),
   160  		WalletPassword:   password,
   161  	}
   162  	ctx := context.Background()
   163  	dr, err := NewKeymanager(ctx, &SetupConfig{
   164  		Wallet:           wallet,
   165  		ListenForChanges: false,
   166  	})
   167  	require.NoError(t, err)
   168  	numAccounts := 5
   169  	err = dr.RecoverAccountsFromMnemonic(ctx, constant.TestMnemonic, "", numAccounts)
   170  	require.NoError(t, err)
   171  
   172  	pubKeys, err := dr.FetchValidatingPublicKeys(ctx)
   173  	require.NoError(t, err)
   174  	// We prepare naive data to sign.
   175  	data := []byte("eth2data")
   176  	signRequest := &validatorpb.SignRequest{
   177  		PublicKey:   pubKeys[0][:],
   178  		SigningRoot: data,
   179  	}
   180  	sig, err := dr.Sign(ctx, signRequest)
   181  	require.NoError(t, err)
   182  	pubKey, err := bls.PublicKeyFromBytes(pubKeys[0][:])
   183  	require.NoError(t, err)
   184  	wrongPubKey, err := bls.PublicKeyFromBytes(pubKeys[1][:])
   185  	require.NoError(t, err)
   186  
   187  	// Check if the signature verifies.
   188  	assert.Equal(t, true, sig.Verify(pubKey, data))
   189  	// Check if the bad signature fails.
   190  	assert.Equal(t, false, sig.Verify(wrongPubKey, data))
   191  }
   192  
   193  func TestDerivedKeymanager_Sign_NoPublicKeySpecified(t *testing.T) {
   194  	req := &validatorpb.SignRequest{
   195  		PublicKey: nil,
   196  	}
   197  	dr := &Keymanager{}
   198  	_, err := dr.Sign(context.Background(), req)
   199  	assert.ErrorContains(t, "nil public key", err)
   200  }
   201  
   202  func TestDerivedKeymanager_Sign_NoPublicKeyInCache(t *testing.T) {
   203  	req := &validatorpb.SignRequest{
   204  		PublicKey: []byte("hello world"),
   205  	}
   206  	dr := &Keymanager{}
   207  	_, err := dr.Sign(context.Background(), req)
   208  	assert.ErrorContains(t, "no signing key found", err)
   209  }