aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am1
-rw-r--r--src/Makefile.qttest.include3
-rw-r--r--src/Makefile.test.include23
-rw-r--r--src/compat/byteswap.h19
-rw-r--r--src/cuckoocache.h457
-rw-r--r--src/dbwrapper.h11
-rw-r--r--src/init.cpp7
-rw-r--r--src/net_processing.cpp3
-rw-r--r--src/primitives/transaction.h2
-rw-r--r--src/qt/test/compattests.cpp23
-rw-r--r--src/qt/test/compattests.h19
-rw-r--r--src/qt/test/test_main.cpp4
-rw-r--r--src/script/sigcache.cpp77
-rw-r--r--src/script/sigcache.h9
-rw-r--r--src/test/bswap_tests.cpp26
-rw-r--r--src/test/cuckoocache_tests.cpp394
-rw-r--r--src/test/data/bitcoin-util-test.json47
-rw-r--r--src/test/data/blanktxv1.hex (renamed from src/test/data/blanktx.hex)0
-rw-r--r--src/test/data/blanktxv2.hex1
-rw-r--r--src/test/data/blanktxv2.json11
-rw-r--r--src/test/data/txcreate1.hex2
-rw-r--r--src/test/data/txcreate1.json8
-rw-r--r--src/test/data/txcreate2.hex2
-rw-r--r--src/test/data/txcreate2.json8
-rw-r--r--src/test/data/txcreatedata1.hex2
-rw-r--r--src/test/data/txcreatedata2.hex2
-rw-r--r--src/test/data/txcreatedata2.json8
-rw-r--r--src/test/data/txcreatedata_seq0.hex2
-rw-r--r--src/test/data/txcreatedata_seq0.json8
-rw-r--r--src/test/data/txcreatesignv1.hex (renamed from src/test/data/txcreatesign.hex)0
-rw-r--r--src/test/data/txcreatesignv1.json33
-rw-r--r--src/test/data/txcreatesignv2.hex1
-rw-r--r--src/test/hash_tests.cpp6
-rw-r--r--src/test/mempool_tests.cpp13
-rw-r--r--src/test/test_bitcoin.cpp2
-rw-r--r--src/test/test_bitcoin_fuzzy.cpp256
-rw-r--r--src/test/txvalidationcache_tests.cpp1
37 files changed, 1402 insertions, 89 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index 8c12aee217..389be6c058 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -61,6 +61,7 @@ EXTRA_LIBRARIES += \
lib_LTLIBRARIES = $(LIBBITCOINCONSENSUS)
bin_PROGRAMS =
+noinst_PROGRAMS =
TESTS =
BENCHMARKS =
diff --git a/src/Makefile.qttest.include b/src/Makefile.qttest.include
index cb310d5a1b..039f8ac547 100644
--- a/src/Makefile.qttest.include
+++ b/src/Makefile.qttest.include
@@ -6,6 +6,7 @@ bin_PROGRAMS += qt/test/test_bitcoin-qt
TESTS += qt/test/test_bitcoin-qt
TEST_QT_MOC_CPP = \
+ qt/test/moc_compattests.cpp \
qt/test/moc_rpcnestedtests.cpp \
qt/test/moc_uritests.cpp
@@ -14,6 +15,7 @@ TEST_QT_MOC_CPP += qt/test/moc_paymentservertests.cpp
endif
TEST_QT_H = \
+ qt/test/compattests.h \
qt/test/rpcnestedtests.h \
qt/test/uritests.h \
qt/test/paymentrequestdata.h \
@@ -23,6 +25,7 @@ qt_test_test_bitcoin_qt_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) $(BITCOIN_
$(QT_INCLUDES) $(QT_TEST_INCLUDES) $(PROTOBUF_CFLAGS)
qt_test_test_bitcoin_qt_SOURCES = \
+ qt/test/compattests.cpp \
qt/test/rpcnestedtests.cpp \
qt/test/test_main.cpp \
qt/test/uritests.cpp \
diff --git a/src/Makefile.test.include b/src/Makefile.test.include
index a14adc7876..5969de087e 100644
--- a/src/Makefile.test.include
+++ b/src/Makefile.test.include
@@ -4,6 +4,7 @@
TESTS += test/test_bitcoin
bin_PROGRAMS += test/test_bitcoin
+noinst_PROGRAMS += test/test_bitcoin_fuzzy
TEST_SRCDIR = test
TEST_BINARY=test/test_bitcoin$(EXEEXT)
@@ -38,6 +39,7 @@ RAW_TEST_FILES =
GENERATED_TEST_FILES = $(JSON_TEST_FILES:.json=.json.h) $(RAW_TEST_FILES:.raw=.raw.h)
+# test_bitcoin binary #
BITCOIN_TESTS =\
test/arith_uint256_tests.cpp \
test/scriptnum10.h \
@@ -50,9 +52,11 @@ BITCOIN_TESTS =\
test/bip32_tests.cpp \
test/blockencodings_tests.cpp \
test/bloom_tests.cpp \
+ test/bswap_tests.cpp \
test/coins_tests.cpp \
test/compress_tests.cpp \
test/crypto_tests.cpp \
+ test/cuckoocache_tests.cpp \
test/DoS_tests.cpp \
test/getarg_tests.cpp \
test/hash_tests.cpp \
@@ -119,6 +123,25 @@ test_test_bitcoin_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) -s
if ENABLE_ZMQ
test_test_bitcoin_LDADD += $(ZMQ_LIBS)
endif
+#
+
+# test_bitcoin_fuzzy binary #
+test_test_bitcoin_fuzzy_SOURCES = test/test_bitcoin_fuzzy.cpp
+test_test_bitcoin_fuzzy_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
+test_test_bitcoin_fuzzy_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
+test_test_bitcoin_fuzzy_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+
+test_test_bitcoin_fuzzy_LDADD = \
+ $(LIBUNIVALUE) \
+ $(LIBBITCOIN_SERVER) \
+ $(LIBBITCOIN_COMMON) \
+ $(LIBBITCOIN_UTIL) \
+ $(LIBBITCOIN_CONSENSUS) \
+ $(LIBBITCOIN_CRYPTO) \
+ $(LIBSECP256K1)
+
+test_test_bitcoin_fuzzy_LDADD += $(BOOST_LIBS) $(CRYPTO_LIBS)
+#
nodist_test_test_bitcoin_SOURCES = $(GENERATED_TEST_FILES)
diff --git a/src/compat/byteswap.h b/src/compat/byteswap.h
index 07ca535728..56f4240e49 100644
--- a/src/compat/byteswap.h
+++ b/src/compat/byteswap.h
@@ -15,6 +15,23 @@
#include <byteswap.h>
#endif
+#if defined(__APPLE__)
+
+#if !defined(bswap_16)
+
+// Mac OS X / Darwin features; we include a check for bswap_16 because if it is already defined, protobuf has
+// defined these macros for us already; if it isn't, we do it ourselves. In either case, we get the exact same
+// result regardless which path was taken
+#include <libkern/OSByteOrder.h>
+#define bswap_16(x) OSSwapInt16(x)
+#define bswap_32(x) OSSwapInt32(x)
+#define bswap_64(x) OSSwapInt64(x)
+
+#endif // !defined(bswap_16)
+
+#else
+// Non-Mac OS X / non-Darwin
+
#if HAVE_DECL_BSWAP_16 == 0
inline uint16_t bswap_16(uint16_t x)
{
@@ -44,4 +61,6 @@ inline uint64_t bswap_64(uint64_t x)
}
#endif // HAVE_DECL_BSWAP64
+#endif // defined(__APPLE__)
+
#endif // BITCOIN_COMPAT_BYTESWAP_H
diff --git a/src/cuckoocache.h b/src/cuckoocache.h
new file mode 100644
index 0000000000..efd6a820b5
--- /dev/null
+++ b/src/cuckoocache.h
@@ -0,0 +1,457 @@
+// Copyright (c) 2016 Jeremy Rubin
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#ifndef _BITCOIN_CUCKOOCACHE_H_
+#define _BITCOIN_CUCKOOCACHE_H_
+
+#include <array>
+#include <algorithm>
+#include <atomic>
+#include <cstring>
+#include <cmath>
+#include <memory>
+#include <vector>
+
+
+/** namespace CuckooCache provides high performance cache primitives
+ *
+ * Summary:
+ *
+ * 1) bit_packed_atomic_flags is bit-packed atomic flags for garbage collection
+ *
+ * 2) cache is a cache which is performant in memory usage and lookup speed. It
+ * is lockfree for erase operations. Elements are lazily erased on the next
+ * insert.
+ */
+namespace CuckooCache
+{
+/** bit_packed_atomic_flags implements a container for garbage collection flags
+ * that is only thread unsafe on calls to setup. This class bit-packs collection
+ * flags for memory efficiency.
+ *
+ * All operations are std::memory_order_relaxed so external mechanisms must
+ * ensure that writes and reads are properly synchronized.
+ *
+ * On setup(n), all bits up to n are marked as collected.
+ *
+ * Under the hood, because it is an 8-bit type, it makes sense to use a multiple
+ * of 8 for setup, but it will be safe if that is not the case as well.
+ *
+ */
+class bit_packed_atomic_flags
+{
+ std::unique_ptr<std::atomic<uint8_t>[]> mem;
+
+public:
+ /** No default constructor as there must be some size */
+ bit_packed_atomic_flags() = delete;
+
+ /**
+ * bit_packed_atomic_flags constructor creates memory to sufficiently
+ * keep track of garbage collection information for size entries.
+ *
+ * @param size the number of elements to allocate space for
+ *
+ * @post bit_set, bit_unset, and bit_is_set function properly forall x. x <
+ * size
+ * @post All calls to bit_is_set (without subsequent bit_unset) will return
+ * true.
+ */
+ bit_packed_atomic_flags(uint32_t size)
+ {
+ // pad out the size if needed
+ size = (size + 7) / 8;
+ mem.reset(new std::atomic<uint8_t>[size]);
+ for (uint32_t i = 0; i < size; ++i)
+ mem[i].store(0xFF);
+ };
+
+ /** setup marks all entries and ensures that bit_packed_atomic_flags can store
+ * at least size entries
+ *
+ * @param b the number of elements to allocate space for
+ * @post bit_set, bit_unset, and bit_is_set function properly forall x. x <
+ * b
+ * @post All calls to bit_is_set (without subsequent bit_unset) will return
+ * true.
+ */
+ inline void setup(uint32_t b)
+ {
+ bit_packed_atomic_flags d(b);
+ std::swap(mem, d.mem);
+ }
+
+ /** bit_set sets an entry as discardable.
+ *
+ * @param s the index of the entry to bit_set.
+ * @post immediately subsequent call (assuming proper external memory
+ * ordering) to bit_is_set(s) == true.
+ *
+ */
+ inline void bit_set(uint32_t s)
+ {
+ mem[s >> 3].fetch_or(1 << (s & 7), std::memory_order_relaxed);
+ }
+
+ /** bit_unset marks an entry as something that should not be overwritten
+ *
+ * @param s the index of the entry to bit_unset.
+ * @post immediately subsequent call (assuming proper external memory
+ * ordering) to bit_is_set(s) == false.
+ */
+ inline void bit_unset(uint32_t s)
+ {
+ mem[s >> 3].fetch_and(~(1 << (s & 7)), std::memory_order_relaxed);
+ }
+
+ /** bit_is_set queries the table for discardability at s
+ *
+ * @param s the index of the entry to read.
+ * @returns if the bit at index s was set.
+ * */
+ inline bool bit_is_set(uint32_t s) const
+ {
+ return (1 << (s & 7)) & mem[s >> 3].load(std::memory_order_relaxed);
+ }
+};
+
+/** cache implements a cache with properties similar to a cuckoo-set
+ *
+ * The cache is able to hold up to (~(uint32_t)0) - 1 elements.
+ *
+ * Read Operations:
+ * - contains(*, false)
+ *
+ * Read+Erase Operations:
+ * - contains(*, true)
+ *
+ * Erase Operations:
+ * - allow_erase()
+ *
+ * Write Operations:
+ * - setup()
+ * - setup_bytes()
+ * - insert()
+ * - please_keep()
+ *
+ * Synchronization Free Operations:
+ * - invalid()
+ * - compute_hashes()
+ *
+ * User Must Guarantee:
+ *
+ * 1) Write Requires synchronized access (e.g., a lock)
+ * 2) Read Requires no concurrent Write, synchronized with the last insert.
+ * 3) Erase requires no concurrent Write, synchronized with last insert.
+ * 4) An Erase caller must release all memory before allowing a new Writer.
+ *
+ *
+ * Note on function names:
+ * - The name "allow_erase" is used because the real discard happens later.
+ * - The name "please_keep" is used because elements may be erased anyways on insert.
+ *
+ * @tparam Element should be a movable and copyable type
+ * @tparam Hash should be a function/callable which takes a template parameter
+ * hash_select and an Element and extracts a hash from it. Should return
+ * high-entropy hashes for `Hash h; h<0>(e) ... h<7>(e)`.
+ */
+template <typename Element, typename Hash>
+class cache
+{
+private:
+ /** table stores all the elements */
+ std::vector<Element> table;
+
+ /** size stores the total available slots in the hash table */
+ uint32_t size;
+
+ /** The bit_packed_atomic_flags array is marked mutable because we want
+ * garbage collection to be allowed to occur from const methods */
+ mutable bit_packed_atomic_flags collection_flags;
+
+ /** epoch_flags tracks how recently an element was inserted into
+ * the cache. true denotes recent, false denotes not-recent. See insert()
+ * method for full semantics.
+ */
+ mutable std::vector<bool> epoch_flags;
+
+ /** epoch_heuristic_counter is used to determine when a epoch might be aged
+ * & an expensive scan should be done. epoch_heuristic_counter is
+ * decremented on insert and reset to the new number of inserts which would
+ * cause the epoch to reach epoch_size when it reaches zero.
+ */
+ uint32_t epoch_heuristic_counter;
+
+ /** epoch_size is set to be the number of elements supposed to be in a
+ * epoch. When the number of non-erased elements in a epoch
+ * exceeds epoch_size, a new epoch should be started and all
+ * current entries demoted. epoch_size is set to be 45% of size because
+ * we want to keep load around 90%, and we support 3 epochs at once --
+ * one "dead" which has been erased, one "dying" which has been marked to be
+ * erased next, and one "living" which new inserts add to.
+ */
+ uint32_t epoch_size;
+
+ /** hash_mask should be set to appropriately mask out a hash such that every
+ * masked hash is [0,size), eg, if floor(log2(size)) == 20, then hash_mask
+ * should be (1<<20)-1
+ */
+ uint32_t hash_mask;
+
+ /** depth_limit determines how many elements insert should try to replace.
+ * Should be set to log2(n)*/
+ uint8_t depth_limit;
+
+ /** hash_function is a const instance of the hash function. It cannot be
+ * static or initialized at call time as it may have internal state (such as
+ * a nonce).
+ * */
+ const Hash hash_function;
+
+ /** compute_hashes is convenience for not having to write out this
+ * expression everywhere we use the hash values of an Element.
+ *
+ * @param e the element whose hashes will be returned
+ * @returns std::array<uint32_t, 8> of deterministic hashes derived from e
+ */
+ inline std::array<uint32_t, 8> compute_hashes(const Element& e) const
+ {
+ return {{hash_function.template operator()<0>(e) & hash_mask,
+ hash_function.template operator()<1>(e) & hash_mask,
+ hash_function.template operator()<2>(e) & hash_mask,
+ hash_function.template operator()<3>(e) & hash_mask,
+ hash_function.template operator()<4>(e) & hash_mask,
+ hash_function.template operator()<5>(e) & hash_mask,
+ hash_function.template operator()<6>(e) & hash_mask,
+ hash_function.template operator()<7>(e) & hash_mask}};
+ }
+
+ /* end
+ * @returns a constexpr index that can never be inserted to */
+ constexpr uint32_t invalid() const
+ {
+ return ~(uint32_t)0;
+ }
+
+ /** allow_erase marks the element at index n as discardable. Threadsafe
+ * without any concurrent insert.
+ * @param n the index to allow erasure of
+ */
+ inline void allow_erase(uint32_t n) const
+ {
+ collection_flags.bit_set(n);
+ }
+
+ /** please_keep marks the element at index n as an entry that should be kept.
+ * Threadsafe without any concurrent insert.
+ * @param n the index to prioritize keeping
+ */
+ inline void please_keep(uint32_t n) const
+ {
+ collection_flags.bit_unset(n);
+ }
+
+ /** epoch_check handles the changing of epochs for elements stored in the
+ * cache. epoch_check should be run before every insert.
+ *
+ * First, epoch_check decrements and checks the cheap heuristic, and then does
+ * a more expensive scan if the cheap heuristic runs out. If the expensive
+ * scan suceeds, the epochs are aged and old elements are allow_erased. The
+ * cheap heuristic is reset to retrigger after the worst case growth of the
+ * current epoch's elements would exceed the epoch_size.
+ */
+ void epoch_check()
+ {
+ if (epoch_heuristic_counter != 0) {
+ --epoch_heuristic_counter;
+ return;
+ }
+ // count the number of elements from the latest epoch which
+ // have not been erased.
+ uint32_t epoch_unused_count = 0;
+ for (uint32_t i = 0; i < size; ++i)
+ epoch_unused_count += epoch_flags[i] &&
+ !collection_flags.bit_is_set(i);
+ // If there are more non-deleted entries in the current epoch than the
+ // epoch size, then allow_erase on all elements in the old epoch (marked
+ // false) and move all elements in the current epoch to the old epoch
+ // but do not call allow_erase on their indices.
+ if (epoch_unused_count >= epoch_size) {
+ for (uint32_t i = 0; i < size; ++i)
+ if (epoch_flags[i])
+ epoch_flags[i] = false;
+ else
+ allow_erase(i);
+ epoch_heuristic_counter = epoch_size;
+ } else
+ // reset the epoch_heuristic_counter to next do a scan when worst
+ // case behavior (no intermittent erases) would exceed epoch size,
+ // with a reasonable minimum scan size.
+ // Ordinarily, we would have to sanity check std::min(epoch_size,
+ // epoch_unused_count), but we already know that `epoch_unused_count
+ // < epoch_size` in this branch
+ epoch_heuristic_counter = std::max(1u, std::max(epoch_size / 16,
+ epoch_size - epoch_unused_count));
+ }
+
+public:
+ /** You must always construct a cache with some elements via a subsequent
+ * call to setup or setup_bytes, otherwise operations may segfault.
+ */
+ cache() : table(), size(), collection_flags(0), epoch_flags(),
+ epoch_heuristic_counter(), epoch_size(), depth_limit(0), hash_function()
+ {
+ }
+
+ /** setup initializes the container to store no more than new_size
+ * elements. setup rounds down to a power of two size.
+ *
+ * setup should only be called once.
+ *
+ * @param new_size the desired number of elements to store
+ * @returns the maximum number of elements storable
+ **/
+ uint32_t setup(uint32_t new_size)
+ {
+ // depth_limit must be at least one otherwise errors can occur.
+ depth_limit = static_cast<uint8_t>(std::log2(static_cast<float>(std::max((uint32_t)2, new_size))));
+ size = 1 << depth_limit;
+ hash_mask = size-1;
+ table.resize(size);
+ collection_flags.setup(size);
+ epoch_flags.resize(size);
+ // Set to 45% as described above
+ epoch_size = std::max((uint32_t)1, (45 * size) / 100);
+ // Initially set to wait for a whole epoch
+ epoch_heuristic_counter = epoch_size;
+ return size;
+ }
+
+ /** setup_bytes is a convenience function which accounts for internal memory
+ * usage when deciding how many elements to store. It isn't perfect because
+ * it doesn't account for any overhead (struct size, MallocUsage, collection
+ * and epoch flags). This was done to simplify selecting a power of two
+ * size. In the expected use case, an extra two bits per entry should be
+ * negligible compared to the size of the elements.
+ *
+ * @param bytes the approximate number of bytes to use for this data
+ * structure.
+ * @returns the maximum number of elements storable (see setup()
+ * documentation for more detail)
+ */
+ uint32_t setup_bytes(size_t bytes)
+ {
+ return setup(bytes/sizeof(Element));
+ }
+
+ /** insert loops at most depth_limit times trying to insert a hash
+ * at various locations in the table via a variant of the Cuckoo Algorithm
+ * with eight hash locations.
+ *
+ * It drops the last tried element if it runs out of depth before
+ * encountering an open slot.
+ *
+ * Thus
+ *
+ * insert(x);
+ * return contains(x, false);
+ *
+ * is not guaranteed to return true.
+ *
+ * @param e the element to insert
+ * @post one of the following: All previously inserted elements and e are
+ * now in the table, one previously inserted element is evicted from the
+ * table, the entry attempted to be inserted is evicted.
+ *
+ */
+ inline void insert(Element e)
+ {
+ epoch_check();
+ uint32_t last_loc = invalid();
+ bool last_epoch = true;
+ std::array<uint32_t, 8> locs = compute_hashes(e);
+ // Make sure we have not already inserted this element
+ // If we have, make sure that it does not get deleted
+ for (uint32_t loc : locs)
+ if (table[loc] == e) {
+ please_keep(loc);
+ epoch_flags[loc] = last_epoch;
+ return;
+ }
+ for (uint8_t depth = 0; depth < depth_limit; ++depth) {
+ // First try to insert to an empty slot, if one exists
+ for (uint32_t loc : locs) {
+ if (!collection_flags.bit_is_set(loc))
+ continue;
+ table[loc] = std::move(e);
+ please_keep(loc);
+ epoch_flags[loc] = last_epoch;
+ return;
+ }
+ /** Swap with the element at the location that was
+ * not the last one looked at. Example:
+ *
+ * 1) On first iteration, last_loc == invalid(), find returns last, so
+ * last_loc defaults to locs[0].
+ * 2) On further iterations, where last_loc == locs[k], last_loc will
+ * go to locs[k+1 % 8], i.e., next of the 8 indicies wrapping around
+ * to 0 if needed.
+ *
+ * This prevents moving the element we just put in.
+ *
+ * The swap is not a move -- we must switch onto the evicted element
+ * for the next iteration.
+ */
+ last_loc = locs[(1 + (std::find(locs.begin(), locs.end(), last_loc) - locs.begin())) & 7];
+ std::swap(table[last_loc], e);
+ // Can't std::swap a std::vector<bool>::reference and a bool&.
+ bool epoch = last_epoch;
+ last_epoch = epoch_flags[last_loc];
+ epoch_flags[last_loc] = epoch;
+
+ // Recompute the locs -- unfortunately happens one too many times!
+ locs = compute_hashes(e);
+ }
+ }
+
+ /* contains iterates through the hash locations for a given element
+ * and checks to see if it is present.
+ *
+ * contains does not check garbage collected state (in other words,
+ * garbage is only collected when the space is needed), so:
+ *
+ * insert(x);
+ * if (contains(x, true))
+ * return contains(x, false);
+ * else
+ * return true;
+ *
+ * executed on a single thread will always return true!
+ *
+ * This is a great property for re-org performance for example.
+ *
+ * contains returns a bool set true if the element was found.
+ *
+ * @param e the element to check
+ * @param erase
+ *
+ * @post if erase is true and the element is found, then the garbage collect
+ * flag is set
+ * @returns true if the element is found, false otherwise
+ */
+ inline bool contains(const Element& e, const bool erase) const
+ {
+ std::array<uint32_t, 8> locs = compute_hashes(e);
+ for (uint32_t loc : locs)
+ if (table[loc] == e) {
+ if (erase)
+ allow_erase(loc);
+ return true;
+ }
+ return false;
+ }
+};
+} // namespace CuckooCache
+
+#endif
diff --git a/src/dbwrapper.h b/src/dbwrapper.h
index 4a79bbd17d..08c72c25dd 100644
--- a/src/dbwrapper.h
+++ b/src/dbwrapper.h
@@ -53,38 +53,41 @@ private:
const CDBWrapper &parent;
leveldb::WriteBatch batch;
+ CDataStream ssKey;
+ CDataStream ssValue;
+
public:
/**
* @param[in] _parent CDBWrapper that this batch is to be submitted to
*/
- CDBBatch(const CDBWrapper &_parent) : parent(_parent) { };
+ CDBBatch(const CDBWrapper &_parent) : parent(_parent), ssKey(SER_DISK, CLIENT_VERSION), ssValue(SER_DISK, CLIENT_VERSION) { };
template <typename K, typename V>
void Write(const K& key, const V& value)
{
- CDataStream ssKey(SER_DISK, CLIENT_VERSION);
ssKey.reserve(DBWRAPPER_PREALLOC_KEY_SIZE);
ssKey << key;
leveldb::Slice slKey(&ssKey[0], ssKey.size());
- CDataStream ssValue(SER_DISK, CLIENT_VERSION);
ssValue.reserve(DBWRAPPER_PREALLOC_VALUE_SIZE);
ssValue << value;
ssValue.Xor(dbwrapper_private::GetObfuscateKey(parent));
leveldb::Slice slValue(&ssValue[0], ssValue.size());
batch.Put(slKey, slValue);
+ ssKey.clear();
+ ssValue.clear();
}
template <typename K>
void Erase(const K& key)
{
- CDataStream ssKey(SER_DISK, CLIENT_VERSION);
ssKey.reserve(DBWRAPPER_PREALLOC_KEY_SIZE);
ssKey << key;
leveldb::Slice slKey(&ssKey[0], ssKey.size());
batch.Delete(slKey);
+ ssKey.clear();
}
};
diff --git a/src/init.cpp b/src/init.cpp
index 71971a7642..c5dc9578e7 100644
--- a/src/init.cpp
+++ b/src/init.cpp
@@ -380,7 +380,7 @@ std::string HelpMessage(HelpMessageMode mode)
strUsage += HelpMessageOpt("-port=<port>", strprintf(_("Listen for connections on <port> (default: %u or testnet: %u)"), Params(CBaseChainParams::MAIN).GetDefaultPort(), Params(CBaseChainParams::TESTNET).GetDefaultPort()));
strUsage += HelpMessageOpt("-proxy=<ip:port>", _("Connect through SOCKS5 proxy"));
strUsage += HelpMessageOpt("-proxyrandomize", strprintf(_("Randomize credentials for every proxy connection. This enables Tor stream isolation (default: %u)"), DEFAULT_PROXYRANDOMIZE));
- strUsage += HelpMessageOpt("-rpcserialversion", strprintf(_("Sets the serialization of raw transaction or block hex returned in non-verbose mode, non-segwit(0) or segwit(>0) (default: %d)"), DEFAULT_RPC_SERIALIZE_VERSION));
+ strUsage += HelpMessageOpt("-rpcserialversion", strprintf(_("Sets the serialization of raw transaction or block hex returned in non-verbose mode, non-segwit(0) or segwit(1) (default: %d)"), DEFAULT_RPC_SERIALIZE_VERSION));
strUsage += HelpMessageOpt("-seednode=<ip>", _("Connect to a node to retrieve peer addresses, and disconnect"));
strUsage += HelpMessageOpt("-timeout=<n>", strprintf(_("Specify connection timeout in milliseconds (minimum: 1, default: %d)"), DEFAULT_CONNECT_TIMEOUT));
strUsage += HelpMessageOpt("-torcontrol=<ip>:<port>", strprintf(_("Tor control port to use if onion listening enabled (default: %s)"), DEFAULT_TOR_CONTROL));
@@ -988,6 +988,9 @@ bool AppInitParameterInteraction()
if (GetArg("-rpcserialversion", DEFAULT_RPC_SERIALIZE_VERSION) < 0)
return InitError("rpcserialversion must be non-negative.");
+ if (GetArg("-rpcserialversion", DEFAULT_RPC_SERIALIZE_VERSION) > 1)
+ return InitError("unknown rpcserialversion requested.");
+
nMaxTipAge = GetArg("-maxtipage", DEFAULT_MAX_TIP_AGE);
fEnableReplacement = GetBoolArg("-mempoolreplacement", DEFAULT_ENABLE_REPLACEMENT);
@@ -1103,6 +1106,8 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler)
LogPrintf("Using config file %s\n", GetConfigFile(GetArg("-conf", BITCOIN_CONF_FILENAME)).string());
LogPrintf("Using at most %i connections (%i file descriptors available)\n", nMaxConnections, nFD);
+ InitSignatureCache();
+
LogPrintf("Using %u threads for script verification\n", nScriptCheckThreads);
if (nScriptCheckThreads) {
for (int i=0; i<nScriptCheckThreads-1; i++)
diff --git a/src/net_processing.cpp b/src/net_processing.cpp
index 8a18c886e9..e10694ed18 100644
--- a/src/net_processing.cpp
+++ b/src/net_processing.cpp
@@ -3045,6 +3045,9 @@ bool SendMessages(CNode* pto, CConnman& connman)
static CFeeRate default_feerate(DEFAULT_MIN_RELAY_TX_FEE);
static FeeFilterRounder filterRounder(default_feerate);
CAmount filterToSend = filterRounder.round(currentFilter);
+ // If we don't allow free transactions, then we always have a fee filter of at least minRelayTxFee
+ if (GetArg("-limitfreerelay", DEFAULT_LIMITFREERELAY) <= 0)
+ filterToSend = std::max(filterToSend, ::minRelayTxFee.GetFeePerK());
if (filterToSend != pto->lastSentFeeFilter) {
connman.PushMessage(pto, msgMaker.Make(NetMsgType::FEEFILTER, filterToSend));
pto->lastSentFeeFilter = filterToSend;
diff --git a/src/primitives/transaction.h b/src/primitives/transaction.h
index 66fefafef5..b594cab458 100644
--- a/src/primitives/transaction.h
+++ b/src/primitives/transaction.h
@@ -363,7 +363,7 @@ class CTransaction
{
public:
// Default transaction version.
- static const int32_t CURRENT_VERSION=1;
+ static const int32_t CURRENT_VERSION=2;
// Changing the default transaction version requires a two step process: first
// adapting relay policy by bumping MAX_STANDARD_VERSION, and then later date
diff --git a/src/qt/test/compattests.cpp b/src/qt/test/compattests.cpp
new file mode 100644
index 0000000000..2a7284b5b2
--- /dev/null
+++ b/src/qt/test/compattests.cpp
@@ -0,0 +1,23 @@
+// Copyright (c) 2016 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include "paymentrequestplus.h" // this includes protobuf's port.h which defines its own bswap macos
+
+#include "compattests.h"
+
+#include "compat/byteswap.h"
+
+void CompatTests::bswapTests()
+{
+ // Sibling in bitcoin/src/test/bswap_tests.cpp
+ uint16_t u1 = 0x1234;
+ uint32_t u2 = 0x56789abc;
+ uint64_t u3 = 0xdef0123456789abc;
+ uint16_t e1 = 0x3412;
+ uint32_t e2 = 0xbc9a7856;
+ uint64_t e3 = 0xbc9a78563412f0de;
+ QVERIFY(bswap_16(u1) == e1);
+ QVERIFY(bswap_32(u2) == e2);
+ QVERIFY(bswap_64(u3) == e3);
+}
diff --git a/src/qt/test/compattests.h b/src/qt/test/compattests.h
new file mode 100644
index 0000000000..35dede7743
--- /dev/null
+++ b/src/qt/test/compattests.h
@@ -0,0 +1,19 @@
+// Copyright (c) 2009-2015 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#ifndef BITCOIN_QT_TEST_COMPATTESTS_H
+#define BITCOIN_QT_TEST_COMPATTESTS_H
+
+#include <QObject>
+#include <QTest>
+
+class CompatTests : public QObject
+{
+ Q_OBJECT
+
+private Q_SLOTS:
+ void bswapTests();
+};
+
+#endif // BITCOIN_QT_TEST_COMPATTESTS_H
diff --git a/src/qt/test/test_main.cpp b/src/qt/test/test_main.cpp
index dbaab54fb6..d44d711315 100644
--- a/src/qt/test/test_main.cpp
+++ b/src/qt/test/test_main.cpp
@@ -11,6 +11,7 @@
#include "rpcnestedtests.h"
#include "util.h"
#include "uritests.h"
+#include "compattests.h"
#ifdef ENABLE_WALLET
#include "paymentservertests.h"
@@ -61,6 +62,9 @@ int main(int argc, char *argv[])
RPCNestedTests test3;
if (QTest::qExec(&test3) != 0)
fInvalid = true;
+ CompatTests test4;
+ if (QTest::qExec(&test4) != 0)
+ fInvalid = true;
ECC_Stop();
return fInvalid;
diff --git a/src/script/sigcache.cpp b/src/script/sigcache.cpp
index bdc0bfdc1c..b78d7b607f 100644
--- a/src/script/sigcache.cpp
+++ b/src/script/sigcache.cpp
@@ -11,20 +11,29 @@
#include "uint256.h"
#include "util.h"
+#include "cuckoocache.h"
#include <boost/thread.hpp>
-#include <boost/unordered_set.hpp>
namespace {
/**
* We're hashing a nonce into the entries themselves, so we don't need extra
* blinding in the set hash computation.
+ *
+ * This may exhibit platform endian dependent behavior but because these are
+ * nonced hashes (random) and this state is only ever used locally it is safe.
+ * All that matters is local consistency.
*/
-class CSignatureCacheHasher
+class SignatureCacheHasher
{
public:
- size_t operator()(const uint256& key) const {
- return key.GetCheapHash();
+ template <uint8_t hash_select>
+ uint32_t operator()(const uint256& key) const
+ {
+ static_assert(hash_select <8, "SignatureCacheHasher only has 8 hashes available.");
+ uint32_t u;
+ std::memcpy(&u, key.begin()+4*hash_select, 4);
+ return u;
}
};
@@ -38,11 +47,10 @@ class CSignatureCache
private:
//! Entries are SHA256(nonce || signature hash || public key || signature):
uint256 nonce;
- typedef boost::unordered_set<uint256, CSignatureCacheHasher> map_type;
+ typedef CuckooCache::cache<uint256, SignatureCacheHasher> map_type;
map_type setValid;
boost::shared_mutex cs_sigcache;
-
public:
CSignatureCache()
{
@@ -56,58 +64,51 @@ public:
}
bool
- Get(const uint256& entry)
+ Get(const uint256& entry, const bool erase)
{
boost::shared_lock<boost::shared_mutex> lock(cs_sigcache);
- return setValid.count(entry);
+ return setValid.contains(entry, erase);
}
- void Erase(const uint256& entry)
+ void Set(uint256& entry)
{
boost::unique_lock<boost::shared_mutex> lock(cs_sigcache);
- setValid.erase(entry);
+ setValid.insert(entry);
}
-
- void Set(const uint256& entry)
+ uint32_t setup_bytes(size_t n)
{
- size_t nMaxCacheSize = GetArg("-maxsigcachesize", DEFAULT_MAX_SIG_CACHE_SIZE) * ((size_t) 1 << 20);
- if (nMaxCacheSize <= 0) return;
-
- boost::unique_lock<boost::shared_mutex> lock(cs_sigcache);
- while (memusage::DynamicUsage(setValid) > nMaxCacheSize)
- {
- map_type::size_type s = GetRand(setValid.bucket_count());
- map_type::local_iterator it = setValid.begin(s);
- if (it != setValid.end(s)) {
- setValid.erase(*it);
- }
- }
-
- setValid.insert(entry);
+ return setValid.setup_bytes(n);
}
};
+/* In previous versions of this code, signatureCache was a local static variable
+ * in CachingTransactionSignatureChecker::VerifySignature. We initialize
+ * signatureCache outside of VerifySignature to avoid the atomic operation per
+ * call overhead associated with local static variables even though
+ * signatureCache could be made local to VerifySignature.
+*/
+static CSignatureCache signatureCache;
}
-bool CachingTransactionSignatureChecker::VerifySignature(const std::vector<unsigned char>& vchSig, const CPubKey& pubkey, const uint256& sighash) const
+// To be called once in AppInit2/TestingSetup to initialize the signatureCache
+void InitSignatureCache()
{
- static CSignatureCache signatureCache;
+ size_t nMaxCacheSize = GetArg("-maxsigcachesize", DEFAULT_MAX_SIG_CACHE_SIZE) * ((size_t) 1 << 20);
+ if (nMaxCacheSize <= 0) return;
+ size_t nElems = signatureCache.setup_bytes(nMaxCacheSize);
+ LogPrintf("Using %zu MiB out of %zu requested for signature cache, able to store %zu elements\n",
+ (nElems*sizeof(uint256)) >>20, nMaxCacheSize>>20, nElems);
+}
+bool CachingTransactionSignatureChecker::VerifySignature(const std::vector<unsigned char>& vchSig, const CPubKey& pubkey, const uint256& sighash) const
+{
uint256 entry;
signatureCache.ComputeEntry(entry, sighash, vchSig, pubkey);
-
- if (signatureCache.Get(entry)) {
- if (!store) {
- signatureCache.Erase(entry);
- }
+ if (signatureCache.Get(entry, !store))
return true;
- }
-
if (!TransactionSignatureChecker::VerifySignature(vchSig, pubkey, sighash))
return false;
-
- if (store) {
+ if (store)
signatureCache.Set(entry);
- }
return true;
}
diff --git a/src/script/sigcache.h b/src/script/sigcache.h
index 44551ec2bc..5243fc0a42 100644
--- a/src/script/sigcache.h
+++ b/src/script/sigcache.h
@@ -10,9 +10,10 @@
#include <vector>
-// DoS prevention: limit cache size to less than 40MB (over 500000
-// entries on 64-bit systems).
-static const unsigned int DEFAULT_MAX_SIG_CACHE_SIZE = 40;
+// DoS prevention: limit cache size to 32MB (over 1000000 entries on 64-bit
+// systems). Due to how we count cache size, actual memory usage is slightly
+// more (~32.25 MB)
+static const unsigned int DEFAULT_MAX_SIG_CACHE_SIZE = 32;
class CPubKey;
@@ -27,4 +28,6 @@ public:
bool VerifySignature(const std::vector<unsigned char>& vchSig, const CPubKey& vchPubKey, const uint256& sighash) const;
};
+void InitSignatureCache();
+
#endif // BITCOIN_SCRIPT_SIGCACHE_H
diff --git a/src/test/bswap_tests.cpp b/src/test/bswap_tests.cpp
new file mode 100644
index 0000000000..7b3134d327
--- /dev/null
+++ b/src/test/bswap_tests.cpp
@@ -0,0 +1,26 @@
+// Copyright (c) 2016 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include "compat/byteswap.h"
+#include "test/test_bitcoin.h"
+
+#include <boost/test/unit_test.hpp>
+
+BOOST_FIXTURE_TEST_SUITE(bswap_tests, BasicTestingSetup)
+
+BOOST_AUTO_TEST_CASE(bswap_tests)
+{
+ // Sibling in bitcoin/src/qt/test/compattests.cpp
+ uint16_t u1 = 0x1234;
+ uint32_t u2 = 0x56789abc;
+ uint64_t u3 = 0xdef0123456789abc;
+ uint16_t e1 = 0x3412;
+ uint32_t e2 = 0xbc9a7856;
+ uint64_t e3 = 0xbc9a78563412f0de;
+ BOOST_CHECK(bswap_16(u1) == e1);
+ BOOST_CHECK(bswap_32(u2) == e2);
+ BOOST_CHECK(bswap_64(u3) == e3);
+}
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/cuckoocache_tests.cpp b/src/test/cuckoocache_tests.cpp
new file mode 100644
index 0000000000..1bc50d5ea9
--- /dev/null
+++ b/src/test/cuckoocache_tests.cpp
@@ -0,0 +1,394 @@
+// Copyright (c) 2012-2016 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+#include <boost/test/unit_test.hpp>
+#include "cuckoocache.h"
+#include "test/test_bitcoin.h"
+#include "random.h"
+#include <thread>
+#include <boost/thread.hpp>
+
+
+/** Test Suite for CuckooCache
+ *
+ * 1) All tests should have a deterministic result (using insecure rand
+ * with deterministic seeds)
+ * 2) Some test methods are templated to allow for easier testing
+ * against new versions / comparing
+ * 3) Results should be treated as a regression test, ie, did the behavior
+ * change significantly from what was expected. This can be OK, depending on
+ * the nature of the change, but requires updating the tests to reflect the new
+ * expected behavior. For example improving the hit rate may cause some tests
+ * using BOOST_CHECK_CLOSE to fail.
+ *
+ */
+FastRandomContext insecure_rand(true);
+
+BOOST_AUTO_TEST_SUITE(cuckoocache_tests);
+
+
+/** insecure_GetRandHash fills in a uint256 from insecure_rand
+ */
+void insecure_GetRandHash(uint256& t)
+{
+ uint32_t* ptr = (uint32_t*)t.begin();
+ for (uint8_t j = 0; j < 8; ++j)
+ *(ptr++) = insecure_rand.rand32();
+}
+
+/** Definition copied from /src/script/sigcache.cpp
+ */
+class uint256Hasher
+{
+public:
+ template <uint8_t hash_select>
+ uint32_t operator()(const uint256& key) const
+ {
+ static_assert(hash_select <8, "SignatureCacheHasher only has 8 hashes available.");
+ uint32_t u;
+ std::memcpy(&u, key.begin() + 4 * hash_select, 4);
+ return u;
+ }
+};
+
+
+/* Test that no values not inserted into the cache are read out of it.
+ *
+ * There are no repeats in the first 200000 insecure_GetRandHash calls
+ */
+BOOST_AUTO_TEST_CASE(test_cuckoocache_no_fakes)
+{
+ insecure_rand = FastRandomContext(true);
+ CuckooCache::cache<uint256, uint256Hasher> cc{};
+ cc.setup_bytes(32 << 20);
+ uint256 v;
+ for (int x = 0; x < 100000; ++x) {
+ insecure_GetRandHash(v);
+ cc.insert(v);
+ }
+ for (int x = 0; x < 100000; ++x) {
+ insecure_GetRandHash(v);
+ BOOST_CHECK(!cc.contains(v, false));
+ }
+};
+
+/** This helper returns the hit rate when megabytes*load worth of entries are
+ * inserted into a megabytes sized cache
+ */
+template <typename Cache>
+double test_cache(size_t megabytes, double load)
+{
+ insecure_rand = FastRandomContext(true);
+ std::vector<uint256> hashes;
+ Cache set{};
+ size_t bytes = megabytes * (1 << 20);
+ set.setup_bytes(bytes);
+ uint32_t n_insert = static_cast<uint32_t>(load * (bytes / sizeof(uint256)));
+ hashes.resize(n_insert);
+ for (uint32_t i = 0; i < n_insert; ++i) {
+ uint32_t* ptr = (uint32_t*)hashes[i].begin();
+ for (uint8_t j = 0; j < 8; ++j)
+ *(ptr++) = insecure_rand.rand32();
+ }
+ /** We make a copy of the hashes because future optimizations of the
+ * cuckoocache may overwrite the inserted element, so the test is
+ * "future proofed".
+ */
+ std::vector<uint256> hashes_insert_copy = hashes;
+ /** Do the insert */
+ for (uint256& h : hashes_insert_copy)
+ set.insert(h);
+ /** Count the hits */
+ uint32_t count = 0;
+ for (uint256& h : hashes)
+ count += set.contains(h, false);
+ double hit_rate = ((double)count) / ((double)n_insert);
+ return hit_rate;
+}
+
+/** The normalized hit rate for a given load.
+ *
+ * The semantics are a little confusing, so please see the below
+ * explanation.
+ *
+ * Examples:
+ *
+ * 1) at load 0.5, we expect a perfect hit rate, so we multiply by
+ * 1.0
+ * 2) at load 2.0, we expect to see half the entries, so a perfect hit rate
+ * would be 0.5. Therefore, if we see a hit rate of 0.4, 0.4*2.0 = 0.8 is the
+ * normalized hit rate.
+ *
+ * This is basically the right semantics, but has a bit of a glitch depending on
+ * how you measure around load 1.0 as after load 1.0 your normalized hit rate
+ * becomes effectively perfect, ignoring freshness.
+ */
+double normalize_hit_rate(double hits, double load)
+{
+ return hits * std::max(load, 1.0);
+}
+
+/** Check the hit rate on loads ranging from 0.1 to 2.0 */
+BOOST_AUTO_TEST_CASE(cuckoocache_hit_rate_ok)
+{
+ /** Arbitrarily selected Hit Rate threshold that happens to work for this test
+ * as a lower bound on performance.
+ */
+ double HitRateThresh = 0.98;
+ size_t megabytes = 32;
+ for (double load = 0.1; load < 2; load *= 2) {
+ double hits = test_cache<CuckooCache::cache<uint256, uint256Hasher>>(megabytes, load);
+ BOOST_CHECK(normalize_hit_rate(hits, load) > HitRateThresh);
+ }
+}
+
+
+/** This helper checks that erased elements are preferentially inserted onto and
+ * that the hit rate of "fresher" keys is reasonable*/
+template <typename Cache>
+void test_cache_erase(size_t megabytes)
+{
+ double load = 1;
+ insecure_rand = FastRandomContext(true);
+ std::vector<uint256> hashes;
+ Cache set{};
+ size_t bytes = megabytes * (1 << 20);
+ set.setup_bytes(bytes);
+ uint32_t n_insert = static_cast<uint32_t>(load * (bytes / sizeof(uint256)));
+ hashes.resize(n_insert);
+ for (uint32_t i = 0; i < n_insert; ++i) {
+ uint32_t* ptr = (uint32_t*)hashes[i].begin();
+ for (uint8_t j = 0; j < 8; ++j)
+ *(ptr++) = insecure_rand.rand32();
+ }
+ /** We make a copy of the hashes because future optimizations of the
+ * cuckoocache may overwrite the inserted element, so the test is
+ * "future proofed".
+ */
+ std::vector<uint256> hashes_insert_copy = hashes;
+
+ /** Insert the first half */
+ for (uint32_t i = 0; i < (n_insert / 2); ++i)
+ set.insert(hashes_insert_copy[i]);
+ /** Erase the first quarter */
+ for (uint32_t i = 0; i < (n_insert / 4); ++i)
+ set.contains(hashes[i], true);
+ /** Insert the second half */
+ for (uint32_t i = (n_insert / 2); i < n_insert; ++i)
+ set.insert(hashes_insert_copy[i]);
+
+ /** elements that we marked erased but that are still there */
+ size_t count_erased_but_contained = 0;
+ /** elements that we did not erase but are older */
+ size_t count_stale = 0;
+ /** elements that were most recently inserted */
+ size_t count_fresh = 0;
+
+ for (uint32_t i = 0; i < (n_insert / 4); ++i)
+ count_erased_but_contained += set.contains(hashes[i], false);
+ for (uint32_t i = (n_insert / 4); i < (n_insert / 2); ++i)
+ count_stale += set.contains(hashes[i], false);
+ for (uint32_t i = (n_insert / 2); i < n_insert; ++i)
+ count_fresh += set.contains(hashes[i], false);
+
+ double hit_rate_erased_but_contained = double(count_erased_but_contained) / (double(n_insert) / 4.0);
+ double hit_rate_stale = double(count_stale) / (double(n_insert) / 4.0);
+ double hit_rate_fresh = double(count_fresh) / (double(n_insert) / 2.0);
+
+ // Check that our hit_rate_fresh is perfect
+ BOOST_CHECK_EQUAL(hit_rate_fresh, 1.0);
+ // Check that we have a more than 2x better hit rate on stale elements than
+ // erased elements.
+ BOOST_CHECK(hit_rate_stale > 2 * hit_rate_erased_but_contained);
+}
+
+BOOST_AUTO_TEST_CASE(cuckoocache_erase_ok)
+{
+ size_t megabytes = 32;
+ test_cache_erase<CuckooCache::cache<uint256, uint256Hasher>>(megabytes);
+}
+
+template <typename Cache>
+void test_cache_erase_parallel(size_t megabytes)
+{
+ double load = 1;
+ insecure_rand = FastRandomContext(true);
+ std::vector<uint256> hashes;
+ Cache set{};
+ size_t bytes = megabytes * (1 << 20);
+ set.setup_bytes(bytes);
+ uint32_t n_insert = static_cast<uint32_t>(load * (bytes / sizeof(uint256)));
+ hashes.resize(n_insert);
+ for (uint32_t i = 0; i < n_insert; ++i) {
+ uint32_t* ptr = (uint32_t*)hashes[i].begin();
+ for (uint8_t j = 0; j < 8; ++j)
+ *(ptr++) = insecure_rand.rand32();
+ }
+ /** We make a copy of the hashes because future optimizations of the
+ * cuckoocache may overwrite the inserted element, so the test is
+ * "future proofed".
+ */
+ std::vector<uint256> hashes_insert_copy = hashes;
+ boost::shared_mutex mtx;
+
+ {
+ /** Grab lock to make sure we release inserts */
+ boost::unique_lock<boost::shared_mutex> l(mtx);
+ /** Insert the first half */
+ for (uint32_t i = 0; i < (n_insert / 2); ++i)
+ set.insert(hashes_insert_copy[i]);
+ }
+
+ /** Spin up 3 threads to run contains with erase.
+ */
+ std::vector<std::thread> threads;
+ /** Erase the first quarter */
+ for (uint32_t x = 0; x < 3; ++x)
+ /** Each thread is emplaced with x copy-by-value
+ */
+ threads.emplace_back([&, x] {
+ boost::shared_lock<boost::shared_mutex> l(mtx);
+ size_t ntodo = (n_insert/4)/3;
+ size_t start = ntodo*x;
+ size_t end = ntodo*(x+1);
+ for (uint32_t i = start; i < end; ++i)
+ set.contains(hashes[i], true);
+ });
+
+ /** Wait for all threads to finish
+ */
+ for (std::thread& t : threads)
+ t.join();
+ /** Grab lock to make sure we observe erases */
+ boost::unique_lock<boost::shared_mutex> l(mtx);
+ /** Insert the second half */
+ for (uint32_t i = (n_insert / 2); i < n_insert; ++i)
+ set.insert(hashes_insert_copy[i]);
+
+ /** elements that we marked erased but that are still there */
+ size_t count_erased_but_contained = 0;
+ /** elements that we did not erase but are older */
+ size_t count_stale = 0;
+ /** elements that were most recently inserted */
+ size_t count_fresh = 0;
+
+ for (uint32_t i = 0; i < (n_insert / 4); ++i)
+ count_erased_but_contained += set.contains(hashes[i], false);
+ for (uint32_t i = (n_insert / 4); i < (n_insert / 2); ++i)
+ count_stale += set.contains(hashes[i], false);
+ for (uint32_t i = (n_insert / 2); i < n_insert; ++i)
+ count_fresh += set.contains(hashes[i], false);
+
+ double hit_rate_erased_but_contained = double(count_erased_but_contained) / (double(n_insert) / 4.0);
+ double hit_rate_stale = double(count_stale) / (double(n_insert) / 4.0);
+ double hit_rate_fresh = double(count_fresh) / (double(n_insert) / 2.0);
+
+ // Check that our hit_rate_fresh is perfect
+ BOOST_CHECK_EQUAL(hit_rate_fresh, 1.0);
+ // Check that we have a more than 2x better hit rate on stale elements than
+ // erased elements.
+ BOOST_CHECK(hit_rate_stale > 2 * hit_rate_erased_but_contained);
+}
+BOOST_AUTO_TEST_CASE(cuckoocache_erase_parallel_ok)
+{
+ size_t megabytes = 32;
+ test_cache_erase_parallel<CuckooCache::cache<uint256, uint256Hasher>>(megabytes);
+}
+
+
+template <typename Cache>
+void test_cache_generations()
+{
+ // This test checks that for a simulation of network activity, the fresh hit
+ // rate is never below 99%, and the number of times that it is worse than
+ // 99.9% are less than 1% of the time.
+ double min_hit_rate = 0.99;
+ double tight_hit_rate = 0.999;
+ double max_rate_less_than_tight_hit_rate = 0.01;
+ // A cache that meets this specification is therefore shown to have a hit
+ // rate of at least tight_hit_rate * (1 - max_rate_less_than_tight_hit_rate) +
+ // min_hit_rate*max_rate_less_than_tight_hit_rate = 0.999*99%+0.99*1% == 99.89%
+ // hit rate with low variance.
+
+ // We use deterministic values, but this test has also passed on many
+ // iterations with non-deterministic values, so it isn't "overfit" to the
+ // specific entropy in FastRandomContext(true) and implementation of the
+ // cache.
+ insecure_rand = FastRandomContext(true);
+
+ // block_activity models a chunk of network activity. n_insert elements are
+ // adde to the cache. The first and last n/4 are stored for removal later
+ // and the middle n/2 are not stored. This models a network which uses half
+ // the signatures of recently (since the last block) added transactions
+ // immediately and never uses the other half.
+ struct block_activity {
+ std::vector<uint256> reads;
+ block_activity(uint32_t n_insert, Cache& c) : reads()
+ {
+ std::vector<uint256> inserts;
+ inserts.resize(n_insert);
+ reads.reserve(n_insert / 2);
+ for (uint32_t i = 0; i < n_insert; ++i) {
+ uint32_t* ptr = (uint32_t*)inserts[i].begin();
+ for (uint8_t j = 0; j < 8; ++j)
+ *(ptr++) = insecure_rand.rand32();
+ }
+ for (uint32_t i = 0; i < n_insert / 4; ++i)
+ reads.push_back(inserts[i]);
+ for (uint32_t i = n_insert - (n_insert / 4); i < n_insert; ++i)
+ reads.push_back(inserts[i]);
+ for (auto h : inserts)
+ c.insert(h);
+ }
+ };
+
+ const uint32_t BLOCK_SIZE = 10000;
+ // We expect window size 60 to perform reasonably given that each epoch
+ // stores 45% of the cache size (~472k).
+ const uint32_t WINDOW_SIZE = 60;
+ const uint32_t POP_AMOUNT = (BLOCK_SIZE / WINDOW_SIZE) / 2;
+ const double load = 10;
+ const size_t megabytes = 32;
+ const size_t bytes = megabytes * (1 << 20);
+ const uint32_t n_insert = static_cast<uint32_t>(load * (bytes / sizeof(uint256)));
+
+ std::vector<block_activity> hashes;
+ Cache set{};
+ set.setup_bytes(bytes);
+ hashes.reserve(n_insert / BLOCK_SIZE);
+ std::deque<block_activity> last_few;
+ uint32_t out_of_tight_tolerance = 0;
+ uint32_t total = n_insert / BLOCK_SIZE;
+ // we use the deque last_few to model a sliding window of blocks. at each
+ // step, each of the last WINDOW_SIZE block_activities checks the cache for
+ // POP_AMOUNT of the hashes that they inserted, and marks these erased.
+ for (uint32_t i = 0; i < total; ++i) {
+ if (last_few.size() == WINDOW_SIZE)
+ last_few.pop_front();
+ last_few.emplace_back(BLOCK_SIZE, set);
+ uint32_t count = 0;
+ for (auto& act : last_few)
+ for (uint32_t k = 0; k < POP_AMOUNT; ++k) {
+ count += set.contains(act.reads.back(), true);
+ act.reads.pop_back();
+ }
+ // We use last_few.size() rather than WINDOW_SIZE for the correct
+ // behavior on the first WINDOW_SIZE iterations where the deque is not
+ // full yet.
+ double hit = (double(count)) / (last_few.size() * POP_AMOUNT);
+ // Loose Check that hit rate is above min_hit_rate
+ BOOST_CHECK(hit > min_hit_rate);
+ // Tighter check, count number of times we are less than tight_hit_rate
+ // (and implicityly, greater than min_hit_rate)
+ out_of_tight_tolerance += hit < tight_hit_rate;
+ }
+ // Check that being out of tolerance happens less than
+ // max_rate_less_than_tight_hit_rate of the time
+ BOOST_CHECK(double(out_of_tight_tolerance) / double(total) < max_rate_less_than_tight_hit_rate);
+}
+BOOST_AUTO_TEST_CASE(cuckoocache_generations)
+{
+ test_cache_generations<CuckooCache::cache<uint256, uint256Hasher>>();
+}
+
+BOOST_AUTO_TEST_SUITE_END();
diff --git a/src/test/data/bitcoin-util-test.json b/src/test/data/bitcoin-util-test.json
index 98be75919b..ab1a1385a3 100644
--- a/src/test/data/bitcoin-util-test.json
+++ b/src/test/data/bitcoin-util-test.json
@@ -1,24 +1,24 @@
[
{ "exec": "./bitcoin-tx",
- "args": ["-create"],
- "output_cmp": "blanktx.hex",
- "description": "Creates a blank transaction"
+ "args": ["-create", "nversion=1"],
+ "output_cmp": "blanktxv1.hex",
+ "description": "Creates a blank v1 transaction"
},
{ "exec": "./bitcoin-tx",
"args": ["-json","-create"],
- "output_cmp": "blanktx.json",
+ "output_cmp": "blanktxv2.json",
"description": "Creates a blank transaction (output in json)"
},
{ "exec": "./bitcoin-tx",
"args": ["-"],
- "input": "blanktx.hex",
- "output_cmp": "blanktx.hex",
+ "input": "blanktxv2.hex",
+ "output_cmp": "blanktxv2.hex",
"description": "Creates a blank transaction when nothing is piped into bitcoin-tx"
},
{ "exec": "./bitcoin-tx",
"args": ["-json","-"],
- "input": "blanktx.hex",
- "output_cmp": "blanktx.json",
+ "input": "blanktxv2.hex",
+ "output_cmp": "blanktxv2.json",
"description": "Creates a blank transaction when nothing is piped into bitcoin-tx (output in json)"
},
{ "exec": "./bitcoin-tx",
@@ -103,37 +103,48 @@
"description": "Creates a new transaction with a single empty output script (output in json)"
},
{ "exec": "./bitcoin-tx",
- "args": ["01000000000100000000000000000000000000"],
+ "args": ["02000000000100000000000000000000000000"],
"output_cmp": "txcreate2.hex",
"description": "Parses a transation with no inputs and a single output script"
},
{ "exec": "./bitcoin-tx",
- "args": ["-json", "01000000000100000000000000000000000000"],
+ "args": ["-json", "02000000000100000000000000000000000000"],
"output_cmp": "txcreate2.json",
"description": "Parses a transation with no inputs and a single output script (output in json)"
},
{ "exec": "./bitcoin-tx",
"args":
- ["-create",
+ ["-create", "nversion=1",
"in=4d49a71ec9da436f71ec4ee231d04f292a29cd316f598bb7068feccabdc59485:0",
"set=privatekeys:[\"5HpHagT65TZzG1PH3CSu63k8DbpvD8s5ip4nEB3kEsreAnchuDf\"]",
"set=prevtxs:[{\"txid\":\"4d49a71ec9da436f71ec4ee231d04f292a29cd316f598bb7068feccabdc59485\",\"vout\":0,\"scriptPubKey\":\"76a91491b24bf9f5288532960ac687abb035127b1d28a588ac\"}]",
"sign=ALL",
"outaddr=0.001:193P6LtvS4nCnkDvM9uXn1gsSRqh4aDAz7"],
- "output_cmp": "txcreatesign.hex",
- "description": "Creates a new transaction with a single input and a single output, and then signs the transaction"
+ "output_cmp": "txcreatesignv1.hex",
+ "description": "Creates a new v1 transaction with a single input and a single output, and then signs the transaction"
},
{ "exec": "./bitcoin-tx",
"args":
["-json",
- "-create",
+ "-create", "nversion=1",
"in=4d49a71ec9da436f71ec4ee231d04f292a29cd316f598bb7068feccabdc59485:0",
"set=privatekeys:[\"5HpHagT65TZzG1PH3CSu63k8DbpvD8s5ip4nEB3kEsreAnchuDf\"]",
"set=prevtxs:[{\"txid\":\"4d49a71ec9da436f71ec4ee231d04f292a29cd316f598bb7068feccabdc59485\",\"vout\":0,\"scriptPubKey\":\"76a91491b24bf9f5288532960ac687abb035127b1d28a588ac\"}]",
"sign=ALL",
"outaddr=0.001:193P6LtvS4nCnkDvM9uXn1gsSRqh4aDAz7"],
- "output_cmp": "txcreatesign.json",
- "description": "Creates a new transaction with a single input and a single output, and then signs the transaction (output in json)"
+ "output_cmp": "txcreatesignv1.json",
+ "description": "Creates a new v1 transaction with a single input and a single output, and then signs the transaction (output in json)"
+ },
+ { "exec": "./bitcoin-tx",
+ "args":
+ ["-create",
+ "in=4d49a71ec9da436f71ec4ee231d04f292a29cd316f598bb7068feccabdc59485:0",
+ "set=privatekeys:[\"5HpHagT65TZzG1PH3CSu63k8DbpvD8s5ip4nEB3kEsreAnchuDf\"]",
+ "set=prevtxs:[{\"txid\":\"4d49a71ec9da436f71ec4ee231d04f292a29cd316f598bb7068feccabdc59485\",\"vout\":0,\"scriptPubKey\":\"76a91491b24bf9f5288532960ac687abb035127b1d28a588ac\"}]",
+ "sign=ALL",
+ "outaddr=0.001:193P6LtvS4nCnkDvM9uXn1gsSRqh4aDAz7"],
+ "output_cmp": "txcreatesignv2.hex",
+ "description": "Creates a new transaction with a single input and a single output, and then signs the transaction"
},
{ "exec": "./bitcoin-tx",
"args":
@@ -163,12 +174,12 @@
{ "exec": "./bitcoin-tx",
"args":
["-json",
- "-create",
+ "-create", "nversion=1",
"in=5897de6bd6027a475eadd57019d4e6872c396d0716c4875a5f1a6fcfdf385c1f:0",
"outaddr=0.18:13tuJJDR2RgArmgfv6JScSdreahzgc4T6o",
"outdata=4:54686973204f505f52455455524e207472616e73616374696f6e206f7574707574207761732063726561746564206279206d6f646966696564206372656174657261777472616e73616374696f6e2e"],
"output_cmp": "txcreatedata1.json",
- "description": "Creates a new transaction with one input, one address output and one data output (output in json)"
+ "description": "Creates a new v1 transaction with one input, one address output and one data output (output in json)"
},
{ "exec": "./bitcoin-tx",
"args":
diff --git a/src/test/data/blanktx.hex b/src/test/data/blanktxv1.hex
index 36b6f00fb6..36b6f00fb6 100644
--- a/src/test/data/blanktx.hex
+++ b/src/test/data/blanktxv1.hex
diff --git a/src/test/data/blanktxv2.hex b/src/test/data/blanktxv2.hex
new file mode 100644
index 0000000000..22d830eda1
--- /dev/null
+++ b/src/test/data/blanktxv2.hex
@@ -0,0 +1 @@
+02000000000000000000
diff --git a/src/test/data/blanktxv2.json b/src/test/data/blanktxv2.json
new file mode 100644
index 0000000000..266919f445
--- /dev/null
+++ b/src/test/data/blanktxv2.json
@@ -0,0 +1,11 @@
+{
+ "txid": "4ebd325a4b394cff8c57e8317ccf5a8d0e2bdf1b8526f8aad6c8e43d8240621a",
+ "hash": "4ebd325a4b394cff8c57e8317ccf5a8d0e2bdf1b8526f8aad6c8e43d8240621a",
+ "version": 2,
+ "locktime": 0,
+ "vin": [
+ ],
+ "vout": [
+ ],
+ "hex": "02000000000000000000"
+}
diff --git a/src/test/data/txcreate1.hex b/src/test/data/txcreate1.hex
index e2981a51c9..9ec6ee3531 100644
--- a/src/test/data/txcreate1.hex
+++ b/src/test/data/txcreate1.hex
@@ -1 +1 @@
-01000000031f5c38dfcf6f1a5f5a87c416076d392c87e6d41970d5ad5e477a02d66bde97580000000000ffffffff7cca453133921c50d5025878f7f738d1df891fd359763331935784cf6b9c82bf1200000000fffffffffccd319e04a996c96cfc0bf4c07539aa90bd0b1a700ef72fae535d6504f9a6220100000000ffffffff0280a81201000000001976a9141fc11f39be1729bf973a7ab6a615ca4729d6457488ac0084d717000000001976a914f2d4db28cad6502226ee484ae24505c2885cb12d88ac00000000
+02000000031f5c38dfcf6f1a5f5a87c416076d392c87e6d41970d5ad5e477a02d66bde97580000000000ffffffff7cca453133921c50d5025878f7f738d1df891fd359763331935784cf6b9c82bf1200000000fffffffffccd319e04a996c96cfc0bf4c07539aa90bd0b1a700ef72fae535d6504f9a6220100000000ffffffff0280a81201000000001976a9141fc11f39be1729bf973a7ab6a615ca4729d6457488ac0084d717000000001976a914f2d4db28cad6502226ee484ae24505c2885cb12d88ac00000000
diff --git a/src/test/data/txcreate1.json b/src/test/data/txcreate1.json
index 567e8026a3..f83e036f33 100644
--- a/src/test/data/txcreate1.json
+++ b/src/test/data/txcreate1.json
@@ -1,7 +1,7 @@
{
- "txid": "f70f0d6c71416ed538e37549f430ab3665fee2437a42f10238c1bd490e782231",
- "hash": "f70f0d6c71416ed538e37549f430ab3665fee2437a42f10238c1bd490e782231",
- "version": 1,
+ "txid": "fe7d174f42dce0cffa7a527e9bc8368956057619ec817648f6138b98f2533e8f",
+ "hash": "fe7d174f42dce0cffa7a527e9bc8368956057619ec817648f6138b98f2533e8f",
+ "version": 2,
"locktime": 0,
"vin": [
{
@@ -60,5 +60,5 @@
}
}
],
- "hex": "01000000031f5c38dfcf6f1a5f5a87c416076d392c87e6d41970d5ad5e477a02d66bde97580000000000ffffffff7cca453133921c50d5025878f7f738d1df891fd359763331935784cf6b9c82bf1200000000fffffffffccd319e04a996c96cfc0bf4c07539aa90bd0b1a700ef72fae535d6504f9a6220100000000ffffffff0280a81201000000001976a9141fc11f39be1729bf973a7ab6a615ca4729d6457488ac0084d717000000001976a914f2d4db28cad6502226ee484ae24505c2885cb12d88ac00000000"
+ "hex": "02000000031f5c38dfcf6f1a5f5a87c416076d392c87e6d41970d5ad5e477a02d66bde97580000000000ffffffff7cca453133921c50d5025878f7f738d1df891fd359763331935784cf6b9c82bf1200000000fffffffffccd319e04a996c96cfc0bf4c07539aa90bd0b1a700ef72fae535d6504f9a6220100000000ffffffff0280a81201000000001976a9141fc11f39be1729bf973a7ab6a615ca4729d6457488ac0084d717000000001976a914f2d4db28cad6502226ee484ae24505c2885cb12d88ac00000000"
}
diff --git a/src/test/data/txcreate2.hex b/src/test/data/txcreate2.hex
index 5243c2d02e..38bb7b1046 100644
--- a/src/test/data/txcreate2.hex
+++ b/src/test/data/txcreate2.hex
@@ -1 +1 @@
-01000000000100000000000000000000000000
+02000000000100000000000000000000000000
diff --git a/src/test/data/txcreate2.json b/src/test/data/txcreate2.json
index a70c1d302a..fb5e177db7 100644
--- a/src/test/data/txcreate2.json
+++ b/src/test/data/txcreate2.json
@@ -1,7 +1,7 @@
{
- "txid": "cf90229625e9eb10f6be8156bf6aa5ec2eca19a42b1e05c11f3029b560a32e13",
- "hash": "cf90229625e9eb10f6be8156bf6aa5ec2eca19a42b1e05c11f3029b560a32e13",
- "version": 1,
+ "txid": "0481afb29931341d0d7861d8a2f6f26456fa042abf54a23e96440ed7946e0715",
+ "hash": "0481afb29931341d0d7861d8a2f6f26456fa042abf54a23e96440ed7946e0715",
+ "version": 2,
"locktime": 0,
"vin": [
],
@@ -16,5 +16,5 @@
}
}
],
- "hex": "01000000000100000000000000000000000000"
+ "hex": "02000000000100000000000000000000000000"
}
diff --git a/src/test/data/txcreatedata1.hex b/src/test/data/txcreatedata1.hex
index eccc7604e6..cefd1a05a6 100644
--- a/src/test/data/txcreatedata1.hex
+++ b/src/test/data/txcreatedata1.hex
@@ -1 +1 @@
-01000000011f5c38dfcf6f1a5f5a87c416076d392c87e6d41970d5ad5e477a02d66bde97580000000000ffffffff0280a81201000000001976a9141fc11f39be1729bf973a7ab6a615ca4729d6457488ac0084d71700000000526a4c4f54686973204f505f52455455524e207472616e73616374696f6e206f7574707574207761732063726561746564206279206d6f646966696564206372656174657261777472616e73616374696f6e2e00000000
+02000000011f5c38dfcf6f1a5f5a87c416076d392c87e6d41970d5ad5e477a02d66bde97580000000000ffffffff0280a81201000000001976a9141fc11f39be1729bf973a7ab6a615ca4729d6457488ac0084d71700000000526a4c4f54686973204f505f52455455524e207472616e73616374696f6e206f7574707574207761732063726561746564206279206d6f646966696564206372656174657261777472616e73616374696f6e2e00000000
diff --git a/src/test/data/txcreatedata2.hex b/src/test/data/txcreatedata2.hex
index 3c7644c297..d69cf58ba1 100644
--- a/src/test/data/txcreatedata2.hex
+++ b/src/test/data/txcreatedata2.hex
@@ -1 +1 @@
-01000000011f5c38dfcf6f1a5f5a87c416076d392c87e6d41970d5ad5e477a02d66bde97580000000000ffffffff0280a81201000000001976a9141fc11f39be1729bf973a7ab6a615ca4729d6457488ac0000000000000000526a4c4f54686973204f505f52455455524e207472616e73616374696f6e206f7574707574207761732063726561746564206279206d6f646966696564206372656174657261777472616e73616374696f6e2e00000000
+02000000011f5c38dfcf6f1a5f5a87c416076d392c87e6d41970d5ad5e477a02d66bde97580000000000ffffffff0280a81201000000001976a9141fc11f39be1729bf973a7ab6a615ca4729d6457488ac0000000000000000526a4c4f54686973204f505f52455455524e207472616e73616374696f6e206f7574707574207761732063726561746564206279206d6f646966696564206372656174657261777472616e73616374696f6e2e00000000
diff --git a/src/test/data/txcreatedata2.json b/src/test/data/txcreatedata2.json
index 56dfe4a1b0..3c6da40f90 100644
--- a/src/test/data/txcreatedata2.json
+++ b/src/test/data/txcreatedata2.json
@@ -1,7 +1,7 @@
{
- "txid": "4ed17118f5e932ba8c75c461787d171bc02a016d8557cb5bcf34cd416c27bb8b",
- "hash": "4ed17118f5e932ba8c75c461787d171bc02a016d8557cb5bcf34cd416c27bb8b",
- "version": 1,
+ "txid": "c14b007fa3a6c1e7765919c1d14c1cfc2b8642c3a5d3be4b1fa8c4ccfec98bb0",
+ "hash": "c14b007fa3a6c1e7765919c1d14c1cfc2b8642c3a5d3be4b1fa8c4ccfec98bb0",
+ "version": 2,
"locktime": 0,
"vin": [
{
@@ -38,5 +38,5 @@
}
}
],
- "hex": "01000000011f5c38dfcf6f1a5f5a87c416076d392c87e6d41970d5ad5e477a02d66bde97580000000000ffffffff0280a81201000000001976a9141fc11f39be1729bf973a7ab6a615ca4729d6457488ac0000000000000000526a4c4f54686973204f505f52455455524e207472616e73616374696f6e206f7574707574207761732063726561746564206279206d6f646966696564206372656174657261777472616e73616374696f6e2e00000000"
+ "hex": "02000000011f5c38dfcf6f1a5f5a87c416076d392c87e6d41970d5ad5e477a02d66bde97580000000000ffffffff0280a81201000000001976a9141fc11f39be1729bf973a7ab6a615ca4729d6457488ac0000000000000000526a4c4f54686973204f505f52455455524e207472616e73616374696f6e206f7574707574207761732063726561746564206279206d6f646966696564206372656174657261777472616e73616374696f6e2e00000000"
}
diff --git a/src/test/data/txcreatedata_seq0.hex b/src/test/data/txcreatedata_seq0.hex
index db02b5e4a4..54b89d2381 100644
--- a/src/test/data/txcreatedata_seq0.hex
+++ b/src/test/data/txcreatedata_seq0.hex
@@ -1 +1 @@
-01000000011f5c38dfcf6f1a5f5a87c416076d392c87e6d41970d5ad5e477a02d66bde97580000000000fdffffff0180a81201000000001976a9141fc11f39be1729bf973a7ab6a615ca4729d6457488ac00000000
+02000000011f5c38dfcf6f1a5f5a87c416076d392c87e6d41970d5ad5e477a02d66bde97580000000000fdffffff0180a81201000000001976a9141fc11f39be1729bf973a7ab6a615ca4729d6457488ac00000000
diff --git a/src/test/data/txcreatedata_seq0.json b/src/test/data/txcreatedata_seq0.json
index 9bc0ed4593..d272a4c447 100644
--- a/src/test/data/txcreatedata_seq0.json
+++ b/src/test/data/txcreatedata_seq0.json
@@ -1,7 +1,7 @@
{
- "txid": "71603ccb1cd76d73d76eb6cfd5f0b9df6d65d90d76860ee52cb461c4be7032e8",
- "hash": "71603ccb1cd76d73d76eb6cfd5f0b9df6d65d90d76860ee52cb461c4be7032e8",
- "version": 1,
+ "txid": "8df6ed527472542dd5e137c242a7c5a9f337ac34f7b257ae4af886aeaebb51b0",
+ "hash": "8df6ed527472542dd5e137c242a7c5a9f337ac34f7b257ae4af886aeaebb51b0",
+ "version": 2,
"locktime": 0,
"vin": [
{
@@ -29,5 +29,5 @@
}
}
],
- "hex": "01000000011f5c38dfcf6f1a5f5a87c416076d392c87e6d41970d5ad5e477a02d66bde97580000000000fdffffff0180a81201000000001976a9141fc11f39be1729bf973a7ab6a615ca4729d6457488ac00000000"
+ "hex": "02000000011f5c38dfcf6f1a5f5a87c416076d392c87e6d41970d5ad5e477a02d66bde97580000000000fdffffff0180a81201000000001976a9141fc11f39be1729bf973a7ab6a615ca4729d6457488ac00000000"
}
diff --git a/src/test/data/txcreatesign.hex b/src/test/data/txcreatesignv1.hex
index a46fcc88cb..a46fcc88cb 100644
--- a/src/test/data/txcreatesign.hex
+++ b/src/test/data/txcreatesignv1.hex
diff --git a/src/test/data/txcreatesignv1.json b/src/test/data/txcreatesignv1.json
new file mode 100644
index 0000000000..ff39e71b40
--- /dev/null
+++ b/src/test/data/txcreatesignv1.json
@@ -0,0 +1,33 @@
+{
+ "txid": "977e7cd286cb72cd470d539ba6cb48400f8f387d97451d45cdb8819437a303af",
+ "hash": "977e7cd286cb72cd470d539ba6cb48400f8f387d97451d45cdb8819437a303af",
+ "version": 1,
+ "locktime": 0,
+ "vin": [
+ {
+ "txid": "4d49a71ec9da436f71ec4ee231d04f292a29cd316f598bb7068feccabdc59485",
+ "vout": 0,
+ "scriptSig": {
+ "asm": "304502210096a75056c9e2cc62b7214777b3d2a592cfda7092520126d4ebfcd6d590c99bd8022051bb746359cf98c0603f3004477eac68701132380db8facba19c89dc5ab5c5e2[ALL] 0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8",
+ "hex": "48304502210096a75056c9e2cc62b7214777b3d2a592cfda7092520126d4ebfcd6d590c99bd8022051bb746359cf98c0603f3004477eac68701132380db8facba19c89dc5ab5c5e201410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8"
+ },
+ "sequence": 4294967295
+ }
+ ],
+ "vout": [
+ {
+ "value": 0.001,
+ "n": 0,
+ "scriptPubKey": {
+ "asm": "OP_DUP OP_HASH160 5834479edbbe0539b31ffd3a8f8ebadc2165ed01 OP_EQUALVERIFY OP_CHECKSIG",
+ "hex": "76a9145834479edbbe0539b31ffd3a8f8ebadc2165ed0188ac",
+ "reqSigs": 1,
+ "type": "pubkeyhash",
+ "addresses": [
+ "193P6LtvS4nCnkDvM9uXn1gsSRqh4aDAz7"
+ ]
+ }
+ }
+ ],
+ "hex": "01000000018594c5bdcaec8f06b78b596f31cd292a294fd031e24eec716f43dac91ea7494d000000008b48304502210096a75056c9e2cc62b7214777b3d2a592cfda7092520126d4ebfcd6d590c99bd8022051bb746359cf98c0603f3004477eac68701132380db8facba19c89dc5ab5c5e201410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8ffffffff01a0860100000000001976a9145834479edbbe0539b31ffd3a8f8ebadc2165ed0188ac00000000"
+}
diff --git a/src/test/data/txcreatesignv2.hex b/src/test/data/txcreatesignv2.hex
new file mode 100644
index 0000000000..ee425cd98c
--- /dev/null
+++ b/src/test/data/txcreatesignv2.hex
@@ -0,0 +1 @@
+02000000018594c5bdcaec8f06b78b596f31cd292a294fd031e24eec716f43dac91ea7494d000000008a473044022079c7aa014177a2e973caf6df7c7b8f15399083b91eba370ea1e19c4caed9181e02205f8f8763505ce8e6cbdd2cd28fab3fd407a75003e7d0dc04e6bebb0a3c89e7cb01410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8ffffffff01a0860100000000001976a9145834479edbbe0539b31ffd3a8f8ebadc2165ed0188ac00000000
diff --git a/src/test/hash_tests.cpp b/src/test/hash_tests.cpp
index fa9624f13d..8be9c580c4 100644
--- a/src/test/hash_tests.cpp
+++ b/src/test/hash_tests.cpp
@@ -124,7 +124,11 @@ BOOST_AUTO_TEST_CASE(siphash)
}
CHashWriter ss(SER_DISK, CLIENT_VERSION);
- ss << CTransaction();
+ CMutableTransaction tx;
+ // Note these tests were originally written with tx.nVersion=1
+ // and the test would be affected by default tx version bumps if not fixed.
+ tx.nVersion = 1;
+ ss << tx;
BOOST_CHECK_EQUAL(SipHashUint256(1, 2, ss.GetHash()), 0x79751e980c2a0a35ULL);
}
diff --git a/src/test/mempool_tests.cpp b/src/test/mempool_tests.cpp
index bdd6eaf538..84871600b2 100644
--- a/src/test/mempool_tests.cpp
+++ b/src/test/mempool_tests.cpp
@@ -390,7 +390,12 @@ BOOST_AUTO_TEST_CASE(MempoolAncestorIndexingTest)
pool.addUnchecked(tx6.GetHash(), entry.Fee(0LL).FromTx(tx6));
BOOST_CHECK_EQUAL(pool.size(), 6);
- sortedOrder.push_back(tx6.GetHash().ToString());
+ // Ties are broken by hash
+ if (tx3.GetHash() < tx6.GetHash())
+ sortedOrder.push_back(tx6.GetHash().ToString());
+ else
+ sortedOrder.insert(sortedOrder.end()-1,tx6.GetHash().ToString());
+
CheckSort<ancestor_score>(pool, sortedOrder);
CMutableTransaction tx7 = CMutableTransaction();
@@ -417,7 +422,11 @@ BOOST_AUTO_TEST_CASE(MempoolAncestorIndexingTest)
pool.removeForBlock(vtx, 1);
sortedOrder.erase(sortedOrder.begin()+1);
- sortedOrder.pop_back();
+ // Ties are broken by hash
+ if (tx3.GetHash() < tx6.GetHash())
+ sortedOrder.pop_back();
+ else
+ sortedOrder.erase(sortedOrder.end()-2);
sortedOrder.insert(sortedOrder.begin(), tx7.GetHash().ToString());
CheckSort<ancestor_score>(pool, sortedOrder);
}
diff --git a/src/test/test_bitcoin.cpp b/src/test/test_bitcoin.cpp
index dc459bed0d..2a5a78de02 100644
--- a/src/test/test_bitcoin.cpp
+++ b/src/test/test_bitcoin.cpp
@@ -20,6 +20,7 @@
#include "ui_interface.h"
#include "rpc/server.h"
#include "rpc/register.h"
+#include "script/sigcache.h"
#include "test/testutil.h"
@@ -40,6 +41,7 @@ BasicTestingSetup::BasicTestingSetup(const std::string& chainName)
ECC_Start();
SetupEnvironment();
SetupNetworking();
+ InitSignatureCache();
fPrintToDebugLog = false; // don't want to write to debug.log file
fCheckBlockIndex = true;
SelectParams(chainName);
diff --git a/src/test/test_bitcoin_fuzzy.cpp b/src/test/test_bitcoin_fuzzy.cpp
new file mode 100644
index 0000000000..584e6ed008
--- /dev/null
+++ b/src/test/test_bitcoin_fuzzy.cpp
@@ -0,0 +1,256 @@
+// Copyright (c) 2009-2015 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#if defined(HAVE_CONFIG_H)
+#include "config/bitcoin-config.h"
+#endif
+
+#include "consensus/merkle.h"
+#include "primitives/block.h"
+#include "script/script.h"
+#include "addrman.h"
+#include "chain.h"
+#include "coins.h"
+#include "compressor.h"
+#include "net.h"
+#include "protocol.h"
+#include "streams.h"
+#include "undo.h"
+#include "version.h"
+
+#include <stdint.h>
+#include <unistd.h>
+
+#include <algorithm>
+#include <vector>
+
+enum TEST_ID {
+ CBLOCK_DESERIALIZE=0,
+ CTRANSACTION_DESERIALIZE,
+ CBLOCKLOCATOR_DESERIALIZE,
+ CBLOCKMERKLEROOT,
+ CADDRMAN_DESERIALIZE,
+ CBLOCKHEADER_DESERIALIZE,
+ CBANENTRY_DESERIALIZE,
+ CTXUNDO_DESERIALIZE,
+ CBLOCKUNDO_DESERIALIZE,
+ CCOINS_DESERIALIZE,
+ CNETADDR_DESERIALIZE,
+ CSERVICE_DESERIALIZE,
+ CMESSAGEHEADER_DESERIALIZE,
+ CADDRESS_DESERIALIZE,
+ CINV_DESERIALIZE,
+ CBLOOMFILTER_DESERIALIZE,
+ CDISKBLOCKINDEX_DESERIALIZE,
+ CTXOUTCOMPRESSOR_DESERIALIZE,
+ TEST_ID_END
+};
+
+bool read_stdin(std::vector<char> &data) {
+ char buffer[1024];
+ ssize_t length=0;
+ while((length = read(STDIN_FILENO, buffer, 1024)) > 0) {
+ data.insert(data.end(), buffer, buffer+length);
+
+ if (data.size() > (1<<20)) return false;
+ }
+ return length==0;
+}
+
+int main(int argc, char **argv)
+{
+ std::vector<char> buffer;
+ if (!read_stdin(buffer)) return 0;
+
+ if (buffer.size() < sizeof(uint32_t)) return 0;
+
+ uint32_t test_id = 0xffffffff;
+ memcpy(&test_id, &buffer[0], sizeof(uint32_t));
+ buffer.erase(buffer.begin(), buffer.begin() + sizeof(uint32_t));
+
+ if (test_id >= TEST_ID_END) return 0;
+
+ CDataStream ds(buffer, SER_NETWORK, INIT_PROTO_VERSION);
+ try {
+ int nVersion;
+ ds >> nVersion;
+ ds.SetVersion(nVersion);
+ } catch (const std::ios_base::failure& e) {
+ return 0;
+ }
+
+ switch(test_id) {
+ case CBLOCK_DESERIALIZE:
+ {
+ try
+ {
+ CBlock block;
+ ds >> block;
+ } catch (const std::ios_base::failure& e) {return 0;}
+ break;
+ }
+ case CTRANSACTION_DESERIALIZE:
+ {
+ try
+ {
+ CTransaction tx(deserialize, ds);
+ } catch (const std::ios_base::failure& e) {return 0;}
+ break;
+ }
+ case CBLOCKLOCATOR_DESERIALIZE:
+ {
+ try
+ {
+ CBlockLocator bl;
+ ds >> bl;
+ } catch (const std::ios_base::failure& e) {return 0;}
+ break;
+ }
+ case CBLOCKMERKLEROOT:
+ {
+ try
+ {
+ CBlock block;
+ ds >> block;
+ bool mutated;
+ BlockMerkleRoot(block, &mutated);
+ } catch (const std::ios_base::failure& e) {return 0;}
+ break;
+ }
+ case CADDRMAN_DESERIALIZE:
+ {
+ try
+ {
+ CAddrMan am;
+ ds >> am;
+ } catch (const std::ios_base::failure& e) {return 0;}
+ break;
+ }
+ case CBLOCKHEADER_DESERIALIZE:
+ {
+ try
+ {
+ CBlockHeader bh;
+ ds >> bh;
+ } catch (const std::ios_base::failure& e) {return 0;}
+ break;
+ }
+ case CBANENTRY_DESERIALIZE:
+ {
+ try
+ {
+ CBanEntry be;
+ ds >> be;
+ } catch (const std::ios_base::failure& e) {return 0;}
+ break;
+ }
+ case CTXUNDO_DESERIALIZE:
+ {
+ try
+ {
+ CTxUndo tu;
+ ds >> tu;
+ } catch (const std::ios_base::failure& e) {return 0;}
+ break;
+ }
+ case CBLOCKUNDO_DESERIALIZE:
+ {
+ try
+ {
+ CBlockUndo bu;
+ ds >> bu;
+ } catch (const std::ios_base::failure& e) {return 0;}
+ break;
+ }
+ case CCOINS_DESERIALIZE:
+ {
+ try
+ {
+ CCoins block;
+ ds >> block;
+ } catch (const std::ios_base::failure& e) {return 0;}
+ break;
+ }
+ case CNETADDR_DESERIALIZE:
+ {
+ try
+ {
+ CNetAddr na;
+ ds >> na;
+ } catch (const std::ios_base::failure& e) {return 0;}
+ break;
+ }
+ case CSERVICE_DESERIALIZE:
+ {
+ try
+ {
+ CService s;
+ ds >> s;
+ } catch (const std::ios_base::failure& e) {return 0;}
+ break;
+ }
+ case CMESSAGEHEADER_DESERIALIZE:
+ {
+ CMessageHeader::MessageStartChars pchMessageStart = {0x00, 0x00, 0x00, 0x00};
+ try
+ {
+ CMessageHeader mh(pchMessageStart);
+ ds >> mh;
+ if (!mh.IsValid(pchMessageStart)) {return 0;}
+ } catch (const std::ios_base::failure& e) {return 0;}
+ break;
+ }
+ case CADDRESS_DESERIALIZE:
+ {
+ try
+ {
+ CAddress a;
+ ds >> a;
+ } catch (const std::ios_base::failure& e) {return 0;}
+ break;
+ }
+ case CINV_DESERIALIZE:
+ {
+ try
+ {
+ CInv i;
+ ds >> i;
+ } catch (const std::ios_base::failure& e) {return 0;}
+ break;
+ }
+ case CBLOOMFILTER_DESERIALIZE:
+ {
+ try
+ {
+ CBloomFilter bf;
+ ds >> bf;
+ } catch (const std::ios_base::failure& e) {return 0;}
+ break;
+ }
+ case CDISKBLOCKINDEX_DESERIALIZE:
+ {
+ try
+ {
+ CDiskBlockIndex dbi;
+ ds >> dbi;
+ } catch (const std::ios_base::failure& e) {return 0;}
+ break;
+ }
+ case CTXOUTCOMPRESSOR_DESERIALIZE:
+ {
+ CTxOut to;
+ CTxOutCompressor toc(to);
+ try
+ {
+ ds >> toc;
+ } catch (const std::ios_base::failure& e) {return 0;}
+
+ break;
+ }
+ default:
+ return 0;
+ }
+ return 0;
+}
+
diff --git a/src/test/txvalidationcache_tests.cpp b/src/test/txvalidationcache_tests.cpp
index 55b4d28fb2..9f58f004d5 100644
--- a/src/test/txvalidationcache_tests.cpp
+++ b/src/test/txvalidationcache_tests.cpp
@@ -39,6 +39,7 @@ BOOST_FIXTURE_TEST_CASE(tx_mempool_block_doublespend, TestChain100Setup)
spends.resize(2);
for (int i = 0; i < 2; i++)
{
+ spends[i].nVersion = 1;
spends[i].vin.resize(1);
spends[i].vin[0].prevout.hash = coinbaseTxns[0].GetHash();
spends[i].vin[0].prevout.n = 0;