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 }