aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am6
-rw-r--r--src/Makefile.bench.include2
-rw-r--r--src/Makefile.qt.include2
-rw-r--r--src/Makefile.qttest.include2
-rw-r--r--src/Makefile.test.include3
-rw-r--r--src/bench/crypto_hash.cpp54
-rw-r--r--src/crypto/muhash.cpp338
-rw-r--r--src/crypto/muhash.h130
-rw-r--r--src/flatfile.cpp1
-rw-r--r--src/init.cpp20
-rw-r--r--src/interfaces/node.h2
-rw-r--r--src/mapport.cpp336
-rw-r--r--src/mapport.h30
-rw-r--r--src/net.cpp130
-rw-r--r--src/net.h18
-rw-r--r--src/node/interfaces.cpp11
-rw-r--r--src/qt/forms/optionsdialog.ui10
-rw-r--r--src/qt/optionsdialog.cpp9
-rw-r--r--src/qt/optionsmodel.cpp24
-rw-r--r--src/qt/optionsmodel.h1
-rw-r--r--src/rpc/net.cpp2
-rw-r--r--src/test/crypto_tests.cpp90
-rw-r--r--src/test/fuzz/crypto.cpp16
-rw-r--r--src/test/fuzz/muhash.cpp53
-rw-r--r--src/util/system.cpp27
-rw-r--r--src/util/system.h12
26 files changed, 1154 insertions, 175 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index 23d790d552..1a0791dccd 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -152,6 +152,7 @@ BITCOIN_CORE_H = \
key_io.h \
logging.h \
logging/timer.h \
+ mapport.h \
memusage.h \
merkleblock.h \
miner.h \
@@ -299,6 +300,7 @@ libbitcoin_server_a_SOURCES = \
index/blockfilterindex.cpp \
index/txindex.cpp \
init.cpp \
+ mapport.cpp \
miner.cpp \
net.cpp \
net_processing.cpp \
@@ -408,6 +410,8 @@ crypto_libbitcoin_crypto_base_a_SOURCES = \
crypto/hmac_sha512.h \
crypto/poly1305.h \
crypto/poly1305.cpp \
+ crypto/muhash.h \
+ crypto/muhash.cpp \
crypto/ripemd160.cpp \
crypto/ripemd160.h \
crypto/sha1.cpp \
@@ -596,7 +600,7 @@ bitcoin_bin_ldadd = \
$(LIBMEMENV) \
$(LIBSECP256K1)
-bitcoin_bin_ldadd += $(BOOST_LIBS) $(BDB_LIBS) $(MINIUPNPC_LIBS) $(EVENT_PTHREADS_LIBS) $(EVENT_LIBS) $(ZMQ_LIBS) $(SQLITE_LIBS)
+bitcoin_bin_ldadd += $(BOOST_LIBS) $(BDB_LIBS) $(MINIUPNPC_LIBS) $(NATPMP_LIBS) $(EVENT_PTHREADS_LIBS) $(EVENT_LIBS) $(ZMQ_LIBS) $(SQLITE_LIBS)
bitcoind_SOURCES = $(bitcoin_daemon_sources)
bitcoind_CPPFLAGS = $(bitcoin_bin_cppflags)
diff --git a/src/Makefile.bench.include b/src/Makefile.bench.include
index beb3f8dfd2..56b8ca8ce6 100644
--- a/src/Makefile.bench.include
+++ b/src/Makefile.bench.include
@@ -74,7 +74,7 @@ bench_bench_bitcoin_SOURCES += bench/coin_selection.cpp
bench_bench_bitcoin_SOURCES += bench/wallet_balance.cpp
endif
-bench_bench_bitcoin_LDADD += $(BOOST_LIBS) $(BDB_LIBS) $(EVENT_PTHREADS_LIBS) $(EVENT_LIBS) $(MINIUPNPC_LIBS) $(SQLITE_LIBS)
+bench_bench_bitcoin_LDADD += $(BOOST_LIBS) $(BDB_LIBS) $(EVENT_PTHREADS_LIBS) $(EVENT_LIBS) $(MINIUPNPC_LIBS) $(NATPMP_LIBS) $(SQLITE_LIBS)
bench_bench_bitcoin_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) $(PTHREAD_FLAGS)
CLEAN_BITCOIN_BENCH = bench/*.gcda bench/*.gcno $(GENERATED_BENCH_FILES)
diff --git a/src/Makefile.qt.include b/src/Makefile.qt.include
index f46310a603..3d41d203d3 100644
--- a/src/Makefile.qt.include
+++ b/src/Makefile.qt.include
@@ -320,7 +320,7 @@ if ENABLE_ZMQ
bitcoin_qt_ldadd += $(LIBBITCOIN_ZMQ) $(ZMQ_LIBS)
endif
bitcoin_qt_ldadd += $(LIBBITCOIN_CLI) $(LIBBITCOIN_COMMON) $(LIBBITCOIN_UTIL) $(LIBBITCOIN_CONSENSUS) $(LIBBITCOIN_CRYPTO) $(LIBUNIVALUE) $(LIBLEVELDB) $(LIBLEVELDB_SSE42) $(LIBMEMENV) \
- $(BOOST_LIBS) $(QT_LIBS) $(QT_DBUS_LIBS) $(QR_LIBS) $(BDB_LIBS) $(MINIUPNPC_LIBS) $(LIBSECP256K1) \
+ $(BOOST_LIBS) $(QT_LIBS) $(QT_DBUS_LIBS) $(QR_LIBS) $(BDB_LIBS) $(MINIUPNPC_LIBS) $(NATPMP_LIBS) $(LIBSECP256K1) \
$(EVENT_PTHREADS_LIBS) $(EVENT_LIBS) $(SQLITE_LIBS)
bitcoin_qt_ldflags = $(RELDFLAGS) $(AM_LDFLAGS) $(QT_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) $(PTHREAD_FLAGS)
bitcoin_qt_libtoolflags = $(AM_LIBTOOLFLAGS) --tag CXX
diff --git a/src/Makefile.qttest.include b/src/Makefile.qttest.include
index c05dd38737..a6a857d952 100644
--- a/src/Makefile.qttest.include
+++ b/src/Makefile.qttest.include
@@ -55,7 +55,7 @@ qt_test_test_bitcoin_qt_LDADD += $(LIBBITCOIN_ZMQ) $(ZMQ_LIBS)
endif
qt_test_test_bitcoin_qt_LDADD += $(LIBBITCOIN_CLI) $(LIBBITCOIN_COMMON) $(LIBBITCOIN_UTIL) $(LIBBITCOIN_CONSENSUS) $(LIBBITCOIN_CRYPTO) $(LIBUNIVALUE) $(LIBLEVELDB) \
$(LIBLEVELDB_SSE42) $(LIBMEMENV) $(BOOST_LIBS) $(QT_DBUS_LIBS) $(QT_TEST_LIBS) $(QT_LIBS) \
- $(QR_LIBS) $(BDB_LIBS) $(MINIUPNPC_LIBS) $(LIBSECP256K1) \
+ $(QR_LIBS) $(BDB_LIBS) $(MINIUPNPC_LIBS) $(NATPMP_LIBS) $(LIBSECP256K1) \
$(EVENT_PTHREADS_LIBS) $(EVENT_LIBS) $(SQLITE_LIBS)
qt_test_test_bitcoin_qt_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(QT_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) $(PTHREAD_FLAGS)
qt_test_test_bitcoin_qt_CXXFLAGS = $(AM_CXXFLAGS) $(QT_PIE_FLAGS)
diff --git a/src/Makefile.test.include b/src/Makefile.test.include
index 3faa5ac968..e9f9b73abe 100644
--- a/src/Makefile.test.include
+++ b/src/Makefile.test.include
@@ -167,7 +167,7 @@ test_test_bitcoin_LDADD += $(LIBBITCOIN_SERVER) $(LIBBITCOIN_CLI) $(LIBBITCOIN_C
$(LIBLEVELDB) $(LIBLEVELDB_SSE42) $(LIBMEMENV) $(BOOST_LIBS) $(BOOST_UNIT_TEST_FRAMEWORK_LIB) $(LIBSECP256K1) $(EVENT_LIBS) $(EVENT_PTHREADS_LIBS)
test_test_bitcoin_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
-test_test_bitcoin_LDADD += $(BDB_LIBS) $(MINIUPNPC_LIBS) $(SQLITE_LIBS)
+test_test_bitcoin_LDADD += $(BDB_LIBS) $(MINIUPNPC_LIBS) $(NATPMP_LIBS) $(SQLITE_LIBS)
test_test_bitcoin_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) $(PTHREAD_FLAGS) -static
if ENABLE_ZMQ
@@ -230,6 +230,7 @@ test_fuzz_fuzz_SOURCES = \
test/fuzz/locale.cpp \
test/fuzz/merkleblock.cpp \
test/fuzz/message.cpp \
+ test/fuzz/muhash.cpp \
test/fuzz/multiplication_overflow.cpp \
test/fuzz/net.cpp \
test/fuzz/net_permissions.cpp \
diff --git a/src/bench/crypto_hash.cpp b/src/bench/crypto_hash.cpp
index 65d16d47d8..30fe11be6b 100644
--- a/src/bench/crypto_hash.cpp
+++ b/src/bench/crypto_hash.cpp
@@ -4,6 +4,7 @@
#include <bench/bench.h>
+#include <crypto/muhash.h>
#include <crypto/ripemd160.h>
#include <crypto/sha1.h>
#include <crypto/sha256.h>
@@ -105,6 +106,54 @@ static void FastRandom_1bit(benchmark::Bench& bench)
});
}
+static void MuHash(benchmark::Bench& bench)
+{
+ MuHash3072 acc;
+ unsigned char key[32] = {0};
+ int i = 0;
+ bench.run([&] {
+ key[0] = ++i;
+ acc *= MuHash3072(key);
+ });
+}
+
+static void MuHashMul(benchmark::Bench& bench)
+{
+ MuHash3072 acc;
+ FastRandomContext rng(true);
+ MuHash3072 muhash{rng.randbytes(32)};
+
+ bench.run([&] {
+ acc *= muhash;
+ });
+}
+
+static void MuHashDiv(benchmark::Bench& bench)
+{
+ MuHash3072 acc;
+ FastRandomContext rng(true);
+ MuHash3072 muhash{rng.randbytes(32)};
+
+ for (size_t i = 0; i < bench.epochIterations(); ++i) {
+ acc *= muhash;
+ }
+
+ bench.run([&] {
+ acc /= muhash;
+ });
+}
+
+static void MuHashPrecompute(benchmark::Bench& bench)
+{
+ MuHash3072 acc;
+ FastRandomContext rng(true);
+ std::vector<unsigned char> key{rng.randbytes(32)};
+
+ bench.run([&] {
+ MuHash3072{key};
+ });
+}
+
BENCHMARK(RIPEMD160);
BENCHMARK(SHA1);
BENCHMARK(SHA256);
@@ -116,3 +165,8 @@ BENCHMARK(SipHash_32b);
BENCHMARK(SHA256D64_1024);
BENCHMARK(FastRandom_32bit);
BENCHMARK(FastRandom_1bit);
+
+BENCHMARK(MuHash);
+BENCHMARK(MuHashMul);
+BENCHMARK(MuHashDiv);
+BENCHMARK(MuHashPrecompute);
diff --git a/src/crypto/muhash.cpp b/src/crypto/muhash.cpp
new file mode 100644
index 0000000000..fbd14f9325
--- /dev/null
+++ b/src/crypto/muhash.cpp
@@ -0,0 +1,338 @@
+// Copyright (c) 2017-2020 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 <crypto/muhash.h>
+
+#include <crypto/chacha20.h>
+#include <crypto/common.h>
+#include <hash.h>
+
+#include <cassert>
+#include <cstdio>
+#include <limits>
+
+namespace {
+
+using limb_t = Num3072::limb_t;
+using double_limb_t = Num3072::double_limb_t;
+constexpr int LIMB_SIZE = Num3072::LIMB_SIZE;
+constexpr int LIMBS = Num3072::LIMBS;
+/** 2^3072 - 1103717, the largest 3072-bit safe prime number, is used as the modulus. */
+constexpr limb_t MAX_PRIME_DIFF = 1103717;
+
+/** Extract the lowest limb of [c0,c1,c2] into n, and left shift the number by 1 limb. */
+inline void extract3(limb_t& c0, limb_t& c1, limb_t& c2, limb_t& n)
+{
+ n = c0;
+ c0 = c1;
+ c1 = c2;
+ c2 = 0;
+}
+
+/** [c0,c1] = a * b */
+inline void mul(limb_t& c0, limb_t& c1, const limb_t& a, const limb_t& b)
+{
+ double_limb_t t = (double_limb_t)a * b;
+ c1 = t >> LIMB_SIZE;
+ c0 = t;
+}
+
+/* [c0,c1,c2] += n * [d0,d1,d2]. c2 is 0 initially */
+inline void mulnadd3(limb_t& c0, limb_t& c1, limb_t& c2, limb_t& d0, limb_t& d1, limb_t& d2, const limb_t& n)
+{
+ double_limb_t t = (double_limb_t)d0 * n + c0;
+ c0 = t;
+ t >>= LIMB_SIZE;
+ t += (double_limb_t)d1 * n + c1;
+ c1 = t;
+ t >>= LIMB_SIZE;
+ c2 = t + d2 * n;
+}
+
+/* [c0,c1] *= n */
+inline void muln2(limb_t& c0, limb_t& c1, const limb_t& n)
+{
+ double_limb_t t = (double_limb_t)c0 * n;
+ c0 = t;
+ t >>= LIMB_SIZE;
+ t += (double_limb_t)c1 * n;
+ c1 = t;
+}
+
+/** [c0,c1,c2] += a * b */
+inline void muladd3(limb_t& c0, limb_t& c1, limb_t& c2, const limb_t& a, const limb_t& b)
+{
+ double_limb_t t = (double_limb_t)a * b;
+ limb_t th = t >> LIMB_SIZE;
+ limb_t tl = t;
+
+ c0 += tl;
+ th += (c0 < tl) ? 1 : 0;
+ c1 += th;
+ c2 += (c1 < th) ? 1 : 0;
+}
+
+/** [c0,c1,c2] += 2 * a * b */
+inline void muldbladd3(limb_t& c0, limb_t& c1, limb_t& c2, const limb_t& a, const limb_t& b)
+{
+ double_limb_t t = (double_limb_t)a * b;
+ limb_t th = t >> LIMB_SIZE;
+ limb_t tl = t;
+
+ c0 += tl;
+ limb_t tt = th + ((c0 < tl) ? 1 : 0);
+ c1 += tt;
+ c2 += (c1 < tt) ? 1 : 0;
+ c0 += tl;
+ th += (c0 < tl) ? 1 : 0;
+ c1 += th;
+ c2 += (c1 < th) ? 1 : 0;
+}
+
+/**
+ * Add limb a to [c0,c1]: [c0,c1] += a. Then extract the lowest
+ * limb of [c0,c1] into n, and left shift the number by 1 limb.
+ * */
+inline void addnextract2(limb_t& c0, limb_t& c1, const limb_t& a, limb_t& n)
+{
+ limb_t c2 = 0;
+
+ // add
+ c0 += a;
+ if (c0 < a) {
+ c1 += 1;
+
+ // Handle case when c1 has overflown
+ if (c1 == 0)
+ c2 = 1;
+ }
+
+ // extract
+ n = c0;
+ c0 = c1;
+ c1 = c2;
+}
+
+/** in_out = in_out^(2^sq) * mul */
+inline void square_n_mul(Num3072& in_out, const int sq, const Num3072& mul)
+{
+ for (int j = 0; j < sq; ++j) in_out.Square();
+ in_out.Multiply(mul);
+}
+
+} // namespace
+
+/** Indicates wether d is larger than the modulus. */
+bool Num3072::IsOverflow() const
+{
+ if (this->limbs[0] <= std::numeric_limits<limb_t>::max() - MAX_PRIME_DIFF) return false;
+ for (int i = 1; i < LIMBS; ++i) {
+ if (this->limbs[i] != std::numeric_limits<limb_t>::max()) return false;
+ }
+ return true;
+}
+
+void Num3072::FullReduce()
+{
+ limb_t c0 = MAX_PRIME_DIFF;
+ limb_t c1 = 0;
+ for (int i = 0; i < LIMBS; ++i) {
+ addnextract2(c0, c1, this->limbs[i], this->limbs[i]);
+ }
+}
+
+Num3072 Num3072::GetInverse() const
+{
+ // For fast exponentiation a sliding window exponentiation with repunit
+ // precomputation is utilized. See "Fast Point Decompression for Standard
+ // Elliptic Curves" (Brumley, Järvinen, 2008).
+
+ Num3072 p[12]; // p[i] = a^(2^(2^i)-1)
+ Num3072 out;
+
+ p[0] = *this;
+
+ for (int i = 0; i < 11; ++i) {
+ p[i + 1] = p[i];
+ for (int j = 0; j < (1 << i); ++j) p[i + 1].Square();
+ p[i + 1].Multiply(p[i]);
+ }
+
+ out = p[11];
+
+ square_n_mul(out, 512, p[9]);
+ square_n_mul(out, 256, p[8]);
+ square_n_mul(out, 128, p[7]);
+ square_n_mul(out, 64, p[6]);
+ square_n_mul(out, 32, p[5]);
+ square_n_mul(out, 8, p[3]);
+ square_n_mul(out, 2, p[1]);
+ square_n_mul(out, 1, p[0]);
+ square_n_mul(out, 5, p[2]);
+ square_n_mul(out, 3, p[0]);
+ square_n_mul(out, 2, p[0]);
+ square_n_mul(out, 4, p[0]);
+ square_n_mul(out, 4, p[1]);
+ square_n_mul(out, 3, p[0]);
+
+ return out;
+}
+
+void Num3072::Multiply(const Num3072& a)
+{
+ limb_t c0 = 0, c1 = 0, c2 = 0;
+ Num3072 tmp;
+
+ /* Compute limbs 0..N-2 of this*a into tmp, including one reduction. */
+ for (int j = 0; j < LIMBS - 1; ++j) {
+ limb_t d0 = 0, d1 = 0, d2 = 0;
+ mul(d0, d1, this->limbs[1 + j], a.limbs[LIMBS + j - (1 + j)]);
+ for (int i = 2 + j; i < LIMBS; ++i) muladd3(d0, d1, d2, this->limbs[i], a.limbs[LIMBS + j - i]);
+ mulnadd3(c0, c1, c2, d0, d1, d2, MAX_PRIME_DIFF);
+ for (int i = 0; i < j + 1; ++i) muladd3(c0, c1, c2, this->limbs[i], a.limbs[j - i]);
+ extract3(c0, c1, c2, tmp.limbs[j]);
+ }
+
+ /* Compute limb N-1 of a*b into tmp. */
+ assert(c2 == 0);
+ for (int i = 0; i < LIMBS; ++i) muladd3(c0, c1, c2, this->limbs[i], a.limbs[LIMBS - 1 - i]);
+ extract3(c0, c1, c2, tmp.limbs[LIMBS - 1]);
+
+ /* Perform a second reduction. */
+ muln2(c0, c1, MAX_PRIME_DIFF);
+ for (int j = 0; j < LIMBS; ++j) {
+ addnextract2(c0, c1, tmp.limbs[j], this->limbs[j]);
+ }
+
+ assert(c1 == 0);
+ assert(c0 == 0 || c0 == 1);
+
+ /* Perform up to two more reductions if the internal state has already
+ * overflown the MAX of Num3072 or if it is larger than the modulus or
+ * if both are the case.
+ * */
+ if (this->IsOverflow()) this->FullReduce();
+ if (c0) this->FullReduce();
+}
+
+void Num3072::Square()
+{
+ limb_t c0 = 0, c1 = 0, c2 = 0;
+ Num3072 tmp;
+
+ /* Compute limbs 0..N-2 of this*this into tmp, including one reduction. */
+ for (int j = 0; j < LIMBS - 1; ++j) {
+ limb_t d0 = 0, d1 = 0, d2 = 0;
+ for (int i = 0; i < (LIMBS - 1 - j) / 2; ++i) muldbladd3(d0, d1, d2, this->limbs[i + j + 1], this->limbs[LIMBS - 1 - i]);
+ if ((j + 1) & 1) muladd3(d0, d1, d2, this->limbs[(LIMBS - 1 - j) / 2 + j + 1], this->limbs[LIMBS - 1 - (LIMBS - 1 - j) / 2]);
+ mulnadd3(c0, c1, c2, d0, d1, d2, MAX_PRIME_DIFF);
+ for (int i = 0; i < (j + 1) / 2; ++i) muldbladd3(c0, c1, c2, this->limbs[i], this->limbs[j - i]);
+ if ((j + 1) & 1) muladd3(c0, c1, c2, this->limbs[(j + 1) / 2], this->limbs[j - (j + 1) / 2]);
+ extract3(c0, c1, c2, tmp.limbs[j]);
+ }
+
+ assert(c2 == 0);
+ for (int i = 0; i < LIMBS / 2; ++i) muldbladd3(c0, c1, c2, this->limbs[i], this->limbs[LIMBS - 1 - i]);
+ extract3(c0, c1, c2, tmp.limbs[LIMBS - 1]);
+
+ /* Perform a second reduction. */
+ muln2(c0, c1, MAX_PRIME_DIFF);
+ for (int j = 0; j < LIMBS; ++j) {
+ addnextract2(c0, c1, tmp.limbs[j], this->limbs[j]);
+ }
+
+ assert(c1 == 0);
+ assert(c0 == 0 || c0 == 1);
+
+ /* Perform up to two more reductions if the internal state has already
+ * overflown the MAX of Num3072 or if it is larger than the modulus or
+ * if both are the case.
+ * */
+ if (this->IsOverflow()) this->FullReduce();
+ if (c0) this->FullReduce();
+}
+
+void Num3072::SetToOne()
+{
+ this->limbs[0] = 1;
+ for (int i = 1; i < LIMBS; ++i) this->limbs[i] = 0;
+}
+
+void Num3072::Divide(const Num3072& a)
+{
+ if (this->IsOverflow()) this->FullReduce();
+
+ Num3072 inv{};
+ if (a.IsOverflow()) {
+ Num3072 b = a;
+ b.FullReduce();
+ inv = b.GetInverse();
+ } else {
+ inv = a.GetInverse();
+ }
+
+ this->Multiply(inv);
+ if (this->IsOverflow()) this->FullReduce();
+}
+
+Num3072 MuHash3072::ToNum3072(Span<const unsigned char> in) {
+ Num3072 out{};
+ uint256 hashed_in = (CHashWriter(SER_DISK, 0) << in).GetSHA256();
+ unsigned char tmp[BYTE_SIZE];
+ ChaCha20(hashed_in.data(), hashed_in.size()).Keystream(tmp, BYTE_SIZE);
+ for (int i = 0; i < LIMBS; ++i) {
+ if (sizeof(limb_t) == 4) {
+ out.limbs[i] = ReadLE32(tmp + 4 * i);
+ } else if (sizeof(limb_t) == 8) {
+ out.limbs[i] = ReadLE64(tmp + 8 * i);
+ }
+ }
+ return out;
+}
+
+MuHash3072::MuHash3072(Span<const unsigned char> in) noexcept
+{
+ m_numerator = ToNum3072(in);
+}
+
+void MuHash3072::Finalize(uint256& out) noexcept
+{
+ m_numerator.Divide(m_denominator);
+ m_denominator.SetToOne(); // Needed to keep the MuHash object valid
+
+ unsigned char data[384];
+ for (int i = 0; i < LIMBS; ++i) {
+ if (sizeof(limb_t) == 4) {
+ WriteLE32(data + i * 4, m_numerator.limbs[i]);
+ } else if (sizeof(limb_t) == 8) {
+ WriteLE64(data + i * 8, m_numerator.limbs[i]);
+ }
+ }
+
+ out = (CHashWriter(SER_DISK, 0) << data).GetSHA256();
+}
+
+MuHash3072& MuHash3072::operator*=(const MuHash3072& mul) noexcept
+{
+ m_numerator.Multiply(mul.m_numerator);
+ m_denominator.Multiply(mul.m_denominator);
+ return *this;
+}
+
+MuHash3072& MuHash3072::operator/=(const MuHash3072& div) noexcept
+{
+ m_numerator.Multiply(div.m_denominator);
+ m_denominator.Multiply(div.m_numerator);
+ return *this;
+}
+
+MuHash3072& MuHash3072::Insert(Span<const unsigned char> in) noexcept {
+ m_numerator.Multiply(ToNum3072(in));
+ return *this;
+}
+
+MuHash3072& MuHash3072::Remove(Span<const unsigned char> in) noexcept {
+ m_numerator.Divide(ToNum3072(in));
+ return *this;
+}
diff --git a/src/crypto/muhash.h b/src/crypto/muhash.h
new file mode 100644
index 0000000000..0c710007c4
--- /dev/null
+++ b/src/crypto/muhash.h
@@ -0,0 +1,130 @@
+// Copyright (c) 2017-2020 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#ifndef BITCOIN_CRYPTO_MUHASH_H
+#define BITCOIN_CRYPTO_MUHASH_H
+
+#if defined(HAVE_CONFIG_H)
+#include <config/bitcoin-config.h>
+#endif
+
+#include <serialize.h>
+#include <uint256.h>
+
+#include <stdint.h>
+
+class Num3072
+{
+private:
+ void FullReduce();
+ bool IsOverflow() const;
+ Num3072 GetInverse() const;
+
+public:
+
+#ifdef HAVE___INT128
+ typedef unsigned __int128 double_limb_t;
+ typedef uint64_t limb_t;
+ static constexpr int LIMBS = 48;
+ static constexpr int LIMB_SIZE = 64;
+#else
+ typedef uint64_t double_limb_t;
+ typedef uint32_t limb_t;
+ static constexpr int LIMBS = 96;
+ static constexpr int LIMB_SIZE = 32;
+#endif
+ limb_t limbs[LIMBS];
+
+ // Sanity check for Num3072 constants
+ static_assert(LIMB_SIZE * LIMBS == 3072, "Num3072 isn't 3072 bits");
+ static_assert(sizeof(double_limb_t) == sizeof(limb_t) * 2, "bad size for double_limb_t");
+ static_assert(sizeof(limb_t) * 8 == LIMB_SIZE, "LIMB_SIZE is incorrect");
+
+ // Hard coded values in MuHash3072 constructor and Finalize
+ static_assert(sizeof(limb_t) == 4 || sizeof(limb_t) == 8, "bad size for limb_t");
+
+ void Multiply(const Num3072& a);
+ void Divide(const Num3072& a);
+ void SetToOne();
+ void Square();
+
+ Num3072() { this->SetToOne(); };
+
+ SERIALIZE_METHODS(Num3072, obj)
+ {
+ for (auto& limb : obj.limbs) {
+ READWRITE(limb);
+ }
+ }
+};
+
+/** A class representing MuHash sets
+ *
+ * MuHash is a hashing algorithm that supports adding set elements in any
+ * order but also deleting in any order. As a result, it can maintain a
+ * running sum for a set of data as a whole, and add/remove when data
+ * is added to or removed from it. A downside of MuHash is that computing
+ * an inverse is relatively expensive. This is solved by representing
+ * the running value as a fraction, and multiplying added elements into
+ * the numerator and removed elements into the denominator. Only when the
+ * final hash is desired, a single modular inverse and multiplication is
+ * needed to combine the two. The combination is also run on serialization
+ * to allow for space-efficient storage on disk.
+ *
+ * As the update operations are also associative, H(a)+H(b)+H(c)+H(d) can
+ * in fact be computed as (H(a)+H(b)) + (H(c)+H(d)). This implies that
+ * all of this is perfectly parallellizable: each thread can process an
+ * arbitrary subset of the update operations, allowing them to be
+ * efficiently combined later.
+ *
+ * Muhash does not support checking if an element is already part of the
+ * set. That is why this class does not enforce the use of a set as the
+ * data it represents because there is no efficient way to do so.
+ * It is possible to add elements more than once and also to remove
+ * elements that have not been added before. However, this implementation
+ * is intended to represent a set of elements.
+ *
+ * See also https://cseweb.ucsd.edu/~mihir/papers/inchash.pdf and
+ * https://lists.linuxfoundation.org/pipermail/bitcoin-dev/2017-May/014337.html.
+ */
+class MuHash3072
+{
+private:
+ static constexpr size_t BYTE_SIZE = 384;
+
+ Num3072 m_numerator;
+ Num3072 m_denominator;
+
+ Num3072 ToNum3072(Span<const unsigned char> in);
+
+public:
+ /* The empty set. */
+ MuHash3072() noexcept {};
+
+ /* A singleton with variable sized data in it. */
+ explicit MuHash3072(Span<const unsigned char> in) noexcept;
+
+ /* Insert a single piece of data into the set. */
+ MuHash3072& Insert(Span<const unsigned char> in) noexcept;
+
+ /* Remove a single piece of data from the set. */
+ MuHash3072& Remove(Span<const unsigned char> in) noexcept;
+
+ /* Multiply (resulting in a hash for the union of the sets) */
+ MuHash3072& operator*=(const MuHash3072& mul) noexcept;
+
+ /* Divide (resulting in a hash for the difference of the sets) */
+ MuHash3072& operator/=(const MuHash3072& div) noexcept;
+
+ /* Finalize into a 32-byte hash. Does not change this object's value. */
+ void Finalize(uint256& out) noexcept;
+
+ SERIALIZE_METHODS(MuHash3072, obj)
+ {
+ READWRITE(obj.m_numerator);
+ READWRITE(obj.m_denominator);
+ }
+};
+
+#endif // BITCOIN_CRYPTO_MUHASH_H
diff --git a/src/flatfile.cpp b/src/flatfile.cpp
index 8a8f7b681c..11cf357f3d 100644
--- a/src/flatfile.cpp
+++ b/src/flatfile.cpp
@@ -92,6 +92,7 @@ bool FlatFileSeq::Flush(const FlatFilePos& pos, bool finalize)
fclose(file);
return error("%s: failed to commit file %d", __func__, pos.nFile);
}
+ DirectoryCommit(m_dir);
fclose(file);
return true;
diff --git a/src/init.cpp b/src/init.cpp
index 42e9925f98..9a63fe0e03 100644
--- a/src/init.cpp
+++ b/src/init.cpp
@@ -26,6 +26,7 @@
#include <interfaces/chain.h>
#include <interfaces/node.h>
#include <key.h>
+#include <mapport.h>
#include <miner.h>
#include <net.h>
#include <net_permissions.h>
@@ -468,6 +469,11 @@ void SetupServerArgs(NodeContext& node)
#else
hidden_args.emplace_back("-upnp");
#endif
+#ifdef USE_NATPMP
+ argsman.AddArg("-natpmp", strprintf("Use NAT-PMP to map the listening port (default: %s)", DEFAULT_NATPMP ? "1 when listening and no -proxy" : "0"), ArgsManager::ALLOW_BOOL, OptionsCategory::CONNECTION);
+#else
+ hidden_args.emplace_back("-natpmp");
+#endif // USE_NATPMP
argsman.AddArg("-whitebind=<[permissions@]addr>", "Bind to the given address and add permission flags to the peers connecting to it. "
"Use [host]:port notation for IPv6. Allowed permissions: " + Join(NET_PERMISSIONS_DOC, ", ") + ". "
"Specify multiple permissions separated by commas (default: download,noban,mempool,relay). Can be specified multiple times.", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
@@ -812,10 +818,13 @@ void InitParameterInteraction(ArgsManager& args)
// to protect privacy, do not listen by default if a default proxy server is specified
if (args.SoftSetBoolArg("-listen", false))
LogPrintf("%s: parameter interaction: -proxy set -> setting -listen=0\n", __func__);
- // to protect privacy, do not use UPNP when a proxy is set. The user may still specify -listen=1
+ // to protect privacy, do not map ports when a proxy is set. The user may still specify -listen=1
// to listen locally, so don't rely on this happening through -listen below.
if (args.SoftSetBoolArg("-upnp", false))
LogPrintf("%s: parameter interaction: -proxy set -> setting -upnp=0\n", __func__);
+ if (args.SoftSetBoolArg("-natpmp", false)) {
+ LogPrintf("%s: parameter interaction: -proxy set -> setting -natpmp=0\n", __func__);
+ }
// to protect privacy, do not discover addresses by default
if (args.SoftSetBoolArg("-discover", false))
LogPrintf("%s: parameter interaction: -proxy set -> setting -discover=0\n", __func__);
@@ -825,6 +834,9 @@ void InitParameterInteraction(ArgsManager& args)
// do not map ports or try to retrieve public IP when not listening (pointless)
if (args.SoftSetBoolArg("-upnp", false))
LogPrintf("%s: parameter interaction: -listen=0 -> setting -upnp=0\n", __func__);
+ if (args.SoftSetBoolArg("-natpmp", false)) {
+ LogPrintf("%s: parameter interaction: -listen=0 -> setting -natpmp=0\n", __func__);
+ }
if (args.SoftSetBoolArg("-discover", false))
LogPrintf("%s: parameter interaction: -listen=0 -> setting -discover=0\n", __func__);
if (args.SoftSetBoolArg("-listenonion", false))
@@ -1898,10 +1910,8 @@ bool AppInitMain(const util::Ref& context, NodeContext& node, interfaces::BlockA
Discover();
- // Map ports with UPnP
- if (args.GetBoolArg("-upnp", DEFAULT_UPNP)) {
- StartMapPort();
- }
+ // Map ports with UPnP or NAT-PMP.
+ StartMapPort(args.GetBoolArg("-upnp", DEFAULT_UPNP), gArgs.GetBoolArg("-natpmp", DEFAULT_NATPMP));
CConnman::Options connOptions;
connOptions.nLocalServices = nLocalServices;
diff --git a/src/interfaces/node.h b/src/interfaces/node.h
index 97549aeb0e..15f7ef6256 100644
--- a/src/interfaces/node.h
+++ b/src/interfaces/node.h
@@ -82,7 +82,7 @@ public:
virtual bool shutdownRequested() = 0;
//! Map port.
- virtual void mapPort(bool use_upnp) = 0;
+ virtual void mapPort(bool use_upnp, bool use_natpmp) = 0;
//! Get proxy.
virtual bool getProxy(Network net, proxyType& proxy_info) = 0;
diff --git a/src/mapport.cpp b/src/mapport.cpp
new file mode 100644
index 0000000000..2df4ce45d2
--- /dev/null
+++ b/src/mapport.cpp
@@ -0,0 +1,336 @@
+// Copyright (c) 2011-2020 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#if defined(HAVE_CONFIG_H)
+#include <config/bitcoin-config.h>
+#endif
+
+#include <mapport.h>
+
+#include <clientversion.h>
+#include <logging.h>
+#include <net.h>
+#include <netaddress.h>
+#include <netbase.h>
+#include <threadinterrupt.h>
+#include <util/system.h>
+
+#ifdef USE_NATPMP
+#include <compat.h>
+#include <natpmp.h>
+#endif // USE_NATPMP
+
+#ifdef USE_UPNP
+#include <miniupnpc/miniupnpc.h>
+#include <miniupnpc/upnpcommands.h>
+#include <miniupnpc/upnperrors.h>
+// The minimum supported miniUPnPc API version is set to 10. This keeps compatibility
+// with Ubuntu 16.04 LTS and Debian 8 libminiupnpc-dev packages.
+static_assert(MINIUPNPC_API_VERSION >= 10, "miniUPnPc API version >= 10 assumed");
+#endif // USE_UPNP
+
+#include <atomic>
+#include <cassert>
+#include <chrono>
+#include <functional>
+#include <string>
+#include <thread>
+
+#if defined(USE_NATPMP) || defined(USE_UPNP)
+static CThreadInterrupt g_mapport_interrupt;
+static std::thread g_mapport_thread;
+static std::atomic_uint g_mapport_enabled_protos{MapPortProtoFlag::NONE};
+static std::atomic<MapPortProtoFlag> g_mapport_current_proto{MapPortProtoFlag::NONE};
+
+using namespace std::chrono_literals;
+static constexpr auto PORT_MAPPING_REANNOUNCE_PERIOD{20min};
+static constexpr auto PORT_MAPPING_RETRY_PERIOD{5min};
+
+#ifdef USE_NATPMP
+static uint16_t g_mapport_external_port = 0;
+static bool NatpmpInit(natpmp_t* natpmp)
+{
+ const int r_init = initnatpmp(natpmp, /* detect gateway automatically */ 0, /* forced gateway - NOT APPLIED*/ 0);
+ if (r_init == 0) return true;
+ LogPrintf("natpmp: initnatpmp() failed with %d error.\n", r_init);
+ return false;
+}
+
+static bool NatpmpDiscover(natpmp_t* natpmp, struct in_addr& external_ipv4_addr)
+{
+ const int r_send = sendpublicaddressrequest(natpmp);
+ if (r_send == 2 /* OK */) {
+ int r_read;
+ natpmpresp_t response;
+ do {
+ r_read = readnatpmpresponseorretry(natpmp, &response);
+ } while (r_read == NATPMP_TRYAGAIN);
+
+ if (r_read == 0) {
+ external_ipv4_addr = response.pnu.publicaddress.addr;
+ return true;
+ } else if (r_read == NATPMP_ERR_NOGATEWAYSUPPORT) {
+ LogPrintf("natpmp: The gateway does not support NAT-PMP.\n");
+ } else {
+ LogPrintf("natpmp: readnatpmpresponseorretry() for public address failed with %d error.\n", r_read);
+ }
+ } else {
+ LogPrintf("natpmp: sendpublicaddressrequest() failed with %d error.\n", r_send);
+ }
+
+ return false;
+}
+
+static bool NatpmpMapping(natpmp_t* natpmp, const struct in_addr& external_ipv4_addr, uint16_t private_port, bool& external_ip_discovered)
+{
+ const uint16_t suggested_external_port = g_mapport_external_port ? g_mapport_external_port : private_port;
+ const int r_send = sendnewportmappingrequest(natpmp, NATPMP_PROTOCOL_TCP, private_port, suggested_external_port, 3600 /*seconds*/);
+ if (r_send == 12 /* OK */) {
+ int r_read;
+ natpmpresp_t response;
+ do {
+ r_read = readnatpmpresponseorretry(natpmp, &response);
+ } while (r_read == NATPMP_TRYAGAIN);
+
+ if (r_read == 0) {
+ auto pm = response.pnu.newportmapping;
+ if (private_port == pm.privateport && pm.lifetime > 0) {
+ g_mapport_external_port = pm.mappedpublicport;
+ const CService external{external_ipv4_addr, pm.mappedpublicport};
+ if (!external_ip_discovered && fDiscover) {
+ AddLocal(external, LOCAL_MAPPED);
+ external_ip_discovered = true;
+ }
+ LogPrintf("natpmp: Port mapping successful. External address = %s\n", external.ToString());
+ return true;
+ } else {
+ LogPrintf("natpmp: Port mapping failed.\n");
+ }
+ } else if (r_read == NATPMP_ERR_NOGATEWAYSUPPORT) {
+ LogPrintf("natpmp: The gateway does not support NAT-PMP.\n");
+ } else {
+ LogPrintf("natpmp: readnatpmpresponseorretry() for port mapping failed with %d error.\n", r_read);
+ }
+ } else {
+ LogPrintf("natpmp: sendnewportmappingrequest() failed with %d error.\n", r_send);
+ }
+
+ return false;
+}
+
+static bool ProcessNatpmp()
+{
+ bool ret = false;
+ natpmp_t natpmp;
+ struct in_addr external_ipv4_addr;
+ if (NatpmpInit(&natpmp) && NatpmpDiscover(&natpmp, external_ipv4_addr)) {
+ bool external_ip_discovered = false;
+ const uint16_t private_port = GetListenPort();
+ do {
+ ret = NatpmpMapping(&natpmp, external_ipv4_addr, private_port, external_ip_discovered);
+ } while (ret && g_mapport_interrupt.sleep_for(PORT_MAPPING_REANNOUNCE_PERIOD));
+ g_mapport_interrupt.reset();
+
+ const int r_send = sendnewportmappingrequest(&natpmp, NATPMP_PROTOCOL_TCP, private_port, g_mapport_external_port, /* remove a port mapping */ 0);
+ g_mapport_external_port = 0;
+ if (r_send == 12 /* OK */) {
+ LogPrintf("natpmp: Port mapping removed successfully.\n");
+ } else {
+ LogPrintf("natpmp: sendnewportmappingrequest(0) failed with %d error.\n", r_send);
+ }
+ }
+
+ closenatpmp(&natpmp);
+ return ret;
+}
+#endif // USE_NATPMP
+
+#ifdef USE_UPNP
+static bool ProcessUpnp()
+{
+ bool ret = false;
+ std::string port = strprintf("%u", GetListenPort());
+ const char * multicastif = nullptr;
+ const char * minissdpdpath = nullptr;
+ struct UPNPDev * devlist = nullptr;
+ char lanaddr[64];
+
+ int error = 0;
+#if MINIUPNPC_API_VERSION < 14
+ devlist = upnpDiscover(2000, multicastif, minissdpdpath, 0, 0, &error);
+#else
+ devlist = upnpDiscover(2000, multicastif, minissdpdpath, 0, 0, 2, &error);
+#endif
+
+ struct UPNPUrls urls;
+ struct IGDdatas data;
+ int r;
+
+ r = UPNP_GetValidIGD(devlist, &urls, &data, lanaddr, sizeof(lanaddr));
+ if (r == 1)
+ {
+ if (fDiscover) {
+ char externalIPAddress[40];
+ r = UPNP_GetExternalIPAddress(urls.controlURL, data.first.servicetype, externalIPAddress);
+ if (r != UPNPCOMMAND_SUCCESS) {
+ LogPrintf("UPnP: GetExternalIPAddress() returned %d\n", r);
+ } else {
+ if (externalIPAddress[0]) {
+ CNetAddr resolved;
+ if (LookupHost(externalIPAddress, resolved, false)) {
+ LogPrintf("UPnP: ExternalIPAddress = %s\n", resolved.ToString());
+ AddLocal(resolved, LOCAL_MAPPED);
+ }
+ } else {
+ LogPrintf("UPnP: GetExternalIPAddress failed.\n");
+ }
+ }
+ }
+
+ std::string strDesc = PACKAGE_NAME " " + FormatFullVersion();
+
+ do {
+ r = UPNP_AddPortMapping(urls.controlURL, data.first.servicetype, port.c_str(), port.c_str(), lanaddr, strDesc.c_str(), "TCP", 0, "0");
+
+ if (r != UPNPCOMMAND_SUCCESS) {
+ ret = false;
+ LogPrintf("AddPortMapping(%s, %s, %s) failed with code %d (%s)\n", port, port, lanaddr, r, strupnperror(r));
+ break;
+ } else {
+ ret = true;
+ LogPrintf("UPnP Port Mapping successful.\n");
+ }
+ } while (g_mapport_interrupt.sleep_for(PORT_MAPPING_REANNOUNCE_PERIOD));
+ g_mapport_interrupt.reset();
+
+ r = UPNP_DeletePortMapping(urls.controlURL, data.first.servicetype, port.c_str(), "TCP", 0);
+ LogPrintf("UPNP_DeletePortMapping() returned: %d\n", r);
+ freeUPNPDevlist(devlist); devlist = nullptr;
+ FreeUPNPUrls(&urls);
+ } else {
+ LogPrintf("No valid UPnP IGDs found\n");
+ freeUPNPDevlist(devlist); devlist = nullptr;
+ if (r != 0)
+ FreeUPNPUrls(&urls);
+ }
+
+ return ret;
+}
+#endif // USE_UPNP
+
+static void ThreadMapPort()
+{
+ bool ok;
+ do {
+ ok = false;
+
+#ifdef USE_UPNP
+ // High priority protocol.
+ if (g_mapport_enabled_protos & MapPortProtoFlag::UPNP) {
+ g_mapport_current_proto = MapPortProtoFlag::UPNP;
+ ok = ProcessUpnp();
+ if (ok) continue;
+ }
+#endif // USE_UPNP
+
+#ifdef USE_NATPMP
+ // Low priority protocol.
+ if (g_mapport_enabled_protos & MapPortProtoFlag::NAT_PMP) {
+ g_mapport_current_proto = MapPortProtoFlag::NAT_PMP;
+ ok = ProcessNatpmp();
+ if (ok) continue;
+ }
+#endif // USE_NATPMP
+
+ g_mapport_current_proto = MapPortProtoFlag::NONE;
+ if (g_mapport_enabled_protos == MapPortProtoFlag::NONE) {
+ return;
+ }
+
+ } while (ok || g_mapport_interrupt.sleep_for(PORT_MAPPING_RETRY_PERIOD));
+}
+
+void StartThreadMapPort()
+{
+ if (!g_mapport_thread.joinable()) {
+ assert(!g_mapport_interrupt);
+ g_mapport_thread = std::thread(std::bind(&TraceThread<void (*)()>, "mapport", &ThreadMapPort));
+ }
+}
+
+static void DispatchMapPort()
+{
+ if (g_mapport_current_proto == MapPortProtoFlag::NONE && g_mapport_enabled_protos == MapPortProtoFlag::NONE) {
+ return;
+ }
+
+ if (g_mapport_current_proto == MapPortProtoFlag::NONE && g_mapport_enabled_protos != MapPortProtoFlag::NONE) {
+ StartThreadMapPort();
+ return;
+ }
+
+ if (g_mapport_current_proto != MapPortProtoFlag::NONE && g_mapport_enabled_protos == MapPortProtoFlag::NONE) {
+ InterruptMapPort();
+ StopMapPort();
+ return;
+ }
+
+ if (g_mapport_enabled_protos & g_mapport_current_proto) {
+ // Enabling another protocol does not cause switching from the currently used one.
+ return;
+ }
+
+ assert(g_mapport_thread.joinable());
+ assert(!g_mapport_interrupt);
+ // Interrupt a protocol-specific loop in the ThreadUpnp() or in the ThreadNatpmp()
+ // to force trying the next protocol in the ThreadMapPort() loop.
+ g_mapport_interrupt();
+}
+
+static void MapPortProtoSetEnabled(MapPortProtoFlag proto, bool enabled)
+{
+ if (enabled) {
+ g_mapport_enabled_protos |= proto;
+ } else {
+ g_mapport_enabled_protos &= ~proto;
+ }
+}
+
+void StartMapPort(bool use_upnp, bool use_natpmp)
+{
+ MapPortProtoSetEnabled(MapPortProtoFlag::UPNP, use_upnp);
+ MapPortProtoSetEnabled(MapPortProtoFlag::NAT_PMP, use_natpmp);
+ DispatchMapPort();
+}
+
+void InterruptMapPort()
+{
+ g_mapport_enabled_protos = MapPortProtoFlag::NONE;
+ if (g_mapport_thread.joinable()) {
+ g_mapport_interrupt();
+ }
+}
+
+void StopMapPort()
+{
+ if (g_mapport_thread.joinable()) {
+ g_mapport_thread.join();
+ g_mapport_interrupt.reset();
+ }
+}
+
+#else // #if defined(USE_NATPMP) || defined(USE_UPNP)
+void StartMapPort(bool use_upnp, bool use_natpmp)
+{
+ // Intentionally left blank.
+}
+void InterruptMapPort()
+{
+ // Intentionally left blank.
+}
+void StopMapPort()
+{
+ // Intentionally left blank.
+}
+#endif // #if defined(USE_NATPMP) || defined(USE_UPNP)
diff --git a/src/mapport.h b/src/mapport.h
new file mode 100644
index 0000000000..279d65167f
--- /dev/null
+++ b/src/mapport.h
@@ -0,0 +1,30 @@
+// Copyright (c) 2011-2020 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#ifndef BITCOIN_MAPPORT_H
+#define BITCOIN_MAPPORT_H
+
+#ifdef USE_UPNP
+static constexpr bool DEFAULT_UPNP = USE_UPNP;
+#else
+static constexpr bool DEFAULT_UPNP = false;
+#endif // USE_UPNP
+
+#ifdef USE_NATPMP
+static constexpr bool DEFAULT_NATPMP = USE_NATPMP;
+#else
+static constexpr bool DEFAULT_NATPMP = false;
+#endif // USE_NATPMP
+
+enum MapPortProtoFlag : unsigned int {
+ NONE = 0x00,
+ UPNP = 0x01,
+ NAT_PMP = 0x02,
+};
+
+void StartMapPort(bool use_upnp, bool use_natpmp);
+void InterruptMapPort();
+void StopMapPort();
+
+#endif // BITCOIN_MAPPORT_H
diff --git a/src/net.cpp b/src/net.cpp
index ad4af4abb2..31960664e3 100644
--- a/src/net.cpp
+++ b/src/net.cpp
@@ -33,15 +33,6 @@
#include <poll.h>
#endif
-#ifdef USE_UPNP
-#include <miniupnpc/miniupnpc.h>
-#include <miniupnpc/upnpcommands.h>
-#include <miniupnpc/upnperrors.h>
-// The minimum supported miniUPnPc API version is set to 10. This keeps compatibility
-// with Ubuntu 16.04 LTS and Debian 8 libminiupnpc-dev packages.
-static_assert(MINIUPNPC_API_VERSION >= 10, "miniUPnPc API version >= 10 assumed");
-#endif
-
#include <algorithm>
#include <cstdint>
#include <unordered_map>
@@ -507,9 +498,9 @@ void CConnman::AddWhitelistPermissionFlags(NetPermissionFlags& flags, const CNet
}
}
-std::string CNode::ConnectionTypeAsString() const
+std::string ConnectionTypeAsString(ConnectionType conn_type)
{
- switch (m_conn_type) {
+ switch (conn_type) {
case ConnectionType::INBOUND:
return "inbound";
case ConnectionType::MANUAL:
@@ -627,7 +618,7 @@ void CNode::copyStats(CNodeStats &stats, const std::vector<bool> &m_asmap)
CService addrLocalUnlocked = GetAddrLocal();
stats.addrLocal = addrLocalUnlocked.IsValid() ? addrLocalUnlocked.ToString() : "";
- stats.m_conn_type_string = ConnectionTypeAsString();
+ X(m_conn_type);
}
#undef X
@@ -1539,121 +1530,6 @@ void CConnman::WakeMessageHandler()
condMsgProc.notify_one();
}
-
-
-
-
-
-#ifdef USE_UPNP
-static CThreadInterrupt g_upnp_interrupt;
-static std::thread g_upnp_thread;
-static void ThreadMapPort()
-{
- std::string port = strprintf("%u", GetListenPort());
- const char * multicastif = nullptr;
- const char * minissdpdpath = nullptr;
- struct UPNPDev * devlist = nullptr;
- char lanaddr[64];
-
- int error = 0;
-#if MINIUPNPC_API_VERSION < 14
- devlist = upnpDiscover(2000, multicastif, minissdpdpath, 0, 0, &error);
-#else
- devlist = upnpDiscover(2000, multicastif, minissdpdpath, 0, 0, 2, &error);
-#endif
-
- struct UPNPUrls urls;
- struct IGDdatas data;
- int r;
-
- r = UPNP_GetValidIGD(devlist, &urls, &data, lanaddr, sizeof(lanaddr));
- if (r == 1)
- {
- if (fDiscover) {
- char externalIPAddress[40];
- r = UPNP_GetExternalIPAddress(urls.controlURL, data.first.servicetype, externalIPAddress);
- if (r != UPNPCOMMAND_SUCCESS) {
- LogPrintf("UPnP: GetExternalIPAddress() returned %d\n", r);
- } else {
- if (externalIPAddress[0]) {
- CNetAddr resolved;
- if (LookupHost(externalIPAddress, resolved, false)) {
- LogPrintf("UPnP: ExternalIPAddress = %s\n", resolved.ToString());
- AddLocal(resolved, LOCAL_UPNP);
- }
- } else {
- LogPrintf("UPnP: GetExternalIPAddress failed.\n");
- }
- }
- }
-
- std::string strDesc = PACKAGE_NAME " " + FormatFullVersion();
-
- do {
- r = UPNP_AddPortMapping(urls.controlURL, data.first.servicetype, port.c_str(), port.c_str(), lanaddr, strDesc.c_str(), "TCP", 0, "0");
-
- if (r != UPNPCOMMAND_SUCCESS) {
- LogPrintf("AddPortMapping(%s, %s, %s) failed with code %d (%s)\n", port, port, lanaddr, r, strupnperror(r));
- } else {
- LogPrintf("UPnP Port Mapping successful.\n");
- }
- } while (g_upnp_interrupt.sleep_for(std::chrono::minutes(20)));
-
- r = UPNP_DeletePortMapping(urls.controlURL, data.first.servicetype, port.c_str(), "TCP", 0);
- LogPrintf("UPNP_DeletePortMapping() returned: %d\n", r);
- freeUPNPDevlist(devlist); devlist = nullptr;
- FreeUPNPUrls(&urls);
- } else {
- LogPrintf("No valid UPnP IGDs found\n");
- freeUPNPDevlist(devlist); devlist = nullptr;
- if (r != 0)
- FreeUPNPUrls(&urls);
- }
-}
-
-void StartMapPort()
-{
- if (!g_upnp_thread.joinable()) {
- assert(!g_upnp_interrupt);
- g_upnp_thread = std::thread((std::bind(&TraceThread<void (*)()>, "upnp", &ThreadMapPort)));
- }
-}
-
-void InterruptMapPort()
-{
- if(g_upnp_thread.joinable()) {
- g_upnp_interrupt();
- }
-}
-
-void StopMapPort()
-{
- if(g_upnp_thread.joinable()) {
- g_upnp_thread.join();
- g_upnp_interrupt.reset();
- }
-}
-
-#else
-void StartMapPort()
-{
- // Intentionally left blank.
-}
-void InterruptMapPort()
-{
- // Intentionally left blank.
-}
-void StopMapPort()
-{
- // Intentionally left blank.
-}
-#endif
-
-
-
-
-
-
void CConnman::ThreadDNSAddressSeed()
{
FastRandomContext rng;
diff --git a/src/net.h b/src/net.h
index 8ae184e473..357b4cc40d 100644
--- a/src/net.h
+++ b/src/net.h
@@ -67,12 +67,6 @@ static const int MAX_BLOCK_RELAY_ONLY_CONNECTIONS = 2;
static const int MAX_FEELER_CONNECTIONS = 1;
/** -listen default */
static const bool DEFAULT_LISTEN = true;
-/** -upnp default */
-#ifdef USE_UPNP
-static const bool DEFAULT_UPNP = USE_UPNP;
-#else
-static const bool DEFAULT_UPNP = false;
-#endif
/** The maximum number of peer connections to maintain. */
static const unsigned int DEFAULT_MAX_PEER_CONNECTIONS = 125;
/** The default for -maxuploadtarget. 0 = Unlimited */
@@ -180,10 +174,9 @@ enum class ConnectionType {
ADDR_FETCH,
};
+/** Convert ConnectionType enum to a string value */
+std::string ConnectionTypeAsString(ConnectionType conn_type);
void Discover();
-void StartMapPort();
-void InterruptMapPort();
-void StopMapPort();
uint16_t GetListenPort();
enum
@@ -191,7 +184,7 @@ enum
LOCAL_NONE, // unknown
LOCAL_IF, // address a local interface listens on
LOCAL_BIND, // address explicit bound to
- LOCAL_UPNP, // address reported by UPnP
+ LOCAL_MAPPED, // address reported by UPnP or NAT-PMP
LOCAL_MANUAL, // address explicitly specified (-externalip=)
LOCAL_MAX
@@ -273,11 +266,10 @@ public:
// Network the peer connected through
Network m_network;
uint32_t m_mapped_as;
- std::string m_conn_type_string;
+ ConnectionType m_conn_type;
};
-
/** Transport protocol agnostic message container.
* Ideally it should only contain receive time, payload,
* command and size.
@@ -764,7 +756,7 @@ public:
//! Sets the addrName only if it was not previously set
void MaybeSetAddrName(const std::string& addrNameIn);
- std::string ConnectionTypeAsString() const;
+ std::string ConnectionTypeAsString() const { return ::ConnectionTypeAsString(m_conn_type); }
/** Whether this peer is an inbound onion, e.g. connected via our Tor onion service. */
bool IsInboundOnion() const { return m_inbound_onion; }
diff --git a/src/node/interfaces.cpp b/src/node/interfaces.cpp
index 317a5c7cbe..e07eaa33d8 100644
--- a/src/node/interfaces.cpp
+++ b/src/node/interfaces.cpp
@@ -12,6 +12,7 @@
#include <interfaces/handler.h>
#include <interfaces/node.h>
#include <interfaces/wallet.h>
+#include <mapport.h>
#include <net.h>
#include <net_processing.h>
#include <netaddress.h>
@@ -93,15 +94,7 @@ public:
}
}
bool shutdownRequested() override { return ShutdownRequested(); }
- void mapPort(bool use_upnp) override
- {
- if (use_upnp) {
- StartMapPort();
- } else {
- InterruptMapPort();
- StopMapPort();
- }
- }
+ void mapPort(bool use_upnp, bool use_natpmp) override { StartMapPort(use_upnp, use_natpmp); }
bool getProxy(Network net, proxyType& proxy_info) override { return GetProxy(net, proxy_info); }
size_t getNodeCount(CConnman::NumConnections flags) override
{
diff --git a/src/qt/forms/optionsdialog.ui b/src/qt/forms/optionsdialog.ui
index 88944a58a6..8181cc47e2 100644
--- a/src/qt/forms/optionsdialog.ui
+++ b/src/qt/forms/optionsdialog.ui
@@ -260,6 +260,16 @@
</widget>
</item>
<item>
+ <widget class="QCheckBox" name="mapPortNatpmp">
+ <property name="toolTip">
+ <string>Automatically open the Bitcoin client port on the router. This only works when your router supports NAT-PMP and it is enabled. The external port could be random.</string>
+ </property>
+ <property name="text">
+ <string>Map port using NA&amp;T-PMP</string>
+ </property>
+ </widget>
+ </item>
+ <item>
<widget class="QCheckBox" name="allowIncoming">
<property name="toolTip">
<string>Accept connections from outside.</string>
diff --git a/src/qt/optionsdialog.cpp b/src/qt/optionsdialog.cpp
index 468008693a..416b9c83c9 100644
--- a/src/qt/optionsdialog.cpp
+++ b/src/qt/optionsdialog.cpp
@@ -24,6 +24,7 @@
#include <QIntValidator>
#include <QLocale>
#include <QMessageBox>
+#include <QSettings>
#include <QSystemTrayIcon>
#include <QTimer>
@@ -50,6 +51,13 @@ OptionsDialog::OptionsDialog(QWidget *parent, bool enableWallet) :
#ifndef USE_UPNP
ui->mapPortUpnp->setEnabled(false);
#endif
+#ifndef USE_NATPMP
+ ui->mapPortNatpmp->setEnabled(false);
+#endif
+ connect(this, &QDialog::accepted, [this](){
+ QSettings settings;
+ model->node().mapPort(settings.value("fUseUPnP").toBool(), settings.value("fUseNatpmp").toBool());
+ });
ui->proxyIp->setEnabled(false);
ui->proxyPort->setEnabled(false);
@@ -214,6 +222,7 @@ void OptionsDialog::setMapper()
/* Network */
mapper->addMapping(ui->mapPortUpnp, OptionsModel::MapPortUPnP);
+ mapper->addMapping(ui->mapPortNatpmp, OptionsModel::MapPortNatpmp);
mapper->addMapping(ui->allowIncoming, OptionsModel::Listen);
mapper->addMapping(ui->connectSocks, OptionsModel::ProxyUse);
diff --git a/src/qt/optionsmodel.cpp b/src/qt/optionsmodel.cpp
index 152de6decb..1e0391a35c 100644
--- a/src/qt/optionsmodel.cpp
+++ b/src/qt/optionsmodel.cpp
@@ -13,11 +13,12 @@
#include <qt/guiutil.h>
#include <interfaces/node.h>
-#include <validation.h> // For DEFAULT_SCRIPTCHECK_THREADS
+#include <mapport.h>
#include <net.h>
#include <netbase.h>
-#include <txdb.h> // for -dbcache defaults
+#include <txdb.h> // for -dbcache defaults
#include <util/string.h>
+#include <validation.h> // For DEFAULT_SCRIPTCHECK_THREADS
#include <QDebug>
#include <QSettings>
@@ -123,6 +124,13 @@ void OptionsModel::Init(bool resetSettings)
if (!gArgs.SoftSetBoolArg("-upnp", settings.value("fUseUPnP").toBool()))
addOverriddenOption("-upnp");
+ if (!settings.contains("fUseNatpmp")) {
+ settings.setValue("fUseNatpmp", DEFAULT_NATPMP);
+ }
+ if (!gArgs.SoftSetBoolArg("-natpmp", settings.value("fUseNatpmp").toBool())) {
+ addOverriddenOption("-natpmp");
+ }
+
if (!settings.contains("fListen"))
settings.setValue("fListen", DEFAULT_LISTEN);
if (!gArgs.SoftSetBoolArg("-listen", settings.value("fListen").toBool()))
@@ -282,7 +290,13 @@ QVariant OptionsModel::data(const QModelIndex & index, int role) const
return settings.value("fUseUPnP");
#else
return false;
-#endif
+#endif // USE_UPNP
+ case MapPortNatpmp:
+#ifdef USE_NATPMP
+ return settings.value("fUseNatpmp");
+#else
+ return false;
+#endif // USE_NATPMP
case MinimizeOnClose:
return fMinimizeOnClose;
@@ -354,7 +368,9 @@ bool OptionsModel::setData(const QModelIndex & index, const QVariant & value, in
break;
case MapPortUPnP: // core option - can be changed on-the-fly
settings.setValue("fUseUPnP", value.toBool());
- node().mapPort(value.toBool());
+ break;
+ case MapPortNatpmp: // core option - can be changed on-the-fly
+ settings.setValue("fUseNatpmp", value.toBool());
break;
case MinimizeOnClose:
fMinimizeOnClose = value.toBool();
diff --git a/src/qt/optionsmodel.h b/src/qt/optionsmodel.h
index f1929a5e06..f7171951a1 100644
--- a/src/qt/optionsmodel.h
+++ b/src/qt/optionsmodel.h
@@ -48,6 +48,7 @@ public:
ShowTrayIcon, // bool
MinimizeToTray, // bool
MapPortUPnP, // bool
+ MapPortNatpmp, // bool
MinimizeOnClose, // bool
ProxyUse, // bool
ProxyIP, // QString
diff --git a/src/rpc/net.cpp b/src/rpc/net.cpp
index 89ddfb35cb..ddbd7e9142 100644
--- a/src/rpc/net.cpp
+++ b/src/rpc/net.cpp
@@ -249,7 +249,7 @@ static RPCHelpMan getpeerinfo()
recvPerMsgCmd.pushKV(i.first, i.second);
}
obj.pushKV("bytesrecv_per_msg", recvPerMsgCmd);
- obj.pushKV("connection_type", stats.m_conn_type_string);
+ obj.pushKV("connection_type", ConnectionTypeAsString(stats.m_conn_type));
ret.push_back(obj);
}
diff --git a/src/test/crypto_tests.cpp b/src/test/crypto_tests.cpp
index 0ad5066603..7358b246b6 100644
--- a/src/test/crypto_tests.cpp
+++ b/src/test/crypto_tests.cpp
@@ -14,7 +14,9 @@
#include <crypto/sha256.h>
#include <crypto/sha3.h>
#include <crypto/sha512.h>
+#include <crypto/muhash.h>
#include <random.h>
+#include <streams.h>
#include <test/util/setup_common.h>
#include <util/strencodings.h>
@@ -857,4 +859,92 @@ BOOST_AUTO_TEST_CASE(sha3_256_tests)
TestSHA3_256("72c57c359e10684d0517e46653a02d18d29eff803eb009e4d5eb9e95add9ad1a4ac1f38a70296f3a369a16985ca3c957de2084cdc9bdd8994eb59b8815e0debad4ec1f001feac089820db8becdaf896aaf95721e8674e5d476b43bd2b873a7d135cd685f545b438210f9319e4dcd55986c85303c1ddf18dc746fe63a409df0a998ed376eb683e16c09e6e9018504152b3e7628ef350659fb716e058a5263a18823d2f2f6ee6a8091945a48ae1c5cb1694cf2c1fe76ef9177953afe8899cfa2b7fe0603bfa3180937dadfb66fbbdd119bbf8063338aa4a699075a3bfdbae8db7e5211d0917e9665a702fc9b0a0a901d08bea97654162d82a9f05622b060b634244779c33427eb7a29353a5f48b07cbefa72f3622ac5900bef77b71d6b314296f304c8426f451f32049b1f6af156a9dab702e8907d3cd72bb2c50493f4d593e731b285b70c803b74825b3524cda3205a8897106615260ac93c01c5ec14f5b11127783989d1824527e99e04f6a340e827b559f24db9292fcdd354838f9339a5fa1d7f6b2087f04835828b13463dd40927866f16ae33ed501ec0e6c4e63948768c5aeea3e4f6754985954bea7d61088c44430204ef491b74a64bde1358cecb2cad28ee6a3de5b752ff6a051104d88478653339457ac45ba44cbb65f54d1969d047cda746931d5e6a8b48e211416aefd5729f3d60b56b54e7f85aa2f42de3cb69419240c24e67139a11790a709edef2ac52cf35dd0a08af45926ebe9761f498ff83bfe263d6897ee97943a4b982fe3404ef0b4a45e06113c60340e0664f14799bf59cb4b3934b465fabefd87155905ee5309ba41e9e402973311831ea600b16437f71df39ee77130490c4d0227e5d1757fdc66af3ae6b9953053ed9aafca0160209858a7d4dd38fe10e0cb153672d08633ed6c54977aa0a6e67f9ff2f8c9d22dd7b21de08192960fd0e0da68d77c8d810db11dcaa61c725cd4092cbff76c8e1debd8d0361bb3f2e607911d45716f53067bdc0d89dd4889177765166a424e9fc0cb711201099dda213355e6639ac7eb86eca2ae0ab38b7f674f37ef8a6fcca1a6f52f55d9e1dcd631d2c3c82bba129172feb991d5af51afecd9d61a88b6832e4107480e392aed61a8644f551665ebff6b20953b635737a4f895e429fddcfe801f606fbda74b3bf6f5767d0fac14907fcfd0aa1d4c11b9e91b01d68052399b51a29f1ae6acd965109977c14a555cbcbd21ad8cb9f8853506d4bc21c01e62d61d7b21be1b923be54914e6b0a7ca84dd11f1159193e1184568a6134a6bbadf5b4df986edcf2019390ae841cfaa44435e28ce877d3dae4177992fa5d4e5c005876dbe3d1e63bec7dcc0942762b48b1ecc6c1a918409a8a72812a1e245c0c67be6e729c2b49bc6ee4d24a8f63e78e75db45655c26a9a78aff36fcd67117f26b8f654dca664b9f0e30681874cb749e1a692720078856286c2560b0292cc837933423147569350955c9571bf8941ba128fd339cb4268f46b94bc6ee203eb7026813706ea51c4f24c91866fc23a724bf2501327e6ae89c29f8db315dc28d2c7c719514036367e018f4835f63fdecd71f9bdced7132b6c4f8b13c69a517026fcd3622d67cb632320d5e7308f78f4b7cea11f6291b137851dc6cd6366f2785c71c3f237f81a7658b2a8d512b61e0ad5a4710b7b124151689fcb2116063fbff7e9115fed7b93de834970b838e49f8f8ba5f1f874c354078b5810a55ae289a56da563f1da6cd80a3757d6073fa55e016e45ac6cec1f69d871c92fd0ae9670c74249045e6b464787f9504128736309fed205f8df4d90e332908581298d9c75a3fa36ab0c3c9272e62de53ab290c803d67b696fd615c260a47bffad16746f18ba1a10a061bacbea9369693b3c042eec36bed289d7d12e52bca8aa1c2dff88ca7816498d25626d0f1e106ebb0b4a12138e00f3df5b1c2f49d98b1756e69b641b7c6353d99dbff050f4d76842c6cf1c2a4b062fc8e6336fa689b7c9d5c6b4ab8c15a5c20e514ff070a602d85ae52fa7810c22f8eeffd34a095b93342144f7a98d024216b3d68ed7bea047517bfcd83ec83febd1ba0e5858e2bdc1d8b1f7b0f89e90ccc432a3f930cb8209462e64556c5054c56ca2a85f16b32eb83a10459d13516faa4d23302b7607b9bd38dab2239ac9e9440c314433fdfb3ceadab4b4f87415ed6f240e017221f3b5f7ac196cdf54957bec42fe6893994b46de3d27dc7fb58ca88feb5b9e79cf20053d12530ac524337b22a3629bea52f40b06d3e2128f32060f9105847daed81d35f20e2002817434659baff64494c5b5c7f9216bfda38412a0f70511159dc73bb6bae1f8eaa0ef08d99bcb31f94f6be12c29c83df45926430b366c99fca3270c15fc4056398fdf3135b7779e3066a006961d1ac0ad1c83179ce39e87a96b722ec23aabc065badf3e188347a360772ca6a447abac7e6a44f0d4632d52926332e44a0a86bff5ce699fd063bdda3ffd4c41b53ded49fecec67f40599b934e16e3fd1bc063ad7026f8d71bfd4cbaf56599586774723194b692036f1b6bb242e2ffb9c600b5215b412764599476ce475c9e5b396fbcebd6be323dcf4d0048077400aac7500db41dc95fc7f7edbe7c9c2ec5ea89943fe13b42217eef530bbd023671509e12dfce4e1c1c82955d965e6a68aa66f6967dba48feda572db1f099d9a6dc4bc8edade852b5e824a06890dc48a6a6510ecaf8cf7620d757290e3166d431abecc624fa9ac2234d2eb783308ead45544910c633a94964b2ef5fbc409cb8835ac4147d384e12e0a5e13951f7de0ee13eafcb0ca0c04946d7804040c0a3cd088352424b097adb7aad1ca4495952f3e6c0158c02d2bcec33bfda69301434a84d9027ce02c0b9725dad118", "d894b86261436362e64241e61f6b3e6589daf64dc641f60570c4c0bf3b1f2ca3");
}
+static MuHash3072 FromInt(unsigned char i) {
+ unsigned char tmp[32] = {i, 0};
+ return MuHash3072(tmp);
+}
+
+BOOST_AUTO_TEST_CASE(muhash_tests)
+{
+ uint256 out;
+
+ for (int iter = 0; iter < 10; ++iter) {
+ uint256 res;
+ int table[4];
+ for (int i = 0; i < 4; ++i) {
+ table[i] = g_insecure_rand_ctx.randbits(3);
+ }
+ for (int order = 0; order < 4; ++order) {
+ MuHash3072 acc;
+ for (int i = 0; i < 4; ++i) {
+ int t = table[i ^ order];
+ if (t & 4) {
+ acc /= FromInt(t & 3);
+ } else {
+ acc *= FromInt(t & 3);
+ }
+ }
+ acc.Finalize(out);
+ if (order == 0) {
+ res = out;
+ } else {
+ BOOST_CHECK(res == out);
+ }
+ }
+
+ MuHash3072 x = FromInt(g_insecure_rand_ctx.randbits(4)); // x=X
+ MuHash3072 y = FromInt(g_insecure_rand_ctx.randbits(4)); // x=X, y=Y
+ MuHash3072 z; // x=X, y=Y, z=1
+ z *= x; // x=X, y=Y, z=X
+ z *= y; // x=X, y=Y, z=X*Y
+ y *= x; // x=X, y=Y*X, z=X*Y
+ z /= y; // x=X, y=Y*X, z=1
+ z.Finalize(out);
+
+ uint256 out2;
+ MuHash3072 a;
+ a.Finalize(out2);
+
+ BOOST_CHECK_EQUAL(out, out2);
+ }
+
+ MuHash3072 acc = FromInt(0);
+ acc *= FromInt(1);
+ acc /= FromInt(2);
+ acc.Finalize(out);
+ BOOST_CHECK_EQUAL(out, uint256S("10d312b100cbd32ada024a6646e40d3482fcff103668d2625f10002a607d5863"));
+
+ MuHash3072 acc2 = FromInt(0);
+ unsigned char tmp[32] = {1, 0};
+ acc2.Insert(tmp);
+ unsigned char tmp2[32] = {2, 0};
+ acc2.Remove(tmp2);
+ acc2.Finalize(out);
+ BOOST_CHECK_EQUAL(out, uint256S("10d312b100cbd32ada024a6646e40d3482fcff103668d2625f10002a607d5863"));
+
+ // Test MuHash3072 serialization
+ MuHash3072 serchk = FromInt(1); serchk *= FromInt(2);
+ std::string ser_exp = "1fa093295ea30a6a3acdc7b3f770fa538eff537528e990e2910e40bbcfd7f6696b1256901929094694b56316de342f593303dd12ac43e06dce1be1ff8301c845beb15468fff0ef002dbf80c29f26e6452bccc91b5cb9437ad410d2a67ea847887fa3c6a6553309946880fe20db2c73fe0641adbd4e86edfee0d9f8cd0ee1230898873dc13ed8ddcaf045c80faa082774279007a2253f8922ee3ef361d378a6af3ddaf180b190ac97e556888c36b3d1fb1c85aab9ccd46e3deaeb7b7cf5db067a7e9ff86b658cf3acd6662bbcce37232daa753c48b794356c020090c831a8304416e2aa7ad633c0ddb2f11be1be316a81be7f7e472071c042cb68faef549c221ebff209273638b741aba5a81675c45a5fa92fea4ca821d7a324cb1e1a2ccd3b76c4228ec8066dad2a5df6e1bd0de45c7dd5de8070bdb46db6c554cf9aefc9b7b2bbf9f75b1864d9f95005314593905c0109b71f703d49944ae94477b51dac10a816bb6d1c700bafabc8bd86fac8df24be519a2f2836b16392e18036cb13e48c5c010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000";
+ CDataStream ss_chk(SER_DISK, PROTOCOL_VERSION);
+ ss_chk << serchk;
+ BOOST_CHECK_EQUAL(ser_exp, HexStr(ss_chk.str()));
+
+ // Test MuHash3072 deserialization
+ MuHash3072 deserchk;
+ ss_chk >> deserchk;
+ uint256 out3;
+ serchk.Finalize(out);
+ deserchk.Finalize(out3);
+ BOOST_CHECK_EQUAL(HexStr(out), HexStr(out3));
+
+ // Test MuHash3072 overflow, meaning the internal data is larger than the modulus.
+ CDataStream ss_max(ParseHex("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"), SER_DISK, PROTOCOL_VERSION);
+ MuHash3072 overflowchk;
+ ss_max >> overflowchk;
+
+ uint256 out4;
+ overflowchk.Finalize(out4);
+ BOOST_CHECK_EQUAL(HexStr(out4), "3a31e6903aff0de9f62f9a9f7f8b861de76ce2cda09822b90014319ae5dc2271");
+}
+
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/fuzz/crypto.cpp b/src/test/fuzz/crypto.cpp
index 4783cc1c43..9668b84e7b 100644
--- a/src/test/fuzz/crypto.cpp
+++ b/src/test/fuzz/crypto.cpp
@@ -4,6 +4,7 @@
#include <crypto/hmac_sha256.h>
#include <crypto/hmac_sha512.h>
+#include <crypto/muhash.h>
#include <crypto/ripemd160.h>
#include <crypto/sha1.h>
#include <crypto/sha256.h>
@@ -35,6 +36,7 @@ FUZZ_TARGET(crypto)
CSHA512 sha512;
SHA3_256 sha3;
CSipHasher sip_hasher{fuzzed_data_provider.ConsumeIntegral<uint64_t>(), fuzzed_data_provider.ConsumeIntegral<uint64_t>()};
+ MuHash3072 muhash;
while (fuzzed_data_provider.ConsumeBool()) {
switch (fuzzed_data_provider.ConsumeIntegralInRange<int>(0, 2)) {
@@ -60,6 +62,12 @@ FUZZ_TARGET(crypto)
(void)Hash(data);
(void)Hash160(data);
(void)sha512.Size();
+
+ if (fuzzed_data_provider.ConsumeBool()) {
+ muhash *= MuHash3072(data);
+ } else {
+ muhash /= MuHash3072(data);
+ }
break;
}
case 1: {
@@ -70,10 +78,11 @@ FUZZ_TARGET(crypto)
(void)sha256.Reset();
(void)sha3.Reset();
(void)sha512.Reset();
+ muhash = MuHash3072();
break;
}
case 2: {
- switch (fuzzed_data_provider.ConsumeIntegralInRange<int>(0, 9)) {
+ switch (fuzzed_data_provider.ConsumeIntegralInRange<int>(0, 10)) {
case 0: {
data.resize(CHash160::OUTPUT_SIZE);
hash160.Finalize(data);
@@ -124,6 +133,11 @@ FUZZ_TARGET(crypto)
sha3.Finalize(data);
break;
}
+ case 10: {
+ uint256 out;
+ muhash.Finalize(out);
+ break;
+ }
}
break;
}
diff --git a/src/test/fuzz/muhash.cpp b/src/test/fuzz/muhash.cpp
new file mode 100644
index 0000000000..8f843ca773
--- /dev/null
+++ b/src/test/fuzz/muhash.cpp
@@ -0,0 +1,53 @@
+// Copyright (c) 2020 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 <crypto/muhash.h>
+#include <test/fuzz/FuzzedDataProvider.h>
+#include <test/fuzz/fuzz.h>
+#include <test/fuzz/util.h>
+
+#include <vector>
+
+FUZZ_TARGET(muhash)
+{
+ FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};
+ std::vector<uint8_t> data = ConsumeRandomLengthByteVector(fuzzed_data_provider);
+ std::vector<uint8_t> data2 = ConsumeRandomLengthByteVector(fuzzed_data_provider);
+ if (data.empty()) {
+ data.resize(fuzzed_data_provider.ConsumeIntegralInRange<size_t>(1, 4096), fuzzed_data_provider.ConsumeIntegral<uint8_t>());
+ }
+ if (data2.empty()) {
+ data2.resize(fuzzed_data_provider.ConsumeIntegralInRange<size_t>(1, 4096), fuzzed_data_provider.ConsumeIntegral<uint8_t>());
+ }
+
+ data = ConsumeRandomLengthByteVector(fuzzed_data_provider);
+ data2 = ConsumeRandomLengthByteVector(fuzzed_data_provider);
+
+ MuHash3072 muhash;
+
+ // Test that MuHash result is consistent independent of order of operations
+ muhash.Insert(data);
+ muhash.Insert(data2);
+
+ uint256 out;
+ muhash.Finalize(out);
+
+ muhash = MuHash3072();
+ muhash.Insert(data2);
+ muhash.Insert(data);
+
+ uint256 out2;
+ muhash.Finalize(out2);
+
+ assert(out == out2);
+
+ // Test that removing all added elements brings the object back to it's initial state
+ muhash /= muhash;
+ muhash.Finalize(out);
+
+ MuHash3072 muhash2;
+ muhash2.Finalize(out2);
+
+ assert(out == out2);
+}
diff --git a/src/util/system.cpp b/src/util/system.cpp
index 5f30136fa2..917d37ccc7 100644
--- a/src/util/system.cpp
+++ b/src/util/system.cpp
@@ -1047,27 +1047,36 @@ bool FileCommit(FILE *file)
LogPrintf("%s: FlushFileBuffers failed: %d\n", __func__, GetLastError());
return false;
}
-#else
- #if HAVE_FDATASYNC
- if (fdatasync(fileno(file)) != 0 && errno != EINVAL) { // Ignore EINVAL for filesystems that don't support sync
- LogPrintf("%s: fdatasync failed: %d\n", __func__, errno);
- return false;
- }
- #elif defined(MAC_OSX) && defined(F_FULLFSYNC)
+#elif defined(MAC_OSX) && defined(F_FULLFSYNC)
if (fcntl(fileno(file), F_FULLFSYNC, 0) == -1) { // Manpage says "value other than -1" is returned on success
LogPrintf("%s: fcntl F_FULLFSYNC failed: %d\n", __func__, errno);
return false;
}
- #else
+#elif HAVE_FDATASYNC
+ if (fdatasync(fileno(file)) != 0 && errno != EINVAL) { // Ignore EINVAL for filesystems that don't support sync
+ LogPrintf("%s: fdatasync failed: %d\n", __func__, errno);
+ return false;
+ }
+#else
if (fsync(fileno(file)) != 0 && errno != EINVAL) {
LogPrintf("%s: fsync failed: %d\n", __func__, errno);
return false;
}
- #endif
#endif
return true;
}
+void DirectoryCommit(const fs::path &dirname)
+{
+#ifndef WIN32
+ FILE* file = fsbridge::fopen(dirname, "r");
+ if (file) {
+ fsync(fileno(file));
+ fclose(file);
+ }
+#endif
+}
+
bool TruncateFile(FILE *file, unsigned int length) {
#if defined(WIN32)
return _chsize(_fileno(file), length) == 0;
diff --git a/src/util/system.h b/src/util/system.h
index 2be8bb754b..010fc5b49f 100644
--- a/src/util/system.h
+++ b/src/util/system.h
@@ -56,7 +56,19 @@ bool error(const char* fmt, const Args&... args)
}
void PrintExceptionContinue(const std::exception *pex, const char* pszThread);
+
+/**
+ * Ensure file contents are fully committed to disk, using a platform-specific
+ * feature analogous to fsync().
+ */
bool FileCommit(FILE *file);
+
+/**
+ * Sync directory contents. This is required on some environments to ensure that
+ * newly created files are committed to disk.
+ */
+void DirectoryCommit(const fs::path &dirname);
+
bool TruncateFile(FILE *file, unsigned int length);
int RaiseFileDescriptorLimit(int nMinFD);
void AllocateFileRange(FILE *file, unsigned int offset, unsigned int length);