Merge branch 'spentpertxout' of https://github.com/sipa/bitcoin
This commit is contained in:
commit
2e8b33824f
2 changed files with 208 additions and 88 deletions
172
main.cpp
172
main.cpp
|
@ -136,11 +136,7 @@ bool AddToWallet(const CWalletTx& wtxIn)
|
|||
wtx.fFromMe = wtxIn.fFromMe;
|
||||
fUpdated = true;
|
||||
}
|
||||
if (wtxIn.fSpent && wtxIn.fSpent != wtx.fSpent)
|
||||
{
|
||||
wtx.fSpent = wtxIn.fSpent;
|
||||
fUpdated = true;
|
||||
}
|
||||
fUpdated |= wtx.UpdateSpent(wtxIn.vfSpent);
|
||||
}
|
||||
|
||||
//// debug print
|
||||
|
@ -221,10 +217,10 @@ void WalletUpdateSpent(const COutPoint& prevout)
|
|||
if (mi != mapWallet.end())
|
||||
{
|
||||
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());
|
||||
wtx.fSpent = true;
|
||||
wtx.MarkSpent(prevout.n);
|
||||
wtx.WriteToDisk();
|
||||
vWalletUpdated.push_back(prevout.hash);
|
||||
}
|
||||
|
@ -939,34 +935,34 @@ void ReacceptWalletTransactions()
|
|||
foreach(PAIRTYPE(const uint256, CWalletTx)& item, mapWallet)
|
||||
{
|
||||
CWalletTx& wtx = item.second;
|
||||
if (wtx.fSpent && wtx.IsCoinBase())
|
||||
if (wtx.IsCoinBase() && wtx.IsSpent(0))
|
||||
continue;
|
||||
|
||||
CTxIndex txindex;
|
||||
bool fUpdated = false;
|
||||
if (txdb.ReadTxIndex(wtx.GetHash(), txindex))
|
||||
{
|
||||
// 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());
|
||||
continue;
|
||||
}
|
||||
for (int i = 0; i < txindex.vSpent.size(); i++)
|
||||
{
|
||||
if (!txindex.vSpent[i].IsNull() && wtx.vout[i].IsMine())
|
||||
{
|
||||
printf("ERROR: ReacceptWalletTransactions() : txindex.vSpent.size() %d != wtx.vout.size() %d\n", txindex.vSpent.size(), wtx.vout.size());
|
||||
continue;
|
||||
}
|
||||
for (int i = 0; i < txindex.vSpent.size(); i++)
|
||||
{
|
||||
if (!txindex.vSpent[i].IsNull() && wtx.vout[i].IsMine())
|
||||
{
|
||||
wtx.fSpent = true;
|
||||
vMissingTx.push_back(txindex.vSpent[i]);
|
||||
}
|
||||
}
|
||||
if (wtx.fSpent)
|
||||
{
|
||||
printf("ReacceptWalletTransactions found spent coin %sbc %s\n", FormatMoney(wtx.GetCredit()).c_str(), wtx.GetHash().ToString().c_str());
|
||||
wtx.WriteToDisk();
|
||||
wtx.MarkSpent(i);
|
||||
fUpdated = true;
|
||||
vMissingTx.push_back(txindex.vSpent[i]);
|
||||
}
|
||||
}
|
||||
if (fUpdated)
|
||||
{
|
||||
printf("ReacceptWalletTransactions found spent coin %sbc %s\n", FormatMoney(wtx.GetCredit()).c_str(), wtx.GetHash().ToString().c_str());
|
||||
wtx.MarkDirty();
|
||||
wtx.WriteToDisk();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -3732,9 +3728,9 @@ int64 GetBalance()
|
|||
for (map<uint256, CWalletTx>::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
|
||||
{
|
||||
CWalletTx* pcoin = &(*it).second;
|
||||
if (!pcoin->IsFinal() || pcoin->fSpent || !pcoin->IsConfirmed())
|
||||
if (!pcoin->IsFinal() || !pcoin->IsConfirmed())
|
||||
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();
|
||||
nValueRet = 0;
|
||||
|
||||
// List of values less than target
|
||||
int64 nLowestLarger = INT64_MAX;
|
||||
CWalletTx* pcoinLowestLarger = NULL;
|
||||
vector<pair<int64, CWalletTx*> > vValue;
|
||||
pair<int64, pair<CWalletTx*,unsigned int> > coinLowestLarger;
|
||||
coinLowestLarger.first = INT64_MAX;
|
||||
coinLowestLarger.second.first = NULL;
|
||||
vector<pair<int64, pair<CWalletTx*,unsigned int> > > vValue;
|
||||
int64 nTotalLower = 0;
|
||||
|
||||
CRITICAL_BLOCK(cs_mapWallet)
|
||||
|
@ -3763,30 +3761,43 @@ bool SelectCoinsMinConf(int64 nTargetValue, int nConfMine, int nConfTheirs, set<
|
|||
|
||||
foreach(CWalletTx* pcoin, vCoins)
|
||||
{
|
||||
if (!pcoin->IsFinal() || pcoin->fSpent || !pcoin->IsConfirmed())
|
||||
if (!pcoin->IsFinal() || !pcoin->IsConfirmed())
|
||||
continue;
|
||||
|
||||
if (pcoin->IsCoinBase() && pcoin->GetBlocksToMaturity() > 0)
|
||||
continue;
|
||||
|
||||
int nDepth = pcoin->GetDepthInMainChain();
|
||||
if (nDepth < (pcoin->IsFromMe() ? nConfMine : nConfTheirs))
|
||||
continue;
|
||||
|
||||
int64 n = pcoin->GetCredit();
|
||||
if (n <= 0)
|
||||
continue;
|
||||
if (n == nTargetValue)
|
||||
for (int i = 0; i < pcoin->vout.size(); i++)
|
||||
{
|
||||
setCoinsRet.insert(pcoin);
|
||||
return true;
|
||||
}
|
||||
else if (n < nTargetValue + CENT)
|
||||
{
|
||||
vValue.push_back(make_pair(n, pcoin));
|
||||
nTotalLower += n;
|
||||
}
|
||||
else if (n < nLowestLarger)
|
||||
{
|
||||
nLowestLarger = n;
|
||||
pcoinLowestLarger = pcoin;
|
||||
if (pcoin->IsSpent(i) || !pcoin->vout[i].IsMine())
|
||||
continue;
|
||||
|
||||
int64 n = pcoin->vout[i].nValue;
|
||||
|
||||
if (n <= 0)
|
||||
continue;
|
||||
|
||||
pair<int64,pair<CWalletTx*,unsigned int> > coin = make_pair(n,make_pair(pcoin,i));
|
||||
|
||||
if (n == nTargetValue)
|
||||
{
|
||||
setCoinsRet.insert(coin.second);
|
||||
nValueRet += coin.first;
|
||||
return true;
|
||||
}
|
||||
else if (n < nTargetValue + CENT)
|
||||
{
|
||||
vValue.push_back(coin);
|
||||
nTotalLower += n;
|
||||
}
|
||||
else if (n < coinLowestLarger.first)
|
||||
{
|
||||
coinLowestLarger = coin;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3794,15 +3805,19 @@ bool SelectCoinsMinConf(int64 nTargetValue, int nConfMine, int nConfTheirs, set<
|
|||
if (nTotalLower == nTargetValue || nTotalLower == nTargetValue + CENT)
|
||||
{
|
||||
for (int i = 0; i < vValue.size(); ++i)
|
||||
{
|
||||
setCoinsRet.insert(vValue[i].second);
|
||||
nValueRet += vValue[i].first;
|
||||
}
|
||||
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;
|
||||
setCoinsRet.insert(pcoinLowestLarger);
|
||||
setCoinsRet.insert(coinLowestLarger.second);
|
||||
nValueRet += coinLowestLarger.first;
|
||||
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 (pcoinLowestLarger && nLowestLarger - nTargetValue <= nBest - nTargetValue)
|
||||
setCoinsRet.insert(pcoinLowestLarger);
|
||||
else
|
||||
if (coinLowestLarger.second.first && coinLowestLarger.first - nTargetValue <= nBest - nTargetValue)
|
||||
{
|
||||
setCoinsRet.insert(coinLowestLarger.second);
|
||||
nValueRet += coinLowestLarger.first;
|
||||
}
|
||||
else {
|
||||
for (int i = 0; i < vValue.size(); i++)
|
||||
if (vfBest[i])
|
||||
{
|
||||
setCoinsRet.insert(vValue[i].second);
|
||||
nValueRet += vValue[i].first;
|
||||
}
|
||||
|
||||
//// debug print
|
||||
printf("SelectCoins() best subset: ");
|
||||
|
@ -3864,11 +3884,11 @@ bool SelectCoinsMinConf(int64 nTargetValue, int nConfMine, int nConfTheirs, set<
|
|||
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) ||
|
||||
SelectCoinsMinConf(nTargetValue, 1, 1, setCoinsRet) ||
|
||||
SelectCoinsMinConf(nTargetValue, 0, 1, setCoinsRet));
|
||||
return (SelectCoinsMinConf(nTargetValue, 1, 6, setCoinsRet, nValueRet) ||
|
||||
SelectCoinsMinConf(nTargetValue, 1, 1, setCoinsRet, nValueRet) ||
|
||||
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));
|
||||
|
||||
// Choose coins to use
|
||||
set<CWalletTx*> setCoins;
|
||||
if (!SelectCoins(nTotalValue, setCoins))
|
||||
return false;
|
||||
set<pair<CWalletTx*,unsigned int> > setCoins;
|
||||
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();
|
||||
nValueIn += nCredit;
|
||||
dPriority += (double)nCredit * pcoin->GetDepthInMainChain();
|
||||
int64 nCredit = pcoin.first->vout[pcoin.second].nValue;
|
||||
dPriority += (double)nCredit * pcoin.first->GetDepthInMainChain();
|
||||
}
|
||||
|
||||
// Fill a vout back to self with any change
|
||||
|
@ -3947,18 +3966,14 @@ bool CreateTransaction(const vector<pair<CScript, int64> >& vecSend, CWalletTx&
|
|||
reservekey.ReturnKey();
|
||||
|
||||
// Fill vin
|
||||
foreach(CWalletTx* pcoin, setCoins)
|
||||
for (int nOut = 0; nOut < pcoin->vout.size(); nOut++)
|
||||
if (pcoin->vout[nOut].IsMine())
|
||||
wtxNew.vin.push_back(CTxIn(pcoin->GetHash(), nOut));
|
||||
foreach(const PAIRTYPE(CWalletTx*,unsigned int)& coin, setCoins)
|
||||
wtxNew.vin.push_back(CTxIn(coin.first->GetHash(),coin.second));
|
||||
|
||||
// Sign
|
||||
int nIn = 0;
|
||||
foreach(CWalletTx* pcoin, setCoins)
|
||||
for (int nOut = 0; nOut < pcoin->vout.size(); nOut++)
|
||||
if (pcoin->vout[nOut].IsMine())
|
||||
if (!SignSignature(*pcoin, wtxNew, nIn++))
|
||||
return false;
|
||||
foreach(const PAIRTYPE(CWalletTx*,unsigned int)& coin, setCoins)
|
||||
if (!SignSignature(*coin.first, wtxNew, nIn++))
|
||||
return false;
|
||||
|
||||
// Limit size
|
||||
unsigned int nBytes = ::GetSerializeSize(*(CTransaction*)&wtxNew, SER_NETWORK);
|
||||
|
@ -4017,12 +4032,11 @@ bool CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey)
|
|||
// Mark old coins as spent
|
||||
set<CWalletTx*> setCoins;
|
||||
foreach(const CTxIn& txin, wtxNew.vin)
|
||||
setCoins.insert(&mapWallet[txin.prevout.hash]);
|
||||
foreach(CWalletTx* pcoin, setCoins)
|
||||
{
|
||||
pcoin->fSpent = true;
|
||||
pcoin->WriteToDisk();
|
||||
vWalletUpdated.push_back(pcoin->GetHash());
|
||||
CWalletTx &pcoin = mapWallet[txin.prevout.hash];
|
||||
pcoin.MarkSpent(txin.prevout.n);
|
||||
pcoin.WriteToDisk();
|
||||
vWalletUpdated.push_back(pcoin.GetHash());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
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)
|
||||
|
|
Loading…
Reference in a new issue