diff options
Diffstat (limited to 'src')
124 files changed, 6191 insertions, 1907 deletions
diff --git a/src/Makefile.am b/src/Makefile.am index 3c056386fa..3df8e267bb 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -15,13 +15,12 @@ LIBUNIVALUE = $(UNIVALUE_LIBS) endif BITCOIN_CONFIG_INCLUDES=-I$(builddir)/config -BITCOIN_INCLUDES=-I$(builddir) -I$(builddir)/obj $(BOOST_CPPFLAGS) $(LEVELDB_CPPFLAGS) $(CRYPTO_CFLAGS) $(SSL_CFLAGS) +BITCOIN_INCLUDES=-I$(builddir) -I$(builddir)/obj $(BDB_CPPFLAGS) $(BOOST_CPPFLAGS) $(LEVELDB_CPPFLAGS) $(CRYPTO_CFLAGS) $(SSL_CFLAGS) BITCOIN_INCLUDES += -I$(srcdir)/secp256k1/include BITCOIN_INCLUDES += $(UNIVALUE_CFLAGS) LIBBITCOIN_SERVER=libbitcoin_server.a -LIBBITCOIN_WALLET=libbitcoin_wallet.a LIBBITCOIN_COMMON=libbitcoin_common.a LIBBITCOIN_CONSENSUS=libbitcoin_consensus.a LIBBITCOIN_CLI=libbitcoin_cli.a @@ -30,32 +29,32 @@ LIBBITCOIN_CRYPTO=crypto/libbitcoin_crypto.a LIBBITCOINQT=qt/libbitcoinqt.a LIBSECP256K1=secp256k1/libsecp256k1.la +if ENABLE_ZMQ +LIBBITCOIN_ZMQ=libbitcoin_zmq.a +endif +if BUILD_BITCOIN_LIBS +LIBBITCOINCONSENSUS=libbitcoinconsensus.la +endif +if ENABLE_WALLET +LIBBITCOIN_WALLET=libbitcoin_wallet.a +endif + $(LIBSECP256K1): $(wildcard secp256k1/src/*) $(wildcard secp256k1/include/*) $(AM_V_at)$(MAKE) $(AM_MAKEFLAGS) -C $(@D) $(@F) # Make is not made aware of per-object dependencies to avoid limiting building parallelization # But to build the less dependent modules first, we manually select their order here: EXTRA_LIBRARIES += \ - crypto/libbitcoin_crypto.a \ - libbitcoin_util.a \ - libbitcoin_common.a \ - libbitcoin_consensus.a \ - libbitcoin_server.a \ - libbitcoin_cli.a -if ENABLE_WALLET -BITCOIN_INCLUDES += $(BDB_CPPFLAGS) -EXTRA_LIBRARIES += libbitcoin_wallet.a -endif -if ENABLE_ZMQ -EXTRA_LIBRARIES += libbitcoin_zmq.a -endif + $(LIBBITCOIN_CRYPTO) \ + $(LIBBITCOIN_UTIL) \ + $(LIBBITCOIN_COMMON) \ + $(LIBBITCOIN_CONSENSUS) \ + $(LIBBITCOIN_SERVER) \ + $(LIBBITCOIN_CLI) \ + $(LIBBITCOIN_WALLET) \ + $(LIBBITCOIN_ZMQ) -if BUILD_BITCOIN_LIBS -lib_LTLIBRARIES = libbitcoinconsensus.la -LIBBITCOINCONSENSUS=libbitcoinconsensus.la -else -LIBBITCOINCONSENSUS= -endif +lib_LTLIBRARIES = $(LIBBITCOINCONSENSUS) bin_PROGRAMS = TESTS = @@ -94,6 +93,7 @@ BITCOIN_CORE_H = \ core_memusage.h \ httprpc.h \ httpserver.h \ + indirectmap.h \ init.h \ key.h \ keystore.h \ @@ -196,8 +196,6 @@ libbitcoin_server_a_SOURCES = \ $(BITCOIN_CORE_H) if ENABLE_ZMQ -LIBBITCOIN_ZMQ=libbitcoin_zmq.a - libbitcoin_zmq_a_CPPFLAGS = $(BITCOIN_INCLUDES) $(ZMQ_CFLAGS) libbitcoin_zmq_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) libbitcoin_zmq_a_SOURCES = \ @@ -225,6 +223,8 @@ libbitcoin_wallet_a_SOURCES = \ crypto_libbitcoin_crypto_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_CONFIG_INCLUDES) crypto_libbitcoin_crypto_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) crypto_libbitcoin_crypto_a_SOURCES = \ + crypto/aes.cpp \ + crypto/aes.h \ crypto/common.h \ crypto/hmac_sha256.cpp \ crypto/hmac_sha256.h \ @@ -345,21 +345,15 @@ bitcoind_LDADD = \ $(LIBBITCOIN_COMMON) \ $(LIBUNIVALUE) \ $(LIBBITCOIN_UTIL) \ + $(LIBBITCOIN_WALLET) \ + $(LIBBITCOIN_ZMQ) \ $(LIBBITCOIN_CONSENSUS) \ $(LIBBITCOIN_CRYPTO) \ $(LIBLEVELDB) \ $(LIBMEMENV) \ $(LIBSECP256K1) -if ENABLE_ZMQ -bitcoind_LDADD += $(LIBBITCOIN_ZMQ) $(ZMQ_LIBS) -endif - -if ENABLE_WALLET -bitcoind_LDADD += libbitcoin_wallet.a -endif - -bitcoind_LDADD += $(BOOST_LIBS) $(BDB_LIBS) $(SSL_LIBS) $(CRYPTO_LIBS) $(MINIUPNPC_LIBS) $(EVENT_PTHREADS_LIBS) $(EVENT_LIBS) +bitcoind_LDADD += $(BOOST_LIBS) $(BDB_LIBS) $(SSL_LIBS) $(CRYPTO_LIBS) $(MINIUPNPC_LIBS) $(EVENT_PTHREADS_LIBS) $(EVENT_LIBS) $(ZMQ_LIBS) # bitcoin-cli binary # bitcoin_cli_SOURCES = bitcoin-cli.cpp @@ -374,7 +368,8 @@ endif bitcoin_cli_LDADD = \ $(LIBBITCOIN_CLI) \ $(LIBUNIVALUE) \ - $(LIBBITCOIN_UTIL) + $(LIBBITCOIN_UTIL) \ + $(LIBBITCOIN_CRYPTO) bitcoin_cli_LDADD += $(BOOST_LIBS) $(SSL_LIBS) $(CRYPTO_LIBS) $(EVENT_LIBS) # @@ -417,8 +412,14 @@ libbitcoinconsensus_la_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) endif # -CLEANFILES = leveldb/libleveldb.a leveldb/libmemenv.a -CLEANFILES += $(EXTRA_LIBRARIES) +CTAES_DIST = crypto/ctaes/bench.c +CTAES_DIST += crypto/ctaes/ctaes.c +CTAES_DIST += crypto/ctaes/ctaes.h +CTAES_DIST += crypto/ctaes/README.md +CTAES_DIST += crypto/ctaes/test.c + +CLEANFILES = $(EXTRA_LIBRARIES) + CLEANFILES += *.gcda *.gcno CLEANFILES += compat/*.gcda compat/*.gcno CLEANFILES += consensus/*.gcda consensus/*.gcno @@ -434,14 +435,14 @@ CLEANFILES += zmq/*.gcda zmq/*.gcno DISTCLEANFILES = obj/build.h -EXTRA_DIST = leveldb +EXTRA_DIST = $(CTAES_DIST) clean-local: - -$(MAKE) -C leveldb clean -$(MAKE) -C secp256k1 clean -$(MAKE) -C univalue clean -rm -f leveldb/*/*.gcda leveldb/*/*.gcno leveldb/helpers/memenv/*.gcda leveldb/helpers/memenv/*.gcno -rm -f config.h + -rm -rf test/__pycache__ .rc.o: @test -f $(WINDRES) diff --git a/src/Makefile.bench.include b/src/Makefile.bench.include index 65fd24e051..4067ceb399 100644 --- a/src/Makefile.bench.include +++ b/src/Makefile.bench.include @@ -9,7 +9,8 @@ bench_bench_bitcoin_SOURCES = \ bench/bench.h \ bench/Examples.cpp \ bench/rollingbloom.cpp \ - bench/crypto_hash.cpp + bench/crypto_hash.cpp \ + bench/base58.cpp bench_bench_bitcoin_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) $(EVENT_CLFAGS) $(EVENT_PTHREADS_CFLAGS) -I$(builddir)/bench/ bench_bench_bitcoin_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) diff --git a/src/Makefile.leveldb.include b/src/Makefile.leveldb.include index 88bb0c1932..4b3cd6364a 100644 --- a/src/Makefile.leveldb.include +++ b/src/Makefile.leveldb.include @@ -26,6 +26,61 @@ leveldb_libleveldb_a_CPPFLAGS = $(AM_CPPFLAGS) $(LEVELDB_CPPFLAGS_INT) $(LEVELDB leveldb_libleveldb_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) leveldb_libleveldb_a_SOURCES= +leveldb_libleveldb_a_SOURCES += leveldb/port/atomic_pointer.h +leveldb_libleveldb_a_SOURCES += leveldb/port/port_example.h +leveldb_libleveldb_a_SOURCES += leveldb/port/port_posix.h +leveldb_libleveldb_a_SOURCES += leveldb/port/win/stdint.h +leveldb_libleveldb_a_SOURCES += leveldb/port/port.h +leveldb_libleveldb_a_SOURCES += leveldb/port/port_win.h +leveldb_libleveldb_a_SOURCES += leveldb/port/thread_annotations.h +leveldb_libleveldb_a_SOURCES += leveldb/include/leveldb/db.h +leveldb_libleveldb_a_SOURCES += leveldb/include/leveldb/options.h +leveldb_libleveldb_a_SOURCES += leveldb/include/leveldb/comparator.h +leveldb_libleveldb_a_SOURCES += leveldb/include/leveldb/filter_policy.h +leveldb_libleveldb_a_SOURCES += leveldb/include/leveldb/slice.h +leveldb_libleveldb_a_SOURCES += leveldb/include/leveldb/table_builder.h +leveldb_libleveldb_a_SOURCES += leveldb/include/leveldb/env.h +leveldb_libleveldb_a_SOURCES += leveldb/include/leveldb/c.h +leveldb_libleveldb_a_SOURCES += leveldb/include/leveldb/iterator.h +leveldb_libleveldb_a_SOURCES += leveldb/include/leveldb/cache.h +leveldb_libleveldb_a_SOURCES += leveldb/include/leveldb/dumpfile.h +leveldb_libleveldb_a_SOURCES += leveldb/include/leveldb/table.h +leveldb_libleveldb_a_SOURCES += leveldb/include/leveldb/write_batch.h +leveldb_libleveldb_a_SOURCES += leveldb/include/leveldb/status.h +leveldb_libleveldb_a_SOURCES += leveldb/db/log_format.h +leveldb_libleveldb_a_SOURCES += leveldb/db/memtable.h +leveldb_libleveldb_a_SOURCES += leveldb/db/version_set.h +leveldb_libleveldb_a_SOURCES += leveldb/db/write_batch_internal.h +leveldb_libleveldb_a_SOURCES += leveldb/db/filename.h +leveldb_libleveldb_a_SOURCES += leveldb/db/version_edit.h +leveldb_libleveldb_a_SOURCES += leveldb/db/dbformat.h +leveldb_libleveldb_a_SOURCES += leveldb/db/builder.h +leveldb_libleveldb_a_SOURCES += leveldb/db/log_writer.h +leveldb_libleveldb_a_SOURCES += leveldb/db/db_iter.h +leveldb_libleveldb_a_SOURCES += leveldb/db/skiplist.h +leveldb_libleveldb_a_SOURCES += leveldb/db/db_impl.h +leveldb_libleveldb_a_SOURCES += leveldb/db/table_cache.h +leveldb_libleveldb_a_SOURCES += leveldb/db/snapshot.h +leveldb_libleveldb_a_SOURCES += leveldb/db/log_reader.h +leveldb_libleveldb_a_SOURCES += leveldb/table/filter_block.h +leveldb_libleveldb_a_SOURCES += leveldb/table/block_builder.h +leveldb_libleveldb_a_SOURCES += leveldb/table/block.h +leveldb_libleveldb_a_SOURCES += leveldb/table/two_level_iterator.h +leveldb_libleveldb_a_SOURCES += leveldb/table/merger.h +leveldb_libleveldb_a_SOURCES += leveldb/table/format.h +leveldb_libleveldb_a_SOURCES += leveldb/table/iterator_wrapper.h +leveldb_libleveldb_a_SOURCES += leveldb/util/crc32c.h +leveldb_libleveldb_a_SOURCES += leveldb/util/arena.h +leveldb_libleveldb_a_SOURCES += leveldb/util/random.h +leveldb_libleveldb_a_SOURCES += leveldb/util/posix_logger.h +leveldb_libleveldb_a_SOURCES += leveldb/util/hash.h +leveldb_libleveldb_a_SOURCES += leveldb/util/histogram.h +leveldb_libleveldb_a_SOURCES += leveldb/util/coding.h +leveldb_libleveldb_a_SOURCES += leveldb/util/testutil.h +leveldb_libleveldb_a_SOURCES += leveldb/util/mutexlock.h +leveldb_libleveldb_a_SOURCES += leveldb/util/logging.h +leveldb_libleveldb_a_SOURCES += leveldb/util/testharness.h + leveldb_libleveldb_a_SOURCES += leveldb/db/builder.cc leveldb_libleveldb_a_SOURCES += leveldb/db/c.cc leveldb_libleveldb_a_SOURCES += leveldb/db/dbformat.cc @@ -76,3 +131,4 @@ endif leveldb_libmemenv_a_CPPFLAGS = $(leveldb_libleveldb_a_CPPFLAGS) leveldb_libmemenv_a_CXXFLAGS = $(leveldb_libleveldb_a_CXXFLAGS) leveldb_libmemenv_a_SOURCES = leveldb/helpers/memenv/memenv.cc +leveldb_libmemenv_a_SOURCES += leveldb/helpers/memenv/memenv.h diff --git a/src/Makefile.qt.include b/src/Makefile.qt.include index 3b39919441..9381cca9f2 100644 --- a/src/Makefile.qt.include +++ b/src/Makefile.qt.include @@ -390,19 +390,20 @@ QT_QM=$(QT_TS:.ts=.qm) SECONDARY: $(QT_QM) -qt/bitcoinstrings.cpp: $(libbitcoin_server_a_SOURCES) $(libbitcoin_wallet_a_SOURCES) +$(srcdir)/qt/bitcoinstrings.cpp: $(libbitcoin_server_a_SOURCES) $(libbitcoin_wallet_a_SOURCES) @test -n $(XGETTEXT) || echo "xgettext is required for updating translations" $(AM_V_GEN) cd $(srcdir); XGETTEXT=$(XGETTEXT) PACKAGE_NAME="$(PACKAGE_NAME)" COPYRIGHT_HOLDERS="$(COPYRIGHT_HOLDERS)" COPYRIGHT_HOLDERS_SUBSTITUTION="$(COPYRIGHT_HOLDERS_SUBSTITUTION)" $(PYTHON) ../share/qt/extract_strings_qt.py $^ -translate: qt/bitcoinstrings.cpp $(QT_FORMS_UI) $(QT_FORMS_UI) $(BITCOIN_QT_CPP) $(BITCOIN_QT_H) $(BITCOIN_MM) +translate: $(srcdir)/qt/bitcoinstrings.cpp $(QT_FORMS_UI) $(QT_FORMS_UI) $(BITCOIN_QT_CPP) $(BITCOIN_QT_H) $(BITCOIN_MM) @test -n $(LUPDATE) || echo "lupdate is required for updating translations" - $(AM_V_GEN) QT_SELECT=$(QT_SELECT) $(LUPDATE) $^ -locations relative -no-obsolete -ts qt/locale/bitcoin_en.ts + $(AM_V_GEN) QT_SELECT=$(QT_SELECT) $(LUPDATE) $^ -locations relative -no-obsolete -ts $(srcdir)/qt/locale/bitcoin_en.ts $(QT_QRC_LOCALE_CPP): $(QT_QRC_LOCALE) $(QT_QM) @test -f $(RCC) - @test -f $(@D)/$(<F) || cp -f $< $(@D) - $(AM_V_GEN) QT_SELECT=$(QT_SELECT) $(RCC) -name bitcoin_locale $(@D)/$(<F) | \ + @cp -f $< $(@D)/temp_$(<F) + $(AM_V_GEN) QT_SELECT=$(QT_SELECT) $(RCC) -name bitcoin_locale $(@D)/temp_$(<F) | \ $(SED) -e '/^\*\*.*Created:/d' -e '/^\*\*.*by:/d' > $@ + @rm $(@D)/temp_$(<F) $(QT_QRC_CPP): $(QT_QRC) $(QT_FORMS_H) $(RES_ICONS) $(RES_IMAGES) $(RES_MOVIES) $(PROTOBUF_H) @test -f $(RCC) diff --git a/src/Makefile.test.include b/src/Makefile.test.include index 08e2f6af4d..41d811fb54 100644 --- a/src/Makefile.test.include +++ b/src/Makefile.test.include @@ -17,7 +17,9 @@ EXTRA_DIST += \ test/data/txcreate2.hex \ test/data/txcreatedata1.hex \ test/data/txcreatedata2.hex \ - test/data/txcreatesign.hex + test/data/txcreatesign.hex \ + test/data/txcreatedata_seq0.hex \ + test/data/txcreatedata_seq1.hex JSON_TEST_FILES = \ test/data/script_tests.json \ @@ -59,6 +61,7 @@ BITCOIN_TESTS =\ test/merkle_tests.cpp \ test/miner_tests.cpp \ test/multisig_tests.cpp \ + test/net_tests.cpp \ test/netbase_tests.cpp \ test/pmt_tests.cpp \ test/policyestimator_tests.cpp \ @@ -94,6 +97,7 @@ BITCOIN_TESTS += \ wallet/test/wallet_test_fixture.h \ wallet/test/accounting_tests.cpp \ wallet/test/wallet_tests.cpp \ + wallet/test/crypto_tests.cpp \ wallet/test/rpc_wallet_tests.cpp endif @@ -121,6 +125,9 @@ CLEAN_BITCOIN_TEST = test/*.gcda test/*.gcno $(GENERATED_TEST_FILES) CLEANFILES += $(CLEAN_BITCOIN_TEST) +# This file is problematic for out-of-tree builds if it exists. +DISTCLEANFILES += test/buildenv.pyc + bitcoin_test: $(TEST_BINARY) bitcoin_test_check: $(TEST_BINARY) FORCE diff --git a/src/addrman.cpp b/src/addrman.cpp index 6c54cfa4cd..cebb1c8e5e 100644 --- a/src/addrman.cpp +++ b/src/addrman.cpp @@ -197,6 +197,9 @@ void CAddrMan::MakeTried(CAddrInfo& info, int nId) void CAddrMan::Good_(const CService& addr, int64_t nTime) { int nId; + + nLastGood = nTime; + CAddrInfo* pinfo = Find(addr, &nId); // if not found, bail out @@ -260,7 +263,7 @@ bool CAddrMan::Add_(const CAddress& addr, const CNetAddr& source, int64_t nTimeP pinfo->nTime = std::max((int64_t)0, addr.nTime - nTimePenalty); // add services - pinfo->nServices |= addr.nServices; + pinfo->nServices = ServiceFlags(pinfo->nServices | addr.nServices); // do not update if no new information is present if (!addr.nTime || (pinfo->nTime && addr.nTime <= pinfo->nTime)) @@ -311,7 +314,7 @@ bool CAddrMan::Add_(const CAddress& addr, const CNetAddr& source, int64_t nTimeP return fNew; } -void CAddrMan::Attempt_(const CService& addr, int64_t nTime) +void CAddrMan::Attempt_(const CService& addr, bool fCountFailure, int64_t nTime) { CAddrInfo* pinfo = Find(addr); @@ -327,7 +330,10 @@ void CAddrMan::Attempt_(const CService& addr, int64_t nTime) // update info info.nLastTry = nTime; - info.nAttempts++; + if (fCountFailure && info.nLastCountAttempt < nLastGood) { + info.nLastCountAttempt = nTime; + info.nAttempts++; + } } CAddrInfo CAddrMan::Select_(bool newOnly) @@ -496,6 +502,24 @@ void CAddrMan::Connected_(const CService& addr, int64_t nTime) info.nTime = nTime; } +void CAddrMan::SetServices_(const CService& addr, ServiceFlags nServices) +{ + CAddrInfo* pinfo = Find(addr); + + // if not found, bail out + if (!pinfo) + return; + + CAddrInfo& info = *pinfo; + + // check whether we are talking about the exact same CService (including same port) + if (info != addr) + return; + + // update info + info.nServices = nServices; +} + int CAddrMan::RandomInt(int nMax){ return GetRandInt(nMax); } diff --git a/src/addrman.h b/src/addrman.h index 4f3de8d7c5..1caf540758 100644 --- a/src/addrman.h +++ b/src/addrman.h @@ -29,6 +29,9 @@ public: //! last try whatsoever by us (memory only) int64_t nLastTry; + //! last counted attempt (memory only) + int64_t nLastCountAttempt; + private: //! where knowledge about this address first came from CNetAddr source; @@ -66,6 +69,7 @@ public: { nLastSuccess = 0; nLastTry = 0; + nLastCountAttempt = 0; nAttempts = 0; nRefCount = 0; fInTried = false; @@ -200,6 +204,9 @@ private: //! list of "new" buckets int vvNew[ADDRMAN_NEW_BUCKET_COUNT][ADDRMAN_BUCKET_SIZE]; + //! last time Good was called (memory only) + int64_t nLastGood; + protected: //! secret key to randomize bucket select with uint256 nKey; @@ -230,7 +237,7 @@ protected: bool Add_(const CAddress &addr, const CNetAddr& source, int64_t nTimePenalty); //! Mark an entry as attempted to connect. - void Attempt_(const CService &addr, int64_t nTime); + void Attempt_(const CService &addr, bool fCountFailure, int64_t nTime); //! Select an address to connect to, if newOnly is set to true, only the new table is selected from. CAddrInfo Select_(bool newOnly); @@ -249,6 +256,9 @@ protected: //! Mark an entry as currently-connected-to. void Connected_(const CService &addr, int64_t nTime); + //! Update an entry's service bits. + void SetServices_(const CService &addr, ServiceFlags nServices); + public: /** * serialized format: @@ -350,6 +360,14 @@ public: nUBuckets ^= (1 << 30); } + if (nNew > ADDRMAN_NEW_BUCKET_COUNT * ADDRMAN_BUCKET_SIZE) { + throw std::ios_base::failure("Corrupt CAddrMan serialization, nNew exceeds limit."); + } + + if (nTried > ADDRMAN_TRIED_BUCKET_COUNT * ADDRMAN_BUCKET_SIZE) { + throw std::ios_base::failure("Corrupt CAddrMan serialization, nTried exceeds limit."); + } + // Deserialize entries from the new table. for (int n = 0; n < nNew; n++) { CAddrInfo &info = mapInfo[n]; @@ -450,6 +468,7 @@ public: nIdCount = 0; nTried = 0; nNew = 0; + nLastGood = 1; //Initially at 1 so that "never" is strictly worse. } CAddrMan() @@ -524,12 +543,12 @@ public: } //! Mark an entry as connection attempted to. - void Attempt(const CService &addr, int64_t nTime = GetAdjustedTime()) + void Attempt(const CService &addr, bool fCountFailure, int64_t nTime = GetAdjustedTime()) { { LOCK(cs); Check(); - Attempt_(addr, nTime); + Attempt_(addr, fCountFailure, nTime); Check(); } } @@ -573,6 +592,14 @@ public: } } + void SetServices(const CService &addr, ServiceFlags nServices) + { + LOCK(cs); + Check(); + SetServices_(addr, nServices); + Check(); + } + }; #endif // BITCOIN_ADDRMAN_H diff --git a/src/bench/base58.cpp b/src/bench/base58.cpp new file mode 100644 index 0000000000..1279c3e7df --- /dev/null +++ b/src/bench/base58.cpp @@ -0,0 +1,56 @@ +// 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 "bench.h" + +#include "main.h" +#include "base58.h" + +#include <vector> +#include <string> + + +static void Base58Encode(benchmark::State& state) +{ + unsigned char buff[32] = { + 17, 79, 8, 99, 150, 189, 208, 162, 22, 23, 203, 163, 36, 58, 147, + 227, 139, 2, 215, 100, 91, 38, 11, 141, 253, 40, 117, 21, 16, 90, + 200, 24 + }; + unsigned char* b = buff; + while (state.KeepRunning()) { + EncodeBase58(b, b + 32); + } +} + + +static void Base58CheckEncode(benchmark::State& state) +{ + unsigned char buff[32] = { + 17, 79, 8, 99, 150, 189, 208, 162, 22, 23, 203, 163, 36, 58, 147, + 227, 139, 2, 215, 100, 91, 38, 11, 141, 253, 40, 117, 21, 16, 90, + 200, 24 + }; + unsigned char* b = buff; + std::vector<unsigned char> vch; + vch.assign(b, b + 32); + while (state.KeepRunning()) { + EncodeBase58Check(vch); + } +} + + +static void Base58Decode(benchmark::State& state) +{ + const char* addr = "17VZNX1SN5NtKa8UQFxwQbFeFc3iqRYhem"; + std::vector<unsigned char> vch; + while (state.KeepRunning()) { + DecodeBase58(addr, vch); + } +} + + +BENCHMARK(Base58Encode); +BENCHMARK(Base58CheckEncode); +BENCHMARK(Base58Decode); diff --git a/src/bench/bench.cpp b/src/bench/bench.cpp index 6ee3cdc27a..227546a7a7 100644 --- a/src/bench/bench.cpp +++ b/src/bench/bench.cpp @@ -5,6 +5,7 @@ #include "bench.h" #include <iostream> +#include <iomanip> #include <sys/time.h> using namespace benchmark; @@ -25,7 +26,7 @@ BenchRunner::BenchRunner(std::string name, BenchFunction func) void BenchRunner::RunAll(double elapsedTimeForOne) { - std::cout << "Benchmark" << "," << "count" << "," << "min" << "," << "max" << "," << "average" << "\n"; + std::cout << "#Benchmark" << "," << "count" << "," << "min" << "," << "max" << "," << "average" << "\n"; for (std::map<std::string,BenchFunction>::iterator it = benchmarks.begin(); it != benchmarks.end(); ++it) { @@ -38,22 +39,34 @@ BenchRunner::RunAll(double elapsedTimeForOne) bool State::KeepRunning() { + if (count & countMask) { + ++count; + return true; + } double now; if (count == 0) { - beginTime = now = gettimedouble(); + lastTime = beginTime = now = gettimedouble(); } else { - // timeCheckCount is used to avoid calling gettime most of the time, - // so benchmarks that run very quickly get consistent results. - if ((count+1)%timeCheckCount != 0) { - ++count; - return true; // keep going - } now = gettimedouble(); - double elapsedOne = (now - lastTime)/timeCheckCount; + double elapsed = now - lastTime; + double elapsedOne = elapsed * countMaskInv; if (elapsedOne < minTime) minTime = elapsedOne; if (elapsedOne > maxTime) maxTime = elapsedOne; - if (elapsedOne*timeCheckCount < maxElapsed/16) timeCheckCount *= 2; + if (elapsed*128 < maxElapsed) { + // If the execution was much too fast (1/128th of maxElapsed), increase the count mask by 8x and restart timing. + // The restart avoids including the overhead of this code in the measurement. + countMask = ((countMask<<3)|7) & ((1LL<<60)-1); + countMaskInv = 1./(countMask+1); + count = 0; + minTime = std::numeric_limits<double>::max(); + maxTime = std::numeric_limits<double>::min(); + return true; + } + if (elapsed*16 < maxElapsed) { + countMask = ((countMask<<1)|1) & ((1LL<<60)-1); + countMaskInv = 1./(countMask+1); + } } lastTime = now; ++count; @@ -64,7 +77,7 @@ bool State::KeepRunning() // Output results double average = (now-beginTime)/count; - std::cout << name << "," << count << "," << minTime << "," << maxTime << "," << average << "\n"; + std::cout << std::fixed << std::setprecision(15) << name << "," << count << "," << minTime << "," << maxTime << "," << average << "\n"; return false; } diff --git a/src/bench/bench.h b/src/bench/bench.h index 5ce13c642b..f13b145aaf 100644 --- a/src/bench/bench.h +++ b/src/bench/bench.h @@ -40,14 +40,15 @@ namespace benchmark { std::string name; double maxElapsed; double beginTime; - double lastTime, minTime, maxTime; + double lastTime, minTime, maxTime, countMaskInv; int64_t count; - int64_t timeCheckCount; + int64_t countMask; public: State(std::string _name, double _maxElapsed) : name(_name), maxElapsed(_maxElapsed), count(0) { minTime = std::numeric_limits<double>::max(); maxTime = std::numeric_limits<double>::min(); - timeCheckCount = 1; + countMask = 1; + countMaskInv = 1./(countMask + 1); } bool KeepRunning(); }; diff --git a/src/bench/crypto_hash.cpp b/src/bench/crypto_hash.cpp index 6b753f6308..168006154f 100644 --- a/src/bench/crypto_hash.cpp +++ b/src/bench/crypto_hash.cpp @@ -6,6 +6,8 @@ #include "bench.h" #include "bloom.h" +#include "hash.h" +#include "uint256.h" #include "utiltime.h" #include "crypto/ripemd160.h" #include "crypto/sha1.h" @@ -39,6 +41,16 @@ static void SHA256(benchmark::State& state) CSHA256().Write(begin_ptr(in), in.size()).Finalize(hash); } +static void SHA256_32b(benchmark::State& state) +{ + std::vector<uint8_t> in(32,0); + while (state.KeepRunning()) { + for (int i = 0; i < 1000000; i++) { + CSHA256().Write(begin_ptr(in), in.size()).Finalize(&in[0]); + } + } +} + static void SHA512(benchmark::State& state) { uint8_t hash[CSHA512::OUTPUT_SIZE]; @@ -47,7 +59,20 @@ static void SHA512(benchmark::State& state) CSHA512().Write(begin_ptr(in), in.size()).Finalize(hash); } +static void SipHash_32b(benchmark::State& state) +{ + uint256 x; + while (state.KeepRunning()) { + for (int i = 0; i < 1000000; i++) { + *((uint64_t*)x.begin()) = SipHashUint256(0, i, x); + } + } +} + BENCHMARK(RIPEMD160); BENCHMARK(SHA1); BENCHMARK(SHA256); BENCHMARK(SHA512); + +BENCHMARK(SHA256_32b); +BENCHMARK(SipHash_32b); diff --git a/src/bitcoin-tx.cpp b/src/bitcoin-tx.cpp index 95d7a085a0..f9ea94b9f4 100644 --- a/src/bitcoin-tx.cpp +++ b/src/bitcoin-tx.cpp @@ -71,7 +71,7 @@ static bool AppInitRawTx(int argc, char* argv[]) strUsage = HelpMessageGroup(_("Commands:")); strUsage += HelpMessageOpt("delin=N", _("Delete input N from TX")); strUsage += HelpMessageOpt("delout=N", _("Delete output N from TX")); - strUsage += HelpMessageOpt("in=TXID:VOUT", _("Add input to TX")); + strUsage += HelpMessageOpt("in=TXID:VOUT(:SEQUENCE_NUMBER)", _("Add input to TX")); strUsage += HelpMessageOpt("locktime=N", _("Set TX lock time to N")); strUsage += HelpMessageOpt("nversion=N", _("Set TX version to N")); strUsage += HelpMessageOpt("outaddr=VALUE:ADDRESS", _("Add address-based output to TX")); @@ -181,15 +181,15 @@ static void MutateTxLocktime(CMutableTransaction& tx, const string& cmdVal) static void MutateTxAddInput(CMutableTransaction& tx, const string& strInput) { + std::vector<std::string> vStrInputParts; + boost::split(vStrInputParts, strInput, boost::is_any_of(":")); + // separate TXID:VOUT in string - size_t pos = strInput.find(':'); - if ((pos == string::npos) || - (pos == 0) || - (pos == (strInput.size() - 1))) + if (vStrInputParts.size()<2) throw runtime_error("TX input missing separator"); // extract and validate TXID - string strTxid = strInput.substr(0, pos); + string strTxid = vStrInputParts[0]; if ((strTxid.size() != 64) || !IsHex(strTxid)) throw runtime_error("invalid TX input txid"); uint256 txid(uint256S(strTxid)); @@ -198,13 +198,18 @@ static void MutateTxAddInput(CMutableTransaction& tx, const string& strInput) static const unsigned int maxVout = MAX_BLOCK_SIZE / minTxOutSz; // extract and validate vout - string strVout = strInput.substr(pos + 1, string::npos); + string strVout = vStrInputParts[1]; int vout = atoi(strVout); if ((vout < 0) || (vout > (int)maxVout)) throw runtime_error("invalid TX input vout"); + // extract the optional sequence number + uint32_t nSequenceIn=std::numeric_limits<unsigned int>::max(); + if (vStrInputParts.size() > 2) + nSequenceIn = std::stoul(vStrInputParts[2]); + // append to transaction input list - CTxIn txin(txid, vout); + CTxIn txin(txid, vout, CScript(), nSequenceIn); tx.vin.push_back(txin); } diff --git a/src/chain.cpp b/src/chain.cpp index 32f6480f84..77e924e703 100644 --- a/src/chain.cpp +++ b/src/chain.cpp @@ -93,6 +93,7 @@ CBlockIndex* CBlockIndex::GetAncestor(int height) pindexWalk = pindexWalk->pskip; heightWalk = heightSkip; } else { + assert(pindexWalk->pprev); pindexWalk = pindexWalk->pprev; heightWalk--; } diff --git a/src/chainparams.cpp b/src/chainparams.cpp index 5c7d190125..8c27a578bb 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -197,6 +197,8 @@ public: vFixedSeeds.clear(); vSeeds.clear(); + // nodes with support for servicebits filtering should be at the top + vSeeds.push_back(CDNSSeedData("testnetbitcoin.jonasschnelli.ch", "testnet-seed.bitcoin.jonasschnelli.ch", true)); vSeeds.push_back(CDNSSeedData("bitcoin.petertodd.org", "testnet-seed.bitcoin.petertodd.org")); vSeeds.push_back(CDNSSeedData("bluematt.me", "testnet-seed.bluematt.me")); vSeeds.push_back(CDNSSeedData("bitcoin.schildbach.de", "testnet-seed.bitcoin.schildbach.de")); diff --git a/src/chainparams.h b/src/chainparams.h index 59202f548a..638893e9ad 100644 --- a/src/chainparams.h +++ b/src/chainparams.h @@ -15,7 +15,8 @@ struct CDNSSeedData { std::string name, host; - CDNSSeedData(const std::string &strName, const std::string &strHost) : name(strName), host(strHost) {} + bool supportsServiceBitsFiltering; + CDNSSeedData(const std::string &strName, const std::string &strHost, bool supportsServiceBitsFilteringIn = false) : name(strName), host(strHost), supportsServiceBitsFiltering(supportsServiceBitsFilteringIn) {} }; struct SeedSpec6 { diff --git a/src/clientversion.cpp b/src/clientversion.cpp index aae0569bba..bfe9e16f80 100644 --- a/src/clientversion.cpp +++ b/src/clientversion.cpp @@ -67,16 +67,7 @@ const std::string CLIENT_NAME("Satoshi"); #endif #endif -#ifndef BUILD_DATE -#ifdef GIT_COMMIT_DATE -#define BUILD_DATE GIT_COMMIT_DATE -#else -#define BUILD_DATE __DATE__ ", " __TIME__ -#endif -#endif - const std::string CLIENT_BUILD(BUILD_DESC CLIENT_VERSION_SUFFIX); -const std::string CLIENT_DATE(BUILD_DATE); static std::string FormatVersion(int nVersion) { diff --git a/src/clientversion.h b/src/clientversion.h index 6f255d69c9..47263d5344 100644 --- a/src/clientversion.h +++ b/src/clientversion.h @@ -59,7 +59,6 @@ static const int CLIENT_VERSION = extern const std::string CLIENT_NAME; extern const std::string CLIENT_BUILD; -extern const std::string CLIENT_DATE; std::string FormatFullVersion(); diff --git a/src/coincontrol.h b/src/coincontrol.h index 12fe9ce219..e33adc4d2b 100644 --- a/src/coincontrol.h +++ b/src/coincontrol.h @@ -18,6 +18,10 @@ public: bool fAllowWatchOnly; //! Minimum absolute fee (not per kilobyte) CAmount nMinimumTotalFee; + //! Override estimated feerate + bool fOverrideFeeRate; + //! Feerate to use if overrideFeeRate is true + CFeeRate nFeeRate; CCoinControl() { @@ -31,6 +35,8 @@ public: fAllowWatchOnly = false; setSelected.clear(); nMinimumTotalFee = 0; + nFeeRate = CFeeRate(0); + fOverrideFeeRate = false; } bool HasSelected() const diff --git a/src/coins.cpp b/src/coins.cpp index 1c329740b4..39db7dedfb 100644 --- a/src/coins.cpp +++ b/src/coins.cpp @@ -56,7 +56,7 @@ void CCoinsViewBacked::SetBackend(CCoinsView &viewIn) { base = &viewIn; } bool CCoinsViewBacked::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock) { return base->BatchWrite(mapCoins, hashBlock); } CCoinsViewCursor *CCoinsViewBacked::Cursor() const { return base->Cursor(); } -CCoinsKeyHasher::CCoinsKeyHasher() : salt(GetRandHash()) {} +SaltedTxidHasher::SaltedTxidHasher() : k0(GetRand(std::numeric_limits<uint64_t>::max())), k1(GetRand(std::numeric_limits<uint64_t>::max())) {} CCoinsViewCache::CCoinsViewCache(CCoinsView *baseIn) : CCoinsViewBacked(baseIn), hasModifier(false), cachedCoinsUsage(0) { } diff --git a/src/coins.h b/src/coins.h index d72f885473..033651a435 100644 --- a/src/coins.h +++ b/src/coins.h @@ -8,6 +8,7 @@ #include "compressor.h" #include "core_memusage.h" +#include "hash.h" #include "memusage.h" #include "serialize.h" #include "uint256.h" @@ -264,21 +265,22 @@ public: } }; -class CCoinsKeyHasher +class SaltedTxidHasher { private: - uint256 salt; + /** Salt */ + const uint64_t k0, k1; public: - CCoinsKeyHasher(); + SaltedTxidHasher(); /** * This *must* return size_t. With Boost 1.46 on 32-bit systems the * unordered_map will behave unpredictably if the custom hasher returns a * uint64_t, resulting in failures when syncing the chain (#4634). */ - size_t operator()(const uint256& key) const { - return key.GetHash(salt); + size_t operator()(const uint256& txid) const { + return SipHashUint256(k0, k1, txid); } }; @@ -295,7 +297,7 @@ struct CCoinsCacheEntry CCoinsCacheEntry() : coins(), flags(0) {} }; -typedef boost::unordered_map<uint256, CCoinsCacheEntry, CCoinsKeyHasher> CCoinsMap; +typedef boost::unordered_map<uint256, CCoinsCacheEntry, SaltedTxidHasher> CCoinsMap; /** Cursor for iterating over CoinsView state */ class CCoinsViewCursor diff --git a/src/consensus/params.h b/src/consensus/params.h index 4f3480b89b..6c4cc49479 100644 --- a/src/consensus/params.h +++ b/src/consensus/params.h @@ -16,6 +16,7 @@ enum DeploymentPos { DEPLOYMENT_TESTDUMMY, DEPLOYMENT_CSV, // Deployment of BIP68, BIP112, and BIP113. + // NOTE: Also add new deployments to VersionBitsDeploymentInfo in versionbits.cpp MAX_VERSION_BITS_DEPLOYMENTS }; diff --git a/src/crypto/aes.cpp b/src/crypto/aes.cpp new file mode 100644 index 0000000000..1d469d0fb4 --- /dev/null +++ b/src/crypto/aes.cpp @@ -0,0 +1,217 @@ +// 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 "aes.h" +#include "crypto/common.h" + +#include <assert.h> +#include <string.h> + +extern "C" { +#include "crypto/ctaes/ctaes.c" +} + +AES128Encrypt::AES128Encrypt(const unsigned char key[16]) +{ + AES128_init(&ctx, key); +} + +AES128Encrypt::~AES128Encrypt() +{ + memset(&ctx, 0, sizeof(ctx)); +} + +void AES128Encrypt::Encrypt(unsigned char ciphertext[16], const unsigned char plaintext[16]) const +{ + AES128_encrypt(&ctx, 1, ciphertext, plaintext); +} + +AES128Decrypt::AES128Decrypt(const unsigned char key[16]) +{ + AES128_init(&ctx, key); +} + +AES128Decrypt::~AES128Decrypt() +{ + memset(&ctx, 0, sizeof(ctx)); +} + +void AES128Decrypt::Decrypt(unsigned char plaintext[16], const unsigned char ciphertext[16]) const +{ + AES128_decrypt(&ctx, 1, plaintext, ciphertext); +} + +AES256Encrypt::AES256Encrypt(const unsigned char key[32]) +{ + AES256_init(&ctx, key); +} + +AES256Encrypt::~AES256Encrypt() +{ + memset(&ctx, 0, sizeof(ctx)); +} + +void AES256Encrypt::Encrypt(unsigned char ciphertext[16], const unsigned char plaintext[16]) const +{ + AES256_encrypt(&ctx, 1, ciphertext, plaintext); +} + +AES256Decrypt::AES256Decrypt(const unsigned char key[32]) +{ + AES256_init(&ctx, key); +} + +AES256Decrypt::~AES256Decrypt() +{ + memset(&ctx, 0, sizeof(ctx)); +} + +void AES256Decrypt::Decrypt(unsigned char plaintext[16], const unsigned char ciphertext[16]) const +{ + AES256_decrypt(&ctx, 1, plaintext, ciphertext); +} + + +template <typename T> +static int CBCEncrypt(const T& enc, const unsigned char iv[AES_BLOCKSIZE], const unsigned char* data, int size, bool pad, unsigned char* out) +{ + int written = 0; + int padsize = size % AES_BLOCKSIZE; + unsigned char mixed[AES_BLOCKSIZE]; + + if (!data || !size || !out) + return 0; + + if (!pad && padsize != 0) + return 0; + + memcpy(mixed, iv, AES_BLOCKSIZE); + + // Write all but the last block + while (written + AES_BLOCKSIZE <= size) { + for (int i = 0; i != AES_BLOCKSIZE; i++) + mixed[i] ^= *data++; + enc.Encrypt(out + written, mixed); + memcpy(mixed, out + written, AES_BLOCKSIZE); + written += AES_BLOCKSIZE; + } + if (pad) { + // For all that remains, pad each byte with the value of the remaining + // space. If there is none, pad by a full block. + for (int i = 0; i != padsize; i++) + mixed[i] ^= *data++; + for (int i = padsize; i != AES_BLOCKSIZE; i++) + mixed[i] ^= AES_BLOCKSIZE - padsize; + enc.Encrypt(out + written, mixed); + written += AES_BLOCKSIZE; + } + return written; +} + +template <typename T> +static int CBCDecrypt(const T& dec, const unsigned char iv[AES_BLOCKSIZE], const unsigned char* data, int size, bool pad, unsigned char* out) +{ + unsigned char padsize = 0; + int written = 0; + bool fail = false; + const unsigned char* prev = iv; + + if (!data || !size || !out) + return 0; + + if (size % AES_BLOCKSIZE != 0) + return 0; + + // Decrypt all data. Padding will be checked in the output. + while (written != size) { + dec.Decrypt(out, data + written); + for (int i = 0; i != AES_BLOCKSIZE; i++) + *out++ ^= prev[i]; + prev = data + written; + written += AES_BLOCKSIZE; + } + + // When decrypting padding, attempt to run in constant-time + if (pad) { + // If used, padding size is the value of the last decrypted byte. For + // it to be valid, It must be between 1 and AES_BLOCKSIZE. + padsize = *--out; + fail = !padsize | (padsize > AES_BLOCKSIZE); + + // If not well-formed, treat it as though there's no padding. + padsize *= !fail; + + // All padding must equal the last byte otherwise it's not well-formed + for (int i = AES_BLOCKSIZE; i != 0; i--) + fail |= ((i > AES_BLOCKSIZE - padsize) & (*out-- != padsize)); + + written -= padsize; + } + return written * !fail; +} + +AES256CBCEncrypt::AES256CBCEncrypt(const unsigned char key[AES256_KEYSIZE], const unsigned char ivIn[AES_BLOCKSIZE], bool padIn) + : enc(key), pad(padIn) +{ + memcpy(iv, ivIn, AES_BLOCKSIZE); +} + +int AES256CBCEncrypt::Encrypt(const unsigned char* data, int size, unsigned char* out) const +{ + return CBCEncrypt(enc, iv, data, size, pad, out); +} + +AES256CBCEncrypt::~AES256CBCEncrypt() +{ + memset(iv, 0, sizeof(iv)); +} + +AES256CBCDecrypt::AES256CBCDecrypt(const unsigned char key[AES256_KEYSIZE], const unsigned char ivIn[AES_BLOCKSIZE], bool padIn) + : dec(key), pad(padIn) +{ + memcpy(iv, ivIn, AES_BLOCKSIZE); +} + + +int AES256CBCDecrypt::Decrypt(const unsigned char* data, int size, unsigned char* out) const +{ + return CBCDecrypt(dec, iv, data, size, pad, out); +} + +AES256CBCDecrypt::~AES256CBCDecrypt() +{ + memset(iv, 0, sizeof(iv)); +} + +AES128CBCEncrypt::AES128CBCEncrypt(const unsigned char key[AES128_KEYSIZE], const unsigned char ivIn[AES_BLOCKSIZE], bool padIn) + : enc(key), pad(padIn) +{ + memcpy(iv, ivIn, AES_BLOCKSIZE); +} + +AES128CBCEncrypt::~AES128CBCEncrypt() +{ + memset(iv, 0, AES_BLOCKSIZE); +} + +int AES128CBCEncrypt::Encrypt(const unsigned char* data, int size, unsigned char* out) const +{ + return CBCEncrypt(enc, iv, data, size, pad, out); +} + +AES128CBCDecrypt::AES128CBCDecrypt(const unsigned char key[AES128_KEYSIZE], const unsigned char ivIn[AES_BLOCKSIZE], bool padIn) + : dec(key), pad(padIn) +{ + memcpy(iv, ivIn, AES_BLOCKSIZE); +} + +AES128CBCDecrypt::~AES128CBCDecrypt() +{ + memset(iv, 0, AES_BLOCKSIZE); +} + +int AES128CBCDecrypt::Decrypt(const unsigned char* data, int size, unsigned char* out) const +{ + return CBCDecrypt(dec, iv, data, size, pad, out); +} diff --git a/src/crypto/aes.h b/src/crypto/aes.h new file mode 100644 index 0000000000..8cae357c12 --- /dev/null +++ b/src/crypto/aes.h @@ -0,0 +1,118 @@ +// Copyright (c) 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. +// +// C++ wrapper around ctaes, a constant-time AES implementation + +#ifndef BITCOIN_CRYPTO_AES_H +#define BITCOIN_CRYPTO_AES_H + +extern "C" { +#include "crypto/ctaes/ctaes.h" +} + +static const int AES_BLOCKSIZE = 16; +static const int AES128_KEYSIZE = 16; +static const int AES256_KEYSIZE = 32; + +/** An encryption class for AES-128. */ +class AES128Encrypt +{ +private: + AES128_ctx ctx; + +public: + AES128Encrypt(const unsigned char key[16]); + ~AES128Encrypt(); + void Encrypt(unsigned char ciphertext[16], const unsigned char plaintext[16]) const; +}; + +/** A decryption class for AES-128. */ +class AES128Decrypt +{ +private: + AES128_ctx ctx; + +public: + AES128Decrypt(const unsigned char key[16]); + ~AES128Decrypt(); + void Decrypt(unsigned char plaintext[16], const unsigned char ciphertext[16]) const; +}; + +/** An encryption class for AES-256. */ +class AES256Encrypt +{ +private: + AES256_ctx ctx; + +public: + AES256Encrypt(const unsigned char key[32]); + ~AES256Encrypt(); + void Encrypt(unsigned char ciphertext[16], const unsigned char plaintext[16]) const; +}; + +/** A decryption class for AES-256. */ +class AES256Decrypt +{ +private: + AES256_ctx ctx; + +public: + AES256Decrypt(const unsigned char key[32]); + ~AES256Decrypt(); + void Decrypt(unsigned char plaintext[16], const unsigned char ciphertext[16]) const; +}; + +class AES256CBCEncrypt +{ +public: + AES256CBCEncrypt(const unsigned char key[AES256_KEYSIZE], const unsigned char ivIn[AES_BLOCKSIZE], bool padIn); + ~AES256CBCEncrypt(); + int Encrypt(const unsigned char* data, int size, unsigned char* out) const; + +private: + const AES256Encrypt enc; + const bool pad; + unsigned char iv[AES_BLOCKSIZE]; +}; + +class AES256CBCDecrypt +{ +public: + AES256CBCDecrypt(const unsigned char key[AES256_KEYSIZE], const unsigned char ivIn[AES_BLOCKSIZE], bool padIn); + ~AES256CBCDecrypt(); + int Decrypt(const unsigned char* data, int size, unsigned char* out) const; + +private: + const AES256Decrypt dec; + const bool pad; + unsigned char iv[AES_BLOCKSIZE]; +}; + +class AES128CBCEncrypt +{ +public: + AES128CBCEncrypt(const unsigned char key[AES128_KEYSIZE], const unsigned char ivIn[AES_BLOCKSIZE], bool padIn); + ~AES128CBCEncrypt(); + int Encrypt(const unsigned char* data, int size, unsigned char* out) const; + +private: + const AES128Encrypt enc; + const bool pad; + unsigned char iv[AES_BLOCKSIZE]; +}; + +class AES128CBCDecrypt +{ +public: + AES128CBCDecrypt(const unsigned char key[AES128_KEYSIZE], const unsigned char ivIn[AES_BLOCKSIZE], bool padIn); + ~AES128CBCDecrypt(); + int Decrypt(const unsigned char* data, int size, unsigned char* out) const; + +private: + const AES128Decrypt dec; + const bool pad; + unsigned char iv[AES_BLOCKSIZE]; +}; + +#endif // BITCOIN_CRYPTO_AES_H diff --git a/src/crypto/ctaes/COPYING b/src/crypto/ctaes/COPYING new file mode 100644 index 0000000000..415b202a2a --- /dev/null +++ b/src/crypto/ctaes/COPYING @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2016 Pieter Wuille + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/src/crypto/ctaes/README.md b/src/crypto/ctaes/README.md new file mode 100644 index 0000000000..0e7fe17751 --- /dev/null +++ b/src/crypto/ctaes/README.md @@ -0,0 +1,41 @@ +ctaes +===== + +Simple C module for constant-time AES encryption and decryption. + +Features: +* Simple, pure C code without any dependencies. +* No tables or data-dependent branches whatsoever, but using bit sliced approach from https://eprint.iacr.org/2009/129.pdf. +* Very small object code: slightly over 4k of executable code when compiled with -Os. +* Slower than implementations based on precomputed tables or specialized instructions, but can do ~15 MB/s on modern CPUs. + +Performance +----------- + +Compiled with GCC 5.3.1 with -O3, on an Intel(R) Core(TM) i7-4800MQ CPU, numbers in CPU cycles: + +| Algorithm | Key schedule | Encryption per byte | Decryption per byte | +| --------- | ------------:| -------------------:| -------------------:| +| AES-128 | 2.8k | 154 | 161 | +| AES-192 | 3.1k | 169 | 181 | +| AES-256 | 4.0k | 191 | 203 | + +Build steps +----------- + +Object code: + + $ gcc -O3 ctaes.c -c -o ctaes.o + +Tests: + + $ gcc -O3 ctaes.c test.c -o test + +Benchmark: + + $ gcc -O3 ctaes.c bench.c -o bench + +Review +------ + +Results of a formal review of the code can be found in http://bitcoin.sipa.be/ctaes/review.zip diff --git a/src/crypto/ctaes/bench.c b/src/crypto/ctaes/bench.c new file mode 100644 index 0000000000..a86df496c8 --- /dev/null +++ b/src/crypto/ctaes/bench.c @@ -0,0 +1,170 @@ +#include <stdio.h> +#include <math.h> +#include "sys/time.h" + +#include "ctaes.h" + +static double gettimedouble(void) { + struct timeval tv; + gettimeofday(&tv, NULL); + return tv.tv_usec * 0.000001 + tv.tv_sec; +} + +static void print_number(double x) { + double y = x; + int c = 0; + if (y < 0.0) { + y = -y; + } + while (y < 100.0) { + y *= 10.0; + c++; + } + printf("%.*f", c, x); +} + +static void run_benchmark(char *name, void (*benchmark)(void*), void (*setup)(void*), void (*teardown)(void*), void* data, int count, int iter) { + int i; + double min = HUGE_VAL; + double sum = 0.0; + double max = 0.0; + for (i = 0; i < count; i++) { + double begin, total; + if (setup != NULL) { + setup(data); + } + begin = gettimedouble(); + benchmark(data); + total = gettimedouble() - begin; + if (teardown != NULL) { + teardown(data); + } + if (total < min) { + min = total; + } + if (total > max) { + max = total; + } + sum += total; + } + printf("%s: min ", name); + print_number(min * 1000000000.0 / iter); + printf("ns / avg "); + print_number((sum / count) * 1000000000.0 / iter); + printf("ns / max "); + print_number(max * 1000000000.0 / iter); + printf("ns\n"); +} + +static void bench_AES128_init(void* data) { + AES128_ctx* ctx = (AES128_ctx*)data; + int i; + for (i = 0; i < 50000; i++) { + AES128_init(ctx, (unsigned char*)ctx); + } +} + +static void bench_AES128_encrypt_setup(void* data) { + AES128_ctx* ctx = (AES128_ctx*)data; + static const unsigned char key[16] = {0}; + AES128_init(ctx, key); +} + +static void bench_AES128_encrypt(void* data) { + const AES128_ctx* ctx = (const AES128_ctx*)data; + unsigned char scratch[16] = {0}; + int i; + for (i = 0; i < 4000000 / 16; i++) { + AES128_encrypt(ctx, 1, scratch, scratch); + } +} + +static void bench_AES128_decrypt(void* data) { + const AES128_ctx* ctx = (const AES128_ctx*)data; + unsigned char scratch[16] = {0}; + int i; + for (i = 0; i < 4000000 / 16; i++) { + AES128_decrypt(ctx, 1, scratch, scratch); + } +} + +static void bench_AES192_init(void* data) { + AES192_ctx* ctx = (AES192_ctx*)data; + int i; + for (i = 0; i < 50000; i++) { + AES192_init(ctx, (unsigned char*)ctx); + } +} + +static void bench_AES192_encrypt_setup(void* data) { + AES192_ctx* ctx = (AES192_ctx*)data; + static const unsigned char key[16] = {0}; + AES192_init(ctx, key); +} + +static void bench_AES192_encrypt(void* data) { + const AES192_ctx* ctx = (const AES192_ctx*)data; + unsigned char scratch[16] = {0}; + int i; + for (i = 0; i < 4000000 / 16; i++) { + AES192_encrypt(ctx, 1, scratch, scratch); + } +} + +static void bench_AES192_decrypt(void* data) { + const AES192_ctx* ctx = (const AES192_ctx*)data; + unsigned char scratch[16] = {0}; + int i; + for (i = 0; i < 4000000 / 16; i++) { + AES192_decrypt(ctx, 1, scratch, scratch); + } +} + +static void bench_AES256_init(void* data) { + AES256_ctx* ctx = (AES256_ctx*)data; + int i; + for (i = 0; i < 50000; i++) { + AES256_init(ctx, (unsigned char*)ctx); + } +} + + +static void bench_AES256_encrypt_setup(void* data) { + AES256_ctx* ctx = (AES256_ctx*)data; + static const unsigned char key[16] = {0}; + AES256_init(ctx, key); +} + +static void bench_AES256_encrypt(void* data) { + const AES256_ctx* ctx = (const AES256_ctx*)data; + unsigned char scratch[16] = {0}; + int i; + for (i = 0; i < 4000000 / 16; i++) { + AES256_encrypt(ctx, 1, scratch, scratch); + } +} + +static void bench_AES256_decrypt(void* data) { + const AES256_ctx* ctx = (const AES256_ctx*)data; + unsigned char scratch[16] = {0}; + int i; + for (i = 0; i < 4000000 / 16; i++) { + AES256_decrypt(ctx, 1, scratch, scratch); + } +} + +int main(void) { + AES128_ctx ctx128; + AES192_ctx ctx192; + AES256_ctx ctx256; + run_benchmark("aes128_init", bench_AES128_init, NULL, NULL, &ctx128, 20, 50000); + run_benchmark("aes128_encrypt_byte", bench_AES128_encrypt, bench_AES128_encrypt_setup, NULL, &ctx128, 20, 4000000); + run_benchmark("aes128_decrypt_byte", bench_AES128_decrypt, bench_AES128_encrypt_setup, NULL, &ctx128, 20, 4000000); + run_benchmark("aes192_init", bench_AES192_init, NULL, NULL, &ctx192, 20, 50000); + run_benchmark("aes192_encrypt_byte", bench_AES192_encrypt, bench_AES192_encrypt_setup, NULL, &ctx192, 20, 4000000); + run_benchmark("aes192_decrypt_byte", bench_AES192_decrypt, bench_AES192_encrypt_setup, NULL, &ctx192, 20, 4000000); + run_benchmark("aes256_init", bench_AES256_init, NULL, NULL, &ctx256, 20, 50000); + run_benchmark("aes256_encrypt_byte", bench_AES256_encrypt, bench_AES256_encrypt_setup, NULL, &ctx256, 20, 4000000); + run_benchmark("aes256_decrypt_byte", bench_AES256_decrypt, bench_AES256_encrypt_setup, NULL, &ctx256, 20, 4000000); + return 0; +} diff --git a/src/crypto/ctaes/ctaes.c b/src/crypto/ctaes/ctaes.c new file mode 100644 index 0000000000..2389fc0bb2 --- /dev/null +++ b/src/crypto/ctaes/ctaes.c @@ -0,0 +1,556 @@ + /********************************************************************* + * Copyright (c) 2016 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +/* Constant time, unoptimized, concise, plain C, AES implementation + * Based On: + * Emilia Kasper and Peter Schwabe, Faster and Timing-Attack Resistant AES-GCM + * http://www.iacr.org/archive/ches2009/57470001/57470001.pdf + * But using 8 16-bit integers representing a single AES state rather than 8 128-bit + * integers representing 8 AES states. + */ + +#include "ctaes.h" + +/* Slice variable slice_i contains the i'th bit of the 16 state variables in this order: + * 0 1 2 3 + * 4 5 6 7 + * 8 9 10 11 + * 12 13 14 15 + */ + +/** Convert a byte to sliced form, storing it corresponding to given row and column in s */ +static void LoadByte(AES_state* s, unsigned char byte, int r, int c) { + int i; + for (i = 0; i < 8; i++) { + s->slice[i] |= (byte & 1) << (r * 4 + c); + byte >>= 1; + } +} + +/** Load 16 bytes of data into 8 sliced integers */ +static void LoadBytes(AES_state *s, const unsigned char* data16) { + int c; + for (c = 0; c < 4; c++) { + int r; + for (r = 0; r < 4; r++) { + LoadByte(s, *(data16++), r, c); + } + } +} + +/** Convert 8 sliced integers into 16 bytes of data */ +static void SaveBytes(unsigned char* data16, const AES_state *s) { + int c; + for (c = 0; c < 4; c++) { + int r; + for (r = 0; r < 4; r++) { + int b; + uint8_t v = 0; + for (b = 0; b < 8; b++) { + v |= ((s->slice[b] >> (r * 4 + c)) & 1) << b; + } + *(data16++) = v; + } + } +} + +/* S-box implementation based on the gate logic from: + * Joan Boyar and Rene Peralta, A depth-16 circuit for the AES S-box. + * https://eprint.iacr.org/2011/332.pdf +*/ +static void SubBytes(AES_state *s, int inv) { + /* Load the bit slices */ + uint16_t U0 = s->slice[7], U1 = s->slice[6], U2 = s->slice[5], U3 = s->slice[4]; + uint16_t U4 = s->slice[3], U5 = s->slice[2], U6 = s->slice[1], U7 = s->slice[0]; + + uint16_t T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16; + uint16_t T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, D; + uint16_t M1, M6, M11, M13, M15, M20, M21, M22, M23, M25, M37, M38, M39, M40; + uint16_t M41, M42, M43, M44, M45, M46, M47, M48, M49, M50, M51, M52, M53, M54; + uint16_t M55, M56, M57, M58, M59, M60, M61, M62, M63; + + if (inv) { + uint16_t R5, R13, R17, R18, R19; + /* Undo linear postprocessing */ + T23 = U0 ^ U3; + T22 = ~(U1 ^ U3); + T2 = ~(U0 ^ U1); + T1 = U3 ^ U4; + T24 = ~(U4 ^ U7); + R5 = U6 ^ U7; + T8 = ~(U1 ^ T23); + T19 = T22 ^ R5; + T9 = ~(U7 ^ T1); + T10 = T2 ^ T24; + T13 = T2 ^ R5; + T3 = T1 ^ R5; + T25 = ~(U2 ^ T1); + R13 = U1 ^ U6; + T17 = ~(U2 ^ T19); + T20 = T24 ^ R13; + T4 = U4 ^ T8; + R17 = ~(U2 ^ U5); + R18 = ~(U5 ^ U6); + R19 = ~(U2 ^ U4); + D = U0 ^ R17; + T6 = T22 ^ R17; + T16 = R13 ^ R19; + T27 = T1 ^ R18; + T15 = T10 ^ T27; + T14 = T10 ^ R18; + T26 = T3 ^ T16; + } else { + /* Linear preprocessing. */ + T1 = U0 ^ U3; + T2 = U0 ^ U5; + T3 = U0 ^ U6; + T4 = U3 ^ U5; + T5 = U4 ^ U6; + T6 = T1 ^ T5; + T7 = U1 ^ U2; + T8 = U7 ^ T6; + T9 = U7 ^ T7; + T10 = T6 ^ T7; + T11 = U1 ^ U5; + T12 = U2 ^ U5; + T13 = T3 ^ T4; + T14 = T6 ^ T11; + T15 = T5 ^ T11; + T16 = T5 ^ T12; + T17 = T9 ^ T16; + T18 = U3 ^ U7; + T19 = T7 ^ T18; + T20 = T1 ^ T19; + T21 = U6 ^ U7; + T22 = T7 ^ T21; + T23 = T2 ^ T22; + T24 = T2 ^ T10; + T25 = T20 ^ T17; + T26 = T3 ^ T16; + T27 = T1 ^ T12; + D = U7; + } + + /* Non-linear transformation (identical to the code in SubBytes) */ + M1 = T13 & T6; + M6 = T3 & T16; + M11 = T1 & T15; + M13 = (T4 & T27) ^ M11; + M15 = (T2 & T10) ^ M11; + M20 = T14 ^ M1 ^ (T23 & T8) ^ M13; + M21 = (T19 & D) ^ M1 ^ T24 ^ M15; + M22 = T26 ^ M6 ^ (T22 & T9) ^ M13; + M23 = (T20 & T17) ^ M6 ^ M15 ^ T25; + M25 = M22 & M20; + M37 = M21 ^ ((M20 ^ M21) & (M23 ^ M25)); + M38 = M20 ^ M25 ^ (M21 | (M20 & M23)); + M39 = M23 ^ ((M22 ^ M23) & (M21 ^ M25)); + M40 = M22 ^ M25 ^ (M23 | (M21 & M22)); + M41 = M38 ^ M40; + M42 = M37 ^ M39; + M43 = M37 ^ M38; + M44 = M39 ^ M40; + M45 = M42 ^ M41; + M46 = M44 & T6; + M47 = M40 & T8; + M48 = M39 & D; + M49 = M43 & T16; + M50 = M38 & T9; + M51 = M37 & T17; + M52 = M42 & T15; + M53 = M45 & T27; + M54 = M41 & T10; + M55 = M44 & T13; + M56 = M40 & T23; + M57 = M39 & T19; + M58 = M43 & T3; + M59 = M38 & T22; + M60 = M37 & T20; + M61 = M42 & T1; + M62 = M45 & T4; + M63 = M41 & T2; + + if (inv){ + /* Undo linear preprocessing */ + uint16_t P0 = M52 ^ M61; + uint16_t P1 = M58 ^ M59; + uint16_t P2 = M54 ^ M62; + uint16_t P3 = M47 ^ M50; + uint16_t P4 = M48 ^ M56; + uint16_t P5 = M46 ^ M51; + uint16_t P6 = M49 ^ M60; + uint16_t P7 = P0 ^ P1; + uint16_t P8 = M50 ^ M53; + uint16_t P9 = M55 ^ M63; + uint16_t P10 = M57 ^ P4; + uint16_t P11 = P0 ^ P3; + uint16_t P12 = M46 ^ M48; + uint16_t P13 = M49 ^ M51; + uint16_t P14 = M49 ^ M62; + uint16_t P15 = M54 ^ M59; + uint16_t P16 = M57 ^ M61; + uint16_t P17 = M58 ^ P2; + uint16_t P18 = M63 ^ P5; + uint16_t P19 = P2 ^ P3; + uint16_t P20 = P4 ^ P6; + uint16_t P22 = P2 ^ P7; + uint16_t P23 = P7 ^ P8; + uint16_t P24 = P5 ^ P7; + uint16_t P25 = P6 ^ P10; + uint16_t P26 = P9 ^ P11; + uint16_t P27 = P10 ^ P18; + uint16_t P28 = P11 ^ P25; + uint16_t P29 = P15 ^ P20; + s->slice[7] = P13 ^ P22; + s->slice[6] = P26 ^ P29; + s->slice[5] = P17 ^ P28; + s->slice[4] = P12 ^ P22; + s->slice[3] = P23 ^ P27; + s->slice[2] = P19 ^ P24; + s->slice[1] = P14 ^ P23; + s->slice[0] = P9 ^ P16; + } else { + /* Linear postprocessing */ + uint16_t L0 = M61 ^ M62; + uint16_t L1 = M50 ^ M56; + uint16_t L2 = M46 ^ M48; + uint16_t L3 = M47 ^ M55; + uint16_t L4 = M54 ^ M58; + uint16_t L5 = M49 ^ M61; + uint16_t L6 = M62 ^ L5; + uint16_t L7 = M46 ^ L3; + uint16_t L8 = M51 ^ M59; + uint16_t L9 = M52 ^ M53; + uint16_t L10 = M53 ^ L4; + uint16_t L11 = M60 ^ L2; + uint16_t L12 = M48 ^ M51; + uint16_t L13 = M50 ^ L0; + uint16_t L14 = M52 ^ M61; + uint16_t L15 = M55 ^ L1; + uint16_t L16 = M56 ^ L0; + uint16_t L17 = M57 ^ L1; + uint16_t L18 = M58 ^ L8; + uint16_t L19 = M63 ^ L4; + uint16_t L20 = L0 ^ L1; + uint16_t L21 = L1 ^ L7; + uint16_t L22 = L3 ^ L12; + uint16_t L23 = L18 ^ L2; + uint16_t L24 = L15 ^ L9; + uint16_t L25 = L6 ^ L10; + uint16_t L26 = L7 ^ L9; + uint16_t L27 = L8 ^ L10; + uint16_t L28 = L11 ^ L14; + uint16_t L29 = L11 ^ L17; + s->slice[7] = L6 ^ L24; + s->slice[6] = ~(L16 ^ L26); + s->slice[5] = ~(L19 ^ L28); + s->slice[4] = L6 ^ L21; + s->slice[3] = L20 ^ L22; + s->slice[2] = L25 ^ L29; + s->slice[1] = ~(L13 ^ L27); + s->slice[0] = ~(L6 ^ L23); + } +} + +#define BIT_RANGE(from,to) (((1 << ((to) - (from))) - 1) << (from)) + +#define BIT_RANGE_LEFT(x,from,to,shift) (((x) & BIT_RANGE((from), (to))) << (shift)) +#define BIT_RANGE_RIGHT(x,from,to,shift) (((x) & BIT_RANGE((from), (to))) >> (shift)) + +static void ShiftRows(AES_state* s) { + int i; + for (i = 0; i < 8; i++) { + uint16_t v = s->slice[i]; + s->slice[i] = + (v & BIT_RANGE(0, 4)) | + BIT_RANGE_LEFT(v, 4, 5, 3) | BIT_RANGE_RIGHT(v, 5, 8, 1) | + BIT_RANGE_LEFT(v, 8, 10, 2) | BIT_RANGE_RIGHT(v, 10, 12, 2) | + BIT_RANGE_LEFT(v, 12, 15, 1) | BIT_RANGE_RIGHT(v, 15, 16, 3); + } +} + +static void InvShiftRows(AES_state* s) { + int i; + for (i = 0; i < 8; i++) { + uint16_t v = s->slice[i]; + s->slice[i] = + (v & BIT_RANGE(0, 4)) | + BIT_RANGE_LEFT(v, 4, 7, 1) | BIT_RANGE_RIGHT(v, 7, 8, 3) | + BIT_RANGE_LEFT(v, 8, 10, 2) | BIT_RANGE_RIGHT(v, 10, 12, 2) | + BIT_RANGE_LEFT(v, 12, 13, 3) | BIT_RANGE_RIGHT(v, 13, 16, 1); + } +} + +#define ROT(x,b) (((x) >> ((b) * 4)) | ((x) << ((4-(b)) * 4))) + +static void MixColumns(AES_state* s, int inv) { + /* The MixColumns transform treats the bytes of the columns of the state as + * coefficients of a 3rd degree polynomial over GF(2^8) and multiplies them + * by the fixed polynomial a(x) = {03}x^3 + {01}x^2 + {01}x + {02}, modulo + * x^4 + {01}. + * + * In the inverse transform, we multiply by the inverse of a(x), + * a^-1(x) = {0b}x^3 + {0d}x^2 + {09}x + {0e}. This is equal to + * a(x) * ({04}x^2 + {05}), so we can reuse the forward transform's code + * (found in OpenSSL's bsaes-x86_64.pl, attributed to Jussi Kivilinna) + * + * In the bitsliced representation, a multiplication of every column by x + * mod x^4 + 1 is simply a right rotation. + */ + + /* Shared for both directions is a multiplication by a(x), which can be + * rewritten as (x^3 + x^2 + x) + {02}*(x^3 + {01}). + * + * First compute s into the s? variables, (x^3 + {01}) * s into the s?_01 + * variables and (x^3 + x^2 + x)*s into the s?_123 variables. + */ + uint16_t s0 = s->slice[0], s1 = s->slice[1], s2 = s->slice[2], s3 = s->slice[3]; + uint16_t s4 = s->slice[4], s5 = s->slice[5], s6 = s->slice[6], s7 = s->slice[7]; + uint16_t s0_01 = s0 ^ ROT(s0, 1), s0_123 = ROT(s0_01, 1) ^ ROT(s0, 3); + uint16_t s1_01 = s1 ^ ROT(s1, 1), s1_123 = ROT(s1_01, 1) ^ ROT(s1, 3); + uint16_t s2_01 = s2 ^ ROT(s2, 1), s2_123 = ROT(s2_01, 1) ^ ROT(s2, 3); + uint16_t s3_01 = s3 ^ ROT(s3, 1), s3_123 = ROT(s3_01, 1) ^ ROT(s3, 3); + uint16_t s4_01 = s4 ^ ROT(s4, 1), s4_123 = ROT(s4_01, 1) ^ ROT(s4, 3); + uint16_t s5_01 = s5 ^ ROT(s5, 1), s5_123 = ROT(s5_01, 1) ^ ROT(s5, 3); + uint16_t s6_01 = s6 ^ ROT(s6, 1), s6_123 = ROT(s6_01, 1) ^ ROT(s6, 3); + uint16_t s7_01 = s7 ^ ROT(s7, 1), s7_123 = ROT(s7_01, 1) ^ ROT(s7, 3); + /* Now compute s = s?_123 + {02} * s?_01. */ + s->slice[0] = s7_01 ^ s0_123; + s->slice[1] = s7_01 ^ s0_01 ^ s1_123; + s->slice[2] = s1_01 ^ s2_123; + s->slice[3] = s7_01 ^ s2_01 ^ s3_123; + s->slice[4] = s7_01 ^ s3_01 ^ s4_123; + s->slice[5] = s4_01 ^ s5_123; + s->slice[6] = s5_01 ^ s6_123; + s->slice[7] = s6_01 ^ s7_123; + if (inv) { + /* In the reverse direction, we further need to multiply by + * {04}x^2 + {05}, which can be written as {04} * (x^2 + {01}) + {01}. + * + * First compute (x^2 + {01}) * s into the t?_02 variables: */ + uint16_t t0_02 = s->slice[0] ^ ROT(s->slice[0], 2); + uint16_t t1_02 = s->slice[1] ^ ROT(s->slice[1], 2); + uint16_t t2_02 = s->slice[2] ^ ROT(s->slice[2], 2); + uint16_t t3_02 = s->slice[3] ^ ROT(s->slice[3], 2); + uint16_t t4_02 = s->slice[4] ^ ROT(s->slice[4], 2); + uint16_t t5_02 = s->slice[5] ^ ROT(s->slice[5], 2); + uint16_t t6_02 = s->slice[6] ^ ROT(s->slice[6], 2); + uint16_t t7_02 = s->slice[7] ^ ROT(s->slice[7], 2); + /* And then update s += {04} * t?_02 */ + s->slice[0] ^= t6_02; + s->slice[1] ^= t6_02 ^ t7_02; + s->slice[2] ^= t0_02 ^ t7_02; + s->slice[3] ^= t1_02 ^ t6_02; + s->slice[4] ^= t2_02 ^ t6_02 ^ t7_02; + s->slice[5] ^= t3_02 ^ t7_02; + s->slice[6] ^= t4_02; + s->slice[7] ^= t5_02; + } +} + +static void AddRoundKey(AES_state* s, const AES_state* round) { + int b; + for (b = 0; b < 8; b++) { + s->slice[b] ^= round->slice[b]; + } +} + +/** column_0(s) = column_c(a) */ +static void GetOneColumn(AES_state* s, const AES_state* a, int c) { + int b; + for (b = 0; b < 8; b++) { + s->slice[b] = (a->slice[b] >> c) & 0x1111; + } +} + +/** column_c1(r) |= (column_0(s) ^= column_c2(a)) */ +static void KeySetupColumnMix(AES_state* s, AES_state* r, const AES_state* a, int c1, int c2) { + int b; + for (b = 0; b < 8; b++) { + r->slice[b] |= ((s->slice[b] ^= ((a->slice[b] >> c2) & 0x1111)) & 0x1111) << c1; + } +} + +/** Rotate the rows in s one position upwards, and xor in r */ +static void KeySetupTransform(AES_state* s, const AES_state* r) { + int b; + for (b = 0; b < 8; b++) { + s->slice[b] = ((s->slice[b] >> 4) | (s->slice[b] << 12)) ^ r->slice[b]; + } +} + +/* Multiply the cells in s by x, as polynomials over GF(2) mod x^8 + x^4 + x^3 + x + 1 */ +static void MultX(AES_state* s) { + uint16_t top = s->slice[7]; + s->slice[7] = s->slice[6]; + s->slice[6] = s->slice[5]; + s->slice[5] = s->slice[4]; + s->slice[4] = s->slice[3] ^ top; + s->slice[3] = s->slice[2] ^ top; + s->slice[2] = s->slice[1]; + s->slice[1] = s->slice[0] ^ top; + s->slice[0] = top; +} + +/** Expand the cipher key into the key schedule. + * + * state must be a pointer to an array of size nrounds + 1. + * key must be a pointer to 4 * nkeywords bytes. + * + * AES128 uses nkeywords = 4, nrounds = 10 + * AES192 uses nkeywords = 6, nrounds = 12 + * AES256 uses nkeywords = 8, nrounds = 14 + */ +static void AES_setup(AES_state* rounds, const uint8_t* key, int nkeywords, int nrounds) +{ + int i; + + /* The one-byte round constant */ + AES_state rcon = {{1,0,0,0,0,0,0,0}}; + /* The number of the word being generated, modulo nkeywords */ + int pos = 0; + /* The column representing the word currently being processed */ + AES_state column; + + for (i = 0; i < nrounds + 1; i++) { + int b; + for (b = 0; b < 8; b++) { + rounds[i].slice[b] = 0; + } + } + + /* The first nkeywords round columns are just taken from the key directly. */ + for (i = 0; i < nkeywords; i++) { + int r; + for (r = 0; r < 4; r++) { + LoadByte(&rounds[i >> 2], *(key++), r, i & 3); + } + } + + GetOneColumn(&column, &rounds[(nkeywords - 1) >> 2], (nkeywords - 1) & 3); + + for (i = nkeywords; i < 4 * (nrounds + 1); i++) { + /* Transform column */ + if (pos == 0) { + SubBytes(&column, 0); + KeySetupTransform(&column, &rcon); + MultX(&rcon); + } else if (nkeywords > 6 && pos == 4) { + SubBytes(&column, 0); + } + if (++pos == nkeywords) pos = 0; + KeySetupColumnMix(&column, &rounds[i >> 2], &rounds[(i - nkeywords) >> 2], i & 3, (i - nkeywords) & 3); + } +} + +static void AES_encrypt(const AES_state* rounds, int nrounds, unsigned char* cipher16, const unsigned char* plain16) { + AES_state s = {{0}}; + int round; + + LoadBytes(&s, plain16); + AddRoundKey(&s, rounds++); + + for (round = 1; round < nrounds; round++) { + SubBytes(&s, 0); + ShiftRows(&s); + MixColumns(&s, 0); + AddRoundKey(&s, rounds++); + } + + SubBytes(&s, 0); + ShiftRows(&s); + AddRoundKey(&s, rounds); + + SaveBytes(cipher16, &s); +} + +static void AES_decrypt(const AES_state* rounds, int nrounds, unsigned char* plain16, const unsigned char* cipher16) { + /* Most AES decryption implementations use the alternate scheme + * (the Equivalent Inverse Cipher), which looks more like encryption, but + * needs different round constants. We can't reuse any code here anyway, so + * don't bother. */ + AES_state s = {{0}}; + int round; + + rounds += nrounds; + + LoadBytes(&s, cipher16); + AddRoundKey(&s, rounds--); + + for (round = 1; round < nrounds; round++) { + InvShiftRows(&s); + SubBytes(&s, 1); + AddRoundKey(&s, rounds--); + MixColumns(&s, 1); + } + + InvShiftRows(&s); + SubBytes(&s, 1); + AddRoundKey(&s, rounds); + + SaveBytes(plain16, &s); +} + +void AES128_init(AES128_ctx* ctx, const unsigned char* key16) { + AES_setup(ctx->rk, key16, 4, 10); +} + +void AES128_encrypt(const AES128_ctx* ctx, size_t blocks, unsigned char* cipher16, const unsigned char* plain16) { + while (blocks--) { + AES_encrypt(ctx->rk, 10, cipher16, plain16); + cipher16 += 16; + plain16 += 16; + } +} + +void AES128_decrypt(const AES128_ctx* ctx, size_t blocks, unsigned char* plain16, const unsigned char* cipher16) { + while (blocks--) { + AES_decrypt(ctx->rk, 10, plain16, cipher16); + cipher16 += 16; + plain16 += 16; + } +} + +void AES192_init(AES192_ctx* ctx, const unsigned char* key24) { + AES_setup(ctx->rk, key24, 6, 12); +} + +void AES192_encrypt(const AES192_ctx* ctx, size_t blocks, unsigned char* cipher16, const unsigned char* plain16) { + while (blocks--) { + AES_encrypt(ctx->rk, 12, cipher16, plain16); + cipher16 += 16; + plain16 += 16; + } + +} + +void AES192_decrypt(const AES192_ctx* ctx, size_t blocks, unsigned char* plain16, const unsigned char* cipher16) { + while (blocks--) { + AES_decrypt(ctx->rk, 12, plain16, cipher16); + cipher16 += 16; + plain16 += 16; + } +} + +void AES256_init(AES256_ctx* ctx, const unsigned char* key32) { + AES_setup(ctx->rk, key32, 8, 14); +} + +void AES256_encrypt(const AES256_ctx* ctx, size_t blocks, unsigned char* cipher16, const unsigned char* plain16) { + while (blocks--) { + AES_encrypt(ctx->rk, 14, cipher16, plain16); + cipher16 += 16; + plain16 += 16; + } +} + +void AES256_decrypt(const AES256_ctx* ctx, size_t blocks, unsigned char* plain16, const unsigned char* cipher16) { + while (blocks--) { + AES_decrypt(ctx->rk, 14, plain16, cipher16); + cipher16 += 16; + plain16 += 16; + } +} diff --git a/src/crypto/ctaes/ctaes.h b/src/crypto/ctaes/ctaes.h new file mode 100644 index 0000000000..2f0af04216 --- /dev/null +++ b/src/crypto/ctaes/ctaes.h @@ -0,0 +1,41 @@ + /********************************************************************* + * Copyright (c) 2016 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef _CTAES_H_ +#define _CTAES_H_ 1 + +#include <stdint.h> +#include <stdlib.h> + +typedef struct { + uint16_t slice[8]; +} AES_state; + +typedef struct { + AES_state rk[11]; +} AES128_ctx; + +typedef struct { + AES_state rk[13]; +} AES192_ctx; + +typedef struct { + AES_state rk[15]; +} AES256_ctx; + +void AES128_init(AES128_ctx* ctx, const unsigned char* key16); +void AES128_encrypt(const AES128_ctx* ctx, size_t blocks, unsigned char* cipher16, const unsigned char* plain16); +void AES128_decrypt(const AES128_ctx* ctx, size_t blocks, unsigned char* plain16, const unsigned char* cipher16); + +void AES192_init(AES192_ctx* ctx, const unsigned char* key24); +void AES192_encrypt(const AES192_ctx* ctx, size_t blocks, unsigned char* cipher16, const unsigned char* plain16); +void AES192_decrypt(const AES192_ctx* ctx, size_t blocks, unsigned char* plain16, const unsigned char* cipher16); + +void AES256_init(AES256_ctx* ctx, const unsigned char* key32); +void AES256_encrypt(const AES256_ctx* ctx, size_t blocks, unsigned char* cipher16, const unsigned char* plain16); +void AES256_decrypt(const AES256_ctx* ctx, size_t blocks, unsigned char* plain16, const unsigned char* cipher16); + +#endif diff --git a/src/crypto/ctaes/test.c b/src/crypto/ctaes/test.c new file mode 100644 index 0000000000..fce1696acd --- /dev/null +++ b/src/crypto/ctaes/test.c @@ -0,0 +1,110 @@ + /********************************************************************* + * Copyright (c) 2016 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#include "ctaes.h" + +#include <stdio.h> +#include <string.h> +#include <assert.h> + +typedef struct { + int keysize; + const char* key; + const char* plain; + const char* cipher; +} ctaes_test; + +static const ctaes_test ctaes_tests[] = { + /* AES test vectors from FIPS 197. */ + {128, "000102030405060708090a0b0c0d0e0f", "00112233445566778899aabbccddeeff", "69c4e0d86a7b0430d8cdb78070b4c55a"}, + {192, "000102030405060708090a0b0c0d0e0f1011121314151617", "00112233445566778899aabbccddeeff", "dda97ca4864cdfe06eaf70a0ec0d7191"}, + {256, "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", "00112233445566778899aabbccddeeff", "8ea2b7ca516745bfeafc49904b496089"}, + + /* AES-ECB test vectors from NIST sp800-38a. */ + {128, "2b7e151628aed2a6abf7158809cf4f3c", "6bc1bee22e409f96e93d7e117393172a", "3ad77bb40d7a3660a89ecaf32466ef97"}, + {128, "2b7e151628aed2a6abf7158809cf4f3c", "ae2d8a571e03ac9c9eb76fac45af8e51", "f5d3d58503b9699de785895a96fdbaaf"}, + {128, "2b7e151628aed2a6abf7158809cf4f3c", "30c81c46a35ce411e5fbc1191a0a52ef", "43b1cd7f598ece23881b00e3ed030688"}, + {128, "2b7e151628aed2a6abf7158809cf4f3c", "f69f2445df4f9b17ad2b417be66c3710", "7b0c785e27e8ad3f8223207104725dd4"}, + {192, "8e73b0f7da0e6452c810f32b809079e562f8ead2522c6b7b", "6bc1bee22e409f96e93d7e117393172a", "bd334f1d6e45f25ff712a214571fa5cc"}, + {192, "8e73b0f7da0e6452c810f32b809079e562f8ead2522c6b7b", "ae2d8a571e03ac9c9eb76fac45af8e51", "974104846d0ad3ad7734ecb3ecee4eef"}, + {192, "8e73b0f7da0e6452c810f32b809079e562f8ead2522c6b7b", "30c81c46a35ce411e5fbc1191a0a52ef", "ef7afd2270e2e60adce0ba2face6444e"}, + {192, "8e73b0f7da0e6452c810f32b809079e562f8ead2522c6b7b", "f69f2445df4f9b17ad2b417be66c3710", "9a4b41ba738d6c72fb16691603c18e0e"}, + {256, "603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4", "6bc1bee22e409f96e93d7e117393172a", "f3eed1bdb5d2a03c064b5a7e3db181f8"}, + {256, "603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4", "ae2d8a571e03ac9c9eb76fac45af8e51", "591ccb10d410ed26dc5ba74a31362870"}, + {256, "603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4", "30c81c46a35ce411e5fbc1191a0a52ef", "b6ed21b99ca6f4f9f153e7b1beafed1d"}, + {256, "603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4", "f69f2445df4f9b17ad2b417be66c3710", "23304b7a39f9f3ff067d8d8f9e24ecc7"} +}; + +static void from_hex(unsigned char* data, int len, const char* hex) { + int p; + for (p = 0; p < len; p++) { + int v = 0; + int n; + for (n = 0; n < 2; n++) { + assert((*hex >= '0' && *hex <= '9') || (*hex >= 'a' && *hex <= 'f')); + if (*hex >= '0' && *hex <= '9') { + v |= (*hex - '0') << (4 * (1 - n)); + } else { + v |= (*hex - 'a' + 10) << (4 * (1 - n)); + } + hex++; + } + *(data++) = v; + } + assert(*hex == 0); +} + +int main(void) { + int i; + int fail = 0; + for (i = 0; i < sizeof(ctaes_tests) / sizeof(ctaes_tests[0]); i++) { + unsigned char key[32], plain[16], cipher[16], ciphered[16], deciphered[16]; + const ctaes_test* test = &ctaes_tests[i]; + assert(test->keysize == 128 || test->keysize == 192 || test->keysize == 256); + from_hex(plain, 16, test->plain); + from_hex(cipher, 16, test->cipher); + switch (test->keysize) { + case 128: { + AES128_ctx ctx; + from_hex(key, 16, test->key); + AES128_init(&ctx, key); + AES128_encrypt(&ctx, 1, ciphered, plain); + AES128_decrypt(&ctx, 1, deciphered, cipher); + break; + } + case 192: { + AES192_ctx ctx; + from_hex(key, 24, test->key); + AES192_init(&ctx, key); + AES192_encrypt(&ctx, 1, ciphered, plain); + AES192_decrypt(&ctx, 1, deciphered, cipher); + break; + } + case 256: { + AES256_ctx ctx; + from_hex(key, 32, test->key); + AES256_init(&ctx, key); + AES256_encrypt(&ctx, 1, ciphered, plain); + AES256_decrypt(&ctx, 1, deciphered, cipher); + break; + } + } + if (memcmp(cipher, ciphered, 16)) { + fprintf(stderr, "E(key=\"%s\", plain=\"%s\") != \"%s\"\n", test->key, test->plain, test->cipher); + fail++; + } + if (memcmp(plain, deciphered, 16)) { + fprintf(stderr, "D(key=\"%s\", cipher=\"%s\") != \"%s\"\n", test->key, test->cipher, test->plain); + fail++; + } + } + if (fail == 0) { + fprintf(stderr, "All tests succesful\n"); + } else { + fprintf(stderr, "%i tests failed\n", fail); + } + return (fail != 0); +} diff --git a/src/hash.cpp b/src/hash.cpp index 7f3cf1a1fa..20a83342db 100644 --- a/src/hash.cpp +++ b/src/hash.cpp @@ -81,3 +81,130 @@ void BIP32Hash(const ChainCode &chainCode, unsigned int nChild, unsigned char he num[3] = (nChild >> 0) & 0xFF; CHMAC_SHA512(chainCode.begin(), chainCode.size()).Write(&header, 1).Write(data, 32).Write(num, 4).Finalize(output); } + +#define ROTL(x, b) (uint64_t)(((x) << (b)) | ((x) >> (64 - (b)))) + +#define SIPROUND do { \ + v0 += v1; v1 = ROTL(v1, 13); v1 ^= v0; \ + v0 = ROTL(v0, 32); \ + v2 += v3; v3 = ROTL(v3, 16); v3 ^= v2; \ + v0 += v3; v3 = ROTL(v3, 21); v3 ^= v0; \ + v2 += v1; v1 = ROTL(v1, 17); v1 ^= v2; \ + v2 = ROTL(v2, 32); \ +} while (0) + +CSipHasher::CSipHasher(uint64_t k0, uint64_t k1) +{ + v[0] = 0x736f6d6570736575ULL ^ k0; + v[1] = 0x646f72616e646f6dULL ^ k1; + v[2] = 0x6c7967656e657261ULL ^ k0; + v[3] = 0x7465646279746573ULL ^ k1; + count = 0; + tmp = 0; +} + +CSipHasher& CSipHasher::Write(uint64_t data) +{ + uint64_t v0 = v[0], v1 = v[1], v2 = v[2], v3 = v[3]; + + assert(count % 8 == 0); + + v3 ^= data; + SIPROUND; + SIPROUND; + v0 ^= data; + + v[0] = v0; + v[1] = v1; + v[2] = v2; + v[3] = v3; + + count += 8; + return *this; +} + +CSipHasher& CSipHasher::Write(const unsigned char* data, size_t size) +{ + uint64_t v0 = v[0], v1 = v[1], v2 = v[2], v3 = v[3]; + uint64_t t = tmp; + int c = count; + + while (size--) { + t |= ((uint64_t)(*(data++))) << (8 * (c % 8)); + c++; + if ((c & 7) == 0) { + v3 ^= t; + SIPROUND; + SIPROUND; + v0 ^= t; + t = 0; + } + } + + v[0] = v0; + v[1] = v1; + v[2] = v2; + v[3] = v3; + count = c; + tmp = t; + + return *this; +} + +uint64_t CSipHasher::Finalize() const +{ + uint64_t v0 = v[0], v1 = v[1], v2 = v[2], v3 = v[3]; + + uint64_t t = tmp | (((uint64_t)count) << 56); + + v3 ^= t; + SIPROUND; + SIPROUND; + v0 ^= t; + v2 ^= 0xFF; + SIPROUND; + SIPROUND; + SIPROUND; + SIPROUND; + return v0 ^ v1 ^ v2 ^ v3; +} + +uint64_t SipHashUint256(uint64_t k0, uint64_t k1, const uint256& val) +{ + /* Specialized implementation for efficiency */ + uint64_t d = val.GetUint64(0); + + uint64_t v0 = 0x736f6d6570736575ULL ^ k0; + uint64_t v1 = 0x646f72616e646f6dULL ^ k1; + uint64_t v2 = 0x6c7967656e657261ULL ^ k0; + uint64_t v3 = 0x7465646279746573ULL ^ k1 ^ d; + + SIPROUND; + SIPROUND; + v0 ^= d; + d = val.GetUint64(1); + v3 ^= d; + SIPROUND; + SIPROUND; + v0 ^= d; + d = val.GetUint64(2); + v3 ^= d; + SIPROUND; + SIPROUND; + v0 ^= d; + d = val.GetUint64(3); + v3 ^= d; + SIPROUND; + SIPROUND; + v0 ^= d; + v3 ^= ((uint64_t)4) << 59; + SIPROUND; + SIPROUND; + v0 ^= ((uint64_t)4) << 59; + v2 ^= 0xFF; + SIPROUND; + SIPROUND; + SIPROUND; + SIPROUND; + return v0 ^ v1 ^ v2 ^ v3; +} diff --git a/src/hash.h b/src/hash.h index 97955c8d5a..db4e130ae7 100644 --- a/src/hash.h +++ b/src/hash.h @@ -171,4 +171,38 @@ unsigned int MurmurHash3(unsigned int nHashSeed, const std::vector<unsigned char void BIP32Hash(const ChainCode &chainCode, unsigned int nChild, unsigned char header, const unsigned char data[32], unsigned char output[64]); +/** SipHash-2-4 */ +class CSipHasher +{ +private: + uint64_t v[4]; + uint64_t tmp; + int count; + +public: + /** Construct a SipHash calculator initialized with 128-bit key (k0, k1) */ + CSipHasher(uint64_t k0, uint64_t k1); + /** Hash a 64-bit integer worth of data + * It is treated as if this was the little-endian interpretation of 8 bytes. + * This function can only be used when a multiple of 8 bytes have been written so far. + */ + CSipHasher& Write(uint64_t data); + /** Hash arbitrary bytes. */ + CSipHasher& Write(const unsigned char* data, size_t size); + /** Compute the 64-bit SipHash-2-4 of the data written so far. The object remains untouched. */ + uint64_t Finalize() const; +}; + +/** Optimized SipHash-2-4 implementation for uint256. + * + * It is identical to: + * SipHasher(k0, k1) + * .Write(val.GetUint64(0)) + * .Write(val.GetUint64(1)) + * .Write(val.GetUint64(2)) + * .Write(val.GetUint64(3)) + * .Finalize() + */ +uint64_t SipHashUint256(uint64_t k0, uint64_t k1, const uint256& val); + #endif // BITCOIN_HASH_H diff --git a/src/indirectmap.h b/src/indirectmap.h new file mode 100644 index 0000000000..28e1e8dedd --- /dev/null +++ b/src/indirectmap.h @@ -0,0 +1,52 @@ +#ifndef BITCOIN_INDIRECTMAP_H +#define BITCOIN_INDIRECTMAP_H + +template <class T> +struct DereferencingComparator { bool operator()(const T a, const T b) const { return *a < *b; } }; + +/* Map whose keys are pointers, but are compared by their dereferenced values. + * + * Differs from a plain std::map<const K*, T, DereferencingComparator<K*> > in + * that methods that take a key for comparison take a K rather than taking a K* + * (taking a K* would be confusing, since it's the value rather than the address + * of the object for comparison that matters due to the dereferencing comparator). + * + * Objects pointed to by keys must not be modified in any way that changes the + * result of DereferencingComparator. + */ +template <class K, class T> +class indirectmap { +private: + typedef std::map<const K*, T, DereferencingComparator<const K*> > base; + base m; +public: + typedef typename base::iterator iterator; + typedef typename base::const_iterator const_iterator; + typedef typename base::size_type size_type; + typedef typename base::value_type value_type; + + // passthrough (pointer interface) + std::pair<iterator, bool> insert(const value_type& value) { return m.insert(value); } + + // pass address (value interface) + iterator find(const K& key) { return m.find(&key); } + const_iterator find(const K& key) const { return m.find(&key); } + iterator lower_bound(const K& key) { return m.lower_bound(&key); } + const_iterator lower_bound(const K& key) const { return m.lower_bound(&key); } + size_type erase(const K& key) { return m.erase(&key); } + size_type count(const K& key) const { return m.count(&key); } + + // passthrough + bool empty() const { return m.empty(); } + size_type size() const { return m.size(); } + size_type max_size() const { return m.max_size(); } + void clear() { m.clear(); } + iterator begin() { return m.begin(); } + iterator end() { return m.end(); } + const_iterator begin() const { return m.begin(); } + const_iterator end() const { return m.end(); } + const_iterator cbegin() const { return m.cbegin(); } + const_iterator cend() const { return m.cend(); } +}; + +#endif // BITCOIN_INDIRECTMAP_H diff --git a/src/init.cpp b/src/init.cpp index d19ca530b3..b572bfc327 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -123,7 +123,7 @@ static const char* FEE_ESTIMATES_FILENAME="fee_estimates.dat"; // shutdown thing. // -volatile sig_atomic_t fRequestShutdown = false; +std::atomic<bool> fRequestShutdown(false); void StartShutdown() { @@ -134,6 +134,11 @@ bool ShutdownRequested() return fRequestShutdown; } +/** + * This is a minimally invasive approach to shutdown on LevelDB read errors from the + * chainstate, while keeping user interface out of the common library, which is shared + * between bitcoind, and bitcoin-qt and non-server tools. +*/ class CCoinsViewErrorCatcher : public CCoinsViewBacked { public: @@ -314,7 +319,8 @@ std::string HelpMessage(HelpMessageMode mode) } strUsage += HelpMessageOpt("-datadir=<dir>", _("Specify data directory")); strUsage += HelpMessageOpt("-dbcache=<n>", strprintf(_("Set database cache size in megabytes (%d to %d, default: %d)"), nMinDbCache, nMaxDbCache, nDefaultDbCache)); - strUsage += HelpMessageOpt("-feefilter", strprintf(_("Tell other nodes to filter invs to us by our mempool min fee (default: %u)"), DEFAULT_FEEFILTER)); + if (showDebug) + strUsage += HelpMessageOpt("-feefilter", strprintf("Tell other nodes to filter invs to us by our mempool min fee (default: %u)", DEFAULT_FEEFILTER)); strUsage += HelpMessageOpt("-loadblock=<file>", _("Imports blocks from external blk000??.dat file on startup")); strUsage += HelpMessageOpt("-maxorphantx=<n>", strprintf(_("Keep at most <n> unconnectable transactions in memory (default: %u)"), DEFAULT_MAX_ORPHAN_TRANSACTIONS)); strUsage += HelpMessageOpt("-maxmempool=<n>", strprintf(_("Keep the transaction memory pool below <n> megabytes (default: %u)"), DEFAULT_MAX_MEMPOOL_SIZE)); @@ -327,7 +333,8 @@ std::string HelpMessage(HelpMessageMode mode) strUsage += HelpMessageOpt("-prune=<n>", strprintf(_("Reduce storage requirements by pruning (deleting) old blocks. This mode is incompatible with -txindex and -rescan. " "Warning: Reverting this setting requires re-downloading the entire blockchain. " "(default: 0 = disable pruning blocks, >%u = target size in MiB to use for block files)"), MIN_DISK_SPACE_FOR_BLOCK_FILES / 1024 / 1024)); - strUsage += HelpMessageOpt("-reindex", _("Rebuild block chain index from current blk000??.dat files on startup")); + strUsage += HelpMessageOpt("-reindex-chainstate", _("Rebuild chain state from the currently indexed blocks")); + strUsage += HelpMessageOpt("-reindex", _("Rebuild chain state and block index from the blk*.dat files on disk")); #ifndef WIN32 strUsage += HelpMessageOpt("-sysperms", _("Create new files with system default permissions, instead of umask 077 (only effective with disabled wallet functionality)")); #endif @@ -404,7 +411,7 @@ std::string HelpMessage(HelpMessageMode mode) strUsage += HelpMessageOpt("-limitdescendantcount=<n>", strprintf("Do not accept transactions if any ancestor would have <n> or more in-mempool descendants (default: %u)", DEFAULT_DESCENDANT_LIMIT)); strUsage += HelpMessageOpt("-limitdescendantsize=<n>", strprintf("Do not accept transactions if any ancestor would have more than <n> kilobytes of in-mempool descendants (default: %u).", DEFAULT_DESCENDANT_SIZE_LIMIT)); } - string debugCategories = "addrman, alert, bench, coindb, db, lock, rand, rpc, selectcoins, mempool, mempoolrej, net, proxy, prune, http, libevent, tor, zmq"; // Don't translate these and qt below + string debugCategories = "addrman, alert, bench, coindb, db, http, libevent, lock, mempool, mempoolrej, net, proxy, prune, rand, reindex, rpc, selectcoins, tor, zmq"; // Don't translate these and qt below if (mode == HMM_BITCOIN_QT) debugCategories += ", qt"; strUsage += HelpMessageOpt("-debug=<category>", strprintf(_("Output debugging information (default: %u, supplying <category> is optional)"), 0) + ". " + @@ -472,11 +479,20 @@ std::string HelpMessage(HelpMessageMode mode) std::string LicenseInfo() { + const std::string URL_SOURCE_CODE = "<https://github.com/bitcoin/bitcoin>"; + const std::string URL_WEBSITE = "<https://bitcoincore.org>"; // todo: remove urls from translations on next change return CopyrightHolders(strprintf(_("Copyright (C) %i-%i"), 2009, COPYRIGHT_YEAR) + " ") + "\n" + "\n" + - _("This is experimental software.") + "\n" + + strprintf(_("Please contribute if you find %s useful. " + "Visit %s for further information about the software."), + PACKAGE_NAME, URL_WEBSITE) + "\n" + + strprintf(_("The source code is available from %s."), + URL_SOURCE_CODE) + + "\n" + + "\n" + + _("This is experimental software.") + "\n" + _("Distributed under the MIT software license, see the accompanying file COPYING or <http://www.opensource.org/licenses/mit-license.php>.") + "\n" + "\n" + _("This product includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit <https://www.openssl.org/> and cryptographic software written by Eric Young and UPnP software written by Thomas Bernard.") + @@ -554,9 +570,10 @@ void ThreadImport(std::vector<boost::filesystem::path> vImportFiles) { const CChainParams& chainparams = Params(); RenameThread("bitcoin-loadblk"); + CImportingNow imp; + // -reindex if (fReindex) { - CImportingNow imp; int nFile = 0; while (true) { CDiskBlockPos pos(nFile, 0); @@ -581,7 +598,6 @@ void ThreadImport(std::vector<boost::filesystem::path> vImportFiles) if (boost::filesystem::exists(pathBootstrap)) { FILE *file = fopen(pathBootstrap.string().c_str(), "rb"); if (file) { - CImportingNow imp; boost::filesystem::path pathBootstrapOld = GetDataDir() / "bootstrap.dat.old"; LogPrintf("Importing bootstrap.dat...\n"); LoadExternalBlockFile(chainparams, file); @@ -595,7 +611,6 @@ void ThreadImport(std::vector<boost::filesystem::path> vImportFiles) BOOST_FOREACH(const boost::filesystem::path& path, vImportFiles) { FILE *file = fopen(path.string().c_str(), "rb"); if (file) { - CImportingNow imp; LogPrintf("Importing blocks file %s...\n", path.string()); LoadExternalBlockFile(chainparams, file); } else { @@ -603,6 +618,13 @@ void ThreadImport(std::vector<boost::filesystem::path> vImportFiles) } } + // scan for better chains in the block chain database, that are not yet connected in the active best chain + CValidationState state; + if (!ActivateBestChain(state, chainparams)) { + LogPrintf("Failed to connect best block"); + StartShutdown(); + } + if (GetBoolArg("-stopafterblockimport", DEFAULT_STOPAFTERBLOCKIMPORT)) { LogPrintf("Stopping after block import\n"); StartShutdown(); @@ -735,7 +757,7 @@ void InitLogging() fLogIPs = GetBoolArg("-logips", DEFAULT_LOGIPS); LogPrintf("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"); - LogPrintf("Bitcoin version %s (%s)\n", FormatFullVersion(), CLIENT_DATE); + LogPrintf("Bitcoin version %s\n", FormatFullVersion()); } /** Initialize bitcoin. @@ -937,7 +959,7 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) SetMockTime(GetArg("-mocktime", 0)); // SetMockTime(0) is a no-op if (GetBoolArg("-peerbloomfilters", DEFAULT_PEERBLOOMFILTERS)) - nLocalServices |= NODE_BLOOM; + nLocalServices = ServiceFlags(nLocalServices | NODE_BLOOM); nMaxTipAge = GetArg("-maxtipage", DEFAULT_MAX_TIP_AGE); @@ -1102,6 +1124,7 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) fListen = GetBoolArg("-listen", DEFAULT_LISTEN); fDiscover = GetBoolArg("-discover", true); fNameLookup = GetBoolArg("-dns", DEFAULT_NAME_LOOKUP); + fRelayTxes = !GetBoolArg("-blocksonly", DEFAULT_BLOCKSONLY); bool fBound = false; if (fListen) { @@ -1158,6 +1181,7 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) // ********************************************************* Step 7: load block chain fReindex = GetBoolArg("-reindex", false); + bool fReindexChainState = GetBoolArg("-reindex-chainstate", false); // Upgrading to 0.8; hard-link the old blknnnn.dat files into /blocks/ boost::filesystem::path blocksDir = GetDataDir() / "blocks"; @@ -1219,7 +1243,7 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) delete pblocktree; pblocktree = new CBlockTreeDB(nBlockTreeDBCache, false, fReindex); - pcoinsdbview = new CCoinsViewDB(nCoinDBCache, false, fReindex); + pcoinsdbview = new CCoinsViewDB(nCoinDBCache, false, fReindex || fReindexChainState); pcoinscatcher = new CCoinsViewErrorCatcher(pcoinsdbview); pcoinsTip = new CCoinsViewCache(pcoinscatcher); @@ -1248,7 +1272,7 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) // Check for changed -txindex state if (fTxIndex != GetBoolArg("-txindex", DEFAULT_TXINDEX)) { - strLoadError = _("You need to rebuild the database using -reindex to change -txindex"); + strLoadError = _("You need to rebuild the database using -reindex-chainstate to change -txindex"); break; } @@ -1261,8 +1285,8 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) uiInterface.InitMessage(_("Verifying blocks...")); if (fHavePruned && GetArg("-checkblocks", DEFAULT_CHECKBLOCKS) > MIN_BLOCKS_TO_KEEP) { - LogPrintf("Prune: pruned datadir may not have more than %d blocks; -checkblocks=%d may fail\n", - MIN_BLOCKS_TO_KEEP, GetArg("-checkblocks", DEFAULT_CHECKBLOCKS)); + LogPrintf("Prune: pruned datadir may not have more than %d blocks; only checking available blocks", + MIN_BLOCKS_TO_KEEP); } { @@ -1346,7 +1370,7 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) // after any wallet rescanning has taken place. if (fPruneMode) { LogPrintf("Unsetting NODE_NETWORK on prune mode\n"); - nLocalServices &= ~NODE_NETWORK; + nLocalServices = ServiceFlags(nLocalServices & ~NODE_NETWORK); if (!fReindex) { uiInterface.InitMessage(_("Pruning blockstore...")); PruneAndFlush(); @@ -1358,12 +1382,6 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) if (mapArgs.count("-blocknotify")) uiInterface.NotifyBlockTip.connect(BlockNotifyCallback); - uiInterface.InitMessage(_("Activating best chain...")); - // scan for better chains in the block chain database, that are not yet connected in the active best chain - CValidationState state; - if (!ActivateBestChain(state, chainparams)) - strErrors << "Failed to connect best block"; - std::vector<boost::filesystem::path> vImportFiles; if (mapArgs.count("-loadblock")) { @@ -1371,10 +1389,18 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) vImportFiles.push_back(strFile); } threadGroup.create_thread(boost::bind(&ThreadImport, vImportFiles)); - if (chainActive.Tip() == NULL) { - LogPrintf("Waiting for genesis block to be imported...\n"); - while (!fRequestShutdown && chainActive.Tip() == NULL) + + // Wait for genesis block to be processed + bool fHaveGenesis = false; + while (!fHaveGenesis && !fRequestShutdown) { + { + LOCK(cs_main); + fHaveGenesis = (chainActive.Tip() != NULL); + } + + if (!fHaveGenesis) { MilliSleep(10); + } } // ********************************************************* Step 11: start node @@ -1385,8 +1411,6 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) if (!strErrors.str().empty()) return InitError(strErrors.str()); - RandAddSeedPerfmon(); - //// debug print LogPrintf("mapBlockIndex.size() = %u\n", mapBlockIndex.size()); LogPrintf("nBestHeight = %d\n", chainActive.Height()); diff --git a/src/key.cpp b/src/key.cpp index 6a3d9aa140..79023566c3 100644 --- a/src/key.cpp +++ b/src/key.cpp @@ -124,9 +124,8 @@ bool CKey::Check(const unsigned char *vch) { } void CKey::MakeNewKey(bool fCompressedIn) { - RandAddSeedPerfmon(); do { - GetRandBytes(vch, sizeof(vch)); + GetStrongRandBytes(vch, sizeof(vch)); } while (!Check(vch)); fValid = true; fCompressed = fCompressedIn; diff --git a/src/main.cpp b/src/main.cpp index 09f82312a9..bdb3457f8e 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -37,9 +37,11 @@ #include "validationinterface.h" #include "versionbits.h" +#include <atomic> #include <sstream> #include <boost/algorithm/string/replace.hpp> +#include <boost/algorithm/string/join.hpp> #include <boost/filesystem.hpp> #include <boost/filesystem/fstream.hpp> #include <boost/math/distributions/poisson.hpp> @@ -79,6 +81,7 @@ uint64_t nPruneTarget = 0; int64_t nMaxTipAge = DEFAULT_MAX_TIP_AGE; bool fEnableReplacement = DEFAULT_ENABLE_REPLACEMENT; + CFeeRate minRelayTxFee = CFeeRate(DEFAULT_MIN_RELAY_TX_FEE); CAmount maxTxFee = DEFAULT_TRANSACTION_MAXFEE; @@ -210,6 +213,12 @@ namespace { /** Number of peers from which we're downloading blocks. */ int nPeersWithValidatedDownloads = 0; + + /** Relay map, protected by cs_main. */ + typedef std::map<uint256, std::shared_ptr<const CTransaction>> MapRelay; + MapRelay mapRelay; + /** Expiration-time ordered list of (expire time, relay map entry) pairs, protected by cs_main). */ + std::deque<std::pair<int64_t, MapRelay::iterator>> vRelayExpiration; } // anon namespace ////////////////////////////////////////////////////////////////////////////// @@ -1049,9 +1058,10 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState& state, const C LOCK(pool.cs); // protect pool.mapNextTx BOOST_FOREACH(const CTxIn &txin, tx.vin) { - if (pool.mapNextTx.count(txin.prevout)) + auto itConflicting = pool.mapNextTx.find(txin.prevout); + if (itConflicting != pool.mapNextTx.end()) { - const CTransaction *ptxConflicting = pool.mapNextTx[txin.prevout].ptx; + const CTransaction *ptxConflicting = itConflicting->second; if (!setConflicts.count(ptxConflicting->GetHash())) { // Allow opt-out of transaction replacement by setting @@ -1436,8 +1446,10 @@ bool GetTransaction(const uint256 &hash, CTransaction &txOut, const Consensus::P LOCK(cs_main); - if (mempool.lookup(hash, txOut)) + std::shared_ptr<const CTransaction> ptx = mempool.get(hash); + if (ptx) { + txOut = *ptx; return true; } @@ -1571,18 +1583,24 @@ CAmount GetBlockSubsidy(int nHeight, const Consensus::Params& consensusParams) bool IsInitialBlockDownload() { const CChainParams& chainParams = Params(); + + // Once this function has returned false, it must remain false. + static std::atomic<bool> latchToFalse{false}; + // Optimization: pre-test latch before taking the lock. + if (latchToFalse.load(std::memory_order_relaxed)) + return false; + LOCK(cs_main); + if (latchToFalse.load(std::memory_order_relaxed)) + return false; if (fImporting || fReindex) return true; if (fCheckpointsEnabled && chainActive.Height() < Checkpoints::GetTotalBlocksEstimate(chainParams.Checkpoints())) return true; - static bool lockIBDState = false; - if (lockIBDState) - return false; bool state = (chainActive.Height() < pindexBestHeader->nHeight - 24 * 6 || std::max(chainActive.Tip()->GetBlockTime(), pindexBestHeader->GetBlockTime()) < GetTime() - nMaxTipAge); if (!state) - lockIBDState = true; + latchToFalse.store(true, std::memory_order_relaxed); return state; } @@ -1880,8 +1898,8 @@ bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsVi // such nodes as they are not following the protocol. That // said during an upgrade careful thought should be taken // as to the correct behavior - we may want to continue - // peering with non-upgraded nodes even after a soft-fork - // super-majority vote has passed. + // peering with non-upgraded nodes even after soft-fork + // super-majority signaling has occurred. return state.DoS(100,false, REJECT_INVALID, strprintf("mandatory-script-verify-flag-failed (%s)", ScriptErrorString(check.GetScriptError()))); } } @@ -2163,7 +2181,7 @@ void PartitionCheck(bool (*initialDownloadCheck)(), CCriticalSection& cs, const } // Protected by cs_main -static VersionBitsCache versionbitscache; +VersionBitsCache versionbitscache; int32_t ComputeBlockVersion(const CBlockIndex* pindexPrev, const Consensus::Params& params) { @@ -2223,7 +2241,7 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin int64_t nTimeStart = GetTimeMicros(); // Check it again in case a previous version let a bad block in - if (!CheckBlock(block, state, !fJustCheck, !fJustCheck)) + if (!CheckBlock(block, state, chainparams.GetConsensus(), GetAdjustedTime(), !fJustCheck, !fJustCheck)) return error("%s: Consensus::CheckBlock: %s", __func__, FormatStateMessage(state)); // verify that the view's current state corresponds to the previous block @@ -2571,16 +2589,10 @@ void static UpdateTip(CBlockIndex *pindexNew, const CChainParams& chainParams) { nTimeBestReceived = GetTime(); mempool.AddTransactionsUpdated(1); - LogPrintf("%s: new best=%s height=%d version=0x%08x log2_work=%.8g tx=%lu date='%s' progress=%f cache=%.1fMiB(%utx)\n", __func__, - chainActive.Tip()->GetBlockHash().ToString(), chainActive.Height(), chainActive.Tip()->nVersion, - log(chainActive.Tip()->nChainWork.getdouble())/log(2.0), (unsigned long)chainActive.Tip()->nChainTx, - DateTimeStrFormat("%Y-%m-%d %H:%M:%S", chainActive.Tip()->GetBlockTime()), - Checkpoints::GuessVerificationProgress(chainParams.Checkpoints(), chainActive.Tip()), pcoinsTip->DynamicMemoryUsage() * (1.0 / (1<<20)), pcoinsTip->GetCacheSize()); - cvBlockChange.notify_all(); - // Check the version of the last 100 blocks to see if we need to upgrade: static bool fWarned = false; + std::vector<std::string> warningMessages; if (!IsInitialBlockDownload()) { int nUpgraded = 0; @@ -2596,10 +2608,11 @@ void static UpdateTip(CBlockIndex *pindexNew, const CChainParams& chainParams) { fWarned = true; } } else { - LogPrintf("%s: unknown new rules are about to activate (versionbit %i)\n", __func__, bit); + warningMessages.push_back(strprintf("unknown new rules are about to activate (versionbit %i)", bit)); } } } + // Check the version of the last 100 blocks to see if we need to upgrade: for (int i = 0; i < 100 && pindex != NULL; i++) { int32_t nExpectedVersion = ComputeBlockVersion(pindex->pprev, chainParams.GetConsensus()); @@ -2608,7 +2621,7 @@ void static UpdateTip(CBlockIndex *pindexNew, const CChainParams& chainParams) { pindex = pindex->pprev; } if (nUpgraded > 0) - LogPrintf("%s: %d of last 100 blocks have unexpected version\n", __func__, nUpgraded); + warningMessages.push_back(strprintf("%d of last 100 blocks have unexpected version", nUpgraded)); if (nUpgraded > 100/2) { // strMiscWarning is read by GetWarnings(), called by Qt and the JSON-RPC code to warn the user: @@ -2619,6 +2632,15 @@ void static UpdateTip(CBlockIndex *pindexNew, const CChainParams& chainParams) { } } } + LogPrintf("%s: new best=%s height=%d version=0x%08x log2_work=%.8g tx=%lu date='%s' progress=%f cache=%.1fMiB(%utx)", __func__, + chainActive.Tip()->GetBlockHash().ToString(), chainActive.Height(), chainActive.Tip()->nVersion, + log(chainActive.Tip()->nChainWork.getdouble())/log(2.0), (unsigned long)chainActive.Tip()->nChainTx, + DateTimeStrFormat("%Y-%m-%d %H:%M:%S", chainActive.Tip()->GetBlockTime()), + Checkpoints::GuessVerificationProgress(chainParams.Checkpoints(), chainActive.Tip()), pcoinsTip->DynamicMemoryUsage() * (1.0 / (1<<20)), pcoinsTip->GetCacheSize()); + if (!warningMessages.empty()) + LogPrintf(" warning='%s'", boost::algorithm::join(warningMessages, ", ")); + LogPrintf("\n"); + } /** Disconnect chainActive's tip. You probably want to call mempool.removeForReorg and manually re-limit mempool size after this, with cs_main held. */ @@ -2811,10 +2833,9 @@ static void PruneBlockIndexCandidates() { * Try to make some progress towards making pindexMostWork the active block. * pblock is either NULL or a pointer to a CBlock corresponding to pindexMostWork. */ -static bool ActivateBestChainStep(CValidationState& state, const CChainParams& chainparams, CBlockIndex* pindexMostWork, const CBlock* pblock) +static bool ActivateBestChainStep(CValidationState& state, const CChainParams& chainparams, CBlockIndex* pindexMostWork, const CBlock* pblock, bool& fInvalidFound) { AssertLockHeld(cs_main); - bool fInvalidFound = false; const CBlockIndex *pindexOldTip = chainActive.Tip(); const CBlockIndex *pindexFork = chainActive.FindFork(pindexMostWork); @@ -2884,6 +2905,28 @@ static bool ActivateBestChainStep(CValidationState& state, const CChainParams& c return true; } +static void NotifyHeaderTip() { + bool fNotify = false; + bool fInitialBlockDownload = false; + static CBlockIndex* pindexHeaderOld = NULL; + CBlockIndex* pindexHeader = NULL; + { + LOCK(cs_main); + if (!setBlockIndexCandidates.empty()) { + pindexHeader = *setBlockIndexCandidates.rbegin(); + } + if (pindexHeader != pindexHeaderOld) { + fNotify = true; + fInitialBlockDownload = IsInitialBlockDownload(); + pindexHeaderOld = pindexHeader; + } + } + // Send block tip changed notifications without cs_main + if (fNotify) { + uiInterface.NotifyHeaderTip(fInitialBlockDownload, pindexHeader); + } +} + /** * Make the best chain active, in multiple steps. The result is either failure * or an activated best chain. pblock is either NULL or a pointer to a block @@ -2891,29 +2934,38 @@ static bool ActivateBestChainStep(CValidationState& state, const CChainParams& c */ bool ActivateBestChain(CValidationState &state, const CChainParams& chainparams, const CBlock *pblock) { CBlockIndex *pindexMostWork = NULL; + CBlockIndex *pindexNewTip = NULL; do { boost::this_thread::interruption_point(); if (ShutdownRequested()) break; - CBlockIndex *pindexNewTip = NULL; const CBlockIndex *pindexFork; bool fInitialDownload; + int nNewHeight; { LOCK(cs_main); CBlockIndex *pindexOldTip = chainActive.Tip(); - pindexMostWork = FindMostWorkChain(); + if (pindexMostWork == NULL) { + pindexMostWork = FindMostWorkChain(); + } // Whether we have anything to do at all. if (pindexMostWork == NULL || pindexMostWork == chainActive.Tip()) return true; - if (!ActivateBestChainStep(state, chainparams, pindexMostWork, pblock && pblock->GetHash() == pindexMostWork->GetBlockHash() ? pblock : NULL)) + bool fInvalidFound = false; + if (!ActivateBestChainStep(state, chainparams, pindexMostWork, pblock && pblock->GetHash() == pindexMostWork->GetBlockHash() ? pblock : NULL, fInvalidFound)) return false; + if (fInvalidFound) { + // Wipe cache, we may need another branch now. + pindexMostWork = NULL; + } pindexNewTip = chainActive.Tip(); pindexFork = chainActive.FindFork(pindexOldTip); fInitialDownload = IsInitialBlockDownload(); + nNewHeight = chainActive.Height(); } // When we reach this point, we switched to a new tip (stored in pindexNewTip). @@ -2942,7 +2994,7 @@ bool ActivateBestChain(CValidationState &state, const CChainParams& chainparams, { LOCK(cs_vNodes); BOOST_FOREACH(CNode* pnode, vNodes) { - if (chainActive.Height() > (pnode->nStartingHeight != -1 ? pnode->nStartingHeight - 2000 : nBlockEstimate)) { + if (nNewHeight > (pnode->nStartingHeight != -1 ? pnode->nStartingHeight - 2000 : nBlockEstimate)) { BOOST_REVERSE_FOREACH(const uint256& hash, vHashes) { pnode->PushBlockHash(hash); } @@ -2955,7 +3007,7 @@ bool ActivateBestChain(CValidationState &state, const CChainParams& chainparams, } } } - } while(pindexMostWork != chainActive.Tip()); + } while (pindexNewTip != pindexMostWork); CheckBlockIndex(chainparams.GetConsensus()); // Write changes periodically to disk, after relay. @@ -3206,20 +3258,20 @@ bool FindUndoPos(CValidationState &state, int nFile, CDiskBlockPos &pos, unsigne return true; } -bool CheckBlockHeader(const CBlockHeader& block, CValidationState& state, bool fCheckPOW) +bool CheckBlockHeader(const CBlockHeader& block, CValidationState& state, const Consensus::Params& consensusParams, int64_t nAdjustedTime, bool fCheckPOW) { // Check proof of work matches claimed amount - if (fCheckPOW && !CheckProofOfWork(block.GetHash(), block.nBits, Params().GetConsensus())) + if (fCheckPOW && !CheckProofOfWork(block.GetHash(), block.nBits, consensusParams)) return state.DoS(50, false, REJECT_INVALID, "high-hash", false, "proof of work failed"); // Check timestamp - if (block.GetBlockTime() > GetAdjustedTime() + 2 * 60 * 60) + if (block.GetBlockTime() > nAdjustedTime + 2 * 60 * 60) return state.Invalid(false, REJECT_INVALID, "time-too-new", "block timestamp too far in the future"); return true; } -bool CheckBlock(const CBlock& block, CValidationState& state, bool fCheckPOW, bool fCheckMerkleRoot) +bool CheckBlock(const CBlock& block, CValidationState& state, const Consensus::Params& consensusParams, int64_t nAdjustedTime, bool fCheckPOW, bool fCheckMerkleRoot) { // These are checks that are independent of context. @@ -3228,7 +3280,7 @@ bool CheckBlock(const CBlock& block, CValidationState& state, bool fCheckPOW, bo // Check that the header is valid (particularly PoW). This is mostly // redundant with the call in AcceptBlockHeader. - if (!CheckBlockHeader(block, state, fCheckPOW)) + if (!CheckBlockHeader(block, state, consensusParams, nAdjustedTime, fCheckPOW)) return false; // Check the merkle root. @@ -3294,9 +3346,8 @@ static bool CheckIndexAgainstCheckpoint(const CBlockIndex* pindexPrev, CValidati return true; } -bool ContextualCheckBlockHeader(const CBlockHeader& block, CValidationState& state, CBlockIndex * const pindexPrev) +bool ContextualCheckBlockHeader(const CBlockHeader& block, CValidationState& state, const Consensus::Params& consensusParams, CBlockIndex * const pindexPrev) { - const Consensus::Params& consensusParams = Params().GetConsensus(); // Check proof of work if (block.nBits != GetNextWorkRequired(pindexPrev, &block, consensusParams)) return state.DoS(100, false, REJECT_INVALID, "bad-diffbits", false, "incorrect proof of work"); @@ -3369,7 +3420,7 @@ static bool AcceptBlockHeader(const CBlockHeader& block, CValidationState& state return true; } - if (!CheckBlockHeader(block, state)) + if (!CheckBlockHeader(block, state, chainparams.GetConsensus(), GetAdjustedTime())) return error("%s: Consensus::CheckBlockHeader: %s, %s", __func__, hash.ToString(), FormatStateMessage(state)); // Get prev block index @@ -3385,7 +3436,7 @@ static bool AcceptBlockHeader(const CBlockHeader& block, CValidationState& state if (fCheckpointsEnabled && !CheckIndexAgainstCheckpoint(pindexPrev, state, chainparams, hash)) return error("%s: CheckIndexAgainstCheckpoint(): %s", __func__, state.GetRejectReason().c_str()); - if (!ContextualCheckBlockHeader(block, state, pindexPrev)) + if (!ContextualCheckBlockHeader(block, state, chainparams.GetConsensus(), pindexPrev)) return error("%s: Consensus::ContextualCheckBlockHeader: %s, %s", __func__, hash.ToString(), FormatStateMessage(state)); } if (pindex == NULL) @@ -3398,11 +3449,13 @@ static bool AcceptBlockHeader(const CBlockHeader& block, CValidationState& state } /** Store block on disk. If dbp is non-NULL, the file is known to already reside on disk */ -static bool AcceptBlock(const CBlock& block, CValidationState& state, const CChainParams& chainparams, CBlockIndex** ppindex, bool fRequested, CDiskBlockPos* dbp) +static bool AcceptBlock(const CBlock& block, CValidationState& state, const CChainParams& chainparams, CBlockIndex** ppindex, bool fRequested, const CDiskBlockPos* dbp, bool* fNewBlock) { + if (fNewBlock) *fNewBlock = false; AssertLockHeld(cs_main); - CBlockIndex *&pindex = *ppindex; + CBlockIndex *pindexDummy = NULL; + CBlockIndex *&pindex = ppindex ? *ppindex : pindexDummy; if (!AcceptBlockHeader(block, state, chainparams, &pindex)) return false; @@ -3427,8 +3480,9 @@ static bool AcceptBlock(const CBlock& block, CValidationState& state, const CCha if (!fHasMoreWork) return true; // Don't process less-work chains if (fTooFarAhead) return true; // Block height is too high } + if (fNewBlock) *fNewBlock = true; - if ((!CheckBlock(block, state)) || !ContextualCheckBlock(block, state, pindex->pprev)) { + if ((!CheckBlock(block, state, chainparams.GetConsensus(), GetAdjustedTime())) || !ContextualCheckBlock(block, state, pindex->pprev)) { if (state.IsInvalid() && !state.CorruptionPossible()) { pindex->nStatus |= BLOCK_FAILED_VALID; setDirtyBlockIndex.insert(pindex); @@ -3474,7 +3528,7 @@ static bool IsSuperMajority(int minVersion, const CBlockIndex* pstart, unsigned } -bool ProcessNewBlock(CValidationState& state, const CChainParams& chainparams, const CNode* pfrom, const CBlock* pblock, bool fForceProcessing, CDiskBlockPos* dbp) +bool ProcessNewBlock(CValidationState& state, const CChainParams& chainparams, CNode* pfrom, const CBlock* pblock, bool fForceProcessing, const CDiskBlockPos* dbp) { { LOCK(cs_main); @@ -3483,15 +3537,19 @@ bool ProcessNewBlock(CValidationState& state, const CChainParams& chainparams, c // Store to disk CBlockIndex *pindex = NULL; - bool ret = AcceptBlock(*pblock, state, chainparams, &pindex, fRequested, dbp); + bool fNewBlock = false; + bool ret = AcceptBlock(*pblock, state, chainparams, &pindex, fRequested, dbp, &fNewBlock); if (pindex && pfrom) { mapBlockSource[pindex->GetBlockHash()] = pfrom->GetId(); + if (fNewBlock) pfrom->nLastBlockTime = GetTime(); } CheckBlockIndex(chainparams.GetConsensus()); if (!ret) return error("%s: AcceptBlock FAILED", __func__); } + NotifyHeaderTip(); + if (!ActivateBestChain(state, chainparams, pblock)) return error("%s: ActivateBestChain failed", __func__); @@ -3511,9 +3569,9 @@ bool TestBlockValidity(CValidationState& state, const CChainParams& chainparams, indexDummy.nHeight = pindexPrev->nHeight + 1; // NOTE: CheckBlockHeader is called by CheckBlock - if (!ContextualCheckBlockHeader(block, state, pindexPrev)) + if (!ContextualCheckBlockHeader(block, state, chainparams.GetConsensus(), pindexPrev)) return error("%s: Consensus::ContextualCheckBlockHeader: %s", __func__, FormatStateMessage(state)); - if (!CheckBlock(block, state, fCheckPOW, fCheckMerkleRoot)) + if (!CheckBlock(block, state, chainparams.GetConsensus(), GetAdjustedTime(), fCheckPOW, fCheckMerkleRoot)) return error("%s: Consensus::CheckBlock: %s", __func__, FormatStateMessage(state)); if (!ContextualCheckBlock(block, state, pindexPrev)) return error("%s: Consensus::ContextualCheckBlock: %s", __func__, FormatStateMessage(state)); @@ -3834,18 +3892,31 @@ bool CVerifyDB::VerifyDB(const CChainParams& chainparams, CCoinsView *coinsview, CBlockIndex* pindexFailure = NULL; int nGoodTransactions = 0; CValidationState state; + int reportDone = 0; + LogPrintf("[0%]..."); for (CBlockIndex* pindex = chainActive.Tip(); pindex && pindex->pprev; pindex = pindex->pprev) { boost::this_thread::interruption_point(); - uiInterface.ShowProgress(_("Verifying blocks..."), std::max(1, std::min(99, (int)(((double)(chainActive.Height() - pindex->nHeight)) / (double)nCheckDepth * (nCheckLevel >= 4 ? 50 : 100))))); + int percentageDone = std::max(1, std::min(99, (int)(((double)(chainActive.Height() - pindex->nHeight)) / (double)nCheckDepth * (nCheckLevel >= 4 ? 50 : 100)))); + if (reportDone < percentageDone/10) { + // report every 10% step + LogPrintf("[%d%%]...", percentageDone); + reportDone = percentageDone/10; + } + uiInterface.ShowProgress(_("Verifying blocks..."), percentageDone); if (pindex->nHeight < chainActive.Height()-nCheckDepth) break; + if (fPruneMode && !(pindex->nStatus & BLOCK_HAVE_DATA)) { + // If pruning, only go back as far as we have data. + LogPrintf("VerifyDB(): block verification stopping at height %d (pruning, no data)\n", pindex->nHeight); + break; + } CBlock block; // check level 0: read from disk if (!ReadBlockFromDisk(block, pindex, chainparams.GetConsensus())) return error("VerifyDB(): *** ReadBlockFromDisk failed at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString()); // check level 1: verify block validity - if (nCheckLevel >= 1 && !CheckBlock(block, state)) + if (nCheckLevel >= 1 && !CheckBlock(block, state, chainparams.GetConsensus(), GetAdjustedTime())) return error("%s: *** found bad block at %d, hash=%s (%s)\n", __func__, pindex->nHeight, pindex->GetBlockHash().ToString(), FormatStateMessage(state)); // check level 2: verify undo validity @@ -3890,6 +3961,7 @@ bool CVerifyDB::VerifyDB(const CChainParams& chainparams, CCoinsView *coinsview, } } + LogPrintf("[DONE].\n"); LogPrintf("No coin database inconsistencies in last %i blocks (%i transactions)\n", chainActive.Height() - pindexState->nHeight, nGoodTransactions); return true; @@ -4037,15 +4109,26 @@ bool LoadExternalBlockFile(const CChainParams& chainparams, FILE* fileIn, CDiskB // process in case the block isn't known yet if (mapBlockIndex.count(hash) == 0 || (mapBlockIndex[hash]->nStatus & BLOCK_HAVE_DATA) == 0) { + LOCK(cs_main); CValidationState state; - if (ProcessNewBlock(state, chainparams, NULL, &block, true, dbp)) + if (AcceptBlock(block, state, chainparams, NULL, true, dbp, NULL)) nLoaded++; if (state.IsError()) break; } else if (hash != chainparams.GetConsensus().hashGenesisBlock && mapBlockIndex[hash]->nHeight % 1000 == 0) { - LogPrintf("Block Import: already had block %s at height %d\n", hash.ToString(), mapBlockIndex[hash]->nHeight); + LogPrint("reindex", "Block Import: already had block %s at height %d\n", hash.ToString(), mapBlockIndex[hash]->nHeight); } + // Activate the genesis block so normal node progress can continue + if (hash == chainparams.GetConsensus().hashGenesisBlock) { + CValidationState state; + if (!ActivateBestChain(state, chainparams)) { + break; + } + } + + NotifyHeaderTip(); + // Recursively process earlier encountered successors of this block deque<uint256> queue; queue.push_back(hash); @@ -4057,10 +4140,11 @@ bool LoadExternalBlockFile(const CChainParams& chainparams, FILE* fileIn, CDiskB std::multimap<uint256, CDiskBlockPos>::iterator it = range.first; if (ReadBlockFromDisk(block, it->second, chainparams.GetConsensus())) { - LogPrintf("%s: Processing out of order child %s of %s\n", __func__, block.GetHash().ToString(), + LogPrint("reindex", "%s: Processing out of order child %s of %s\n", __func__, block.GetHash().ToString(), head.ToString()); + LOCK(cs_main); CValidationState dummy; - if (ProcessNewBlock(dummy, chainparams, NULL, &block, true, &it->second)) + if (AcceptBlock(block, dummy, chainparams, NULL, true, &it->second, NULL)) { nLoaded++; queue.push_back(block.GetHash()); @@ -4068,6 +4152,7 @@ bool LoadExternalBlockFile(const CChainParams& chainparams, FILE* fileIn, CDiskB } range.first++; mapBlocksUnknownParent.erase(it); + NotifyHeaderTip(); } } } catch (const std::exception& e) { @@ -4445,26 +4530,24 @@ void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParam } } } - else if (inv.IsKnownType()) + else if (inv.type == MSG_TX) { // Send stream from relay memory - bool pushed = false; - { - LOCK(cs_mapRelay); - map<uint256, CTransaction>::iterator mi = mapRelay.find(inv.hash); - if (mi != mapRelay.end()) { - pfrom->PushMessage(inv.GetCommand(), (*mi).second); - pushed = true; - } - } - if (!pushed && inv.type == MSG_TX) { - CTransaction tx; - if (mempool.lookup(inv.hash, tx)) { - pfrom->PushMessage(NetMsgType::TX, tx); - pushed = true; + bool push = false; + auto mi = mapRelay.find(inv.hash); + if (mi != mapRelay.end()) { + pfrom->PushMessage(NetMsgType::TX, *mi->second); + push = true; + } else if (pfrom->timeLastMempoolReq) { + auto txinfo = mempool.info(inv.hash); + // To protect privacy, do not answer getdata using the mempool when + // that TX couldn't have been INVed in reply to a MEMPOOL request. + if (txinfo.tx && txinfo.nTime <= pfrom->timeLastMempoolReq) { + pfrom->PushMessage(NetMsgType::TX, *txinfo.tx); + push = true; } } - if (!pushed) { + if (!push) { vNotFound.push_back(inv); } } @@ -4493,7 +4576,6 @@ void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParam bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, int64_t nTimeReceived, const CChainParams& chainparams) { - RandAddSeedPerfmon(); LogPrint("net", "received: %s (%u bytes) peer=%d\n", SanitizeString(strCommand), vRecv.size(), pfrom->id); if (mapArgs.count("-dropmessagestest") && GetRand(atoi(mapArgs["-dropmessagestest"])) == 0) { @@ -4508,6 +4590,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, strCommand == NetMsgType::FILTERCLEAR)) { if (pfrom->nVersion >= NO_BLOOM_VERSION) { + LOCK(cs_main); Misbehaving(pfrom->GetId(), 100); return false; } else { @@ -4523,6 +4606,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, if (pfrom->nVersion != 0) { pfrom->PushMessage(NetMsgType::REJECT, strCommand, REJECT_DUPLICATE, string("Duplicate version message")); + LOCK(cs_main); Misbehaving(pfrom->GetId(), 1); return false; } @@ -4531,7 +4615,22 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, CAddress addrMe; CAddress addrFrom; uint64_t nNonce = 1; - vRecv >> pfrom->nVersion >> pfrom->nServices >> nTime >> addrMe; + uint64_t nServiceInt; + vRecv >> pfrom->nVersion >> nServiceInt >> nTime >> addrMe; + pfrom->nServices = ServiceFlags(nServiceInt); + if (!pfrom->fInbound) + { + addrman.SetServices(pfrom->addr, pfrom->nServices); + } + if (pfrom->nServicesExpected & ~pfrom->nServices) + { + LogPrint("net", "peer=%d does not offer the expected services (%08x offered, %08x expected); disconnecting\n", pfrom->id, pfrom->nServices, pfrom->nServicesExpected); + pfrom->PushMessage(NetMsgType::REJECT, strCommand, REJECT_NONSTANDARD, + strprintf("Expected to offer services %08x", pfrom->nServicesExpected)); + pfrom->fDisconnect = true; + return false; + } + if (pfrom->nVersion < MIN_PEER_PROTO_VERSION) { // disconnect from peers older than this proto version @@ -4582,7 +4681,10 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, pfrom->fClient = !(pfrom->nServices & NODE_NETWORK); // Potentially mark this peer as a preferred download peer. + { + LOCK(cs_main); UpdatePreferredDownload(pfrom, State(pfrom->GetId())); + } // Change version pfrom->PushMessage(NetMsgType::VERACK); @@ -4640,6 +4742,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, else if (pfrom->nVersion == 0) { // Must have a version message before anything else + LOCK(cs_main); Misbehaving(pfrom->GetId(), 1); return false; } @@ -4675,6 +4778,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, return true; if (vAddr.size() > 1000) { + LOCK(cs_main); Misbehaving(pfrom->GetId(), 20); return error("message addr size() = %u", vAddr.size()); } @@ -4687,6 +4791,9 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, { boost::this_thread::interruption_point(); + if ((addr.nServices & REQUIRED_SERVICES) != REQUIRED_SERVICES) + continue; + if (addr.nTime <= 100000000 || addr.nTime > nNow + 10 * 60) addr.nTime = nNow - 5 * 24 * 60 * 60; pfrom->AddAddressKnown(addr); @@ -4698,25 +4805,20 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, LOCK(cs_vNodes); // Use deterministic randomness to send to the same nodes for 24 hours // at a time so the addrKnowns of the chosen nodes prevent repeats - static uint256 hashSalt; - if (hashSalt.IsNull()) - hashSalt = GetRandHash(); + static const uint64_t salt0 = GetRand(std::numeric_limits<uint64_t>::max()); + static const uint64_t salt1 = GetRand(std::numeric_limits<uint64_t>::max()); uint64_t hashAddr = addr.GetHash(); - uint256 hashRand = ArithToUint256(UintToArith256(hashSalt) ^ (hashAddr<<32) ^ ((GetTime()+hashAddr)/(24*60*60))); - hashRand = Hash(BEGIN(hashRand), END(hashRand)); - multimap<uint256, CNode*> mapMix; + multimap<uint64_t, CNode*> mapMix; + const CSipHasher hasher = CSipHasher(salt0, salt1).Write(hashAddr << 32).Write((GetTime() + hashAddr) / (24*60*60)); BOOST_FOREACH(CNode* pnode, vNodes) { if (pnode->nVersion < CADDR_TIME_VERSION) continue; - unsigned int nPointer; - memcpy(&nPointer, &pnode, sizeof(nPointer)); - uint256 hashKey = ArithToUint256(UintToArith256(hashRand) ^ nPointer); - hashKey = Hash(BEGIN(hashKey), END(hashKey)); + uint64_t hashKey = CSipHasher(hasher).Write(pnode->id).Finalize(); mapMix.insert(make_pair(hashKey, pnode)); } int nRelayNodes = fReachable ? 2 : 1; // limited relaying of addresses outside our network(s) - for (multimap<uint256, CNode*>::iterator mi = mapMix.begin(); mi != mapMix.end() && nRelayNodes-- > 0; ++mi) + for (multimap<uint64_t, CNode*>::iterator mi = mapMix.begin(); mi != mapMix.end() && nRelayNodes-- > 0; ++mi) ((*mi).second)->PushAddress(addr); } } @@ -4744,11 +4846,12 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, vRecv >> vInv; if (vInv.size() > MAX_INV_SZ) { + LOCK(cs_main); Misbehaving(pfrom->GetId(), 20); return error("message inv size() = %u", vInv.size()); } - bool fBlocksOnly = GetBoolArg("-blocksonly", DEFAULT_BLOCKSONLY); + bool fBlocksOnly = !fRelayTxes; // Allow whitelisted peers to send data other than blocks in blocks only mode if whitelistrelay is true if (pfrom->fWhitelisted && GetBoolArg("-whitelistrelay", DEFAULT_WHITELISTRELAY)) @@ -4763,7 +4866,6 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, const CInv &inv = vInv[nInv]; boost::this_thread::interruption_point(); - pfrom->AddInventoryKnown(inv); bool fAlreadyHave = AlreadyHave(inv); LogPrint("net", "got inv: %s %s peer=%d\n", inv.ToString(), fAlreadyHave ? "have" : "new", pfrom->id); @@ -4793,6 +4895,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, } else { + pfrom->AddInventoryKnown(inv); if (fBlocksOnly) LogPrint("net", "transaction (%s) inv sent in violation of protocol peer=%d\n", inv.hash.ToString(), pfrom->id); else if (!fAlreadyHave && !fImporting && !fReindex && !IsInitialBlockDownload()) @@ -4819,6 +4922,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, vRecv >> vInv; if (vInv.size() > MAX_INV_SZ) { + LOCK(cs_main); Misbehaving(pfrom->GetId(), 20); return error("message getdata size() = %u", vInv.size()); } @@ -4931,7 +5035,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, { // Stop processing the transaction early if // We are in blocks only mode and peer is either not whitelisted or whitelistrelay is off - if (GetBoolArg("-blocksonly", DEFAULT_BLOCKSONLY) && (!pfrom->fWhitelisted || !GetBoolArg("-whitelistrelay", DEFAULT_WHITELISTRELAY))) + if (!fRelayTxes && (!pfrom->fWhitelisted || !GetBoolArg("-whitelistrelay", DEFAULT_WHITELISTRELAY))) { LogPrint("net", "transaction sent in violation of protocol peer=%d\n", pfrom->id); return true; @@ -4958,6 +5062,8 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, RelayTransaction(tx); vWorkQueue.push_back(inv.hash); + pfrom->nLastTXTime = GetTime(); + LogPrint("mempool", "AcceptToMemoryPool: peer=%d: accepted %s (poolsz %u txn, %u kB)\n", pfrom->id, tx.GetHash().ToString(), @@ -5070,6 +5176,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, // Bypass the normal CBlock deserialization, as we don't want to risk deserializing 2000 full blocks. unsigned int nCount = ReadCompactSize(vRecv); if (nCount > MAX_HEADERS_RESULTS) { + LOCK(cs_main); Misbehaving(pfrom->GetId(), 20); return error("headers message size = %u", nCount); } @@ -5079,6 +5186,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, ReadCompactSize(vRecv); // ignore tx count; assume it is 0. } + { LOCK(cs_main); if (nCount == 0) { @@ -5086,6 +5194,13 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, return true; } + // If we already know the last header in the message, then it contains + // no new information for us. In this case, we do not request + // more headers later. This prevents multiple chains of redundant + // getheader requests from running in parallel if triggered by incoming + // blocks while the node is still in initial headers sync. + const bool hasNewHeaders = (mapBlockIndex.count(headers.back().GetHash()) == 0); + CBlockIndex *pindexLast = NULL; BOOST_FOREACH(const CBlockHeader& header, headers) { CValidationState state; @@ -5106,7 +5221,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, if (pindexLast) UpdateBlockAvailability(pfrom->GetId(), pindexLast->GetBlockHash()); - if (nCount == MAX_HEADERS_RESULTS && pindexLast) { + if (nCount == MAX_HEADERS_RESULTS && pindexLast && hasNewHeaders) { // Headers message had its maximum size; the peer may have more headers. // TODO: optimize: if pindexLast is an ancestor of chainActive.Tip or pindexBestHeader, continue // from there instead. @@ -5162,6 +5277,9 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, } CheckBlockIndex(chainparams.GetConsensus()); + } + + NotifyHeaderTip(); } else if (strCommand == NetMsgType::BLOCK && !fImporting && !fReindex) // Ignore blocks received while importing @@ -5169,10 +5287,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, CBlock block; vRecv >> block; - CInv inv(MSG_BLOCK, block.GetHash()); - LogPrint("net", "received block %s peer=%d\n", inv.hash.ToString(), pfrom->id); - - pfrom->AddInventoryKnown(inv); + LogPrint("net", "received block %s peer=%d\n", block.GetHash().ToString(), pfrom->id); CValidationState state; // Process all blocks from whitelisted peers, even if not requested, @@ -5185,7 +5300,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, if (state.IsInvalid(nDoS)) { assert (state.GetRejectCode() < REJECT_INTERNAL); // Blocks are never rejected with internal reject codes pfrom->PushMessage(NetMsgType::REJECT, strCommand, (unsigned char)state.GetRejectCode(), - state.GetRejectReason().substr(0, MAX_REJECT_MESSAGE_LENGTH), inv.hash); + state.GetRejectReason().substr(0, MAX_REJECT_MESSAGE_LENGTH), block.GetHash()); if (nDoS > 0) { LOCK(cs_main); Misbehaving(pfrom->GetId(), nDoS); @@ -5224,6 +5339,13 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, else if (strCommand == NetMsgType::MEMPOOL) { + if (!(nLocalServices & NODE_BLOOM) && !pfrom->fWhitelisted) + { + LogPrint("net", "mempool request with bloom filters disabled, disconnect peer=%d\n", pfrom->GetId()); + pfrom->fDisconnect = true; + return true; + } + if (CNode::OutboundTargetReached(false) && !pfrom->fWhitelisted) { LogPrint("net", "mempool request with bandwidth limit reached, disconnect peer=%d\n", pfrom->GetId()); @@ -5323,8 +5445,11 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, LOCK(pfrom->cs_filter); if (!filter.IsWithinSizeConstraints()) + { // There is no excuse for sending a too-large filter + LOCK(cs_main); Misbehaving(pfrom->GetId(), 100); + } else { delete pfrom->pfilter; @@ -5344,13 +5469,17 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, // and thus, the maximum size any matched object can have) in a filteradd message if (vData.size() > MAX_SCRIPT_ELEMENT_SIZE) { + LOCK(cs_main); Misbehaving(pfrom->GetId(), 100); } else { LOCK(pfrom->cs_filter); if (pfrom->pfilter) pfrom->pfilter->insert(vData); else + { + LOCK(cs_main); Misbehaving(pfrom->GetId(), 100); + } } } @@ -5504,6 +5633,11 @@ bool ProcessMessages(CNode* pfrom) // Allow exceptions from over-long size LogPrintf("%s(%s, %u bytes): Exception '%s' caught\n", __func__, SanitizeString(strCommand), nMessageSize, e.what()); } + else if (strstr(e.what(), "non-canonical ReadCompactSize()")) + { + // Allow exceptions from non-canonical encoding + LogPrintf("%s(%s, %u bytes): Exception '%s' caught\n", __func__, SanitizeString(strCommand), nMessageSize, e.what()); + } else { PrintExceptionContinue(&e, "ProcessMessages()"); @@ -5620,6 +5754,9 @@ bool SendMessages(CNode* pto) pto->vAddrToSend.clear(); if (!vAddr.empty()) pto->PushMessage(NetMsgType::ADDR, vAddr); + // we only send the big addr message once + if (pto->vAddrToSend.capacity() > 40) + pto->vAddrToSend.shrink_to_fit(); } CNodeState &state = *State(pto->GetId()); @@ -5757,9 +5894,7 @@ bool SendMessages(CNode* pto) hashToAnnounce.ToString(), chainActive.Tip()->GetBlockHash().ToString()); } - // If the peer announced this block to us, don't inv it back. - // (Since block announcements may not be via inv's, we can't solely rely on - // setInventoryKnown to track this.) + // If the peer's chain has this block, don't inv it back. if (!PeerHasHeader(&state, pindex)) { pto->PushInventory(CInv(MSG_BLOCK, hashToAnnounce)); LogPrint("net", "%s: sending inv peer=%d hash=%s\n", __func__, @@ -5816,8 +5951,7 @@ bool SendMessages(CNode* pto) // Respond to BIP35 mempool requests if (fSendTrickle && pto->fSendMempool) { - std::vector<uint256> vtxid; - mempool.queryHashes(vtxid); + auto vtxinfo = mempool.infoAll(); pto->fSendMempool = false; CAmount filterrate = 0; { @@ -5827,20 +5961,16 @@ bool SendMessages(CNode* pto) LOCK(pto->cs_filter); - BOOST_FOREACH(const uint256& hash, vtxid) { + for (const auto& txinfo : vtxinfo) { + const uint256& hash = txinfo.tx->GetHash(); CInv inv(MSG_TX, hash); pto->setInventoryTxToSend.erase(hash); if (filterrate) { - CFeeRate feeRate; - mempool.lookupFeeRate(hash, feeRate); - if (feeRate.GetFeePerK() < filterrate) + if (txinfo.feeRate.GetFeePerK() < filterrate) continue; } if (pto->pfilter) { - CTransaction tx; - bool fInMemPool = mempool.lookup(hash, tx); - if (!fInMemPool) continue; // another thread removed since queryHashes, maybe... - if (!pto->pfilter->IsRelevantAndUpdate(tx)) continue; + if (!pto->pfilter->IsRelevantAndUpdate(*txinfo.tx)) continue; } pto->filterInventoryKnown.insert(hash); vInv.push_back(inv); @@ -5849,6 +5979,7 @@ bool SendMessages(CNode* pto) vInv.clear(); } } + pto->timeLastMempoolReq = GetTime(); } // Determine transactions to relay @@ -5885,21 +6016,30 @@ bool SendMessages(CNode* pto) continue; } // Not in the mempool anymore? don't bother sending it. - CFeeRate feeRate; - if (!mempool.lookupFeeRate(hash, feeRate)) { + auto txinfo = mempool.info(hash); + if (!txinfo.tx) { continue; } - if (filterrate && feeRate.GetFeePerK() < filterrate) { + if (filterrate && txinfo.feeRate.GetFeePerK() < filterrate) { continue; } - if (pto->pfilter) { - CTransaction tx; - if (!mempool.lookup(hash, tx)) continue; - if (!pto->pfilter->IsRelevantAndUpdate(tx)) continue; - } + if (pto->pfilter && !pto->pfilter->IsRelevantAndUpdate(*txinfo.tx)) continue; // Send vInv.push_back(CInv(MSG_TX, hash)); nRelayedTransactions++; + { + // Expire old relay messages + while (!vRelayExpiration.empty() && vRelayExpiration.front().first < nNow) + { + mapRelay.erase(vRelayExpiration.front().second); + vRelayExpiration.pop_front(); + } + + auto ret = mapRelay.insert(std::make_pair(hash, std::move(txinfo.tx))); + if (ret.second) { + vRelayExpiration.push_back(std::make_pair(nNow + 15 * 60 * 1000000, ret.first)); + } + } if (vInv.size() == MAX_INV_SZ) { pto->PushMessage(NetMsgType::INV, vInv); vInv.clear(); diff --git a/src/main.h b/src/main.h index 576f73b5f2..e2bfdfdf6e 100644 --- a/src/main.h +++ b/src/main.h @@ -212,10 +212,10 @@ void UnregisterNodeSignals(CNodeSignals& nodeSignals); * @param[in] pfrom The node which we are receiving the block from; it is added to mapBlockSource and may be penalised if the block is invalid. * @param[in] pblock The block we want to process. * @param[in] fForceProcessing Process this block even if unrequested; used for non-network block sources and whitelisted peers. - * @param[out] dbp If pblock is stored to disk (or already there), this will be set to its location. + * @param[out] dbp The already known disk position of pblock, or NULL if not yet stored. * @return True if state.IsValid() */ -bool ProcessNewBlock(CValidationState& state, const CChainParams& chainparams, const CNode* pfrom, const CBlock* pblock, bool fForceProcessing, CDiskBlockPos* dbp); +bool ProcessNewBlock(CValidationState& state, const CChainParams& chainparams, CNode* pfrom, const CBlock* pblock, bool fForceProcessing, const CDiskBlockPos* dbp); /** Check whether enough disk space is available for an incoming block */ bool CheckDiskSpace(uint64_t nAdditionalBytes = 0); /** Open a block file (blk?????.dat) */ @@ -425,13 +425,13 @@ bool ReadBlockFromDisk(CBlock& block, const CBlockIndex* pindex, const Consensus /** Functions for validating blocks and updating the block tree */ /** Context-independent validity checks */ -bool CheckBlockHeader(const CBlockHeader& block, CValidationState& state, bool fCheckPOW = true); -bool CheckBlock(const CBlock& block, CValidationState& state, bool fCheckPOW = true, bool fCheckMerkleRoot = true); +bool CheckBlockHeader(const CBlockHeader& block, CValidationState& state, const Consensus::Params& consensusParams, int64_t nAdjustedTime, bool fCheckPOW = true); +bool CheckBlock(const CBlock& block, CValidationState& state, const Consensus::Params& consensusParams, int64_t nAdjustedTime, bool fCheckPOW = true, bool fCheckMerkleRoot = true); /** Context-dependent validity checks. * By "context", we mean only the previous block headers, but not the UTXO * set; UTXO-related validity checks are done in ConnectBlock(). */ -bool ContextualCheckBlockHeader(const CBlockHeader& block, CValidationState& state, CBlockIndex *pindexPrev); +bool ContextualCheckBlockHeader(const CBlockHeader& block, CValidationState& state, const Consensus::Params& consensusParams, CBlockIndex* pindexPrev); bool ContextualCheckBlock(const CBlock& block, CValidationState& state, CBlockIndex *pindexPrev); /** Apply the effects of this block (with given index) on the UTXO set represented by coins. @@ -482,6 +482,8 @@ extern CBlockTreeDB *pblocktree; */ int GetSpendHeight(const CCoinsViewCache& inputs); +extern VersionBitsCache versionbitscache; + /** * Determine what nVersion a new block should use. */ diff --git a/src/memusage.h b/src/memusage.h index 49760e64c7..3810bfad07 100644 --- a/src/memusage.h +++ b/src/memusage.h @@ -5,6 +5,8 @@ #ifndef BITCOIN_MEMUSAGE_H #define BITCOIN_MEMUSAGE_H +#include "indirectmap.h" + #include <stdlib.h> #include <map> @@ -70,6 +72,15 @@ private: X x; }; +struct stl_shared_counter +{ + /* Various platforms use different sized counters here. + * Conservatively assume that they won't be larger than size_t. */ + void* class_type; + size_t use_count; + size_t weak_count; +}; + template<typename X> static inline size_t DynamicUsage(const std::vector<X>& v) { @@ -106,6 +117,35 @@ static inline size_t IncrementalDynamicUsage(const std::map<X, Y, Z>& m) return MallocUsage(sizeof(stl_tree_node<std::pair<const X, Y> >)); } +// indirectmap has underlying map with pointer as key + +template<typename X, typename Y> +static inline size_t DynamicUsage(const indirectmap<X, Y>& m) +{ + return MallocUsage(sizeof(stl_tree_node<std::pair<const X*, Y> >)) * m.size(); +} + +template<typename X, typename Y> +static inline size_t IncrementalDynamicUsage(const indirectmap<X, Y>& m) +{ + return MallocUsage(sizeof(stl_tree_node<std::pair<const X*, Y> >)); +} + +template<typename X> +static inline size_t DynamicUsage(const std::unique_ptr<X>& p) +{ + return p ? MallocUsage(sizeof(X)) : 0; +} + +template<typename X> +static inline size_t DynamicUsage(const std::shared_ptr<X>& p) +{ + // A shared_ptr can either use a single continuous memory block for both + // the counter and the storage (when using std::make_shared), or separate. + // We can't observe the difference, however, so assume the worst. + return p ? MallocUsage(sizeof(X)) + MallocUsage(sizeof(stl_shared_counter)) : 0; +} + // Boost data structures template<typename X> diff --git a/src/miner.cpp b/src/miner.cpp index eaf29a767b..989ad11a26 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -25,6 +25,7 @@ #include "utilmoneystr.h" #include "validationinterface.h" +#include <algorithm> #include <boost/thread.hpp> #include <boost/tuple/tuple.hpp> #include <queue> @@ -71,231 +72,507 @@ int64_t UpdateTime(CBlockHeader* pblock, const Consensus::Params& consensusParam return nNewTime - nOldTime; } -CBlockTemplate* CreateNewBlock(const CChainParams& chainparams, const CScript& scriptPubKeyIn) +BlockAssembler::BlockAssembler(const CChainParams& _chainparams) + : chainparams(_chainparams) { - // Create new block - std::unique_ptr<CBlockTemplate> pblocktemplate(new CBlockTemplate()); + // Largest block you're willing to create: + nBlockMaxSize = GetArg("-blockmaxsize", DEFAULT_BLOCK_MAX_SIZE); + // Limit to between 1K and MAX_BLOCK_SIZE-1K for sanity: + nBlockMaxSize = std::max((unsigned int)1000, std::min((unsigned int)(MAX_BLOCK_SIZE-1000), nBlockMaxSize)); + + // Minimum block size you want to create; block will be filled with free transactions + // until there are no more or the block reaches this size: + nBlockMinSize = GetArg("-blockminsize", DEFAULT_BLOCK_MIN_SIZE); + nBlockMinSize = std::min(nBlockMaxSize, nBlockMinSize); +} + +void BlockAssembler::resetBlock() +{ + inBlock.clear(); + + // Reserve space for coinbase tx + nBlockSize = 1000; + nBlockSigOps = 100; + + // These counters do not include coinbase tx + nBlockTx = 0; + nFees = 0; + + lastFewTxs = 0; + blockFinished = false; +} + +CBlockTemplate* BlockAssembler::CreateNewBlock(const CScript& scriptPubKeyIn) +{ + resetBlock(); + + pblocktemplate.reset(new CBlockTemplate()); + if(!pblocktemplate.get()) return NULL; - CBlock *pblock = &pblocktemplate->block; // pointer for convenience - - // Create coinbase tx - CMutableTransaction txNew; - txNew.vin.resize(1); - txNew.vin[0].prevout.SetNull(); - txNew.vout.resize(1); - txNew.vout[0].scriptPubKey = scriptPubKeyIn; + pblock = &pblocktemplate->block; // pointer for convenience // Add dummy coinbase tx as first transaction pblock->vtx.push_back(CTransaction()); pblocktemplate->vTxFees.push_back(-1); // updated at end pblocktemplate->vTxSigOps.push_back(-1); // updated at end - // Largest block you're willing to create: - unsigned int nBlockMaxSize = GetArg("-blockmaxsize", DEFAULT_BLOCK_MAX_SIZE); - // Limit to between 1K and MAX_BLOCK_SIZE-1K for sanity: - nBlockMaxSize = std::max((unsigned int)1000, std::min((unsigned int)(MAX_BLOCK_SIZE-1000), nBlockMaxSize)); + LOCK2(cs_main, mempool.cs); + CBlockIndex* pindexPrev = chainActive.Tip(); + nHeight = pindexPrev->nHeight + 1; + + pblock->nVersion = ComputeBlockVersion(pindexPrev, chainparams.GetConsensus()); + // -regtest only: allow overriding block.nVersion with + // -blockversion=N to test forking scenarios + if (chainparams.MineBlocksOnDemand()) + pblock->nVersion = GetArg("-blockversion", pblock->nVersion); + + pblock->nTime = GetAdjustedTime(); + const int64_t nMedianTimePast = pindexPrev->GetMedianTimePast(); + + nLockTimeCutoff = (STANDARD_LOCKTIME_VERIFY_FLAGS & LOCKTIME_MEDIAN_TIME_PAST) + ? nMedianTimePast + : pblock->GetBlockTime(); + + addPriorityTxs(); + addPackageTxs(); + + nLastBlockTx = nBlockTx; + nLastBlockSize = nBlockSize; + LogPrintf("CreateNewBlock(): total size %u txs: %u fees: %ld sigops %d\n", nBlockSize, nBlockTx, nFees, nBlockSigOps); + + // Create coinbase transaction. + CMutableTransaction coinbaseTx; + coinbaseTx.vin.resize(1); + coinbaseTx.vin[0].prevout.SetNull(); + coinbaseTx.vout.resize(1); + coinbaseTx.vout[0].scriptPubKey = scriptPubKeyIn; + coinbaseTx.vout[0].nValue = nFees + GetBlockSubsidy(nHeight, chainparams.GetConsensus()); + coinbaseTx.vin[0].scriptSig = CScript() << nHeight << OP_0; + pblock->vtx[0] = coinbaseTx; + pblocktemplate->vTxFees[0] = -nFees; + + // Fill in header + pblock->hashPrevBlock = pindexPrev->GetBlockHash(); + UpdateTime(pblock, chainparams.GetConsensus(), pindexPrev); + pblock->nBits = GetNextWorkRequired(pindexPrev, pblock, chainparams.GetConsensus()); + pblock->nNonce = 0; + pblocktemplate->vTxSigOps[0] = GetLegacySigOpCount(pblock->vtx[0]); + + CValidationState state; + if (!TestBlockValidity(state, chainparams, *pblock, pindexPrev, false, false)) { + throw std::runtime_error(strprintf("%s: TestBlockValidity failed: %s", __func__, FormatStateMessage(state))); + } - // How much of the block should be dedicated to high-priority transactions, - // included regardless of the fees they pay - unsigned int nBlockPrioritySize = GetArg("-blockprioritysize", DEFAULT_BLOCK_PRIORITY_SIZE); - nBlockPrioritySize = std::min(nBlockMaxSize, nBlockPrioritySize); + return pblocktemplate.release(); +} - // Minimum block size you want to create; block will be filled with free transactions - // until there are no more or the block reaches this size: - unsigned int nBlockMinSize = GetArg("-blockminsize", DEFAULT_BLOCK_MIN_SIZE); - nBlockMinSize = std::min(nBlockMaxSize, nBlockMinSize); +bool BlockAssembler::isStillDependent(CTxMemPool::txiter iter) +{ + BOOST_FOREACH(CTxMemPool::txiter parent, mempool.GetMemPoolParents(iter)) + { + if (!inBlock.count(parent)) { + return true; + } + } + return false; +} - // Collect memory pool transactions into the block - CTxMemPool::setEntries inBlock; - CTxMemPool::setEntries waitSet; +void BlockAssembler::onlyUnconfirmed(CTxMemPool::setEntries& testSet) +{ + for (CTxMemPool::setEntries::iterator iit = testSet.begin(); iit != testSet.end(); ) { + // Only test txs not already in the block + if (inBlock.count(*iit)) { + testSet.erase(iit++); + } + else { + iit++; + } + } +} - // This vector will be sorted into a priority queue: - vector<TxCoinAgePriority> vecPriority; - TxCoinAgePriorityCompare pricomparer; - std::map<CTxMemPool::txiter, double, CTxMemPool::CompareIteratorByHash> waitPriMap; - typedef std::map<CTxMemPool::txiter, double, CTxMemPool::CompareIteratorByHash>::iterator waitPriIter; - double actualPriority = -1; +bool BlockAssembler::TestPackage(uint64_t packageSize, unsigned int packageSigOps) +{ + if (nBlockSize + packageSize >= nBlockMaxSize) + return false; + if (nBlockSigOps + packageSigOps >= MAX_BLOCK_SIGOPS) + return false; + return true; +} + +// Block size and sigops have already been tested. Check that all transactions +// are final. +bool BlockAssembler::TestPackageFinality(const CTxMemPool::setEntries& package) +{ + BOOST_FOREACH (const CTxMemPool::txiter it, package) { + if (!IsFinalTx(it->GetTx(), nHeight, nLockTimeCutoff)) + return false; + } + return true; +} + +bool BlockAssembler::TestForBlock(CTxMemPool::txiter iter) +{ + if (nBlockSize + iter->GetTxSize() >= nBlockMaxSize) { + // If the block is so close to full that no more txs will fit + // or if we've tried more than 50 times to fill remaining space + // then flag that the block is finished + if (nBlockSize > nBlockMaxSize - 100 || lastFewTxs > 50) { + blockFinished = true; + return false; + } + // Once we're within 1000 bytes of a full block, only look at 50 more txs + // to try to fill the remaining space. + if (nBlockSize > nBlockMaxSize - 1000) { + lastFewTxs++; + } + return false; + } + + if (nBlockSigOps + iter->GetSigOpCount() >= MAX_BLOCK_SIGOPS) { + // If the block has room for no more sig ops then + // flag that the block is finished + if (nBlockSigOps > MAX_BLOCK_SIGOPS - 2) { + blockFinished = true; + return false; + } + // Otherwise attempt to find another tx with fewer sigops + // to put in the block. + return false; + } + + // Must check that lock times are still valid + // This can be removed once MTP is always enforced + // as long as reorgs keep the mempool consistent. + if (!IsFinalTx(iter->GetTx(), nHeight, nLockTimeCutoff)) + return false; + + return true; +} + +void BlockAssembler::AddToBlock(CTxMemPool::txiter iter) +{ + pblock->vtx.push_back(iter->GetTx()); + pblocktemplate->vTxFees.push_back(iter->GetFee()); + pblocktemplate->vTxSigOps.push_back(iter->GetSigOpCount()); + nBlockSize += iter->GetTxSize(); + ++nBlockTx; + nBlockSigOps += iter->GetSigOpCount(); + nFees += iter->GetFee(); + inBlock.insert(iter); - std::priority_queue<CTxMemPool::txiter, std::vector<CTxMemPool::txiter>, ScoreCompare> clearedTxs; bool fPrintPriority = GetBoolArg("-printpriority", DEFAULT_PRINTPRIORITY); - uint64_t nBlockSize = 1000; - uint64_t nBlockTx = 0; - unsigned int nBlockSigOps = 100; - int lastFewTxs = 0; - CAmount nFees = 0; + if (fPrintPriority) { + double dPriority = iter->GetPriority(nHeight); + CAmount dummy; + mempool.ApplyDeltas(iter->GetTx().GetHash(), dPriority, dummy); + LogPrintf("priority %.1f fee %s txid %s\n", + dPriority, + CFeeRate(iter->GetModifiedFee(), iter->GetTxSize()).ToString(), + iter->GetTx().GetHash().ToString()); + } +} +void BlockAssembler::addScoreTxs() +{ + std::priority_queue<CTxMemPool::txiter, std::vector<CTxMemPool::txiter>, ScoreCompare> clearedTxs; + CTxMemPool::setEntries waitSet; + CTxMemPool::indexed_transaction_set::index<mining_score>::type::iterator mi = mempool.mapTx.get<mining_score>().begin(); + CTxMemPool::txiter iter; + while (!blockFinished && (mi != mempool.mapTx.get<mining_score>().end() || !clearedTxs.empty())) { - LOCK2(cs_main, mempool.cs); - CBlockIndex* pindexPrev = chainActive.Tip(); - const int nHeight = pindexPrev->nHeight + 1; - pblock->nTime = GetAdjustedTime(); - const int64_t nMedianTimePast = pindexPrev->GetMedianTimePast(); - - pblock->nVersion = ComputeBlockVersion(pindexPrev, chainparams.GetConsensus()); - // -regtest only: allow overriding block.nVersion with - // -blockversion=N to test forking scenarios - if (chainparams.MineBlocksOnDemand()) - pblock->nVersion = GetArg("-blockversion", pblock->nVersion); - - int64_t nLockTimeCutoff = (STANDARD_LOCKTIME_VERIFY_FLAGS & LOCKTIME_MEDIAN_TIME_PAST) - ? nMedianTimePast - : pblock->GetBlockTime(); - - bool fPriorityBlock = nBlockPrioritySize > 0; - if (fPriorityBlock) { - vecPriority.reserve(mempool.mapTx.size()); - for (CTxMemPool::indexed_transaction_set::iterator mi = mempool.mapTx.begin(); - mi != mempool.mapTx.end(); ++mi) - { - double dPriority = mi->GetPriority(nHeight); - CAmount dummy; - mempool.ApplyDeltas(mi->GetTx().GetHash(), dPriority, dummy); - vecPriority.push_back(TxCoinAgePriority(dPriority, mi)); - } - std::make_heap(vecPriority.begin(), vecPriority.end(), pricomparer); + // If no txs that were previously postponed are available to try + // again, then try the next highest score tx + if (clearedTxs.empty()) { + iter = mempool.mapTx.project<0>(mi); + mi++; + } + // If a previously postponed tx is available to try again, then it + // has higher score than all untried so far txs + else { + iter = clearedTxs.top(); + clearedTxs.pop(); } - CTxMemPool::indexed_transaction_set::index<mining_score>::type::iterator mi = mempool.mapTx.get<mining_score>().begin(); - CTxMemPool::txiter iter; - - while (mi != mempool.mapTx.get<mining_score>().end() || !clearedTxs.empty()) - { - bool priorityTx = false; - if (fPriorityBlock && !vecPriority.empty()) { // add a tx from priority queue to fill the blockprioritysize - priorityTx = true; - iter = vecPriority.front().second; - actualPriority = vecPriority.front().first; - std::pop_heap(vecPriority.begin(), vecPriority.end(), pricomparer); - vecPriority.pop_back(); - } - else if (clearedTxs.empty()) { // add tx with next highest score - iter = mempool.mapTx.project<0>(mi); - mi++; - } - else { // try to add a previously postponed child tx - iter = clearedTxs.top(); - clearedTxs.pop(); - } + // If tx already in block, skip (added by addPriorityTxs) + if (inBlock.count(iter)) { + continue; + } + + // If tx is dependent on other mempool txs which haven't yet been included + // then put it in the waitSet + if (isStillDependent(iter)) { + waitSet.insert(iter); + continue; + } - if (inBlock.count(iter)) - continue; // could have been added to the priorityBlock + // If the fee rate is below the min fee rate for mining, then we're done + // adding txs based on score (fee rate) + if (iter->GetModifiedFee() < ::minRelayTxFee.GetFee(iter->GetTxSize()) && nBlockSize >= nBlockMinSize) { + return; + } - const CTransaction& tx = iter->GetTx(); + // If this tx fits in the block add it, otherwise keep looping + if (TestForBlock(iter)) { + AddToBlock(iter); - bool fOrphan = false; - BOOST_FOREACH(CTxMemPool::txiter parent, mempool.GetMemPoolParents(iter)) + // This tx was successfully added, so + // add transactions that depend on this one to the priority queue to try again + BOOST_FOREACH(CTxMemPool::txiter child, mempool.GetMemPoolChildren(iter)) { - if (!inBlock.count(parent)) { - fOrphan = true; - break; + if (waitSet.count(child)) { + clearedTxs.push(child); + waitSet.erase(child); } } - if (fOrphan) { - if (priorityTx) - waitPriMap.insert(std::make_pair(iter,actualPriority)); - else - waitSet.insert(iter); + } + } +} + +void BlockAssembler::UpdatePackagesForAdded(const CTxMemPool::setEntries& alreadyAdded, + indexed_modified_transaction_set &mapModifiedTx) +{ + BOOST_FOREACH(const CTxMemPool::txiter it, alreadyAdded) { + CTxMemPool::setEntries descendants; + mempool.CalculateDescendants(it, descendants); + // Insert all descendants (not yet in block) into the modified set + BOOST_FOREACH(CTxMemPool::txiter desc, descendants) { + if (alreadyAdded.count(desc)) continue; + modtxiter mit = mapModifiedTx.find(desc); + if (mit == mapModifiedTx.end()) { + CTxMemPoolModifiedEntry modEntry(desc); + modEntry.nSizeWithAncestors -= it->GetTxSize(); + modEntry.nModFeesWithAncestors -= it->GetModifiedFee(); + modEntry.nSigOpCountWithAncestors -= it->GetSigOpCount(); + mapModifiedTx.insert(modEntry); + } else { + mapModifiedTx.modify(mit, update_for_parent_inclusion(it)); } + } + } +} - unsigned int nTxSize = iter->GetTxSize(); - if (fPriorityBlock && - (nBlockSize + nTxSize >= nBlockPrioritySize || !AllowFree(actualPriority))) { - fPriorityBlock = false; - waitPriMap.clear(); - } - if (!priorityTx && - (iter->GetModifiedFee() < ::minRelayTxFee.GetFee(nTxSize) && nBlockSize >= nBlockMinSize)) { - break; - } - if (nBlockSize + nTxSize >= nBlockMaxSize) { - if (nBlockSize > nBlockMaxSize - 100 || lastFewTxs > 50) { - break; - } - // Once we're within 1000 bytes of a full block, only look at 50 more txs - // to try to fill the remaining space. - if (nBlockSize > nBlockMaxSize - 1000) { - lastFewTxs++; - } - continue; +// Skip entries in mapTx that are already in a block or are present +// in mapModifiedTx (which implies that the mapTx ancestor state is +// stale due to ancestor inclusion in the block) +// Also skip transactions that we've already failed to add. This can happen if +// we consider a transaction in mapModifiedTx and it fails: we can then +// potentially consider it again while walking mapTx. It's currently +// guaranteed to fail again, but as a belt-and-suspenders check we put it in +// failedTx and avoid re-evaluation, since the re-evaluation would be using +// cached size/sigops/fee values that are not actually correct. +bool BlockAssembler::SkipMapTxEntry(CTxMemPool::txiter it, indexed_modified_transaction_set &mapModifiedTx, CTxMemPool::setEntries &failedTx) +{ + assert (it != mempool.mapTx.end()); + if (mapModifiedTx.count(it) || inBlock.count(it) || failedTx.count(it)) + return true; + return false; +} + +void BlockAssembler::SortForBlock(const CTxMemPool::setEntries& package, CTxMemPool::txiter entry, std::vector<CTxMemPool::txiter>& sortedEntries) +{ + // Sort package by ancestor count + // If a transaction A depends on transaction B, then A's ancestor count + // must be greater than B's. So this is sufficient to validly order the + // transactions for block inclusion. + sortedEntries.clear(); + sortedEntries.insert(sortedEntries.begin(), package.begin(), package.end()); + std::sort(sortedEntries.begin(), sortedEntries.end(), CompareTxIterByAncestorCount()); +} + +// This transaction selection algorithm orders the mempool based +// on feerate of a transaction including all unconfirmed ancestors. +// Since we don't remove transactions from the mempool as we select them +// for block inclusion, we need an alternate method of updating the feerate +// of a transaction with its not-yet-selected ancestors as we go. +// This is accomplished by walking the in-mempool descendants of selected +// transactions and storing a temporary modified state in mapModifiedTxs. +// Each time through the loop, we compare the best transaction in +// mapModifiedTxs with the next transaction in the mempool to decide what +// transaction package to work on next. +void BlockAssembler::addPackageTxs() +{ + // mapModifiedTx will store sorted packages after they are modified + // because some of their txs are already in the block + indexed_modified_transaction_set mapModifiedTx; + // Keep track of entries that failed inclusion, to avoid duplicate work + CTxMemPool::setEntries failedTx; + + // Start by adding all descendants of previously added txs to mapModifiedTx + // and modifying them for their already included ancestors + UpdatePackagesForAdded(inBlock, mapModifiedTx); + + CTxMemPool::indexed_transaction_set::index<ancestor_score>::type::iterator mi = mempool.mapTx.get<ancestor_score>().begin(); + CTxMemPool::txiter iter; + while (mi != mempool.mapTx.get<ancestor_score>().end() || !mapModifiedTx.empty()) + { + // First try to find a new transaction in mapTx to evaluate. + if (mi != mempool.mapTx.get<ancestor_score>().end() && + SkipMapTxEntry(mempool.mapTx.project<0>(mi), mapModifiedTx, failedTx)) { + ++mi; + continue; + } + + // Now that mi is not stale, determine which transaction to evaluate: + // the next entry from mapTx, or the best from mapModifiedTx? + bool fUsingModified = false; + + modtxscoreiter modit = mapModifiedTx.get<ancestor_score>().begin(); + if (mi == mempool.mapTx.get<ancestor_score>().end()) { + // We're out of entries in mapTx; use the entry from mapModifiedTx + iter = modit->iter; + fUsingModified = true; + } else { + // Try to compare the mapTx entry to the mapModifiedTx entry + iter = mempool.mapTx.project<0>(mi); + if (modit != mapModifiedTx.get<ancestor_score>().end() && + CompareModifiedEntry()(*modit, CTxMemPoolModifiedEntry(iter))) { + // The best entry in mapModifiedTx has higher score + // than the one from mapTx. + // Switch which transaction (package) to consider + iter = modit->iter; + fUsingModified = true; + } else { + // Either no entry in mapModifiedTx, or it's worse than mapTx. + // Increment mi for the next loop iteration. + ++mi; } + } - if (!IsFinalTx(tx, nHeight, nLockTimeCutoff)) - continue; + // We skip mapTx entries that are inBlock, and mapModifiedTx shouldn't + // contain anything that is inBlock. + assert(!inBlock.count(iter)); + + uint64_t packageSize = iter->GetSizeWithAncestors(); + CAmount packageFees = iter->GetModFeesWithAncestors(); + unsigned int packageSigOps = iter->GetSigOpCountWithAncestors(); + if (fUsingModified) { + packageSize = modit->nSizeWithAncestors; + packageFees = modit->nModFeesWithAncestors; + packageSigOps = modit->nSigOpCountWithAncestors; + } - unsigned int nTxSigOps = iter->GetSigOpCount(); - if (nBlockSigOps + nTxSigOps >= MAX_BLOCK_SIGOPS) { - if (nBlockSigOps > MAX_BLOCK_SIGOPS - 2) { - break; - } - continue; + if (packageFees < ::minRelayTxFee.GetFee(packageSize) && nBlockSize >= nBlockMinSize) { + // Everything else we might consider has a lower fee rate + return; + } + + if (!TestPackage(packageSize, packageSigOps)) { + if (fUsingModified) { + // Since we always look at the best entry in mapModifiedTx, + // we must erase failed entries so that we can consider the + // next best entry on the next loop iteration + mapModifiedTx.get<ancestor_score>().erase(modit); + failedTx.insert(iter); } + continue; + } - CAmount nTxFees = iter->GetFee(); - // Added - pblock->vtx.push_back(tx); - pblocktemplate->vTxFees.push_back(nTxFees); - pblocktemplate->vTxSigOps.push_back(nTxSigOps); - nBlockSize += nTxSize; - ++nBlockTx; - nBlockSigOps += nTxSigOps; - nFees += nTxFees; - - if (fPrintPriority) - { - double dPriority = iter->GetPriority(nHeight); - CAmount dummy; - mempool.ApplyDeltas(tx.GetHash(), dPriority, dummy); - LogPrintf("priority %.1f fee %s txid %s\n", - dPriority , CFeeRate(iter->GetModifiedFee(), nTxSize).ToString(), tx.GetHash().ToString()); + CTxMemPool::setEntries ancestors; + uint64_t nNoLimit = std::numeric_limits<uint64_t>::max(); + std::string dummy; + mempool.CalculateMemPoolAncestors(*iter, ancestors, nNoLimit, nNoLimit, nNoLimit, nNoLimit, dummy, false); + + onlyUnconfirmed(ancestors); + ancestors.insert(iter); + + // Test if all tx's are Final + if (!TestPackageFinality(ancestors)) { + if (fUsingModified) { + mapModifiedTx.get<ancestor_score>().erase(modit); + failedTx.insert(iter); } + continue; + } + + // Package can be added. Sort the entries in a valid order. + vector<CTxMemPool::txiter> sortedEntries; + SortForBlock(ancestors, iter, sortedEntries); + + for (size_t i=0; i<sortedEntries.size(); ++i) { + AddToBlock(sortedEntries[i]); + // Erase from the modified set, if present + mapModifiedTx.erase(sortedEntries[i]); + } + + // Update transactions that depend on each of these + UpdatePackagesForAdded(ancestors, mapModifiedTx); + } +} + +void BlockAssembler::addPriorityTxs() +{ + // How much of the block should be dedicated to high-priority transactions, + // included regardless of the fees they pay + unsigned int nBlockPrioritySize = GetArg("-blockprioritysize", DEFAULT_BLOCK_PRIORITY_SIZE); + nBlockPrioritySize = std::min(nBlockMaxSize, nBlockPrioritySize); + + if (nBlockPrioritySize == 0) { + return; + } - inBlock.insert(iter); + // This vector will be sorted into a priority queue: + vector<TxCoinAgePriority> vecPriority; + TxCoinAgePriorityCompare pricomparer; + std::map<CTxMemPool::txiter, double, CTxMemPool::CompareIteratorByHash> waitPriMap; + typedef std::map<CTxMemPool::txiter, double, CTxMemPool::CompareIteratorByHash>::iterator waitPriIter; + double actualPriority = -1; + + vecPriority.reserve(mempool.mapTx.size()); + for (CTxMemPool::indexed_transaction_set::iterator mi = mempool.mapTx.begin(); + mi != mempool.mapTx.end(); ++mi) + { + double dPriority = mi->GetPriority(nHeight); + CAmount dummy; + mempool.ApplyDeltas(mi->GetTx().GetHash(), dPriority, dummy); + vecPriority.push_back(TxCoinAgePriority(dPriority, mi)); + } + std::make_heap(vecPriority.begin(), vecPriority.end(), pricomparer); + + CTxMemPool::txiter iter; + while (!vecPriority.empty() && !blockFinished) { // add a tx from priority queue to fill the blockprioritysize + iter = vecPriority.front().second; + actualPriority = vecPriority.front().first; + std::pop_heap(vecPriority.begin(), vecPriority.end(), pricomparer); + vecPriority.pop_back(); + + // If tx already in block, skip + if (inBlock.count(iter)) { + assert(false); // shouldn't happen for priority txs + continue; + } - // Add transactions that depend on this one to the priority queue + // If tx is dependent on other mempool txs which haven't yet been included + // then put it in the waitSet + if (isStillDependent(iter)) { + waitPriMap.insert(std::make_pair(iter, actualPriority)); + continue; + } + + // If this tx fits in the block add it, otherwise keep looping + if (TestForBlock(iter)) { + AddToBlock(iter); + + // If now that this txs is added we've surpassed our desired priority size + // or have dropped below the AllowFreeThreshold, then we're done adding priority txs + if (nBlockSize >= nBlockPrioritySize || !AllowFree(actualPriority)) { + return; + } + + // This tx was successfully added, so + // add transactions that depend on this one to the priority queue to try again BOOST_FOREACH(CTxMemPool::txiter child, mempool.GetMemPoolChildren(iter)) { - if (fPriorityBlock) { - waitPriIter wpiter = waitPriMap.find(child); - if (wpiter != waitPriMap.end()) { - vecPriority.push_back(TxCoinAgePriority(wpiter->second,child)); - std::push_heap(vecPriority.begin(), vecPriority.end(), pricomparer); - waitPriMap.erase(wpiter); - } - } - else { - if (waitSet.count(child)) { - clearedTxs.push(child); - waitSet.erase(child); - } + waitPriIter wpiter = waitPriMap.find(child); + if (wpiter != waitPriMap.end()) { + vecPriority.push_back(TxCoinAgePriority(wpiter->second,child)); + std::push_heap(vecPriority.begin(), vecPriority.end(), pricomparer); + waitPriMap.erase(wpiter); } } } - nLastBlockTx = nBlockTx; - nLastBlockSize = nBlockSize; - LogPrintf("CreateNewBlock(): total size %u txs: %u fees: %ld sigops %d\n", nBlockSize, nBlockTx, nFees, nBlockSigOps); - - // Compute final coinbase transaction. - txNew.vout[0].nValue = nFees + GetBlockSubsidy(nHeight, chainparams.GetConsensus()); - txNew.vin[0].scriptSig = CScript() << nHeight << OP_0; - pblock->vtx[0] = txNew; - pblocktemplate->vTxFees[0] = -nFees; - - // Fill in header - pblock->hashPrevBlock = pindexPrev->GetBlockHash(); - UpdateTime(pblock, chainparams.GetConsensus(), pindexPrev); - pblock->nBits = GetNextWorkRequired(pindexPrev, pblock, chainparams.GetConsensus()); - pblock->nNonce = 0; - pblocktemplate->vTxSigOps[0] = GetLegacySigOpCount(pblock->vtx[0]); - - CValidationState state; - if (!TestBlockValidity(state, chainparams, *pblock, pindexPrev, false, false)) { - throw std::runtime_error(strprintf("%s: TestBlockValidity failed: %s", __func__, FormatStateMessage(state))); - } } - - return pblocktemplate.release(); } void IncrementExtraNonce(CBlock* pblock, const CBlockIndex* pindexPrev, unsigned int& nExtraNonce) diff --git a/src/miner.h b/src/miner.h index cd0f136625..a9fea85304 100644 --- a/src/miner.h +++ b/src/miner.h @@ -7,14 +7,19 @@ #define BITCOIN_MINER_H #include "primitives/block.h" +#include "txmempool.h" #include <stdint.h> +#include <memory> +#include "boost/multi_index_container.hpp" +#include "boost/multi_index/ordered_index.hpp" class CBlockIndex; class CChainParams; class CReserveKey; class CScript; class CWallet; + namespace Consensus { struct Params; }; static const bool DEFAULT_PRINTPRIORITY = false; @@ -26,8 +31,175 @@ struct CBlockTemplate std::vector<int64_t> vTxSigOps; }; +// Container for tracking updates to ancestor feerate as we include (parent) +// transactions in a block +struct CTxMemPoolModifiedEntry { + CTxMemPoolModifiedEntry(CTxMemPool::txiter entry) + { + iter = entry; + nSizeWithAncestors = entry->GetSizeWithAncestors(); + nModFeesWithAncestors = entry->GetModFeesWithAncestors(); + nSigOpCountWithAncestors = entry->GetSigOpCountWithAncestors(); + } + + CTxMemPool::txiter iter; + uint64_t nSizeWithAncestors; + CAmount nModFeesWithAncestors; + unsigned int nSigOpCountWithAncestors; +}; + +/** Comparator for CTxMemPool::txiter objects. + * It simply compares the internal memory address of the CTxMemPoolEntry object + * pointed to. This means it has no meaning, and is only useful for using them + * as key in other indexes. + */ +struct CompareCTxMemPoolIter { + bool operator()(const CTxMemPool::txiter& a, const CTxMemPool::txiter& b) const + { + return &(*a) < &(*b); + } +}; + +struct modifiedentry_iter { + typedef CTxMemPool::txiter result_type; + result_type operator() (const CTxMemPoolModifiedEntry &entry) const + { + return entry.iter; + } +}; + +// This matches the calculation in CompareTxMemPoolEntryByAncestorFee, +// except operating on CTxMemPoolModifiedEntry. +// TODO: refactor to avoid duplication of this logic. +struct CompareModifiedEntry { + bool operator()(const CTxMemPoolModifiedEntry &a, const CTxMemPoolModifiedEntry &b) + { + double f1 = (double)a.nModFeesWithAncestors * b.nSizeWithAncestors; + double f2 = (double)b.nModFeesWithAncestors * a.nSizeWithAncestors; + if (f1 == f2) { + return CTxMemPool::CompareIteratorByHash()(a.iter, b.iter); + } + return f1 > f2; + } +}; + +// A comparator that sorts transactions based on number of ancestors. +// This is sufficient to sort an ancestor package in an order that is valid +// to appear in a block. +struct CompareTxIterByAncestorCount { + bool operator()(const CTxMemPool::txiter &a, const CTxMemPool::txiter &b) + { + if (a->GetCountWithAncestors() != b->GetCountWithAncestors()) + return a->GetCountWithAncestors() < b->GetCountWithAncestors(); + return CTxMemPool::CompareIteratorByHash()(a, b); + } +}; + +typedef boost::multi_index_container< + CTxMemPoolModifiedEntry, + boost::multi_index::indexed_by< + boost::multi_index::ordered_unique< + modifiedentry_iter, + CompareCTxMemPoolIter + >, + // sorted by modified ancestor fee rate + boost::multi_index::ordered_non_unique< + // Reuse same tag from CTxMemPool's similar index + boost::multi_index::tag<ancestor_score>, + boost::multi_index::identity<CTxMemPoolModifiedEntry>, + CompareModifiedEntry + > + > +> indexed_modified_transaction_set; + +typedef indexed_modified_transaction_set::nth_index<0>::type::iterator modtxiter; +typedef indexed_modified_transaction_set::index<ancestor_score>::type::iterator modtxscoreiter; + +struct update_for_parent_inclusion +{ + update_for_parent_inclusion(CTxMemPool::txiter it) : iter(it) {} + + void operator() (CTxMemPoolModifiedEntry &e) + { + e.nModFeesWithAncestors -= iter->GetFee(); + e.nSizeWithAncestors -= iter->GetTxSize(); + e.nSigOpCountWithAncestors -= iter->GetSigOpCount(); + } + + CTxMemPool::txiter iter; +}; + /** Generate a new block, without valid proof-of-work */ -CBlockTemplate* CreateNewBlock(const CChainParams& chainparams, const CScript& scriptPubKeyIn); +class BlockAssembler +{ +private: + // The constructed block template + std::unique_ptr<CBlockTemplate> pblocktemplate; + // A convenience pointer that always refers to the CBlock in pblocktemplate + CBlock* pblock; + + // Configuration parameters for the block size + unsigned int nBlockMaxSize, nBlockMinSize; + + // Information on the current status of the block + uint64_t nBlockSize; + uint64_t nBlockTx; + unsigned int nBlockSigOps; + CAmount nFees; + CTxMemPool::setEntries inBlock; + + // Chain context for the block + int nHeight; + int64_t nLockTimeCutoff; + const CChainParams& chainparams; + + // Variables used for addScoreTxs and addPriorityTxs + int lastFewTxs; + bool blockFinished; + +public: + BlockAssembler(const CChainParams& chainparams); + /** Construct a new block template with coinbase to scriptPubKeyIn */ + CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn); + +private: + // utility functions + /** Clear the block's state and prepare for assembling a new block */ + void resetBlock(); + /** Add a tx to the block */ + void AddToBlock(CTxMemPool::txiter iter); + + // Methods for how to add transactions to a block. + /** Add transactions based on modified feerate */ + void addScoreTxs(); + /** Add transactions based on tx "priority" */ + void addPriorityTxs(); + /** Add transactions based on feerate including unconfirmed ancestors */ + void addPackageTxs(); + + // helper function for addScoreTxs and addPriorityTxs + /** Test if tx will still "fit" in the block */ + bool TestForBlock(CTxMemPool::txiter iter); + /** Test if tx still has unconfirmed parents not yet in block */ + bool isStillDependent(CTxMemPool::txiter iter); + + // helper functions for addPackageTxs() + /** Remove confirmed (inBlock) entries from given set */ + void onlyUnconfirmed(CTxMemPool::setEntries& testSet); + /** Test if a new package would "fit" in the block */ + bool TestPackage(uint64_t packageSize, unsigned int packageSigOps); + /** Test if a set of transactions are all final */ + bool TestPackageFinality(const CTxMemPool::setEntries& package); + /** Return true if given transaction from mapTx has already been evaluated, + * or if the transaction's cached data in mapTx is incorrect. */ + bool SkipMapTxEntry(CTxMemPool::txiter it, indexed_modified_transaction_set &mapModifiedTx, CTxMemPool::setEntries &failedTx); + /** Sort the package in an order that is valid to appear in a block */ + void SortForBlock(const CTxMemPool::setEntries& package, CTxMemPool::txiter entry, std::vector<CTxMemPool::txiter>& sortedEntries); + /** Add descendants of given transactions to mapModifiedTx with ancestor + * state updated assuming given transactions are inBlock. */ + void UpdatePackagesForAdded(const CTxMemPool::setEntries& alreadyAdded, indexed_modified_transaction_set &mapModifiedTx); +}; + /** Modify the extranonce in a block */ void IncrementExtraNonce(CBlock* pblock, const CBlockIndex* pindexPrev, unsigned int& nExtraNonce); int64_t UpdateTime(CBlockHeader* pblock, const Consensus::Params& consensusParams, const CBlockIndex* pindexPrev); diff --git a/src/net.cpp b/src/net.cpp index aa5b47340a..4eca3d75cc 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -14,6 +14,7 @@ #include "clientversion.h" #include "consensus/consensus.h" #include "crypto/common.h" +#include "crypto/sha256.h" #include "hash.h" #include "primitives/transaction.h" #include "scheduler.h" @@ -56,7 +57,6 @@ #endif #endif -using namespace std; namespace { const int MAX_OUTBOUND_CONNECTIONS = 8; @@ -71,14 +71,18 @@ namespace { const static std::string NET_MESSAGE_COMMAND_OTHER = "*other*"; +/** Services this node implementation cares about */ +static const ServiceFlags nRelevantServices = NODE_NETWORK; + // // Global state variables // bool fDiscover = true; bool fListen = true; -uint64_t nLocalServices = NODE_NETWORK; +ServiceFlags nLocalServices = NODE_NETWORK; +bool fRelayTxes = true; CCriticalSection cs_mapLocalHost; -map<CNetAddr, LocalServiceInfo> mapLocalHost; +std::map<CNetAddr, LocalServiceInfo> mapLocalHost; static bool vfLimited[NET_MAX] = {}; static CNode* pnodeLocalHost = NULL; uint64_t nLocalHostNonce = 0; @@ -88,20 +92,14 @@ int nMaxConnections = DEFAULT_MAX_PEER_CONNECTIONS; bool fAddressesInitialized = false; std::string strSubVersion; -vector<CNode*> vNodes; +std::vector<CNode*> vNodes; CCriticalSection cs_vNodes; -map<uint256, CTransaction> mapRelay; -deque<pair<int64_t, uint256> > vRelayExpiration; -CCriticalSection cs_mapRelay; limitedmap<uint256, int64_t> mapAlreadyAskedFor(MAX_INV_SZ); -static deque<string> vOneShots; +static std::deque<std::string> vOneShots; CCriticalSection cs_vOneShots; -set<CNetAddr> setservAddNodeAddresses; -CCriticalSection cs_setservAddNodeAddresses; - -vector<std::string> vAddedNodes; +std::vector<std::string> vAddedNodes; CCriticalSection cs_vAddedNodes; NodeId nLastNodeId = 0; @@ -135,7 +133,7 @@ bool GetLocal(CService& addr, const CNetAddr *paddrPeer) int nBestReachability = -1; { LOCK(cs_mapLocalHost); - for (map<CNetAddr, LocalServiceInfo>::iterator it = mapLocalHost.begin(); it != mapLocalHost.end(); it++) + for (std::map<CNetAddr, LocalServiceInfo>::iterator it = mapLocalHost.begin(); it != mapLocalHost.end(); it++) { int nScore = (*it).second.nScore; int nReachability = (*it).first.GetReachabilityFrom(paddrPeer); @@ -164,7 +162,7 @@ static std::vector<CAddress> convertSeed6(const std::vector<SeedSpec6> &vSeedsIn { struct in6_addr ip; memcpy(&ip, i->addr, sizeof(ip)); - CAddress addr(CService(ip, i->port)); + CAddress addr(CService(ip, i->port), NODE_NETWORK); addr.nTime = GetTime() - GetRand(nOneWeek) - nOneWeek; vSeedsOut.push_back(addr); } @@ -177,13 +175,12 @@ static std::vector<CAddress> convertSeed6(const std::vector<SeedSpec6> &vSeedsIn // one by discovery. CAddress GetLocalAddress(const CNetAddr *paddrPeer) { - CAddress ret(CService("0.0.0.0",GetListenPort()),0); + CAddress ret(CService("0.0.0.0",GetListenPort()), NODE_NONE); CService addr; if (GetLocal(addr, paddrPeer)) { - ret = CAddress(addr); + ret = CAddress(addr, nLocalServices); } - ret.nServices = nLocalServices; ret.nTime = GetAdjustedTime(); return ret; } @@ -371,7 +368,7 @@ CNode* FindNode(const CService& addr) return NULL; } -CNode* ConnectNode(CAddress addrConnect, const char *pszDest) +CNode* ConnectNode(CAddress addrConnect, const char *pszDest, bool fCountFailure) { if (pszDest == NULL) { if (IsLocal(addrConnect)) @@ -403,7 +400,27 @@ CNode* ConnectNode(CAddress addrConnect, const char *pszDest) return NULL; } - addrman.Attempt(addrConnect); + if (pszDest && addrConnect.IsValid()) { + // It is possible that we already have a connection to the IP/port pszDest resolved to. + // In that case, drop the connection that was just created, and return the existing CNode instead. + // Also store the name we used to connect in that CNode, so that future FindNode() calls to that + // name catch this early. + CNode* pnode = FindNode((CService)addrConnect); + if (pnode) + { + pnode->AddRef(); + { + LOCK(cs_vNodes); + if (pnode->addrName.empty()) { + pnode->addrName = std::string(pszDest); + } + } + CloseSocket(hSocket); + return pnode; + } + } + + addrman.Attempt(addrConnect, fCountFailure); // Add node CNode* pnode = new CNode(hSocket, addrConnect, pszDest ? pszDest : "", false); @@ -414,18 +431,39 @@ CNode* ConnectNode(CAddress addrConnect, const char *pszDest) vNodes.push_back(pnode); } + pnode->nServicesExpected = ServiceFlags(addrConnect.nServices & nRelevantServices); pnode->nTimeConnected = GetTime(); return pnode; } else if (!proxyConnectionFailed) { // If connecting to the node failed, and failure is not caused by a problem connecting to // the proxy, mark this as an attempt. - addrman.Attempt(addrConnect); + addrman.Attempt(addrConnect, fCountFailure); } return NULL; } +static void DumpBanlist() +{ + CNode::SweepBanned(); // clean unused entries (if bantime has expired) + + if (!CNode::BannedSetIsDirty()) + return; + + int64_t nStart = GetTimeMillis(); + + CBanDB bandb; + banmap_t banmap; + CNode::SetBannedSetDirty(false); + CNode::GetBanned(banmap); + if (!bandb.Write(banmap)) + CNode::SetBannedSetDirty(true); + + LogPrint("net", "Flushed %d banned node ips/subnets to banlist.dat %dms\n", + banmap.size(), GetTimeMillis() - nStart); +} + void CNode::CloseSocketDisconnect() { fDisconnect = true; @@ -443,18 +481,18 @@ void CNode::CloseSocketDisconnect() void CNode::PushVersion() { - int nBestHeight = g_signals.GetHeight().get_value_or(0); + int nBestHeight = GetNodeSignals().GetHeight().get_value_or(0); int64_t nTime = (fInbound ? GetAdjustedTime() : GetTime()); - CAddress addrYou = (addr.IsRoutable() && !IsProxy(addr) ? addr : CAddress(CService("0.0.0.0",0))); + CAddress addrYou = (addr.IsRoutable() && !IsProxy(addr) ? addr : CAddress(CService("0.0.0.0", 0), addr.nServices)); CAddress addrMe = GetLocalAddress(&addr); GetRandBytes((unsigned char*)&nLocalHostNonce, sizeof(nLocalHostNonce)); if (fLogIPs) LogPrint("net", "send version message: version %d, blocks=%d, us=%s, them=%s, peer=%d\n", PROTOCOL_VERSION, nBestHeight, addrMe.ToString(), addrYou.ToString(), id); else LogPrint("net", "send version message: version %d, blocks=%d, us=%s, peer=%d\n", PROTOCOL_VERSION, nBestHeight, addrMe.ToString(), id); - PushMessage(NetMsgType::VERSION, PROTOCOL_VERSION, nLocalServices, nTime, addrYou, addrMe, - nLocalHostNonce, strSubVersion, nBestHeight, !GetBoolArg("-blocksonly", DEFAULT_BLOCKSONLY)); + PushMessage(NetMsgType::VERSION, PROTOCOL_VERSION, (uint64_t)nLocalServices, nTime, addrYou, addrMe, + nLocalHostNonce, strSubVersion, nBestHeight, ::fRelayTxes); } @@ -467,9 +505,13 @@ bool CNode::setBannedIsDirty; void CNode::ClearBanned() { - LOCK(cs_setBanned); - setBanned.clear(); - setBannedIsDirty = true; + { + LOCK(cs_setBanned); + setBanned.clear(); + setBannedIsDirty = true; + } + DumpBanlist(); //store banlist to disk + uiInterface.BannedListChanged(); } bool CNode::IsBanned(CNetAddr ip) @@ -520,11 +562,25 @@ void CNode::Ban(const CSubNet& subNet, const BanReason &banReason, int64_t banti } banEntry.nBanUntil = (sinceUnixEpoch ? 0 : GetTime() )+bantimeoffset; - LOCK(cs_setBanned); - if (setBanned[subNet].nBanUntil < banEntry.nBanUntil) - setBanned[subNet] = banEntry; - - setBannedIsDirty = true; + { + LOCK(cs_setBanned); + if (setBanned[subNet].nBanUntil < banEntry.nBanUntil) { + setBanned[subNet] = banEntry; + setBannedIsDirty = true; + } + else + return; + } + uiInterface.BannedListChanged(); + { + LOCK(cs_vNodes); + BOOST_FOREACH(CNode* pnode, vNodes) { + if (subNet.Match((CNetAddr)pnode->addr)) + pnode->fDisconnect = true; + } + } + if(banReason == BanReasonManuallyAdded) + DumpBanlist(); //store banlist to disk immediately if user requested ban } bool CNode::Unban(const CNetAddr &addr) { @@ -533,13 +589,15 @@ bool CNode::Unban(const CNetAddr &addr) { } bool CNode::Unban(const CSubNet &subNet) { - LOCK(cs_setBanned); - if (setBanned.erase(subNet)) { + LOCK(cs_setBanned); + if (!setBanned.erase(subNet)) + return false; setBannedIsDirty = true; - return true; } - return false; + uiInterface.BannedListChanged(); + DumpBanlist(); //store banlist to disk immediately + return true; } void CNode::GetBanned(banmap_t &banMap) @@ -796,86 +854,52 @@ void SocketSendData(CNode *pnode) pnode->vSendMsg.erase(pnode->vSendMsg.begin(), it); } -static list<CNode*> vNodesDisconnected; - -class CNodeRef { -public: - CNodeRef(CNode *pnode) : _pnode(pnode) { - LOCK(cs_vNodes); - _pnode->AddRef(); - } - - ~CNodeRef() { - LOCK(cs_vNodes); - _pnode->Release(); - } - - CNode& operator *() const {return *_pnode;}; - CNode* operator ->() const {return _pnode;}; - - CNodeRef& operator =(const CNodeRef& other) - { - if (this != &other) { - LOCK(cs_vNodes); +static std::list<CNode*> vNodesDisconnected; - _pnode->Release(); - _pnode = other._pnode; - _pnode->AddRef(); - } - return *this; - } - - CNodeRef(const CNodeRef& other): - _pnode(other._pnode) - { - LOCK(cs_vNodes); - _pnode->AddRef(); - } -private: - CNode *_pnode; +struct NodeEvictionCandidate +{ + NodeId id; + int64_t nTimeConnected; + int64_t nMinPingUsecTime; + int64_t nLastBlockTime; + int64_t nLastTXTime; + bool fNetworkNode; + bool fRelayTxes; + bool fBloomFilter; + CAddress addr; + uint64_t nKeyedNetGroup; }; -static bool ReverseCompareNodeMinPingTime(const CNodeRef &a, const CNodeRef &b) +static bool ReverseCompareNodeMinPingTime(const NodeEvictionCandidate &a, const NodeEvictionCandidate &b) { - return a->nMinPingUsecTime > b->nMinPingUsecTime; + return a.nMinPingUsecTime > b.nMinPingUsecTime; } -static bool ReverseCompareNodeTimeConnected(const CNodeRef &a, const CNodeRef &b) +static bool ReverseCompareNodeTimeConnected(const NodeEvictionCandidate &a, const NodeEvictionCandidate &b) { - return a->nTimeConnected > b->nTimeConnected; + return a.nTimeConnected > b.nTimeConnected; } -class CompareNetGroupKeyed -{ - std::vector<unsigned char> vchSecretKey; -public: - CompareNetGroupKeyed() - { - vchSecretKey.resize(32, 0); - GetRandBytes(vchSecretKey.data(), vchSecretKey.size()); - } - - bool operator()(const CNodeRef &a, const CNodeRef &b) - { - std::vector<unsigned char> vchGroupA, vchGroupB; - CSHA256 hashA, hashB; - std::vector<unsigned char> vchA(32), vchB(32); - - vchGroupA = a->addr.GetGroup(); - vchGroupB = b->addr.GetGroup(); - - hashA.Write(begin_ptr(vchGroupA), vchGroupA.size()); - hashB.Write(begin_ptr(vchGroupB), vchGroupB.size()); - - hashA.Write(begin_ptr(vchSecretKey), vchSecretKey.size()); - hashB.Write(begin_ptr(vchSecretKey), vchSecretKey.size()); +static bool CompareNetGroupKeyed(const NodeEvictionCandidate &a, const NodeEvictionCandidate &b) { + return a.nKeyedNetGroup < b.nKeyedNetGroup; +} - hashA.Finalize(begin_ptr(vchA)); - hashB.Finalize(begin_ptr(vchB)); +static bool CompareNodeBlockTime(const NodeEvictionCandidate &a, const NodeEvictionCandidate &b) +{ + // There is a fall-through here because it is common for a node to have many peers which have not yet relayed a block. + if (a.nLastBlockTime != b.nLastBlockTime) return a.nLastBlockTime < b.nLastBlockTime; + if (a.fNetworkNode != b.fNetworkNode) return b.fNetworkNode; + return a.nTimeConnected > b.nTimeConnected; +} - return vchA < vchB; - } -}; +static bool CompareNodeTXTime(const NodeEvictionCandidate &a, const NodeEvictionCandidate &b) +{ + // There is a fall-through here because it is common for a node to have more than a few peers that have not yet relayed txn. + if (a.nLastTXTime != b.nLastTXTime) return a.nLastTXTime < b.nLastTXTime; + if (a.fRelayTxes != b.fRelayTxes) return b.fRelayTxes; + if (a.fBloomFilter != b.fBloomFilter) return a.fBloomFilter; + return a.nTimeConnected > b.nTimeConnected; +} /** Try to find a connection to evict when the node is full. * Extreme care must be taken to avoid opening the node to attacker @@ -885,8 +909,8 @@ public: * to forge. In order to partition a node the attacker must be * simultaneously better at all of them than honest peers. */ -static bool AttemptToEvictConnection(bool fPreferNewConnection) { - std::vector<CNodeRef> vEvictionCandidates; +static bool AttemptToEvictConnection() { + std::vector<NodeEvictionCandidate> vEvictionCandidates; { LOCK(cs_vNodes); @@ -897,7 +921,10 @@ static bool AttemptToEvictConnection(bool fPreferNewConnection) { continue; if (node->fDisconnect) continue; - vEvictionCandidates.push_back(CNodeRef(node)); + NodeEvictionCandidate candidate = {node->id, node->nTimeConnected, node->nMinPingUsecTime, + node->nLastBlockTime, node->nLastTXTime, node->fNetworkNode, + node->fRelayTxes, node->pfilter != NULL, node->addr, node->nKeyedNetGroup}; + vEvictionCandidates.push_back(candidate); } } @@ -906,9 +933,8 @@ static bool AttemptToEvictConnection(bool fPreferNewConnection) { // Protect connections with certain characteristics // Deterministically select 4 peers to protect by netgroup. - // An attacker cannot predict which netgroups will be protected. - static CompareNetGroupKeyed comparerNetGroupKeyed; - std::sort(vEvictionCandidates.begin(), vEvictionCandidates.end(), comparerNetGroupKeyed); + // An attacker cannot predict which netgroups will be protected + std::sort(vEvictionCandidates.begin(), vEvictionCandidates.end(), CompareNetGroupKeyed); vEvictionCandidates.erase(vEvictionCandidates.end() - std::min(4, static_cast<int>(vEvictionCandidates.size())), vEvictionCandidates.end()); if (vEvictionCandidates.empty()) return false; @@ -920,6 +946,20 @@ static bool AttemptToEvictConnection(bool fPreferNewConnection) { if (vEvictionCandidates.empty()) return false; + // Protect 4 nodes that most recently sent us transactions. + // An attacker cannot manipulate this metric without performing useful work. + std::sort(vEvictionCandidates.begin(), vEvictionCandidates.end(), CompareNodeTXTime); + vEvictionCandidates.erase(vEvictionCandidates.end() - std::min(4, static_cast<int>(vEvictionCandidates.size())), vEvictionCandidates.end()); + + if (vEvictionCandidates.empty()) return false; + + // Protect 4 nodes that most recently sent us blocks. + // An attacker cannot manipulate this metric without performing useful work. + std::sort(vEvictionCandidates.begin(), vEvictionCandidates.end(), CompareNodeBlockTime); + vEvictionCandidates.erase(vEvictionCandidates.end() - std::min(4, static_cast<int>(vEvictionCandidates.size())), vEvictionCandidates.end()); + + if (vEvictionCandidates.empty()) return false; + // Protect the half of the remaining nodes which have been connected the longest. // This replicates the non-eviction implicit behavior, and precludes attacks that start later. std::sort(vEvictionCandidates.begin(), vEvictionCandidates.end(), ReverseCompareNodeTimeConnected); @@ -929,36 +969,35 @@ static bool AttemptToEvictConnection(bool fPreferNewConnection) { // Identify the network group with the most connections and youngest member. // (vEvictionCandidates is already sorted by reverse connect time) - std::vector<unsigned char> naMostConnections; + uint64_t naMostConnections; unsigned int nMostConnections = 0; int64_t nMostConnectionsTime = 0; - std::map<std::vector<unsigned char>, std::vector<CNodeRef> > mapAddrCounts; - BOOST_FOREACH(const CNodeRef &node, vEvictionCandidates) { - mapAddrCounts[node->addr.GetGroup()].push_back(node); - int64_t grouptime = mapAddrCounts[node->addr.GetGroup()][0]->nTimeConnected; - size_t groupsize = mapAddrCounts[node->addr.GetGroup()].size(); + std::map<uint64_t, std::vector<NodeEvictionCandidate> > mapAddrCounts; + BOOST_FOREACH(const NodeEvictionCandidate &node, vEvictionCandidates) { + mapAddrCounts[node.nKeyedNetGroup].push_back(node); + int64_t grouptime = mapAddrCounts[node.nKeyedNetGroup][0].nTimeConnected; + size_t groupsize = mapAddrCounts[node.nKeyedNetGroup].size(); if (groupsize > nMostConnections || (groupsize == nMostConnections && grouptime > nMostConnectionsTime)) { nMostConnections = groupsize; nMostConnectionsTime = grouptime; - naMostConnections = node->addr.GetGroup(); + naMostConnections = node.nKeyedNetGroup; } } // Reduce to the network group with the most connections - vEvictionCandidates = mapAddrCounts[naMostConnections]; - - // Do not disconnect peers if there is only one unprotected connection from their network group. - // This step excessively favors netgroup diversity, and should be removed once more protective criteria are established. - if (vEvictionCandidates.size() <= 1) - // unless we prefer the new connection (for whitelisted peers) - if (!fPreferNewConnection) - return false; + vEvictionCandidates = std::move(mapAddrCounts[naMostConnections]); // Disconnect from the network group with the most connections - vEvictionCandidates[0]->fDisconnect = true; - - return true; + NodeId evicted = vEvictionCandidates.front().id; + LOCK(cs_vNodes); + for(std::vector<CNode*>::const_iterator it(vNodes.begin()); it != vNodes.end(); ++it) { + if ((*it)->GetId() == evicted) { + (*it)->fDisconnect = true; + return true; + } + } + return false; } static void AcceptConnection(const ListenSocket& hListenSocket) { @@ -1014,7 +1053,7 @@ static void AcceptConnection(const ListenSocket& hListenSocket) { if (nInbound >= nMaxInbound) { - if (!AttemptToEvictConnection(whitelisted)) { + if (!AttemptToEvictConnection()) { // No connection to evict, disconnect the new connection LogPrint("net", "failed to find an eviction candidate - connection dropped (full)\n"); CloseSocket(hSocket); @@ -1045,7 +1084,7 @@ void ThreadSocketHandler() { LOCK(cs_vNodes); // Disconnect unused nodes - vector<CNode*> vNodesCopy = vNodes; + std::vector<CNode*> vNodesCopy = vNodes; BOOST_FOREACH(CNode* pnode, vNodesCopy) { if (pnode->fDisconnect || @@ -1069,7 +1108,7 @@ void ThreadSocketHandler() } { // Delete disconnected nodes - list<CNode*> vNodesDisconnectedCopy = vNodesDisconnected; + std::list<CNode*> vNodesDisconnectedCopy = vNodesDisconnected; BOOST_FOREACH(CNode* pnode, vNodesDisconnectedCopy) { // wait until threads are done using it @@ -1120,7 +1159,7 @@ void ThreadSocketHandler() BOOST_FOREACH(const ListenSocket& hListenSocket, vhListenSocket) { FD_SET(hListenSocket.socket, &fdsetRecv); - hSocketMax = max(hSocketMax, hListenSocket.socket); + hSocketMax = std::max(hSocketMax, hListenSocket.socket); have_fds = true; } @@ -1131,7 +1170,7 @@ void ThreadSocketHandler() if (pnode->hSocket == INVALID_SOCKET) continue; FD_SET(pnode->hSocket, &fdsetError); - hSocketMax = max(hSocketMax, pnode->hSocket); + hSocketMax = std::max(hSocketMax, pnode->hSocket); have_fds = true; // Implement the following logic: @@ -1198,7 +1237,7 @@ void ThreadSocketHandler() // // Service each socket // - vector<CNode*> vNodesCopy; + std::vector<CNode*> vNodesCopy; { LOCK(cs_vNodes); vNodesCopy = vNodes; @@ -1355,7 +1394,7 @@ void ThreadMapPort() } } - string strDesc = "Bitcoin " + FormatFullVersion(); + std::string strDesc = "Bitcoin " + FormatFullVersion(); try { while (true) { @@ -1427,6 +1466,18 @@ void MapPort(bool) +static std::string GetDNSHost(const CDNSSeedData& data, ServiceFlags* requiredServiceBits) +{ + //use default host for non-filter-capable seeds or if we use the default service bits (NODE_NETWORK) + if (!data.supportsServiceBitsFiltering || *requiredServiceBits == NODE_NETWORK) { + *requiredServiceBits = NODE_NETWORK; + return data.host; + } + + return strprintf("x%x.%s", *requiredServiceBits, data.host); +} + + void ThreadDNSAddressSeed() { // goal: only query DNS seeds if address need is acute @@ -1441,7 +1492,7 @@ void ThreadDNSAddressSeed() } } - const vector<CDNSSeedData> &vSeeds = Params().DNSSeeds(); + const std::vector<CDNSSeedData> &vSeeds = Params().DNSSeeds(); int found = 0; LogPrintf("Loading addresses from DNS seeds (could take a while)\n"); @@ -1450,14 +1501,15 @@ void ThreadDNSAddressSeed() if (HaveNameProxy()) { AddOneShot(seed.host); } else { - vector<CNetAddr> vIPs; - vector<CAddress> vAdd; - if (LookupHost(seed.host.c_str(), vIPs, 0, true)) + std::vector<CNetAddr> vIPs; + std::vector<CAddress> vAdd; + ServiceFlags requiredServiceBits = nRelevantServices; + if (LookupHost(GetDNSHost(seed, &requiredServiceBits).c_str(), vIPs, 0, true)) { BOOST_FOREACH(const CNetAddr& ip, vIPs) { int nOneDay = 24*3600; - CAddress addr = CAddress(CService(ip, Params().GetDefaultPort())); + CAddress addr = CAddress(CService(ip, Params().GetDefaultPort()), requiredServiceBits); addr.nTime = GetTime() - 3*nOneDay - GetRand(4*nOneDay); // use a random age between 3 and 7 days old vAdd.push_back(addr); found++; @@ -1508,7 +1560,7 @@ void DumpData() void static ProcessOneShot() { - string strDest; + std::string strDest; { LOCK(cs_vOneShots); if (vOneShots.empty()) @@ -1519,7 +1571,7 @@ void static ProcessOneShot() CAddress addr; CSemaphoreGrant grant(*semOutbound, true); if (grant) { - if (!OpenNetworkConnection(addr, &grant, strDest.c_str(), true)) + if (!OpenNetworkConnection(addr, false, &grant, strDest.c_str(), true)) AddOneShot(strDest); } } @@ -1534,8 +1586,8 @@ void ThreadOpenConnections() ProcessOneShot(); BOOST_FOREACH(const std::string& strAddr, mapMultiArgs["-connect"]) { - CAddress addr; - OpenNetworkConnection(addr, NULL, strAddr.c_str()); + CAddress addr(CService(), NODE_NONE); + OpenNetworkConnection(addr, false, NULL, strAddr.c_str()); for (int i = 0; i < 10 && i < nLoop; i++) { MilliSleep(500); @@ -1574,7 +1626,7 @@ void ThreadOpenConnections() // Only connect out to one peer per network group (/16 for IPv4). // Do this here so we don't have to critsect vNodes inside mapAddresses critsect. int nOutbound = 0; - set<vector<unsigned char> > setConnected; + std::set<std::vector<unsigned char> > setConnected; { LOCK(cs_vNodes); BOOST_FOREACH(CNode* pnode, vNodes) { @@ -1606,6 +1658,10 @@ void ThreadOpenConnections() if (IsLimited(addr)) continue; + // only connect to full nodes + if ((addr.nServices & REQUIRED_SERVICES) != REQUIRED_SERVICES) + continue; + // only consider very recently tried nodes after 30 failed attempts if (nANow - addr.nLastTry < 600 && nTries < 30) continue; @@ -1619,83 +1675,89 @@ void ThreadOpenConnections() } if (addrConnect.IsValid()) - OpenNetworkConnection(addrConnect, &grant); + OpenNetworkConnection(addrConnect, (int)setConnected.size() >= std::min(nMaxConnections - 1, 2), &grant); } } -void ThreadOpenAddedConnections() +std::vector<AddedNodeInfo> GetAddedNodeInfo() { + std::vector<AddedNodeInfo> ret; + + std::list<std::string> lAddresses(0); { LOCK(cs_vAddedNodes); - vAddedNodes = mapMultiArgs["-addnode"]; + ret.reserve(vAddedNodes.size()); + BOOST_FOREACH(const std::string& strAddNode, vAddedNodes) + lAddresses.push_back(strAddNode); } - if (HaveNameProxy()) { - while(true) { - list<string> lAddresses(0); - { - LOCK(cs_vAddedNodes); - BOOST_FOREACH(const std::string& strAddNode, vAddedNodes) - lAddresses.push_back(strAddNode); + + // Build a map of all already connected addresses (by IP:port and by name) to inbound/outbound and resolved CService + std::map<CService, bool> mapConnected; + std::map<std::string, std::pair<bool, CService>> mapConnectedByName; + { + LOCK(cs_vNodes); + for (const CNode* pnode : vNodes) { + if (pnode->addr.IsValid()) { + mapConnected[pnode->addr] = pnode->fInbound; } - BOOST_FOREACH(const std::string& strAddNode, lAddresses) { - CAddress addr; - CSemaphoreGrant grant(*semOutbound); - OpenNetworkConnection(addr, &grant, strAddNode.c_str()); - MilliSleep(500); + if (!pnode->addrName.empty()) { + mapConnectedByName[pnode->addrName] = std::make_pair(pnode->fInbound, static_cast<const CService&>(pnode->addr)); } - MilliSleep(120000); // Retry every 2 minutes } } - for (unsigned int i = 0; true; i++) - { - list<string> lAddresses(0); - { - LOCK(cs_vAddedNodes); - BOOST_FOREACH(const std::string& strAddNode, vAddedNodes) - lAddresses.push_back(strAddNode); + BOOST_FOREACH(const std::string& strAddNode, lAddresses) { + CService service(strAddNode, Params().GetDefaultPort()); + if (service.IsValid()) { + // strAddNode is an IP:port + auto it = mapConnected.find(service); + if (it != mapConnected.end()) { + ret.push_back(AddedNodeInfo{strAddNode, service, true, it->second}); + } else { + ret.push_back(AddedNodeInfo{strAddNode, CService(), false, false}); + } + } else { + // strAddNode is a name + auto it = mapConnectedByName.find(strAddNode); + if (it != mapConnectedByName.end()) { + ret.push_back(AddedNodeInfo{strAddNode, it->second.second, true, it->second.first}); + } else { + ret.push_back(AddedNodeInfo{strAddNode, CService(), false, false}); + } } + } - list<vector<CService> > lservAddressesToAdd(0); - BOOST_FOREACH(const std::string& strAddNode, lAddresses) { - vector<CService> vservNode(0); - if(Lookup(strAddNode.c_str(), vservNode, Params().GetDefaultPort(), fNameLookup, 0)) - { - lservAddressesToAdd.push_back(vservNode); - { - LOCK(cs_setservAddNodeAddresses); - BOOST_FOREACH(const CService& serv, vservNode) - setservAddNodeAddresses.insert(serv); - } + return ret; +} + +void ThreadOpenAddedConnections() +{ + { + LOCK(cs_vAddedNodes); + vAddedNodes = mapMultiArgs["-addnode"]; + } + + for (unsigned int i = 0; true; i++) + { + std::vector<AddedNodeInfo> vInfo = GetAddedNodeInfo(); + for (const AddedNodeInfo& info : vInfo) { + if (!info.fConnected) { + CSemaphoreGrant grant(*semOutbound); + // If strAddedNode is an IP/port, decode it immediately, so + // OpenNetworkConnection can detect existing connections to that IP/port. + CService service(info.strAddedNode, Params().GetDefaultPort()); + OpenNetworkConnection(CAddress(service, NODE_NONE), false, &grant, info.strAddedNode.c_str(), false); + MilliSleep(500); } } - // Attempt to connect to each IP for each addnode entry until at least one is successful per addnode entry - // (keeping in mind that addnode entries can have many IPs if fNameLookup) - { - LOCK(cs_vNodes); - BOOST_FOREACH(CNode* pnode, vNodes) - for (list<vector<CService> >::iterator it = lservAddressesToAdd.begin(); it != lservAddressesToAdd.end(); it++) - BOOST_FOREACH(const CService& addrNode, *(it)) - if (pnode->addr == addrNode) - { - it = lservAddressesToAdd.erase(it); - it--; - break; - } - } - BOOST_FOREACH(vector<CService>& vserv, lservAddressesToAdd) - { - CSemaphoreGrant grant(*semOutbound); - OpenNetworkConnection(CAddress(vserv[i % vserv.size()]), &grant); - MilliSleep(500); - } + MilliSleep(120000); // Retry every 2 minutes } } // if successful, this moves the passed grant to the constructed node -bool OpenNetworkConnection(const CAddress& addrConnect, CSemaphoreGrant *grantOutbound, const char *pszDest, bool fOneShot) +bool OpenNetworkConnection(const CAddress& addrConnect, bool fCountFailure, CSemaphoreGrant *grantOutbound, const char *pszDest, bool fOneShot) { // // Initiate outbound network connection @@ -1709,7 +1771,7 @@ bool OpenNetworkConnection(const CAddress& addrConnect, CSemaphoreGrant *grantOu } else if (FindNode(std::string(pszDest))) return false; - CNode* pnode = ConnectNode(addrConnect, pszDest); + CNode* pnode = ConnectNode(addrConnect, pszDest, fCountFailure); boost::this_thread::interruption_point(); if (!pnode) @@ -1731,7 +1793,7 @@ void ThreadMessageHandler() while (true) { - vector<CNode*> vNodesCopy; + std::vector<CNode*> vNodesCopy; { LOCK(cs_vNodes); vNodesCopy = vNodes; @@ -1752,7 +1814,7 @@ void ThreadMessageHandler() TRY_LOCK(pnode->cs_vRecvMsg, lockRecv); if (lockRecv) { - if (!g_signals.ProcessMessages(pnode)) + if (!GetNodeSignals().ProcessMessages(pnode)) pnode->CloseSocketDisconnect(); if (pnode->nSendSize < SendBufferSize()) @@ -1770,7 +1832,7 @@ void ThreadMessageHandler() { TRY_LOCK(pnode->cs_vSend, lockSend); if (lockSend) - g_signals.SendMessages(pnode); + GetNodeSignals().SendMessages(pnode); } boost::this_thread::interruption_point(); } @@ -1791,7 +1853,7 @@ void ThreadMessageHandler() -bool BindListenPort(const CService &addrBind, string& strError, bool fWhitelisted) +bool BindListenPort(const CService &addrBind, std::string& strError, bool fWhitelisted) { strError = ""; int nOne = 1; @@ -1899,7 +1961,7 @@ void static Discover(boost::thread_group& threadGroup) char pszHostName[256] = ""; if (gethostname(pszHostName, sizeof(pszHostName)) != SOCKET_ERROR) { - vector<CNetAddr> vaddr; + std::vector<CNetAddr> vaddr; if (LookupHost(pszHostName, vaddr, 0, true)) { BOOST_FOREACH (const CNetAddr &addr, vaddr) @@ -1950,6 +2012,7 @@ void StartNode(boost::thread_group& threadGroup, CScheduler& scheduler) if (adb.Read(addrman)) LogPrintf("Loaded %i addresses from peers.dat %dms\n", addrman.size(), GetTimeMillis() - nStart); else { + addrman.Clear(); // Addrman can be in an inconsistent state after failure, reset it LogPrintf("Invalid or missing peers.dat; recreating\n"); DumpAddresses(); } @@ -2072,18 +2135,6 @@ instance_of_cnetcleanup; void RelayTransaction(const CTransaction& tx) { CInv inv(MSG_TX, tx.GetHash()); - { - LOCK(cs_mapRelay); - // Expire old relay messages - while (!vRelayExpiration.empty() && vRelayExpiration.front().first < GetTime()) - { - mapRelay.erase(vRelayExpiration.front().second); - vRelayExpiration.pop_front(); - } - - mapRelay.insert(std::make_pair(inv.hash, tx)); - vRelayExpiration.push_back(std::make_pair(GetTime() + 15 * 60, inv.hash)); - } LOCK(cs_vNodes); BOOST_FOREACH(CNode* pnode, vNodes) { @@ -2299,7 +2350,7 @@ bool CAddrDB::Read(CAddrMan& addr) // Don't try to resize to a negative number if file is small if (fileSize >= sizeof(uint256)) dataSize = fileSize - sizeof(uint256); - vector<unsigned char> vchData; + std::vector<unsigned char> vchData; vchData.resize(dataSize); uint256 hashIn; @@ -2320,6 +2371,11 @@ bool CAddrDB::Read(CAddrMan& addr) if (hashIn != hashTmp) return error("%s: Checksum mismatch, data corrupted", __func__); + return Read(addr, ssPeers); +} + +bool CAddrDB::Read(CAddrMan& addr, CDataStream& ssPeers) +{ unsigned char pchMsgTmp[4]; try { // de-serialize file header (network specific magic number) and .. @@ -2333,6 +2389,8 @@ bool CAddrDB::Read(CAddrMan& addr) ssPeers >> addr; } catch (const std::exception& e) { + // de-serialization has failed, ensure addrman is left in a clean state + addr.Clear(); return error("%s: Deserialize or I/O error - %s", __func__, e.what()); } @@ -2344,10 +2402,13 @@ unsigned int SendBufferSize() { return 1000*GetArg("-maxsendbuffer", DEFAULT_MAX CNode::CNode(SOCKET hSocketIn, const CAddress& addrIn, const std::string& addrNameIn, bool fInboundIn) : ssSend(SER_NETWORK, INIT_PROTO_VERSION), + addr(addrIn), + nKeyedNetGroup(CalculateKeyedNetGroup(addrIn)), addrKnown(5000, 0.001), filterInventoryKnown(50000, 0.000001) { - nServices = 0; + nServices = NODE_NONE; + nServicesExpected = NODE_NONE; hSocket = hSocketIn; nRecvVersion = INIT_PROTO_VERSION; nLastSend = 0; @@ -2356,7 +2417,6 @@ CNode::CNode(SOCKET hSocketIn, const CAddress& addrIn, const std::string& addrNa nRecvBytes = 0; nTimeConnected = GetTime(); nTimeOffset = 0; - addr = addrIn; addrName = addrNameIn == "" ? addr.ToStringIPPort() : addrNameIn; nVersion = 0; strSubVer = ""; @@ -2381,6 +2441,9 @@ CNode::CNode(SOCKET hSocketIn, const CAddress& addrIn, const std::string& addrNa fRelayTxes = false; fSentAddr = false; pfilter = new CBloomFilter(); + timeLastMempoolReq = 0; + nLastBlockTime = 0; + nLastTXTime = 0; nPingNonceSent = 0; nPingUsecStart = 0; nPingUsecTime = 0; @@ -2579,7 +2642,7 @@ bool CBanDB::Read(banmap_t& banSet) // Don't try to resize to a negative number if file is small if (fileSize >= sizeof(uint256)) dataSize = fileSize - sizeof(uint256); - vector<unsigned char> vchData; + std::vector<unsigned char> vchData; vchData.resize(dataSize); uint256 hashIn; @@ -2619,26 +2682,16 @@ bool CBanDB::Read(banmap_t& banSet) return true; } -void DumpBanlist() -{ - CNode::SweepBanned(); // clean unused entries (if bantime has expired) - - if (!CNode::BannedSetIsDirty()) - return; - - int64_t nStart = GetTimeMillis(); +int64_t PoissonNextSend(int64_t nNow, int average_interval_seconds) { + return nNow + (int64_t)(log1p(GetRand(1ULL << 48) * -0.0000000000000035527136788 /* -1/2^48 */) * average_interval_seconds * -1000000.0 + 0.5); +} - CBanDB bandb; - banmap_t banmap; - CNode::SetBannedSetDirty(false); - CNode::GetBanned(banmap); - if (!bandb.Write(banmap)) - CNode::SetBannedSetDirty(true); +/* static */ uint64_t CNode::CalculateKeyedNetGroup(const CAddress& ad) +{ + static const uint64_t k0 = GetRand(std::numeric_limits<uint64_t>::max()); + static const uint64_t k1 = GetRand(std::numeric_limits<uint64_t>::max()); - LogPrint("net", "Flushed %d banned node ips/subnets to banlist.dat %dms\n", - banmap.size(), GetTimeMillis() - nStart); -} + std::vector<unsigned char> vchNetGroup(ad.GetGroup()); -int64_t PoissonNextSend(int64_t nNow, int average_interval_seconds) { - return nNow + (int64_t)(log1p(GetRand(1ULL << 48) * -0.0000000000000035527136788 /* -1/2^48 */) * average_interval_seconds * -1000000.0 + 0.5); + return CSipHasher(k0, k1).Write(&vchNetGroup[0], vchNetGroup.size()).Finalize(); } @@ -17,6 +17,7 @@ #include "sync.h" #include "uint256.h" +#include <atomic> #include <deque> #include <stdint.h> @@ -71,6 +72,8 @@ static const bool DEFAULT_FORCEDNSSEED = false; static const size_t DEFAULT_MAXRECEIVEBUFFER = 5 * 1000; static const size_t DEFAULT_MAXSENDBUFFER = 1 * 1000; +static const ServiceFlags REQUIRED_SERVICES = NODE_NETWORK; + // NOTE: When adjusting this, update rpcnet:setban's help ("24h") static const unsigned int DEFAULT_MISBEHAVING_BANTIME = 60 * 60 * 24; // Default 24-hour ban @@ -83,8 +86,7 @@ CNode* FindNode(const CNetAddr& ip); CNode* FindNode(const CSubNet& subNet); CNode* FindNode(const std::string& addrName); CNode* FindNode(const CService& ip); -CNode* ConnectNode(CAddress addrConnect, const char *pszDest = NULL); -bool OpenNetworkConnection(const CAddress& addrConnect, CSemaphoreGrant *grantOutbound = NULL, const char *strDest = NULL, bool fOneShot = false); +bool OpenNetworkConnection(const CAddress& addrConnect, bool fCountFailure, CSemaphoreGrant *grantOutbound = NULL, const char *strDest = NULL, bool fOneShot = false); void MapPort(bool fUseUPnP); unsigned short GetListenPort(); bool BindListenPort(const CService &bindAddr, std::string& strError, bool fWhitelisted = false); @@ -152,7 +154,8 @@ CAddress GetLocalAddress(const CNetAddr *paddrPeer = NULL); extern bool fDiscover; extern bool fListen; -extern uint64_t nLocalServices; +extern ServiceFlags nLocalServices; +extern bool fRelayTxes; extern uint64_t nLocalHostNonce; extern CAddrMan addrman; @@ -161,9 +164,6 @@ extern int nMaxConnections; extern std::vector<CNode*> vNodes; extern CCriticalSection cs_vNodes; -extern std::map<uint256, CTransaction> mapRelay; -extern std::deque<std::pair<int64_t, uint256> > vRelayExpiration; -extern CCriticalSection cs_mapRelay; extern limitedmap<uint256, int64_t> mapAlreadyAskedFor; extern std::vector<std::string> vAddedNodes; @@ -188,7 +188,7 @@ class CNodeStats { public: NodeId nodeid; - uint64_t nServices; + ServiceFlags nServices; bool fRelayTxes; int64_t nLastSend; int64_t nLastRecv; @@ -318,7 +318,8 @@ class CNode { public: // socket - uint64_t nServices; + ServiceFlags nServices; + ServiceFlags nServicesExpected; SOCKET hSocket; CDataStream ssSend; size_t nSendSize; // total size of all vSendMsg entries @@ -337,7 +338,7 @@ public: int64_t nLastRecv; int64_t nTimeConnected; int64_t nTimeOffset; - CAddress addr; + const CAddress addr; std::string addrName; CService addrLocal; int nVersion; @@ -364,6 +365,8 @@ public: CBloomFilter* pfilter; int nRefCount; NodeId id; + + const uint64_t nKeyedNetGroup; protected: // Denial-of-service detection/prevention @@ -414,6 +417,13 @@ public: // Used for BIP35 mempool sending, also protected by cs_inventory bool fSendMempool; + // Last time a "MEMPOOL" request was serviced. + std::atomic<int64_t> timeLastMempoolReq; + + // Block and TXN accept times + std::atomic<int64_t> nLastBlockTime; + std::atomic<int64_t> nLastTXTime; + // Ping time measurement: // The pong reply we're expecting, or 0 if no pong expected. uint64_t nPingNonceSent; @@ -450,6 +460,8 @@ private: CNode(const CNode&); void operator=(const CNode&); + static uint64_t CalculateKeyedNetGroup(const CAddress& ad); + public: NodeId GetId() const { @@ -794,6 +806,7 @@ public: CAddrDB(); bool Write(const CAddrMan& addr); bool Read(CAddrMan& addr); + bool Read(CAddrMan& addr, CDataStream& ssPeers); }; /** Access to the banlist database (banlist.dat) */ @@ -807,9 +820,17 @@ public: bool Read(banmap_t& banSet); }; -void DumpBanlist(); - /** Return a timestamp in the future (in microseconds) for exponentially distributed events. */ int64_t PoissonNextSend(int64_t nNow, int average_interval_seconds); +struct AddedNodeInfo +{ + std::string strAddedNode; + CService resolvedAddress; + bool fConnected; + bool fInbound; +}; + +std::vector<AddedNodeInfo> GetAddedNodeInfo(); + #endif // BITCOIN_NET_H diff --git a/src/netbase.cpp b/src/netbase.cpp index b44a8b16e2..e2a516986c 100644 --- a/src/netbase.cpp +++ b/src/netbase.cpp @@ -291,10 +291,25 @@ struct ProxyCredentials std::string password; }; +std::string Socks5ErrorString(int err) +{ + switch(err) { + case 0x01: return "general failure"; + case 0x02: return "connection not allowed"; + case 0x03: return "network unreachable"; + case 0x04: return "host unreachable"; + case 0x05: return "connection refused"; + case 0x06: return "TTL expired"; + case 0x07: return "protocol error"; + case 0x08: return "address type not supported"; + default: return "unknown"; + } +} + /** Connect using SOCKS5 (as described in RFC1928) */ static bool Socks5(const std::string& strDest, int port, const ProxyCredentials *auth, SOCKET& hSocket) { - LogPrintf("SOCKS5 connecting %s\n", strDest); + LogPrint("net", "SOCKS5 connecting %s\n", strDest); if (strDest.size() > 255) { CloseSocket(hSocket); return error("Hostname too long"); @@ -318,7 +333,8 @@ static bool Socks5(const std::string& strDest, int port, const ProxyCredentials char pchRet1[2]; if (!InterruptibleRecv(pchRet1, 2, SOCKS5_RECV_TIMEOUT, hSocket)) { CloseSocket(hSocket); - return error("Error reading proxy response"); + LogPrintf("Socks5() connect to %s:%d failed: InterruptibleRecv() timeout or other failure\n", strDest, port); + return false; } if (pchRet1[0] != 0x05) { CloseSocket(hSocket); @@ -379,19 +395,10 @@ static bool Socks5(const std::string& strDest, int port, const ProxyCredentials return error("Proxy failed to accept request"); } if (pchRet2[1] != 0x00) { + // Failures to connect to a peer that are not proxy errors CloseSocket(hSocket); - switch (pchRet2[1]) - { - case 0x01: return error("Proxy error: general failure"); - case 0x02: return error("Proxy error: connection not allowed"); - case 0x03: return error("Proxy error: network unreachable"); - case 0x04: return error("Proxy error: host unreachable"); - case 0x05: return error("Proxy error: connection refused"); - case 0x06: return error("Proxy error: TTL expired"); - case 0x07: return error("Proxy error: protocol error"); - case 0x08: return error("Proxy error: address type not supported"); - default: return error("Proxy error: unknown"); - } + LogPrintf("Socks5() connect to %s:%d failed: %s\n", strDest, port, Socks5ErrorString(pchRet2[1])); + return false; } if (pchRet2[2] != 0x00) { CloseSocket(hSocket); @@ -423,7 +430,7 @@ static bool Socks5(const std::string& strDest, int port, const ProxyCredentials CloseSocket(hSocket); return error("Error reading from proxy"); } - LogPrintf("SOCKS5 connected %s\n", strDest); + LogPrint("net", "SOCKS5 connected %s\n", strDest); return true; } @@ -614,10 +621,10 @@ bool ConnectSocketByName(CService &addr, SOCKET& hSocketRet, const char *pszDest proxyType nameProxy; GetNameProxy(nameProxy); - CService addrResolved; - if (Lookup(strDest.c_str(), addrResolved, port, fNameLookup && !HaveNameProxy())) { - if (addrResolved.IsValid()) { - addr = addrResolved; + std::vector<CService> addrResolved; + if (Lookup(strDest.c_str(), addrResolved, port, fNameLookup && !HaveNameProxy(), 256)) { + if (addrResolved.size() > 0) { + addr = addrResolved[GetRand(addrResolved.size())]; return ConnectSocket(addr, hSocketRet, nTimeout); } } diff --git a/src/protocol.cpp b/src/protocol.cpp index 8c4bd05725..422ef6f636 100644 --- a/src/protocol.cpp +++ b/src/protocol.cpp @@ -133,7 +133,7 @@ CAddress::CAddress() : CService() Init(); } -CAddress::CAddress(CService ipIn, uint64_t nServicesIn) : CService(ipIn) +CAddress::CAddress(CService ipIn, ServiceFlags nServicesIn) : CService(ipIn) { Init(); nServices = nServicesIn; @@ -141,7 +141,7 @@ CAddress::CAddress(CService ipIn, uint64_t nServicesIn) : CService(ipIn) void CAddress::Init() { - nServices = NODE_NETWORK; + nServices = NODE_NONE; nTime = 100000000; } diff --git a/src/protocol.h b/src/protocol.h index 1b049e52af..ab0a581783 100644 --- a/src/protocol.h +++ b/src/protocol.h @@ -223,7 +223,9 @@ extern const char *FEEFILTER; const std::vector<std::string> &getAllNetMessageTypes(); /** nServices flags */ -enum { +enum ServiceFlags : uint64_t { + // Nothing + NODE_NONE = 0, // NODE_NETWORK means that the node is capable of serving the block chain. It is currently // set by all Bitcoin Core nodes, and is unset by SPV clients or other peers that just want // network services but don't provide them. @@ -251,7 +253,7 @@ class CAddress : public CService { public: CAddress(); - explicit CAddress(CService ipIn, uint64_t nServicesIn = NODE_NETWORK); + explicit CAddress(CService ipIn, ServiceFlags nServicesIn); void Init(); @@ -267,13 +269,15 @@ public: if ((nType & SER_DISK) || (nVersion >= CADDR_TIME_VERSION && !(nType & SER_GETHASH))) READWRITE(nTime); - READWRITE(nServices); + uint64_t nServicesInt = nServices; + READWRITE(nServicesInt); + nServices = (ServiceFlags)nServicesInt; READWRITE(*(CService*)this); } // TODO: make private (improves encapsulation) public: - uint64_t nServices; + ServiceFlags nServices; // disk and network only unsigned int nTime; diff --git a/src/qt/askpassphrasedialog.cpp b/src/qt/askpassphrasedialog.cpp index 415bffb991..e8aa79679c 100644 --- a/src/qt/askpassphrasedialog.cpp +++ b/src/qt/askpassphrasedialog.cpp @@ -77,10 +77,7 @@ AskPassphraseDialog::AskPassphraseDialog(Mode mode, QWidget *parent) : AskPassphraseDialog::~AskPassphraseDialog() { - // Attempt to overwrite text so that they do not linger around in memory - ui->passEdit1->setText(QString(" ").repeated(ui->passEdit1->text().size())); - ui->passEdit2->setText(QString(" ").repeated(ui->passEdit2->text().size())); - ui->passEdit3->setText(QString(" ").repeated(ui->passEdit3->text().size())); + secureClearPassFields(); delete ui; } @@ -103,6 +100,8 @@ void AskPassphraseDialog::accept() newpass1.assign(ui->passEdit2->text().toStdString().c_str()); newpass2.assign(ui->passEdit3->text().toStdString().c_str()); + secureClearPassFields(); + switch(mode) { case Encrypt: { @@ -260,3 +259,17 @@ bool AskPassphraseDialog::eventFilter(QObject *object, QEvent *event) } return QDialog::eventFilter(object, event); } + +static void SecureClearQLineEdit(QLineEdit* edit) +{ + // Attempt to overwrite text so that they do not linger around in memory + edit->setText(QString(" ").repeated(edit->text().size())); + edit->clear(); +} + +void AskPassphraseDialog::secureClearPassFields() +{ + SecureClearQLineEdit(ui->passEdit1); + SecureClearQLineEdit(ui->passEdit2); + SecureClearQLineEdit(ui->passEdit3); +} diff --git a/src/qt/askpassphrasedialog.h b/src/qt/askpassphrasedialog.h index 727b5a1ada..34bf7ccb31 100644 --- a/src/qt/askpassphrasedialog.h +++ b/src/qt/askpassphrasedialog.h @@ -42,6 +42,7 @@ private: private Q_SLOTS: void textChanged(); + void secureClearPassFields(); protected: bool event(QEvent *event); diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp index 6218ab6ab0..64b5c83d72 100644 --- a/src/qt/bitcoin.cpp +++ b/src/qt/bitcoin.cpp @@ -533,6 +533,9 @@ int main(int argc, char *argv[]) // Generate high-dpi pixmaps QApplication::setAttribute(Qt::AA_UseHighDpiPixmaps); #endif +#if QT_VERSION >= 0x050600 + QGuiApplication::setAttribute(Qt::AA_EnableHighDpiScaling); +#endif #ifdef Q_OS_MAC QApplication::setAttribute(Qt::AA_DontShowIconsInMenus); #endif diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index 9984486364..50c19c3848 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -320,12 +320,14 @@ void BitcoinGUI::createActions() aboutAction = new QAction(platformStyle->TextColorIcon(":/icons/about"), tr("&About %1").arg(tr(PACKAGE_NAME)), this); aboutAction->setStatusTip(tr("Show information about %1").arg(tr(PACKAGE_NAME))); aboutAction->setMenuRole(QAction::AboutRole); + aboutAction->setEnabled(false); aboutQtAction = new QAction(platformStyle->TextColorIcon(":/icons/about_qt"), tr("About &Qt"), this); aboutQtAction->setStatusTip(tr("Show information about Qt")); aboutQtAction->setMenuRole(QAction::AboutQtRole); optionsAction = new QAction(platformStyle->TextColorIcon(":/icons/options"), tr("&Options..."), this); optionsAction->setStatusTip(tr("Modify configuration options for %1").arg(tr(PACKAGE_NAME))); optionsAction->setMenuRole(QAction::PreferencesRole); + optionsAction->setEnabled(false); toggleHideAction = new QAction(platformStyle->TextColorIcon(":/icons/about"), tr("&Show / Hide"), this); toggleHideAction->setStatusTip(tr("Show or hide the main Window")); @@ -343,6 +345,8 @@ void BitcoinGUI::createActions() openRPCConsoleAction = new QAction(platformStyle->TextColorIcon(":/icons/debugwindow"), tr("&Debug window"), this); openRPCConsoleAction->setStatusTip(tr("Open debugging and diagnostic console")); + // initially disable the debug window menu item + openRPCConsoleAction->setEnabled(false); usedSendingAddressesAction = new QAction(platformStyle->TextColorIcon(":/icons/address-book"), tr("&Sending addresses..."), this); usedSendingAddressesAction->setStatusTip(tr("Show the list of used sending addresses and labels")); @@ -457,8 +461,8 @@ void BitcoinGUI::setClientModel(ClientModel *clientModel) setNumConnections(clientModel->getNumConnections()); connect(clientModel, SIGNAL(numConnectionsChanged(int)), this, SLOT(setNumConnections(int))); - setNumBlocks(clientModel->getNumBlocks(), clientModel->getLastBlockDate(), clientModel->getVerificationProgress(NULL)); - connect(clientModel, SIGNAL(numBlocksChanged(int,QDateTime,double)), this, SLOT(setNumBlocks(int,QDateTime,double))); + setNumBlocks(clientModel->getNumBlocks(), clientModel->getLastBlockDate(), clientModel->getVerificationProgress(NULL), false); + connect(clientModel, SIGNAL(numBlocksChanged(int,QDateTime,double,bool)), this, SLOT(setNumBlocks(int,QDateTime,double,bool))); // Receive and report messages from client model connect(clientModel, SIGNAL(message(QString,QString,unsigned int)), this, SLOT(message(QString,QString,unsigned int))); @@ -696,7 +700,7 @@ void BitcoinGUI::setNumConnections(int count) labelConnectionsIcon->setToolTip(tr("%n active connection(s) to Bitcoin network", "", count)); } -void BitcoinGUI::setNumBlocks(int count, const QDateTime& blockDate, double nVerificationProgress) +void BitcoinGUI::setNumBlocks(int count, const QDateTime& blockDate, double nVerificationProgress, bool header) { if(!clientModel) return; @@ -708,15 +712,25 @@ void BitcoinGUI::setNumBlocks(int count, const QDateTime& blockDate, double nVer enum BlockSource blockSource = clientModel->getBlockSource(); switch (blockSource) { case BLOCK_SOURCE_NETWORK: + if (header) { + return; + } progressBarLabel->setText(tr("Synchronizing with network...")); break; case BLOCK_SOURCE_DISK: - progressBarLabel->setText(tr("Importing blocks from disk...")); + if (header) { + progressBarLabel->setText(tr("Indexing blocks on disk...")); + } else { + progressBarLabel->setText(tr("Processing blocks on disk...")); + } break; case BLOCK_SOURCE_REINDEX: progressBarLabel->setText(tr("Reindexing blocks on disk...")); break; case BLOCK_SOURCE_NONE: + if (header) { + return; + } // Case: not Importing, not Reindexing and no network connection progressBarLabel->setText(tr("No block source available...")); break; @@ -901,6 +915,14 @@ void BitcoinGUI::closeEvent(QCloseEvent *event) QMainWindow::closeEvent(event); } +void BitcoinGUI::showEvent(QShowEvent *event) +{ + // enable the debug window when the main window shows up + openRPCConsoleAction->setEnabled(true); + aboutAction->setEnabled(true); + optionsAction->setEnabled(true); +} + #ifdef ENABLE_WALLET void BitcoinGUI::incomingTransaction(const QString& date, int unit, const CAmount& amount, const QString& type, const QString& address, const QString& label) { diff --git a/src/qt/bitcoingui.h b/src/qt/bitcoingui.h index 27ef11c75c..12e7702ed8 100644 --- a/src/qt/bitcoingui.h +++ b/src/qt/bitcoingui.h @@ -72,6 +72,7 @@ public: protected: void changeEvent(QEvent *e); void closeEvent(QCloseEvent *event); + void showEvent(QShowEvent *event); void dragEnterEvent(QDragEnterEvent *event); void dropEvent(QDropEvent *event); bool eventFilter(QObject *object, QEvent *event); @@ -150,7 +151,7 @@ public Q_SLOTS: /** Set number of connections shown in the UI */ void setNumConnections(int count); /** Set number of blocks and last block date shown in the UI */ - void setNumBlocks(int count, const QDateTime& blockDate, double nVerificationProgress); + void setNumBlocks(int count, const QDateTime& blockDate, double nVerificationProgress, bool headers); /** Notify the user of an event from the core network or transaction handling code. @param[in] title the message box / notification title diff --git a/src/qt/bitcoinstrings.cpp b/src/qt/bitcoinstrings.cpp index 41f1d5841a..9e53f19591 100644 --- a/src/qt/bitcoinstrings.cpp +++ b/src/qt/bitcoinstrings.cpp @@ -1,26 +1,35 @@ -// Copyright (c) 2013-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. + #include <QtGlobal> -// Automatically generated by extract_strings.py +// Automatically generated by extract_strings_qt.py #ifdef __GNUC__ #define UNUSED __attribute__((unused)) #else #define UNUSED #endif static const char UNUSED *bitcoin_strings[] = { +QT_TRANSLATE_NOOP("bitcoin-core", "Bitcoin Core"), +QT_TRANSLATE_NOOP("bitcoin-core", "The %s developers"), QT_TRANSLATE_NOOP("bitcoin-core", "" "(1 = keep tx meta data e.g. account owner and payment request information, 2 " "= drop tx meta data)"), QT_TRANSLATE_NOOP("bitcoin-core", "" +"-fallbackfee is set very high! This is the transaction fee you may pay when " +"fee estimates are not available."), +QT_TRANSLATE_NOOP("bitcoin-core", "" "-maxtxfee is set very high! Fees this large could be paid on a single " "transaction."), QT_TRANSLATE_NOOP("bitcoin-core", "" "-paytxfee is set very high! This is the transaction fee you will pay if you " "send a transaction."), QT_TRANSLATE_NOOP("bitcoin-core", "" +"A fee rate (in %s/kB) that will be used when fee estimation has insufficient " +"data (default: %s)"), +QT_TRANSLATE_NOOP("bitcoin-core", "" +"Accept relayed transactions received from whitelisted peers even when not " +"relaying transactions (default: %d)"), +QT_TRANSLATE_NOOP("bitcoin-core", "" "Allow JSON-RPC connections from specified source. Valid for <ip> are a " "single IP (e.g. 1.2.3.4), a network/netmask (e.g. 1.2.3.4/255.255.255.0) or " "a network/CIDR (e.g. 1.2.3.4/24). This option can be specified multiple times"), @@ -35,8 +44,7 @@ QT_TRANSLATE_NOOP("bitcoin-core", "" "notation for IPv6. This option can be specified multiple times (default: " "bind to all interfaces)"), QT_TRANSLATE_NOOP("bitcoin-core", "" -"Cannot obtain a lock on data directory %s. Bitcoin Core is probably already " -"running."), +"Cannot obtain a lock on data directory %s. %s is probably already running."), QT_TRANSLATE_NOOP("bitcoin-core", "" "Create new files with system default permissions, instead of umask 077 (only " "effective with disabled wallet functionality)"), @@ -52,8 +60,8 @@ QT_TRANSLATE_NOOP("bitcoin-core", "" QT_TRANSLATE_NOOP("bitcoin-core", "" "Do not keep transactions in the mempool longer than <n> hours (default: %u)"), QT_TRANSLATE_NOOP("bitcoin-core", "" -"Error reading wallet.dat! All keys read correctly, but transaction data or " -"address book entries might be missing or incorrect."), +"Error reading %s! All keys read correctly, but transaction data or address " +"book entries might be missing or incorrect."), QT_TRANSLATE_NOOP("bitcoin-core", "" "Error: Listening for incoming connections failed (listen returned error %s)"), QT_TRANSLATE_NOOP("bitcoin-core", "" @@ -72,6 +80,9 @@ QT_TRANSLATE_NOOP("bitcoin-core", "" "Fees (in %s/kB) smaller than this are considered zero fee for transaction " "creation (default: %s)"), QT_TRANSLATE_NOOP("bitcoin-core", "" +"Force relay of transactions from whitelisted peers even they violate local " +"relay policy (default: %d)"), +QT_TRANSLATE_NOOP("bitcoin-core", "" "How thorough the block verification of -checkblocks is (0-4, default: %u)"), QT_TRANSLATE_NOOP("bitcoin-core", "" "If <category> is not supplied or if <category> = 1, output all debugging " @@ -86,18 +97,22 @@ QT_TRANSLATE_NOOP("bitcoin-core", "" "Maintain a full transaction index, used by the getrawtransaction rpc call " "(default: %u)"), QT_TRANSLATE_NOOP("bitcoin-core", "" +"Maximum allowed median peer time offset adjustment. Local perspective of " +"time may be influenced by peers forward or backward by this amount. " +"(default: %u seconds)"), +QT_TRANSLATE_NOOP("bitcoin-core", "" "Maximum size of data in data carrier transactions we relay and mine " "(default: %u)"), QT_TRANSLATE_NOOP("bitcoin-core", "" -"Maximum total fees (in %s) to use in a single wallet transaction; setting " -"this too low may abort large transactions (default: %s)"), +"Maximum total fees (in %s) to use in a single wallet transaction or raw " +"transaction; setting this too low may abort large transactions (default: %s)"), QT_TRANSLATE_NOOP("bitcoin-core", "" "Number of seconds to keep misbehaving peers from reconnecting (default: %u)"), QT_TRANSLATE_NOOP("bitcoin-core", "" "Output debugging information (default: %u, supplying <category> is optional)"), QT_TRANSLATE_NOOP("bitcoin-core", "" "Please check that your computer's date and time are correct! If your clock " -"is wrong Bitcoin Core will not work properly."), +"is wrong, %s will not work properly."), QT_TRANSLATE_NOOP("bitcoin-core", "" "Prune configured below the minimum of %d MiB. Please use a higher number."), QT_TRANSLATE_NOOP("bitcoin-core", "" @@ -123,9 +138,6 @@ QT_TRANSLATE_NOOP("bitcoin-core", "" "Set the number of script verification threads (%u to %d, 0 = auto, <0 = " "leave that many cores free, default: %d)"), QT_TRANSLATE_NOOP("bitcoin-core", "" -"Set the number of threads for coin generation if enabled (-1 = all cores, " -"default: %d)"), -QT_TRANSLATE_NOOP("bitcoin-core", "" "Support filtering of blocks and transaction with bloom filters (default: %u)"), QT_TRANSLATE_NOOP("bitcoin-core", "" "The block database contains a block which appears to be from the future. " @@ -148,12 +160,12 @@ QT_TRANSLATE_NOOP("bitcoin-core", "" "Tries to keep outbound traffic under the given target (in MiB per 24h), 0 = " "no limit (default: %d)"), QT_TRANSLATE_NOOP("bitcoin-core", "" -"Unable to bind to %s on this computer. Bitcoin Core is probably already " -"running."), -QT_TRANSLATE_NOOP("bitcoin-core", "" "Unsupported argument -socks found. Setting SOCKS version isn't possible " "anymore, only SOCKS5 proxies are supported."), QT_TRANSLATE_NOOP("bitcoin-core", "" +"Unsupported argument -whitelistalwaysrelay ignored, use -whitelistrelay and/" +"or -whitelistforcerelay."), +QT_TRANSLATE_NOOP("bitcoin-core", "" "Use UPnP to map the listening port (default: 1 when listening and no -proxy)"), QT_TRANSLATE_NOOP("bitcoin-core", "" "Use separate SOCKS5 proxy to reach peers via Tor hidden services (default: " @@ -172,13 +184,16 @@ QT_TRANSLATE_NOOP("bitcoin-core", "" "Warning: The network does not appear to fully agree! Some miners appear to " "be experiencing issues."), QT_TRANSLATE_NOOP("bitcoin-core", "" +"Warning: Unknown block versions being mined! It's possible unknown rules are " +"in effect"), +QT_TRANSLATE_NOOP("bitcoin-core", "" +"Warning: Wallet file corrupt, data salvaged! Original %s saved as %s in %s; " +"if your balance or transactions are incorrect you should restore from a " +"backup."), +QT_TRANSLATE_NOOP("bitcoin-core", "" "Warning: We do not appear to fully agree with our peers! You may need to " "upgrade, or other nodes may need to upgrade."), QT_TRANSLATE_NOOP("bitcoin-core", "" -"Warning: wallet.dat corrupt, data salvaged! Original wallet.dat saved as " -"wallet.{timestamp}.bak in %s; if your balance or transactions are incorrect " -"you should restore from a backup."), -QT_TRANSLATE_NOOP("bitcoin-core", "" "Whitelist peers connecting from the given netmask or IP address. Can be " "specified multiple times."), QT_TRANSLATE_NOOP("bitcoin-core", "" @@ -187,6 +202,9 @@ QT_TRANSLATE_NOOP("bitcoin-core", "" QT_TRANSLATE_NOOP("bitcoin-core", "" "You need to rebuild the database using -reindex to go back to unpruned " "mode. This will redownload the entire blockchain"), +QT_TRANSLATE_NOOP("bitcoin-core", "" +"You need to rebuild the database using -reindex-chainstate to change -txindex"), +QT_TRANSLATE_NOOP("bitcoin-core", "%s corrupt, salvage failed"), QT_TRANSLATE_NOOP("bitcoin-core", "(default: %s)"), QT_TRANSLATE_NOOP("bitcoin-core", "(default: %u)"), QT_TRANSLATE_NOOP("bitcoin-core", "-maxmempool must be at least %d MB"), @@ -194,24 +212,22 @@ QT_TRANSLATE_NOOP("bitcoin-core", "<category> can be:"), QT_TRANSLATE_NOOP("bitcoin-core", "Accept command line and JSON-RPC commands"), QT_TRANSLATE_NOOP("bitcoin-core", "Accept connections from outside (default: 1 if no -proxy or -connect)"), QT_TRANSLATE_NOOP("bitcoin-core", "Accept public REST requests (default: %u)"), -QT_TRANSLATE_NOOP("bitcoin-core", "Activating best chain..."), QT_TRANSLATE_NOOP("bitcoin-core", "Add a node to connect to and attempt to keep the connection open"), QT_TRANSLATE_NOOP("bitcoin-core", "Allow DNS lookups for -addnode, -seednode and -connect"), QT_TRANSLATE_NOOP("bitcoin-core", "Always query for peer addresses via DNS lookup (default: %u)"), -QT_TRANSLATE_NOOP("bitcoin-core", "Always relay transactions received from whitelisted peers (default: %d)"), -QT_TRANSLATE_NOOP("bitcoin-core", "Attempt to recover private keys from a corrupt wallet.dat on startup"), +QT_TRANSLATE_NOOP("bitcoin-core", "Append comment to the user agent string"), +QT_TRANSLATE_NOOP("bitcoin-core", "Attempt to recover private keys from a corrupt wallet on startup"), QT_TRANSLATE_NOOP("bitcoin-core", "Automatically create Tor hidden service (default: %d)"), QT_TRANSLATE_NOOP("bitcoin-core", "Block creation options:"), QT_TRANSLATE_NOOP("bitcoin-core", "Cannot downgrade wallet"), -QT_TRANSLATE_NOOP("bitcoin-core", "Cannot resolve -bind address: '%s'"), -QT_TRANSLATE_NOOP("bitcoin-core", "Cannot resolve -externalip address: '%s'"), -QT_TRANSLATE_NOOP("bitcoin-core", "Cannot resolve -whitebind address: '%s'"), +QT_TRANSLATE_NOOP("bitcoin-core", "Cannot resolve -%s address: '%s'"), QT_TRANSLATE_NOOP("bitcoin-core", "Cannot write default address"), +QT_TRANSLATE_NOOP("bitcoin-core", "Change index out of range"), QT_TRANSLATE_NOOP("bitcoin-core", "Connect only to the specified node(s)"), QT_TRANSLATE_NOOP("bitcoin-core", "Connect through SOCKS5 proxy"), QT_TRANSLATE_NOOP("bitcoin-core", "Connect to a node to retrieve peer addresses, and disconnect"), QT_TRANSLATE_NOOP("bitcoin-core", "Connection options:"), -QT_TRANSLATE_NOOP("bitcoin-core", "Copyright (C) 2009-%i The Bitcoin Core Developers"), +QT_TRANSLATE_NOOP("bitcoin-core", "Copyright (C) %i-%i"), QT_TRANSLATE_NOOP("bitcoin-core", "Corrupted block database detected"), QT_TRANSLATE_NOOP("bitcoin-core", "Debugging/Testing options:"), QT_TRANSLATE_NOOP("bitcoin-core", "Do not load the wallet and disable wallet RPC calls"), @@ -221,12 +237,13 @@ QT_TRANSLATE_NOOP("bitcoin-core", "Enable publish hash block in <address>"), QT_TRANSLATE_NOOP("bitcoin-core", "Enable publish hash transaction in <address>"), QT_TRANSLATE_NOOP("bitcoin-core", "Enable publish raw block in <address>"), QT_TRANSLATE_NOOP("bitcoin-core", "Enable publish raw transaction in <address>"), +QT_TRANSLATE_NOOP("bitcoin-core", "Enable transaction replacement in the memory pool (default: %u)"), QT_TRANSLATE_NOOP("bitcoin-core", "Error initializing block database"), QT_TRANSLATE_NOOP("bitcoin-core", "Error initializing wallet database environment %s!"), +QT_TRANSLATE_NOOP("bitcoin-core", "Error loading %s"), +QT_TRANSLATE_NOOP("bitcoin-core", "Error loading %s: Wallet corrupted"), +QT_TRANSLATE_NOOP("bitcoin-core", "Error loading %s: Wallet requires newer version of %s"), 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 Core"), QT_TRANSLATE_NOOP("bitcoin-core", "Error opening block database"), QT_TRANSLATE_NOOP("bitcoin-core", "Error reading from database, shutting down."), QT_TRANSLATE_NOOP("bitcoin-core", "Error"), @@ -234,34 +251,34 @@ QT_TRANSLATE_NOOP("bitcoin-core", "Error: A fatal internal error occurred, see d QT_TRANSLATE_NOOP("bitcoin-core", "Error: Disk space is low!"), QT_TRANSLATE_NOOP("bitcoin-core", "Failed to listen on any port. Use -listen=0 if you want this."), QT_TRANSLATE_NOOP("bitcoin-core", "Fee (in %s/kB) to add to transactions you send (default: %s)"), -QT_TRANSLATE_NOOP("bitcoin-core", "Generate coins (default: %u)"), QT_TRANSLATE_NOOP("bitcoin-core", "How many blocks to check at startup (default: %u, 0 = all)"), QT_TRANSLATE_NOOP("bitcoin-core", "Importing..."), QT_TRANSLATE_NOOP("bitcoin-core", "Imports blocks from external blk000??.dat file on startup"), QT_TRANSLATE_NOOP("bitcoin-core", "Include IP addresses in debug output (default: %u)"), QT_TRANSLATE_NOOP("bitcoin-core", "Incorrect or no genesis block found. Wrong datadir for network?"), QT_TRANSLATE_NOOP("bitcoin-core", "Information"), -QT_TRANSLATE_NOOP("bitcoin-core", "Initialization sanity check failed. Bitcoin Core is shutting down."), +QT_TRANSLATE_NOOP("bitcoin-core", "Initialization sanity check failed. %s is shutting down."), QT_TRANSLATE_NOOP("bitcoin-core", "Insufficient funds"), QT_TRANSLATE_NOOP("bitcoin-core", "Invalid -onion address: '%s'"), QT_TRANSLATE_NOOP("bitcoin-core", "Invalid -proxy address: '%s'"), -QT_TRANSLATE_NOOP("bitcoin-core", "Invalid amount for -maxtxfee=<amount>: '%s'"), -QT_TRANSLATE_NOOP("bitcoin-core", "Invalid amount for -minrelaytxfee=<amount>: '%s'"), -QT_TRANSLATE_NOOP("bitcoin-core", "Invalid amount for -mintxfee=<amount>: '%s'"), +QT_TRANSLATE_NOOP("bitcoin-core", "Invalid amount for -%s=<amount>: '%s'"), +QT_TRANSLATE_NOOP("bitcoin-core", "Invalid amount for -fallbackfee=<amount>: '%s'"), QT_TRANSLATE_NOOP("bitcoin-core", "Invalid amount for -paytxfee=<amount>: '%s' (must be at least %s)"), -QT_TRANSLATE_NOOP("bitcoin-core", "Invalid amount for -paytxfee=<amount>: '%s'"), QT_TRANSLATE_NOOP("bitcoin-core", "Invalid netmask specified in -whitelist: '%s'"), QT_TRANSLATE_NOOP("bitcoin-core", "Keep at most <n> unconnectable transactions in memory (default: %u)"), QT_TRANSLATE_NOOP("bitcoin-core", "Keep the transaction memory pool below <n> megabytes (default: %u)"), QT_TRANSLATE_NOOP("bitcoin-core", "Listen for JSON-RPC connections on <port> (default: %u or testnet: %u)"), QT_TRANSLATE_NOOP("bitcoin-core", "Listen for connections on <port> (default: %u or testnet: %u)"), QT_TRANSLATE_NOOP("bitcoin-core", "Loading addresses..."), +QT_TRANSLATE_NOOP("bitcoin-core", "Loading banlist..."), QT_TRANSLATE_NOOP("bitcoin-core", "Loading block index..."), QT_TRANSLATE_NOOP("bitcoin-core", "Loading wallet..."), +QT_TRANSLATE_NOOP("bitcoin-core", "Location of the auth cookie (default: data dir)"), QT_TRANSLATE_NOOP("bitcoin-core", "Maintain at most <n> connections to peers (default: %u)"), QT_TRANSLATE_NOOP("bitcoin-core", "Make the wallet broadcast transactions"), QT_TRANSLATE_NOOP("bitcoin-core", "Maximum per-connection receive buffer, <n>*1000 bytes (default: %u)"), QT_TRANSLATE_NOOP("bitcoin-core", "Maximum per-connection send buffer, <n>*1000 bytes (default: %u)"), +QT_TRANSLATE_NOOP("bitcoin-core", "Minimum bytes per sigop in transactions we relay and mine (default: %u)"), QT_TRANSLATE_NOOP("bitcoin-core", "Need to specify a port with -whitebind: '%s'"), QT_TRANSLATE_NOOP("bitcoin-core", "Node relay options:"), QT_TRANSLATE_NOOP("bitcoin-core", "Not enough file descriptors available."), @@ -269,12 +286,14 @@ QT_TRANSLATE_NOOP("bitcoin-core", "Only connect to nodes in network <net> (ipv4, QT_TRANSLATE_NOOP("bitcoin-core", "Options:"), QT_TRANSLATE_NOOP("bitcoin-core", "Password for JSON-RPC connections"), QT_TRANSLATE_NOOP("bitcoin-core", "Prepend debug output with timestamp (default: %u)"), +QT_TRANSLATE_NOOP("bitcoin-core", "Print this help message and exit"), +QT_TRANSLATE_NOOP("bitcoin-core", "Print version and exit"), QT_TRANSLATE_NOOP("bitcoin-core", "Prune cannot be configured with a negative value."), QT_TRANSLATE_NOOP("bitcoin-core", "Prune mode is incompatible with -txindex."), QT_TRANSLATE_NOOP("bitcoin-core", "Pruning blockstore..."), QT_TRANSLATE_NOOP("bitcoin-core", "RPC server options:"), -QT_TRANSLATE_NOOP("bitcoin-core", "Rebuild block chain index from current blk000??.dat files on startup"), -QT_TRANSLATE_NOOP("bitcoin-core", "Receive and display P2P network alerts (default: %u)"), +QT_TRANSLATE_NOOP("bitcoin-core", "Rebuild chain state and block index from the blk*.dat files on disk"), +QT_TRANSLATE_NOOP("bitcoin-core", "Rebuild chain state from the currently indexed blocks"), QT_TRANSLATE_NOOP("bitcoin-core", "Reducing -maxconnections from %d to %d, because of system limitations."), QT_TRANSLATE_NOOP("bitcoin-core", "Relay and mine data carrier transactions (default: %u)"), QT_TRANSLATE_NOOP("bitcoin-core", "Relay non-P2SH multisig (default: %u)"), @@ -299,7 +318,6 @@ QT_TRANSLATE_NOOP("bitcoin-core", "Specify wallet file (within data directory)") QT_TRANSLATE_NOOP("bitcoin-core", "Specify your own public address"), QT_TRANSLATE_NOOP("bitcoin-core", "Spend unconfirmed change when sending transactions (default: %u)"), QT_TRANSLATE_NOOP("bitcoin-core", "The transaction amount is too small to pay the fee"), -QT_TRANSLATE_NOOP("bitcoin-core", "This help message"), QT_TRANSLATE_NOOP("bitcoin-core", "This is experimental software."), QT_TRANSLATE_NOOP("bitcoin-core", "Threshold for disconnecting misbehaving peers (default: %u)"), QT_TRANSLATE_NOOP("bitcoin-core", "Tor control port password (default: empty)"), @@ -309,6 +327,7 @@ QT_TRANSLATE_NOOP("bitcoin-core", "Transaction amounts must be positive"), QT_TRANSLATE_NOOP("bitcoin-core", "Transaction too large for fee policy"), QT_TRANSLATE_NOOP("bitcoin-core", "Transaction too large"), QT_TRANSLATE_NOOP("bitcoin-core", "Unable to bind to %s on this computer (bind returned error %s)"), +QT_TRANSLATE_NOOP("bitcoin-core", "Unable to bind to %s on this computer. %s is probably already running."), QT_TRANSLATE_NOOP("bitcoin-core", "Unable to start HTTP server. See debug log for details."), QT_TRANSLATE_NOOP("bitcoin-core", "Unknown network specified in -onlynet: '%s'"), QT_TRANSLATE_NOOP("bitcoin-core", "Unsupported argument -benchmark ignored, use -debug=bench."), @@ -321,13 +340,12 @@ QT_TRANSLATE_NOOP("bitcoin-core", "Username for JSON-RPC connections"), QT_TRANSLATE_NOOP("bitcoin-core", "Verifying blocks..."), QT_TRANSLATE_NOOP("bitcoin-core", "Verifying wallet..."), QT_TRANSLATE_NOOP("bitcoin-core", "Wallet %s resides outside data directory %s"), -QT_TRANSLATE_NOOP("bitcoin-core", "Wallet needed to be rewritten: restart Bitcoin Core to complete"), +QT_TRANSLATE_NOOP("bitcoin-core", "Wallet debugging/testing options:"), +QT_TRANSLATE_NOOP("bitcoin-core", "Wallet needed to be rewritten: restart %s to complete"), QT_TRANSLATE_NOOP("bitcoin-core", "Wallet options:"), QT_TRANSLATE_NOOP("bitcoin-core", "Warning"), -QT_TRANSLATE_NOOP("bitcoin-core", "Warning: This version is obsolete; upgrade required!"), +QT_TRANSLATE_NOOP("bitcoin-core", "Warning: unknown new rules activated (versionbit %i)"), QT_TRANSLATE_NOOP("bitcoin-core", "Whether to operate in a blocks only mode (default: %u)"), -QT_TRANSLATE_NOOP("bitcoin-core", "You need to rebuild the database using -reindex to change -txindex"), QT_TRANSLATE_NOOP("bitcoin-core", "Zapping all transactions from wallet..."), QT_TRANSLATE_NOOP("bitcoin-core", "ZeroMQ notification options:"), -QT_TRANSLATE_NOOP("bitcoin-core", "wallet.dat corrupt, salvage failed"), }; diff --git a/src/qt/clientmodel.cpp b/src/qt/clientmodel.cpp index 697736cc88..108500654b 100644 --- a/src/qt/clientmodel.cpp +++ b/src/qt/clientmodel.cpp @@ -24,6 +24,7 @@ class CBlockIndex; static const int64_t nClientStartupTime = GetTime(); +static int64_t nLastHeaderTipUpdateNotification = 0; static int64_t nLastBlockTipUpdateNotification = 0; ClientModel::ClientModel(OptionsModel *optionsModel, QObject *parent) : @@ -226,7 +227,7 @@ static void BannedListChanged(ClientModel *clientmodel) QMetaObject::invokeMethod(clientmodel, "updateBanlist", Qt::QueuedConnection); } -static void BlockTipChanged(ClientModel *clientmodel, bool initialSync, const CBlockIndex *pIndex) +static void BlockTipChanged(ClientModel *clientmodel, bool initialSync, const CBlockIndex *pIndex, bool fHeader) { // lock free async UI updates in case we have a new block tip // during initial sync, only update the UI if the last update @@ -235,14 +236,17 @@ static void BlockTipChanged(ClientModel *clientmodel, bool initialSync, const CB if (initialSync) now = GetTimeMillis(); + int64_t& nLastUpdateNotification = fHeader ? nLastHeaderTipUpdateNotification : nLastBlockTipUpdateNotification; + // if we are in-sync, update the UI regardless of last update time - if (!initialSync || now - nLastBlockTipUpdateNotification > MODEL_UPDATE_DELAY) { + if (!initialSync || now - nLastUpdateNotification > MODEL_UPDATE_DELAY) { //pass a async signal to the UI thread QMetaObject::invokeMethod(clientmodel, "numBlocksChanged", Qt::QueuedConnection, Q_ARG(int, pIndex->nHeight), Q_ARG(QDateTime, QDateTime::fromTime_t(pIndex->GetBlockTime())), - Q_ARG(double, clientmodel->getVerificationProgress(pIndex))); - nLastBlockTipUpdateNotification = now; + Q_ARG(double, clientmodel->getVerificationProgress(pIndex)), + Q_ARG(bool, fHeader)); + nLastUpdateNotification = now; } } @@ -253,7 +257,8 @@ void ClientModel::subscribeToCoreSignals() uiInterface.NotifyNumConnectionsChanged.connect(boost::bind(NotifyNumConnectionsChanged, this, _1)); uiInterface.NotifyAlertChanged.connect(boost::bind(NotifyAlertChanged, this)); uiInterface.BannedListChanged.connect(boost::bind(BannedListChanged, this)); - uiInterface.NotifyBlockTip.connect(boost::bind(BlockTipChanged, this, _1, _2)); + uiInterface.NotifyBlockTip.connect(boost::bind(BlockTipChanged, this, _1, _2, false)); + uiInterface.NotifyHeaderTip.connect(boost::bind(BlockTipChanged, this, _1, _2, true)); } void ClientModel::unsubscribeFromCoreSignals() @@ -263,5 +268,6 @@ void ClientModel::unsubscribeFromCoreSignals() uiInterface.NotifyNumConnectionsChanged.disconnect(boost::bind(NotifyNumConnectionsChanged, this, _1)); uiInterface.NotifyAlertChanged.disconnect(boost::bind(NotifyAlertChanged, this)); uiInterface.BannedListChanged.disconnect(boost::bind(BannedListChanged, this)); - uiInterface.NotifyBlockTip.disconnect(boost::bind(BlockTipChanged, this, _1, _2)); + uiInterface.NotifyBlockTip.disconnect(boost::bind(BlockTipChanged, this, _1, _2, false)); + uiInterface.NotifyHeaderTip.disconnect(boost::bind(BlockTipChanged, this, _1, _2, true)); } diff --git a/src/qt/clientmodel.h b/src/qt/clientmodel.h index 109f95a2a7..4396804319 100644 --- a/src/qt/clientmodel.h +++ b/src/qt/clientmodel.h @@ -89,7 +89,7 @@ private: Q_SIGNALS: void numConnectionsChanged(int count); - void numBlocksChanged(int count, const QDateTime& blockDate, double nVerificationProgress); + void numBlocksChanged(int count, const QDateTime& blockDate, double nVerificationProgress, bool header); void mempoolSizeChanged(long count, size_t mempoolSizeInBytes); void alertsChanged(const QString &warnings); void bytesChanged(quint64 totalBytesIn, quint64 totalBytesOut); diff --git a/src/qt/forms/receiverequestdialog.ui b/src/qt/forms/receiverequestdialog.ui index 1e484dd9a0..4163f4189c 100644 --- a/src/qt/forms/receiverequestdialog.ui +++ b/src/qt/forms/receiverequestdialog.ui @@ -22,7 +22,7 @@ <property name="minimumSize"> <size> <width>300</width> - <height>300</height> + <height>320</height> </size> </property> <property name="toolTip"> diff --git a/src/qt/guiconstants.h b/src/qt/guiconstants.h index 4b2c10dd48..bab9923d20 100644 --- a/src/qt/guiconstants.h +++ b/src/qt/guiconstants.h @@ -43,7 +43,7 @@ static const int TOOLTIP_WRAP_THRESHOLD = 80; static const int MAX_URI_LENGTH = 255; /* QRCodeDialog -- size of exported QR Code image */ -#define EXPORT_IMAGE_SIZE 256 +#define QR_IMAGE_SIZE 300 /* Number of frames in spinner animation */ #define SPINNER_FRAMES 36 diff --git a/src/qt/locale/bitcoin_en.ts b/src/qt/locale/bitcoin_en.ts index 00411741f1..b90221f2c2 100644 --- a/src/qt/locale/bitcoin_en.ts +++ b/src/qt/locale/bitcoin_en.ts @@ -165,7 +165,7 @@ <translation>Repeat new passphrase</translation> </message> <message> - <location filename="../askpassphrasedialog.cpp" line="+45"/> + <location filename="../askpassphrasedialog.cpp" line="+49"/> <source>Encrypt wallet</source> <translation>Encrypt wallet</translation> </message> @@ -195,7 +195,7 @@ <translation>Change passphrase</translation> </message> <message> - <location line="+46"/> + <location line="+45"/> <source>Confirm wallet encryption</source> <translation>Confirm wallet encryption</translation> </message> @@ -210,12 +210,7 @@ <translation>Are you sure you wish to encrypt your wallet?</translation> </message> <message> - <location line="+11"/> - <source>Bitcoin Core will close now to finish the encryption process. Remember that encrypting your wallet cannot fully protect your bitcoins from being stolen by malware infecting your computer.</source> - <translation type="unfinished"></translation> - </message> - <message> - <location line="+4"/> + <location line="+15"/> <source>IMPORTANT: Any previous backups you have made of your wallet file should be replaced with the newly generated, encrypted wallet file. For security reasons, previous backups of the unencrypted wallet file will become useless as soon as you start using the new, encrypted wallet.</source> <translation>IMPORTANT: Any previous backups you have made of your wallet file should be replaced with the newly generated, encrypted wallet file. For security reasons, previous backups of the unencrypted wallet file will become useless as soon as you start using the new, encrypted wallet.</translation> </message> @@ -232,7 +227,7 @@ <translation>Wallet encrypted</translation> </message> <message> - <location line="-136"/> + <location line="-135"/> <source>Enter the new passphrase to the wallet.<br/>Please use a passphrase of <b>ten or more random characters</b>, or <b>eight or more words</b>.</source> <translation type="unfinished"></translation> </message> @@ -242,7 +237,12 @@ <translation type="unfinished"></translation> </message> <message> - <location line="+70"/> + <location line="+56"/> + <source>%1 will close now to finish the encryption process. Remember that encrypting your wallet cannot fully protect your bitcoins from being stolen by malware infecting your computer.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+13"/> <location line="+7"/> <location line="+42"/> <location line="+6"/> @@ -299,17 +299,17 @@ <context> <name>BitcoinGUI</name> <message> - <location filename="../bitcoingui.cpp" line="+335"/> + <location filename="../bitcoingui.cpp" line="+341"/> <source>Sign &message...</source> <translation>Sign &message...</translation> </message> <message> - <location line="+362"/> + <location line="+377"/> <source>Synchronizing with network...</source> <translation>Synchronizing with network...</translation> </message> <message> - <location line="-438"/> + <location line="-455"/> <source>&Overview</source> <translation>&Overview</translation> </message> @@ -344,7 +344,17 @@ <translation>Quit application</translation> </message> <message> - <location line="+6"/> + <location line="+3"/> + <source>&About %1</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+1"/> + <source>Show information about %1</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+3"/> <source>About &Qt</source> <translation>About &Qt</translation> </message> @@ -359,6 +369,11 @@ <translation>&Options...</translation> </message> <message> + <location line="+1"/> + <source>Modify configuration options for %1</source> + <translation type="unfinished"></translation> + </message> + <message> <location line="+6"/> <source>&Encrypt Wallet...</source> <translation>&Encrypt Wallet...</translation> @@ -374,7 +389,7 @@ <translation>&Change Passphrase...</translation> </message> <message> - <location line="+10"/> + <location line="+12"/> <source>&Sending addresses...</source> <translation type="unfinished"></translation> </message> @@ -389,27 +404,17 @@ <translation type="unfinished"></translation> </message> <message> - <location line="+183"/> - <source>Bitcoin Core client</source> - <translation type="unfinished"></translation> - </message> - <message> - <location line="+169"/> - <source>Importing blocks from disk...</source> - <translation>Importing blocks from disk...</translation> - </message> - <message> - <location line="+3"/> + <location line="+372"/> <source>Reindexing blocks on disk...</source> <translation>Reindexing blocks on disk...</translation> </message> <message> - <location line="-436"/> + <location line="-457"/> <source>Send coins to a Bitcoin address</source> <translation>Send coins to a Bitcoin address</translation> </message> <message> - <location line="+65"/> + <location line="+67"/> <source>Backup wallet to another location</source> <translation>Backup wallet to another location</translation> </message> @@ -434,12 +439,12 @@ <translation>&Verify message...</translation> </message> <message> - <location line="+459"/> + <location line="+481"/> <source>Bitcoin</source> <translation>Bitcoin</translation> </message> <message> - <location line="-669"/> + <location line="-693"/> <source>Wallet</source> <translation>Wallet</translation> </message> @@ -454,12 +459,7 @@ <translation>&Receive</translation> </message> <message> - <location line="+40"/> - <source>Show information about Bitcoin Core</source> - <translation type="unfinished"></translation> - </message> - <message> - <location line="+8"/> + <location line="+50"/> <source>&Show / Hide</source> <translation>&Show / Hide</translation> </message> @@ -484,7 +484,7 @@ <translation>Verify messages to ensure they were signed with specified Bitcoin addresses</translation> </message> <message> - <location line="+56"/> + <location line="+58"/> <source>&File</source> <translation>&File</translation> </message> @@ -504,27 +504,12 @@ <translation>Tabs toolbar</translation> </message> <message> - <location line="-314"/> - <source>Bitcoin Core</source> - <translation type="unfinished">Bitcoin Core</translation> - </message> - <message> - <location line="+160"/> + <location line="-158"/> <source>Request payments (generates QR codes and bitcoin: URIs)</source> <translation type="unfinished"></translation> </message> <message> - <location line="+38"/> - <source>&About Bitcoin Core</source> - <translation type="unfinished"></translation> - </message> - <message> - <location line="+7"/> - <source>Modify configuration options for Bitcoin Core</source> - <translation type="unfinished"></translation> - </message> - <message> - <location line="+21"/> + <location line="+70"/> <source>Show the list of used sending addresses and labels</source> <translation type="unfinished"></translation> </message> @@ -543,13 +528,8 @@ <source>&Command-line options</source> <translation type="unfinished"></translation> </message> - <message> - <location line="+2"/> - <source>Show the Bitcoin Core help message to get a list with possible Bitcoin command-line options</source> - <translation type="unfinished"></translation> - </message> <message numerus="yes"> - <location line="+329"/> + <location line="+341"/> <source>%n active connection(s) to Bitcoin network</source> <translation> <numerusform>%n active connection to Bitcoin network</numerusform> @@ -557,7 +537,17 @@ </translation> </message> <message> - <location line="+25"/> + <location line="+22"/> + <source>Indexing blocks on disk...</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+2"/> + <source>Processing blocks on disk...</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+11"/> <source>No block source available...</source> <translation>No block source available...</translation> </message> @@ -643,12 +633,22 @@ <translation>Up to date</translation> </message> <message> - <location line="+44"/> + <location line="-388"/> + <source>Show the %1 help message to get a list with possible Bitcoin command-line options</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+188"/> + <source>%1 client</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+244"/> <source>Catching up...</source> <translation>Catching up...</translation> </message> <message> - <location line="+129"/> + <location line="+137"/> <source>Date: %1 </source> <translation type="unfinished"></translation> @@ -699,14 +699,6 @@ </message> </context> <context> - <name>ClientModel</name> - <message> - <location filename="../clientmodel.cpp" line="+135"/> - <source>Network Alert</source> - <translation>Network Alert</translation> - </message> -</context> -<context> <name>CoinControlDialog</name> <message> <location filename="../forms/coincontroldialog.ui" line="+14"/> @@ -870,7 +862,7 @@ <translation type="unfinished"></translation> </message> <message> - <location line="+351"/> + <location line="+349"/> <source>highest</source> <translation type="unfinished"></translation> </message> @@ -935,12 +927,7 @@ <translation type="unfinished"></translation> </message> <message> - <location line="+3"/> - <source>This label turns red if any recipient receives an amount smaller than %1.</source> - <translation type="unfinished"></translation> - </message> - <message> - <location line="+9"/> + <location line="+12"/> <source>Can vary +/- %1 satoshi(s) per input.</source> <translation type="unfinished"></translation> </message> @@ -971,7 +958,12 @@ <translation type="unfinished"></translation> </message> <message> - <location line="+59"/> + <location line="+4"/> + <source>This label turns red if any recipient receives an amount smaller than the current dust threshold.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+55"/> <location line="+60"/> <source>(no label)</source> <translation type="unfinished">(no label)</translation> @@ -1058,7 +1050,7 @@ <context> <name>FreespaceChecker</name> <message> - <location filename="../intro.cpp" line="+68"/> + <location filename="../intro.cpp" line="+78"/> <source>A new data directory will be created.</source> <translation>A new data directory will be created.</translation> </message> @@ -1086,12 +1078,7 @@ <context> <name>HelpMessageDialog</name> <message> - <location filename="../utilitydialog.cpp" line="+36"/> - <source>Bitcoin Core</source> - <translation type="unfinished">Bitcoin Core</translation> - </message> - <message> - <location line="+0"/> + <location filename="../utilitydialog.cpp" line="+40"/> <source>version</source> <translation type="unfinished">version</translation> </message> @@ -1103,7 +1090,7 @@ </message> <message> <location line="+5"/> - <source>About Bitcoin Core</source> + <source>About %1</source> <translation type="unfinished"></translation> </message> <message> @@ -1166,17 +1153,17 @@ </message> <message> <location line="+9"/> - <source>Welcome to Bitcoin Core.</source> + <source>Welcome to %1.</source> <translation type="unfinished"></translation> </message> <message> <location line="+26"/> - <source>As this is the first time the program is launched, you can choose where Bitcoin Core will store its data.</source> + <source>As this is the first time the program is launched, you can choose where %1 will store its data.</source> <translation type="unfinished"></translation> </message> <message> <location line="+10"/> - <source>Bitcoin Core will download and store a copy of the Bitcoin block chain. At least %1GB of data will be stored in this directory, and it will grow over time. The wallet will also be stored in this directory.</source> + <source>%1 will download and store a copy of the Bitcoin block chain. At least %2GB of data will be stored in this directory, and it will grow over time. The wallet will also be stored in this directory.</source> <translation type="unfinished"></translation> </message> <message> @@ -1190,12 +1177,7 @@ <translation>Use a custom data directory:</translation> </message> <message> - <location filename="../intro.cpp" line="+82"/> - <source>Bitcoin Core</source> - <translation type="unfinished">Bitcoin Core</translation> - </message> - <message> - <location line="+1"/> + <location filename="../intro.cpp" line="+89"/> <source>Error: Specified data directory "%1" cannot be created.</source> <translation type="unfinished"></translation> </message> @@ -1262,7 +1244,17 @@ <translation>&Main</translation> </message> <message> - <location line="+18"/> + <location line="+6"/> + <source>Automatically start %1 after logging in to the system.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+3"/> + <source>&Start %1 on system login</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+9"/> <source>Size of &database cache</source> <translation type="unfinished"></translation> </message> @@ -1293,17 +1285,12 @@ <translation type="unfinished"></translation> </message> <message> - <location line="+84"/> + <location line="+94"/> <source>Minimize instead of exit the application when the window is closed. When this option is enabled, the application will be closed only after selecting Exit in the menu.</source> <translation type="unfinished"></translation> </message> <message> - <location line="+45"/> - <source>The user interface language can be set here. This setting will take effect after restarting Bitcoin Core.</source> - <translation type="unfinished"></translation> - </message> - <message> - <location line="+35"/> + <location line="+80"/> <location line="+13"/> <source>Third party URLs (e.g. a block explorer) that appear in the transactions tab as context menu items. %s in the URL is replaced by transaction hash. Multiple URLs are separated by vertical bar |.</source> <translation type="unfinished"></translation> @@ -1329,22 +1316,12 @@ <translation>&Reset Options</translation> </message> <message> - <location line="-504"/> + <location line="-514"/> <source>&Network</source> <translation>&Network</translation> </message> <message> - <location line="-153"/> - <source>Automatically start Bitcoin Core after logging in to the system.</source> - <translation type="unfinished"></translation> - </message> - <message> - <location line="+3"/> - <source>&Start Bitcoin Core on system login</source> - <translation type="unfinished"></translation> - </message> - <message> - <location line="+65"/> + <location line="-85"/> <source>(0 = auto, <0 = leave that many cores free)</source> <translation type="unfinished"></translation> </message> @@ -1455,6 +1432,16 @@ </message> <message> <location line="+6"/> + <source>&Hide the icon from the system tray.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+3"/> + <source>Hide tray icon</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+7"/> <source>Show only a tray icon after minimizing the window.</source> <translation>Show only a tray icon after minimizing the window.</translation> </message> @@ -1479,7 +1466,12 @@ <translation>User Interface &language:</translation> </message> <message> - <location line="+24"/> + <location line="+13"/> + <source>The user interface language can be set here. This setting will take effect after restarting %1.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+11"/> <source>&Unit to show amounts in:</source> <translation>&Unit to show amounts in:</translation> </message> @@ -1489,12 +1481,12 @@ <translation>Choose the default subdivision unit to show in the interface and when sending coins.</translation> </message> <message> - <location line="-440"/> + <location line="-450"/> <source>Whether to show coin control features or not.</source> <translation type="unfinished"></translation> </message> <message> - <location line="+602"/> + <location line="+612"/> <source>&OK</source> <translation>&OK</translation> </message> @@ -1504,7 +1496,7 @@ <translation>&Cancel</translation> </message> <message> - <location filename="../optionsdialog.cpp" line="+81"/> + <location filename="../optionsdialog.cpp" line="+86"/> <source>default</source> <translation>default</translation> </message> @@ -1514,23 +1506,23 @@ <translation type="unfinished"></translation> </message> <message> - <location line="+71"/> + <location line="+72"/> <source>Confirm options reset</source> <translation>Confirm options reset</translation> </message> <message> <location line="+1"/> - <location line="+30"/> + <location line="+43"/> <source>Client restart required to activate changes.</source> <translation type="unfinished"></translation> </message> <message> - <location line="-30"/> + <location line="-43"/> <source>Client will be shut down. Do you want to proceed?</source> <translation type="unfinished"></translation> </message> <message> - <location line="+34"/> + <location line="+47"/> <source>This change would require a client restart.</source> <translation type="unfinished"></translation> </message> @@ -1781,12 +1773,12 @@ <context> <name>QObject</name> <message> - <location filename="../bitcoinunits.cpp" line="+183"/> + <location filename="../bitcoinunits.cpp" line="+176"/> <source>Amount</source> <translation type="unfinished">Amount</translation> </message> <message> - <location filename="../guiutil.cpp" line="+110"/> + <location filename="../guiutil.cpp" line="+118"/> <source>Enter a Bitcoin address (e.g. %1)</source> <translation type="unfinished"></translation> </message> @@ -1807,7 +1799,7 @@ </message> <message> <location line="+2"/> - <location line="+41"/> + <location line="+44"/> <source>%1 s</source> <translation type="unfinished"></translation> </message> @@ -1862,16 +1854,15 @@ <location line="+23"/> <location line="+26"/> <location line="+26"/> - <location line="+26"/> - <location line="+23"/> - <location line="+23"/> - <location line="+23"/> <location line="+23"/> + <location line="+26"/> <location line="+36"/> <location line="+23"/> <location line="+36"/> + <location line="+23"/> <location line="+36"/> - <location line="+534"/> + <location line="+23"/> + <location line="+663"/> <location line="+23"/> <location line="+23"/> <location line="+23"/> @@ -1892,7 +1883,7 @@ <translation>N/A</translation> </message> <message> - <location line="-1216"/> + <location line="-1322"/> <source>Client version</source> <translation>Client version</translation> </message> @@ -1913,26 +1904,26 @@ </message> <message> <location line="+79"/> - <source>Using OpenSSL version</source> - <translation>Using OpenSSL version</translation> + <source>Using BerkeleyDB version</source> + <translation type="unfinished"></translation> </message> <message> <location line="+26"/> - <source>Using BerkeleyDB version</source> + <source>Datadir</source> <translation type="unfinished"></translation> </message> <message> - <location line="+49"/> + <location line="+26"/> <source>Startup time</source> <translation>Startup time</translation> </message> <message> - <location line="+170"/> + <location line="+29"/> <source>Network</source> <translation>Network</translation> </message> <message> - <location line="-147"/> + <location line="+7"/> <source>Name</source> <translation type="unfinished"></translation> </message> @@ -1962,41 +1953,36 @@ <translation type="unfinished"></translation> </message> <message> - <location line="+36"/> + <location line="+23"/> <source>Memory usage</source> <translation type="unfinished"></translation> </message> <message> - <location line="+48"/> - <source>Open the Bitcoin Core debug log file from the current data directory. This can take a few seconds for large log files.</source> - <translation type="unfinished"></translation> - </message> - <message> - <location line="+233"/> - <location line="+552"/> + <location line="+404"/> + <location line="+558"/> <source>Received</source> <translation type="unfinished"></translation> </message> <message> - <location line="-472"/> - <location line="+449"/> + <location line="-478"/> + <location line="+455"/> <source>Sent</source> <translation type="unfinished"></translation> </message> <message> - <location line="-408"/> + <location line="-414"/> <source>&Peers</source> <translation type="unfinished"></translation> </message> <message> - <location line="+50"/> + <location line="+53"/> <source>Banned peers</source> <translation type="unfinished"></translation> </message> <message> - <location line="+57"/> - <location filename="../rpcconsole.cpp" line="+287"/> - <location line="+578"/> + <location line="+60"/> + <location filename="../rpcconsole.cpp" line="+295"/> + <location line="+635"/> <source>Select a peer to view detailed information.</source> <translation type="unfinished"></translation> </message> @@ -2031,13 +2017,28 @@ <translation type="unfinished"></translation> </message> <message> - <location line="-973"/> - <location line="+881"/> + <location line="-1079"/> + <location line="+987"/> <source>User Agent</source> <translation type="unfinished"></translation> </message> <message> - <location line="+23"/> + <location line="-684"/> + <source>Open the %1 debug log file from the current data directory. This can take a few seconds for large log files.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+68"/> + <source>Decrease font size</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+29"/> + <source>Increase font size</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+610"/> <source>Services</source> <translation type="unfinished"></translation> </message> @@ -2082,12 +2083,12 @@ <translation type="unfinished"></translation> </message> <message> - <location line="-977"/> + <location line="-1093"/> <source>Last block time</source> <translation>Last block time</translation> </message> <message> - <location line="+123"/> + <location line="+110"/> <source>&Open</source> <translation>&Open</translation> </message> @@ -2097,7 +2098,7 @@ <translation>&Console</translation> </message> <message> - <location line="+72"/> + <location line="+195"/> <source>&Network Traffic</source> <translation type="unfinished"></translation> </message> @@ -2112,7 +2113,7 @@ <translation type="unfinished"></translation> </message> <message> - <location filename="../rpcconsole.cpp" line="-343"/> + <location filename="../rpcconsole.cpp" line="-342"/> <source>In:</source> <translation type="unfinished"></translation> </message> @@ -2122,22 +2123,17 @@ <translation type="unfinished"></translation> </message> <message> - <location filename="../forms/debugwindow.ui" line="-417"/> - <source>Build date</source> - <translation>Build date</translation> - </message> - <message> - <location line="+241"/> + <location filename="../forms/debugwindow.ui" line="-299"/> <source>Debug log file</source> <translation>Debug log file</translation> </message> <message> - <location line="+85"/> + <location line="+136"/> <source>Clear console</source> <translation>Clear console</translation> </message> <message> - <location filename="../rpcconsole.cpp" line="-156"/> + <location filename="../rpcconsole.cpp" line="-204"/> <source>&Disconnect Node</source> <translation type="unfinished"></translation> </message> @@ -2175,8 +2171,8 @@ <translation type="unfinished"></translation> </message> <message> - <location line="+70"/> - <source>Welcome to the Bitcoin Core RPC console.</source> + <location line="+118"/> + <source>Welcome to the %1 RPC console.</source> <translation type="unfinished"></translation> </message> <message> @@ -2190,7 +2186,7 @@ <translation>Type <b>help</b> for an overview of available commands.</translation> </message> <message> - <location line="+144"/> + <location line="+146"/> <source>%1 B</source> <translation type="unfinished"></translation> </message> @@ -2444,9 +2440,14 @@ <translation type="unfinished">Message</translation> </message> <message> - <location line="+99"/> - <source>Amount</source> - <translation type="unfinished">Amount</translation> + <location line="+57"/> + <source>(no amount requested)</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+42"/> + <source>Requested</source> + <translation type="unfinished"></translation> </message> <message> <location line="-59"/> @@ -2458,17 +2459,12 @@ <source>(no message)</source> <translation type="unfinished"></translation> </message> - <message> - <location line="+8"/> - <source>(no amount)</source> - <translation type="unfinished"></translation> - </message> </context> <context> <name>SendCoinsDialog</name> <message> <location filename="../forms/sendcoinsdialog.ui" line="+14"/> - <location filename="../sendcoinsdialog.cpp" line="+546"/> + <location filename="../sendcoinsdialog.cpp" line="+543"/> <source>Send Coins</source> <translation>Send Coins</translation> </message> @@ -2615,17 +2611,7 @@ <translation type="unfinished"></translation> </message> <message> - <location line="+38"/> - <source>Send as zero-fee transaction if possible</source> - <translation type="unfinished"></translation> - </message> - <message> - <location line="+7"/> - <source>(confirmation may take longer)</source> - <translation type="unfinished"></translation> - </message> - <message> - <location line="+110"/> + <location line="+102"/> <source>Send to multiple recipients at once</source> <translation>Send to multiple recipients at once</translation> </message> @@ -2640,12 +2626,12 @@ <translation type="unfinished"></translation> </message> <message> - <location line="-858"/> + <location line="-805"/> <source>Dust:</source> <translation type="unfinished"></translation> </message> <message> - <location line="+861"/> + <location line="+808"/> <source>Clear &All</source> <translation>Clear &All</translation> </message> @@ -2678,7 +2664,7 @@ <translation type="unfinished"></translation> </message> <message> - <location line="-221"/> + <location line="-215"/> <source>Copy quantity</source> <translation type="unfinished"></translation> </message> @@ -2713,7 +2699,7 @@ <translation type="unfinished"></translation> </message> <message> - <location line="+244"/> + <location line="+238"/> <source>Total Amount %1</source> <translation type="unfinished"></translation> </message> @@ -2758,12 +2744,12 @@ <translation type="unfinished"></translation> </message> <message> - <location line="+91"/> + <location line="+89"/> <source>Pay only the required fee of %1</source> <translation type="unfinished"></translation> </message> <message numerus="yes"> - <location line="+23"/> + <location line="+25"/> <source>Estimated to begin confirmation within %n block(s).</source> <translation type="unfinished"> <numerusform>Estimated to begin confirmation within %n block.</numerusform> @@ -2796,12 +2782,12 @@ <translation type="unfinished"></translation> </message> <message> - <location line="-695"/> + <location line="-689"/> <source>Copy dust</source> <translation type="unfinished"></translation> </message> <message> - <location line="+221"/> + <location line="+215"/> <source>Are you sure you want to send?</source> <translation type="unfinished"></translation> </message> @@ -2921,10 +2907,19 @@ </message> </context> <context> + <name>SendConfirmationDialog</name> + <message> + <location filename="../sendcoinsdialog.cpp" line="+571"/> + <location line="+5"/> + <source>Yes</source> + <translation type="unfinished"></translation> + </message> +</context> +<context> <name>ShutdownWindow</name> <message> <location filename="../utilitydialog.cpp" line="+78"/> - <source>Bitcoin Core is shutting down...</source> + <source>%1 is shutting down...</source> <translation type="unfinished"></translation> </message> <message> @@ -3118,16 +3113,6 @@ <context> <name>SplashScreen</name> <message> - <location filename="../splashscreen.cpp" line="+41"/> - <source>Bitcoin Core</source> - <translation type="unfinished">Bitcoin Core</translation> - </message> - <message> - <location line="+2"/> - <source>The Bitcoin Core developers</source> - <translation type="unfinished"></translation> - </message> - <message> <location filename="../networkstyle.cpp" line="+19"/> <source>[testnet]</source> <translation>[testnet]</translation> @@ -3149,14 +3134,29 @@ <translation>Open until %1</translation> </message> <message> - <location line="+6"/> - <source>conflicted</source> - <translation type="unfinished"></translation> + <location line="+8"/> + <source>%1/offline</source> + <translation>%1/offline</translation> </message> <message> <location line="+2"/> - <source>%1/offline</source> - <translation>%1/offline</translation> + <source>0/unconfirmed, %1</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+0"/> + <source>in memory pool</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+0"/> + <source>not in memory pool</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+0"/> + <source>abandoned</source> + <translation type="unfinished"></translation> </message> <message> <location line="+2"/> @@ -3231,12 +3231,12 @@ <location line="+12"/> <location line="+53"/> <location line="+26"/> - <location line="+53"/> + <location line="+54"/> <source>Credit</source> <translation>Credit</translation> </message> <message numerus="yes"> - <location line="-142"/> + <location line="-143"/> <source>matures in %n more block(s)</source> <translation> <numerusform>matures in %n more block</numerusform> @@ -3251,12 +3251,12 @@ <message> <location line="+59"/> <location line="+25"/> - <location line="+53"/> + <location line="+54"/> <source>Debit</source> <translation>Debit</translation> </message> <message> - <location line="-68"/> + <location line="-69"/> <source>Total debit</source> <translation type="unfinished"></translation> </message> @@ -3277,12 +3277,12 @@ </message> <message> <location line="+6"/> - <location line="+9"/> + <location line="+10"/> <source>Message</source> <translation>Message</translation> </message> <message> - <location line="-7"/> + <location line="-8"/> <source>Comment</source> <translation>Comment</translation> </message> @@ -3292,6 +3292,11 @@ <translation>Transaction ID</translation> </message> <message> + <location line="+1"/> + <source>Output index</source> + <translation type="unfinished"></translation> + </message> + <message> <location line="+18"/> <source>Merchant</source> <translation type="unfinished"></translation> @@ -3334,12 +3339,12 @@ <translation>false</translation> </message> <message> - <location line="-242"/> + <location line="-243"/> <source>, has not been successfully broadcast yet</source> <translation>, has not been successfully broadcast yet</translation> </message> <message numerus="yes"> - <location line="-36"/> + <location line="-38"/> <source>Open for %n more block(s)</source> <translation> <numerusform>Open for %n more block</numerusform> @@ -3347,7 +3352,12 @@ </translation> </message> <message> - <location line="+67"/> + <location line="+8"/> + <source>conflicted with a transaction with %1 confirmations</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+61"/> <source>unknown</source> <translation>unknown</translation> </message> @@ -3355,15 +3365,15 @@ <context> <name>TransactionDescDialog</name> <message> - <location filename="../forms/transactiondescdialog.ui" line="+14"/> - <source>Transaction details</source> - <translation>Transaction details</translation> - </message> - <message> - <location line="+6"/> + <location filename="../forms/transactiondescdialog.ui" line="+20"/> <source>This pane shows a detailed description of the transaction</source> <translation>This pane shows a detailed description of the transaction</translation> </message> + <message> + <location filename="../transactiondescdialog.cpp" line="+17"/> + <source>Details for %1</source> + <translation type="unfinished"></translation> + </message> </context> <context> <name>TransactionTableModel</name> @@ -3378,12 +3388,12 @@ <translation>Type</translation> </message> <message> - <location line="+79"/> + <location line="+82"/> <source>Immature (%1 confirmations, will be available after %2)</source> <translation type="unfinished"></translation> </message> <message numerus="yes"> - <location line="-21"/> + <location line="-24"/> <source>Open for %n more block(s)</source> <translation> <numerusform>Open for %n more block</numerusform> @@ -3396,7 +3406,7 @@ <translation>Open until %1</translation> </message> <message> - <location line="+12"/> + <location line="+15"/> <source>Confirmed (%1 confirmations)</source> <translation>Confirmed (%1 confirmations)</translation> </message> @@ -3411,7 +3421,7 @@ <translation>Generated but not accepted</translation> </message> <message> - <location line="-21"/> + <location line="-24"/> <source>Offline</source> <translation type="unfinished"></translation> </message> @@ -3427,6 +3437,11 @@ </message> <message> <location line="+3"/> + <source>Abandoned</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+3"/> <source>Confirming (%1 of %2 recommended confirmations)</source> <translation type="unfinished"></translation> </message> @@ -3471,7 +3486,12 @@ <translation>(n/a)</translation> </message> <message> - <location line="+217"/> + <location line="+213"/> + <source>(no label)</source> + <translation type="unfinished">(no label)</translation> + </message> + <message> + <location line="+39"/> <source>Transaction status. Hover over this field to show number of confirmations.</source> <translation>Transaction status. Hover over this field to show number of confirmations.</translation> </message> @@ -3576,6 +3596,11 @@ </message> <message> <location line="+36"/> + <source>Abandon transaction</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+1"/> <source>Copy address</source> <translation>Copy address</translation> </message> @@ -3601,6 +3626,11 @@ </message> <message> <location line="+1"/> + <source>Copy full transaction details</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+1"/> <source>Edit label</source> <translation>Edit label</translation> </message> @@ -3610,7 +3640,7 @@ <translation>Show transaction details</translation> </message> <message> - <location line="+181"/> + <location line="+186"/> <source>Export Transaction History</source> <translation type="unfinished"></translation> </message> @@ -3675,7 +3705,7 @@ <translation>ID</translation> </message> <message> - <location line="+121"/> + <location line="+152"/> <source>Range:</source> <translation>Range:</translation> </message> @@ -3688,7 +3718,7 @@ <context> <name>UnitDisplayStatusBarControl</name> <message> - <location filename="../bitcoingui.cpp" line="+106"/> + <location filename="../bitcoingui.cpp" line="+114"/> <source>Unit to show amounts in. Click to select another unit.</source> <translation type="unfinished"></translation> </message> @@ -3755,47 +3785,37 @@ <context> <name>bitcoin-core</name> <message> - <location filename="../bitcoinstrings.cpp" line="+267"/> + <location filename="../bitcoinstrings.cpp" line="+286"/> <source>Options:</source> <translation>Options:</translation> </message> <message> - <location line="+27"/> + <location line="+29"/> <source>Specify data directory</source> <translation>Specify data directory</translation> </message> <message> - <location line="-84"/> + <location line="-87"/> <source>Connect to a node to retrieve peer addresses, and disconnect</source> <translation>Connect to a node to retrieve peer addresses, and disconnect</translation> </message> <message> - <location line="+87"/> + <location line="+90"/> <source>Specify your own public address</source> <translation>Specify your own public address</translation> </message> <message> - <location line="-105"/> + <location line="-106"/> <source>Accept command line and JSON-RPC commands</source> <translation>Accept command line and JSON-RPC commands</translation> </message> <message> - <location line="-118"/> + <location line="-125"/> <source>If <category> is not supplied or if <category> = 1, output all debugging information.</source> <translation type="unfinished"></translation> </message> <message> - <location line="+15"/> - <source>Maximum total fees (in %s) to use in a single wallet transaction; setting this too low may abort large transactions (default: %s)</source> - <translation type="unfinished"></translation> - </message> - <message> - <location line="+7"/> - <source>Please check that your computer's date and time are correct! If your clock is wrong Bitcoin Core will not work properly.</source> - <translation type="unfinished"></translation> - </message> - <message> - <location line="+3"/> + <location line="+29"/> <source>Prune configured below the minimum of %d MiB. Please use a higher number.</source> <translation type="unfinished"></translation> </message> @@ -3815,7 +3835,7 @@ <translation type="unfinished"></translation> </message> <message> - <location line="+116"/> + <location line="+118"/> <source>Error: A fatal internal error occurred, see debug.log for details</source> <translation type="unfinished"></translation> </message> @@ -3825,7 +3845,7 @@ <translation type="unfinished"></translation> </message> <message> - <location line="+38"/> + <location line="+40"/> <source>Pruning blockstore...</source> <translation type="unfinished"></translation> </message> @@ -3840,17 +3860,47 @@ <translation type="unfinished"></translation> </message> <message> - <location line="-117"/> + <location line="-118"/> <source>Accept connections from outside (default: 1 if no -proxy or -connect)</source> <translation>Accept connections from outside (default: 1 if no -proxy or -connect)</translation> </message> <message> - <location line="-168"/> + <location line="-201"/> + <source>Bitcoin Core</source> + <translation type="unfinished">Bitcoin Core</translation> + </message> + <message> + <location line="+1"/> + <source>The %s developers</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+4"/> + <source>-fallbackfee is set very high! This is the transaction fee you may pay when fee estimates are not available.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+9"/> + <source>A fee rate (in %s/kB) that will be used when fee estimation has insufficient data (default: %s)</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+3"/> + <source>Accept relayed transactions received from whitelisted peers even when not relaying transactions (default: %d)</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+7"/> <source>Bind to given address and always listen on it. Use [host]:port notation for IPv6</source> <translation>Bind to given address and always listen on it. Use [host]:port notation for IPv6</translation> </message> <message> - <location line="+16"/> + <location line="+10"/> + <source>Cannot obtain a lock on data directory %s. %s is probably already running.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+5"/> <source>Delete all wallet transactions and only recover those parts of the blockchain through -rescan on startup</source> <translation type="unfinished"></translation> </message> @@ -3860,17 +3910,42 @@ <translation type="unfinished"></translation> </message> <message> - <location line="+13"/> + <location line="+5"/> + <source>Error reading %s! All keys read correctly, but transaction data or address book entries might be missing or incorrect.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+8"/> <source>Execute command when a wallet transaction changes (%s in cmd is replaced by TxID)</source> <translation>Execute command when a wallet transaction changes (%s in cmd is replaced by TxID)</translation> </message> <message> - <location line="+60"/> + <location line="+12"/> + <source>Force relay of transactions from whitelisted peers even they violate local relay policy (default: %d)</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+17"/> + <source>Maximum allowed median peer time offset adjustment. Local perspective of time may be influenced by peers forward or backward by this amount. (default: %u seconds)</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+7"/> + <source>Maximum total fees (in %s) to use in a single wallet transaction or raw transaction; setting this too low may abort large transactions (default: %s)</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+7"/> + <source>Please check that your computer's date and time are correct! If your clock is wrong, %s will not work properly.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+24"/> <source>Set the number of script verification threads (%u to %d, 0 = auto, <0 = leave that many cores free, default: %d)</source> <translation type="unfinished"></translation> </message> <message> - <location line="+8"/> + <location line="+5"/> <source>The block database contains a block which appears to be from the future. This may be due to your computer's date and time being set incorrectly. Only rebuild the block database if you are sure that your computer's date and time are correct</source> <translation type="unfinished"></translation> </message> @@ -3880,12 +3955,7 @@ <translation>This is a pre-release test build - use at your own risk - do not use for mining or merchant applications</translation> </message> <message> - <location line="+13"/> - <source>Unable to bind to %s on this computer. Bitcoin Core is probably already running.</source> - <translation type="unfinished"></translation> - </message> - <message> - <location line="+6"/> + <location line="+19"/> <source>Use UPnP to map the listening port (default: 1 when listening and no -proxy)</source> <translation type="unfinished"></translation> </message> @@ -3905,22 +3975,27 @@ <translation>Warning: The network does not appear to fully agree! Some miners appear to be experiencing issues.</translation> </message> <message> - <location line="+3"/> + <location line="+10"/> <source>Warning: We do not appear to fully agree with our peers! You may need to upgrade, or other nodes may need to upgrade.</source> <translation>Warning: We do not appear to fully agree with our peers! You may need to upgrade, or other nodes may need to upgrade.</translation> </message> <message> <location line="+3"/> - <source>Warning: wallet.dat corrupt, data salvaged! Original wallet.dat saved as wallet.{timestamp}.bak in %s; if your balance or transactions are incorrect you should restore from a backup.</source> - <translation>Warning: wallet.dat corrupt, data salvaged! Original wallet.dat saved as wallet.{timestamp}.bak in %s; if your balance or transactions are incorrect you should restore from a backup.</translation> + <source>Whitelist peers connecting from the given netmask or IP address. Can be specified multiple times.</source> + <translation type="unfinished"></translation> </message> <message> - <location line="+4"/> - <source>Whitelist peers connecting from the given netmask or IP address. Can be specified multiple times.</source> + <location line="+9"/> + <source>You need to rebuild the database using -reindex-chainstate to change -txindex</source> <translation type="unfinished"></translation> </message> <message> - <location line="+11"/> + <location line="+2"/> + <source>%s corrupt, salvage failed</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+3"/> <source>-maxmempool must be at least %d MB</source> <translation type="unfinished"></translation> </message> @@ -3930,12 +4005,32 @@ <translation type="unfinished"></translation> </message> <message> - <location line="+11"/> + <location line="+7"/> + <source>Append comment to the user agent string</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+1"/> + <source>Attempt to recover private keys from a corrupt wallet on startup</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+2"/> <source>Block creation options:</source> <translation>Block creation options:</translation> </message> <message> - <location line="+6"/> + <location line="+2"/> + <source>Cannot resolve -%s address: '%s'</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+2"/> + <source>Change index out of range</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+1"/> <source>Connect only to the specified node(s)</source> <translation>Connect only to the specified node(s)</translation> </message> @@ -3945,7 +4040,12 @@ <translation type="unfinished"></translation> </message> <message> - <location line="+2"/> + <location line="+1"/> + <source>Copyright (C) %i-%i</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+1"/> <source>Corrupted block database detected</source> <translation>Corrupted block database detected</translation> </message> @@ -3986,6 +4086,11 @@ </message> <message> <location line="+1"/> + <source>Enable transaction replacement in the memory pool (default: %u)</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+1"/> <source>Error initializing block database</source> <translation>Error initializing block database</translation> </message> @@ -3996,11 +4101,26 @@ </message> <message> <location line="+1"/> + <source>Error loading %s</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+1"/> + <source>Error loading %s: Wallet corrupted</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+1"/> + <source>Error loading %s: Wallet requires newer version of %s</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+1"/> <source>Error loading block database</source> <translation>Error loading block database</translation> </message> <message> - <location line="+4"/> + <location line="+1"/> <source>Error opening block database</source> <translation>Error opening block database</translation> </message> @@ -4015,7 +4135,7 @@ <translation>Failed to listen on any port. Use -listen=0 if you want this.</translation> </message> <message> - <location line="+4"/> + <location line="+3"/> <source>Importing...</source> <translation type="unfinished"></translation> </message> @@ -4025,17 +4145,47 @@ <translation>Incorrect or no genesis block found. Wrong datadir for network?</translation> </message> <message> - <location line="+4"/> + <location line="+2"/> + <source>Initialization sanity check failed. %s is shutting down.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+2"/> <source>Invalid -onion address: '%s'</source> <translation type="unfinished"></translation> </message> <message> - <location line="+9"/> + <location line="+2"/> + <source>Invalid amount for -%s=<amount>: '%s'</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+1"/> + <source>Invalid amount for -fallbackfee=<amount>: '%s'</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+4"/> <source>Keep the transaction memory pool below <n> megabytes (default: %u)</source> <translation type="unfinished"></translation> </message> <message> - <location line="+12"/> + <location line="+4"/> + <source>Loading banlist...</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+3"/> + <source>Location of the auth cookie (default: data dir)</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+5"/> + <source>Minimum bytes per sigop in transactions we relay and mine (default: %u)</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+3"/> <source>Not enough file descriptors available.</source> <translation>Not enough file descriptors available.</translation> </message> @@ -4046,6 +4196,16 @@ </message> <message> <location line="+4"/> + <source>Print this help message and exit</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+1"/> + <source>Print version and exit</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+1"/> <source>Prune cannot be configured with a negative value.</source> <translation type="unfinished"></translation> </message> @@ -4055,7 +4215,17 @@ <translation type="unfinished"></translation> </message> <message> - <location line="+13"/> + <location line="+3"/> + <source>Rebuild chain state and block index from the blk*.dat files on disk</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+1"/> + <source>Rebuild chain state from the currently indexed blocks</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+9"/> <source>Set database cache size in megabytes (%d to %d, default: %d)</source> <translation type="unfinished"></translation> </message> @@ -4070,7 +4240,12 @@ <translation>Specify wallet file (within data directory)</translation> </message> <message> - <location line="+16"/> + <location line="+13"/> + <source>Unable to bind to %s on this computer. %s is probably already running.</source> + <translation type="unfinished"></translation> + </message> + <message> + <location line="+3"/> <source>Unsupported argument -benchmark ignored, use -debug=bench.</source> <translation type="unfinished"></translation> </message> @@ -4110,22 +4285,22 @@ <translation>Wallet %s resides outside data directory %s</translation> </message> <message> - <location line="+2"/> - <source>Wallet options:</source> + <location line="+1"/> + <source>Wallet debugging/testing options:</source> <translation type="unfinished"></translation> </message> <message> - <location line="+2"/> - <source>Warning: This version is obsolete; upgrade required!</source> + <location line="+1"/> + <source>Wallet needed to be rewritten: restart %s to complete</source> <translation type="unfinished"></translation> </message> <message> - <location line="+2"/> - <source>You need to rebuild the database using -reindex to change -txindex</source> - <translation>You need to rebuild the database using -reindex to change -txindex</translation> + <location line="+1"/> + <source>Wallet options:</source> + <translation type="unfinished"></translation> </message> <message> - <location line="-306"/> + <location line="-313"/> <source>Allow JSON-RPC connections from specified source. Valid for <ip> are a single IP (e.g. 1.2.3.4), a network/netmask (e.g. 1.2.3.4/255.255.255.0) or a network/CIDR (e.g. 1.2.3.4/24). This option can be specified multiple times</source> <translation type="unfinished"></translation> </message> @@ -4140,12 +4315,7 @@ <translation type="unfinished"></translation> </message> <message> - <location line="+4"/> - <source>Cannot obtain a lock on data directory %s. Bitcoin Core is probably already running.</source> - <translation type="unfinished"></translation> - </message> - <message> - <location line="+3"/> + <location line="+6"/> <source>Create new files with system default permissions, instead of umask 077 (only effective with disabled wallet functionality)</source> <translation type="unfinished"></translation> </message> @@ -4170,7 +4340,7 @@ <translation type="unfinished"></translation> </message> <message> - <location line="+11"/> + <location line="+14"/> <source>If paytxfee is not set, include enough fee so transactions begin confirmation on average within n blocks (default: %u)</source> <translation type="unfinished"></translation> </message> @@ -4180,7 +4350,7 @@ <translation type="unfinished"></translation> </message> <message> - <location line="+6"/> + <location line="+10"/> <source>Maximum size of data in data carrier transactions we relay and mine (default: %u)</source> <translation type="unfinished"></translation> </message> @@ -4200,12 +4370,7 @@ <translation type="unfinished"></translation> </message> <message> - <location line="+5"/> - <source>Set the number of threads for coin generation if enabled (-1 = all cores, default: %d)</source> - <translation type="unfinished"></translation> - </message> - <message> - <location line="+10"/> + <location line="+12"/> <source>The transaction amount is too small to send after the fee has been deducted</source> <translation type="unfinished"></translation> </message> @@ -4215,7 +4380,7 @@ <translation type="unfinished"></translation> </message> <message> - <location line="+44"/> + <location line="+47"/> <source>Whitelisted peers cannot be DoS banned and their transactions are always relayed, even if they are already in the mempool, useful e.g. for a gateway</source> <translation type="unfinished"></translation> </message> @@ -4225,7 +4390,7 @@ <translation type="unfinished"></translation> </message> <message> - <location line="+4"/> + <location line="+7"/> <source>(default: %u)</source> <translation type="unfinished"></translation> </message> @@ -4235,52 +4400,22 @@ <translation type="unfinished"></translation> </message> <message> - <location line="+1"/> - <source>Activating best chain...</source> - <translation type="unfinished"></translation> - </message> - <message> - <location line="+4"/> - <source>Always relay transactions received from whitelisted peers (default: %d)</source> - <translation type="unfinished"></translation> - </message> - <message> - <location line="+1"/> - <source>Attempt to recover private keys from a corrupt wallet.dat on startup</source> - <translation type="unfinished"></translation> - </message> - <message> - <location line="+1"/> + <location line="+6"/> <source>Automatically create Tor hidden service (default: %d)</source> <translation type="unfinished"></translation> </message> <message> - <location line="+5"/> - <source>Cannot resolve -whitebind address: '%s'</source> - <translation type="unfinished"></translation> - </message> - <message> - <location line="+3"/> + <location line="+7"/> <source>Connect through SOCKS5 proxy</source> <translation type="unfinished"></translation> </message> <message> - <location line="+3"/> - <source>Copyright (C) 2009-%i The Bitcoin Core Developers</source> - <translation type="unfinished"></translation> - </message> - <message> - <location line="+15"/> - <source>Error loading wallet.dat: Wallet requires newer version of Bitcoin Core</source> - <translation type="unfinished"></translation> - </message> - <message> - <location line="+2"/> + <location line="+21"/> <source>Error reading from database, shutting down.</source> <translation type="unfinished"></translation> </message> <message> - <location line="+9"/> + <location line="+8"/> <source>Imports blocks from external blk000??.dat file on startup</source> <translation type="unfinished"></translation> </message> @@ -4290,32 +4425,12 @@ <translation>Information</translation> </message> <message> - <location line="+1"/> - <source>Initialization sanity check failed. Bitcoin Core is shutting down.</source> - <translation type="unfinished"></translation> - </message> - <message> - <location line="+4"/> - <source>Invalid amount for -maxtxfee=<amount>: '%s'</source> - <translation type="unfinished"></translation> - </message> - <message> - <location line="+1"/> - <source>Invalid amount for -minrelaytxfee=<amount>: '%s'</source> - <translation>Invalid amount for -minrelaytxfee=<amount>: '%s'</translation> - </message> - <message> - <location line="+1"/> - <source>Invalid amount for -mintxfee=<amount>: '%s'</source> - <translation>Invalid amount for -mintxfee=<amount>: '%s'</translation> - </message> - <message> - <location line="+1"/> + <location line="+7"/> <source>Invalid amount for -paytxfee=<amount>: '%s' (must be at least %s)</source> <translation type="unfinished"></translation> </message> <message> - <location line="+2"/> + <location line="+1"/> <source>Invalid netmask specified in -whitelist: '%s'</source> <translation type="unfinished"></translation> </message> @@ -4325,7 +4440,7 @@ <translation type="unfinished"></translation> </message> <message> - <location line="+11"/> + <location line="+14"/> <source>Need to specify a port with -whitebind: '%s'</source> <translation type="unfinished"></translation> </message> @@ -4335,22 +4450,12 @@ <translation type="unfinished"></translation> </message> <message> - <location line="+9"/> + <location line="+11"/> <source>RPC server options:</source> <translation type="unfinished"></translation> </message> <message> - <location line="+1"/> - <source>Rebuild block chain index from current blk000??.dat files on startup</source> - <translation type="unfinished"></translation> - </message> - <message> - <location line="+1"/> - <source>Receive and display P2P network alerts (default: %u)</source> - <translation type="unfinished"></translation> - </message> - <message> - <location line="+1"/> + <location line="+3"/> <source>Reducing -maxconnections from %d to %d, because of system limitations.</source> <translation type="unfinished"></translation> </message> @@ -4390,7 +4495,7 @@ <translation type="unfinished"></translation> </message> <message> - <location line="+2"/> + <location line="+1"/> <source>This is experimental software.</source> <translation type="unfinished"></translation> </message> @@ -4430,7 +4535,7 @@ <translation type="unfinished"></translation> </message> <message> - <location line="+6"/> + <location line="+7"/> <source>Upgrade wallet to latest format on startup</source> <translation type="unfinished"></translation> </message> @@ -4440,72 +4545,57 @@ <translation>Username for JSON-RPC connections</translation> </message> <message> - <location line="+4"/> - <source>Wallet needed to be rewritten: restart Bitcoin Core to complete</source> - <translation type="unfinished"></translation> - </message> - <message> - <location line="+2"/> + <location line="+7"/> <source>Warning</source> <translation>Warning</translation> </message> <message> - <location line="+2"/> - <source>Whether to operate in a blocks only mode (default: %u)</source> + <location line="+1"/> + <source>Warning: unknown new rules activated (versionbit %i)</source> <translation type="unfinished"></translation> </message> <message> - <location line="+2"/> - <source>Zapping all transactions from wallet...</source> + <location line="+1"/> + <source>Whether to operate in a blocks only mode (default: %u)</source> <translation type="unfinished"></translation> </message> <message> <location line="+1"/> - <source>ZeroMQ notification options:</source> + <source>Zapping all transactions from wallet...</source> <translation type="unfinished"></translation> </message> <message> <location line="+1"/> - <source>wallet.dat corrupt, salvage failed</source> - <translation>wallet.dat corrupt, salvage failed</translation> + <source>ZeroMQ notification options:</source> + <translation type="unfinished"></translation> </message> <message> - <location line="-62"/> + <location line="-63"/> <source>Password for JSON-RPC connections</source> <translation>Password for JSON-RPC connections</translation> </message> <message> - <location line="-205"/> + <location line="-214"/> <source>Execute command when the best block changes (%s in cmd is replaced by block hash)</source> <translation>Execute command when the best block changes (%s in cmd is replaced by block hash)</translation> </message> <message> - <location line="+237"/> - <source>This help message</source> - <translation>This help message</translation> - </message> - <message> - <location line="-103"/> + <location line="+143"/> <source>Allow DNS lookups for -addnode, -seednode and -connect</source> <translation>Allow DNS lookups for -addnode, -seednode and -connect</translation> </message> <message> - <location line="+59"/> + <location line="+56"/> <source>Loading addresses...</source> <translation>Loading addresses...</translation> </message> <message> - <location line="-30"/> - <source>Error loading wallet.dat: Wallet corrupted</source> - <translation>Error loading wallet.dat: Wallet corrupted</translation> - </message> - <message> - <location line="-214"/> + <location line="-258"/> <source>(1 = keep tx meta data e.g. account owner and payment request information, 2 = drop tx meta data)</source> <translation type="unfinished"></translation> </message> <message> - <location line="+3"/> + <location line="+6"/> <source>-maxtxfee is set very high! Fees this large could be paid on a single transaction.</source> <translation type="unfinished"></translation> </message> @@ -4515,22 +4605,17 @@ <translation type="unfinished"></translation> </message> <message> - <location line="+32"/> + <location line="+37"/> <source>Do not keep transactions in the mempool longer than <n> hours (default: %u)</source> <translation type="unfinished"></translation> </message> <message> - <location line="+2"/> - <source>Error reading wallet.dat! All keys read correctly, but transaction data or address book entries might be missing or incorrect.</source> - <translation type="unfinished"></translation> - </message> - <message> - <location line="+17"/> + <location line="+19"/> <source>Fees (in %s/kB) smaller than this are considered zero fee for transaction creation (default: %s)</source> <translation type="unfinished"></translation> </message> <message> - <location line="+3"/> + <location line="+6"/> <source>How thorough the block verification of -checkblocks is (0-4, default: %u)</source> <translation type="unfinished"></translation> </message> @@ -4540,7 +4625,7 @@ <translation type="unfinished"></translation> </message> <message> - <location line="+9"/> + <location line="+13"/> <source>Number of seconds to keep misbehaving peers from reconnecting (default: %u)</source> <translation type="unfinished"></translation> </message> @@ -4550,7 +4635,7 @@ <translation type="unfinished"></translation> </message> <message> - <location line="+32"/> + <location line="+29"/> <source>Support filtering of blocks and transaction with bloom filters (default: %u)</source> <translation type="unfinished"></translation> </message> @@ -4565,11 +4650,16 @@ <translation type="unfinished"></translation> </message> <message> - <location line="+6"/> + <location line="+3"/> <source>Unsupported argument -socks found. Setting SOCKS version isn't possible anymore, only SOCKS5 proxies are supported.</source> <translation type="unfinished"></translation> </message> <message> + <location line="+3"/> + <source>Unsupported argument -whitelistalwaysrelay ignored, use -whitelistrelay and/or -whitelistforcerelay.</source> + <translation type="unfinished"></translation> + </message> + <message> <location line="+5"/> <source>Use separate SOCKS5 proxy to reach peers via Tor hidden services (default: %s)</source> <translation type="unfinished"></translation> @@ -4580,27 +4670,27 @@ <translation type="unfinished"></translation> </message> <message> - <location line="+29"/> - <source>(default: %s)</source> + <location line="+13"/> + <source>Warning: Unknown block versions being mined! It's possible unknown rules are in effect</source> <translation type="unfinished"></translation> </message> <message> - <location line="+10"/> - <source>Always query for peer addresses via DNS lookup (default: %u)</source> + <location line="+3"/> + <source>Warning: Wallet file corrupt, data salvaged! Original %s saved as %s in %s; if your balance or transactions are incorrect you should restore from a backup.</source> <translation type="unfinished"></translation> </message> <message> - <location line="+27"/> - <source>Error loading wallet.dat</source> - <translation>Error loading wallet.dat</translation> + <location line="+19"/> + <source>(default: %s)</source> + <translation type="unfinished"></translation> </message> <message> - <location line="+10"/> - <source>Generate coins (default: %u)</source> + <location line="+9"/> + <source>Always query for peer addresses via DNS lookup (default: %u)</source> <translation type="unfinished"></translation> </message> <message> - <location line="+1"/> + <location line="+37"/> <source>How many blocks to check at startup (default: %u, 0 = all)</source> <translation type="unfinished"></translation> </message> @@ -4615,7 +4705,7 @@ <translation>Invalid -proxy address: '%s'</translation> </message> <message> - <location line="+9"/> + <location line="+7"/> <source>Listen for JSON-RPC connections on <port> (default: %u or testnet: %u)</source> <translation type="unfinished"></translation> </message> @@ -4625,7 +4715,7 @@ <translation type="unfinished"></translation> </message> <message> - <location line="+4"/> + <location line="+6"/> <source>Maintain at most <n> connections to peers (default: %u)</source> <translation type="unfinished"></translation> </message> @@ -4645,12 +4735,12 @@ <translation type="unfinished"></translation> </message> <message> - <location line="+7"/> + <location line="+8"/> <source>Prepend debug output with timestamp (default: %u)</source> <translation type="unfinished"></translation> </message> <message> - <location line="+8"/> + <location line="+10"/> <source>Relay and mine data carrier transactions (default: %u)</source> <translation type="unfinished"></translation> </message> @@ -4695,72 +4785,57 @@ <translation type="unfinished"></translation> </message> <message> - <location line="+4"/> + <location line="+3"/> <source>Threshold for disconnecting misbehaving peers (default: %u)</source> <translation type="unfinished"></translation> </message> <message> - <location line="+9"/> + <location line="+10"/> <source>Unknown network specified in -onlynet: '%s'</source> <translation>Unknown network specified in -onlynet: '%s'</translation> </message> <message> - <location line="-107"/> - <source>Cannot resolve -bind address: '%s'</source> - <translation>Cannot resolve -bind address: '%s'</translation> - </message> - <message> - <location line="+1"/> - <source>Cannot resolve -externalip address: '%s'</source> - <translation>Cannot resolve -externalip address: '%s'</translation> - </message> - <message> - <location line="+45"/> - <source>Invalid amount for -paytxfee=<amount>: '%s'</source> - <translation>Invalid amount for -paytxfee=<amount>: '%s'</translation> - </message> - <message> - <location line="-7"/> + <location line="-71"/> <source>Insufficient funds</source> <translation>Insufficient funds</translation> </message> <message> - <location line="+14"/> + <location line="+13"/> <source>Loading block index...</source> <translation>Loading block index...</translation> </message> <message> - <location line="-61"/> + <location line="-59"/> <source>Add a node to connect to and attempt to keep the connection open</source> <translation>Add a node to connect to and attempt to keep the connection open</translation> </message> <message> - <location line="+62"/> + <location line="+60"/> <source>Loading wallet...</source> <translation>Loading wallet...</translation> </message> <message> - <location line="-55"/> + <location line="-53"/> <source>Cannot downgrade wallet</source> <translation>Cannot downgrade wallet</translation> </message> <message> - <location line="+4"/> + <location line="+2"/> <source>Cannot write default address</source> <translation>Cannot write default address</translation> </message> <message> - <location line="+73"/> + <location line="+77"/> <source>Rescanning...</source> <translation>Rescanning...</translation> </message> <message> - <location line="-63"/> + <location line="-66"/> <source>Done loading</source> <translation>Done loading</translation> </message> <message> - <location line="+13"/> + <location line="+14"/> <source>Error</source> <translation>Error</translation> </message> diff --git a/src/qt/overviewpage.cpp b/src/qt/overviewpage.cpp index d577345e49..6a0404cbf7 100644 --- a/src/qt/overviewpage.cpp +++ b/src/qt/overviewpage.cpp @@ -219,7 +219,7 @@ void OverviewPage::setWalletModel(WalletModel *model) filter->setDynamicSortFilter(true); filter->setSortRole(Qt::EditRole); filter->setShowInactive(false); - filter->sort(TransactionTableModel::Status, Qt::DescendingOrder); + filter->sort(TransactionTableModel::Date, Qt::DescendingOrder); ui->listTransactions->setModel(filter); ui->listTransactions->setModelColumn(TransactionTableModel::ToAddress); diff --git a/src/qt/receiverequestdialog.cpp b/src/qt/receiverequestdialog.cpp index a1e9156eea..b13ea3df70 100644 --- a/src/qt/receiverequestdialog.cpp +++ b/src/qt/receiverequestdialog.cpp @@ -45,7 +45,7 @@ QImage QRImageWidget::exportImage() { if(!pixmap()) return QImage(); - return pixmap()->toImage().scaled(EXPORT_IMAGE_SIZE, EXPORT_IMAGE_SIZE); + return pixmap()->toImage(); } void QRImageWidget::mousePressEvent(QMouseEvent *event) @@ -166,20 +166,32 @@ void ReceiveRequestDialog::update() ui->lblQRCode->setText(tr("Error encoding URI into QR Code.")); return; } - QImage myImage = QImage(code->width + 8, code->width + 8, QImage::Format_RGB32); - myImage.fill(0xffffff); + QImage qrImage = QImage(code->width + 8, code->width + 8, QImage::Format_RGB32); + qrImage.fill(0xffffff); unsigned char *p = code->data; for (int y = 0; y < code->width; y++) { for (int x = 0; x < code->width; x++) { - myImage.setPixel(x + 4, y + 4, ((*p & 1) ? 0x0 : 0xffffff)); + qrImage.setPixel(x + 4, y + 4, ((*p & 1) ? 0x0 : 0xffffff)); p++; } } QRcode_free(code); - ui->lblQRCode->setPixmap(QPixmap::fromImage(myImage).scaled(300, 300)); + QImage qrAddrImage = QImage(QR_IMAGE_SIZE, QR_IMAGE_SIZE+20, QImage::Format_RGB32); + qrAddrImage.fill(0xffffff); + QPainter painter(&qrAddrImage); + painter.drawImage(0, 0, qrImage.scaled(QR_IMAGE_SIZE, QR_IMAGE_SIZE)); + QFont font = GUIUtil::fixedPitchFont(); + font.setPixelSize(12); + painter.setFont(font); + QRect paddedRect = qrAddrImage.rect(); + paddedRect.setHeight(QR_IMAGE_SIZE+12); + painter.drawText(paddedRect, Qt::AlignBottom|Qt::AlignCenter, info.address); + painter.end(); + + ui->lblQRCode->setPixmap(QPixmap::fromImage(qrAddrImage)); ui->btnSaveAs->setEnabled(true); } } diff --git a/src/qt/receiverequestdialog.h b/src/qt/receiverequestdialog.h index 4cab4caff1..676745a858 100644 --- a/src/qt/receiverequestdialog.h +++ b/src/qt/receiverequestdialog.h @@ -10,6 +10,7 @@ #include <QDialog> #include <QImage> #include <QLabel> +#include <QPainter> class OptionsModel; diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp index d8647d902a..11f3e49a06 100644 --- a/src/qt/rpcconsole.cpp +++ b/src/qt/rpcconsole.cpp @@ -327,6 +327,14 @@ bool RPCConsole::eventFilter(QObject* obj, QEvent *event) return true; } break; + case Qt::Key_Return: + case Qt::Key_Enter: + // forward these events to lineEdit + if(obj == autoCompleter->popup()) { + QApplication::postEvent(ui->lineEdit, new QKeyEvent(*keyevt)); + return true; + } + break; default: // Typing in messages widget brings focus to line edit, and redirects key there // Exclude most combinations and keys that emit no text, except paste shortcuts @@ -353,8 +361,8 @@ void RPCConsole::setClientModel(ClientModel *model) setNumConnections(model->getNumConnections()); connect(model, SIGNAL(numConnectionsChanged(int)), this, SLOT(setNumConnections(int))); - setNumBlocks(model->getNumBlocks(), model->getLastBlockDate(), model->getVerificationProgress(NULL)); - connect(model, SIGNAL(numBlocksChanged(int,QDateTime,double)), this, SLOT(setNumBlocks(int,QDateTime,double))); + setNumBlocks(model->getNumBlocks(), model->getLastBlockDate(), model->getVerificationProgress(NULL), false); + connect(model, SIGNAL(numBlocksChanged(int,QDateTime,double,bool)), this, SLOT(setNumBlocks(int,QDateTime,double,bool))); updateTrafficStats(model->getTotalBytesRecv(), model->getTotalBytesSent()); connect(model, SIGNAL(bytesChanged(quint64,quint64)), this, SLOT(updateTrafficStats(quint64, quint64))); @@ -458,9 +466,7 @@ void RPCConsole::setClientModel(ClientModel *model) autoCompleter = new QCompleter(wordList, this); ui->lineEdit->setCompleter(autoCompleter); - - // clear the lineEdit after activating from QCompleter - connect(autoCompleter, SIGNAL(activated(const QString&)), ui->lineEdit, SLOT(clear()), Qt::QueuedConnection); + autoCompleter->popup()->installEventFilter(this); } } @@ -585,10 +591,12 @@ void RPCConsole::setNumConnections(int count) ui->numberOfConnections->setText(connections); } -void RPCConsole::setNumBlocks(int count, const QDateTime& blockDate, double nVerificationProgress) +void RPCConsole::setNumBlocks(int count, const QDateTime& blockDate, double nVerificationProgress, bool headers) { - ui->numberOfBlocks->setText(QString::number(count)); - ui->lastBlockTime->setText(blockDate.toString()); + if (!headers) { + ui->numberOfBlocks->setText(QString::number(count)); + ui->lastBlockTime->setText(blockDate.toString()); + } } void RPCConsole::setMempoolSize(long numberOfTxs, size_t dynUsage) @@ -885,15 +893,13 @@ void RPCConsole::banSelectedNode(int bantime) // Get currently selected peer address QString strNode = GUIUtil::getEntryData(ui->peerWidget, 0, PeerTableModel::Address); // Find possible nodes, ban it and clear the selected node - if (CNode *bannedNode = FindNode(strNode.toStdString())) { + if (FindNode(strNode.toStdString())) { std::string nStr = strNode.toStdString(); std::string addr; int port = 0; SplitHostPort(nStr, port, addr); CNode::Ban(CNetAddr(addr), BanReasonManuallyAdded, bantime); - bannedNode->fDisconnect = true; - DumpBanlist(); clearSelectedNode(); clientModel->getBanTableModel()->refresh(); @@ -912,7 +918,6 @@ void RPCConsole::unbanSelectedNode() if (possibleSubnet.IsValid()) { CNode::Unban(possibleSubnet); - DumpBanlist(); clientModel->getBanTableModel()->refresh(); } } diff --git a/src/qt/rpcconsole.h b/src/qt/rpcconsole.h index 2923587bc8..28affa954d 100644 --- a/src/qt/rpcconsole.h +++ b/src/qt/rpcconsole.h @@ -87,7 +87,7 @@ public Q_SLOTS: /** Set number of connections shown in the UI */ void setNumConnections(int count); /** Set number of blocks and last block date shown in the UI */ - void setNumBlocks(int count, const QDateTime& blockDate, double nVerificationProgress); + void setNumBlocks(int count, const QDateTime& blockDate, double nVerificationProgress, bool headers); /** Set size (number of transactions and memory usage) of the mempool in the UI */ void setMempoolSize(long numberOfTxs, size_t dynUsage); /** Go forward or back in history */ diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp index 54ebd25833..6d50be56ec 100644 --- a/src/qt/sendcoinsdialog.cpp +++ b/src/qt/sendcoinsdialog.cpp @@ -124,7 +124,7 @@ void SendCoinsDialog::setClientModel(ClientModel *clientModel) this->clientModel = clientModel; if (clientModel) { - connect(clientModel, SIGNAL(numBlocksChanged(int,QDateTime,double)), this, SLOT(updateSmartFeeLabel())); + connect(clientModel, SIGNAL(numBlocksChanged(int,QDateTime,double,bool)), this, SLOT(updateSmartFeeLabel())); } } diff --git a/src/qt/transactionview.cpp b/src/qt/transactionview.cpp index eb6111e682..199a7b2d77 100644 --- a/src/qt/transactionview.cpp +++ b/src/qt/transactionview.cpp @@ -203,7 +203,7 @@ void TransactionView::setModel(WalletModel *model) transactionView->setSelectionBehavior(QAbstractItemView::SelectRows); transactionView->setSelectionMode(QAbstractItemView::ExtendedSelection); transactionView->setSortingEnabled(true); - transactionView->sortByColumn(TransactionTableModel::Status, Qt::DescendingOrder); + transactionView->sortByColumn(TransactionTableModel::Date, Qt::DescendingOrder); transactionView->verticalHeader()->hide(); transactionView->setColumnWidth(TransactionTableModel::Status, STATUS_COLUMN_WIDTH); diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp index a0d0e70442..3867310cd6 100644 --- a/src/qt/walletmodel.cpp +++ b/src/qt/walletmodel.cpp @@ -447,7 +447,7 @@ bool WalletModel::changePassphrase(const SecureString &oldPass, const SecureStri bool WalletModel::backupWallet(const QString &filename) { - return BackupWallet(*wallet, filename.toLocal8Bit().data()); + return wallet->BackupWallet(filename.toLocal8Bit().data()); } // Handlers for core signals diff --git a/src/random.cpp b/src/random.cpp index 6155c0d8cf..d9a8cc145e 100644 --- a/src/random.cpp +++ b/src/random.cpp @@ -5,14 +5,17 @@ #include "random.h" +#include "crypto/sha512.h" #include "support/cleanse.h" #ifdef WIN32 #include "compat.h" // for Windows API +#include <wincrypt.h> #endif #include "serialize.h" // for begin_ptr(vec) #include "util.h" // for LogPrint() #include "utilstrencodings.h" // for GetTime() +#include <stdlib.h> #include <limits> #ifndef WIN32 @@ -22,6 +25,12 @@ #include <openssl/err.h> #include <openssl/rand.h> +static void RandFailure() +{ + LogPrintf("Failed to read randomness, aborting\n"); + abort(); +} + static inline int64_t GetPerformanceCounter() { int64_t nCounter = 0; @@ -43,7 +52,7 @@ void RandAddSeed() memory_cleanse((void*)&nCounter, sizeof(nCounter)); } -void RandAddSeedPerfmon() +static void RandAddSeedPerfmon() { RandAddSeed(); @@ -83,14 +92,65 @@ void RandAddSeedPerfmon() #endif } +/** Get 32 bytes of system entropy. */ +static void GetOSRand(unsigned char *ent32) +{ +#ifdef WIN32 + HCRYPTPROV hProvider; + int ret = CryptAcquireContextW(&hProvider, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT); + if (!ret) { + RandFailure(); + } + ret = CryptGenRandom(hProvider, 32, ent32); + if (!ret) { + RandFailure(); + } + CryptReleaseContext(hProvider, 0); +#else + int f = open("/dev/urandom", O_RDONLY); + if (f == -1) { + RandFailure(); + } + int have = 0; + do { + ssize_t n = read(f, ent32 + have, 32 - have); + if (n <= 0 || n + have > 32) { + RandFailure(); + } + have += n; + } while (have < 32); + close(f); +#endif +} + void GetRandBytes(unsigned char* buf, int num) { if (RAND_bytes(buf, num) != 1) { - LogPrintf("%s: OpenSSL RAND_bytes() failed with error: %s\n", __func__, ERR_error_string(ERR_get_error(), NULL)); - assert(false); + RandFailure(); } } +void GetStrongRandBytes(unsigned char* out, int num) +{ + assert(num <= 32); + CSHA512 hasher; + unsigned char buf[64]; + + // First source: OpenSSL's RNG + RandAddSeedPerfmon(); + GetRandBytes(buf, 32); + hasher.Write(buf, 32); + + // Second source: OS RNG + GetOSRand(buf); + hasher.Write(buf, 32); + + // Produce output + hasher.Finalize(buf); + memcpy(out, buf, num); + memory_cleanse(buf, 64); +} + uint64_t GetRand(uint64_t nMax) { if (nMax == 0) diff --git a/src/random.h b/src/random.h index 1a2d3e8ee2..31b80bd565 100644 --- a/src/random.h +++ b/src/random.h @@ -10,11 +10,8 @@ #include <stdint.h> -/** - * Seed OpenSSL PRNG with additional entropy data - */ +/* Seed OpenSSL PRNG with additional entropy data */ void RandAddSeed(); -void RandAddSeedPerfmon(); /** * Functions to gather random data via the OpenSSL PRNG @@ -25,6 +22,12 @@ int GetRandInt(int nMax); uint256 GetRandHash(); /** + * Function to gather random data from multiple sources, failing whenever any + * of those source fail to provide a result. + */ +void GetStrongRandBytes(unsigned char* buf, int num); + +/** * Seed insecure_rand using the random pool. * @param Deterministic Use a deterministic seed */ diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index cf3c73c4df..1bb365d36c 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -183,6 +183,60 @@ UniValue getdifficulty(const UniValue& params, bool fHelp) return GetDifficulty(); } +std::string EntryDescriptionString() +{ + return " \"size\" : n, (numeric) transaction size in bytes\n" + " \"fee\" : n, (numeric) transaction fee in " + CURRENCY_UNIT + "\n" + " \"modifiedfee\" : n, (numeric) transaction fee with fee deltas used for mining priority\n" + " \"time\" : n, (numeric) local time transaction entered pool in seconds since 1 Jan 1970 GMT\n" + " \"height\" : n, (numeric) block height when transaction entered pool\n" + " \"startingpriority\" : n, (numeric) priority when transaction entered pool\n" + " \"currentpriority\" : n, (numeric) transaction priority now\n" + " \"descendantcount\" : n, (numeric) number of in-mempool descendant transactions (including this one)\n" + " \"descendantsize\" : n, (numeric) size of in-mempool descendants (including this one)\n" + " \"descendantfees\" : n, (numeric) modified fees (see above) of in-mempool descendants (including this one)\n" + " \"ancestorcount\" : n, (numeric) number of in-mempool ancestor transactions (including this one)\n" + " \"ancestorsize\" : n, (numeric) size of in-mempool ancestors (including this one)\n" + " \"ancestorfees\" : n, (numeric) modified fees (see above) of in-mempool ancestors (including this one)\n" + " \"depends\" : [ (array) unconfirmed transactions used as inputs for this transaction\n" + " \"transactionid\", (string) parent transaction id\n" + " ... ]\n"; +} + +void entryToJSON(UniValue &info, const CTxMemPoolEntry &e) +{ + AssertLockHeld(mempool.cs); + + info.push_back(Pair("size", (int)e.GetTxSize())); + info.push_back(Pair("fee", ValueFromAmount(e.GetFee()))); + info.push_back(Pair("modifiedfee", ValueFromAmount(e.GetModifiedFee()))); + info.push_back(Pair("time", e.GetTime())); + info.push_back(Pair("height", (int)e.GetHeight())); + info.push_back(Pair("startingpriority", e.GetPriority(e.GetHeight()))); + info.push_back(Pair("currentpriority", e.GetPriority(chainActive.Height()))); + info.push_back(Pair("descendantcount", e.GetCountWithDescendants())); + info.push_back(Pair("descendantsize", e.GetSizeWithDescendants())); + info.push_back(Pair("descendantfees", e.GetModFeesWithDescendants())); + info.push_back(Pair("ancestorcount", e.GetCountWithAncestors())); + info.push_back(Pair("ancestorsize", e.GetSizeWithAncestors())); + info.push_back(Pair("ancestorfees", e.GetModFeesWithAncestors())); + const CTransaction& tx = e.GetTx(); + set<string> setDepends; + BOOST_FOREACH(const CTxIn& txin, tx.vin) + { + if (mempool.exists(txin.prevout.hash)) + setDepends.insert(txin.prevout.hash.ToString()); + } + + UniValue depends(UniValue::VARR); + BOOST_FOREACH(const string& dep, setDepends) + { + depends.push_back(dep); + } + + info.push_back(Pair("depends", depends)); +} + UniValue mempoolToJSON(bool fVerbose = false) { if (fVerbose) @@ -193,31 +247,7 @@ UniValue mempoolToJSON(bool fVerbose = false) { const uint256& hash = e.GetTx().GetHash(); UniValue info(UniValue::VOBJ); - info.push_back(Pair("size", (int)e.GetTxSize())); - info.push_back(Pair("fee", ValueFromAmount(e.GetFee()))); - info.push_back(Pair("modifiedfee", ValueFromAmount(e.GetModifiedFee()))); - info.push_back(Pair("time", e.GetTime())); - info.push_back(Pair("height", (int)e.GetHeight())); - info.push_back(Pair("startingpriority", e.GetPriority(e.GetHeight()))); - info.push_back(Pair("currentpriority", e.GetPriority(chainActive.Height()))); - info.push_back(Pair("descendantcount", e.GetCountWithDescendants())); - info.push_back(Pair("descendantsize", e.GetSizeWithDescendants())); - info.push_back(Pair("descendantfees", e.GetModFeesWithDescendants())); - const CTransaction& tx = e.GetTx(); - set<string> setDepends; - BOOST_FOREACH(const CTxIn& txin, tx.vin) - { - if (mempool.exists(txin.prevout.hash)) - setDepends.insert(txin.prevout.hash.ToString()); - } - - UniValue depends(UniValue::VARR); - BOOST_FOREACH(const string& dep, setDepends) - { - depends.push_back(dep); - } - - info.push_back(Pair("depends", depends)); + entryToJSON(info, e); o.push_back(Pair(hash.ToString(), info)); } return o; @@ -251,20 +281,8 @@ UniValue getrawmempool(const UniValue& params, bool fHelp) "\nResult: (for verbose = true):\n" "{ (json object)\n" " \"transactionid\" : { (json object)\n" - " \"size\" : n, (numeric) transaction size in bytes\n" - " \"fee\" : n, (numeric) transaction fee in " + CURRENCY_UNIT + "\n" - " \"modifiedfee\" : n, (numeric) transaction fee with fee deltas used for mining priority\n" - " \"time\" : n, (numeric) local time transaction entered pool in seconds since 1 Jan 1970 GMT\n" - " \"height\" : n, (numeric) block height when transaction entered pool\n" - " \"startingpriority\" : n, (numeric) priority when transaction entered pool\n" - " \"currentpriority\" : n, (numeric) transaction priority now\n" - " \"descendantcount\" : n, (numeric) number of in-mempool descendant transactions (including this one)\n" - " \"descendantsize\" : n, (numeric) size of in-mempool descendants (including this one)\n" - " \"descendantfees\" : n, (numeric) modified fees (see above) of in-mempool descendants (including this one)\n" - " \"depends\" : [ (array) unconfirmed transactions used as inputs for this transaction\n" - " \"transactionid\", (string) parent transaction id\n" - " ... ]\n" - " }, ...\n" + + EntryDescriptionString() + + " }, ...\n" "}\n" "\nExamples\n" + HelpExampleCli("getrawmempool", "true") @@ -280,6 +298,167 @@ UniValue getrawmempool(const UniValue& params, bool fHelp) return mempoolToJSON(fVerbose); } +UniValue getmempoolancestors(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() < 1 || params.size() > 2) { + throw runtime_error( + "getmempoolancestors txid (verbose)\n" + "\nIf txid is in the mempool, returns all in-mempool ancestors.\n" + "\nArguments:\n" + "1. \"txid\" (string, required) The transaction id (must be in mempool)\n" + "2. verbose (boolean, optional, default=false) true for a json object, false for array of transaction ids\n" + "\nResult (for verbose=false):\n" + "[ (json array of strings)\n" + " \"transactionid\" (string) The transaction id of an in-mempool ancestor transaction\n" + " ,...\n" + "]\n" + "\nResult (for verbose=true):\n" + "{ (json object)\n" + " \"transactionid\" : { (json object)\n" + + EntryDescriptionString() + + " }, ...\n" + "}\n" + "\nExamples\n" + + HelpExampleCli("getmempoolancestors", "\"mytxid\"") + + HelpExampleRpc("getmempoolancestors", "\"mytxid\"") + ); + } + + bool fVerbose = false; + if (params.size() > 1) + fVerbose = params[1].get_bool(); + + uint256 hash = ParseHashV(params[0], "parameter 1"); + + LOCK(mempool.cs); + + CTxMemPool::txiter it = mempool.mapTx.find(hash); + if (it == mempool.mapTx.end()) { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Transaction not in mempool"); + } + + CTxMemPool::setEntries setAncestors; + uint64_t noLimit = std::numeric_limits<uint64_t>::max(); + std::string dummy; + mempool.CalculateMemPoolAncestors(*it, setAncestors, noLimit, noLimit, noLimit, noLimit, dummy, false); + + if (!fVerbose) { + UniValue o(UniValue::VARR); + BOOST_FOREACH(CTxMemPool::txiter ancestorIt, setAncestors) { + o.push_back(ancestorIt->GetTx().GetHash().ToString()); + } + + return o; + } else { + UniValue o(UniValue::VOBJ); + BOOST_FOREACH(CTxMemPool::txiter ancestorIt, setAncestors) { + const CTxMemPoolEntry &e = *ancestorIt; + const uint256& hash = e.GetTx().GetHash(); + UniValue info(UniValue::VOBJ); + entryToJSON(info, e); + o.push_back(Pair(hash.ToString(), info)); + } + return o; + } +} + +UniValue getmempooldescendants(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() < 1 || params.size() > 2) { + throw runtime_error( + "getmempooldescendants txid (verbose)\n" + "\nIf txid is in the mempool, returns all in-mempool descendants.\n" + "\nArguments:\n" + "1. \"txid\" (string, required) The transaction id (must be in mempool)\n" + "2. verbose (boolean, optional, default=false) true for a json object, false for array of transaction ids\n" + "\nResult (for verbose=false):\n" + "[ (json array of strings)\n" + " \"transactionid\" (string) The transaction id of an in-mempool descendant transaction\n" + " ,...\n" + "]\n" + "\nResult (for verbose=true):\n" + "{ (json object)\n" + " \"transactionid\" : { (json object)\n" + + EntryDescriptionString() + + " }, ...\n" + "}\n" + "\nExamples\n" + + HelpExampleCli("getmempooldescendants", "\"mytxid\"") + + HelpExampleRpc("getmempooldescendants", "\"mytxid\"") + ); + } + + bool fVerbose = false; + if (params.size() > 1) + fVerbose = params[1].get_bool(); + + uint256 hash = ParseHashV(params[0], "parameter 1"); + + LOCK(mempool.cs); + + CTxMemPool::txiter it = mempool.mapTx.find(hash); + if (it == mempool.mapTx.end()) { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Transaction not in mempool"); + } + + CTxMemPool::setEntries setDescendants; + mempool.CalculateDescendants(it, setDescendants); + // CTxMemPool::CalculateDescendants will include the given tx + setDescendants.erase(it); + + if (!fVerbose) { + UniValue o(UniValue::VARR); + BOOST_FOREACH(CTxMemPool::txiter descendantIt, setDescendants) { + o.push_back(descendantIt->GetTx().GetHash().ToString()); + } + + return o; + } else { + UniValue o(UniValue::VOBJ); + BOOST_FOREACH(CTxMemPool::txiter descendantIt, setDescendants) { + const CTxMemPoolEntry &e = *descendantIt; + const uint256& hash = e.GetTx().GetHash(); + UniValue info(UniValue::VOBJ); + entryToJSON(info, e); + o.push_back(Pair(hash.ToString(), info)); + } + return o; + } +} + +UniValue getmempoolentry(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() != 1) { + throw runtime_error( + "getmempoolentry txid\n" + "\nReturns mempool data for given transaction\n" + "\nArguments:\n" + "1. \"txid\" (string, required) The transaction id (must be in mempool)\n" + "\nResult:\n" + "{ (json object)\n" + + EntryDescriptionString() + + "}\n" + "\nExamples\n" + + HelpExampleCli("getmempoolentry", "\"mytxid\"") + + HelpExampleRpc("getmempoolentry", "\"mytxid\"") + ); + } + + uint256 hash = ParseHashV(params[0], "parameter 1"); + + LOCK(mempool.cs); + + CTxMemPool::txiter it = mempool.mapTx.find(hash); + if (it == mempool.mapTx.end()) { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Transaction not in mempool"); + } + + const CTxMemPoolEntry &e = *it; + UniValue info(UniValue::VOBJ); + entryToJSON(info, e); + return info; +} + UniValue getblockhash(const UniValue& params, bool fHelp) { if (fHelp || params.size() != 1) @@ -1004,6 +1183,9 @@ static const CRPCCommand commands[] = { "blockchain", "getblockheader", &getblockheader, true }, { "blockchain", "getchaintips", &getchaintips, true }, { "blockchain", "getdifficulty", &getdifficulty, true }, + { "blockchain", "getmempoolancestors", &getmempoolancestors, true }, + { "blockchain", "getmempooldescendants", &getmempooldescendants, true }, + { "blockchain", "getmempoolentry", &getmempoolentry, true }, { "blockchain", "getmempoolinfo", &getmempoolinfo, true }, { "blockchain", "getrawmempool", &getrawmempool, true }, { "blockchain", "gettxout", &gettxout, true }, diff --git a/src/rpc/client.cpp b/src/rpc/client.cpp index c89af6bfa7..d0675fdb49 100644 --- a/src/rpc/client.cpp +++ b/src/rpc/client.cpp @@ -102,6 +102,8 @@ static const CRPCConvertParam vRPCConvertParams[] = { "prioritisetransaction", 2 }, { "setban", 2 }, { "setban", 3 }, + { "getmempoolancestors", 1 }, + { "getmempooldescendants", 1 }, }; class CRPCConvertTable diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp index 9a7d9d53a0..94eeea91f3 100644 --- a/src/rpc/mining.cpp +++ b/src/rpc/mining.cpp @@ -8,6 +8,7 @@ #include "chain.h" #include "chainparams.h" #include "consensus/consensus.h" +#include "consensus/params.h" #include "consensus/validation.h" #include "core_io.h" #include "init.h" @@ -111,7 +112,7 @@ UniValue generateBlocks(boost::shared_ptr<CReserveScript> coinbaseScript, int nG UniValue blockHashes(UniValue::VARR); while (nHeight < nHeightEnd) { - std::unique_ptr<CBlockTemplate> pblocktemplate(CreateNewBlock(Params(), coinbaseScript->reserveScript)); + std::unique_ptr<CBlockTemplate> pblocktemplate(BlockAssembler(Params()).CreateNewBlock(coinbaseScript->reserveScript)); if (!pblocktemplate.get()) throw JSONRPCError(RPC_INTERNAL_ERROR, "Couldn't create new block"); CBlock *pblock = &pblocktemplate->block; @@ -303,6 +304,15 @@ static UniValue BIP22ValidationResult(const CValidationState& state) return "valid?"; } +std::string gbt_vb_name(const Consensus::DeploymentPos pos) { + const struct BIP9DeploymentInfo& vbinfo = VersionBitsDeploymentInfo[pos]; + std::string s = vbinfo.name; + if (!vbinfo.gbt_force) { + s.insert(s.begin(), '!'); + } + return s; +} + UniValue getblocktemplate(const UniValue& params, bool fHelp) { if (fHelp || params.size() > 1) @@ -310,7 +320,9 @@ UniValue getblocktemplate(const UniValue& params, bool fHelp) "getblocktemplate ( \"jsonrequestobject\" )\n" "\nIf the request parameters include a 'mode' key, that is used to explicitly select between the default 'template' request or a 'proposal'.\n" "It returns data needed to construct a block to work on.\n" - "See https://en.bitcoin.it/wiki/BIP_0022 for full specification.\n" + "For full specification, see BIPs 22 and 9:\n" + " https://github.com/bitcoin/bips/blob/master/bip-0022.mediawiki\n" + " https://github.com/bitcoin/bips/blob/master/bip-0009.mediawiki#getblocktemplate_changes\n" "\nArguments:\n" "1. \"jsonrequestobject\" (string, optional) A json object in the following spec\n" @@ -326,6 +338,12 @@ UniValue getblocktemplate(const UniValue& params, bool fHelp) "\nResult:\n" "{\n" " \"version\" : n, (numeric) The block version\n" + " \"rules\" : [ \"rulename\", ... ], (array of strings) specific block rules that are to be enforced\n" + " \"vbavailable\" : { (json object) set of pending, supported versionbit (BIP 9) softfork deployments\n" + " \"rulename\" : bitnumber (numeric) identifies the bit number as indicating acceptance and readiness for the named softfork rule\n" + " ,...\n" + " },\n" + " \"vbrequired\" : n, (numeric) bit mask of versionbits the server requires set in submissions\n" " \"previousblockhash\" : \"xxxx\", (string) The hash of current highest block\n" " \"transactions\" : [ (array) contents of non-coinbase transactions that should be included in the next block\n" " {\n" @@ -369,6 +387,8 @@ UniValue getblocktemplate(const UniValue& params, bool fHelp) std::string strMode = "template"; UniValue lpval = NullUniValue; + std::set<std::string> setClientRules; + int64_t nMaxVersionPreVB = -1; if (params.size() > 0) { const UniValue& oparam = params[0].get_obj(); @@ -412,6 +432,20 @@ UniValue getblocktemplate(const UniValue& params, bool fHelp) TestBlockValidity(state, Params(), block, pindexPrev, false, true); return BIP22ValidationResult(state); } + + const UniValue& aClientRules = find_value(oparam, "rules"); + if (aClientRules.isArray()) { + for (unsigned int i = 0; i < aClientRules.size(); ++i) { + const UniValue& v = aClientRules[i]; + setClientRules.insert(v.get_str()); + } + } else { + // NOTE: It is important that this NOT be read if versionbits is supported + const UniValue& uvMaxVersion = find_value(oparam, "maxversion"); + if (uvMaxVersion.isNum()) { + nMaxVersionPreVB = uvMaxVersion.get_int64(); + } + } } if (strMode != "template") @@ -493,7 +527,7 @@ UniValue getblocktemplate(const UniValue& params, bool fHelp) pblocktemplate = NULL; } CScript scriptDummy = CScript() << OP_TRUE; - pblocktemplate = CreateNewBlock(Params(), scriptDummy); + pblocktemplate = BlockAssembler(Params()).CreateNewBlock(scriptDummy); if (!pblocktemplate) throw JSONRPCError(RPC_OUT_OF_MEMORY, "Out of memory"); @@ -501,9 +535,10 @@ UniValue getblocktemplate(const UniValue& params, bool fHelp) pindexPrev = pindexPrevNew; } CBlock* pblock = &pblocktemplate->block; // pointer for convenience + const Consensus::Params& consensusParams = Params().GetConsensus(); // Update nTime - UpdateTime(pblock, Params().GetConsensus(), pindexPrev); + UpdateTime(pblock, consensusParams, pindexPrev); pblock->nNonce = 0; UniValue aCaps(UniValue::VARR); aCaps.push_back("proposal"); @@ -544,17 +579,69 @@ UniValue getblocktemplate(const UniValue& params, bool fHelp) arith_uint256 hashTarget = arith_uint256().SetCompact(pblock->nBits); - static UniValue aMutable(UniValue::VARR); - if (aMutable.empty()) - { - aMutable.push_back("time"); - aMutable.push_back("transactions"); - aMutable.push_back("prevblock"); - } + UniValue aMutable(UniValue::VARR); + aMutable.push_back("time"); + aMutable.push_back("transactions"); + aMutable.push_back("prevblock"); UniValue result(UniValue::VOBJ); result.push_back(Pair("capabilities", aCaps)); + + UniValue aRules(UniValue::VARR); + UniValue vbavailable(UniValue::VOBJ); + for (int i = 0; i < (int)Consensus::MAX_VERSION_BITS_DEPLOYMENTS; ++i) { + Consensus::DeploymentPos pos = Consensus::DeploymentPos(i); + ThresholdState state = VersionBitsState(pindexPrev, consensusParams, pos, versionbitscache); + switch (state) { + case THRESHOLD_DEFINED: + case THRESHOLD_FAILED: + // Not exposed to GBT at all + break; + case THRESHOLD_LOCKED_IN: + // Ensure bit is set in block version + pblock->nVersion |= VersionBitsMask(consensusParams, pos); + // FALL THROUGH to get vbavailable set... + case THRESHOLD_STARTED: + { + const struct BIP9DeploymentInfo& vbinfo = VersionBitsDeploymentInfo[pos]; + vbavailable.push_back(Pair(gbt_vb_name(pos), consensusParams.vDeployments[pos].bit)); + if (setClientRules.find(vbinfo.name) == setClientRules.end()) { + if (!vbinfo.gbt_force) { + // If the client doesn't support this, don't indicate it in the [default] version + pblock->nVersion &= ~VersionBitsMask(consensusParams, pos); + } + } + break; + } + case THRESHOLD_ACTIVE: + { + // Add to rules only + const struct BIP9DeploymentInfo& vbinfo = VersionBitsDeploymentInfo[pos]; + aRules.push_back(gbt_vb_name(pos)); + if (setClientRules.find(vbinfo.name) == setClientRules.end()) { + // Not supported by the client; make sure it's safe to proceed + if (!vbinfo.gbt_force) { + // If we do anything other than throw an exception here, be sure version/force isn't sent to old clients + throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Support for '%s' rule requires explicit client support", vbinfo.name)); + } + } + break; + } + } + } result.push_back(Pair("version", pblock->nVersion)); + result.push_back(Pair("rules", aRules)); + result.push_back(Pair("vbavailable", vbavailable)); + result.push_back(Pair("vbrequired", int(0))); + + if (nMaxVersionPreVB >= 2) { + // If VB is supported by the client, nMaxVersionPreVB is -1, so we won't get here + // Because BIP 34 changed how the generation transaction is serialised, we can only use version/force back to v2 blocks + // This is safe to do [otherwise-]unconditionally only because we are throwing an exception above if a non-force deployment gets activated + // Note that this can probably also be removed entirely after the first BIP9 non-force deployment (ie, probably segwit) gets activated + aMutable.push_back("version/force"); + } + result.push_back(Pair("previousblockhash", pblock->hashPrevBlock.GetHex())); result.push_back(Pair("transactions", transactions)); result.push_back(Pair("coinbaseaux", aux)); diff --git a/src/rpc/net.cpp b/src/rpc/net.cpp index 320091b9c4..b85c7b2e1a 100644 --- a/src/rpc/net.cpp +++ b/src/rpc/net.cpp @@ -219,7 +219,7 @@ UniValue addnode(const UniValue& params, bool fHelp) if (strCommand == "onetry") { CAddress addr; - OpenNetworkConnection(addr, NULL, strNode.c_str()); + OpenNetworkConnection(addr, false, NULL, strNode.c_str()); return NullUniValue; } @@ -271,25 +271,22 @@ UniValue getaddednodeinfo(const UniValue& params, bool fHelp) { if (fHelp || params.size() < 1 || params.size() > 2) throw runtime_error( - "getaddednodeinfo dns ( \"node\" )\n" + "getaddednodeinfo dummy ( \"node\" )\n" "\nReturns information about the given added node, or all added nodes\n" "(note that onetry addnodes are not listed here)\n" - "If dns is false, only a list of added nodes will be provided,\n" - "otherwise connected information will also be available.\n" "\nArguments:\n" - "1. dns (boolean, required) If false, only a list of added nodes will be provided, otherwise connected information will also be available.\n" + "1. dummy (boolean, required) Kept for historical purposes but ignored\n" "2. \"node\" (string, optional) If provided, return information about this specific node, otherwise all nodes are returned.\n" "\nResult:\n" "[\n" " {\n" - " \"addednode\" : \"192.168.0.201\", (string) The node ip address\n" + " \"addednode\" : \"192.168.0.201\", (string) The node ip address or name (as provided to addnode)\n" " \"connected\" : true|false, (boolean) If connected\n" - " \"addresses\" : [\n" + " \"addresses\" : [ (list of objects) Only when connected = true\n" " {\n" - " \"address\" : \"192.168.0.201:8333\", (string) The bitcoin server host and port\n" + " \"address\" : \"192.168.0.201:8333\", (string) The bitcoin server IP and port we're connected to\n" " \"connected\" : \"outbound\" (string) connection, inbound or outbound\n" " }\n" - " ,...\n" " ]\n" " }\n" " ,...\n" @@ -300,83 +297,35 @@ UniValue getaddednodeinfo(const UniValue& params, bool fHelp) + HelpExampleRpc("getaddednodeinfo", "true, \"192.168.0.201\"") ); - bool fDns = params[0].get_bool(); + std::vector<AddedNodeInfo> vInfo = GetAddedNodeInfo(); - list<string> laddedNodes(0); - if (params.size() == 1) - { - LOCK(cs_vAddedNodes); - BOOST_FOREACH(const std::string& strAddNode, vAddedNodes) - laddedNodes.push_back(strAddNode); - } - else - { - string strNode = params[1].get_str(); - LOCK(cs_vAddedNodes); - BOOST_FOREACH(const std::string& strAddNode, vAddedNodes) { - if (strAddNode == strNode) - { - laddedNodes.push_back(strAddNode); + if (params.size() == 2) { + bool found = false; + for (const AddedNodeInfo& info : vInfo) { + if (info.strAddedNode == params[1].get_str()) { + vInfo.assign(1, info); + found = true; break; } } - if (laddedNodes.size() == 0) + if (!found) { throw JSONRPCError(RPC_CLIENT_NODE_NOT_ADDED, "Error: Node has not been added."); - } - - UniValue ret(UniValue::VARR); - if (!fDns) - { - BOOST_FOREACH (const std::string& strAddNode, laddedNodes) { - UniValue obj(UniValue::VOBJ); - obj.push_back(Pair("addednode", strAddNode)); - ret.push_back(obj); } - return ret; } - list<pair<string, vector<CService> > > laddedAddreses(0); - BOOST_FOREACH(const std::string& strAddNode, laddedNodes) { - vector<CService> vservNode(0); - if(Lookup(strAddNode.c_str(), vservNode, Params().GetDefaultPort(), fNameLookup, 0)) - laddedAddreses.push_back(make_pair(strAddNode, vservNode)); - else - { - UniValue obj(UniValue::VOBJ); - obj.push_back(Pair("addednode", strAddNode)); - obj.push_back(Pair("connected", false)); - UniValue addresses(UniValue::VARR); - obj.push_back(Pair("addresses", addresses)); - ret.push_back(obj); - } - } + UniValue ret(UniValue::VARR); - LOCK(cs_vNodes); - for (list<pair<string, vector<CService> > >::iterator it = laddedAddreses.begin(); it != laddedAddreses.end(); it++) - { + for (const AddedNodeInfo& info : vInfo) { UniValue obj(UniValue::VOBJ); - obj.push_back(Pair("addednode", it->first)); - + obj.push_back(Pair("addednode", info.strAddedNode)); + obj.push_back(Pair("connected", info.fConnected)); UniValue addresses(UniValue::VARR); - bool fConnected = false; - BOOST_FOREACH(const CService& addrNode, it->second) { - bool fFound = false; - UniValue node(UniValue::VOBJ); - node.push_back(Pair("address", addrNode.ToString())); - BOOST_FOREACH(CNode* pnode, vNodes) { - if (pnode->addr == addrNode) - { - fFound = true; - fConnected = true; - node.push_back(Pair("connected", pnode->fInbound ? "inbound" : "outbound")); - break; - } - } - if (!fFound) - node.push_back(Pair("connected", "false")); - addresses.push_back(node); + if (info.fConnected) { + UniValue address(UniValue::VOBJ); + address.push_back(Pair("address", info.resolvedAddress.ToString())); + address.push_back(Pair("connected", info.fInbound ? "inbound" : "outbound")); + addresses.push_back(address); } - obj.push_back(Pair("connected", fConnected)); obj.push_back(Pair("addresses", addresses)); ret.push_back(obj); } @@ -460,6 +409,7 @@ UniValue getnetworkinfo(const UniValue& params, bool fHelp) " \"subversion\": \"/Satoshi:x.x.x/\", (string) the server subversion string\n" " \"protocolversion\": xxxxx, (numeric) the protocol version\n" " \"localservices\": \"xxxxxxxxxxxxxxxx\", (string) the services we offer to the network\n" + " \"localrelay\": true|false, (bool) true if transaction relay is requested from peers\n" " \"timeoffset\": xxxxx, (numeric) the time offset\n" " \"connections\": xxxxx, (numeric) the number of connections\n" " \"networks\": [ (array) information per network\n" @@ -494,6 +444,7 @@ UniValue getnetworkinfo(const UniValue& params, bool fHelp) obj.push_back(Pair("subversion", strSubVersion)); obj.push_back(Pair("protocolversion",PROTOCOL_VERSION)); obj.push_back(Pair("localservices", strprintf("%016x", nLocalServices))); + obj.push_back(Pair("localrelay", fRelayTxes)); obj.push_back(Pair("timeoffset", GetTimeOffset())); obj.push_back(Pair("connections", (int)vNodes.size())); obj.push_back(Pair("networks", GetNetworksInfo())); @@ -565,20 +516,12 @@ UniValue setban(const UniValue& params, bool fHelp) absolute = true; isSubnet ? CNode::Ban(subNet, BanReasonManuallyAdded, banTime, absolute) : CNode::Ban(netAddr, BanReasonManuallyAdded, banTime, absolute); - - //disconnect possible nodes - while(CNode *bannedNode = (isSubnet ? FindNode(subNet) : FindNode(netAddr))) - bannedNode->fDisconnect = true; } else if(strCommand == "remove") { if (!( isSubnet ? CNode::Unban(subNet) : CNode::Unban(netAddr) )) throw JSONRPCError(RPC_MISC_ERROR, "Error: Unban failed"); } - - DumpBanlist(); //store banlist to disk - uiInterface.BannedListChanged(); - return NullUniValue; } @@ -624,8 +567,6 @@ UniValue clearbanned(const UniValue& params, bool fHelp) ); CNode::ClearBanned(); - DumpBanlist(); //store banlist to disk - uiInterface.BannedListChanged(); return NullUniValue; } diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp index bec7ebe55f..9723e394d6 100644 --- a/src/rpc/rawtransaction.cpp +++ b/src/rpc/rawtransaction.cpp @@ -211,7 +211,7 @@ UniValue gettxoutproof(const UniValue& params, bool fHelp) "\nNOTE: By default this function only works sometimes. This is when there is an\n" "unspent output in the utxo for this transaction. To make it always work,\n" "you need to maintain a transaction index, using the -txindex command line option or\n" - "specify the block in which the transaction is included in manually (by blockhash).\n" + "specify the block in which the transaction is included manually (by blockhash).\n" "\nReturn the raw transaction data.\n" "\nArguments:\n" "1. \"txids\" (string) A json array of txids to filter\n" @@ -334,6 +334,7 @@ UniValue createrawtransaction(const UniValue& params, bool fHelp) " {\n" " \"txid\":\"id\", (string, required) The transaction id\n" " \"vout\":n (numeric, required) The output number\n" + " \"sequence\":n (numeric, optional) The sequence number\n" " }\n" " ,...\n" " ]\n" @@ -384,6 +385,17 @@ UniValue createrawtransaction(const UniValue& params, bool fHelp) throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, vout must be positive"); uint32_t nSequence = (rawTx.nLockTime ? std::numeric_limits<uint32_t>::max() - 1 : std::numeric_limits<uint32_t>::max()); + + // set the sequence number if passed in the parameters object + const UniValue& sequenceObj = find_value(o, "sequence"); + if (sequenceObj.isNum()) { + int64_t seqNr64 = sequenceObj.get_int64(); + if (seqNr64 < 0 || seqNr64 > std::numeric_limits<uint32_t>::max()) + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, sequence number is out of range"); + else + nSequence = (uint32_t)seqNr64; + } + CTxIn in(COutPoint(txid, nOutput), CScript(), nSequence); rawTx.vin.push_back(in); @@ -675,7 +687,12 @@ UniValue signrawtransaction(const UniValue& params, bool fHelp) UniValue prevOut = p.get_obj(); - RPCTypeCheckObj(prevOut, boost::assign::map_list_of("txid", UniValue::VSTR)("vout", UniValue::VNUM)("scriptPubKey", UniValue::VSTR)); + RPCTypeCheckObj(prevOut, + { + {"txid", UniValueType(UniValue::VSTR)}, + {"vout", UniValueType(UniValue::VNUM)}, + {"scriptPubKey", UniValueType(UniValue::VSTR)}, + }); uint256 txid = ParseHashO(prevOut, "txid"); @@ -703,7 +720,13 @@ UniValue signrawtransaction(const UniValue& params, bool fHelp) // if redeemScript given and not using the local wallet (private keys // given), add redeemScript to the tempKeystore so it can be signed: if (fGivenKeys && scriptPubKey.IsPayToScriptHash()) { - RPCTypeCheckObj(prevOut, boost::assign::map_list_of("txid", UniValue::VSTR)("vout", UniValue::VNUM)("scriptPubKey", UniValue::VSTR)("redeemScript",UniValue::VSTR)); + RPCTypeCheckObj(prevOut, + { + {"txid", UniValueType(UniValue::VSTR)}, + {"vout", UniValueType(UniValue::VNUM)}, + {"scriptPubKey", UniValueType(UniValue::VSTR)}, + {"redeemScript", UniValueType(UniValue::VSTR)}, + }); UniValue v = find_value(prevOut, "redeemScript"); if (!v.isNull()) { vector<unsigned char> rsData(ParseHexV(v, "redeemScript")); @@ -743,6 +766,9 @@ UniValue signrawtransaction(const UniValue& params, bool fHelp) // Script verification errors UniValue vErrors(UniValue::VARR); + // Use CTransaction for the constant parts of the + // transaction to avoid rehashing. + const CTransaction txConst(mergedTx); // Sign what we can: for (unsigned int i = 0; i < mergedTx.vin.size(); i++) { CTxIn& txin = mergedTx.vin[i]; @@ -760,10 +786,10 @@ UniValue signrawtransaction(const UniValue& params, bool fHelp) // ... and merge in other signatures: BOOST_FOREACH(const CMutableTransaction& txv, txVariants) { - txin.scriptSig = CombineSignatures(prevPubKey, mergedTx, i, txin.scriptSig, txv.vin[i].scriptSig); + txin.scriptSig = CombineSignatures(prevPubKey, txConst, i, txin.scriptSig, txv.vin[i].scriptSig); } ScriptError serror = SCRIPT_ERR_OK; - if (!VerifyScript(txin.scriptSig, prevPubKey, STANDARD_SCRIPT_VERIFY_FLAGS, MutableTransactionSignatureChecker(&mergedTx, i), &serror)) { + if (!VerifyScript(txin.scriptSig, prevPubKey, STANDARD_SCRIPT_VERIFY_FLAGS, TransactionSignatureChecker(&txConst, i), &serror)) { TxInErrorToJSON(txin, vErrors, ScriptErrorString(serror)); } } diff --git a/src/rpc/server.cpp b/src/rpc/server.cpp index d06a9142b6..23149baa6d 100644 --- a/src/rpc/server.cpp +++ b/src/rpc/server.cpp @@ -88,20 +88,18 @@ void RPCTypeCheck(const UniValue& params, } void RPCTypeCheckObj(const UniValue& o, - const map<string, UniValue::VType>& typesExpected, - bool fAllowNull, - bool fStrict) + const map<string, UniValueType>& typesExpected, + bool fAllowNull, + bool fStrict) { - BOOST_FOREACH(const PAIRTYPE(string, UniValue::VType)& t, typesExpected) - { + for (const auto& t : typesExpected) { const UniValue& v = find_value(o, t.first); if (!fAllowNull && v.isNull()) throw JSONRPCError(RPC_TYPE_ERROR, strprintf("Missing %s", t.first)); - if (!((v.type() == t.second) || (fAllowNull && (v.isNull())))) - { + if (!(t.second.typeAny || v.type() == t.second.type || (fAllowNull && v.isNull()))) { string err = strprintf("Expected type %s for %s, got %s", - uvTypeName(t.second), t.first, uvTypeName(v.type())); + uvTypeName(t.second.type), t.first, uvTypeName(v.type())); throw JSONRPCError(RPC_TYPE_ERROR, err); } } diff --git a/src/rpc/server.h b/src/rpc/server.h index b471336617..b5ccc153d0 100644 --- a/src/rpc/server.h +++ b/src/rpc/server.h @@ -32,6 +32,15 @@ namespace RPCServer class CBlockIndex; class CNetAddr; +/** Wrapper for UniValue::VType, which includes typeAny: + * Used to denote don't care type. Only used by RPCTypeCheckObj */ +struct UniValueType { + UniValueType(UniValue::VType _type) : typeAny(false), type(_type) {} + UniValueType() : typeAny(true) {} + bool typeAny; + UniValue::VType type; +}; + class JSONRequest { public: @@ -60,17 +69,17 @@ bool RPCIsInWarmup(std::string *statusOut); /** * Type-check arguments; throws JSONRPCError if wrong type given. Does not check that * the right number of arguments are passed, just that any passed are the correct type. - * Use like: RPCTypeCheck(params, boost::assign::list_of(str_type)(int_type)(obj_type)); */ void RPCTypeCheck(const UniValue& params, const std::list<UniValue::VType>& typesExpected, bool fAllowNull=false); /* Check for expected keys/value types in an Object. - Use like: RPCTypeCheckObj(object, boost::assign::map_list_of("name", str_type)("value", int_type)); */ void RPCTypeCheckObj(const UniValue& o, - const std::map<std::string, UniValue::VType>& typesExpected, bool fAllowNull=false, bool fStrict=false); + const std::map<std::string, UniValueType>& typesExpected, + bool fAllowNull = false, + bool fStrict = false); /** Opaque base class for timers returned by NewTimerFunc. * This provides no methods at the moment, but makes sure that delete diff --git a/src/test/DoS_tests.cpp b/src/test/DoS_tests.cpp index 95342498fa..4a373fc60b 100644 --- a/src/test/DoS_tests.cpp +++ b/src/test/DoS_tests.cpp @@ -45,7 +45,7 @@ BOOST_FIXTURE_TEST_SUITE(DoS_tests, TestingSetup) BOOST_AUTO_TEST_CASE(DoS_banning) { CNode::ClearBanned(); - CAddress addr1(ip(0xa0b0c001)); + CAddress addr1(ip(0xa0b0c001), NODE_NONE); CNode dummyNode1(INVALID_SOCKET, addr1, "", true); dummyNode1.nVersion = 1; Misbehaving(dummyNode1.GetId(), 100); // Should get banned @@ -53,7 +53,7 @@ BOOST_AUTO_TEST_CASE(DoS_banning) BOOST_CHECK(CNode::IsBanned(addr1)); BOOST_CHECK(!CNode::IsBanned(ip(0xa0b0c001|0x0000ff00))); // Different IP, not banned - CAddress addr2(ip(0xa0b0c002)); + CAddress addr2(ip(0xa0b0c002), NODE_NONE); CNode dummyNode2(INVALID_SOCKET, addr2, "", true); dummyNode2.nVersion = 1; Misbehaving(dummyNode2.GetId(), 50); @@ -69,7 +69,7 @@ BOOST_AUTO_TEST_CASE(DoS_banscore) { CNode::ClearBanned(); mapArgs["-banscore"] = "111"; // because 11 is my favorite number - CAddress addr1(ip(0xa0b0c001)); + CAddress addr1(ip(0xa0b0c001), NODE_NONE); CNode dummyNode1(INVALID_SOCKET, addr1, "", true); dummyNode1.nVersion = 1; Misbehaving(dummyNode1.GetId(), 100); @@ -90,7 +90,7 @@ BOOST_AUTO_TEST_CASE(DoS_bantime) int64_t nStartTime = GetTime(); SetMockTime(nStartTime); // Overrides future calls to GetTime() - CAddress addr(ip(0xa0b0c001)); + CAddress addr(ip(0xa0b0c001), NODE_NONE); CNode dummyNode(INVALID_SOCKET, addr, "", true); dummyNode.nVersion = 1; diff --git a/src/test/addrman_tests.cpp b/src/test/addrman_tests.cpp index 767b653e47..b6cec24b57 100644 --- a/src/test/addrman_tests.cpp +++ b/src/test/addrman_tests.cpp @@ -68,7 +68,7 @@ BOOST_AUTO_TEST_CASE(addrman_simple) // Test 2: Does Addrman::Add work as expected. CService addr1 = CService("250.1.1.1", 8333); - addrman.Add(CAddress(addr1), source); + addrman.Add(CAddress(addr1, NODE_NONE), source); BOOST_CHECK(addrman.size() == 1); CAddrInfo addr_ret1 = addrman.Select(); BOOST_CHECK(addr_ret1.ToString() == "250.1.1.1:8333"); @@ -76,14 +76,14 @@ BOOST_AUTO_TEST_CASE(addrman_simple) // Test 3: Does IP address deduplication work correctly. // Expected dup IP should not be added. CService addr1_dup = CService("250.1.1.1", 8333); - addrman.Add(CAddress(addr1_dup), source); + addrman.Add(CAddress(addr1_dup, NODE_NONE), source); BOOST_CHECK(addrman.size() == 1); // Test 5: New table has one addr and we add a diff addr we should // have two addrs. CService addr2 = CService("250.1.1.2", 8333); - addrman.Add(CAddress(addr2), source); + addrman.Add(CAddress(addr2, NODE_NONE), source); BOOST_CHECK(addrman.size() == 2); // Test 6: AddrMan::Clear() should empty the new table. @@ -106,18 +106,18 @@ BOOST_AUTO_TEST_CASE(addrman_ports) // Test 7; Addr with same IP but diff port does not replace existing addr. CService addr1 = CService("250.1.1.1", 8333); - addrman.Add(CAddress(addr1), source); + addrman.Add(CAddress(addr1, NODE_NONE), source); BOOST_CHECK(addrman.size() == 1); CService addr1_port = CService("250.1.1.1", 8334); - addrman.Add(CAddress(addr1_port), source); + addrman.Add(CAddress(addr1_port, NODE_NONE), source); BOOST_CHECK(addrman.size() == 1); CAddrInfo addr_ret2 = addrman.Select(); BOOST_CHECK(addr_ret2.ToString() == "250.1.1.1:8333"); // Test 8: Add same IP but diff port to tried table, it doesn't get added. // Perhaps this is not ideal behavior but it is the current behavior. - addrman.Good(CAddress(addr1_port)); + addrman.Good(CAddress(addr1_port, NODE_NONE)); BOOST_CHECK(addrman.size() == 1); bool newOnly = true; CAddrInfo addr_ret3 = addrman.Select(newOnly); @@ -136,7 +136,7 @@ BOOST_AUTO_TEST_CASE(addrman_select) // Test 9: Select from new with 1 addr in new. CService addr1 = CService("250.1.1.1", 8333); - addrman.Add(CAddress(addr1), source); + addrman.Add(CAddress(addr1, NODE_NONE), source); BOOST_CHECK(addrman.size() == 1); bool newOnly = true; @@ -144,7 +144,7 @@ BOOST_AUTO_TEST_CASE(addrman_select) BOOST_CHECK(addr_ret1.ToString() == "250.1.1.1:8333"); // Test 10: move addr to tried, select from new expected nothing returned. - addrman.Good(CAddress(addr1)); + addrman.Good(CAddress(addr1, NODE_NONE)); BOOST_CHECK(addrman.size() == 1); CAddrInfo addr_ret2 = addrman.Select(newOnly); BOOST_CHECK(addr_ret2.ToString() == "[::]:0"); @@ -160,21 +160,21 @@ BOOST_AUTO_TEST_CASE(addrman_select) CService addr3 = CService("250.3.2.2", 9999); CService addr4 = CService("250.3.3.3", 9999); - addrman.Add(CAddress(addr2), CService("250.3.1.1", 8333)); - addrman.Add(CAddress(addr3), CService("250.3.1.1", 8333)); - addrman.Add(CAddress(addr4), CService("250.4.1.1", 8333)); + addrman.Add(CAddress(addr2, NODE_NONE), CService("250.3.1.1", 8333)); + addrman.Add(CAddress(addr3, NODE_NONE), CService("250.3.1.1", 8333)); + addrman.Add(CAddress(addr4, NODE_NONE), CService("250.4.1.1", 8333)); // Add three addresses to tried table. CService addr5 = CService("250.4.4.4", 8333); CService addr6 = CService("250.4.5.5", 7777); CService addr7 = CService("250.4.6.6", 8333); - addrman.Add(CAddress(addr5), CService("250.3.1.1", 8333)); - addrman.Good(CAddress(addr5)); - addrman.Add(CAddress(addr6), CService("250.3.1.1", 8333)); - addrman.Good(CAddress(addr6)); - addrman.Add(CAddress(addr7), CService("250.1.1.3", 8333)); - addrman.Good(CAddress(addr7)); + addrman.Add(CAddress(addr5, NODE_NONE), CService("250.3.1.1", 8333)); + addrman.Good(CAddress(addr5, NODE_NONE)); + addrman.Add(CAddress(addr6, NODE_NONE), CService("250.3.1.1", 8333)); + addrman.Good(CAddress(addr6, NODE_NONE)); + addrman.Add(CAddress(addr7, NODE_NONE), CService("250.1.1.3", 8333)); + addrman.Good(CAddress(addr7, NODE_NONE)); // Test 11: 6 addrs + 1 addr from last test = 7. BOOST_CHECK(addrman.size() == 7); @@ -199,7 +199,7 @@ BOOST_AUTO_TEST_CASE(addrman_new_collisions) for (unsigned int i = 1; i < 18; i++) { CService addr = CService("250.1.1." + boost::to_string(i)); - addrman.Add(CAddress(addr), source); + addrman.Add(CAddress(addr, NODE_NONE), source); //Test 13: No collision in new table yet. BOOST_CHECK(addrman.size() == i); @@ -207,11 +207,11 @@ BOOST_AUTO_TEST_CASE(addrman_new_collisions) //Test 14: new table collision! CService addr1 = CService("250.1.1.18"); - addrman.Add(CAddress(addr1), source); + addrman.Add(CAddress(addr1, NODE_NONE), source); BOOST_CHECK(addrman.size() == 17); CService addr2 = CService("250.1.1.19"); - addrman.Add(CAddress(addr2), source); + addrman.Add(CAddress(addr2, NODE_NONE), source); BOOST_CHECK(addrman.size() == 18); } @@ -228,8 +228,8 @@ BOOST_AUTO_TEST_CASE(addrman_tried_collisions) for (unsigned int i = 1; i < 80; i++) { CService addr = CService("250.1.1." + boost::to_string(i)); - addrman.Add(CAddress(addr), source); - addrman.Good(CAddress(addr)); + addrman.Add(CAddress(addr, NODE_NONE), source); + addrman.Good(CAddress(addr, NODE_NONE)); //Test 15: No collision in tried table yet. BOOST_TEST_MESSAGE(addrman.size()); @@ -238,11 +238,11 @@ BOOST_AUTO_TEST_CASE(addrman_tried_collisions) //Test 16: tried table collision! CService addr1 = CService("250.1.1.80"); - addrman.Add(CAddress(addr1), source); + addrman.Add(CAddress(addr1, NODE_NONE), source); BOOST_CHECK(addrman.size() == 79); CService addr2 = CService("250.1.1.81"); - addrman.Add(CAddress(addr2), source); + addrman.Add(CAddress(addr2, NODE_NONE), source); BOOST_CHECK(addrman.size() == 80); } @@ -255,9 +255,9 @@ BOOST_AUTO_TEST_CASE(addrman_find) BOOST_CHECK(addrman.size() == 0); - CAddress addr1 = CAddress(CService("250.1.2.1", 8333)); - CAddress addr2 = CAddress(CService("250.1.2.1", 9999)); - CAddress addr3 = CAddress(CService("251.255.2.1", 8333)); + CAddress addr1 = CAddress(CService("250.1.2.1", 8333), NODE_NONE); + CAddress addr2 = CAddress(CService("250.1.2.1", 9999), NODE_NONE); + CAddress addr3 = CAddress(CService("251.255.2.1", 8333), NODE_NONE); CNetAddr source1 = CNetAddr("250.1.2.1"); CNetAddr source2 = CNetAddr("250.1.2.2"); @@ -294,7 +294,7 @@ BOOST_AUTO_TEST_CASE(addrman_create) BOOST_CHECK(addrman.size() == 0); - CAddress addr1 = CAddress(CService("250.1.2.1", 8333)); + CAddress addr1 = CAddress(CService("250.1.2.1", 8333), NODE_NONE); CNetAddr source1 = CNetAddr("250.1.2.1"); int nId; @@ -317,7 +317,7 @@ BOOST_AUTO_TEST_CASE(addrman_delete) BOOST_CHECK(addrman.size() == 0); - CAddress addr1 = CAddress(CService("250.1.2.1", 8333)); + CAddress addr1 = CAddress(CService("250.1.2.1", 8333), NODE_NONE); CNetAddr source1 = CNetAddr("250.1.2.1"); int nId; @@ -344,15 +344,15 @@ BOOST_AUTO_TEST_CASE(addrman_getaddr) vector<CAddress> vAddr1 = addrman.GetAddr(); BOOST_CHECK(vAddr1.size() == 0); - CAddress addr1 = CAddress(CService("250.250.2.1", 8333)); + CAddress addr1 = CAddress(CService("250.250.2.1", 8333), NODE_NONE); addr1.nTime = GetAdjustedTime(); // Set time so isTerrible = false - CAddress addr2 = CAddress(CService("250.251.2.2", 9999)); + CAddress addr2 = CAddress(CService("250.251.2.2", 9999), NODE_NONE); addr2.nTime = GetAdjustedTime(); - CAddress addr3 = CAddress(CService("251.252.2.3", 8333)); + CAddress addr3 = CAddress(CService("251.252.2.3", 8333), NODE_NONE); addr3.nTime = GetAdjustedTime(); - CAddress addr4 = CAddress(CService("252.253.3.4", 8333)); + CAddress addr4 = CAddress(CService("252.253.3.4", 8333), NODE_NONE); addr4.nTime = GetAdjustedTime(); - CAddress addr5 = CAddress(CService("252.254.4.5", 8333)); + CAddress addr5 = CAddress(CService("252.254.4.5", 8333), NODE_NONE); addr5.nTime = GetAdjustedTime(); CNetAddr source1 = CNetAddr("250.1.2.1"); CNetAddr source2 = CNetAddr("250.2.3.3"); @@ -368,8 +368,8 @@ BOOST_AUTO_TEST_CASE(addrman_getaddr) BOOST_CHECK(addrman.GetAddr().size() == 1); // Test 24: Ensure GetAddr works with new and tried addresses. - addrman.Good(CAddress(addr1)); - addrman.Good(CAddress(addr2)); + addrman.Good(CAddress(addr1, NODE_NONE)); + addrman.Good(CAddress(addr2, NODE_NONE)); BOOST_CHECK(addrman.GetAddr().size() == 1); // Test 25: Ensure GetAddr still returns 23% when addrman has many addrs. @@ -378,7 +378,7 @@ BOOST_AUTO_TEST_CASE(addrman_getaddr) int octet2 = (i / 256) % 256; int octet3 = (i / (256 * 2)) % 256; string strAddr = boost::to_string(octet1) + "." + boost::to_string(octet2) + "." + boost::to_string(octet3) + ".23"; - CAddress addr = CAddress(CService(strAddr)); + CAddress addr = CAddress(CService(strAddr), NODE_NONE); // Ensure that for all addrs in addrman, isTerrible == false. addr.nTime = GetAdjustedTime(); @@ -403,8 +403,8 @@ BOOST_AUTO_TEST_CASE(caddrinfo_get_tried_bucket) // Set addrman addr placement to be deterministic. addrman.MakeDeterministic(); - CAddress addr1 = CAddress(CService("250.1.1.1", 8333)); - CAddress addr2 = CAddress(CService("250.1.1.1", 9999)); + CAddress addr1 = CAddress(CService("250.1.1.1", 8333), NODE_NONE); + CAddress addr2 = CAddress(CService("250.1.1.1", 9999), NODE_NONE); CNetAddr source1 = CNetAddr("250.1.1.1"); @@ -431,7 +431,7 @@ BOOST_AUTO_TEST_CASE(caddrinfo_get_tried_bucket) set<int> buckets; for (int i = 0; i < 255; i++) { CAddrInfo infoi = CAddrInfo( - CAddress(CService("250.1.1." + boost::to_string(i))), + CAddress(CService("250.1.1." + boost::to_string(i)), NODE_NONE), CNetAddr("250.1.1." + boost::to_string(i))); int bucket = infoi.GetTriedBucket(nKey1); buckets.insert(bucket); @@ -443,7 +443,7 @@ BOOST_AUTO_TEST_CASE(caddrinfo_get_tried_bucket) buckets.clear(); for (int j = 0; j < 255; j++) { CAddrInfo infoj = CAddrInfo( - CAddress(CService("250." + boost::to_string(j) + ".1.1")), + CAddress(CService("250." + boost::to_string(j) + ".1.1"), NODE_NONE), CNetAddr("250." + boost::to_string(j) + ".1.1")); int bucket = infoj.GetTriedBucket(nKey1); buckets.insert(bucket); @@ -460,8 +460,8 @@ BOOST_AUTO_TEST_CASE(caddrinfo_get_new_bucket) // Set addrman addr placement to be deterministic. addrman.MakeDeterministic(); - CAddress addr1 = CAddress(CService("250.1.2.1", 8333)); - CAddress addr2 = CAddress(CService("250.1.2.1", 9999)); + CAddress addr1 = CAddress(CService("250.1.2.1", 8333), NODE_NONE); + CAddress addr2 = CAddress(CService("250.1.2.1", 9999), NODE_NONE); CNetAddr source1 = CNetAddr("250.1.2.1"); @@ -484,7 +484,7 @@ BOOST_AUTO_TEST_CASE(caddrinfo_get_new_bucket) set<int> buckets; for (int i = 0; i < 255; i++) { CAddrInfo infoi = CAddrInfo( - CAddress(CService("250.1.1." + boost::to_string(i))), + CAddress(CService("250.1.1." + boost::to_string(i)), NODE_NONE), CNetAddr("250.1.1." + boost::to_string(i))); int bucket = infoi.GetNewBucket(nKey1); buckets.insert(bucket); @@ -497,7 +497,7 @@ BOOST_AUTO_TEST_CASE(caddrinfo_get_new_bucket) for (int j = 0; j < 4 * 255; j++) { CAddrInfo infoj = CAddrInfo(CAddress( CService( - boost::to_string(250 + (j / 255)) + "." + boost::to_string(j % 256) + ".1.1")), + boost::to_string(250 + (j / 255)) + "." + boost::to_string(j % 256) + ".1.1"), NODE_NONE), CNetAddr("251.4.1.1")); int bucket = infoj.GetNewBucket(nKey1); buckets.insert(bucket); @@ -509,7 +509,7 @@ BOOST_AUTO_TEST_CASE(caddrinfo_get_new_bucket) buckets.clear(); for (int p = 0; p < 255; p++) { CAddrInfo infoj = CAddrInfo( - CAddress(CService("250.1.1.1")), + CAddress(CService("250.1.1.1"), NODE_NONE), CNetAddr("250." + boost::to_string(p) + ".1.1")); int bucket = infoj.GetNewBucket(nKey1); buckets.insert(bucket); diff --git a/src/test/base58_tests.cpp b/src/test/base58_tests.cpp index e5a2e28b2e..01eb2aee9e 100644 --- a/src/test/base58_tests.cpp +++ b/src/test/base58_tests.cpp @@ -79,7 +79,7 @@ class TestAddrTypeVisitor : public boost::static_visitor<bool> private: std::string exp_addrType; public: - TestAddrTypeVisitor(const std::string &exp_addrType) : exp_addrType(exp_addrType) { } + TestAddrTypeVisitor(const std::string &_exp_addrType) : exp_addrType(_exp_addrType) { } bool operator()(const CKeyID &id) const { return (exp_addrType == "pubkey"); @@ -100,7 +100,7 @@ class TestPayloadVisitor : public boost::static_visitor<bool> private: std::vector<unsigned char> exp_payload; public: - TestPayloadVisitor(std::vector<unsigned char> &exp_payload) : exp_payload(exp_payload) { } + TestPayloadVisitor(std::vector<unsigned char> &_exp_payload) : exp_payload(_exp_payload) { } bool operator()(const CKeyID &id) const { uint160 exp_key(exp_payload); diff --git a/src/test/crypto_tests.cpp b/src/test/crypto_tests.cpp index 0b46d718d1..58a62ee022 100644 --- a/src/test/crypto_tests.cpp +++ b/src/test/crypto_tests.cpp @@ -2,6 +2,7 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. +#include "crypto/aes.h" #include "crypto/ripemd160.h" #include "crypto/sha1.h" #include "crypto/sha256.h" @@ -16,6 +17,8 @@ #include <boost/assign/list_of.hpp> #include <boost/test/unit_test.hpp> +#include <openssl/aes.h> +#include <openssl/evp.h> BOOST_FIXTURE_TEST_SUITE(crypto_tests, BasicTestingSetup) @@ -63,6 +66,127 @@ void TestHMACSHA512(const std::string &hexkey, const std::string &hexin, const s TestVector(CHMAC_SHA512(&key[0], key.size()), ParseHex(hexin), ParseHex(hexout)); } +void TestAES128(const std::string &hexkey, const std::string &hexin, const std::string &hexout) +{ + std::vector<unsigned char> key = ParseHex(hexkey); + std::vector<unsigned char> in = ParseHex(hexin); + std::vector<unsigned char> correctout = ParseHex(hexout); + std::vector<unsigned char> buf, buf2; + + assert(key.size() == 16); + assert(in.size() == 16); + assert(correctout.size() == 16); + AES128Encrypt enc(&key[0]); + buf.resize(correctout.size()); + buf2.resize(correctout.size()); + enc.Encrypt(&buf[0], &in[0]); + BOOST_CHECK_EQUAL(HexStr(buf), HexStr(correctout)); + AES128Decrypt dec(&key[0]); + dec.Decrypt(&buf2[0], &buf[0]); + BOOST_CHECK_EQUAL(HexStr(buf2), HexStr(in)); +} + +void TestAES256(const std::string &hexkey, const std::string &hexin, const std::string &hexout) +{ + std::vector<unsigned char> key = ParseHex(hexkey); + std::vector<unsigned char> in = ParseHex(hexin); + std::vector<unsigned char> correctout = ParseHex(hexout); + std::vector<unsigned char> buf; + + assert(key.size() == 32); + assert(in.size() == 16); + assert(correctout.size() == 16); + AES256Encrypt enc(&key[0]); + buf.resize(correctout.size()); + enc.Encrypt(&buf[0], &in[0]); + BOOST_CHECK(buf == correctout); + AES256Decrypt dec(&key[0]); + dec.Decrypt(&buf[0], &buf[0]); + BOOST_CHECK(buf == in); +} + +void TestAES128CBC(const std::string &hexkey, const std::string &hexiv, bool pad, const std::string &hexin, const std::string &hexout) +{ + std::vector<unsigned char> key = ParseHex(hexkey); + std::vector<unsigned char> iv = ParseHex(hexiv); + std::vector<unsigned char> in = ParseHex(hexin); + std::vector<unsigned char> correctout = ParseHex(hexout); + std::vector<unsigned char> realout(in.size() + AES_BLOCKSIZE); + + // Encrypt the plaintext and verify that it equals the cipher + AES128CBCEncrypt enc(&key[0], &iv[0], pad); + int size = enc.Encrypt(&in[0], in.size(), &realout[0]); + realout.resize(size); + BOOST_CHECK(realout.size() == correctout.size()); + BOOST_CHECK_MESSAGE(realout == correctout, HexStr(realout) + std::string(" != ") + hexout); + + // Decrypt the cipher and verify that it equals the plaintext + std::vector<unsigned char> decrypted(correctout.size()); + AES128CBCDecrypt dec(&key[0], &iv[0], pad); + size = dec.Decrypt(&correctout[0], correctout.size(), &decrypted[0]); + decrypted.resize(size); + BOOST_CHECK(decrypted.size() == in.size()); + BOOST_CHECK_MESSAGE(decrypted == in, HexStr(decrypted) + std::string(" != ") + hexin); + + // Encrypt and re-decrypt substrings of the plaintext and verify that they equal each-other + for(std::vector<unsigned char>::iterator i(in.begin()); i != in.end(); ++i) + { + std::vector<unsigned char> sub(i, in.end()); + std::vector<unsigned char> subout(sub.size() + AES_BLOCKSIZE); + int size = enc.Encrypt(&sub[0], sub.size(), &subout[0]); + if (size != 0) + { + subout.resize(size); + std::vector<unsigned char> subdecrypted(subout.size()); + size = dec.Decrypt(&subout[0], subout.size(), &subdecrypted[0]); + subdecrypted.resize(size); + BOOST_CHECK(decrypted.size() == in.size()); + BOOST_CHECK_MESSAGE(subdecrypted == sub, HexStr(subdecrypted) + std::string(" != ") + HexStr(sub)); + } + } +} + +void TestAES256CBC(const std::string &hexkey, const std::string &hexiv, bool pad, const std::string &hexin, const std::string &hexout) +{ + std::vector<unsigned char> key = ParseHex(hexkey); + std::vector<unsigned char> iv = ParseHex(hexiv); + std::vector<unsigned char> in = ParseHex(hexin); + std::vector<unsigned char> correctout = ParseHex(hexout); + std::vector<unsigned char> realout(in.size() + AES_BLOCKSIZE); + + // Encrypt the plaintext and verify that it equals the cipher + AES256CBCEncrypt enc(&key[0], &iv[0], pad); + int size = enc.Encrypt(&in[0], in.size(), &realout[0]); + realout.resize(size); + BOOST_CHECK(realout.size() == correctout.size()); + BOOST_CHECK_MESSAGE(realout == correctout, HexStr(realout) + std::string(" != ") + hexout); + + // Decrypt the cipher and verify that it equals the plaintext + std::vector<unsigned char> decrypted(correctout.size()); + AES256CBCDecrypt dec(&key[0], &iv[0], pad); + size = dec.Decrypt(&correctout[0], correctout.size(), &decrypted[0]); + decrypted.resize(size); + BOOST_CHECK(decrypted.size() == in.size()); + BOOST_CHECK_MESSAGE(decrypted == in, HexStr(decrypted) + std::string(" != ") + hexin); + + // Encrypt and re-decrypt substrings of the plaintext and verify that they equal each-other + for(std::vector<unsigned char>::iterator i(in.begin()); i != in.end(); ++i) + { + std::vector<unsigned char> sub(i, in.end()); + std::vector<unsigned char> subout(sub.size() + AES_BLOCKSIZE); + int size = enc.Encrypt(&sub[0], sub.size(), &subout[0]); + if (size != 0) + { + subout.resize(size); + std::vector<unsigned char> subdecrypted(subout.size()); + size = dec.Decrypt(&subout[0], subout.size(), &subdecrypted[0]); + subdecrypted.resize(size); + BOOST_CHECK(decrypted.size() == in.size()); + BOOST_CHECK_MESSAGE(subdecrypted == sub, HexStr(subdecrypted) + std::string(" != ") + HexStr(sub)); + } + } +} + std::string LongTestString(void) { std::string ret; for (int i=0; i<200000; i++) { @@ -248,4 +372,71 @@ BOOST_AUTO_TEST_CASE(hmac_sha512_testvectors) { "b6022cac3c4982b10d5eeb55c3e4de15134676fb6de0446065c97440fa8c6a58"); } +BOOST_AUTO_TEST_CASE(aes_testvectors) { + // AES test vectors from FIPS 197. + TestAES128("000102030405060708090a0b0c0d0e0f", "00112233445566778899aabbccddeeff", "69c4e0d86a7b0430d8cdb78070b4c55a"); + TestAES256("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", "00112233445566778899aabbccddeeff", "8ea2b7ca516745bfeafc49904b496089"); + + // AES-ECB test vectors from NIST sp800-38a. + TestAES128("2b7e151628aed2a6abf7158809cf4f3c", "6bc1bee22e409f96e93d7e117393172a", "3ad77bb40d7a3660a89ecaf32466ef97"); + TestAES128("2b7e151628aed2a6abf7158809cf4f3c", "ae2d8a571e03ac9c9eb76fac45af8e51", "f5d3d58503b9699de785895a96fdbaaf"); + TestAES128("2b7e151628aed2a6abf7158809cf4f3c", "30c81c46a35ce411e5fbc1191a0a52ef", "43b1cd7f598ece23881b00e3ed030688"); + TestAES128("2b7e151628aed2a6abf7158809cf4f3c", "f69f2445df4f9b17ad2b417be66c3710", "7b0c785e27e8ad3f8223207104725dd4"); + TestAES256("603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4", "6bc1bee22e409f96e93d7e117393172a", "f3eed1bdb5d2a03c064b5a7e3db181f8"); + TestAES256("603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4", "ae2d8a571e03ac9c9eb76fac45af8e51", "591ccb10d410ed26dc5ba74a31362870"); + TestAES256("603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4", "30c81c46a35ce411e5fbc1191a0a52ef", "b6ed21b99ca6f4f9f153e7b1beafed1d"); + TestAES256("603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4", "f69f2445df4f9b17ad2b417be66c3710", "23304b7a39f9f3ff067d8d8f9e24ecc7"); +} + +BOOST_AUTO_TEST_CASE(aes_cbc_testvectors) { + + // NIST AES CBC 128-bit encryption test-vectors + TestAES128CBC("2b7e151628aed2a6abf7158809cf4f3c", "000102030405060708090A0B0C0D0E0F", false, \ + "6bc1bee22e409f96e93d7e117393172a", "7649abac8119b246cee98e9b12e9197d"); + TestAES128CBC("2b7e151628aed2a6abf7158809cf4f3c", "7649ABAC8119B246CEE98E9B12E9197D", false, \ + "ae2d8a571e03ac9c9eb76fac45af8e51", "5086cb9b507219ee95db113a917678b2"); + TestAES128CBC("2b7e151628aed2a6abf7158809cf4f3c", "5086cb9b507219ee95db113a917678b2", false, \ + "30c81c46a35ce411e5fbc1191a0a52ef", "73bed6b8e3c1743b7116e69e22229516"); + TestAES128CBC("2b7e151628aed2a6abf7158809cf4f3c", "73bed6b8e3c1743b7116e69e22229516", false, \ + "f69f2445df4f9b17ad2b417be66c3710", "3ff1caa1681fac09120eca307586e1a7"); + + // The same vectors with padding enabled + TestAES128CBC("2b7e151628aed2a6abf7158809cf4f3c", "000102030405060708090A0B0C0D0E0F", true, \ + "6bc1bee22e409f96e93d7e117393172a", "7649abac8119b246cee98e9b12e9197d8964e0b149c10b7b682e6e39aaeb731c"); + TestAES128CBC("2b7e151628aed2a6abf7158809cf4f3c", "7649ABAC8119B246CEE98E9B12E9197D", true, \ + "ae2d8a571e03ac9c9eb76fac45af8e51", "5086cb9b507219ee95db113a917678b255e21d7100b988ffec32feeafaf23538"); + TestAES128CBC("2b7e151628aed2a6abf7158809cf4f3c", "5086cb9b507219ee95db113a917678b2", true, \ + "30c81c46a35ce411e5fbc1191a0a52ef", "73bed6b8e3c1743b7116e69e22229516f6eccda327bf8e5ec43718b0039adceb"); + TestAES128CBC("2b7e151628aed2a6abf7158809cf4f3c", "73bed6b8e3c1743b7116e69e22229516", true, \ + "f69f2445df4f9b17ad2b417be66c3710", "3ff1caa1681fac09120eca307586e1a78cb82807230e1321d3fae00d18cc2012"); + + // NIST AES CBC 256-bit encryption test-vectors + TestAES256CBC("603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4", \ + "000102030405060708090A0B0C0D0E0F", false, "6bc1bee22e409f96e93d7e117393172a", \ + "f58c4c04d6e5f1ba779eabfb5f7bfbd6"); + TestAES256CBC("603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4", \ + "F58C4C04D6E5F1BA779EABFB5F7BFBD6", false, "ae2d8a571e03ac9c9eb76fac45af8e51", \ + "9cfc4e967edb808d679f777bc6702c7d"); + TestAES256CBC("603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4", \ + "9CFC4E967EDB808D679F777BC6702C7D", false, "30c81c46a35ce411e5fbc1191a0a52ef", + "39f23369a9d9bacfa530e26304231461"); + TestAES256CBC("603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4", \ + "39F23369A9D9BACFA530E26304231461", false, "f69f2445df4f9b17ad2b417be66c3710", \ + "b2eb05e2c39be9fcda6c19078c6a9d1b"); + + // The same vectors with padding enabled + TestAES256CBC("603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4", \ + "000102030405060708090A0B0C0D0E0F", true, "6bc1bee22e409f96e93d7e117393172a", \ + "f58c4c04d6e5f1ba779eabfb5f7bfbd6485a5c81519cf378fa36d42b8547edc0"); + TestAES256CBC("603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4", \ + "F58C4C04D6E5F1BA779EABFB5F7BFBD6", true, "ae2d8a571e03ac9c9eb76fac45af8e51", \ + "9cfc4e967edb808d679f777bc6702c7d3a3aa5e0213db1a9901f9036cf5102d2"); + TestAES256CBC("603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4", \ + "9CFC4E967EDB808D679F777BC6702C7D", true, "30c81c46a35ce411e5fbc1191a0a52ef", + "39f23369a9d9bacfa530e263042314612f8da707643c90a6f732b3de1d3f5cee"); + TestAES256CBC("603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4", \ + "39F23369A9D9BACFA530E26304231461", true, "f69f2445df4f9b17ad2b417be66c3710", \ + "b2eb05e2c39be9fcda6c19078c6a9d1b3f461796d6b0d6b2e0c2a72b4d80e644"); +} + BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/data/bitcoin-util-test.json b/src/test/data/bitcoin-util-test.json index 3bf80ca434..5cb383de85 100644 --- a/src/test/data/bitcoin-util-test.json +++ b/src/test/data/bitcoin-util-test.json @@ -86,5 +86,18 @@ "outaddr=0.18:13tuJJDR2RgArmgfv6JScSdreahzgc4T6o", "outdata=54686973204f505f52455455524e207472616e73616374696f6e206f7574707574207761732063726561746564206279206d6f646966696564206372656174657261777472616e73616374696f6e2e"], "output_cmp": "txcreatedata2.hex" + }, + { "exec": "./bitcoin-tx", + "args": + ["-create", + "in=5897de6bd6027a475eadd57019d4e6872c396d0716c4875a5f1a6fcfdf385c1f:0:4294967293", + "outaddr=0.18:13tuJJDR2RgArmgfv6JScSdreahzgc4T6o"], + "output_cmp": "txcreatedata_seq0.hex" + }, + { "exec": "./bitcoin-tx", + "args": + ["01000000011f5c38dfcf6f1a5f5a87c416076d392c87e6d41970d5ad5e477a02d66bde97580000000000fdffffff0180a81201000000001976a9141fc11f39be1729bf973a7ab6a615ca4729d6457488ac00000000", + "in=5897de6bd6027a475eadd57019d4e6872c396d0716c4875a5f1a6fcfdf385c1f:0:1"], + "output_cmp": "txcreatedata_seq1.hex" } ] diff --git a/src/test/data/script_tests.json b/src/test/data/script_tests.json index 757d94b526..0bdac182e0 100644 --- a/src/test/data/script_tests.json +++ b/src/test/data/script_tests.json @@ -1311,6 +1311,13 @@ "P2SH(P2PK), bad redeemscript" ], [ + "0x47 0x30440220781ba4f59a7b207a10db87628bc2168df4d59b844b397d2dbc9a5835fb2f2b7602206ed8fbcc1072fe2dfc5bb25909269e5dc42ffcae7ec2bc81d59692210ff30c2b01 0x41 0x0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8 0x19 0x76a91491b24bf9f5288532960ac687abb035127b1d28a588ac", + "HASH160 0x14 0x7f67f0521934a57d3039f77f9f32cf313f3ac74b EQUAL", + "P2SH", + "OK", + "P2SH(P2PKH)" +], +[ "0x47 0x304402204e2eb034be7b089534ac9e798cf6a2c79f38bcb34d1b179efd6f2de0841735db022071461beb056b5a7be1819da6a3e3ce3662831ecc298419ca101eb6887b5dd6a401 0x19 0x76a9147cf9c846cd4882efec4bf07e44ebdad495c94f4b88ac", "HASH160 0x14 0x2df519943d5acc0ef5222091f9dfe3543f489a82 EQUAL", "", diff --git a/src/test/data/txcreatedata_seq0.hex b/src/test/data/txcreatedata_seq0.hex new file mode 100644 index 0000000000..db02b5e4a4 --- /dev/null +++ b/src/test/data/txcreatedata_seq0.hex @@ -0,0 +1 @@ +01000000011f5c38dfcf6f1a5f5a87c416076d392c87e6d41970d5ad5e477a02d66bde97580000000000fdffffff0180a81201000000001976a9141fc11f39be1729bf973a7ab6a615ca4729d6457488ac00000000 diff --git a/src/test/data/txcreatedata_seq1.hex b/src/test/data/txcreatedata_seq1.hex new file mode 100644 index 0000000000..4cedcd975c --- /dev/null +++ b/src/test/data/txcreatedata_seq1.hex @@ -0,0 +1 @@ +01000000021f5c38dfcf6f1a5f5a87c416076d392c87e6d41970d5ad5e477a02d66bde97580000000000fdffffff1f5c38dfcf6f1a5f5a87c416076d392c87e6d41970d5ad5e477a02d66bde97580000000000010000000180a81201000000001976a9141fc11f39be1729bf973a7ab6a615ca4729d6457488ac00000000 diff --git a/src/test/dbwrapper_tests.cpp b/src/test/dbwrapper_tests.cpp index 081d57831d..a0bdcf4afb 100644 --- a/src/test/dbwrapper_tests.cpp +++ b/src/test/dbwrapper_tests.cpp @@ -203,5 +203,125 @@ BOOST_AUTO_TEST_CASE(existing_data_reindex) BOOST_CHECK(odbw.Read(key, res3)); BOOST_CHECK_EQUAL(res3.ToString(), in2.ToString()); } - + +BOOST_AUTO_TEST_CASE(iterator_ordering) +{ + path ph = temp_directory_path() / unique_path(); + CDBWrapper dbw(ph, (1 << 20), true, false, false); + for (int x=0x00; x<256; ++x) { + uint8_t key = x; + uint32_t value = x*x; + BOOST_CHECK(dbw.Write(key, value)); + } + + boost::scoped_ptr<CDBIterator> it(const_cast<CDBWrapper*>(&dbw)->NewIterator()); + for (int c=0; c<2; ++c) { + int seek_start; + if (c == 0) + seek_start = 0x00; + else + seek_start = 0x80; + it->Seek((uint8_t)seek_start); + for (int x=seek_start; x<256; ++x) { + uint8_t key; + uint32_t value; + BOOST_CHECK(it->Valid()); + if (!it->Valid()) // Avoid spurious errors about invalid iterator's key and value in case of failure + break; + BOOST_CHECK(it->GetKey(key)); + BOOST_CHECK(it->GetValue(value)); + BOOST_CHECK_EQUAL(key, x); + BOOST_CHECK_EQUAL(value, x*x); + it->Next(); + } + BOOST_CHECK(!it->Valid()); + } +} + +struct StringContentsSerializer { + // Used to make two serialized objects the same while letting them have a different lengths + // This is a terrible idea + string str; + StringContentsSerializer() {} + StringContentsSerializer(const string& inp) : str(inp) {} + + StringContentsSerializer& operator+=(const string& s) { + str += s; + return *this; + } + StringContentsSerializer& operator+=(const StringContentsSerializer& s) { return *this += s.str; } + + ADD_SERIALIZE_METHODS; + + template <typename Stream, typename Operation> + inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) { + if (ser_action.ForRead()) { + str.clear(); + char c = 0; + while (true) { + try { + READWRITE(c); + str.push_back(c); + } catch (const std::ios_base::failure& e) { + break; + } + } + } else { + for (size_t i = 0; i < str.size(); i++) + READWRITE(str[i]); + } + } +}; + +BOOST_AUTO_TEST_CASE(iterator_string_ordering) +{ + char buf[10]; + + path ph = temp_directory_path() / unique_path(); + CDBWrapper dbw(ph, (1 << 20), true, false, false); + for (int x=0x00; x<10; ++x) { + for (int y = 0; y < 10; y++) { + sprintf(buf, "%d", x); + StringContentsSerializer key(buf); + for (int z = 0; z < y; z++) + key += key; + uint32_t value = x*x; + BOOST_CHECK(dbw.Write(key, value)); + } + } + + boost::scoped_ptr<CDBIterator> it(const_cast<CDBWrapper*>(&dbw)->NewIterator()); + for (int c=0; c<2; ++c) { + int seek_start; + if (c == 0) + seek_start = 0; + else + seek_start = 5; + sprintf(buf, "%d", seek_start); + StringContentsSerializer seek_key(buf); + it->Seek(seek_key); + for (int x=seek_start; x<10; ++x) { + for (int y = 0; y < 10; y++) { + sprintf(buf, "%d", x); + string exp_key(buf); + for (int z = 0; z < y; z++) + exp_key += exp_key; + StringContentsSerializer key; + uint32_t value; + BOOST_CHECK(it->Valid()); + if (!it->Valid()) // Avoid spurious errors about invalid iterator's key and value in case of failure + break; + BOOST_CHECK(it->GetKey(key)); + BOOST_CHECK(it->GetValue(value)); + BOOST_CHECK_EQUAL(key.str, exp_key); + BOOST_CHECK_EQUAL(value, x*x); + it->Next(); + } + } + BOOST_CHECK(!it->Valid()); + } +} + + + BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/hash_tests.cpp b/src/test/hash_tests.cpp index 35079d1614..82d61209b5 100644 --- a/src/test/hash_tests.cpp +++ b/src/test/hash_tests.cpp @@ -47,4 +47,81 @@ BOOST_AUTO_TEST_CASE(murmurhash3) #undef T } +/* + SipHash-2-4 output with + k = 00 01 02 ... + and + in = (empty string) + in = 00 (1 byte) + in = 00 01 (2 bytes) + in = 00 01 02 (3 bytes) + ... + in = 00 01 02 ... 3e (63 bytes) + + from: https://131002.net/siphash/siphash24.c +*/ +uint64_t siphash_4_2_testvec[] = { + 0x726fdb47dd0e0e31, 0x74f839c593dc67fd, 0x0d6c8009d9a94f5a, 0x85676696d7fb7e2d, + 0xcf2794e0277187b7, 0x18765564cd99a68d, 0xcbc9466e58fee3ce, 0xab0200f58b01d137, + 0x93f5f5799a932462, 0x9e0082df0ba9e4b0, 0x7a5dbbc594ddb9f3, 0xf4b32f46226bada7, + 0x751e8fbc860ee5fb, 0x14ea5627c0843d90, 0xf723ca908e7af2ee, 0xa129ca6149be45e5, + 0x3f2acc7f57c29bdb, 0x699ae9f52cbe4794, 0x4bc1b3f0968dd39c, 0xbb6dc91da77961bd, + 0xbed65cf21aa2ee98, 0xd0f2cbb02e3b67c7, 0x93536795e3a33e88, 0xa80c038ccd5ccec8, + 0xb8ad50c6f649af94, 0xbce192de8a85b8ea, 0x17d835b85bbb15f3, 0x2f2e6163076bcfad, + 0xde4daaaca71dc9a5, 0xa6a2506687956571, 0xad87a3535c49ef28, 0x32d892fad841c342, + 0x7127512f72f27cce, 0xa7f32346f95978e3, 0x12e0b01abb051238, 0x15e034d40fa197ae, + 0x314dffbe0815a3b4, 0x027990f029623981, 0xcadcd4e59ef40c4d, 0x9abfd8766a33735c, + 0x0e3ea96b5304a7d0, 0xad0c42d6fc585992, 0x187306c89bc215a9, 0xd4a60abcf3792b95, + 0xf935451de4f21df2, 0xa9538f0419755787, 0xdb9acddff56ca510, 0xd06c98cd5c0975eb, + 0xe612a3cb9ecba951, 0xc766e62cfcadaf96, 0xee64435a9752fe72, 0xa192d576b245165a, + 0x0a8787bf8ecb74b2, 0x81b3e73d20b49b6f, 0x7fa8220ba3b2ecea, 0x245731c13ca42499, + 0xb78dbfaf3a8d83bd, 0xea1ad565322a1a0b, 0x60e61c23a3795013, 0x6606d7e446282b93, + 0x6ca4ecb15c5f91e1, 0x9f626da15c9625f3, 0xe51b38608ef25f57, 0x958a324ceb064572 +}; + +BOOST_AUTO_TEST_CASE(siphash) +{ + CSipHasher hasher(0x0706050403020100ULL, 0x0F0E0D0C0B0A0908ULL); + BOOST_CHECK_EQUAL(hasher.Finalize(), 0x726fdb47dd0e0e31ull); + static const unsigned char t0[1] = {0}; + hasher.Write(t0, 1); + BOOST_CHECK_EQUAL(hasher.Finalize(), 0x74f839c593dc67fdull); + static const unsigned char t1[7] = {1,2,3,4,5,6,7}; + hasher.Write(t1, 7); + BOOST_CHECK_EQUAL(hasher.Finalize(), 0x93f5f5799a932462ull); + hasher.Write(0x0F0E0D0C0B0A0908ULL); + BOOST_CHECK_EQUAL(hasher.Finalize(), 0x3f2acc7f57c29bdbull); + static const unsigned char t2[2] = {16,17}; + hasher.Write(t2, 2); + BOOST_CHECK_EQUAL(hasher.Finalize(), 0x4bc1b3f0968dd39cull); + static const unsigned char t3[9] = {18,19,20,21,22,23,24,25,26}; + hasher.Write(t3, 9); + BOOST_CHECK_EQUAL(hasher.Finalize(), 0x2f2e6163076bcfadull); + static const unsigned char t4[5] = {27,28,29,30,31}; + hasher.Write(t4, 5); + BOOST_CHECK_EQUAL(hasher.Finalize(), 0x7127512f72f27cceull); + hasher.Write(0x2726252423222120ULL); + BOOST_CHECK_EQUAL(hasher.Finalize(), 0x0e3ea96b5304a7d0ull); + hasher.Write(0x2F2E2D2C2B2A2928ULL); + BOOST_CHECK_EQUAL(hasher.Finalize(), 0xe612a3cb9ecba951ull); + + BOOST_CHECK_EQUAL(SipHashUint256(0x0706050403020100ULL, 0x0F0E0D0C0B0A0908ULL, uint256S("1f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100")), 0x7127512f72f27cceull); + + // Check test vectors from spec, one byte at a time + CSipHasher hasher2(0x0706050403020100ULL, 0x0F0E0D0C0B0A0908ULL); + for (uint8_t x=0; x<ARRAYLEN(siphash_4_2_testvec); ++x) + { + BOOST_CHECK_EQUAL(hasher2.Finalize(), siphash_4_2_testvec[x]); + hasher2.Write(&x, 1); + } + // Check test vectors from spec, eight bytes at a time + CSipHasher hasher3(0x0706050403020100ULL, 0x0F0E0D0C0B0A0908ULL); + for (uint8_t x=0; x<ARRAYLEN(siphash_4_2_testvec); x+=8) + { + BOOST_CHECK_EQUAL(hasher3.Finalize(), siphash_4_2_testvec[x]); + hasher3.Write(uint64_t(x)|(uint64_t(x+1)<<8)|(uint64_t(x+2)<<16)|(uint64_t(x+3)<<24)| + (uint64_t(x+4)<<32)|(uint64_t(x+5)<<40)|(uint64_t(x+6)<<48)|(uint64_t(x+7)<<56)); + } +} + BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/miner_tests.cpp b/src/test/miner_tests.cpp index 469862518c..ca8d6d2e05 100644 --- a/src/test/miner_tests.cpp +++ b/src/test/miner_tests.cpp @@ -71,6 +71,113 @@ bool TestSequenceLocks(const CTransaction &tx, int flags) return CheckSequenceLocks(tx, flags); } +// Test suite for ancestor feerate transaction selection. +// Implemented as an additional function, rather than a separate test case, +// to allow reusing the blockchain created in CreateNewBlock_validity. +// Note that this test assumes blockprioritysize is 0. +void TestPackageSelection(const CChainParams& chainparams, CScript scriptPubKey, std::vector<CTransaction *>& txFirst) +{ + // Test the ancestor feerate transaction selection. + TestMemPoolEntryHelper entry; + + // Test that a medium fee transaction will be selected after a higher fee + // rate package with a low fee rate parent. + CMutableTransaction tx; + tx.vin.resize(1); + tx.vin[0].scriptSig = CScript() << OP_1; + tx.vin[0].prevout.hash = txFirst[0]->GetHash(); + tx.vin[0].prevout.n = 0; + tx.vout.resize(1); + tx.vout[0].nValue = 5000000000LL - 1000; + // This tx has a low fee: 1000 satoshis + uint256 hashParentTx = tx.GetHash(); // save this txid for later use + mempool.addUnchecked(hashParentTx, entry.Fee(1000).Time(GetTime()).SpendsCoinbase(true).FromTx(tx)); + + // This tx has a medium fee: 10000 satoshis + tx.vin[0].prevout.hash = txFirst[1]->GetHash(); + tx.vout[0].nValue = 5000000000LL - 10000; + uint256 hashMediumFeeTx = tx.GetHash(); + mempool.addUnchecked(hashMediumFeeTx, entry.Fee(10000).Time(GetTime()).SpendsCoinbase(true).FromTx(tx)); + + // This tx has a high fee, but depends on the first transaction + tx.vin[0].prevout.hash = hashParentTx; + tx.vout[0].nValue = 5000000000LL - 1000 - 50000; // 50k satoshi fee + uint256 hashHighFeeTx = tx.GetHash(); + mempool.addUnchecked(hashHighFeeTx, entry.Fee(50000).Time(GetTime()).SpendsCoinbase(false).FromTx(tx)); + + CBlockTemplate *pblocktemplate = BlockAssembler(chainparams).CreateNewBlock(scriptPubKey); + BOOST_CHECK(pblocktemplate->block.vtx[1].GetHash() == hashParentTx); + BOOST_CHECK(pblocktemplate->block.vtx[2].GetHash() == hashHighFeeTx); + BOOST_CHECK(pblocktemplate->block.vtx[3].GetHash() == hashMediumFeeTx); + + // Test that a package below the min relay fee doesn't get included + tx.vin[0].prevout.hash = hashHighFeeTx; + tx.vout[0].nValue = 5000000000LL - 1000 - 50000; // 0 fee + uint256 hashFreeTx = tx.GetHash(); + mempool.addUnchecked(hashFreeTx, entry.Fee(0).FromTx(tx)); + size_t freeTxSize = ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION); + + // Calculate a fee on child transaction that will put the package just + // below the min relay fee (assuming 1 child tx of the same size). + CAmount feeToUse = minRelayTxFee.GetFee(2*freeTxSize) - 1; + + tx.vin[0].prevout.hash = hashFreeTx; + tx.vout[0].nValue = 5000000000LL - 1000 - 50000 - feeToUse; + uint256 hashLowFeeTx = tx.GetHash(); + mempool.addUnchecked(hashLowFeeTx, entry.Fee(feeToUse).FromTx(tx)); + pblocktemplate = BlockAssembler(chainparams).CreateNewBlock(scriptPubKey); + // Verify that the free tx and the low fee tx didn't get selected + for (size_t i=0; i<pblocktemplate->block.vtx.size(); ++i) { + BOOST_CHECK(pblocktemplate->block.vtx[i].GetHash() != hashFreeTx); + BOOST_CHECK(pblocktemplate->block.vtx[i].GetHash() != hashLowFeeTx); + } + + // Test that packages above the min relay fee do get included, even if one + // of the transactions is below the min relay fee + // Remove the low fee transaction and replace with a higher fee transaction + std::list<CTransaction> dummy; + mempool.removeRecursive(tx, dummy); + tx.vout[0].nValue -= 2; // Now we should be just over the min relay fee + hashLowFeeTx = tx.GetHash(); + mempool.addUnchecked(hashLowFeeTx, entry.Fee(feeToUse+2).FromTx(tx)); + pblocktemplate = BlockAssembler(chainparams).CreateNewBlock(scriptPubKey); + BOOST_CHECK(pblocktemplate->block.vtx[4].GetHash() == hashFreeTx); + BOOST_CHECK(pblocktemplate->block.vtx[5].GetHash() == hashLowFeeTx); + + // Test that transaction selection properly updates ancestor fee + // calculations as ancestor transactions get included in a block. + // Add a 0-fee transaction that has 2 outputs. + tx.vin[0].prevout.hash = txFirst[2]->GetHash(); + tx.vout.resize(2); + tx.vout[0].nValue = 5000000000LL - 100000000; + tx.vout[1].nValue = 100000000; // 1BTC output + uint256 hashFreeTx2 = tx.GetHash(); + mempool.addUnchecked(hashFreeTx2, entry.Fee(0).SpendsCoinbase(true).FromTx(tx)); + + // This tx can't be mined by itself + tx.vin[0].prevout.hash = hashFreeTx2; + tx.vout.resize(1); + feeToUse = minRelayTxFee.GetFee(freeTxSize); + tx.vout[0].nValue = 5000000000LL - 100000000 - feeToUse; + uint256 hashLowFeeTx2 = tx.GetHash(); + mempool.addUnchecked(hashLowFeeTx2, entry.Fee(feeToUse).SpendsCoinbase(false).FromTx(tx)); + pblocktemplate = BlockAssembler(chainparams).CreateNewBlock(scriptPubKey); + + // Verify that this tx isn't selected. + for (size_t i=0; i<pblocktemplate->block.vtx.size(); ++i) { + BOOST_CHECK(pblocktemplate->block.vtx[i].GetHash() != hashFreeTx2); + BOOST_CHECK(pblocktemplate->block.vtx[i].GetHash() != hashLowFeeTx2); + } + + // This tx will be mineable, and should cause hashLowFeeTx2 to be selected + // as well. + tx.vin[0].prevout.n = 1; + tx.vout[0].nValue = 100000000 - 10000; // 10k satoshi fee + mempool.addUnchecked(tx.GetHash(), entry.Fee(10000).FromTx(tx)); + pblocktemplate = BlockAssembler(chainparams).CreateNewBlock(scriptPubKey); + BOOST_CHECK(pblocktemplate->block.vtx[8].GetHash() == hashLowFeeTx2); +} + // NOTE: These tests rely on CreateNewBlock doing its own self-validation! BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) { @@ -89,7 +196,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) fCheckpointsEnabled = false; // Simple block creation, nothing special yet: - BOOST_CHECK(pblocktemplate = CreateNewBlock(chainparams, scriptPubKey)); + BOOST_CHECK(pblocktemplate = BlockAssembler(chainparams).CreateNewBlock(scriptPubKey)); // We can't make transactions until we have inputs // Therefore, load 100 blocks :) @@ -121,7 +228,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) delete pblocktemplate; // Just to make sure we can still make simple blocks - BOOST_CHECK(pblocktemplate = CreateNewBlock(chainparams, scriptPubKey)); + BOOST_CHECK(pblocktemplate = BlockAssembler(chainparams).CreateNewBlock(scriptPubKey)); delete pblocktemplate; const CAmount BLOCKSUBSIDY = 50*COIN; @@ -146,7 +253,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) mempool.addUnchecked(hash, entry.Fee(LOWFEE).Time(GetTime()).SpendsCoinbase(spendsCoinbase).FromTx(tx)); tx.vin[0].prevout.hash = hash; } - BOOST_CHECK_THROW(CreateNewBlock(chainparams, scriptPubKey), std::runtime_error); + BOOST_CHECK_THROW(BlockAssembler(chainparams).CreateNewBlock(scriptPubKey), std::runtime_error); mempool.clear(); tx.vin[0].prevout.hash = txFirst[0]->GetHash(); @@ -160,7 +267,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) mempool.addUnchecked(hash, entry.Fee(LOWFEE).Time(GetTime()).SpendsCoinbase(spendsCoinbase).SigOps(20).FromTx(tx)); tx.vin[0].prevout.hash = hash; } - BOOST_CHECK(pblocktemplate = CreateNewBlock(chainparams, scriptPubKey)); + BOOST_CHECK(pblocktemplate = BlockAssembler(chainparams).CreateNewBlock(scriptPubKey)); delete pblocktemplate; mempool.clear(); @@ -181,14 +288,14 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) mempool.addUnchecked(hash, entry.Fee(LOWFEE).Time(GetTime()).SpendsCoinbase(spendsCoinbase).FromTx(tx)); tx.vin[0].prevout.hash = hash; } - BOOST_CHECK(pblocktemplate = CreateNewBlock(chainparams, scriptPubKey)); + BOOST_CHECK(pblocktemplate = BlockAssembler(chainparams).CreateNewBlock(scriptPubKey)); delete pblocktemplate; mempool.clear(); // orphan in mempool, template creation fails hash = tx.GetHash(); mempool.addUnchecked(hash, entry.Fee(LOWFEE).Time(GetTime()).FromTx(tx)); - BOOST_CHECK_THROW(CreateNewBlock(chainparams, scriptPubKey), std::runtime_error); + BOOST_CHECK_THROW(BlockAssembler(chainparams).CreateNewBlock(scriptPubKey), std::runtime_error); mempool.clear(); // child with higher priority than parent @@ -205,7 +312,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) tx.vout[0].nValue = tx.vout[0].nValue+BLOCKSUBSIDY-HIGHERFEE; //First txn output + fresh coinbase - new txn fee hash = tx.GetHash(); mempool.addUnchecked(hash, entry.Fee(HIGHERFEE).Time(GetTime()).SpendsCoinbase(true).FromTx(tx)); - BOOST_CHECK(pblocktemplate = CreateNewBlock(chainparams, scriptPubKey)); + BOOST_CHECK(pblocktemplate = BlockAssembler(chainparams).CreateNewBlock(scriptPubKey)); delete pblocktemplate; mempool.clear(); @@ -217,7 +324,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) hash = tx.GetHash(); // give it a fee so it'll get mined mempool.addUnchecked(hash, entry.Fee(LOWFEE).Time(GetTime()).SpendsCoinbase(false).FromTx(tx)); - BOOST_CHECK_THROW(CreateNewBlock(chainparams, scriptPubKey), std::runtime_error); + BOOST_CHECK_THROW(BlockAssembler(chainparams).CreateNewBlock(scriptPubKey), std::runtime_error); mempool.clear(); // invalid (pre-p2sh) txn in mempool, template creation fails @@ -234,7 +341,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) tx.vout[0].nValue -= LOWFEE; hash = tx.GetHash(); mempool.addUnchecked(hash, entry.Fee(LOWFEE).Time(GetTime()).SpendsCoinbase(false).FromTx(tx)); - BOOST_CHECK_THROW(CreateNewBlock(chainparams, scriptPubKey), std::runtime_error); + BOOST_CHECK_THROW(BlockAssembler(chainparams).CreateNewBlock(scriptPubKey), std::runtime_error); mempool.clear(); // double spend txn pair in mempool, template creation fails @@ -247,7 +354,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) tx.vout[0].scriptPubKey = CScript() << OP_2; hash = tx.GetHash(); mempool.addUnchecked(hash, entry.Fee(HIGHFEE).Time(GetTime()).SpendsCoinbase(true).FromTx(tx)); - BOOST_CHECK_THROW(CreateNewBlock(chainparams, scriptPubKey), std::runtime_error); + BOOST_CHECK_THROW(BlockAssembler(chainparams).CreateNewBlock(scriptPubKey), std::runtime_error); mempool.clear(); // subsidy changing @@ -263,7 +370,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) next->BuildSkip(); chainActive.SetTip(next); } - BOOST_CHECK(pblocktemplate = CreateNewBlock(chainparams, scriptPubKey)); + BOOST_CHECK(pblocktemplate = BlockAssembler(chainparams).CreateNewBlock(scriptPubKey)); delete pblocktemplate; // Extend to a 210000-long block chain. while (chainActive.Tip()->nHeight < 210000) { @@ -276,7 +383,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) next->BuildSkip(); chainActive.SetTip(next); } - BOOST_CHECK(pblocktemplate = CreateNewBlock(chainparams, scriptPubKey)); + BOOST_CHECK(pblocktemplate = BlockAssembler(chainparams).CreateNewBlock(scriptPubKey)); delete pblocktemplate; // Delete the dummy blocks again. while (chainActive.Tip()->nHeight > nHeight) { @@ -363,7 +470,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) tx.vin[0].nSequence = CTxIn::SEQUENCE_LOCKTIME_TYPE_FLAG | 1; BOOST_CHECK(!TestSequenceLocks(tx, flags)); // Sequence locks fail - BOOST_CHECK(pblocktemplate = CreateNewBlock(chainparams, scriptPubKey)); + BOOST_CHECK(pblocktemplate = BlockAssembler(chainparams).CreateNewBlock(scriptPubKey)); // None of the of the absolute height/time locked tx should have made // it into the template because we still check IsFinalTx in CreateNewBlock, @@ -377,7 +484,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) chainActive.Tip()->nHeight++; SetMockTime(chainActive.Tip()->GetMedianTimePast() + 1); - BOOST_CHECK(pblocktemplate = CreateNewBlock(chainparams, scriptPubKey)); + BOOST_CHECK(pblocktemplate = BlockAssembler(chainparams).CreateNewBlock(scriptPubKey)); BOOST_CHECK_EQUAL(pblocktemplate->block.vtx.size(), 5); delete pblocktemplate; @@ -385,8 +492,10 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) SetMockTime(0); mempool.clear(); - BOOST_FOREACH(CTransaction *tx, txFirst) - delete tx; + TestPackageSelection(chainparams, scriptPubKey, txFirst); + + BOOST_FOREACH(CTransaction *_tx, txFirst) + delete _tx; fCheckpointsEnabled = true; } diff --git a/src/test/net_tests.cpp b/src/test/net_tests.cpp new file mode 100644 index 0000000000..d005d6a163 --- /dev/null +++ b/src/test/net_tests.cpp @@ -0,0 +1,145 @@ +// 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 "addrman.h" +#include "test/test_bitcoin.h" +#include <string> +#include <boost/test/unit_test.hpp> +#include "hash.h" +#include "serialize.h" +#include "streams.h" +#include "net.h" +#include "chainparams.h" + +using namespace std; + +class CAddrManSerializationMock : public CAddrMan +{ +public: + virtual void Serialize(CDataStream& s, int nType, int nVersionDummy) const = 0; + + //! Ensure that bucket placement is always the same for testing purposes. + void MakeDeterministic() + { + nKey.SetNull(); + seed_insecure_rand(true); + } +}; + +class CAddrManUncorrupted : public CAddrManSerializationMock +{ +public: + void Serialize(CDataStream& s, int nType, int nVersionDummy) const + { + CAddrMan::Serialize(s, nType, nVersionDummy); + } +}; + +class CAddrManCorrupted : public CAddrManSerializationMock +{ +public: + void Serialize(CDataStream& s, int nType, int nVersionDummy) const + { + // Produces corrupt output that claims addrman has 20 addrs when it only has one addr. + unsigned char nVersion = 1; + s << nVersion; + s << ((unsigned char)32); + s << nKey; + s << 10; // nNew + s << 10; // nTried + + int nUBuckets = ADDRMAN_NEW_BUCKET_COUNT ^ (1 << 30); + s << nUBuckets; + + CAddress addr = CAddress(CService("252.1.1.1", 7777), NODE_NONE); + CAddrInfo info = CAddrInfo(addr, CNetAddr("252.2.2.2")); + s << info; + } +}; + +CDataStream AddrmanToStream(CAddrManSerializationMock& addrman) +{ + CDataStream ssPeersIn(SER_DISK, CLIENT_VERSION); + ssPeersIn << FLATDATA(Params().MessageStart()); + ssPeersIn << addrman; + std::string str = ssPeersIn.str(); + vector<unsigned char> vchData(str.begin(), str.end()); + return CDataStream(vchData, SER_DISK, CLIENT_VERSION); +} + +BOOST_FIXTURE_TEST_SUITE(net_tests, BasicTestingSetup) + +BOOST_AUTO_TEST_CASE(caddrdb_read) +{ + CAddrManUncorrupted addrmanUncorrupted; + addrmanUncorrupted.MakeDeterministic(); + + CService addr1 = CService("250.7.1.1", 8333); + CService addr2 = CService("250.7.2.2", 9999); + CService addr3 = CService("250.7.3.3", 9999); + + // Add three addresses to new table. + addrmanUncorrupted.Add(CAddress(addr1, NODE_NONE), CService("252.5.1.1", 8333)); + addrmanUncorrupted.Add(CAddress(addr2, NODE_NONE), CService("252.5.1.1", 8333)); + addrmanUncorrupted.Add(CAddress(addr3, NODE_NONE), CService("252.5.1.1", 8333)); + + // Test that the de-serialization does not throw an exception. + CDataStream ssPeers1 = AddrmanToStream(addrmanUncorrupted); + bool exceptionThrown = false; + CAddrMan addrman1; + + BOOST_CHECK(addrman1.size() == 0); + try { + unsigned char pchMsgTmp[4]; + ssPeers1 >> FLATDATA(pchMsgTmp); + ssPeers1 >> addrman1; + } catch (const std::exception& e) { + exceptionThrown = true; + } + + BOOST_CHECK(addrman1.size() == 3); + BOOST_CHECK(exceptionThrown == false); + + // Test that CAddrDB::Read creates an addrman with the correct number of addrs. + CDataStream ssPeers2 = AddrmanToStream(addrmanUncorrupted); + + CAddrMan addrman2; + CAddrDB adb; + BOOST_CHECK(addrman2.size() == 0); + adb.Read(addrman2, ssPeers2); + BOOST_CHECK(addrman2.size() == 3); +} + + +BOOST_AUTO_TEST_CASE(caddrdb_read_corrupted) +{ + CAddrManCorrupted addrmanCorrupted; + addrmanCorrupted.MakeDeterministic(); + + // Test that the de-serialization of corrupted addrman throws an exception. + CDataStream ssPeers1 = AddrmanToStream(addrmanCorrupted); + bool exceptionThrown = false; + CAddrMan addrman1; + BOOST_CHECK(addrman1.size() == 0); + try { + unsigned char pchMsgTmp[4]; + ssPeers1 >> FLATDATA(pchMsgTmp); + ssPeers1 >> addrman1; + } catch (const std::exception& e) { + exceptionThrown = true; + } + // Even through de-serialization failed addrman is not left in a clean state. + BOOST_CHECK(addrman1.size() == 1); + BOOST_CHECK(exceptionThrown); + + // Test that CAddrDB::Read leaves addrman in a clean state if de-serialization fails. + CDataStream ssPeers2 = AddrmanToStream(addrmanCorrupted); + + CAddrMan addrman2; + CAddrDB adb; + BOOST_CHECK(addrman2.size() == 0); + adb.Read(addrman2, ssPeers2); + BOOST_CHECK(addrman2.size() == 0); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/pmt_tests.cpp b/src/test/pmt_tests.cpp index 2f3f607889..74ffe0cc74 100644 --- a/src/test/pmt_tests.cpp +++ b/src/test/pmt_tests.cpp @@ -37,8 +37,8 @@ BOOST_AUTO_TEST_CASE(pmt_test1) seed_insecure_rand(false); 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]; + for (int i = 0; i < 12; i++) { + unsigned int nTx = nTxCounts[i]; // build a block with some dummy transactions CBlock block; diff --git a/src/test/policyestimator_tests.cpp b/src/test/policyestimator_tests.cpp index 644c3da213..2b00e6f567 100644 --- a/src/test/policyestimator_tests.cpp +++ b/src/test/policyestimator_tests.cpp @@ -74,9 +74,9 @@ BOOST_AUTO_TEST_CASE(BlockPolicyEstimates) // 9/10 blocks add 2nd highest and so on until ... // 1/10 blocks add lowest fee/pri transactions while (txHashes[9-h].size()) { - CTransaction btx; - if (mpool.lookup(txHashes[9-h].back(), btx)) - block.push_back(btx); + std::shared_ptr<const CTransaction> ptx = mpool.get(txHashes[9-h].back()); + if (ptx) + block.push_back(*ptx); txHashes[9-h].pop_back(); } } @@ -160,9 +160,9 @@ BOOST_AUTO_TEST_CASE(BlockPolicyEstimates) // Estimates should still not be below original for (int j = 0; j < 10; j++) { while(txHashes[j].size()) { - CTransaction btx; - if (mpool.lookup(txHashes[j].back(), btx)) - block.push_back(btx); + std::shared_ptr<const CTransaction> ptx = mpool.get(txHashes[j].back()); + if (ptx) + block.push_back(*ptx); txHashes[j].pop_back(); } } @@ -181,9 +181,9 @@ BOOST_AUTO_TEST_CASE(BlockPolicyEstimates) tx.vin[0].prevout.n = 10000*blocknum+100*j+k; uint256 hash = tx.GetHash(); mpool.addUnchecked(hash, entry.Fee(feeV[k/4][j]).Time(GetTime()).Priority(priV[k/4][j]).Height(blocknum).FromTx(tx, &mpool)); - CTransaction btx; - if (mpool.lookup(hash, btx)) - block.push_back(btx); + std::shared_ptr<const CTransaction> ptx = mpool.get(hash); + if (ptx) + block.push_back(*ptx); } } mpool.removeForBlock(block, ++blocknum, dummyConflicted); diff --git a/src/test/prevector_tests.cpp b/src/test/prevector_tests.cpp index b39b903530..d1407c1da9 100644 --- a/src/test/prevector_tests.cpp +++ b/src/test/prevector_tests.cpp @@ -192,8 +192,8 @@ BOOST_AUTO_TEST_CASE(PrevectorTestInt) if (((r >> 21) % 32) == 7) { int values[4]; int num = 1 + (insecure_rand() % 4); - for (int i = 0; i < num; i++) { - values[i] = insecure_rand(); + for (int k = 0; k < num; k++) { + values[k] = insecure_rand(); } test.insert_range(insecure_rand() % (test.size() + 1), values, values + num); } diff --git a/src/test/script_tests.cpp b/src/test/script_tests.cpp index 5e9711a4a7..39089f103d 100644 --- a/src/test/script_tests.cpp +++ b/src/test/script_tests.cpp @@ -427,7 +427,10 @@ BOOST_AUTO_TEST_CASE(script_build) tests.push_back(TestBuilder(CScript() << ToByteVector(keys.pubkey0C) << OP_CHECKSIG, "P2SH(P2PK), bad redeemscript", SCRIPT_VERIFY_P2SH, true ).PushSig(keys.key0).PushRedeem().DamagePush(10).ScriptError(SCRIPT_ERR_EVAL_FALSE)); - + + tests.push_back(TestBuilder(CScript() << OP_DUP << OP_HASH160 << ToByteVector(keys.pubkey0.GetID()) << OP_EQUALVERIFY << OP_CHECKSIG, + "P2SH(P2PKH)", SCRIPT_VERIFY_P2SH, true + ).PushSig(keys.key0).Push(keys.pubkey0).PushRedeem()); tests.push_back(TestBuilder(CScript() << OP_DUP << OP_HASH160 << ToByteVector(keys.pubkey1.GetID()) << OP_EQUALVERIFY << OP_CHECKSIG, "P2SH(P2PKH), bad sig but no VERIFY_P2SH", 0, true ).PushSig(keys.key0).DamagePush(10).PushRedeem()); diff --git a/src/test/sighash_tests.cpp b/src/test/sighash_tests.cpp index 04c6fa9625..e43b2ff6c4 100644 --- a/src/test/sighash_tests.cpp +++ b/src/test/sighash_tests.cpp @@ -195,7 +195,6 @@ BOOST_AUTO_TEST_CASE(sighash_from_data) nHashType = test[3].get_int(); sigHashHex = test[4].get_str(); - uint256 sh; CDataStream stream(ParseHex(raw_tx), SER_NETWORK, PROTOCOL_VERSION); stream >> tx; diff --git a/src/test/test_bitcoin.cpp b/src/test/test_bitcoin.cpp index 9bcb07626a..c68320ba8b 100644 --- a/src/test/test_bitcoin.cpp +++ b/src/test/test_bitcoin.cpp @@ -98,7 +98,7 @@ CBlock TestChain100Setup::CreateAndProcessBlock(const std::vector<CMutableTransaction>& txns, const CScript& scriptPubKey) { const CChainParams& chainparams = Params(); - CBlockTemplate *pblocktemplate = CreateNewBlock(chainparams, scriptPubKey); + CBlockTemplate *pblocktemplate = BlockAssembler(chainparams).CreateNewBlock(scriptPubKey); CBlock& block = pblocktemplate->block; // Replace mempool-selected txns with just coinbase plus passed-in txns: diff --git a/src/test/util_tests.cpp b/src/test/util_tests.cpp index b99f952a0d..e467a4171d 100644 --- a/src/test/util_tests.cpp +++ b/src/test/util_tests.cpp @@ -376,6 +376,69 @@ BOOST_AUTO_TEST_CASE(test_ParseInt64) BOOST_CHECK(!ParseInt64("32482348723847471234", NULL)); } +BOOST_AUTO_TEST_CASE(test_ParseUInt32) +{ + uint32_t n; + // Valid values + BOOST_CHECK(ParseUInt32("1234", NULL)); + BOOST_CHECK(ParseUInt32("0", &n) && n == 0); + BOOST_CHECK(ParseUInt32("1234", &n) && n == 1234); + BOOST_CHECK(ParseUInt32("01234", &n) && n == 1234); // no octal + BOOST_CHECK(ParseUInt32("2147483647", &n) && n == 2147483647); + BOOST_CHECK(ParseUInt32("2147483648", &n) && n == (uint32_t)2147483648); + BOOST_CHECK(ParseUInt32("4294967295", &n) && n == (uint32_t)4294967295); + // Invalid values + BOOST_CHECK(!ParseUInt32("", &n)); + BOOST_CHECK(!ParseUInt32(" 1", &n)); // no padding inside + BOOST_CHECK(!ParseUInt32(" -1", &n)); + BOOST_CHECK(!ParseUInt32("1 ", &n)); + BOOST_CHECK(!ParseUInt32("1a", &n)); + BOOST_CHECK(!ParseUInt32("aap", &n)); + BOOST_CHECK(!ParseUInt32("0x1", &n)); // no hex + BOOST_CHECK(!ParseUInt32("0x1", &n)); // no hex + const char test_bytes[] = {'1', 0, '1'}; + std::string teststr(test_bytes, sizeof(test_bytes)); + BOOST_CHECK(!ParseUInt32(teststr, &n)); // no embedded NULs + // Overflow and underflow + BOOST_CHECK(!ParseUInt32("-2147483648", &n)); + BOOST_CHECK(!ParseUInt32("4294967296", &n)); + BOOST_CHECK(!ParseUInt32("-1234", &n)); + BOOST_CHECK(!ParseUInt32("-32482348723847471234", NULL)); + BOOST_CHECK(!ParseUInt32("32482348723847471234", NULL)); +} + +BOOST_AUTO_TEST_CASE(test_ParseUInt64) +{ + uint64_t n; + // Valid values + BOOST_CHECK(ParseUInt64("1234", NULL)); + BOOST_CHECK(ParseUInt64("0", &n) && n == 0LL); + BOOST_CHECK(ParseUInt64("1234", &n) && n == 1234LL); + BOOST_CHECK(ParseUInt64("01234", &n) && n == 1234LL); // no octal + BOOST_CHECK(ParseUInt64("2147483647", &n) && n == 2147483647LL); + BOOST_CHECK(ParseUInt64("9223372036854775807", &n) && n == 9223372036854775807ULL); + BOOST_CHECK(ParseUInt64("9223372036854775808", &n) && n == 9223372036854775808ULL); + BOOST_CHECK(ParseUInt64("18446744073709551615", &n) && n == 18446744073709551615ULL); + // Invalid values + BOOST_CHECK(!ParseUInt64("", &n)); + BOOST_CHECK(!ParseUInt64(" 1", &n)); // no padding inside + BOOST_CHECK(!ParseUInt64(" -1", &n)); + BOOST_CHECK(!ParseUInt64("1 ", &n)); + BOOST_CHECK(!ParseUInt64("1a", &n)); + BOOST_CHECK(!ParseUInt64("aap", &n)); + BOOST_CHECK(!ParseUInt64("0x1", &n)); // no hex + const char test_bytes[] = {'1', 0, '1'}; + std::string teststr(test_bytes, sizeof(test_bytes)); + BOOST_CHECK(!ParseUInt64(teststr, &n)); // no embedded NULs + // Overflow and underflow + BOOST_CHECK(!ParseUInt64("-9223372036854775809", NULL)); + BOOST_CHECK(!ParseUInt64("18446744073709551616", NULL)); + BOOST_CHECK(!ParseUInt64("-32482348723847471234", NULL)); + BOOST_CHECK(!ParseUInt64("-2147483648", &n)); + BOOST_CHECK(!ParseUInt64("-9223372036854775808", &n)); + BOOST_CHECK(!ParseUInt64("-1234", &n)); +} + BOOST_AUTO_TEST_CASE(test_ParseDouble) { double n; diff --git a/src/torcontrol.cpp b/src/torcontrol.cpp index 47d834c7b4..0d6b655675 100644 --- a/src/torcontrol.cpp +++ b/src/torcontrol.cpp @@ -574,7 +574,15 @@ void TorController::protocolinfo_cb(TorControlConnection& conn, const TorControl * password: "password" */ std::string torpassword = GetArg("-torpassword", ""); - if (methods.count("NULL")) { + if (!torpassword.empty()) { + if (methods.count("HASHEDPASSWORD")) { + LogPrint("tor", "tor: Using HASHEDPASSWORD authentication\n"); + boost::replace_all(torpassword, "\"", "\\\""); + conn.Command("AUTHENTICATE \"" + torpassword + "\"", boost::bind(&TorController::auth_cb, this, _1, _2)); + } else { + LogPrintf("tor: Password provided with -torpassword, but HASHEDPASSWORD authentication is not available\n"); + } + } else if (methods.count("NULL")) { LogPrint("tor", "tor: Using NULL authentication\n"); conn.Command("AUTHENTICATE", boost::bind(&TorController::auth_cb, this, _1, _2)); } else if (methods.count("SAFECOOKIE")) { @@ -595,13 +603,7 @@ void TorController::protocolinfo_cb(TorControlConnection& conn, const TorControl } } } else if (methods.count("HASHEDPASSWORD")) { - if (!torpassword.empty()) { - LogPrint("tor", "tor: Using HASHEDPASSWORD authentication\n"); - boost::replace_all(torpassword, "\"", "\\\""); - conn.Command("AUTHENTICATE \"" + torpassword + "\"", boost::bind(&TorController::auth_cb, this, _1, _2)); - } else { - LogPrintf("tor: Password authentication required, but no password provided with -torpassword\n"); - } + LogPrintf("tor: The only supported authentication mechanism left is password, but no password provided with -torpassword\n"); } else { LogPrintf("tor: No supported authentication method\n"); } diff --git a/src/txmempool.cpp b/src/txmempool.cpp index aa5df6ca4e..205ffd6379 100644 --- a/src/txmempool.cpp +++ b/src/txmempool.cpp @@ -23,18 +23,18 @@ CTxMemPoolEntry::CTxMemPoolEntry(const CTransaction& _tx, const CAmount& _nFee, int64_t _nTime, double _entryPriority, unsigned int _entryHeight, bool poolHasNoInputsOf, CAmount _inChainInputValue, bool _spendsCoinbase, unsigned int _sigOps, LockPoints lp): - tx(_tx), nFee(_nFee), nTime(_nTime), entryPriority(_entryPriority), entryHeight(_entryHeight), + tx(std::make_shared<CTransaction>(_tx)), nFee(_nFee), nTime(_nTime), entryPriority(_entryPriority), entryHeight(_entryHeight), hadNoDependencies(poolHasNoInputsOf), inChainInputValue(_inChainInputValue), spendsCoinbase(_spendsCoinbase), sigOpCount(_sigOps), lockPoints(lp) { - nTxSize = ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION); - nModSize = tx.CalculateModifiedSize(nTxSize); - nUsageSize = RecursiveDynamicUsage(tx); + nTxSize = ::GetSerializeSize(_tx, SER_NETWORK, PROTOCOL_VERSION); + nModSize = _tx.CalculateModifiedSize(nTxSize); + nUsageSize = RecursiveDynamicUsage(*tx) + memusage::DynamicUsage(tx); nCountWithDescendants = 1; nSizeWithDescendants = nTxSize; nModFeesWithDescendants = nFee; - CAmount nValueIn = tx.GetValueOut()+nFee; + CAmount nValueIn = _tx.GetValueOut()+nFee; assert(inChainInputValue <= nValueIn); feeDelta = 0; @@ -147,11 +147,11 @@ void CTxMemPool::UpdateTransactionsFromBlock(const std::vector<uint256> &vHashes if (it == mapTx.end()) { continue; } - std::map<COutPoint, CInPoint>::iterator iter = mapNextTx.lower_bound(COutPoint(hash, 0)); + auto iter = mapNextTx.lower_bound(COutPoint(hash, 0)); // First calculate the children, and update setMemPoolChildren to // include them, and update their setMemPoolParents to include this tx. - for (; iter != mapNextTx.end() && iter->first.hash == hash; ++iter) { - const uint256 &childHash = iter->second.ptx->GetHash(); + for (; iter != mapNextTx.end() && iter->first->hash == hash; ++iter) { + const uint256 &childHash = iter->second->GetHash(); txiter childIter = mapTx.find(childHash); assert(childIter != mapTx.end()); // We can skip updating entries we've encountered before or that @@ -365,11 +365,11 @@ void CTxMemPool::pruneSpent(const uint256 &hashTx, CCoins &coins) { LOCK(cs); - std::map<COutPoint, CInPoint>::iterator it = mapNextTx.lower_bound(COutPoint(hashTx, 0)); + auto it = mapNextTx.lower_bound(COutPoint(hashTx, 0)); // iterate over all COutPoints in mapNextTx whose hash equals the provided hashTx - while (it != mapNextTx.end() && it->first.hash == hashTx) { - coins.Spend(it->first.n); // and remove those outputs from coins + while (it != mapNextTx.end() && it->first->hash == hashTx) { + coins.Spend(it->first->n); // and remove those outputs from coins it++; } } @@ -414,7 +414,7 @@ bool CTxMemPool::addUnchecked(const uint256& hash, const CTxMemPoolEntry &entry, const CTransaction& tx = newit->GetTx(); std::set<uint256> setParentTransactions; for (unsigned int i = 0; i < tx.vin.size(); i++) { - mapNextTx[tx.vin[i].prevout] = CInPoint(&tx, i); + mapNextTx.insert(std::make_pair(&tx.vin[i].prevout, &tx)); setParentTransactions.insert(tx.vin[i].prevout.hash); } // Don't bother worrying about child transactions of this one. @@ -500,10 +500,10 @@ void CTxMemPool::removeRecursive(const CTransaction &origTx, std::list<CTransact // happen during chain re-orgs if origTx isn't re-accepted into // the mempool for any reason. for (unsigned int i = 0; i < origTx.vout.size(); i++) { - std::map<COutPoint, CInPoint>::iterator it = mapNextTx.find(COutPoint(origTx.GetHash(), i)); + auto it = mapNextTx.find(COutPoint(origTx.GetHash(), i)); if (it == mapNextTx.end()) continue; - txiter nextit = mapTx.find(it->second.ptx->GetHash()); + txiter nextit = mapTx.find(it->second->GetHash()); assert(nextit != mapTx.end()); txToRemove.insert(nextit); } @@ -561,9 +561,9 @@ void CTxMemPool::removeConflicts(const CTransaction &tx, std::list<CTransaction> list<CTransaction> result; LOCK(cs); BOOST_FOREACH(const CTxIn &txin, tx.vin) { - std::map<COutPoint, CInPoint>::iterator it = mapNextTx.find(txin.prevout); + auto it = mapNextTx.find(txin.prevout); if (it != mapNextTx.end()) { - const CTransaction &txConflict = *it->second.ptx; + const CTransaction &txConflict = *it->second; if (txConflict != tx) { removeRecursive(txConflict, removed); @@ -671,10 +671,10 @@ void CTxMemPool::check(const CCoinsViewCache *pcoins) const assert(coins && coins->IsAvailable(txin.prevout.n)); } // Check whether its inputs are marked in mapNextTx. - std::map<COutPoint, CInPoint>::const_iterator it3 = mapNextTx.find(txin.prevout); + auto it3 = mapNextTx.find(txin.prevout); assert(it3 != mapNextTx.end()); - assert(it3->second.ptx == &tx); - assert(it3->second.n == i); + assert(it3->first == &txin.prevout); + assert(it3->second == &tx); i++; } assert(setParentCheck == GetMemPoolParents(it)); @@ -701,10 +701,10 @@ void CTxMemPool::check(const CCoinsViewCache *pcoins) const // Check children against mapNextTx CTxMemPool::setEntries setChildrenCheck; - std::map<COutPoint, CInPoint>::const_iterator iter = mapNextTx.lower_bound(COutPoint(it->GetTx().GetHash(), 0)); + auto iter = mapNextTx.lower_bound(COutPoint(it->GetTx().GetHash(), 0)); int64_t childSizes = 0; - for (; iter != mapNextTx.end() && iter->first.hash == it->GetTx().GetHash(); ++iter) { - txiter childit = mapTx.find(iter->second.ptx->GetHash()); + for (; iter != mapNextTx.end() && iter->first->hash == it->GetTx().GetHash(); ++iter) { + txiter childit = mapTx.find(iter->second->GetHash()); assert(childit != mapTx.end()); // mapNextTx points to in-mempool transactions if (setChildrenCheck.insert(childit).second) { childSizes += childit->GetTxSize(); @@ -738,14 +738,12 @@ void CTxMemPool::check(const CCoinsViewCache *pcoins) const stepsSinceLastRemove = 0; } } - for (std::map<COutPoint, CInPoint>::const_iterator it = mapNextTx.begin(); it != mapNextTx.end(); it++) { - uint256 hash = it->second.ptx->GetHash(); + for (auto it = mapNextTx.cbegin(); it != mapNextTx.cend(); it++) { + uint256 hash = it->second->GetHash(); indexed_transaction_set::const_iterator it2 = mapTx.find(hash); const CTransaction& tx = it2->GetTx(); assert(it2 != mapTx.end()); - assert(&tx == it->second.ptx); - assert(tx.vin.size() > it->second.n); - assert(it->first == it->second.ptx->vin[it->second.n].prevout); + assert(&tx == it->second); } assert(totalTxSize == checkTotal); @@ -770,42 +768,76 @@ bool CTxMemPool::CompareDepthAndScore(const uint256& hasha, const uint256& hashb namespace { class DepthAndScoreComparator { - CTxMemPool *mp; public: - DepthAndScoreComparator(CTxMemPool *mempool) : mp(mempool) {} - bool operator()(const uint256& a, const uint256& b) { return mp->CompareDepthAndScore(a, b); } + bool operator()(const CTxMemPool::indexed_transaction_set::const_iterator& a, const CTxMemPool::indexed_transaction_set::const_iterator& b) + { + uint64_t counta = a->GetCountWithAncestors(); + uint64_t countb = b->GetCountWithAncestors(); + if (counta == countb) { + return CompareTxMemPoolEntryByScore()(*a, *b); + } + return counta < countb; + } }; } +std::vector<CTxMemPool::indexed_transaction_set::const_iterator> CTxMemPool::GetSortedDepthAndScore() const +{ + std::vector<indexed_transaction_set::const_iterator> iters; + AssertLockHeld(cs); + + iters.reserve(mapTx.size()); + + for (indexed_transaction_set::iterator mi = mapTx.begin(); mi != mapTx.end(); ++mi) { + iters.push_back(mi); + } + std::sort(iters.begin(), iters.end(), DepthAndScoreComparator()); + return iters; +} + void CTxMemPool::queryHashes(vector<uint256>& vtxid) { + LOCK(cs); + auto iters = GetSortedDepthAndScore(); + vtxid.clear(); + vtxid.reserve(mapTx.size()); + for (auto it : iters) { + vtxid.push_back(it->GetTx().GetHash()); + } +} + +std::vector<TxMempoolInfo> CTxMemPool::infoAll() const +{ LOCK(cs); - vtxid.reserve(mapTx.size()); - for (indexed_transaction_set::iterator mi = mapTx.begin(); mi != mapTx.end(); ++mi) - vtxid.push_back(mi->GetTx().GetHash()); + auto iters = GetSortedDepthAndScore(); + + std::vector<TxMempoolInfo> ret; + ret.reserve(mapTx.size()); + for (auto it : iters) { + ret.push_back(TxMempoolInfo{it->GetSharedTx(), it->GetTime(), CFeeRate(it->GetFee(), it->GetTxSize())}); + } - std::sort(vtxid.begin(), vtxid.end(), DepthAndScoreComparator(this)); + return ret; } -bool CTxMemPool::lookup(uint256 hash, CTransaction& result) const +std::shared_ptr<const CTransaction> CTxMemPool::get(const uint256& hash) const { LOCK(cs); indexed_transaction_set::const_iterator i = mapTx.find(hash); - if (i == mapTx.end()) return false; - result = i->GetTx(); - return true; + if (i == mapTx.end()) + return nullptr; + return i->GetSharedTx(); } -bool CTxMemPool::lookupFeeRate(const uint256& hash, CFeeRate& feeRate) const +TxMempoolInfo CTxMemPool::info(const uint256& hash) const { LOCK(cs); indexed_transaction_set::const_iterator i = mapTx.find(hash); if (i == mapTx.end()) - return false; - feeRate = CFeeRate(i->GetFee(), i->GetTxSize()); - return true; + return TxMempoolInfo(); + return TxMempoolInfo{i->GetSharedTx(), i->GetTime(), CFeeRate(i->GetFee(), i->GetTxSize())}; } CFeeRate CTxMemPool::estimateFee(int nBlocks) const @@ -918,9 +950,9 @@ bool CCoinsViewMemPool::GetCoins(const uint256 &txid, CCoins &coins) const { // If an entry in the mempool exists, always return that one, as it's guaranteed to never // conflict with the underlying cache, and it cannot have pruned entries (as it contains full) // transactions. First checking the underlying cache risks returning a pruned entry instead. - CTransaction tx; - if (mempool.lookup(txid, tx)) { - coins = CCoins(tx, MEMPOOL_HEIGHT); + shared_ptr<const CTransaction> ptx = mempool.get(txid); + if (ptx) { + coins = CCoins(*ptx, MEMPOOL_HEIGHT); return true; } return (base->GetCoins(txid, coins) && !coins.IsPruned()); @@ -1071,8 +1103,8 @@ void CTxMemPool::TrimToSize(size_t sizelimit, std::vector<uint256>* pvNoSpendsRe BOOST_FOREACH(const CTxIn& txin, tx.vin) { if (exists(txin.prevout.hash)) continue; - std::map<COutPoint, CInPoint>::iterator it = mapNextTx.lower_bound(COutPoint(txin.prevout.hash, 0)); - if (it == mapNextTx.end() || it->first.hash != txin.prevout.hash) + auto it = mapNextTx.lower_bound(COutPoint(txin.prevout.hash, 0)); + if (it == mapNextTx.end() || it->first->hash != txin.prevout.hash) pvNoSpendsRemaining->push_back(txin.prevout.hash); } } diff --git a/src/txmempool.h b/src/txmempool.h index bca8dd9791..f0e9b2e2c6 100644 --- a/src/txmempool.h +++ b/src/txmempool.h @@ -7,16 +7,19 @@ #define BITCOIN_TXMEMPOOL_H #include <list> +#include <memory> #include <set> #include "amount.h" #include "coins.h" +#include "indirectmap.h" #include "primitives/transaction.h" #include "sync.h" #undef foreach #include "boost/multi_index_container.hpp" #include "boost/multi_index/ordered_index.hpp" +#include "boost/multi_index/hashed_index.hpp" class CAutoFile; class CBlockIndex; @@ -73,7 +76,7 @@ class CTxMemPool; class CTxMemPoolEntry { private: - CTransaction tx; + std::shared_ptr<const CTransaction> tx; CAmount nFee; //!< Cached to avoid expensive parent-transaction lookups size_t nTxSize; //!< ... and avoid recomputing tx size size_t nModSize; //!< ... and modified size for priority @@ -110,7 +113,8 @@ public: unsigned int nSigOps, LockPoints lp); CTxMemPoolEntry(const CTxMemPoolEntry& other); - const CTransaction& GetTx() const { return this->tx; } + const CTransaction& GetTx() const { return *this->tx; } + std::shared_ptr<const CTransaction> GetSharedTx() const { return this->tx; } /** * Fast calculation of lower bound of current priority as update * from entry priority. Only inputs that were originally in-chain will age. @@ -305,18 +309,19 @@ struct ancestor_score {}; class CBlockPolicyEstimator; -/** An inpoint - a combination of a transaction and an index n into its vin */ -class CInPoint +/** + * Information about a mempool transaction. + */ +struct TxMempoolInfo { -public: - const CTransaction* ptx; - uint32_t n; - - CInPoint() { SetNull(); } - CInPoint(const CTransaction* ptxIn, uint32_t nIn) { ptx = ptxIn; n = nIn; } - void SetNull() { ptx = NULL; n = (uint32_t) -1; } - bool IsNull() const { return (ptx == NULL && n == (uint32_t) -1); } - size_t DynamicMemoryUsage() const { return 0; } + /** The transaction itself */ + std::shared_ptr<const CTransaction> tx; + + /** Time the transaction entered the mempool. */ + int64_t nTime; + + /** Feerate of the transaction. */ + CFeeRate feeRate; }; /** @@ -422,7 +427,7 @@ public: CTxMemPoolEntry, boost::multi_index::indexed_by< // sorted by txid - boost::multi_index::ordered_unique<mempoolentry_txid>, + boost::multi_index::hashed_unique<mempoolentry_txid, SaltedTxidHasher>, // sorted by fee rate boost::multi_index::ordered_non_unique< boost::multi_index::tag<descendant_score>, @@ -476,8 +481,10 @@ private: void UpdateParent(txiter entry, txiter parent, bool add); void UpdateChild(txiter entry, txiter child, bool add); + std::vector<indexed_transaction_set::const_iterator> GetSortedDepthAndScore() const; + public: - std::map<COutPoint, CInPoint> mapNextTx; + indirectmap<COutPoint, const CTransaction*> mapNextTx; std::map<uint256, std::pair<double, CAmount> > mapDeltas; /** Create a new CTxMemPool. @@ -600,8 +607,9 @@ public: return (mapTx.count(hash) != 0); } - bool lookup(uint256 hash, CTransaction& result) const; - bool lookupFeeRate(const uint256& hash, CFeeRate& feeRate) const; + std::shared_ptr<const CTransaction> get(const uint256& hash) const; + TxMempoolInfo info(const uint256& hash) const; + std::vector<TxMempoolInfo> infoAll() const; /** Estimate fee rate needed to get into the next nBlocks * If no answer can be given at nBlocks, return an estimate diff --git a/src/ui_interface.h b/src/ui_interface.h index a27918c507..7ebfc17e5d 100644 --- a/src/ui_interface.h +++ b/src/ui_interface.h @@ -96,6 +96,9 @@ public: /** New block has been accepted */ boost::signals2::signal<void (bool, const CBlockIndex *)> NotifyBlockTip; + /** Best header has changed */ + boost::signals2::signal<void (bool, const CBlockIndex *)> NotifyHeaderTip; + /** Banlist did change. */ boost::signals2::signal<void (void)> BannedListChanged; }; diff --git a/src/uint256.cpp b/src/uint256.cpp index c58c88bf4a..f22ddcd1ef 100644 --- a/src/uint256.cpp +++ b/src/uint256.cpp @@ -80,67 +80,3 @@ template std::string base_blob<256>::GetHex() const; template std::string base_blob<256>::ToString() const; template void base_blob<256>::SetHex(const char*); template void base_blob<256>::SetHex(const std::string&); - -static void inline HashMix(uint32_t& a, uint32_t& b, uint32_t& c) -{ - // Taken from lookup3, by Bob Jenkins. - a -= c; - a ^= ((c << 4) | (c >> 28)); - c += b; - b -= a; - b ^= ((a << 6) | (a >> 26)); - a += c; - c -= b; - c ^= ((b << 8) | (b >> 24)); - b += a; - a -= c; - a ^= ((c << 16) | (c >> 16)); - c += b; - b -= a; - b ^= ((a << 19) | (a >> 13)); - a += c; - c -= b; - c ^= ((b << 4) | (b >> 28)); - b += a; -} - -static void inline HashFinal(uint32_t& a, uint32_t& b, uint32_t& c) -{ - // Taken from lookup3, by Bob Jenkins. - c ^= b; - c -= ((b << 14) | (b >> 18)); - a ^= c; - a -= ((c << 11) | (c >> 21)); - b ^= a; - b -= ((a << 25) | (a >> 7)); - c ^= b; - c -= ((b << 16) | (b >> 16)); - a ^= c; - a -= ((c << 4) | (c >> 28)); - b ^= a; - b -= ((a << 14) | (a >> 18)); - c ^= b; - c -= ((b << 24) | (b >> 8)); -} - -uint64_t uint256::GetHash(const uint256& salt) const -{ - uint32_t a, b, c; - const uint32_t *pn = (const uint32_t*)data; - const uint32_t *salt_pn = (const uint32_t*)salt.data; - a = b = c = 0xdeadbeef + WIDTH; - - a += pn[0] ^ salt_pn[0]; - b += pn[1] ^ salt_pn[1]; - c += pn[2] ^ salt_pn[2]; - HashMix(a, b, c); - a += pn[3] ^ salt_pn[3]; - b += pn[4] ^ salt_pn[4]; - c += pn[5] ^ salt_pn[5]; - HashMix(a, b, c); - a += pn[6] ^ salt_pn[6]; - b += pn[7] ^ salt_pn[7]; - HashFinal(a, b, c); - - return ((((uint64_t)b) << 32) | c); -} diff --git a/src/uint256.h b/src/uint256.h index bcdb6dd7c2..dd8432d74c 100644 --- a/src/uint256.h +++ b/src/uint256.h @@ -83,6 +83,19 @@ public: return sizeof(data); } + uint64_t GetUint64(int pos) const + { + const uint8_t* ptr = data + pos * 8; + return ((uint64_t)ptr[0]) | \ + ((uint64_t)ptr[1]) << 8 | \ + ((uint64_t)ptr[2]) << 16 | \ + ((uint64_t)ptr[3]) << 24 | \ + ((uint64_t)ptr[4]) << 32 | \ + ((uint64_t)ptr[5]) << 40 | \ + ((uint64_t)ptr[6]) << 48 | \ + ((uint64_t)ptr[7]) << 56; + } + template<typename Stream> void Serialize(Stream& s, int nType, int nVersion) const { @@ -127,11 +140,6 @@ public: { return ReadLE64(data); } - - /** A more secure, salted hash function. - * @note This hash is not stable between little and big endian. - */ - uint64_t GetHash(const uint256& salt) const; }; /* uint256 from const char *. diff --git a/src/univalue/Makefile.am b/src/univalue/Makefile.am index 34fe9e3f13..6c1ec81e63 100644 --- a/src/univalue/Makefile.am +++ b/src/univalue/Makefile.am @@ -3,7 +3,7 @@ ACLOCAL_AMFLAGS = -I build-aux/m4 .INTERMEDIATE: $(GENBIN) include_HEADERS = include/univalue.h -noinst_HEADERS = lib/univalue_escapes.h +noinst_HEADERS = lib/univalue_escapes.h lib/univalue_utffilter.h lib_LTLIBRARIES = libunivalue.la @@ -73,6 +73,10 @@ TEST_FILES = \ $(TEST_DATA_DIR)/fail35.json \ $(TEST_DATA_DIR)/fail36.json \ $(TEST_DATA_DIR)/fail37.json \ + $(TEST_DATA_DIR)/fail38.json \ + $(TEST_DATA_DIR)/fail39.json \ + $(TEST_DATA_DIR)/fail40.json \ + $(TEST_DATA_DIR)/fail41.json \ $(TEST_DATA_DIR)/fail3.json \ $(TEST_DATA_DIR)/fail4.json \ $(TEST_DATA_DIR)/fail5.json \ @@ -83,6 +87,7 @@ TEST_FILES = \ $(TEST_DATA_DIR)/pass1.json \ $(TEST_DATA_DIR)/pass2.json \ $(TEST_DATA_DIR)/pass3.json \ - $(TEST_DATA_DIR)/round1.json + $(TEST_DATA_DIR)/round1.json \ + $(TEST_DATA_DIR)/round2.json EXTRA_DIST=$(TEST_FILES) $(GEN_SRCS) diff --git a/src/univalue/configure.ac b/src/univalue/configure.ac index 0515b632bd..93d3ba945d 100644 --- a/src/univalue/configure.ac +++ b/src/univalue/configure.ac @@ -1,7 +1,7 @@ m4_define([libunivalue_major_version], [1]) m4_define([libunivalue_minor_version], [1]) -m4_define([libunivalue_micro_version], [1]) -m4_define([libunivalue_interface_age], [1]) +m4_define([libunivalue_micro_version], [2]) +m4_define([libunivalue_interface_age], [2]) # If you need a modifier for the version number. # Normally empty, but can be used to make "fixup" releases. m4_define([libunivalue_extraversion], []) @@ -14,7 +14,7 @@ m4_define([libunivalue_age], [m4_eval(libunivalue_binary_age - libunivalue_inter m4_define([libunivalue_version], [libunivalue_major_version().libunivalue_minor_version().libunivalue_micro_version()libunivalue_extraversion()]) -AC_INIT([univalue], [1.0.1], +AC_INIT([univalue], [1.0.2], [http://github.com/jgarzik/univalue/]) dnl make the compilation flags quiet unless V=1 is used diff --git a/src/univalue/lib/univalue_read.cpp b/src/univalue/lib/univalue_read.cpp index c7516b9628..95bac6958d 100644 --- a/src/univalue/lib/univalue_read.cpp +++ b/src/univalue/lib/univalue_read.cpp @@ -6,6 +6,7 @@ #include <vector> #include <stdio.h> #include "univalue.h" +#include "univalue_utffilter.h" using namespace std; @@ -174,41 +175,31 @@ enum jtokentype getJsonToken(string& tokenVal, unsigned int& consumed, raw++; // skip " string valStr; + JSONUTF8StringFilter writer(valStr); while (*raw) { - if (*raw < 0x20) + if ((unsigned char)*raw < 0x20) return JTOK_ERR; else if (*raw == '\\') { raw++; // skip backslash switch (*raw) { - case '"': valStr += "\""; break; - case '\\': valStr += "\\"; break; - case '/': valStr += "/"; break; - case 'b': valStr += "\b"; break; - case 'f': valStr += "\f"; break; - case 'n': valStr += "\n"; break; - case 'r': valStr += "\r"; break; - case 't': valStr += "\t"; break; + case '"': writer.push_back('\"'); break; + case '\\': writer.push_back('\\'); break; + case '/': writer.push_back('/'); break; + case 'b': writer.push_back('\b'); break; + case 'f': writer.push_back('\f'); break; + case 'n': writer.push_back('\n'); break; + case 'r': writer.push_back('\r'); break; + case 't': writer.push_back('\t'); break; case 'u': { unsigned int codepoint; if (hatoui(raw + 1, raw + 1 + 4, codepoint) != raw + 1 + 4) return JTOK_ERR; - - if (codepoint <= 0x7f) - valStr.push_back((char)codepoint); - else if (codepoint <= 0x7FF) { - valStr.push_back((char)(0xC0 | (codepoint >> 6))); - valStr.push_back((char)(0x80 | (codepoint & 0x3F))); - } else if (codepoint <= 0xFFFF) { - valStr.push_back((char)(0xE0 | (codepoint >> 12))); - valStr.push_back((char)(0x80 | ((codepoint >> 6) & 0x3F))); - valStr.push_back((char)(0x80 | (codepoint & 0x3F))); - } - + writer.push_back_u(codepoint); raw += 4; break; } @@ -226,11 +217,13 @@ enum jtokentype getJsonToken(string& tokenVal, unsigned int& consumed, } else { - valStr += *raw; + writer.push_back(*raw); raw++; } } + if (!writer.finalize()) + return JTOK_ERR; tokenVal = valStr; consumed = (raw - rawStart); return JTOK_STRING; diff --git a/src/univalue/lib/univalue_utffilter.h b/src/univalue/lib/univalue_utffilter.h new file mode 100644 index 0000000000..0e330dce9c --- /dev/null +++ b/src/univalue/lib/univalue_utffilter.h @@ -0,0 +1,119 @@ +// Copyright 2016 Wladimir J. van der Laan +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. +#ifndef UNIVALUE_UTFFILTER_H +#define UNIVALUE_UTFFILTER_H + +#include <string> + +/** + * Filter that generates and validates UTF-8, as well as collates UTF-16 + * surrogate pairs as specified in RFC4627. + */ +class JSONUTF8StringFilter +{ +public: + JSONUTF8StringFilter(std::string &s): + str(s), is_valid(true), codepoint(0), state(0), surpair(0) + { + } + // Write single 8-bit char (may be part of UTF-8 sequence) + void push_back(unsigned char ch) + { + if (state == 0) { + if (ch < 0x80) // 7-bit ASCII, fast direct pass-through + str.push_back(ch); + else if (ch < 0xc0) // Mid-sequence character, invalid in this state + is_valid = false; + else if (ch < 0xe0) { // Start of 2-byte sequence + codepoint = (ch & 0x1f) << 6; + state = 6; + } else if (ch < 0xf0) { // Start of 3-byte sequence + codepoint = (ch & 0x0f) << 12; + state = 12; + } else if (ch < 0xf8) { // Start of 4-byte sequence + codepoint = (ch & 0x07) << 18; + state = 18; + } else // Reserved, invalid + is_valid = false; + } else { + if ((ch & 0xc0) != 0x80) // Not a continuation, invalid + is_valid = false; + state -= 6; + codepoint |= (ch & 0x3f) << state; + if (state == 0) + push_back_u(codepoint); + } + } + // Write codepoint directly, possibly collating surrogate pairs + void push_back_u(unsigned int codepoint) + { + if (state) // Only accept full codepoints in open state + is_valid = false; + if (codepoint >= 0xD800 && codepoint < 0xDC00) { // First half of surrogate pair + if (surpair) // Two subsequent surrogate pair openers - fail + is_valid = false; + else + surpair = codepoint; + } else if (codepoint >= 0xDC00 && codepoint < 0xE000) { // Second half of surrogate pair + if (surpair) { // Open surrogate pair, expect second half + // Compute code point from UTF-16 surrogate pair + append_codepoint(0x10000 | ((surpair - 0xD800)<<10) | (codepoint - 0xDC00)); + surpair = 0; + } else // Second half doesn't follow a first half - fail + is_valid = false; + } else { + if (surpair) // First half of surrogate pair not followed by second - fail + is_valid = false; + else + append_codepoint(codepoint); + } + } + // Check that we're in a state where the string can be ended + // No open sequences, no open surrogate pairs, etc + bool finalize() + { + if (state || surpair) + is_valid = false; + return is_valid; + } +private: + std::string &str; + bool is_valid; + // Current UTF-8 decoding state + unsigned int codepoint; + int state; // Top bit to be filled in for next UTF-8 byte, or 0 + + // Keep track of the following state to handle the following section of + // RFC4627: + // + // To escape an extended character that is not in the Basic Multilingual + // Plane, the character is represented as a twelve-character sequence, + // encoding the UTF-16 surrogate pair. So, for example, a string + // containing only the G clef character (U+1D11E) may be represented as + // "\uD834\uDD1E". + // + // Two subsequent \u.... may have to be replaced with one actual codepoint. + unsigned int surpair; // First half of open UTF-16 surrogate pair, or 0 + + void append_codepoint(unsigned int codepoint) + { + if (codepoint <= 0x7f) + str.push_back((char)codepoint); + else if (codepoint <= 0x7FF) { + str.push_back((char)(0xC0 | (codepoint >> 6))); + str.push_back((char)(0x80 | (codepoint & 0x3F))); + } else if (codepoint <= 0xFFFF) { + str.push_back((char)(0xE0 | (codepoint >> 12))); + str.push_back((char)(0x80 | ((codepoint >> 6) & 0x3F))); + str.push_back((char)(0x80 | (codepoint & 0x3F))); + } else if (codepoint <= 0x1FFFFF) { + str.push_back((char)(0xF0 | (codepoint >> 18))); + str.push_back((char)(0x80 | ((codepoint >> 12) & 0x3F))); + str.push_back((char)(0x80 | ((codepoint >> 6) & 0x3F))); + str.push_back((char)(0x80 | (codepoint & 0x3F))); + } + } +}; + +#endif diff --git a/src/univalue/lib/univalue_write.cpp b/src/univalue/lib/univalue_write.cpp index ceb4cc9166..cfbdad3284 100644 --- a/src/univalue/lib/univalue_write.cpp +++ b/src/univalue/lib/univalue_write.cpp @@ -8,8 +8,6 @@ #include "univalue.h" #include "univalue_escapes.h" -// TODO: Using UTF8 - using namespace std; static string json_escape(const string& inS) @@ -23,15 +21,8 @@ static string json_escape(const string& inS) if (escStr) outS += escStr; - - else if (ch < 0x80) + else outS += ch; - - else { // TODO handle UTF-8 properly - char tmpesc[16]; - sprintf(tmpesc, "\\u%04x", ch); - outS += tmpesc; - } } return outS; diff --git a/src/univalue/test/fail38.json b/src/univalue/test/fail38.json new file mode 100644 index 0000000000..b245e2e46c --- /dev/null +++ b/src/univalue/test/fail38.json @@ -0,0 +1 @@ +["\ud834"] diff --git a/src/univalue/test/fail39.json b/src/univalue/test/fail39.json new file mode 100644 index 0000000000..7c9e263f27 --- /dev/null +++ b/src/univalue/test/fail39.json @@ -0,0 +1 @@ +["\udd61"] diff --git a/src/univalue/test/fail40.json b/src/univalue/test/fail40.json new file mode 100644 index 0000000000..664dc9e245 --- /dev/null +++ b/src/univalue/test/fail40.json @@ -0,0 +1 @@ +["…¡"]
\ No newline at end of file diff --git a/src/univalue/test/fail41.json b/src/univalue/test/fail41.json new file mode 100644 index 0000000000..0de342a2b5 --- /dev/null +++ b/src/univalue/test/fail41.json @@ -0,0 +1 @@ +["ð…"]
\ No newline at end of file diff --git a/src/univalue/test/round2.json b/src/univalue/test/round2.json new file mode 100644 index 0000000000..b766cccc68 --- /dev/null +++ b/src/univalue/test/round2.json @@ -0,0 +1 @@ +["a§■ðŽ’ð…¡"] diff --git a/src/univalue/test/unitester.cpp b/src/univalue/test/unitester.cpp index 5a052fe92c..05f3842cd1 100644 --- a/src/univalue/test/unitester.cpp +++ b/src/univalue/test/unitester.cpp @@ -22,6 +22,7 @@ string srcdir(JSON_TEST_SRC); static bool test_failed = false; #define d_assert(expr) { if (!(expr)) { test_failed = true; fprintf(stderr, "%s failed\n", filename.c_str()); } } +#define f_assert(expr) { if (!(expr)) { test_failed = true; fprintf(stderr, "%s failed\n", __func__); } } static std::string rtrim(std::string s) { @@ -108,6 +109,10 @@ static const char *filenames[] = { "fail35.json", "fail36.json", "fail37.json", + "fail38.json", // invalid unicode: only first half of surrogate pair + "fail39.json", // invalid unicode: only second half of surrogate pair + "fail40.json", // invalid unicode: broken UTF-8 + "fail41.json", // invalid unicode: unfinished UTF-8 "fail3.json", "fail4.json", // extra comma "fail5.json", @@ -119,14 +124,40 @@ static const char *filenames[] = { "pass2.json", "pass3.json", "round1.json", // round-trip test + "round2.json", // unicode }; +// Test \u handling +void unescape_unicode_test() +{ + UniValue val; + bool testResult; + // Escaped ASCII (quote) + testResult = val.read("[\"\\u0022\"]"); + f_assert(testResult); + f_assert(val[0].get_str() == "\""); + // Escaped Basic Plane character, two-byte UTF-8 + testResult = val.read("[\"\\u0191\"]"); + f_assert(testResult); + f_assert(val[0].get_str() == "\xc6\x91"); + // Escaped Basic Plane character, three-byte UTF-8 + testResult = val.read("[\"\\u2191\"]"); + f_assert(testResult); + f_assert(val[0].get_str() == "\xe2\x86\x91"); + // Escaped Supplementary Plane character U+1d161 + testResult = val.read("[\"\\ud834\\udd61\"]"); + f_assert(testResult); + f_assert(val[0].get_str() == "\xf0\x9d\x85\xa1"); +} + int main (int argc, char *argv[]) { for (unsigned int fidx = 0; fidx < ARRAY_SIZE(filenames); fidx++) { runtest_file(filenames[fidx]); } + unescape_unicode_test(); + return test_failed ? 1 : 0; } diff --git a/src/util.cpp b/src/util.cpp index 80f2193016..9a9209c621 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -113,7 +113,7 @@ string strMiscWarning; bool fLogTimestamps = DEFAULT_LOGTIMESTAMPS; bool fLogTimeMicros = DEFAULT_LOGTIMEMICROS; bool fLogIPs = DEFAULT_LOGIPS; -volatile sig_atomic_t fReopenDebugLog = false; +std::atomic<bool> fReopenDebugLog(false); CTranslationInterface translationInterface; /** Init OpenSSL library multithreading support */ diff --git a/src/util.h b/src/util.h index 88a00d3ca1..ac4b947785 100644 --- a/src/util.h +++ b/src/util.h @@ -18,6 +18,7 @@ #include "tinyformat.h" #include "utiltime.h" +#include <atomic> #include <exception> #include <map> #include <stdint.h> @@ -28,10 +29,6 @@ #include <boost/signals2/signal.hpp> #include <boost/thread/exceptions.hpp> -#ifndef WIN32 -#include <signal.h> -#endif - static const bool DEFAULT_LOGTIMEMICROS = false; static const bool DEFAULT_LOGIPS = false; static const bool DEFAULT_LOGTIMESTAMPS = true; @@ -54,7 +51,7 @@ extern std::string strMiscWarning; extern bool fLogTimestamps; extern bool fLogTimeMicros; extern bool fLogIPs; -extern volatile sig_atomic_t fReopenDebugLog; +extern std::atomic<bool> fReopenDebugLog; extern CTranslationInterface translationInterface; extern const char * const BITCOIN_CONF_FILENAME; diff --git a/src/utilstrencodings.cpp b/src/utilstrencodings.cpp index 0f9334cbe3..5ffdb3be15 100644 --- a/src/utilstrencodings.cpp +++ b/src/utilstrencodings.cpp @@ -461,6 +461,40 @@ bool ParseInt64(const std::string& str, int64_t *out) n <= std::numeric_limits<int64_t>::max(); } +bool ParseUInt32(const std::string& str, uint32_t *out) +{ + if (!ParsePrechecks(str)) + return false; + if (str.size() >= 1 && str[0] == '-') // Reject negative values, unfortunately strtoul accepts these by default if they fit in the range + return false; + char *endp = NULL; + errno = 0; // strtoul will not set errno if valid + unsigned long int n = strtoul(str.c_str(), &endp, 10); + if(out) *out = (uint32_t)n; + // Note that strtoul returns a *unsigned long int*, so even if it doesn't report a over/underflow + // we still have to check that the returned value is within the range of an *uint32_t*. On 64-bit + // platforms the size of these types may be different. + return endp && *endp == 0 && !errno && + n <= std::numeric_limits<uint32_t>::max(); +} + +bool ParseUInt64(const std::string& str, uint64_t *out) +{ + if (!ParsePrechecks(str)) + return false; + if (str.size() >= 1 && str[0] == '-') // Reject negative values, unfortunately strtoull accepts these by default if they fit in the range + return false; + char *endp = NULL; + errno = 0; // strtoull will not set errno if valid + unsigned long long int n = strtoull(str.c_str(), &endp, 10); + if(out) *out = (uint64_t)n; + // Note that strtoull returns a *unsigned long long int*, so even if it doesn't report a over/underflow + // we still have to check that the returned value is within the range of an *uint64_t*. + return endp && *endp == 0 && !errno && + n <= std::numeric_limits<uint64_t>::max(); +} + + bool ParseDouble(const std::string& str, double *out) { if (!ParsePrechecks(str)) diff --git a/src/utilstrencodings.h b/src/utilstrencodings.h index d40613cfc4..5744f78c6e 100644 --- a/src/utilstrencodings.h +++ b/src/utilstrencodings.h @@ -71,6 +71,20 @@ bool ParseInt32(const std::string& str, int32_t *out); bool ParseInt64(const std::string& str, int64_t *out); /** + * Convert decimal string to unsigned 32-bit integer with strict parse error feedback. + * @returns true if the entire string could be parsed as valid integer, + * false if not the entire string could be parsed or when overflow or underflow occurred. + */ +bool ParseUInt32(const std::string& str, uint32_t *out); + +/** + * Convert decimal string to unsigned 64-bit integer with strict parse error feedback. + * @returns true if the entire string could be parsed as valid integer, + * false if not the entire string could be parsed or when overflow or underflow occurred. + */ +bool ParseUInt64(const std::string& str, uint64_t *out); + +/** * Convert string to double with strict parse error feedback. * @returns true if the entire string could be parsed as valid double, * false if not the entire string could be parsed or when overflow or underflow occurred. diff --git a/src/versionbits.cpp b/src/versionbits.cpp index 78feb8ab0c..043819c654 100644 --- a/src/versionbits.cpp +++ b/src/versionbits.cpp @@ -4,6 +4,19 @@ #include "versionbits.h" +#include "consensus/params.h" + +const struct BIP9DeploymentInfo VersionBitsDeploymentInfo[Consensus::MAX_VERSION_BITS_DEPLOYMENTS] = { + { + /*.name =*/ "testdummy", + /*.gbt_force =*/ true, + }, + { + /*.name =*/ "csv", + /*.gbt_force =*/ true, + } +}; + ThresholdState AbstractThresholdConditionChecker::GetStateFor(const CBlockIndex* pindexPrev, const Consensus::Params& params, ThresholdConditionCache& cache) const { int nPeriod = Period(params); diff --git a/src/versionbits.h b/src/versionbits.h index 04f4738272..ede2dcdda8 100644 --- a/src/versionbits.h +++ b/src/versionbits.h @@ -30,6 +30,15 @@ enum ThresholdState { // will either be NULL or a block with (height + 1) % Period() == 0. typedef std::map<const CBlockIndex*, ThresholdState> ThresholdConditionCache; +struct BIP9DeploymentInfo { + /** Deployment name */ + const char *name; + /** Whether GBT clients can safely ignore this rule in simplified usage */ + bool gbt_force; +}; + +extern const struct BIP9DeploymentInfo VersionBitsDeploymentInfo[]; + /** * Abstract class that implements BIP9-style threshold logic, and caches results. */ diff --git a/src/wallet/crypter.cpp b/src/wallet/crypter.cpp index 95aa4c2593..190f8ecf2a 100644 --- a/src/wallet/crypter.cpp +++ b/src/wallet/crypter.cpp @@ -4,6 +4,8 @@ #include "crypter.h" +#include "crypto/aes.h" +#include "crypto/sha512.h" #include "script/script.h" #include "script/standard.h" #include "util.h" @@ -11,8 +13,33 @@ #include <string> #include <vector> #include <boost/foreach.hpp> -#include <openssl/aes.h> -#include <openssl/evp.h> + +int CCrypter::BytesToKeySHA512AES(const std::vector<unsigned char>& chSalt, const SecureString& strKeyData, int count, unsigned char *key,unsigned char *iv) const +{ + // This mimics the behavior of openssl's EVP_BytesToKey with an aes256cbc + // cipher and sha512 message digest. Because sha512's output size (64b) is + // greater than the aes256 block size (16b) + aes256 key size (32b), + // there's no need to process more than once (D_0). + + if(!count || !key || !iv) + return 0; + + unsigned char buf[CSHA512::OUTPUT_SIZE]; + CSHA512 di; + + di.Write((const unsigned char*)strKeyData.c_str(), strKeyData.size()); + if(chSalt.size()) + di.Write(&chSalt[0], chSalt.size()); + di.Finalize(buf); + + for(int i = 0; i != count - 1; i++) + di.Reset().Write(buf, sizeof(buf)).Finalize(buf); + + memcpy(key, buf, WALLET_CRYPTO_KEY_SIZE); + memcpy(iv, buf + WALLET_CRYPTO_KEY_SIZE, WALLET_CRYPTO_IV_SIZE); + memory_cleanse(buf, sizeof(buf)); + return WALLET_CRYPTO_KEY_SIZE; +} bool CCrypter::SetKeyFromPassphrase(const SecureString& strKeyData, const std::vector<unsigned char>& chSalt, const unsigned int nRounds, const unsigned int nDerivationMethod) { @@ -21,8 +48,7 @@ bool CCrypter::SetKeyFromPassphrase(const SecureString& strKeyData, const std::v int i = 0; if (nDerivationMethod == 0) - i = EVP_BytesToKey(EVP_aes_256_cbc(), EVP_sha512(), &chSalt[0], - (unsigned char *)&strKeyData[0], strKeyData.size(), nRounds, chKey, chIV); + i = BytesToKeySHA512AES(chSalt, strKeyData, nRounds, chKey, chIV); if (i != (int)WALLET_CRYPTO_KEY_SIZE) { @@ -37,7 +63,7 @@ bool CCrypter::SetKeyFromPassphrase(const SecureString& strKeyData, const std::v bool CCrypter::SetKey(const CKeyingMaterial& chNewKey, const std::vector<unsigned char>& chNewIV) { - if (chNewKey.size() != WALLET_CRYPTO_KEY_SIZE || chNewIV.size() != WALLET_CRYPTO_KEY_SIZE) + if (chNewKey.size() != WALLET_CRYPTO_KEY_SIZE || chNewIV.size() != WALLET_CRYPTO_IV_SIZE) return false; memcpy(&chKey[0], &chNewKey[0], sizeof chKey); @@ -47,57 +73,39 @@ bool CCrypter::SetKey(const CKeyingMaterial& chNewKey, const std::vector<unsigne return true; } -bool CCrypter::Encrypt(const CKeyingMaterial& vchPlaintext, std::vector<unsigned char> &vchCiphertext) +bool CCrypter::Encrypt(const CKeyingMaterial& vchPlaintext, std::vector<unsigned char> &vchCiphertext) const { if (!fKeySet) return false; // max ciphertext len for a n bytes of plaintext is - // n + AES_BLOCK_SIZE - 1 bytes - int nLen = vchPlaintext.size(); - int nCLen = nLen + AES_BLOCK_SIZE, nFLen = 0; - vchCiphertext = std::vector<unsigned char> (nCLen); - - EVP_CIPHER_CTX ctx; + // n + AES_BLOCKSIZE bytes + vchCiphertext.resize(vchPlaintext.size() + AES_BLOCKSIZE); - bool fOk = true; - - EVP_CIPHER_CTX_init(&ctx); - if (fOk) fOk = EVP_EncryptInit_ex(&ctx, EVP_aes_256_cbc(), NULL, chKey, chIV) != 0; - if (fOk) fOk = EVP_EncryptUpdate(&ctx, &vchCiphertext[0], &nCLen, &vchPlaintext[0], nLen) != 0; - if (fOk) fOk = EVP_EncryptFinal_ex(&ctx, (&vchCiphertext[0]) + nCLen, &nFLen) != 0; - EVP_CIPHER_CTX_cleanup(&ctx); - - if (!fOk) return false; + AES256CBCEncrypt enc(chKey, chIV, true); + size_t nLen = enc.Encrypt(&vchPlaintext[0], vchPlaintext.size(), &vchCiphertext[0]); + if(nLen < vchPlaintext.size()) + return false; + vchCiphertext.resize(nLen); - vchCiphertext.resize(nCLen + nFLen); return true; } -bool CCrypter::Decrypt(const std::vector<unsigned char>& vchCiphertext, CKeyingMaterial& vchPlaintext) +bool CCrypter::Decrypt(const std::vector<unsigned char>& vchCiphertext, CKeyingMaterial& vchPlaintext) const { if (!fKeySet) return false; // plaintext will always be equal to or lesser than length of ciphertext int nLen = vchCiphertext.size(); - int nPLen = nLen, nFLen = 0; - - vchPlaintext = CKeyingMaterial(nPLen); - EVP_CIPHER_CTX ctx; + vchPlaintext.resize(nLen); - bool fOk = true; - - EVP_CIPHER_CTX_init(&ctx); - if (fOk) fOk = EVP_DecryptInit_ex(&ctx, EVP_aes_256_cbc(), NULL, chKey, chIV) != 0; - if (fOk) fOk = EVP_DecryptUpdate(&ctx, &vchPlaintext[0], &nPLen, &vchCiphertext[0], nLen) != 0; - if (fOk) fOk = EVP_DecryptFinal_ex(&ctx, (&vchPlaintext[0]) + nPLen, &nFLen) != 0; - EVP_CIPHER_CTX_cleanup(&ctx); - - if (!fOk) return false; - - vchPlaintext.resize(nPLen + nFLen); + AES256CBCDecrypt dec(chKey, chIV, true); + nLen = dec.Decrypt(&vchCiphertext[0], vchCiphertext.size(), &vchPlaintext[0]); + if(nLen == 0) + return false; + vchPlaintext.resize(nLen); return true; } @@ -105,8 +113,8 @@ bool CCrypter::Decrypt(const std::vector<unsigned char>& vchCiphertext, CKeyingM static bool EncryptSecret(const CKeyingMaterial& vMasterKey, const CKeyingMaterial &vchPlaintext, const uint256& nIV, std::vector<unsigned char> &vchCiphertext) { CCrypter cKeyCrypter; - std::vector<unsigned char> chIV(WALLET_CRYPTO_KEY_SIZE); - memcpy(&chIV[0], &nIV, WALLET_CRYPTO_KEY_SIZE); + std::vector<unsigned char> chIV(WALLET_CRYPTO_IV_SIZE); + memcpy(&chIV[0], &nIV, WALLET_CRYPTO_IV_SIZE); if(!cKeyCrypter.SetKey(vMasterKey, chIV)) return false; return cKeyCrypter.Encrypt(*((const CKeyingMaterial*)&vchPlaintext), vchCiphertext); @@ -115,8 +123,8 @@ static bool EncryptSecret(const CKeyingMaterial& vMasterKey, const CKeyingMateri static bool DecryptSecret(const CKeyingMaterial& vMasterKey, const std::vector<unsigned char>& vchCiphertext, const uint256& nIV, CKeyingMaterial& vchPlaintext) { CCrypter cKeyCrypter; - std::vector<unsigned char> chIV(WALLET_CRYPTO_KEY_SIZE); - memcpy(&chIV[0], &nIV, WALLET_CRYPTO_KEY_SIZE); + std::vector<unsigned char> chIV(WALLET_CRYPTO_IV_SIZE); + memcpy(&chIV[0], &nIV, WALLET_CRYPTO_IV_SIZE); if(!cKeyCrypter.SetKey(vMasterKey, chIV)) return false; return cKeyCrypter.Decrypt(vchCiphertext, *((CKeyingMaterial*)&vchPlaintext)); diff --git a/src/wallet/crypter.h b/src/wallet/crypter.h index eb06a7866a..5d0a4a3305 100644 --- a/src/wallet/crypter.h +++ b/src/wallet/crypter.h @@ -13,6 +13,7 @@ class uint256; const unsigned int WALLET_CRYPTO_KEY_SIZE = 32; const unsigned int WALLET_CRYPTO_SALT_SIZE = 8; +const unsigned int WALLET_CRYPTO_IV_SIZE = 16; /** * Private key encryption is done based on a CMasterKey, @@ -66,18 +67,26 @@ public: typedef std::vector<unsigned char, secure_allocator<unsigned char> > CKeyingMaterial; +namespace wallet_crypto +{ + class TestCrypter; +} + /** Encryption/decryption context with key information */ class CCrypter { +friend class wallet_crypto::TestCrypter; // for test access to chKey/chIV private: unsigned char chKey[WALLET_CRYPTO_KEY_SIZE]; - unsigned char chIV[WALLET_CRYPTO_KEY_SIZE]; + unsigned char chIV[WALLET_CRYPTO_IV_SIZE]; bool fKeySet; + int BytesToKeySHA512AES(const std::vector<unsigned char>& chSalt, const SecureString& strKeyData, int count, unsigned char *key,unsigned char *iv) const; + public: bool SetKeyFromPassphrase(const SecureString &strKeyData, const std::vector<unsigned char>& chSalt, const unsigned int nRounds, const unsigned int nDerivationMethod); - bool Encrypt(const CKeyingMaterial& vchPlaintext, std::vector<unsigned char> &vchCiphertext); - bool Decrypt(const std::vector<unsigned char>& vchCiphertext, CKeyingMaterial& vchPlaintext); + bool Encrypt(const CKeyingMaterial& vchPlaintext, std::vector<unsigned char> &vchCiphertext) const; + bool Decrypt(const std::vector<unsigned char>& vchCiphertext, CKeyingMaterial& vchPlaintext) const; bool SetKey(const CKeyingMaterial& chNewKey, const std::vector<unsigned char>& chNewIV); void CleanKey() diff --git a/src/wallet/rpcdump.cpp b/src/wallet/rpcdump.cpp index bb40cf7245..14c2e31d95 100644 --- a/src/wallet/rpcdump.cpp +++ b/src/wallet/rpcdump.cpp @@ -590,7 +590,7 @@ UniValue dumpwallet(const UniValue& params, bool fHelp) std::sort(vKeyBirth.begin(), vKeyBirth.end()); // produce output - file << strprintf("# Wallet dump created by Bitcoin %s (%s)\n", CLIENT_BUILD, CLIENT_DATE); + file << strprintf("# Wallet dump created by Bitcoin %s\n", CLIENT_BUILD); file << strprintf("# * Created on %s\n", EncodeDumpTime(GetTime())); file << strprintf("# * Best block at time of backup was %i (%s),\n", chainActive.Height(), chainActive.Tip()->GetBlockHash().ToString()); file << strprintf("# mined on %s\n", EncodeDumpTime(chainActive.Tip()->GetBlockTime())); diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 27596929f1..2d4e95911d 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -146,38 +146,12 @@ UniValue getnewaddress(const UniValue& params, bool fHelp) CBitcoinAddress GetAccountAddress(string strAccount, bool bForceNew=false) { - CWalletDB walletdb(pwalletMain->strWalletFile); - - CAccount account; - walletdb.ReadAccount(strAccount, account); - - if (!bForceNew) { - if (!account.vchPubKey.IsValid()) - bForceNew = true; - else { - // Check if the current key has been used - CScript scriptPubKey = GetScriptForDestination(account.vchPubKey.GetID()); - for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); - it != pwalletMain->mapWallet.end() && account.vchPubKey.IsValid(); - ++it) - BOOST_FOREACH(const CTxOut& txout, (*it).second.vout) - if (txout.scriptPubKey == scriptPubKey) { - bForceNew = true; - break; - } - } - } - - // Generate a new key - if (bForceNew) { - if (!pwalletMain->GetKeyFromPool(account.vchPubKey)) - throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, "Error: Keypool ran out, please call keypoolrefill first"); - - pwalletMain->SetAddressBook(account.vchPubKey.GetID(), strAccount, "receive"); - walletdb.WriteAccount(strAccount, account); + CPubKey pubKey; + if (!pwalletMain->GetAccountPubkey(pubKey, strAccount, bForceNew)) { + throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, "Error: Keypool ran out, please call keypoolrefill first"); } - return CBitcoinAddress(account.vchPubKey.GetID()); + return CBitcoinAddress(pubKey.GetID()); } UniValue getaccountaddress(const UniValue& params, bool fHelp) @@ -673,38 +647,6 @@ UniValue getreceivedbyaccount(const UniValue& params, bool fHelp) } -CAmount GetAccountBalance(CWalletDB& walletdb, const string& strAccount, int nMinDepth, const isminefilter& filter) -{ - CAmount nBalance = 0; - - // Tally wallet transactions - for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it) - { - const CWalletTx& wtx = (*it).second; - if (!CheckFinalTx(wtx) || wtx.GetBlocksToMaturity() > 0 || wtx.GetDepthInMainChain() < 0) - continue; - - CAmount nReceived, nSent, nFee; - wtx.GetAccountAmounts(strAccount, nReceived, nSent, nFee, filter); - - if (nReceived != 0 && wtx.GetDepthInMainChain() >= nMinDepth) - nBalance += nReceived; - nBalance -= nSent + nFee; - } - - // Tally internal accounting entries - nBalance += walletdb.GetAccountCreditDebit(strAccount); - - return nBalance; -} - -CAmount GetAccountBalance(const string& strAccount, int nMinDepth, const isminefilter& filter) -{ - CWalletDB walletdb(pwalletMain->strWalletFile); - return GetAccountBalance(walletdb, strAccount, nMinDepth, filter); -} - - UniValue getbalance(const UniValue& params, bool fHelp) { if (!EnsureWalletIsAvailable(fHelp)) @@ -775,7 +717,7 @@ UniValue getbalance(const UniValue& params, bool fHelp) string strAccount = AccountFromValue(params[0]); - CAmount nBalance = GetAccountBalance(strAccount, nMinDepth, filter); + CAmount nBalance = pwalletMain->GetAccountBalance(strAccount, nMinDepth, filter); return ValueFromAmount(nBalance); } @@ -836,33 +778,7 @@ UniValue movecmd(const UniValue& params, bool fHelp) if (params.size() > 4) strComment = params[4].get_str(); - CWalletDB walletdb(pwalletMain->strWalletFile); - if (!walletdb.TxnBegin()) - throw JSONRPCError(RPC_DATABASE_ERROR, "database error"); - - int64_t nNow = GetAdjustedTime(); - - // Debit - CAccountingEntry debit; - debit.nOrderPos = pwalletMain->IncOrderPosNext(&walletdb); - debit.strAccount = strFrom; - debit.nCreditDebit = -nAmount; - debit.nTime = nNow; - debit.strOtherAccount = strTo; - debit.strComment = strComment; - pwalletMain->AddAccountingEntry(debit, walletdb); - - // Credit - CAccountingEntry credit; - credit.nOrderPos = pwalletMain->IncOrderPosNext(&walletdb); - credit.strAccount = strTo; - credit.nCreditDebit = nAmount; - credit.nTime = nNow; - credit.strOtherAccount = strFrom; - credit.strComment = strComment; - pwalletMain->AddAccountingEntry(credit, walletdb); - - if (!walletdb.TxnCommit()) + if (!pwalletMain->AccountMove(strFrom, strTo, nAmount, strComment)) throw JSONRPCError(RPC_DATABASE_ERROR, "database error"); return true; @@ -923,7 +839,7 @@ UniValue sendfrom(const UniValue& params, bool fHelp) EnsureWalletIsUnlocked(); // Check funds - CAmount nBalance = GetAccountBalance(strAccount, nMinDepth, ISMINE_SPENDABLE); + CAmount nBalance = pwalletMain->GetAccountBalance(strAccount, nMinDepth, ISMINE_SPENDABLE); if (nAmount > nBalance) throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Account has insufficient funds"); @@ -1026,7 +942,7 @@ UniValue sendmany(const UniValue& params, bool fHelp) EnsureWalletIsUnlocked(); // Check funds - CAmount nBalance = GetAccountBalance(strAccount, nMinDepth, ISMINE_SPENDABLE); + CAmount nBalance = pwalletMain->GetAccountBalance(strAccount, nMinDepth, ISMINE_SPENDABLE); if (totalAmount > nBalance) throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Account has insufficient funds"); @@ -1836,7 +1752,7 @@ UniValue backupwallet(const UniValue& params, bool fHelp) LOCK2(cs_main, pwalletMain->cs_wallet); string strDest = params[0].get_str(); - if (!BackupWallet(*pwalletMain, strDest)) + if (!pwalletMain->BackupWallet(strDest)) throw JSONRPCError(RPC_WALLET_ERROR, "Error: Wallet backup failed!"); return NullUniValue; @@ -2153,7 +2069,11 @@ UniValue lockunspent(const UniValue& params, bool fHelp) throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected object"); const UniValue& o = output.get_obj(); - RPCTypeCheckObj(o, boost::assign::map_list_of("txid", UniValue::VSTR)("vout", UniValue::VNUM)); + RPCTypeCheckObj(o, + { + {"txid", UniValueType(UniValue::VSTR)}, + {"vout", UniValueType(UniValue::VNUM)}, + }); string txid = find_value(o, "txid").get_str(); if (!IsHex(txid)) @@ -2339,13 +2259,14 @@ UniValue listunspent(const UniValue& params, bool fHelp) "\nResult\n" "[ (array of json object)\n" " {\n" - " \"txid\" : \"txid\", (string) the transaction id \n" + " \"txid\" : \"txid\", (string) the transaction id \n" " \"vout\" : n, (numeric) the vout value\n" - " \"address\" : \"address\", (string) the bitcoin address\n" - " \"account\" : \"account\", (string) DEPRECATED. The associated account, or \"\" for the default account\n" - " \"scriptPubKey\" : \"key\", (string) the script key\n" + " \"address\" : \"address\", (string) the bitcoin address\n" + " \"account\" : \"account\", (string) DEPRECATED. The associated account, or \"\" for the default account\n" + " \"scriptPubKey\" : \"key\", (string) the script key\n" " \"amount\" : x.xxx, (numeric) the transaction amount in " + CURRENCY_UNIT + "\n" " \"confirmations\" : n, (numeric) The number of confirmations\n" + " \"redeemScript\" : n (string) The redeemScript if scriptPubKey is P2SH\n" " \"spendable\" : xxx, (bool) Whether we have the private keys to spend this output\n" " \"solvable\" : xxx (bool) Whether we know how to spend this output, ignoring the lack of keys\n" " }\n" @@ -2391,38 +2312,34 @@ UniValue listunspent(const UniValue& params, bool fHelp) if (out.nDepth < nMinDepth || out.nDepth > nMaxDepth) continue; - if (setAddress.size()) { - CTxDestination address; - if (!ExtractDestination(out.tx->vout[out.i].scriptPubKey, address)) - continue; + CTxDestination address; + const CScript& scriptPubKey = out.tx->vout[out.i].scriptPubKey; + bool fValidAddress = ExtractDestination(scriptPubKey, address); - if (!setAddress.count(address)) - continue; - } + if (setAddress.size() && (!fValidAddress || !setAddress.count(address))) + continue; - CAmount nValue = out.tx->vout[out.i].nValue; - const CScript& pk = out.tx->vout[out.i].scriptPubKey; UniValue entry(UniValue::VOBJ); entry.push_back(Pair("txid", out.tx->GetHash().GetHex())); entry.push_back(Pair("vout", out.i)); - CTxDestination address; - if (ExtractDestination(out.tx->vout[out.i].scriptPubKey, address)) { + + if (fValidAddress) { entry.push_back(Pair("address", CBitcoinAddress(address).ToString())); + if (pwalletMain->mapAddressBook.count(address)) entry.push_back(Pair("account", pwalletMain->mapAddressBook[address].name)); - } - entry.push_back(Pair("scriptPubKey", HexStr(pk.begin(), pk.end()))); - if (pk.IsPayToScriptHash()) { - CTxDestination address; - if (ExtractDestination(pk, address)) { + + if (scriptPubKey.IsPayToScriptHash()) { const CScriptID& hash = boost::get<CScriptID>(address); CScript redeemScript; if (pwalletMain->GetCScript(hash, redeemScript)) entry.push_back(Pair("redeemScript", HexStr(redeemScript.begin(), redeemScript.end()))); } } - entry.push_back(Pair("amount",ValueFromAmount(nValue))); - entry.push_back(Pair("confirmations",out.nDepth)); + + entry.push_back(Pair("scriptPubKey", HexStr(scriptPubKey.begin(), scriptPubKey.end()))); + entry.push_back(Pair("amount", ValueFromAmount(out.tx->vout[out.i].nValue))); + entry.push_back(Pair("confirmations", out.nDepth)); entry.push_back(Pair("spendable", out.fSpendable)); entry.push_back(Pair("solvable", out.fSolvable)); results.push_back(entry); @@ -2456,12 +2373,13 @@ UniValue fundrawtransaction(const UniValue& params, bool fHelp) " \"changePosition\" (numeric, optional, default random) The index of the change output\n" " \"includeWatching\" (boolean, optional, default false) Also select inputs which are watch only\n" " \"lockUnspents\" (boolean, optional, default false) Lock selected unspent outputs\n" + " \"feeRate\" (numeric, optional, default not set: makes wallet determine the fee) Set a specific feerate (" + CURRENCY_UNIT + " per KB)\n" " }\n" " for backward compatibility: passing in a true instead of an object will result in {\"includeWatching\":true}\n" "\nResult:\n" "{\n" " \"hex\": \"value\", (string) The resulting raw transaction (hex-encoded string)\n" - " \"fee\": n, (numeric) Fee the resulting transaction pays\n" + " \"fee\": n, (numeric) Fee in " + CURRENCY_UNIT + " the resulting transaction pays\n" " \"changepos\": n (numeric) The position of the added change output, or -1\n" "}\n" "\"hex\" \n" @@ -2482,6 +2400,8 @@ UniValue fundrawtransaction(const UniValue& params, bool fHelp) int changePosition = -1; bool includeWatching = false; bool lockUnspents = false; + CFeeRate feeRate = CFeeRate(0); + bool overrideEstimatedFeerate = false; if (params.size() > 1) { if (params[1].type() == UniValue::VBOOL) { @@ -2493,7 +2413,15 @@ UniValue fundrawtransaction(const UniValue& params, bool fHelp) UniValue options = params[1]; - RPCTypeCheckObj(options, boost::assign::map_list_of("changeAddress", UniValue::VSTR)("changePosition", UniValue::VNUM)("includeWatching", UniValue::VBOOL)("lockUnspents", UniValue::VBOOL), true, true); + RPCTypeCheckObj(options, + { + {"changeAddress", UniValueType(UniValue::VSTR)}, + {"changePosition", UniValueType(UniValue::VNUM)}, + {"includeWatching", UniValueType(UniValue::VBOOL)}, + {"lockUnspents", UniValueType(UniValue::VBOOL)}, + {"feeRate", UniValueType()}, // will be checked below + }, + true, true); if (options.exists("changeAddress")) { CBitcoinAddress address(options["changeAddress"].get_str()); @@ -2512,6 +2440,12 @@ UniValue fundrawtransaction(const UniValue& params, bool fHelp) if (options.exists("lockUnspents")) lockUnspents = options["lockUnspents"].get_bool(); + + if (options.exists("feeRate")) + { + feeRate = CFeeRate(AmountFromValue(options["feeRate"])); + overrideEstimatedFeerate = true; + } } } @@ -2523,20 +2457,20 @@ UniValue fundrawtransaction(const UniValue& params, bool fHelp) if (origTx.vout.size() == 0) throw JSONRPCError(RPC_INVALID_PARAMETER, "TX must have at least one output"); - if (changePosition != -1 && (changePosition < 0 || changePosition > origTx.vout.size())) + if (changePosition != -1 && (changePosition < 0 || (unsigned int)changePosition > origTx.vout.size())) throw JSONRPCError(RPC_INVALID_PARAMETER, "changePosition out of bounds"); CMutableTransaction tx(origTx); - CAmount nFee; + CAmount nFeeOut; string strFailReason; - if(!pwalletMain->FundTransaction(tx, nFee, changePosition, strFailReason, includeWatching, lockUnspents, changeAddress)) + if(!pwalletMain->FundTransaction(tx, nFeeOut, overrideEstimatedFeerate, feeRate, changePosition, strFailReason, includeWatching, lockUnspents, changeAddress)) throw JSONRPCError(RPC_INTERNAL_ERROR, strFailReason); UniValue result(UniValue::VOBJ); result.push_back(Pair("hex", EncodeHexTx(tx))); result.push_back(Pair("changepos", changePosition)); - result.push_back(Pair("fee", ValueFromAmount(nFee))); + result.push_back(Pair("fee", ValueFromAmount(nFeeOut))); return result; } diff --git a/src/wallet/test/crypto_tests.cpp b/src/wallet/test/crypto_tests.cpp new file mode 100644 index 0000000000..05387f5f2b --- /dev/null +++ b/src/wallet/test/crypto_tests.cpp @@ -0,0 +1,230 @@ +// Copyright (c) 2014 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 "random.h" +#include "utilstrencodings.h" +#include "test/test_bitcoin.h" +#include "wallet/crypter.h" + +#include <vector> + +#include <boost/test/unit_test.hpp> +#include <openssl/aes.h> +#include <openssl/evp.h> + +BOOST_FIXTURE_TEST_SUITE(wallet_crypto, BasicTestingSetup) + +bool OldSetKeyFromPassphrase(const SecureString& strKeyData, const std::vector<unsigned char>& chSalt, const unsigned int nRounds, const unsigned int nDerivationMethod, unsigned char* chKey, unsigned char* chIV) +{ + if (nRounds < 1 || chSalt.size() != WALLET_CRYPTO_SALT_SIZE) + return false; + + int i = 0; + if (nDerivationMethod == 0) + i = EVP_BytesToKey(EVP_aes_256_cbc(), EVP_sha512(), &chSalt[0], + (unsigned char *)&strKeyData[0], strKeyData.size(), nRounds, chKey, chIV); + + if (i != (int)WALLET_CRYPTO_KEY_SIZE) + { + memory_cleanse(chKey, sizeof(chKey)); + memory_cleanse(chIV, sizeof(chIV)); + return false; + } + return true; +} + +bool OldEncrypt(const CKeyingMaterial& vchPlaintext, std::vector<unsigned char> &vchCiphertext, const unsigned char chKey[32], const unsigned char chIV[16]) +{ + // max ciphertext len for a n bytes of plaintext is + // n + AES_BLOCK_SIZE - 1 bytes + int nLen = vchPlaintext.size(); + int nCLen = nLen + AES_BLOCK_SIZE, nFLen = 0; + vchCiphertext = std::vector<unsigned char> (nCLen); + + EVP_CIPHER_CTX ctx; + + bool fOk = true; + + EVP_CIPHER_CTX_init(&ctx); + if (fOk) fOk = EVP_EncryptInit_ex(&ctx, EVP_aes_256_cbc(), NULL, chKey, chIV) != 0; + if (fOk) fOk = EVP_EncryptUpdate(&ctx, &vchCiphertext[0], &nCLen, &vchPlaintext[0], nLen) != 0; + if (fOk) fOk = EVP_EncryptFinal_ex(&ctx, (&vchCiphertext[0]) + nCLen, &nFLen) != 0; + EVP_CIPHER_CTX_cleanup(&ctx); + + if (!fOk) return false; + + vchCiphertext.resize(nCLen + nFLen); + return true; +} + +bool OldDecrypt(const std::vector<unsigned char>& vchCiphertext, CKeyingMaterial& vchPlaintext, const unsigned char chKey[32], const unsigned char chIV[16]) +{ + // plaintext will always be equal to or lesser than length of ciphertext + int nLen = vchCiphertext.size(); + int nPLen = nLen, nFLen = 0; + + vchPlaintext = CKeyingMaterial(nPLen); + + EVP_CIPHER_CTX ctx; + + bool fOk = true; + + EVP_CIPHER_CTX_init(&ctx); + if (fOk) fOk = EVP_DecryptInit_ex(&ctx, EVP_aes_256_cbc(), NULL, chKey, chIV) != 0; + if (fOk) fOk = EVP_DecryptUpdate(&ctx, &vchPlaintext[0], &nPLen, &vchCiphertext[0], nLen) != 0; + if (fOk) fOk = EVP_DecryptFinal_ex(&ctx, (&vchPlaintext[0]) + nPLen, &nFLen) != 0; + EVP_CIPHER_CTX_cleanup(&ctx); + + if (!fOk) return false; + + vchPlaintext.resize(nPLen + nFLen); + return true; +} + +class TestCrypter +{ +public: +static void TestPassphraseSingle(const std::vector<unsigned char>& vchSalt, const SecureString& passphrase, uint32_t rounds, + const std::vector<unsigned char>& correctKey = std::vector<unsigned char>(), + const std::vector<unsigned char>& correctIV=std::vector<unsigned char>()) +{ + unsigned char chKey[WALLET_CRYPTO_KEY_SIZE]; + unsigned char chIV[WALLET_CRYPTO_IV_SIZE]; + + CCrypter crypt; + crypt.SetKeyFromPassphrase(passphrase, vchSalt, rounds, 0); + + OldSetKeyFromPassphrase(passphrase, vchSalt, rounds, 0, chKey, chIV); + + BOOST_CHECK_MESSAGE(memcmp(chKey, crypt.chKey, sizeof(chKey)) == 0, \ + HexStr(chKey, chKey+sizeof(chKey)) + std::string(" != ") + HexStr(crypt.chKey, crypt.chKey + (sizeof crypt.chKey))); + BOOST_CHECK_MESSAGE(memcmp(chIV, crypt.chIV, sizeof(chIV)) == 0, \ + HexStr(chIV, chIV+sizeof(chIV)) + std::string(" != ") + HexStr(crypt.chIV, crypt.chIV + (sizeof crypt.chIV))); + + if(!correctKey.empty()) + BOOST_CHECK_MESSAGE(memcmp(chKey, &correctKey[0], sizeof(chKey)) == 0, \ + HexStr(chKey, chKey+sizeof(chKey)) + std::string(" != ") + HexStr(correctKey.begin(), correctKey.end())); + if(!correctIV.empty()) + BOOST_CHECK_MESSAGE(memcmp(chIV, &correctIV[0], sizeof(chIV)) == 0, + HexStr(chIV, chIV+sizeof(chIV)) + std::string(" != ") + HexStr(correctIV.begin(), correctIV.end())); +} + +static void TestPassphrase(const std::vector<unsigned char>& vchSalt, const SecureString& passphrase, uint32_t rounds, + const std::vector<unsigned char>& correctKey = std::vector<unsigned char>(), + const std::vector<unsigned char>& correctIV=std::vector<unsigned char>()) +{ + TestPassphraseSingle(vchSalt, passphrase, rounds, correctKey, correctIV); + for(SecureString::const_iterator i(passphrase.begin()); i != passphrase.end(); ++i) + TestPassphraseSingle(vchSalt, SecureString(i, passphrase.end()), rounds); +} + + +static void TestDecrypt(const CCrypter& crypt, const std::vector<unsigned char>& vchCiphertext, \ + const std::vector<unsigned char>& vchPlaintext = std::vector<unsigned char>()) +{ + CKeyingMaterial vchDecrypted1; + CKeyingMaterial vchDecrypted2; + int result1, result2; + result1 = crypt.Decrypt(vchCiphertext, vchDecrypted1); + result2 = OldDecrypt(vchCiphertext, vchDecrypted2, crypt.chKey, crypt.chIV); + BOOST_CHECK(result1 == result2); + + // These two should be equal. However, OpenSSL 1.0.1j introduced a change + // that would zero all padding except for the last byte for failed decrypts. + // This behavior was reverted for 1.0.1k. + if (vchDecrypted1 != vchDecrypted2 && vchDecrypted1.size() >= AES_BLOCK_SIZE && SSLeay() == 0x100010afL) + { + for(CKeyingMaterial::iterator it = vchDecrypted1.end() - AES_BLOCK_SIZE; it != vchDecrypted1.end() - 1; it++) + *it = 0; + } + + BOOST_CHECK_MESSAGE(vchDecrypted1 == vchDecrypted2, HexStr(vchDecrypted1.begin(), vchDecrypted1.end()) + " != " + HexStr(vchDecrypted2.begin(), vchDecrypted2.end())); + + if (vchPlaintext.size()) + BOOST_CHECK(CKeyingMaterial(vchPlaintext.begin(), vchPlaintext.end()) == vchDecrypted2); +} + +static void TestEncryptSingle(const CCrypter& crypt, const CKeyingMaterial& vchPlaintext, + const std::vector<unsigned char>& vchCiphertextCorrect = std::vector<unsigned char>()) +{ + std::vector<unsigned char> vchCiphertext1; + std::vector<unsigned char> vchCiphertext2; + int result1 = crypt.Encrypt(vchPlaintext, vchCiphertext1); + + int result2 = OldEncrypt(vchPlaintext, vchCiphertext2, crypt.chKey, crypt.chIV); + BOOST_CHECK(result1 == result2); + BOOST_CHECK(vchCiphertext1 == vchCiphertext2); + + if (!vchCiphertextCorrect.empty()) + BOOST_CHECK(vchCiphertext2 == vchCiphertextCorrect); + + const std::vector<unsigned char> vchPlaintext2(vchPlaintext.begin(), vchPlaintext.end()); + + if(vchCiphertext1 == vchCiphertext2) + TestDecrypt(crypt, vchCiphertext1, vchPlaintext2); +} + +static void TestEncrypt(const CCrypter& crypt, const std::vector<unsigned char>& vchPlaintextIn, \ + const std::vector<unsigned char>& vchCiphertextCorrect = std::vector<unsigned char>()) +{ + TestEncryptSingle(crypt, CKeyingMaterial(vchPlaintextIn.begin(), vchPlaintextIn.end()), vchCiphertextCorrect); + for(std::vector<unsigned char>::const_iterator i(vchPlaintextIn.begin()); i != vchPlaintextIn.end(); ++i) + TestEncryptSingle(crypt, CKeyingMaterial(i, vchPlaintextIn.end())); +} + +}; + +BOOST_AUTO_TEST_CASE(passphrase) { + // These are expensive. + + TestCrypter::TestPassphrase(ParseHex("0000deadbeef0000"), "test", 25000, \ + ParseHex("fc7aba077ad5f4c3a0988d8daa4810d0d4a0e3bcb53af662998898f33df0556a"), \ + ParseHex("cf2f2691526dd1aa220896fb8bf7c369")); + + std::string hash(GetRandHash().ToString()); + std::vector<unsigned char> vchSalt(8); + GetRandBytes(&vchSalt[0], vchSalt.size()); + uint32_t rounds = insecure_rand(); + if (rounds > 30000) + rounds = 30000; + TestCrypter::TestPassphrase(vchSalt, SecureString(hash.begin(), hash.end()), rounds); +} + +BOOST_AUTO_TEST_CASE(encrypt) { + std::vector<unsigned char> vchSalt = ParseHex("0000deadbeef0000"); + BOOST_CHECK(vchSalt.size() == WALLET_CRYPTO_SALT_SIZE); + CCrypter crypt; + crypt.SetKeyFromPassphrase("passphrase", vchSalt, 25000, 0); + TestCrypter::TestEncrypt(crypt, ParseHex("22bcade09ac03ff6386914359cfe885cfeb5f77ff0d670f102f619687453b29d")); + + for (int i = 0; i != 100; i++) + { + uint256 hash(GetRandHash()); + TestCrypter::TestEncrypt(crypt, std::vector<unsigned char>(hash.begin(), hash.end())); + } + +} + +BOOST_AUTO_TEST_CASE(decrypt) { + std::vector<unsigned char> vchSalt = ParseHex("0000deadbeef0000"); + BOOST_CHECK(vchSalt.size() == WALLET_CRYPTO_SALT_SIZE); + CCrypter crypt; + crypt.SetKeyFromPassphrase("passphrase", vchSalt, 25000, 0); + + // Some corner cases the came up while testing + TestCrypter::TestDecrypt(crypt,ParseHex("795643ce39d736088367822cdc50535ec6f103715e3e48f4f3b1a60a08ef59ca")); + TestCrypter::TestDecrypt(crypt,ParseHex("de096f4a8f9bd97db012aa9d90d74de8cdea779c3ee8bc7633d8b5d6da703486")); + TestCrypter::TestDecrypt(crypt,ParseHex("32d0a8974e3afd9c6c3ebf4d66aa4e6419f8c173de25947f98cf8b7ace49449c")); + TestCrypter::TestDecrypt(crypt,ParseHex("e7c055cca2faa78cb9ac22c9357a90b4778ded9b2cc220a14cea49f931e596ea")); + TestCrypter::TestDecrypt(crypt,ParseHex("b88efddd668a6801d19516d6830da4ae9811988ccbaf40df8fbb72f3f4d335fd")); + TestCrypter::TestDecrypt(crypt,ParseHex("8cae76aa6a43694e961ebcb28c8ca8f8540b84153d72865e8561ddd93fa7bfa9")); + + for (int i = 0; i != 100; i++) + { + uint256 hash(GetRandHash()); + TestCrypter::TestDecrypt(crypt, std::vector<unsigned char>(hash.begin(), hash.end())); + } +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/src/wallet/test/wallet_tests.cpp b/src/wallet/test/wallet_tests.cpp index 387b223589..0a4f06ba88 100644 --- a/src/wallet/test/wallet_tests.cpp +++ b/src/wallet/test/wallet_tests.cpp @@ -27,7 +27,7 @@ typedef set<pair<const CWalletTx*,unsigned int> > CoinSet; BOOST_FIXTURE_TEST_SUITE(wallet_tests, WalletTestingSetup) -static CWallet wallet; +static const CWallet wallet; static vector<COutput> vCoins; static void add_coin(const CAmount& nValue, int nAge = 6*24, bool fIsFromMe = false, int nInput=0) @@ -188,11 +188,11 @@ BOOST_AUTO_TEST_CASE(coin_selection_tests) // empty the wallet and start again, now with fractions of a cent, to test small change avoidance empty_wallet(); - add_coin(0.1*MIN_CHANGE); - add_coin(0.2*MIN_CHANGE); - add_coin(0.3*MIN_CHANGE); - add_coin(0.4*MIN_CHANGE); - add_coin(0.5*MIN_CHANGE); + add_coin(MIN_CHANGE * 1 / 10); + add_coin(MIN_CHANGE * 2 / 10); + add_coin(MIN_CHANGE * 3 / 10); + add_coin(MIN_CHANGE * 4 / 10); + add_coin(MIN_CHANGE * 5 / 10); // try making 1 * MIN_CHANGE from the 1.5 * MIN_CHANGE // we'll get change smaller than MIN_CHANGE whatever happens, so can expect MIN_CHANGE exactly @@ -207,8 +207,8 @@ BOOST_AUTO_TEST_CASE(coin_selection_tests) BOOST_CHECK_EQUAL(nValueRet, 1 * MIN_CHANGE); // we should get the exact amount // if we add more small coins: - add_coin(0.6*MIN_CHANGE); - add_coin(0.7*MIN_CHANGE); + add_coin(MIN_CHANGE * 6 / 10); + add_coin(MIN_CHANGE * 7 / 10); // and try again to make 1.0 * MIN_CHANGE BOOST_CHECK( wallet.SelectCoinsMinConf(1 * MIN_CHANGE, 1, 1, vCoins, setCoinsRet, nValueRet)); @@ -229,9 +229,9 @@ BOOST_AUTO_TEST_CASE(coin_selection_tests) // sometimes it will fail, and so we use the next biggest coin: empty_wallet(); - add_coin(0.5 * MIN_CHANGE); - add_coin(0.6 * MIN_CHANGE); - add_coin(0.7 * MIN_CHANGE); + add_coin(MIN_CHANGE * 5 / 10); + add_coin(MIN_CHANGE * 6 / 10); + add_coin(MIN_CHANGE * 7 / 10); add_coin(1111 * MIN_CHANGE); BOOST_CHECK( wallet.SelectCoinsMinConf(1 * MIN_CHANGE, 1, 1, vCoins, setCoinsRet, nValueRet)); BOOST_CHECK_EQUAL(nValueRet, 1111 * MIN_CHANGE); // we get the bigger coin @@ -239,9 +239,9 @@ BOOST_AUTO_TEST_CASE(coin_selection_tests) // but sometimes it's possible, and we use an exact subset (0.4 + 0.6 = 1.0) empty_wallet(); - add_coin(0.4 * MIN_CHANGE); - add_coin(0.6 * MIN_CHANGE); - add_coin(0.8 * MIN_CHANGE); + add_coin(MIN_CHANGE * 4 / 10); + add_coin(MIN_CHANGE * 6 / 10); + add_coin(MIN_CHANGE * 8 / 10); add_coin(1111 * MIN_CHANGE); BOOST_CHECK( wallet.SelectCoinsMinConf(MIN_CHANGE, 1, 1, vCoins, setCoinsRet, nValueRet)); BOOST_CHECK_EQUAL(nValueRet, MIN_CHANGE); // we should get the exact amount @@ -249,17 +249,17 @@ BOOST_AUTO_TEST_CASE(coin_selection_tests) // test avoiding small change empty_wallet(); - add_coin(0.05 * MIN_CHANGE); - add_coin(1 * MIN_CHANGE); - add_coin(100 * MIN_CHANGE); + add_coin(MIN_CHANGE * 5 / 100); + add_coin(MIN_CHANGE * 1); + add_coin(MIN_CHANGE * 100); // trying to make 100.01 from these three coins - BOOST_CHECK( wallet.SelectCoinsMinConf(100.01 * MIN_CHANGE, 1, 1, vCoins, setCoinsRet, nValueRet)); - BOOST_CHECK_EQUAL(nValueRet, 101.05 * MIN_CHANGE); // we should get all coins + BOOST_CHECK(wallet.SelectCoinsMinConf(MIN_CHANGE * 10001 / 100, 1, 1, vCoins, setCoinsRet, nValueRet)); + BOOST_CHECK_EQUAL(nValueRet, MIN_CHANGE * 10105 / 100); // we should get all coins BOOST_CHECK_EQUAL(setCoinsRet.size(), 3U); // but if we try to make 99.9, we should take the bigger of the two small coins to avoid small change - BOOST_CHECK( wallet.SelectCoinsMinConf(99.9 * MIN_CHANGE, 1, 1, vCoins, setCoinsRet, nValueRet)); + BOOST_CHECK(wallet.SelectCoinsMinConf(MIN_CHANGE * 9990 / 100, 1, 1, vCoins, setCoinsRet, nValueRet)); BOOST_CHECK_EQUAL(nValueRet, 101 * MIN_CHANGE); BOOST_CHECK_EQUAL(setCoinsRet.size(), 2U); @@ -310,7 +310,11 @@ BOOST_AUTO_TEST_CASE(coin_selection_tests) // add 75 cents in small change. not enough to make 90 cents, // then try making 90 cents. there are multiple competing "smallest bigger" coins, // one of which should be picked at random - add_coin( 5*CENT); add_coin(10*CENT); add_coin(15*CENT); add_coin(20*CENT); add_coin(25*CENT); + add_coin(5 * CENT); + add_coin(10 * CENT); + add_coin(15 * CENT); + add_coin(20 * CENT); + add_coin(25 * CENT); fails = 0; for (int i = 0; i < RANDOM_REPEATS; i++) diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 6b942e29d7..723b2eceff 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -42,6 +42,7 @@ bool bSpendZeroConfChange = DEFAULT_SPEND_ZEROCONF_CHANGE; bool fSendFreeTransactions = DEFAULT_SEND_FREE_TRANSACTIONS; const char * DEFAULT_WALLET_DAT = "wallet.dat"; +const uint32_t BIP32_HARDENED_KEY_LIMIT = 0x80000000; /** * Fees smaller than this (in satoshi) are considered zero fee (for transaction creation) @@ -91,7 +92,51 @@ CPubKey CWallet::GenerateNewKey() bool fCompressed = CanSupportFeature(FEATURE_COMPRPUBKEY); // default to compressed public keys if we want 0.6.0 wallets CKey secret; - secret.MakeNewKey(fCompressed); + + // Create new metadata + int64_t nCreationTime = GetTime(); + CKeyMetadata metadata(nCreationTime); + + // use HD key derivation if HD was enabled during wallet creation + if (!hdChain.masterKeyID.IsNull()) { + // for now we use a fixed keypath scheme of m/0'/0'/k + CKey key; //master key seed (256bit) + CExtKey masterKey; //hd master key + CExtKey accountKey; //key at m/0' + CExtKey externalChainChildKey; //key at m/0'/0' + CExtKey childKey; //key at m/0'/0'/<n>' + + // try to get the master key + if (!GetKey(hdChain.masterKeyID, key)) + throw std::runtime_error("CWallet::GenerateNewKey(): Master key not found"); + + masterKey.SetMaster(key.begin(), key.size()); + + // derive m/0' + // use hardened derivation (child keys >= 0x80000000 are hardened after bip32) + masterKey.Derive(accountKey, BIP32_HARDENED_KEY_LIMIT); + + // derive m/0'/0' + accountKey.Derive(externalChainChildKey, BIP32_HARDENED_KEY_LIMIT); + + // derive child key at next index, skip keys already known to the wallet + do + { + // always derive hardened keys + // childIndex | BIP32_HARDENED_KEY_LIMIT = derive childIndex in hardened child-index-range + // example: 1 | BIP32_HARDENED_KEY_LIMIT == 0x80000001 == 2147483649 + externalChainChildKey.Derive(childKey, hdChain.nExternalChainCounter | BIP32_HARDENED_KEY_LIMIT); + // increment childkey index + hdChain.nExternalChainCounter++; + } while(HaveKey(childKey.key.GetPubKey().GetID())); + secret = childKey.key; + + // update the chain model in the database + if (!CWalletDB(strWalletFile).WriteHDChain(hdChain)) + throw std::runtime_error("CWallet::GenerateNewKey(): Writing HD chain model failed"); + } else { + secret.MakeNewKey(fCompressed); + } // Compressed public keys were introduced in version 0.6.0 if (fCompressed) @@ -100,9 +145,7 @@ CPubKey CWallet::GenerateNewKey() CPubKey pubkey = secret.GetPubKey(); assert(secret.VerifyPubKey(pubkey)); - // Create new metadata - int64_t nCreationTime = GetTime(); - mapKeyMetadata[pubkey.GetID()] = CKeyMetadata(nCreationTime); + mapKeyMetadata[pubkey.GetID()] = metadata; if (!nTimeFirstKey || nCreationTime < nTimeFirstKey) nTimeFirstKey = nCreationTime; @@ -509,16 +552,14 @@ bool CWallet::EncryptWallet(const SecureString& strWalletPassphrase) return false; CKeyingMaterial vMasterKey; - RandAddSeedPerfmon(); vMasterKey.resize(WALLET_CRYPTO_KEY_SIZE); - GetRandBytes(&vMasterKey[0], WALLET_CRYPTO_KEY_SIZE); + GetStrongRandBytes(&vMasterKey[0], WALLET_CRYPTO_KEY_SIZE); CMasterKey kMasterKey; - RandAddSeedPerfmon(); kMasterKey.vchSalt.resize(WALLET_CRYPTO_SALT_SIZE); - GetRandBytes(&kMasterKey.vchSalt[0], WALLET_CRYPTO_SALT_SIZE); + GetStrongRandBytes(&kMasterKey.vchSalt[0], WALLET_CRYPTO_SALT_SIZE); CCrypter crypter; int64_t nStartTime = GetTimeMillis(); @@ -608,6 +649,78 @@ int64_t CWallet::IncOrderPosNext(CWalletDB *pwalletdb) return nRet; } +bool CWallet::AccountMove(std::string strFrom, std::string strTo, CAmount nAmount, std::string strComment) +{ + CWalletDB walletdb(strWalletFile); + if (!walletdb.TxnBegin()) + return false; + + int64_t nNow = GetAdjustedTime(); + + // Debit + CAccountingEntry debit; + debit.nOrderPos = IncOrderPosNext(&walletdb); + debit.strAccount = strFrom; + debit.nCreditDebit = -nAmount; + debit.nTime = nNow; + debit.strOtherAccount = strTo; + debit.strComment = strComment; + AddAccountingEntry(debit, walletdb); + + // Credit + CAccountingEntry credit; + credit.nOrderPos = IncOrderPosNext(&walletdb); + credit.strAccount = strTo; + credit.nCreditDebit = nAmount; + credit.nTime = nNow; + credit.strOtherAccount = strFrom; + credit.strComment = strComment; + AddAccountingEntry(credit, walletdb); + + if (!walletdb.TxnCommit()) + return false; + + return true; +} + +bool CWallet::GetAccountPubkey(CPubKey &pubKey, std::string strAccount, bool bForceNew) +{ + CWalletDB walletdb(strWalletFile); + + CAccount account; + walletdb.ReadAccount(strAccount, account); + + if (!bForceNew) { + if (!account.vchPubKey.IsValid()) + bForceNew = true; + else { + // Check if the current key has been used + CScript scriptPubKey = GetScriptForDestination(account.vchPubKey.GetID()); + for (map<uint256, CWalletTx>::iterator it = mapWallet.begin(); + it != mapWallet.end() && account.vchPubKey.IsValid(); + ++it) + BOOST_FOREACH(const CTxOut& txout, (*it).second.vout) + if (txout.scriptPubKey == scriptPubKey) { + bForceNew = true; + break; + } + } + } + + // Generate a new key + if (bForceNew) { + if (!GetKeyFromPool(account.vchPubKey)) + return false; + + SetAddressBook(account.vchPubKey.GetID(), strAccount, "receive"); + walletdb.WriteAccount(strAccount, account); + } + + pubKey = account.vchPubKey; + + return true; +} + void CWallet::MarkDirty() { { @@ -1051,6 +1164,37 @@ CAmount CWallet::GetChange(const CTransaction& tx) const return nChange; } +bool CWallet::SetHDMasterKey(const CKey& key) +{ + LOCK(cs_wallet); + + // store the key as normal "key"/"ckey" object + // in the database + // key metadata is not required + CPubKey pubkey = key.GetPubKey(); + if (!AddKeyPubKey(key, pubkey)) + throw std::runtime_error("CWallet::GenerateNewKey(): AddKey failed"); + + // store the keyid (hash160) together with + // the child index counter in the database + // as a hdchain object + CHDChain newHdChain; + newHdChain.masterKeyID = pubkey.GetID(); + SetHDChain(newHdChain, false); + + return true; +} + +bool CWallet::SetHDChain(const CHDChain& chain, bool memonly) +{ + LOCK(cs_wallet); + if (!memonly && !CWalletDB(strWalletFile).WriteHDChain(chain)) + throw runtime_error("AddHDChain(): writing chain failed"); + + hdChain = chain; + return true; +} + int64_t CWalletTx::GetTxTime() const { int64_t n = nTimeSmart; @@ -1911,7 +2055,7 @@ bool CWallet::SelectCoins(const vector<COutput>& vAvailableCoins, const CAmount& return res; } -bool CWallet::FundTransaction(CMutableTransaction& tx, CAmount& nFeeRet, int& nChangePosInOut, std::string& strFailReason, bool includeWatching, bool lockUnspents, const CTxDestination& destChange) +bool CWallet::FundTransaction(CMutableTransaction& tx, CAmount& nFeeRet, bool overrideEstimatedFeeRate, const CFeeRate& specificFeeRate, int& nChangePosInOut, std::string& strFailReason, bool includeWatching, bool lockUnspents, const CTxDestination& destChange) { vector<CRecipient> vecSend; @@ -1926,6 +2070,9 @@ bool CWallet::FundTransaction(CMutableTransaction& tx, CAmount& nFeeRet, int& nC coinControl.destChange = destChange; coinControl.fAllowOtherInputs = true; coinControl.fAllowWatchOnly = includeWatching; + coinControl.fOverrideFeeRate = overrideEstimatedFeeRate; + coinControl.nFeeRate = specificFeeRate; + BOOST_FOREACH(const CTxIn& txin, tx.vin) coinControl.Select(txin.prevout); @@ -2159,7 +2306,7 @@ bool CWallet::CreateTransaction(const vector<CRecipient>& vecSend, CWalletTx& wt // Insert change txn at random position: nChangePosInOut = GetRandInt(txNew.vout.size()+1); } - else if (nChangePosInOut > txNew.vout.size()) + else if ((unsigned int)nChangePosInOut > txNew.vout.size()) { strFailReason = _("Change index out of range"); return false; @@ -2235,6 +2382,8 @@ bool CWallet::CreateTransaction(const vector<CRecipient>& vecSend, CWalletTx& wt if (coinControl && nFeeNeeded > 0 && coinControl->nMinimumTotalFee > nFeeNeeded) { nFeeNeeded = coinControl->nMinimumTotalFee; } + if (coinControl && coinControl->fOverrideFeeRate) + nFeeNeeded = coinControl->nFeeRate.GetFee(nBytes); // If we made it here and we aren't even able to meet the relay fee on the next pass, give up // because we must be at the maximum allowed fee. @@ -2759,6 +2908,37 @@ set< set<CTxDestination> > CWallet::GetAddressGroupings() return ret; } +CAmount CWallet::GetAccountBalance(const std::string& strAccount, int nMinDepth, const isminefilter& filter) +{ + CWalletDB walletdb(strWalletFile); + return GetAccountBalance(walletdb, strAccount, nMinDepth, filter); +} + +CAmount CWallet::GetAccountBalance(CWalletDB& walletdb, const std::string& strAccount, int nMinDepth, const isminefilter& filter) +{ + CAmount nBalance = 0; + + // Tally wallet transactions + for (map<uint256, CWalletTx>::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) + { + const CWalletTx& wtx = (*it).second; + if (!CheckFinalTx(wtx) || wtx.GetBlocksToMaturity() > 0 || wtx.GetDepthInMainChain() < 0) + continue; + + CAmount nReceived, nSent, nFee; + wtx.GetAccountAmounts(strAccount, nReceived, nSent, nFee, filter); + + if (nReceived != 0 && wtx.GetDepthInMainChain() >= nMinDepth) + nBalance += nReceived; + nBalance -= nSent + nFee; + } + + // Tally internal accounting entries + nBalance += walletdb.GetAccountCreditDebit(strAccount); + + return nBalance; +} + std::set<CTxDestination> CWallet::GetAccountAddresses(const std::string& strAccount) const { LOCK(cs_wallet); @@ -3029,6 +3209,7 @@ std::string CWallet::GetWalletHelpString(bool showDebug) strUsage += HelpMessageOpt("-sendfreetransactions", strprintf(_("Send transactions as zero-fee transactions if possible (default: %u)"), DEFAULT_SEND_FREE_TRANSACTIONS)); strUsage += HelpMessageOpt("-spendzeroconfchange", strprintf(_("Spend unconfirmed change when sending transactions (default: %u)"), DEFAULT_SPEND_ZEROCONF_CHANGE)); strUsage += HelpMessageOpt("-txconfirmtarget=<n>", strprintf(_("If paytxfee is not set, include enough fee so transactions begin confirmation on average within n blocks (default: %u)"), DEFAULT_TX_CONFIRM_TARGET)); + strUsage += HelpMessageOpt("-usehd", _("Use hierarchical deterministic key generation (HD) after bip32. Only has effect during wallet creation/first start") + " " + strprintf(_("(default: %u)"), DEFAULT_USE_HD_WALLET)); strUsage += HelpMessageOpt("-upgradewallet", _("Upgrade wallet to latest format on startup")); strUsage += HelpMessageOpt("-wallet=<file>", _("Specify wallet file (within data directory)") + " " + strprintf(_("(default: %s)"), DEFAULT_WALLET_DAT)); strUsage += HelpMessageOpt("-walletbroadcast", _("Make the wallet broadcast transactions") + " " + strprintf(_("(default: %u)"), DEFAULT_WALLETBROADCAST)); @@ -3116,8 +3297,13 @@ bool CWallet::InitLoadWallet() if (fFirstRun) { // Create new keyUser and set as default key - RandAddSeedPerfmon(); - + if (GetBoolArg("-usehd", DEFAULT_USE_HD_WALLET)) { + // generate a new master key + CKey key; + key.MakeNewKey(true); + if (!walletInstance->SetHDMasterKey(key)) + throw std::runtime_error("CWallet::GenerateNewKey(): Storing master key failed"); + } CPubKey newDefaultKey; if (walletInstance->GetKeyFromPool(newDefaultKey)) { walletInstance->SetDefaultKey(newDefaultKey); @@ -3127,6 +3313,13 @@ bool CWallet::InitLoadWallet() walletInstance->SetBestChain(chainActive.GetLocator()); } + else if (mapArgs.count("-usehd")) { + bool useHD = GetBoolArg("-usehd", DEFAULT_USE_HD_WALLET); + if (!walletInstance->hdChain.masterKeyID.IsNull() && !useHD) + return InitError(strprintf(_("Error loading %s: You can't disable HD on a already existing HD wallet"), walletFile)); + if (walletInstance->hdChain.masterKeyID.IsNull() && useHD) + return InitError(strprintf(_("Error loading %s: You can't enable HD on a already existing non-HD wallet"), walletFile)); + } LogPrintf(" wallet %15dms\n", GetTimeMillis() - nStart); @@ -3252,6 +3445,46 @@ bool CWallet::ParameterInteraction() return true; } +bool CWallet::BackupWallet(const std::string& strDest) +{ + if (!fFileBacked) + return false; + while (true) + { + { + LOCK(bitdb.cs_db); + if (!bitdb.mapFileUseCount.count(strWalletFile) || bitdb.mapFileUseCount[strWalletFile] == 0) + { + // Flush log data to the dat file + bitdb.CloseDb(strWalletFile); + bitdb.CheckpointLSN(strWalletFile); + bitdb.mapFileUseCount.erase(strWalletFile); + + // Copy wallet file + boost::filesystem::path pathSrc = GetDataDir() / strWalletFile; + boost::filesystem::path pathDest(strDest); + if (boost::filesystem::is_directory(pathDest)) + pathDest /= strWalletFile; + + try { +#if BOOST_VERSION >= 104000 + boost::filesystem::copy_file(pathSrc, pathDest, boost::filesystem::copy_option::overwrite_if_exists); +#else + boost::filesystem::copy_file(pathSrc, pathDest); +#endif + LogPrintf("copied %s to %s\n", strWalletFile, pathDest.string()); + return true; + } catch (const boost::filesystem::filesystem_error& e) { + LogPrintf("error copying %s to %s - %s\n", strWalletFile, pathDest.string(), e.what()); + return false; + } + } + } + MilliSleep(100); + } + return false; +} + CKeyPool::CKeyPool() { nTime = GetTime(); diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index cc568c3749..7fc6ce5de5 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -57,6 +57,9 @@ static const unsigned int DEFAULT_TX_CONFIRM_TARGET = 2; static const unsigned int MAX_FREE_TRANSACTION_CREATE_SIZE = 1000; static const bool DEFAULT_WALLETBROADCAST = true; +//! if set, all keys will be derived by using BIP32 +static const bool DEFAULT_USE_HD_WALLET = true; + extern const char * DEFAULT_WALLET_DAT; class CBlockIndex; @@ -574,6 +577,9 @@ private: void SyncMetaData(std::pair<TxSpends::iterator, TxSpends::iterator>); + /* the hd chain data model (external chain counters) */ + CHDChain hdChain; + public: /* * Main wallet lock. @@ -718,6 +724,8 @@ public: * @return next transaction order id */ int64_t IncOrderPosNext(CWalletDB *pwalletdb = NULL); + bool AccountMove(std::string strFrom, std::string strTo, CAmount nAmount, std::string strComment = ""); + bool GetAccountPubkey(CPubKey &pubKey, std::string strAccount, bool bForceNew = false); void MarkDirty(); bool AddToWallet(const CWalletTx& wtxIn, bool fFromLoadWallet, CWalletDB* pwalletdb); @@ -738,7 +746,7 @@ public: * Insert additional inputs into the transaction by * calling CreateTransaction(); */ - bool FundTransaction(CMutableTransaction& tx, CAmount& nFeeRet, int& nChangePosInOut, std::string& strFailReason, bool includeWatching, bool lockUnspents, const CTxDestination& destChange = CNoDestination()); + bool FundTransaction(CMutableTransaction& tx, CAmount& nFeeRet, bool overrideEstimatedFeeRate, const CFeeRate& specificFeeRate, int& nChangePosInOut, std::string& strFailReason, bool includeWatching, bool lockUnspents, const CTxDestination& destChange = CNoDestination()); /** * Create a new transaction paying the recipients with a set of coins @@ -776,6 +784,8 @@ public: std::set< std::set<CTxDestination> > GetAddressGroupings(); std::map<CTxDestination, CAmount> GetAddressBalances(); + CAmount GetAccountBalance(const std::string& strAccount, int nMinDepth, const isminefilter& filter); + CAmount GetAccountBalance(CWalletDB& walletdb, const std::string& strAccount, int nMinDepth, const isminefilter& filter); std::set<CTxDestination> GetAccountAddresses(const std::string& strAccount) const; isminetype IsMine(const CTxIn& txin) const; @@ -883,6 +893,14 @@ public: /* Wallets parameter interaction */ static bool ParameterInteraction(); + + bool BackupWallet(const std::string& strDest); + + /* Set the hd chain model (chain child index counters) */ + bool SetHDChain(const CHDChain& chain, bool memonly); + + /* Set the current hd master key (will reset the chain child index counters) */ + bool SetHDMasterKey(const CKey& key); }; /** A key allocated from the key pool. */ diff --git a/src/wallet/walletdb.cpp b/src/wallet/walletdb.cpp index 48579b2821..7bfd490950 100644 --- a/src/wallet/walletdb.cpp +++ b/src/wallet/walletdb.cpp @@ -599,6 +599,16 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue, return false; } } + else if (strType == "hdchain") + { + CHDChain chain; + ssValue >> chain; + if (!pwallet->SetHDChain(chain, true)) + { + strErr = "Error reading wallet database: SetHDChain failed"; + return false; + } + } } catch (...) { return false; @@ -903,46 +913,6 @@ void ThreadFlushWalletDB(const string& strFile) } } -bool BackupWallet(const CWallet& wallet, const string& strDest) -{ - if (!wallet.fFileBacked) - return false; - while (true) - { - { - LOCK(bitdb.cs_db); - if (!bitdb.mapFileUseCount.count(wallet.strWalletFile) || bitdb.mapFileUseCount[wallet.strWalletFile] == 0) - { - // Flush log data to the dat file - bitdb.CloseDb(wallet.strWalletFile); - bitdb.CheckpointLSN(wallet.strWalletFile); - bitdb.mapFileUseCount.erase(wallet.strWalletFile); - - // Copy wallet file - boost::filesystem::path pathSrc = GetDataDir() / wallet.strWalletFile; - boost::filesystem::path pathDest(strDest); - if (boost::filesystem::is_directory(pathDest)) - pathDest /= wallet.strWalletFile; - - try { -#if BOOST_VERSION >= 104000 - boost::filesystem::copy_file(pathSrc, pathDest, boost::filesystem::copy_option::overwrite_if_exists); -#else - boost::filesystem::copy_file(pathSrc, pathDest); -#endif - LogPrintf("copied %s to %s\n", wallet.strWalletFile, pathDest.string()); - return true; - } catch (const boost::filesystem::filesystem_error& e) { - LogPrintf("error copying %s to %s - %s\n", wallet.strWalletFile, pathDest.string(), e.what()); - return false; - } - } - } - MilliSleep(100); - } - return false; -} - // // Try to (very carefully!) recover wallet file if there is a problem. // @@ -1043,3 +1013,10 @@ bool CWalletDB::EraseDestData(const std::string &address, const std::string &key nWalletDBUpdated++; return Erase(std::make_pair(std::string("destdata"), std::make_pair(address, key))); } + + +bool CWalletDB::WriteHDChain(const CHDChain& chain) +{ + nWalletDBUpdated++; + return Write(std::string("hdchain"), chain); +} diff --git a/src/wallet/walletdb.h b/src/wallet/walletdb.h index 5345c0907e..71b0ff26db 100644 --- a/src/wallet/walletdb.h +++ b/src/wallet/walletdb.h @@ -40,6 +40,35 @@ enum DBErrors DB_NEED_REWRITE }; +/* simple hd chain data model */ +class CHDChain +{ +public: + uint32_t nExternalChainCounter; + CKeyID masterKeyID; //!< master key hash160 + + static const int CURRENT_VERSION = 1; + int nVersion; + + CHDChain() { SetNull(); } + ADD_SERIALIZE_METHODS; + template <typename Stream, typename Operation> + inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) + { + READWRITE(this->nVersion); + nVersion = this->nVersion; + READWRITE(nExternalChainCounter); + READWRITE(masterKeyID); + } + + void SetNull() + { + nVersion = CHDChain::CURRENT_VERSION; + nExternalChainCounter = 0; + masterKeyID.SetNull(); + } +}; + class CKeyMetadata { public: @@ -134,6 +163,9 @@ public: static bool Recover(CDBEnv& dbenv, const std::string& filename, bool fOnlyKeys); static bool Recover(CDBEnv& dbenv, const std::string& filename); + //! write the hdchain model (external chain child index counter) + bool WriteHDChain(const CHDChain& chain); + private: CWalletDB(const CWalletDB&); void operator=(const CWalletDB&); @@ -141,7 +173,6 @@ private: bool WriteAccountingEntry(const uint64_t nAccEntryNum, const CAccountingEntry& acentry); }; -bool BackupWallet(const CWallet& wallet, const std::string& strDest); void ThreadFlushWalletDB(const std::string& strFile); #endif // BITCOIN_WALLET_WALLETDB_H |