aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.md52
-rw-r--r--bitcoin-qt.pro26
-rwxr-xr-xcontrib/macdeploy/macdeployqtplus12
-rw-r--r--src/bloom.cpp156
-rw-r--r--src/bloom.h88
-rw-r--r--src/checkqueue.h206
-rw-r--r--src/hash.cpp58
-rw-r--r--src/hash.h3
-rw-r--r--src/init.cpp57
-rw-r--r--src/main.cpp355
-rw-r--r--src/main.h185
-rw-r--r--src/makefile.linux-mingw2
-rw-r--r--src/makefile.mingw2
-rw-r--r--src/makefile.osx2
-rw-r--r--src/makefile.unix2
-rw-r--r--src/net.cpp49
-rw-r--r--src/net.h63
-rw-r--r--src/protocol.cpp1
-rw-r--r--src/protocol.h3
-rw-r--r--src/qt/bitcoinstrings.cpp9
-rw-r--r--src/qt/locale/bitcoin_en.ts146
-rw-r--r--src/qt/signverifymessagedialog.cpp4
-rw-r--r--src/qt/signverifymessagedialog.h4
-rw-r--r--src/qt/transactiondesc.cpp2
-rw-r--r--src/qt/transactionrecord.cpp2
-rw-r--r--src/qt/transactionrecord.h4
-rw-r--r--src/qt/transactiontablemodel.cpp2
-rw-r--r--src/rpcblockchain.cpp7
-rw-r--r--src/rpcrawtransaction.cpp2
-rw-r--r--src/script.cpp31
-rw-r--r--src/script.h9
-rw-r--r--src/test/bloom_tests.cpp447
-rw-r--r--src/test/pmt_tests.cpp98
-rw-r--r--src/test/test_bitcoin.cpp4
-rw-r--r--src/uint256.h12
-rw-r--r--src/util.cpp97
-rw-r--r--src/util.h2
-rw-r--r--src/version.h2
-rw-r--r--src/wallet.cpp12
39 files changed, 1938 insertions, 280 deletions
diff --git a/README.md b/README.md
index 0c475ea6c4..1ecb8c7efe 100644
--- a/README.md
+++ b/README.md
@@ -22,21 +22,45 @@ or are controversial.
The master branch is regularly built and tested, but is not guaranteed
to be completely stable. Tags are regularly created to indicate new
-official, stable release versions of Bitcoin. If you would like to
-help test the Bitcoin core, please contact QA@BitcoinTesting.org.
+official, stable release versions of Bitcoin.
-Feature branches are created when there are major new features being
-worked on by several people.
+Testing
+=======
-From time to time a pull request will become outdated. If this occurs, and
-the pull is no longer automatically mergeable; a comment on the pull will
-be used to issue a warning of closure. The pull will be closed 15 days
-after the warning if action is not taken by the author. Pull requests closed
-in this manner will have their corresponding issue labeled 'stagnant'.
+Testing and code review is the bottleneck for development; we get more
+pull requests than we can review and test. Please be patient and help
+out, and remember this is a security-critical project where any
+mistake might cost people lots of money.
-Issues with no commits will be given a similar warning, and closed after
-15 days from their last activity. Issues closed in this manner will be
-labeled 'stale'.
+Automated Testing
+-----------------
-Requests to reopen closed pull requests and/or issues can be submitted to
-QA@BitcoinTesting.org. \ No newline at end of file
+Developers are strongly encouraged to write unit tests for new code,
+and to submit new unit tests for old code.
+
+Unit tests for the core code are in src/test/
+To compile and run them:
+ cd src; make -f makefile.linux test
+
+Unit tests for the GUI code are in src/qt/test/
+To compile and run them:
+ qmake BITCOIN_QT_TEST=1 -o Makefile.test bitcoin-qt.pro
+ make -f Makefile.test
+ ./Bitcoin-Qt
+
+Every pull request is built for both Windows and
+Linux on a dedicated server, and unit and sanity
+tests are automatically run. The binaries
+produced may be used for manual QA testing
+(a link to them will appear in a comment on the pull request
+from 'BitcoinPullTester').
+See https://github.com/TheBlueMatt/test-scripts for the
+build/test scripts.
+
+Manual Quality Assurance (QA) Testing
+-------------------------------------
+
+Large changes should have a test plan, and should be tested
+by somebody other than the developer who wrote the code.
+
+See https://github.com/bitcoin/QA/ for how to create a test plan.
diff --git a/bitcoin-qt.pro b/bitcoin-qt.pro
index 2ca142add6..6a2d7092c2 100644
--- a/bitcoin-qt.pro
+++ b/bitcoin-qt.pro
@@ -27,7 +27,7 @@ contains(RELEASE, 1) {
macx:QMAKE_CFLAGS += -mmacosx-version-min=10.5 -arch i386 -isysroot /Developer/SDKs/MacOSX10.5.sdk
macx:QMAKE_OBJECTIVE_CFLAGS += -mmacosx-version-min=10.5 -arch i386 -isysroot /Developer/SDKs/MacOSX10.5.sdk
- !windows:!macx {
+ !win32:!macx {
# Linux: static link
LIBS += -Wl,-Bstatic
}
@@ -96,7 +96,7 @@ contains(BITCOIN_NEED_QT_PLUGINS, 1) {
INCLUDEPATH += src/leveldb/include src/leveldb/helpers
LIBS += $$PWD/src/leveldb/libleveldb.a $$PWD/src/leveldb/libmemenv.a
-!windows {
+!win32 {
genleveldb.commands = cd $$PWD/src/leveldb && $(MAKE) libleveldb.a libmemenv.a
} else {
# make an educated guess about what the ranlib command is called
@@ -113,7 +113,7 @@ QMAKE_EXTRA_TARGETS += genleveldb
QMAKE_CLEAN += $$PWD/src/leveldb/libleveldb.a; cd $$PWD/src/leveldb ; $(MAKE) clean
# regenerate src/build.h
-!windows|contains(USE_BUILD_INFO, 1) {
+!win32|contains(USE_BUILD_INFO, 1) {
genbuild.depends = FORCE
genbuild.commands = cd $$PWD; /bin/sh share/genbuild.sh $$OUT_PWD/build/build.h
genbuild.target = $$OUT_PWD/build/build.h
@@ -155,7 +155,9 @@ HEADERS += src/qt/bitcoingui.h \
src/script.h \
src/init.h \
src/irc.h \
+ src/bloom.h \
src/mruset.h \
+ src/checkqueue.h \
src/json/json_spirit_writer_template.h \
src/json/json_spirit_writer.h \
src/json/json_spirit_value.h \
@@ -215,6 +217,7 @@ SOURCES += src/qt/bitcoin.cpp src/qt/bitcoingui.cpp \
src/version.cpp \
src/sync.cpp \
src/util.cpp \
+ src/hash.cpp \
src/netbase.cpp \
src/key.cpp \
src/script.cpp \
@@ -222,6 +225,7 @@ SOURCES += src/qt/bitcoin.cpp src/qt/bitcoingui.cpp \
src/init.cpp \
src/net.cpp \
src/irc.cpp \
+ src/bloom.cpp \
src/checkpoints.cpp \
src/addrman.cpp \
src/db.cpp \
@@ -321,7 +325,7 @@ OTHER_FILES += \
# platform specific defaults, if not overridden on command line
isEmpty(BOOST_LIB_SUFFIX) {
macx:BOOST_LIB_SUFFIX = -mt
- windows:BOOST_LIB_SUFFIX = -mgw44-mt-s-1_50
+ win32:BOOST_LIB_SUFFIX = -mgw44-mt-s-1_50
}
isEmpty(BOOST_THREAD_LIB_SUFFIX) {
@@ -348,10 +352,10 @@ isEmpty(BOOST_INCLUDE_PATH) {
macx:BOOST_INCLUDE_PATH = /opt/local/include
}
-windows:DEFINES += WIN32
-windows:RC_FILE = src/qt/res/bitcoin-qt.rc
+win32:DEFINES += WIN32
+win32:RC_FILE = src/qt/res/bitcoin-qt.rc
-windows:!contains(MINGW_THREAD_BUGFIX, 0) {
+win32:!contains(MINGW_THREAD_BUGFIX, 0) {
# At least qmake's win32-g++-cross profile is missing the -lmingwthrd
# thread-safety flag. GCC has -mthreads to enable this, but it doesn't
# work with static linking. -lmingwthrd must come BEFORE -lmingw, so
@@ -362,7 +366,7 @@ windows:!contains(MINGW_THREAD_BUGFIX, 0) {
QMAKE_LIBS_QT_ENTRY = -lmingwthrd $$QMAKE_LIBS_QT_ENTRY
}
-!windows:!macx {
+!win32:!macx {
DEFINES += LINUX
LIBS += -lrt
}
@@ -382,12 +386,12 @@ INCLUDEPATH += $$BOOST_INCLUDE_PATH $$BDB_INCLUDE_PATH $$OPENSSL_INCLUDE_PATH $$
LIBS += $$join(BOOST_LIB_PATH,,-L,) $$join(BDB_LIB_PATH,,-L,) $$join(OPENSSL_LIB_PATH,,-L,) $$join(QRENCODE_LIB_PATH,,-L,)
LIBS += -lssl -lcrypto -ldb_cxx$$BDB_LIB_SUFFIX
# -lgdi32 has to happen after -lcrypto (see #681)
-windows:LIBS += -lws2_32 -lshlwapi -lmswsock -lole32 -loleaut32 -luuid -lgdi32
+win32:LIBS += -lws2_32 -lshlwapi -lmswsock -lole32 -loleaut32 -luuid -lgdi32
LIBS += -lboost_system$$BOOST_LIB_SUFFIX -lboost_filesystem$$BOOST_LIB_SUFFIX -lboost_program_options$$BOOST_LIB_SUFFIX -lboost_thread$$BOOST_THREAD_LIB_SUFFIX
-windows:LIBS += -lboost_chrono$$BOOST_LIB_SUFFIX
+win32:LIBS += -lboost_chrono$$BOOST_LIB_SUFFIX
contains(RELEASE, 1) {
- !windows:!macx {
+ !win32:!macx {
# Linux: turn dynamic linking back on for c/c++ runtime libraries
LIBS += -Wl,-Bdynamic
}
diff --git a/contrib/macdeploy/macdeployqtplus b/contrib/macdeploy/macdeployqtplus
index 16daa59b08..cc60537589 100755
--- a/contrib/macdeploy/macdeployqtplus
+++ b/contrib/macdeploy/macdeployqtplus
@@ -172,7 +172,9 @@ class DeploymentInfo(object):
elif os.path.exists(os.path.join(os.path.dirname(parentDir), "share", "qt4", "translations")):
# Newer Macports layout
self.qtPath = os.path.join(os.path.dirname(parentDir), "share", "qt4")
-
+ else:
+ self.qtPath = os.getenv("QTDIR", None)
+
if self.qtPath is not None:
pluginPath = os.path.join(self.qtPath, "plugins")
if os.path.exists(pluginPath):
@@ -242,7 +244,11 @@ def runStrip(binaryPath, verbose):
subprocess.check_call(["strip", "-x", binaryPath])
def copyFramework(framework, path, verbose):
- fromPath = framework.sourceFilePath
+ if framework.sourceFilePath.startswith("Qt"):
+ #standard place for Nokia Qt installer's frameworks
+ fromPath = "/Library/Frameworks/" + framework.sourceFilePath
+ else:
+ fromPath = framework.sourceFilePath
toDir = os.path.join(path, framework.destinationDirectory)
toPath = os.path.join(toDir, framework.binaryName)
@@ -345,7 +351,7 @@ def deployPlugins(appBundleInfo, deploymentInfo, strip, verbose):
if pluginDirectory == "designer":
# Skip designer plugins
continue
- elif pluginDirectory == "phonon":
+ elif pluginDirectory == "phonon" or pluginDirectory == "phonon_backend":
# Deploy the phonon plugins only if phonon is in use
if not deploymentInfo.usesFramework("phonon"):
continue
diff --git a/src/bloom.cpp b/src/bloom.cpp
new file mode 100644
index 0000000000..36f5e50134
--- /dev/null
+++ b/src/bloom.cpp
@@ -0,0 +1,156 @@
+// Copyright (c) 2012 The Bitcoin developers
+// Distributed under the MIT/X11 software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+#include <math.h>
+#include <stdlib.h>
+
+#include "bloom.h"
+#include "main.h"
+#include "script.h"
+
+#define LN2SQUARED 0.4804530139182014246671025263266649717305529515945455
+#define LN2 0.6931471805599453094172321214581765680755001343602552
+
+using namespace std;
+
+static const unsigned char bit_mask[8] = {0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80};
+
+CBloomFilter::CBloomFilter(unsigned int nElements, double nFPRate, unsigned int nTweakIn, unsigned char nFlagsIn) :
+// The ideal size for a bloom filter with a given number of elements and false positive rate is:
+// - nElements * log(fp rate) / ln(2)^2
+// We ignore filter parameters which will create a bloom filter larger than the protocol limits
+vData(min((unsigned int)(-1 / LN2SQUARED * nElements * log(nFPRate)), MAX_BLOOM_FILTER_SIZE * 8) / 8),
+// The ideal number of hash functions is filter size * ln(2) / number of elements
+// Again, we ignore filter parameters which will create a bloom filter with more hash functions than the protocol limits
+// See http://en.wikipedia.org/wiki/Bloom_filter for an explanation of these formulas
+nHashFuncs(min((unsigned int)(vData.size() * 8 / nElements * LN2), MAX_HASH_FUNCS)),
+nTweak(nTweakIn),
+nFlags(nFlagsIn)
+{
+}
+
+inline unsigned int CBloomFilter::Hash(unsigned int nHashNum, const std::vector<unsigned char>& vDataToHash) const
+{
+ // 0xFBA4C795 chosen as it guarantees a reasonable bit difference between nHashNum values.
+ return MurmurHash3(nHashNum * 0xFBA4C795 + nTweak, vDataToHash) % (vData.size() * 8);
+}
+
+void CBloomFilter::insert(const vector<unsigned char>& vKey)
+{
+ for (unsigned int i = 0; i < nHashFuncs; i++)
+ {
+ unsigned int nIndex = Hash(i, vKey);
+ // Sets bit nIndex of vData
+ vData[nIndex >> 3] |= bit_mask[7 & nIndex];
+ }
+}
+
+void CBloomFilter::insert(const COutPoint& outpoint)
+{
+ CDataStream stream(SER_NETWORK, PROTOCOL_VERSION);
+ stream << outpoint;
+ vector<unsigned char> data(stream.begin(), stream.end());
+ insert(data);
+}
+
+void CBloomFilter::insert(const uint256& hash)
+{
+ vector<unsigned char> data(hash.begin(), hash.end());
+ insert(data);
+}
+
+bool CBloomFilter::contains(const vector<unsigned char>& vKey) const
+{
+ for (unsigned int i = 0; i < nHashFuncs; i++)
+ {
+ unsigned int nIndex = Hash(i, vKey);
+ // Checks bit nIndex of vData
+ if (!(vData[nIndex >> 3] & bit_mask[7 & nIndex]))
+ return false;
+ }
+ return true;
+}
+
+bool CBloomFilter::contains(const COutPoint& outpoint) const
+{
+ CDataStream stream(SER_NETWORK, PROTOCOL_VERSION);
+ stream << outpoint;
+ vector<unsigned char> data(stream.begin(), stream.end());
+ return contains(data);
+}
+
+bool CBloomFilter::contains(const uint256& hash) const
+{
+ vector<unsigned char> data(hash.begin(), hash.end());
+ return contains(data);
+}
+
+bool CBloomFilter::IsWithinSizeConstraints() const
+{
+ return vData.size() <= MAX_BLOOM_FILTER_SIZE && nHashFuncs <= MAX_HASH_FUNCS;
+}
+
+bool CBloomFilter::IsRelevantAndUpdate(const CTransaction& tx, const uint256& hash)
+{
+ bool fFound = false;
+ // Match if the filter contains the hash of tx
+ // for finding tx when they appear in a block
+ if (contains(hash))
+ fFound = true;
+
+ for (unsigned int i = 0; i < tx.vout.size(); i++)
+ {
+ const CTxOut& txout = tx.vout[i];
+ // Match if the filter contains any arbitrary script data element in any scriptPubKey in tx
+ // If this matches, also add the specific output that was matched.
+ // This means clients don't have to update the filter themselves when a new relevant tx
+ // is discovered in order to find spending transactions, which avoids round-tripping and race conditions.
+ CScript::const_iterator pc = txout.scriptPubKey.begin();
+ vector<unsigned char> data;
+ while (pc < txout.scriptPubKey.end())
+ {
+ opcodetype opcode;
+ if (!txout.scriptPubKey.GetOp(pc, opcode, data))
+ break;
+ if (data.size() != 0 && contains(data))
+ {
+ fFound = true;
+ if ((nFlags & BLOOM_UPDATE_MASK) == BLOOM_UPDATE_ALL)
+ insert(COutPoint(hash, i));
+ else if ((nFlags & BLOOM_UPDATE_MASK) == BLOOM_UPDATE_P2PUBKEY_ONLY)
+ {
+ txnouttype type;
+ vector<vector<unsigned char> > vSolutions;
+ if (Solver(txout.scriptPubKey, type, vSolutions) &&
+ (type == TX_PUBKEY || type == TX_MULTISIG))
+ insert(COutPoint(hash, i));
+ }
+ break;
+ }
+ }
+ }
+
+ if (fFound)
+ return true;
+
+ BOOST_FOREACH(const CTxIn& txin, tx.vin)
+ {
+ // Match if the filter contains an outpoint tx spends
+ if (contains(txin.prevout))
+ return true;
+
+ // Match if the filter contains any arbitrary script data element in any scriptSig in tx
+ CScript::const_iterator pc = txin.scriptSig.begin();
+ vector<unsigned char> data;
+ while (pc < txin.scriptSig.end())
+ {
+ opcodetype opcode;
+ if (!txin.scriptSig.GetOp(pc, opcode, data))
+ break;
+ if (data.size() != 0 && contains(data))
+ return true;
+ }
+ }
+
+ return false;
+}
diff --git a/src/bloom.h b/src/bloom.h
new file mode 100644
index 0000000000..389ae748e6
--- /dev/null
+++ b/src/bloom.h
@@ -0,0 +1,88 @@
+// Copyright (c) 2012 The Bitcoin developers
+// Distributed under the MIT/X11 software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+#ifndef BITCOIN_BLOOM_H
+#define BITCOIN_BLOOM_H
+
+#include <vector>
+
+#include "uint256.h"
+#include "serialize.h"
+
+class COutPoint;
+class CTransaction;
+
+// 20,000 items with fp rate < 0.1% or 10,000 items and <0.0001%
+static const unsigned int MAX_BLOOM_FILTER_SIZE = 36000; // bytes
+static const unsigned int MAX_HASH_FUNCS = 50;
+
+// First two bits of nFlags control how much IsRelevantAndUpdate actually updates
+// The remaining bits are reserved
+enum bloomflags
+{
+ BLOOM_UPDATE_NONE = 0,
+ BLOOM_UPDATE_ALL = 1,
+ // Only adds outpoints to the filter if the output is a pay-to-pubkey/pay-to-multisig script
+ BLOOM_UPDATE_P2PUBKEY_ONLY = 2,
+ BLOOM_UPDATE_MASK = 3,
+};
+
+/**
+ * BloomFilter is a probabilistic filter which SPV clients provide
+ * so that we can filter the transactions we sends them.
+ *
+ * This allows for significantly more efficient transaction and block downloads.
+ *
+ * Because bloom filters are probabilistic, an SPV node can increase the false-
+ * positive rate, making us send them transactions which aren't actually theirs,
+ * allowing clients to trade more bandwidth for more privacy by obfuscating which
+ * keys are owned by them.
+ */
+class CBloomFilter
+{
+private:
+ std::vector<unsigned char> vData;
+ unsigned int nHashFuncs;
+ unsigned int nTweak;
+ unsigned char nFlags;
+
+ unsigned int Hash(unsigned int nHashNum, const std::vector<unsigned char>& vDataToHash) const;
+
+public:
+ // Creates a new bloom filter which will provide the given fp rate when filled with the given number of elements
+ // Note that if the given parameters will result in a filter outside the bounds of the protocol limits,
+ // the filter created will be as close to the given parameters as possible within the protocol limits.
+ // This will apply if nFPRate is very low or nElements is unreasonably high.
+ // nTweak is a constant which is added to the seed value passed to the hash function
+ // It should generally always be a random value (and is largely only exposed for unit testing)
+ // nFlags should be one of the BLOOM_UPDATE_* enums (not _MASK)
+ CBloomFilter(unsigned int nElements, double nFPRate, unsigned int nTweak, unsigned char nFlagsIn);
+ // Using a filter initialized with this results in undefined behavior
+ // Should only be used for deserialization
+ CBloomFilter() {}
+
+ IMPLEMENT_SERIALIZE
+ (
+ READWRITE(vData);
+ READWRITE(nHashFuncs);
+ READWRITE(nTweak);
+ READWRITE(nFlags);
+ )
+
+ void insert(const std::vector<unsigned char>& vKey);
+ void insert(const COutPoint& outpoint);
+ void insert(const uint256& hash);
+
+ bool contains(const std::vector<unsigned char>& vKey) const;
+ bool contains(const COutPoint& outpoint) const;
+ bool contains(const uint256& hash) const;
+
+ // 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;
+
+ // Also adds any outputs which match the filter to the filter (to match their spending txes)
+ bool IsRelevantAndUpdate(const CTransaction& tx, const uint256& hash);
+};
+
+#endif /* BITCOIN_BLOOM_H */
diff --git a/src/checkqueue.h b/src/checkqueue.h
new file mode 100644
index 0000000000..36141dd74b
--- /dev/null
+++ b/src/checkqueue.h
@@ -0,0 +1,206 @@
+// Copyright (c) 2012 The Bitcoin developers
+// Distributed under the MIT/X11 software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+#ifndef CHECKQUEUE_H
+#define CHECKQUEUE_H
+
+#include <boost/thread/mutex.hpp>
+#include <boost/thread/locks.hpp>
+#include <boost/thread/condition_variable.hpp>
+
+#include <vector>
+#include <algorithm>
+
+template<typename T> class CCheckQueueControl;
+
+/** Queue for verifications that have to be performed.
+ * The verifications are represented by a type T, which must provide an
+ * operator(), returning a bool.
+ *
+ * One thread (the master) is assumed to push batches of verifications
+ * onto the queue, where they are processed by N-1 worker threads. When
+ * the master is done adding work, it temporarily joins the worker pool
+ * as an N'th worker, until all jobs are done.
+ */
+template<typename T> class CCheckQueue {
+private:
+ // Mutex to protect the inner state
+ boost::mutex mutex;
+
+ // Worker threads block on this when out of work
+ boost::condition_variable condWorker;
+
+ // Master thread blocks on this when out of work
+ boost::condition_variable condMaster;
+
+ // Quit method blocks on this until all workers are gone
+ boost::condition_variable condQuit;
+
+ // The queue of elements to be processed.
+ // As the order of booleans doesn't matter, it is used as a LIFO (stack)
+ std::vector<T> queue;
+
+ // The number of workers (including the master) that are idle.
+ int nIdle;
+
+ // The total number of workers (including the master).
+ int nTotal;
+
+ // The temporary evaluation result.
+ bool fAllOk;
+
+ // Number of verifications that haven't completed yet.
+ // This includes elements that are not anymore in queue, but still in
+ // worker's own batches.
+ unsigned int nTodo;
+
+ // Whether we're shutting down.
+ bool fQuit;
+
+ // The maximum number of elements to be processed in one batch
+ unsigned int nBatchSize;
+
+ // Internal function that does bulk of the verification work.
+ bool Loop(bool fMaster = false) {
+ boost::condition_variable &cond = fMaster ? condMaster : condWorker;
+ std::vector<T> vChecks;
+ vChecks.reserve(nBatchSize);
+ unsigned int nNow = 0;
+ bool fOk = true;
+ do {
+ {
+ boost::unique_lock<boost::mutex> lock(mutex);
+ // first do the clean-up of the previous loop run (allowing us to do it in the same critsect)
+ if (nNow) {
+ fAllOk &= fOk;
+ nTodo -= nNow;
+ if (nTodo == 0 && !fMaster)
+ // We processed the last element; inform the master he can exit and return the result
+ condMaster.notify_one();
+ } else {
+ // first iteration
+ nTotal++;
+ }
+ // logically, the do loop starts here
+ while (queue.empty()) {
+ if ((fMaster || fQuit) && nTodo == 0) {
+ nTotal--;
+ if (nTotal==0)
+ condQuit.notify_one();
+ bool fRet = fAllOk;
+ // reset the status for new work later
+ if (fMaster)
+ fAllOk = true;
+ // return the current status
+ return fRet;
+ }
+ nIdle++;
+ cond.wait(lock); // wait
+ nIdle--;
+ }
+ // Decide how many work units to process now.
+ // * Do not try to do everything at once, but aim for increasingly smaller batches so
+ // all workers finish approximately simultaneously.
+ // * Try to account for idle jobs which will instantly start helping.
+ // * Don't do batches smaller than 1 (duh), or larger than nBatchSize.
+ nNow = std::max(1U, std::min(nBatchSize, (unsigned int)queue.size() / (nTotal + nIdle + 1)));
+ vChecks.resize(nNow);
+ for (unsigned int i = 0; i < nNow; i++) {
+ // We want the lock on the mutex to be as short as possible, so swap jobs from the global
+ // queue to the local batch vector instead of copying.
+ vChecks[i].swap(queue.back());
+ queue.pop_back();
+ }
+ // Check whether we need to do work at all
+ fOk = fAllOk;
+ }
+ // execute work
+ BOOST_FOREACH(T &check, vChecks)
+ if (fOk)
+ fOk = check();
+ vChecks.clear();
+ } while(true);
+ }
+
+public:
+ // Create a new check queue
+ CCheckQueue(unsigned int nBatchSizeIn) :
+ nIdle(0), nTotal(0), fAllOk(true), nTodo(0), fQuit(false), nBatchSize(nBatchSizeIn) {}
+
+ // Worker thread
+ void Thread() {
+ Loop();
+ }
+
+ // Wait until execution finishes, and return whether all evaluations where succesful.
+ bool Wait() {
+ return Loop(true);
+ }
+
+ // Add a batch of checks to the queue
+ void Add(std::vector<T> &vChecks) {
+ boost::unique_lock<boost::mutex> lock(mutex);
+ BOOST_FOREACH(T &check, vChecks) {
+ queue.push_back(T());
+ check.swap(queue.back());
+ }
+ nTodo += vChecks.size();
+ if (vChecks.size() == 1)
+ condWorker.notify_one();
+ else if (vChecks.size() > 1)
+ condWorker.notify_all();
+ }
+
+ // Shut the queue down
+ void Quit() {
+ boost::unique_lock<boost::mutex> lock(mutex);
+ fQuit = true;
+ // No need to wake the master, as he will quit automatically when all jobs are
+ // done.
+ condWorker.notify_all();
+
+ while (nTotal > 0)
+ condQuit.wait(lock);
+ }
+
+ friend class CCheckQueueControl<T>;
+};
+
+/** RAII-style controller object for a CCheckQueue that guarantees the passed
+ * queue is finished before continuing.
+ */
+template<typename T> class CCheckQueueControl {
+private:
+ CCheckQueue<T> *pqueue;
+ bool fDone;
+
+public:
+ CCheckQueueControl(CCheckQueue<T> *pqueueIn) : pqueue(pqueueIn), fDone(false) {
+ // passed queue is supposed to be unused, or NULL
+ if (pqueue != NULL) {
+ assert(pqueue->nTotal == pqueue->nIdle);
+ assert(pqueue->nTodo == 0);
+ assert(pqueue->fAllOk == true);
+ }
+ }
+
+ bool Wait() {
+ if (pqueue == NULL)
+ return true;
+ bool fRet = pqueue->Wait();
+ fDone = true;
+ return fRet;
+ }
+
+ void Add(std::vector<T> &vChecks) {
+ if (pqueue != NULL)
+ pqueue->Add(vChecks);
+ }
+
+ ~CCheckQueueControl() {
+ if (!fDone)
+ Wait();
+ }
+};
+
+#endif
diff --git a/src/hash.cpp b/src/hash.cpp
new file mode 100644
index 0000000000..bddd8abf38
--- /dev/null
+++ b/src/hash.cpp
@@ -0,0 +1,58 @@
+#include "hash.h"
+
+inline uint32_t ROTL32 ( uint32_t x, int8_t r )
+{
+ return (x << r) | (x >> (32 - r));
+}
+
+unsigned int MurmurHash3(unsigned int nHashSeed, const std::vector<unsigned char>& vDataToHash)
+{
+ // The following is MurmurHash3 (x86_32), see http://code.google.com/p/smhasher/source/browse/trunk/MurmurHash3.cpp
+ uint32_t h1 = nHashSeed;
+ const uint32_t c1 = 0xcc9e2d51;
+ const uint32_t c2 = 0x1b873593;
+
+ const int nblocks = vDataToHash.size() / 4;
+
+ //----------
+ // body
+ const uint32_t * blocks = (const uint32_t *)(&vDataToHash[0] + nblocks*4);
+
+ for(int i = -nblocks; i; i++)
+ {
+ uint32_t k1 = blocks[i];
+
+ k1 *= c1;
+ k1 = ROTL32(k1,15);
+ k1 *= c2;
+
+ h1 ^= k1;
+ h1 = ROTL32(h1,13);
+ h1 = h1*5+0xe6546b64;
+ }
+
+ //----------
+ // tail
+ const uint8_t * tail = (const uint8_t*)(&vDataToHash[0] + nblocks*4);
+
+ uint32_t k1 = 0;
+
+ switch(vDataToHash.size() & 3)
+ {
+ case 3: k1 ^= tail[2] << 16;
+ case 2: k1 ^= tail[1] << 8;
+ case 1: k1 ^= tail[0];
+ k1 *= c1; k1 = ROTL32(k1,15); k1 *= c2; h1 ^= k1;
+ };
+
+ //----------
+ // finalization
+ h1 ^= vDataToHash.size();
+ h1 ^= h1 >> 16;
+ h1 *= 0x85ebca6b;
+ h1 ^= h1 >> 13;
+ h1 *= 0xc2b2ae35;
+ h1 ^= h1 >> 16;
+
+ return h1;
+}
diff --git a/src/hash.h b/src/hash.h
index bc013139bb..eaa1780c04 100644
--- a/src/hash.h
+++ b/src/hash.h
@@ -10,6 +10,7 @@
#include <openssl/sha.h>
#include <openssl/ripemd.h>
+#include <vector>
template<typename T1>
inline uint256 Hash(const T1 pbegin, const T1 pend)
@@ -113,4 +114,6 @@ inline uint160 Hash160(const std::vector<unsigned char>& vch)
return hash2;
}
+unsigned int MurmurHash3(unsigned int nHashSeed, const std::vector<unsigned char>& vDataToHash);
+
#endif
diff --git a/src/init.cpp b/src/init.cpp
index 87650bb9b5..1d49a7bedb 100644
--- a/src/init.cpp
+++ b/src/init.cpp
@@ -84,6 +84,10 @@ void Shutdown(void* parg)
fShutdown = true;
nTransactionsUpdated++;
bitdb.Flush(false);
+ {
+ LOCK(cs_main);
+ ThreadScriptCheckQuit();
+ }
StopNode();
{
LOCK(cs_main);
@@ -303,6 +307,7 @@ std::string HelpMessage()
" -checklevel=<n> " + _("How thorough the block verification is (0-4, default: 3)") + "\n" +
" -loadblock=<file> " + _("Imports blocks from external blk000??.dat file") + "\n" +
" -reindex " + _("Rebuild blockchain index from current blk000??.dat files") + "\n" +
+ " -par=N " + _("Set the number of script verification threads (1-16, 0=auto, default: 0)") + "\n" +
"\n" + _("Block creation options:") + "\n" +
" -blockminsize=<n> " + _("Set minimum block size in bytes (default: 0)") + "\n" +
@@ -484,6 +489,15 @@ bool AppInit2()
fDebug = GetBoolArg("-debug");
fBenchmark = GetBoolArg("-benchmark");
+ // -par=0 means autodetect, but nScriptCheckThreads==0 means no concurrency
+ nScriptCheckThreads = GetArg("-par", 0);
+ if (nScriptCheckThreads == 0)
+ nScriptCheckThreads = boost::thread::hardware_concurrency();
+ if (nScriptCheckThreads <= 1)
+ nScriptCheckThreads = 0;
+ else if (nScriptCheckThreads > MAX_SCRIPTCHECK_THREADS)
+ nScriptCheckThreads = MAX_SCRIPTCHECK_THREADS;
+
// -debug implies fDebug*
if (fDebug)
fDebugNet = true;
@@ -579,11 +593,17 @@ bool AppInit2()
if (fDaemon)
fprintf(stdout, "Bitcoin server starting\n");
+ if (nScriptCheckThreads) {
+ printf("Using %u threads for script verification\n", nScriptCheckThreads);
+ for (int i=0; i<nScriptCheckThreads-1; i++)
+ NewThread(ThreadScriptCheck, NULL);
+ }
+
int64 nStart;
- // ********************************************************* Step 5: verify database integrity
+ // ********************************************************* Step 5: verify wallet database integrity
- uiInterface.InitMessage(_("Verifying database integrity..."));
+ uiInterface.InitMessage(_("Verifying wallet integrity..."));
if (!bitdb.Open(GetDataDir()))
{
@@ -729,6 +749,33 @@ bool AppInit2()
return InitError(msg);
}
+ // Upgrading to 0.8; hard-link the old blknnnn.dat files into /blocks/
+ filesystem::path blocksDir = GetDataDir() / "blocks";
+ if (!filesystem::exists(blocksDir))
+ {
+ filesystem::create_directories(blocksDir);
+ bool linked = false;
+ for (unsigned int i = 1; i < 10000; i++) {
+ filesystem::path source = GetDataDir() / strprintf("blk%04u.dat", i);
+ if (!filesystem::exists(source)) break;
+ filesystem::path dest = blocksDir / strprintf("blk%05u.dat", i-1);
+ try {
+ filesystem::create_hard_link(source, dest);
+ printf("Hardlinked %s -> %s\n", source.string().c_str(), dest.string().c_str());
+ linked = true;
+ } catch (filesystem::filesystem_error & e) {
+ // Note: hardlink creation failing is not a disaster, it just means
+ // blocks will get re-downloaded from peers.
+ printf("Error hardlinking blk%04u.dat : %s\n", i, e.what());
+ break;
+ }
+ }
+ if (linked)
+ {
+ fReindex = true;
+ }
+ }
+
// cache size calculations
size_t nTotalCache = GetArg("-dbcache", 25) << 20;
if (nTotalCache < (1 << 22))
@@ -752,10 +799,12 @@ bool AppInit2()
pblocktree->WriteReindexing(true);
if (!LoadBlockIndex())
- return InitError(_("Error loading block/coin databases"));
+ return InitError(_("Error loading block database"));
+
+ uiInterface.InitMessage(_("Verifying block database integrity..."));
if (!VerifyDB())
- return InitError(_("Corrupted database detected. Please restart the client with -reindex."));
+ return InitError(_("Corrupted block database detected. Please restart the client with -reindex."));
// as LoadBlockIndex can take several minutes, it's possible the user
// requested to kill bitcoin-qt during the last operation. If so, exit.
diff --git a/src/main.cpp b/src/main.cpp
index 6be8dea4e2..a6394e0bff 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -10,6 +10,7 @@
#include "net.h"
#include "init.h"
#include "ui_interface.h"
+#include "checkqueue.h"
#include <boost/algorithm/string/replace.hpp>
#include <boost/filesystem.hpp>
#include <boost/filesystem/fstream.hpp>
@@ -40,6 +41,7 @@ uint256 hashBestChain = 0;
CBlockIndex* pindexBest = NULL;
set<CBlockIndex*, CBlockIndexWorkComparator> setBlockIndexValid; // may contain all CBlockIndex*'s that have validness >=BLOCK_VALID_TRANSACTIONS, and must contain those who aren't failed
int64 nTimeBestReceived = 0;
+int nScriptCheckThreads = 0;
bool fImporting = false;
bool fReindex = false;
bool fBenchmark = false;
@@ -467,28 +469,21 @@ CTransaction::GetLegacySigOpCount() const
int CMerkleTx::SetMerkleBranch(const CBlock* pblock)
{
- if (fClient)
- {
- if (hashBlock == 0)
- return 0;
- }
- else
- {
- CBlock blockTmp;
-
- if (pblock == NULL) {
- CCoins coins;
- if (pcoinsTip->GetCoins(GetHash(), coins)) {
- CBlockIndex *pindex = FindBlockByHeight(coins.nHeight);
- if (pindex) {
- if (!blockTmp.ReadFromDisk(pindex))
- return 0;
- pblock = &blockTmp;
- }
+ CBlock blockTmp;
+
+ if (pblock == NULL) {
+ CCoins coins;
+ if (pcoinsTip->GetCoins(GetHash(), coins)) {
+ CBlockIndex *pindex = FindBlockByHeight(coins.nHeight);
+ if (pindex) {
+ if (!blockTmp.ReadFromDisk(pindex))
+ return 0;
+ pblock = &blockTmp;
}
}
+ }
- if (pblock) {
+ if (pblock) {
// Update the tx's hashBlock
hashBlock = pblock->GetHash();
@@ -506,7 +501,6 @@ int CMerkleTx::SetMerkleBranch(const CBlock* pblock)
// Fill in merkle branch
vMerkleBranch = pblock->GetMerkleBranch(nIndex);
- }
}
// Is the tx in a block that's in the main chain
@@ -772,7 +766,7 @@ bool CTxMemPool::accept(CTransaction &tx, bool fCheckInputs,
// Check against previous transactions
// This is done last to help prevent CPU exhaustion denial-of-service attacks.
- if (!tx.CheckInputs(view, CS_ALWAYS, SCRIPT_VERIFY_P2SH))
+ if (!tx.CheckInputs(view, true, SCRIPT_VERIFY_P2SH))
{
return error("CTxMemPool::accept() : ConnectInputs failed %s", hash.ToString().substr(0,10).c_str());
}
@@ -915,16 +909,7 @@ int CMerkleTx::GetBlocksToMaturity() const
bool CMerkleTx::AcceptToMemoryPool(bool fCheckInputs)
{
- if (fClient)
- {
- if (!IsInMainChain() && !ClientCheckInputs())
- return false;
- return CTransaction::AcceptToMemoryPool(false);
- }
- else
- {
- return CTransaction::AcceptToMemoryPool(fCheckInputs);
- }
+ return CTransaction::AcceptToMemoryPool(fCheckInputs);
}
@@ -1348,10 +1333,25 @@ bool CTransaction::HaveInputs(CCoinsViewCache &inputs) const
return true;
}
-bool CTransaction::CheckInputs(CCoinsViewCache &inputs, enum CheckSig_mode csmode, unsigned int flags) const
+bool CScriptCheck::operator()() const {
+ const CScript &scriptSig = ptxTo->vin[nIn].scriptSig;
+ if (!VerifyScript(scriptSig, scriptPubKey, *ptxTo, nIn, nFlags, nHashType))
+ return error("CScriptCheck() : %s VerifySignature failed", ptxTo->GetHash().ToString().substr(0,10).c_str());
+ return true;
+}
+
+bool VerifySignature(const CCoins& txFrom, const CTransaction& txTo, unsigned int nIn, unsigned int flags, int nHashType)
+{
+ return CScriptCheck(txFrom, txTo, nIn, flags, nHashType)();
+}
+
+bool CTransaction::CheckInputs(CCoinsViewCache &inputs, bool fScriptChecks, unsigned int flags, std::vector<CScriptCheck> *pvChecks) const
{
if (!IsCoinBase())
{
+ if (pvChecks)
+ pvChecks->reserve(vin.size());
+
// This doesn't trigger the DoS code on purpose; if it did, it would make it easier
// for an attacker to attempt to split the network.
if (!HaveInputs(inputs))
@@ -1398,15 +1398,18 @@ bool CTransaction::CheckInputs(CCoinsViewCache &inputs, enum CheckSig_mode csmod
// Skip ECDSA signature verification when connecting blocks
// before the last block chain checkpoint. This is safe because block merkle hashes are
// still computed and checked, and any change will be caught at the next checkpoint.
- if (csmode == CS_ALWAYS ||
- (csmode == CS_AFTER_CHECKPOINT && inputs.GetBestBlock()->nHeight >= Checkpoints::GetTotalBlocksEstimate())) {
+ if (fScriptChecks) {
for (unsigned int i = 0; i < vin.size(); i++) {
const COutPoint &prevout = vin[i].prevout;
const CCoins &coins = inputs.GetCoins(prevout.hash);
// Verify signature
- if (!VerifySignature(coins, *this, i, flags, 0))
- return DoS(100,error("CheckInputs() : %s VerifySignature failed", GetHash().ToString().substr(0,10).c_str()));
+ CScriptCheck check(coins, *this, i, flags, 0);
+ if (pvChecks) {
+ pvChecks->push_back(CScriptCheck());
+ check.swap(pvChecks->back());
+ } else if (!check())
+ return DoS(100,false);
}
}
}
@@ -1567,6 +1570,19 @@ void static FlushBlockFile()
bool FindUndoPos(int nFile, CDiskBlockPos &pos, unsigned int nAddSize);
+static CCheckQueue<CScriptCheck> scriptcheckqueue(128);
+
+void ThreadScriptCheck(void*) {
+ vnThreadsRunning[THREAD_SCRIPTCHECK]++;
+ RenameThread("bitcoin-scriptch");
+ scriptcheckqueue.Thread();
+ vnThreadsRunning[THREAD_SCRIPTCHECK]--;
+}
+
+void ThreadScriptCheckQuit() {
+ scriptcheckqueue.Quit();
+}
+
bool CBlock::ConnectBlock(CBlockIndex* pindex, CCoinsViewCache &view, bool fJustCheck)
{
// Check it again in case a previous version let a bad block in
@@ -1576,6 +1592,8 @@ bool CBlock::ConnectBlock(CBlockIndex* pindex, CCoinsViewCache &view, bool fJust
// verify that the view's current state corresponds to the previous block
assert(pindex->pprev == view.GetBestBlock());
+ bool fScriptChecks = pindex->nHeight >= Checkpoints::GetTotalBlocksEstimate();
+
// Do not allow blocks that contain transactions which 'overwrite' older transactions,
// unless those are already completely spent.
// If such overwrites are allowed, coinbases and transactions depending upon those
@@ -1603,8 +1621,13 @@ bool CBlock::ConnectBlock(CBlockIndex* pindex, CCoinsViewCache &view, bool fJust
int64 nBIP16SwitchTime = 1333238400;
bool fStrictPayToScriptHash = (pindex->nTime >= nBIP16SwitchTime);
+ unsigned int flags = SCRIPT_VERIFY_NOCACHE |
+ (fStrictPayToScriptHash ? SCRIPT_VERIFY_P2SH : SCRIPT_VERIFY_NONE);
+
CBlockUndo blockundo;
+ CCheckQueueControl<CScriptCheck> control(fScriptChecks && nScriptCheckThreads ? &scriptcheckqueue : NULL);
+
int64 nStart = GetTimeMicros();
int64 nFees = 0;
int nInputs = 0;
@@ -1636,8 +1659,10 @@ bool CBlock::ConnectBlock(CBlockIndex* pindex, CCoinsViewCache &view, bool fJust
nFees += tx.GetValueIn(view)-tx.GetValueOut();
- if (!tx.CheckInputs(view, CS_AFTER_CHECKPOINT, fStrictPayToScriptHash ? SCRIPT_VERIFY_P2SH : SCRIPT_VERIFY_NONE))
+ std::vector<CScriptCheck> vChecks;
+ if (!tx.CheckInputs(view, fScriptChecks, flags, nScriptCheckThreads ? &vChecks : NULL))
return false;
+ control.Add(vChecks);
}
CTxUndo txundo;
@@ -1654,6 +1679,12 @@ bool CBlock::ConnectBlock(CBlockIndex* pindex, CCoinsViewCache &view, bool fJust
if (vtx[0].GetValueOut() > GetBlockValue(pindex->nHeight, nFees))
return error("ConnectBlock() : coinbase pays too much (actual=%"PRI64d" vs limit=%"PRI64d")", vtx[0].GetValueOut(), GetBlockValue(pindex->nHeight, nFees));
+ if (!control.Wait())
+ return DoS(100, false);
+ int64 nTime2 = GetTimeMicros() - nStart;
+ if (fBenchmark)
+ printf("- Verify %u txins: %.2fms (%.3fms/txin)\n", nInputs - 1, 0.001 * nTime2, nInputs <= 1 ? 0 : 0.001 * nTime2 / (nInputs-1));
+
if (fJustCheck)
return true;
@@ -2256,6 +2287,160 @@ bool ProcessBlock(CNode* pfrom, CBlock* pblock, CDiskBlockPos *dbp)
+CMerkleBlock::CMerkleBlock(const CBlock& block, CBloomFilter& filter)
+{
+ header = block.GetBlockHeader();
+
+ vector<bool> vMatch;
+ vector<uint256> vHashes;
+
+ vMatch.reserve(block.vtx.size());
+ vHashes.reserve(block.vtx.size());
+
+ for (unsigned int i = 0; i < block.vtx.size(); i++)
+ {
+ uint256 hash = block.vtx[i].GetHash();
+ if (filter.IsRelevantAndUpdate(block.vtx[i], hash))
+ {
+ vMatch.push_back(true);
+ vMatchedTxn.push_back(make_pair(i, hash));
+ }
+ else
+ vMatch.push_back(false);
+ vHashes.push_back(hash);
+ }
+
+ txn = CPartialMerkleTree(vHashes, vMatch);
+}
+
+
+
+
+
+
+
+
+uint256 CPartialMerkleTree::CalcHash(int height, unsigned int pos, const std::vector<uint256> &vTxid) {
+ if (height == 0) {
+ // hash at height 0 is the txids themself
+ return vTxid[pos];
+ } else {
+ // calculate left hash
+ uint256 left = CalcHash(height-1, pos*2, vTxid), right;
+ // calculate right hash if not beyong the end of the array - copy left hash otherwise1
+ if (pos*2+1 < CalcTreeWidth(height-1))
+ right = CalcHash(height-1, pos*2+1, vTxid);
+ else
+ right = left;
+ // combine subhashes
+ return Hash(BEGIN(left), END(left), BEGIN(right), END(right));
+ }
+}
+
+void CPartialMerkleTree::TraverseAndBuild(int height, unsigned int pos, const std::vector<uint256> &vTxid, const std::vector<bool> &vMatch) {
+ // determine whether this node is the parent of at least one matched txid
+ bool fParentOfMatch = false;
+ for (unsigned int p = pos << height; p < (pos+1) << height && p < nTransactions; p++)
+ fParentOfMatch |= vMatch[p];
+ // store as flag bit
+ vBits.push_back(fParentOfMatch);
+ if (height==0 || !fParentOfMatch) {
+ // if at height 0, or nothing interesting below, store hash and stop
+ vHash.push_back(CalcHash(height, pos, vTxid));
+ } else {
+ // otherwise, don't store any hash, but descend into the subtrees
+ TraverseAndBuild(height-1, pos*2, vTxid, vMatch);
+ if (pos*2+1 < CalcTreeWidth(height-1))
+ TraverseAndBuild(height-1, pos*2+1, vTxid, vMatch);
+ }
+}
+
+uint256 CPartialMerkleTree::TraverseAndExtract(int height, unsigned int pos, unsigned int &nBitsUsed, unsigned int &nHashUsed, std::vector<uint256> &vMatch) {
+ if (nBitsUsed >= vBits.size()) {
+ // overflowed the bits array - failure
+ fBad = true;
+ return 0;
+ }
+ bool fParentOfMatch = vBits[nBitsUsed++];
+ if (height==0 || !fParentOfMatch) {
+ // if at height 0, or nothing interesting below, use stored hash and do not descend
+ if (nHashUsed >= vHash.size()) {
+ // overflowed the hash array - failure
+ fBad = true;
+ return 0;
+ }
+ const uint256 &hash = vHash[nHashUsed++];
+ if (height==0 && fParentOfMatch) // in case of height 0, we have a matched txid
+ vMatch.push_back(hash);
+ return hash;
+ } else {
+ // otherwise, descend into the subtrees to extract matched txids and hashes
+ uint256 left = TraverseAndExtract(height-1, pos*2, nBitsUsed, nHashUsed, vMatch), right;
+ if (pos*2+1 < CalcTreeWidth(height-1))
+ right = TraverseAndExtract(height-1, pos*2+1, nBitsUsed, nHashUsed, vMatch);
+ else
+ right = left;
+ // and combine them before returning
+ return Hash(BEGIN(left), END(left), BEGIN(right), END(right));
+ }
+}
+
+CPartialMerkleTree::CPartialMerkleTree(const std::vector<uint256> &vTxid, const std::vector<bool> &vMatch) : nTransactions(vTxid.size()), fBad(false) {
+ // reset state
+ vBits.clear();
+ vHash.clear();
+
+ // calculate height of tree
+ int nHeight = 0;
+ while (CalcTreeWidth(nHeight) > 1)
+ nHeight++;
+
+ // traverse the partial tree
+ TraverseAndBuild(nHeight, 0, vTxid, vMatch);
+}
+
+CPartialMerkleTree::CPartialMerkleTree() : nTransactions(0), fBad(true) {}
+
+uint256 CPartialMerkleTree::ExtractMatches(std::vector<uint256> &vMatch) {
+ vMatch.clear();
+ // An empty set will not work
+ if (nTransactions == 0)
+ return 0;
+ // check for excessively high numbers of transactions
+ if (nTransactions > MAX_BLOCK_SIZE / 60) // 60 is the lower bound for the size of a serialized CTransaction
+ return 0;
+ // there can never be more hashes provided than one for every txid
+ if (vHash.size() > nTransactions)
+ return 0;
+ // there must be at least one bit per node in the partial tree, and at least one node per hash
+ if (vBits.size() < vHash.size())
+ return 0;
+ // calculate height of tree
+ int nHeight = 0;
+ while (CalcTreeWidth(nHeight) > 1)
+ nHeight++;
+ // traverse the partial tree
+ unsigned int nBitsUsed = 0, nHashUsed = 0;
+ uint256 hashMerkleRoot = TraverseAndExtract(nHeight, 0, nBitsUsed, nHashUsed, vMatch);
+ // verify that no problems occured during the tree traversal
+ if (fBad)
+ return 0;
+ // verify that all bits were consumed (except for the padding caused by serializing it as a byte sequence)
+ if ((nBitsUsed+7)/8 != (vBits.size()+7)/8)
+ return 0;
+ // verify that all hashes were consumed
+ if (nHashUsed != vHash.size())
+ return 0;
+ return hashMerkleRoot;
+}
+
+
+
+
+
+
+
+
bool CheckDiskSpace(uint64 nAdditionalBytes)
{
uint64 nFreeBytesAvailable = filesystem::space(GetDataDir()).available;
@@ -2832,6 +3017,10 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv)
vRecv >> pfrom->strSubVer;
if (!vRecv.empty())
vRecv >> pfrom->nStartingHeight;
+ if (!vRecv.empty())
+ vRecv >> pfrom->fRelayTxes; // set to true after we get the first filter* message
+ else
+ pfrom->fRelayTxes = true;
if (pfrom->fInbound && addrMe.IsRoutable())
{
@@ -3062,7 +3251,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv)
if (fDebugNet || (vInv.size() == 1))
printf("received getdata for: %s\n", inv.ToString().c_str());
- if (inv.type == MSG_BLOCK)
+ if (inv.type == MSG_BLOCK || inv.type == MSG_FILTERED_BLOCK)
{
// Send block from disk
map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(inv.hash);
@@ -3070,7 +3259,29 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv)
{
CBlock block;
block.ReadFromDisk((*mi).second);
- pfrom->PushMessage("block", block);
+ if (inv.type == MSG_BLOCK)
+ pfrom->PushMessage("block", block);
+ else // MSG_FILTERED_BLOCK)
+ {
+ LOCK(pfrom->cs_filter);
+ if (pfrom->pfilter)
+ {
+ CMerkleBlock merkleBlock(block, *pfrom->pfilter);
+ // CMerkleBlock just contains hashes, so also push any transactions in the block the client did not see
+ // This avoids hurting performance by pointlessly requiring a round-trip
+ // Note that there is currently no way for a node to request any single transactions we didnt send here -
+ // they must either disconnect and retry or request the full block.
+ // Thus, the protocol spec specified allows for us to provide duplicate txn here,
+ // however we MUST always provide at least what the remote peer needs
+ typedef std::pair<unsigned int, uint256> PairType;
+ BOOST_FOREACH(PairType& pair, merkleBlock.vMatchedTxn)
+ if (!pfrom->setInventoryKnown.count(CInv(MSG_TX, pair.second)))
+ pfrom->PushMessage("tx", block.vtx[pair.first]);
+ pfrom->PushMessage("merkleblock", merkleBlock);
+ }
+ // else
+ // no response
+ }
// Trigger them to send a getblocks request for the next batch of inventory
if (inv.hash == pfrom->hashContinue)
@@ -3201,7 +3412,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv)
if (tx.AcceptToMemoryPool(true, &fMissingInputs))
{
SyncWithWallets(inv.hash, tx, NULL, true);
- RelayMessage(inv, vMsg);
+ RelayTransaction(tx, inv.hash, vMsg);
mapAlreadyAskedFor.erase(inv);
vWorkQueue.push_back(inv.hash);
vEraseQueue.push_back(inv.hash);
@@ -3224,7 +3435,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv)
{
printf(" accepted orphan tx %s\n", inv.hash.ToString().substr(0,10).c_str());
SyncWithWallets(inv.hash, tx, NULL, true);
- RelayMessage(inv, vMsg);
+ RelayTransaction(tx, inv.hash, vMsg);
mapAlreadyAskedFor.erase(inv);
vWorkQueue.push_back(inv.hash);
vEraseQueue.push_back(inv.hash);
@@ -3283,13 +3494,16 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv)
else if (strCommand == "mempool")
{
std::vector<uint256> vtxid;
+ LOCK2(mempool.cs, pfrom->cs_filter);
mempool.queryHashes(vtxid);
vector<CInv> vInv;
- for (unsigned int i = 0; i < vtxid.size(); i++) {
- CInv inv(MSG_TX, vtxid[i]);
- vInv.push_back(inv);
- if (i == (MAX_INV_SZ - 1))
- break;
+ BOOST_FOREACH(uint256& hash, vtxid) {
+ CInv inv(MSG_TX, hash);
+ if ((pfrom->pfilter && pfrom->pfilter->IsRelevantAndUpdate(mempool.lookup(hash), hash)) ||
+ (!pfrom->pfilter))
+ vInv.push_back(inv);
+ if (vInv.size() == MAX_INV_SZ)
+ break;
}
if (vInv.size() > 0)
pfrom->PushMessage("inv", vInv);
@@ -3349,6 +3563,53 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv)
}
+ else if (strCommand == "filterload")
+ {
+ CBloomFilter filter;
+ vRecv >> filter;
+
+ if (!filter.IsWithinSizeConstraints())
+ // There is no excuse for sending a too-large filter
+ pfrom->Misbehaving(100);
+ else
+ {
+ LOCK(pfrom->cs_filter);
+ delete pfrom->pfilter;
+ pfrom->pfilter = new CBloomFilter(filter);
+ }
+ pfrom->fRelayTxes = true;
+ }
+
+
+ else if (strCommand == "filteradd")
+ {
+ vector<unsigned char> vData;
+ vRecv >> vData;
+
+ // Nodes must NEVER send a data item > 520 bytes (the max size for a script data object,
+ // and thus, the maximum size any matched object can have) in a filteradd message
+ if (vData.size() > 520)
+ {
+ pfrom->Misbehaving(100);
+ } else {
+ LOCK(pfrom->cs_filter);
+ if (pfrom->pfilter)
+ pfrom->pfilter->insert(vData);
+ else
+ pfrom->Misbehaving(100);
+ }
+ }
+
+
+ else if (strCommand == "filterclear")
+ {
+ LOCK(pfrom->cs_filter);
+ delete pfrom->pfilter;
+ pfrom->pfilter = NULL;
+ pfrom->fRelayTxes = true;
+ }
+
+
else
{
// Ignore unknown commands for extensibility
@@ -3967,7 +4228,7 @@ CBlockTemplate* CreateNewBlock(CReserveKey& reservekey)
if (nBlockSigOps + nTxSigOps >= MAX_BLOCK_SIGOPS)
continue;
- if (!tx.CheckInputs(viewTemp, CS_ALWAYS, SCRIPT_VERIFY_P2SH))
+ if (!tx.CheckInputs(viewTemp, true, SCRIPT_VERIFY_P2SH))
continue;
CTxUndo txundo;
diff --git a/src/main.h b/src/main.h
index 3290b16318..2d3a9f5bd0 100644
--- a/src/main.h
+++ b/src/main.h
@@ -53,6 +53,8 @@ inline bool MoneyRange(int64 nValue) { return (nValue >= 0 && nValue <= MAX_MONE
static const int COINBASE_MATURITY = 100;
/** Threshold for nLockTime: below this value it is interpreted as block number, otherwise as UNIX timestamp. */
static const unsigned int LOCKTIME_THRESHOLD = 500000000; // Tue Nov 5 00:53:20 1985 UTC
+/** Maximum number of script-checking threads allowed */
+static const int MAX_SCRIPTCHECK_THREADS = 16;
#ifdef USE_UPNP
static const int fHaveUPnP = true;
#else
@@ -90,6 +92,7 @@ extern unsigned char pchMessageStart[4];
extern bool fImporting;
extern bool fReindex;
extern bool fBenchmark;
+extern int nScriptCheckThreads;
extern unsigned int nCoinCacheSize;
// Settings
@@ -107,6 +110,7 @@ class CCoins;
class CTxUndo;
class CCoinsView;
class CCoinsViewCache;
+class CScriptCheck;
struct CBlockTemplate;
@@ -140,6 +144,10 @@ bool ProcessMessages(CNode* pfrom);
bool SendMessages(CNode* pto, bool fSendTrickle);
/** Run the importer thread, which deals with reindexing, loading bootstrap.dat, and whatever is passed to -loadblock */
void ThreadImport(void *parg);
+/** Run an instance of the script checking thread */
+void ThreadScriptCheck(void* parg);
+/** Stop the script checking threads */
+void ThreadScriptCheckQuit();
/** Run the miner threads */
void GenerateBitcoins(bool fGenerate, CWallet* pwallet);
/** Generate a new block, without valid proof-of-work */
@@ -168,6 +176,8 @@ bool SetBestChain(CBlockIndex* pindexNew);
bool ConnectBestBlock();
/** Create a new block index entry for a given block hash */
CBlockIndex * InsertBlockIndex(uint256 hash);
+/** Verify a signature */
+bool VerifySignature(const CCoins& txFrom, const CTransaction& txTo, unsigned int nIn, unsigned int flags, int nHashType);
@@ -432,14 +442,6 @@ enum GetMinFee_mode
GMF_SEND,
};
-// Modes for script/signature checking
-enum CheckSig_mode
-{
- CS_NEVER, // never validate scripts
- CS_AFTER_CHECKPOINT, // validate scripts after the last checkpoint
- CS_ALWAYS // always validate scripts
-};
-
/** The basic transaction that is broadcasted on the network and contained in
* blocks. A transaction can contain multiple inputs and outputs.
*/
@@ -639,8 +641,11 @@ public:
bool HaveInputs(CCoinsViewCache &view) const;
// Check whether all inputs of this transaction are valid (no double spends, scripts & sigs, amounts)
- // This does not modify the UTXO set
- bool CheckInputs(CCoinsViewCache &view, enum CheckSig_mode csmode, unsigned int flags = SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_STRICTENC) const;
+ // This does not modify the UTXO set. If pvChecks is not NULL, script checks are pushed onto it
+ // instead of being performed inline.
+ bool CheckInputs(CCoinsViewCache &view, bool fScriptChecks = true,
+ unsigned int flags = SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_STRICTENC,
+ std::vector<CScriptCheck> *pvChecks = NULL) const;
// Apply the effects of this transaction on the UTXO set represented by view
bool UpdateCoins(CCoinsViewCache &view, CTxUndo &txundo, int nHeight, const uint256 &txhash) const;
@@ -1056,7 +1061,33 @@ public:
}
};
+/** Closure representing one script verification
+ * Note that this stores references to the spending transaction */
+class CScriptCheck
+{
+private:
+ CScript scriptPubKey;
+ const CTransaction *ptxTo;
+ unsigned int nIn;
+ unsigned int nFlags;
+ int nHashType;
+
+public:
+ CScriptCheck() {}
+ CScriptCheck(const CCoins& txFromIn, const CTransaction& txToIn, unsigned int nInIn, unsigned int nFlagsIn, int nHashTypeIn) :
+ scriptPubKey(txFromIn.vout[txToIn.vin[nInIn].prevout.n].scriptPubKey),
+ ptxTo(&txToIn), nIn(nInIn), nFlags(nFlagsIn), nHashType(nHashTypeIn) { }
+
+ bool operator()() const;
+ void swap(CScriptCheck &check) {
+ scriptPubKey.swap(check.scriptPubKey);
+ std::swap(ptxTo, check.ptxTo);
+ std::swap(nIn, check.nIn);
+ std::swap(nFlags, check.nFlags);
+ std::swap(nHashType, check.nHashType);
+ }
+};
/** A transaction with a merkle branch linking it to the block chain. */
class CMerkleTx : public CTransaction
@@ -1110,11 +1141,101 @@ public:
+/** Data structure that represents a partial merkle tree.
+ *
+ * It respresents a subset of the txid's of a known block, in a way that
+ * allows recovery of the list of txid's and the merkle root, in an
+ * authenticated way.
+ *
+ * The encoding works as follows: we traverse the tree in depth-first order,
+ * storing a bit for each traversed node, signifying whether the node is the
+ * parent of at least one matched leaf txid (or a matched txid itself). In
+ * case we are at the leaf level, or this bit is 0, its merkle node hash is
+ * stored, and its children are not explorer further. Otherwise, no hash is
+ * stored, but we recurse into both (or the only) child branch. During
+ * decoding, the same depth-first traversal is performed, consuming bits and
+ * hashes as they written during encoding.
+ *
+ * The serialization is fixed and provides a hard guarantee about the
+ * encoded size:
+ *
+ * SIZE <= 10 + ceil(32.25*N)
+ *
+ * Where N represents the number of leaf nodes of the partial tree. N itself
+ * is bounded by:
+ *
+ * N <= total_transactions
+ * N <= 1 + matched_transactions*tree_height
+ *
+ * The serialization format:
+ * - uint32 total_transactions (4 bytes)
+ * - varint number of hashes (1-3 bytes)
+ * - uint256[] hashes in depth-first order (<= 32*N bytes)
+ * - varint number of bytes of flag bits (1-3 bytes)
+ * - byte[] flag bits, packed per 8 in a byte, least significant bit first (<= 2*N-1 bits)
+ * The size constraints follow from this.
+ */
+class CPartialMerkleTree
+{
+protected:
+ // the total number of transactions in the block
+ unsigned int nTransactions;
+
+ // node-is-parent-of-matched-txid bits
+ std::vector<bool> vBits;
+
+ // txids and internal hashes
+ std::vector<uint256> vHash;
+
+ // flag set when encountering invalid data
+ bool fBad;
+
+ // helper function to efficiently calculate the number of nodes at given height in the merkle tree
+ unsigned int CalcTreeWidth(int height) {
+ return (nTransactions+(1 << height)-1) >> height;
+ }
+
+ // calculate the hash of a node in the merkle tree (at leaf level: the txid's themself)
+ uint256 CalcHash(int height, unsigned int pos, const std::vector<uint256> &vTxid);
+ // recursive function that traverses tree nodes, storing the data as bits and hashes
+ void TraverseAndBuild(int height, unsigned int pos, const std::vector<uint256> &vTxid, const std::vector<bool> &vMatch);
+ // recursive function that traverses tree nodes, consuming the bits and hashes produced by TraverseAndBuild.
+ // it returns the hash of the respective node.
+ uint256 TraverseAndExtract(int height, unsigned int pos, unsigned int &nBitsUsed, unsigned int &nHashUsed, std::vector<uint256> &vMatch);
+public:
+ // serialization implementation
+ IMPLEMENT_SERIALIZE(
+ READWRITE(nTransactions);
+ READWRITE(vHash);
+ std::vector<unsigned char> vBytes;
+ if (fRead) {
+ READWRITE(vBytes);
+ CPartialMerkleTree &us = *(const_cast<CPartialMerkleTree*>(this));
+ us.vBits.resize(vBytes.size() * 8);
+ for (unsigned int p = 0; p < us.vBits.size(); p++)
+ us.vBits[p] = (vBytes[p / 8] & (1 << (p % 8))) != 0;
+ us.fBad = false;
+ } else {
+ vBytes.resize((vBits.size()+7)/8);
+ for (unsigned int p = 0; p < vBits.size(); p++)
+ vBytes[p / 8] |= vBits[p] << (p % 8);
+ READWRITE(vBytes);
+ }
+ )
+ // Construct a partial merkle tree from a list of transaction id's, and a mask that selects a subset of them
+ CPartialMerkleTree(const std::vector<uint256> &vTxid, const std::vector<bool> &vMatch);
+
+ CPartialMerkleTree();
+
+ // extract the matching txid's represented by this partial merkle tree.
+ // returns the merkle root, or 0 in case of failure
+ uint256 ExtractMatches(std::vector<uint256> &vMatch);
+};
/** Nodes collect new transactions into a block, hash them into a hash tree,
@@ -1218,6 +1339,18 @@ public:
nDoS = 0;
}
+ CBlockHeader GetBlockHeader() const
+ {
+ CBlockHeader block;
+ block.nVersion = nVersion;
+ block.hashPrevBlock = hashPrevBlock;
+ block.hashMerkleRoot = hashMerkleRoot;
+ block.nTime = nTime;
+ block.nBits = nBits;
+ block.nNonce = nNonce;
+ return block;
+ }
+
uint256 BuildMerkleTree() const
{
vMerkleTree.clear();
@@ -2027,4 +2160,36 @@ struct CBlockTemplate
std::vector<int64_t> vTxSigOps;
};
+
+
+
+
+
+/** Used to relay blocks as header + vector<merkle branch>
+ * to filtered nodes.
+ */
+class CMerkleBlock
+{
+public:
+ // Public only for unit testing
+ CBlockHeader header;
+ CPartialMerkleTree txn;
+
+public:
+ // Public only for unit testing and relay testing
+ // (not relayed)
+ std::vector<std::pair<unsigned int, uint256> > vMatchedTxn;
+
+ // Create from a CBlock, filtering transactions according to filter
+ // Note that this will call IsRelevantAndUpdate on the filter for each transaction,
+ // thus the filter will likely be modified.
+ CMerkleBlock(const CBlock& block, CBloomFilter& filter);
+
+ IMPLEMENT_SERIALIZE
+ (
+ READWRITE(header);
+ READWRITE(txn);
+ )
+};
+
#endif
diff --git a/src/makefile.linux-mingw b/src/makefile.linux-mingw
index 47dc7c5c40..ff565f2a2d 100644
--- a/src/makefile.linux-mingw
+++ b/src/makefile.linux-mingw
@@ -83,6 +83,8 @@ OBJS= \
obj/wallet.o \
obj/walletdb.o \
obj/noui.o \
+ obj/hash.o \
+ obj/bloom.o \
obj/leveldb.o \
obj/txdb.o
diff --git a/src/makefile.mingw b/src/makefile.mingw
index 22d65d6703..9a6680bf40 100644
--- a/src/makefile.mingw
+++ b/src/makefile.mingw
@@ -78,6 +78,8 @@ OBJS= \
obj/util.o \
obj/wallet.o \
obj/walletdb.o \
+ obj/hash.o \
+ obj/bloom.o \
obj/noui.o \
obj/leveldb.o \
obj/txdb.o
diff --git a/src/makefile.osx b/src/makefile.osx
index 25164c8679..8b7c559fa1 100644
--- a/src/makefile.osx
+++ b/src/makefile.osx
@@ -96,6 +96,8 @@ OBJS= \
obj/util.o \
obj/wallet.o \
obj/walletdb.o \
+ obj/hash.o \
+ obj/bloom.o \
obj/noui.o \
obj/leveldb.o \
obj/txdb.o
diff --git a/src/makefile.unix b/src/makefile.unix
index 9e17e8ace2..14cf1b8fa5 100644
--- a/src/makefile.unix
+++ b/src/makefile.unix
@@ -127,6 +127,8 @@ OBJS= \
obj/util.o \
obj/wallet.o \
obj/walletdb.o \
+ obj/hash.o \
+ obj/bloom.o \
obj/noui.o \
obj/leveldb.o \
obj/txdb.o
diff --git a/src/net.cpp b/src/net.cpp
index b54f8c15f7..319739429c 100644
--- a/src/net.cpp
+++ b/src/net.cpp
@@ -9,6 +9,7 @@
#include "init.h"
#include "addrman.h"
#include "ui_interface.h"
+#include "script.h"
#ifdef WIN32
#include <string.h>
@@ -45,10 +46,9 @@ struct LocalServiceInfo {
//
// Global state variables
//
-bool fClient = false;
bool fDiscover = true;
bool fUseUPnP = false;
-uint64 nLocalServices = (fClient ? 0 : NODE_NETWORK);
+uint64 nLocalServices = NODE_NETWORK;
static CCriticalSection cs_mapLocalHost;
static map<CNetAddr, LocalServiceInfo> mapLocalHost;
static bool vfReachable[NET_MAX] = {};
@@ -1997,3 +1997,48 @@ public:
}
}
instance_of_cnetcleanup;
+
+
+
+
+
+
+
+void RelayTransaction(const CTransaction& tx, const uint256& hash)
+{
+ CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
+ ss.reserve(10000);
+ ss << tx;
+ RelayTransaction(tx, hash, ss);
+}
+
+void RelayTransaction(const CTransaction& tx, const uint256& hash, const CDataStream& ss)
+{
+ CInv inv(MSG_TX, hash);
+ {
+ LOCK(cs_mapRelay);
+ // Expire old relay messages
+ while (!vRelayExpiration.empty() && vRelayExpiration.front().first < GetTime())
+ {
+ mapRelay.erase(vRelayExpiration.front().second);
+ vRelayExpiration.pop_front();
+ }
+
+ // Save original serialized message so newer versions are preserved
+ mapRelay.insert(std::make_pair(inv, ss));
+ vRelayExpiration.push_back(std::make_pair(GetTime() + 15 * 60, inv));
+ }
+ LOCK(cs_vNodes);
+ BOOST_FOREACH(CNode* pnode, vNodes)
+ {
+ if(!pnode->fRelayTxes)
+ continue;
+ LOCK(pnode->cs_filter);
+ if (pnode->pfilter)
+ {
+ if (pnode->pfilter->IsRelevantAndUpdate(tx, hash))
+ pnode->PushInventory(inv);
+ } else
+ pnode->PushInventory(inv);
+ }
+}
diff --git a/src/net.h b/src/net.h
index e37953772e..99f41161b1 100644
--- a/src/net.h
+++ b/src/net.h
@@ -19,6 +19,7 @@
#include "protocol.h"
#include "addrman.h"
#include "hash.h"
+#include "bloom.h"
class CNode;
class CBlockIndex;
@@ -82,11 +83,11 @@ enum threadId
THREAD_DUMPADDRESS,
THREAD_RPCHANDLER,
THREAD_IMPORT,
+ THREAD_SCRIPTCHECK,
THREAD_MAX
};
-extern bool fClient;
extern bool fDiscover;
extern bool fUseUPnP;
extern uint64 nLocalServices;
@@ -152,7 +153,14 @@ public:
bool fNetworkNode;
bool fSuccessfullyConnected;
bool fDisconnect;
+ // We use fRelayTxes for two purposes -
+ // a) it allows us to not relay tx invs before receiving the peer's version message
+ // b) the peer may tell us in their version message that we should not relay tx invs
+ // until they have initialized their bloom filter.
+ bool fRelayTxes;
CSemaphoreGrant grantOutbound;
+ CCriticalSection cs_filter;
+ CBloomFilter* pfilter;
protected:
int nRefCount;
@@ -209,7 +217,9 @@ public:
nStartingHeight = -1;
fGetAddr = false;
nMisbehavior = 0;
+ fRelayTxes = false;
setInventoryKnown.max_size(SendBufferSize() / 1000);
+ pfilter = NULL;
// Be shy and don't send version until we hear
if (!fInbound)
@@ -223,6 +233,8 @@ public:
closesocket(hSocket);
hSocket = INVALID_SOCKET;
}
+ if (pfilter)
+ delete pfilter;
}
private:
@@ -557,51 +569,8 @@ public:
-
-
-
-
-
-
-
-inline void RelayInventory(const CInv& inv)
-{
- // Put on lists to offer to the other nodes
- {
- LOCK(cs_vNodes);
- BOOST_FOREACH(CNode* pnode, vNodes)
- pnode->PushInventory(inv);
- }
-}
-
-template<typename T>
-void RelayMessage(const CInv& inv, const T& a)
-{
- CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
- ss.reserve(10000);
- ss << a;
- RelayMessage(inv, ss);
-}
-
-template<>
-inline void RelayMessage<>(const CInv& inv, const CDataStream& ss)
-{
- {
- LOCK(cs_mapRelay);
- // Expire old relay messages
- while (!vRelayExpiration.empty() && vRelayExpiration.front().first < GetTime())
- {
- mapRelay.erase(vRelayExpiration.front().second);
- vRelayExpiration.pop_front();
- }
-
- // Save original serialized message so newer versions are preserved
- mapRelay.insert(std::make_pair(inv, ss));
- vRelayExpiration.push_back(std::make_pair(GetTime() + 15 * 60, inv));
- }
-
- RelayInventory(inv);
-}
-
+class CTransaction;
+void RelayTransaction(const CTransaction& tx, const uint256& hash);
+void RelayTransaction(const CTransaction& tx, const uint256& hash, const CDataStream& ss);
#endif
diff --git a/src/protocol.cpp b/src/protocol.cpp
index 23969e5b97..7b42f5270b 100644
--- a/src/protocol.cpp
+++ b/src/protocol.cpp
@@ -17,6 +17,7 @@ static const char* ppszTypeName[] =
"ERROR",
"tx",
"block",
+ "filtered block"
};
CMessageHeader::CMessageHeader()
diff --git a/src/protocol.h b/src/protocol.h
index 96fd197ecd..f5c162054e 100644
--- a/src/protocol.h
+++ b/src/protocol.h
@@ -138,6 +138,9 @@ enum
{
MSG_TX = 1,
MSG_BLOCK,
+ // Nodes may always request a MSG_FILTERED_BLOCK in a getdata, however,
+ // MSG_FILTERED_BLOCK should not appear in any invs except as a part of getdata.
+ MSG_FILTERED_BLOCK,
};
#endif // __INCLUDED_PROTOCOL_H__
diff --git a/src/qt/bitcoinstrings.cpp b/src/qt/bitcoinstrings.cpp
index 7fcb7acf0e..c41ee89299 100644
--- a/src/qt/bitcoinstrings.cpp
+++ b/src/qt/bitcoinstrings.cpp
@@ -31,6 +31,8 @@ QT_TRANSLATE_NOOP("bitcoin-core", ""
"Cannot obtain a lock on data directory %s. Bitcoin is probably already "
"running."),
QT_TRANSLATE_NOOP("bitcoin-core", ""
+"Corrupted block database detected. Please restart the client with -reindex."),
+QT_TRANSLATE_NOOP("bitcoin-core", ""
"Error initializing database environment %s! To recover, BACKUP THAT "
"DIRECTORY, then remove everything from it except for wallet.dat."),
QT_TRANSLATE_NOOP("bitcoin-core", ""
@@ -96,7 +98,7 @@ QT_TRANSLATE_NOOP("bitcoin-core", "Connect to a node to retrieve peer addresses,
QT_TRANSLATE_NOOP("bitcoin-core", "Discover own IP address (default: 1 when listening and no -externalip)"),
QT_TRANSLATE_NOOP("bitcoin-core", "Don't generate coins"),
QT_TRANSLATE_NOOP("bitcoin-core", "Done loading"),
-QT_TRANSLATE_NOOP("bitcoin-core", "Error loading blkindex.dat"),
+QT_TRANSLATE_NOOP("bitcoin-core", "Error loading block database"),
QT_TRANSLATE_NOOP("bitcoin-core", "Error loading wallet.dat"),
QT_TRANSLATE_NOOP("bitcoin-core", "Error loading wallet.dat: Wallet corrupted"),
QT_TRANSLATE_NOOP("bitcoin-core", "Error loading wallet.dat: Wallet requires newer version of Bitcoin"),
@@ -112,7 +114,7 @@ QT_TRANSLATE_NOOP("bitcoin-core", "Find peers using internet relay chat (default
QT_TRANSLATE_NOOP("bitcoin-core", "Generate coins"),
QT_TRANSLATE_NOOP("bitcoin-core", "Get help for a command"),
QT_TRANSLATE_NOOP("bitcoin-core", "How many blocks to check at startup (default: 2500, 0 = all)"),
-QT_TRANSLATE_NOOP("bitcoin-core", "How thorough the block verification is (0-6, default: 1)"),
+QT_TRANSLATE_NOOP("bitcoin-core", "How thorough the block verification is (0-4, default: 3)"),
QT_TRANSLATE_NOOP("bitcoin-core", "Importing blocks from block database..."),
QT_TRANSLATE_NOOP("bitcoin-core", "Imports blocks from external blk000??.dat file"),
QT_TRANSLATE_NOOP("bitcoin-core", "Information"),
@@ -172,7 +174,8 @@ QT_TRANSLATE_NOOP("bitcoin-core", "Use UPnP to map the listening port (default:
QT_TRANSLATE_NOOP("bitcoin-core", "Use proxy to reach tor hidden services (default: same as -proxy)"),
QT_TRANSLATE_NOOP("bitcoin-core", "Use the test network"),
QT_TRANSLATE_NOOP("bitcoin-core", "Username for JSON-RPC connections"),
-QT_TRANSLATE_NOOP("bitcoin-core", "Verifying database integrity..."),
+QT_TRANSLATE_NOOP("bitcoin-core", "Verifying block database integrity..."),
+QT_TRANSLATE_NOOP("bitcoin-core", "Verifying wallet integrity..."),
QT_TRANSLATE_NOOP("bitcoin-core", "Wallet needed to be rewritten: restart Bitcoin to complete"),
QT_TRANSLATE_NOOP("bitcoin-core", "Warning"),
QT_TRANSLATE_NOOP("bitcoin-core", "Warning: This version is obsolete, upgrade required!"),
diff --git a/src/qt/locale/bitcoin_en.ts b/src/qt/locale/bitcoin_en.ts
index cb6f982d32..5a0d4a8a4e 100644
--- a/src/qt/locale/bitcoin_en.ts
+++ b/src/qt/locale/bitcoin_en.ts
@@ -713,7 +713,7 @@ Address: %4
<context>
<name>ClientModel</name>
<message>
- <location filename="../clientmodel.cpp" line="+87"/>
+ <location filename="../clientmodel.cpp" line="+86"/>
<source>Network Alert</source>
<translation>Network Alert</translation>
</message>
@@ -863,7 +863,17 @@ Address: %4
<translation>&amp;Start Bitcoin on system login</translation>
</message>
<message>
- <location line="+21"/>
+ <location line="+35"/>
+ <source>Reset all client options to default.</source>
+ <translation>Reset all client options to default.</translation>
+ </message>
+ <message>
+ <location line="+3"/>
+ <source>&amp;Reset Options</source>
+ <translation>&amp;Reset Options</translation>
+ </message>
+ <message>
+ <location line="+13"/>
<source>&amp;Network</source>
<translation>&amp;Network</translation>
</message>
@@ -998,7 +1008,22 @@ Address: %4
<translation>default</translation>
</message>
<message>
- <location line="+146"/>
+ <location line="+130"/>
+ <source>Confirm options reset</source>
+ <translation>Confirm options reset</translation>
+ </message>
+ <message>
+ <location line="+1"/>
+ <source>Some settings may require a client restart to take effect.</source>
+ <translation>Some settings may require a client restart to take effect.</translation>
+ </message>
+ <message>
+ <location line="+0"/>
+ <source>Do you want to proceed?</source>
+ <translation>Do you want to proceed?</translation>
+ </message>
+ <message>
+ <location line="+42"/>
<location line="+9"/>
<source>Warning</source>
<translation>Warning</translation>
@@ -1023,7 +1048,7 @@ Address: %4
<translation>Form</translation>
</message>
<message>
- <location line="+52"/>
+ <location line="+51"/>
<location line="+183"/>
<source>The displayed information may be out of date. Your wallet automatically synchronizes with the Bitcoin network after a connection is established, but this process has not completed yet.</source>
<translation>The displayed information may be out of date. Your wallet automatically synchronizes with the Bitcoin network after a connection is established, but this process has not completed yet.</translation>
@@ -1160,7 +1185,7 @@ Address: %4
<location line="+53"/>
<location line="+23"/>
<location line="+23"/>
- <location filename="../rpcconsole.cpp" line="+349"/>
+ <location filename="../rpcconsole.cpp" line="+344"/>
<source>N/A</source>
<translation>N/A</translation>
</message>
@@ -1275,7 +1300,7 @@ Address: %4
<translation>Clear console</translation>
</message>
<message>
- <location filename="../rpcconsole.cpp" line="-33"/>
+ <location filename="../rpcconsole.cpp" line="-30"/>
<source>Welcome to the Bitcoin RPC console.</source>
<translation>Welcome to the Bitcoin RPC console.</translation>
</message>
@@ -1488,18 +1513,18 @@ Address: %4
</message>
<message>
<location line="+10"/>
- <location line="+203"/>
+ <location line="+213"/>
<source>Choose an address from the address book</source>
<translation>Choose an address from the address book</translation>
</message>
<message>
- <location line="-193"/>
- <location line="+203"/>
+ <location line="-203"/>
+ <location line="+213"/>
<source>Alt+A</source>
<translation>Alt+A</translation>
</message>
<message>
- <location line="-193"/>
+ <location line="-203"/>
<source>Paste address from clipboard</source>
<translation>Paste address from clipboard</translation>
</message>
@@ -1514,7 +1539,12 @@ Address: %4
<translation>Enter the message you want to sign here</translation>
</message>
<message>
- <location line="+24"/>
+ <location line="+7"/>
+ <source>Signature</source>
+ <translation>Signature</translation>
+ </message>
+ <message>
+ <location line="+27"/>
<source>Copy the current signature to the system clipboard</source>
<translation>Copy the current signature to the system clipboard</translation>
</message>
@@ -1659,16 +1689,8 @@ Address: %4
<source>Open until %1</source>
<translation>Open until %1</translation>
</message>
- <message numerus="yes">
- <location line="-2"/>
- <source>Open for %n block(s)</source>
- <translation>
- <numerusform>Open for %n block</numerusform>
- <numerusform>Open for %n blocks</numerusform>
- </translation>
- </message>
<message>
- <location line="+8"/>
+ <location line="+6"/>
<source>%1/offline</source>
<translation>%1/offline</translation>
</message>
@@ -1829,8 +1851,16 @@ Address: %4
<source>, has not been successfully broadcast yet</source>
<translation>, has not been successfully broadcast yet</translation>
</message>
+ <message numerus="yes">
+ <location line="-35"/>
+ <source>Open for %n more block(s)</source>
+ <translation>
+ <numerusform>Open for %n more block</numerusform>
+ <numerusform>Open for %n more blocks</numerusform>
+ </translation>
+ </message>
<message>
- <location line="+35"/>
+ <location line="+70"/>
<source>unknown</source>
<translation>unknown</translation>
</message>
@@ -1872,10 +1902,10 @@ Address: %4
</message>
<message numerus="yes">
<location line="+57"/>
- <source>Open for %n block(s)</source>
+ <source>Open for %n more block(s)</source>
<translation>
- <numerusform>Open for %n block</numerusform>
- <numerusform>Open for %n blocks</numerusform>
+ <numerusform>Open for %n more block</numerusform>
+ <numerusform>Open for %n more blocks</numerusform>
</translation>
</message>
<message>
@@ -2139,7 +2169,7 @@ Address: %4
<context>
<name>bitcoin-core</name>
<message>
- <location filename="../bitcoinstrings.cpp" line="+86"/>
+ <location filename="../bitcoinstrings.cpp" line="+88"/>
<source>Bitcoin version</source>
<translation>Bitcoin version</translation>
</message>
@@ -2229,12 +2259,12 @@ Address: %4
<translation>Number of seconds to keep misbehaving peers from reconnecting (default: 86400)</translation>
</message>
<message>
- <location line="-26"/>
+ <location line="-28"/>
<source>An error occurred while setting up the RPC port %u for listening on IPv4: %s</source>
<translation>An error occurred while setting up the RPC port %u for listening on IPv4: %s</translation>
</message>
<message>
- <location line="+24"/>
+ <location line="+26"/>
<source>Listen for JSON-RPC connections on &lt;port&gt; (default: 8332 or testnet: 18332)</source>
<translation>Listen for JSON-RPC connections on &lt;port&gt; (default: 8332 or testnet: 18332)</translation>
</message>
@@ -2259,7 +2289,7 @@ Address: %4
<translation>Accept connections from outside (default: 1 if no -proxy or -connect)</translation>
</message>
<message>
- <location line="-72"/>
+ <location line="-74"/>
<source>%s, you must set a rpcpassword in the configuration file:
%s
It is recommended you use the following random password:
@@ -2295,6 +2325,11 @@ If the file does not exist, create it with owner-readable-only file permissions.
</message>
<message>
<location line="+3"/>
+ <source>Corrupted block database detected. Please restart the client with -reindex.</source>
+ <translation>Corrupted block database detected. Please restart the client with -reindex.</translation>
+ </message>
+ <message>
+ <location line="+2"/>
<source>Error initializing database environment %s! To recover, BACKUP THAT DIRECTORY, then remove everything from it except for wallet.dat.</source>
<translation>Error initializing database environment %s! To recover, BACKUP THAT DIRECTORY, then remove everything from it except for wallet.dat.</translation>
</message>
@@ -2364,7 +2399,12 @@ If the file does not exist, create it with owner-readable-only file permissions.
<translation>Discover own IP address (default: 1 when listening and no -externalip)</translation>
</message>
<message>
- <location line="+8"/>
+ <location line="+3"/>
+ <source>Error loading block database</source>
+ <translation>Error loading block database</translation>
+ </message>
+ <message>
+ <location line="+5"/>
<source>Error: Disk space is low!</source>
<translation>Error: Disk space is low!</translation>
</message>
@@ -2389,7 +2429,12 @@ If the file does not exist, create it with owner-readable-only file permissions.
<translation>Find peers using DNS lookup (default: 1 unless -connect)</translation>
</message>
<message>
- <location line="+6"/>
+ <location line="+5"/>
+ <source>How thorough the block verification is (0-4, default: 3)</source>
+ <translation>How thorough the block verification is (0-4, default: 3)</translation>
+ </message>
+ <message>
+ <location line="+1"/>
<source>Importing blocks from block database...</source>
<translation>Importing blocks from block database...</translation>
</message>
@@ -2509,12 +2554,7 @@ If the file does not exist, create it with owner-readable-only file permissions.
<translation>Username for JSON-RPC connections</translation>
</message>
<message>
- <location line="+1"/>
- <source>Verifying database integrity...</source>
- <translation>Verifying database integrity...</translation>
- </message>
- <message>
- <location line="+2"/>
+ <location line="+4"/>
<source>Warning</source>
<translation>Warning</translation>
</message>
@@ -2529,7 +2569,7 @@ If the file does not exist, create it with owner-readable-only file permissions.
<translation>wallet.dat corrupt, salvage failed</translation>
</message>
<message>
- <location line="-42"/>
+ <location line="-43"/>
<source>Password for JSON-RPC connections</source>
<translation>Password for JSON-RPC connections</translation>
</message>
@@ -2569,12 +2609,7 @@ If the file does not exist, create it with owner-readable-only file permissions.
<translation>How many blocks to check at startup (default: 2500, 0 = all)</translation>
</message>
<message>
- <location line="+1"/>
- <source>How thorough the block verification is (0-6, default: 1)</source>
- <translation>How thorough the block verification is (0-6, default: 1)</translation>
- </message>
- <message>
- <location line="+54"/>
+ <location line="+55"/>
<source>Use OpenSSL (https) for JSON-RPC connections</source>
<translation>Use OpenSSL (https) for JSON-RPC connections</translation>
</message>
@@ -2589,12 +2624,12 @@ If the file does not exist, create it with owner-readable-only file permissions.
<translation>Server private key (default: server.pem)</translation>
</message>
<message>
- <location line="-131"/>
+ <location line="-133"/>
<source>Acceptable ciphers (default: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH)</source>
<translation>Acceptable ciphers (default: TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH)</translation>
</message>
<message>
- <location line="+142"/>
+ <location line="+144"/>
<source>This help message</source>
<translation>This help message</translation>
</message>
@@ -2619,12 +2654,7 @@ If the file does not exist, create it with owner-readable-only file permissions.
<translation>Loading addresses...</translation>
</message>
<message>
- <location line="-27"/>
- <source>Error loading blkindex.dat</source>
- <translation>Error loading blkindex.dat</translation>
- </message>
- <message>
- <location line="+2"/>
+ <location line="-25"/>
<source>Error loading wallet.dat: Wallet corrupted</source>
<translation>Error loading wallet.dat: Wallet corrupted</translation>
</message>
@@ -2634,12 +2664,22 @@ If the file does not exist, create it with owner-readable-only file permissions.
<translation>Error loading wallet.dat: Wallet requires newer version of Bitcoin</translation>
</message>
<message>
- <location line="+74"/>
+ <location line="+73"/>
+ <source>Verifying block database integrity...</source>
+ <translation>Verifying block database integrity...</translation>
+ </message>
+ <message>
+ <location line="+1"/>
+ <source>Verifying wallet integrity...</source>
+ <translation>Verifying wallet integrity...</translation>
+ </message>
+ <message>
+ <location line="+1"/>
<source>Wallet needed to be rewritten: restart Bitcoin to complete</source>
<translation>Wallet needed to be rewritten: restart Bitcoin to complete</translation>
</message>
<message>
- <location line="-76"/>
+ <location line="-77"/>
<source>Error loading wallet.dat</source>
<translation>Error loading wallet.dat</translation>
</message>
diff --git a/src/qt/signverifymessagedialog.cpp b/src/qt/signverifymessagedialog.cpp
index b3fc69ef0f..371b5ba535 100644
--- a/src/qt/signverifymessagedialog.cpp
+++ b/src/qt/signverifymessagedialog.cpp
@@ -55,13 +55,13 @@ void SignVerifyMessageDialog::setModel(WalletModel *model)
this->model = model;
}
-void SignVerifyMessageDialog::setAddress_SM(QString address)
+void SignVerifyMessageDialog::setAddress_SM(const QString &address)
{
ui->addressIn_SM->setText(address);
ui->messageIn_SM->setFocus();
}
-void SignVerifyMessageDialog::setAddress_VM(QString address)
+void SignVerifyMessageDialog::setAddress_VM(const QString &address)
{
ui->addressIn_VM->setText(address);
ui->messageIn_VM->setFocus();
diff --git a/src/qt/signverifymessagedialog.h b/src/qt/signverifymessagedialog.h
index 5569c8bf33..2c2677cc0c 100644
--- a/src/qt/signverifymessagedialog.h
+++ b/src/qt/signverifymessagedialog.h
@@ -20,8 +20,8 @@ public:
~SignVerifyMessageDialog();
void setModel(WalletModel *model);
- void setAddress_SM(QString address);
- void setAddress_VM(QString address);
+ void setAddress_SM(const QString &address);
+ void setAddress_VM(const QString &address);
void showTab_SM(bool fShow);
void showTab_VM(bool fShow);
diff --git a/src/qt/transactiondesc.cpp b/src/qt/transactiondesc.cpp
index e358c12e96..d5b08448dd 100644
--- a/src/qt/transactiondesc.cpp
+++ b/src/qt/transactiondesc.cpp
@@ -14,7 +14,7 @@ QString TransactionDesc::FormatTxStatus(const CWalletTx& wtx)
if (!wtx.IsFinal())
{
if (wtx.nLockTime < LOCKTIME_THRESHOLD)
- return tr("Open for %n block(s)", "", nBestHeight - wtx.nLockTime);
+ return tr("Open for %n more block(s)", "", wtx.nLockTime - nBestHeight + 1);
else
return tr("Open until %1").arg(GUIUtil::dateTimeStr(wtx.nLockTime));
}
diff --git a/src/qt/transactionrecord.cpp b/src/qt/transactionrecord.cpp
index 4c3071984f..40a5f735cd 100644
--- a/src/qt/transactionrecord.cpp
+++ b/src/qt/transactionrecord.cpp
@@ -167,7 +167,7 @@ void TransactionRecord::updateStatus(const CWalletTx &wtx)
if (wtx.nLockTime < LOCKTIME_THRESHOLD)
{
status.status = TransactionStatus::OpenUntilBlock;
- status.open_for = nBestHeight - wtx.nLockTime;
+ status.open_for = wtx.nLockTime - nBestHeight + 1;
}
else
{
diff --git a/src/qt/transactionrecord.h b/src/qt/transactionrecord.h
index db06374c44..f6570803de 100644
--- a/src/qt/transactionrecord.h
+++ b/src/qt/transactionrecord.h
@@ -47,7 +47,9 @@ public:
@{*/
Status status;
int64 depth;
- int64 open_for; /**< Timestamp if status==OpenUntilDate, otherwise number of blocks */
+ int64 open_for; /**< Timestamp if status==OpenUntilDate, otherwise number
+ of additional blocks that need to be mined before
+ finalization */
/**@}*/
/** Current number of blocks (to know whether cached status is still valid) */
diff --git a/src/qt/transactiontablemodel.cpp b/src/qt/transactiontablemodel.cpp
index 847c9e9733..aef0e409bd 100644
--- a/src/qt/transactiontablemodel.cpp
+++ b/src/qt/transactiontablemodel.cpp
@@ -280,7 +280,7 @@ QString TransactionTableModel::formatTxStatus(const TransactionRecord *wtx) cons
switch(wtx->status.status)
{
case TransactionStatus::OpenUntilBlock:
- status = tr("Open for %n block(s)","",wtx->status.open_for);
+ status = tr("Open for %n more block(s)","",wtx->status.open_for);
break;
case TransactionStatus::OpenUntilDate:
status = tr("Open until %1").arg(GUIUtil::dateTimeStr(wtx->status.open_for));
diff --git a/src/rpcblockchain.cpp b/src/rpcblockchain.cpp
index 5554f039a7..2200679050 100644
--- a/src/rpcblockchain.cpp
+++ b/src/rpcblockchain.cpp
@@ -9,6 +9,8 @@
using namespace json_spirit;
using namespace std;
+void ScriptPubKeyToJSON(const CScript& scriptPubKey, Object& out);
+
double GetDifficulty(const CBlockIndex* blockindex)
{
// Floating point number that is a multiple of the minimum difficulty,
@@ -213,10 +215,9 @@ Value gettxout(const Array& params, bool fHelp)
ret.push_back(Pair("confirmations", 0));
else
ret.push_back(Pair("confirmations", pcoinsTip->GetBestBlock()->nHeight - coins.nHeight + 1));
- ret.push_back(Pair("amount", (boost::int64_t)coins.vout[n].nValue));
+ ret.push_back(Pair("value", ValueFromAmount(coins.vout[n].nValue)));
Object o;
- o.push_back(Pair("asm", coins.vout[n].scriptPubKey.ToString()));
- o.push_back(Pair("hex", HexStr(coins.vout[n].scriptPubKey.begin(), coins.vout[n].scriptPubKey.end())));
+ ScriptPubKeyToJSON(coins.vout[n].scriptPubKey, o);
ret.push_back(Pair("scriptPubKey", o));
ret.push_back(Pair("version", coins.nVersion));
ret.push_back(Pair("coinbase", coins.fCoinBase));
diff --git a/src/rpcrawtransaction.cpp b/src/rpcrawtransaction.cpp
index 9531b12678..09fbaa30cd 100644
--- a/src/rpcrawtransaction.cpp
+++ b/src/rpcrawtransaction.cpp
@@ -558,7 +558,7 @@ Value sendrawtransaction(const Array& params, bool fHelp)
} else {
SyncWithWallets(hashTx, tx, NULL, true);
}
- RelayMessage(CInv(MSG_TX, hashTx), tx);
+ RelayTransaction(tx, hashTx);
return hashTx.GetHex();
}
diff --git a/src/script.cpp b/src/script.cpp
index f65508aacc..70adf1f9dc 100644
--- a/src/script.cpp
+++ b/src/script.cpp
@@ -16,7 +16,7 @@ using namespace boost;
#include "sync.h"
#include "util.h"
-bool CheckSig(vector<unsigned char> vchSig, vector<unsigned char> vchPubKey, CScript scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType);
+bool CheckSig(vector<unsigned char> vchSig, vector<unsigned char> vchPubKey, CScript scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType, int flags);
@@ -1007,7 +1007,7 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, co
bool fSuccess = (!fStrictEncodings || (IsCanonicalSignature(vchSig) && IsCanonicalPubKey(vchPubKey)));
if (fSuccess)
- fSuccess = CheckSig(vchSig, vchPubKey, scriptCode, txTo, nIn, nHashType);
+ fSuccess = CheckSig(vchSig, vchPubKey, scriptCode, txTo, nIn, nHashType, flags);
popstack(stack);
popstack(stack);
@@ -1069,7 +1069,7 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, co
// Check signature
bool fOk = (!fStrictEncodings || (IsCanonicalSignature(vchSig) && IsCanonicalPubKey(vchPubKey)));
if (fOk)
- fOk = CheckSig(vchSig, vchPubKey, scriptCode, txTo, nIn, nHashType);
+ fOk = CheckSig(vchSig, vchPubKey, scriptCode, txTo, nIn, nHashType, flags);
if (fOk) {
isig++;
@@ -1199,13 +1199,13 @@ private:
// sigdata_type is (signature hash, signature, public key):
typedef boost::tuple<uint256, std::vector<unsigned char>, std::vector<unsigned char> > sigdata_type;
std::set< sigdata_type> setValid;
- CCriticalSection cs_sigcache;
+ boost::shared_mutex cs_sigcache;
public:
bool
Get(uint256 hash, const std::vector<unsigned char>& vchSig, const std::vector<unsigned char>& pubKey)
{
- LOCK(cs_sigcache);
+ boost::shared_lock<boost::shared_mutex> lock(cs_sigcache);
sigdata_type k(hash, vchSig, pubKey);
std::set<sigdata_type>::iterator mi = setValid.find(k);
@@ -1223,7 +1223,7 @@ public:
int64 nMaxCacheSize = GetArg("-maxsigcachesize", 50000);
if (nMaxCacheSize <= 0) return;
- LOCK(cs_sigcache);
+ boost::unique_lock<boost::shared_mutex> lock(cs_sigcache);
while (static_cast<int64>(setValid.size()) > nMaxCacheSize)
{
@@ -1246,7 +1246,7 @@ public:
};
bool CheckSig(vector<unsigned char> vchSig, vector<unsigned char> vchPubKey, CScript scriptCode,
- const CTransaction& txTo, unsigned int nIn, int nHashType)
+ const CTransaction& txTo, unsigned int nIn, int nHashType, int flags)
{
static CSignatureCache signatureCache;
@@ -1271,7 +1271,9 @@ bool CheckSig(vector<unsigned char> vchSig, vector<unsigned char> vchPubKey, CSc
if (!key.Verify(sighash, vchSig))
return false;
- signatureCache.Set(sighash, vchSig, vchPubKey);
+ if (!(flags & SCRIPT_VERIFY_NOCACHE))
+ signatureCache.Set(sighash, vchSig, vchPubKey);
+
return true;
}
@@ -1723,17 +1725,6 @@ bool SignSignature(const CKeyStore &keystore, const CTransaction& txFrom, CTrans
return SignSignature(keystore, txout.scriptPubKey, txTo, nIn, nHashType);
}
-bool VerifySignature(const CCoins& txFrom, const CTransaction& txTo, unsigned int nIn, unsigned int flags, int nHashType)
-{
- assert(nIn < txTo.vin.size());
- const CTxIn& txin = txTo.vin[nIn];
- if (txin.prevout.n >= txFrom.vout.size())
- return false;
- const CTxOut& txout = txFrom.vout[txin.prevout.n];
-
- return VerifyScript(txin.scriptSig, txout.scriptPubKey, txTo, nIn, flags, nHashType);
-}
-
static CScript PushAll(const vector<valtype>& values)
{
CScript result;
@@ -1772,7 +1763,7 @@ static CScript CombineMultisig(CScript scriptPubKey, const CTransaction& txTo, u
if (sigs.count(pubkey))
continue; // Already got a sig for this pubkey
- if (CheckSig(sig, pubkey, scriptPubKey, txTo, nIn, 0))
+ if (CheckSig(sig, pubkey, scriptPubKey, txTo, nIn, 0, 0))
{
sigs[pubkey] = sig;
break;
diff --git a/src/script.h b/src/script.h
index 7b0643f70a..0b481eb605 100644
--- a/src/script.h
+++ b/src/script.h
@@ -32,6 +32,7 @@ enum
SCRIPT_VERIFY_NONE = 0,
SCRIPT_VERIFY_P2SH = (1U << 0),
SCRIPT_VERIFY_STRICTENC = (1U << 1),
+ SCRIPT_VERIFY_NOCACHE = (1U << 2),
};
enum txnouttype
@@ -434,7 +435,7 @@ public:
// Immediate operand
if (opcode <= OP_PUSHDATA4)
{
- unsigned int nSize;
+ unsigned int nSize = 0;
if (opcode < OP_PUSHDATA1)
{
nSize = opcode;
@@ -646,7 +647,7 @@ public:
template<typename Stream>
void Unserialize(Stream &s, int nType, int nVersion) {
- unsigned int nSize;
+ unsigned int nSize = 0;
s >> VARINT(nSize);
if (nSize < nSpecialScripts) {
std::vector<unsigned char> vch(GetSpecialSize(nSize), 0x00);
@@ -673,9 +674,7 @@ bool ExtractDestination(const CScript& scriptPubKey, CTxDestination& addressRet)
bool ExtractDestinations(const CScript& scriptPubKey, txnouttype& typeRet, std::vector<CTxDestination>& addressRet, int& nRequiredRet);
bool SignSignature(const CKeyStore& keystore, const CScript& fromPubKey, CTransaction& txTo, unsigned int nIn, int nHashType=SIGHASH_ALL);
bool SignSignature(const CKeyStore& keystore, const CTransaction& txFrom, CTransaction& txTo, unsigned int nIn, int nHashType=SIGHASH_ALL);
-bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const CTransaction& txTo, unsigned int nIn,
- unsigned int flags, int nHashType);
-bool VerifySignature(const CCoins& txFrom, const CTransaction& txTo, unsigned int nIn, unsigned int flags, int nHashType);
+bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const CTransaction& txTo, unsigned int nIn, unsigned int flags, int nHashType);
// Given two sets of signatures for scriptPubKey, possibly with OP_0 placeholders,
// combine them intelligently and return the result.
diff --git a/src/test/bloom_tests.cpp b/src/test/bloom_tests.cpp
new file mode 100644
index 0000000000..4a2851cf46
--- /dev/null
+++ b/src/test/bloom_tests.cpp
@@ -0,0 +1,447 @@
+#include <boost/test/unit_test.hpp>
+#include <vector>
+
+#include "bloom.h"
+#include "util.h"
+#include "key.h"
+#include "base58.h"
+#include "main.h"
+
+using namespace std;
+using namespace boost::tuples;
+
+BOOST_AUTO_TEST_SUITE(bloom_tests)
+
+BOOST_AUTO_TEST_CASE(bloom_create_insert_serialize)
+{
+ CBloomFilter filter(3, 0.01, 0, BLOOM_UPDATE_ALL);
+
+ filter.insert(ParseHex("99108ad8ed9bb6274d3980bab5a85c048f0950c8"));
+ BOOST_CHECK_MESSAGE( filter.contains(ParseHex("99108ad8ed9bb6274d3980bab5a85c048f0950c8")), "BloomFilter doesn't contain just-inserted object!");
+ // One bit different in first byte
+ BOOST_CHECK_MESSAGE(!filter.contains(ParseHex("19108ad8ed9bb6274d3980bab5a85c048f0950c8")), "BloomFilter contains something it shouldn't!");
+
+ filter.insert(ParseHex("b5a2c786d9ef4658287ced5914b37a1b4aa32eee"));
+ BOOST_CHECK_MESSAGE(filter.contains(ParseHex("b5a2c786d9ef4658287ced5914b37a1b4aa32eee")), "BloomFilter doesn't contain just-inserted object (2)!");
+
+ filter.insert(ParseHex("b9300670b4c5366e95b2699e8b18bc75e5f729c5"));
+ BOOST_CHECK_MESSAGE(filter.contains(ParseHex("b9300670b4c5366e95b2699e8b18bc75e5f729c5")), "BloomFilter doesn't contain just-inserted object (3)!");
+
+ CDataStream stream(SER_NETWORK, PROTOCOL_VERSION);
+ filter.Serialize(stream, SER_NETWORK, PROTOCOL_VERSION);
+
+ vector<unsigned char> vch = ParseHex("03614e9b050000000000000001");
+ vector<char> expected(vch.size());
+
+ for (unsigned int i = 0; i < vch.size(); i++)
+ expected[i] = (char)vch[i];
+
+ BOOST_CHECK_EQUAL_COLLECTIONS(stream.begin(), stream.end(), expected.begin(), expected.end());
+}
+
+BOOST_AUTO_TEST_CASE(bloom_create_insert_serialize_with_tweak)
+{
+ // Same test as bloom_create_insert_serialize, but we add a nTweak of 100
+ CBloomFilter filter(3, 0.01, 2147483649, BLOOM_UPDATE_ALL);
+
+ filter.insert(ParseHex("99108ad8ed9bb6274d3980bab5a85c048f0950c8"));
+ BOOST_CHECK_MESSAGE( filter.contains(ParseHex("99108ad8ed9bb6274d3980bab5a85c048f0950c8")), "BloomFilter doesn't contain just-inserted object!");
+ // One bit different in first byte
+ BOOST_CHECK_MESSAGE(!filter.contains(ParseHex("19108ad8ed9bb6274d3980bab5a85c048f0950c8")), "BloomFilter contains something it shouldn't!");
+
+ filter.insert(ParseHex("b5a2c786d9ef4658287ced5914b37a1b4aa32eee"));
+ BOOST_CHECK_MESSAGE(filter.contains(ParseHex("b5a2c786d9ef4658287ced5914b37a1b4aa32eee")), "BloomFilter doesn't contain just-inserted object (2)!");
+
+ filter.insert(ParseHex("b9300670b4c5366e95b2699e8b18bc75e5f729c5"));
+ BOOST_CHECK_MESSAGE(filter.contains(ParseHex("b9300670b4c5366e95b2699e8b18bc75e5f729c5")), "BloomFilter doesn't contain just-inserted object (3)!");
+
+ CDataStream stream(SER_NETWORK, PROTOCOL_VERSION);
+ filter.Serialize(stream, SER_NETWORK, PROTOCOL_VERSION);
+
+ vector<unsigned char> vch = ParseHex("03ce4299050000000100008001");
+ vector<char> expected(vch.size());
+
+ for (unsigned int i = 0; i < vch.size(); i++)
+ expected[i] = (char)vch[i];
+
+ BOOST_CHECK_EQUAL_COLLECTIONS(stream.begin(), stream.end(), expected.begin(), expected.end());
+}
+
+BOOST_AUTO_TEST_CASE(bloom_create_insert_key)
+{
+ string strSecret = string("5Kg1gnAjaLfKiwhhPpGS3QfRg2m6awQvaj98JCZBZQ5SuS2F15C");
+ CBitcoinSecret vchSecret;
+ BOOST_CHECK(vchSecret.SetString(strSecret));
+
+ CKey key;
+ bool fCompressed;
+ CSecret secret = vchSecret.GetSecret(fCompressed);
+ key.SetSecret(secret, fCompressed);
+
+ CBloomFilter filter(2, 0.001, 0, BLOOM_UPDATE_ALL);
+ filter.insert(key.GetPubKey().Raw());
+ uint160 hash = key.GetPubKey().GetID();
+ filter.insert(vector<unsigned char>(hash.begin(), hash.end()));
+
+ CDataStream stream(SER_NETWORK, PROTOCOL_VERSION);
+ filter.Serialize(stream, SER_NETWORK, PROTOCOL_VERSION);
+
+ vector<unsigned char> vch = ParseHex("038fc16b080000000000000001");
+ vector<char> expected(vch.size());
+
+ for (unsigned int i = 0; i < vch.size(); i++)
+ expected[i] = (char)vch[i];
+
+ BOOST_CHECK_EQUAL_COLLECTIONS(stream.begin(), stream.end(), expected.begin(), expected.end());
+}
+
+BOOST_AUTO_TEST_CASE(bloom_match)
+{
+ // Random real transaction (b4749f017444b051c44dfd2720e88f314ff94f3dd6d56d40ef65854fcd7fff6b)
+ CTransaction tx;
+ CDataStream stream(ParseHex("01000000010b26e9b7735eb6aabdf358bab62f9816a21ba9ebdb719d5299e88607d722c190000000008b4830450220070aca44506c5cef3a16ed519d7c3c39f8aab192c4e1c90d065f37b8a4af6141022100a8e160b856c2d43d27d8fba71e5aef6405b8643ac4cb7cb3c462aced7f14711a0141046d11fee51b0e60666d5049a9101a72741df480b96ee26488a4d3466b95c9a40ac5eeef87e10a5cd336c19a84565f80fa6c547957b7700ff4dfbdefe76036c339ffffffff021bff3d11000000001976a91404943fdd508053c75000106d3bc6e2754dbcff1988ac2f15de00000000001976a914a266436d2965547608b9e15d9032a7b9d64fa43188ac00000000"), SER_DISK, CLIENT_VERSION);
+ stream >> tx;
+
+ // and one which spends it (e2769b09e784f32f62ef849763d4f45b98e07ba658647343b915ff832b110436)
+ unsigned char ch[] = {0x01, 0x00, 0x00, 0x00, 0x01, 0x6b, 0xff, 0x7f, 0xcd, 0x4f, 0x85, 0x65, 0xef, 0x40, 0x6d, 0xd5, 0xd6, 0x3d, 0x4f, 0xf9, 0x4f, 0x31, 0x8f, 0xe8, 0x20, 0x27, 0xfd, 0x4d, 0xc4, 0x51, 0xb0, 0x44, 0x74, 0x01, 0x9f, 0x74, 0xb4, 0x00, 0x00, 0x00, 0x00, 0x8c, 0x49, 0x30, 0x46, 0x02, 0x21, 0x00, 0xda, 0x0d, 0xc6, 0xae, 0xce, 0xfe, 0x1e, 0x06, 0xef, 0xdf, 0x05, 0x77, 0x37, 0x57, 0xde, 0xb1, 0x68, 0x82, 0x09, 0x30, 0xe3, 0xb0, 0xd0, 0x3f, 0x46, 0xf5, 0xfc, 0xf1, 0x50, 0xbf, 0x99, 0x0c, 0x02, 0x21, 0x00, 0xd2, 0x5b, 0x5c, 0x87, 0x04, 0x00, 0x76, 0xe4, 0xf2, 0x53, 0xf8, 0x26, 0x2e, 0x76, 0x3e, 0x2d, 0xd5, 0x1e, 0x7f, 0xf0, 0xbe, 0x15, 0x77, 0x27, 0xc4, 0xbc, 0x42, 0x80, 0x7f, 0x17, 0xbd, 0x39, 0x01, 0x41, 0x04, 0xe6, 0xc2, 0x6e, 0xf6, 0x7d, 0xc6, 0x10, 0xd2, 0xcd, 0x19, 0x24, 0x84, 0x78, 0x9a, 0x6c, 0xf9, 0xae, 0xa9, 0x93, 0x0b, 0x94, 0x4b, 0x7e, 0x2d, 0xb5, 0x34, 0x2b, 0x9d, 0x9e, 0x5b, 0x9f, 0xf7, 0x9a, 0xff, 0x9a, 0x2e, 0xe1, 0x97, 0x8d, 0xd7, 0xfd, 0x01, 0xdf, 0xc5, 0x22, 0xee, 0x02, 0x28, 0x3d, 0x3b, 0x06, 0xa9, 0xd0, 0x3a, 0xcf, 0x80, 0x96, 0x96, 0x8d, 0x7d, 0xbb, 0x0f, 0x91, 0x78, 0xff, 0xff, 0xff, 0xff, 0x02, 0x8b, 0xa7, 0x94, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x19, 0x76, 0xa9, 0x14, 0xba, 0xde, 0xec, 0xfd, 0xef, 0x05, 0x07, 0x24, 0x7f, 0xc8, 0xf7, 0x42, 0x41, 0xd7, 0x3b, 0xc0, 0x39, 0x97, 0x2d, 0x7b, 0x88, 0xac, 0x40, 0x94, 0xa8, 0x02, 0x00, 0x00, 0x00, 0x00, 0x19, 0x76, 0xa9, 0x14, 0xc1, 0x09, 0x32, 0x48, 0x3f, 0xec, 0x93, 0xed, 0x51, 0xf5, 0xfe, 0x95, 0xe7, 0x25, 0x59, 0xf2, 0xcc, 0x70, 0x43, 0xf9, 0x88, 0xac, 0x00, 0x00, 0x00, 0x00, 0x00};
+ vector<unsigned char> vch(ch, ch + sizeof(ch) -1);
+ CDataStream spendStream(vch, SER_DISK, CLIENT_VERSION);
+ CTransaction spendingTx;
+ spendStream >> spendingTx;
+
+ CBloomFilter filter(10, 0.000001, 0, BLOOM_UPDATE_ALL);
+ filter.insert(uint256("0xb4749f017444b051c44dfd2720e88f314ff94f3dd6d56d40ef65854fcd7fff6b"));
+ BOOST_CHECK_MESSAGE(filter.IsRelevantAndUpdate(tx, tx.GetHash()), "Simple Bloom filter didn't match tx hash");
+
+ filter = CBloomFilter(10, 0.000001, 0, BLOOM_UPDATE_ALL);
+ // byte-reversed tx hash
+ filter.insert(ParseHex("6bff7fcd4f8565ef406dd5d63d4ff94f318fe82027fd4dc451b04474019f74b4"));
+ BOOST_CHECK_MESSAGE(filter.IsRelevantAndUpdate(tx, tx.GetHash()), "Simple Bloom filter didn't match manually serialized tx hash");
+
+ filter = CBloomFilter(10, 0.000001, 0, BLOOM_UPDATE_ALL);
+ filter.insert(ParseHex("30450220070aca44506c5cef3a16ed519d7c3c39f8aab192c4e1c90d065f37b8a4af6141022100a8e160b856c2d43d27d8fba71e5aef6405b8643ac4cb7cb3c462aced7f14711a01"));
+ BOOST_CHECK_MESSAGE(filter.IsRelevantAndUpdate(tx, tx.GetHash()), "Simple Bloom filter didn't match input signature");
+
+ filter = CBloomFilter(10, 0.000001, 0, BLOOM_UPDATE_ALL);
+ filter.insert(ParseHex("046d11fee51b0e60666d5049a9101a72741df480b96ee26488a4d3466b95c9a40ac5eeef87e10a5cd336c19a84565f80fa6c547957b7700ff4dfbdefe76036c339"));
+ BOOST_CHECK_MESSAGE(filter.IsRelevantAndUpdate(tx, tx.GetHash()), "Simple Bloom filter didn't match input pub key");
+
+ filter = CBloomFilter(10, 0.000001, 0, BLOOM_UPDATE_ALL);
+ filter.insert(ParseHex("04943fdd508053c75000106d3bc6e2754dbcff19"));
+ BOOST_CHECK_MESSAGE(filter.IsRelevantAndUpdate(tx, tx.GetHash()), "Simple Bloom filter didn't match output address");
+ BOOST_CHECK_MESSAGE(filter.IsRelevantAndUpdate(spendingTx, spendingTx.GetHash()), "Simple Bloom filter didn't add output");
+
+ filter = CBloomFilter(10, 0.000001, 0, BLOOM_UPDATE_ALL);
+ filter.insert(ParseHex("a266436d2965547608b9e15d9032a7b9d64fa431"));
+ BOOST_CHECK_MESSAGE(filter.IsRelevantAndUpdate(tx, tx.GetHash()), "Simple Bloom filter didn't match output address");
+
+ filter = CBloomFilter(10, 0.000001, 0, BLOOM_UPDATE_ALL);
+ filter.insert(COutPoint(uint256("0x90c122d70786e899529d71dbeba91ba216982fb6ba58f3bdaab65e73b7e9260b"), 0));
+ BOOST_CHECK_MESSAGE(filter.IsRelevantAndUpdate(tx, tx.GetHash()), "Simple Bloom filter didn't match COutPoint");
+
+ filter = CBloomFilter(10, 0.000001, 0, BLOOM_UPDATE_ALL);
+ COutPoint prevOutPoint(uint256("0x90c122d70786e899529d71dbeba91ba216982fb6ba58f3bdaab65e73b7e9260b"), 0);
+ {
+ vector<unsigned char> data(32 + sizeof(unsigned int));
+ memcpy(&data[0], prevOutPoint.hash.begin(), 32);
+ memcpy(&data[32], &prevOutPoint.n, sizeof(unsigned int));
+ filter.insert(data);
+ }
+ BOOST_CHECK_MESSAGE(filter.IsRelevantAndUpdate(tx, tx.GetHash()), "Simple Bloom filter didn't match manually serialized COutPoint");
+
+ filter = CBloomFilter(10, 0.000001, 0, BLOOM_UPDATE_ALL);
+ filter.insert(uint256("00000009e784f32f62ef849763d4f45b98e07ba658647343b915ff832b110436"));
+ BOOST_CHECK_MESSAGE(!filter.IsRelevantAndUpdate(tx, tx.GetHash()), "Simple Bloom filter matched random tx hash");
+
+ filter = CBloomFilter(10, 0.000001, 0, BLOOM_UPDATE_ALL);
+ filter.insert(ParseHex("0000006d2965547608b9e15d9032a7b9d64fa431"));
+ BOOST_CHECK_MESSAGE(!filter.IsRelevantAndUpdate(tx, tx.GetHash()), "Simple Bloom filter matched random address");
+
+ filter = CBloomFilter(10, 0.000001, 0, BLOOM_UPDATE_ALL);
+ filter.insert(COutPoint(uint256("0x90c122d70786e899529d71dbeba91ba216982fb6ba58f3bdaab65e73b7e9260b"), 1));
+ BOOST_CHECK_MESSAGE(!filter.IsRelevantAndUpdate(tx, tx.GetHash()), "Simple Bloom filter matched COutPoint for an output we didn't care about");
+
+ filter = CBloomFilter(10, 0.000001, 0, BLOOM_UPDATE_ALL);
+ filter.insert(COutPoint(uint256("0x000000d70786e899529d71dbeba91ba216982fb6ba58f3bdaab65e73b7e9260b"), 0));
+ BOOST_CHECK_MESSAGE(!filter.IsRelevantAndUpdate(tx, tx.GetHash()), "Simple Bloom filter matched COutPoint for an output we didn't care about");
+}
+
+BOOST_AUTO_TEST_CASE(merkle_block_1)
+{
+ // Random real block (0000000000013b8ab2cd513b0261a14096412195a72a0c4827d229dcc7e0f7af)
+ // With 9 txes
+ CBlock block;
+ CDataStream stream(ParseHex("0100000090f0a9f110702f808219ebea1173056042a714bad51b916cb6800000000000005275289558f51c9966699404ae2294730c3c9f9bda53523ce50e9b95e558da2fdb261b4d4c86041b1ab1bf930901000000010000000000000000000000000000000000000000000000000000000000000000ffffffff07044c86041b0146ffffffff0100f2052a01000000434104e18f7afbe4721580e81e8414fc8c24d7cfacf254bb5c7b949450c3e997c2dc1242487a8169507b631eb3771f2b425483fb13102c4eb5d858eef260fe70fbfae0ac00000000010000000196608ccbafa16abada902780da4dc35dafd7af05fa0da08cf833575f8cf9e836000000004a493046022100dab24889213caf43ae6adc41cf1c9396c08240c199f5225acf45416330fd7dbd022100fe37900e0644bf574493a07fc5edba06dbc07c311b947520c2d514bc5725dcb401ffffffff0100f2052a010000001976a914f15d1921f52e4007b146dfa60f369ed2fc393ce288ac000000000100000001fb766c1288458c2bafcfec81e48b24d98ec706de6b8af7c4e3c29419bfacb56d000000008c493046022100f268ba165ce0ad2e6d93f089cfcd3785de5c963bb5ea6b8c1b23f1ce3e517b9f022100da7c0f21adc6c401887f2bfd1922f11d76159cbc597fbd756a23dcbb00f4d7290141042b4e8625a96127826915a5b109852636ad0da753c9e1d5606a50480cd0c40f1f8b8d898235e571fe9357d9ec842bc4bba1827daaf4de06d71844d0057707966affffffff0280969800000000001976a9146963907531db72d0ed1a0cfb471ccb63923446f388ac80d6e34c000000001976a914f0688ba1c0d1ce182c7af6741e02658c7d4dfcd388ac000000000100000002c40297f730dd7b5a99567eb8d27b78758f607507c52292d02d4031895b52f2ff010000008b483045022100f7edfd4b0aac404e5bab4fd3889e0c6c41aa8d0e6fa122316f68eddd0a65013902205b09cc8b2d56e1cd1f7f2fafd60a129ed94504c4ac7bdc67b56fe67512658b3e014104732012cb962afa90d31b25d8fb0e32c94e513ab7a17805c14ca4c3423e18b4fb5d0e676841733cb83abaf975845c9f6f2a8097b7d04f4908b18368d6fc2d68ecffffffffca5065ff9617cbcba45eb23726df6498a9b9cafed4f54cbab9d227b0035ddefb000000008a473044022068010362a13c7f9919fa832b2dee4e788f61f6f5d344a7c2a0da6ae740605658022006d1af525b9a14a35c003b78b72bd59738cd676f845d1ff3fc25049e01003614014104732012cb962afa90d31b25d8fb0e32c94e513ab7a17805c14ca4c3423e18b4fb5d0e676841733cb83abaf975845c9f6f2a8097b7d04f4908b18368d6fc2d68ecffffffff01001ec4110200000043410469ab4181eceb28985b9b4e895c13fa5e68d85761b7eee311db5addef76fa8621865134a221bd01f28ec9999ee3e021e60766e9d1f3458c115fb28650605f11c9ac000000000100000001cdaf2f758e91c514655e2dc50633d1e4c84989f8aa90a0dbc883f0d23ed5c2fa010000008b48304502207ab51be6f12a1962ba0aaaf24a20e0b69b27a94fac5adf45aa7d2d18ffd9236102210086ae728b370e5329eead9accd880d0cb070aea0c96255fae6c4f1ddcce1fd56e014104462e76fd4067b3a0aa42070082dcb0bf2f388b6495cf33d789904f07d0f55c40fbd4b82963c69b3dc31895d0c772c812b1d5fbcade15312ef1c0e8ebbb12dcd4ffffffff02404b4c00000000001976a9142b6ba7c9d796b75eef7942fc9288edd37c32f5c388ac002d3101000000001976a9141befba0cdc1ad56529371864d9f6cb042faa06b588ac000000000100000001b4a47603e71b61bc3326efd90111bf02d2f549b067f4c4a8fa183b57a0f800cb010000008a4730440220177c37f9a505c3f1a1f0ce2da777c339bd8339ffa02c7cb41f0a5804f473c9230220585b25a2ee80eb59292e52b987dad92acb0c64eced92ed9ee105ad153cdb12d001410443bd44f683467e549dae7d20d1d79cbdb6df985c6e9c029c8d0c6cb46cc1a4d3cf7923c5021b27f7a0b562ada113bc85d5fda5a1b41e87fe6e8802817cf69996ffffffff0280651406000000001976a9145505614859643ab7b547cd7f1f5e7e2a12322d3788ac00aa0271000000001976a914ea4720a7a52fc166c55ff2298e07baf70ae67e1b88ac00000000010000000586c62cd602d219bb60edb14a3e204de0705176f9022fe49a538054fb14abb49e010000008c493046022100f2bc2aba2534becbdf062eb993853a42bbbc282083d0daf9b4b585bd401aa8c9022100b1d7fd7ee0b95600db8535bbf331b19eed8d961f7a8e54159c53675d5f69df8c014104462e76fd4067b3a0aa42070082dcb0bf2f388b6495cf33d789904f07d0f55c40fbd4b82963c69b3dc31895d0c772c812b1d5fbcade15312ef1c0e8ebbb12dcd4ffffffff03ad0e58ccdac3df9dc28a218bcf6f1997b0a93306faaa4b3a28ae83447b2179010000008b483045022100be12b2937179da88599e27bb31c3525097a07cdb52422d165b3ca2f2020ffcf702200971b51f853a53d644ebae9ec8f3512e442b1bcb6c315a5b491d119d10624c83014104462e76fd4067b3a0aa42070082dcb0bf2f388b6495cf33d789904f07d0f55c40fbd4b82963c69b3dc31895d0c772c812b1d5fbcade15312ef1c0e8ebbb12dcd4ffffffff2acfcab629bbc8685792603762c921580030ba144af553d271716a95089e107b010000008b483045022100fa579a840ac258871365dd48cd7552f96c8eea69bd00d84f05b283a0dab311e102207e3c0ee9234814cfbb1b659b83671618f45abc1326b9edcc77d552a4f2a805c0014104462e76fd4067b3a0aa42070082dcb0bf2f388b6495cf33d789904f07d0f55c40fbd4b82963c69b3dc31895d0c772c812b1d5fbcade15312ef1c0e8ebbb12dcd4ffffffffdcdc6023bbc9944a658ddc588e61eacb737ddf0a3cd24f113b5a8634c517fcd2000000008b4830450221008d6df731df5d32267954bd7d2dda2302b74c6c2a6aa5c0ca64ecbabc1af03c75022010e55c571d65da7701ae2da1956c442df81bbf076cdbac25133f99d98a9ed34c014104462e76fd4067b3a0aa42070082dcb0bf2f388b6495cf33d789904f07d0f55c40fbd4b82963c69b3dc31895d0c772c812b1d5fbcade15312ef1c0e8ebbb12dcd4ffffffffe15557cd5ce258f479dfd6dc6514edf6d7ed5b21fcfa4a038fd69f06b83ac76e010000008b483045022023b3e0ab071eb11de2eb1cc3a67261b866f86bf6867d4558165f7c8c8aca2d86022100dc6e1f53a91de3efe8f63512850811f26284b62f850c70ca73ed5de8771fb451014104462e76fd4067b3a0aa42070082dcb0bf2f388b6495cf33d789904f07d0f55c40fbd4b82963c69b3dc31895d0c772c812b1d5fbcade15312ef1c0e8ebbb12dcd4ffffffff01404b4c00000000001976a9142b6ba7c9d796b75eef7942fc9288edd37c32f5c388ac00000000010000000166d7577163c932b4f9690ca6a80b6e4eb001f0a2fa9023df5595602aae96ed8d000000008a4730440220262b42546302dfb654a229cefc86432b89628ff259dc87edd1154535b16a67e102207b4634c020a97c3e7bbd0d4d19da6aa2269ad9dded4026e896b213d73ca4b63f014104979b82d02226b3a4597523845754d44f13639e3bf2df5e82c6aab2bdc79687368b01b1ab8b19875ae3c90d661a3d0a33161dab29934edeb36aa01976be3baf8affffffff02404b4c00000000001976a9144854e695a02af0aeacb823ccbc272134561e0a1688ac40420f00000000001976a914abee93376d6b37b5c2940655a6fcaf1c8e74237988ac0000000001000000014e3f8ef2e91349a9059cb4f01e54ab2597c1387161d3da89919f7ea6acdbb371010000008c49304602210081f3183471a5ca22307c0800226f3ef9c353069e0773ac76bb580654d56aa523022100d4c56465bdc069060846f4fbf2f6b20520b2a80b08b168b31e66ddb9c694e240014104976c79848e18251612f8940875b2b08d06e6dc73b9840e8860c066b7e87432c477e9a59a453e71e6d76d5fe34058b800a098fc1740ce3012e8fc8a00c96af966ffffffff02c0e1e400000000001976a9144134e75a6fcb6042034aab5e18570cf1f844f54788ac404b4c00000000001976a9142b6ba7c9d796b75eef7942fc9288edd37c32f5c388ac00000000"), SER_NETWORK, PROTOCOL_VERSION);
+ stream >> block;
+
+ CBloomFilter filter(10, 0.000001, 0, BLOOM_UPDATE_ALL);
+ // Match the last transaction
+ filter.insert(uint256("0x74d681e0e03bafa802c8aa084379aa98d9fcd632ddc2ed9782b586ec87451f20"));
+
+ CMerkleBlock merkleBlock(block, filter);
+ BOOST_CHECK(merkleBlock.header.GetHash() == block.GetHash());
+
+ BOOST_CHECK(merkleBlock.vMatchedTxn.size() == 1);
+ pair<unsigned int, uint256> pair = merkleBlock.vMatchedTxn[0];
+
+ BOOST_CHECK(merkleBlock.vMatchedTxn[0].second == uint256("0x74d681e0e03bafa802c8aa084379aa98d9fcd632ddc2ed9782b586ec87451f20"));
+ BOOST_CHECK(merkleBlock.vMatchedTxn[0].first == 8);
+
+ vector<uint256> vMatched;
+ BOOST_CHECK(merkleBlock.txn.ExtractMatches(vMatched) == block.hashMerkleRoot);
+ BOOST_CHECK(vMatched.size() == merkleBlock.vMatchedTxn.size());
+ for (unsigned int i = 0; i < vMatched.size(); i++)
+ BOOST_CHECK(vMatched[i] == merkleBlock.vMatchedTxn[i].second);
+
+ // Also match the 8th transaction
+ filter.insert(uint256("0xdd1fd2a6fc16404faf339881a90adbde7f4f728691ac62e8f168809cdfae1053"));
+ merkleBlock = CMerkleBlock(block, filter);
+ BOOST_CHECK(merkleBlock.header.GetHash() == block.GetHash());
+
+ BOOST_CHECK(merkleBlock.vMatchedTxn.size() == 2);
+
+ BOOST_CHECK(merkleBlock.vMatchedTxn[1] == pair);
+
+ BOOST_CHECK(merkleBlock.vMatchedTxn[0].second == uint256("0xdd1fd2a6fc16404faf339881a90adbde7f4f728691ac62e8f168809cdfae1053"));
+ BOOST_CHECK(merkleBlock.vMatchedTxn[0].first == 7);
+
+ BOOST_CHECK(merkleBlock.txn.ExtractMatches(vMatched) == block.hashMerkleRoot);
+ BOOST_CHECK(vMatched.size() == merkleBlock.vMatchedTxn.size());
+ for (unsigned int i = 0; i < vMatched.size(); i++)
+ BOOST_CHECK(vMatched[i] == merkleBlock.vMatchedTxn[i].second);
+}
+
+BOOST_AUTO_TEST_CASE(merkle_block_2)
+{
+ // Random real block (000000005a4ded781e667e06ceefafb71410b511fe0d5adc3e5a27ecbec34ae6)
+ // With 4 txes
+ CBlock block;
+ CDataStream stream(ParseHex("0100000075616236cc2126035fadb38deb65b9102cc2c41c09cdf29fc051906800000000fe7d5e12ef0ff901f6050211249919b1c0653771832b3a80c66cea42847f0ae1d4d26e49ffff001d00f0a4410401000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0804ffff001d029105ffffffff0100f2052a010000004341046d8709a041d34357697dfcb30a9d05900a6294078012bf3bb09c6f9b525f1d16d5503d7905db1ada9501446ea00728668fc5719aa80be2fdfc8a858a4dbdd4fbac00000000010000000255605dc6f5c3dc148b6da58442b0b2cd422be385eab2ebea4119ee9c268d28350000000049483045022100aa46504baa86df8a33b1192b1b9367b4d729dc41e389f2c04f3e5c7f0559aae702205e82253a54bf5c4f65b7428551554b2045167d6d206dfe6a2e198127d3f7df1501ffffffff55605dc6f5c3dc148b6da58442b0b2cd422be385eab2ebea4119ee9c268d2835010000004847304402202329484c35fa9d6bb32a55a70c0982f606ce0e3634b69006138683bcd12cbb6602200c28feb1e2555c3210f1dddb299738b4ff8bbe9667b68cb8764b5ac17b7adf0001ffffffff0200e1f505000000004341046a0765b5865641ce08dd39690aade26dfbf5511430ca428a3089261361cef170e3929a68aee3d8d4848b0c5111b0a37b82b86ad559fd2a745b44d8e8d9dfdc0cac00180d8f000000004341044a656f065871a353f216ca26cef8dde2f03e8c16202d2e8ad769f02032cb86a5eb5e56842e92e19141d60a01928f8dd2c875a390f67c1f6c94cfc617c0ea45afac0000000001000000025f9a06d3acdceb56be1bfeaa3e8a25e62d182fa24fefe899d1c17f1dad4c2028000000004847304402205d6058484157235b06028c30736c15613a28bdb768ee628094ca8b0030d4d6eb0220328789c9a2ec27ddaec0ad5ef58efded42e6ea17c2e1ce838f3d6913f5e95db601ffffffff5f9a06d3acdceb56be1bfeaa3e8a25e62d182fa24fefe899d1c17f1dad4c2028010000004a493046022100c45af050d3cea806cedd0ab22520c53ebe63b987b8954146cdca42487b84bdd6022100b9b027716a6b59e640da50a864d6dd8a0ef24c76ce62391fa3eabaf4d2886d2d01ffffffff0200e1f505000000004341046a0765b5865641ce08dd39690aade26dfbf5511430ca428a3089261361cef170e3929a68aee3d8d4848b0c5111b0a37b82b86ad559fd2a745b44d8e8d9dfdc0cac00180d8f000000004341046a0765b5865641ce08dd39690aade26dfbf5511430ca428a3089261361cef170e3929a68aee3d8d4848b0c5111b0a37b82b86ad559fd2a745b44d8e8d9dfdc0cac000000000100000002e2274e5fea1bf29d963914bd301aa63b64daaf8a3e88f119b5046ca5738a0f6b0000000048473044022016e7a727a061ea2254a6c358376aaa617ac537eb836c77d646ebda4c748aac8b0220192ce28bf9f2c06a6467e6531e27648d2b3e2e2bae85159c9242939840295ba501ffffffffe2274e5fea1bf29d963914bd301aa63b64daaf8a3e88f119b5046ca5738a0f6b010000004a493046022100b7a1a755588d4190118936e15cd217d133b0e4a53c3c15924010d5648d8925c9022100aaef031874db2114f2d869ac2de4ae53908fbfea5b2b1862e181626bb9005c9f01ffffffff0200e1f505000000004341044a656f065871a353f216ca26cef8dde2f03e8c16202d2e8ad769f02032cb86a5eb5e56842e92e19141d60a01928f8dd2c875a390f67c1f6c94cfc617c0ea45afac00180d8f000000004341046a0765b5865641ce08dd39690aade26dfbf5511430ca428a3089261361cef170e3929a68aee3d8d4848b0c5111b0a37b82b86ad559fd2a745b44d8e8d9dfdc0cac00000000"), SER_NETWORK, PROTOCOL_VERSION);
+ stream >> block;
+
+ CBloomFilter filter(10, 0.000001, 0, BLOOM_UPDATE_ALL);
+ // Match the first transaction
+ filter.insert(uint256("0xe980fe9f792d014e73b95203dc1335c5f9ce19ac537a419e6df5b47aecb93b70"));
+
+ CMerkleBlock merkleBlock(block, filter);
+ BOOST_CHECK(merkleBlock.header.GetHash() == block.GetHash());
+
+ BOOST_CHECK(merkleBlock.vMatchedTxn.size() == 1);
+ pair<unsigned int, uint256> pair = merkleBlock.vMatchedTxn[0];
+
+ BOOST_CHECK(merkleBlock.vMatchedTxn[0].second == uint256("0xe980fe9f792d014e73b95203dc1335c5f9ce19ac537a419e6df5b47aecb93b70"));
+ BOOST_CHECK(merkleBlock.vMatchedTxn[0].first == 0);
+
+ vector<uint256> vMatched;
+ BOOST_CHECK(merkleBlock.txn.ExtractMatches(vMatched) == block.hashMerkleRoot);
+ BOOST_CHECK(vMatched.size() == merkleBlock.vMatchedTxn.size());
+ for (unsigned int i = 0; i < vMatched.size(); i++)
+ BOOST_CHECK(vMatched[i] == merkleBlock.vMatchedTxn[i].second);
+
+ // Match an output from the second transaction (the pubkey for address 1DZTzaBHUDM7T3QvUKBz4qXMRpkg8jsfB5)
+ // This should match the third transaction because it spends the output matched
+ // It also matches the fourth transaction, which spends to the pubkey again
+ filter.insert(ParseHex("044a656f065871a353f216ca26cef8dde2f03e8c16202d2e8ad769f02032cb86a5eb5e56842e92e19141d60a01928f8dd2c875a390f67c1f6c94cfc617c0ea45af"));
+
+ merkleBlock = CMerkleBlock(block, filter);
+ BOOST_CHECK(merkleBlock.header.GetHash() == block.GetHash());
+
+ BOOST_CHECK(merkleBlock.vMatchedTxn.size() == 4);
+
+ BOOST_CHECK(pair == merkleBlock.vMatchedTxn[0]);
+
+ BOOST_CHECK(merkleBlock.vMatchedTxn[1].second == uint256("0x28204cad1d7fc1d199e8ef4fa22f182de6258a3eaafe1bbe56ebdcacd3069a5f"));
+ BOOST_CHECK(merkleBlock.vMatchedTxn[1].first == 1);
+
+ BOOST_CHECK(merkleBlock.vMatchedTxn[2].second == uint256("0x6b0f8a73a56c04b519f1883e8aafda643ba61a30bd1439969df21bea5f4e27e2"));
+ BOOST_CHECK(merkleBlock.vMatchedTxn[2].first == 2);
+
+ BOOST_CHECK(merkleBlock.vMatchedTxn[3].second == uint256("0x3c1d7e82342158e4109df2e0b6348b6e84e403d8b4046d7007663ace63cddb23"));
+ BOOST_CHECK(merkleBlock.vMatchedTxn[3].first == 3);
+
+ BOOST_CHECK(merkleBlock.txn.ExtractMatches(vMatched) == block.hashMerkleRoot);
+ BOOST_CHECK(vMatched.size() == merkleBlock.vMatchedTxn.size());
+ for (unsigned int i = 0; i < vMatched.size(); i++)
+ BOOST_CHECK(vMatched[i] == merkleBlock.vMatchedTxn[i].second);
+}
+
+BOOST_AUTO_TEST_CASE(merkle_block_2_with_update_none)
+{
+ // Random real block (000000005a4ded781e667e06ceefafb71410b511fe0d5adc3e5a27ecbec34ae6)
+ // With 4 txes
+ CBlock block;
+ CDataStream stream(ParseHex("0100000075616236cc2126035fadb38deb65b9102cc2c41c09cdf29fc051906800000000fe7d5e12ef0ff901f6050211249919b1c0653771832b3a80c66cea42847f0ae1d4d26e49ffff001d00f0a4410401000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0804ffff001d029105ffffffff0100f2052a010000004341046d8709a041d34357697dfcb30a9d05900a6294078012bf3bb09c6f9b525f1d16d5503d7905db1ada9501446ea00728668fc5719aa80be2fdfc8a858a4dbdd4fbac00000000010000000255605dc6f5c3dc148b6da58442b0b2cd422be385eab2ebea4119ee9c268d28350000000049483045022100aa46504baa86df8a33b1192b1b9367b4d729dc41e389f2c04f3e5c7f0559aae702205e82253a54bf5c4f65b7428551554b2045167d6d206dfe6a2e198127d3f7df1501ffffffff55605dc6f5c3dc148b6da58442b0b2cd422be385eab2ebea4119ee9c268d2835010000004847304402202329484c35fa9d6bb32a55a70c0982f606ce0e3634b69006138683bcd12cbb6602200c28feb1e2555c3210f1dddb299738b4ff8bbe9667b68cb8764b5ac17b7adf0001ffffffff0200e1f505000000004341046a0765b5865641ce08dd39690aade26dfbf5511430ca428a3089261361cef170e3929a68aee3d8d4848b0c5111b0a37b82b86ad559fd2a745b44d8e8d9dfdc0cac00180d8f000000004341044a656f065871a353f216ca26cef8dde2f03e8c16202d2e8ad769f02032cb86a5eb5e56842e92e19141d60a01928f8dd2c875a390f67c1f6c94cfc617c0ea45afac0000000001000000025f9a06d3acdceb56be1bfeaa3e8a25e62d182fa24fefe899d1c17f1dad4c2028000000004847304402205d6058484157235b06028c30736c15613a28bdb768ee628094ca8b0030d4d6eb0220328789c9a2ec27ddaec0ad5ef58efded42e6ea17c2e1ce838f3d6913f5e95db601ffffffff5f9a06d3acdceb56be1bfeaa3e8a25e62d182fa24fefe899d1c17f1dad4c2028010000004a493046022100c45af050d3cea806cedd0ab22520c53ebe63b987b8954146cdca42487b84bdd6022100b9b027716a6b59e640da50a864d6dd8a0ef24c76ce62391fa3eabaf4d2886d2d01ffffffff0200e1f505000000004341046a0765b5865641ce08dd39690aade26dfbf5511430ca428a3089261361cef170e3929a68aee3d8d4848b0c5111b0a37b82b86ad559fd2a745b44d8e8d9dfdc0cac00180d8f000000004341046a0765b5865641ce08dd39690aade26dfbf5511430ca428a3089261361cef170e3929a68aee3d8d4848b0c5111b0a37b82b86ad559fd2a745b44d8e8d9dfdc0cac000000000100000002e2274e5fea1bf29d963914bd301aa63b64daaf8a3e88f119b5046ca5738a0f6b0000000048473044022016e7a727a061ea2254a6c358376aaa617ac537eb836c77d646ebda4c748aac8b0220192ce28bf9f2c06a6467e6531e27648d2b3e2e2bae85159c9242939840295ba501ffffffffe2274e5fea1bf29d963914bd301aa63b64daaf8a3e88f119b5046ca5738a0f6b010000004a493046022100b7a1a755588d4190118936e15cd217d133b0e4a53c3c15924010d5648d8925c9022100aaef031874db2114f2d869ac2de4ae53908fbfea5b2b1862e181626bb9005c9f01ffffffff0200e1f505000000004341044a656f065871a353f216ca26cef8dde2f03e8c16202d2e8ad769f02032cb86a5eb5e56842e92e19141d60a01928f8dd2c875a390f67c1f6c94cfc617c0ea45afac00180d8f000000004341046a0765b5865641ce08dd39690aade26dfbf5511430ca428a3089261361cef170e3929a68aee3d8d4848b0c5111b0a37b82b86ad559fd2a745b44d8e8d9dfdc0cac00000000"), SER_NETWORK, PROTOCOL_VERSION);
+ stream >> block;
+
+ CBloomFilter filter(10, 0.000001, 0, BLOOM_UPDATE_NONE);
+ // Match the first transaction
+ filter.insert(uint256("0xe980fe9f792d014e73b95203dc1335c5f9ce19ac537a419e6df5b47aecb93b70"));
+
+ CMerkleBlock merkleBlock(block, filter);
+ BOOST_CHECK(merkleBlock.header.GetHash() == block.GetHash());
+
+ BOOST_CHECK(merkleBlock.vMatchedTxn.size() == 1);
+ pair<unsigned int, uint256> pair = merkleBlock.vMatchedTxn[0];
+
+ BOOST_CHECK(merkleBlock.vMatchedTxn[0].second == uint256("0xe980fe9f792d014e73b95203dc1335c5f9ce19ac537a419e6df5b47aecb93b70"));
+ BOOST_CHECK(merkleBlock.vMatchedTxn[0].first == 0);
+
+ vector<uint256> vMatched;
+ BOOST_CHECK(merkleBlock.txn.ExtractMatches(vMatched) == block.hashMerkleRoot);
+ BOOST_CHECK(vMatched.size() == merkleBlock.vMatchedTxn.size());
+ for (unsigned int i = 0; i < vMatched.size(); i++)
+ BOOST_CHECK(vMatched[i] == merkleBlock.vMatchedTxn[i].second);
+
+ // Match an output from the second transaction (the pubkey for address 1DZTzaBHUDM7T3QvUKBz4qXMRpkg8jsfB5)
+ // This should not match the third transaction though it spends the output matched
+ // It will match the fourth transaction, which has another pay-to-pubkey output to the same address
+ filter.insert(ParseHex("044a656f065871a353f216ca26cef8dde2f03e8c16202d2e8ad769f02032cb86a5eb5e56842e92e19141d60a01928f8dd2c875a390f67c1f6c94cfc617c0ea45af"));
+
+ merkleBlock = CMerkleBlock(block, filter);
+ BOOST_CHECK(merkleBlock.header.GetHash() == block.GetHash());
+
+ BOOST_CHECK(merkleBlock.vMatchedTxn.size() == 3);
+
+ BOOST_CHECK(pair == merkleBlock.vMatchedTxn[0]);
+
+ BOOST_CHECK(merkleBlock.vMatchedTxn[1].second == uint256("0x28204cad1d7fc1d199e8ef4fa22f182de6258a3eaafe1bbe56ebdcacd3069a5f"));
+ BOOST_CHECK(merkleBlock.vMatchedTxn[1].first == 1);
+
+ BOOST_CHECK(merkleBlock.vMatchedTxn[2].second == uint256("0x3c1d7e82342158e4109df2e0b6348b6e84e403d8b4046d7007663ace63cddb23"));
+ BOOST_CHECK(merkleBlock.vMatchedTxn[2].first == 3);
+
+ BOOST_CHECK(merkleBlock.txn.ExtractMatches(vMatched) == block.hashMerkleRoot);
+ BOOST_CHECK(vMatched.size() == merkleBlock.vMatchedTxn.size());
+ for (unsigned int i = 0; i < vMatched.size(); i++)
+ BOOST_CHECK(vMatched[i] == merkleBlock.vMatchedTxn[i].second);
+}
+
+BOOST_AUTO_TEST_CASE(merkle_block_3_and_serialize)
+{
+ // Random real block (000000000000dab0130bbcc991d3d7ae6b81aa6f50a798888dfe62337458dc45)
+ // With one tx
+ CBlock block;
+ CDataStream stream(ParseHex("0100000079cda856b143d9db2c1caff01d1aecc8630d30625d10e8b4b8b0000000000000b50cc069d6a3e33e3ff84a5c41d9d3febe7c770fdcc96b2c3ff60abe184f196367291b4d4c86041b8fa45d630101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff08044c86041b020a02ffffffff0100f2052a01000000434104ecd3229b0571c3be876feaac0442a9f13c5a572742927af1dc623353ecf8c202225f64868137a18cdd85cbbb4c74fbccfd4f49639cf1bdc94a5672bb15ad5d4cac00000000"), SER_NETWORK, PROTOCOL_VERSION);
+ stream >> block;
+
+ CBloomFilter filter(10, 0.000001, 0, BLOOM_UPDATE_ALL);
+ // Match the only transaction
+ filter.insert(uint256("0x63194f18be0af63f2c6bc9dc0f777cbefed3d9415c4af83f3ee3a3d669c00cb5"));
+
+ CMerkleBlock merkleBlock(block, filter);
+ BOOST_CHECK(merkleBlock.header.GetHash() == block.GetHash());
+
+ BOOST_CHECK(merkleBlock.vMatchedTxn.size() == 1);
+
+ BOOST_CHECK(merkleBlock.vMatchedTxn[0].second == uint256("0x63194f18be0af63f2c6bc9dc0f777cbefed3d9415c4af83f3ee3a3d669c00cb5"));
+ BOOST_CHECK(merkleBlock.vMatchedTxn[0].first == 0);
+
+ vector<uint256> vMatched;
+ BOOST_CHECK(merkleBlock.txn.ExtractMatches(vMatched) == block.hashMerkleRoot);
+ BOOST_CHECK(vMatched.size() == merkleBlock.vMatchedTxn.size());
+ for (unsigned int i = 0; i < vMatched.size(); i++)
+ BOOST_CHECK(vMatched[i] == merkleBlock.vMatchedTxn[i].second);
+
+ CDataStream merkleStream(SER_NETWORK, PROTOCOL_VERSION);
+ merkleStream << merkleBlock;
+
+ vector<unsigned char> vch = ParseHex("0100000079cda856b143d9db2c1caff01d1aecc8630d30625d10e8b4b8b0000000000000b50cc069d6a3e33e3ff84a5c41d9d3febe7c770fdcc96b2c3ff60abe184f196367291b4d4c86041b8fa45d630100000001b50cc069d6a3e33e3ff84a5c41d9d3febe7c770fdcc96b2c3ff60abe184f19630101");
+ vector<char> expected(vch.size());
+
+ for (unsigned int i = 0; i < vch.size(); i++)
+ expected[i] = (char)vch[i];
+
+ BOOST_CHECK_EQUAL_COLLECTIONS(expected.begin(), expected.end(), merkleStream.begin(), merkleStream.end());
+}
+
+BOOST_AUTO_TEST_CASE(merkle_block_4)
+{
+ // Random real block (000000000000b731f2eef9e8c63173adfb07e41bd53eb0ef0a6b720d6cb6dea4)
+ // With 7 txes
+ CBlock block;
+ CDataStream stream(ParseHex("0100000082bb869cf3a793432a66e826e05a6fc37469f8efb7421dc880670100000000007f16c5962e8bd963659c793ce370d95f093bc7e367117b3c30c1f8fdd0d9728776381b4d4c86041b554b85290701000000010000000000000000000000000000000000000000000000000000000000000000ffffffff07044c86041b0136ffffffff0100f2052a01000000434104eaafc2314def4ca98ac970241bcab022b9c1e1f4ea423a20f134c876f2c01ec0f0dd5b2e86e7168cefe0d81113c3807420ce13ad1357231a2252247d97a46a91ac000000000100000001bcad20a6a29827d1424f08989255120bf7f3e9e3cdaaa6bb31b0737fe048724300000000494830450220356e834b046cadc0f8ebb5a8a017b02de59c86305403dad52cd77b55af062ea10221009253cd6c119d4729b77c978e1e2aa19f5ea6e0e52b3f16e32fa608cd5bab753901ffffffff02008d380c010000001976a9142b4b8072ecbba129b6453c63e129e643207249ca88ac0065cd1d000000001976a9141b8dd13b994bcfc787b32aeadf58ccb3615cbd5488ac000000000100000003fdacf9b3eb077412e7a968d2e4f11b9a9dee312d666187ed77ee7d26af16cb0b000000008c493046022100ea1608e70911ca0de5af51ba57ad23b9a51db8d28f82c53563c56a05c20f5a87022100a8bdc8b4a8acc8634c6b420410150775eb7f2474f5615f7fccd65af30f310fbf01410465fdf49e29b06b9a1582287b6279014f834edc317695d125ef623c1cc3aaece245bd69fcad7508666e9c74a49dc9056d5fc14338ef38118dc4afae5fe2c585caffffffff309e1913634ecb50f3c4f83e96e70b2df071b497b8973a3e75429df397b5af83000000004948304502202bdb79c596a9ffc24e96f4386199aba386e9bc7b6071516e2b51dda942b3a1ed022100c53a857e76b724fc14d45311eac5019650d415c3abb5428f3aae16d8e69bec2301ffffffff2089e33491695080c9edc18a428f7d834db5b6d372df13ce2b1b0e0cbcb1e6c10000000049483045022100d4ce67c5896ee251c810ac1ff9ceccd328b497c8f553ab6e08431e7d40bad6b5022033119c0c2b7d792d31f1187779c7bd95aefd93d90a715586d73801d9b47471c601ffffffff0100714460030000001976a914c7b55141d097ea5df7a0ed330cf794376e53ec8d88ac0000000001000000045bf0e214aa4069a3e792ecee1e1bf0c1d397cde8dd08138f4b72a00681743447000000008b48304502200c45de8c4f3e2c1821f2fc878cba97b1e6f8807d94930713aa1c86a67b9bf1e40221008581abfef2e30f957815fc89978423746b2086375ca8ecf359c85c2a5b7c88ad01410462bb73f76ca0994fcb8b4271e6fb7561f5c0f9ca0cf6485261c4a0dc894f4ab844c6cdfb97cd0b60ffb5018ffd6238f4d87270efb1d3ae37079b794a92d7ec95ffffffffd669f7d7958d40fc59d2253d88e0f248e29b599c80bbcec344a83dda5f9aa72c000000008a473044022078124c8beeaa825f9e0b30bff96e564dd859432f2d0cb3b72d3d5d93d38d7e930220691d233b6c0f995be5acb03d70a7f7a65b6bc9bdd426260f38a1346669507a3601410462bb73f76ca0994fcb8b4271e6fb7561f5c0f9ca0cf6485261c4a0dc894f4ab844c6cdfb97cd0b60ffb5018ffd6238f4d87270efb1d3ae37079b794a92d7ec95fffffffff878af0d93f5229a68166cf051fd372bb7a537232946e0a46f53636b4dafdaa4000000008c493046022100c717d1714551663f69c3c5759bdbb3a0fcd3fab023abc0e522fe6440de35d8290221008d9cbe25bffc44af2b18e81c58eb37293fd7fe1c2e7b46fc37ee8c96c50ab1e201410462bb73f76ca0994fcb8b4271e6fb7561f5c0f9ca0cf6485261c4a0dc894f4ab844c6cdfb97cd0b60ffb5018ffd6238f4d87270efb1d3ae37079b794a92d7ec95ffffffff27f2b668859cd7f2f894aa0fd2d9e60963bcd07c88973f425f999b8cbfd7a1e2000000008c493046022100e00847147cbf517bcc2f502f3ddc6d284358d102ed20d47a8aa788a62f0db780022100d17b2d6fa84dcaf1c95d88d7e7c30385aecf415588d749afd3ec81f6022cecd701410462bb73f76ca0994fcb8b4271e6fb7561f5c0f9ca0cf6485261c4a0dc894f4ab844c6cdfb97cd0b60ffb5018ffd6238f4d87270efb1d3ae37079b794a92d7ec95ffffffff0100c817a8040000001976a914b6efd80d99179f4f4ff6f4dd0a007d018c385d2188ac000000000100000001834537b2f1ce8ef9373a258e10545ce5a50b758df616cd4356e0032554ebd3c4000000008b483045022100e68f422dd7c34fdce11eeb4509ddae38201773dd62f284e8aa9d96f85099d0b002202243bd399ff96b649a0fad05fa759d6a882f0af8c90cf7632c2840c29070aec20141045e58067e815c2f464c6a2a15f987758374203895710c2d452442e28496ff38ba8f5fd901dc20e29e88477167fe4fc299bf818fd0d9e1632d467b2a3d9503b1aaffffffff0280d7e636030000001976a914f34c3e10eb387efe872acb614c89e78bfca7815d88ac404b4c00000000001976a914a84e272933aaf87e1715d7786c51dfaeb5b65a6f88ac00000000010000000143ac81c8e6f6ef307dfe17f3d906d999e23e0189fda838c5510d850927e03ae7000000008c4930460221009c87c344760a64cb8ae6685a3eec2c1ac1bed5b88c87de51acd0e124f266c16602210082d07c037359c3a257b5c63ebd90f5a5edf97b2ac1c434b08ca998839f346dd40141040ba7e521fa7946d12edbb1d1e95a15c34bd4398195e86433c92b431cd315f455fe30032ede69cad9d1e1ed6c3c4ec0dbfced53438c625462afb792dcb098544bffffffff0240420f00000000001976a9144676d1b820d63ec272f1900d59d43bc6463d96f888ac40420f00000000001976a914648d04341d00d7968b3405c034adc38d4d8fb9bd88ac00000000010000000248cc917501ea5c55f4a8d2009c0567c40cfe037c2e71af017d0a452ff705e3f1000000008b483045022100bf5fdc86dc5f08a5d5c8e43a8c9d5b1ed8c65562e280007b52b133021acd9acc02205e325d613e555f772802bf413d36ba807892ed1a690a77811d3033b3de226e0a01410429fa713b124484cb2bd7b5557b2c0b9df7b2b1fee61825eadc5ae6c37a9920d38bfccdc7dc3cb0c47d7b173dbc9db8d37db0a33ae487982c59c6f8606e9d1791ffffffff41ed70551dd7e841883ab8f0b16bf04176b7d1480e4f0af9f3d4c3595768d068000000008b4830450221008513ad65187b903aed1102d1d0c47688127658c51106753fed0151ce9c16b80902201432b9ebcb87bd04ceb2de66035fbbaf4bf8b00d1cfe41f1a1f7338f9ad79d210141049d4cf80125bf50be1709f718c07ad15d0fc612b7da1f5570dddc35f2a352f0f27c978b06820edca9ef982c35fda2d255afba340068c5035552368bc7200c1488ffffffff0100093d00000000001976a9148edb68822f1ad580b043c7b3df2e400f8699eb4888ac00000000"), SER_NETWORK, PROTOCOL_VERSION);
+ stream >> block;
+
+ CBloomFilter filter(10, 0.000001, 0, BLOOM_UPDATE_ALL);
+ // Match the last transaction
+ filter.insert(uint256("0x0a2a92f0bda4727d0a13eaddf4dd9ac6b5c61a1429e6b2b818f19b15df0ac154"));
+
+ CMerkleBlock merkleBlock(block, filter);
+ BOOST_CHECK(merkleBlock.header.GetHash() == block.GetHash());
+
+ BOOST_CHECK(merkleBlock.vMatchedTxn.size() == 1);
+ pair<unsigned int, uint256> pair = merkleBlock.vMatchedTxn[0];
+
+ BOOST_CHECK(merkleBlock.vMatchedTxn[0].second == uint256("0x0a2a92f0bda4727d0a13eaddf4dd9ac6b5c61a1429e6b2b818f19b15df0ac154"));
+ BOOST_CHECK(merkleBlock.vMatchedTxn[0].first == 6);
+
+ vector<uint256> vMatched;
+ BOOST_CHECK(merkleBlock.txn.ExtractMatches(vMatched) == block.hashMerkleRoot);
+ BOOST_CHECK(vMatched.size() == merkleBlock.vMatchedTxn.size());
+ for (unsigned int i = 0; i < vMatched.size(); i++)
+ BOOST_CHECK(vMatched[i] == merkleBlock.vMatchedTxn[i].second);
+
+ // Also match the 4th transaction
+ filter.insert(uint256("0x02981fa052f0481dbc5868f4fc2166035a10f27a03cfd2de67326471df5bc041"));
+ merkleBlock = CMerkleBlock(block, filter);
+ BOOST_CHECK(merkleBlock.header.GetHash() == block.GetHash());
+
+ BOOST_CHECK(merkleBlock.vMatchedTxn.size() == 2);
+
+ BOOST_CHECK(merkleBlock.vMatchedTxn[0].second == uint256("0x02981fa052f0481dbc5868f4fc2166035a10f27a03cfd2de67326471df5bc041"));
+ BOOST_CHECK(merkleBlock.vMatchedTxn[0].first == 3);
+
+ BOOST_CHECK(merkleBlock.vMatchedTxn[1] == pair);
+
+ BOOST_CHECK(merkleBlock.txn.ExtractMatches(vMatched) == block.hashMerkleRoot);
+ BOOST_CHECK(vMatched.size() == merkleBlock.vMatchedTxn.size());
+ for (unsigned int i = 0; i < vMatched.size(); i++)
+ BOOST_CHECK(vMatched[i] == merkleBlock.vMatchedTxn[i].second);
+}
+
+BOOST_AUTO_TEST_CASE(merkle_block_4_test_p2pubkey_only)
+{
+ // Random real block (000000000000b731f2eef9e8c63173adfb07e41bd53eb0ef0a6b720d6cb6dea4)
+ // With 7 txes
+ CBlock block;
+ CDataStream stream(ParseHex("0100000082bb869cf3a793432a66e826e05a6fc37469f8efb7421dc880670100000000007f16c5962e8bd963659c793ce370d95f093bc7e367117b3c30c1f8fdd0d9728776381b4d4c86041b554b85290701000000010000000000000000000000000000000000000000000000000000000000000000ffffffff07044c86041b0136ffffffff0100f2052a01000000434104eaafc2314def4ca98ac970241bcab022b9c1e1f4ea423a20f134c876f2c01ec0f0dd5b2e86e7168cefe0d81113c3807420ce13ad1357231a2252247d97a46a91ac000000000100000001bcad20a6a29827d1424f08989255120bf7f3e9e3cdaaa6bb31b0737fe048724300000000494830450220356e834b046cadc0f8ebb5a8a017b02de59c86305403dad52cd77b55af062ea10221009253cd6c119d4729b77c978e1e2aa19f5ea6e0e52b3f16e32fa608cd5bab753901ffffffff02008d380c010000001976a9142b4b8072ecbba129b6453c63e129e643207249ca88ac0065cd1d000000001976a9141b8dd13b994bcfc787b32aeadf58ccb3615cbd5488ac000000000100000003fdacf9b3eb077412e7a968d2e4f11b9a9dee312d666187ed77ee7d26af16cb0b000000008c493046022100ea1608e70911ca0de5af51ba57ad23b9a51db8d28f82c53563c56a05c20f5a87022100a8bdc8b4a8acc8634c6b420410150775eb7f2474f5615f7fccd65af30f310fbf01410465fdf49e29b06b9a1582287b6279014f834edc317695d125ef623c1cc3aaece245bd69fcad7508666e9c74a49dc9056d5fc14338ef38118dc4afae5fe2c585caffffffff309e1913634ecb50f3c4f83e96e70b2df071b497b8973a3e75429df397b5af83000000004948304502202bdb79c596a9ffc24e96f4386199aba386e9bc7b6071516e2b51dda942b3a1ed022100c53a857e76b724fc14d45311eac5019650d415c3abb5428f3aae16d8e69bec2301ffffffff2089e33491695080c9edc18a428f7d834db5b6d372df13ce2b1b0e0cbcb1e6c10000000049483045022100d4ce67c5896ee251c810ac1ff9ceccd328b497c8f553ab6e08431e7d40bad6b5022033119c0c2b7d792d31f1187779c7bd95aefd93d90a715586d73801d9b47471c601ffffffff0100714460030000001976a914c7b55141d097ea5df7a0ed330cf794376e53ec8d88ac0000000001000000045bf0e214aa4069a3e792ecee1e1bf0c1d397cde8dd08138f4b72a00681743447000000008b48304502200c45de8c4f3e2c1821f2fc878cba97b1e6f8807d94930713aa1c86a67b9bf1e40221008581abfef2e30f957815fc89978423746b2086375ca8ecf359c85c2a5b7c88ad01410462bb73f76ca0994fcb8b4271e6fb7561f5c0f9ca0cf6485261c4a0dc894f4ab844c6cdfb97cd0b60ffb5018ffd6238f4d87270efb1d3ae37079b794a92d7ec95ffffffffd669f7d7958d40fc59d2253d88e0f248e29b599c80bbcec344a83dda5f9aa72c000000008a473044022078124c8beeaa825f9e0b30bff96e564dd859432f2d0cb3b72d3d5d93d38d7e930220691d233b6c0f995be5acb03d70a7f7a65b6bc9bdd426260f38a1346669507a3601410462bb73f76ca0994fcb8b4271e6fb7561f5c0f9ca0cf6485261c4a0dc894f4ab844c6cdfb97cd0b60ffb5018ffd6238f4d87270efb1d3ae37079b794a92d7ec95fffffffff878af0d93f5229a68166cf051fd372bb7a537232946e0a46f53636b4dafdaa4000000008c493046022100c717d1714551663f69c3c5759bdbb3a0fcd3fab023abc0e522fe6440de35d8290221008d9cbe25bffc44af2b18e81c58eb37293fd7fe1c2e7b46fc37ee8c96c50ab1e201410462bb73f76ca0994fcb8b4271e6fb7561f5c0f9ca0cf6485261c4a0dc894f4ab844c6cdfb97cd0b60ffb5018ffd6238f4d87270efb1d3ae37079b794a92d7ec95ffffffff27f2b668859cd7f2f894aa0fd2d9e60963bcd07c88973f425f999b8cbfd7a1e2000000008c493046022100e00847147cbf517bcc2f502f3ddc6d284358d102ed20d47a8aa788a62f0db780022100d17b2d6fa84dcaf1c95d88d7e7c30385aecf415588d749afd3ec81f6022cecd701410462bb73f76ca0994fcb8b4271e6fb7561f5c0f9ca0cf6485261c4a0dc894f4ab844c6cdfb97cd0b60ffb5018ffd6238f4d87270efb1d3ae37079b794a92d7ec95ffffffff0100c817a8040000001976a914b6efd80d99179f4f4ff6f4dd0a007d018c385d2188ac000000000100000001834537b2f1ce8ef9373a258e10545ce5a50b758df616cd4356e0032554ebd3c4000000008b483045022100e68f422dd7c34fdce11eeb4509ddae38201773dd62f284e8aa9d96f85099d0b002202243bd399ff96b649a0fad05fa759d6a882f0af8c90cf7632c2840c29070aec20141045e58067e815c2f464c6a2a15f987758374203895710c2d452442e28496ff38ba8f5fd901dc20e29e88477167fe4fc299bf818fd0d9e1632d467b2a3d9503b1aaffffffff0280d7e636030000001976a914f34c3e10eb387efe872acb614c89e78bfca7815d88ac404b4c00000000001976a914a84e272933aaf87e1715d7786c51dfaeb5b65a6f88ac00000000010000000143ac81c8e6f6ef307dfe17f3d906d999e23e0189fda838c5510d850927e03ae7000000008c4930460221009c87c344760a64cb8ae6685a3eec2c1ac1bed5b88c87de51acd0e124f266c16602210082d07c037359c3a257b5c63ebd90f5a5edf97b2ac1c434b08ca998839f346dd40141040ba7e521fa7946d12edbb1d1e95a15c34bd4398195e86433c92b431cd315f455fe30032ede69cad9d1e1ed6c3c4ec0dbfced53438c625462afb792dcb098544bffffffff0240420f00000000001976a9144676d1b820d63ec272f1900d59d43bc6463d96f888ac40420f00000000001976a914648d04341d00d7968b3405c034adc38d4d8fb9bd88ac00000000010000000248cc917501ea5c55f4a8d2009c0567c40cfe037c2e71af017d0a452ff705e3f1000000008b483045022100bf5fdc86dc5f08a5d5c8e43a8c9d5b1ed8c65562e280007b52b133021acd9acc02205e325d613e555f772802bf413d36ba807892ed1a690a77811d3033b3de226e0a01410429fa713b124484cb2bd7b5557b2c0b9df7b2b1fee61825eadc5ae6c37a9920d38bfccdc7dc3cb0c47d7b173dbc9db8d37db0a33ae487982c59c6f8606e9d1791ffffffff41ed70551dd7e841883ab8f0b16bf04176b7d1480e4f0af9f3d4c3595768d068000000008b4830450221008513ad65187b903aed1102d1d0c47688127658c51106753fed0151ce9c16b80902201432b9ebcb87bd04ceb2de66035fbbaf4bf8b00d1cfe41f1a1f7338f9ad79d210141049d4cf80125bf50be1709f718c07ad15d0fc612b7da1f5570dddc35f2a352f0f27c978b06820edca9ef982c35fda2d255afba340068c5035552368bc7200c1488ffffffff0100093d00000000001976a9148edb68822f1ad580b043c7b3df2e400f8699eb4888ac00000000"), SER_NETWORK, PROTOCOL_VERSION);
+ stream >> block;
+
+ CBloomFilter filter(10, 0.000001, 0, BLOOM_UPDATE_P2PUBKEY_ONLY);
+ // Match the generation pubkey
+ filter.insert(ParseHex("04eaafc2314def4ca98ac970241bcab022b9c1e1f4ea423a20f134c876f2c01ec0f0dd5b2e86e7168cefe0d81113c3807420ce13ad1357231a2252247d97a46a91"));
+ // ...and the output address of the 4th transaction
+ filter.insert(ParseHex("b6efd80d99179f4f4ff6f4dd0a007d018c385d21"));
+
+ CMerkleBlock merkleBlock(block, filter);
+ BOOST_CHECK(merkleBlock.header.GetHash() == block.GetHash());
+
+ // We should match the generation outpoint
+ BOOST_CHECK(filter.contains(COutPoint(uint256("0x147caa76786596590baa4e98f5d9f48b86c7765e489f7a6ff3360fe5c674360b"), 0)));
+ // ... but not the 4th transaction's output (its not pay-2-pubkey)
+ BOOST_CHECK(!filter.contains(COutPoint(uint256("0x02981fa052f0481dbc5868f4fc2166035a10f27a03cfd2de67326471df5bc041"), 0)));
+}
+
+BOOST_AUTO_TEST_CASE(merkle_block_4_test_update_none)
+{
+ // Random real block (000000000000b731f2eef9e8c63173adfb07e41bd53eb0ef0a6b720d6cb6dea4)
+ // With 7 txes
+ CBlock block;
+ CDataStream stream(ParseHex("0100000082bb869cf3a793432a66e826e05a6fc37469f8efb7421dc880670100000000007f16c5962e8bd963659c793ce370d95f093bc7e367117b3c30c1f8fdd0d9728776381b4d4c86041b554b85290701000000010000000000000000000000000000000000000000000000000000000000000000ffffffff07044c86041b0136ffffffff0100f2052a01000000434104eaafc2314def4ca98ac970241bcab022b9c1e1f4ea423a20f134c876f2c01ec0f0dd5b2e86e7168cefe0d81113c3807420ce13ad1357231a2252247d97a46a91ac000000000100000001bcad20a6a29827d1424f08989255120bf7f3e9e3cdaaa6bb31b0737fe048724300000000494830450220356e834b046cadc0f8ebb5a8a017b02de59c86305403dad52cd77b55af062ea10221009253cd6c119d4729b77c978e1e2aa19f5ea6e0e52b3f16e32fa608cd5bab753901ffffffff02008d380c010000001976a9142b4b8072ecbba129b6453c63e129e643207249ca88ac0065cd1d000000001976a9141b8dd13b994bcfc787b32aeadf58ccb3615cbd5488ac000000000100000003fdacf9b3eb077412e7a968d2e4f11b9a9dee312d666187ed77ee7d26af16cb0b000000008c493046022100ea1608e70911ca0de5af51ba57ad23b9a51db8d28f82c53563c56a05c20f5a87022100a8bdc8b4a8acc8634c6b420410150775eb7f2474f5615f7fccd65af30f310fbf01410465fdf49e29b06b9a1582287b6279014f834edc317695d125ef623c1cc3aaece245bd69fcad7508666e9c74a49dc9056d5fc14338ef38118dc4afae5fe2c585caffffffff309e1913634ecb50f3c4f83e96e70b2df071b497b8973a3e75429df397b5af83000000004948304502202bdb79c596a9ffc24e96f4386199aba386e9bc7b6071516e2b51dda942b3a1ed022100c53a857e76b724fc14d45311eac5019650d415c3abb5428f3aae16d8e69bec2301ffffffff2089e33491695080c9edc18a428f7d834db5b6d372df13ce2b1b0e0cbcb1e6c10000000049483045022100d4ce67c5896ee251c810ac1ff9ceccd328b497c8f553ab6e08431e7d40bad6b5022033119c0c2b7d792d31f1187779c7bd95aefd93d90a715586d73801d9b47471c601ffffffff0100714460030000001976a914c7b55141d097ea5df7a0ed330cf794376e53ec8d88ac0000000001000000045bf0e214aa4069a3e792ecee1e1bf0c1d397cde8dd08138f4b72a00681743447000000008b48304502200c45de8c4f3e2c1821f2fc878cba97b1e6f8807d94930713aa1c86a67b9bf1e40221008581abfef2e30f957815fc89978423746b2086375ca8ecf359c85c2a5b7c88ad01410462bb73f76ca0994fcb8b4271e6fb7561f5c0f9ca0cf6485261c4a0dc894f4ab844c6cdfb97cd0b60ffb5018ffd6238f4d87270efb1d3ae37079b794a92d7ec95ffffffffd669f7d7958d40fc59d2253d88e0f248e29b599c80bbcec344a83dda5f9aa72c000000008a473044022078124c8beeaa825f9e0b30bff96e564dd859432f2d0cb3b72d3d5d93d38d7e930220691d233b6c0f995be5acb03d70a7f7a65b6bc9bdd426260f38a1346669507a3601410462bb73f76ca0994fcb8b4271e6fb7561f5c0f9ca0cf6485261c4a0dc894f4ab844c6cdfb97cd0b60ffb5018ffd6238f4d87270efb1d3ae37079b794a92d7ec95fffffffff878af0d93f5229a68166cf051fd372bb7a537232946e0a46f53636b4dafdaa4000000008c493046022100c717d1714551663f69c3c5759bdbb3a0fcd3fab023abc0e522fe6440de35d8290221008d9cbe25bffc44af2b18e81c58eb37293fd7fe1c2e7b46fc37ee8c96c50ab1e201410462bb73f76ca0994fcb8b4271e6fb7561f5c0f9ca0cf6485261c4a0dc894f4ab844c6cdfb97cd0b60ffb5018ffd6238f4d87270efb1d3ae37079b794a92d7ec95ffffffff27f2b668859cd7f2f894aa0fd2d9e60963bcd07c88973f425f999b8cbfd7a1e2000000008c493046022100e00847147cbf517bcc2f502f3ddc6d284358d102ed20d47a8aa788a62f0db780022100d17b2d6fa84dcaf1c95d88d7e7c30385aecf415588d749afd3ec81f6022cecd701410462bb73f76ca0994fcb8b4271e6fb7561f5c0f9ca0cf6485261c4a0dc894f4ab844c6cdfb97cd0b60ffb5018ffd6238f4d87270efb1d3ae37079b794a92d7ec95ffffffff0100c817a8040000001976a914b6efd80d99179f4f4ff6f4dd0a007d018c385d2188ac000000000100000001834537b2f1ce8ef9373a258e10545ce5a50b758df616cd4356e0032554ebd3c4000000008b483045022100e68f422dd7c34fdce11eeb4509ddae38201773dd62f284e8aa9d96f85099d0b002202243bd399ff96b649a0fad05fa759d6a882f0af8c90cf7632c2840c29070aec20141045e58067e815c2f464c6a2a15f987758374203895710c2d452442e28496ff38ba8f5fd901dc20e29e88477167fe4fc299bf818fd0d9e1632d467b2a3d9503b1aaffffffff0280d7e636030000001976a914f34c3e10eb387efe872acb614c89e78bfca7815d88ac404b4c00000000001976a914a84e272933aaf87e1715d7786c51dfaeb5b65a6f88ac00000000010000000143ac81c8e6f6ef307dfe17f3d906d999e23e0189fda838c5510d850927e03ae7000000008c4930460221009c87c344760a64cb8ae6685a3eec2c1ac1bed5b88c87de51acd0e124f266c16602210082d07c037359c3a257b5c63ebd90f5a5edf97b2ac1c434b08ca998839f346dd40141040ba7e521fa7946d12edbb1d1e95a15c34bd4398195e86433c92b431cd315f455fe30032ede69cad9d1e1ed6c3c4ec0dbfced53438c625462afb792dcb098544bffffffff0240420f00000000001976a9144676d1b820d63ec272f1900d59d43bc6463d96f888ac40420f00000000001976a914648d04341d00d7968b3405c034adc38d4d8fb9bd88ac00000000010000000248cc917501ea5c55f4a8d2009c0567c40cfe037c2e71af017d0a452ff705e3f1000000008b483045022100bf5fdc86dc5f08a5d5c8e43a8c9d5b1ed8c65562e280007b52b133021acd9acc02205e325d613e555f772802bf413d36ba807892ed1a690a77811d3033b3de226e0a01410429fa713b124484cb2bd7b5557b2c0b9df7b2b1fee61825eadc5ae6c37a9920d38bfccdc7dc3cb0c47d7b173dbc9db8d37db0a33ae487982c59c6f8606e9d1791ffffffff41ed70551dd7e841883ab8f0b16bf04176b7d1480e4f0af9f3d4c3595768d068000000008b4830450221008513ad65187b903aed1102d1d0c47688127658c51106753fed0151ce9c16b80902201432b9ebcb87bd04ceb2de66035fbbaf4bf8b00d1cfe41f1a1f7338f9ad79d210141049d4cf80125bf50be1709f718c07ad15d0fc612b7da1f5570dddc35f2a352f0f27c978b06820edca9ef982c35fda2d255afba340068c5035552368bc7200c1488ffffffff0100093d00000000001976a9148edb68822f1ad580b043c7b3df2e400f8699eb4888ac00000000"), SER_NETWORK, PROTOCOL_VERSION);
+ stream >> block;
+
+ CBloomFilter filter(10, 0.000001, 0, BLOOM_UPDATE_NONE);
+ // Match the generation pubkey
+ filter.insert(ParseHex("04eaafc2314def4ca98ac970241bcab022b9c1e1f4ea423a20f134c876f2c01ec0f0dd5b2e86e7168cefe0d81113c3807420ce13ad1357231a2252247d97a46a91"));
+ // ...and the output address of the 4th transaction
+ filter.insert(ParseHex("b6efd80d99179f4f4ff6f4dd0a007d018c385d21"));
+
+ CMerkleBlock merkleBlock(block, filter);
+ BOOST_CHECK(merkleBlock.header.GetHash() == block.GetHash());
+
+ // We shouldn't match any outpoints (UPDATE_NONE)
+ BOOST_CHECK(!filter.contains(COutPoint(uint256("0x147caa76786596590baa4e98f5d9f48b86c7765e489f7a6ff3360fe5c674360b"), 0)));
+ BOOST_CHECK(!filter.contains(COutPoint(uint256("0x02981fa052f0481dbc5868f4fc2166035a10f27a03cfd2de67326471df5bc041"), 0)));
+}
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/pmt_tests.cpp b/src/test/pmt_tests.cpp
new file mode 100644
index 0000000000..cf09421617
--- /dev/null
+++ b/src/test/pmt_tests.cpp
@@ -0,0 +1,98 @@
+#include <boost/test/unit_test.hpp>
+
+#include "uint256.h"
+#include "main.h"
+
+using namespace std;
+
+class CPartialMerkleTreeTester : public CPartialMerkleTree
+{
+public:
+ // flip one bit in one of the hashes - this should break the authentication
+ void Damage() {
+ unsigned int n = rand() % vHash.size();
+ int bit = rand() % 256;
+ uint256 &hash = vHash[n];
+ hash ^= ((uint256)1 << bit);
+ }
+};
+
+BOOST_AUTO_TEST_SUITE(pmt_tests)
+
+BOOST_AUTO_TEST_CASE(pmt_test1)
+{
+ static const unsigned int nTxCounts[] = {1, 4, 7, 17, 56, 100, 127, 256, 312, 513, 1000, 4095};
+
+ for (int n = 0; n < 12; n++) {
+ unsigned int nTx = nTxCounts[n];
+
+ // build a block with some dummy transactions
+ CBlock block;
+ for (unsigned int j=0; j<nTx; j++) {
+ CTransaction tx;
+ tx.nLockTime = rand(); // actual transaction data doesn't matter; just make the nLockTime's unique
+ block.vtx.push_back(tx);
+ }
+
+ // calculate actual merkle root and height
+ uint256 merkleRoot1 = block.BuildMerkleTree();
+ std::vector<uint256> vTxid(nTx, 0);
+ for (unsigned int j=0; j<nTx; j++)
+ vTxid[j] = block.vtx[j].GetHash();
+ int nHeight = 1, nTx_ = nTx;
+ while (nTx_ > 1) {
+ nTx_ = (nTx_+1)/2;
+ nHeight++;
+ }
+
+ // check with random subsets with inclusion chances 1, 1/2, 1/4, ..., 1/128
+ for (int att = 1; att < 15; att++) {
+ // build random subset of txid's
+ std::vector<bool> vMatch(nTx, false);
+ std::vector<uint256> vMatchTxid1;
+ for (unsigned int j=0; j<nTx; j++) {
+ bool fInclude = (rand() & ((1 << (att/2)) - 1)) == 0;
+ vMatch[j] = fInclude;
+ if (fInclude)
+ vMatchTxid1.push_back(vTxid[j]);
+ }
+
+ // build the partial merkle tree
+ CPartialMerkleTree pmt1(vTxid, vMatch);
+
+ // serialize
+ CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
+ ss << pmt1;
+
+ // verify CPartialMerkleTree's size guarantees
+ unsigned int n = std::min<unsigned int>(nTx, 1 + vMatchTxid1.size()*nHeight);
+ BOOST_CHECK(ss.size() <= 10 + (258*n+7)/8);
+
+ // deserialize into a tester copy
+ CPartialMerkleTreeTester pmt2;
+ ss >> pmt2;
+
+ // extract merkle root and matched txids from copy
+ std::vector<uint256> vMatchTxid2;
+ uint256 merkleRoot2 = pmt2.ExtractMatches(vMatchTxid2);
+
+ // check that it has the same merkle root as the original, and a valid one
+ BOOST_CHECK(merkleRoot1 == merkleRoot2);
+ BOOST_CHECK(merkleRoot2 != 0);
+
+ // check that it contains the matched transactions (in the same order!)
+ BOOST_CHECK(vMatchTxid1 == vMatchTxid2);
+
+ // check that random bit flips break the authentication
+ for (int j=0; j<4; j++) {
+ CPartialMerkleTreeTester pmt3(pmt2);
+ pmt3.Damage();
+ std::vector<uint256> vMatchTxid3;
+ uint256 merkleRoot3 = pmt3.ExtractMatches(vMatchTxid3);
+ BOOST_CHECK(merkleRoot3 != merkleRoot1);
+ }
+ }
+ }
+}
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/test_bitcoin.cpp b/src/test/test_bitcoin.cpp
index b98816d53d..f75b762f1f 100644
--- a/src/test/test_bitcoin.cpp
+++ b/src/test/test_bitcoin.cpp
@@ -33,9 +33,13 @@ struct TestingSetup {
pwalletMain = new CWallet("wallet.dat");
pwalletMain->LoadWallet(fFirstRun);
RegisterWallet(pwalletMain);
+ nScriptCheckThreads = 3;
+ for (int i=0; i < nScriptCheckThreads-1; i++)
+ NewThread(ThreadScriptCheck, NULL);
}
~TestingSetup()
{
+ ThreadScriptCheckQuit();
delete pwalletMain;
pwalletMain = NULL;
delete pcoinsTip;
diff --git a/src/uint256.h b/src/uint256.h
index abd0b71e6f..eb0066fa27 100644
--- a/src/uint256.h
+++ b/src/uint256.h
@@ -344,7 +344,17 @@ public:
return (unsigned char*)&pn[WIDTH];
}
- unsigned int size()
+ const unsigned char* begin() const
+ {
+ return (unsigned char*)&pn[0];
+ }
+
+ const unsigned char* end() const
+ {
+ return (unsigned char*)&pn[WIDTH];
+ }
+
+ unsigned int size() const
{
return sizeof(pn);
}
diff --git a/src/util.cpp b/src/util.cpp
index 576ba50dbb..d8f05cb9fd 100644
--- a/src/util.cpp
+++ b/src/util.cpp
@@ -74,7 +74,7 @@ bool fTestNet = false;
bool fNoListen = false;
bool fLogTimestamps = false;
CMedianFilter<int64> vTimeOffsets(200,0);
-bool fReopenDebugLog = false;
+volatile bool fReopenDebugLog = false;
// Init OpenSSL library multithreading support
static CCriticalSection** ppmutexOpenSSL;
@@ -195,62 +195,76 @@ uint256 GetRandHash()
+//
+// OutputDebugStringF (aka printf -- there is a #define that we really
+// should get rid of one day) has been broken a couple of times now
+// by well-meaning people adding mutexes in the most straightforward way.
+// It breaks because it may be called by global destructors during shutdown.
+// Since the order of destruction of static/global objects is undefined,
+// defining a mutex as a global object doesn't work (the mutex gets
+// destroyed, and then some later destructor calls OutputDebugStringF,
+// maybe indirectly, and you get a core dump at shutdown trying to lock
+// the mutex).
+
+static boost::once_flag debugPrintInitFlag = BOOST_ONCE_INIT;
+// We use boost::call_once() to make sure these are initialized in
+// in a thread-safe manner the first time it is called:
+static FILE* fileout = NULL;
+static boost::mutex* mutexDebugLog = NULL;
+
+static void DebugPrintInit()
+{
+ assert(fileout == NULL);
+ assert(mutexDebugLog == NULL);
+
+ boost::filesystem::path pathDebug = GetDataDir() / "debug.log";
+ fileout = fopen(pathDebug.string().c_str(), "a");
+ if (fileout) setbuf(fileout, NULL); // unbuffered
+
+ mutexDebugLog = new boost::mutex();
+}
-inline int OutputDebugStringF(const char* pszFormat, ...)
+int OutputDebugStringF(const char* pszFormat, ...)
{
- int ret = 0;
+ int ret = 0; // Returns total number of characters written
if (fPrintToConsole)
{
// print to console
va_list arg_ptr;
va_start(arg_ptr, pszFormat);
- ret = vprintf(pszFormat, arg_ptr);
+ ret += vprintf(pszFormat, arg_ptr);
va_end(arg_ptr);
}
else if (!fPrintToDebugger)
{
- // print to debug.log
- static FILE* fileout = NULL;
+ static bool fStartedNewLine = true;
+ boost::call_once(&DebugPrintInit, debugPrintInitFlag);
- if (!fileout)
- {
+ if (fileout == NULL)
+ return ret;
+
+ boost::mutex::scoped_lock scoped_lock(*mutexDebugLog);
+
+ // reopen the log file, if requested
+ if (fReopenDebugLog) {
+ fReopenDebugLog = false;
boost::filesystem::path pathDebug = GetDataDir() / "debug.log";
- fileout = fopen(pathDebug.string().c_str(), "a");
- if (fileout) setbuf(fileout, NULL); // unbuffered
+ if (freopen(pathDebug.string().c_str(),"a",fileout) != NULL)
+ setbuf(fileout, NULL); // unbuffered
}
- if (fileout)
- {
- static bool fStartedNewLine = true;
-
- // This routine may be called by global destructors during shutdown.
- // Since the order of destruction of static/global objects is undefined,
- // allocate mutexDebugLog on the heap the first time this routine
- // is called to avoid crashes during shutdown.
- static boost::mutex* mutexDebugLog = NULL;
- if (mutexDebugLog == NULL) mutexDebugLog = new boost::mutex();
- boost::mutex::scoped_lock scoped_lock(*mutexDebugLog);
-
- // reopen the log file, if requested
- if (fReopenDebugLog) {
- fReopenDebugLog = false;
- boost::filesystem::path pathDebug = GetDataDir() / "debug.log";
- if (freopen(pathDebug.string().c_str(),"a",fileout) != NULL)
- setbuf(fileout, NULL); // unbuffered
- }
- // Debug print useful for profiling
- if (fLogTimestamps && fStartedNewLine)
- fprintf(fileout, "%s ", DateTimeStrFormat("%Y-%m-%d %H:%M:%S", GetTime()).c_str());
- if (pszFormat[strlen(pszFormat) - 1] == '\n')
- fStartedNewLine = true;
- else
- fStartedNewLine = false;
+ // Debug print useful for profiling
+ if (fLogTimestamps && fStartedNewLine)
+ ret += fprintf(fileout, "%s ", DateTimeStrFormat("%Y-%m-%d %H:%M:%S", GetTime()).c_str());
+ if (pszFormat[strlen(pszFormat) - 1] == '\n')
+ fStartedNewLine = true;
+ else
+ fStartedNewLine = false;
- va_list arg_ptr;
- va_start(arg_ptr, pszFormat);
- ret = vfprintf(fileout, pszFormat, arg_ptr);
- va_end(arg_ptr);
- }
+ va_list arg_ptr;
+ va_start(arg_ptr, pszFormat);
+ ret += vfprintf(fileout, pszFormat, arg_ptr);
+ va_end(arg_ptr);
}
#ifdef WIN32
@@ -273,6 +287,7 @@ inline int OutputDebugStringF(const char* pszFormat, ...)
{
OutputDebugStringA(buffer.substr(line_start, line_end - line_start).c_str());
line_start = line_end + 1;
+ ret += line_end-line_start;
}
buffer.erase(0, line_start);
}
diff --git a/src/util.h b/src/util.h
index 8bea0dd2b3..97911d7493 100644
--- a/src/util.h
+++ b/src/util.h
@@ -138,7 +138,7 @@ extern std::string strMiscWarning;
extern bool fTestNet;
extern bool fNoListen;
extern bool fLogTimestamps;
-extern bool fReopenDebugLog;
+extern volatile bool fReopenDebugLog;
void RandAddSeed();
void RandAddSeedPerfmon();
diff --git a/src/version.h b/src/version.h
index b66d056928..f1e7c4cd7b 100644
--- a/src/version.h
+++ b/src/version.h
@@ -25,7 +25,7 @@ extern const std::string CLIENT_DATE;
// network protocol versioning
//
-static const int PROTOCOL_VERSION = 60002;
+static const int PROTOCOL_VERSION = 70001;
// earlier versions not supported as of Feb 2012, and are disconnected
static const int MIN_PROTO_VERSION = 209;
diff --git a/src/wallet.cpp b/src/wallet.cpp
index c07adff6c6..f49bfb5f8f 100644
--- a/src/wallet.cpp
+++ b/src/wallet.cpp
@@ -826,17 +826,16 @@ void CWalletTx::RelayWalletTransaction()
{
BOOST_FOREACH(const CMerkleTx& tx, vtxPrev)
{
- if (!tx.IsCoinBase()) {
+ if (!tx.IsCoinBase())
if (tx.GetDepthInMainChain() == 0)
- RelayMessage(CInv(MSG_TX, tx.GetHash()), (CTransaction)tx);
- }
+ RelayTransaction((CTransaction)tx, tx.GetHash());
}
if (!IsCoinBase())
{
if (GetDepthInMainChain() == 0) {
uint256 hash = GetHash();
printf("Relaying wtx %s\n", hash.ToString().substr(0,10).c_str());
- RelayMessage(CInv(MSG_TX, hash), (CTransaction)*this);
+ RelayTransaction((CTransaction)*this, hash);
}
}
}
@@ -1154,7 +1153,10 @@ bool CWallet::CreateTransaction(const vector<pair<CScript, int64> >& vecSend, CW
BOOST_FOREACH(PAIRTYPE(const CWalletTx*, unsigned int) pcoin, setCoins)
{
int64 nCredit = pcoin.first->vout[pcoin.second].nValue;
- dPriority += (double)nCredit * pcoin.first->GetDepthInMainChain();
+ //The priority after the next block (depth+1) is used instead of the current,
+ //reflecting an assumption the user would accept a bit more delay for
+ //a chance at a free transaction.
+ dPriority += (double)nCredit * (pcoin.first->GetDepthInMainChain()+1);
}
int64 nChange = nValueIn - nValue - nFeeRet;