upleb.uk

Public git repos — served from a NIP-34 GRASP relay at git.upleb.uk

summaryrefslogtreecommitdiff
path: root/src/selftest.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/selftest.cpp')
-rw-r--r--src/selftest.cpp149
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
317static 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
317int RunSelfTest(const std::string& name) 462int 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}