mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-03-13 11:35:20 +01:00
Merge 9f1041cb1f
into a50af6e4c4
This commit is contained in:
commit
69aeca1d24
18 changed files with 236 additions and 157 deletions
|
@ -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;
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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); }
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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()) {
|
||||
|
|
|
@ -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 =
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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{
|
||||
|
|
|
@ -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()) {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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};
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
|
|
Loading…
Add table
Reference in a new issue