Merge pull request #3671 from gavinandresen/txn_conflicts

Report transaction conflicts, and tentative account balance fix
This commit is contained in:
Gavin Andresen 2014-02-15 08:56:55 -05:00
commit 085c62149a
5 changed files with 128 additions and 9 deletions

View file

@ -88,8 +88,10 @@ B2ADDRESS=$( $CLI $B2ARGS getnewaddress )
# Have B1 create two transactions; second will # Have B1 create two transactions; second will
# spend change from first, since B1 starts with only a single # spend change from first, since B1 starts with only a single
# 50 bitcoin output: # 50 bitcoin output:
TXID1=$( $CLI $B1ARGS sendtoaddress $B2ADDRESS 1.0 ) $CLI $B1ARGS move "" "foo" 10.0
TXID2=$( $CLI $B1ARGS sendtoaddress $B2ADDRESS 2.0 ) $CLI $B1ARGS move "" "bar" 10.0
TXID1=$( $CLI $B1ARGS sendfrom foo $B2ADDRESS 1.0 0)
TXID2=$( $CLI $B1ARGS sendfrom bar $B2ADDRESS 2.0 0)
# Mutate TXID1 and add it to B2's memory pool: # Mutate TXID1 and add it to B2's memory pool:
RAWTX1=$( $CLI $B1ARGS getrawtransaction $TXID1 ) RAWTX1=$( $CLI $B1ARGS getrawtransaction $TXID1 )
@ -122,7 +124,9 @@ echo "Mutated: " $MUTATEDTXID
$CLI $B2ARGS addnode 127.0.0.1:11000 onetry $CLI $B2ARGS addnode 127.0.0.1:11000 onetry
WaitPeers "$B1ARGS" 1 WaitPeers "$B1ARGS" 1
$CLI $B2ARGS setgenerate true 1 $CLI $B2ARGS setgenerate true 3
WaitBlocks
$CLI $B1ARGS setgenerate true 3
WaitBlocks WaitBlocks
$CLI $B2ARGS stop > /dev/null 2>&1 $CLI $B2ARGS stop > /dev/null 2>&1

View file

@ -51,7 +51,12 @@ void WalletTxToJSON(const CWalletTx& wtx, Object& entry)
entry.push_back(Pair("blockindex", wtx.nIndex)); entry.push_back(Pair("blockindex", wtx.nIndex));
entry.push_back(Pair("blocktime", (boost::int64_t)(mapBlockIndex[wtx.hashBlock]->nTime))); entry.push_back(Pair("blocktime", (boost::int64_t)(mapBlockIndex[wtx.hashBlock]->nTime)));
} }
entry.push_back(Pair("txid", wtx.GetHash().GetHex())); uint256 hash = wtx.GetHash();
entry.push_back(Pair("txid", hash.GetHex()));
Array conflicts;
BOOST_FOREACH(const uint256& conflict, wtx.GetConflicts())
conflicts.push_back(conflict.GetHex());
entry.push_back(Pair("walletconflicts", conflicts));
entry.push_back(Pair("time", (boost::int64_t)wtx.GetTxTime())); entry.push_back(Pair("time", (boost::int64_t)wtx.GetTxTime()));
entry.push_back(Pair("timereceived", (boost::int64_t)wtx.nTimeReceived)); entry.push_back(Pair("timereceived", (boost::int64_t)wtx.nTimeReceived));
BOOST_FOREACH(const PAIRTYPE(string,string)& item, wtx.mapValue) BOOST_FOREACH(const PAIRTYPE(string,string)& item, wtx.mapValue)
@ -621,7 +626,7 @@ Value getbalance(const Array& params, bool fHelp)
for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it) for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
{ {
const CWalletTx& wtx = (*it).second; const CWalletTx& wtx = (*it).second;
if (!wtx.IsTrusted()) if (!wtx.IsTrusted() || wtx.GetBlocksToMaturity() > 0)
continue; continue;
int64_t allFee; int64_t allFee;
@ -1325,6 +1330,8 @@ Value listaccounts(const Array& params, bool fHelp)
string strSentAccount; string strSentAccount;
list<pair<CTxDestination, int64_t> > listReceived; list<pair<CTxDestination, int64_t> > listReceived;
list<pair<CTxDestination, int64_t> > listSent; list<pair<CTxDestination, int64_t> > listSent;
if (wtx.GetBlocksToMaturity() > 0)
continue;
wtx.GetAmounts(listReceived, listSent, nFee, strSentAccount); wtx.GetAmounts(listReceived, listSent, nFee, strSentAccount);
mapAccountBalances[strSentAccount] -= nFee; mapAccountBalances[strSentAccount] -= nFee;
BOOST_FOREACH(const PAIRTYPE(CTxDestination, int64_t)& s, listSent) BOOST_FOREACH(const PAIRTYPE(CTxDestination, int64_t)& s, listSent)

View file

@ -231,6 +231,82 @@ bool CWallet::SetMaxVersion(int nVersion)
return true; return true;
} }
set<uint256> CWallet::GetConflicts(const uint256& txid) const
{
set<uint256> result;
AssertLockHeld(cs_wallet);
std::map<uint256, CWalletTx>::const_iterator it = mapWallet.find(txid);
if (it == mapWallet.end())
return result;
const CWalletTx& wtx = it->second;
std::pair<TxConflicts::const_iterator, TxConflicts::const_iterator> range;
BOOST_FOREACH(const CTxIn& txin, wtx.vin)
{
range = mapTxConflicts.equal_range(txin.prevout);
for (TxConflicts::const_iterator it = range.first; it != range.second; ++it)
result.insert(it->second);
}
return result;
}
void CWallet::SyncMetaData(pair<TxConflicts::iterator, TxConflicts::iterator> range)
{
// We want all the wallet transactions in range to have the same metadata as
// the oldest (smallest nOrderPos).
// So: find smallest nOrderPos:
int nMinOrderPos = std::numeric_limits<int>::max();
const CWalletTx* copyFrom = NULL;
for (TxConflicts::iterator it = range.first; it != range.second; ++it)
{
const uint256& hash = it->second;
int n = mapWallet[hash].nOrderPos;
if (n < nMinOrderPos)
{
nMinOrderPos = n;
copyFrom = &mapWallet[hash];
}
}
// Now copy data from copyFrom to rest:
for (TxConflicts::iterator it = range.first; it != range.second; ++it)
{
const uint256& hash = it->second;
CWalletTx* copyTo = &mapWallet[hash];
if (copyFrom == copyTo) continue;
copyTo->mapValue = copyFrom->mapValue;
copyTo->vOrderForm = copyFrom->vOrderForm;
// fTimeReceivedIsTxTime not copied on purpose
// nTimeReceived not copied on purpose
copyTo->nTimeSmart = copyFrom->nTimeSmart;
copyTo->fFromMe = copyFrom->fFromMe;
copyTo->strFromAccount = copyFrom->strFromAccount;
// vfSpent not copied on purpose
// nOrderPos not copied on purpose
// cached members not copied on purpose
}
}
void CWallet::AddToConflicts(const uint256& wtxhash)
{
assert(mapWallet.count(wtxhash));
CWalletTx& thisTx = mapWallet[wtxhash];
if (thisTx.IsCoinBase())
return;
BOOST_FOREACH(const CTxIn& txin, thisTx.vin)
{
mapTxConflicts.insert(make_pair(txin.prevout, wtxhash));
pair<TxConflicts::iterator, TxConflicts::iterator> range;
range = mapTxConflicts.equal_range(txin.prevout);
if (range.first != range.second)
SyncMetaData(range);
}
}
bool CWallet::EncryptWallet(const SecureString& strWalletPassphrase) bool CWallet::EncryptWallet(const SecureString& strWalletPassphrase)
{ {
if (IsCrypted()) if (IsCrypted())
@ -385,9 +461,16 @@ void CWallet::MarkDirty()
} }
} }
bool CWallet::AddToWallet(const CWalletTx& wtxIn) bool CWallet::AddToWallet(const CWalletTx& wtxIn, bool fFromLoadWallet)
{ {
uint256 hash = wtxIn.GetHash(); uint256 hash = wtxIn.GetHash();
if (fFromLoadWallet)
{
mapWallet[hash] = wtxIn;
AddToConflicts(hash);
}
else
{ {
LOCK(cs_wallet); LOCK(cs_wallet);
// Inserts only if not already there, returns tx inserted or tx found // Inserts only if not already there, returns tx inserted or tx found
@ -445,6 +528,7 @@ bool CWallet::AddToWallet(const CWalletTx& wtxIn)
wtxIn.GetHash().ToString(), wtxIn.GetHash().ToString(),
wtxIn.hashBlock.ToString()); wtxIn.hashBlock.ToString());
} }
AddToConflicts(hash);
} }
bool fUpdated = false; bool fUpdated = false;
@ -907,6 +991,18 @@ void CWalletTx::RelayWalletTransaction()
} }
} }
set<uint256> CWalletTx::GetConflicts() const
{
set<uint256> result;
if (pwallet != NULL)
{
uint256 myHash = GetHash();
result = pwallet->GetConflicts(myHash);
result.erase(myHash);
}
return result;
}
void CWallet::ResendWalletTransactions() void CWallet::ResendWalletTransactions()
{ {
// Do this infrequently and randomly to avoid giving away // Do this infrequently and randomly to avoid giving away
@ -980,7 +1076,7 @@ int64_t CWallet::GetUnconfirmedBalance() const
for (map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) for (map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
{ {
const CWalletTx* pcoin = &(*it).second; const CWalletTx* pcoin = &(*it).second;
if (!IsFinalTx(*pcoin) || !pcoin->IsTrusted()) if (!IsFinalTx(*pcoin) || (!pcoin->IsTrusted() && pcoin->GetDepthInMainChain() == 0))
nTotal += pcoin->GetAvailableCredit(); nTotal += pcoin->GetAvailableCredit();
} }
} }

View file

@ -108,6 +108,12 @@ private:
int64_t nNextResend; int64_t nNextResend;
int64_t nLastResend; int64_t nLastResend;
// Used to detect and report conflicted transactions:
typedef std::multimap<COutPoint, uint256> TxConflicts;
TxConflicts mapTxConflicts;
void AddToConflicts(const uint256& wtxhash);
void SyncMetaData(std::pair<TxConflicts::iterator, TxConflicts::iterator>);
public: public:
/// Main wallet lock. /// Main wallet lock.
/// This lock protects all the fields added by CWallet /// This lock protects all the fields added by CWallet
@ -151,6 +157,7 @@ public:
} }
std::map<uint256, CWalletTx> mapWallet; std::map<uint256, CWalletTx> mapWallet;
int64_t nOrderPosNext; int64_t nOrderPosNext;
std::map<uint256, int> mapRequestCount; std::map<uint256, int> mapRequestCount;
@ -223,7 +230,7 @@ public:
TxItems OrderedTxItems(std::list<CAccountingEntry>& acentries, std::string strAccount = ""); TxItems OrderedTxItems(std::list<CAccountingEntry>& acentries, std::string strAccount = "");
void MarkDirty(); void MarkDirty();
bool AddToWallet(const CWalletTx& wtxIn); bool AddToWallet(const CWalletTx& wtxIn, bool fFromLoadWallet=false);
void SyncTransaction(const uint256 &hash, const CTransaction& tx, const CBlock* pblock); void SyncTransaction(const uint256 &hash, const CTransaction& tx, const CBlock* pblock);
bool AddToWalletIfInvolvingMe(const uint256 &hash, const CTransaction& tx, const CBlock* pblock, bool fUpdate); bool AddToWalletIfInvolvingMe(const uint256 &hash, const CTransaction& tx, const CBlock* pblock, bool fUpdate);
void EraseFromWallet(const uint256 &hash); void EraseFromWallet(const uint256 &hash);
@ -358,6 +365,9 @@ public:
// get the current wallet format (the oldest client version guaranteed to understand this wallet) // get the current wallet format (the oldest client version guaranteed to understand this wallet)
int GetVersion() { AssertLockHeld(cs_wallet); return nWalletVersion; } int GetVersion() { AssertLockHeld(cs_wallet); return nWalletVersion; }
// Get wallet transactions that conflict with given transaction (spend same outputs)
std::set<uint256> GetConflicts(const uint256& txid) const;
/** Address book entry changed. /** Address book entry changed.
* @note called with lock cs_wallet held. * @note called with lock cs_wallet held.
*/ */
@ -753,6 +763,8 @@ public:
void AddSupportingTransactions(); void AddSupportingTransactions();
bool AcceptWalletTransaction(); bool AcceptWalletTransaction();
void RelayWalletTransaction(); void RelayWalletTransaction();
std::set<uint256> GetConflicts() const;
}; };

View file

@ -382,7 +382,7 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue,
if (wtx.nOrderPos == -1) if (wtx.nOrderPos == -1)
wss.fAnyUnordered = true; wss.fAnyUnordered = true;
pwallet->mapWallet[hash] = wtx; pwallet->AddToWallet(wtx, true);
//// debug print //// debug print
//LogPrintf("LoadWallet %s\n", wtx.GetHash().ToString()); //LogPrintf("LoadWallet %s\n", wtx.GetHash().ToString());
//LogPrintf(" %12"PRId64" %s %s %s\n", //LogPrintf(" %12"PRId64" %s %s %s\n",