Spent per txout
Change some internal data structures to keep track of spentness of each wallet transaction output separately, to support partially-spent transactions: * an update to the data structures (vfSpent in CWalletTx instead of fSpent) * a backward-compatible update to the wallet disk format. Old clients reading back an updated wallet will ignore partially spent transactions when creating new ones, and may report a wrong balance, though. * some helper functions (CWalletTx: IsSpent, MarkSpent, MarkDirty to reset cached values, GetAvailableCredit which only counts unredeemed outputs)
This commit is contained in:
124
main.h
124
main.h
@@ -738,6 +738,7 @@ public:
|
||||
fMerkleVerified = false;
|
||||
}
|
||||
|
||||
|
||||
IMPLEMENT_SERIALIZE
|
||||
(
|
||||
nSerSize += SerReadWrite(s, *(CTransaction*)this, nType, nVersion, ser_action);
|
||||
@@ -774,15 +775,17 @@ public:
|
||||
unsigned int fTimeReceivedIsTxTime;
|
||||
unsigned int nTimeReceived; // time received by this node
|
||||
char fFromMe;
|
||||
char fSpent;
|
||||
string strFromAccount;
|
||||
vector<char> vfSpent;
|
||||
|
||||
// memory only
|
||||
mutable char fDebitCached;
|
||||
mutable char fCreditCached;
|
||||
mutable char fAvailableCreditCached;
|
||||
mutable char fChangeCached;
|
||||
mutable int64 nDebitCached;
|
||||
mutable int64 nCreditCached;
|
||||
mutable int64 nAvailableCreditCached;
|
||||
mutable int64 nChangeCached;
|
||||
|
||||
// memory only UI hints
|
||||
@@ -814,13 +817,15 @@ public:
|
||||
fTimeReceivedIsTxTime = false;
|
||||
nTimeReceived = 0;
|
||||
fFromMe = false;
|
||||
fSpent = false;
|
||||
strFromAccount.clear();
|
||||
vfSpent.clear();
|
||||
fDebitCached = false;
|
||||
fCreditCached = false;
|
||||
fAvailableCreditCached = false;
|
||||
fChangeCached = false;
|
||||
nDebitCached = 0;
|
||||
nCreditCached = 0;
|
||||
nAvailableCreditCached = 0;
|
||||
nChangeCached = 0;
|
||||
nTimeDisplayed = 0;
|
||||
nLinesDisplayed = 0;
|
||||
@@ -832,22 +837,96 @@ public:
|
||||
CWalletTx* pthis = const_cast<CWalletTx*>(this);
|
||||
if (fRead)
|
||||
pthis->Init();
|
||||
nSerSize += SerReadWrite(s, *(CMerkleTx*)this, nType, nVersion, ser_action);
|
||||
char fSpent = false;
|
||||
|
||||
if (!fRead)
|
||||
{
|
||||
pthis->mapValue["fromaccount"] = pthis->strFromAccount;
|
||||
|
||||
string str;
|
||||
foreach(char f, vfSpent)
|
||||
{
|
||||
str += (f ? '1' : '0');
|
||||
if (f)
|
||||
fSpent = true;
|
||||
}
|
||||
pthis->mapValue["spent"] = str;
|
||||
}
|
||||
|
||||
nSerSize += SerReadWrite(s, *(CMerkleTx*)this, nType, nVersion,ser_action);
|
||||
READWRITE(vtxPrev);
|
||||
|
||||
pthis->mapValue["fromaccount"] = pthis->strFromAccount;
|
||||
READWRITE(mapValue);
|
||||
pthis->strFromAccount = pthis->mapValue["fromaccount"];
|
||||
pthis->mapValue.erase("fromaccount");
|
||||
pthis->mapValue.erase("version");
|
||||
|
||||
READWRITE(vOrderForm);
|
||||
READWRITE(fTimeReceivedIsTxTime);
|
||||
READWRITE(nTimeReceived);
|
||||
READWRITE(fFromMe);
|
||||
READWRITE(fSpent);
|
||||
|
||||
if (fRead)
|
||||
{
|
||||
pthis->strFromAccount = pthis->mapValue["fromaccount"];
|
||||
|
||||
if (mapValue.count("spent"))
|
||||
foreach(char c, pthis->mapValue["spent"])
|
||||
pthis->vfSpent.push_back(c != '0');
|
||||
else
|
||||
pthis->vfSpent.assign(vout.size(), fSpent);
|
||||
}
|
||||
|
||||
pthis->mapValue.erase("fromaccount");
|
||||
pthis->mapValue.erase("version");
|
||||
pthis->mapValue.erase("spent");
|
||||
)
|
||||
|
||||
// marks certain txout's as spent
|
||||
// returns true if any update took place
|
||||
bool UpdateSpent(const vector<char>& vfNewSpent)
|
||||
{
|
||||
bool fReturn = false;
|
||||
for (int i=0; i < vfNewSpent.size(); i++)
|
||||
{
|
||||
if (i == vfSpent.size())
|
||||
break;
|
||||
|
||||
if (vfNewSpent[i] && !vfSpent[i])
|
||||
{
|
||||
vfSpent[i] = true;
|
||||
fReturn = true;
|
||||
fAvailableCreditCached = false;
|
||||
}
|
||||
}
|
||||
return fReturn;
|
||||
}
|
||||
|
||||
void MarkDirty()
|
||||
{
|
||||
fCreditCached = false;
|
||||
fAvailableCreditCached = false;
|
||||
fDebitCached = false;
|
||||
fChangeCached = false;
|
||||
}
|
||||
|
||||
void MarkSpent(unsigned int nOut)
|
||||
{
|
||||
if (nOut >= vout.size())
|
||||
throw runtime_error("CWalletTx::MarkSpent() : nOut out of range");
|
||||
vfSpent.resize(vout.size());
|
||||
if (!vfSpent[nOut])
|
||||
{
|
||||
vfSpent[nOut] = true;
|
||||
fAvailableCreditCached = false;
|
||||
}
|
||||
}
|
||||
|
||||
bool IsSpent(unsigned int nOut) const
|
||||
{
|
||||
if (nOut >= vout.size())
|
||||
throw runtime_error("CWalletTx::IsSpent() : nOut out of range");
|
||||
if (nOut >= vfSpent.size())
|
||||
return false;
|
||||
return (!!vfSpent[nOut]);
|
||||
}
|
||||
|
||||
int64 GetDebit() const
|
||||
{
|
||||
if (vin.empty())
|
||||
@@ -873,6 +952,33 @@ public:
|
||||
return nCreditCached;
|
||||
}
|
||||
|
||||
int64 GetAvailableCredit(bool fUseCache=true) const
|
||||
{
|
||||
// Must wait until coinbase is safely deep enough in the chain before valuing it
|
||||
if (IsCoinBase() && GetBlocksToMaturity() > 0)
|
||||
return 0;
|
||||
|
||||
if (fUseCache && fAvailableCreditCached)
|
||||
return nAvailableCreditCached;
|
||||
|
||||
int64 nCredit = 0;
|
||||
for (int i = 0; i < vout.size(); i++)
|
||||
{
|
||||
if (!IsSpent(i))
|
||||
{
|
||||
const CTxOut &txout = vout[i];
|
||||
nCredit += txout.GetCredit();
|
||||
if (!MoneyRange(nCredit))
|
||||
throw runtime_error("CWalletTx::GetAvailableCredit() : value out of range");
|
||||
}
|
||||
}
|
||||
|
||||
nAvailableCreditCached = nCredit;
|
||||
fAvailableCreditCached = true;
|
||||
return nCredit;
|
||||
}
|
||||
|
||||
|
||||
int64 GetChange() const
|
||||
{
|
||||
if (fChangeCached)
|
||||
|
||||
Reference in New Issue
Block a user