This commit is contained in:
Ava Chow 2025-03-13 02:04:15 +01:00 committed by GitHub
commit 69aeca1d24
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
18 changed files with 236 additions and 157 deletions

View file

@ -201,7 +201,7 @@ std::shared_ptr<CWallet> SetupLegacyWatchOnlyWallet(interfaces::Node& node, Test
CPubKey pubKey = test.coinbaseKey.GetPubKey();
bool import_keys = wallet->ImportPubKeys({{pubKey.GetID(), false}}, {{pubKey.GetID(), pubKey}} , /*key_origins=*/{}, /*add_keypool=*/false, /*timestamp=*/1);
assert(import_keys);
wallet->SetLastBlockProcessed(105, WITH_LOCK(node.context()->chainman->GetMutex(), return node.context()->chainman->ActiveChain().Tip()->GetBlockHash()));
wallet->SetBestBlock(105, WITH_LOCK(node.context()->chainman->GetMutex(), return node.context()->chainman->ActiveChain().Tip()->GetBlockHash()));
}
SyncUpWallet(wallet, node);
return wallet;
@ -226,7 +226,7 @@ std::shared_ptr<CWallet> SetupDescriptorsWallet(interfaces::Node& node, TestChai
if (!wallet->AddWalletDescriptor(w_desc, provider, "", false)) assert(false);
CTxDestination dest = GetDestinationForKey(test.coinbaseKey.GetPubKey(), wallet->m_default_address_type);
wallet->SetAddressBook(dest, "", wallet::AddressPurpose::RECEIVE);
wallet->SetLastBlockProcessed(105, WITH_LOCK(node.context()->chainman->GetMutex(), return node.context()->chainman->ActiveChain().Tip()->GetBlockHash()));
wallet->SetBestBlock(105, WITH_LOCK(node.context()->chainman->GetMutex(), return node.context()->chainman->ActiveChain().Tip()->GetBlockHash()));
SyncUpWallet(wallet, node);
wallet->SetBroadcastTransactions(true);
return wallet;

View file

@ -29,7 +29,7 @@ struct FuzzedWallet {
LOCK(wallet->cs_wallet);
wallet->SetWalletFlag(WALLET_FLAG_DESCRIPTORS);
auto height{*Assert(chain.getHeight())};
wallet->SetLastBlockProcessed(height, chain.getBlockHash(height));
wallet->SetBestBlock(height, chain.getBlockHash(height));
}
wallet->m_keypool_size = 1; // Avoid timeout in TopUp()
assert(wallet->IsWalletFlagSet(WALLET_FLAG_DESCRIPTORS));

View file

@ -368,9 +368,9 @@ public:
if (mi == m_wallet->mapWallet.end()) {
return false;
}
num_blocks = m_wallet->GetLastBlockHeight();
num_blocks = m_wallet->GetBestBlockHeight();
block_time = -1;
CHECK_NONFATAL(m_wallet->chain().findBlock(m_wallet->GetLastBlockHash(), FoundBlock().time(block_time)));
CHECK_NONFATAL(m_wallet->chain().findBlock(m_wallet->GetBestBlockHash(), FoundBlock().time(block_time)));
tx_status = MakeWalletTxStatus(*m_wallet, mi->second);
return true;
}
@ -383,7 +383,7 @@ public:
LOCK(m_wallet->cs_wallet);
auto mi = m_wallet->mapWallet.find(txid);
if (mi != m_wallet->mapWallet.end()) {
num_blocks = m_wallet->GetLastBlockHeight();
num_blocks = m_wallet->GetBestBlockHeight();
in_mempool = mi->second.InMempool();
order_form = mi->second.vOrderForm;
tx_status = MakeWalletTxStatus(*m_wallet, mi->second);
@ -421,7 +421,7 @@ public:
if (!locked_wallet) {
return false;
}
block_hash = m_wallet->GetLastBlockHash();
block_hash = m_wallet->GetBestBlockHash();
balances = getBalances();
return true;
}
@ -572,7 +572,7 @@ public:
m_context.chain = &chain;
m_context.args = &args;
}
~WalletLoaderImpl() override { UnloadWallets(m_context); }
~WalletLoaderImpl() override { CloseWallets(m_context); }
//! ChainClient methods
void registerRpcs() override
@ -594,7 +594,7 @@ public:
return StartWallets(m_context);
}
void flush() override { return FlushWallets(m_context); }
void stop() override { return StopWallets(m_context); }
void stop() override { return UnloadWallets(m_context); }
void setMockTime(int64_t time) override { return SetMockTime(time); }
void schedulerMockForward(std::chrono::seconds delta) override { Assert(m_context.scheduler)->MockForward(delta); }

View file

@ -173,7 +173,7 @@ void FlushWallets(WalletContext& context)
}
}
void StopWallets(WalletContext& context)
void CloseWallets(WalletContext& context)
{
for (const std::shared_ptr<CWallet>& pwallet : GetWallets(context)) {
pwallet->Close();

View file

@ -31,10 +31,10 @@ void StartWallets(WalletContext& context);
//! Flush all wallets in preparation for shutdown.
void FlushWallets(WalletContext& context);
//! Stop all wallets. Wallets will be flushed first.
void StopWallets(WalletContext& context);
//! Close all wallet databases.
void CloseWallets(WalletContext& context);
//! Close all wallets.
//! Flush and unload all wallets.
void UnloadWallets(WalletContext& context);
} // namespace wallet

View file

@ -107,7 +107,7 @@ static void EnsureBlockDataFromTime(const CWallet& wallet, int64_t timestamp)
int height{0};
const bool found{chain.findFirstBlockWithTimeAndHeight(timestamp - TIMESTAMP_WINDOW, 0, FoundBlock().height(height))};
uint256 tip_hash{WITH_LOCK(wallet.cs_wallet, return wallet.GetLastBlockHash())};
uint256 tip_hash{WITH_LOCK(wallet.cs_wallet, return wallet.GetBestBlockHash())};
if (found && !chain.hasBlocks(tip_hash, height)) {
throw JSONRPCError(RPC_WALLET_ERROR, strprintf("Pruned blocks from height %d required to import keys. Use RPC call getblockchaininfo to determine your pruned height.", height));
}
@ -352,7 +352,7 @@ RPCHelpMan importprunedfunds()
LOCK(pwallet->cs_wallet);
int height;
if (!pwallet->chain().findAncestorByHash(pwallet->GetLastBlockHash(), merkleBlock.header.GetHash(), FoundBlock().height(height))) {
if (!pwallet->chain().findAncestorByHash(pwallet->GetBestBlockHash(), merkleBlock.header.GetHash(), FoundBlock().height(height))) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found in chain");
}
@ -527,7 +527,7 @@ RPCHelpMan importwallet()
if (!file.is_open()) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot open wallet dump file");
}
CHECK_NONFATAL(pwallet->chain().findBlock(pwallet->GetLastBlockHash(), FoundBlock().time(nTimeBegin)));
CHECK_NONFATAL(pwallet->chain().findBlock(pwallet->GetBestBlockHash(), FoundBlock().time(nTimeBegin)));
int64_t nFilesize = std::max((int64_t)1, (int64_t)file.tellg());
file.seekg(0, file.beg);
@ -738,7 +738,7 @@ RPCHelpMan dumpwallet()
wallet.GetKeyBirthTimes(mapKeyBirth);
int64_t block_time = 0;
CHECK_NONFATAL(wallet.chain().findBlock(wallet.GetLastBlockHash(), FoundBlock().time(block_time)));
CHECK_NONFATAL(wallet.chain().findBlock(wallet.GetBestBlockHash(), FoundBlock().time(block_time)));
// Note: To avoid a lock order issue, access to cs_main must be locked before cs_KeyStore.
// So we do the two things in this function that lock cs_main first: GetKeyBirthTimes, and findBlock.
@ -759,7 +759,7 @@ RPCHelpMan dumpwallet()
// produce output
file << strprintf("# Wallet dump created by %s %s\n", CLIENT_NAME, FormatFullVersion());
file << strprintf("# * Created on %s\n", FormatISO8601DateTime(GetTime()));
file << strprintf("# * Best block at time of backup was %i (%s),\n", wallet.GetLastBlockHeight(), wallet.GetLastBlockHash().ToString());
file << strprintf("# * Best block at time of backup was %i (%s),\n", wallet.GetBestBlockHeight(), wallet.GetBestBlockHash().ToString());
file << strprintf("# mined on %s\n", FormatISO8601DateTime(block_time));
file << "\n";
@ -1379,7 +1379,7 @@ RPCHelpMan importmulti()
if (!is_watchonly) EnsureWalletIsUnlocked(wallet);
// Verify all timestamps are present before importing any keys.
CHECK_NONFATAL(pwallet->chain().findBlock(pwallet->GetLastBlockHash(), FoundBlock().time(nLowestTimestamp).mtpTime(now)));
CHECK_NONFATAL(pwallet->chain().findBlock(pwallet->GetBestBlockHash(), FoundBlock().time(nLowestTimestamp).mtpTime(now)));
for (const UniValue& data : requests.getValues()) {
GetImportTimestamp(data, now);
}
@ -1699,7 +1699,7 @@ RPCHelpMan importdescriptors()
LOCK(pwallet->cs_wallet);
EnsureWalletIsUnlocked(*pwallet);
CHECK_NONFATAL(pwallet->chain().findBlock(pwallet->GetLastBlockHash(), FoundBlock().time(lowest_timestamp).mtpTime(now)));
CHECK_NONFATAL(pwallet->chain().findBlock(pwallet->GetBestBlockHash(), FoundBlock().time(lowest_timestamp).mtpTime(now)));
// Get all timestamps and extract the lowest timestamp
for (const UniValue& request : requests.getValues()) {

View file

@ -622,7 +622,7 @@ RPCHelpMan listsinceblock()
blockId = ParseHashV(request.params[0], "blockhash");
height = int{};
altheight = int{};
if (!wallet.chain().findCommonAncestor(blockId, wallet.GetLastBlockHash(), /*ancestor_out=*/FoundBlock().height(*height), /*block1_out=*/FoundBlock().height(*altheight))) {
if (!wallet.chain().findCommonAncestor(blockId, wallet.GetBestBlockHash(), /*ancestor_out=*/FoundBlock().height(*height), /*block1_out=*/FoundBlock().height(*altheight))) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found");
}
}
@ -646,7 +646,7 @@ RPCHelpMan listsinceblock()
std::optional<std::string> filter_label;
if (!request.params[5].isNull()) filter_label.emplace(LabelFromValue(request.params[5]));
int depth = height ? wallet.GetLastBlockHeight() + 1 - *height : -1;
int depth = height ? wallet.GetBestBlockHeight() + 1 - *height : -1;
UniValue transactions(UniValue::VARR);
@ -679,8 +679,8 @@ RPCHelpMan listsinceblock()
}
uint256 lastblock;
target_confirms = std::min(target_confirms, wallet.GetLastBlockHeight() + 1);
CHECK_NONFATAL(wallet.chain().findAncestorByHeight(wallet.GetLastBlockHash(), wallet.GetLastBlockHeight() + 1 - target_confirms, FoundBlock().hash(lastblock)));
target_confirms = std::min(target_confirms, wallet.GetBestBlockHeight() + 1);
CHECK_NONFATAL(wallet.chain().findAncestorByHeight(wallet.GetBestBlockHash(), wallet.GetBestBlockHeight() + 1 - target_confirms, FoundBlock().hash(lastblock)));
UniValue ret(UniValue::VOBJ);
ret.pushKV("transactions", std::move(transactions));
@ -892,7 +892,7 @@ RPCHelpMan rescanblockchain()
{
LOCK(pwallet->cs_wallet);
EnsureWalletIsUnlocked(*pwallet);
int tip_height = pwallet->GetLastBlockHeight();
int tip_height = pwallet->GetBestBlockHeight();
if (!request.params[0].isNull()) {
start_height = request.params[0].getInt<int>();
@ -911,7 +911,7 @@ RPCHelpMan rescanblockchain()
}
// We can't rescan unavailable blocks, stop and throw an error
if (!pwallet->chain().hasBlocks(pwallet->GetLastBlockHash(), start_height, stop_height)) {
if (!pwallet->chain().hasBlocks(pwallet->GetBestBlockHash(), start_height, stop_height)) {
if (pwallet->chain().havePruned() && pwallet->chain().getPruneHeight() >= start_height) {
throw JSONRPCError(RPC_MISC_ERROR, "Can't rescan beyond pruned data. Use RPC call getblockchaininfo to determine your pruned height.");
}
@ -921,7 +921,7 @@ RPCHelpMan rescanblockchain()
throw JSONRPCError(RPC_MISC_ERROR, "Failed to rescan unavailable blocks, potentially caused by data corruption. If the issue persists you may want to reindex (see -reindex option).");
}
CHECK_NONFATAL(pwallet->chain().findAncestorByHeight(pwallet->GetLastBlockHash(), start_height, FoundBlock().hash(start_block)));
CHECK_NONFATAL(pwallet->chain().findAncestorByHeight(pwallet->GetBestBlockHash(), start_height, FoundBlock().hash(start_block)));
}
CWallet::ScanResult result =

View file

@ -167,8 +167,8 @@ void AppendLastProcessedBlock(UniValue& entry, const CWallet& wallet)
{
AssertLockHeld(wallet.cs_wallet);
UniValue lastprocessedblock{UniValue::VOBJ};
lastprocessedblock.pushKV("hash", wallet.GetLastBlockHash().GetHex());
lastprocessedblock.pushKV("height", wallet.GetLastBlockHeight());
lastprocessedblock.pushKV("hash", wallet.GetBestBlockHash().GetHex());
lastprocessedblock.pushKV("height", wallet.GetBestBlockHeight());
entry.pushKV("lastprocessedblock", std::move(lastprocessedblock));
}

View file

@ -1246,7 +1246,7 @@ static util::Result<CreatedTransactionResult> CreateTransactionInternal(
use_anti_fee_sniping = false;
}
if (use_anti_fee_sniping) {
DiscourageFeeSniping(txNew, rng_fast, wallet.chain(), wallet.GetLastBlockHash(), wallet.GetLastBlockHeight());
DiscourageFeeSniping(txNew, rng_fast, wallet.chain(), wallet.GetBestBlockHash(), wallet.GetBestBlockHeight());
}
// Calculate the transaction fee

View file

@ -460,7 +460,7 @@ BOOST_AUTO_TEST_CASE(bnb_sffo_restriction)
// Verify the coin selection process does not produce a BnB solution when SFFO is enabled.
// This is currently problematic because it could require a change output. And BnB is specialized on changeless solutions.
std::unique_ptr<CWallet> wallet = NewWallet(m_node);
WITH_LOCK(wallet->cs_wallet, wallet->SetLastBlockProcessed(300, uint256{})); // set a high block so internal UTXOs are selectable
WITH_LOCK(wallet->cs_wallet, wallet->SetBestBlock(300, uint256{})); // set a high block so internal UTXOs are selectable
FastRandomContext rand{};
CoinSelectionParams params{

View file

@ -33,7 +33,7 @@ FUZZ_TARGET(wallet_fees, .init = initialize_setup)
CWallet& wallet = *g_wallet_ptr;
{
LOCK(wallet.cs_wallet);
wallet.SetLastBlockProcessed(chainstate->m_chain.Height(), chainstate->m_chain.Tip()->GetBlockHash());
wallet.SetBestBlock(chainstate->m_chain.Height(), chainstate->m_chain.Tip()->GetBlockHash());
}
if (fuzzed_data_provider.ConsumeBool()) {

View file

@ -96,7 +96,7 @@ FUZZ_TARGET(scriptpubkeyman, .init = initialize_spkm)
{
LOCK(wallet.cs_wallet);
wallet.SetWalletFlag(WALLET_FLAG_DESCRIPTORS);
wallet.SetLastBlockProcessed(chainstate.m_chain.Height(), chainstate.m_chain.Tip()->GetBlockHash());
wallet.SetBestBlock(chainstate.m_chain.Height(), chainstate.m_chain.Tip()->GetBlockHash());
wallet.m_keypool_size = 1;
}

View file

@ -22,7 +22,7 @@ std::unique_ptr<CWallet> CreateSyncedWallet(interfaces::Chain& chain, CChain& cc
auto wallet = std::make_unique<CWallet>(&chain, "", CreateMockableWalletDatabase());
{
LOCK2(wallet->cs_wallet, ::cs_main);
wallet->SetLastBlockProcessed(cchain.Height(), cchain.Tip()->GetBlockHash());
wallet->SetBestBlock(cchain.Height(), cchain.Tip()->GetBlockHash());
}
{
LOCK(wallet->cs_wallet);

View file

@ -87,7 +87,7 @@ BOOST_FIXTURE_TEST_CASE(scan_for_wallet_transactions, TestChain100Setup)
LOCK(wallet.cs_wallet);
LOCK(Assert(m_node.chainman)->GetMutex());
wallet.SetWalletFlag(WALLET_FLAG_DESCRIPTORS);
wallet.SetLastBlockProcessed(m_node.chainman->ActiveChain().Height(), m_node.chainman->ActiveChain().Tip()->GetBlockHash());
wallet.SetBestBlock(m_node.chainman->ActiveChain().Height(), m_node.chainman->ActiveChain().Tip()->GetBlockHash());
}
AddKey(wallet, coinbaseKey);
WalletRescanReserver reserver(wallet);
@ -108,7 +108,7 @@ BOOST_FIXTURE_TEST_CASE(scan_for_wallet_transactions, TestChain100Setup)
LOCK(wallet.cs_wallet);
LOCK(Assert(m_node.chainman)->GetMutex());
wallet.SetWalletFlag(WALLET_FLAG_DESCRIPTORS);
wallet.SetLastBlockProcessed(m_node.chainman->ActiveChain().Height(), m_node.chainman->ActiveChain().Tip()->GetBlockHash());
wallet.SetBestBlock(newTip->nHeight, newTip->GetBlockHash());
}
AddKey(wallet, coinbaseKey);
WalletRescanReserver reserver(wallet);
@ -117,9 +117,9 @@ BOOST_FIXTURE_TEST_CASE(scan_for_wallet_transactions, TestChain100Setup)
reserver.reserve();
{
CBlockLocator locator;
BOOST_CHECK(!WalletBatch{wallet.GetDatabase()}.ReadBestBlock(locator));
BOOST_CHECK(locator.IsNull());
BestBlock best_block;
BOOST_CHECK(WalletBatch{wallet.GetDatabase()}.ReadBestBlock(best_block));
BOOST_CHECK(!best_block.IsNull() && best_block.m_locator.vHave.front() == newTip->GetBlockHash() && best_block.m_hash == newTip->GetBlockHash());
}
CWallet::ScanResult result = wallet.ScanForWalletTransactions(/*start_block=*/oldTip->GetBlockHash(), /*start_height=*/oldTip->nHeight, /*max_height=*/{}, reserver, /*fUpdate=*/false, /*save_progress=*/true);
@ -130,9 +130,9 @@ BOOST_FIXTURE_TEST_CASE(scan_for_wallet_transactions, TestChain100Setup)
BOOST_CHECK_EQUAL(GetBalance(wallet).m_mine_immature, 100 * COIN);
{
CBlockLocator locator;
BOOST_CHECK(WalletBatch{wallet.GetDatabase()}.ReadBestBlock(locator));
BOOST_CHECK(!locator.IsNull());
BestBlock best_block;
BOOST_CHECK(WalletBatch{wallet.GetDatabase()}.ReadBestBlock(best_block));
BOOST_CHECK(!best_block.IsNull() && best_block.m_locator.vHave.front() == newTip->GetBlockHash() && best_block.m_hash == newTip->GetBlockHash());
}
}
@ -153,7 +153,7 @@ BOOST_FIXTURE_TEST_CASE(scan_for_wallet_transactions, TestChain100Setup)
LOCK(wallet.cs_wallet);
LOCK(Assert(m_node.chainman)->GetMutex());
wallet.SetWalletFlag(WALLET_FLAG_DESCRIPTORS);
wallet.SetLastBlockProcessed(m_node.chainman->ActiveChain().Height(), m_node.chainman->ActiveChain().Tip()->GetBlockHash());
wallet.SetBestBlock(m_node.chainman->ActiveChain().Height(), m_node.chainman->ActiveChain().Tip()->GetBlockHash());
}
AddKey(wallet, coinbaseKey);
WalletRescanReserver reserver(wallet);
@ -181,7 +181,7 @@ BOOST_FIXTURE_TEST_CASE(scan_for_wallet_transactions, TestChain100Setup)
LOCK(wallet.cs_wallet);
LOCK(Assert(m_node.chainman)->GetMutex());
wallet.SetWalletFlag(WALLET_FLAG_DESCRIPTORS);
wallet.SetLastBlockProcessed(m_node.chainman->ActiveChain().Height(), m_node.chainman->ActiveChain().Tip()->GetBlockHash());
wallet.SetBestBlock(m_node.chainman->ActiveChain().Height(), m_node.chainman->ActiveChain().Tip()->GetBlockHash());
}
AddKey(wallet, coinbaseKey);
WalletRescanReserver reserver(wallet);
@ -218,7 +218,7 @@ BOOST_FIXTURE_TEST_CASE(importmulti_rescan, TestChain100Setup)
{
const std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(m_node.chain.get(), "", CreateMockableWalletDatabase());
wallet->SetupLegacyScriptPubKeyMan();
WITH_LOCK(wallet->cs_wallet, wallet->SetLastBlockProcessed(newTip->nHeight, newTip->GetBlockHash()));
WITH_LOCK(wallet->cs_wallet, wallet->SetBestBlock(newTip->nHeight, newTip->GetBlockHash()));
WalletContext context;
context.args = &m_args;
AddWallet(context, wallet);
@ -290,7 +290,7 @@ BOOST_FIXTURE_TEST_CASE(importwallet_rescan, TestChain100Setup)
AddWallet(context, wallet);
LOCK(Assert(m_node.chainman)->GetMutex());
wallet->SetLastBlockProcessed(m_node.chainman->ActiveChain().Height(), m_node.chainman->ActiveChain().Tip()->GetBlockHash());
wallet->SetBestBlock(m_node.chainman->ActiveChain().Height(), m_node.chainman->ActiveChain().Tip()->GetBlockHash());
}
JSONRPCRequest request;
request.context = &context;
@ -316,7 +316,7 @@ BOOST_FIXTURE_TEST_CASE(importwallet_rescan, TestChain100Setup)
request.params.push_back(backup_file);
AddWallet(context, wallet);
LOCK(Assert(m_node.chainman)->GetMutex());
wallet->SetLastBlockProcessed(m_node.chainman->ActiveChain().Height(), m_node.chainman->ActiveChain().Tip()->GetBlockHash());
wallet->SetBestBlock(m_node.chainman->ActiveChain().Height(), m_node.chainman->ActiveChain().Tip()->GetBlockHash());
wallet::importwallet().HandleRequest(request);
RemoveWallet(context, wallet, /* load_on_start= */ std::nullopt);
@ -379,7 +379,7 @@ BOOST_FIXTURE_TEST_CASE(coin_mark_dirty_immature_credit, TestChain100Setup)
wallet.SetWalletFlag(WALLET_FLAG_DESCRIPTORS);
wallet.SetupDescriptorScriptPubKeyMans();
wallet.SetLastBlockProcessed(m_node.chainman->ActiveChain().Height(), m_node.chainman->ActiveChain().Tip()->GetBlockHash());
wallet.SetBestBlock(m_node.chainman->ActiveChain().Height(), m_node.chainman->ActiveChain().Tip()->GetBlockHash());
// Call GetImmatureCredit() once before adding the key to the wallet to
// cache the current immature credit amount, which is 0.
@ -609,7 +609,7 @@ public:
LOCK(wallet->cs_wallet);
LOCK(Assert(m_node.chainman)->GetMutex());
wallet->SetLastBlockProcessed(wallet->GetLastBlockHeight() + 1, m_node.chainman->ActiveChain().Tip()->GetBlockHash());
wallet->SetBestBlock(wallet->GetBestBlockHeight() + 1, m_node.chainman->ActiveChain().Tip()->GetBlockHash());
auto it = wallet->mapWallet.find(tx->GetHash());
BOOST_CHECK(it != wallet->mapWallet.end());
it->second.m_state = TxStateConfirmed{m_node.chainman->ActiveChain().Tip()->GetBlockHash(), m_node.chainman->ActiveChain().Height(), /*index=*/1};

View file

@ -164,6 +164,7 @@ bool RemoveWallet(WalletContext& context, const std::shared_ptr<CWallet>& wallet
interfaces::Chain& chain = wallet->chain();
std::string name = wallet->GetName();
wallet->WriteBestBlock();
// Unregister with the validation interface which also drops shared pointers.
wallet->m_chain_notifications_handler.reset();
@ -648,15 +649,29 @@ bool CWallet::ChangeWalletPassphrase(const SecureString& strOldWalletPassphrase,
return false;
}
void CWallet::chainStateFlushed(ChainstateRole role, const CBlockLocator& loc)
void CWallet::SetBestBlockInMem(int block_height, uint256 block_hash)
{
// Don't update the best block until the chain is attached so that in case of a shutdown,
// the rescan will be restarted at next startup.
if (m_attaching_chain || role == ChainstateRole::BACKGROUND) {
return;
}
AssertLockHeld(cs_wallet);
m_best_block.m_hash = block_hash;
m_best_block.m_height = block_height;
chain().findBlock(m_best_block.m_hash, FoundBlock().locator(m_best_block.m_locator));
}
void CWallet::SetBestBlock(int block_height, uint256 block_hash)
{
AssertLockHeld(cs_wallet);
SetBestBlockInMem(block_height, block_hash);
WalletBatch batch(GetDatabase());
batch.WriteBestBlock(loc);
batch.WriteBestBlock(m_best_block);
}
void CWallet::LoadBestBlock(const BestBlock& best_block)
{
m_best_block = best_block;
}
void CWallet::SetMinVersion(enum WalletFeature nVersion, WalletBatch* batch_in)
@ -1358,10 +1373,10 @@ void CWallet::MarkConflicted(const uint256& hashBlock, int conflicting_height, c
// that the block is still unknown or not yet part of the main chain,
// for example when loading the wallet during a reindex. Do nothing in that
// case.
if (m_last_block_processed_height < 0 || conflicting_height < 0) {
if (!m_best_block.m_height.has_value() || conflicting_height < 0) {
return;
}
int conflictconfirms = (m_last_block_processed_height - conflicting_height + 1) * -1;
int conflictconfirms = (m_best_block.m_height.value() - conflicting_height + 1) * -1;
if (conflictconfirms >= 0)
return;
@ -1425,15 +1440,16 @@ void CWallet::RecursiveUpdateTxState(WalletBatch* batch, const uint256& tx_hash,
}
}
void CWallet::SyncTransaction(const CTransactionRef& ptx, const SyncTxState& state, bool update_tx, bool rescanning_old_block)
bool CWallet::SyncTransaction(const CTransactionRef& ptx, const SyncTxState& state, bool update_tx, bool rescanning_old_block)
{
if (!AddToWalletIfInvolvingMe(ptx, state, update_tx, rescanning_old_block))
return; // Not one of ours
return false; // Not one of ours
// If a transaction changes 'conflicted' state, that changes the balance
// available of the outputs it spends. So force those to be
// recomputed, also:
MarkInputsDirty(ptx);
return true;
}
void CWallet::transactionAddedToMempool(const CTransactionRef& tx) {
@ -1520,18 +1536,25 @@ void CWallet::blockConnected(ChainstateRole role, const interfaces::BlockInfo& b
assert(block.data);
LOCK(cs_wallet);
m_last_block_processed_height = block.height;
m_last_block_processed = block.hash;
// Update the best block in memory first. This will set the best block's height, which is
// needed by MarkConflicted.
SetBestBlockInMem(block.height, block.hash);
// No need to scan block if it was created before the wallet birthday.
// Uses chain max time and twice the grace period to adjust time for block time variability.
if (block.chain_time_max < m_birth_time.load() - (TIMESTAMP_WINDOW * 2)) return;
// Scan block
bool wallet_updated = false;
for (size_t index = 0; index < block.data->vtx.size(); index++) {
SyncTransaction(block.data->vtx[index], TxStateConfirmed{block.hash, block.height, static_cast<int>(index)});
wallet_updated |= SyncTransaction(block.data->vtx[index], TxStateConfirmed{block.hash, block.height, static_cast<int>(index)});
transactionRemovedFromMempool(block.data->vtx[index], MemPoolRemovalReason::BLOCK);
}
// Update on disk if this block resulted in us updating a tx, or periodically every 144 blocks (~1 day)
if (wallet_updated || block.height % 144 == 0) {
SetBestBlock(block.height, block.hash);
}
}
void CWallet::blockDisconnected(const interfaces::BlockInfo& block)
@ -1543,11 +1566,7 @@ void CWallet::blockDisconnected(const interfaces::BlockInfo& block)
// be unconfirmed, whether or not the transaction is added back to the mempool.
// User may have to call abandontransaction again. It may be addressed in the
// future with a stickier abandoned state or even removing abandontransaction call.
m_last_block_processed_height = block.height - 1;
m_last_block_processed = *Assert(block.prev_hash);
int disconnect_height = block.height;
for (const CTransactionRef& ptx : Assert(block.data)->vtx) {
SyncTransaction(ptx, TxStateInactive{});
@ -1576,6 +1595,9 @@ void CWallet::blockDisconnected(const interfaces::BlockInfo& block)
}
}
}
// Update the best block
SetBestBlock(block.height - 1, *Assert(block.prev_hash));
}
void CWallet::updatedBlockTip()
@ -1589,7 +1611,7 @@ void CWallet::BlockUntilSyncedToCurrentChain() const {
// chain().Tip(), otherwise put a callback in the validation interface queue and wait
// for the queue to drain enough to execute it (indicating we are caught up
// at least with the time we entered this function).
uint256 last_block_hash = WITH_LOCK(cs_wallet, return m_last_block_processed);
uint256 last_block_hash = WITH_LOCK(cs_wallet, return m_best_block.m_hash);
chain().waitForNotificationsIfTipChanged(last_block_hash);
}
@ -1852,7 +1874,7 @@ int64_t CWallet::RescanFromTime(int64_t startTime, const WalletRescanReserver& r
int start_height = 0;
uint256 start_block;
bool start = chain().findFirstBlockWithTimeAndHeight(startTime - TIMESTAMP_WINDOW, 0, FoundBlock().hash(start_block).height(start_height));
WalletLogPrintf("%s: Rescanning last %i blocks\n", __func__, start ? WITH_LOCK(cs_wallet, return GetLastBlockHeight()) - start_height + 1 : 0);
WalletLogPrintf("%s: Rescanning last %i blocks\n", __func__, start ? WITH_LOCK(cs_wallet, return GetBestBlockHeight()) - start_height + 1 : 0);
if (start) {
// TODO: this should take into account failure by ScanResult::USER_ABORT
@ -1907,7 +1929,7 @@ CWallet::ScanResult CWallet::ScanForWalletTransactions(const uint256& start_bloc
fAbortRescan = false;
ShowProgress(strprintf("%s %s", GetDisplayName(), _("Rescanning…")), 0); // show rescan progress in GUI as dialog or on splashscreen, if rescan required on startup (e.g. due to corruption)
uint256 tip_hash = WITH_LOCK(cs_wallet, return GetLastBlockHash());
uint256 tip_hash = WITH_LOCK(cs_wallet, return GetBestBlockHash());
uint256 end_hash = tip_hash;
if (max_height) chain().findAncestorByHeight(tip_hash, *max_height, FoundBlock().hash(end_hash));
double progress_begin = chain().guessVerificationProgress(block_hash);
@ -1981,7 +2003,7 @@ CWallet::ScanResult CWallet::ScanForWalletTransactions(const uint256& start_bloc
if (!loc.IsNull()) {
WalletLogPrintf("Saving scan progress %d.\n", block_height);
WalletBatch batch(GetDatabase());
batch.WriteBestBlock(loc);
batch.WriteBestBlock(BestBlock{loc, block_hash, block_height});
}
}
} else {
@ -1997,7 +2019,7 @@ CWallet::ScanResult CWallet::ScanForWalletTransactions(const uint256& start_bloc
// aren't processed here but will be processed with the pending blockConnected notifications after the lock is released.
// If rescanning without a permanent cs_wallet lock, additional blocks that were added during the rescan will be re-processed if
// the notification was processed and the last block height was updated.
if (block_height >= WITH_LOCK(cs_wallet, return GetLastBlockHeight())) {
if (block_height >= WITH_LOCK(cs_wallet, return GetBestBlockHeight())) {
break;
}
@ -2015,7 +2037,7 @@ CWallet::ScanResult CWallet::ScanForWalletTransactions(const uint256& start_bloc
// handle updated tip hash
const uint256 prev_tip_hash = tip_hash;
tip_hash = WITH_LOCK(cs_wallet, return GetLastBlockHash());
tip_hash = WITH_LOCK(cs_wallet, return GetBestBlockHash());
if (!max_height && prev_tip_hash != tip_hash) {
// in case the tip has changed, update progress max
progress_end = chain().guessVerificationProgress(tip_hash);
@ -2782,8 +2804,8 @@ void CWallet::GetKeyBirthTimes(std::map<CKeyID, int64_t>& mapKeyBirth) const {
// map in which we'll infer heights of other keys
std::map<CKeyID, const TxStateConfirmed*> mapKeyFirstBlock;
TxStateConfirmed max_confirm{uint256{}, /*height=*/-1, /*index=*/-1};
max_confirm.confirmed_block_height = GetLastBlockHeight() > 144 ? GetLastBlockHeight() - 144 : 0; // the tip can be reorganized; use a 144-block safety margin
CHECK_NONFATAL(chain().findAncestorByHeight(GetLastBlockHash(), max_confirm.confirmed_block_height, FoundBlock().hash(max_confirm.confirmed_block_hash)));
max_confirm.confirmed_block_height = GetBestBlockHeight() > 144 ? GetBestBlockHeight() - 144 : 0; // the tip can be reorganized; use a 144-block safety margin
CHECK_NONFATAL(chain().findAncestorByHeight(GetBestBlockHash(), max_confirm.confirmed_block_height, FoundBlock().hash(max_confirm.confirmed_block_hash)));
{
LegacyScriptPubKeyMan* spk_man = GetLegacyScriptPubKeyMan();
@ -3088,7 +3110,10 @@ std::shared_ptr<CWallet> CWallet::Create(WalletContext& context, const std::stri
}
if (chain) {
walletInstance->chainStateFlushed(ChainstateRole::NORMAL, chain->getTipLocator());
const std::optional<int> tip_height = chain->getHeight();
if (tip_height) {
WITH_LOCK(walletInstance->cs_wallet, walletInstance->SetBestBlock(*tip_height, chain->getBlockHash(*tip_height)));
}
}
} else if (wallet_creation_flags & WALLET_FLAG_DISABLE_PRIVATE_KEYS) {
// Make it impossible to disable private keys after creation
@ -3267,15 +3292,14 @@ bool CWallet::AttachChain(const std::shared_ptr<CWallet>& walletInstance, interf
// allow setting the chain if it hasn't been set already but prevent changing it
assert(!walletInstance->m_chain || walletInstance->m_chain == &chain);
walletInstance->m_chain = &chain;
CBlockLocator best_block_locator = walletInstance->GetBestBlockLocator();
// Unless allowed, ensure wallet files are not reused across chains:
if (!gArgs.GetBoolArg("-walletcrosschain", DEFAULT_WALLETCROSSCHAIN)) {
WalletBatch batch(walletInstance->GetDatabase());
CBlockLocator locator;
if (batch.ReadBestBlock(locator) && locator.vHave.size() > 0 && chain.getHeight()) {
if (best_block_locator.vHave.size() > 0 && chain.getHeight()) {
// Wallet is assumed to be from another chain, if genesis block in the active
// chain differs from the genesis block known to the wallet.
if (chain.getBlockHash(0) != locator.vHave.back()) {
if (chain.getBlockHash(0) != best_block_locator.vHave.back()) {
error = Untranslated("Wallet files should not be reused across chains. Restart bitcoind with -walletcrosschain to override.");
return false;
}
@ -3288,33 +3312,33 @@ bool CWallet::AttachChain(const std::shared_ptr<CWallet>& walletInstance, interf
// be pending on the validation-side until lock release. Blocks that are connected while the
// rescan is ongoing will not be processed in the rescan but with the block connected notifications,
// so the wallet will only be completeley synced after the notifications delivery.
// chainStateFlushed notifications are ignored until the rescan is finished
// so that in case of a shutdown event, the rescan will be repeated at the next start.
// This is temporary until rescan and notifications delivery are unified under same
// interface.
walletInstance->m_attaching_chain = true; //ignores chainStateFlushed notifications
walletInstance->m_chain_notifications_handler = walletInstance->chain().handleNotifications(walletInstance);
// If rescan_required = true, rescan_height remains equal to 0
int rescan_height = 0;
if (!rescan_required)
{
WalletBatch batch(walletInstance->GetDatabase());
CBlockLocator locator;
if (batch.ReadBestBlock(locator)) {
if (const std::optional<int> fork_height = chain.findLocatorFork(locator)) {
rescan_height = *fork_height;
}
// Update the best block locator if it is missing the height.
// If the chain has already processed the genesis block, this will be immediately overwritten with the current tip.
if (!walletInstance->HasBestBlockHeight()) {
// Best block record without height info, lookup height and rewrite the record
// Also sets for this wallet
int found_height;
if (chain.findBlock(walletInstance->GetBestBlockHash(), FoundBlock().height(found_height))) {
walletInstance->SetBestBlock(found_height, walletInstance->GetBestBlockHash());
}
}
// If rescan_required = true, rescan_height remains equal to 0
int rescan_height = 0;
if (!rescan_required) {
if (const std::optional<int> fork_height = chain.findLocatorFork(walletInstance->GetBestBlockLocator())) {
rescan_height = *fork_height;
}
}
// If we have a tip, always update the best block to the current tip.
const std::optional<int> tip_height = chain.getHeight();
if (tip_height) {
walletInstance->m_last_block_processed = chain.getBlockHash(*tip_height);
walletInstance->m_last_block_processed_height = *tip_height;
} else {
walletInstance->m_last_block_processed.SetNull();
walletInstance->m_last_block_processed_height = -1;
// Always update best block to the current tip. If the wallet isn't actually at the tip yet, a rescan
// is already planned
walletInstance->SetBestBlock(*tip_height, chain.getBlockHash(*tip_height));
}
if (tip_height && *tip_height != rescan_height)
@ -3369,16 +3393,22 @@ bool CWallet::AttachChain(const std::shared_ptr<CWallet>& walletInstance, interf
{
WalletRescanReserver reserver(*walletInstance);
if (!reserver.reserve() || (ScanResult::SUCCESS != walletInstance->ScanForWalletTransactions(chain.getBlockHash(rescan_height), rescan_height, /*max_height=*/{}, reserver, /*fUpdate=*/true, /*save_progress=*/true).status)) {
if (!reserver.reserve()) {
error = _("Failed to acquire rescan reserver during wallet initialization");
return false;
}
ScanResult scan_res = walletInstance->ScanForWalletTransactions(chain.getBlockHash(rescan_height), rescan_height, /*max_height=*/{}, reserver, /*fUpdate=*/true, /*save_progress=*/true);
if (ScanResult::SUCCESS != scan_res.status) {
error = _("Failed to rescan the wallet during initialization");
return false;
}
// Set and update the best block record
// Although ScanForWalletTransactions will have stopped at the best block that was set prior to the rescan,
// we still need to make sure that the best block on disk is set correctly as rescanning may overwrite it.
walletInstance->SetBestBlock(*scan_res.last_scanned_height, scan_res.last_scanned_block);
}
walletInstance->m_attaching_chain = false;
walletInstance->chainStateFlushed(ChainstateRole::NORMAL, chain.getTipLocator());
walletInstance->GetDatabase().IncrementUpdateCounter();
}
walletInstance->m_attaching_chain = false;
return true;
}
@ -3438,13 +3468,10 @@ void CWallet::postInitProcess()
bool CWallet::BackupWallet(const std::string& strDest) const
{
if (m_chain) {
CBlockLocator loc;
WITH_LOCK(cs_wallet, chain().findBlock(m_last_block_processed, FoundBlock().locator(loc)));
if (!loc.IsNull()) {
WalletBatch batch(GetDatabase());
batch.WriteBestBlock(loc);
}
LOCK(cs_wallet);
if (!m_best_block.IsNull()) {
WalletBatch batch(GetDatabase());
batch.WriteBestBlock(m_best_block);
}
return GetDatabase().Backup(strDest);
}
@ -3469,10 +3496,10 @@ int CWallet::GetTxDepthInMainChain(const CWalletTx& wtx) const
AssertLockHeld(cs_wallet);
if (auto* conf = wtx.state<TxStateConfirmed>()) {
assert(conf->confirmed_block_height >= 0);
return GetLastBlockHeight() - conf->confirmed_block_height + 1;
return GetBestBlockHeight() - conf->confirmed_block_height + 1;
} else if (auto* conf = wtx.state<TxStateBlockConflicted>()) {
assert(conf->conflicting_block_height >= 0);
return -1 * (GetLastBlockHeight() - conf->conflicting_block_height + 1);
return -1 * (GetBestBlockHeight() - conf->conflicting_block_height + 1);
} else {
return 0;
}
@ -4146,12 +4173,6 @@ util::Result<void> CWallet::ApplyMigrationData(WalletBatch& local_wallet_batch,
}
}
// Get best block locator so that we can copy it to the watchonly and solvables
CBlockLocator best_block_locator;
if (!local_wallet_batch.ReadBestBlock(best_block_locator)) {
return util::Error{_("Error: Unable to read wallet's best block locator record")};
}
// Check if the transactions in the wallet are still ours. Either they belong here, or they belong in the watchonly wallet.
// We need to go through these in the tx insertion order so that lookups to spends works.
std::vector<uint256> txids_to_delete;
@ -4164,7 +4185,7 @@ util::Result<void> CWallet::ApplyMigrationData(WalletBatch& local_wallet_batch,
data.watchonly_wallet->nOrderPosNext = nOrderPosNext;
watchonly_batch->WriteOrderPosNext(data.watchonly_wallet->nOrderPosNext);
// Write the best block locator to avoid rescanning on reload
if (!watchonly_batch->WriteBestBlock(best_block_locator)) {
if (!watchonly_batch->WriteBestBlock(m_best_block)) {
return util::Error{_("Error: Unable to write watchonly wallet best block locator record")};
}
}
@ -4173,7 +4194,7 @@ util::Result<void> CWallet::ApplyMigrationData(WalletBatch& local_wallet_batch,
solvables_batch = std::make_unique<WalletBatch>(data.solvable_wallet->GetDatabase());
if (!solvables_batch->TxnBegin()) return util::Error{strprintf(_("Error: database transaction cannot be executed for wallet %s"), data.solvable_wallet->GetName())};
// Write the best block locator to avoid rescanning on reload
if (!solvables_batch->WriteBestBlock(best_block_locator)) {
if (!solvables_batch->WriteBestBlock(m_best_block)) {
return util::Error{_("Error: Unable to write solvable wallet best block locator record")};
}
}
@ -4421,7 +4442,7 @@ util::Result<MigrationResult> MigrateLegacyToDescriptor(const std::string& walle
// Flush chain state before unloading wallet
CBlockLocator locator;
WITH_LOCK(wallet->cs_wallet, context.chain->findBlock(wallet->GetLastBlockHash(), FoundBlock().locator(locator)));
WITH_LOCK(wallet->cs_wallet, context.chain->findBlock(wallet->GetBestBlockHash(), FoundBlock().locator(locator)));
if (!locator.IsNull()) wallet->chainStateFlushed(ChainstateRole::NORMAL, locator);
if (!RemoveWallet(context, wallet, /*load_on_start=*/std::nullopt, warnings)) {
@ -4672,4 +4693,13 @@ std::optional<CKey> CWallet::GetKey(const CKeyID& keyid) const
}
return std::nullopt;
}
void CWallet::WriteBestBlock() const
{
LOCK(cs_wallet);
if (!m_best_block.IsNull()) {
WalletBatch batch(GetDatabase());
batch.WriteBestBlock(m_best_block);
}
}
} // namespace wallet

View file

@ -292,6 +292,39 @@ struct CRecipient
bool fSubtractFeeFromAmount;
};
struct BestBlock
{
CBlockLocator m_locator;
uint256 m_hash;
std::optional<int> m_height;
bool IsNull() const { return m_locator.IsNull(); }
template<typename Stream>
void Serialize(Stream& s) const
{
s << m_locator;
// No need to write m_hash since it's the first thing in m_locator
if (m_height.has_value()) {
s << m_height.value();
}
}
template<typename Stream>
void Unserialize(Stream& s)
{
s >> m_locator;
if (!s.eof()) {
int height;
s >> height;
m_height = height;
}
if (!m_locator.IsNull()) {
m_hash = m_locator.vHave[0];
}
}
};
class WalletRescanReserver; //forward declarations for ScanForWalletTransactions/RescanFromTime
/**
* A CWallet maintains a set of transactions and balances, and provides the ability to create new transactions.
@ -305,7 +338,6 @@ private:
std::atomic<bool> fAbortRescan{false};
std::atomic<bool> fScanningWallet{false}; // controlled by WalletRescanReserver
std::atomic<bool> m_attaching_chain{false};
std::atomic<bool> m_scanning_with_passphrase{false};
std::atomic<SteadyClock::time_point> m_scanning_start{SteadyClock::time_point{}};
std::atomic<double> m_scanning_progress{0};
@ -370,7 +402,7 @@ private:
void SyncMetaData(std::pair<TxSpends::iterator, TxSpends::iterator>) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
void SyncTransaction(const CTransactionRef& tx, const SyncTxState& state, bool update_tx = true, bool rescanning_old_block = false) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
bool SyncTransaction(const CTransactionRef& tx, const SyncTxState& state, bool update_tx = true, bool rescanning_old_block = false) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
/** WalletFlags set on this wallet. */
std::atomic<uint64_t> m_wallet_flags{0};
@ -393,20 +425,11 @@ private:
std::unique_ptr<WalletDatabase> m_database;
/**
* The following is used to keep track of how far behind the wallet is
* m_best_block is used to keep track of how far behind the wallet is
* from the chain sync, and to allow clients to block on us being caught up.
*
* Processed hash is a pointer on node's tip and doesn't imply that the wallet
* has scanned sequentially all blocks up to this one.
* The wallet is guaranteed to have completed scanning up to this block.
*/
uint256 m_last_block_processed GUARDED_BY(cs_wallet);
/** Height of last block processed is used by wallet to know depth of transactions
* without relying on Chain interface beyond asynchronous updates. For safety, we
* initialize it to -1. Height is a pointer on node's tip and doesn't imply
* that the wallet has scanned sequentially all blocks up to this one.
*/
int m_last_block_processed_height GUARDED_BY(cs_wallet) = -1;
BestBlock m_best_block GUARDED_BY(cs_wallet);
std::map<OutputType, ScriptPubKeyMan*> m_external_spk_managers;
std::map<OutputType, ScriptPubKeyMan*> m_internal_spk_managers;
@ -430,13 +453,16 @@ private:
/**
* Catch wallet up to current chain, scanning new blocks, updating the best
* block locator and m_last_block_processed, and registering for
* block locator, and registering for
* notifications about new blocks and transactions.
*/
static bool AttachChain(const std::shared_ptr<CWallet>& wallet, interfaces::Chain& chain, const bool rescan_required, bilingual_str& error, std::vector<bilingual_str>& warnings);
static NodeClock::time_point GetDefaultNextResend();
// Update last block processed in memory only
void SetBestBlockInMem(int block_height, uint256 block_hash) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
public:
/**
* Main wallet lock.
@ -788,7 +814,6 @@ public:
/** should probably be renamed to IsRelevantToMe */
bool IsFromMe(const CTransaction& tx) const;
CAmount GetDebit(const CTransaction& tx, const isminefilter& filter) const;
void chainStateFlushed(ChainstateRole role, const CBlockLocator& loc) override;
DBErrors LoadWallet();
@ -977,25 +1002,31 @@ public:
bool HaveCryptedKeys() const;
/** Get last block processed height */
int GetLastBlockHeight() const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet)
int GetBestBlockHeight() const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet)
{
AssertLockHeld(cs_wallet);
assert(m_last_block_processed_height >= 0);
return m_last_block_processed_height;
assert(m_best_block.m_height.has_value());
return m_best_block.m_height.value();
};
uint256 GetLastBlockHash() const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet)
bool HasBestBlockHeight() const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet)
{
AssertLockHeld(cs_wallet);
assert(m_last_block_processed_height >= 0);
return m_last_block_processed;
return !m_best_block.IsNull() && m_best_block.m_height.has_value();
}
/** Set last block processed height, currently only use in unit test */
void SetLastBlockProcessed(int block_height, uint256 block_hash) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet)
uint256 GetBestBlockHash() const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet)
{
AssertLockHeld(cs_wallet);
m_last_block_processed_height = block_height;
m_last_block_processed = block_hash;
};
return m_best_block.m_hash;
}
CBlockLocator GetBestBlockLocator() const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet)
{
AssertLockHeld(cs_wallet);
return m_best_block.m_locator;
}
/** Set last block processed height, and write to database */
void SetBestBlock(int block_height, uint256 block_hash) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
void LoadBestBlock(const BestBlock& best_block) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
/** Write the current best block to database */
void WriteBestBlock() const;
//! Connect the signals from ScriptPubKeyMans to the signals in CWallet
void ConnectScriptPubKeyManNotifiers();

View file

@ -180,16 +180,21 @@ bool WalletBatch::EraseWatchOnly(const CScript &dest)
return EraseIC(std::make_pair(DBKeys::WATCHS, dest));
}
bool WalletBatch::WriteBestBlock(const CBlockLocator& locator)
bool WalletBatch::WriteBestBlock(const BestBlock& best_block)
{
WriteIC(DBKeys::BESTBLOCK, CBlockLocator()); // Write empty block locator so versions that require a merkle branch automatically rescan
return WriteIC(DBKeys::BESTBLOCK_NOMERKLE, locator);
return WriteIC(DBKeys::BESTBLOCK_NOMERKLE, best_block);
}
bool WalletBatch::ReadBestBlock(CBlockLocator& locator)
bool WalletBatch::ReadBestBlock(BestBlock& best_block)
{
if (m_batch->Read(DBKeys::BESTBLOCK, locator) && !locator.vHave.empty()) return true;
return m_batch->Read(DBKeys::BESTBLOCK_NOMERKLE, locator);
CBlockLocator locator;
if (m_batch->Read(DBKeys::BESTBLOCK, locator) && !locator.vHave.empty()) {
best_block.m_locator = locator;
best_block.m_hash = locator.vHave[0];
return true;
}
return m_batch->Read(DBKeys::BESTBLOCK_NOMERKLE, best_block);
}
bool WalletBatch::IsEncrypted()
@ -491,6 +496,16 @@ static DBErrors LoadWalletFlags(CWallet* pwallet, DatabaseBatch& batch) EXCLUSIV
return DBErrors::LOAD_OK;
}
static DBErrors LoadBestBlock(CWallet* pwallet, WalletBatch& batch) EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet)
{
AssertLockHeld(pwallet->cs_wallet);
BestBlock best_block;
if (batch.ReadBestBlock(best_block) && !best_block.IsNull()) {
pwallet->LoadBestBlock(best_block);
}
return DBErrors::LOAD_OK;
}
struct LoadResult
{
DBErrors m_result{DBErrors::LOAD_OK};
@ -1196,6 +1211,8 @@ DBErrors WalletBatch::LoadWallet(CWallet* pwallet)
return DBErrors::EXTERNAL_SIGNER_SUPPORT_REQUIRED;
}
#endif
// Load the best block
if ((result = LoadBestBlock(pwallet, *this)) != DBErrors::LOAD_OK) return result;
// Load legacy wallet keys
result = std::max(LoadLegacyWalletRecords(pwallet, *m_batch, last_client), result);

View file

@ -21,6 +21,7 @@ class uint256;
struct CBlockLocator;
namespace wallet {
struct BestBlock;
class CKeyPool;
class CMasterKey;
class CWallet;
@ -250,8 +251,8 @@ public:
bool WriteWatchOnly(const CScript &script, const CKeyMetadata &keymeta);
bool EraseWatchOnly(const CScript &script);
bool WriteBestBlock(const CBlockLocator& locator);
bool ReadBestBlock(CBlockLocator& locator);
bool WriteBestBlock(const BestBlock& best_block);
bool ReadBestBlock(BestBlock& best_block);
// Returns true if wallet stores encryption keys
bool IsEncrypted();