github.com/iotexproject/iotex-core@v1.14.1-rc1/ioctl/newcmd/account/account_test.go (about) 1 // Copyright (c) 2019 IoTeX Foundation 2 // This source code is provided 'as is' and no warranties are given as to title or non-infringement, merchantability 3 // or fitness for purpose and, to the extent permitted by law, all liability for your use of the code is disclaimed. 4 // This source code is governed by Apache License 2.0 that can be found in the LICENSE file. 5 6 package account 7 8 import ( 9 "bytes" 10 "crypto/ecdsa" 11 "fmt" 12 "math/rand" 13 "os" 14 "strconv" 15 "testing" 16 17 "github.com/ethereum/go-ethereum/accounts/keystore" 18 "github.com/golang/mock/gomock" 19 "github.com/iotexproject/go-pkgs/crypto" 20 "github.com/iotexproject/go-pkgs/hash" 21 "github.com/iotexproject/iotex-address/address" 22 "github.com/iotexproject/iotex-proto/golang/iotexapi" 23 "github.com/iotexproject/iotex-proto/golang/iotexapi/mock_iotexapi" 24 "github.com/iotexproject/iotex-proto/golang/iotextypes" 25 "github.com/pkg/errors" 26 "github.com/spf13/cobra" 27 "github.com/stretchr/testify/require" 28 29 "github.com/iotexproject/iotex-core/ioctl/config" 30 "github.com/iotexproject/iotex-core/ioctl/util" 31 "github.com/iotexproject/iotex-core/test/identityset" 32 "github.com/iotexproject/iotex-core/test/mock/mock_ioctlclient" 33 ) 34 35 const ( 36 _testPath = "testNewAccount" 37 veryLightScryptN = 2 38 veryLightScryptP = 1 39 ) 40 41 func TestNewAccountCmd(t *testing.T) { 42 require := require.New(t) 43 ctrl := gomock.NewController(t) 44 defer ctrl.Finish() 45 client := mock_ioctlclient.NewMockClient(ctrl) 46 client.EXPECT().SelectTranslation(gomock.Any()).Return("mockTranslationString", config.English).AnyTimes() 47 48 testData := []struct { 49 endpoint string 50 insecure bool 51 }{ 52 { 53 endpoint: "111:222:333:444:5678", 54 insecure: false, 55 }, 56 { 57 endpoint: "", 58 insecure: true, 59 }, 60 } 61 for _, test := range testData { 62 callbackEndpoint := func(cb func(*string, string, string, string)) { 63 cb(&test.endpoint, "endpoint", test.endpoint, "endpoint usage") 64 } 65 callbackInsecure := func(cb func(*bool, string, bool, string)) { 66 cb(&test.insecure, "insecure", !test.insecure, "insecure usage") 67 } 68 client.EXPECT().SetEndpointWithFlag(gomock.Any()).Do(callbackEndpoint) 69 client.EXPECT().SetInsecureWithFlag(gomock.Any()).Do(callbackInsecure) 70 71 cmd := NewAccountCmd(client) 72 result, err := util.ExecuteCmd(cmd) 73 require.NoError(err) 74 require.Contains(result, "Available Commands") 75 76 result, err = util.ExecuteCmd(cmd, "--endpoint", "0.0.0.0:1", "--insecure") 77 require.NoError(err) 78 require.Contains(result, "Available Commands") 79 require.Equal("0.0.0.0:1", test.endpoint) 80 require.True(test.insecure) 81 } 82 } 83 84 func TestSign(t *testing.T) { 85 require := require.New(t) 86 _, ks, passwd, _, err := newTestAccountWithKeyStore(t, keystore.StandardScryptN, keystore.StandardScryptP) 87 require.NoError(err) 88 89 ctrl := gomock.NewController(t) 90 defer ctrl.Finish() 91 client := mock_ioctlclient.NewMockClient(ctrl) 92 client.EXPECT().NewKeyStore().Return(ks).Times(15) 93 client.EXPECT().IsCryptoSm2().Return(false).Times(15) 94 95 account, err := ks.NewAccount(passwd) 96 require.NoError(err) 97 addr, err := address.FromBytes(account.Address.Bytes()) 98 require.NoError(err) 99 require.True(IsSignerExist(client, addr.String())) 100 cmd := &cobra.Command{} 101 cmd.SetOut(new(bytes.Buffer)) 102 client.EXPECT().Address(gomock.Any()).Return(addr.String(), nil).Times(7) 103 104 result, err := Sign(client, cmd, addr.String(), passwd, "abcd") 105 require.NoError(err) 106 require.NotEmpty(result) 107 108 result, err = Sign(client, cmd, addr.String(), passwd, "0xe3a1") 109 require.NoError(err) 110 require.NotEmpty(result) 111 112 // wrong message 113 _, err = Sign(client, cmd, addr.String(), passwd, "abc") 114 require.Error(err) 115 require.Contains(err.Error(), "odd length hex string") 116 117 // invalid singer 118 _, err = Sign(client, cmd, "hdw::aaaa", passwd, "0xe3a1") 119 require.Error(err) 120 require.Contains(err.Error(), "invalid HDWallet key format") 121 122 // wrong password 123 _, err = Sign(client, cmd, addr.String(), "123456", "abcd") 124 require.Error(err) 125 require.Contains(err.Error(), "could not decrypt key with given password") 126 127 // invalid signer 128 _, err = Sign(client, cmd, "bace9b2435db45b119e1570b4ea9c57993b2311e0c408d743d87cd22838ae892", "123456", "test") 129 require.Error(err) 130 require.Contains(err.Error(), "invalid address") 131 132 prvKey, err := PrivateKeyFromSigner(client, cmd, addr.String(), passwd) 133 require.NoError(err) 134 require.Equal(addr.String(), prvKey.PublicKey().Address().String()) 135 136 // wrong password 137 prvKey, err = PrivateKeyFromSigner(client, cmd, addr.String(), "123456") 138 require.Error(err) 139 require.Contains(err.Error(), "could not decrypt key with given password") 140 require.Nil(prvKey) 141 142 // empty password 143 client.EXPECT().ReadSecret().Return(passwd, nil) 144 prvKey, err = PrivateKeyFromSigner(client, cmd, addr.String(), "") 145 require.NoError(err) 146 require.Equal(addr.String(), prvKey.PublicKey().Address().String()) 147 } 148 149 func TestAccount(t *testing.T) { 150 require := require.New(t) 151 testWallet, ks, passwd, nonce, err := newTestAccountWithKeyStore(t, veryLightScryptN, veryLightScryptP) 152 require.NoError(err) 153 154 ctrl := gomock.NewController(t) 155 defer ctrl.Finish() 156 client := mock_ioctlclient.NewMockClient(ctrl) 157 158 t.Run("CryptoSm2 is false", func(t *testing.T) { 159 client.EXPECT().IsCryptoSm2().Return(false).Times(2) 160 client.EXPECT().NewKeyStore().Return(ks).Times(2) 161 162 // test new account by ks 163 account, err := ks.NewAccount(passwd) 164 require.NoError(err) 165 addr, err := address.FromBytes(account.Address.Bytes()) 166 require.NoError(err) 167 require.True(IsSignerExist(client, addr.String())) 168 client.EXPECT().Address(gomock.Any()).Return(addr.String(), nil) 169 170 // test keystore conversion and signing 171 prvKey, err := keyStoreAccountToPrivateKey(client, addr.String(), passwd) 172 require.NoError(err) 173 msg := hash.Hash256b([]byte(nonce)) 174 sig, err := prvKey.Sign(msg[:]) 175 require.NoError(err) 176 require.True(prvKey.PublicKey().Verify(msg[:], sig)) 177 178 // test import existing key 179 sk, err := crypto.GenerateKey() 180 require.NoError(err) 181 p256k1, ok := sk.EcdsaPrivateKey().(*ecdsa.PrivateKey) 182 require.True(ok) 183 account, err = ks.ImportECDSA(p256k1, passwd) 184 require.NoError(err) 185 require.Equal(sk.PublicKey().Hash(), account.Address.Bytes()) 186 }) 187 188 t.Run("CryptoSm2 is true", func(t *testing.T) { 189 client.EXPECT().IsCryptoSm2().Return(true).Times(4) 190 client.EXPECT().Config().Return(config.Config{Wallet: testWallet}).Times(8) 191 192 // test store unexisted key 193 account2, err := crypto.GenerateKeySm2() 194 require.NoError(err) 195 require.NotNil(account2) 196 addr2 := account2.PublicKey().Address() 197 require.NotNil(addr2) 198 require.False(IsSignerExist(client, addr2.String())) 199 client.EXPECT().Address(gomock.Any()).Return(addr2.String(), nil).Times(2) 200 _, err = keyStoreAccountToPrivateKey(client, addr2.String(), passwd) 201 require.Contains(err.Error(), "does not match all local keys") 202 filePath := sm2KeyPath(client, addr2) 203 addrString, err := storeKey(client, account2.HexString(), passwd) 204 require.NoError(err) 205 require.Equal(addr2.String(), addrString) 206 require.True(IsSignerExist(client, addr2.String())) 207 208 // test findSm2PemFile 209 path, err := findSm2PemFile(client, addr2) 210 require.NoError(err) 211 require.Equal(filePath, path) 212 213 // test listSm2Account 214 accounts, err := listSm2Account(client) 215 require.NoError(err) 216 require.Equal(1, len(accounts)) 217 require.Equal(addr2.String(), accounts[0]) 218 219 // test keyStoreAccountToPrivateKey 220 prvKey2, err := keyStoreAccountToPrivateKey(client, addr2.String(), passwd) 221 require.NoError(err) 222 msg2 := hash.Hash256b([]byte(nonce)) 223 sig2, err := prvKey2.Sign(msg2[:]) 224 require.NoError(err) 225 require.True(prvKey2.PublicKey().Verify(msg2[:], sig2)) 226 }) 227 } 228 229 func TestMeta(t *testing.T) { 230 require := require.New(t) 231 ctrl := gomock.NewController(t) 232 defer ctrl.Finish() 233 client := mock_ioctlclient.NewMockClient(ctrl) 234 client.EXPECT().Config().Return(config.Config{}).AnyTimes() 235 236 apiServiceClient := mock_iotexapi.NewMockAPIServiceClient(ctrl) 237 client.EXPECT().APIServiceClient().Return(apiServiceClient, nil) 238 239 accAddr := identityset.Address(28).String() 240 accountResponse := &iotexapi.GetAccountResponse{AccountMeta: &iotextypes.AccountMeta{ 241 Address: accAddr, 242 PendingNonce: 2, 243 }} 244 apiServiceClient.EXPECT().GetAccount(gomock.Any(), gomock.Any()).Return(accountResponse, nil) 245 result, err := Meta(client, accAddr) 246 require.NoError(err) 247 require.Equal(accountResponse.AccountMeta, result) 248 249 expectedErr := errors.New("failed to dial grpc connection") 250 client.EXPECT().APIServiceClient().Return(nil, expectedErr) 251 result, err = Meta(client, accAddr) 252 require.Error(err) 253 require.Equal(expectedErr, err) 254 require.Nil(result) 255 256 expectedErr = errors.New("failed to invoke GetAccount api") 257 client.EXPECT().APIServiceClient().Return(apiServiceClient, nil) 258 apiServiceClient.EXPECT().GetAccount(gomock.Any(), gomock.Any()).Return(nil, expectedErr) 259 result, err = Meta(client, accAddr) 260 require.Error(err) 261 require.Contains(err.Error(), expectedErr.Error()) 262 require.Nil(result) 263 } 264 265 func TestAccountError(t *testing.T) { 266 require := require.New(t) 267 testFilePath := t.TempDir() 268 alias := "aaa" 269 passwordOfKeyStore := "123456" 270 keyStorePath := testFilePath 271 272 ctrl := gomock.NewController(t) 273 defer ctrl.Finish() 274 client := mock_ioctlclient.NewMockClient(ctrl) 275 testWallet := t.TempDir() 276 277 client.EXPECT().DecryptPrivateKey(gomock.Any(), gomock.Any()).DoAndReturn( 278 func(passwordOfKeyStore, keyStorePath string) (*ecdsa.PrivateKey, error) { 279 _, err := os.ReadFile(keyStorePath) 280 require.Error(err) 281 return nil, fmt.Errorf("keystore file \"%s\" read error", keyStorePath) 282 }) 283 cmd := &cobra.Command{} 284 cmd.SetOut(new(bytes.Buffer)) 285 _, err := newAccountByKeyStore(client, cmd, alias, passwordOfKeyStore, keyStorePath) 286 require.Error(err) 287 require.Contains(err.Error(), fmt.Sprintf("keystore file \"%s\" read error", keyStorePath)) 288 289 asswordOfPem := "abc1234" 290 pemFilePath := testFilePath 291 _, err = newAccountByPem(client, cmd, alias, asswordOfPem, pemFilePath) 292 require.Error(err) 293 require.Contains(err.Error(), "failed to read private key from pem file") 294 295 addr2, err := address.FromString("io1aqazxjx4d6useyhdsq02ah5effg6293wumtldh") 296 require.NoError(err) 297 client.EXPECT().Config().Return(config.Config{Wallet: testWallet}).Times(1) 298 path, err := findSm2PemFile(client, addr2) 299 require.Error(err) 300 require.Contains(err.Error(), "crypto file not found") 301 require.Equal("", path) 302 303 client.EXPECT().Config().Return(config.Config{Wallet: ""}).Times(1) 304 accounts, err := listSm2Account(client) 305 require.Error(err) 306 require.Contains(err.Error(), "failed to read files in wallet") 307 require.Equal(0, len(accounts)) 308 } 309 310 func TestStoreKey(t *testing.T) { 311 require := require.New(t) 312 testWallet, ks, passwd, _, err := newTestAccountWithKeyStore(t, veryLightScryptN, veryLightScryptP) 313 require.NoError(err) 314 315 ctrl := gomock.NewController(t) 316 defer ctrl.Finish() 317 client := mock_ioctlclient.NewMockClient(ctrl) 318 319 t.Run("CryptoSm2 is false", func(t *testing.T) { 320 client.EXPECT().IsCryptoSm2().Return(false).Times(4) 321 client.EXPECT().NewKeyStore().Return(ks).Times(6) 322 323 account, err := ks.NewAccount(passwd) 324 require.NoError(err) 325 addr, err := address.FromBytes(account.Address.Bytes()) 326 require.NoError(err) 327 require.True(IsSignerExist(client, addr.String())) 328 329 // invalid private key 330 addrString, err := storeKey(client, account.Address.String(), passwd) 331 require.Error(err) 332 require.Contains(err.Error(), "failed to generate private key from hex string") 333 require.Equal("", addrString) 334 335 // valid private key 336 client.EXPECT().Address(gomock.Any()).Return(addr.String(), nil) 337 prvKey, err := keyStoreAccountToPrivateKey(client, addr.String(), passwd) 338 require.NoError(err) 339 // import the existed account addr 340 addrString, err = storeKey(client, prvKey.HexString(), passwd) 341 require.Error(err) 342 require.Contains(err.Error(), "failed to import private key into keystore") 343 require.Equal("", addrString) 344 345 // import the unexisted account addr 346 prvKey, err = crypto.GenerateKey() 347 require.NoError(err) 348 addr = prvKey.PublicKey().Address() 349 require.NotNil(addr) 350 require.False(IsSignerExist(client, addr.String())) 351 addrString, err = storeKey(client, prvKey.HexString(), passwd) 352 require.NoError(err) 353 require.Equal(addr.String(), addrString) 354 require.True(IsSignerExist(client, addr.String())) 355 }) 356 357 t.Run("CryptoSm2 is true", func(t *testing.T) { 358 client.EXPECT().IsCryptoSm2().Return(true).Times(2) 359 client.EXPECT().Config().Return(config.Config{Wallet: testWallet}).Times(4) 360 361 priKey2, err := crypto.GenerateKeySm2() 362 require.NoError(err) 363 addr2 := priKey2.PublicKey().Address() 364 require.NotNil(addr2) 365 require.False(IsSignerExist(client, addr2.String())) 366 367 pemFilePath := sm2KeyPath(client, addr2) 368 require.NoError(crypto.WritePrivateKeyToPem(pemFilePath, priKey2.(*crypto.P256sm2PrvKey), passwd)) 369 require.True(IsSignerExist(client, addr2.String())) 370 371 addrString2, err := storeKey(client, priKey2.HexString(), passwd) 372 require.NoError(err) 373 require.Equal(addr2.String(), addrString2) 374 }) 375 } 376 377 func TestNewAccount(t *testing.T) { 378 require := require.New(t) 379 _, ks, passwd, _, err := newTestAccountWithKeyStore(t, veryLightScryptN, veryLightScryptP) 380 require.NoError(err) 381 382 ctrl := gomock.NewController(t) 383 defer ctrl.Finish() 384 client := mock_ioctlclient.NewMockClient(ctrl) 385 client.EXPECT().ReadSecret().Return(passwd, nil).Times(2) 386 client.EXPECT().NewKeyStore().Return(ks) 387 cmd := &cobra.Command{} 388 cmd.SetOut(new(bytes.Buffer)) 389 _, err = newAccount(client, cmd, "alias1234") 390 require.NoError(err) 391 } 392 393 func TestNewAccountSm2(t *testing.T) { 394 require := require.New(t) 395 testWallet, passwd, _, err := newTestAccount(t) 396 require.NoError(err) 397 398 ctrl := gomock.NewController(t) 399 defer ctrl.Finish() 400 client := mock_ioctlclient.NewMockClient(ctrl) 401 client.EXPECT().ReadSecret().Return(passwd, nil).Times(2) 402 client.EXPECT().Config().Return(config.Config{Wallet: testWallet}).Times(1) 403 cmd := &cobra.Command{} 404 cmd.SetOut(new(bytes.Buffer)) 405 _, err = newAccountSm2(client, cmd, "alias1234") 406 require.NoError(err) 407 } 408 409 func TestNewAccountByKey(t *testing.T) { 410 require := require.New(t) 411 _, ks, passwd, _, err := newTestAccountWithKeyStore(t, veryLightScryptN, veryLightScryptP) 412 require.NoError(err) 413 414 ctrl := gomock.NewController(t) 415 defer ctrl.Finish() 416 client := mock_ioctlclient.NewMockClient(ctrl) 417 client.EXPECT().ReadSecret().Return(passwd, nil).Times(2) 418 client.EXPECT().NewKeyStore().Return(ks) 419 420 prvKey, err := crypto.GenerateKey() 421 require.NoError(err) 422 cmd := &cobra.Command{} 423 cmd.SetOut(new(bytes.Buffer)) 424 result, err := newAccountByKey(client, cmd, "alias1234", prvKey.HexString()) 425 require.NoError(err) 426 require.Equal(prvKey.PublicKey().Address().String(), result) 427 } 428 429 func newTestAccount(t *testing.T) (string, string, string, error) { 430 testWallet := t.TempDir() 431 nonce := strconv.FormatInt(rand.Int63(), 10) 432 passwd := "3dj,<>@@SF{}rj0ZF#" + nonce 433 return testWallet, passwd, nonce, nil 434 } 435 436 func newTestAccountWithKeyStore(t *testing.T, scryptN, scryptP int) (string, *keystore.KeyStore, string, string, error) { 437 testWallet, passwd, nonce, err := newTestAccount(t) 438 if err != nil { 439 return testWallet, nil, "", "", err 440 } 441 ks := keystore.NewKeyStore(testWallet, scryptN, scryptP) 442 return testWallet, ks, passwd, nonce, nil 443 }