aboutsummaryrefslogtreecommitdiff
path: root/src/wallet
diff options
context:
space:
mode:
authorMarcoFalke <falke.marco@gmail.com>2020-04-14 07:18:02 -0400
committerMarcoFalke <falke.marco@gmail.com>2020-04-14 07:18:12 -0400
commit4702cadca94520a36bfe979c85750563c30f7c85 (patch)
tree5e83ec9d43b7b5f4459feb8c421b3282fad74604 /src/wallet
parent6110ae8326c74704c9e105deca725f2411395969 (diff)
parent48973402d8bccb673eaeb68b7aa86faa39d3cb8a (diff)
downloadbitcoin-4702cadca94520a36bfe979c85750563c30f7c85.tar.xz
Merge #17954: wallet: Remove calls to Chain::Lock methods
48973402d8bccb673eaeb68b7aa86faa39d3cb8a wallet: Avoid use of Chain::Lock in CWallet::GetKeyBirthTimes (Russell Yanofsky) e958ff9ab5607da2cd321f29fc785a6d359e44f4 wallet: Avoid use of Chain::Lock in CWallet::CreateTransaction (Russell Yanofsky) c0d07dc4cba7634cde4e8bf586557772f3248a42 wallet: Avoid use of Chain::Lock in CWallet::ScanForWalletTransactions (Russell Yanofsky) 1be8ff280c78c30baabae9429c53c0bebb89c44d wallet: Avoid use of Chain::Lock in rescanblockchain (Russell Yanofsky) 3cb85ac594f115db99f96b0a0f4bfdcd69ef0590 wallet refactor: Avoid use of Chain::Lock in CWallet::RescanFromTime (Russell Yanofsky) f7ba881bc669451a60fedac58a449794702a3e23 wallet: Avoid use of Chain::Lock in listsinceblock (Russell Yanofsky) bc96a9bfc61afdb696fb92cb644ed5fc3d1793f1 wallet: Avoid use of Chain::Lock in importmulti (Russell Yanofsky) 25a9fcf9e53bfa94e8f8b19a4abfda0f444f6b2a wallet: Avoid use of Chain::Lock in importwallet and dumpwallet (Russell Yanofsky) c1694ce6bb7e19a8722d5583cd85ad17da40bb67 wallet: Avoid use of Chain::Lock in importprunedfunds (Russell Yanofsky) ade5f87971211bc67753f14a0d49e020142efc7c wallet refactor: Avoid use of Chain::Lock in qt wallettests (Russell Yanofsky) f6da44ccce4cfff53433e665305a6fe0a01364e4 wallet: Avoid use of Chain::Lock in tryGetTxStatus and tryGetBalances (Russell Yanofsky) bf30cd4922ea62577d7bf63f5029e8be62665d45 refactor: Add interfaces::FoundBlock class to selectively return block data (Russell Yanofsky) Pull request description: This is a set of changes updating wallet code to make fewer calls to `Chain::Lock` methods, so the `Chain::Lock` class will be easier to remove in #16426 with fewer code changes and small changes to behavior. ACKs for top commit: MarcoFalke: re-ACK 48973402d8, only change is fixing bug 📀 fjahr: re-ACK 48973402d8bccb673eaeb68b7aa86faa39d3cb8a, reviewed rebase and changes since last review, built and ran tests locally ariard: Coce Review ACK 4897340, only changes are one suggested by last review on more accurate variable naming, human-readable output, args comments in `findCommonAncestor` Tree-SHA512: cfd2f559f976b6faaa032794c40c9659191d5597b013abcb6c7968d36b2abb2b14d4e596f8ed8b9a077e96522365261299a241a939b3111eaf729ba0c3ef519b
Diffstat (limited to 'src/wallet')
-rw-r--r--src/wallet/rpcdump.cpp30
-rw-r--r--src/wallet/rpcwallet.cpp46
-rw-r--r--src/wallet/test/wallet_tests.cpp19
-rw-r--r--src/wallet/wallet.cpp130
-rw-r--r--src/wallet/wallet.h8
5 files changed, 113 insertions, 120 deletions
diff --git a/src/wallet/rpcdump.cpp b/src/wallet/rpcdump.cpp
index ea54027c48..6baada3f46 100644
--- a/src/wallet/rpcdump.cpp
+++ b/src/wallet/rpcdump.cpp
@@ -28,6 +28,8 @@
+using interfaces::FoundBlock;
+
std::string static EncodeDumpString(const std::string &str) {
std::stringstream ret;
for (const unsigned char c : str) {
@@ -360,8 +362,9 @@ UniValue importprunedfunds(const JSONRPCRequest& request)
}
auto locked_chain = pwallet->chain().lock();
- Optional<int> height = locked_chain->getBlockHeight(merkleBlock.header.GetHash());
- if (height == nullopt) {
+ LOCK(pwallet->cs_wallet);
+ int height;
+ if (!pwallet->chain().findAncestorByHash(pwallet->GetLastBlockHash(), merkleBlock.header.GetHash(), FoundBlock().height(height))) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found in chain");
}
@@ -372,11 +375,9 @@ UniValue importprunedfunds(const JSONRPCRequest& request)
unsigned int txnIndex = vIndex[it - vMatch.begin()];
- CWalletTx::Confirmation confirm(CWalletTx::Status::CONFIRMED, *height, merkleBlock.header.GetHash(), txnIndex);
+ CWalletTx::Confirmation confirm(CWalletTx::Status::CONFIRMED, height, merkleBlock.header.GetHash(), txnIndex);
wtx.m_confirm = confirm;
- LOCK(pwallet->cs_wallet);
-
if (pwallet->IsMine(*wtx.tx)) {
pwallet->AddToWallet(wtx, false);
return NullUniValue;
@@ -566,8 +567,7 @@ UniValue importwallet(const JSONRPCRequest& request)
if (!file.is_open()) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot open wallet dump file");
}
- Optional<int> tip_height = locked_chain->getHeight();
- nTimeBegin = tip_height ? locked_chain->getBlockTime(*tip_height) : 0;
+ CHECK_NONFATAL(pwallet->chain().findBlock(pwallet->GetLastBlockHash(), FoundBlock().time(nTimeBegin)));
int64_t nFilesize = std::max((int64_t)1, (int64_t)file.tellg());
file.seekg(0, file.beg);
@@ -791,9 +791,10 @@ UniValue dumpwallet(const JSONRPCRequest& request)
// produce output
file << strprintf("# Wallet dump created by Bitcoin %s\n", CLIENT_BUILD);
file << strprintf("# * Created on %s\n", FormatISO8601DateTime(GetTime()));
- const Optional<int> tip_height = locked_chain->getHeight();
- file << strprintf("# * Best block at time of backup was %i (%s),\n", tip_height.get_value_or(-1), tip_height ? locked_chain->getBlockHash(*tip_height).ToString() : "(missing block hash)");
- file << strprintf("# mined on %s\n", tip_height ? FormatISO8601DateTime(locked_chain->getBlockTime(*tip_height)) : "(missing block time)");
+ file << strprintf("# * Best block at time of backup was %i (%s),\n", pwallet->GetLastBlockHeight(), pwallet->GetLastBlockHash().ToString());
+ int64_t block_time = 0;
+ CHECK_NONFATAL(pwallet->chain().findBlock(pwallet->GetLastBlockHash(), FoundBlock().time(block_time)));
+ file << strprintf("# mined on %s\n", FormatISO8601DateTime(block_time));
file << "\n";
// add the base58check encoded extended master if the wallet uses HD
@@ -1379,20 +1380,13 @@ UniValue importmulti(const JSONRPCRequest& mainRequest)
EnsureWalletIsUnlocked(pwallet);
// Verify all timestamps are present before importing any keys.
- const Optional<int> tip_height = locked_chain->getHeight();
- now = tip_height ? locked_chain->getBlockMedianTimePast(*tip_height) : 0;
+ CHECK_NONFATAL(pwallet->chain().findBlock(pwallet->GetLastBlockHash(), FoundBlock().time(nLowestTimestamp).mtpTime(now)));
for (const UniValue& data : requests.getValues()) {
GetImportTimestamp(data, now);
}
const int64_t minimumTimestamp = 1;
- if (fRescan && tip_height) {
- nLowestTimestamp = locked_chain->getBlockTime(*tip_height);
- } else {
- fRescan = false;
- }
-
for (const UniValue& data : requests.getValues()) {
const int64_t timestamp = std::max(GetImportTimestamp(data, now), minimumTimestamp);
const UniValue result = ProcessImport(pwallet, data, timestamp);
diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp
index e98e928a1d..d24d92a178 100644
--- a/src/wallet/rpcwallet.cpp
+++ b/src/wallet/rpcwallet.cpp
@@ -37,6 +37,8 @@
#include <univalue.h>
+using interfaces::FoundBlock;
+
static const std::string WALLET_ENDPOINT_BASE = "/wallet/";
static const std::string HELP_REQUIRING_PASSPHRASE{"\nRequires wallet passphrase to be set with walletpassphrase call if wallet is encrypted.\n"};
@@ -143,8 +145,7 @@ static void WalletTxToJSON(interfaces::Chain& chain, interfaces::Chain::Lock& lo
entry.pushKV("blockheight", wtx.m_confirm.block_height);
entry.pushKV("blockindex", wtx.m_confirm.nIndex);
int64_t block_time;
- bool found_block = chain.findBlock(wtx.m_confirm.hashBlock, nullptr /* block */, &block_time);
- CHECK_NONFATAL(found_block);
+ CHECK_NONFATAL(chain.findBlock(wtx.m_confirm.hashBlock, FoundBlock().time(block_time)));
entry.pushKV("blocktime", block_time);
} else {
entry.pushKV("trusted", wtx.IsTrusted(locked_chain));
@@ -1578,8 +1579,9 @@ static UniValue listsinceblock(const JSONRPCRequest& request)
uint256 blockId;
if (!request.params[0].isNull() && !request.params[0].get_str().empty()) {
blockId = ParseHashV(request.params[0], "blockhash");
- height = locked_chain->findFork(blockId, &altheight);
- if (!height) {
+ height.emplace();
+ altheight.emplace();
+ if (!pwallet->chain().findCommonAncestor(blockId, pwallet->GetLastBlockHash(), /* ancestor out */ FoundBlock().height(*height), /* blockId out */ FoundBlock().height(*altheight))) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found");
}
}
@@ -1598,8 +1600,7 @@ static UniValue listsinceblock(const JSONRPCRequest& request)
bool include_removed = (request.params[3].isNull() || request.params[3].get_bool());
- const Optional<int> tip_height = locked_chain->getHeight();
- int depth = tip_height && height ? (1 + *tip_height - *height) : -1;
+ int depth = height ? pwallet->GetLastBlockHeight() + 1 - *height : -1;
UniValue transactions(UniValue::VARR);
@@ -1616,7 +1617,7 @@ static UniValue listsinceblock(const JSONRPCRequest& request)
UniValue removed(UniValue::VARR);
while (include_removed && altheight && *altheight > *height) {
CBlock block;
- if (!pwallet->chain().findBlock(blockId, &block) || block.IsNull()) {
+ if (!pwallet->chain().findBlock(blockId, FoundBlock().data(block)) || block.IsNull()) {
throw JSONRPCError(RPC_INTERNAL_ERROR, "Can't read block from disk");
}
for (const CTransactionRef& tx : block.vtx) {
@@ -1631,8 +1632,8 @@ static UniValue listsinceblock(const JSONRPCRequest& request)
--*altheight;
}
- int last_height = tip_height ? *tip_height + 1 - target_confirms : -1;
- uint256 lastblock = last_height >= 0 ? locked_chain->getBlockHash(last_height) : uint256();
+ uint256 lastblock;
+ CHECK_NONFATAL(pwallet->chain().findAncestorByHeight(pwallet->GetLastBlockHash(), pwallet->GetLastBlockHeight() + 1 - target_confirms, FoundBlock().hash(lastblock)));
UniValue ret(UniValue::VOBJ);
ret.pushKV("transactions", transactions);
@@ -3541,22 +3542,23 @@ UniValue rescanblockchain(const JSONRPCRequest& request)
}
int start_height = 0;
- uint256 start_block, stop_block;
+ Optional<int> stop_height;
+ uint256 start_block;
{
auto locked_chain = pwallet->chain().lock();
- Optional<int> tip_height = locked_chain->getHeight();
+ LOCK(pwallet->cs_wallet);
+ int tip_height = pwallet->GetLastBlockHeight();
if (!request.params[0].isNull()) {
start_height = request.params[0].get_int();
- if (start_height < 0 || !tip_height || start_height > *tip_height) {
+ if (start_height < 0 || start_height > tip_height) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid start_height");
}
}
- Optional<int> stop_height;
if (!request.params[1].isNull()) {
stop_height = request.params[1].get_int();
- if (*stop_height < 0 || !tip_height || *stop_height > *tip_height) {
+ if (*stop_height < 0 || *stop_height > tip_height) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid stop_height");
}
else if (*stop_height < start_height) {
@@ -3565,25 +3567,15 @@ UniValue rescanblockchain(const JSONRPCRequest& request)
}
// We can't rescan beyond non-pruned blocks, stop and throw an error
- if (locked_chain->findPruned(start_height, stop_height)) {
+ if (!pwallet->chain().hasBlocks(pwallet->GetLastBlockHash(), start_height, stop_height)) {
throw JSONRPCError(RPC_MISC_ERROR, "Can't rescan beyond pruned data. Use RPC call getblockchaininfo to determine your pruned height.");
}
- if (tip_height) {
- start_block = locked_chain->getBlockHash(start_height);
- // If called with a stop_height, set the stop_height here to
- // trigger a rescan to that height.
- // If called without a stop height, leave stop_height as null here
- // so rescan continues to the tip (even if the tip advances during
- // rescan).
- if (stop_height) {
- stop_block = locked_chain->getBlockHash(*stop_height);
- }
- }
+ CHECK_NONFATAL(pwallet->chain().findAncestorByHeight(pwallet->GetLastBlockHash(), start_height, FoundBlock().hash(start_block)));
}
CWallet::ScanResult result =
- pwallet->ScanForWalletTransactions(start_block, stop_block, reserver, true /* fUpdate */);
+ pwallet->ScanForWalletTransactions(start_block, start_height, stop_height, reserver, true /* fUpdate */);
switch (result.status) {
case CWallet::ScanResult::SUCCESS:
break;
diff --git a/src/wallet/test/wallet_tests.cpp b/src/wallet/test/wallet_tests.cpp
index 13e10c1eab..32665f41c8 100644
--- a/src/wallet/test/wallet_tests.cpp
+++ b/src/wallet/test/wallet_tests.cpp
@@ -46,7 +46,7 @@ BOOST_FIXTURE_TEST_CASE(scan_for_wallet_transactions, TestChain100Setup)
auto locked_chain = chain->lock();
LockAssertion lock(::cs_main);
- // Verify ScanForWalletTransactions accommodates a null start block.
+ // Verify ScanForWalletTransactions fails to read an unknown start block.
{
CWallet wallet(chain.get(), WalletLocation(), WalletDatabase::CreateDummy());
{
@@ -56,8 +56,8 @@ BOOST_FIXTURE_TEST_CASE(scan_for_wallet_transactions, TestChain100Setup)
AddKey(wallet, coinbaseKey);
WalletRescanReserver reserver(&wallet);
reserver.reserve();
- CWallet::ScanResult result = wallet.ScanForWalletTransactions({} /* start_block */, {} /* stop_block */, reserver, false /* update */);
- BOOST_CHECK_EQUAL(result.status, CWallet::ScanResult::SUCCESS);
+ CWallet::ScanResult result = wallet.ScanForWalletTransactions({} /* start_block */, 0 /* start_height */, {} /* max_height */, reserver, false /* update */);
+ BOOST_CHECK_EQUAL(result.status, CWallet::ScanResult::FAILURE);
BOOST_CHECK(result.last_failed_block.IsNull());
BOOST_CHECK(result.last_scanned_block.IsNull());
BOOST_CHECK(!result.last_scanned_height);
@@ -75,7 +75,7 @@ BOOST_FIXTURE_TEST_CASE(scan_for_wallet_transactions, TestChain100Setup)
AddKey(wallet, coinbaseKey);
WalletRescanReserver reserver(&wallet);
reserver.reserve();
- CWallet::ScanResult result = wallet.ScanForWalletTransactions(oldTip->GetBlockHash(), {} /* stop_block */, reserver, false /* update */);
+ CWallet::ScanResult result = wallet.ScanForWalletTransactions(oldTip->GetBlockHash(), oldTip->nHeight, {} /* max_height */, reserver, false /* update */);
BOOST_CHECK_EQUAL(result.status, CWallet::ScanResult::SUCCESS);
BOOST_CHECK(result.last_failed_block.IsNull());
BOOST_CHECK_EQUAL(result.last_scanned_block, newTip->GetBlockHash());
@@ -98,7 +98,7 @@ BOOST_FIXTURE_TEST_CASE(scan_for_wallet_transactions, TestChain100Setup)
AddKey(wallet, coinbaseKey);
WalletRescanReserver reserver(&wallet);
reserver.reserve();
- CWallet::ScanResult result = wallet.ScanForWalletTransactions(oldTip->GetBlockHash(), {} /* stop_block */, reserver, false /* update */);
+ CWallet::ScanResult result = wallet.ScanForWalletTransactions(oldTip->GetBlockHash(), oldTip->nHeight, {} /* max_height */, reserver, false /* update */);
BOOST_CHECK_EQUAL(result.status, CWallet::ScanResult::FAILURE);
BOOST_CHECK_EQUAL(result.last_failed_block, oldTip->GetBlockHash());
BOOST_CHECK_EQUAL(result.last_scanned_block, newTip->GetBlockHash());
@@ -120,7 +120,7 @@ BOOST_FIXTURE_TEST_CASE(scan_for_wallet_transactions, TestChain100Setup)
AddKey(wallet, coinbaseKey);
WalletRescanReserver reserver(&wallet);
reserver.reserve();
- CWallet::ScanResult result = wallet.ScanForWalletTransactions(oldTip->GetBlockHash(), {} /* stop_block */, reserver, false /* update */);
+ CWallet::ScanResult result = wallet.ScanForWalletTransactions(oldTip->GetBlockHash(), oldTip->nHeight, {} /* max_height */, reserver, false /* update */);
BOOST_CHECK_EQUAL(result.status, CWallet::ScanResult::FAILURE);
BOOST_CHECK_EQUAL(result.last_failed_block, newTip->GetBlockHash());
BOOST_CHECK(result.last_scanned_block.IsNull());
@@ -152,6 +152,7 @@ BOOST_FIXTURE_TEST_CASE(importmulti_rescan, TestChain100Setup)
{
std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(chain.get(), WalletLocation(), WalletDatabase::CreateDummy());
wallet->SetupLegacyScriptPubKeyMan();
+ WITH_LOCK(wallet->cs_wallet, wallet->SetLastBlockProcessed(newTip->nHeight, newTip->GetBlockHash()));
AddWallet(wallet);
UniValue keys;
keys.setArray();
@@ -225,6 +226,7 @@ BOOST_FIXTURE_TEST_CASE(importwallet_rescan, TestChain100Setup)
request.params.setArray();
request.params.push_back(backup_file);
AddWallet(wallet);
+ wallet->SetLastBlockProcessed(::ChainActive().Height(), ::ChainActive().Tip()->GetBlockHash());
::dumpwallet(request);
RemoveWallet(wallet);
}
@@ -233,16 +235,17 @@ BOOST_FIXTURE_TEST_CASE(importwallet_rescan, TestChain100Setup)
// were scanned, and no prior blocks were scanned.
{
std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(chain.get(), WalletLocation(), WalletDatabase::CreateDummy());
+ LOCK(wallet->cs_wallet);
wallet->SetupLegacyScriptPubKeyMan();
JSONRPCRequest request;
request.params.setArray();
request.params.push_back(backup_file);
AddWallet(wallet);
+ wallet->SetLastBlockProcessed(::ChainActive().Height(), ::ChainActive().Tip()->GetBlockHash());
::importwallet(request);
RemoveWallet(wallet);
- LOCK(wallet->cs_wallet);
BOOST_CHECK_EQUAL(wallet->mapWallet.size(), 3U);
BOOST_CHECK_EQUAL(m_coinbase_txns.size(), 103U);
for (size_t i = 0; i < m_coinbase_txns.size(); ++i) {
@@ -462,7 +465,7 @@ public:
AddKey(*wallet, coinbaseKey);
WalletRescanReserver reserver(wallet.get());
reserver.reserve();
- CWallet::ScanResult result = wallet->ScanForWalletTransactions(::ChainActive().Genesis()->GetBlockHash(), {} /* stop_block */, reserver, false /* update */);
+ CWallet::ScanResult result = wallet->ScanForWalletTransactions(::ChainActive().Genesis()->GetBlockHash(), 0 /* start_height */, {} /* max_height */, reserver, false /* update */);
BOOST_CHECK_EQUAL(result.status, CWallet::ScanResult::SUCCESS);
BOOST_CHECK_EQUAL(result.last_scanned_block, ::ChainActive().Tip()->GetBlockHash());
BOOST_CHECK_EQUAL(*result.last_scanned_height, ::ChainActive().Height());
diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp
index 45f5542cad..519f022cc8 100644
--- a/src/wallet/wallet.cpp
+++ b/src/wallet/wallet.cpp
@@ -22,6 +22,7 @@
#include <script/script.h>
#include <script/signingprovider.h>
#include <util/bip32.h>
+#include <util/check.h>
#include <util/error.h>
#include <util/fees.h>
#include <util/moneystr.h>
@@ -35,6 +36,8 @@
#include <boost/algorithm/string/replace.hpp>
+using interfaces::FoundBlock;
+
const std::map<uint64_t,std::string> WALLET_FLAG_CAVEATS{
{WALLET_FLAG_AVOID_REUSE,
"You need to rescan the blockchain in order to correctly mark used "
@@ -1594,22 +1597,17 @@ int64_t CWallet::RescanFromTime(int64_t startTime, const WalletRescanReserver& r
// Find starting block. May be null if nCreateTime is greater than the
// highest blockchain timestamp, in which case there is nothing that needs
// to be scanned.
+ int start_height = 0;
uint256 start_block;
- {
- auto locked_chain = chain().lock();
- const Optional<int> start_height = locked_chain->findFirstBlockWithTimeAndHeight(startTime - TIMESTAMP_WINDOW, 0, &start_block);
- const Optional<int> tip_height = locked_chain->getHeight();
- WalletLogPrintf("%s: Rescanning last %i blocks\n", __func__, tip_height && start_height ? *tip_height - *start_height + 1 : 0);
- }
+ 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);
- if (!start_block.IsNull()) {
+ if (start) {
// TODO: this should take into account failure by ScanResult::USER_ABORT
- ScanResult result = ScanForWalletTransactions(start_block, {} /* stop_block */, reserver, update);
+ ScanResult result = ScanForWalletTransactions(start_block, start_height, {} /* max_height */, reserver, update);
if (result.status == ScanResult::FAILURE) {
int64_t time_max;
- if (!chain().findBlock(result.last_failed_block, nullptr /* block */, nullptr /* time */, &time_max)) {
- throw std::logic_error("ScanForWalletTransactions returned invalid block hash");
- }
+ CHECK_NONFATAL(chain().findBlock(result.last_failed_block, FoundBlock().maxTime(time_max)));
return time_max + TIMESTAMP_WINDOW + 1;
}
}
@@ -1623,9 +1621,9 @@ int64_t CWallet::RescanFromTime(int64_t startTime, const WalletRescanReserver& r
*
* @param[in] start_block Scan starting block. If block is not on the active
* chain, the scan will return SUCCESS immediately.
- * @param[in] stop_block Scan ending block. If block is not on the active
- * chain, the scan will continue until it reaches the
- * chain tip.
+ * @param[in] start_height Height of start_block
+ * @param[in] max_height Optional max scanning height. If unset there is
+ * no maximum and scanning can continue to the tip
*
* @return ScanResult returning scan information and indicating success or
* failure. Return status will be set to SUCCESS if scan was
@@ -1637,7 +1635,7 @@ int64_t CWallet::RescanFromTime(int64_t startTime, const WalletRescanReserver& r
* the main chain after to the addition of any new keys you want to detect
* transactions for.
*/
-CWallet::ScanResult CWallet::ScanForWalletTransactions(const uint256& start_block, const uint256& stop_block, const WalletRescanReserver& reserver, bool fUpdate)
+CWallet::ScanResult CWallet::ScanForWalletTransactions(const uint256& start_block, int start_height, Optional<int> max_height, const WalletRescanReserver& reserver, bool fUpdate)
{
int64_t nNow = GetTime();
int64_t start_time = GetTimeMillis();
@@ -1651,36 +1649,32 @@ CWallet::ScanResult CWallet::ScanForWalletTransactions(const uint256& start_bloc
fAbortRescan = false;
ShowProgress(strprintf("%s " + _("Rescanning...").translated, GetDisplayName()), 0); // show rescan progress in GUI as dialog or on splashscreen, if -rescan on startup
- uint256 tip_hash;
- // The way the 'block_height' is initialized is just a workaround for the gcc bug #47679 since version 4.6.0.
- Optional<int> block_height = MakeOptional(false, int());
- double progress_begin;
- double progress_end;
- {
- auto locked_chain = chain().lock();
- if (Optional<int> tip_height = locked_chain->getHeight()) {
- tip_hash = locked_chain->getBlockHash(*tip_height);
- }
- block_height = locked_chain->getBlockHeight(block_hash);
- progress_begin = chain().guessVerificationProgress(block_hash);
- progress_end = chain().guessVerificationProgress(stop_block.IsNull() ? tip_hash : stop_block);
- }
+ uint256 tip_hash = WITH_LOCK(cs_wallet, return GetLastBlockHash());
+ 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);
+ double progress_end = chain().guessVerificationProgress(end_hash);
double progress_current = progress_begin;
- while (block_height && !fAbortRescan && !chain().shutdownRequested()) {
+ int block_height = start_height;
+ while (!fAbortRescan && !chain().shutdownRequested()) {
m_scanning_progress = (progress_current - progress_begin) / (progress_end - progress_begin);
- if (*block_height % 100 == 0 && progress_end - progress_begin > 0.0) {
+ if (block_height % 100 == 0 && progress_end - progress_begin > 0.0) {
ShowProgress(strprintf("%s " + _("Rescanning...").translated, GetDisplayName()), std::max(1, std::min(99, (int)(m_scanning_progress * 100))));
}
if (GetTime() >= nNow + 60) {
nNow = GetTime();
- WalletLogPrintf("Still rescanning. At block %d. Progress=%f\n", *block_height, progress_current);
+ WalletLogPrintf("Still rescanning. At block %d. Progress=%f\n", block_height, progress_current);
}
CBlock block;
- if (chain().findBlock(block_hash, &block) && !block.IsNull()) {
+ bool next_block;
+ uint256 next_block_hash;
+ bool reorg = false;
+ if (chain().findBlock(block_hash, FoundBlock().data(block)) && !block.IsNull()) {
auto locked_chain = chain().lock();
LOCK(cs_wallet);
- if (!locked_chain->getBlockHeight(block_hash)) {
+ next_block = chain().findNextBlock(block_hash, block_height, FoundBlock().hash(next_block_hash), &reorg);
+ if (reorg) {
// Abort scan if current block is no longer active, to prevent
// marking transactions as coming from the wrong block.
// TODO: This should return success instead of failure, see
@@ -1690,37 +1684,38 @@ CWallet::ScanResult CWallet::ScanForWalletTransactions(const uint256& start_bloc
break;
}
for (size_t posInBlock = 0; posInBlock < block.vtx.size(); ++posInBlock) {
- CWalletTx::Confirmation confirm(CWalletTx::Status::CONFIRMED, *block_height, block_hash, posInBlock);
+ CWalletTx::Confirmation confirm(CWalletTx::Status::CONFIRMED, block_height, block_hash, posInBlock);
SyncTransaction(block.vtx[posInBlock], confirm, fUpdate);
}
// scan succeeded, record block as most recent successfully scanned
result.last_scanned_block = block_hash;
- result.last_scanned_height = *block_height;
+ result.last_scanned_height = block_height;
} else {
// could not scan block, keep scanning but record this block as the most recent failure
result.last_failed_block = block_hash;
result.status = ScanResult::FAILURE;
+ next_block = chain().findNextBlock(block_hash, block_height, FoundBlock().hash(next_block_hash), &reorg);
}
- if (block_hash == stop_block) {
+ if (max_height && block_height >= *max_height) {
break;
}
{
auto locked_chain = chain().lock();
- Optional<int> tip_height = locked_chain->getHeight();
- if (!tip_height || *tip_height <= block_height || !locked_chain->getBlockHeight(block_hash)) {
+ if (!next_block || reorg) {
// break successfully when rescan has reached the tip, or
// previous block is no longer on the chain due to a reorg
break;
}
// increment block and verification progress
- block_hash = locked_chain->getBlockHash(++*block_height);
+ block_hash = next_block_hash;
+ ++block_height;
progress_current = chain().guessVerificationProgress(block_hash);
// handle updated tip hash
const uint256 prev_tip_hash = tip_hash;
- tip_hash = locked_chain->getBlockHash(*tip_height);
- if (stop_block.IsNull() && prev_tip_hash != tip_hash) {
+ tip_hash = WITH_LOCK(cs_wallet, return GetLastBlockHash());
+ if (!max_height && prev_tip_hash != tip_hash) {
// in case the tip has changed, update progress max
progress_end = chain().guessVerificationProgress(tip_hash);
}
@@ -1728,10 +1723,10 @@ CWallet::ScanResult CWallet::ScanForWalletTransactions(const uint256& start_bloc
}
ShowProgress(strprintf("%s " + _("Rescanning...").translated, GetDisplayName()), 100); // hide progress dialog in GUI
if (block_height && fAbortRescan) {
- WalletLogPrintf("Rescan aborted at block %d. Progress=%f\n", *block_height, progress_current);
+ WalletLogPrintf("Rescan aborted at block %d. Progress=%f\n", block_height, progress_current);
result.status = ScanResult::USER_ABORT;
} else if (block_height && chain().shutdownRequested()) {
- WalletLogPrintf("Rescan interrupted by shutdown request at block %d. Progress=%f\n", *block_height, progress_current);
+ WalletLogPrintf("Rescan interrupted by shutdown request at block %d. Progress=%f\n", block_height, progress_current);
result.status = ScanResult::USER_ABORT;
} else {
WalletLogPrintf("Rescan completed in %15dms\n", GetTimeMillis() - start_time);
@@ -2631,13 +2626,15 @@ bool CWallet::FundTransaction(CMutableTransaction& tx, CAmount& nFeeRet, int& nC
return true;
}
-static bool IsCurrentForAntiFeeSniping(interfaces::Chain& chain, interfaces::Chain::Lock& locked_chain)
+static bool IsCurrentForAntiFeeSniping(interfaces::Chain& chain, const uint256& block_hash)
{
if (chain.isInitialBlockDownload()) {
return false;
}
constexpr int64_t MAX_ANTI_FEE_SNIPING_TIP_AGE = 8 * 60 * 60; // in seconds
- if (locked_chain.getBlockTime(*locked_chain.getHeight()) < (GetTime() - MAX_ANTI_FEE_SNIPING_TIP_AGE)) {
+ int64_t block_time;
+ CHECK_NONFATAL(chain.findBlock(block_hash, FoundBlock().time(block_time)));
+ if (block_time < (GetTime() - MAX_ANTI_FEE_SNIPING_TIP_AGE)) {
return false;
}
return true;
@@ -2647,9 +2644,8 @@ static bool IsCurrentForAntiFeeSniping(interfaces::Chain& chain, interfaces::Cha
* Return a height-based locktime for new transactions (uses the height of the
* current chain tip unless we are not synced with the current chain
*/
-static uint32_t GetLocktimeForNewTransaction(interfaces::Chain& chain, interfaces::Chain::Lock& locked_chain)
+static uint32_t GetLocktimeForNewTransaction(interfaces::Chain& chain, const uint256& block_hash, int block_height)
{
- uint32_t const height = locked_chain.getHeight().get_value_or(-1);
uint32_t locktime;
// Discourage fee sniping.
//
@@ -2671,8 +2667,8 @@ static uint32_t GetLocktimeForNewTransaction(interfaces::Chain& chain, interface
// enough, that fee sniping isn't a problem yet, but by implementing a fix
// now we ensure code won't be written that makes assumptions about
// nLockTime that preclude a fix later.
- if (IsCurrentForAntiFeeSniping(chain, locked_chain)) {
- locktime = height;
+ if (IsCurrentForAntiFeeSniping(chain, block_hash)) {
+ locktime = block_height;
// Secondly occasionally randomly pick a nLockTime even further back, so
// that transactions that are delayed after signing for whatever reason,
@@ -2686,7 +2682,6 @@ static uint32_t GetLocktimeForNewTransaction(interfaces::Chain& chain, interface
// unique "nLockTime fingerprint", set nLockTime to a constant.
locktime = 0;
}
- assert(locktime <= height);
assert(locktime < LOCKTIME_THRESHOLD);
return locktime;
}
@@ -2746,9 +2741,6 @@ bool CWallet::CreateTransaction(interfaces::Chain::Lock& locked_chain, const std
}
CMutableTransaction txNew;
-
- txNew.nLockTime = GetLocktimeForNewTransaction(chain(), locked_chain);
-
FeeCalculation feeCalc;
CAmount nFeeNeeded;
int nBytes;
@@ -2756,6 +2748,7 @@ bool CWallet::CreateTransaction(interfaces::Chain::Lock& locked_chain, const std
std::set<CInputCoin> setCoins;
auto locked_chain = chain().lock();
LOCK(cs_wallet);
+ txNew.nLockTime = GetLocktimeForNewTransaction(chain(), GetLastBlockHash(), GetLastBlockHeight());
{
std::vector<COutput> vAvailableCoins;
AvailableCoins(*locked_chain, vAvailableCoins, true, &coin_control, 1, MAX_MONEY, MAX_MONEY, 0);
@@ -3576,12 +3569,13 @@ void CWallet::GetKeyBirthTimes(interfaces::Chain::Lock& locked_chain, std::map<C
}
// map in which we'll infer heights of other keys
- const Optional<int> tip_height = locked_chain.getHeight();
- const int max_height = tip_height && *tip_height > 144 ? *tip_height - 144 : 0; // the tip can be reorganized; use a 144-block safety margin
- std::map<CKeyID, int> mapKeyFirstBlock;
+ std::map<CKeyID, const CWalletTx::Confirmation*> mapKeyFirstBlock;
+ CWalletTx::Confirmation max_confirm;
+ max_confirm.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.block_height, FoundBlock().hash(max_confirm.hashBlock)));
for (const CKeyID &keyid : spk_man->GetKeys()) {
if (mapKeyBirth.count(keyid) == 0)
- mapKeyFirstBlock[keyid] = max_height;
+ mapKeyFirstBlock[keyid] = &max_confirm;
}
// if there are no such keys, we're done
@@ -3592,23 +3586,27 @@ void CWallet::GetKeyBirthTimes(interfaces::Chain::Lock& locked_chain, std::map<C
for (const auto& entry : mapWallet) {
// iterate over all wallet transactions...
const CWalletTx &wtx = entry.second;
- if (Optional<int> height = locked_chain.getBlockHeight(wtx.m_confirm.hashBlock)) {
+ if (wtx.m_confirm.status == CWalletTx::CONFIRMED) {
// ... which are already in a block
for (const CTxOut &txout : wtx.tx->vout) {
// iterate over all their outputs
for (const auto &keyid : GetAffectedKeys(txout.scriptPubKey, *spk_man)) {
// ... and all their affected keys
- std::map<CKeyID, int>::iterator rit = mapKeyFirstBlock.find(keyid);
- if (rit != mapKeyFirstBlock.end() && *height < rit->second)
- rit->second = *height;
+ auto rit = mapKeyFirstBlock.find(keyid);
+ if (rit != mapKeyFirstBlock.end() && wtx.m_confirm.block_height < rit->second->block_height) {
+ rit->second = &wtx.m_confirm;
+ }
}
}
}
}
// Extract block timestamps for those keys
- for (const auto& entry : mapKeyFirstBlock)
- mapKeyBirth[entry.first] = locked_chain.getBlockTime(entry.second) - TIMESTAMP_WINDOW; // block times can be 2h off
+ for (const auto& entry : mapKeyFirstBlock) {
+ int64_t block_time;
+ CHECK_NONFATAL(chain().findBlock(entry.second->hashBlock, FoundBlock().time(block_time)));
+ mapKeyBirth[entry.first] = block_time - TIMESTAMP_WINDOW; // block times can be 2h off
+ }
}
/**
@@ -3637,7 +3635,7 @@ unsigned int CWallet::ComputeTimeSmart(const CWalletTx& wtx) const
unsigned int nTimeSmart = wtx.nTimeReceived;
if (!wtx.isUnconfirmed() && !wtx.isAbandoned()) {
int64_t blocktime;
- if (chain().findBlock(wtx.m_confirm.hashBlock, nullptr /* block */, &blocktime)) {
+ if (chain().findBlock(wtx.m_confirm.hashBlock, FoundBlock().time(blocktime))) {
int64_t latestNow = wtx.nTimeReceived;
int64_t latestEntry = 0;
@@ -4065,7 +4063,7 @@ std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(interfaces::Chain& chain,
{
WalletRescanReserver reserver(walletInstance.get());
- if (!reserver.reserve() || (ScanResult::SUCCESS != walletInstance->ScanForWalletTransactions(locked_chain->getBlockHash(rescan_height), {} /* stop block */, reserver, true /* update */).status)) {
+ if (!reserver.reserve() || (ScanResult::SUCCESS != walletInstance->ScanForWalletTransactions(locked_chain->getBlockHash(rescan_height), rescan_height, {} /* max height */, reserver, true /* update */).status)) {
error = _("Failed to rescan the wallet during initialization").translated;
return nullptr;
}
diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h
index 6c54c72e76..577a739d89 100644
--- a/src/wallet/wallet.h
+++ b/src/wallet/wallet.h
@@ -907,7 +907,7 @@ public:
//! USER_ABORT.
uint256 last_failed_block;
};
- ScanResult ScanForWalletTransactions(const uint256& first_block, const uint256& last_block, const WalletRescanReserver& reserver, bool fUpdate);
+ ScanResult ScanForWalletTransactions(const uint256& start_block, int start_height, Optional<int> max_height, const WalletRescanReserver& reserver, bool fUpdate);
void transactionRemovedFromMempool(const CTransactionRef &ptx) override;
void ReacceptWalletTransactions() EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
void ResendWalletTransactions();
@@ -1213,6 +1213,12 @@ public:
assert(m_last_block_processed_height >= 0);
return m_last_block_processed_height;
};
+ uint256 GetLastBlockHash() const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet)
+ {
+ AssertLockHeld(cs_wallet);
+ assert(m_last_block_processed_height >= 0);
+ return m_last_block_processed;
+ }
/** Set last block processed height, currently only use in unit test */
void SetLastBlockProcessed(int block_height, uint256 block_hash) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet)
{