Skip to content

Commit 085c621

Browse files
committed
Merge pull request #3671 from gavinandresen/txn_conflicts
Report transaction conflicts, and tentative account balance fix
2 parents 05d3ded + 731b89b commit 085c621

File tree

5 files changed

+128
-9
lines changed

5 files changed

+128
-9
lines changed

qa/rpc-tests/txnmall.sh

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -88,8 +88,10 @@ B2ADDRESS=$( $CLI $B2ARGS getnewaddress )
8888
# Have B1 create two transactions; second will
8989
# spend change from first, since B1 starts with only a single
9090
# 50 bitcoin output:
91-
TXID1=$( $CLI $B1ARGS sendtoaddress $B2ADDRESS 1.0 )
92-
TXID2=$( $CLI $B1ARGS sendtoaddress $B2ADDRESS 2.0 )
91+
$CLI $B1ARGS move "" "foo" 10.0
92+
$CLI $B1ARGS move "" "bar" 10.0
93+
TXID1=$( $CLI $B1ARGS sendfrom foo $B2ADDRESS 1.0 0)
94+
TXID2=$( $CLI $B1ARGS sendfrom bar $B2ADDRESS 2.0 0)
9395

9496
# Mutate TXID1 and add it to B2's memory pool:
9597
RAWTX1=$( $CLI $B1ARGS getrawtransaction $TXID1 )
@@ -122,7 +124,9 @@ echo "Mutated: " $MUTATEDTXID
122124
$CLI $B2ARGS addnode 127.0.0.1:11000 onetry
123125
WaitPeers "$B1ARGS" 1
124126

125-
$CLI $B2ARGS setgenerate true 1
127+
$CLI $B2ARGS setgenerate true 3
128+
WaitBlocks
129+
$CLI $B1ARGS setgenerate true 3
126130
WaitBlocks
127131

128132
$CLI $B2ARGS stop > /dev/null 2>&1

src/rpcwallet.cpp

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,12 @@ void WalletTxToJSON(const CWalletTx& wtx, Object& entry)
5151
entry.push_back(Pair("blockindex", wtx.nIndex));
5252
entry.push_back(Pair("blocktime", (boost::int64_t)(mapBlockIndex[wtx.hashBlock]->nTime)));
5353
}
54-
entry.push_back(Pair("txid", wtx.GetHash().GetHex()));
54+
uint256 hash = wtx.GetHash();
55+
entry.push_back(Pair("txid", hash.GetHex()));
56+
Array conflicts;
57+
BOOST_FOREACH(const uint256& conflict, wtx.GetConflicts())
58+
conflicts.push_back(conflict.GetHex());
59+
entry.push_back(Pair("walletconflicts", conflicts));
5560
entry.push_back(Pair("time", (boost::int64_t)wtx.GetTxTime()));
5661
entry.push_back(Pair("timereceived", (boost::int64_t)wtx.nTimeReceived));
5762
BOOST_FOREACH(const PAIRTYPE(string,string)& item, wtx.mapValue)
@@ -621,7 +626,7 @@ Value getbalance(const Array& params, bool fHelp)
621626
for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
622627
{
623628
const CWalletTx& wtx = (*it).second;
624-
if (!wtx.IsTrusted())
629+
if (!wtx.IsTrusted() || wtx.GetBlocksToMaturity() > 0)
625630
continue;
626631

627632
int64_t allFee;
@@ -1325,6 +1330,8 @@ Value listaccounts(const Array& params, bool fHelp)
13251330
string strSentAccount;
13261331
list<pair<CTxDestination, int64_t> > listReceived;
13271332
list<pair<CTxDestination, int64_t> > listSent;
1333+
if (wtx.GetBlocksToMaturity() > 0)
1334+
continue;
13281335
wtx.GetAmounts(listReceived, listSent, nFee, strSentAccount);
13291336
mapAccountBalances[strSentAccount] -= nFee;
13301337
BOOST_FOREACH(const PAIRTYPE(CTxDestination, int64_t)& s, listSent)

src/wallet.cpp

Lines changed: 98 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,82 @@ bool CWallet::SetMaxVersion(int nVersion)
231231
return true;
232232
}
233233

234+
set<uint256> CWallet::GetConflicts(const uint256& txid) const
235+
{
236+
set<uint256> result;
237+
AssertLockHeld(cs_wallet);
238+
239+
std::map<uint256, CWalletTx>::const_iterator it = mapWallet.find(txid);
240+
if (it == mapWallet.end())
241+
return result;
242+
const CWalletTx& wtx = it->second;
243+
244+
std::pair<TxConflicts::const_iterator, TxConflicts::const_iterator> range;
245+
246+
BOOST_FOREACH(const CTxIn& txin, wtx.vin)
247+
{
248+
range = mapTxConflicts.equal_range(txin.prevout);
249+
for (TxConflicts::const_iterator it = range.first; it != range.second; ++it)
250+
result.insert(it->second);
251+
}
252+
return result;
253+
}
254+
255+
void CWallet::SyncMetaData(pair<TxConflicts::iterator, TxConflicts::iterator> range)
256+
{
257+
// We want all the wallet transactions in range to have the same metadata as
258+
// the oldest (smallest nOrderPos).
259+
// So: find smallest nOrderPos:
260+
261+
int nMinOrderPos = std::numeric_limits<int>::max();
262+
const CWalletTx* copyFrom = NULL;
263+
for (TxConflicts::iterator it = range.first; it != range.second; ++it)
264+
{
265+
const uint256& hash = it->second;
266+
int n = mapWallet[hash].nOrderPos;
267+
if (n < nMinOrderPos)
268+
{
269+
nMinOrderPos = n;
270+
copyFrom = &mapWallet[hash];
271+
}
272+
}
273+
// Now copy data from copyFrom to rest:
274+
for (TxConflicts::iterator it = range.first; it != range.second; ++it)
275+
{
276+
const uint256& hash = it->second;
277+
CWalletTx* copyTo = &mapWallet[hash];
278+
if (copyFrom == copyTo) continue;
279+
copyTo->mapValue = copyFrom->mapValue;
280+
copyTo->vOrderForm = copyFrom->vOrderForm;
281+
// fTimeReceivedIsTxTime not copied on purpose
282+
// nTimeReceived not copied on purpose
283+
copyTo->nTimeSmart = copyFrom->nTimeSmart;
284+
copyTo->fFromMe = copyFrom->fFromMe;
285+
copyTo->strFromAccount = copyFrom->strFromAccount;
286+
// vfSpent not copied on purpose
287+
// nOrderPos not copied on purpose
288+
// cached members not copied on purpose
289+
}
290+
}
291+
292+
void CWallet::AddToConflicts(const uint256& wtxhash)
293+
{
294+
assert(mapWallet.count(wtxhash));
295+
CWalletTx& thisTx = mapWallet[wtxhash];
296+
if (thisTx.IsCoinBase())
297+
return;
298+
299+
BOOST_FOREACH(const CTxIn& txin, thisTx.vin)
300+
{
301+
mapTxConflicts.insert(make_pair(txin.prevout, wtxhash));
302+
303+
pair<TxConflicts::iterator, TxConflicts::iterator> range;
304+
range = mapTxConflicts.equal_range(txin.prevout);
305+
if (range.first != range.second)
306+
SyncMetaData(range);
307+
}
308+
}
309+
234310
bool CWallet::EncryptWallet(const SecureString& strWalletPassphrase)
235311
{
236312
if (IsCrypted())
@@ -385,9 +461,16 @@ void CWallet::MarkDirty()
385461
}
386462
}
387463

388-
bool CWallet::AddToWallet(const CWalletTx& wtxIn)
464+
bool CWallet::AddToWallet(const CWalletTx& wtxIn, bool fFromLoadWallet)
389465
{
390466
uint256 hash = wtxIn.GetHash();
467+
468+
if (fFromLoadWallet)
469+
{
470+
mapWallet[hash] = wtxIn;
471+
AddToConflicts(hash);
472+
}
473+
else
391474
{
392475
LOCK(cs_wallet);
393476
// Inserts only if not already there, returns tx inserted or tx found
@@ -445,6 +528,7 @@ bool CWallet::AddToWallet(const CWalletTx& wtxIn)
445528
wtxIn.GetHash().ToString(),
446529
wtxIn.hashBlock.ToString());
447530
}
531+
AddToConflicts(hash);
448532
}
449533

450534
bool fUpdated = false;
@@ -907,6 +991,18 @@ void CWalletTx::RelayWalletTransaction()
907991
}
908992
}
909993

994+
set<uint256> CWalletTx::GetConflicts() const
995+
{
996+
set<uint256> result;
997+
if (pwallet != NULL)
998+
{
999+
uint256 myHash = GetHash();
1000+
result = pwallet->GetConflicts(myHash);
1001+
result.erase(myHash);
1002+
}
1003+
return result;
1004+
}
1005+
9101006
void CWallet::ResendWalletTransactions()
9111007
{
9121008
// Do this infrequently and randomly to avoid giving away
@@ -980,7 +1076,7 @@ int64_t CWallet::GetUnconfirmedBalance() const
9801076
for (map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
9811077
{
9821078
const CWalletTx* pcoin = &(*it).second;
983-
if (!IsFinalTx(*pcoin) || !pcoin->IsTrusted())
1079+
if (!IsFinalTx(*pcoin) || (!pcoin->IsTrusted() && pcoin->GetDepthInMainChain() == 0))
9841080
nTotal += pcoin->GetAvailableCredit();
9851081
}
9861082
}

src/wallet.h

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,12 @@ class CWallet : public CCryptoKeyStore, public CWalletInterface
108108
int64_t nNextResend;
109109
int64_t nLastResend;
110110

111+
// Used to detect and report conflicted transactions:
112+
typedef std::multimap<COutPoint, uint256> TxConflicts;
113+
TxConflicts mapTxConflicts;
114+
void AddToConflicts(const uint256& wtxhash);
115+
void SyncMetaData(std::pair<TxConflicts::iterator, TxConflicts::iterator>);
116+
111117
public:
112118
/// Main wallet lock.
113119
/// This lock protects all the fields added by CWallet
@@ -151,6 +157,7 @@ class CWallet : public CCryptoKeyStore, public CWalletInterface
151157
}
152158

153159
std::map<uint256, CWalletTx> mapWallet;
160+
154161
int64_t nOrderPosNext;
155162
std::map<uint256, int> mapRequestCount;
156163

@@ -223,7 +230,7 @@ class CWallet : public CCryptoKeyStore, public CWalletInterface
223230
TxItems OrderedTxItems(std::list<CAccountingEntry>& acentries, std::string strAccount = "");
224231

225232
void MarkDirty();
226-
bool AddToWallet(const CWalletTx& wtxIn);
233+
bool AddToWallet(const CWalletTx& wtxIn, bool fFromLoadWallet=false);
227234
void SyncTransaction(const uint256 &hash, const CTransaction& tx, const CBlock* pblock);
228235
bool AddToWalletIfInvolvingMe(const uint256 &hash, const CTransaction& tx, const CBlock* pblock, bool fUpdate);
229236
void EraseFromWallet(const uint256 &hash);
@@ -358,6 +365,9 @@ class CWallet : public CCryptoKeyStore, public CWalletInterface
358365
// get the current wallet format (the oldest client version guaranteed to understand this wallet)
359366
int GetVersion() { AssertLockHeld(cs_wallet); return nWalletVersion; }
360367

368+
// Get wallet transactions that conflict with given transaction (spend same outputs)
369+
std::set<uint256> GetConflicts(const uint256& txid) const;
370+
361371
/** Address book entry changed.
362372
* @note called with lock cs_wallet held.
363373
*/
@@ -753,6 +763,8 @@ class CWalletTx : public CMerkleTx
753763
void AddSupportingTransactions();
754764
bool AcceptWalletTransaction();
755765
void RelayWalletTransaction();
766+
767+
std::set<uint256> GetConflicts() const;
756768
};
757769

758770

src/walletdb.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -382,7 +382,7 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue,
382382
if (wtx.nOrderPos == -1)
383383
wss.fAnyUnordered = true;
384384

385-
pwallet->mapWallet[hash] = wtx;
385+
pwallet->AddToWallet(wtx, true);
386386
//// debug print
387387
//LogPrintf("LoadWallet %s\n", wtx.GetHash().ToString());
388388
//LogPrintf(" %12"PRId64" %s %s %s\n",

0 commit comments

Comments
 (0)