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
3 changed files with 88 additions and 23 deletions
|
@ -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)
|
||||||
|
|
|
@ -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
|
||||||
|
|
16
src/wallet.h
16
src/wallet.h
|
@ -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
|
||||||
|
|
Loading…
Reference in a new issue