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.cpp115
1 files changed, 105 insertions, 10 deletions
diff --git a/src/selftest.cpp b/src/selftest.cpp
index df55bcd..aee2951 100644
--- a/src/selftest.cpp
+++ b/src/selftest.cpp
@@ -6,7 +6,9 @@
6// and against a temporary data directory. 6// and against a temporary data directory.
7 7
8#include "headers_core.h" 8#include "headers_core.h"
9#include "bip32.h"
9#include "selftest.h" 10#include "selftest.h"
11#include "walletcmd.h"
10 12
11// Test results go to the terminal, not to debug.log. 13// Test results go to the terminal, not to debug.log.
12// 14//
@@ -342,18 +344,68 @@ static int RunWalletHDSelfTest()
342 nFail += Check(SetHDSeedFromMnemonic(strPhraseA, strError), 344 nFail += Check(SetHDSeedFromMnemonic(strPhraseA, strError),
343 "a valid phrase installs a seed") ? 0 : 1; 345 "a valid phrase installs a seed") ? 0 : 1;
344 nFail += Check(HaveHDSeed(), "the wallet reports having a seed") ? 0 : 1; 346 nFail += Check(HaveHDSeed(), "the wallet reports having a seed") ? 0 : 1;
347 nFail += Check(nHDKeySchema == HD_SCHEMA_BIP44,
348 "a new recovery phrase records the BIP44 HD schema") ? 0 : 1;
349 nFail += Check(nHDCoinType == HD_BIP44_COIN_TYPE_BITFLASH_PROVISIONAL,
350 "a new recovery phrase records the provisional BIP44 coin type") ? 0 : 1;
351 nFail += Check(nHDReceiveNext == 1 && nHDChangeNext == 0,
352 "BIP44 receive/change counters reserve the default receive key") ? 0 : 1;
353 std::vector<unsigned int> vBIP44Path = HDBIP44Path(HD_BIP44_COIN_TYPE_BITFLASH_PROVISIONAL,
354 HD_BIP44_ACCOUNT,
355 HD_BIP44_CHAIN_RECEIVE,
356 0);
357 nFail += Check(vBIP44Path.size() == 5 &&
358 vBIP44Path[0] == (HD_BIP44_PURPOSE | bitflash::BIP32_HARDENED) &&
359 vBIP44Path[1] == (HD_BIP44_COIN_TYPE_BITFLASH_PROVISIONAL | bitflash::BIP32_HARDENED) &&
360 vBIP44Path[2] == (HD_BIP44_ACCOUNT | bitflash::BIP32_HARDENED) &&
361 vBIP44Path[3] == HD_BIP44_CHAIN_RECEIVE &&
362 vBIP44Path[4] == 0,
363 "the BIP44 helper builds m/44'/coin_type'/account'/change/index") ? 0 : 1;
345 364
346 int nPoolAfterSeed = 0; 365 int nPoolAfterSeed = 0;
347 CRITICAL_BLOCK(cs_keyPool) 366 CRITICAL_BLOCK(cs_keyPool)
348 nPoolAfterSeed = (int)mapKeyPool.size(); 367 nPoolAfterSeed = (int)mapKeyPool.size();
349 nFail += Check(nPoolAfterSeed == 0, 368 nFail += Check(nPoolAfterSeed == 0,
350 "installing a phrase clears the old random key pool") ? 0 : 1; 369 "installing a phrase clears the old random key pool") ? 0 : 1;
351 nFail += Check(nHDNext == 1, 370 nFail += Check(nHDNext == 0,
352 "installing a phrase reserves one derived default key") ? 0 : 1; 371 "installing a BIP44 phrase leaves the legacy counter unused") ? 0 : 1;
353 372
354 CKey keyFirstDerived; 373 CKey keyFirstDerived;
355 if (!DeriveHDKey(0, keyFirstDerived, strError)) 374 if (!DeriveHDKey(0, keyFirstDerived, strError))
356 throw std::runtime_error("default derivation failed: " + strError); 375 throw std::runtime_error("default derivation failed: " + strError);
376 bitflash::BIP32PrivateNode hdParent;
377 hdParent.privateKey = vchHDMaster;
378 hdParent.chainCode = vchHDChainCode;
379 bitflash::BIP32PrivateNode hdBIP44ReceiveChild;
380 if (!bitflash::BIP32DerivePath(hdParent,
381 HDBIP44Path(HD_BIP44_COIN_TYPE_BITFLASH_PROVISIONAL,
382 HD_BIP44_ACCOUNT,
383 HD_BIP44_CHAIN_RECEIVE,
384 0),
385 hdBIP44ReceiveChild,
386 strError))
387 throw std::runtime_error("BIP44 receive path derivation failed: " + strError);
388 CKey keyBIP44ReceivePath;
389 if (!keyBIP44ReceivePath.SetSecret(hdBIP44ReceiveChild.privateKey))
390 throw std::runtime_error("BIP44 receive path produced an unusable key");
391 nFail += Check(keyBIP44ReceivePath.GetPubKey() == keyFirstDerived.GetPubKey(),
392 "the default HD key path is m/44'/coin_type'/0'/0/0") ? 0 : 1;
393
394 bitflash::BIP32PrivateNode hdLegacyChild;
395 int nSchemaForLegacyCheck = nHDKeySchema;
396 nHDKeySchema = HD_SCHEMA_LEGACY;
397 CKey keyLegacyDerived;
398 if (!DeriveHDKey(0, keyLegacyDerived, strError))
399 throw std::runtime_error("legacy derivation failed: " + strError);
400 nHDKeySchema = nSchemaForLegacyCheck;
401 if (!bitflash::BIP32DerivePath(hdParent, HDLegacyPath(0), hdLegacyChild, strError))
402 throw std::runtime_error("legacy path derivation failed: " + strError);
403 CKey keyLegacyPath;
404 if (!keyLegacyPath.SetSecret(hdLegacyChild.privateKey))
405 throw std::runtime_error("legacy path produced an unusable key");
406 nFail += Check(keyLegacyPath.GetPubKey() == keyLegacyDerived.GetPubKey(),
407 "the legacy HD key path remains m/index'") ? 0 : 1;
408
357 std::vector<unsigned char> vchDefaultKey; 409 std::vector<unsigned char> vchDefaultKey;
358 bool fDefaultRead = CWalletDB("r").ReadDefaultKey(vchDefaultKey); 410 bool fDefaultRead = CWalletDB("r").ReadDefaultKey(vchDefaultKey);
359 nFail += Check(fDefaultRead && vchDefaultKey == keyFirstDerived.GetPubKey(), 411 nFail += Check(fDefaultRead && vchDefaultKey == keyFirstDerived.GetPubKey(),
@@ -370,10 +422,16 @@ static int RunWalletHDSelfTest()
370 "the pre-seed address is kept, named apart from the new one") ? 0 : 1; 422 "the pre-seed address is kept, named apart from the new one") ? 0 : 1;
371 423
372 std::vector<unsigned char> vchBefore = vchHDMaster; 424 std::vector<unsigned char> vchBefore = vchHDMaster;
425 int nSchemaBefore = nHDKeySchema;
426 unsigned int nCoinTypeBefore = nHDCoinType;
373 nFail += Check(!SetHDSeedFromMnemonic("not a mnemonic at all", strError), 427 nFail += Check(!SetHDSeedFromMnemonic("not a mnemonic at all", strError),
374 "an invalid phrase is refused") ? 0 : 1; 428 "an invalid phrase is refused") ? 0 : 1;
375 nFail += Check(vchHDMaster == vchBefore, 429 nFail += Check(vchHDMaster == vchBefore,
376 "a refused phrase leaves the existing seed alone") ? 0 : 1; 430 "a refused phrase leaves the existing seed alone") ? 0 : 1;
431 nFail += Check(nHDKeySchema == nSchemaBefore,
432 "a refused phrase leaves the derivation schema alone") ? 0 : 1;
433 nFail += Check(nHDCoinType == nCoinTypeBefore,
434 "a refused phrase leaves the BIP44 coin type alone") ? 0 : 1;
377 435
378 // The property the whole feature exists for: the same words give back 436 // The property the whole feature exists for: the same words give back
379 // the same keys, in the same order. 437 // the same keys, in the same order.
@@ -416,18 +474,26 @@ static int RunWalletHDSelfTest()
416 // With a seed installed the pool must be derived from it, and the 474 // With a seed installed the pool must be derived from it, and the
417 // counter must move exactly once per key. 475 // counter must move exactly once per key.
418 SetHDSeedFromMnemonic(strPhraseA, strError); 476 SetHDSeedFromMnemonic(strPhraseA, strError);
419 unsigned int nNextBefore = nHDNext; 477 unsigned int nReceiveNextBefore = nHDReceiveNext;
420 TopUpKeyPool(); 478 TopUpKeyPool();
421 479
422 int nPool = 0; 480 int nPool = 0;
423 CRITICAL_BLOCK(cs_keyPool) 481 CRITICAL_BLOCK(cs_keyPool)
424 nPool = (int)mapKeyPool.size(); 482 nPool = (int)mapKeyPool.size();
425 nFail += Check(nPool == KEYPOOL_SIZE, "the derived pool fills") ? 0 : 1; 483 nFail += Check(nPool == KEYPOOL_SIZE, "the derived pool fills") ? 0 : 1;
426 nFail += Check(nHDNext == nNextBefore + (unsigned int)KEYPOOL_SIZE, 484 nFail += Check(nHDReceiveNext == nReceiveNextBefore + (unsigned int)KEYPOOL_SIZE,
427 "the derivation counter advances once per pooled key") ? 0 : 1; 485 "the BIP44 receive counter advances once per pooled key") ? 0 : 1;
486 nFail += Check(nHDChangeNext == 0,
487 "filling the receive pool leaves the BIP44 change counter alone") ? 0 : 1;
488 nFail += Check(!RestoreScanReachedDepth(HD_SCHEMA_BIP44, 600, 400, 600, 600),
489 "BIP44 restore depth is not satisfied by receive plus legacy alone") ? 0 : 1;
490 nFail += Check(RestoreScanReachedDepth(HD_SCHEMA_BIP44, 600, 600, 600, 600),
491 "BIP44 restore depth is satisfied on each branch") ? 0 : 1;
492 nFail += Check(RestoreScanReachedDepth(HD_SCHEMA_LEGACY, 0, 0, 600, 600),
493 "legacy restore depth still follows the legacy counter") ? 0 : 1;
428 494
429 std::set<std::string> derived; 495 std::set<std::string> derived;
430 for (unsigned int i = nNextBefore; i < nHDNext; i++) 496 for (unsigned int i = nReceiveNextBefore; i < nHDReceiveNext; i++)
431 { 497 {
432 CKey key; 498 CKey key;
433 if (!DeriveHDKey(i, key, strError)) 499 if (!DeriveHDKey(i, key, strError))
@@ -456,11 +522,32 @@ static int RunWalletHDSelfTest()
456 CKey keyAuditDerived; 522 CKey keyAuditDerived;
457 if (!DeriveHDKey(0, keyAuditDerived, strError)) 523 if (!DeriveHDKey(0, keyAuditDerived, strError))
458 throw std::runtime_error("audit derivation failed: " + strError); 524 throw std::runtime_error("audit derivation failed: " + strError);
525 CKey keyAuditChange;
526 if (!DeriveHDChangeKey(0, keyAuditChange, strError))
527 throw std::runtime_error("audit change derivation failed: " + strError);
528 nFail += Check(keyAuditChange.GetPubKey() != keyAuditDerived.GetPubKey(),
529 "BIP44 receive and change chains derive different keys") ? 0 : 1;
530 if (!AddKey(keyAuditChange))
531 throw std::runtime_error("could not store the audit change key");
532 nHDChangeNext = 1;
533 int nSchemaForLegacyAudit = nHDKeySchema;
534 nHDKeySchema = HD_SCHEMA_LEGACY;
535 CKey keyAuditLegacyHD;
536 if (!DeriveHDKey(0, keyAuditLegacyHD, strError))
537 throw std::runtime_error("audit legacy compatibility derivation failed: " + strError);
538 nHDKeySchema = nSchemaForLegacyAudit;
539 if (!AddKey(keyAuditLegacyHD))
540 throw std::runtime_error("could not store the audit legacy compatibility key");
541 nHDNext = 1;
459 542
460 CWalletTx wtxLegacy; 543 CWalletTx wtxLegacy;
461 wtxLegacy.vout.push_back(CTxOut(5 * COIN, CScript() << vchPreSeedKey << OP_CHECKSIG)); 544 wtxLegacy.vout.push_back(CTxOut(5 * COIN, CScript() << vchPreSeedKey << OP_CHECKSIG));
462 CWalletTx wtxDerived; 545 CWalletTx wtxDerived;
463 wtxDerived.vout.push_back(CTxOut(7 * COIN, CScript() << keyAuditDerived.GetPubKey() << OP_CHECKSIG)); 546 wtxDerived.vout.push_back(CTxOut(7 * COIN, CScript() << keyAuditDerived.GetPubKey() << OP_CHECKSIG));
547 CWalletTx wtxChange;
548 wtxChange.vout.push_back(CTxOut(13 * COIN, CScript() << keyAuditChange.GetPubKey() << OP_CHECKSIG));
549 CWalletTx wtxLegacyHD;
550 wtxLegacyHD.vout.push_back(CTxOut(17 * COIN, CScript() << keyAuditLegacyHD.GetPubKey() << OP_CHECKSIG));
464 CWalletTx wtxImmatureLegacy; 551 CWalletTx wtxImmatureLegacy;
465 wtxImmatureLegacy.vin.push_back(CTxIn()); 552 wtxImmatureLegacy.vin.push_back(CTxIn());
466 wtxImmatureLegacy.vout.push_back(CTxOut(11 * COIN, CScript() << vchPreSeedKey << OP_CHECKSIG)); 553 wtxImmatureLegacy.vout.push_back(CTxOut(11 * COIN, CScript() << vchPreSeedKey << OP_CHECKSIG));
@@ -470,17 +557,25 @@ static int RunWalletHDSelfTest()
470 mapWallet.clear(); 557 mapWallet.clear();
471 mapWallet[wtxLegacy.GetHash()] = wtxLegacy; 558 mapWallet[wtxLegacy.GetHash()] = wtxLegacy;
472 mapWallet[wtxDerived.GetHash()] = wtxDerived; 559 mapWallet[wtxDerived.GetHash()] = wtxDerived;
560 mapWallet[wtxChange.GetHash()] = wtxChange;
561 mapWallet[wtxLegacyHD.GetHash()] = wtxLegacyHD;
473 mapWallet[wtxImmatureLegacy.GetHash()] = wtxImmatureLegacy; 562 mapWallet[wtxImmatureLegacy.GetHash()] = wtxImmatureLegacy;
474 } 563 }
475 564
476 WalletRecoveryAudit audit = GetWalletRecoveryAudit(); 565 WalletRecoveryAudit audit = GetWalletRecoveryAudit();
477 nFail += Check(audit.fHaveSeed, "the recovery audit reports the phrase") ? 0 : 1; 566 nFail += Check(audit.fHaveSeed, "the recovery audit reports the phrase") ? 0 : 1;
567 nFail += Check(audit.nSchema == HD_SCHEMA_BIP44,
568 "the recovery audit reports the derivation schema") ? 0 : 1;
569 nFail += Check(audit.nCoinType == HD_BIP44_COIN_TYPE_BITFLASH_PROVISIONAL,
570 "the recovery audit reports the BIP44 coin type") ? 0 : 1;
571 nFail += Check(audit.nReceiveNext == nHDReceiveNext && audit.nChangeNext == 1,
572 "the recovery audit reports receive/change counters") ? 0 : 1;
478 nFail += Check(audit.nLegacyCredit == 5 * COIN, 573 nFail += Check(audit.nLegacyCredit == 5 * COIN,
479 "the recovery audit finds wallet.dat-only balance") ? 0 : 1; 574 "the recovery audit finds wallet.dat-only balance") ? 0 : 1;
480 nFail += Check(audit.nRecoverableCredit == 7 * COIN, 575 nFail += Check(audit.nRecoverableCredit == 37 * COIN,
481 "the recovery audit finds phrase-backed balance") ? 0 : 1; 576 "the recovery audit finds BIP44 and legacy-HD phrase balance") ? 0 : 1;
482 nFail += Check(audit.nLegacyTx == 1 && audit.nRecoverableTx == 1, 577 nFail += Check(audit.nLegacyTx == 1 && audit.nRecoverableTx == 3,
483 "the recovery audit counts legacy and phrase-backed transactions") ? 0 : 1; 578 "the recovery audit counts wallet.dat-only and phrase-backed transactions") ? 0 : 1;
484 nFail += Check(audit.nLegacyImmatureCredit == 11 * COIN, 579 nFail += Check(audit.nLegacyImmatureCredit == 11 * COIN,
485 "the recovery audit finds wallet.dat-only immature mining rewards") ? 0 : 1; 580 "the recovery audit finds wallet.dat-only immature mining rewards") ? 0 : 1;
486 nFail += Check(audit.nLegacyImmatureTx == 1, 581 nFail += Check(audit.nLegacyImmatureTx == 1,