|
10 | 10 | #include <protocol.h>
|
11 | 11 | #include <serialize.h>
|
12 | 12 | #include <sync.h>
|
| 13 | +#include <util/bip32.h> |
13 | 14 | #include <util/system.h>
|
14 | 15 | #include <util/time.h>
|
15 | 16 | #include <wallet/wallet.h>
|
@@ -245,6 +246,7 @@ class CWalletScanState {
|
245 | 246 | std::map<uint256, DescriptorCache> m_descriptor_caches;
|
246 | 247 | std::map<std::pair<uint256, CKeyID>, CKey> m_descriptor_keys;
|
247 | 248 | std::map<std::pair<uint256, CKeyID>, std::pair<CPubKey, std::vector<unsigned char>>> m_descriptor_crypt_keys;
|
| 249 | + std::map<uint160, CHDChain> m_hd_chains; |
248 | 250 |
|
249 | 251 | CWalletScanState() {
|
250 | 252 | }
|
@@ -405,6 +407,65 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue,
|
405 | 407 | ssValue >> keyMeta;
|
406 | 408 | wss.nKeyMeta++;
|
407 | 409 | pwallet->GetOrCreateLegacyScriptPubKeyMan()->LoadKeyMetadata(vchPubKey.GetID(), keyMeta);
|
| 410 | + |
| 411 | + // Extract some CHDChain info from this metadata if it has any |
| 412 | + if (keyMeta.nVersion >= CKeyMetadata::VERSION_WITH_HDDATA && !keyMeta.hd_seed_id.IsNull() && keyMeta.hdKeypath.size() > 0) { |
| 413 | + // Get the path from the key origin or from the path string |
| 414 | + // Not applicable when path is "s" as that indicates a seed |
| 415 | + bool internal = false; |
| 416 | + uint32_t index = 0; |
| 417 | + if (keyMeta.hdKeypath != "s") { |
| 418 | + std::vector<uint32_t> path; |
| 419 | + if (keyMeta.has_key_origin) { |
| 420 | + // We have a key origin, so pull it from its path vector |
| 421 | + path = keyMeta.key_origin.path; |
| 422 | + } else { |
| 423 | + // No key origin, have to parse the string |
| 424 | + if (!ParseHDKeypath(keyMeta.hdKeypath, path)) { |
| 425 | + strErr = "Error reading wallet database: keymeta with invalid HD keypath"; |
| 426 | + return false; |
| 427 | + } |
| 428 | + } |
| 429 | + |
| 430 | + // Extract the index and internal from the path |
| 431 | + // Path string is m/0'/k'/i' |
| 432 | + // Path vector is [0', k', i'] (but as ints OR'd with the hardened bit |
| 433 | + // k == 0 for external, 1 for internal. i is the index |
| 434 | + if (path.size() != 3) { |
| 435 | + strErr = "Error reading wallet database: keymeta found with unexpected path"; |
| 436 | + return false; |
| 437 | + } |
| 438 | + if (path[0] != 0x80000000) { |
| 439 | + strErr = strprintf("Unexpected path index of 0x%08x (expected 0x80000000) for the element at index 0", path[0]); |
| 440 | + return false; |
| 441 | + } |
| 442 | + if (path[1] != 0x80000000 && path[1] != (1 | 0x80000000)) { |
| 443 | + strErr = strprintf("Unexpected path index of 0x%08x (expected 0x80000000 or 0x80000001) for the element at index 1", path[1]); |
| 444 | + return false; |
| 445 | + } |
| 446 | + if ((path[2] & 0x80000000) == 0) { |
| 447 | + strErr = strprintf("Unexpected path index of 0x%08x (expected to be greater than or equal to 0x80000000)", path[2]); |
| 448 | + return false; |
| 449 | + } |
| 450 | + internal = path[1] == (1 | 0x80000000); |
| 451 | + index = path[2] & ~0x80000000; |
| 452 | + } |
| 453 | + |
| 454 | + // Insert a new CHDChain, or get the one that already exists |
| 455 | + auto ins = wss.m_hd_chains.emplace(keyMeta.hd_seed_id, CHDChain()); |
| 456 | + CHDChain& chain = ins.first->second; |
| 457 | + if (ins.second) { |
| 458 | + // For new chains, we want to default to VERSION_HD_BASE until we see an internal |
| 459 | + chain.nVersion = CHDChain::VERSION_HD_BASE; |
| 460 | + chain.seed_id = keyMeta.hd_seed_id; |
| 461 | + } |
| 462 | + if (internal) { |
| 463 | + chain.nVersion = CHDChain::VERSION_HD_CHAIN_SPLIT; |
| 464 | + chain.nInternalChainCounter = std::max(chain.nInternalChainCounter, index); |
| 465 | + } else { |
| 466 | + chain.nExternalChainCounter = std::max(chain.nExternalChainCounter, index); |
| 467 | + } |
| 468 | + } |
408 | 469 | } else if (strType == DBKeys::WATCHMETA) {
|
409 | 470 | CScript script;
|
410 | 471 | ssKey >> script;
|
@@ -728,6 +789,20 @@ DBErrors WalletBatch::LoadWallet(CWallet* pwallet)
|
728 | 789 | result = DBErrors::CORRUPT;
|
729 | 790 | }
|
730 | 791 |
|
| 792 | + // Set the inactive chain |
| 793 | + if (wss.m_hd_chains.size() > 0) { |
| 794 | + LegacyScriptPubKeyMan* legacy_spkm = pwallet->GetLegacyScriptPubKeyMan(); |
| 795 | + if (!legacy_spkm) { |
| 796 | + pwallet->WalletLogPrintf("Inactive HD Chains found but no Legacy ScriptPubKeyMan\n"); |
| 797 | + return DBErrors::CORRUPT; |
| 798 | + } |
| 799 | + for (const auto& chain_pair : wss.m_hd_chains) { |
| 800 | + if (chain_pair.first != pwallet->GetLegacyScriptPubKeyMan()->GetHDChain().seed_id) { |
| 801 | + pwallet->GetLegacyScriptPubKeyMan()->AddInactiveHDChain(chain_pair.second); |
| 802 | + } |
| 803 | + } |
| 804 | + } |
| 805 | + |
731 | 806 | return result;
|
732 | 807 | }
|
733 | 808 |
|
|
0 commit comments