Browse Source

Merge branch 'spentpertxout' of https://github.com/sipa/bitcoin

miguelfreitas
Gavin Andresen 14 years ago
parent
commit
2e8b33824f
  1. 126
      main.cpp
  2. 122
      main.h

126
main.cpp

@ -136,11 +136,7 @@ bool AddToWallet(const CWalletTx& wtxIn)
wtx.fFromMe = wtxIn.fFromMe; wtx.fFromMe = wtxIn.fFromMe;
fUpdated = true; fUpdated = true;
} }
if (wtxIn.fSpent && wtxIn.fSpent != wtx.fSpent) fUpdated |= wtx.UpdateSpent(wtxIn.vfSpent);
{
wtx.fSpent = wtxIn.fSpent;
fUpdated = true;
}
} }
//// debug print //// debug print
@ -221,10 +217,10 @@ void WalletUpdateSpent(const COutPoint& prevout)
if (mi != mapWallet.end()) if (mi != mapWallet.end())
{ {
CWalletTx& wtx = (*mi).second; CWalletTx& wtx = (*mi).second;
if (!wtx.fSpent && wtx.vout[prevout.n].IsMine()) if (!wtx.IsSpent(prevout.n) && wtx.vout[prevout.n].IsMine())
{ {
printf("WalletUpdateSpent found spent coin %sbc %s\n", FormatMoney(wtx.GetCredit()).c_str(), wtx.GetHash().ToString().c_str()); printf("WalletUpdateSpent found spent coin %sbc %s\n", FormatMoney(wtx.GetCredit()).c_str(), wtx.GetHash().ToString().c_str());
wtx.fSpent = true; wtx.MarkSpent(prevout.n);
wtx.WriteToDisk(); wtx.WriteToDisk();
vWalletUpdated.push_back(prevout.hash); vWalletUpdated.push_back(prevout.hash);
} }
@ -939,15 +935,14 @@ void ReacceptWalletTransactions()
foreach(PAIRTYPE(const uint256, CWalletTx)& item, mapWallet) foreach(PAIRTYPE(const uint256, CWalletTx)& item, mapWallet)
{ {
CWalletTx& wtx = item.second; CWalletTx& wtx = item.second;
if (wtx.fSpent && wtx.IsCoinBase()) if (wtx.IsCoinBase() && wtx.IsSpent(0))
continue; continue;
CTxIndex txindex; CTxIndex txindex;
bool fUpdated = false;
if (txdb.ReadTxIndex(wtx.GetHash(), txindex)) if (txdb.ReadTxIndex(wtx.GetHash(), txindex))
{ {
// Update fSpent if a tx got spent somewhere else by a copy of wallet.dat // Update fSpent if a tx got spent somewhere else by a copy of wallet.dat
if (!wtx.fSpent)
{
if (txindex.vSpent.size() != wtx.vout.size()) if (txindex.vSpent.size() != wtx.vout.size())
{ {
printf("ERROR: ReacceptWalletTransactions() : txindex.vSpent.size() %d != wtx.vout.size() %d\n", txindex.vSpent.size(), wtx.vout.size()); printf("ERROR: ReacceptWalletTransactions() : txindex.vSpent.size() %d != wtx.vout.size() %d\n", txindex.vSpent.size(), wtx.vout.size());
@ -957,17 +952,18 @@ void ReacceptWalletTransactions()
{ {
if (!txindex.vSpent[i].IsNull() && wtx.vout[i].IsMine()) if (!txindex.vSpent[i].IsNull() && wtx.vout[i].IsMine())
{ {
wtx.fSpent = true; wtx.MarkSpent(i);
fUpdated = true;
vMissingTx.push_back(txindex.vSpent[i]); vMissingTx.push_back(txindex.vSpent[i]);
} }
} }
if (wtx.fSpent) if (fUpdated)
{ {
printf("ReacceptWalletTransactions found spent coin %sbc %s\n", FormatMoney(wtx.GetCredit()).c_str(), wtx.GetHash().ToString().c_str()); printf("ReacceptWalletTransactions found spent coin %sbc %s\n", FormatMoney(wtx.GetCredit()).c_str(), wtx.GetHash().ToString().c_str());
wtx.MarkDirty();
wtx.WriteToDisk(); wtx.WriteToDisk();
} }
} }
}
else else
{ {
// Reaccept any txes of ours that aren't already in a block // Reaccept any txes of ours that aren't already in a block
@ -3732,9 +3728,9 @@ int64 GetBalance()
for (map<uint256, CWalletTx>::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) for (map<uint256, CWalletTx>::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
{ {
CWalletTx* pcoin = &(*it).second; CWalletTx* pcoin = &(*it).second;
if (!pcoin->IsFinal() || pcoin->fSpent || !pcoin->IsConfirmed()) if (!pcoin->IsFinal() || !pcoin->IsConfirmed())
continue; continue;
nTotal += pcoin->GetCredit(); nTotal += pcoin->GetAvailableCredit();
} }
} }
@ -3743,14 +3739,16 @@ int64 GetBalance()
} }
bool SelectCoinsMinConf(int64 nTargetValue, int nConfMine, int nConfTheirs, set<CWalletTx*>& setCoinsRet) bool SelectCoinsMinConf(int64 nTargetValue, int nConfMine, int nConfTheirs, set<pair<CWalletTx*,unsigned int> >& setCoinsRet, int64& nValueRet)
{ {
setCoinsRet.clear(); setCoinsRet.clear();
nValueRet = 0;
// List of values less than target // List of values less than target
int64 nLowestLarger = INT64_MAX; pair<int64, pair<CWalletTx*,unsigned int> > coinLowestLarger;
CWalletTx* pcoinLowestLarger = NULL; coinLowestLarger.first = INT64_MAX;
vector<pair<int64, CWalletTx*> > vValue; coinLowestLarger.second.first = NULL;
vector<pair<int64, pair<CWalletTx*,unsigned int> > > vValue;
int64 nTotalLower = 0; int64 nTotalLower = 0;
CRITICAL_BLOCK(cs_mapWallet) CRITICAL_BLOCK(cs_mapWallet)
@ -3763,30 +3761,43 @@ bool SelectCoinsMinConf(int64 nTargetValue, int nConfMine, int nConfTheirs, set<
foreach(CWalletTx* pcoin, vCoins) foreach(CWalletTx* pcoin, vCoins)
{ {
if (!pcoin->IsFinal() || pcoin->fSpent || !pcoin->IsConfirmed()) if (!pcoin->IsFinal() || !pcoin->IsConfirmed())
continue;
if (pcoin->IsCoinBase() && pcoin->GetBlocksToMaturity() > 0)
continue; continue;
int nDepth = pcoin->GetDepthInMainChain(); int nDepth = pcoin->GetDepthInMainChain();
if (nDepth < (pcoin->IsFromMe() ? nConfMine : nConfTheirs)) if (nDepth < (pcoin->IsFromMe() ? nConfMine : nConfTheirs))
continue; continue;
int64 n = pcoin->GetCredit(); for (int i = 0; i < pcoin->vout.size(); i++)
{
if (pcoin->IsSpent(i) || !pcoin->vout[i].IsMine())
continue;
int64 n = pcoin->vout[i].nValue;
if (n <= 0) if (n <= 0)
continue; continue;
pair<int64,pair<CWalletTx*,unsigned int> > coin = make_pair(n,make_pair(pcoin,i));
if (n == nTargetValue) if (n == nTargetValue)
{ {
setCoinsRet.insert(pcoin); setCoinsRet.insert(coin.second);
nValueRet += coin.first;
return true; return true;
} }
else if (n < nTargetValue + CENT) else if (n < nTargetValue + CENT)
{ {
vValue.push_back(make_pair(n, pcoin)); vValue.push_back(coin);
nTotalLower += n; nTotalLower += n;
} }
else if (n < nLowestLarger) else if (n < coinLowestLarger.first)
{ {
nLowestLarger = n; coinLowestLarger = coin;
pcoinLowestLarger = pcoin; }
} }
} }
} }
@ -3794,15 +3805,19 @@ bool SelectCoinsMinConf(int64 nTargetValue, int nConfMine, int nConfTheirs, set<
if (nTotalLower == nTargetValue || nTotalLower == nTargetValue + CENT) if (nTotalLower == nTargetValue || nTotalLower == nTargetValue + CENT)
{ {
for (int i = 0; i < vValue.size(); ++i) for (int i = 0; i < vValue.size(); ++i)
{
setCoinsRet.insert(vValue[i].second); setCoinsRet.insert(vValue[i].second);
nValueRet += vValue[i].first;
}
return true; return true;
} }
if (nTotalLower < nTargetValue + (pcoinLowestLarger ? CENT : 0)) if (nTotalLower < nTargetValue + (coinLowestLarger.second.first ? CENT : 0))
{ {
if (pcoinLowestLarger == NULL) if (coinLowestLarger.second.first == NULL)
return false; return false;
setCoinsRet.insert(pcoinLowestLarger); setCoinsRet.insert(coinLowestLarger.second);
nValueRet += coinLowestLarger.first;
return true; return true;
} }
@ -3845,13 +3860,18 @@ bool SelectCoinsMinConf(int64 nTargetValue, int nConfMine, int nConfTheirs, set<
} }
// If the next larger is still closer, return it // If the next larger is still closer, return it
if (pcoinLowestLarger && nLowestLarger - nTargetValue <= nBest - nTargetValue) if (coinLowestLarger.second.first && coinLowestLarger.first - nTargetValue <= nBest - nTargetValue)
setCoinsRet.insert(pcoinLowestLarger);
else
{ {
setCoinsRet.insert(coinLowestLarger.second);
nValueRet += coinLowestLarger.first;
}
else {
for (int i = 0; i < vValue.size(); i++) for (int i = 0; i < vValue.size(); i++)
if (vfBest[i]) if (vfBest[i])
{
setCoinsRet.insert(vValue[i].second); setCoinsRet.insert(vValue[i].second);
nValueRet += vValue[i].first;
}
//// debug print //// debug print
printf("SelectCoins() best subset: "); printf("SelectCoins() best subset: ");
@ -3864,11 +3884,11 @@ bool SelectCoinsMinConf(int64 nTargetValue, int nConfMine, int nConfTheirs, set<
return true; return true;
} }
bool SelectCoins(int64 nTargetValue, set<CWalletTx*>& setCoinsRet) bool SelectCoins(int64 nTargetValue, set<pair<CWalletTx*,unsigned int> >& setCoinsRet, int64& nValueRet)
{ {
return (SelectCoinsMinConf(nTargetValue, 1, 6, setCoinsRet) || return (SelectCoinsMinConf(nTargetValue, 1, 6, setCoinsRet, nValueRet) ||
SelectCoinsMinConf(nTargetValue, 1, 1, setCoinsRet) || SelectCoinsMinConf(nTargetValue, 1, 1, setCoinsRet, nValueRet) ||
SelectCoinsMinConf(nTargetValue, 0, 1, setCoinsRet)); SelectCoinsMinConf(nTargetValue, 0, 1, setCoinsRet, nValueRet));
} }
@ -3906,15 +3926,14 @@ bool CreateTransaction(const vector<pair<CScript, int64> >& vecSend, CWalletTx&
wtxNew.vout.push_back(CTxOut(s.second, s.first)); wtxNew.vout.push_back(CTxOut(s.second, s.first));
// Choose coins to use // Choose coins to use
set<CWalletTx*> setCoins; set<pair<CWalletTx*,unsigned int> > setCoins;
if (!SelectCoins(nTotalValue, setCoins))
return false;
int64 nValueIn = 0; int64 nValueIn = 0;
foreach(CWalletTx* pcoin, setCoins) if (!SelectCoins(nTotalValue, setCoins, nValueIn))
return false;
foreach(PAIRTYPE(CWalletTx*, unsigned int) pcoin, setCoins)
{ {
int64 nCredit = pcoin->GetCredit(); int64 nCredit = pcoin.first->vout[pcoin.second].nValue;
nValueIn += nCredit; dPriority += (double)nCredit * pcoin.first->GetDepthInMainChain();
dPriority += (double)nCredit * pcoin->GetDepthInMainChain();
} }
// Fill a vout back to self with any change // Fill a vout back to self with any change
@ -3947,17 +3966,13 @@ bool CreateTransaction(const vector<pair<CScript, int64> >& vecSend, CWalletTx&
reservekey.ReturnKey(); reservekey.ReturnKey();
// Fill vin // Fill vin
foreach(CWalletTx* pcoin, setCoins) foreach(const PAIRTYPE(CWalletTx*,unsigned int)& coin, setCoins)
for (int nOut = 0; nOut < pcoin->vout.size(); nOut++) wtxNew.vin.push_back(CTxIn(coin.first->GetHash(),coin.second));
if (pcoin->vout[nOut].IsMine())
wtxNew.vin.push_back(CTxIn(pcoin->GetHash(), nOut));
// Sign // Sign
int nIn = 0; int nIn = 0;
foreach(CWalletTx* pcoin, setCoins) foreach(const PAIRTYPE(CWalletTx*,unsigned int)& coin, setCoins)
for (int nOut = 0; nOut < pcoin->vout.size(); nOut++) if (!SignSignature(*coin.first, wtxNew, nIn++))
if (pcoin->vout[nOut].IsMine())
if (!SignSignature(*pcoin, wtxNew, nIn++))
return false; return false;
// Limit size // Limit size
@ -4017,12 +4032,11 @@ bool CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey)
// Mark old coins as spent // Mark old coins as spent
set<CWalletTx*> setCoins; set<CWalletTx*> setCoins;
foreach(const CTxIn& txin, wtxNew.vin) foreach(const CTxIn& txin, wtxNew.vin)
setCoins.insert(&mapWallet[txin.prevout.hash]);
foreach(CWalletTx* pcoin, setCoins)
{ {
pcoin->fSpent = true; CWalletTx &pcoin = mapWallet[txin.prevout.hash];
pcoin->WriteToDisk(); pcoin.MarkSpent(txin.prevout.n);
vWalletUpdated.push_back(pcoin->GetHash()); pcoin.WriteToDisk();
vWalletUpdated.push_back(pcoin.GetHash());
} }
} }

122
main.h

@ -738,6 +738,7 @@ public:
fMerkleVerified = false; fMerkleVerified = false;
} }
IMPLEMENT_SERIALIZE IMPLEMENT_SERIALIZE
( (
nSerSize += SerReadWrite(s, *(CTransaction*)this, nType, nVersion, ser_action); nSerSize += SerReadWrite(s, *(CTransaction*)this, nType, nVersion, ser_action);
@ -774,15 +775,17 @@ public:
unsigned int fTimeReceivedIsTxTime; unsigned int fTimeReceivedIsTxTime;
unsigned int nTimeReceived; // time received by this node unsigned int nTimeReceived; // time received by this node
char fFromMe; char fFromMe;
char fSpent;
string strFromAccount; string strFromAccount;
vector<char> vfSpent;
// memory only // memory only
mutable char fDebitCached; mutable char fDebitCached;
mutable char fCreditCached; mutable char fCreditCached;
mutable char fAvailableCreditCached;
mutable char fChangeCached; mutable char fChangeCached;
mutable int64 nDebitCached; mutable int64 nDebitCached;
mutable int64 nCreditCached; mutable int64 nCreditCached;
mutable int64 nAvailableCreditCached;
mutable int64 nChangeCached; mutable int64 nChangeCached;
// memory only UI hints // memory only UI hints
@ -814,13 +817,15 @@ public:
fTimeReceivedIsTxTime = false; fTimeReceivedIsTxTime = false;
nTimeReceived = 0; nTimeReceived = 0;
fFromMe = false; fFromMe = false;
fSpent = false;
strFromAccount.clear(); strFromAccount.clear();
vfSpent.clear();
fDebitCached = false; fDebitCached = false;
fCreditCached = false; fCreditCached = false;
fAvailableCreditCached = false;
fChangeCached = false; fChangeCached = false;
nDebitCached = 0; nDebitCached = 0;
nCreditCached = 0; nCreditCached = 0;
nAvailableCreditCached = 0;
nChangeCached = 0; nChangeCached = 0;
nTimeDisplayed = 0; nTimeDisplayed = 0;
nLinesDisplayed = 0; nLinesDisplayed = 0;
@ -832,22 +837,96 @@ public:
CWalletTx* pthis = const_cast<CWalletTx*>(this); CWalletTx* pthis = const_cast<CWalletTx*>(this);
if (fRead) if (fRead)
pthis->Init(); pthis->Init();
nSerSize += SerReadWrite(s, *(CMerkleTx*)this, nType, nVersion, ser_action); char fSpent = false;
READWRITE(vtxPrev);
if (!fRead)
{
pthis->mapValue["fromaccount"] = pthis->strFromAccount; pthis->mapValue["fromaccount"] = pthis->strFromAccount;
READWRITE(mapValue);
pthis->strFromAccount = pthis->mapValue["fromaccount"];
pthis->mapValue.erase("fromaccount");
pthis->mapValue.erase("version");
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);
READWRITE(mapValue);
READWRITE(vOrderForm); READWRITE(vOrderForm);
READWRITE(fTimeReceivedIsTxTime); READWRITE(fTimeReceivedIsTxTime);
READWRITE(nTimeReceived); READWRITE(nTimeReceived);
READWRITE(fFromMe); READWRITE(fFromMe);
READWRITE(fSpent); 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 int64 GetDebit() const
{ {
if (vin.empty()) if (vin.empty())
@ -873,6 +952,33 @@ public:
return nCreditCached; 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 int64 GetChange() const
{ {
if (fChangeCached) if (fChangeCached)

Loading…
Cancel
Save