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:
parent
bdbfd2329a
commit
c3f95ef13f
@ -986,29 +986,11 @@ Value listtransactions(const Array& params, bool fHelp)
|
||||
throw JSONRPCError(-8, "Negative from");
|
||||
|
||||
Array ret;
|
||||
CWalletDB walletdb(pwalletMain->strWalletFile);
|
||||
|
||||
// First: get all CWalletTx and CAccountingEntry into a sorted-by-order multimap.
|
||||
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)));
|
||||
}
|
||||
CWallet::TxItems txOrdered = pwalletMain->OrderedTxItems(strAccount);
|
||||
|
||||
// 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;
|
||||
if (pwtx != 0)
|
||||
|
@ -291,6 +291,31 @@ bool CWallet::EncryptWallet(const SecureString& strWalletPassphrase)
|
||||
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)
|
||||
{
|
||||
// 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.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;
|
||||
@ -488,7 +558,8 @@ bool CWallet::IsChange(const CTxOut& txout) const
|
||||
|
||||
int64 CWalletTx::GetTxTime() const
|
||||
{
|
||||
return nTimeReceived;
|
||||
int64 n = nTimeSmart;
|
||||
return n ? n : nTimeReceived;
|
||||
}
|
||||
|
||||
int CWalletTx::GetRequestCount() const
|
||||
|
16
src/wallet.h
16
src/wallet.h
@ -17,6 +17,7 @@
|
||||
#include "ui_interface.h"
|
||||
#include "util.h"
|
||||
|
||||
class CAccountingEntry;
|
||||
class CWalletTx;
|
||||
class CReserveKey;
|
||||
class CWalletDB;
|
||||
@ -143,6 +144,10 @@ public:
|
||||
bool ChangeWalletPassphrase(const SecureString& strOldWalletPassphrase, const SecureString& strNewWalletPassphrase);
|
||||
bool EncryptWallet(const SecureString& strWalletPassphrase);
|
||||
|
||||
typedef std::pair<CWalletTx*, CAccountingEntry*> TxPair;
|
||||
typedef std::multimap<int64, TxPair > TxItems;
|
||||
TxItems OrderedTxItems(std::string strAccount = "");
|
||||
|
||||
void MarkDirty();
|
||||
bool AddToWallet(const CWalletTx& wtxIn);
|
||||
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;
|
||||
unsigned int fTimeReceivedIsTxTime;
|
||||
unsigned int nTimeReceived; // time received by this node
|
||||
unsigned int nTimeSmart;
|
||||
char fFromMe;
|
||||
std::string strFromAccount;
|
||||
std::vector<char> vfSpent; // which outputs are already spent
|
||||
@ -394,6 +400,7 @@ public:
|
||||
vOrderForm.clear();
|
||||
fTimeReceivedIsTxTime = false;
|
||||
nTimeReceived = 0;
|
||||
nTimeSmart = 0;
|
||||
fFromMe = false;
|
||||
strFromAccount.clear();
|
||||
vfSpent.clear();
|
||||
@ -429,6 +436,9 @@ public:
|
||||
pthis->mapValue["spent"] = str;
|
||||
|
||||
WriteOrderPos(pthis->nOrderPos, pthis->mapValue);
|
||||
|
||||
if (nTimeSmart)
|
||||
pthis->mapValue["timesmart"] = strprintf("%u", nTimeSmart);
|
||||
}
|
||||
|
||||
nSerSize += SerReadWrite(s, *(CMerkleTx*)this, nType, nVersion,ser_action);
|
||||
@ -449,15 +459,17 @@ public:
|
||||
pthis->vfSpent.push_back(c != '0');
|
||||
else
|
||||
pthis->vfSpent.assign(vout.size(), fSpent);
|
||||
}
|
||||
|
||||
if (fRead)
|
||||
ReadOrderPos(pthis->nOrderPos, pthis->mapValue);
|
||||
|
||||
pthis->nTimeSmart = mapValue.count("timesmart") ? (unsigned int)atoi64(pthis->mapValue["timesmart"]) : 0;
|
||||
}
|
||||
|
||||
pthis->mapValue.erase("fromaccount");
|
||||
pthis->mapValue.erase("version");
|
||||
pthis->mapValue.erase("spent");
|
||||
pthis->mapValue.erase("n");
|
||||
pthis->mapValue.erase("timesmart");
|
||||
)
|
||||
|
||||
// marks certain txout's as spent
|
||||
|
Loading…
Reference in New Issue
Block a user