mirror of
https://github.com/bitcoin/bitcoin.git
synced 2026-06-08 21:59:10 +02:00
Merge bitcoin/bitcoin#20591: wallet, bugfix: fix ComputeTimeSmart function during rescanning process.
240ea294d5doc: update doxygen documention of ComputeTimeSmart() and AddToWalletIfInvolvingMe() regarding rescanning_old_block parameter (BitcoinTsunami)d6eb39af21test: add functional test to check transaction time determination during block rescanning (BitcoinTsunami)07b44f16e7wallet: fix ComputeTimeSmart algorithm to use blocktime during old block rescanning (BitcoinTsunami) Pull request description: The function ComputeTimeSmart in wallet.cpp assume that transaction are discovered in the right order. Moreover the 'smarttime' determination algorithm is coded with realtime scenario in mind and not rescanning of old block. The functional test demonstrate that if the user import a wallet, then rescan only recent history, and then rescan the entire history, the older transaction discovered would have an incorrect time determination. In the context of rescanning old block, the only time value that as a meaning is the blocktime. That's why I've fixed the problem with a simple separation between rescanning of old block and realtime time determination. The fix is written to have no impact on every realtime scenario and only impact the behaviour during a rescanning process. This PR Fixes #20181. To be fair, I don't think that this bug could be triggered with the wallet GUI, because it always proceed with a proper rescan. But RPC API provide the possibility to trigger it. I've discovered it, because Specter desktop v0.10.0 was impacted. (https://github.com/cryptoadvance/specter-desktop/issues/680). ACKs for top commit: jonatack: ACK240ea294d5per `git diff b92d552 240ea29`, re-verified rebase to latest master + debug build clean + new test passes on the branch and fails on master, only change since my review a few hours ago is incorporation of latest review suggestions meshcollider: re-utACK240ea294d5Tree-SHA512: 514b02e41d011ddfa325f5e8080b93800e1ea4ed5853fa420670a6ac700e8b463000dbea65f8ced8565cfb950c7f51b69154034dcb111e67aca3b964a0061494
This commit is contained in:
@@ -884,7 +884,7 @@ bool CWallet::IsSpentKey(const uint256& hash, unsigned int n) const
|
||||
return false;
|
||||
}
|
||||
|
||||
CWalletTx* CWallet::AddToWallet(CTransactionRef tx, const CWalletTx::Confirmation& confirm, const UpdateWalletTxFn& update_wtx, bool fFlushOnClose)
|
||||
CWalletTx* CWallet::AddToWallet(CTransactionRef tx, const CWalletTx::Confirmation& confirm, const UpdateWalletTxFn& update_wtx, bool fFlushOnClose, bool rescanning_old_block)
|
||||
{
|
||||
LOCK(cs_wallet);
|
||||
|
||||
@@ -914,7 +914,7 @@ CWalletTx* CWallet::AddToWallet(CTransactionRef tx, const CWalletTx::Confirmatio
|
||||
wtx.nTimeReceived = chain().getAdjustedTime();
|
||||
wtx.nOrderPos = IncOrderPosNext(&batch);
|
||||
wtx.m_it_wtxOrdered = wtxOrdered.insert(std::make_pair(wtx.nOrderPos, &wtx));
|
||||
wtx.nTimeSmart = ComputeTimeSmart(wtx);
|
||||
wtx.nTimeSmart = ComputeTimeSmart(wtx, rescanning_old_block);
|
||||
AddToSpends(hash, &batch);
|
||||
}
|
||||
|
||||
@@ -1031,7 +1031,7 @@ bool CWallet::LoadToWallet(const uint256& hash, const UpdateWalletTxFn& fill_wtx
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CWallet::AddToWalletIfInvolvingMe(const CTransactionRef& ptx, CWalletTx::Confirmation confirm, bool fUpdate)
|
||||
bool CWallet::AddToWalletIfInvolvingMe(const CTransactionRef& ptx, CWalletTx::Confirmation confirm, bool fUpdate, bool rescanning_old_block)
|
||||
{
|
||||
const CTransaction& tx = *ptx;
|
||||
{
|
||||
@@ -1069,7 +1069,7 @@ bool CWallet::AddToWalletIfInvolvingMe(const CTransactionRef& ptx, CWalletTx::Co
|
||||
|
||||
// Block disconnection override an abandoned tx as unconfirmed
|
||||
// which means user may have to call abandontransaction again
|
||||
return AddToWallet(MakeTransactionRef(tx), confirm, /* update_wtx= */ nullptr, /* fFlushOnClose= */ false);
|
||||
return AddToWallet(MakeTransactionRef(tx), confirm, /* update_wtx= */ nullptr, /* fFlushOnClose= */ false, rescanning_old_block);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
@@ -1198,9 +1198,9 @@ void CWallet::MarkConflicted(const uint256& hashBlock, int conflicting_height, c
|
||||
}
|
||||
}
|
||||
|
||||
void CWallet::SyncTransaction(const CTransactionRef& ptx, CWalletTx::Confirmation confirm, bool update_tx)
|
||||
void CWallet::SyncTransaction(const CTransactionRef& ptx, CWalletTx::Confirmation confirm, bool update_tx, bool rescanning_old_block)
|
||||
{
|
||||
if (!AddToWalletIfInvolvingMe(ptx, confirm, update_tx))
|
||||
if (!AddToWalletIfInvolvingMe(ptx, confirm, update_tx, rescanning_old_block))
|
||||
return; // Not one of ours
|
||||
|
||||
// If a transaction changes 'conflicted' state, that changes the balance
|
||||
@@ -1643,7 +1643,7 @@ CWallet::ScanResult CWallet::ScanForWalletTransactions(const uint256& start_bloc
|
||||
break;
|
||||
}
|
||||
for (size_t posInBlock = 0; posInBlock < block.vtx.size(); ++posInBlock) {
|
||||
SyncTransaction(block.vtx[posInBlock], {CWalletTx::Status::CONFIRMED, block_height, block_hash, (int)posInBlock}, fUpdate);
|
||||
SyncTransaction(block.vtx[posInBlock], {CWalletTx::Status::CONFIRMED, block_height, block_hash, (int)posInBlock}, fUpdate, /* rescanning_old_block */ true);
|
||||
}
|
||||
// scan succeeded, record block as most recent successfully scanned
|
||||
result.last_scanned_block = block_hash;
|
||||
@@ -2384,6 +2384,8 @@ void CWallet::GetKeyBirthTimes(std::map<CKeyID, int64_t>& mapKeyBirth) const {
|
||||
* - If sending a transaction, assign its timestamp to the current time.
|
||||
* - If receiving a transaction outside a block, assign its timestamp to the
|
||||
* current time.
|
||||
* - If receiving a transaction during a rescanning process, assign all its
|
||||
* (not already known) transactions' timestamps to the block time.
|
||||
* - If receiving a block with a future timestamp, assign all its (not already
|
||||
* known) transactions' timestamps to the current time.
|
||||
* - If receiving a block with a past timestamp, before the most recent known
|
||||
@@ -2398,38 +2400,43 @@ void CWallet::GetKeyBirthTimes(std::map<CKeyID, int64_t>& mapKeyBirth) const {
|
||||
* https://bitcointalk.org/?topic=54527, or
|
||||
* https://github.com/bitcoin/bitcoin/pull/1393.
|
||||
*/
|
||||
unsigned int CWallet::ComputeTimeSmart(const CWalletTx& wtx) const
|
||||
unsigned int CWallet::ComputeTimeSmart(const CWalletTx& wtx, bool rescanning_old_block) const
|
||||
{
|
||||
unsigned int nTimeSmart = wtx.nTimeReceived;
|
||||
if (!wtx.isUnconfirmed() && !wtx.isAbandoned()) {
|
||||
int64_t blocktime;
|
||||
if (chain().findBlock(wtx.m_confirm.hashBlock, FoundBlock().time(blocktime))) {
|
||||
int64_t latestNow = wtx.nTimeReceived;
|
||||
int64_t latestEntry = 0;
|
||||
int64_t block_max_time;
|
||||
if (chain().findBlock(wtx.m_confirm.hashBlock, FoundBlock().time(blocktime).maxTime(block_max_time))) {
|
||||
if (rescanning_old_block) {
|
||||
nTimeSmart = block_max_time;
|
||||
} else {
|
||||
int64_t latestNow = wtx.nTimeReceived;
|
||||
int64_t latestEntry = 0;
|
||||
|
||||
// Tolerate times up to the last timestamp in the wallet not more than 5 minutes into the future
|
||||
int64_t latestTolerated = latestNow + 300;
|
||||
const TxItems& txOrdered = wtxOrdered;
|
||||
for (auto it = txOrdered.rbegin(); it != txOrdered.rend(); ++it) {
|
||||
CWalletTx* const pwtx = it->second;
|
||||
if (pwtx == &wtx) {
|
||||
continue;
|
||||
}
|
||||
int64_t nSmartTime;
|
||||
nSmartTime = pwtx->nTimeSmart;
|
||||
if (!nSmartTime) {
|
||||
nSmartTime = pwtx->nTimeReceived;
|
||||
}
|
||||
if (nSmartTime <= latestTolerated) {
|
||||
latestEntry = nSmartTime;
|
||||
if (nSmartTime > latestNow) {
|
||||
latestNow = nSmartTime;
|
||||
// Tolerate times up to the last timestamp in the wallet not more than 5 minutes into the future
|
||||
int64_t latestTolerated = latestNow + 300;
|
||||
const TxItems& txOrdered = wtxOrdered;
|
||||
for (auto it = txOrdered.rbegin(); it != txOrdered.rend(); ++it) {
|
||||
CWalletTx* const pwtx = it->second;
|
||||
if (pwtx == &wtx) {
|
||||
continue;
|
||||
}
|
||||
int64_t nSmartTime;
|
||||
nSmartTime = pwtx->nTimeSmart;
|
||||
if (!nSmartTime) {
|
||||
nSmartTime = pwtx->nTimeReceived;
|
||||
}
|
||||
if (nSmartTime <= latestTolerated) {
|
||||
latestEntry = nSmartTime;
|
||||
if (nSmartTime > latestNow) {
|
||||
latestNow = nSmartTime;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
nTimeSmart = std::max(latestEntry, std::min(blocktime, latestNow));
|
||||
nTimeSmart = std::max(latestEntry, std::min(blocktime, latestNow));
|
||||
}
|
||||
} else {
|
||||
WalletLogPrintf("%s: found %s in block %s not in index\n", __func__, wtx.GetHash().ToString(), wtx.m_confirm.hashBlock.ToString());
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user