aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGavin Andresen <gavinandresen@gmail.com>2014-02-15 08:56:55 -0500
committerGavin Andresen <gavinandresen@gmail.com>2014-02-15 08:56:55 -0500
commit085c62149ad19cecf235829e49df07884a279e1e (patch)
tree4106ba96196e883464f5891ef88be34514c67cb9
parent05d3ded072d782036c31c1d773c0441b87de843e (diff)
parent731b89b8b53cb2ea4d2d5c8f2875def515766ea1 (diff)
Merge pull request #3671 from gavinandresen/txn_conflicts
Report transaction conflicts, and tentative account balance fix
-rwxr-xr-xqa/rpc-tests/txnmall.sh10
-rw-r--r--src/rpcwallet.cpp11
-rw-r--r--src/wallet.cpp100
-rw-r--r--src/wallet.h14
-rw-r--r--src/walletdb.cpp2
5 files changed, 128 insertions, 9 deletions
diff --git a/qa/rpc-tests/txnmall.sh b/qa/rpc-tests/txnmall.sh
index 7aca5f36df..6bf92fce40 100755
--- a/qa/rpc-tests/txnmall.sh
+++ b/qa/rpc-tests/txnmall.sh
@@ -88,8 +88,10 @@ B2ADDRESS=$( $CLI $B2ARGS getnewaddress )
# Have B1 create two transactions; second will
# spend change from first, since B1 starts with only a single
# 50 bitcoin output:
-TXID1=$( $CLI $B1ARGS sendtoaddress $B2ADDRESS 1.0 )
-TXID2=$( $CLI $B1ARGS sendtoaddress $B2ADDRESS 2.0 )
+$CLI $B1ARGS move "" "foo" 10.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:
RAWTX1=$( $CLI $B1ARGS getrawtransaction $TXID1 )
@@ -122,7 +124,9 @@ echo "Mutated: " $MUTATEDTXID
$CLI $B2ARGS addnode 127.0.0.1:11000 onetry
WaitPeers "$B1ARGS" 1
-$CLI $B2ARGS setgenerate true 1
+$CLI $B2ARGS setgenerate true 3
+WaitBlocks
+$CLI $B1ARGS setgenerate true 3
WaitBlocks
$CLI $B2ARGS stop > /dev/null 2>&1
diff --git a/src/rpcwallet.cpp b/src/rpcwallet.cpp
index 2b49762d4a..97c4008dac 100644
--- a/src/rpcwallet.cpp
+++ b/src/rpcwallet.cpp
@@ -51,7 +51,12 @@ void WalletTxToJSON(const CWalletTx& wtx, Object& entry)
entry.push_back(Pair("blockindex", wtx.nIndex));
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("timereceived", (boost::int64_t)wtx.nTimeReceived));
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)
{
const CWalletTx& wtx = (*it).second;
- if (!wtx.IsTrusted())
+ if (!wtx.IsTrusted() || wtx.GetBlocksToMaturity() > 0)
continue;
int64_t allFee;
@@ -1325,6 +1330,8 @@ Value listaccounts(const Array& params, bool fHelp)
string strSentAccount;
list<pair<CTxDestination, int64_t> > listReceived;
list<pair<CTxDestination, int64_t> > listSent;
+ if (wtx.GetBlocksToMaturity() > 0)
+ continue;
wtx.GetAmounts(listReceived, listSent, nFee, strSentAccount);
mapAccountBalances[strSentAccount] -= nFee;
BOOST_FOREACH(const PAIRTYPE(CTxDestination, int64_t)& s, listSent)
diff --git a/src/wallet.cpp b/src/wallet.cpp
index b579480f7d..b4d6fc3199 100644
--- a/src/wallet.cpp
+++ b/src/wallet.cpp
@@ -231,6 +231,82 @@ bool CWallet::SetMaxVersion(int nVersion)
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)
{
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();
+
+ if (fFromLoadWallet)
+ {
+ mapWallet[hash] = wtxIn;
+ AddToConflicts(hash);
+ }
+ else
{
LOCK(cs_wallet);
// 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.hashBlock.ToString());
}
+ AddToConflicts(hash);
}
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()
{
// 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)
{
const CWalletTx* pcoin = &(*it).second;
- if (!IsFinalTx(*pcoin) || !pcoin->IsTrusted())
+ if (!IsFinalTx(*pcoin) || (!pcoin->IsTrusted() && pcoin->GetDepthInMainChain() == 0))
nTotal += pcoin->GetAvailableCredit();
}
}
diff --git a/src/wallet.h b/src/wallet.h
index dc709632b4..95fb0ee9de 100644
--- a/src/wallet.h
+++ b/src/wallet.h
@@ -108,6 +108,12 @@ private:
int64_t nNextResend;
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:
/// Main wallet lock.
/// This lock protects all the fields added by CWallet
@@ -151,6 +157,7 @@ public:
}
std::map<uint256, CWalletTx> mapWallet;
+
int64_t nOrderPosNext;
std::map<uint256, int> mapRequestCount;
@@ -223,7 +230,7 @@ public:
TxItems OrderedTxItems(std::list<CAccountingEntry>& acentries, std::string strAccount = "");
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);
bool AddToWalletIfInvolvingMe(const uint256 &hash, const CTransaction& tx, const CBlock* pblock, bool fUpdate);
void EraseFromWallet(const uint256 &hash);
@@ -358,6 +365,9 @@ public:
// get the current wallet format (the oldest client version guaranteed to understand this wallet)
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.
* @note called with lock cs_wallet held.
*/
@@ -753,6 +763,8 @@ public:
void AddSupportingTransactions();
bool AcceptWalletTransaction();
void RelayWalletTransaction();
+
+ std::set<uint256> GetConflicts() const;
};
diff --git a/src/walletdb.cpp b/src/walletdb.cpp
index b3cc9a2350..2f8c827bc2 100644
--- a/src/walletdb.cpp
+++ b/src/walletdb.cpp
@@ -382,7 +382,7 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue,
if (wtx.nOrderPos == -1)
wss.fAnyUnordered = true;
- pwallet->mapWallet[hash] = wtx;
+ pwallet->AddToWallet(wtx, true);
//// debug print
//LogPrintf("LoadWallet %s\n", wtx.GetHash().ToString());
//LogPrintf(" %12"PRId64" %s %s %s\n",