Skip to content

Commit 3178b2c

Browse files
committed
Merge #9369: Factor out CWallet::nTimeSmart computation into a method.
630fc54 Clean up braces in CWallet::ComputeTimeSmart (Russell Yanofsky) 6c996c2 Add documentation describing CWallet::nTimeSmart. (Russell Yanofsky) 1f98abe Factor out CWallet::nTimeSmart computation into a method. (Russell Yanofsky) c6b82d1 Add tests for CWalletTx::nTimeSmart (Russell Yanofsky) Tree-SHA512: 457a30251e572cf20dac0198af1a94128d269b1e0ce6605a213d56fc14d85c84a0a494e3dcbb18c201c4f39e6f7b000bd9cb6f283930d8452e4bb93ba406f8d4
2 parents 6015df5 + 630fc54 commit 3178b2c

File tree

3 files changed

+129
-45
lines changed

3 files changed

+129
-45
lines changed

src/wallet/test/wallet_tests.cpp

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -453,4 +453,57 @@ BOOST_FIXTURE_TEST_CASE(coin_mark_dirty_immature_credit, TestChain100Setup)
453453
BOOST_CHECK_EQUAL(wtx.GetImmatureCredit(), 50*COIN);
454454
}
455455

456+
static int64_t AddTx(CWallet& wallet, uint32_t lockTime, int64_t mockTime, int64_t blockTime)
457+
{
458+
CMutableTransaction tx;
459+
tx.nLockTime = lockTime;
460+
SetMockTime(mockTime);
461+
CBlockIndex* block = nullptr;
462+
if (blockTime > 0) {
463+
auto inserted = mapBlockIndex.emplace(GetRandHash(), new CBlockIndex);
464+
assert(inserted.second);
465+
const uint256& hash = inserted.first->first;
466+
block = inserted.first->second;
467+
block->nTime = blockTime;
468+
block->phashBlock = &hash;
469+
}
470+
471+
CWalletTx wtx(&wallet, MakeTransactionRef(tx));
472+
if (block) {
473+
wtx.SetMerkleBranch(block, 0);
474+
}
475+
wallet.AddToWallet(wtx);
476+
return wallet.mapWallet.at(wtx.GetHash()).nTimeSmart;
477+
}
478+
479+
// Simple test to verify assignment of CWalletTx::nSmartTime value. Could be
480+
// expanded to cover more corner cases of smart time logic.
481+
BOOST_AUTO_TEST_CASE(ComputeTimeSmart)
482+
{
483+
CWallet wallet;
484+
485+
// New transaction should use clock time if lower than block time.
486+
BOOST_CHECK_EQUAL(AddTx(wallet, 1, 100, 120), 100);
487+
488+
// Test that updating existing transaction does not change smart time.
489+
BOOST_CHECK_EQUAL(AddTx(wallet, 1, 200, 220), 100);
490+
491+
// New transaction should use clock time if there's no block time.
492+
BOOST_CHECK_EQUAL(AddTx(wallet, 2, 300, 0), 300);
493+
494+
// New transaction should use block time if lower than clock time.
495+
BOOST_CHECK_EQUAL(AddTx(wallet, 3, 420, 400), 400);
496+
497+
// New transaction should use latest entry time if higher than
498+
// min(block time, clock time).
499+
BOOST_CHECK_EQUAL(AddTx(wallet, 4, 500, 390), 400);
500+
501+
// If there are future entries, new transaction should use time of the
502+
// newest entry that is no more than 300 seconds ahead of the clock time.
503+
BOOST_CHECK_EQUAL(AddTx(wallet, 5, 50, 600), 300);
504+
505+
// Reset mock time for other tests.
506+
SetMockTime(0);
507+
}
508+
456509
BOOST_AUTO_TEST_SUITE_END()

src/wallet/wallet.cpp

Lines changed: 66 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -870,51 +870,7 @@ bool CWallet::AddToWallet(const CWalletTx& wtxIn, bool fFlushOnClose)
870870
wtx.nTimeReceived = GetAdjustedTime();
871871
wtx.nOrderPos = IncOrderPosNext(&walletdb);
872872
wtxOrdered.insert(make_pair(wtx.nOrderPos, TxPair(&wtx, (CAccountingEntry*)0)));
873-
874-
wtx.nTimeSmart = wtx.nTimeReceived;
875-
if (!wtxIn.hashUnset())
876-
{
877-
if (mapBlockIndex.count(wtxIn.hashBlock))
878-
{
879-
int64_t latestNow = wtx.nTimeReceived;
880-
int64_t latestEntry = 0;
881-
{
882-
// Tolerate times up to the last timestamp in the wallet not more than 5 minutes into the future
883-
int64_t latestTolerated = latestNow + 300;
884-
const TxItems & txOrdered = wtxOrdered;
885-
for (TxItems::const_reverse_iterator it = txOrdered.rbegin(); it != txOrdered.rend(); ++it)
886-
{
887-
CWalletTx *const pwtx = (*it).second.first;
888-
if (pwtx == &wtx)
889-
continue;
890-
CAccountingEntry *const pacentry = (*it).second.second;
891-
int64_t nSmartTime;
892-
if (pwtx)
893-
{
894-
nSmartTime = pwtx->nTimeSmart;
895-
if (!nSmartTime)
896-
nSmartTime = pwtx->nTimeReceived;
897-
}
898-
else
899-
nSmartTime = pacentry->nTime;
900-
if (nSmartTime <= latestTolerated)
901-
{
902-
latestEntry = nSmartTime;
903-
if (nSmartTime > latestNow)
904-
latestNow = nSmartTime;
905-
break;
906-
}
907-
}
908-
}
909-
910-
int64_t blocktime = mapBlockIndex[wtxIn.hashBlock]->GetBlockTime();
911-
wtx.nTimeSmart = std::max(latestEntry, std::min(blocktime, latestNow));
912-
}
913-
else
914-
LogPrintf("AddToWallet(): found %s in block %s not in index\n",
915-
wtxIn.GetHash().ToString(),
916-
wtxIn.hashBlock.ToString());
917-
}
873+
wtx.nTimeSmart = ComputeTimeSmart(wtx);
918874
AddToSpends(hash);
919875
}
920876

@@ -3472,6 +3428,71 @@ void CWallet::GetKeyBirthTimes(std::map<CTxDestination, int64_t> &mapKeyBirth) c
34723428
mapKeyBirth[it->first] = it->second->GetBlockTime() - TIMESTAMP_WINDOW; // block times can be 2h off
34733429
}
34743430

3431+
/**
3432+
* Compute smart timestamp for a transaction being added to the wallet.
3433+
*
3434+
* Logic:
3435+
* - If sending a transaction, assign its timestamp to the current time.
3436+
* - If receiving a transaction outside a block, assign its timestamp to the
3437+
* current time.
3438+
* - If receiving a block with a future timestamp, assign all its (not already
3439+
* known) transactions' timestamps to the current time.
3440+
* - If receiving a block with a past timestamp, before the most recent known
3441+
* transaction (that we care about), assign all its (not already known)
3442+
* transactions' timestamps to the same timestamp as that most-recent-known
3443+
* transaction.
3444+
* - If receiving a block with a past timestamp, but after the most recent known
3445+
* transaction, assign all its (not already known) transactions' timestamps to
3446+
* the block time.
3447+
*
3448+
* For more information see CWalletTx::nTimeSmart,
3449+
* https://bitcointalk.org/?topic=54527, or
3450+
* https://github.com/bitcoin/bitcoin/pull/1393.
3451+
*/
3452+
unsigned int CWallet::ComputeTimeSmart(const CWalletTx& wtx) const
3453+
{
3454+
unsigned int nTimeSmart = wtx.nTimeReceived;
3455+
if (!wtx.hashUnset()) {
3456+
if (mapBlockIndex.count(wtx.hashBlock)) {
3457+
int64_t latestNow = wtx.nTimeReceived;
3458+
int64_t latestEntry = 0;
3459+
3460+
// Tolerate times up to the last timestamp in the wallet not more than 5 minutes into the future
3461+
int64_t latestTolerated = latestNow + 300;
3462+
const TxItems& txOrdered = wtxOrdered;
3463+
for (auto it = txOrdered.rbegin(); it != txOrdered.rend(); ++it) {
3464+
CWalletTx* const pwtx = it->second.first;
3465+
if (pwtx == &wtx) {
3466+
continue;
3467+
}
3468+
CAccountingEntry* const pacentry = it->second.second;
3469+
int64_t nSmartTime;
3470+
if (pwtx) {
3471+
nSmartTime = pwtx->nTimeSmart;
3472+
if (!nSmartTime) {
3473+
nSmartTime = pwtx->nTimeReceived;
3474+
}
3475+
} else {
3476+
nSmartTime = pacentry->nTime;
3477+
}
3478+
if (nSmartTime <= latestTolerated) {
3479+
latestEntry = nSmartTime;
3480+
if (nSmartTime > latestNow) {
3481+
latestNow = nSmartTime;
3482+
}
3483+
break;
3484+
}
3485+
}
3486+
3487+
int64_t blocktime = mapBlockIndex[wtx.hashBlock]->GetBlockTime();
3488+
nTimeSmart = std::max(latestEntry, std::min(blocktime, latestNow));
3489+
} else {
3490+
LogPrintf("%s: found %s in block %s not in index\n", __func__, wtx.GetHash().ToString(), wtx.hashBlock.ToString());
3491+
}
3492+
}
3493+
return nTimeSmart;
3494+
}
3495+
34753496
bool CWallet::AddDestData(const CTxDestination &dest, const std::string &key, const std::string &value)
34763497
{
34773498
if (boost::get<CNoDestination>(&dest))

src/wallet/wallet.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -285,6 +285,15 @@ class CWalletTx : public CMerkleTx
285285
std::vector<std::pair<std::string, std::string> > vOrderForm;
286286
unsigned int fTimeReceivedIsTxTime;
287287
unsigned int nTimeReceived; //!< time received by this node
288+
/**
289+
* Stable timestamp that never changes, and reflects the order a transaction
290+
* was added to the wallet. Timestamp is based on the block time for a
291+
* transaction added as part of a block, or else the time when the
292+
* transaction was received if it wasn't part of a block, with the timestamp
293+
* adjusted in both cases so timestamp order matches the order transactions
294+
* were added to the wallet. More details can be found in
295+
* CWallet::ComputeTimeSmart().
296+
*/
288297
unsigned int nTimeSmart;
289298
/**
290299
* From me flag is set to 1 for transactions that were created by the wallet
@@ -800,6 +809,7 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface
800809
bool EncryptWallet(const SecureString& strWalletPassphrase);
801810

802811
void GetKeyBirthTimes(std::map<CTxDestination, int64_t> &mapKeyBirth) const;
812+
unsigned int ComputeTimeSmart(const CWalletTx& wtx) const;
803813

804814
/**
805815
* Increment the next transaction order id

0 commit comments

Comments
 (0)