diff options
-rw-r--r-- | contrib/debian/manpages/bitcoin-qt.1 | 3 | ||||
-rw-r--r-- | doc/release-notes.md | 46 | ||||
-rw-r--r-- | src/bloom.cpp | 7 | ||||
-rw-r--r-- | src/bloom.h | 2 | ||||
-rw-r--r-- | src/core.cpp | 16 | ||||
-rw-r--r-- | src/core.h | 3 | ||||
-rw-r--r-- | src/init.cpp | 1 | ||||
-rw-r--r-- | src/main.cpp | 104 | ||||
-rw-r--r-- | src/main.h | 3 | ||||
-rw-r--r-- | src/qt/guiconstants.h | 4 | ||||
-rw-r--r-- | src/qt/transactionfilterproxy.cpp | 4 | ||||
-rw-r--r-- | src/qt/transactionrecord.cpp | 10 | ||||
-rw-r--r-- | src/qt/transactionrecord.h | 24 | ||||
-rw-r--r-- | src/qt/transactiontablemodel.cpp | 18 | ||||
-rw-r--r-- | src/qt/walletmodel.cpp | 8 | ||||
-rw-r--r-- | src/rpcwallet.cpp | 22 | ||||
-rw-r--r-- | src/test/bloom_tests.cpp | 4 | ||||
-rw-r--r-- | src/txmempool.cpp | 1 | ||||
-rw-r--r-- | src/ui_interface.h | 3 | ||||
-rw-r--r-- | src/wallet.cpp | 41 | ||||
-rw-r--r-- | src/wallet.h | 15 |
21 files changed, 42 insertions, 297 deletions
diff --git a/contrib/debian/manpages/bitcoin-qt.1 b/contrib/debian/manpages/bitcoin-qt.1 index 25a423f9c4..cd478b1875 100644 --- a/contrib/debian/manpages/bitcoin-qt.1 +++ b/contrib/debian/manpages/bitcoin-qt.1 @@ -139,9 +139,6 @@ Execute command when the best block changes (%s in cmd is replaced by block hash \fB\-walletnotify=\fR<cmd> Execute command when a wallet transaction changes (%s in cmd is replaced by TxID) .TP -\fB\-respendnotify=\fR<cmd> -Execute command when a network tx respends wallet tx input (%s=respend TxID, %t=wallet TxID) -.TP \fB\-alertnotify=\fR<cmd> Execute command when a relevant alert is received (%s in cmd is replaced by message) .TP diff --git a/doc/release-notes.md b/doc/release-notes.md index 66059800b6..967a39a0e7 100644 --- a/doc/release-notes.md +++ b/doc/release-notes.md @@ -39,49 +39,3 @@ estimate. Statistics used to estimate fees and priorities are saved in the data directory in the 'fee_estimates.dat' file just before program shutdown, and are read in at startup. - -Double-Spend Relay and Alerts -============================= -VERY IMPORTANT: *It has never been safe, and remains unsafe, to rely* -*on unconfirmed transactions.* - -Relay ------ -When an attempt is seen on the network to spend the same unspent funds -more than once, it is no longer ignored. Instead, it is broadcast, to -serve as an alert. This broadcast is subject to protections against -denial-of-service attacks. - -Wallets and other bitcoin services should alert their users to -double-spends that affect them. Merchants and other users may have -enough time to withhold goods or services when payment becomes -uncertain, until confirmation. - -Bitcoin Core Wallet Alerts --------------------------- -The Bitcoin Core wallet now makes respend attempts visible in several -ways. - -If you are online, and a respend affecting one of your wallet -transactions is seen, a notification is immediately issued to the -command registered with `-respendnotify=<cmd>`. Additionally, if -using the GUI: - - An alert box is immediately displayed. - - The affected wallet transaction is highlighted in red until it is - confirmed (and it may never be confirmed). - -A `respendsobserved` array is added to `gettransaction`, `listtransactions`, -and `listsinceblock` RPC results. - -Warning -------- -*If you rely on an unconfirmed transaction, these change do VERY* -*LITTLE to protect you from a malicious double-spend, because:* - - - You may learn about the respend too late to avoid doing whatever - you were being paid for - - Using other relay rules, a double-spender can craft his crime to - resist broadcast - - Miners can choose which conflicting spend to confirm, and some - miners may not confirm the first acceptable spend they see - diff --git a/src/bloom.cpp b/src/bloom.cpp index 85a2ddc189..26e366179c 100644 --- a/src/bloom.cpp +++ b/src/bloom.cpp @@ -94,13 +94,6 @@ bool CBloomFilter::contains(const uint256& hash) const return contains(data); } -void CBloomFilter::clear() -{ - vData.assign(vData.size(),0); - isFull = false; - isEmpty = true; -} - bool CBloomFilter::IsWithinSizeConstraints() const { return vData.size() <= MAX_BLOOM_FILTER_SIZE && nHashFuncs <= MAX_HASH_FUNCS; diff --git a/src/bloom.h b/src/bloom.h index d0caf9e9fa..956bead87f 100644 --- a/src/bloom.h +++ b/src/bloom.h @@ -78,8 +78,6 @@ public: bool contains(const COutPoint& outpoint) const; bool contains(const uint256& hash) const; - void clear(); - // True if the size is <= MAX_BLOOM_FILTER_SIZE and the number of hash functions is <= MAX_HASH_FUNCS // (catch a filter which was just deserialized which was too big) bool IsWithinSizeConstraints() const; diff --git a/src/core.cpp b/src/core.cpp index b56994ecf3..149b3532a1 100644 --- a/src/core.cpp +++ b/src/core.cpp @@ -124,22 +124,6 @@ CTransaction& CTransaction::operator=(const CTransaction &tx) { return *this; } -bool CTransaction::IsEquivalentTo(const CTransaction& tx) const -{ - if (nVersion != tx.nVersion || - nLockTime != tx.nLockTime || - vin.size() != tx.vin.size() || - vout != tx.vout) - return false; - for (unsigned int i = 0; i < vin.size(); i++) - { - if (vin[i].nSequence != tx.vin[i].nSequence || - vin[i].prevout != tx.vin[i].prevout) - return false; - } - return true; -} - int64_t CTransaction::GetValueOut() const { int64_t nValueOut = 0; diff --git a/src/core.h b/src/core.h index 0387336c98..fb64e6c08e 100644 --- a/src/core.h +++ b/src/core.h @@ -255,9 +255,6 @@ public: return hash; } - // True if only scriptSigs are different - bool IsEquivalentTo(const CTransaction& tx) const; - // Return sum of txouts. int64_t GetValueOut() const; // GetValueIn() is a method on CCoinsViewCache, because diff --git a/src/init.cpp b/src/init.cpp index 031b9db480..3488a8bedf 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -1229,7 +1229,6 @@ bool AppInit2(boost::thread_group& threadGroup) LogPrintf("mapAddressBook.size() = %u\n", pwalletMain ? pwalletMain->mapAddressBook.size() : 0); #endif - InitRespendFilter(); StartNode(threadGroup); if (fServer) StartRPCThreads(); diff --git a/src/main.cpp b/src/main.cpp index dd9e76378e..84178b16e2 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -7,7 +7,6 @@ #include "addrman.h" #include "alert.h" -#include "bloom.h" #include "chainparams.h" #include "checkpoints.h" #include "checkqueue.h" @@ -126,15 +125,6 @@ namespace { } // anon namespace -// Bloom filter to limit respend relays to one -static const unsigned int MAX_DOUBLESPEND_BLOOM = 1000; -static CBloomFilter doubleSpendFilter; -void InitRespendFilter() { - seed_insecure_rand(); - doubleSpendFilter = CBloomFilter(MAX_DOUBLESPEND_BLOOM, 0.01, insecure_rand(), BLOOM_UPDATE_NONE); -} - - ////////////////////////////////////////////////////////////////////////////// // // dispatching functions @@ -161,7 +151,6 @@ struct CMainSignals { } // anon namespace - void RegisterWallet(CWalletInterface* pwalletIn) { g_signals.SyncTransaction.connect(boost::bind(&CWalletInterface::SyncTransaction, pwalletIn, _1, _2)); g_signals.EraseTransaction.connect(boost::bind(&CWalletInterface::EraseFromWallet, pwalletIn, _1)); @@ -881,60 +870,6 @@ int64_t GetMinRelayFee(const CTransaction& tx, unsigned int nBytes, bool fAllowF return nMinFee; } -// Exponentially limit the rate of nSize flow to nLimit. nLimit unit is thousands-per-minute. -bool RateLimitExceeded(double& dCount, int64_t& nLastTime, int64_t nLimit, unsigned int nSize) -{ - static CCriticalSection csLimiter; - int64_t nNow = GetTime(); - - LOCK(csLimiter); - - dCount *= pow(1.0 - 1.0/600.0, (double)(nNow - nLastTime)); - nLastTime = nNow; - if (dCount >= nLimit*10*1000) - return true; - dCount += nSize; - return false; -} - -static bool RelayableRespend(const COutPoint& outPoint, const CTransaction& doubleSpend, bool fInBlock, CBloomFilter& filter) -{ - // Relaying double-spend attempts to our peers lets them detect when - // somebody might be trying to cheat them. However, blindly relaying - // every double-spend across the entire network gives attackers - // a denial-of-service attack: just generate a stream of double-spends - // re-spending the same (limited) set of outpoints owned by the attacker. - // So, we use a bloom filter and only relay (at most) the first double - // spend for each outpoint. False-positives ("we have already relayed") - // are OK, because if the peer doesn't hear about the double-spend - // from us they are very likely to hear about it from another peer, since - // each peer uses a different, randomized bloom filter. - - if (fInBlock || filter.contains(outPoint)) return false; - - // Apply an independent rate limit to double-spend relays - static double dRespendCount; - static int64_t nLastRespendTime; - static int64_t nRespendLimit = GetArg("-limitrespendrelay", 100); - unsigned int nSize = ::GetSerializeSize(doubleSpend, SER_NETWORK, PROTOCOL_VERSION); - - if (RateLimitExceeded(dRespendCount, nLastRespendTime, nRespendLimit, nSize)) - { - LogPrint("mempool", "Double-spend relay rejected by rate limiter\n"); - return false; - } - - LogPrint("mempool", "Rate limit dRespendCount: %g => %g\n", dRespendCount, dRespendCount+nSize); - - // Clear the filter on average every MAX_DOUBLE_SPEND_BLOOM - // insertions - if (insecure_rand()%MAX_DOUBLESPEND_BLOOM == 0) - filter.clear(); - - filter.insert(outPoint); - - return true; -} bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransaction &tx, bool fLimitFree, bool* pfMissingInputs, bool fRejectInsaneFee) @@ -964,18 +899,15 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa return false; // Check for conflicts with in-memory transactions - bool relayableRespend = false; { LOCK(pool.cs); // protect pool.mapNextTx for (unsigned int i = 0; i < tx.vin.size(); i++) { COutPoint outpoint = tx.vin[i].prevout; - // Does tx conflict with a member of the pool, and is it not equivalent to that member? - if (pool.mapNextTx.count(outpoint) && !tx.IsEquivalentTo(*pool.mapNextTx[outpoint].ptx)) + if (pool.mapNextTx.count(outpoint)) { - relayableRespend = RelayableRespend(outpoint, tx, false, doubleSpendFilter); - if (!relayableRespend) - return false; + // Disable replacement feature for now + return false; } } } @@ -1046,15 +978,23 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa // be annoying or make others' transactions take longer to confirm. if (fLimitFree && nFees < ::minRelayTxFee.GetFee(nSize)) { + static CCriticalSection csFreeLimiter; static double dFreeCount; - static int64_t nLastFreeTime; - static int64_t nFreeLimit = GetArg("-limitfreerelay", 15); + static int64_t nLastTime; + int64_t nNow = GetTime(); - if (RateLimitExceeded(dFreeCount, nLastFreeTime, nFreeLimit, nSize)) + LOCK(csFreeLimiter); + + // Use an exponentially decaying ~10-minute window: + dFreeCount *= pow(1.0 - 1.0/600.0, (double)(nNow - nLastTime)); + nLastTime = nNow; + // -limitfreerelay unit is thousand-bytes-per-minute + // At default rate it would take over a month to fill 1GB + if (dFreeCount >= GetArg("-limitfreerelay", 15)*10*1000) return state.DoS(0, error("AcceptToMemoryPool : free transaction rejected by rate limiter"), REJECT_INSUFFICIENTFEE, "insufficient priority"); - LogPrint("mempool", "Rate limit dFreeCount: %g => %g\n", dFreeCount, dFreeCount+nSize); + dFreeCount += nSize; } if (fRejectInsaneFee && nFees > ::minRelayTxFee.GetFee(nSize) * 10000) @@ -1068,21 +1008,13 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa { return error("AcceptToMemoryPool: : ConnectInputs failed %s", hash.ToString()); } - - if (relayableRespend) - { - RelayTransaction(tx); - } - else - { - // Store transaction in memory - pool.addUnchecked(hash, entry); - } + // Store transaction in memory + pool.addUnchecked(hash, entry); } g_signals.SyncTransaction(tx, NULL); - return !relayableRespend; + return true; } diff --git a/src/main.h b/src/main.h index 20a83db605..5f231fa45b 100644 --- a/src/main.h +++ b/src/main.h @@ -112,9 +112,6 @@ struct CNodeStateStats; struct CBlockTemplate; -/** Initialize respend bloom filter **/ -void InitRespendFilter(); - /** Register a wallet to receive updates from core */ void RegisterWallet(CWalletInterface* pwalletIn); /** Unregister a wallet from core */ diff --git a/src/qt/guiconstants.h b/src/qt/guiconstants.h index 696761e234..5ae4bc833d 100644 --- a/src/qt/guiconstants.h +++ b/src/qt/guiconstants.h @@ -23,10 +23,6 @@ static const int STATUSBAR_ICONSIZE = 16; #define COLOR_NEGATIVE QColor(255, 0, 0) /* Transaction list -- bare address (without label) */ #define COLOR_BAREADDRESS QColor(140, 140, 140) -/* Transaction list -- has conflicting transactions */ -#define COLOR_HASCONFLICTING QColor(255, 255, 255) -/* Transaction list -- has conflicting transactions - background */ -#define COLOR_HASCONFLICTING_BG QColor(192, 0, 0) /* Tooltips longer than this (in characters) are converted into rich text, so that they can be word-wrapped. diff --git a/src/qt/transactionfilterproxy.cpp b/src/qt/transactionfilterproxy.cpp index 7293029787..f9546fddb5 100644 --- a/src/qt/transactionfilterproxy.cpp +++ b/src/qt/transactionfilterproxy.cpp @@ -24,7 +24,7 @@ TransactionFilterProxy::TransactionFilterProxy(QObject *parent) : typeFilter(ALL_TYPES), minAmount(0), limitRows(-1), - showInactive(false) + showInactive(true) { } @@ -39,7 +39,7 @@ bool TransactionFilterProxy::filterAcceptsRow(int sourceRow, const QModelIndex & qint64 amount = llabs(index.data(TransactionTableModel::AmountRole).toLongLong()); int status = index.data(TransactionTableModel::StatusRole).toInt(); - if(!showInactive && status == TransactionStatus::Conflicted && type == TransactionRecord::Other) + if(!showInactive && status == TransactionStatus::Conflicted) return false; if(!(TYPE(type) & typeFilter)) return false; diff --git a/src/qt/transactionrecord.cpp b/src/qt/transactionrecord.cpp index 7d29c212b3..d7bd25e08b 100644 --- a/src/qt/transactionrecord.cpp +++ b/src/qt/transactionrecord.cpp @@ -184,8 +184,6 @@ void TransactionRecord::updateStatus(const CWalletTx &wtx) status.depth = wtx.GetDepthInMainChain(); status.cur_num_blocks = chainActive.Height(); - status.hasConflicting = false; - if (!IsFinalTx(wtx, chainActive.Height() + 1)) { if (wtx.nLockTime < LOCKTIME_THRESHOLD) @@ -229,7 +227,6 @@ void TransactionRecord::updateStatus(const CWalletTx &wtx) if (status.depth < 0) { status.status = TransactionStatus::Conflicted; - status.hasConflicting = !(wtx.GetConflicts(false).empty()); } else if (GetAdjustedTime() - wtx.nTimeReceived > 2 * 60 && wtx.GetRequestCount() == 0) { @@ -238,7 +235,6 @@ void TransactionRecord::updateStatus(const CWalletTx &wtx) else if (status.depth == 0) { status.status = TransactionStatus::Unconfirmed; - status.hasConflicting = !(wtx.GetConflicts(false).empty()); } else if (status.depth < RecommendedNumConfirmations) { @@ -249,13 +245,13 @@ void TransactionRecord::updateStatus(const CWalletTx &wtx) status.status = TransactionStatus::Confirmed; } } + } -bool TransactionRecord::statusUpdateNeeded(int64_t nConflictsReceived) +bool TransactionRecord::statusUpdateNeeded() { AssertLockHeld(cs_main); - return (status.cur_num_blocks != chainActive.Height() || - status.cur_num_conflicts != nConflictsReceived); + return status.cur_num_blocks != chainActive.Height(); } QString TransactionRecord::getTxID() const diff --git a/src/qt/transactionrecord.h b/src/qt/transactionrecord.h index d3cfa77d97..626b7654c6 100644 --- a/src/qt/transactionrecord.h +++ b/src/qt/transactionrecord.h @@ -19,17 +19,9 @@ class TransactionStatus { public: TransactionStatus(): - countsForBalance(false), - sortKey(""), - matures_in(0), - status(Offline), - hasConflicting(false), - depth(0), - open_for(0), - cur_num_blocks(-1), - cur_num_conflicts(-1) - { - } + countsForBalance(false), sortKey(""), + matures_in(0), status(Offline), depth(0), open_for(0), cur_num_blocks(-1) + { } enum Status { Confirmed, /**< Have 6 or more confirmations (normal tx) or fully mature (mined tx) **/ @@ -59,10 +51,6 @@ public: /** @name Reported status @{*/ Status status; - - // Has conflicting transactions spending same prevout - bool hasConflicting; - qint64 depth; qint64 open_for; /**< Timestamp if status==OpenUntilDate, otherwise number of additional blocks that need to be mined before @@ -71,10 +59,6 @@ public: /** Current number of blocks (to know whether cached status is still valid) */ int cur_num_blocks; - - /** Number of conflicts received into wallet as of last status update */ - int64_t cur_num_conflicts; - }; /** UI model for a transaction. A core transaction can be represented by multiple UI transactions if it has @@ -152,7 +136,7 @@ public: /** Return whether a status update is needed. */ - bool statusUpdateNeeded(int64_t nConflictsReceived); + bool statusUpdateNeeded(); }; #endif // TRANSACTIONRECORD_H diff --git a/src/qt/transactiontablemodel.cpp b/src/qt/transactiontablemodel.cpp index cf4c90c7ff..4825713b69 100644 --- a/src/qt/transactiontablemodel.cpp +++ b/src/qt/transactiontablemodel.cpp @@ -167,7 +167,8 @@ public: parent->endRemoveRows(); break; case CT_UPDATED: - emit parent->dataChanged(parent->index(lowerIndex, parent->Status), parent->index(upperIndex-1, parent->Amount)); + // Miscellaneous updates -- nothing to do, status update will take care of this, and is only computed for + // visible transactions. break; } } @@ -188,21 +189,20 @@ public: // stuck if the core is holding the locks for a longer time - for // example, during a wallet rescan. // - // If a status update is needed (blocks or conflicts came in since last check), - // update the status of this transaction from the wallet. Otherwise, + // If a status update is needed (blocks came in since last check), + // update the status of this transaction from the wallet. Otherwise, // simply re-use the cached status. TRY_LOCK(cs_main, lockMain); if(lockMain) { TRY_LOCK(wallet->cs_wallet, lockWallet); - if(lockWallet && rec->statusUpdateNeeded(wallet->nConflictsReceived)) + if(lockWallet && rec->statusUpdateNeeded()) { std::map<uint256, CWalletTx>::iterator mi = wallet->mapWallet.find(rec->hash); if(mi != wallet->mapWallet.end()) { rec->updateStatus(mi->second); - rec->status.cur_num_conflicts = wallet->nConflictsReceived; } } } @@ -368,8 +368,6 @@ QString TransactionTableModel::formatTxType(const TransactionRecord *wtx) const return tr("Payment to yourself"); case TransactionRecord::Generated: return tr("Mined"); - case TransactionRecord::Other: - return tr("Other"); default: return QString(); } @@ -545,13 +543,7 @@ QVariant TransactionTableModel::data(const QModelIndex &index, int role) const return formatTooltip(rec); case Qt::TextAlignmentRole: return column_alignments[index.column()]; - case Qt::BackgroundColorRole: - if (rec->status.hasConflicting) - return COLOR_HASCONFLICTING_BG; - break; case Qt::ForegroundRole: - if (rec->status.hasConflicting) - return COLOR_HASCONFLICTING; // Non-confirmed (but not immature) as transactions are grey if(!rec->status.countsForBalance && rec->status.status != TransactionStatus::Immature) { diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp index 7df8812ccd..0ad123f39d 100644 --- a/src/qt/walletmodel.cpp +++ b/src/qt/walletmodel.cpp @@ -164,14 +164,6 @@ void WalletModel::checkBalanceChanged() void WalletModel::updateTransaction(const QString &hash, int status) { - if (status == CT_GOT_CONFLICT) - { - emit message(tr("Conflict Received"), - tr("WARNING: Transaction may never be confirmed. Its input was seen being spent by another transaction on the network. Wait for confirmation!"), - CClientUIInterface::MSG_WARNING); - return; - } - if(transactionTableModel) transactionTableModel->updateTransaction(hash, status); diff --git a/src/rpcwallet.cpp b/src/rpcwallet.cpp index 631f72d1a1..5b83fe900e 100644 --- a/src/rpcwallet.cpp +++ b/src/rpcwallet.cpp @@ -58,10 +58,6 @@ void WalletTxToJSON(const CWalletTx& wtx, Object& entry) BOOST_FOREACH(const uint256& conflict, wtx.GetConflicts()) conflicts.push_back(conflict.GetHex()); entry.push_back(Pair("walletconflicts", conflicts)); - Array respends; - BOOST_FOREACH(const uint256& respend, wtx.GetConflicts(false)) - respends.push_back(respend.GetHex()); - entry.push_back(Pair("respendsobserved", respends)); entry.push_back(Pair("time", wtx.GetTxTime())); entry.push_back(Pair("timereceived", (int64_t)wtx.nTimeReceived)); BOOST_FOREACH(const PAIRTYPE(string,string)& item, wtx.mapValue) @@ -1252,12 +1248,6 @@ Value listtransactions(const Array& params, bool fHelp) " \"blockindex\": n, (numeric) The block index containing the transaction. Available for 'send' and 'receive'\n" " category of transactions.\n" " \"txid\": \"transactionid\", (string) The transaction id. Available for 'send' and 'receive' category of transactions.\n" - " \"walletconflicts\" : [\n" - " \"conflictid\", (string) Ids of transactions, including equivalent clones, that re-spend a txid input.\n" - " ],\n" - " \"respendsobserved\" : [\n" - " \"respendid\", (string) Ids of transactions, NOT equivalent clones, that re-spend a txid input. \"Double-spends.\"\n" - " ],\n" " \"time\": xxx, (numeric) The transaction time in seconds since epoch (midnight Jan 1 1970 GMT).\n" " \"timereceived\": xxx, (numeric) The time received in seconds since epoch (midnight Jan 1 1970 GMT). Available \n" " for 'send' and 'receive' category of transactions.\n" @@ -1434,12 +1424,6 @@ Value listsinceblock(const Array& params, bool fHelp) " \"blockindex\": n, (numeric) The block index containing the transaction. Available for 'send' and 'receive' category of transactions.\n" " \"blocktime\": xxx, (numeric) The block time in seconds since epoch (1 Jan 1970 GMT).\n" " \"txid\": \"transactionid\", (string) The transaction id. Available for 'send' and 'receive' category of transactions.\n" - " \"walletconflicts\" : [\n" - " \"conflictid\", (string) Ids of transactions, including equivalent clones, that re-spend a txid input.\n" - " ],\n" - " \"respendsobserved\" : [\n" - " \"respendid\", (string) Ids of transactions, NOT equivalent clones, that re-spend a txid input. \"Double-spends.\"\n" - " ],\n" " \"time\": xxx, (numeric) The transaction time in seconds since epoch (Jan 1 1970 GMT).\n" " \"timereceived\": xxx, (numeric) The time received in seconds since epoch (Jan 1 1970 GMT). Available for 'send' and 'receive' category of transactions.\n" " \"comment\": \"...\", (string) If a comment is associated with the transaction.\n" @@ -1518,12 +1502,6 @@ Value gettransaction(const Array& params, bool fHelp) " \"blockindex\" : xx, (numeric) The block index\n" " \"blocktime\" : ttt, (numeric) The time in seconds since epoch (1 Jan 1970 GMT)\n" " \"txid\" : \"transactionid\", (string) The transaction id.\n" - " \"walletconflicts\" : [\n" - " \"conflictid\", (string) Ids of transactions, including equivalent clones, that re-spend a txid input.\n" - " ],\n" - " \"respendsobserved\" : [\n" - " \"respendid\", (string) Ids of transactions, NOT equivalent clones, that re-spend a txid input. \"Double-spends.\"\n" - " ],\n" " \"time\" : ttt, (numeric) The transaction time in seconds since epoch (1 Jan 1970 GMT)\n" " \"timereceived\" : ttt, (numeric) The time received in seconds since epoch (1 Jan 1970 GMT)\n" " \"details\" : [\n" diff --git a/src/test/bloom_tests.cpp b/src/test/bloom_tests.cpp index 2cdafa4bdd..69de3b5bb1 100644 --- a/src/test/bloom_tests.cpp +++ b/src/test/bloom_tests.cpp @@ -45,10 +45,6 @@ BOOST_AUTO_TEST_CASE(bloom_create_insert_serialize) expected[i] = (char)vch[i]; BOOST_CHECK_EQUAL_COLLECTIONS(stream.begin(), stream.end(), expected.begin(), expected.end()); - - BOOST_CHECK_MESSAGE( filter.contains(ParseHex("99108ad8ed9bb6274d3980bab5a85c048f0950c8")), "BloomFilter doesn't contain just-inserted object!"); - filter.clear(); - BOOST_CHECK_MESSAGE( !filter.contains(ParseHex("99108ad8ed9bb6274d3980bab5a85c048f0950c8")), "BloomFilter should be empty!"); } BOOST_AUTO_TEST_CASE(bloom_create_insert_serialize_with_tweak) diff --git a/src/txmempool.cpp b/src/txmempool.cpp index a852de5da8..ebb1369e31 100644 --- a/src/txmempool.cpp +++ b/src/txmempool.cpp @@ -420,6 +420,7 @@ void CTxMemPool::remove(const CTransaction &tx, std::list<CTransaction>& removed void CTxMemPool::removeConflicts(const CTransaction &tx, std::list<CTransaction>& removed) { // Remove transactions which depend on inputs of tx, recursively + list<CTransaction> result; LOCK(cs); BOOST_FOREACH(const CTxIn &txin, tx.vin) { std::map<COutPoint, CInPoint>::iterator it = mapNextTx.find(txin.prevout); diff --git a/src/ui_interface.h b/src/ui_interface.h index a48ba237d2..b3df2b5a85 100644 --- a/src/ui_interface.h +++ b/src/ui_interface.h @@ -21,8 +21,7 @@ enum ChangeType { CT_NEW, CT_UPDATED, - CT_DELETED, - CT_GOT_CONFLICT + CT_DELETED }; /** Signals for UI communication. */ diff --git a/src/wallet.cpp b/src/wallet.cpp index efefb71b6d..7c04743c0f 100644 --- a/src/wallet.cpp +++ b/src/wallet.cpp @@ -274,7 +274,7 @@ bool CWallet::SetMaxVersion(int nVersion) return true; } -set<uint256> CWallet::GetConflicts(const uint256& txid, bool includeEquivalent) const +set<uint256> CWallet::GetConflicts(const uint256& txid) const { set<uint256> result; AssertLockHeld(cs_wallet); @@ -292,8 +292,7 @@ set<uint256> CWallet::GetConflicts(const uint256& txid, bool includeEquivalent) continue; // No conflict if zero or one spends range = mapTxSpends.equal_range(txin.prevout); for (TxSpends::const_iterator it = range.first; it != range.second; ++it) - if (includeEquivalent || !wtx.IsEquivalentTo(mapWallet.at(it->second))) - result.insert(it->second); + result.insert(it->second); } return result; } @@ -322,7 +321,6 @@ void CWallet::SyncMetaData(pair<TxSpends::iterator, TxSpends::iterator> range) const uint256& hash = it->second; CWalletTx* copyTo = &mapWallet[hash]; if (copyFrom == copyTo) continue; - if (!copyFrom->IsEquivalentTo(*copyTo)) continue; copyTo->mapValue = copyFrom->mapValue; copyTo->vOrderForm = copyFrom->vOrderForm; // fTimeReceivedIsTxTime not copied on purpose @@ -610,28 +608,6 @@ bool CWallet::AddToWallet(const CWalletTx& wtxIn, bool fFromLoadWallet) // Notify UI of new or updated transaction NotifyTransactionChanged(this, hash, fInsertedNew ? CT_NEW : CT_UPDATED); - // Notifications for existing transactions that now have conflicts with this one - if (fInsertedNew) - { - BOOST_FOREACH(const uint256& conflictHash, wtxIn.GetConflicts(false)) - { - CWalletTx& txConflict = mapWallet[conflictHash]; - NotifyTransactionChanged(this, conflictHash, CT_UPDATED); //Updates UI table - if (IsFromMe(txConflict) || IsMine(txConflict)) - { - NotifyTransactionChanged(this, conflictHash, CT_GOT_CONFLICT); //Throws dialog - // external respend notify - std::string strCmd = GetArg("-respendnotify", ""); - if (!strCmd.empty()) - { - boost::replace_all(strCmd, "%s", wtxIn.GetHash().GetHex()); - boost::replace_all(strCmd, "%t", conflictHash.GetHex()); - boost::thread t(runCommand, strCmd); // thread runs free - } - } - } - } - // notify an external script when a wallet transaction comes in or is updated std::string strCmd = GetArg("-walletnotify", ""); @@ -654,12 +630,7 @@ bool CWallet::AddToWalletIfInvolvingMe(const CTransaction& tx, const CBlock* pbl AssertLockHeld(cs_wallet); bool fExisted = mapWallet.count(tx.GetHash()); if (fExisted && !fUpdate) return false; - - bool fIsConflicting = IsConflicting(tx); - if (fIsConflicting) - nConflictsReceived++; - - if (fExisted || IsMine(tx) || IsFromMe(tx) || fIsConflicting) + if (fExisted || IsMine(tx) || IsFromMe(tx)) { CWalletTx wtx(this,tx); // Get merkle branch if transaction was found in a block @@ -948,7 +919,7 @@ void CWallet::ReacceptWalletTransactions() int nDepth = wtx.GetDepthInMainChain(); - if (!wtx.IsCoinBase() && nDepth < 0 && (IsMine(wtx) || IsFromMe(wtx))) + if (!wtx.IsCoinBase() && nDepth < 0) { // Try to add to memory pool LOCK(mempool.cs); @@ -968,13 +939,13 @@ void CWalletTx::RelayWalletTransaction() } } -set<uint256> CWalletTx::GetConflicts(bool includeEquivalent) const +set<uint256> CWalletTx::GetConflicts() const { set<uint256> result; if (pwallet != NULL) { uint256 myHash = GetHash(); - result = pwallet->GetConflicts(myHash, includeEquivalent); + result = pwallet->GetConflicts(myHash); result.erase(myHash); } return result; diff --git a/src/wallet.h b/src/wallet.h index eaf9e96309..73fcfa24e0 100644 --- a/src/wallet.h +++ b/src/wallet.h @@ -144,9 +144,6 @@ public: MasterKeyMap mapMasterKeys; unsigned int nMasterKeyMaxID; - // Increment to cause UI refresh, similar to new block - int64_t nConflictsReceived; - CWallet() { SetNull(); @@ -169,7 +166,6 @@ public: nNextResend = 0; nLastResend = 0; nTimeFirstKey = 0; - nConflictsReceived = 0; } std::map<uint256, CWalletTx> mapWallet; @@ -322,13 +318,6 @@ public: { return (GetDebit(tx, ISMINE_ALL) > 0); } - bool IsConflicting(const CTransaction& tx) const - { - BOOST_FOREACH(const CTxIn& txin, tx.vin) - if (mapTxSpends.count(txin.prevout)) - return true; - return false; - } int64_t GetDebit(const CTransaction& tx, const isminefilter& filter) const { int64_t nDebit = 0; @@ -401,7 +390,7 @@ public: int GetVersion() { LOCK(cs_wallet); return nWalletVersion; } // Get wallet transactions that conflict with given transaction (spend same outputs) - std::set<uint256> GetConflicts(const uint256& txid, bool includeEquivalent) const; + std::set<uint256> GetConflicts(const uint256& txid) const; /** Address book entry changed. * @note called with lock cs_wallet held. @@ -812,7 +801,7 @@ public: void RelayWalletTransaction(); - std::set<uint256> GetConflicts(bool includeEquivalent=true) const; + std::set<uint256> GetConflicts() const; }; |