diff options
Diffstat (limited to 'src/selftest.cpp')
| -rw-r--r-- | src/selftest.cpp | 149 |
1 files changed, 148 insertions, 1 deletions
diff --git a/src/selftest.cpp b/src/selftest.cpp index abe1d15..6792ae7 100644 --- a/src/selftest.cpp +++ b/src/selftest.cpp | |||
| @@ -314,14 +314,161 @@ static void AttachTerminal() | |||
| 314 | #endif | 314 | #endif |
| 315 | } | 315 | } |
| 316 | 316 | ||
| 317 | static int RunWalletHDSelfTest() | ||
| 318 | { | ||
| 319 | fflush(stdout); | ||
| 320 | printf("wallet-hd self-test\n"); | ||
| 321 | |||
| 322 | std::string tmp; | ||
| 323 | std::string cwd; | ||
| 324 | if (!MakeTempDir(tmp)) | ||
| 325 | { | ||
| 326 | printf(" FAIL could not create a temporary data directory\n"); | ||
| 327 | return 1; | ||
| 328 | } | ||
| 329 | if (!GetCurrentDir(cwd) || !SetCurrentDir(tmp)) | ||
| 330 | { | ||
| 331 | printf(" FAIL could not move into the temporary data directory\n"); | ||
| 332 | RemoveTree(tmp); | ||
| 333 | return 1; | ||
| 334 | } | ||
| 335 | |||
| 336 | int nFail = 0; | ||
| 337 | strSetDataDir = tmp; | ||
| 338 | printf(" temp datadir: %s\n", tmp.c_str()); | ||
| 339 | |||
| 340 | // Test-vector phrases, so nothing here depends on randomness. | ||
| 341 | const std::string strPhraseA = | ||
| 342 | "abandon abandon abandon abandon abandon abandon " | ||
| 343 | "abandon abandon abandon abandon abandon about"; | ||
| 344 | const std::string strPhraseB = | ||
| 345 | "legal winner thank year wave sausage worth useful " | ||
| 346 | "legal winner thank yellow"; | ||
| 347 | |||
| 348 | try | ||
| 349 | { | ||
| 350 | if (!LoadWallet()) | ||
| 351 | throw std::runtime_error("LoadWallet failed"); | ||
| 352 | |||
| 353 | std::string strError; | ||
| 354 | nFail += Check(SetHDSeedFromMnemonic(strPhraseA, strError), | ||
| 355 | "a valid phrase installs a seed") ? 0 : 1; | ||
| 356 | nFail += Check(HaveHDSeed(), "the wallet reports having a seed") ? 0 : 1; | ||
| 357 | |||
| 358 | std::vector<unsigned char> vchBefore = vchHDMaster; | ||
| 359 | nFail += Check(!SetHDSeedFromMnemonic("not a mnemonic at all", strError), | ||
| 360 | "an invalid phrase is refused") ? 0 : 1; | ||
| 361 | nFail += Check(vchHDMaster == vchBefore, | ||
| 362 | "a refused phrase leaves the existing seed alone") ? 0 : 1; | ||
| 363 | |||
| 364 | // The property the whole feature exists for: the same words give back | ||
| 365 | // the same keys, in the same order. | ||
| 366 | std::vector<std::string> first; | ||
| 367 | for (unsigned int i = 0; i < 5; i++) | ||
| 368 | { | ||
| 369 | CKey key; | ||
| 370 | if (!DeriveHDKey(i, key, strError)) | ||
| 371 | throw std::runtime_error("derivation failed: " + strError); | ||
| 372 | first.push_back(HexStrLocal(key.GetPubKey())); | ||
| 373 | } | ||
| 374 | |||
| 375 | SetHDSeedFromMnemonic(strPhraseA, strError); | ||
| 376 | std::vector<std::string> again; | ||
| 377 | for (unsigned int i = 0; i < 5; i++) | ||
| 378 | { | ||
| 379 | CKey key; | ||
| 380 | if (!DeriveHDKey(i, key, strError)) | ||
| 381 | throw std::runtime_error("derivation failed: " + strError); | ||
| 382 | again.push_back(HexStrLocal(key.GetPubKey())); | ||
| 383 | } | ||
| 384 | nFail += Check(first == again, | ||
| 385 | "the same phrase derives the same keys in the same order") ? 0 : 1; | ||
| 386 | |||
| 387 | SetHDSeedFromMnemonic(strPhraseB, strError); | ||
| 388 | std::vector<std::string> other; | ||
| 389 | for (unsigned int i = 0; i < 5; i++) | ||
| 390 | { | ||
| 391 | CKey key; | ||
| 392 | if (!DeriveHDKey(i, key, strError)) | ||
| 393 | throw std::runtime_error("derivation failed: " + strError); | ||
| 394 | other.push_back(HexStrLocal(key.GetPubKey())); | ||
| 395 | } | ||
| 396 | nFail += Check(first != other, "a different phrase derives different keys") ? 0 : 1; | ||
| 397 | |||
| 398 | std::set<std::string> distinct(first.begin(), first.end()); | ||
| 399 | nFail += Check(distinct.size() == first.size(), | ||
| 400 | "consecutive indices give distinct keys") ? 0 : 1; | ||
| 401 | |||
| 402 | // With a seed installed the pool must be derived from it, and the | ||
| 403 | // counter must move exactly once per key. | ||
| 404 | SetHDSeedFromMnemonic(strPhraseA, strError); | ||
| 405 | CRITICAL_BLOCK(cs_keyPool) | ||
| 406 | mapKeyPool.clear(); | ||
| 407 | unsigned int nNextBefore = nHDNext; | ||
| 408 | TopUpKeyPool(); | ||
| 409 | |||
| 410 | int nPool = 0; | ||
| 411 | CRITICAL_BLOCK(cs_keyPool) | ||
| 412 | nPool = (int)mapKeyPool.size(); | ||
| 413 | nFail += Check(nPool == KEYPOOL_SIZE, "the derived pool fills") ? 0 : 1; | ||
| 414 | nFail += Check(nHDNext == nNextBefore + (unsigned int)KEYPOOL_SIZE, | ||
| 415 | "the derivation counter advances once per pooled key") ? 0 : 1; | ||
| 416 | |||
| 417 | std::set<std::string> derived; | ||
| 418 | for (unsigned int i = nNextBefore; i < nHDNext; i++) | ||
| 419 | { | ||
| 420 | CKey key; | ||
| 421 | if (!DeriveHDKey(i, key, strError)) | ||
| 422 | throw std::runtime_error("derivation failed: " + strError); | ||
| 423 | derived.insert(HexStrLocal(key.GetPubKey())); | ||
| 424 | } | ||
| 425 | |||
| 426 | bool fStored = true; | ||
| 427 | bool fFromSeed = true; | ||
| 428 | CRITICAL_BLOCK(cs_keyPool) | ||
| 429 | { | ||
| 430 | for (map<int64, vector<unsigned char> >::const_iterator mi = mapKeyPool.begin(); | ||
| 431 | mi != mapKeyPool.end(); ++mi) | ||
| 432 | { | ||
| 433 | if (!WalletHasPrivateKey(mi->second)) | ||
| 434 | fStored = false; | ||
| 435 | if (!derived.count(HexStrLocal(mi->second))) | ||
| 436 | fFromSeed = false; | ||
| 437 | } | ||
| 438 | } | ||
| 439 | nFail += Check(fStored, "every derived pooled key is stored in wallet.dat") ? 0 : 1; | ||
| 440 | nFail += Check(fFromSeed, "every pooled key came from the seed, not from chance") ? 0 : 1; | ||
| 441 | } | ||
| 442 | catch (const std::exception& e) | ||
| 443 | { | ||
| 444 | printf(" FAIL exception: %s\n", e.what()); | ||
| 445 | nFail++; | ||
| 446 | } | ||
| 447 | catch (...) | ||
| 448 | { | ||
| 449 | printf(" FAIL unknown exception\n"); | ||
| 450 | nFail++; | ||
| 451 | } | ||
| 452 | |||
| 453 | DBFlush(true); | ||
| 454 | SetCurrentDir(cwd); | ||
| 455 | RemoveTree(tmp); | ||
| 456 | printf("%s (%d failure%s)\n", nFail == 0 ? "ALL TESTS PASSED" : "TESTS FAILED", | ||
| 457 | nFail, nFail == 1 ? "" : "s"); | ||
| 458 | fflush(stdout); | ||
| 459 | return nFail == 0 ? 0 : 1; | ||
| 460 | } | ||
| 461 | |||
| 317 | int RunSelfTest(const std::string& name) | 462 | int RunSelfTest(const std::string& name) |
| 318 | { | 463 | { |
| 319 | AttachTerminal(); | 464 | AttachTerminal(); |
| 320 | 465 | ||
| 321 | if (name == "wallet-keypool") | 466 | if (name == "wallet-keypool") |
| 322 | return RunWalletKeyPoolSelfTest(); | 467 | return RunWalletKeyPoolSelfTest(); |
| 468 | if (name == "wallet-hd") | ||
| 469 | return RunWalletHDSelfTest(); | ||
| 323 | 470 | ||
| 324 | printf("Unknown self-test '%s'\n", name.c_str()); | 471 | printf("Unknown self-test '%s'\n", name.c_str()); |
| 325 | printf("Known self-tests: wallet-keypool\n"); | 472 | printf("Known self-tests: wallet-keypool, wallet-hd\n"); |
| 326 | return 1; | 473 | return 1; |
| 327 | } | 474 | } |