| 2694 | /** @} */ // end of Actions |
| 2695 | |
| 2696 | void CWallet::GetKeyBirthTimes(std::map<CKeyID, int64_t>& mapKeyBirth) const { |
| 2697 | AssertLockHeld(cs_wallet); |
| 2698 | mapKeyBirth.clear(); |
| 2699 | |
| 2700 | // map in which we'll infer heights of other keys |
| 2701 | std::map<CKeyID, const TxStateConfirmed*> mapKeyFirstBlock; |
| 2702 | TxStateConfirmed max_confirm{uint256{}, /*height=*/-1, /*index=*/-1}; |
| 2703 | max_confirm.confirmed_block_height = GetLastBlockHeight() > 144 ? GetLastBlockHeight() - 144 : 0; // the tip can be reorganized; use a 144-block safety margin |
| 2704 | CHECK_NONFATAL(chain().findAncestorByHeight(GetLastBlockHash(), max_confirm.confirmed_block_height, FoundBlock().hash(max_confirm.confirmed_block_hash))); |
| 2705 | |
| 2706 | { |
| 2707 | LegacyScriptPubKeyMan* spk_man = GetLegacyScriptPubKeyMan(); |
| 2708 | assert(spk_man != nullptr); |
| 2709 | LOCK(spk_man->cs_KeyStore); |
| 2710 | |
| 2711 | // get birth times for keys with metadata |
| 2712 | for (const auto& entry : spk_man->mapKeyMetadata) { |
| 2713 | if (entry.second.nCreateTime) { |
| 2714 | mapKeyBirth[entry.first] = entry.second.nCreateTime; |
| 2715 | } |
| 2716 | } |
| 2717 | |
| 2718 | // Prepare to infer birth heights for keys without metadata |
| 2719 | for (const CKeyID &keyid : spk_man->GetKeys()) { |
| 2720 | if (mapKeyBirth.count(keyid) == 0) |
| 2721 | mapKeyFirstBlock[keyid] = &max_confirm; |
| 2722 | } |
| 2723 | |
| 2724 | // if there are no such keys, we're done |
| 2725 | if (mapKeyFirstBlock.empty()) |
| 2726 | return; |
| 2727 | |
| 2728 | // find first block that affects those keys, if there are any left |
| 2729 | for (const auto& entry : mapWallet) { |
| 2730 | // iterate over all wallet transactions... |
| 2731 | const CWalletTx &wtx = entry.second; |
| 2732 | if (auto* conf = wtx.state<TxStateConfirmed>()) { |
| 2733 | // ... which are already in a block |
| 2734 | for (const CTxOut &txout : wtx.tx->vout) { |
| 2735 | // iterate over all their outputs |
| 2736 | for (const auto &keyid : GetAffectedKeys(txout.scriptPubKey, *spk_man)) { |
| 2737 | // ... and all their affected keys |
| 2738 | auto rit = mapKeyFirstBlock.find(keyid); |
| 2739 | if (rit != mapKeyFirstBlock.end() && conf->confirmed_block_height < rit->second->confirmed_block_height) { |
| 2740 | rit->second = conf; |
| 2741 | } |
| 2742 | } |
| 2743 | } |
| 2744 | } |
| 2745 | } |
| 2746 | } |
| 2747 | |
| 2748 | // Extract block timestamps for those keys |
| 2749 | for (const auto& entry : mapKeyFirstBlock) { |
| 2750 | int64_t block_time; |
| 2751 | CHECK_NONFATAL(chain().findBlock(entry.second->confirmed_block_hash, FoundBlock().time(block_time))); |
| 2752 | mapKeyBirth[entry.first] = block_time - TIMESTAMP_WINDOW; // block times can be 2h off |
| 2753 | } |
no test coverage detected