Choose reasonable "smart" times to display for transactions

Logic:
- 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 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 transaction (that we care about), assign all its (not already known) transactions' timestamps to the same timestamp as that most-recent-known transaction.
- If receiving a block with a past timestamp, but after the most recent known transaction, assign all its (not already known) transactions' timestamps to the block time.
This commit is contained in:
Luke Dashjr 2012-05-28 18:45:12 +00:00
parent bdbfd2329a
commit c3f95ef13f
3 changed files with 88 additions and 23 deletions

View file

@ -986,29 +986,11 @@ Value listtransactions(const Array& params, bool fHelp)
throw JSONRPCError(-8, "Negative from"); throw JSONRPCError(-8, "Negative from");
Array ret; Array ret;
CWalletDB walletdb(pwalletMain->strWalletFile);
// First: get all CWalletTx and CAccountingEntry into a sorted-by-order multimap. CWallet::TxItems txOrdered = pwalletMain->OrderedTxItems(strAccount);
typedef pair<CWalletTx*, CAccountingEntry*> TxPair;
typedef multimap<int64, TxPair > TxItems;
TxItems txOrdered;
// Note: maintaining indices in the database of (account,time) --> txid and (account, time) --> acentry
// would make this much faster for applications that do this a lot.
for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
{
CWalletTx* wtx = &((*it).second);
txOrdered.insert(make_pair(wtx->nOrderPos, TxPair(wtx, (CAccountingEntry*)0)));
}
list<CAccountingEntry> acentries;
walletdb.ListAccountCreditDebit(strAccount, acentries);
BOOST_FOREACH(CAccountingEntry& entry, acentries)
{
txOrdered.insert(make_pair(entry.nOrderPos, TxPair((CWalletTx*)0, &entry)));
}
// iterate backwards until we have nCount items to return: // iterate backwards until we have nCount items to return:
for (TxItems::reverse_iterator it = txOrdered.rbegin(); it != txOrdered.rend(); ++it) for (CWallet::TxItems::reverse_iterator it = txOrdered.rbegin(); it != txOrdered.rend(); ++it)
{ {
CWalletTx *const pwtx = (*it).second.first; CWalletTx *const pwtx = (*it).second.first;
if (pwtx != 0) if (pwtx != 0)

View file

@ -291,6 +291,31 @@ bool CWallet::EncryptWallet(const SecureString& strWalletPassphrase)
return true; return true;
} }
CWallet::TxItems
CWallet::OrderedTxItems(std::string strAccount)
{
CWalletDB walletdb(strWalletFile);
// First: get all CWalletTx and CAccountingEntry into a sorted-by-order multimap.
TxItems txOrdered;
// Note: maintaining indices in the database of (account,time) --> txid and (account, time) --> acentry
// would make this much faster for applications that do this a lot.
for (map<uint256, CWalletTx>::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
{
CWalletTx* wtx = &((*it).second);
txOrdered.insert(make_pair(wtx->nOrderPos, TxPair(wtx, (CAccountingEntry*)0)));
}
list<CAccountingEntry> acentries;
walletdb.ListAccountCreditDebit(strAccount, acentries);
BOOST_FOREACH(CAccountingEntry& entry, acentries)
{
txOrdered.insert(make_pair(entry.nOrderPos, TxPair((CWalletTx*)0, &entry)));
}
return txOrdered;
}
void CWallet::WalletUpdateSpent(const CTransaction &tx) void CWallet::WalletUpdateSpent(const CTransaction &tx)
{ {
// Anytime a signature is successfully verified, it's proof the outpoint is spent. // Anytime a signature is successfully verified, it's proof the outpoint is spent.
@ -339,6 +364,51 @@ bool CWallet::AddToWallet(const CWalletTx& wtxIn)
{ {
wtx.nTimeReceived = GetAdjustedTime(); wtx.nTimeReceived = GetAdjustedTime();
wtx.nOrderPos = nOrderPosNext++; wtx.nOrderPos = nOrderPosNext++;
wtx.nTimeSmart = wtx.nTimeReceived;
if (wtxIn.hashBlock != 0)
{
if (mapBlockIndex.count(wtxIn.hashBlock))
{
unsigned int latestNow = wtx.nTimeReceived;
unsigned int latestEntry = 0;
{
// Tolerate times up to the last timestamp in the wallet not more than 5 minutes into the future
int64 latestTolerated = latestNow + 300;
TxItems txOrdered = OrderedTxItems();
for (TxItems::reverse_iterator it = txOrdered.rbegin(); it != txOrdered.rend(); ++it)
{
CWalletTx *const pwtx = (*it).second.first;
if (pwtx == &wtx)
continue;
CAccountingEntry *const pacentry = (*it).second.second;
int64 nSmartTime;
if (pwtx)
{
nSmartTime = pwtx->nTimeSmart;
if (!nSmartTime)
nSmartTime = pwtx->nTimeReceived;
}
else
nSmartTime = pacentry->nTime;
if (nSmartTime <= latestTolerated)
{
latestEntry = nSmartTime;
if (nSmartTime > latestNow)
latestNow = nSmartTime;
break;
}
}
}
unsigned int& blocktime = mapBlockIndex[wtxIn.hashBlock]->nTime;
wtx.nTimeSmart = std::max(latestEntry, std::min(blocktime, latestNow));
}
else
printf("AddToWallet() : found %s in block %s not in index\n",
wtxIn.GetHash().ToString().substr(0,10).c_str(),
wtxIn.hashBlock.ToString().c_str());
}
} }
bool fUpdated = false; bool fUpdated = false;
@ -488,7 +558,8 @@ bool CWallet::IsChange(const CTxOut& txout) const
int64 CWalletTx::GetTxTime() const int64 CWalletTx::GetTxTime() const
{ {
return nTimeReceived; int64 n = nTimeSmart;
return n ? n : nTimeReceived;
} }
int CWalletTx::GetRequestCount() const int CWalletTx::GetRequestCount() const

View file

@ -17,6 +17,7 @@
#include "ui_interface.h" #include "ui_interface.h"
#include "util.h" #include "util.h"
class CAccountingEntry;
class CWalletTx; class CWalletTx;
class CReserveKey; class CReserveKey;
class CWalletDB; class CWalletDB;
@ -143,6 +144,10 @@ public:
bool ChangeWalletPassphrase(const SecureString& strOldWalletPassphrase, const SecureString& strNewWalletPassphrase); bool ChangeWalletPassphrase(const SecureString& strOldWalletPassphrase, const SecureString& strNewWalletPassphrase);
bool EncryptWallet(const SecureString& strWalletPassphrase); bool EncryptWallet(const SecureString& strWalletPassphrase);
typedef std::pair<CWalletTx*, CAccountingEntry*> TxPair;
typedef std::multimap<int64, TxPair > TxItems;
TxItems OrderedTxItems(std::string strAccount = "");
void MarkDirty(); void MarkDirty();
bool AddToWallet(const CWalletTx& wtxIn); bool AddToWallet(const CWalletTx& wtxIn);
bool AddToWalletIfInvolvingMe(const CTransaction& tx, const CBlock* pblock, bool fUpdate = false, bool fFindBlock = false); bool AddToWalletIfInvolvingMe(const CTransaction& tx, const CBlock* pblock, bool fUpdate = false, bool fFindBlock = false);
@ -351,6 +356,7 @@ public:
std::vector<std::pair<std::string, std::string> > vOrderForm; std::vector<std::pair<std::string, std::string> > vOrderForm;
unsigned int fTimeReceivedIsTxTime; unsigned int fTimeReceivedIsTxTime;
unsigned int nTimeReceived; // time received by this node unsigned int nTimeReceived; // time received by this node
unsigned int nTimeSmart;
char fFromMe; char fFromMe;
std::string strFromAccount; std::string strFromAccount;
std::vector<char> vfSpent; // which outputs are already spent std::vector<char> vfSpent; // which outputs are already spent
@ -394,6 +400,7 @@ public:
vOrderForm.clear(); vOrderForm.clear();
fTimeReceivedIsTxTime = false; fTimeReceivedIsTxTime = false;
nTimeReceived = 0; nTimeReceived = 0;
nTimeSmart = 0;
fFromMe = false; fFromMe = false;
strFromAccount.clear(); strFromAccount.clear();
vfSpent.clear(); vfSpent.clear();
@ -429,6 +436,9 @@ public:
pthis->mapValue["spent"] = str; pthis->mapValue["spent"] = str;
WriteOrderPos(pthis->nOrderPos, pthis->mapValue); WriteOrderPos(pthis->nOrderPos, pthis->mapValue);
if (nTimeSmart)
pthis->mapValue["timesmart"] = strprintf("%u", nTimeSmart);
} }
nSerSize += SerReadWrite(s, *(CMerkleTx*)this, nType, nVersion,ser_action); nSerSize += SerReadWrite(s, *(CMerkleTx*)this, nType, nVersion,ser_action);
@ -449,15 +459,17 @@ public:
pthis->vfSpent.push_back(c != '0'); pthis->vfSpent.push_back(c != '0');
else else
pthis->vfSpent.assign(vout.size(), fSpent); pthis->vfSpent.assign(vout.size(), fSpent);
}
if (fRead)
ReadOrderPos(pthis->nOrderPos, pthis->mapValue); ReadOrderPos(pthis->nOrderPos, pthis->mapValue);
pthis->nTimeSmart = mapValue.count("timesmart") ? (unsigned int)atoi64(pthis->mapValue["timesmart"]) : 0;
}
pthis->mapValue.erase("fromaccount"); pthis->mapValue.erase("fromaccount");
pthis->mapValue.erase("version"); pthis->mapValue.erase("version");
pthis->mapValue.erase("spent"); pthis->mapValue.erase("spent");
pthis->mapValue.erase("n"); pthis->mapValue.erase("n");
pthis->mapValue.erase("timesmart");
) )
// marks certain txout's as spent // marks certain txout's as spent