aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore3
-rw-r--r--Makefile.am5
-rw-r--r--build-aux/m4/l_atomic.m440
-rw-r--r--configure.ac11
-rw-r--r--contrib/devtools/README.md6
-rwxr-xr-xcontrib/devtools/gen-manpages.sh29
-rwxr-xr-xcontrib/devtools/security-check.py3
-rwxr-xr-xcontrib/devtools/test-security-check.py3
-rw-r--r--contrib/gitian-keys/btcdrak-key.pgpbin8954 -> 4916 bytes
-rwxr-xr-xcontrib/zmq/zmq_sub.py3
-rw-r--r--doc/build-osx.md2
-rw-r--r--doc/gitian-building.md2
-rw-r--r--doc/man/Makefile.am1
-rw-r--r--doc/man/bitcoin-cli.184
-rw-r--r--doc/man/bitcoin-qt.1513
-rw-r--r--doc/man/bitcoin-tx.1107
-rw-r--r--doc/man/bitcoind.1492
-rw-r--r--doc/reduce-traffic.md3
-rw-r--r--doc/translation_process.md2
-rwxr-xr-xqa/pull-tester/rpc-tests.py14
-rwxr-xr-xqa/rpc-tests/create_cache.py10
-rwxr-xr-xqa/rpc-tests/p2p-segwit.py28
-rw-r--r--qa/rpc-tests/test_framework/script.py2
-rwxr-xr-xqa/rpc-tests/test_framework/test_framework.py5
-rw-r--r--qa/rpc-tests/test_framework/util.py8
-rwxr-xr-xqa/rpc-tests/wallet-hd.py6
-rwxr-xr-xqa/rpc-tests/wallet.py5
-rwxr-xr-xqa/rpc-tests/walletbackup.py7
-rwxr-xr-xshare/qt/extract_strings_qt.py3
-rw-r--r--src/Makefile.am2
-rw-r--r--src/addrdb.cpp218
-rw-r--r--src/addrdb.h103
-rw-r--r--src/init.cpp69
-rw-r--r--src/main.cpp248
-rw-r--r--src/main.h10
-rw-r--r--src/net.cpp822
-rw-r--r--src/net.h557
-rw-r--r--src/qt/bantablemodel.cpp3
-rw-r--r--src/qt/clientmodel.cpp26
-rw-r--r--src/qt/guiutil.cpp8
-rw-r--r--src/qt/guiutil.h2
-rw-r--r--src/qt/paymentrequest.proto2
-rw-r--r--src/qt/peertablemodel.cpp21
-rw-r--r--src/qt/peertablemodel.h7
-rw-r--r--src/qt/rpcconsole.cpp41
-rw-r--r--src/qt/sendcoinsdialog.cpp5
-rw-r--r--src/qt/walletmodel.cpp2
-rw-r--r--src/rpc/blockchain.cpp151
-rw-r--r--src/rpc/client.cpp6
-rw-r--r--src/rpc/mining.cpp9
-rw-r--r--src/rpc/misc.cpp49
-rw-r--r--src/rpc/net.cpp108
-rw-r--r--src/rpc/protocol.h1
-rw-r--r--src/rpc/rawtransaction.cpp8
-rw-r--r--src/rpc/server.h1
-rw-r--r--src/test/DoS_tests.cpp56
-rw-r--r--src/test/arith_uint256_tests.cpp2
-rw-r--r--src/test/miner_tests.cpp2
-rw-r--r--src/test/net_tests.cpp6
-rw-r--r--src/test/prevector_tests.cpp62
-rw-r--r--src/test/test_bitcoin.cpp8
-rw-r--r--src/test/test_bitcoin.h2
-rw-r--r--src/validationinterface.cpp4
-rw-r--r--src/validationinterface.h5
-rw-r--r--src/wallet/rpcwallet.cpp17
-rw-r--r--src/wallet/wallet.cpp40
-rw-r--r--src/wallet/wallet.h11
67 files changed, 3048 insertions, 1043 deletions
diff --git a/.gitignore b/.gitignore
index 892b2a3e08..b9db7cbd39 100644
--- a/.gitignore
+++ b/.gitignore
@@ -103,10 +103,7 @@ linux-build
win32-build
qa/pull-tester/run-bitcoind-for-test.sh
qa/pull-tester/tests_config.py
-qa/pull-tester/test.*/*
-qa/tmp
qa/cache/*
-share/BitcoindComparisonTool.jar
!src/leveldb*/Makefile
diff --git a/Makefile.am b/Makefile.am
index 37c1e98eca..2e061c3773 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1,5 +1,8 @@
ACLOCAL_AMFLAGS = -I build-aux/m4
SUBDIRS = src
+if ENABLE_MAN
+SUBDIRS += doc/man
+endif
.PHONY: deploy FORCE
GZIP_ENV="-9n"
@@ -213,6 +216,8 @@ DISTCLEANFILES = qa/pull-tester/tests_config.pyc
.INTERMEDIATE: $(COVERAGE_INFO)
+DISTCHECK_CONFIGURE_FLAGS = --enable-man
+
clean-local:
rm -rf coverage_percent.txt test_bitcoin.coverage/ total.coverage/ qa/tmp/ cache/ $(OSX_APP)
rm -rf qa/pull-tester/__pycache__
diff --git a/build-aux/m4/l_atomic.m4 b/build-aux/m4/l_atomic.m4
new file mode 100644
index 0000000000..906724b640
--- /dev/null
+++ b/build-aux/m4/l_atomic.m4
@@ -0,0 +1,40 @@
+# Some versions of gcc/libstdc++ require linking with -latomic if
+# using the C++ atomic library.
+#
+# Sourced from http://bugs.debian.org/797228
+
+m4_define([_CHECK_ATOMIC_testbody], [[
+ #include <atomic>
+ #include <cstdint>
+
+ int main() {
+ std::atomic<int64_t> a{};
+
+ int64_t v = 5;
+ int64_t r = a.fetch_add(v);
+ return static_cast<int>(r);
+ }
+]])
+
+AC_DEFUN([CHECK_ATOMIC], [
+
+ AC_LANG_PUSH(C++)
+
+ AC_MSG_CHECKING([whether std::atomic can be used without link library])
+
+ AC_LINK_IFELSE([AC_LANG_SOURCE([_CHECK_ATOMIC_testbody])],[
+ AC_MSG_RESULT([yes])
+ ],[
+ AC_MSG_RESULT([no])
+ LIBS="$LIBS -latomic"
+ AC_MSG_CHECKING([whether std::atomic needs -latomic])
+ AC_LINK_IFELSE([AC_LANG_SOURCE([_CHECK_ATOMIC_testbody])],[
+ AC_MSG_RESULT([yes])
+ ],[
+ AC_MSG_RESULT([no])
+ AC_MSG_FAILURE([cannot figure our how to use std::atomic])
+ ])
+ ])
+
+ AC_LANG_POP
+])
diff --git a/configure.ac b/configure.ac
index add8d21781..25b828f18c 100644
--- a/configure.ac
+++ b/configure.ac
@@ -57,6 +57,9 @@ case $host in
esac
dnl Require C++11 compiler (no GNU extensions)
AX_CXX_COMPILE_STDCXX([11], [noext], [mandatory])
+dnl Check if -latomic is required for <std::atomic>
+CHECK_ATOMIC
+
dnl Libtool init checks.
LT_INIT([pic-only])
@@ -168,6 +171,12 @@ AC_ARG_ENABLE([zmq],
AC_ARG_WITH([protoc-bindir],[AS_HELP_STRING([--with-protoc-bindir=BIN_DIR],[specify protoc bin path])], [protoc_bin_path=$withval], [])
+AC_ARG_ENABLE(man,
+ [AS_HELP_STRING([--disable-man],
+ [do not install man pages (default is to install)])],,
+ enable_man=yes)
+AM_CONDITIONAL(ENABLE_MAN, test "$enable_man" != no)
+
# Enable debug
AC_ARG_ENABLE([debug],
[AS_HELP_STRING([--enable-debug],
@@ -1054,7 +1063,7 @@ AC_SUBST(EVENT_PTHREADS_LIBS)
AC_SUBST(ZMQ_LIBS)
AC_SUBST(PROTOBUF_LIBS)
AC_SUBST(QR_LIBS)
-AC_CONFIG_FILES([Makefile src/Makefile share/setup.nsi share/qt/Info.plist src/test/buildenv.py])
+AC_CONFIG_FILES([Makefile src/Makefile doc/man/Makefile share/setup.nsi share/qt/Info.plist src/test/buildenv.py])
AC_CONFIG_FILES([qa/pull-tester/run-bitcoind-for-test.sh],[chmod +x qa/pull-tester/run-bitcoind-for-test.sh])
AC_CONFIG_FILES([qa/pull-tester/tests_config.py],[chmod +x qa/pull-tester/tests_config.py])
AC_CONFIG_FILES([contrib/devtools/split-debug.sh],[chmod +x contrib/devtools/split-debug.sh])
diff --git a/contrib/devtools/README.md b/contrib/devtools/README.md
index af5c000b03..60fe69e7e3 100644
--- a/contrib/devtools/README.md
+++ b/contrib/devtools/README.md
@@ -40,6 +40,12 @@ would be changed to:
```// Copyright (c) 2009-2015 The Bitcoin Core developers```
+gen-manpages.sh
+===============
+
+A small script to automatically create manpages in ../../doc/man by running the release binaries with the -help option.
+This requires help2man which can be found at: https://www.gnu.org/software/help2man/
+
git-subtree-check.sh
====================
diff --git a/contrib/devtools/gen-manpages.sh b/contrib/devtools/gen-manpages.sh
new file mode 100755
index 0000000000..967717e1e0
--- /dev/null
+++ b/contrib/devtools/gen-manpages.sh
@@ -0,0 +1,29 @@
+#!/bin/sh
+
+TOPDIR=${TOPDIR:-$(git rev-parse --show-toplevel)}
+SRCDIR=${SRCDIR:-$TOPDIR/src}
+MANDIR=${MANDIR:-$TOPDIR/doc/man}
+
+BITCOIND=${BITCOIND:-$SRCDIR/bitcoind}
+BITCOINCLI=${BITCOINCLI:-$SRCDIR/bitcoin-cli}
+BITCOINTX=${BITCOINTX:-$SRCDIR/bitcoin-tx}
+BITCOINQT=${BITCOINQT:-$SRCDIR/qt/bitcoin-qt}
+
+[ ! -x $BITCOIND ] && echo "$BITCOIND not found or not executable." && exit 1
+
+# The autodetected version git tag can screw up manpage output a little bit
+BTCVER=($($BITCOINCLI --version | head -n1 | awk -F'[ -]' '{ print $6, $7 }'))
+
+# Create a footer file with copyright content.
+# This gets autodetected fine for bitcoind if --version-string is not set,
+# but has different outcomes for bitcoin-qt and bitcoin-cli.
+echo "[COPYRIGHT]" > footer.h2m
+$BITCOIND --version | sed -n '1!p' >> footer.h2m
+
+for cmd in $BITCOIND $BITCOINCLI $BITCOINTX $BITCOINQT; do
+ cmdname="${cmd##*/}"
+ help2man -N --version-string=${BTCVER[0]} --include=footer.h2m -o ${MANDIR}/${cmdname}.1 ${cmd}
+ sed -i "s/\\\-${BTCVER[1]}//g" ${MANDIR}/${cmdname}.1
+done
+
+rm -f footer.h2m
diff --git a/contrib/devtools/security-check.py b/contrib/devtools/security-check.py
index c61d652641..b4b4b4603d 100755
--- a/contrib/devtools/security-check.py
+++ b/contrib/devtools/security-check.py
@@ -1,4 +1,7 @@
#!/usr/bin/env python
+# Copyright (c) 2015-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.
'''
Perform basic ELF security checks on a series of executables.
Exit status will be 0 if successful, and the program will be silent.
diff --git a/contrib/devtools/test-security-check.py b/contrib/devtools/test-security-check.py
index 324b7bcd85..c0f120392e 100755
--- a/contrib/devtools/test-security-check.py
+++ b/contrib/devtools/test-security-check.py
@@ -1,4 +1,7 @@
#!/usr/bin/env python2
+# Copyright (c) 2015-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.
'''
Test script for security-check.py
'''
diff --git a/contrib/gitian-keys/btcdrak-key.pgp b/contrib/gitian-keys/btcdrak-key.pgp
index 60d76c0ec7..f00dc729d5 100644
--- a/contrib/gitian-keys/btcdrak-key.pgp
+++ b/contrib/gitian-keys/btcdrak-key.pgp
Binary files differ
diff --git a/contrib/zmq/zmq_sub.py b/contrib/zmq/zmq_sub.py
index 6268123dd8..3dea5e3c14 100755
--- a/contrib/zmq/zmq_sub.py
+++ b/contrib/zmq/zmq_sub.py
@@ -1,4 +1,7 @@
#!/usr/bin/env python2
+# Copyright (c) 2014-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.
import array
import binascii
diff --git a/doc/build-osx.md b/doc/build-osx.md
index c9eb4225ab..bc90a30562 100644
--- a/doc/build-osx.md
+++ b/doc/build-osx.md
@@ -16,7 +16,7 @@ Then install [Homebrew](http://brew.sh).
Dependencies
----------------------
- brew install automake berkeley-db4 libtool boost --c++11 miniupnpc openssl pkg-config protobuf --c++11 qt5 libevent
+ brew install automake berkeley-db4 libtool boost --c++11 miniupnpc openssl pkg-config homebrew/versions/protobuf260 --c++11 qt5 libevent
NOTE: Building with Qt4 is still supported, however, could result in a broken UI. Building with Qt5 is recommended.
diff --git a/doc/gitian-building.md b/doc/gitian-building.md
index 938f92ff12..84dce3f082 100644
--- a/doc/gitian-building.md
+++ b/doc/gitian-building.md
@@ -337,7 +337,7 @@ Getting and building the inputs
--------------------------------
Follow the instructions in [doc/release-process.md](release-process.md#fetch-and-build-inputs-first-time-or-when-dependency-versions-change)
-in the bitcoin repository under 'Fetch and build inputs' to install sources which require
+in the bitcoin repository under 'Fetch and create inputs' to install sources which require
manual intervention. Also optionally follow the next step: 'Seed the Gitian sources cache
and offline git repositories' which will fetch the remaining files required for building
offline.
diff --git a/doc/man/Makefile.am b/doc/man/Makefile.am
new file mode 100644
index 0000000000..5252ef4a19
--- /dev/null
+++ b/doc/man/Makefile.am
@@ -0,0 +1 @@
+dist_man1_MANS=bitcoind.1 bitcoin-qt.1 bitcoin-cli.1 bitcoin-tx.1
diff --git a/doc/man/bitcoin-cli.1 b/doc/man/bitcoin-cli.1
new file mode 100644
index 0000000000..11b690cf85
--- /dev/null
+++ b/doc/man/bitcoin-cli.1
@@ -0,0 +1,84 @@
+.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.46.5.
+.TH BITCOIN-CLI "1" "September 2016" "bitcoin-cli v0.13.0.0" "User Commands"
+.SH NAME
+bitcoin-cli \- manual page for bitcoin-cli v0.13.0.0
+.SH DESCRIPTION
+Bitcoin Core RPC client version v0.13.0.0
+.SS "Usage:"
+.TP
+bitcoin\-cli [options] <command> [params]
+Send command to Bitcoin Core
+.TP
+bitcoin\-cli [options] help
+List commands
+.TP
+bitcoin\-cli [options] help <command>
+Get help for a command
+.SH OPTIONS
+.HP
+\-?
+.IP
+This help message
+.HP
+\fB\-conf=\fR<file>
+.IP
+Specify configuration file (default: bitcoin.conf)
+.HP
+\fB\-datadir=\fR<dir>
+.IP
+Specify data directory
+.PP
+Chain selection options:
+.HP
+\fB\-testnet\fR
+.IP
+Use the test chain
+.HP
+\fB\-regtest\fR
+.IP
+Enter regression test mode, which uses a special chain in which blocks
+can be solved instantly. This is intended for regression testing
+tools and app development.
+.HP
+\fB\-rpcconnect=\fR<ip>
+.IP
+Send commands to node running on <ip> (default: 127.0.0.1)
+.HP
+\fB\-rpcport=\fR<port>
+.IP
+Connect to JSON\-RPC on <port> (default: 8332 or testnet: 18332)
+.HP
+\fB\-rpcwait\fR
+.IP
+Wait for RPC server to start
+.HP
+\fB\-rpcuser=\fR<user>
+.IP
+Username for JSON\-RPC connections
+.HP
+\fB\-rpcpassword=\fR<pw>
+.IP
+Password for JSON\-RPC connections
+.HP
+\fB\-rpcclienttimeout=\fR<n>
+.IP
+Timeout during HTTP requests (default: 900)
+.HP
+\fB\-stdin\fR
+.IP
+Read extra arguments from standard input, one per line until EOF/Ctrl\-D
+(recommended for sensitive information such as passphrases)
+.SH COPYRIGHT
+Copyright (C) 2009-2016 The Bitcoin Core developers
+
+Please contribute if you find Bitcoin Core useful. Visit
+<https://bitcoincore.org> for further information about the software.
+The source code is available from <https://github.com/bitcoin/bitcoin>.
+
+This is experimental software.
+Distributed under the MIT software license, see the accompanying file COPYING
+or <http://www.opensource.org/licenses/mit-license.php>.
+
+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.
diff --git a/doc/man/bitcoin-qt.1 b/doc/man/bitcoin-qt.1
new file mode 100644
index 0000000000..24b529dac1
--- /dev/null
+++ b/doc/man/bitcoin-qt.1
@@ -0,0 +1,513 @@
+.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.46.5.
+.TH BITCOIN-QT "1" "September 2016" "bitcoin-qt v0.13.0.0" "User Commands"
+.SH NAME
+bitcoin-qt \- manual page for bitcoin-qt v0.13.0.0
+.SH DESCRIPTION
+Bitcoin Core version v0.13.0.0 (64\-bit)
+Usage:
+.IP
+bitcoin\-qt [command\-line options]
+.SH OPTIONS
+.HP
+\-?
+.IP
+Print this help message and exit
+.HP
+\fB\-version\fR
+.IP
+Print version and exit
+.HP
+\fB\-alertnotify=\fR<cmd>
+.IP
+Execute command when a relevant alert is received or we see a really
+long fork (%s in cmd is replaced by message)
+.HP
+\fB\-blocknotify=\fR<cmd>
+.IP
+Execute command when the best block changes (%s in cmd is replaced by
+block hash)
+.HP
+\fB\-checkblocks=\fR<n>
+.IP
+How many blocks to check at startup (default: 288, 0 = all)
+.HP
+\fB\-checklevel=\fR<n>
+.IP
+How thorough the block verification of \fB\-checkblocks\fR is (0\-4, default: 3)
+.HP
+\fB\-conf=\fR<file>
+.IP
+Specify configuration file (default: bitcoin.conf)
+.HP
+\fB\-datadir=\fR<dir>
+.IP
+Specify data directory
+.HP
+\fB\-dbcache=\fR<n>
+.IP
+Set database cache size in megabytes (4 to 16384, default: 300)
+.HP
+\fB\-loadblock=\fR<file>
+.IP
+Imports blocks from external blk000??.dat file on startup
+.HP
+\fB\-maxorphantx=\fR<n>
+.IP
+Keep at most <n> unconnectable transactions in memory (default: 100)
+.HP
+\fB\-maxmempool=\fR<n>
+.IP
+Keep the transaction memory pool below <n> megabytes (default: 300)
+.HP
+\fB\-mempoolexpiry=\fR<n>
+.IP
+Do not keep transactions in the mempool longer than <n> hours (default:
+72)
+.HP
+\fB\-par=\fR<n>
+.IP
+Set the number of script verification threads (\fB\-4\fR to 16, 0 = auto, <0 =
+leave that many cores free, default: 0)
+.HP
+\fB\-pid=\fR<file>
+.IP
+Specify pid file (default: bitcoind.pid)
+.HP
+\fB\-prune=\fR<n>
+.IP
+Reduce storage requirements by pruning (deleting) old blocks. This mode
+is incompatible with \fB\-txindex\fR and \fB\-rescan\fR. Warning: Reverting
+this setting requires re\-downloading the entire blockchain.
+(default: 0 = disable pruning blocks, >550 = target size in MiB
+to use for block files)
+.HP
+\fB\-reindex\-chainstate\fR
+.IP
+Rebuild chain state from the currently indexed blocks
+.HP
+\fB\-reindex\fR
+.IP
+Rebuild chain state and block index from the blk*.dat files on disk
+.HP
+\fB\-sysperms\fR
+.IP
+Create new files with system default permissions, instead of umask 077
+(only effective with disabled wallet functionality)
+.HP
+\fB\-txindex\fR
+.IP
+Maintain a full transaction index, used by the getrawtransaction rpc
+call (default: 0)
+.PP
+Connection options:
+.HP
+\fB\-addnode=\fR<ip>
+.IP
+Add a node to connect to and attempt to keep the connection open
+.HP
+\fB\-banscore=\fR<n>
+.IP
+Threshold for disconnecting misbehaving peers (default: 100)
+.HP
+\fB\-bantime=\fR<n>
+.IP
+Number of seconds to keep misbehaving peers from reconnecting (default:
+86400)
+.HP
+\fB\-bind=\fR<addr>
+.IP
+Bind to given address and always listen on it. Use [host]:port notation
+for IPv6
+.HP
+\fB\-connect=\fR<ip>
+.IP
+Connect only to the specified node(s)
+.HP
+\fB\-discover\fR
+.IP
+Discover own IP addresses (default: 1 when listening and no \fB\-externalip\fR
+or \fB\-proxy\fR)
+.HP
+\fB\-dns\fR
+.IP
+Allow DNS lookups for \fB\-addnode\fR, \fB\-seednode\fR and \fB\-connect\fR (default: 1)
+.HP
+\fB\-dnsseed\fR
+.IP
+Query for peer addresses via DNS lookup, if low on addresses (default: 1
+unless \fB\-connect\fR)
+.HP
+\fB\-externalip=\fR<ip>
+.IP
+Specify your own public address
+.HP
+\fB\-forcednsseed\fR
+.IP
+Always query for peer addresses via DNS lookup (default: 0)
+.HP
+\fB\-listen\fR
+.IP
+Accept connections from outside (default: 1 if no \fB\-proxy\fR or \fB\-connect\fR)
+.HP
+\fB\-listenonion\fR
+.IP
+Automatically create Tor hidden service (default: 1)
+.HP
+\fB\-maxconnections=\fR<n>
+.IP
+Maintain at most <n> connections to peers (default: 125)
+.HP
+\fB\-maxreceivebuffer=\fR<n>
+.IP
+Maximum per\-connection receive buffer, <n>*1000 bytes (default: 5000)
+.HP
+\fB\-maxsendbuffer=\fR<n>
+.IP
+Maximum per\-connection send buffer, <n>*1000 bytes (default: 1000)
+.HP
+\fB\-maxtimeadjustment\fR
+.IP
+Maximum allowed median peer time offset adjustment. Local perspective of
+time may be influenced by peers forward or backward by this
+amount. (default: 4200 seconds)
+.HP
+\fB\-onion=\fR<ip:port>
+.IP
+Use separate SOCKS5 proxy to reach peers via Tor hidden services
+(default: \fB\-proxy\fR)
+.HP
+\fB\-onlynet=\fR<net>
+.IP
+Only connect to nodes in network <net> (ipv4, ipv6 or onion)
+.HP
+\fB\-permitbaremultisig\fR
+.IP
+Relay non\-P2SH multisig (default: 1)
+.HP
+\fB\-peerbloomfilters\fR
+.IP
+Support filtering of blocks and transaction with bloom filters (default:
+1)
+.HP
+\fB\-port=\fR<port>
+.IP
+Listen for connections on <port> (default: 8333 or testnet: 18333)
+.HP
+\fB\-proxy=\fR<ip:port>
+.IP
+Connect through SOCKS5 proxy
+.HP
+\fB\-proxyrandomize\fR
+.IP
+Randomize credentials for every proxy connection. This enables Tor
+stream isolation (default: 1)
+.HP
+\fB\-seednode=\fR<ip>
+.IP
+Connect to a node to retrieve peer addresses, and disconnect
+.HP
+\fB\-timeout=\fR<n>
+.IP
+Specify connection timeout in milliseconds (minimum: 1, default: 5000)
+.HP
+\fB\-torcontrol=\fR<ip>:<port>
+.IP
+Tor control port to use if onion listening enabled (default:
+127.0.0.1:9051)
+.HP
+\fB\-torpassword=\fR<pass>
+.IP
+Tor control port password (default: empty)
+.HP
+\fB\-whitebind=\fR<addr>
+.IP
+Bind to given address and whitelist peers connecting to it. Use
+[host]:port notation for IPv6
+.HP
+\fB\-whitelist=\fR<netmask>
+.IP
+Whitelist peers connecting from the given netmask or IP address. Can be
+specified multiple times. 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
+.HP
+\fB\-whitelistrelay\fR
+.IP
+Accept relayed transactions received from whitelisted peers even when
+not relaying transactions (default: 1)
+.HP
+\fB\-whitelistforcerelay\fR
+.IP
+Force relay of transactions from whitelisted peers even they violate
+local relay policy (default: 1)
+.HP
+\fB\-maxuploadtarget=\fR<n>
+.IP
+Tries to keep outbound traffic under the given target (in MiB per 24h),
+0 = no limit (default: 0)
+.PP
+Wallet options:
+.HP
+\fB\-disablewallet\fR
+.IP
+Do not load the wallet and disable wallet RPC calls
+.HP
+\fB\-keypool=\fR<n>
+.IP
+Set key pool size to <n> (default: 100)
+.HP
+\fB\-fallbackfee=\fR<amt>
+.IP
+A fee rate (in BTC/kB) that will be used when fee estimation has
+insufficient data (default: 0.0002)
+.HP
+\fB\-mintxfee=\fR<amt>
+.IP
+Fees (in BTC/kB) smaller than this are considered zero fee for
+transaction creation (default: 0.00001)
+.HP
+\fB\-paytxfee=\fR<amt>
+.IP
+Fee (in BTC/kB) to add to transactions you send (default: 0.00)
+.HP
+\fB\-rescan\fR
+.IP
+Rescan the block chain for missing wallet transactions on startup
+.HP
+\fB\-salvagewallet\fR
+.IP
+Attempt to recover private keys from a corrupt wallet on startup
+.HP
+\fB\-spendzeroconfchange\fR
+.IP
+Spend unconfirmed change when sending transactions (default: 1)
+.HP
+\fB\-txconfirmtarget=\fR<n>
+.IP
+If paytxfee is not set, include enough fee so transactions begin
+confirmation on average within n blocks (default: 2)
+.HP
+\fB\-usehd\fR
+.IP
+Use hierarchical deterministic key generation (HD) after BIP32. Only has
+effect during wallet creation/first start (default: 1)
+.HP
+\fB\-upgradewallet\fR
+.IP
+Upgrade wallet to latest format on startup
+.HP
+\fB\-wallet=\fR<file>
+.IP
+Specify wallet file (within data directory) (default: wallet.dat)
+.HP
+\fB\-walletbroadcast\fR
+.IP
+Make the wallet broadcast transactions (default: 1)
+.HP
+\fB\-walletnotify=\fR<cmd>
+.IP
+Execute command when a wallet transaction changes (%s in cmd is replaced
+by TxID)
+.HP
+\fB\-zapwallettxes=\fR<mode>
+.IP
+Delete all wallet transactions and only recover those parts of the
+blockchain through \fB\-rescan\fR on startup (1 = keep tx meta data e.g.
+account owner and payment request information, 2 = drop tx meta
+data)
+.PP
+ZeroMQ notification options:
+.HP
+\fB\-zmqpubhashblock=\fR<address>
+.IP
+Enable publish hash block in <address>
+.HP
+\fB\-zmqpubhashtx=\fR<address>
+.IP
+Enable publish hash transaction in <address>
+.HP
+\fB\-zmqpubrawblock=\fR<address>
+.IP
+Enable publish raw block in <address>
+.HP
+\fB\-zmqpubrawtx=\fR<address>
+.IP
+Enable publish raw transaction in <address>
+.PP
+Debugging/Testing options:
+.HP
+\fB\-uacomment=\fR<cmt>
+.IP
+Append comment to the user agent string
+.HP
+\fB\-debug=\fR<category>
+.IP
+Output debugging information (default: 0, supplying <category> is
+optional). If <category> is not supplied or if <category> = 1,
+output all debugging information.<category> can be: addrman,
+alert, bench, coindb, db, http, libevent, lock, mempool,
+mempoolrej, net, proxy, prune, rand, reindex, rpc, selectcoins,
+tor, zmq, qt.
+.HP
+\fB\-help\-debug\fR
+.IP
+Show all debugging options (usage: \fB\-\-help\fR \fB\-help\-debug\fR)
+.HP
+\fB\-logips\fR
+.IP
+Include IP addresses in debug output (default: 0)
+.HP
+\fB\-logtimestamps\fR
+.IP
+Prepend debug output with timestamp (default: 1)
+.HP
+\fB\-minrelaytxfee=\fR<amt>
+.IP
+Fees (in BTC/kB) smaller than this are considered zero fee for relaying,
+mining and transaction creation (default: 0.00001)
+.HP
+\fB\-maxtxfee=\fR<amt>
+.IP
+Maximum total fees (in BTC) to use in a single wallet transaction or raw
+transaction; setting this too low may abort large transactions
+(default: 0.10)
+.HP
+\fB\-printtoconsole\fR
+.IP
+Send trace/debug info to console instead of debug.log file
+.HP
+\fB\-shrinkdebugfile\fR
+.IP
+Shrink debug.log file on client startup (default: 1 when no \fB\-debug\fR)
+.PP
+Chain selection options:
+.HP
+\fB\-testnet\fR
+.IP
+Use the test chain
+.PP
+Node relay options:
+.HP
+\fB\-bytespersigop\fR
+.IP
+Equivalent bytes per sigop in transactions for relay and mining
+(default: 20)
+.HP
+\fB\-datacarrier\fR
+.IP
+Relay and mine data carrier transactions (default: 1)
+.HP
+\fB\-datacarriersize\fR
+.IP
+Maximum size of data in data carrier transactions we relay and mine
+(default: 83)
+.HP
+\fB\-mempoolreplacement\fR
+.IP
+Enable transaction replacement in the memory pool (default: 1)
+.PP
+Block creation options:
+.HP
+\fB\-blockmaxweight=\fR<n>
+.IP
+Set maximum BIP141 block weight (default: 3000000)
+.HP
+\fB\-blockmaxsize=\fR<n>
+.IP
+Set maximum block size in bytes (default: 750000)
+.HP
+\fB\-blockprioritysize=\fR<n>
+.IP
+Set maximum size of high\-priority/low\-fee transactions in bytes
+(default: 0)
+.PP
+RPC server options:
+.HP
+\fB\-server\fR
+.IP
+Accept command line and JSON\-RPC commands
+.HP
+\fB\-rest\fR
+.IP
+Accept public REST requests (default: 0)
+.HP
+\fB\-rpcbind=\fR<addr>
+.IP
+Bind to given address to listen for JSON\-RPC connections. Use
+[host]:port notation for IPv6. This option can be specified
+multiple times (default: bind to all interfaces)
+.HP
+\fB\-rpccookiefile=\fR<loc>
+.IP
+Location of the auth cookie (default: data dir)
+.HP
+\fB\-rpcuser=\fR<user>
+.IP
+Username for JSON\-RPC connections
+.HP
+\fB\-rpcpassword=\fR<pw>
+.IP
+Password for JSON\-RPC connections
+.HP
+\fB\-rpcauth=\fR<userpw>
+.IP
+Username and hashed password for JSON\-RPC connections. The field
+<userpw> comes in the format: <USERNAME>:<SALT>$<HASH>. A
+canonical python script is included in share/rpcuser. This option
+can be specified multiple times
+.HP
+\fB\-rpcport=\fR<port>
+.IP
+Listen for JSON\-RPC connections on <port> (default: 8332 or testnet:
+18332)
+.HP
+\fB\-rpcallowip=\fR<ip>
+.IP
+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
+.HP
+\fB\-rpcthreads=\fR<n>
+.IP
+Set the number of threads to service RPC calls (default: 4)
+.PP
+UI Options:
+.HP
+\fB\-choosedatadir\fR
+.IP
+Choose data directory on startup (default: 0)
+.HP
+\fB\-lang=\fR<lang>
+.IP
+Set language, for example "de_DE" (default: system locale)
+.HP
+\fB\-min\fR
+.IP
+Start minimized
+.HP
+\fB\-rootcertificates=\fR<file>
+.IP
+Set SSL root certificates for payment request (default: \fB\-system\-\fR)
+.HP
+\fB\-splash\fR
+.IP
+Show splash screen on startup (default: 1)
+.HP
+\fB\-resetguisettings\fR
+.IP
+Reset all settings changed in the GUI
+.SH COPYRIGHT
+Copyright (C) 2009-2016 The Bitcoin Core developers
+
+Please contribute if you find Bitcoin Core useful. Visit
+<https://bitcoincore.org> for further information about the software.
+The source code is available from <https://github.com/bitcoin/bitcoin>.
+
+This is experimental software.
+Distributed under the MIT software license, see the accompanying file COPYING
+or <http://www.opensource.org/licenses/mit-license.php>.
+
+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.
diff --git a/doc/man/bitcoin-tx.1 b/doc/man/bitcoin-tx.1
new file mode 100644
index 0000000000..5c4e31e7f7
--- /dev/null
+++ b/doc/man/bitcoin-tx.1
@@ -0,0 +1,107 @@
+.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.46.5.
+.TH BITCOIN-TX "1" "September 2016" "bitcoin-tx v0.13.0.0" "User Commands"
+.SH NAME
+bitcoin-tx \- manual page for bitcoin-tx v0.13.0.0
+.SH DESCRIPTION
+Bitcoin Core bitcoin\-tx utility version v0.13.0.0
+.SS "Usage:"
+.TP
+bitcoin\-tx [options] <hex\-tx> [commands]
+Update hex\-encoded bitcoin transaction
+.TP
+bitcoin\-tx [options] \fB\-create\fR [commands]
+Create hex\-encoded bitcoin transaction
+.SH OPTIONS
+.HP
+\-?
+.IP
+This help message
+.HP
+\fB\-create\fR
+.IP
+Create new, empty TX.
+.HP
+\fB\-json\fR
+.IP
+Select JSON output
+.HP
+\fB\-txid\fR
+.IP
+Output only the hex\-encoded transaction id of the resultant transaction.
+.PP
+Chain selection options:
+.HP
+\fB\-testnet\fR
+.IP
+Use the test chain
+.HP
+\fB\-regtest\fR
+.IP
+Enter regression test mode, which uses a special chain in which blocks
+can be solved instantly. This is intended for regression testing
+tools and app development.
+.PP
+Commands:
+.IP
+delin=N
+.IP
+Delete input N from TX
+.IP
+delout=N
+.IP
+Delete output N from TX
+.IP
+in=TXID:VOUT(:SEQUENCE_NUMBER)
+.IP
+Add input to TX
+.IP
+locktime=N
+.IP
+Set TX lock time to N
+.IP
+nversion=N
+.IP
+Set TX version to N
+.IP
+outaddr=VALUE:ADDRESS
+.IP
+Add address\-based output to TX
+.IP
+outdata=[VALUE:]DATA
+.IP
+Add data\-based output to TX
+.IP
+outscript=VALUE:SCRIPT
+.IP
+Add raw script output to TX
+.IP
+sign=SIGHASH\-FLAGS
+.IP
+Add zero or more signatures to transaction. This command requires JSON
+registers:prevtxs=JSON object, privatekeys=JSON object. See
+signrawtransaction docs for format of sighash flags, JSON
+objects.
+.PP
+Register Commands:
+.IP
+load=NAME:FILENAME
+.IP
+Load JSON file FILENAME into register NAME
+.IP
+set=NAME:JSON\-STRING
+.IP
+Set register NAME to given JSON\-STRING
+.SH COPYRIGHT
+Copyright (C) 2009-2016 The Bitcoin Core developers
+
+Please contribute if you find Bitcoin Core useful. Visit
+<https://bitcoincore.org> for further information about the software.
+The source code is available from <https://github.com/bitcoin/bitcoin>.
+
+This is experimental software.
+Distributed under the MIT software license, see the accompanying file COPYING
+or <http://www.opensource.org/licenses/mit-license.php>.
+
+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.
diff --git a/doc/man/bitcoind.1 b/doc/man/bitcoind.1
new file mode 100644
index 0000000000..b99657a5fa
--- /dev/null
+++ b/doc/man/bitcoind.1
@@ -0,0 +1,492 @@
+.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.46.5.
+.TH BITCOIND "1" "September 2016" "bitcoind v0.13.0.0" "User Commands"
+.SH NAME
+bitcoind \- manual page for bitcoind v0.13.0.0
+.SH DESCRIPTION
+Bitcoin Core Daemon version v0.13.0.0
+.SS "Usage:"
+.TP
+bitcoind [options]
+Start Bitcoin Core Daemon
+.SH OPTIONS
+.HP
+\-?
+.IP
+Print this help message and exit
+.HP
+\fB\-version\fR
+.IP
+Print version and exit
+.HP
+\fB\-alertnotify=\fR<cmd>
+.IP
+Execute command when a relevant alert is received or we see a really
+long fork (%s in cmd is replaced by message)
+.HP
+\fB\-blocknotify=\fR<cmd>
+.IP
+Execute command when the best block changes (%s in cmd is replaced by
+block hash)
+.HP
+\fB\-checkblocks=\fR<n>
+.IP
+How many blocks to check at startup (default: 288, 0 = all)
+.HP
+\fB\-checklevel=\fR<n>
+.IP
+How thorough the block verification of \fB\-checkblocks\fR is (0\-4, default: 3)
+.HP
+\fB\-conf=\fR<file>
+.IP
+Specify configuration file (default: bitcoin.conf)
+.HP
+\fB\-daemon\fR
+.IP
+Run in the background as a daemon and accept commands
+.HP
+\fB\-datadir=\fR<dir>
+.IP
+Specify data directory
+.HP
+\fB\-dbcache=\fR<n>
+.IP
+Set database cache size in megabytes (4 to 16384, default: 300)
+.HP
+\fB\-loadblock=\fR<file>
+.IP
+Imports blocks from external blk000??.dat file on startup
+.HP
+\fB\-maxorphantx=\fR<n>
+.IP
+Keep at most <n> unconnectable transactions in memory (default: 100)
+.HP
+\fB\-maxmempool=\fR<n>
+.IP
+Keep the transaction memory pool below <n> megabytes (default: 300)
+.HP
+\fB\-mempoolexpiry=\fR<n>
+.IP
+Do not keep transactions in the mempool longer than <n> hours (default:
+72)
+.HP
+\fB\-par=\fR<n>
+.IP
+Set the number of script verification threads (\fB\-4\fR to 16, 0 = auto, <0 =
+leave that many cores free, default: 0)
+.HP
+\fB\-pid=\fR<file>
+.IP
+Specify pid file (default: bitcoind.pid)
+.HP
+\fB\-prune=\fR<n>
+.IP
+Reduce storage requirements by pruning (deleting) old blocks. This mode
+is incompatible with \fB\-txindex\fR and \fB\-rescan\fR. Warning: Reverting
+this setting requires re\-downloading the entire blockchain.
+(default: 0 = disable pruning blocks, >550 = target size in MiB
+to use for block files)
+.HP
+\fB\-reindex\-chainstate\fR
+.IP
+Rebuild chain state from the currently indexed blocks
+.HP
+\fB\-reindex\fR
+.IP
+Rebuild chain state and block index from the blk*.dat files on disk
+.HP
+\fB\-sysperms\fR
+.IP
+Create new files with system default permissions, instead of umask 077
+(only effective with disabled wallet functionality)
+.HP
+\fB\-txindex\fR
+.IP
+Maintain a full transaction index, used by the getrawtransaction rpc
+call (default: 0)
+.PP
+Connection options:
+.HP
+\fB\-addnode=\fR<ip>
+.IP
+Add a node to connect to and attempt to keep the connection open
+.HP
+\fB\-banscore=\fR<n>
+.IP
+Threshold for disconnecting misbehaving peers (default: 100)
+.HP
+\fB\-bantime=\fR<n>
+.IP
+Number of seconds to keep misbehaving peers from reconnecting (default:
+86400)
+.HP
+\fB\-bind=\fR<addr>
+.IP
+Bind to given address and always listen on it. Use [host]:port notation
+for IPv6
+.HP
+\fB\-connect=\fR<ip>
+.IP
+Connect only to the specified node(s)
+.HP
+\fB\-discover\fR
+.IP
+Discover own IP addresses (default: 1 when listening and no \fB\-externalip\fR
+or \fB\-proxy\fR)
+.HP
+\fB\-dns\fR
+.IP
+Allow DNS lookups for \fB\-addnode\fR, \fB\-seednode\fR and \fB\-connect\fR (default: 1)
+.HP
+\fB\-dnsseed\fR
+.IP
+Query for peer addresses via DNS lookup, if low on addresses (default: 1
+unless \fB\-connect\fR)
+.HP
+\fB\-externalip=\fR<ip>
+.IP
+Specify your own public address
+.HP
+\fB\-forcednsseed\fR
+.IP
+Always query for peer addresses via DNS lookup (default: 0)
+.HP
+\fB\-listen\fR
+.IP
+Accept connections from outside (default: 1 if no \fB\-proxy\fR or \fB\-connect\fR)
+.HP
+\fB\-listenonion\fR
+.IP
+Automatically create Tor hidden service (default: 1)
+.HP
+\fB\-maxconnections=\fR<n>
+.IP
+Maintain at most <n> connections to peers (default: 125)
+.HP
+\fB\-maxreceivebuffer=\fR<n>
+.IP
+Maximum per\-connection receive buffer, <n>*1000 bytes (default: 5000)
+.HP
+\fB\-maxsendbuffer=\fR<n>
+.IP
+Maximum per\-connection send buffer, <n>*1000 bytes (default: 1000)
+.HP
+\fB\-maxtimeadjustment\fR
+.IP
+Maximum allowed median peer time offset adjustment. Local perspective of
+time may be influenced by peers forward or backward by this
+amount. (default: 4200 seconds)
+.HP
+\fB\-onion=\fR<ip:port>
+.IP
+Use separate SOCKS5 proxy to reach peers via Tor hidden services
+(default: \fB\-proxy\fR)
+.HP
+\fB\-onlynet=\fR<net>
+.IP
+Only connect to nodes in network <net> (ipv4, ipv6 or onion)
+.HP
+\fB\-permitbaremultisig\fR
+.IP
+Relay non\-P2SH multisig (default: 1)
+.HP
+\fB\-peerbloomfilters\fR
+.IP
+Support filtering of blocks and transaction with bloom filters (default:
+1)
+.HP
+\fB\-port=\fR<port>
+.IP
+Listen for connections on <port> (default: 8333 or testnet: 18333)
+.HP
+\fB\-proxy=\fR<ip:port>
+.IP
+Connect through SOCKS5 proxy
+.HP
+\fB\-proxyrandomize\fR
+.IP
+Randomize credentials for every proxy connection. This enables Tor
+stream isolation (default: 1)
+.HP
+\fB\-seednode=\fR<ip>
+.IP
+Connect to a node to retrieve peer addresses, and disconnect
+.HP
+\fB\-timeout=\fR<n>
+.IP
+Specify connection timeout in milliseconds (minimum: 1, default: 5000)
+.HP
+\fB\-torcontrol=\fR<ip>:<port>
+.IP
+Tor control port to use if onion listening enabled (default:
+127.0.0.1:9051)
+.HP
+\fB\-torpassword=\fR<pass>
+.IP
+Tor control port password (default: empty)
+.HP
+\fB\-whitebind=\fR<addr>
+.IP
+Bind to given address and whitelist peers connecting to it. Use
+[host]:port notation for IPv6
+.HP
+\fB\-whitelist=\fR<netmask>
+.IP
+Whitelist peers connecting from the given netmask or IP address. Can be
+specified multiple times. 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
+.HP
+\fB\-whitelistrelay\fR
+.IP
+Accept relayed transactions received from whitelisted peers even when
+not relaying transactions (default: 1)
+.HP
+\fB\-whitelistforcerelay\fR
+.IP
+Force relay of transactions from whitelisted peers even they violate
+local relay policy (default: 1)
+.HP
+\fB\-maxuploadtarget=\fR<n>
+.IP
+Tries to keep outbound traffic under the given target (in MiB per 24h),
+0 = no limit (default: 0)
+.PP
+Wallet options:
+.HP
+\fB\-disablewallet\fR
+.IP
+Do not load the wallet and disable wallet RPC calls
+.HP
+\fB\-keypool=\fR<n>
+.IP
+Set key pool size to <n> (default: 100)
+.HP
+\fB\-fallbackfee=\fR<amt>
+.IP
+A fee rate (in BTC/kB) that will be used when fee estimation has
+insufficient data (default: 0.0002)
+.HP
+\fB\-mintxfee=\fR<amt>
+.IP
+Fees (in BTC/kB) smaller than this are considered zero fee for
+transaction creation (default: 0.00001)
+.HP
+\fB\-paytxfee=\fR<amt>
+.IP
+Fee (in BTC/kB) to add to transactions you send (default: 0.00)
+.HP
+\fB\-rescan\fR
+.IP
+Rescan the block chain for missing wallet transactions on startup
+.HP
+\fB\-salvagewallet\fR
+.IP
+Attempt to recover private keys from a corrupt wallet on startup
+.HP
+\fB\-spendzeroconfchange\fR
+.IP
+Spend unconfirmed change when sending transactions (default: 1)
+.HP
+\fB\-txconfirmtarget=\fR<n>
+.IP
+If paytxfee is not set, include enough fee so transactions begin
+confirmation on average within n blocks (default: 2)
+.HP
+\fB\-usehd\fR
+.IP
+Use hierarchical deterministic key generation (HD) after BIP32. Only has
+effect during wallet creation/first start (default: 1)
+.HP
+\fB\-upgradewallet\fR
+.IP
+Upgrade wallet to latest format on startup
+.HP
+\fB\-wallet=\fR<file>
+.IP
+Specify wallet file (within data directory) (default: wallet.dat)
+.HP
+\fB\-walletbroadcast\fR
+.IP
+Make the wallet broadcast transactions (default: 1)
+.HP
+\fB\-walletnotify=\fR<cmd>
+.IP
+Execute command when a wallet transaction changes (%s in cmd is replaced
+by TxID)
+.HP
+\fB\-zapwallettxes=\fR<mode>
+.IP
+Delete all wallet transactions and only recover those parts of the
+blockchain through \fB\-rescan\fR on startup (1 = keep tx meta data e.g.
+account owner and payment request information, 2 = drop tx meta
+data)
+.PP
+ZeroMQ notification options:
+.HP
+\fB\-zmqpubhashblock=\fR<address>
+.IP
+Enable publish hash block in <address>
+.HP
+\fB\-zmqpubhashtx=\fR<address>
+.IP
+Enable publish hash transaction in <address>
+.HP
+\fB\-zmqpubrawblock=\fR<address>
+.IP
+Enable publish raw block in <address>
+.HP
+\fB\-zmqpubrawtx=\fR<address>
+.IP
+Enable publish raw transaction in <address>
+.PP
+Debugging/Testing options:
+.HP
+\fB\-uacomment=\fR<cmt>
+.IP
+Append comment to the user agent string
+.HP
+\fB\-debug=\fR<category>
+.IP
+Output debugging information (default: 0, supplying <category> is
+optional). If <category> is not supplied or if <category> = 1,
+output all debugging information.<category> can be: addrman,
+alert, bench, coindb, db, http, libevent, lock, mempool,
+mempoolrej, net, proxy, prune, rand, reindex, rpc, selectcoins,
+tor, zmq.
+.HP
+\fB\-help\-debug\fR
+.IP
+Show all debugging options (usage: \fB\-\-help\fR \fB\-help\-debug\fR)
+.HP
+\fB\-logips\fR
+.IP
+Include IP addresses in debug output (default: 0)
+.HP
+\fB\-logtimestamps\fR
+.IP
+Prepend debug output with timestamp (default: 1)
+.HP
+\fB\-minrelaytxfee=\fR<amt>
+.IP
+Fees (in BTC/kB) smaller than this are considered zero fee for relaying,
+mining and transaction creation (default: 0.00001)
+.HP
+\fB\-maxtxfee=\fR<amt>
+.IP
+Maximum total fees (in BTC) to use in a single wallet transaction or raw
+transaction; setting this too low may abort large transactions
+(default: 0.10)
+.HP
+\fB\-printtoconsole\fR
+.IP
+Send trace/debug info to console instead of debug.log file
+.HP
+\fB\-shrinkdebugfile\fR
+.IP
+Shrink debug.log file on client startup (default: 1 when no \fB\-debug\fR)
+.PP
+Chain selection options:
+.HP
+\fB\-testnet\fR
+.IP
+Use the test chain
+.PP
+Node relay options:
+.HP
+\fB\-bytespersigop\fR
+.IP
+Equivalent bytes per sigop in transactions for relay and mining
+(default: 20)
+.HP
+\fB\-datacarrier\fR
+.IP
+Relay and mine data carrier transactions (default: 1)
+.HP
+\fB\-datacarriersize\fR
+.IP
+Maximum size of data in data carrier transactions we relay and mine
+(default: 83)
+.HP
+\fB\-mempoolreplacement\fR
+.IP
+Enable transaction replacement in the memory pool (default: 1)
+.PP
+Block creation options:
+.HP
+\fB\-blockmaxweight=\fR<n>
+.IP
+Set maximum BIP141 block weight (default: 3000000)
+.HP
+\fB\-blockmaxsize=\fR<n>
+.IP
+Set maximum block size in bytes (default: 750000)
+.HP
+\fB\-blockprioritysize=\fR<n>
+.IP
+Set maximum size of high\-priority/low\-fee transactions in bytes
+(default: 0)
+.PP
+RPC server options:
+.HP
+\fB\-server\fR
+.IP
+Accept command line and JSON\-RPC commands
+.HP
+\fB\-rest\fR
+.IP
+Accept public REST requests (default: 0)
+.HP
+\fB\-rpcbind=\fR<addr>
+.IP
+Bind to given address to listen for JSON\-RPC connections. Use
+[host]:port notation for IPv6. This option can be specified
+multiple times (default: bind to all interfaces)
+.HP
+\fB\-rpccookiefile=\fR<loc>
+.IP
+Location of the auth cookie (default: data dir)
+.HP
+\fB\-rpcuser=\fR<user>
+.IP
+Username for JSON\-RPC connections
+.HP
+\fB\-rpcpassword=\fR<pw>
+.IP
+Password for JSON\-RPC connections
+.HP
+\fB\-rpcauth=\fR<userpw>
+.IP
+Username and hashed password for JSON\-RPC connections. The field
+<userpw> comes in the format: <USERNAME>:<SALT>$<HASH>. A
+canonical python script is included in share/rpcuser. This option
+can be specified multiple times
+.HP
+\fB\-rpcport=\fR<port>
+.IP
+Listen for JSON\-RPC connections on <port> (default: 8332 or testnet:
+18332)
+.HP
+\fB\-rpcallowip=\fR<ip>
+.IP
+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
+.HP
+\fB\-rpcthreads=\fR<n>
+.IP
+Set the number of threads to service RPC calls (default: 4)
+.SH COPYRIGHT
+Copyright (C) 2009-2016 The Bitcoin Core developers
+
+Please contribute if you find Bitcoin Core useful. Visit
+<https://bitcoincore.org> for further information about the software.
+The source code is available from <https://github.com/bitcoin/bitcoin>.
+
+This is experimental software.
+Distributed under the MIT software license, see the accompanying file COPYING
+or <http://www.opensource.org/licenses/mit-license.php>.
+
+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.
diff --git a/doc/reduce-traffic.md b/doc/reduce-traffic.md
index 2d86588eb2..697099beab 100644
--- a/doc/reduce-traffic.md
+++ b/doc/reduce-traffic.md
@@ -19,8 +19,7 @@ This is *not* a hard limit; only a threshold to minimize the outbound
traffic. When the limit is about to be reached, the uploaded data is cut by no
longer serving historic blocks (blocks older than one week).
Keep in mind that new nodes require other nodes that are willing to serve
-historic blocks. **The recommended minimum is 144 blocks per day (max. 144MB
-per day)**
+historic blocks.
Whitelisted peers will never be disconnected, although their traffic counts for
calculating the target.
diff --git a/doc/translation_process.md b/doc/translation_process.md
index d8a85292e8..a443a16fe2 100644
--- a/doc/translation_process.md
+++ b/doc/translation_process.md
@@ -94,7 +94,7 @@ When new plurals are added to the source file, it's important to do the followin
7. Save the source file
### Translating a new language
-To create a new language template, you will need to edit the languages manifest file `src/qt/bitcoin.qrc` and add a new entry. Below is an example of the English language entry.
+To create a new language template, you will need to edit the languages manifest file `src/qt/bitcoin_locale.qrc` and add a new entry. Below is an example of the English language entry.
```xml
<qresource prefix="/translations">
diff --git a/qa/pull-tester/rpc-tests.py b/qa/pull-tester/rpc-tests.py
index 771f6c7a0f..9e187b019b 100755
--- a/qa/pull-tester/rpc-tests.py
+++ b/qa/pull-tester/rpc-tests.py
@@ -251,21 +251,27 @@ class RPCTestHandler:
self.num_running += 1
t = self.test_list.pop(0)
port_seed = ["--portseed=%s" % len(self.test_list)]
+ log_stdout = tempfile.SpooledTemporaryFile(max_size=2**16)
+ log_stderr = tempfile.SpooledTemporaryFile(max_size=2**16)
self.jobs.append((t,
time.time(),
subprocess.Popen((RPC_TESTS_DIR + t).split() + self.flags + port_seed,
universal_newlines=True,
- stdout=subprocess.PIPE,
- stderr=subprocess.PIPE)))
+ stdout=log_stdout,
+ stderr=log_stderr),
+ log_stdout,
+ log_stderr))
if not self.jobs:
raise IndexError('pop from empty list')
while True:
# Return first proc that finishes
time.sleep(.5)
for j in self.jobs:
- (name, time0, proc) = j
+ (name, time0, proc, log_out, log_err) = j
if proc.poll() is not None:
- (stdout, stderr) = proc.communicate(timeout=3)
+ log_out.seek(0), log_err.seek(0)
+ [stdout, stderr] = [l.read().decode('utf-8') for l in (log_out, log_err)]
+ log_out.close(), log_err.close()
passed = stderr == "" and proc.returncode == 0
self.num_running -= 1
self.jobs.remove(j)
diff --git a/qa/rpc-tests/create_cache.py b/qa/rpc-tests/create_cache.py
index b6161e0917..1ace6310d0 100755
--- a/qa/rpc-tests/create_cache.py
+++ b/qa/rpc-tests/create_cache.py
@@ -12,9 +12,15 @@ from test_framework.test_framework import BitcoinTestFramework
class CreateCache(BitcoinTestFramework):
+ def __init__(self):
+ super().__init__()
+
+ # Test network and test nodes are not required:
+ self.num_nodes = 0
+ self.nodes = []
+
def setup_network(self):
- # Don't setup any test nodes
- self.options.noshutdown = True
+ pass
def run_test(self):
pass
diff --git a/qa/rpc-tests/p2p-segwit.py b/qa/rpc-tests/p2p-segwit.py
index eb857ed983..22ec0ad8c8 100755
--- a/qa/rpc-tests/p2p-segwit.py
+++ b/qa/rpc-tests/p2p-segwit.py
@@ -305,13 +305,18 @@ class SegWitTest(BitcoinTestFramework):
sync_blocks(self.nodes)
# We'll add an unnecessary witness to this transaction that would cause
- # it to be too large according to IsStandard.
+ # it to be non-standard, to test that violating policy with a witness before
+ # segwit activation doesn't blind a node to a transaction. Transactions
+ # rejected for having a witness before segwit activation shouldn't be added
+ # to the rejection cache.
tx3 = CTransaction()
tx3.vin.append(CTxIn(COutPoint(tx2.sha256, 0), CScript([p2sh_program])))
tx3.vout.append(CTxOut(tx2.vout[0].nValue-1000, scriptPubKey))
tx3.wit.vtxinwit.append(CTxInWitness())
tx3.wit.vtxinwit[0].scriptWitness.stack = [b'a'*400000]
tx3.rehash()
+ # Note that this should be rejected for the premature witness reason,
+ # rather than a policy check, since segwit hasn't activated yet.
self.std_node.test_transaction_acceptance(tx3, True, False, b'no-witness-yet')
# If we send without witness, it should be accepted.
@@ -949,8 +954,7 @@ class SegWitTest(BitcoinTestFramework):
self.test_node.test_transaction_acceptance(tx, with_witness=True, accepted=False)
# Verify that removing the witness succeeds.
- # Re-announcing won't result in a getdata for ~2.5 minutes, so just
- # deliver the modified transaction.
+ self.test_node.announce_tx_and_wait_for_getdata(tx)
self.test_node.test_transaction_acceptance(tx, with_witness=False, accepted=True)
# Now try to add extra witness data to a valid witness tx.
@@ -964,8 +968,24 @@ class SegWitTest(BitcoinTestFramework):
tx3 = CTransaction()
tx3.vin.append(CTxIn(COutPoint(tx2.sha256, 0), b""))
- tx3.vout.append(CTxOut(tx2.vout[0].nValue-1000, CScript([OP_TRUE])))
tx3.wit.vtxinwit.append(CTxInWitness())
+
+ # Add too-large for IsStandard witness and check that it does not enter reject filter
+ p2sh_program = CScript([OP_TRUE])
+ p2sh_pubkey = hash160(p2sh_program)
+ witness_program2 = CScript([b'a'*400000])
+ tx3.vout.append(CTxOut(tx2.vout[0].nValue-1000, CScript([OP_HASH160, p2sh_pubkey, OP_EQUAL])))
+ tx3.wit.vtxinwit[0].scriptWitness.stack = [witness_program2]
+ tx3.rehash()
+
+ # Node will not be blinded to the transaction
+ self.std_node.announce_tx_and_wait_for_getdata(tx3)
+ self.std_node.test_transaction_acceptance(tx3, True, False, b'tx-size')
+ self.std_node.announce_tx_and_wait_for_getdata(tx3)
+ self.std_node.test_transaction_acceptance(tx3, True, False, b'tx-size')
+
+ # Remove witness stuffing, instead add extra witness push on stack
+ tx3.vout[0] = CTxOut(tx2.vout[0].nValue-1000, CScript([OP_TRUE]))
tx3.wit.vtxinwit[0].scriptWitness.stack = [CScript([CScriptNum(1)]), witness_program ]
tx3.rehash()
diff --git a/qa/rpc-tests/test_framework/script.py b/qa/rpc-tests/test_framework/script.py
index b46c643ccb..83bbf20479 100644
--- a/qa/rpc-tests/test_framework/script.py
+++ b/qa/rpc-tests/test_framework/script.py
@@ -882,7 +882,7 @@ def SignatureHash(script, txTo, inIdx, hashtype):
tmp = txtmp.vout[outIdx]
txtmp.vout = []
for i in range(outIdx):
- txtmp.vout.append(CTxOut())
+ txtmp.vout.append(CTxOut(-1))
txtmp.vout.append(tmp)
for i in range(len(txtmp.vin)):
diff --git a/qa/rpc-tests/test_framework/test_framework.py b/qa/rpc-tests/test_framework/test_framework.py
index a1383729fa..186cf866cf 100755
--- a/qa/rpc-tests/test_framework/test_framework.py
+++ b/qa/rpc-tests/test_framework/test_framework.py
@@ -125,7 +125,8 @@ class BitcoinTestFramework(object):
self.add_options(parser)
(self.options, self.args) = parser.parse_args()
- self.options.tmpdir += '/' + str(self.options.port_seed)
+ # backup dir variable for removal at cleanup
+ self.options.root, self.options.tmpdir = self.options.tmpdir, self.options.tmpdir + '/' + str(self.options.port_seed)
if self.options.trace_rpc:
logging.basicConfig(level=logging.DEBUG, stream=sys.stdout)
@@ -176,6 +177,8 @@ class BitcoinTestFramework(object):
if not self.options.nocleanup and not self.options.noshutdown and success:
print("Cleaning up")
shutil.rmtree(self.options.tmpdir)
+ if not os.listdir(self.options.root):
+ os.rmdir(self.options.root)
else:
print("Not cleaning up dir %s" % self.options.tmpdir)
diff --git a/qa/rpc-tests/test_framework/util.py b/qa/rpc-tests/test_framework/util.py
index 190fa7f661..eee77f1a10 100644
--- a/qa/rpc-tests/test_framework/util.py
+++ b/qa/rpc-tests/test_framework/util.py
@@ -125,12 +125,16 @@ def sync_blocks(rpc_connections, wait=1, timeout=60):
"""
Wait until everybody has the same tip
"""
+ maxheight = 0
while timeout > 0:
- tips = [ x.getbestblockhash() for x in rpc_connections ]
+ tips = [ x.waitforblockheight(maxheight, int(wait * 1000)) for x in rpc_connections ]
+ heights = [ x["height"] for x in tips ]
if tips == [ tips[0] ]*len(tips):
return True
- time.sleep(wait)
+ if heights == [ heights[0] ]*len(heights): #heights are the same but hashes are not
+ raise AssertionError("Block sync failed")
timeout -= wait
+ maxheight = max(heights)
raise AssertionError("Block sync failed")
def sync_mempools(rpc_connections, wait=1, timeout=60):
diff --git a/qa/rpc-tests/wallet-hd.py b/qa/rpc-tests/wallet-hd.py
index c11da1e9a9..a49d91f6f4 100755
--- a/qa/rpc-tests/wallet-hd.py
+++ b/qa/rpc-tests/wallet-hd.py
@@ -39,8 +39,8 @@ class WalletHDTest(BitcoinTestFramework):
self.nodes[1].importprivkey(self.nodes[0].dumpprivkey(non_hd_add))
# This should be enough to keep the master key and the non-HD key
- self.nodes[1].backupwallet(tmpdir + "hd.bak")
- #self.nodes[1].dumpwallet(tmpdir + "hd.dump")
+ self.nodes[1].backupwallet(tmpdir + "/hd.bak")
+ #self.nodes[1].dumpwallet(tmpdir + "/hd.dump")
# Derive some HD addresses and remember the last
# Also send funds to each add
@@ -63,7 +63,7 @@ class WalletHDTest(BitcoinTestFramework):
print("Restore backup ...")
self.stop_node(1)
os.remove(self.options.tmpdir + "/node1/regtest/wallet.dat")
- shutil.copyfile(tmpdir + "hd.bak", tmpdir + "/node1/regtest/wallet.dat")
+ shutil.copyfile(tmpdir + "/hd.bak", tmpdir + "/node1/regtest/wallet.dat")
self.nodes[1] = start_node(1, self.options.tmpdir, self.node_args[1])
#connect_nodes_bi(self.nodes, 0, 1)
diff --git a/qa/rpc-tests/wallet.py b/qa/rpc-tests/wallet.py
index 5d96e7a6e5..3420be1a2e 100755
--- a/qa/rpc-tests/wallet.py
+++ b/qa/rpc-tests/wallet.py
@@ -18,9 +18,10 @@ class WalletTest (BitcoinTestFramework):
super().__init__()
self.setup_clean_chain = True
self.num_nodes = 4
+ self.extra_args = [['-usehd={:d}'.format(i%2==0)] for i in range(4)]
def setup_network(self, split=False):
- self.nodes = start_nodes(3, self.options.tmpdir)
+ self.nodes = start_nodes(3, self.options.tmpdir, self.extra_args[:3])
connect_nodes_bi(self.nodes,0,1)
connect_nodes_bi(self.nodes,1,2)
connect_nodes_bi(self.nodes,0,2)
@@ -154,7 +155,7 @@ class WalletTest (BitcoinTestFramework):
txid2 = self.nodes[1].sendtoaddress(self.nodes[0].getnewaddress(), 1)
sync_mempools(self.nodes)
- self.nodes.append(start_node(3, self.options.tmpdir))
+ self.nodes.append(start_node(3, self.options.tmpdir, self.extra_args[3]))
connect_nodes_bi(self.nodes, 0, 3)
sync_blocks(self.nodes)
diff --git a/qa/rpc-tests/walletbackup.py b/qa/rpc-tests/walletbackup.py
index b991d5c761..e12cb10a50 100755
--- a/qa/rpc-tests/walletbackup.py
+++ b/qa/rpc-tests/walletbackup.py
@@ -45,12 +45,12 @@ class WalletBackupTest(BitcoinTestFramework):
super().__init__()
self.setup_clean_chain = True
self.num_nodes = 4
+ # nodes 1, 2,3 are spenders, let's give them a keypool=100
+ self.extra_args = [["-keypool=100"], ["-keypool=100"], ["-keypool=100"], []]
# This mirrors how the network was setup in the bash test
def setup_network(self, split=False):
- # nodes 1, 2,3 are spenders, let's give them a keypool=100
- extra_args = [["-keypool=100"], ["-keypool=100"], ["-keypool=100"], []]
- self.nodes = start_nodes(self.num_nodes, self.options.tmpdir, extra_args)
+ self.nodes = start_nodes(self.num_nodes, self.options.tmpdir, self.extra_args)
connect_nodes(self.nodes[0], 3)
connect_nodes(self.nodes[1], 3)
connect_nodes(self.nodes[2], 3)
@@ -79,6 +79,7 @@ class WalletBackupTest(BitcoinTestFramework):
# Must sync mempools before mining.
sync_mempools(self.nodes)
self.nodes[3].generate(1)
+ sync_blocks(self.nodes)
# As above, this mirrors the original bash test.
def start_three(self):
diff --git a/share/qt/extract_strings_qt.py b/share/qt/extract_strings_qt.py
index 9624abf1fc..92e0df259e 100755
--- a/share/qt/extract_strings_qt.py
+++ b/share/qt/extract_strings_qt.py
@@ -1,4 +1,7 @@
#!/usr/bin/env python
+# 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.
'''
Extract _("...") strings for translation and convert to Qt stringdefs so that
they can be picked up by Qt linguist.
diff --git a/src/Makefile.am b/src/Makefile.am
index 03fac5bf97..ebdddc87f5 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -71,6 +71,7 @@ endif
.PHONY: FORCE check-symbols check-security
# bitcoin core #
BITCOIN_CORE_H = \
+ addrdb.h \
addrman.h \
base58.h \
bloom.h \
@@ -164,6 +165,7 @@ libbitcoin_server_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) $(MINIUPNPC_CP
libbitcoin_server_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
libbitcoin_server_a_SOURCES = \
addrman.cpp \
+ addrdb.cpp \
bloom.cpp \
blockencodings.cpp \
chain.cpp \
diff --git a/src/addrdb.cpp b/src/addrdb.cpp
new file mode 100644
index 0000000000..ddf41f92de
--- /dev/null
+++ b/src/addrdb.cpp
@@ -0,0 +1,218 @@
+// Copyright (c) 2009-2010 Satoshi Nakamoto
+// Copyright (c) 2009-2015 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include "addrdb.h"
+
+#include "addrman.h"
+#include "chainparams.h"
+#include "clientversion.h"
+#include "hash.h"
+#include "random.h"
+#include "streams.h"
+#include "tinyformat.h"
+#include "util.h"
+
+#include <boost/filesystem.hpp>
+
+CBanDB::CBanDB()
+{
+ pathBanlist = GetDataDir() / "banlist.dat";
+}
+
+bool CBanDB::Write(const banmap_t& banSet)
+{
+ // Generate random temporary filename
+ unsigned short randv = 0;
+ GetRandBytes((unsigned char*)&randv, sizeof(randv));
+ std::string tmpfn = strprintf("banlist.dat.%04x", randv);
+
+ // serialize banlist, checksum data up to that point, then append csum
+ CDataStream ssBanlist(SER_DISK, CLIENT_VERSION);
+ ssBanlist << FLATDATA(Params().MessageStart());
+ ssBanlist << banSet;
+ uint256 hash = Hash(ssBanlist.begin(), ssBanlist.end());
+ ssBanlist << hash;
+
+ // open temp output file, and associate with CAutoFile
+ boost::filesystem::path pathTmp = GetDataDir() / tmpfn;
+ FILE *file = fopen(pathTmp.string().c_str(), "wb");
+ CAutoFile fileout(file, SER_DISK, CLIENT_VERSION);
+ if (fileout.IsNull())
+ return error("%s: Failed to open file %s", __func__, pathTmp.string());
+
+ // Write and commit header, data
+ try {
+ fileout << ssBanlist;
+ }
+ catch (const std::exception& e) {
+ return error("%s: Serialize or I/O error - %s", __func__, e.what());
+ }
+ FileCommit(fileout.Get());
+ fileout.fclose();
+
+ // replace existing banlist.dat, if any, with new banlist.dat.XXXX
+ if (!RenameOver(pathTmp, pathBanlist))
+ return error("%s: Rename-into-place failed", __func__);
+
+ return true;
+}
+
+bool CBanDB::Read(banmap_t& banSet)
+{
+ // open input file, and associate with CAutoFile
+ FILE *file = fopen(pathBanlist.string().c_str(), "rb");
+ CAutoFile filein(file, SER_DISK, CLIENT_VERSION);
+ if (filein.IsNull())
+ return error("%s: Failed to open file %s", __func__, pathBanlist.string());
+
+ // use file size to size memory buffer
+ uint64_t fileSize = boost::filesystem::file_size(pathBanlist);
+ uint64_t dataSize = 0;
+ // Don't try to resize to a negative number if file is small
+ if (fileSize >= sizeof(uint256))
+ dataSize = fileSize - sizeof(uint256);
+ std::vector<unsigned char> vchData;
+ vchData.resize(dataSize);
+ uint256 hashIn;
+
+ // read data and checksum from file
+ try {
+ filein.read((char *)&vchData[0], dataSize);
+ filein >> hashIn;
+ }
+ catch (const std::exception& e) {
+ return error("%s: Deserialize or I/O error - %s", __func__, e.what());
+ }
+ filein.fclose();
+
+ CDataStream ssBanlist(vchData, SER_DISK, CLIENT_VERSION);
+
+ // verify stored checksum matches input data
+ uint256 hashTmp = Hash(ssBanlist.begin(), ssBanlist.end());
+ if (hashIn != hashTmp)
+ return error("%s: Checksum mismatch, data corrupted", __func__);
+
+ unsigned char pchMsgTmp[4];
+ try {
+ // de-serialize file header (network specific magic number) and ..
+ ssBanlist >> FLATDATA(pchMsgTmp);
+
+ // ... verify the network matches ours
+ if (memcmp(pchMsgTmp, Params().MessageStart(), sizeof(pchMsgTmp)))
+ return error("%s: Invalid network magic number", __func__);
+
+ // de-serialize address data into one CAddrMan object
+ ssBanlist >> banSet;
+ }
+ catch (const std::exception& e) {
+ return error("%s: Deserialize or I/O error - %s", __func__, e.what());
+ }
+
+ return true;
+}
+
+CAddrDB::CAddrDB()
+{
+ pathAddr = GetDataDir() / "peers.dat";
+}
+
+bool CAddrDB::Write(const CAddrMan& addr)
+{
+ // Generate random temporary filename
+ unsigned short randv = 0;
+ GetRandBytes((unsigned char*)&randv, sizeof(randv));
+ std::string tmpfn = strprintf("peers.dat.%04x", randv);
+
+ // serialize addresses, checksum data up to that point, then append csum
+ CDataStream ssPeers(SER_DISK, CLIENT_VERSION);
+ ssPeers << FLATDATA(Params().MessageStart());
+ ssPeers << addr;
+ uint256 hash = Hash(ssPeers.begin(), ssPeers.end());
+ ssPeers << hash;
+
+ // open temp output file, and associate with CAutoFile
+ boost::filesystem::path pathTmp = GetDataDir() / tmpfn;
+ FILE *file = fopen(pathTmp.string().c_str(), "wb");
+ CAutoFile fileout(file, SER_DISK, CLIENT_VERSION);
+ if (fileout.IsNull())
+ return error("%s: Failed to open file %s", __func__, pathTmp.string());
+
+ // Write and commit header, data
+ try {
+ fileout << ssPeers;
+ }
+ catch (const std::exception& e) {
+ return error("%s: Serialize or I/O error - %s", __func__, e.what());
+ }
+ FileCommit(fileout.Get());
+ fileout.fclose();
+
+ // replace existing peers.dat, if any, with new peers.dat.XXXX
+ if (!RenameOver(pathTmp, pathAddr))
+ return error("%s: Rename-into-place failed", __func__);
+
+ return true;
+}
+
+bool CAddrDB::Read(CAddrMan& addr)
+{
+ // open input file, and associate with CAutoFile
+ FILE *file = fopen(pathAddr.string().c_str(), "rb");
+ CAutoFile filein(file, SER_DISK, CLIENT_VERSION);
+ if (filein.IsNull())
+ return error("%s: Failed to open file %s", __func__, pathAddr.string());
+
+ // use file size to size memory buffer
+ uint64_t fileSize = boost::filesystem::file_size(pathAddr);
+ uint64_t dataSize = 0;
+ // Don't try to resize to a negative number if file is small
+ if (fileSize >= sizeof(uint256))
+ dataSize = fileSize - sizeof(uint256);
+ std::vector<unsigned char> vchData;
+ vchData.resize(dataSize);
+ uint256 hashIn;
+
+ // read data and checksum from file
+ try {
+ filein.read((char *)&vchData[0], dataSize);
+ filein >> hashIn;
+ }
+ catch (const std::exception& e) {
+ return error("%s: Deserialize or I/O error - %s", __func__, e.what());
+ }
+ filein.fclose();
+
+ CDataStream ssPeers(vchData, SER_DISK, CLIENT_VERSION);
+
+ // verify stored checksum matches input data
+ uint256 hashTmp = Hash(ssPeers.begin(), ssPeers.end());
+ 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 ..
+ ssPeers >> FLATDATA(pchMsgTmp);
+
+ // ... verify the network matches ours
+ if (memcmp(pchMsgTmp, Params().MessageStart(), sizeof(pchMsgTmp)))
+ return error("%s: Invalid network magic number", __func__);
+
+ // de-serialize address data into one CAddrMan object
+ 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());
+ }
+
+ return true;
+}
diff --git a/src/addrdb.h b/src/addrdb.h
new file mode 100644
index 0000000000..d8c66d872b
--- /dev/null
+++ b/src/addrdb.h
@@ -0,0 +1,103 @@
+// Copyright (c) 2009-2010 Satoshi Nakamoto
+// Copyright (c) 2009-2015 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#ifndef BITCOIN_ADDRDB_H
+#define BITCOIN_ADDRDB_H
+
+#include "serialize.h"
+
+#include <string>
+#include <map>
+#include <boost/filesystem/path.hpp>
+
+class CSubNet;
+class CAddrMan;
+class CDataStream;
+
+typedef enum BanReason
+{
+ BanReasonUnknown = 0,
+ BanReasonNodeMisbehaving = 1,
+ BanReasonManuallyAdded = 2
+} BanReason;
+
+class CBanEntry
+{
+public:
+ static const int CURRENT_VERSION=1;
+ int nVersion;
+ int64_t nCreateTime;
+ int64_t nBanUntil;
+ uint8_t banReason;
+
+ CBanEntry()
+ {
+ SetNull();
+ }
+
+ CBanEntry(int64_t nCreateTimeIn)
+ {
+ SetNull();
+ nCreateTime = nCreateTimeIn;
+ }
+
+ 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(nCreateTime);
+ READWRITE(nBanUntil);
+ READWRITE(banReason);
+ }
+
+ void SetNull()
+ {
+ nVersion = CBanEntry::CURRENT_VERSION;
+ nCreateTime = 0;
+ nBanUntil = 0;
+ banReason = BanReasonUnknown;
+ }
+
+ std::string banReasonToString()
+ {
+ switch (banReason) {
+ case BanReasonNodeMisbehaving:
+ return "node misbehaving";
+ case BanReasonManuallyAdded:
+ return "manually added";
+ default:
+ return "unknown";
+ }
+ }
+};
+
+typedef std::map<CSubNet, CBanEntry> banmap_t;
+
+/** Access to the (IP) address database (peers.dat) */
+class CAddrDB
+{
+private:
+ boost::filesystem::path pathAddr;
+public:
+ CAddrDB();
+ bool Write(const CAddrMan& addr);
+ bool Read(CAddrMan& addr);
+ bool Read(CAddrMan& addr, CDataStream& ssPeers);
+};
+
+/** Access to the banlist database (banlist.dat) */
+class CBanDB
+{
+private:
+ boost::filesystem::path pathBanlist;
+public:
+ CBanDB();
+ bool Write(const banmap_t& banSet);
+ bool Read(banmap_t& banSet);
+};
+
+#endif // BITCOIN_ADDRDB_H
diff --git a/src/init.cpp b/src/init.cpp
index 27843fa882..d4af2db1ce 100644
--- a/src/init.cpp
+++ b/src/init.cpp
@@ -42,6 +42,7 @@
#endif
#include <stdint.h>
#include <stdio.h>
+#include <memory>
#ifndef WIN32
#include <signal.h>
@@ -70,6 +71,7 @@ static const bool DEFAULT_REST_ENABLE = false;
static const bool DEFAULT_DISABLE_SAFEMODE = false;
static const bool DEFAULT_STOPAFTERBLOCKIMPORT = false;
+std::unique_ptr<CConnman> g_connman;
#if ENABLE_ZMQ
static CZMQNotificationInterface* pzmqNotificationInterface = NULL;
@@ -197,7 +199,9 @@ void Shutdown()
if (pwalletMain)
pwalletMain->Flush(false);
#endif
- StopNode();
+ MapPort(false);
+ g_connman.reset();
+
StopTorControl();
UnregisterNodeSignals(GetNodeSignals());
@@ -269,20 +273,26 @@ void HandleSIGHUP(int)
fReopenDebugLog = true;
}
-bool static Bind(const CService &addr, unsigned int flags) {
+bool static Bind(CConnman& connman, const CService &addr, unsigned int flags) {
if (!(flags & BF_EXPLICIT) && IsLimited(addr))
return false;
std::string strError;
- if (!BindListenPort(addr, strError, (flags & BF_WHITELIST) != 0)) {
+ if (!connman.BindListenPort(addr, strError, (flags & BF_WHITELIST) != 0)) {
if (flags & BF_REPORT_ERROR)
return InitError(strError);
return false;
}
return true;
}
+void OnRPCStarted()
+{
+ uiInterface.NotifyBlockTip.connect(&RPCNotifyBlockChange);
+}
void OnRPCStopped()
{
+ uiInterface.NotifyBlockTip.disconnect(&RPCNotifyBlockChange);
+ RPCNotifyBlockChange(false, nullptr);
cvBlockChange.notify_all();
LogPrint("rpc", "RPC stopped.\n");
}
@@ -666,6 +676,7 @@ bool InitSanityCheck(void)
bool AppInitServers(boost::thread_group& threadGroup)
{
+ RPCServer::OnStarted(&OnRPCStarted);
RPCServer::OnStopped(&OnRPCStopped);
RPCServer::OnPreCommand(&OnRPCPreCommand);
if (!InitHTTPServer())
@@ -857,7 +868,7 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
// Make sure enough file descriptors are available
int nBind = std::max((int)mapArgs.count("-bind") + (int)mapArgs.count("-whitebind"), 1);
int nUserMaxConnections = GetArg("-maxconnections", DEFAULT_MAX_PEER_CONNECTIONS);
- nMaxConnections = std::max(nUserMaxConnections, 0);
+ int nMaxConnections = std::max(nUserMaxConnections, 0);
// Trim requested connection counts, to fit into system limitations
nMaxConnections = std::max(std::min(nMaxConnections, (int)(FD_SETSIZE - nBind - MIN_CORE_FILEDESCRIPTORS)), 0);
@@ -967,7 +978,7 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
nBytesPerSigOp = GetArg("-bytespersigop", nBytesPerSigOp);
#ifdef ENABLE_WALLET
- if (!CWallet::ParameterInteraction())
+ if (!fDisableWallet && !CWallet::ParameterInteraction())
return false;
#endif // ENABLE_WALLET
@@ -978,6 +989,9 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
// Option to startup with mocktime set (used for regression testing):
SetMockTime(GetArg("-mocktime", 0)); // SetMockTime(0) is a no-op
+ ServiceFlags nLocalServices = NODE_NETWORK;
+ ServiceFlags nRelevantServices = NODE_NETWORK;
+
if (GetBoolArg("-peerbloomfilters", DEFAULT_PEERBLOOMFILTERS))
nLocalServices = ServiceFlags(nLocalServices | NODE_BLOOM);
@@ -1101,6 +1115,10 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
#endif // ENABLE_WALLET
// ********************************************************* Step 6: network initialization
+ assert(!g_connman);
+ g_connman = std::unique_ptr<CConnman>(new CConnman(GetRand(std::numeric_limits<uint64_t>::max()), GetRand(std::numeric_limits<uint64_t>::max())));
+ CConnman& connman = *g_connman;
+
RegisterNodeSignals(GetNodeSignals());
// sanitize comments per BIP-0014, format user agent and check total size
@@ -1138,7 +1156,7 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
LookupSubNet(net.c_str(), subnet);
if (!subnet.IsValid())
return InitError(strprintf(_("Invalid netmask specified in -whitelist: '%s'"), net));
- CNode::AddWhitelistedRange(subnet);
+ connman.AddWhitelistedRange(subnet);
}
}
@@ -1190,7 +1208,7 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
CService addrBind;
if (!Lookup(strBind.c_str(), addrBind, GetListenPort(), false))
return InitError(ResolveErrMsg("bind", strBind));
- fBound |= Bind(addrBind, (BF_EXPLICIT | BF_REPORT_ERROR));
+ fBound |= Bind(connman, addrBind, (BF_EXPLICIT | BF_REPORT_ERROR));
}
BOOST_FOREACH(const std::string& strBind, mapMultiArgs["-whitebind"]) {
CService addrBind;
@@ -1198,14 +1216,14 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
return InitError(ResolveErrMsg("whitebind", strBind));
if (addrBind.GetPort() == 0)
return InitError(strprintf(_("Need to specify a port with -whitebind: '%s'"), strBind));
- fBound |= Bind(addrBind, (BF_EXPLICIT | BF_REPORT_ERROR | BF_WHITELIST));
+ fBound |= Bind(connman, addrBind, (BF_EXPLICIT | BF_REPORT_ERROR | BF_WHITELIST));
}
}
else {
struct in_addr inaddr_any;
inaddr_any.s_addr = INADDR_ANY;
- fBound |= Bind(CService(in6addr_any, GetListenPort()), BF_NONE);
- fBound |= Bind(CService(inaddr_any, GetListenPort()), !fBound ? BF_REPORT_ERROR : BF_NONE);
+ fBound |= Bind(connman, CService(in6addr_any, GetListenPort()), BF_NONE);
+ fBound |= Bind(connman, CService(inaddr_any, GetListenPort()), !fBound ? BF_REPORT_ERROR : BF_NONE);
}
if (!fBound)
return InitError(_("Failed to listen on any port. Use -listen=0 if you want this."));
@@ -1222,7 +1240,7 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
}
BOOST_FOREACH(const std::string& strDest, mapMultiArgs["-seednode"])
- AddOneShot(strDest);
+ connman.AddOneShot(strDest);
#if ENABLE_ZMQ
pzmqNotificationInterface = CZMQNotificationInterface::CreateWithArguments(mapArgs);
@@ -1231,8 +1249,11 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
RegisterValidationInterface(pzmqNotificationInterface);
}
#endif
+ uint64_t nMaxOutboundLimit = 0; //unlimited unless -maxuploadtarget is set
+ uint64_t nMaxOutboundTimeframe = MAX_UPLOAD_TIMEFRAME;
+
if (mapArgs.count("-maxuploadtarget")) {
- CNode::SetMaxOutboundTarget(GetArg("-maxuploadtarget", DEFAULT_MAX_UPLOAD_TARGET)*1024*1024);
+ nMaxOutboundLimit = GetArg("-maxuploadtarget", DEFAULT_MAX_UPLOAD_TARGET)*1024*1024;
}
// ********************************************************* Step 7: load block chain
@@ -1357,6 +1378,7 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
{
LOCK(cs_main);
CBlockIndex* tip = chainActive.Tip();
+ RPCNotifyBlockChange(true, tip);
if (tip && tip->nTime > GetAdjustedTime() + 2 * 60 * 60) {
strLoadError = _("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. "
@@ -1497,7 +1519,28 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
if (GetBoolArg("-listenonion", DEFAULT_LISTEN_ONION))
StartTorControl(threadGroup, scheduler);
- StartNode(threadGroup, scheduler);
+ Discover(threadGroup);
+
+ // Map ports with UPnP
+ MapPort(GetBoolArg("-upnp", DEFAULT_UPNP));
+
+ std::string strNodeError;
+ CConnman::Options connOptions;
+ connOptions.nLocalServices = nLocalServices;
+ connOptions.nRelevantServices = nRelevantServices;
+ connOptions.nMaxConnections = nMaxConnections;
+ connOptions.nMaxOutbound = std::min(MAX_OUTBOUND_CONNECTIONS, connOptions.nMaxConnections);
+ connOptions.nMaxFeeler = 1;
+ connOptions.nBestHeight = chainActive.Height();
+ connOptions.uiInterface = &uiInterface;
+ connOptions.nSendBufferMaxSize = 1000*GetArg("-maxsendbuffer", DEFAULT_MAXSENDBUFFER);
+ connOptions.nReceiveFloodSize = 1000*GetArg("-maxreceivebuffer", DEFAULT_MAXRECEIVEBUFFER);
+
+ connOptions.nMaxOutboundTimeframe = nMaxOutboundTimeframe;
+ connOptions.nMaxOutboundLimit = nMaxOutboundLimit;
+
+ if(!connman.Start(threadGroup, scheduler, strNodeError, connOptions))
+ return InitError(strNodeError);
// ********************************************************* Step 12: finished
diff --git a/src/main.cpp b/src/main.cpp
index aed9591f99..e5ddd31d0c 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -113,6 +113,8 @@ CScript COINBASE_FLAGS;
const string strMessageMagic = "Bitcoin Signed Message:\n";
+static const uint64_t RANDOMIZER_ID_ADDRESS_RELAY = 0x3cac0035b5866b90ULL; // SHA256("main address relay")[0:8]
+
// Internal stuff
namespace {
@@ -325,12 +327,6 @@ CNodeState *State(NodeId pnode) {
return &it->second;
}
-int GetHeight()
-{
- LOCK(cs_main);
- return chainActive.Height();
-}
-
void UpdatePreferredDownload(CNode* node, CNodeState* state)
{
nPreferredDownload -= state->fPreferredDownload;
@@ -348,7 +344,8 @@ void InitializeNode(NodeId nodeid, const CNode *pnode) {
state.address = pnode->addr;
}
-void FinalizeNode(NodeId nodeid) {
+void FinalizeNode(NodeId nodeid, bool& fUpdateConnectionTime) {
+ fUpdateConnectionTime = false;
LOCK(cs_main);
CNodeState *state = State(nodeid);
@@ -356,7 +353,7 @@ void FinalizeNode(NodeId nodeid) {
nSyncStarted--;
if (state->nMisbehavior == 0 && state->fCurrentlyConnected) {
- AddressCurrentlyConnected(state->address);
+ fUpdateConnectionTime = true;
}
BOOST_FOREACH(const QueuedBlock& entry, state->vBlocksInFlight) {
@@ -469,8 +466,8 @@ void UpdateBlockAvailability(NodeId nodeid, const uint256 &hash) {
}
}
-void MaybeSetPeerAsAnnouncingHeaderAndIDs(const CNodeState* nodestate, CNode* pfrom) {
- if (nLocalServices & NODE_WITNESS) {
+void MaybeSetPeerAsAnnouncingHeaderAndIDs(const CNodeState* nodestate, CNode* pfrom, CConnman& connman) {
+ if (pfrom->GetLocalServices() & NODE_WITNESS) {
// Don't ever request compact blocks when segwit is enabled.
return;
}
@@ -483,11 +480,12 @@ void MaybeSetPeerAsAnnouncingHeaderAndIDs(const CNodeState* nodestate, CNode* pf
if (lNodesAnnouncingHeaderAndIDs.size() >= 3) {
// As per BIP152, we only get 3 of our peers to announce
// blocks using compact encodings.
- CNode* pnodeStop = FindNode(lNodesAnnouncingHeaderAndIDs.front());
- if (pnodeStop) {
+ bool found = connman.ForNode(lNodesAnnouncingHeaderAndIDs.front(), [fAnnounceUsingCMPCTBLOCK, nCMPCTBLOCKVersion](CNode* pnodeStop){
pnodeStop->PushMessage(NetMsgType::SENDCMPCT, fAnnounceUsingCMPCTBLOCK, nCMPCTBLOCKVersion);
+ return true;
+ });
+ if(found)
lNodesAnnouncingHeaderAndIDs.pop_front();
- }
}
fAnnounceUsingCMPCTBLOCK = true;
pfrom->PushMessage(NetMsgType::SENDCMPCT, fAnnounceUsingCMPCTBLOCK, nCMPCTBLOCKVersion);
@@ -637,7 +635,6 @@ bool GetNodeStateStats(NodeId nodeid, CNodeStateStats &stats) {
void RegisterNodeSignals(CNodeSignals& nodeSignals)
{
- nodeSignals.GetHeight.connect(&GetHeight);
nodeSignals.ProcessMessages.connect(&ProcessMessages);
nodeSignals.SendMessages.connect(&SendMessages);
nodeSignals.InitializeNode.connect(&InitializeNode);
@@ -646,7 +643,6 @@ void RegisterNodeSignals(CNodeSignals& nodeSignals)
void UnregisterNodeSignals(CNodeSignals& nodeSignals)
{
- nodeSignals.GetHeight.disconnect(&GetHeight);
nodeSignals.ProcessMessages.disconnect(&ProcessMessages);
nodeSignals.SendMessages.disconnect(&SendMessages);
nodeSignals.InitializeNode.disconnect(&InitializeNode);
@@ -1501,9 +1497,9 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState& state, const C
// SCRIPT_VERIFY_CLEANSTACK requires SCRIPT_VERIFY_WITNESS, so we
// need to turn both off, and compare against just turning off CLEANSTACK
// to see if the failure is specifically due to witness validation.
- if (CheckInputs(tx, state, view, true, scriptVerifyFlags & ~(SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_CLEANSTACK), true, txdata) &&
+ if (tx.wit.IsNull() && CheckInputs(tx, state, view, true, scriptVerifyFlags & ~(SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_CLEANSTACK), true, txdata) &&
!CheckInputs(tx, state, view, true, scriptVerifyFlags & ~SCRIPT_VERIFY_CLEANSTACK, true, txdata)) {
- // Only the witness is wrong, so the transaction itself may be fine.
+ // Only the witness is missing, so the transaction itself may be fine.
state.SetCorruptionPossible();
}
return false;
@@ -2068,7 +2064,7 @@ bool UndoReadFromDisk(CBlockUndo& blockundo, const CDiskBlockPos& pos, const uin
// Open history file to read
CAutoFile filein(OpenUndoFile(pos, true), SER_DISK, CLIENT_VERSION);
if (filein.IsNull())
- return error("%s: OpenBlockFile failed", __func__);
+ return error("%s: OpenUndoFile failed", __func__);
// Read block
uint256 hashChecksum;
@@ -2793,7 +2789,7 @@ static int64_t nTimePostConnect = 0;
* Connect a new block to chainActive. pblock is either NULL or a pointer to a CBlock
* corresponding to pindexNew, to bypass loading it again from disk.
*/
-bool static ConnectTip(CValidationState& state, const CChainParams& chainparams, CBlockIndex* pindexNew, const CBlock* pblock, std::list<CTransaction> &txConflicted, std::vector<std::tuple<CTransaction,CBlockIndex*,int> > &txChanged)
+bool static ConnectTip(CValidationState& state, const CChainParams& chainparams, CBlockIndex* pindexNew, const CBlock* pblock, std::list<CTransaction> &txConflicted, std::vector<std::tuple<CTransaction,CBlockIndex*,int>> &txChanged)
{
assert(pindexNew->pprev == chainActive.Tip());
// Read block from disk.
@@ -2835,7 +2831,7 @@ bool static ConnectTip(CValidationState& state, const CChainParams& chainparams,
UpdateTip(pindexNew, chainparams);
for(unsigned int i=0; i < pblock->vtx.size(); i++)
- txChanged.push_back(std::make_tuple(pblock->vtx[i], pindexNew, i));
+ txChanged.emplace_back(pblock->vtx[i], pindexNew, i);
int64_t nTime6 = GetTimeMicros(); nTimePostConnect += nTime6 - nTime5; nTimeTotal += nTime6 - nTime1;
LogPrint("bench", " - Connect postprocess: %.2fms [%.2fs]\n", (nTime6 - nTime5) * 0.001, nTimePostConnect * 0.000001);
@@ -2917,7 +2913,7 @@ 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, bool& fInvalidFound, std::list<CTransaction>& txConflicted, std::vector<std::tuple<CTransaction,CBlockIndex*,int> >& txChanged)
+static bool ActivateBestChainStep(CValidationState& state, const CChainParams& chainparams, CBlockIndex* pindexMostWork, const CBlock* pblock, bool& fInvalidFound, std::list<CTransaction>& txConflicted, std::vector<std::tuple<CTransaction,CBlockIndex*,int>>& txChanged)
{
AssertLockHeld(cs_main);
const CBlockIndex *pindexOldTip = chainActive.Tip();
@@ -3016,17 +3012,20 @@ static void NotifyHeaderTip() {
* or an activated best chain. pblock is either NULL or a pointer to a block
* that is already loaded (to avoid loading it again from disk).
*/
-bool ActivateBestChain(CValidationState &state, const CChainParams& chainparams, const CBlock *pblock) {
+bool ActivateBestChain(CValidationState &state, const CChainParams& chainparams, const CBlock *pblock, CConnman* connman) {
CBlockIndex *pindexMostWork = NULL;
CBlockIndex *pindexNewTip = NULL;
+ std::vector<std::tuple<CTransaction,CBlockIndex*,int>> txChanged;
+ if (pblock)
+ txChanged.reserve(pblock->vtx.size());
do {
+ txChanged.clear();
boost::this_thread::interruption_point();
if (ShutdownRequested())
break;
const CBlockIndex *pindexFork;
std::list<CTransaction> txConflicted;
- std::vector<std::tuple<CTransaction,CBlockIndex*,int> > txChanged;
bool fInitialDownload;
int nNewHeight;
{
@@ -3056,6 +3055,8 @@ bool ActivateBestChain(CValidationState &state, const CChainParams& chainparams,
// When we reach this point, we switched to a new tip (stored in pindexNewTip).
// Notifications/callbacks that can run without cs_main
+ if(connman)
+ connman->SetBestHeight(nNewHeight);
// throw all transactions though the signal-interface
// while _not_ holding the cs_main lock
@@ -3088,15 +3089,14 @@ bool ActivateBestChain(CValidationState &state, const CChainParams& chainparams,
int nBlockEstimate = 0;
if (fCheckpointsEnabled)
nBlockEstimate = Checkpoints::GetTotalBlocksEstimate(chainparams.Checkpoints());
- {
- LOCK(cs_vNodes);
- BOOST_FOREACH(CNode* pnode, vNodes) {
+ if(connman) {
+ connman->ForEachNode([nNewHeight, nBlockEstimate, &vHashes](CNode* pnode) {
if (nNewHeight > (pnode->nStartingHeight != -1 ? pnode->nStartingHeight - 2000 : nBlockEstimate)) {
BOOST_REVERSE_FOREACH(const uint256& hash, vHashes) {
pnode->PushBlockHash(hash);
}
}
- }
+ });
}
// Notify external listeners about the new tip.
if (!vHashes.empty()) {
@@ -3731,7 +3731,7 @@ static bool AcceptBlock(const CBlock& block, CValidationState& state, const CCha
return true;
}
-bool ProcessNewBlock(CValidationState& state, const CChainParams& chainparams, CNode* pfrom, const CBlock* pblock, bool fForceProcessing, const CDiskBlockPos* dbp)
+bool ProcessNewBlock(CValidationState& state, const CChainParams& chainparams, CNode* pfrom, const CBlock* pblock, bool fForceProcessing, const CDiskBlockPos* dbp, CConnman* connman)
{
{
LOCK(cs_main);
@@ -3753,7 +3753,7 @@ bool ProcessNewBlock(CValidationState& state, const CChainParams& chainparams, C
NotifyHeaderTip();
- if (!ActivateBestChain(state, chainparams, pblock))
+ if (!ActivateBestChain(state, chainparams, pblock, connman))
return error("%s: ActivateBestChain failed", __func__);
return true;
@@ -4661,12 +4661,12 @@ std::string GetWarnings(const std::string& strFor)
if (fLargeWorkForkFound)
{
strStatusBar = strRPC = "Warning: The network does not appear to fully agree! Some miners appear to be experiencing issues.";
- strGUI += strGUI.empty() ? "" : uiAlertSeperator + _("Warning: The network does not appear to fully agree! Some miners appear to be experiencing issues.");
+ strGUI += (strGUI.empty() ? "" : uiAlertSeperator) + _("Warning: The network does not appear to fully agree! Some miners appear to be experiencing issues.");
}
else if (fLargeWorkInvalidChainFound)
{
strStatusBar = strRPC = "Warning: We do not appear to fully agree with our peers! You may need to upgrade, or other nodes may need to upgrade.";
- strGUI += strGUI.empty() ? "" : uiAlertSeperator + _("Warning: We do not appear to fully agree with our peers! You may need to upgrade, or other nodes may need to upgrade.");
+ strGUI += (strGUI.empty() ? "" : uiAlertSeperator) + _("Warning: We do not appear to fully agree with our peers! You may need to upgrade, or other nodes may need to upgrade.");
}
if (strFor == "gui")
@@ -4725,9 +4725,45 @@ bool static AlreadyHave(const CInv& inv) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
return true;
}
-void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParams)
+static void RelayTransaction(const CTransaction& tx, CConnman& connman)
+{
+ CInv inv(MSG_TX, tx.GetHash());
+ connman.ForEachNode([&inv](CNode* pnode)
+ {
+ pnode->PushInventory(inv);
+ });
+}
+
+static void RelayAddress(const CAddress& addr, bool fReachable, CConnman& connman)
+{
+ int nRelayNodes = fReachable ? 2 : 1; // limited relaying of addresses outside our network(s)
+
+ // Relay to a limited number of other nodes
+ // Use deterministic randomness to send to the same nodes for 24 hours
+ // at a time so the addrKnowns of the chosen nodes prevent repeats
+ uint64_t hashAddr = addr.GetHash();
+ std::multimap<uint64_t, CNode*> mapMix;
+ const CSipHasher hasher = connman.GetDeterministicRandomizer(RANDOMIZER_ID_ADDRESS_RELAY).Write(hashAddr << 32).Write((GetTime() + hashAddr) / (24*60*60));
+
+ auto sortfunc = [&mapMix, &hasher](CNode* pnode) {
+ if (pnode->nVersion >= CADDR_TIME_VERSION) {
+ uint64_t hashKey = CSipHasher(hasher).Write(pnode->id).Finalize();
+ mapMix.emplace(hashKey, pnode);
+ }
+ };
+
+ auto pushfunc = [&addr, &mapMix, &nRelayNodes] {
+ for (auto mi = mapMix.begin(); mi != mapMix.end() && nRelayNodes-- > 0; ++mi)
+ mi->second->PushAddress(addr);
+ };
+
+ connman.ForEachNodeThen(std::move(sortfunc), std::move(pushfunc));
+}
+
+void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParams, CConnman& connman)
{
std::deque<CInv>::iterator it = pfrom->vRecvGetData.begin();
+ unsigned int nMaxSendBufferSize = connman.GetSendBufferSize();
vector<CInv> vNotFound;
@@ -4735,7 +4771,7 @@ void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParam
while (it != pfrom->vRecvGetData.end()) {
// Don't bother if send buffer is too full to respond anyway
- if (pfrom->nSendSize >= SendBufferSize())
+ if (pfrom->nSendSize >= nMaxSendBufferSize)
break;
const CInv &inv = *it;
@@ -4767,7 +4803,7 @@ void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParam
// disconnect node in case we have reached the outbound limit for serving historical blocks
// never disconnect whitelisted nodes
static const int nOneWeek = 7 * 24 * 60 * 60; // assume > 1 week = historical
- if (send && CNode::OutboundTargetReached(true) && ( ((pindexBestHeader != NULL) && (pindexBestHeader->GetBlockTime() - mi->second->GetBlockTime() > nOneWeek)) || inv.type == MSG_FILTERED_BLOCK) && !pfrom->fWhitelisted)
+ if (send && connman.OutboundTargetReached(true) && ( ((pindexBestHeader != NULL) && (pindexBestHeader->GetBlockTime() - mi->second->GetBlockTime() > nOneWeek)) || inv.type == MSG_FILTERED_BLOCK) && !pfrom->fWhitelisted)
{
LogPrint("net", "historical block serving limit reached, disconnect peer=%d\n", pfrom->GetId());
@@ -4789,10 +4825,16 @@ void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParam
pfrom->PushMessage(NetMsgType::BLOCK, block);
else if (inv.type == MSG_FILTERED_BLOCK)
{
- LOCK(pfrom->cs_filter);
- if (pfrom->pfilter)
+ bool sendMerkleBlock = false;
+ CMerkleBlock merkleBlock;
{
- CMerkleBlock merkleBlock(block, *pfrom->pfilter);
+ LOCK(pfrom->cs_filter);
+ if (pfrom->pfilter) {
+ sendMerkleBlock = true;
+ merkleBlock = CMerkleBlock(block, *pfrom->pfilter);
+ }
+ }
+ if (sendMerkleBlock) {
pfrom->PushMessage(NetMsgType::MERKLEBLOCK, merkleBlock);
// CMerkleBlock just contains hashes, so also push any transactions in the block the client did not see
// This avoids hurting performance by pointlessly requiring a round-trip
@@ -4885,8 +4927,10 @@ uint32_t GetFetchFlags(CNode* pfrom, CBlockIndex* pprev, const Consensus::Params
return nFetchFlags;
}
-bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, int64_t nTimeReceived, const CChainParams& chainparams)
+bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, int64_t nTimeReceived, const CChainParams& chainparams, CConnman& connman)
{
+ unsigned int nMaxSendBufferSize = connman.GetSendBufferSize();
+
LogPrint("net", "received: %s (%u bytes) peer=%d\n", SanitizeString(strCommand), vRecv.size(), pfrom->id);
if (mapArgs.count("-dropmessagestest") && GetRand(atoi(mapArgs["-dropmessagestest"])) == 0)
{
@@ -4895,7 +4939,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
}
- if (!(nLocalServices & NODE_BLOOM) &&
+ if (!(pfrom->GetLocalServices() & NODE_BLOOM) &&
(strCommand == NetMsgType::FILTERLOAD ||
strCommand == NetMsgType::FILTERADD ||
strCommand == NetMsgType::FILTERCLEAR))
@@ -4937,7 +4981,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
pfrom->nServices = ServiceFlags(nServiceInt);
if (!pfrom->fInbound)
{
- addrman.SetServices(pfrom->addr, pfrom->nServices);
+ connman.SetServices(pfrom->addr, pfrom->nServices);
}
if (pfrom->nServicesExpected & ~pfrom->nServices)
{
@@ -4978,7 +5022,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
}
// Disconnect if we connected to ourself
- if (nNonce == nLocalHostNonce && nNonce > 1)
+ if (pfrom->fInbound && !connman.CheckIncomingNonce(nNonce))
{
LogPrintf("connected to self at %s, disconnecting\n", pfrom->addr.ToString());
pfrom->fDisconnect = true;
@@ -5018,7 +5062,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
// Advertise our address
if (fListen && !IsInitialBlockDownload())
{
- CAddress addr = GetLocalAddress(&pfrom->addr);
+ CAddress addr = GetLocalAddress(&pfrom->addr, pfrom->GetLocalServices());
if (addr.IsRoutable())
{
LogPrint("net", "ProcessMessages: advertising address %s\n", addr.ToString());
@@ -5031,18 +5075,12 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
}
// Get recent addresses
- if (pfrom->fOneShot || pfrom->nVersion >= CADDR_TIME_VERSION || addrman.size() < 1000)
+ if (pfrom->fOneShot || pfrom->nVersion >= CADDR_TIME_VERSION || connman.GetAddressCount() < 1000)
{
pfrom->PushMessage(NetMsgType::GETADDR);
pfrom->fGetAddr = true;
}
- addrman.Good(pfrom->addr);
- } else {
- if (((CNetAddr)pfrom->addr) == (CNetAddr)addrFrom)
- {
- addrman.Add(addrFrom, addrFrom);
- addrman.Good(addrFrom);
- }
+ connman.MarkAddressGood(pfrom->addr);
}
pfrom->fSuccessfullyConnected = true;
@@ -5107,7 +5145,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
vRecv >> vAddr;
// Don't want addr from older versions unless seeding
- if (pfrom->nVersion < CADDR_TIME_VERSION && addrman.size() > 1000)
+ if (pfrom->nVersion < CADDR_TIME_VERSION && connman.GetAddressCount() > 1000)
return true;
if (vAddr.size() > 1000)
{
@@ -5134,32 +5172,13 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
if (addr.nTime > nSince && !pfrom->fGetAddr && vAddr.size() <= 10 && addr.IsRoutable())
{
// Relay to a limited number of other nodes
- {
- 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 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();
- 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;
- 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<uint64_t, CNode*>::iterator mi = mapMix.begin(); mi != mapMix.end() && nRelayNodes-- > 0; ++mi)
- ((*mi).second)->PushAddress(addr);
- }
+ RelayAddress(addr, fReachable, connman);
}
// Do not store addresses outside our network
if (fReachable)
vAddrOk.push_back(addr);
}
- addrman.Add(vAddrOk, pfrom->addr, 2 * 60 * 60);
+ connman.AddNewAddresses(vAddrOk, pfrom->addr, 2 * 60 * 60);
if (vAddr.size() < 1000)
pfrom->fGetAddr = false;
if (pfrom->fOneShot)
@@ -5238,7 +5257,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
nodestate->nBlocksInFlight < MAX_BLOCKS_IN_TRANSIT_PER_PEER &&
(!IsWitnessEnabled(chainActive.Tip(), chainparams.GetConsensus()) || State(pfrom->GetId())->fHaveWitness)) {
inv.type |= nFetchFlags;
- if (nodestate->fProvidesHeaderAndIDs && !(nLocalServices & NODE_WITNESS))
+ if (nodestate->fProvidesHeaderAndIDs && !(pfrom->GetLocalServices() & NODE_WITNESS))
vToFetch.push_back(CInv(MSG_CMPCT_BLOCK, inv.hash));
else
vToFetch.push_back(inv);
@@ -5261,7 +5280,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
// Track requests for our stuff
GetMainSignals().Inventory(inv.hash);
- if (pfrom->nSendSize > (SendBufferSize() * 2)) {
+ if (pfrom->nSendSize > (nMaxSendBufferSize * 2)) {
Misbehaving(pfrom->GetId(), 50);
return error("send buffer size() = %u", pfrom->nSendSize);
}
@@ -5290,7 +5309,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
LogPrint("net", "received getdata for: %s peer=%d\n", vInv[0].ToString(), pfrom->id);
pfrom->vRecvGetData.insert(pfrom->vRecvGetData.end(), vInv.begin(), vInv.end());
- ProcessGetData(pfrom, chainparams.GetConsensus());
+ ProcessGetData(pfrom, chainparams.GetConsensus(), connman);
}
@@ -5447,7 +5466,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
if (!AlreadyHave(inv) && AcceptToMemoryPool(mempool, state, tx, true, &fMissingInputs)) {
mempool.check(pcoinsTip);
- RelayTransaction(tx);
+ RelayTransaction(tx, connman);
for (unsigned int i = 0; i < tx.vout.size(); i++) {
vWorkQueue.emplace_back(inv.hash, i);
}
@@ -5484,7 +5503,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
continue;
if (AcceptToMemoryPool(mempool, stateDummy, orphanTx, true, &fMissingInputs2)) {
LogPrint("mempool", " accepted orphan tx %s\n", orphanHash.ToString());
- RelayTransaction(orphanTx);
+ RelayTransaction(orphanTx, connman);
for (unsigned int i = 0; i < orphanTx.vout.size(); i++) {
vWorkQueue.emplace_back(orphanHash, i);
}
@@ -5493,7 +5512,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
else if (!fMissingInputs2)
{
int nDos = 0;
- if (stateDummy.IsInvalid(nDos) && nDos > 0 && (!state.CorruptionPossible() || State(fromPeer)->fHaveWitness))
+ if (stateDummy.IsInvalid(nDos) && nDos > 0)
{
// Punish peer that gave us an invalid orphan tx
Misbehaving(fromPeer, nDos);
@@ -5504,7 +5523,10 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
// Probably non-standard or insufficient fee/priority
LogPrint("mempool", " removed orphan tx %s\n", orphanHash.ToString());
vEraseQueue.push_back(orphanHash);
- if (!stateDummy.CorruptionPossible()) {
+ if (orphanTx.wit.IsNull() && !stateDummy.CorruptionPossible()) {
+ // Do not use rejection cache for witness transactions or
+ // witness-stripped transactions, as they can have been malleated.
+ // See https://github.com/bitcoin/bitcoin/issues/8279 for details.
assert(recentRejects);
recentRejects->insert(orphanHash);
}
@@ -5542,7 +5564,10 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
LogPrint("mempool", "not keeping orphan with rejected parents %s\n",tx.GetHash().ToString());
}
} else {
- if (!state.CorruptionPossible()) {
+ if (tx.wit.IsNull() && !state.CorruptionPossible()) {
+ // Do not use rejection cache for witness transactions or
+ // witness-stripped transactions, as they can have been malleated.
+ // See https://github.com/bitcoin/bitcoin/issues/8279 for details.
assert(recentRejects);
recentRejects->insert(tx.GetHash());
}
@@ -5559,7 +5584,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
int nDoS = 0;
if (!state.IsInvalid(nDoS) || nDoS == 0) {
LogPrintf("Force relaying tx %s from whitelisted peer=%d\n", tx.GetHash().ToString(), pfrom->id);
- RelayTransaction(tx);
+ RelayTransaction(tx, connman);
} else {
LogPrintf("Not relaying invalid transaction %s from whitelisted peer=%d (%s)\n", tx.GetHash().ToString(), pfrom->id, FormatStateMessage(state));
}
@@ -5574,9 +5599,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
if (state.GetRejectCode() < REJECT_INTERNAL) // Never send AcceptToMemoryPool's internal codes over P2P
pfrom->PushMessage(NetMsgType::REJECT, strCommand, (unsigned char)state.GetRejectCode(),
state.GetRejectReason().substr(0, MAX_REJECT_MESSAGE_LENGTH), inv.hash);
- if (nDoS > 0 && (!state.CorruptionPossible() || State(pfrom->id)->fHaveWitness)) {
- // When a non-witness-supporting peer gives us a transaction that would
- // be accepted if witness validation was off, we can't blame them for it.
+ if (nDoS > 0) {
Misbehaving(pfrom->GetId(), nDoS);
}
}
@@ -5680,7 +5703,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
txn.blockhash = cmpctblock.header.GetHash();
CDataStream blockTxnMsg(SER_NETWORK, PROTOCOL_VERSION);
blockTxnMsg << txn;
- return ProcessMessage(pfrom, NetMsgType::BLOCKTXN, blockTxnMsg, nTimeReceived, chainparams);
+ return ProcessMessage(pfrom, NetMsgType::BLOCKTXN, blockTxnMsg, nTimeReceived, chainparams, connman);
} else {
req.blockhash = pindex->GetBlockHash();
pfrom->PushMessage(NetMsgType::GETBLOCKTXN, req);
@@ -5701,7 +5724,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
headers.push_back(cmpctblock.header);
CDataStream vHeadersMsg(SER_NETWORK, PROTOCOL_VERSION);
vHeadersMsg << headers;
- return ProcessMessage(pfrom, NetMsgType::HEADERS, vHeadersMsg, nTimeReceived, chainparams);
+ return ProcessMessage(pfrom, NetMsgType::HEADERS, vHeadersMsg, nTimeReceived, chainparams, connman);
}
}
@@ -5737,7 +5760,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
pfrom->PushMessage(NetMsgType::GETDATA, invs);
} else {
CValidationState state;
- ProcessNewBlock(state, chainparams, pfrom, &block, false, NULL);
+ ProcessNewBlock(state, chainparams, pfrom, &block, false, NULL, &connman);
int nDoS;
if (state.IsInvalid(nDoS)) {
assert (state.GetRejectCode() < REJECT_INTERNAL); // Blocks are never rejected with internal reject codes
@@ -5882,10 +5905,10 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
pindexLast->GetBlockHash().ToString(), pindexLast->nHeight);
}
if (vGetData.size() > 0) {
- if (nodestate->fProvidesHeaderAndIDs && vGetData.size() == 1 && mapBlocksInFlight.size() == 1 && pindexLast->pprev->IsValid(BLOCK_VALID_CHAIN) && !(nLocalServices & NODE_WITNESS)) {
+ if (nodestate->fProvidesHeaderAndIDs && vGetData.size() == 1 && mapBlocksInFlight.size() == 1 && pindexLast->pprev->IsValid(BLOCK_VALID_CHAIN) && !(pfrom->GetLocalServices() & NODE_WITNESS)) {
// We seem to be rather well-synced, so it appears pfrom was the first to provide us
// with this block! Let's get them to announce using compact blocks in the future.
- MaybeSetPeerAsAnnouncingHeaderAndIDs(nodestate, pfrom);
+ MaybeSetPeerAsAnnouncingHeaderAndIDs(nodestate, pfrom, connman);
// In any case, we want to download using a compact block, not a regular one
vGetData[0] = CInv(MSG_CMPCT_BLOCK, vGetData[0].hash);
}
@@ -5913,7 +5936,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
// Such an unrequested block may still be processed, subject to the
// conditions in AcceptBlock().
bool forceProcessing = pfrom->fWhitelisted && !IsInitialBlockDownload();
- ProcessNewBlock(state, chainparams, pfrom, &block, forceProcessing, NULL);
+ ProcessNewBlock(state, chainparams, pfrom, &block, forceProcessing, NULL, &connman);
int nDoS;
if (state.IsInvalid(nDoS)) {
assert (state.GetRejectCode() < REJECT_INTERNAL); // Blocks are never rejected with internal reject codes
@@ -5949,7 +5972,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
pfrom->fSentAddr = true;
pfrom->vAddrToSend.clear();
- vector<CAddress> vAddr = addrman.GetAddr();
+ vector<CAddress> vAddr = connman.GetAddresses();
BOOST_FOREACH(const CAddress &addr, vAddr)
pfrom->PushAddress(addr);
}
@@ -5957,14 +5980,14 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
else if (strCommand == NetMsgType::MEMPOOL)
{
- if (!(nLocalServices & NODE_BLOOM) && !pfrom->fWhitelisted)
+ if (!(pfrom->GetLocalServices() & 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)
+ if (connman.OutboundTargetReached(false) && !pfrom->fWhitelisted)
{
LogPrint("net", "mempool request with bandwidth limit reached, disconnect peer=%d\n", pfrom->GetId());
pfrom->fDisconnect = true;
@@ -6060,8 +6083,6 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
CBloomFilter filter;
vRecv >> filter;
- LOCK(pfrom->cs_filter);
-
if (!filter.IsWithinSizeConstraints())
{
// There is no excuse for sending a too-large filter
@@ -6070,11 +6091,12 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
}
else
{
+ LOCK(pfrom->cs_filter);
delete pfrom->pfilter;
pfrom->pfilter = new CBloomFilter(filter);
pfrom->pfilter->UpdateEmptyFull();
+ pfrom->fRelayTxes = true;
}
- pfrom->fRelayTxes = true;
}
@@ -6085,20 +6107,21 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
// Nodes must NEVER send a data item > 520 bytes (the max size for a script data object,
// and thus, the maximum size any matched object can have) in a filteradd message
- if (vData.size() > MAX_SCRIPT_ELEMENT_SIZE)
- {
- LOCK(cs_main);
- Misbehaving(pfrom->GetId(), 100);
+ bool bad = false;
+ if (vData.size() > MAX_SCRIPT_ELEMENT_SIZE) {
+ bad = true;
} else {
LOCK(pfrom->cs_filter);
- if (pfrom->pfilter)
+ if (pfrom->pfilter) {
pfrom->pfilter->insert(vData);
- else
- {
- LOCK(cs_main);
- Misbehaving(pfrom->GetId(), 100);
+ } else {
+ bad = true;
}
}
+ if (bad) {
+ LOCK(cs_main);
+ Misbehaving(pfrom->GetId(), 100);
+ }
}
@@ -6163,9 +6186,10 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
}
// requires LOCK(cs_vRecvMsg)
-bool ProcessMessages(CNode* pfrom)
+bool ProcessMessages(CNode* pfrom, CConnman& connman)
{
const CChainParams& chainparams = Params();
+ unsigned int nMaxSendBufferSize = connman.GetSendBufferSize();
//if (fDebug)
// LogPrintf("%s(%u messages)\n", __func__, pfrom->vRecvMsg.size());
@@ -6180,7 +6204,7 @@ bool ProcessMessages(CNode* pfrom)
bool fOk = true;
if (!pfrom->vRecvGetData.empty())
- ProcessGetData(pfrom, chainparams.GetConsensus());
+ ProcessGetData(pfrom, chainparams.GetConsensus(), connman);
// this maintains the order of responses
if (!pfrom->vRecvGetData.empty()) return fOk;
@@ -6188,7 +6212,7 @@ bool ProcessMessages(CNode* pfrom)
std::deque<CNetMessage>::iterator it = pfrom->vRecvMsg.begin();
while (!pfrom->fDisconnect && it != pfrom->vRecvMsg.end()) {
// Don't bother if send buffer is too full to respond anyway
- if (pfrom->nSendSize >= SendBufferSize())
+ if (pfrom->nSendSize >= nMaxSendBufferSize)
break;
// get next message
@@ -6240,7 +6264,7 @@ bool ProcessMessages(CNode* pfrom)
bool fRet = false;
try
{
- fRet = ProcessMessage(pfrom, strCommand, vRecv, msg.nTime, chainparams);
+ fRet = ProcessMessage(pfrom, strCommand, vRecv, msg.nTime, chainparams, connman);
boost::this_thread::interruption_point();
}
catch (const std::ios_base::failure& e)
@@ -6305,7 +6329,7 @@ public:
}
};
-bool SendMessages(CNode* pto)
+bool SendMessages(CNode* pto, CConnman& connman)
{
const Consensus::Params& consensusParams = Params().GetConsensus();
{
@@ -6392,7 +6416,7 @@ bool SendMessages(CNode* pto)
LogPrintf("Warning: not banning local peer %s!\n", pto->addr.ToString());
else
{
- CNode::Ban(pto->addr, BanReasonNodeMisbehaving);
+ connman.Ban(pto->addr, BanReasonNodeMisbehaving);
}
}
state.fShouldBan = false;
@@ -6431,7 +6455,7 @@ bool SendMessages(CNode* pto)
// transactions become unconfirmed and spams other nodes.
if (!fReindex && !fImporting && !IsInitialBlockDownload())
{
- GetMainSignals().Broadcast(nTimeBestReceived);
+ GetMainSignals().Broadcast(nTimeBestReceived, &connman);
}
//
diff --git a/src/main.h b/src/main.h
index 18d674612f..2646d8f86b 100644
--- a/src/main.h
+++ b/src/main.h
@@ -34,6 +34,7 @@ class CBlockTreeDB;
class CBloomFilter;
class CChainParams;
class CInv;
+class CConnman;
class CScriptCheck;
class CTxMemPool;
class CValidationInterface;
@@ -222,7 +223,7 @@ void UnregisterNodeSignals(CNodeSignals& nodeSignals);
* @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, CNode* pfrom, const CBlock* pblock, bool fForceProcessing, const CDiskBlockPos* dbp);
+bool ProcessNewBlock(CValidationState& state, const CChainParams& chainparams, CNode* pfrom, const CBlock* pblock, bool fForceProcessing, const CDiskBlockPos* dbp, CConnman* connman);
/** Check whether enough disk space is available for an incoming block */
bool CheckDiskSpace(uint64_t nAdditionalBytes = 0);
/** Open a block file (blk?????.dat) */
@@ -240,13 +241,14 @@ bool LoadBlockIndex();
/** Unload database information */
void UnloadBlockIndex();
/** Process protocol messages received from a given node */
-bool ProcessMessages(CNode* pfrom);
+bool ProcessMessages(CNode* pfrom, CConnman& connman);
/**
* Send queued protocol messages to be sent to a give node.
*
* @param[in] pto The node which we are sending messages to.
+ * @param[in] connman The connection manager for that node.
*/
-bool SendMessages(CNode* pto);
+bool SendMessages(CNode* pto, CConnman& connman);
/** Run an instance of the script checking thread */
void ThreadScriptCheck();
/** Check whether we are doing an initial block download (synchronizing from disk or network) */
@@ -262,7 +264,7 @@ std::string GetWarnings(const std::string& strFor);
/** Retrieve a transaction (from memory pool, or from disk, if possible) */
bool GetTransaction(const uint256 &hash, CTransaction &tx, const Consensus::Params& params, uint256 &hashBlock, bool fAllowSlow = false);
/** Find the best known block, and make it the tip of the block chain */
-bool ActivateBestChain(CValidationState& state, const CChainParams& chainparams, const CBlock* pblock = NULL);
+bool ActivateBestChain(CValidationState& state, const CChainParams& chainparams, const CBlock* pblock = NULL, CConnman* connman = NULL);
CAmount GetBlockSubsidy(int nHeight, const Consensus::Params& consensusParams);
/**
diff --git a/src/net.cpp b/src/net.cpp
index c5b080f794..cce06f2d64 100644
--- a/src/net.cpp
+++ b/src/net.cpp
@@ -61,63 +61,28 @@
#endif
#endif
-
-namespace {
- const int MAX_OUTBOUND_CONNECTIONS = 8;
- const int MAX_FEELER_CONNECTIONS = 1;
-
- struct ListenSocket {
- SOCKET socket;
- bool whitelisted;
-
- ListenSocket(SOCKET _socket, bool _whitelisted) : socket(_socket), whitelisted(_whitelisted) {}
- };
-}
-
const static std::string NET_MESSAGE_COMMAND_OTHER = "*other*";
-/** Services this node implementation cares about */
-ServiceFlags nRelevantServices = NODE_NETWORK;
-
+static const uint64_t RANDOMIZER_ID_NETGROUP = 0x6c0edd8036ef4036ULL; // SHA256("netgroup")[0:8]
//
// Global state variables
//
bool fDiscover = true;
bool fListen = true;
-ServiceFlags nLocalServices = NODE_NETWORK;
bool fRelayTxes = true;
CCriticalSection cs_mapLocalHost;
std::map<CNetAddr, LocalServiceInfo> mapLocalHost;
static bool vfLimited[NET_MAX] = {};
static CNode* pnodeLocalHost = NULL;
-uint64_t nLocalHostNonce = 0;
-static std::vector<ListenSocket> vhListenSocket;
-CAddrMan addrman;
-int nMaxConnections = DEFAULT_MAX_PEER_CONNECTIONS;
-bool fAddressesInitialized = false;
std::string strSubVersion;
-std::vector<CNode*> vNodes;
-CCriticalSection cs_vNodes;
limitedmap<uint256, int64_t> mapAlreadyAskedFor(MAX_INV_SZ);
-static std::deque<std::string> vOneShots;
-CCriticalSection cs_vOneShots;
-
-std::vector<std::string> vAddedNodes;
-CCriticalSection cs_vAddedNodes;
-
-NodeId nLastNodeId = 0;
-CCriticalSection cs_nLastNodeId;
-
-static CSemaphore *semOutbound = NULL;
-boost::condition_variable messageHandlerCondition;
-
// Signals for message handling
static CNodeSignals g_signals;
CNodeSignals& GetNodeSignals() { return g_signals; }
-void AddOneShot(const std::string& strDest)
+void CConnman::AddOneShot(const std::string& strDest)
{
LOCK(cs_vOneShots);
vOneShots.push_back(strDest);
@@ -178,7 +143,7 @@ static std::vector<CAddress> convertSeed6(const std::vector<SeedSpec6> &vSeedsIn
// Otherwise, return the unroutable 0.0.0.0 but filled in with
// the normal parameters, since the IP may be changed to a useful
// one by discovery.
-CAddress GetLocalAddress(const CNetAddr *paddrPeer)
+CAddress GetLocalAddress(const CNetAddr *paddrPeer, ServiceFlags nLocalServices)
{
CAddress ret(CService(CNetAddr(),GetListenPort()), NODE_NONE);
CService addr;
@@ -210,7 +175,7 @@ void AdvertiseLocal(CNode *pnode)
{
if (fListen && pnode->fSuccessfullyConnected)
{
- CAddress addrLocal = GetLocalAddress(&pnode->addr);
+ CAddress addrLocal = GetLocalAddress(&pnode->addr, pnode->GetLocalServices());
// If discovery is enabled, sometimes give our peer the address it
// tells us that it sees us as in case it has a better idea of our
// address than we do.
@@ -321,23 +286,8 @@ bool IsReachable(const CNetAddr& addr)
return IsReachable(net);
}
-void AddressCurrentlyConnected(const CService& addr)
-{
- addrman.Connected(addr);
-}
-
-
-uint64_t CNode::nTotalBytesRecv = 0;
-uint64_t CNode::nTotalBytesSent = 0;
-CCriticalSection CNode::cs_totalBytesRecv;
-CCriticalSection CNode::cs_totalBytesSent;
-
-uint64_t CNode::nMaxOutboundLimit = 0;
-uint64_t CNode::nMaxOutboundTotalBytesSentInCycle = 0;
-uint64_t CNode::nMaxOutboundTimeframe = 60*60*24; //1 day
-uint64_t CNode::nMaxOutboundCycleStartTime = 0;
-CNode* FindNode(const CNetAddr& ip)
+CNode* CConnman::FindNode(const CNetAddr& ip)
{
LOCK(cs_vNodes);
BOOST_FOREACH(CNode* pnode, vNodes)
@@ -346,7 +296,7 @@ CNode* FindNode(const CNetAddr& ip)
return NULL;
}
-CNode* FindNode(const CSubNet& subNet)
+CNode* CConnman::FindNode(const CSubNet& subNet)
{
LOCK(cs_vNodes);
BOOST_FOREACH(CNode* pnode, vNodes)
@@ -355,7 +305,7 @@ CNode* FindNode(const CSubNet& subNet)
return NULL;
}
-CNode* FindNode(const std::string& addrName)
+CNode* CConnman::FindNode(const std::string& addrName)
{
LOCK(cs_vNodes);
BOOST_FOREACH(CNode* pnode, vNodes)
@@ -364,7 +314,7 @@ CNode* FindNode(const std::string& addrName)
return NULL;
}
-CNode* FindNode(const CService& addr)
+CNode* CConnman::FindNode(const CService& addr)
{
LOCK(cs_vNodes);
BOOST_FOREACH(CNode* pnode, vNodes)
@@ -373,17 +323,17 @@ CNode* FindNode(const CService& addr)
return NULL;
}
-//TODO: This is used in only one place in main, and should be removed
-CNode* FindNode(const NodeId nodeid)
+bool CConnman::CheckIncomingNonce(uint64_t nonce)
{
LOCK(cs_vNodes);
- BOOST_FOREACH(CNode* pnode, vNodes)
- if (pnode->GetId() == nodeid)
- return (pnode);
- return NULL;
+ BOOST_FOREACH(CNode* pnode, vNodes) {
+ if (!pnode->fSuccessfullyConnected && !pnode->fInbound && pnode->GetLocalNonce() == nonce)
+ return false;
+ }
+ return true;
}
-CNode* ConnectNode(CAddress addrConnect, const char *pszDest, bool fCountFailure)
+CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCountFailure)
{
if (pszDest == NULL) {
if (IsLocal(addrConnect))
@@ -438,7 +388,8 @@ CNode* ConnectNode(CAddress addrConnect, const char *pszDest, bool fCountFailure
addrman.Attempt(addrConnect, fCountFailure);
// Add node
- CNode* pnode = new CNode(hSocket, addrConnect, pszDest ? pszDest : "", false);
+ CNode* pnode = new CNode(GetNewNodeId(), nLocalServices, GetBestHeight(), hSocket, addrConnect, CalculateKeyedNetGroup(addrConnect), pszDest ? pszDest : "", false);
+ GetNodeSignals().InitializeNode(pnode->GetId(), pnode);
pnode->AddRef();
{
@@ -459,21 +410,21 @@ CNode* ConnectNode(CAddress addrConnect, const char *pszDest, bool fCountFailure
return NULL;
}
-static void DumpBanlist()
+void CConnman::DumpBanlist()
{
- CNode::SweepBanned(); // clean unused entries (if bantime has expired)
+ SweepBanned(); // clean unused entries (if bantime has expired)
- if (!CNode::BannedSetIsDirty())
+ if (!BannedSetIsDirty())
return;
int64_t nStart = GetTimeMillis();
CBanDB bandb;
banmap_t banmap;
- CNode::SetBannedSetDirty(false);
- CNode::GetBanned(banmap);
+ SetBannedSetDirty(false);
+ GetBanned(banmap);
if (!bandb.Write(banmap))
- CNode::SetBannedSetDirty(true);
+ SetBannedSetDirty(true);
LogPrint("net", "Flushed %d banned node ips/subnets to banlist.dat %dms\n",
banmap.size(), GetTimeMillis() - nStart);
@@ -496,29 +447,22 @@ void CNode::CloseSocketDisconnect()
void CNode::PushVersion()
{
- int nBestHeight = GetNodeSignals().GetHeight().get_value_or(0);
-
int64_t nTime = (fInbound ? GetAdjustedTime() : GetTime());
CAddress addrYou = (addr.IsRoutable() && !IsProxy(addr) ? addr : CAddress(CService(), addr.nServices));
- CAddress addrMe = GetLocalAddress(&addr);
- GetRandBytes((unsigned char*)&nLocalHostNonce, sizeof(nLocalHostNonce));
+ CAddress addrMe = CAddress(CService(), nLocalServices);
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);
+ LogPrint("net", "send version message: version %d, blocks=%d, us=%s, them=%s, peer=%d\n", PROTOCOL_VERSION, nMyStartingHeight, 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);
+ LogPrint("net", "send version message: version %d, blocks=%d, us=%s, peer=%d\n", PROTOCOL_VERSION, nMyStartingHeight, addrMe.ToString(), id);
PushMessage(NetMsgType::VERSION, PROTOCOL_VERSION, (uint64_t)nLocalServices, nTime, addrYou, addrMe,
- nLocalHostNonce, strSubVersion, nBestHeight, ::fRelayTxes);
+ nLocalHostNonce, strSubVersion, nMyStartingHeight, ::fRelayTxes);
}
-banmap_t CNode::setBanned;
-CCriticalSection CNode::cs_setBanned;
-bool CNode::setBannedIsDirty;
-
-void CNode::ClearBanned()
+void CConnman::ClearBanned()
{
{
LOCK(cs_setBanned);
@@ -526,10 +470,11 @@ void CNode::ClearBanned()
setBannedIsDirty = true;
}
DumpBanlist(); //store banlist to disk
- uiInterface.BannedListChanged();
+ if(clientInterface)
+ clientInterface->BannedListChanged();
}
-bool CNode::IsBanned(CNetAddr ip)
+bool CConnman::IsBanned(CNetAddr ip)
{
bool fResult = false;
{
@@ -546,7 +491,7 @@ bool CNode::IsBanned(CNetAddr ip)
return fResult;
}
-bool CNode::IsBanned(CSubNet subnet)
+bool CConnman::IsBanned(CSubNet subnet)
{
bool fResult = false;
{
@@ -562,12 +507,12 @@ bool CNode::IsBanned(CSubNet subnet)
return fResult;
}
-void CNode::Ban(const CNetAddr& addr, const BanReason &banReason, int64_t bantimeoffset, bool sinceUnixEpoch) {
+void CConnman::Ban(const CNetAddr& addr, const BanReason &banReason, int64_t bantimeoffset, bool sinceUnixEpoch) {
CSubNet subNet(addr);
Ban(subNet, banReason, bantimeoffset, sinceUnixEpoch);
}
-void CNode::Ban(const CSubNet& subNet, const BanReason &banReason, int64_t bantimeoffset, bool sinceUnixEpoch) {
+void CConnman::Ban(const CSubNet& subNet, const BanReason &banReason, int64_t bantimeoffset, bool sinceUnixEpoch) {
CBanEntry banEntry(GetTime());
banEntry.banReason = banReason;
if (bantimeoffset <= 0)
@@ -586,7 +531,8 @@ void CNode::Ban(const CSubNet& subNet, const BanReason &banReason, int64_t banti
else
return;
}
- uiInterface.BannedListChanged();
+ if(clientInterface)
+ clientInterface->BannedListChanged();
{
LOCK(cs_vNodes);
BOOST_FOREACH(CNode* pnode, vNodes) {
@@ -598,37 +544,38 @@ void CNode::Ban(const CSubNet& subNet, const BanReason &banReason, int64_t banti
DumpBanlist(); //store banlist to disk immediately if user requested ban
}
-bool CNode::Unban(const CNetAddr &addr) {
+bool CConnman::Unban(const CNetAddr &addr) {
CSubNet subNet(addr);
return Unban(subNet);
}
-bool CNode::Unban(const CSubNet &subNet) {
+bool CConnman::Unban(const CSubNet &subNet) {
{
LOCK(cs_setBanned);
if (!setBanned.erase(subNet))
return false;
setBannedIsDirty = true;
}
- uiInterface.BannedListChanged();
+ if(clientInterface)
+ clientInterface->BannedListChanged();
DumpBanlist(); //store banlist to disk immediately
return true;
}
-void CNode::GetBanned(banmap_t &banMap)
+void CConnman::GetBanned(banmap_t &banMap)
{
LOCK(cs_setBanned);
banMap = setBanned; //create a thread safe copy
}
-void CNode::SetBanned(const banmap_t &banMap)
+void CConnman::SetBanned(const banmap_t &banMap)
{
LOCK(cs_setBanned);
setBanned = banMap;
setBannedIsDirty = true;
}
-void CNode::SweepBanned()
+void CConnman::SweepBanned()
{
int64_t now = GetTime();
@@ -649,23 +596,20 @@ void CNode::SweepBanned()
}
}
-bool CNode::BannedSetIsDirty()
+bool CConnman::BannedSetIsDirty()
{
LOCK(cs_setBanned);
return setBannedIsDirty;
}
-void CNode::SetBannedSetDirty(bool dirty)
+void CConnman::SetBannedSetDirty(bool dirty)
{
LOCK(cs_setBanned); //reuse setBanned lock for the isDirty flag
setBannedIsDirty = dirty;
}
-std::vector<CSubNet> CNode::vWhitelistedRange;
-CCriticalSection CNode::cs_vWhitelistedRange;
-
-bool CNode::IsWhitelistedRange(const CNetAddr &addr) {
+bool CConnman::IsWhitelistedRange(const CNetAddr &addr) {
LOCK(cs_vWhitelistedRange);
BOOST_FOREACH(const CSubNet& subnet, vWhitelistedRange) {
if (subnet.Match(addr))
@@ -674,7 +618,7 @@ bool CNode::IsWhitelistedRange(const CNetAddr &addr) {
return false;
}
-void CNode::AddWhitelistedRange(const CSubNet &subnet) {
+void CConnman::AddWhitelistedRange(const CSubNet &subnet) {
LOCK(cs_vWhitelistedRange);
vWhitelistedRange.push_back(subnet);
}
@@ -723,8 +667,9 @@ void CNode::copyStats(CNodeStats &stats)
#undef X
// requires LOCK(cs_vRecvMsg)
-bool CNode::ReceiveMsgBytes(const char *pch, unsigned int nBytes)
+bool CNode::ReceiveMsgBytes(const char *pch, unsigned int nBytes, bool& complete)
{
+ complete = false;
while (nBytes > 0) {
// get current incomplete message, or create a new one
@@ -763,7 +708,7 @@ bool CNode::ReceiveMsgBytes(const char *pch, unsigned int nBytes)
i->second += msg.hdr.nMessageSize + CMessageHeader::HEADER_SIZE;
msg.nTime = GetTimeMicros();
- messageHandlerCondition.notify_one();
+ complete = true;
}
}
@@ -826,9 +771,10 @@ int CNetMessage::readData(const char *pch, unsigned int nBytes)
// requires LOCK(cs_vSend)
-void SocketSendData(CNode *pnode)
+size_t SocketSendData(CNode *pnode)
{
std::deque<CSerializeData>::iterator it = pnode->vSendMsg.begin();
+ size_t nSentSize = 0;
while (it != pnode->vSendMsg.end()) {
const CSerializeData &data = *it;
@@ -838,7 +784,7 @@ void SocketSendData(CNode *pnode)
pnode->nLastSend = GetTime();
pnode->nSendBytes += nBytes;
pnode->nSendOffset += nBytes;
- pnode->RecordBytesSent(nBytes);
+ nSentSize += nBytes;
if (pnode->nSendOffset == data.size()) {
pnode->nSendOffset = 0;
pnode->nSendSize -= data.size();
@@ -867,10 +813,9 @@ void SocketSendData(CNode *pnode)
assert(pnode->nSendSize == 0);
}
pnode->vSendMsg.erase(pnode->vSendMsg.begin(), it);
+ return nSentSize;
}
-static std::list<CNode*> vNodesDisconnected;
-
struct NodeEvictionCandidate
{
NodeId id;
@@ -924,7 +869,8 @@ static bool CompareNodeTXTime(const NodeEvictionCandidate &a, const NodeEviction
* 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 CConnman::AttemptToEvictConnection()
+{
std::vector<NodeEvictionCandidate> vEvictionCandidates;
{
LOCK(cs_vNodes);
@@ -1015,20 +961,20 @@ static bool AttemptToEvictConnection() {
return false;
}
-static void AcceptConnection(const ListenSocket& hListenSocket) {
+void CConnman::AcceptConnection(const ListenSocket& hListenSocket) {
struct sockaddr_storage sockaddr;
socklen_t len = sizeof(sockaddr);
SOCKET hSocket = accept(hListenSocket.socket, (struct sockaddr*)&sockaddr, &len);
CAddress addr;
int nInbound = 0;
- int nMaxInbound = nMaxConnections - (MAX_OUTBOUND_CONNECTIONS + MAX_FEELER_CONNECTIONS);
+ int nMaxInbound = nMaxConnections - (nMaxOutbound + nMaxFeeler);
assert(nMaxInbound > 0);
if (hSocket != INVALID_SOCKET)
if (!addr.SetSockAddr((const struct sockaddr*)&sockaddr))
LogPrintf("Warning: Unknown socket family\n");
- bool whitelisted = hListenSocket.whitelisted || CNode::IsWhitelistedRange(addr);
+ bool whitelisted = hListenSocket.whitelisted || IsWhitelistedRange(addr);
{
LOCK(cs_vNodes);
BOOST_FOREACH(CNode* pnode, vNodes)
@@ -1060,7 +1006,7 @@ static void AcceptConnection(const ListenSocket& hListenSocket) {
setsockopt(hSocket, IPPROTO_TCP, TCP_NODELAY, (void*)&set, sizeof(int));
#endif
- if (CNode::IsBanned(addr) && !whitelisted)
+ if (IsBanned(addr) && !whitelisted)
{
LogPrintf("connection from %s dropped (banned)\n", addr.ToString());
CloseSocket(hSocket);
@@ -1077,7 +1023,8 @@ static void AcceptConnection(const ListenSocket& hListenSocket) {
}
}
- CNode* pnode = new CNode(hSocket, addr, "", true);
+ CNode* pnode = new CNode(GetNewNodeId(), nLocalServices, GetBestHeight(), hSocket, addr, CalculateKeyedNetGroup(addr), "", true);
+ GetNodeSignals().InitializeNode(pnode->GetId(), pnode);
pnode->AddRef();
pnode->fWhitelisted = whitelisted;
@@ -1089,7 +1036,7 @@ static void AcceptConnection(const ListenSocket& hListenSocket) {
}
}
-void ThreadSocketHandler()
+void CConnman::ThreadSocketHandler()
{
unsigned int nPrevNodeCount = 0;
while (true)
@@ -1147,14 +1094,15 @@ void ThreadSocketHandler()
if (fDelete)
{
vNodesDisconnected.remove(pnode);
- delete pnode;
+ DeleteNode(pnode);
}
}
}
}
if(vNodes.size() != nPrevNodeCount) {
nPrevNodeCount = vNodes.size();
- uiInterface.NotifyNumConnectionsChanged(nPrevNodeCount);
+ if(clientInterface)
+ clientInterface->NotifyNumConnectionsChanged(nPrevNodeCount);
}
//
@@ -1206,16 +1154,22 @@ void ThreadSocketHandler()
// * We process a message in the buffer (message handler thread).
{
TRY_LOCK(pnode->cs_vSend, lockSend);
- if (lockSend && !pnode->vSendMsg.empty()) {
- FD_SET(pnode->hSocket, &fdsetSend);
- continue;
+ if (lockSend) {
+ if (pnode->nOptimisticBytesWritten) {
+ RecordBytesSent(pnode->nOptimisticBytesWritten);
+ pnode->nOptimisticBytesWritten = 0;
+ }
+ if (!pnode->vSendMsg.empty()) {
+ FD_SET(pnode->hSocket, &fdsetSend);
+ continue;
+ }
}
}
{
TRY_LOCK(pnode->cs_vRecvMsg, lockRecv);
if (lockRecv && (
pnode->vRecvMsg.empty() || !pnode->vRecvMsg.front().complete() ||
- pnode->GetTotalRecvSize() <= ReceiveFloodSize()))
+ pnode->GetTotalRecvSize() <= GetReceiveFloodSize()))
FD_SET(pnode->hSocket, &fdsetRecv);
}
}
@@ -1280,11 +1234,14 @@ void ThreadSocketHandler()
int nBytes = recv(pnode->hSocket, pchBuf, sizeof(pchBuf), MSG_DONTWAIT);
if (nBytes > 0)
{
- if (!pnode->ReceiveMsgBytes(pchBuf, nBytes))
+ bool notify = false;
+ if (!pnode->ReceiveMsgBytes(pchBuf, nBytes, notify))
pnode->CloseSocketDisconnect();
+ if(notify)
+ messageHandlerCondition.notify_one();
pnode->nLastRecv = GetTime();
pnode->nRecvBytes += nBytes;
- pnode->RecordBytesRecv(nBytes);
+ RecordBytesRecv(nBytes);
}
else if (nBytes == 0)
{
@@ -1316,8 +1273,11 @@ void ThreadSocketHandler()
if (FD_ISSET(pnode->hSocket, &fdsetSend))
{
TRY_LOCK(pnode->cs_vSend, lockSend);
- if (lockSend)
- SocketSendData(pnode);
+ if (lockSend) {
+ size_t nBytes = SocketSendData(pnode);
+ if (nBytes)
+ RecordBytesSent(nBytes);
+ }
}
//
@@ -1497,7 +1457,7 @@ static std::string GetDNSHost(const CDNSSeedData& data, ServiceFlags* requiredSe
}
-void ThreadDNSAddressSeed()
+void CConnman::ThreadDNSAddressSeed()
{
// goal: only query DNS seeds if address need is acute
if ((addrman.size() > 0) &&
@@ -1560,7 +1520,7 @@ void ThreadDNSAddressSeed()
-void DumpAddresses()
+void CConnman::DumpAddresses()
{
int64_t nStart = GetTimeMillis();
@@ -1571,13 +1531,13 @@ void DumpAddresses()
addrman.size(), GetTimeMillis() - nStart);
}
-void DumpData()
+void CConnman::DumpData()
{
DumpAddresses();
DumpBanlist();
}
-void static ProcessOneShot()
+void CConnman::ProcessOneShot()
{
std::string strDest;
{
@@ -1595,7 +1555,7 @@ void static ProcessOneShot()
}
}
-void ThreadOpenConnections()
+void CConnman::ThreadOpenConnections()
{
// Connect to specific addresses
if (mapArgs.count("-connect") && mapMultiArgs["-connect"].size() > 0)
@@ -1660,7 +1620,7 @@ void ThreadOpenConnections()
}
}
}
- assert(nOutbound <= (MAX_OUTBOUND_CONNECTIONS + MAX_FEELER_CONNECTIONS));
+ assert(nOutbound <= (nMaxOutbound + nMaxFeeler));
// Feeler Connections
//
@@ -1675,7 +1635,7 @@ void ThreadOpenConnections()
// * Only make a feeler connection once every few minutes.
//
bool fFeeler = false;
- if (nOutbound >= MAX_OUTBOUND_CONNECTIONS) {
+ if (nOutbound >= nMaxOutbound) {
int64_t nTime = GetTimeMicros(); // The current time right now (in microseconds).
if (nTime > nNextFeeler) {
nNextFeeler = PoissonNextSend(nTime, FEELER_INTERVAL);
@@ -1739,7 +1699,7 @@ void ThreadOpenConnections()
}
}
-std::vector<AddedNodeInfo> GetAddedNodeInfo()
+std::vector<AddedNodeInfo> CConnman::GetAddedNodeInfo()
{
std::vector<AddedNodeInfo> ret;
@@ -1791,7 +1751,7 @@ std::vector<AddedNodeInfo> GetAddedNodeInfo()
return ret;
}
-void ThreadOpenAddedConnections()
+void CConnman::ThreadOpenAddedConnections()
{
{
LOCK(cs_vAddedNodes);
@@ -1817,7 +1777,7 @@ void ThreadOpenAddedConnections()
}
// if successful, this moves the passed grant to the constructed node
-bool OpenNetworkConnection(const CAddress& addrConnect, bool fCountFailure, CSemaphoreGrant *grantOutbound, const char *pszDest, bool fOneShot, bool fFeeler)
+bool CConnman::OpenNetworkConnection(const CAddress& addrConnect, bool fCountFailure, CSemaphoreGrant *grantOutbound, const char *pszDest, bool fOneShot, bool fFeeler)
{
//
// Initiate outbound network connection
@@ -1825,7 +1785,7 @@ bool OpenNetworkConnection(const CAddress& addrConnect, bool fCountFailure, CSem
boost::this_thread::interruption_point();
if (!pszDest) {
if (IsLocal(addrConnect) ||
- FindNode((CNetAddr)addrConnect) || CNode::IsBanned(addrConnect) ||
+ FindNode((CNetAddr)addrConnect) || IsBanned(addrConnect) ||
FindNode(addrConnect.ToStringIPPort()))
return false;
} else if (FindNode(std::string(pszDest)))
@@ -1848,7 +1808,7 @@ bool OpenNetworkConnection(const CAddress& addrConnect, bool fCountFailure, CSem
}
-void ThreadMessageHandler()
+void CConnman::ThreadMessageHandler()
{
boost::mutex condition_mutex;
boost::unique_lock<boost::mutex> lock(condition_mutex);
@@ -1876,10 +1836,10 @@ void ThreadMessageHandler()
TRY_LOCK(pnode->cs_vRecvMsg, lockRecv);
if (lockRecv)
{
- if (!GetNodeSignals().ProcessMessages(pnode))
+ if (!GetNodeSignals().ProcessMessages(pnode, *this))
pnode->CloseSocketDisconnect();
- if (pnode->nSendSize < SendBufferSize())
+ if (pnode->nSendSize < GetSendBufferSize())
{
if (!pnode->vRecvGetData.empty() || (!pnode->vRecvMsg.empty() && pnode->vRecvMsg[0].complete()))
{
@@ -1894,7 +1854,7 @@ void ThreadMessageHandler()
{
TRY_LOCK(pnode->cs_vSend, lockSend);
if (lockSend)
- GetNodeSignals().SendMessages(pnode);
+ GetNodeSignals().SendMessages(pnode, *this);
}
boost::this_thread::interruption_point();
}
@@ -1915,7 +1875,7 @@ void ThreadMessageHandler()
-bool BindListenPort(const CService &addrBind, std::string& strError, bool fWhitelisted)
+bool CConnman::BindListenPort(const CService &addrBind, std::string& strError, bool fWhitelisted)
{
strError = "";
int nOne = 1;
@@ -2013,7 +1973,7 @@ bool BindListenPort(const CService &addrBind, std::string& strError, bool fWhite
return true;
}
-void static Discover(boost::thread_group& threadGroup)
+void Discover(boost::thread_group& threadGroup)
{
if (!fDiscover)
return;
@@ -2064,9 +2024,49 @@ void static Discover(boost::thread_group& threadGroup)
#endif
}
-void StartNode(boost::thread_group& threadGroup, CScheduler& scheduler)
+CConnman::CConnman(uint64_t nSeed0In, uint64_t nSeed1In) : nSeed0(nSeed0In), nSeed1(nSeed1In)
+{
+ setBannedIsDirty = false;
+ fAddressesInitialized = false;
+ nLastNodeId = 0;
+ nSendBufferMaxSize = 0;
+ nReceiveFloodSize = 0;
+ semOutbound = NULL;
+ nMaxConnections = 0;
+ nMaxOutbound = 0;
+ nBestHeight = 0;
+ clientInterface = NULL;
+}
+
+NodeId CConnman::GetNewNodeId()
{
- uiInterface.InitMessage(_("Loading addresses..."));
+ return nLastNodeId.fetch_add(1, std::memory_order_relaxed);
+}
+
+bool CConnman::Start(boost::thread_group& threadGroup, CScheduler& scheduler, std::string& strNodeError, Options connOptions)
+{
+ nTotalBytesRecv = 0;
+ nTotalBytesSent = 0;
+ nMaxOutboundTotalBytesSentInCycle = 0;
+ nMaxOutboundCycleStartTime = 0;
+
+ nRelevantServices = connOptions.nRelevantServices;
+ nLocalServices = connOptions.nLocalServices;
+ nMaxConnections = connOptions.nMaxConnections;
+ nMaxOutbound = std::min((connOptions.nMaxOutbound), nMaxConnections);
+ nMaxFeeler = connOptions.nMaxFeeler;
+
+ nSendBufferMaxSize = connOptions.nSendBufferMaxSize;
+ nReceiveFloodSize = connOptions.nSendBufferMaxSize;
+
+ nMaxOutboundLimit = connOptions.nMaxOutboundLimit;
+ nMaxOutboundTimeframe = connOptions.nMaxOutboundTimeframe;
+
+ SetBestHeight(connOptions.nBestHeight);
+
+ clientInterface = connOptions.uiInterface;
+ if (clientInterface)
+ clientInterface->InitMessage(_("Loading addresses..."));
// Load addresses from peers.dat
int64_t nStart = GetTimeMillis();
{
@@ -2079,22 +2079,22 @@ void StartNode(boost::thread_group& threadGroup, CScheduler& scheduler)
DumpAddresses();
}
}
-
- uiInterface.InitMessage(_("Loading banlist..."));
+ if (clientInterface)
+ clientInterface->InitMessage(_("Loading banlist..."));
// Load addresses from banlist.dat
nStart = GetTimeMillis();
CBanDB bandb;
banmap_t banmap;
if (bandb.Read(banmap)) {
- CNode::SetBanned(banmap); // thread save setter
- CNode::SetBannedSetDirty(false); // no need to write down, just read data
- CNode::SweepBanned(); // sweep out unused entries
+ SetBanned(banmap); // thread save setter
+ SetBannedSetDirty(false); // no need to write down, just read data
+ SweepBanned(); // sweep out unused entries
LogPrint("net", "Loaded %d banned node ips/subnets from banlist.dat %dms\n",
banmap.size(), GetTimeMillis() - nStart);
} else {
LogPrintf("Invalid or missing banlist.dat; recreating\n");
- CNode::SetBannedSetDirty(true); // force write
+ SetBannedSetDirty(true); // force write
DumpBanlist();
}
@@ -2104,18 +2104,16 @@ void StartNode(boost::thread_group& threadGroup, CScheduler& scheduler)
if (semOutbound == NULL) {
// initialize semaphore
- int nMaxOutbound = std::min((MAX_OUTBOUND_CONNECTIONS + MAX_FEELER_CONNECTIONS), nMaxConnections);
- semOutbound = new CSemaphore(nMaxOutbound);
+ semOutbound = new CSemaphore(std::min((nMaxOutbound + nMaxFeeler), nMaxConnections));
}
if (pnodeLocalHost == NULL) {
CNetAddr local;
LookupHost("127.0.0.1", local, false);
- pnodeLocalHost = new CNode(INVALID_SOCKET, CAddress(CService(local, 0), nLocalServices));
+ pnodeLocalHost = new CNode(GetNewNodeId(), nLocalServices, GetBestHeight(), INVALID_SOCKET, CAddress(CService(local, 0), nLocalServices), 0);
+ GetNodeSignals().InitializeNode(pnodeLocalHost->GetId(), pnodeLocalHost);
}
- Discover(threadGroup);
-
//
// Start threads
//
@@ -2123,33 +2121,46 @@ void StartNode(boost::thread_group& threadGroup, CScheduler& scheduler)
if (!GetBoolArg("-dnsseed", true))
LogPrintf("DNS seeding disabled\n");
else
- threadGroup.create_thread(boost::bind(&TraceThread<void (*)()>, "dnsseed", &ThreadDNSAddressSeed));
-
- // Map ports with UPnP
- MapPort(GetBoolArg("-upnp", DEFAULT_UPNP));
+ threadGroup.create_thread(boost::bind(&TraceThread<boost::function<void()> >, "dnsseed", boost::function<void()>(boost::bind(&CConnman::ThreadDNSAddressSeed, this))));
// Send and receive from sockets, accept connections
- threadGroup.create_thread(boost::bind(&TraceThread<void (*)()>, "net", &ThreadSocketHandler));
+ threadGroup.create_thread(boost::bind(&TraceThread<boost::function<void()> >, "net", boost::function<void()>(boost::bind(&CConnman::ThreadSocketHandler, this))));
// Initiate outbound connections from -addnode
- threadGroup.create_thread(boost::bind(&TraceThread<void (*)()>, "addcon", &ThreadOpenAddedConnections));
+ threadGroup.create_thread(boost::bind(&TraceThread<boost::function<void()> >, "addcon", boost::function<void()>(boost::bind(&CConnman::ThreadOpenAddedConnections, this))));
// Initiate outbound connections
- threadGroup.create_thread(boost::bind(&TraceThread<void (*)()>, "opencon", &ThreadOpenConnections));
+ threadGroup.create_thread(boost::bind(&TraceThread<boost::function<void()> >, "opencon", boost::function<void()>(boost::bind(&CConnman::ThreadOpenConnections, this))));
// Process messages
- threadGroup.create_thread(boost::bind(&TraceThread<void (*)()>, "msghand", &ThreadMessageHandler));
+ threadGroup.create_thread(boost::bind(&TraceThread<boost::function<void()> >, "msghand", boost::function<void()>(boost::bind(&CConnman::ThreadMessageHandler, this))));
// Dump network addresses
- scheduler.scheduleEvery(&DumpData, DUMP_ADDRESSES_INTERVAL);
+ scheduler.scheduleEvery(boost::bind(&CConnman::DumpData, this), DUMP_ADDRESSES_INTERVAL);
+
+ return true;
+}
+
+class CNetCleanup
+{
+public:
+ CNetCleanup() {}
+
+ ~CNetCleanup()
+ {
+#ifdef WIN32
+ // Shutdown Windows Sockets
+ WSACleanup();
+#endif
+ }
}
+instance_of_cnetcleanup;
-bool StopNode()
+void CConnman::Stop()
{
- LogPrintf("StopNode()\n");
- MapPort(false);
+ LogPrintf("%s\n",__func__);
if (semOutbound)
- for (int i=0; i<(MAX_OUTBOUND_CONNECTIONS + MAX_FEELER_CONNECTIONS); i++)
+ for (int i=0; i<(nMaxOutbound + nMaxFeeler); i++)
semOutbound->post();
if (fAddressesInitialized)
@@ -2158,48 +2169,167 @@ bool StopNode()
fAddressesInitialized = false;
}
+ // Close sockets
+ BOOST_FOREACH(CNode* pnode, vNodes)
+ if (pnode->hSocket != INVALID_SOCKET)
+ CloseSocket(pnode->hSocket);
+ BOOST_FOREACH(ListenSocket& hListenSocket, vhListenSocket)
+ if (hListenSocket.socket != INVALID_SOCKET)
+ if (!CloseSocket(hListenSocket.socket))
+ LogPrintf("CloseSocket(hListenSocket) failed with error %s\n", NetworkErrorString(WSAGetLastError()));
+
+ // clean up some globals (to help leak detection)
+ BOOST_FOREACH(CNode *pnode, vNodes) {
+ DeleteNode(pnode);
+ }
+ BOOST_FOREACH(CNode *pnode, vNodesDisconnected) {
+ DeleteNode(pnode);
+ }
+ vNodes.clear();
+ vNodesDisconnected.clear();
+ vhListenSocket.clear();
+ delete semOutbound;
+ semOutbound = NULL;
+ if(pnodeLocalHost)
+ DeleteNode(pnodeLocalHost);
+ pnodeLocalHost = NULL;
+}
+
+void CConnman::DeleteNode(CNode* pnode)
+{
+ assert(pnode);
+ bool fUpdateConnectionTime = false;
+ GetNodeSignals().FinalizeNode(pnode->GetId(), fUpdateConnectionTime);
+ if(fUpdateConnectionTime)
+ addrman.Connected(pnode->addr);
+ delete pnode;
+}
+
+CConnman::~CConnman()
+{
+ Stop();
+}
+
+size_t CConnman::GetAddressCount() const
+{
+ return addrman.size();
+}
+
+void CConnman::SetServices(const CService &addr, ServiceFlags nServices)
+{
+ addrman.SetServices(addr, nServices);
+}
+
+void CConnman::MarkAddressGood(const CAddress& addr)
+{
+ addrman.Good(addr);
+}
+
+void CConnman::AddNewAddress(const CAddress& addr, const CAddress& addrFrom, int64_t nTimePenalty)
+{
+ addrman.Add(addr, addrFrom, nTimePenalty);
+}
+
+void CConnman::AddNewAddresses(const std::vector<CAddress>& vAddr, const CAddress& addrFrom, int64_t nTimePenalty)
+{
+ addrman.Add(vAddr, addrFrom, nTimePenalty);
+}
+
+std::vector<CAddress> CConnman::GetAddresses()
+{
+ return addrman.GetAddr();
+}
+
+bool CConnman::AddNode(const std::string& strNode)
+{
+ LOCK(cs_vAddedNodes);
+ for(std::vector<std::string>::const_iterator it = vAddedNodes.begin(); it != vAddedNodes.end(); ++it) {
+ if (strNode == *it)
+ return false;
+ }
+
+ vAddedNodes.push_back(strNode);
return true;
}
-class CNetCleanup
+bool CConnman::RemoveAddedNode(const std::string& strNode)
{
-public:
- CNetCleanup() {}
+ LOCK(cs_vAddedNodes);
+ for(std::vector<std::string>::iterator it = vAddedNodes.begin(); it != vAddedNodes.end(); ++it) {
+ if (strNode == *it) {
+ vAddedNodes.erase(it);
+ return true;
+ }
+ }
+ return false;
+}
- ~CNetCleanup()
- {
- // Close sockets
- BOOST_FOREACH(CNode* pnode, vNodes)
- if (pnode->hSocket != INVALID_SOCKET)
- CloseSocket(pnode->hSocket);
- BOOST_FOREACH(ListenSocket& hListenSocket, vhListenSocket)
- if (hListenSocket.socket != INVALID_SOCKET)
- if (!CloseSocket(hListenSocket.socket))
- LogPrintf("CloseSocket(hListenSocket) failed with error %s\n", NetworkErrorString(WSAGetLastError()));
-
- // clean up some globals (to help leak detection)
- BOOST_FOREACH(CNode *pnode, vNodes)
- delete pnode;
- BOOST_FOREACH(CNode *pnode, vNodesDisconnected)
- delete pnode;
- vNodes.clear();
- vNodesDisconnected.clear();
- vhListenSocket.clear();
- delete semOutbound;
- semOutbound = NULL;
- delete pnodeLocalHost;
- pnodeLocalHost = NULL;
+size_t CConnman::GetNodeCount(NumConnections flags)
+{
+ LOCK(cs_vNodes);
+ if (flags == CConnman::CONNECTIONS_ALL) // Shortcut if we want total
+ return vNodes.size();
-#ifdef WIN32
- // Shutdown Windows Sockets
- WSACleanup();
-#endif
+ int nNum = 0;
+ for(std::vector<CNode*>::const_iterator it = vNodes.begin(); it != vNodes.end(); ++it)
+ if (flags & ((*it)->fInbound ? CONNECTIONS_IN : CONNECTIONS_OUT))
+ nNum++;
+
+ return nNum;
+}
+
+void CConnman::GetNodeStats(std::vector<CNodeStats>& vstats)
+{
+ vstats.clear();
+ LOCK(cs_vNodes);
+ vstats.reserve(vNodes.size());
+ for(std::vector<CNode*>::iterator it = vNodes.begin(); it != vNodes.end(); ++it) {
+ CNode* pnode = *it;
+ CNodeStats stats;
+ pnode->copyStats(stats);
+ vstats.push_back(stats);
}
}
-instance_of_cnetcleanup;
+bool CConnman::DisconnectAddress(const CNetAddr& netAddr)
+{
+ if (CNode* pnode = FindNode(netAddr)) {
+ pnode->fDisconnect = true;
+ return true;
+ }
+ return false;
+}
+
+bool CConnman::DisconnectSubnet(const CSubNet& subNet)
+{
+ if (CNode* pnode = FindNode(subNet)) {
+ pnode->fDisconnect = true;
+ return true;
+ }
+ return false;
+}
+
+bool CConnman::DisconnectNode(const std::string& strNode)
+{
+ if (CNode* pnode = FindNode(strNode)) {
+ pnode->fDisconnect = true;
+ return true;
+ }
+ return false;
+}
+bool CConnman::DisconnectNode(NodeId id)
+{
+ LOCK(cs_vNodes);
+ for(CNode* pnode : vNodes) {
+ if (id == pnode->id) {
+ pnode->fDisconnect = true;
+ return true;
+ }
+ }
+ return false;
+}
-void RelayTransaction(const CTransaction& tx)
+void CConnman::RelayTransaction(const CTransaction& tx)
{
CInv inv(MSG_TX, tx.GetHash());
LOCK(cs_vNodes);
@@ -2209,13 +2339,13 @@ void RelayTransaction(const CTransaction& tx)
}
}
-void CNode::RecordBytesRecv(uint64_t bytes)
+void CConnman::RecordBytesRecv(uint64_t bytes)
{
LOCK(cs_totalBytesRecv);
nTotalBytesRecv += bytes;
}
-void CNode::RecordBytesSent(uint64_t bytes)
+void CConnman::RecordBytesSent(uint64_t bytes)
{
LOCK(cs_totalBytesSent);
nTotalBytesSent += bytes;
@@ -2232,29 +2362,25 @@ void CNode::RecordBytesSent(uint64_t bytes)
nMaxOutboundTotalBytesSentInCycle += bytes;
}
-void CNode::SetMaxOutboundTarget(uint64_t limit)
+void CConnman::SetMaxOutboundTarget(uint64_t limit)
{
LOCK(cs_totalBytesSent);
- uint64_t recommendedMinimum = (nMaxOutboundTimeframe / 600) * MAX_BLOCK_SERIALIZED_SIZE;
nMaxOutboundLimit = limit;
-
- if (limit > 0 && limit < recommendedMinimum)
- LogPrintf("Max outbound target is very small (%s bytes) and will be overshot. Recommended minimum is %s bytes.\n", nMaxOutboundLimit, recommendedMinimum);
}
-uint64_t CNode::GetMaxOutboundTarget()
+uint64_t CConnman::GetMaxOutboundTarget()
{
LOCK(cs_totalBytesSent);
return nMaxOutboundLimit;
}
-uint64_t CNode::GetMaxOutboundTimeframe()
+uint64_t CConnman::GetMaxOutboundTimeframe()
{
LOCK(cs_totalBytesSent);
return nMaxOutboundTimeframe;
}
-uint64_t CNode::GetMaxOutboundTimeLeftInCycle()
+uint64_t CConnman::GetMaxOutboundTimeLeftInCycle()
{
LOCK(cs_totalBytesSent);
if (nMaxOutboundLimit == 0)
@@ -2268,7 +2394,7 @@ uint64_t CNode::GetMaxOutboundTimeLeftInCycle()
return (cycleEndTime < now) ? 0 : cycleEndTime - GetTime();
}
-void CNode::SetMaxOutboundTimeframe(uint64_t timeframe)
+void CConnman::SetMaxOutboundTimeframe(uint64_t timeframe)
{
LOCK(cs_totalBytesSent);
if (nMaxOutboundTimeframe != timeframe)
@@ -2280,7 +2406,7 @@ void CNode::SetMaxOutboundTimeframe(uint64_t timeframe)
nMaxOutboundTimeframe = timeframe;
}
-bool CNode::OutboundTargetReached(bool historicalBlockServingLimit)
+bool CConnman::OutboundTargetReached(bool historicalBlockServingLimit)
{
LOCK(cs_totalBytesSent);
if (nMaxOutboundLimit == 0)
@@ -2300,7 +2426,7 @@ bool CNode::OutboundTargetReached(bool historicalBlockServingLimit)
return false;
}
-uint64_t CNode::GetOutboundTargetBytesLeft()
+uint64_t CConnman::GetOutboundTargetBytesLeft()
{
LOCK(cs_totalBytesSent);
if (nMaxOutboundLimit == 0)
@@ -2309,18 +2435,33 @@ uint64_t CNode::GetOutboundTargetBytesLeft()
return (nMaxOutboundTotalBytesSentInCycle >= nMaxOutboundLimit) ? 0 : nMaxOutboundLimit - nMaxOutboundTotalBytesSentInCycle;
}
-uint64_t CNode::GetTotalBytesRecv()
+uint64_t CConnman::GetTotalBytesRecv()
{
LOCK(cs_totalBytesRecv);
return nTotalBytesRecv;
}
-uint64_t CNode::GetTotalBytesSent()
+uint64_t CConnman::GetTotalBytesSent()
{
LOCK(cs_totalBytesSent);
return nTotalBytesSent;
}
+ServiceFlags CConnman::GetLocalServices() const
+{
+ return nLocalServices;
+}
+
+void CConnman::SetBestHeight(int height)
+{
+ nBestHeight.store(height, std::memory_order_release);
+}
+
+int CConnman::GetBestHeight() const
+{
+ return nBestHeight.load(std::memory_order_acquire);
+}
+
void CNode::Fuzz(int nChance)
{
if (!fSuccessfullyConnected) return; // Don't fuzz initial handshake
@@ -2356,121 +2497,13 @@ void CNode::Fuzz(int nChance)
Fuzz(2);
}
-//
-// CAddrDB
-//
-
-CAddrDB::CAddrDB()
-{
- pathAddr = GetDataDir() / "peers.dat";
-}
-
-bool CAddrDB::Write(const CAddrMan& addr)
-{
- // Generate random temporary filename
- unsigned short randv = 0;
- GetRandBytes((unsigned char*)&randv, sizeof(randv));
- std::string tmpfn = strprintf("peers.dat.%04x", randv);
-
- // serialize addresses, checksum data up to that point, then append csum
- CDataStream ssPeers(SER_DISK, CLIENT_VERSION);
- ssPeers << FLATDATA(Params().MessageStart());
- ssPeers << addr;
- uint256 hash = Hash(ssPeers.begin(), ssPeers.end());
- ssPeers << hash;
-
- // open temp output file, and associate with CAutoFile
- boost::filesystem::path pathTmp = GetDataDir() / tmpfn;
- FILE *file = fopen(pathTmp.string().c_str(), "wb");
- CAutoFile fileout(file, SER_DISK, CLIENT_VERSION);
- if (fileout.IsNull())
- return error("%s: Failed to open file %s", __func__, pathTmp.string());
-
- // Write and commit header, data
- try {
- fileout << ssPeers;
- }
- catch (const std::exception& e) {
- return error("%s: Serialize or I/O error - %s", __func__, e.what());
- }
- FileCommit(fileout.Get());
- fileout.fclose();
-
- // replace existing peers.dat, if any, with new peers.dat.XXXX
- if (!RenameOver(pathTmp, pathAddr))
- return error("%s: Rename-into-place failed", __func__);
-
- return true;
-}
-
-bool CAddrDB::Read(CAddrMan& addr)
-{
- // open input file, and associate with CAutoFile
- FILE *file = fopen(pathAddr.string().c_str(), "rb");
- CAutoFile filein(file, SER_DISK, CLIENT_VERSION);
- if (filein.IsNull())
- return error("%s: Failed to open file %s", __func__, pathAddr.string());
-
- // use file size to size memory buffer
- uint64_t fileSize = boost::filesystem::file_size(pathAddr);
- uint64_t dataSize = 0;
- // Don't try to resize to a negative number if file is small
- if (fileSize >= sizeof(uint256))
- dataSize = fileSize - sizeof(uint256);
- std::vector<unsigned char> vchData;
- vchData.resize(dataSize);
- uint256 hashIn;
-
- // read data and checksum from file
- try {
- filein.read((char *)&vchData[0], dataSize);
- filein >> hashIn;
- }
- catch (const std::exception& e) {
- return error("%s: Deserialize or I/O error - %s", __func__, e.what());
- }
- filein.fclose();
-
- CDataStream ssPeers(vchData, SER_DISK, CLIENT_VERSION);
-
- // verify stored checksum matches input data
- uint256 hashTmp = Hash(ssPeers.begin(), ssPeers.end());
- 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 ..
- ssPeers >> FLATDATA(pchMsgTmp);
-
- // ... verify the network matches ours
- if (memcmp(pchMsgTmp, Params().MessageStart(), sizeof(pchMsgTmp)))
- return error("%s: Invalid network magic number", __func__);
-
- // de-serialize address data into one CAddrMan object
- 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());
- }
-
- return true;
-}
-
-unsigned int ReceiveFloodSize() { return 1000*GetArg("-maxreceivebuffer", DEFAULT_MAXRECEIVEBUFFER); }
-unsigned int SendBufferSize() { return 1000*GetArg("-maxsendbuffer", DEFAULT_MAXSENDBUFFER); }
+unsigned int CConnman::GetReceiveFloodSize() const { return nReceiveFloodSize; }
+unsigned int CConnman::GetSendBufferSize() const{ return nSendBufferMaxSize; }
-CNode::CNode(SOCKET hSocketIn, const CAddress& addrIn, const std::string& addrNameIn, bool fInboundIn) :
+CNode::CNode(NodeId idIn, ServiceFlags nLocalServicesIn, int nMyStartingHeightIn, SOCKET hSocketIn, const CAddress& addrIn, uint64_t nKeyedNetGroupIn, const std::string& addrNameIn, bool fInboundIn) :
ssSend(SER_NETWORK, INIT_PROTO_VERSION),
addr(addrIn),
- nKeyedNetGroup(CalculateKeyedNetGroup(addrIn)),
+ nKeyedNetGroup(nKeyedNetGroupIn),
addrKnown(5000, 0.001),
filterInventoryKnown(50000, 0.000001)
{
@@ -2520,16 +2553,17 @@ CNode::CNode(SOCKET hSocketIn, const CAddress& addrIn, const std::string& addrNa
minFeeFilter = 0;
lastSentFeeFilter = 0;
nextSendTimeFeeFilter = 0;
+ id = idIn;
+ nOptimisticBytesWritten = 0;
+ nLocalServices = nLocalServicesIn;
+
+ GetRandBytes((unsigned char*)&nLocalHostNonce, sizeof(nLocalHostNonce));
+ nMyStartingHeight = nMyStartingHeightIn;
BOOST_FOREACH(const std::string &msg, getAllNetMessageTypes())
mapRecvBytesPerMsgCmd[msg] = 0;
mapRecvBytesPerMsgCmd[NET_MESSAGE_COMMAND_OTHER] = 0;
- {
- LOCK(cs_nLastNodeId);
- id = nLastNodeId++;
- }
-
if (fLogIPs)
LogPrint("net", "Added connection to %s peer=%d\n", addrName, id);
else
@@ -2538,8 +2572,6 @@ CNode::CNode(SOCKET hSocketIn, const CAddress& addrIn, const std::string& addrNa
// Be shy and don't send version until we hear
if (hSocket != INVALID_SOCKET && !fInbound)
PushVersion();
-
- GetNodeSignals().InitializeNode(GetId(), this);
}
CNode::~CNode()
@@ -2548,8 +2580,6 @@ CNode::~CNode()
if (pfilter)
delete pfilter;
-
- GetNodeSignals().FinalizeNode(GetId());
}
void CNode::AskFor(const CInv& inv)
@@ -2644,122 +2674,36 @@ void CNode::EndMessage(const char* pszCommand) UNLOCK_FUNCTION(cs_vSend)
// If write queue empty, attempt "optimistic write"
if (it == vSendMsg.begin())
- SocketSendData(this);
+ nOptimisticBytesWritten += SocketSendData(this);
LEAVE_CRITICAL_SECTION(cs_vSend);
}
-//
-// CBanDB
-//
-
-CBanDB::CBanDB()
+bool CConnman::ForNode(NodeId id, std::function<bool(CNode* pnode)> func)
{
- pathBanlist = GetDataDir() / "banlist.dat";
-}
-
-bool CBanDB::Write(const banmap_t& banSet)
-{
- // Generate random temporary filename
- unsigned short randv = 0;
- GetRandBytes((unsigned char*)&randv, sizeof(randv));
- std::string tmpfn = strprintf("banlist.dat.%04x", randv);
-
- // serialize banlist, checksum data up to that point, then append csum
- CDataStream ssBanlist(SER_DISK, CLIENT_VERSION);
- ssBanlist << FLATDATA(Params().MessageStart());
- ssBanlist << banSet;
- uint256 hash = Hash(ssBanlist.begin(), ssBanlist.end());
- ssBanlist << hash;
-
- // open temp output file, and associate with CAutoFile
- boost::filesystem::path pathTmp = GetDataDir() / tmpfn;
- FILE *file = fopen(pathTmp.string().c_str(), "wb");
- CAutoFile fileout(file, SER_DISK, CLIENT_VERSION);
- if (fileout.IsNull())
- return error("%s: Failed to open file %s", __func__, pathTmp.string());
-
- // Write and commit header, data
- try {
- fileout << ssBanlist;
- }
- catch (const std::exception& e) {
- return error("%s: Serialize or I/O error - %s", __func__, e.what());
- }
- FileCommit(fileout.Get());
- fileout.fclose();
-
- // replace existing banlist.dat, if any, with new banlist.dat.XXXX
- if (!RenameOver(pathTmp, pathBanlist))
- return error("%s: Rename-into-place failed", __func__);
-
- return true;
-}
-
-bool CBanDB::Read(banmap_t& banSet)
-{
- // open input file, and associate with CAutoFile
- FILE *file = fopen(pathBanlist.string().c_str(), "rb");
- CAutoFile filein(file, SER_DISK, CLIENT_VERSION);
- if (filein.IsNull())
- return error("%s: Failed to open file %s", __func__, pathBanlist.string());
-
- // use file size to size memory buffer
- uint64_t fileSize = boost::filesystem::file_size(pathBanlist);
- uint64_t dataSize = 0;
- // Don't try to resize to a negative number if file is small
- if (fileSize >= sizeof(uint256))
- dataSize = fileSize - sizeof(uint256);
- std::vector<unsigned char> vchData;
- vchData.resize(dataSize);
- uint256 hashIn;
-
- // read data and checksum from file
- try {
- filein.read((char *)&vchData[0], dataSize);
- filein >> hashIn;
- }
- catch (const std::exception& e) {
- return error("%s: Deserialize or I/O error - %s", __func__, e.what());
- }
- filein.fclose();
-
- CDataStream ssBanlist(vchData, SER_DISK, CLIENT_VERSION);
-
- // verify stored checksum matches input data
- uint256 hashTmp = Hash(ssBanlist.begin(), ssBanlist.end());
- if (hashIn != hashTmp)
- return error("%s: Checksum mismatch, data corrupted", __func__);
-
- unsigned char pchMsgTmp[4];
- try {
- // de-serialize file header (network specific magic number) and ..
- ssBanlist >> FLATDATA(pchMsgTmp);
-
- // ... verify the network matches ours
- if (memcmp(pchMsgTmp, Params().MessageStart(), sizeof(pchMsgTmp)))
- return error("%s: Invalid network magic number", __func__);
-
- // de-serialize address data into one CAddrMan object
- ssBanlist >> banSet;
- }
- catch (const std::exception& e) {
- return error("%s: Deserialize or I/O error - %s", __func__, e.what());
+ CNode* found = nullptr;
+ LOCK(cs_vNodes);
+ for (auto&& pnode : vNodes) {
+ if(pnode->id == id) {
+ found = pnode;
+ break;
+ }
}
-
- return true;
+ return found != nullptr && func(found);
}
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);
}
-/* static */ uint64_t CNode::CalculateKeyedNetGroup(const CAddress& ad)
+CSipHasher CConnman::GetDeterministicRandomizer(uint64_t id)
{
- static const uint64_t k0 = GetRand(std::numeric_limits<uint64_t>::max());
- static const uint64_t k1 = GetRand(std::numeric_limits<uint64_t>::max());
+ return CSipHasher(nSeed0, nSeed1).Write(id);
+}
+uint64_t CConnman::CalculateKeyedNetGroup(const CAddress& ad)
+{
std::vector<unsigned char> vchNetGroup(ad.GetGroup());
- return CSipHasher(k0, k1).Write(&vchNetGroup[0], vchNetGroup.size()).Finalize();
+ return GetDeterministicRandomizer(RANDOMIZER_ID_NETGROUP).Write(&vchNetGroup[0], vchNetGroup.size()).Finalize();
}
diff --git a/src/net.h b/src/net.h
index b9cc81e4e9..0173854669 100644
--- a/src/net.h
+++ b/src/net.h
@@ -6,9 +6,12 @@
#ifndef BITCOIN_NET_H
#define BITCOIN_NET_H
+#include "addrdb.h"
+#include "addrman.h"
#include "amount.h"
#include "bloom.h"
#include "compat.h"
+#include "hash.h"
#include "limitedmap.h"
#include "netaddress.h"
#include "protocol.h"
@@ -20,6 +23,7 @@
#include <atomic>
#include <deque>
#include <stdint.h>
+#include <memory>
#ifndef WIN32
#include <arpa/inet.h>
@@ -51,6 +55,8 @@ static const unsigned int MAX_ADDR_TO_SEND = 1000;
static const unsigned int MAX_PROTOCOL_MESSAGE_LENGTH = 4 * 1000 * 1000;
/** Maximum length of strSubVer in `version` message */
static const unsigned int MAX_SUBVERSION_LENGTH = 256;
+/** Maximum number of outgoing nodes */
+static const int MAX_OUTBOUND_CONNECTIONS = 8;
/** -listen default */
static const bool DEFAULT_LISTEN = true;
/** -upnp default */
@@ -67,6 +73,8 @@ static const size_t SETASKFOR_MAX_SZ = 2 * MAX_INV_SZ;
static const unsigned int DEFAULT_MAX_PEER_CONNECTIONS = 125;
/** The default for -maxuploadtarget. 0 = Unlimited */
static const uint64_t DEFAULT_MAX_UPLOAD_TARGET = 0;
+/** The default timeframe for -maxuploadtarget. 1 day. */
+static const uint64_t MAX_UPLOAD_TIMEFRAME = 60 * 60 * 24;
/** Default for blocks only*/
static const bool DEFAULT_BLOCKSONLY = false;
@@ -79,25 +87,326 @@ 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
-unsigned int ReceiveFloodSize();
-unsigned int SendBufferSize();
-
typedef int NodeId;
-void AddOneShot(const std::string& strDest);
-void AddressCurrentlyConnected(const CService& addr);
-CNode* FindNode(const CNetAddr& ip);
-CNode* FindNode(const CSubNet& subNet);
-CNode* FindNode(const std::string& addrName);
-CNode* FindNode(const CService& ip);
-CNode* FindNode(const NodeId id); //TODO: Remove this
-bool OpenNetworkConnection(const CAddress& addrConnect, bool fCountFailure, CSemaphoreGrant *grantOutbound = NULL, const char *strDest = NULL, bool fOneShot = false, bool fFeeler = false);
+struct AddedNodeInfo
+{
+ std::string strAddedNode;
+ CService resolvedAddress;
+ bool fConnected;
+ bool fInbound;
+};
+
+class CTransaction;
+class CNodeStats;
+class CClientUIInterface;
+
+class CConnman
+{
+public:
+
+ enum NumConnections {
+ CONNECTIONS_NONE = 0,
+ CONNECTIONS_IN = (1U << 0),
+ CONNECTIONS_OUT = (1U << 1),
+ CONNECTIONS_ALL = (CONNECTIONS_IN | CONNECTIONS_OUT),
+ };
+
+ struct Options
+ {
+ ServiceFlags nLocalServices = NODE_NONE;
+ ServiceFlags nRelevantServices = NODE_NONE;
+ int nMaxConnections = 0;
+ int nMaxOutbound = 0;
+ int nMaxFeeler = 0;
+ int nBestHeight = 0;
+ CClientUIInterface* uiInterface = nullptr;
+ unsigned int nSendBufferMaxSize = 0;
+ unsigned int nReceiveFloodSize = 0;
+ uint64_t nMaxOutboundTimeframe = 0;
+ uint64_t nMaxOutboundLimit = 0;
+ };
+ CConnman(uint64_t seed0, uint64_t seed1);
+ ~CConnman();
+ bool Start(boost::thread_group& threadGroup, CScheduler& scheduler, std::string& strNodeError, Options options);
+ void Stop();
+ bool BindListenPort(const CService &bindAddr, std::string& strError, bool fWhitelisted = false);
+ bool OpenNetworkConnection(const CAddress& addrConnect, bool fCountFailure, CSemaphoreGrant *grantOutbound = NULL, const char *strDest = NULL, bool fOneShot = false, bool fFeeler = false);
+ bool CheckIncomingNonce(uint64_t nonce);
+
+ bool ForNode(NodeId id, std::function<bool(CNode* pnode)> func);
+
+ template<typename Callable>
+ bool ForEachNodeContinueIf(Callable&& func)
+ {
+ LOCK(cs_vNodes);
+ for (auto&& node : vNodes)
+ if(!func(node))
+ return false;
+ return true;
+ };
+
+ template<typename Callable>
+ bool ForEachNodeContinueIf(Callable&& func) const
+ {
+ LOCK(cs_vNodes);
+ for (const auto& node : vNodes)
+ if(!func(node))
+ return false;
+ return true;
+ };
+
+ template<typename Callable, typename CallableAfter>
+ bool ForEachNodeContinueIfThen(Callable&& pre, CallableAfter&& post)
+ {
+ bool ret = true;
+ LOCK(cs_vNodes);
+ for (auto&& node : vNodes)
+ if(!pre(node)) {
+ ret = false;
+ break;
+ }
+ post();
+ return ret;
+ };
+
+ template<typename Callable, typename CallableAfter>
+ bool ForEachNodeContinueIfThen(Callable&& pre, CallableAfter&& post) const
+ {
+ bool ret = true;
+ LOCK(cs_vNodes);
+ for (const auto& node : vNodes)
+ if(!pre(node)) {
+ ret = false;
+ break;
+ }
+ post();
+ return ret;
+ };
+
+ template<typename Callable>
+ void ForEachNode(Callable&& func)
+ {
+ LOCK(cs_vNodes);
+ for (auto&& node : vNodes)
+ func(node);
+ };
+
+ template<typename Callable>
+ void ForEachNode(Callable&& func) const
+ {
+ LOCK(cs_vNodes);
+ for (const auto& node : vNodes)
+ func(node);
+ };
+
+ template<typename Callable, typename CallableAfter>
+ void ForEachNodeThen(Callable&& pre, CallableAfter&& post)
+ {
+ LOCK(cs_vNodes);
+ for (auto&& node : vNodes)
+ pre(node);
+ post();
+ };
+
+ template<typename Callable, typename CallableAfter>
+ void ForEachNodeThen(Callable&& pre, CallableAfter&& post) const
+ {
+ LOCK(cs_vNodes);
+ for (const auto& node : vNodes)
+ pre(node);
+ post();
+ };
+
+ void RelayTransaction(const CTransaction& tx);
+
+ // Addrman functions
+ size_t GetAddressCount() const;
+ void SetServices(const CService &addr, ServiceFlags nServices);
+ void MarkAddressGood(const CAddress& addr);
+ void AddNewAddress(const CAddress& addr, const CAddress& addrFrom, int64_t nTimePenalty = 0);
+ void AddNewAddresses(const std::vector<CAddress>& vAddr, const CAddress& addrFrom, int64_t nTimePenalty = 0);
+ std::vector<CAddress> GetAddresses();
+ void AddressCurrentlyConnected(const CService& addr);
+
+ // Denial-of-service detection/prevention
+ // The idea is to detect peers that are behaving
+ // badly and disconnect/ban them, but do it in a
+ // one-coding-mistake-won't-shatter-the-entire-network
+ // way.
+ // IMPORTANT: There should be nothing I can give a
+ // node that it will forward on that will make that
+ // node's peers drop it. If there is, an attacker
+ // can isolate a node and/or try to split the network.
+ // Dropping a node for sending stuff that is invalid
+ // now but might be valid in a later version is also
+ // dangerous, because it can cause a network split
+ // between nodes running old code and nodes running
+ // new code.
+ void Ban(const CNetAddr& netAddr, const BanReason& reason, int64_t bantimeoffset = 0, bool sinceUnixEpoch = false);
+ void Ban(const CSubNet& subNet, const BanReason& reason, int64_t bantimeoffset = 0, bool sinceUnixEpoch = false);
+ void ClearBanned(); // needed for unit testing
+ bool IsBanned(CNetAddr ip);
+ bool IsBanned(CSubNet subnet);
+ bool Unban(const CNetAddr &ip);
+ bool Unban(const CSubNet &ip);
+ void GetBanned(banmap_t &banmap);
+ void SetBanned(const banmap_t &banmap);
+
+ void AddOneShot(const std::string& strDest);
+
+ bool AddNode(const std::string& node);
+ bool RemoveAddedNode(const std::string& node);
+ std::vector<AddedNodeInfo> GetAddedNodeInfo();
+
+ size_t GetNodeCount(NumConnections num);
+ void GetNodeStats(std::vector<CNodeStats>& vstats);
+ bool DisconnectAddress(const CNetAddr& addr);
+ bool DisconnectNode(const std::string& node);
+ bool DisconnectNode(NodeId id);
+ bool DisconnectSubnet(const CSubNet& subnet);
+
+ unsigned int GetSendBufferSize() const;
+
+ void AddWhitelistedRange(const CSubNet &subnet);
+
+ ServiceFlags GetLocalServices() const;
+
+ //!set the max outbound target in bytes
+ void SetMaxOutboundTarget(uint64_t limit);
+ uint64_t GetMaxOutboundTarget();
+
+ //!set the timeframe for the max outbound target
+ void SetMaxOutboundTimeframe(uint64_t timeframe);
+ uint64_t GetMaxOutboundTimeframe();
+
+ //!check if the outbound target is reached
+ // if param historicalBlockServingLimit is set true, the function will
+ // response true if the limit for serving historical blocks has been reached
+ bool OutboundTargetReached(bool historicalBlockServingLimit);
+
+ //!response the bytes left in the current max outbound cycle
+ // in case of no limit, it will always response 0
+ uint64_t GetOutboundTargetBytesLeft();
+
+ //!response the time in second left in the current max outbound cycle
+ // in case of no limit, it will always response 0
+ uint64_t GetMaxOutboundTimeLeftInCycle();
+
+ uint64_t GetTotalBytesRecv();
+ uint64_t GetTotalBytesSent();
+
+ void SetBestHeight(int height);
+ int GetBestHeight() const;
+
+ /** Get a unique deterministic randomizer. */
+ CSipHasher GetDeterministicRandomizer(uint64_t id);
+
+private:
+ struct ListenSocket {
+ SOCKET socket;
+ bool whitelisted;
+
+ ListenSocket(SOCKET socket_, bool whitelisted_) : socket(socket_), whitelisted(whitelisted_) {}
+ };
+
+ void ThreadOpenAddedConnections();
+ void ProcessOneShot();
+ void ThreadOpenConnections();
+ void ThreadMessageHandler();
+ void AcceptConnection(const ListenSocket& hListenSocket);
+ void ThreadSocketHandler();
+ void ThreadDNSAddressSeed();
+
+ uint64_t CalculateKeyedNetGroup(const CAddress& ad);
+
+ CNode* FindNode(const CNetAddr& ip);
+ CNode* FindNode(const CSubNet& subNet);
+ CNode* FindNode(const std::string& addrName);
+ CNode* FindNode(const CService& addr);
+
+ bool AttemptToEvictConnection();
+ CNode* ConnectNode(CAddress addrConnect, const char *pszDest, bool fCountFailure);
+ bool IsWhitelistedRange(const CNetAddr &addr);
+
+ void DeleteNode(CNode* pnode);
+
+ NodeId GetNewNodeId();
+
+ //!check is the banlist has unwritten changes
+ bool BannedSetIsDirty();
+ //!set the "dirty" flag for the banlist
+ void SetBannedSetDirty(bool dirty=true);
+ //!clean unused entries (if bantime has expired)
+ void SweepBanned();
+ void DumpAddresses();
+ void DumpData();
+ void DumpBanlist();
+
+ unsigned int GetReceiveFloodSize() const;
+
+ // Network stats
+ void RecordBytesRecv(uint64_t bytes);
+ void RecordBytesSent(uint64_t bytes);
+
+ // Network usage totals
+ CCriticalSection cs_totalBytesRecv;
+ CCriticalSection cs_totalBytesSent;
+ uint64_t nTotalBytesRecv;
+ uint64_t nTotalBytesSent;
+
+ // outbound limit & stats
+ uint64_t nMaxOutboundTotalBytesSentInCycle;
+ uint64_t nMaxOutboundCycleStartTime;
+ uint64_t nMaxOutboundLimit;
+ uint64_t nMaxOutboundTimeframe;
+
+ // Whitelisted ranges. Any node connecting from these is automatically
+ // whitelisted (as well as those connecting to whitelisted binds).
+ std::vector<CSubNet> vWhitelistedRange;
+ CCriticalSection cs_vWhitelistedRange;
+
+ unsigned int nSendBufferMaxSize;
+ unsigned int nReceiveFloodSize;
+
+ std::vector<ListenSocket> vhListenSocket;
+ banmap_t setBanned;
+ CCriticalSection cs_setBanned;
+ bool setBannedIsDirty;
+ bool fAddressesInitialized;
+ CAddrMan addrman;
+ std::deque<std::string> vOneShots;
+ CCriticalSection cs_vOneShots;
+ std::vector<std::string> vAddedNodes;
+ CCriticalSection cs_vAddedNodes;
+ std::vector<CNode*> vNodes;
+ std::list<CNode*> vNodesDisconnected;
+ mutable CCriticalSection cs_vNodes;
+ std::atomic<NodeId> nLastNodeId;
+ boost::condition_variable messageHandlerCondition;
+
+ /** Services this instance offers */
+ ServiceFlags nLocalServices;
+
+ /** Services this instance cares about */
+ ServiceFlags nRelevantServices;
+
+ CSemaphore *semOutbound;
+ int nMaxConnections;
+ int nMaxOutbound;
+ int nMaxFeeler;
+ std::atomic<int> nBestHeight;
+ CClientUIInterface* clientInterface;
+
+ /** SipHasher seeds for deterministic randomness */
+ const uint64_t nSeed0, nSeed1;
+};
+extern std::unique_ptr<CConnman> g_connman;
+void Discover(boost::thread_group& threadGroup);
void MapPort(bool fUseUPnP);
unsigned short GetListenPort();
bool BindListenPort(const CService &bindAddr, std::string& strError, bool fWhitelisted = false);
-void StartNode(boost::thread_group& threadGroup, CScheduler& scheduler);
-bool StopNode();
-void SocketSendData(CNode *pnode);
+size_t SocketSendData(CNode *pnode);
struct CombinerAll
{
@@ -117,11 +426,10 @@ struct CombinerAll
// Signals for message handling
struct CNodeSignals
{
- boost::signals2::signal<int ()> GetHeight;
- boost::signals2::signal<bool (CNode*), CombinerAll> ProcessMessages;
- boost::signals2::signal<bool (CNode*), CombinerAll> SendMessages;
+ boost::signals2::signal<bool (CNode*, CConnman&), CombinerAll> ProcessMessages;
+ boost::signals2::signal<bool (CNode*, CConnman&), CombinerAll> SendMessages;
boost::signals2::signal<void (NodeId, const CNode*)> InitializeNode;
- boost::signals2::signal<void (NodeId)> FinalizeNode;
+ boost::signals2::signal<void (NodeId, bool&)> FinalizeNode;
};
@@ -152,30 +460,15 @@ bool IsLocal(const CService& addr);
bool GetLocal(CService &addr, const CNetAddr *paddrPeer = NULL);
bool IsReachable(enum Network net);
bool IsReachable(const CNetAddr &addr);
-CAddress GetLocalAddress(const CNetAddr *paddrPeer = NULL);
+CAddress GetLocalAddress(const CNetAddr *paddrPeer, ServiceFlags nLocalServices);
extern bool fDiscover;
extern bool fListen;
-extern ServiceFlags nLocalServices;
-extern ServiceFlags nRelevantServices;
extern bool fRelayTxes;
-extern uint64_t nLocalHostNonce;
-extern CAddrMan addrman;
-/** Maximum number of connections to simultaneously allow (aka connection slots) */
-extern int nMaxConnections;
-
-extern std::vector<CNode*> vNodes;
-extern CCriticalSection cs_vNodes;
extern limitedmap<uint256, int64_t> mapAlreadyAskedFor;
-extern std::vector<std::string> vAddedNodes;
-extern CCriticalSection cs_vAddedNodes;
-
-extern NodeId nLastNodeId;
-extern CCriticalSection cs_nLastNodeId;
-
/** Subversion as sent to the P2P network in `version` messages */
extern std::string strSubVersion;
@@ -256,67 +549,6 @@ public:
};
-typedef enum BanReason
-{
- BanReasonUnknown = 0,
- BanReasonNodeMisbehaving = 1,
- BanReasonManuallyAdded = 2
-} BanReason;
-
-class CBanEntry
-{
-public:
- static const int CURRENT_VERSION=1;
- int nVersion;
- int64_t nCreateTime;
- int64_t nBanUntil;
- uint8_t banReason;
-
- CBanEntry()
- {
- SetNull();
- }
-
- CBanEntry(int64_t nCreateTimeIn)
- {
- SetNull();
- nCreateTime = nCreateTimeIn;
- }
-
- 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(nCreateTime);
- READWRITE(nBanUntil);
- READWRITE(banReason);
- }
-
- void SetNull()
- {
- nVersion = CBanEntry::CURRENT_VERSION;
- nCreateTime = 0;
- nBanUntil = 0;
- banReason = BanReasonUnknown;
- }
-
- std::string banReasonToString()
- {
- switch (banReason) {
- case BanReasonNodeMisbehaving:
- return "node misbehaving";
- case BanReasonManuallyAdded:
- return "manually added";
- default:
- return "unknown";
- }
- }
-};
-
-typedef std::map<CSubNet, CBanEntry> banmap_t;
-
/** Information about a peer */
class CNode
{
@@ -328,6 +560,7 @@ public:
CDataStream ssSend;
size_t nSendSize; // total size of all vSendMsg entries
size_t nSendOffset; // offset inside the first vSendMsg already sent
+ uint64_t nOptimisticBytesWritten;
uint64_t nSendBytes;
std::deque<CSerializeData> vSendMsg;
CCriticalSection cs_vSend;
@@ -374,17 +607,6 @@ public:
const uint64_t nKeyedNetGroup;
protected:
- // Denial-of-service detection/prevention
- // Key is IP address, value is banned-until-time
- static banmap_t setBanned;
- static CCriticalSection cs_setBanned;
- static bool setBannedIsDirty;
-
- // Whitelisted ranges. Any node connecting from these is automatically
- // whitelisted (as well as those connecting to whitelisted binds).
- static std::vector<CSubNet> vWhitelistedRange;
- static CCriticalSection cs_vWhitelistedRange;
-
mapMsgCmdSize mapSendBytesPerMsgCmd;
mapMsgCmdSize mapRecvBytesPerMsgCmd;
@@ -446,33 +668,27 @@ public:
CAmount lastSentFeeFilter;
int64_t nextSendTimeFeeFilter;
- CNode(SOCKET hSocketIn, const CAddress &addrIn, const std::string &addrNameIn = "", bool fInboundIn = false);
+ CNode(NodeId id, ServiceFlags nLocalServicesIn, int nMyStartingHeightIn, SOCKET hSocketIn, const CAddress &addrIn, uint64_t nKeyedNetGroupIn, const std::string &addrNameIn = "", bool fInboundIn = false);
~CNode();
private:
- // Network usage totals
- static CCriticalSection cs_totalBytesRecv;
- static CCriticalSection cs_totalBytesSent;
- static uint64_t nTotalBytesRecv;
- static uint64_t nTotalBytesSent;
-
- // outbound limit & stats
- static uint64_t nMaxOutboundTotalBytesSentInCycle;
- static uint64_t nMaxOutboundCycleStartTime;
- static uint64_t nMaxOutboundLimit;
- static uint64_t nMaxOutboundTimeframe;
-
CNode(const CNode&);
void operator=(const CNode&);
- static uint64_t CalculateKeyedNetGroup(const CAddress& ad);
+ uint64_t nLocalHostNonce;
+ ServiceFlags nLocalServices;
+ int nMyStartingHeight;
public:
NodeId GetId() const {
return id;
}
+ uint64_t GetLocalNonce() const {
+ return nLocalHostNonce;
+ }
+
int GetRefCount()
{
assert(nRefCount >= 0);
@@ -489,7 +705,7 @@ public:
}
// requires LOCK(cs_vRecvMsg)
- bool ReceiveMsgBytes(const char *pch, unsigned int nBytes);
+ bool ReceiveMsgBytes(const char *pch, unsigned int nBytes, bool& complete);
// requires LOCK(cs_vRecvMsg)
void SetRecvVersion(int nVersionIn)
@@ -749,110 +965,19 @@ public:
void CloseSocketDisconnect();
- // Denial-of-service detection/prevention
- // The idea is to detect peers that are behaving
- // badly and disconnect/ban them, but do it in a
- // one-coding-mistake-won't-shatter-the-entire-network
- // way.
- // IMPORTANT: There should be nothing I can give a
- // node that it will forward on that will make that
- // node's peers drop it. If there is, an attacker
- // can isolate a node and/or try to split the network.
- // Dropping a node for sending stuff that is invalid
- // now but might be valid in a later version is also
- // dangerous, because it can cause a network split
- // between nodes running old code and nodes running
- // new code.
- static void ClearBanned(); // needed for unit testing
- static bool IsBanned(CNetAddr ip);
- static bool IsBanned(CSubNet subnet);
- static void Ban(const CNetAddr &ip, const BanReason &banReason, int64_t bantimeoffset = 0, bool sinceUnixEpoch = false);
- static void Ban(const CSubNet &subNet, const BanReason &banReason, int64_t bantimeoffset = 0, bool sinceUnixEpoch = false);
- static bool Unban(const CNetAddr &ip);
- static bool Unban(const CSubNet &ip);
- static void GetBanned(banmap_t &banmap);
- static void SetBanned(const banmap_t &banmap);
-
- //!check is the banlist has unwritten changes
- static bool BannedSetIsDirty();
- //!set the "dirty" flag for the banlist
- static void SetBannedSetDirty(bool dirty=true);
- //!clean unused entries (if bantime has expired)
- static void SweepBanned();
-
void copyStats(CNodeStats &stats);
- static bool IsWhitelistedRange(const CNetAddr &ip);
- static void AddWhitelistedRange(const CSubNet &subnet);
-
- // Network stats
- static void RecordBytesRecv(uint64_t bytes);
- static void RecordBytesSent(uint64_t bytes);
-
- static uint64_t GetTotalBytesRecv();
- static uint64_t GetTotalBytesSent();
-
- //!set the max outbound target in bytes
- static void SetMaxOutboundTarget(uint64_t limit);
- static uint64_t GetMaxOutboundTarget();
-
- //!set the timeframe for the max outbound target
- static void SetMaxOutboundTimeframe(uint64_t timeframe);
- static uint64_t GetMaxOutboundTimeframe();
-
- //!check if the outbound target is reached
- // if param historicalBlockServingLimit is set true, the function will
- // response true if the limit for serving historical blocks has been reached
- static bool OutboundTargetReached(bool historicalBlockServingLimit);
-
- //!response the bytes left in the current max outbound cycle
- // in case of no limit, it will always response 0
- static uint64_t GetOutboundTargetBytesLeft();
-
- //!response the time in second left in the current max outbound cycle
- // in case of no limit, it will always response 0
- static uint64_t GetMaxOutboundTimeLeftInCycle();
+ ServiceFlags GetLocalServices() const
+ {
+ return nLocalServices;
+ }
};
-class CTransaction;
-void RelayTransaction(const CTransaction& tx);
-/** Access to the (IP) address database (peers.dat) */
-class CAddrDB
-{
-private:
- boost::filesystem::path pathAddr;
-public:
- CAddrDB();
- bool Write(const CAddrMan& addr);
- bool Read(CAddrMan& addr);
- bool Read(CAddrMan& addr, CDataStream& ssPeers);
-};
-
-/** Access to the banlist database (banlist.dat) */
-class CBanDB
-{
-private:
- boost::filesystem::path pathBanlist;
-public:
- CBanDB();
- bool Write(const banmap_t& banSet);
- bool Read(banmap_t& banSet);
-};
/** 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/qt/bantablemodel.cpp b/src/qt/bantablemodel.cpp
index d95106b5ac..6e11e23904 100644
--- a/src/qt/bantablemodel.cpp
+++ b/src/qt/bantablemodel.cpp
@@ -48,7 +48,8 @@ public:
void refreshBanlist()
{
banmap_t banMap;
- CNode::GetBanned(banMap);
+ if(g_connman)
+ g_connman->GetBanned(banMap);
cachedBanlist.clear();
#if QT_VERSION >= 0x040700
diff --git a/src/qt/clientmodel.cpp b/src/qt/clientmodel.cpp
index 14661b857a..83c78850e2 100644
--- a/src/qt/clientmodel.cpp
+++ b/src/qt/clientmodel.cpp
@@ -50,16 +50,18 @@ ClientModel::~ClientModel()
int ClientModel::getNumConnections(unsigned int flags) const
{
- LOCK(cs_vNodes);
- if (flags == CONNECTIONS_ALL) // Shortcut if we want total
- return vNodes.size();
+ CConnman::NumConnections connections = CConnman::CONNECTIONS_NONE;
- int nNum = 0;
- BOOST_FOREACH(const CNode* pnode, vNodes)
- if (flags & (pnode->fInbound ? CONNECTIONS_IN : CONNECTIONS_OUT))
- nNum++;
+ if(flags == CONNECTIONS_IN)
+ connections = CConnman::CONNECTIONS_IN;
+ else if (flags == CONNECTIONS_OUT)
+ connections = CConnman::CONNECTIONS_OUT;
+ else if (flags == CONNECTIONS_ALL)
+ connections = CConnman::CONNECTIONS_ALL;
- return nNum;
+ if(g_connman)
+ return g_connman->GetNodeCount(connections);
+ return 0;
}
int ClientModel::getNumBlocks() const
@@ -70,12 +72,16 @@ int ClientModel::getNumBlocks() const
quint64 ClientModel::getTotalBytesRecv() const
{
- return CNode::GetTotalBytesRecv();
+ if(!g_connman)
+ return 0;
+ return g_connman->GetTotalBytesRecv();
}
quint64 ClientModel::getTotalBytesSent() const
{
- return CNode::GetTotalBytesSent();
+ if(!g_connman)
+ return 0;
+ return g_connman->GetTotalBytesSent();
}
QDateTime ClientModel::getLastBlockDate() const
diff --git a/src/qt/guiutil.cpp b/src/qt/guiutil.cpp
index c00f5e8591..444e35de8a 100644
--- a/src/qt/guiutil.cpp
+++ b/src/qt/guiutil.cpp
@@ -291,17 +291,17 @@ void copyEntryData(QAbstractItemView *view, int column, int role)
}
}
-QString getEntryData(QAbstractItemView *view, int column, int role)
+QVariant getEntryData(QAbstractItemView *view, int column, int role)
{
if(!view || !view->selectionModel())
- return QString();
+ return QVariant();
QModelIndexList selection = view->selectionModel()->selectedRows(column);
if(!selection.isEmpty()) {
// Return first item
- return (selection.at(0).data(role).toString());
+ return (selection.at(0).data(role));
}
- return QString();
+ return QVariant();
}
QString getSaveFileName(QWidget *parent, const QString &caption, const QString &dir,
diff --git a/src/qt/guiutil.h b/src/qt/guiutil.h
index 9267e0a6c9..d5a658e7c0 100644
--- a/src/qt/guiutil.h
+++ b/src/qt/guiutil.h
@@ -70,7 +70,7 @@ namespace GUIUtil
@param[in] role Data role to extract from the model
@see TransactionView::copyLabel, TransactionView::copyAmount, TransactionView::copyAddress
*/
- QString getEntryData(QAbstractItemView *view, int column, int role);
+ QVariant getEntryData(QAbstractItemView *view, int column, int role);
void setClipboard(const QString& str);
diff --git a/src/qt/paymentrequest.proto b/src/qt/paymentrequest.proto
index b2281c4c7b..d2721a34bd 100644
--- a/src/qt/paymentrequest.proto
+++ b/src/qt/paymentrequest.proto
@@ -6,6 +6,8 @@
// https://en.bitcoin.it/wiki/Payment_Request
//
+syntax = "proto2";
+
package payments;
option java_package = "org.bitcoin.protocols.payments";
option java_outer_classname = "Protos";
diff --git a/src/qt/peertablemodel.cpp b/src/qt/peertablemodel.cpp
index 84ad0052fd..a820bd791f 100644
--- a/src/qt/peertablemodel.cpp
+++ b/src/qt/peertablemodel.cpp
@@ -24,6 +24,8 @@ bool NodeLessThan::operator()(const CNodeCombinedStats &left, const CNodeCombine
switch(column)
{
+ case PeerTableModel::NetNodeId:
+ return pLeft->nodeid < pRight->nodeid;
case PeerTableModel::Address:
return pLeft->addrName.compare(pRight->addrName) < 0;
case PeerTableModel::Subversion:
@@ -52,24 +54,21 @@ public:
void refreshPeers()
{
{
- TRY_LOCK(cs_vNodes, lockNodes);
- if (!lockNodes)
- {
- // skip the refresh if we can't immediately get the lock
- return;
- }
cachedNodeStats.clear();
+ std::vector<CNodeStats> vstats;
+ if(g_connman)
+ g_connman->GetNodeStats(vstats);
#if QT_VERSION >= 0x040700
- cachedNodeStats.reserve(vNodes.size());
+ cachedNodeStats.reserve(vstats.size());
#endif
- Q_FOREACH (CNode* pnode, vNodes)
+ Q_FOREACH (const CNodeStats& nodestats, vstats)
{
CNodeCombinedStats stats;
stats.nodeStateStats.nMisbehavior = 0;
stats.nodeStateStats.nSyncHeight = -1;
stats.nodeStateStats.nCommonHeight = -1;
stats.fNodeStateStatsAvailable = false;
- pnode->copyStats(stats.nodeStats);
+ stats.nodeStats = nodestats;
cachedNodeStats.append(stats);
}
}
@@ -114,7 +113,7 @@ PeerTableModel::PeerTableModel(ClientModel *parent) :
clientModel(parent),
timer(0)
{
- columns << tr("Node/Service") << tr("User Agent") << tr("Ping Time");
+ columns << tr("NodeId") << tr("Node/Service") << tr("User Agent") << tr("Ping Time");
priv = new PeerTablePriv();
// default to unsorted
priv->sortColumn = -1;
@@ -160,6 +159,8 @@ QVariant PeerTableModel::data(const QModelIndex &index, int role) const
if (role == Qt::DisplayRole) {
switch(index.column())
{
+ case NetNodeId:
+ return rec->nodeStats.nodeid;
case Address:
return QString::fromStdString(rec->nodeStats.addrName);
case Subversion:
diff --git a/src/qt/peertablemodel.h b/src/qt/peertablemodel.h
index a2aaaa5d24..a4f7bbdb3d 100644
--- a/src/qt/peertablemodel.h
+++ b/src/qt/peertablemodel.h
@@ -52,9 +52,10 @@ public:
void stopAutoRefresh();
enum ColumnIndex {
- Address = 0,
- Subversion = 1,
- Ping = 2
+ NetNodeId = 0,
+ Address = 1,
+ Subversion = 2,
+ Ping = 3
};
/** @name Methods overridden from QAbstractTableModel
diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp
index bcaa9164c9..f35f401d06 100644
--- a/src/qt/rpcconsole.cpp
+++ b/src/qt/rpcconsole.cpp
@@ -876,37 +876,34 @@ void RPCConsole::showBanTableContextMenu(const QPoint& point)
void RPCConsole::disconnectSelectedNode()
{
+ if(!g_connman)
+ return;
// Get currently selected peer address
- QString strNode = GUIUtil::getEntryData(ui->peerWidget, 0, PeerTableModel::Address);
+ NodeId id = GUIUtil::getEntryData(ui->peerWidget, 0, PeerTableModel::NetNodeId).toInt();
// Find the node, disconnect it and clear the selected node
- if (CNode *bannedNode = FindNode(strNode.toStdString())) {
- bannedNode->fDisconnect = true;
+ if(g_connman->DisconnectNode(id))
clearSelectedNode();
- }
}
void RPCConsole::banSelectedNode(int bantime)
{
- if (!clientModel)
+ if (!clientModel || !g_connman)
return;
// Get currently selected peer address
- QString strNode = GUIUtil::getEntryData(ui->peerWidget, 0, PeerTableModel::Address);
+ QString strNode = GUIUtil::getEntryData(ui->peerWidget, 0, PeerTableModel::Address).toString();
// Find possible nodes, ban it and clear the selected node
- if (FindNode(strNode.toStdString())) {
- std::string nStr = strNode.toStdString();
- std::string addr;
- int port = 0;
- SplitHostPort(nStr, port, addr);
-
- CNetAddr resolved;
- if(!LookupHost(addr.c_str(), resolved, false))
- return;
- CNode::Ban(resolved, BanReasonManuallyAdded, bantime);
+ std::string nStr = strNode.toStdString();
+ std::string addr;
+ int port = 0;
+ SplitHostPort(nStr, port, addr);
- clearSelectedNode();
- clientModel->getBanTableModel()->refresh();
- }
+ CNetAddr resolved;
+ if(!LookupHost(addr.c_str(), resolved, false))
+ return;
+ g_connman->Ban(resolved, BanReasonManuallyAdded, bantime);
+ clearSelectedNode();
+ clientModel->getBanTableModel()->refresh();
}
void RPCConsole::unbanSelectedNode()
@@ -915,13 +912,13 @@ void RPCConsole::unbanSelectedNode()
return;
// Get currently selected ban address
- QString strNode = GUIUtil::getEntryData(ui->banlistWidget, 0, BanTableModel::Address);
+ QString strNode = GUIUtil::getEntryData(ui->banlistWidget, 0, BanTableModel::Address).toString();
CSubNet possibleSubnet;
LookupSubNet(strNode.toStdString().c_str(), possibleSubnet);
- if (possibleSubnet.IsValid())
+ if (possibleSubnet.IsValid() && g_connman)
{
- CNode::Unban(possibleSubnet);
+ g_connman->Unban(possibleSubnet);
clientModel->getBanTableModel()->refresh();
}
}
diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp
index 3e96bb18c3..8433818a64 100644
--- a/src/qt/sendcoinsdialog.cpp
+++ b/src/qt/sendcoinsdialog.cpp
@@ -589,6 +589,9 @@ void SendCoinsDialog::updateGlobalFeeVariables()
{
nTxConfirmTarget = defaultConfirmTarget - ui->sliderSmartFee->value();
payTxFee = CFeeRate(0);
+
+ // set nMinimumTotalFee to 0 to not accidentally pay a custom fee
+ CoinControlDialog::coinControl->nMinimumTotalFee = 0;
}
else
{
@@ -781,7 +784,7 @@ void SendCoinsDialog::coinControlUpdateLabels()
ui->radioCustomAtLeast->setVisible(true);
// only enable the feature if inputs are selected
- ui->radioCustomAtLeast->setEnabled(CoinControlDialog::coinControl->HasSelected());
+ ui->radioCustomAtLeast->setEnabled(ui->radioCustomFee->isChecked() && !ui->checkBoxMinimumFee->isChecked() &&CoinControlDialog::coinControl->HasSelected());
}
else
{
diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp
index ae7efc7a0d..73851e97fc 100644
--- a/src/qt/walletmodel.cpp
+++ b/src/qt/walletmodel.cpp
@@ -328,7 +328,7 @@ WalletModel::SendCoinsReturn WalletModel::sendCoins(WalletModelTransaction &tran
}
CReserveKey *keyChange = transaction.getPossibleKeyChange();
- if(!wallet->CommitTransaction(*newTx, *keyChange))
+ if(!wallet->CommitTransaction(*newTx, *keyChange, g_connman.get()))
return TransactionCommitFailed;
CTransaction* t = (CTransaction*)newTx;
diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp
index b90410017b..f05f8ff358 100644
--- a/src/rpc/blockchain.cpp
+++ b/src/rpc/blockchain.cpp
@@ -26,8 +26,20 @@
#include <boost/thread/thread.hpp> // boost::thread::interrupt
+#include <mutex>
+#include <condition_variable>
using namespace std;
+struct CUpdatedBlock
+{
+ uint256 hash;
+ int height;
+};
+
+static std::mutex cs_blockchange;
+static std::condition_variable cond_blockchange;
+static CUpdatedBlock latestblock;
+
extern void TxToJSON(const CTransaction& tx, const uint256 hashBlock, UniValue& entry);
void ScriptPubKeyToJSON(const CScript& scriptPubKey, UniValue& out, bool fIncludeHex);
@@ -168,6 +180,138 @@ UniValue getbestblockhash(const UniValue& params, bool fHelp)
return chainActive.Tip()->GetBlockHash().GetHex();
}
+void RPCNotifyBlockChange(bool ibd, const CBlockIndex * pindex)
+{
+ if(pindex) {
+ std::lock_guard<std::mutex> lock(cs_blockchange);
+ latestblock.hash = pindex->GetBlockHash();
+ latestblock.height = pindex->nHeight;
+ }
+ cond_blockchange.notify_all();
+}
+
+UniValue waitfornewblock(const UniValue& params, bool fHelp)
+{
+ if (fHelp || params.size() > 1)
+ throw runtime_error(
+ "waitfornewblock\n"
+ "\nWaits for a specific new block and returns useful info about it.\n"
+ "\nReturns the current block on timeout or exit.\n"
+ "\nArguments:\n"
+ "1. timeout (milliseconds) (int, optional, default=false)\n"
+ "\nResult::\n"
+ "{ (json object)\n"
+ " \"hash\" : { (string) The blockhash\n"
+ " \"height\" : { (int) Block height\n"
+ "}\n"
+ "\nExamples\n"
+ + HelpExampleCli("waitfornewblock", "1000")
+ + HelpExampleRpc("waitfornewblock", "1000")
+ );
+ int timeout = 0;
+ if (params.size() > 0)
+ timeout = params[0].get_int();
+
+ CUpdatedBlock block;
+ {
+ std::unique_lock<std::mutex> lock(cs_blockchange);
+ block = latestblock;
+ if(timeout)
+ cond_blockchange.wait_for(lock, std::chrono::milliseconds(timeout), [&block]{return latestblock.height != block.height || latestblock.hash != block.hash || !IsRPCRunning(); });
+ else
+ cond_blockchange.wait(lock, [&block]{return latestblock.height != block.height || latestblock.hash != block.hash || !IsRPCRunning(); });
+ block = latestblock;
+ }
+ UniValue ret(UniValue::VOBJ);
+ ret.push_back(Pair("hash", block.hash.GetHex()));
+ ret.push_back(Pair("height", block.height));
+ return ret;
+}
+
+UniValue waitforblock(const UniValue& params, bool fHelp)
+{
+ if (fHelp || params.size() < 1 || params.size() > 2)
+ throw runtime_error(
+ "waitforblock\n"
+ "\nWaits for a specific new block and returns useful info about it.\n"
+ "\nReturns the current block on timeout or exit.\n"
+ "\nArguments:\n"
+ "1. blockhash to wait for (string)\n"
+ "2. timeout (milliseconds) (int, optional, default=false)\n"
+ "\nResult::\n"
+ "{ (json object)\n"
+ " \"hash\" : { (string) The blockhash\n"
+ " \"height\" : { (int) Block height\n"
+ "}\n"
+ "\nExamples\n"
+ + HelpExampleCli("waitforblock", "\"0000000000079f8ef3d2c688c244eb7a4570b24c9ed7b4a8c619eb02596f8862\", 1000")
+ + HelpExampleRpc("waitforblock", "\"0000000000079f8ef3d2c688c244eb7a4570b24c9ed7b4a8c619eb02596f8862\", 1000")
+ );
+ int timeout = 0;
+
+ uint256 hash = uint256S(params[0].get_str());
+
+ if (params.size() > 1)
+ timeout = params[1].get_int();
+
+ CUpdatedBlock block;
+ {
+ std::unique_lock<std::mutex> lock(cs_blockchange);
+ if(timeout)
+ cond_blockchange.wait_for(lock, std::chrono::milliseconds(timeout), [&hash]{return latestblock.hash == hash || !IsRPCRunning();});
+ else
+ cond_blockchange.wait(lock, [&hash]{return latestblock.hash == hash || !IsRPCRunning(); });
+ block = latestblock;
+ }
+
+ UniValue ret(UniValue::VOBJ);
+ ret.push_back(Pair("hash", block.hash.GetHex()));
+ ret.push_back(Pair("height", block.height));
+ return ret;
+}
+
+UniValue waitforblockheight(const UniValue& params, bool fHelp)
+{
+ if (fHelp || params.size() < 1 || params.size() > 2)
+ throw runtime_error(
+ "waitforblock\n"
+ "\nWaits for (at least) block height and returns the height and hash\n"
+ "\nof the current tip.\n"
+ "\nReturns the current block on timeout or exit.\n"
+ "\nArguments:\n"
+ "1. block height to wait for (int)\n"
+ "2. timeout (milliseconds) (int, optional, default=false)\n"
+ "\nResult::\n"
+ "{ (json object)\n"
+ " \"hash\" : { (string) The blockhash\n"
+ " \"height\" : { (int) Block height\n"
+ "}\n"
+ "\nExamples\n"
+ + HelpExampleCli("waitforblockheight", "\"100\", 1000")
+ + HelpExampleRpc("waitforblockheight", "\"100\", 1000")
+ );
+ int timeout = 0;
+
+ int height = params[0].get_int();
+
+ if (params.size() > 1)
+ timeout = params[1].get_int();
+
+ CUpdatedBlock block;
+ {
+ std::unique_lock<std::mutex> lock(cs_blockchange);
+ if(timeout)
+ cond_blockchange.wait_for(lock, std::chrono::milliseconds(timeout), [&height]{return latestblock.height >= height || !IsRPCRunning();});
+ else
+ cond_blockchange.wait(lock, [&height]{return latestblock.height >= height || !IsRPCRunning(); });
+ block = latestblock;
+ }
+ UniValue ret(UniValue::VOBJ);
+ ret.push_back(Pair("hash", block.hash.GetHex()));
+ ret.push_back(Pair("height", block.height));
+ return ret;
+}
+
UniValue getdifficulty(const UniValue& params, bool fHelp)
{
if (fHelp || params.size() != 0)
@@ -1133,7 +1277,7 @@ UniValue invalidateblock(const UniValue& params, bool fHelp)
}
if (state.IsValid()) {
- ActivateBestChain(state, Params());
+ ActivateBestChain(state, Params(), NULL, g_connman.get());
}
if (!state.IsValid()) {
@@ -1171,7 +1315,7 @@ UniValue reconsiderblock(const UniValue& params, bool fHelp)
}
CValidationState state;
- ActivateBestChain(state, Params());
+ ActivateBestChain(state, Params(), NULL, g_connman.get());
if (!state.IsValid()) {
throw JSONRPCError(RPC_DATABASE_ERROR, state.GetRejectReason());
@@ -1203,6 +1347,9 @@ static const CRPCCommand commands[] =
/* Not shown in help */
{ "hidden", "invalidateblock", &invalidateblock, true },
{ "hidden", "reconsiderblock", &reconsiderblock, true },
+ { "hidden", "waitfornewblock", &waitfornewblock, true },
+ { "hidden", "waitforblock", &waitforblock, true },
+ { "hidden", "waitforblockheight", &waitforblockheight, true },
};
void RegisterBlockchainRPCCommands(CRPCTable &t)
diff --git a/src/rpc/client.cpp b/src/rpc/client.cpp
index 3003ea3452..c14d9d6747 100644
--- a/src/rpc/client.cpp
+++ b/src/rpc/client.cpp
@@ -46,6 +46,12 @@ static const CRPCConvertParam vRPCConvertParams[] =
{ "getbalance", 1 },
{ "getbalance", 2 },
{ "getblockhash", 0 },
+ { "waitforblockheight", 0 },
+ { "waitforblockheight", 1 },
+ { "waitforblock", 1 },
+ { "waitforblock", 2 },
+ { "waitfornewblock", 0 },
+ { "waitfornewblock", 1 },
{ "move", 2 },
{ "move", 3 },
{ "sendfrom", 2 },
diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp
index 14183c8e82..7794ac619d 100644
--- a/src/rpc/mining.cpp
+++ b/src/rpc/mining.cpp
@@ -131,7 +131,7 @@ UniValue generateBlocks(boost::shared_ptr<CReserveScript> coinbaseScript, int nG
continue;
}
CValidationState state;
- if (!ProcessNewBlock(state, Params(), NULL, pblock, true, NULL))
+ if (!ProcessNewBlock(state, Params(), NULL, pblock, true, NULL, g_connman.get()))
throw JSONRPCError(RPC_INTERNAL_ERROR, "ProcessNewBlock, block not accepted");
++nHeight;
blockHashes.push_back(pblock->GetHash().GetHex());
@@ -457,7 +457,10 @@ UniValue getblocktemplate(const UniValue& params, bool fHelp)
if (strMode != "template")
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid mode");
- if (vNodes.empty())
+ if(!g_connman)
+ throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled");
+
+ if (g_connman->GetNodeCount(CConnman::CONNECTIONS_ALL) == 0)
throw JSONRPCError(RPC_CLIENT_NOT_CONNECTED, "Bitcoin is not connected!");
if (IsInitialBlockDownload())
@@ -754,7 +757,7 @@ UniValue submitblock(const UniValue& params, bool fHelp)
CValidationState state;
submitblock_StateCatcher sc(block.GetHash());
RegisterValidationInterface(&sc);
- bool fAccepted = ProcessNewBlock(state, Params(), NULL, &block, true, NULL);
+ bool fAccepted = ProcessNewBlock(state, Params(), NULL, &block, true, NULL, g_connman.get());
UnregisterValidationInterface(&sc);
if (fBlockPresent)
{
diff --git a/src/rpc/misc.cpp b/src/rpc/misc.cpp
index e96feaa864..5afcf6353c 100644
--- a/src/rpc/misc.cpp
+++ b/src/rpc/misc.cpp
@@ -89,7 +89,8 @@ UniValue getinfo(const UniValue& params, bool fHelp)
#endif
obj.push_back(Pair("blocks", (int)chainActive.Height()));
obj.push_back(Pair("timeoffset", GetTimeOffset()));
- obj.push_back(Pair("connections", (int)vNodes.size()));
+ if(g_connman)
+ obj.push_back(Pair("connections", (int)g_connman->GetNodeCount(CConnman::CONNECTIONS_ALL)));
obj.push_back(Pair("proxy", (proxy.IsValid() ? proxy.proxy.ToStringIPPort() : string())));
obj.push_back(Pair("difficulty", (double)GetDifficulty()));
obj.push_back(Pair("testnet", Params().TestnetToBeDeprecatedFieldRPC()));
@@ -320,43 +321,6 @@ UniValue createmultisig(const UniValue& params, bool fHelp)
return result;
}
-UniValue createwitnessaddress(const UniValue& params, bool fHelp)
-{
- if (fHelp || params.size() < 1 || params.size() > 1)
- {
- string msg = "createwitnessaddress \"script\"\n"
- "\nCreates a witness address for a particular script.\n"
- "It returns a json object with the address and witness script.\n"
-
- "\nArguments:\n"
- "1. \"script\" (string, required) A hex encoded script\n"
-
- "\nResult:\n"
- "{\n"
- " \"address\":\"multisigaddress\", (string) The value of the new address (P2SH of witness script).\n"
- " \"witnessScript\":\"script\" (string) The string value of the hex-encoded witness script.\n"
- "}\n"
- ;
- throw runtime_error(msg);
- }
-
- if (!IsHex(params[0].get_str())) {
- throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Script must be hex-encoded");
- }
-
- std::vector<unsigned char> code = ParseHex(params[0].get_str());
- CScript script(code.begin(), code.end());
- CScript witscript = GetScriptForWitness(script);
- CScriptID witscriptid(witscript);
- CBitcoinAddress address(witscriptid);
-
- UniValue result(UniValue::VOBJ);
- result.push_back(Pair("address", address.ToString()));
- result.push_back(Pair("witnessScript", HexStr(witscript.begin(), witscript.end())));
-
- return result;
-}
-
UniValue verifymessage(const UniValue& params, bool fHelp)
{
if (fHelp || params.size() != 3)
@@ -471,14 +435,16 @@ UniValue setmocktime(const UniValue& params, bool fHelp)
// atomically with the time change to prevent peers from being
// disconnected because we think we haven't communicated with them
// in a long time.
- LOCK2(cs_main, cs_vNodes);
+ LOCK(cs_main);
RPCTypeCheck(params, boost::assign::list_of(UniValue::VNUM));
SetMockTime(params[0].get_int64());
uint64_t t = GetTime();
- BOOST_FOREACH(CNode* pnode, vNodes) {
- pnode->nLastSend = pnode->nLastRecv = t;
+ if(g_connman) {
+ g_connman->ForEachNode([t](CNode* pnode) {
+ pnode->nLastSend = pnode->nLastRecv = t;
+ });
}
return NullUniValue;
@@ -490,7 +456,6 @@ static const CRPCCommand commands[] =
{ "control", "getinfo", &getinfo, true }, /* uses wallet if enabled */
{ "util", "validateaddress", &validateaddress, true }, /* uses wallet if enabled */
{ "util", "createmultisig", &createmultisig, true },
- { "util", "createwitnessaddress", &createwitnessaddress, true },
{ "util", "verifymessage", &verifymessage, true },
{ "util", "signmessagewithprivkey", &signmessagewithprivkey, true },
diff --git a/src/rpc/net.cpp b/src/rpc/net.cpp
index 840bfd5a24..b011029f51 100644
--- a/src/rpc/net.cpp
+++ b/src/rpc/net.cpp
@@ -36,9 +36,10 @@ UniValue getconnectioncount(const UniValue& params, bool fHelp)
+ HelpExampleRpc("getconnectioncount", "")
);
- LOCK2(cs_main, cs_vNodes);
+ if(!g_connman)
+ throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled");
- return (int)vNodes.size();
+ return (int)g_connman->GetNodeCount(CConnman::CONNECTIONS_ALL);
}
UniValue ping(const UniValue& params, bool fHelp)
@@ -54,29 +55,16 @@ UniValue ping(const UniValue& params, bool fHelp)
+ HelpExampleRpc("ping", "")
);
- // Request that each node send a ping during next message processing pass
- LOCK2(cs_main, cs_vNodes);
-
- BOOST_FOREACH(CNode* pNode, vNodes) {
- pNode->fPingQueued = true;
- }
+ if(!g_connman)
+ throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled");
+ // Request that each node send a ping during next message processing pass
+ g_connman->ForEachNode([](CNode* pnode) {
+ pnode->fPingQueued = true;
+ });
return NullUniValue;
}
-static void CopyNodeStats(std::vector<CNodeStats>& vstats)
-{
- vstats.clear();
-
- LOCK(cs_vNodes);
- vstats.reserve(vNodes.size());
- BOOST_FOREACH(CNode* pnode, vNodes) {
- CNodeStats stats;
- pnode->copyStats(stats);
- vstats.push_back(stats);
- }
-}
-
UniValue getpeerinfo(const UniValue& params, bool fHelp)
{
if (fHelp || params.size() != 0)
@@ -127,10 +115,11 @@ UniValue getpeerinfo(const UniValue& params, bool fHelp)
+ HelpExampleRpc("getpeerinfo", "")
);
- LOCK(cs_main);
+ if(!g_connman)
+ throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled");
vector<CNodeStats> vstats;
- CopyNodeStats(vstats);
+ g_connman->GetNodeStats(vstats);
UniValue ret(UniValue::VARR);
@@ -214,32 +203,27 @@ UniValue addnode(const UniValue& params, bool fHelp)
+ HelpExampleRpc("addnode", "\"192.168.0.6:8333\", \"onetry\"")
);
+ if(!g_connman)
+ throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled");
+
string strNode = params[0].get_str();
if (strCommand == "onetry")
{
CAddress addr;
- OpenNetworkConnection(addr, false, NULL, strNode.c_str());
+ g_connman->OpenNetworkConnection(addr, false, NULL, strNode.c_str());
return NullUniValue;
}
- LOCK(cs_vAddedNodes);
- vector<string>::iterator it = vAddedNodes.begin();
- for(; it != vAddedNodes.end(); it++)
- if (strNode == *it)
- break;
-
if (strCommand == "add")
{
- if (it != vAddedNodes.end())
+ if(!g_connman->AddNode(strNode))
throw JSONRPCError(RPC_CLIENT_NODE_ALREADY_ADDED, "Error: Node already added");
- vAddedNodes.push_back(strNode);
}
else if(strCommand == "remove")
{
- if (it == vAddedNodes.end())
+ if(!g_connman->RemoveAddedNode(strNode))
throw JSONRPCError(RPC_CLIENT_NODE_NOT_ADDED, "Error: Node has not been added.");
- vAddedNodes.erase(it);
}
return NullUniValue;
@@ -258,11 +242,12 @@ UniValue disconnectnode(const UniValue& params, bool fHelp)
+ HelpExampleRpc("disconnectnode", "\"192.168.0.6:8333\"")
);
- CNode* pNode = FindNode(params[0].get_str());
- if (pNode == NULL)
- throw JSONRPCError(RPC_CLIENT_NODE_NOT_CONNECTED, "Node not found in connected nodes");
+ if(!g_connman)
+ throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled");
- pNode->fDisconnect = true;
+ bool ret = g_connman->DisconnectNode(params[0].get_str());
+ if (!ret)
+ throw JSONRPCError(RPC_CLIENT_NODE_NOT_CONNECTED, "Node not found in connected nodes");
return NullUniValue;
}
@@ -296,7 +281,10 @@ UniValue getaddednodeinfo(const UniValue& params, bool fHelp)
+ HelpExampleRpc("getaddednodeinfo", "true, \"192.168.0.201\"")
);
- std::vector<AddedNodeInfo> vInfo = GetAddedNodeInfo();
+ if(!g_connman)
+ throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled");
+
+ std::vector<AddedNodeInfo> vInfo = g_connman->GetAddedNodeInfo();
if (params.size() == 1) {
bool found = false;
@@ -358,19 +346,21 @@ UniValue getnettotals(const UniValue& params, bool fHelp)
+ HelpExampleCli("getnettotals", "")
+ HelpExampleRpc("getnettotals", "")
);
+ if(!g_connman)
+ throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled");
UniValue obj(UniValue::VOBJ);
- obj.push_back(Pair("totalbytesrecv", CNode::GetTotalBytesRecv()));
- obj.push_back(Pair("totalbytessent", CNode::GetTotalBytesSent()));
+ obj.push_back(Pair("totalbytesrecv", g_connman->GetTotalBytesRecv()));
+ obj.push_back(Pair("totalbytessent", g_connman->GetTotalBytesSent()));
obj.push_back(Pair("timemillis", GetTimeMillis()));
UniValue outboundLimit(UniValue::VOBJ);
- outboundLimit.push_back(Pair("timeframe", CNode::GetMaxOutboundTimeframe()));
- outboundLimit.push_back(Pair("target", CNode::GetMaxOutboundTarget()));
- outboundLimit.push_back(Pair("target_reached", CNode::OutboundTargetReached(false)));
- outboundLimit.push_back(Pair("serve_historical_blocks", !CNode::OutboundTargetReached(true)));
- outboundLimit.push_back(Pair("bytes_left_in_cycle", CNode::GetOutboundTargetBytesLeft()));
- outboundLimit.push_back(Pair("time_left_in_cycle", CNode::GetMaxOutboundTimeLeftInCycle()));
+ outboundLimit.push_back(Pair("timeframe", g_connman->GetMaxOutboundTimeframe()));
+ outboundLimit.push_back(Pair("target", g_connman->GetMaxOutboundTarget()));
+ outboundLimit.push_back(Pair("target_reached", g_connman->OutboundTargetReached(false)));
+ outboundLimit.push_back(Pair("serve_historical_blocks", !g_connman->OutboundTargetReached(true)));
+ outboundLimit.push_back(Pair("bytes_left_in_cycle", g_connman->GetOutboundTargetBytesLeft()));
+ outboundLimit.push_back(Pair("time_left_in_cycle", g_connman->GetMaxOutboundTimeLeftInCycle()));
obj.push_back(Pair("uploadtarget", outboundLimit));
return obj;
}
@@ -437,15 +427,16 @@ UniValue getnetworkinfo(const UniValue& params, bool fHelp)
);
LOCK(cs_main);
-
UniValue obj(UniValue::VOBJ);
obj.push_back(Pair("version", CLIENT_VERSION));
obj.push_back(Pair("subversion", strSubVersion));
obj.push_back(Pair("protocolversion",PROTOCOL_VERSION));
- obj.push_back(Pair("localservices", strprintf("%016x", nLocalServices)));
+ if(g_connman)
+ obj.push_back(Pair("localservices", strprintf("%016x", g_connman->GetLocalServices())));
obj.push_back(Pair("localrelay", fRelayTxes));
obj.push_back(Pair("timeoffset", GetTimeOffset()));
- obj.push_back(Pair("connections", (int)vNodes.size()));
+ if(g_connman)
+ obj.push_back(Pair("connections", (int)g_connman->GetNodeCount(CConnman::CONNECTIONS_ALL)));
obj.push_back(Pair("networks", GetNetworksInfo()));
obj.push_back(Pair("relayfee", ValueFromAmount(::minRelayTxFee.GetFeePerK())));
UniValue localAddresses(UniValue::VARR);
@@ -485,6 +476,8 @@ UniValue setban(const UniValue& params, bool fHelp)
+ HelpExampleCli("setban", "\"192.168.0.0/24\" \"add\"")
+ HelpExampleRpc("setban", "\"192.168.0.6\", \"add\", 86400")
);
+ if(!g_connman)
+ throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled");
CSubNet subNet;
CNetAddr netAddr;
@@ -506,7 +499,7 @@ UniValue setban(const UniValue& params, bool fHelp)
if (strCommand == "add")
{
- if (isSubnet ? CNode::IsBanned(subNet) : CNode::IsBanned(netAddr))
+ if (isSubnet ? g_connman->IsBanned(subNet) : g_connman->IsBanned(netAddr))
throw JSONRPCError(RPC_CLIENT_NODE_ALREADY_ADDED, "Error: IP/Subnet already banned");
int64_t banTime = 0; //use standard bantime if not specified
@@ -517,11 +510,11 @@ UniValue setban(const UniValue& params, bool fHelp)
if (params.size() == 4 && params[3].isTrue())
absolute = true;
- isSubnet ? CNode::Ban(subNet, BanReasonManuallyAdded, banTime, absolute) : CNode::Ban(netAddr, BanReasonManuallyAdded, banTime, absolute);
+ isSubnet ? g_connman->Ban(subNet, BanReasonManuallyAdded, banTime, absolute) : g_connman->Ban(netAddr, BanReasonManuallyAdded, banTime, absolute);
}
else if(strCommand == "remove")
{
- if (!( isSubnet ? CNode::Unban(subNet) : CNode::Unban(netAddr) ))
+ if (!( isSubnet ? g_connman->Unban(subNet) : g_connman->Unban(netAddr) ))
throw JSONRPCError(RPC_MISC_ERROR, "Error: Unban failed");
}
return NullUniValue;
@@ -538,8 +531,11 @@ UniValue listbanned(const UniValue& params, bool fHelp)
+ HelpExampleRpc("listbanned", "")
);
+ if(!g_connman)
+ throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled");
+
banmap_t banMap;
- CNode::GetBanned(banMap);
+ g_connman->GetBanned(banMap);
UniValue bannedAddresses(UniValue::VARR);
for (banmap_t::iterator it = banMap.begin(); it != banMap.end(); it++)
@@ -567,8 +563,10 @@ UniValue clearbanned(const UniValue& params, bool fHelp)
+ HelpExampleCli("clearbanned", "")
+ HelpExampleRpc("clearbanned", "")
);
+ if(!g_connman)
+ throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled");
- CNode::ClearBanned();
+ g_connman->ClearBanned();
return NullUniValue;
}
diff --git a/src/rpc/protocol.h b/src/rpc/protocol.h
index 988e0fc5fa..1d2ef0e41e 100644
--- a/src/rpc/protocol.h
+++ b/src/rpc/protocol.h
@@ -63,6 +63,7 @@ enum RPCErrorCode
RPC_CLIENT_NODE_NOT_ADDED = -24, //!< Node has not been added before
RPC_CLIENT_NODE_NOT_CONNECTED = -29, //!< Node to disconnect not found in connected nodes
RPC_CLIENT_INVALID_IP_OR_SUBNET = -30, //!< Invalid IP/Subnet
+ RPC_CLIENT_P2P_DISABLED = -31, //!< No valid connection manager instance found
//! Wallet errors
RPC_WALLET_ERROR = -4, //!< Unspecified problem with wallet (key not found etc.)
diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp
index 9461a7280c..d2ad0a52b7 100644
--- a/src/rpc/rawtransaction.cpp
+++ b/src/rpc/rawtransaction.cpp
@@ -891,8 +891,14 @@ UniValue sendrawtransaction(const UniValue& params, bool fHelp)
} else if (fHaveChain) {
throw JSONRPCError(RPC_TRANSACTION_ALREADY_IN_CHAIN, "transaction already in block chain");
}
- RelayTransaction(tx);
+ if(!g_connman)
+ throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled");
+ CInv inv(MSG_TX, hashTx);
+ g_connman->ForEachNode([&inv](CNode* pnode)
+ {
+ pnode->PushInventory(inv);
+ });
return hashTx.GetHex();
}
diff --git a/src/rpc/server.h b/src/rpc/server.h
index b5ccc153d0..4e0aa2c6d6 100644
--- a/src/rpc/server.h
+++ b/src/rpc/server.h
@@ -194,5 +194,6 @@ bool StartRPC();
void InterruptRPC();
void StopRPC();
std::string JSONRPCExecBatch(const UniValue& vReq);
+void RPCNotifyBlockChange(bool ibd, const CBlockIndex *);
#endif // BITCOIN_RPCSERVER_H
diff --git a/src/test/DoS_tests.cpp b/src/test/DoS_tests.cpp
index a8c5f95ace..97abeb7211 100644
--- a/src/test/DoS_tests.cpp
+++ b/src/test/DoS_tests.cpp
@@ -40,69 +40,75 @@ CService ip(uint32_t i)
return CService(CNetAddr(s), Params().GetDefaultPort());
}
+static NodeId id = 0;
+
BOOST_FIXTURE_TEST_SUITE(DoS_tests, TestingSetup)
BOOST_AUTO_TEST_CASE(DoS_banning)
{
- CNode::ClearBanned();
+ connman->ClearBanned();
CAddress addr1(ip(0xa0b0c001), NODE_NONE);
- CNode dummyNode1(INVALID_SOCKET, addr1, "", true);
+ CNode dummyNode1(id++, NODE_NETWORK, 0, INVALID_SOCKET, addr1, 0, "", true);
+ GetNodeSignals().InitializeNode(dummyNode1.GetId(), &dummyNode1);
dummyNode1.nVersion = 1;
Misbehaving(dummyNode1.GetId(), 100); // Should get banned
- SendMessages(&dummyNode1);
- BOOST_CHECK(CNode::IsBanned(addr1));
- BOOST_CHECK(!CNode::IsBanned(ip(0xa0b0c001|0x0000ff00))); // Different IP, not banned
+ SendMessages(&dummyNode1, *connman);
+ BOOST_CHECK(connman->IsBanned(addr1));
+ BOOST_CHECK(!connman->IsBanned(ip(0xa0b0c001|0x0000ff00))); // Different IP, not banned
CAddress addr2(ip(0xa0b0c002), NODE_NONE);
- CNode dummyNode2(INVALID_SOCKET, addr2, "", true);
+ CNode dummyNode2(id++, NODE_NETWORK, 0, INVALID_SOCKET, addr2, 1, "", true);
+ GetNodeSignals().InitializeNode(dummyNode2.GetId(), &dummyNode2);
dummyNode2.nVersion = 1;
Misbehaving(dummyNode2.GetId(), 50);
- SendMessages(&dummyNode2);
- BOOST_CHECK(!CNode::IsBanned(addr2)); // 2 not banned yet...
- BOOST_CHECK(CNode::IsBanned(addr1)); // ... but 1 still should be
+ SendMessages(&dummyNode2, *connman);
+ BOOST_CHECK(!connman->IsBanned(addr2)); // 2 not banned yet...
+ BOOST_CHECK(connman->IsBanned(addr1)); // ... but 1 still should be
Misbehaving(dummyNode2.GetId(), 50);
- SendMessages(&dummyNode2);
- BOOST_CHECK(CNode::IsBanned(addr2));
+ SendMessages(&dummyNode2, *connman);
+ BOOST_CHECK(connman->IsBanned(addr2));
}
BOOST_AUTO_TEST_CASE(DoS_banscore)
{
- CNode::ClearBanned();
+ connman->ClearBanned();
mapArgs["-banscore"] = "111"; // because 11 is my favorite number
CAddress addr1(ip(0xa0b0c001), NODE_NONE);
- CNode dummyNode1(INVALID_SOCKET, addr1, "", true);
+ CNode dummyNode1(id++, NODE_NETWORK, 0, INVALID_SOCKET, addr1, 3, "", true);
+ GetNodeSignals().InitializeNode(dummyNode1.GetId(), &dummyNode1);
dummyNode1.nVersion = 1;
Misbehaving(dummyNode1.GetId(), 100);
- SendMessages(&dummyNode1);
- BOOST_CHECK(!CNode::IsBanned(addr1));
+ SendMessages(&dummyNode1, *connman);
+ BOOST_CHECK(!connman->IsBanned(addr1));
Misbehaving(dummyNode1.GetId(), 10);
- SendMessages(&dummyNode1);
- BOOST_CHECK(!CNode::IsBanned(addr1));
+ SendMessages(&dummyNode1, *connman);
+ BOOST_CHECK(!connman->IsBanned(addr1));
Misbehaving(dummyNode1.GetId(), 1);
- SendMessages(&dummyNode1);
- BOOST_CHECK(CNode::IsBanned(addr1));
+ SendMessages(&dummyNode1, *connman);
+ BOOST_CHECK(connman->IsBanned(addr1));
mapArgs.erase("-banscore");
}
BOOST_AUTO_TEST_CASE(DoS_bantime)
{
- CNode::ClearBanned();
+ connman->ClearBanned();
int64_t nStartTime = GetTime();
SetMockTime(nStartTime); // Overrides future calls to GetTime()
CAddress addr(ip(0xa0b0c001), NODE_NONE);
- CNode dummyNode(INVALID_SOCKET, addr, "", true);
+ CNode dummyNode(id++, NODE_NETWORK, 0, INVALID_SOCKET, addr, 4, "", true);
+ GetNodeSignals().InitializeNode(dummyNode.GetId(), &dummyNode);
dummyNode.nVersion = 1;
Misbehaving(dummyNode.GetId(), 100);
- SendMessages(&dummyNode);
- BOOST_CHECK(CNode::IsBanned(addr));
+ SendMessages(&dummyNode, *connman);
+ BOOST_CHECK(connman->IsBanned(addr));
SetMockTime(nStartTime+60*60);
- BOOST_CHECK(CNode::IsBanned(addr));
+ BOOST_CHECK(connman->IsBanned(addr));
SetMockTime(nStartTime+60*60*24+1);
- BOOST_CHECK(!CNode::IsBanned(addr));
+ BOOST_CHECK(!connman->IsBanned(addr));
}
CTransaction RandomOrphan()
diff --git a/src/test/arith_uint256_tests.cpp b/src/test/arith_uint256_tests.cpp
index 53ab7e95ee..b19d2faea0 100644
--- a/src/test/arith_uint256_tests.cpp
+++ b/src/test/arith_uint256_tests.cpp
@@ -112,7 +112,7 @@ BOOST_AUTO_TEST_CASE( basics ) // constructors, equality, inequality
BOOST_CHECK( (R1L & arith_uint256("0xffffffffffffffff")) == arith_uint256(R1LLow64));
BOOST_CHECK(ZeroL == arith_uint256(0));
BOOST_CHECK(OneL == arith_uint256(1));
- BOOST_CHECK(arith_uint256("0xffffffffffffffff") = arith_uint256(0xffffffffffffffffULL));
+ BOOST_CHECK(arith_uint256("0xffffffffffffffff") == arith_uint256(0xffffffffffffffffULL));
// Assignment (from base_uint)
arith_uint256 tmpL = ~ZeroL; BOOST_CHECK(tmpL == ~ZeroL);
diff --git a/src/test/miner_tests.cpp b/src/test/miner_tests.cpp
index 15fceb963a..d3aa2364d1 100644
--- a/src/test/miner_tests.cpp
+++ b/src/test/miner_tests.cpp
@@ -222,7 +222,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
pblock->hashMerkleRoot = BlockMerkleRoot(*pblock);
pblock->nNonce = blockinfo[i].nonce;
CValidationState state;
- BOOST_CHECK(ProcessNewBlock(state, chainparams, NULL, pblock, true, NULL));
+ BOOST_CHECK(ProcessNewBlock(state, chainparams, NULL, pblock, true, NULL, connman));
BOOST_CHECK(state.IsValid());
pblock->hashPrevBlock = pblock->GetHash();
}
diff --git a/src/test/net_tests.cpp b/src/test/net_tests.cpp
index 267d1b55e1..680708533e 100644
--- a/src/test/net_tests.cpp
+++ b/src/test/net_tests.cpp
@@ -153,6 +153,8 @@ BOOST_AUTO_TEST_CASE(caddrdb_read_corrupted)
BOOST_AUTO_TEST_CASE(cnode_simple_test)
{
SOCKET hSocket = INVALID_SOCKET;
+ NodeId id = 0;
+ int height = 0;
in_addr ipv4Addr;
ipv4Addr.s_addr = 0xa0b0c001;
@@ -162,12 +164,12 @@ BOOST_AUTO_TEST_CASE(cnode_simple_test)
bool fInboundIn = false;
// Test that fFeeler is false by default.
- CNode* pnode1 = new CNode(hSocket, addr, pszDest, fInboundIn);
+ CNode* pnode1 = new CNode(id++, NODE_NETWORK, height, hSocket, addr, 0, pszDest, fInboundIn);
BOOST_CHECK(pnode1->fInbound == false);
BOOST_CHECK(pnode1->fFeeler == false);
fInboundIn = true;
- CNode* pnode2 = new CNode(hSocket, addr, pszDest, fInboundIn);
+ CNode* pnode2 = new CNode(id++, NODE_NETWORK, height, hSocket, addr, 1, pszDest, fInboundIn);
BOOST_CHECK(pnode2->fInbound == true);
BOOST_CHECK(pnode2->fFeeler == false);
}
diff --git a/src/test/prevector_tests.cpp b/src/test/prevector_tests.cpp
index d1407c1da9..b8c45ca564 100644
--- a/src/test/prevector_tests.cpp
+++ b/src/test/prevector_tests.cpp
@@ -26,57 +26,70 @@ class prevector_tester {
pretype pre_vector_alt;
typedef typename pretype::size_type Size;
+ bool passed = true;
+ uint32_t insecure_rand_Rz_cache;
+ uint32_t insecure_rand_Rw_cache;
+
+ template <typename A, typename B>
+ void local_check_equal(A a, B b)
+ {
+ local_check(a == b);
+ }
+ void local_check(bool b)
+ {
+ passed &= b;
+ }
void test() {
const pretype& const_pre_vector = pre_vector;
- BOOST_CHECK_EQUAL(real_vector.size(), pre_vector.size());
- BOOST_CHECK_EQUAL(real_vector.empty(), pre_vector.empty());
+ local_check_equal(real_vector.size(), pre_vector.size());
+ local_check_equal(real_vector.empty(), pre_vector.empty());
for (Size s = 0; s < real_vector.size(); s++) {
- BOOST_CHECK(real_vector[s] == pre_vector[s]);
- BOOST_CHECK(&(pre_vector[s]) == &(pre_vector.begin()[s]));
- BOOST_CHECK(&(pre_vector[s]) == &*(pre_vector.begin() + s));
- BOOST_CHECK(&(pre_vector[s]) == &*((pre_vector.end() + s) - real_vector.size()));
+ local_check(real_vector[s] == pre_vector[s]);
+ local_check(&(pre_vector[s]) == &(pre_vector.begin()[s]));
+ local_check(&(pre_vector[s]) == &*(pre_vector.begin() + s));
+ local_check(&(pre_vector[s]) == &*((pre_vector.end() + s) - real_vector.size()));
}
- // BOOST_CHECK(realtype(pre_vector) == real_vector);
- BOOST_CHECK(pretype(real_vector.begin(), real_vector.end()) == pre_vector);
- BOOST_CHECK(pretype(pre_vector.begin(), pre_vector.end()) == pre_vector);
+ // local_check(realtype(pre_vector) == real_vector);
+ local_check(pretype(real_vector.begin(), real_vector.end()) == pre_vector);
+ local_check(pretype(pre_vector.begin(), pre_vector.end()) == pre_vector);
size_t pos = 0;
BOOST_FOREACH(const T& v, pre_vector) {
- BOOST_CHECK(v == real_vector[pos++]);
+ local_check(v == real_vector[pos++]);
}
BOOST_REVERSE_FOREACH(const T& v, pre_vector) {
- BOOST_CHECK(v == real_vector[--pos]);
+ local_check(v == real_vector[--pos]);
}
BOOST_FOREACH(const T& v, const_pre_vector) {
- BOOST_CHECK(v == real_vector[pos++]);
+ local_check(v == real_vector[pos++]);
}
BOOST_REVERSE_FOREACH(const T& v, const_pre_vector) {
- BOOST_CHECK(v == real_vector[--pos]);
+ local_check(v == real_vector[--pos]);
}
CDataStream ss1(SER_DISK, 0);
CDataStream ss2(SER_DISK, 0);
ss1 << real_vector;
ss2 << pre_vector;
- BOOST_CHECK_EQUAL(ss1.size(), ss2.size());
+ local_check_equal(ss1.size(), ss2.size());
for (Size s = 0; s < ss1.size(); s++) {
- BOOST_CHECK_EQUAL(ss1[s], ss2[s]);
+ local_check_equal(ss1[s], ss2[s]);
}
}
public:
void resize(Size s) {
real_vector.resize(s);
- BOOST_CHECK_EQUAL(real_vector.size(), s);
+ local_check_equal(real_vector.size(), s);
pre_vector.resize(s);
- BOOST_CHECK_EQUAL(pre_vector.size(), s);
+ local_check_equal(pre_vector.size(), s);
test();
}
void reserve(Size s) {
real_vector.reserve(s);
- BOOST_CHECK(real_vector.capacity() >= s);
+ local_check(real_vector.capacity() >= s);
pre_vector.reserve(s);
- BOOST_CHECK(pre_vector.capacity() >= s);
+ local_check(pre_vector.capacity() >= s);
test();
}
@@ -157,6 +170,17 @@ public:
pre_vector.swap(pre_vector_alt);
test();
}
+ ~prevector_tester() {
+ BOOST_CHECK_MESSAGE(passed, "insecure_rand_Rz: "
+ << insecure_rand_Rz_cache
+ << ", insecure_rand_Rw: "
+ << insecure_rand_Rw_cache);
+ }
+ prevector_tester() {
+ seed_insecure_rand();
+ insecure_rand_Rz_cache = insecure_rand_Rz;
+ insecure_rand_Rw_cache = insecure_rand_Rw;
+ }
};
BOOST_AUTO_TEST_CASE(PrevectorTestInt)
diff --git a/src/test/test_bitcoin.cpp b/src/test/test_bitcoin.cpp
index 056f2982cf..02843d8525 100644
--- a/src/test/test_bitcoin.cpp
+++ b/src/test/test_bitcoin.cpp
@@ -26,6 +26,8 @@
#include <boost/test/unit_test.hpp>
#include <boost/thread.hpp>
+std::unique_ptr<CConnman> g_connman;
+
extern bool fPrintToConsole;
extern void noui_connect();
@@ -43,6 +45,7 @@ BasicTestingSetup::BasicTestingSetup(const std::string& chainName)
BasicTestingSetup::~BasicTestingSetup()
{
ECC_Stop();
+ g_connman.reset();
}
TestingSetup::TestingSetup(const std::string& chainName) : BasicTestingSetup(chainName)
@@ -50,6 +53,7 @@ TestingSetup::TestingSetup(const std::string& chainName) : BasicTestingSetup(cha
const CChainParams& chainparams = Params();
// Ideally we'd move all the RPC tests to the functional testing framework
// instead of unit tests, but for now we need these here.
+
RegisterAllCoreRPCCommands(tableRPC);
ClearDatadirCache();
pathTemp = GetTempPath() / strprintf("test_bitcoin_%lu_%i", (unsigned long)GetTime(), (int)(GetRand(100000)));
@@ -68,6 +72,8 @@ TestingSetup::TestingSetup(const std::string& chainName) : BasicTestingSetup(cha
nScriptCheckThreads = 3;
for (int i=0; i < nScriptCheckThreads-1; i++)
threadGroup.create_thread(&ThreadScriptCheck);
+ g_connman = std::unique_ptr<CConnman>(new CConnman(0x1337, 0x1337)); // Deterministic randomness for tests.
+ connman = g_connman.get();
RegisterNodeSignals(GetNodeSignals());
}
@@ -118,7 +124,7 @@ TestChain100Setup::CreateAndProcessBlock(const std::vector<CMutableTransaction>&
while (!CheckProofOfWork(block.GetHash(), block.nBits, chainparams.GetConsensus())) ++block.nNonce;
CValidationState state;
- ProcessNewBlock(state, chainparams, NULL, &block, true, NULL);
+ ProcessNewBlock(state, chainparams, NULL, &block, true, NULL, connman);
CBlock result = block;
delete pblocktemplate;
diff --git a/src/test/test_bitcoin.h b/src/test/test_bitcoin.h
index bc0d2fe316..9819a7097d 100644
--- a/src/test/test_bitcoin.h
+++ b/src/test/test_bitcoin.h
@@ -27,10 +27,12 @@ struct BasicTestingSetup {
/** Testing setup that configures a complete environment.
* Included are data directory, coins database, script check threads setup.
*/
+class CConnman;
struct TestingSetup: public BasicTestingSetup {
CCoinsViewDB *pcoinsdbview;
boost::filesystem::path pathTemp;
boost::thread_group threadGroup;
+ CConnman* connman;
TestingSetup(const std::string& chainName = CBaseChainParams::MAIN);
~TestingSetup();
diff --git a/src/validationinterface.cpp b/src/validationinterface.cpp
index cf1d6ca086..6ddf37658d 100644
--- a/src/validationinterface.cpp
+++ b/src/validationinterface.cpp
@@ -18,7 +18,7 @@ void RegisterValidationInterface(CValidationInterface* pwalletIn) {
g_signals.UpdatedTransaction.connect(boost::bind(&CValidationInterface::UpdatedTransaction, pwalletIn, _1));
g_signals.SetBestChain.connect(boost::bind(&CValidationInterface::SetBestChain, pwalletIn, _1));
g_signals.Inventory.connect(boost::bind(&CValidationInterface::Inventory, pwalletIn, _1));
- g_signals.Broadcast.connect(boost::bind(&CValidationInterface::ResendWalletTransactions, pwalletIn, _1));
+ g_signals.Broadcast.connect(boost::bind(&CValidationInterface::ResendWalletTransactions, pwalletIn, _1, _2));
g_signals.BlockChecked.connect(boost::bind(&CValidationInterface::BlockChecked, pwalletIn, _1, _2));
g_signals.ScriptForMining.connect(boost::bind(&CValidationInterface::GetScriptForMining, pwalletIn, _1));
g_signals.BlockFound.connect(boost::bind(&CValidationInterface::ResetRequestCount, pwalletIn, _1));
@@ -28,7 +28,7 @@ void UnregisterValidationInterface(CValidationInterface* pwalletIn) {
g_signals.BlockFound.disconnect(boost::bind(&CValidationInterface::ResetRequestCount, pwalletIn, _1));
g_signals.ScriptForMining.disconnect(boost::bind(&CValidationInterface::GetScriptForMining, pwalletIn, _1));
g_signals.BlockChecked.disconnect(boost::bind(&CValidationInterface::BlockChecked, pwalletIn, _1, _2));
- g_signals.Broadcast.disconnect(boost::bind(&CValidationInterface::ResendWalletTransactions, pwalletIn, _1));
+ g_signals.Broadcast.disconnect(boost::bind(&CValidationInterface::ResendWalletTransactions, pwalletIn, _1, _2));
g_signals.Inventory.disconnect(boost::bind(&CValidationInterface::Inventory, pwalletIn, _1));
g_signals.SetBestChain.disconnect(boost::bind(&CValidationInterface::SetBestChain, pwalletIn, _1));
g_signals.UpdatedTransaction.disconnect(boost::bind(&CValidationInterface::UpdatedTransaction, pwalletIn, _1));
diff --git a/src/validationinterface.h b/src/validationinterface.h
index 094b1cfe26..0c91ec8308 100644
--- a/src/validationinterface.h
+++ b/src/validationinterface.h
@@ -13,6 +13,7 @@ class CBlock;
class CBlockIndex;
struct CBlockLocator;
class CBlockIndex;
+class CConnman;
class CReserveScript;
class CTransaction;
class CValidationInterface;
@@ -37,7 +38,7 @@ protected:
virtual void SetBestChain(const CBlockLocator &locator) {}
virtual void UpdatedTransaction(const uint256 &hash) {}
virtual void Inventory(const uint256 &hash) {}
- virtual void ResendWalletTransactions(int64_t nBestBlockTime) {}
+ virtual void ResendWalletTransactions(int64_t nBestBlockTime, CConnman* connman) {}
virtual void BlockChecked(const CBlock&, const CValidationState&) {}
virtual void GetScriptForMining(boost::shared_ptr<CReserveScript>&) {};
virtual void ResetRequestCount(const uint256 &hash) {};
@@ -58,7 +59,7 @@ struct CMainSignals {
/** Notifies listeners about an inventory item being seen on the network. */
boost::signals2::signal<void (const uint256 &)> Inventory;
/** Tells listeners to broadcast their data. */
- boost::signals2::signal<void (int64_t nBestBlockTime)> Broadcast;
+ boost::signals2::signal<void (int64_t nBestBlockTime, CConnman* connman)> Broadcast;
/** Notifies listeners of a block validation result */
boost::signals2::signal<void (const CBlock&, const CValidationState&)> BlockChecked;
/** Notifies listeners that a key for mining is required (coinbase) */
diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp
index 0ba6706baf..993caad400 100644
--- a/src/wallet/rpcwallet.cpp
+++ b/src/wallet/rpcwallet.cpp
@@ -346,6 +346,9 @@ static void SendMoney(const CTxDestination &address, CAmount nValue, bool fSubtr
if (nValue > curBalance)
throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Insufficient funds");
+ if (pwalletMain->GetBroadcastTransactions() && !g_connman)
+ throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled");
+
// Parse Bitcoin address
CScript scriptPubKey = GetScriptForDestination(address);
@@ -362,7 +365,7 @@ static void SendMoney(const CTxDestination &address, CAmount nValue, bool fSubtr
strError = strprintf("Error: This transaction requires a transaction fee of at least %s because of its amount, complexity, or use of recently received funds!", FormatMoney(nFeeRequired));
throw JSONRPCError(RPC_WALLET_ERROR, strError);
}
- if (!pwalletMain->CommitTransaction(wtxNew, reservekey))
+ if (!pwalletMain->CommitTransaction(wtxNew, reservekey, g_connman.get()))
throw JSONRPCError(RPC_WALLET_ERROR, "Error: The transaction was rejected! This might happen if some of the coins in your wallet were already spent, such as if you used a copy of the wallet and coins were spent in the copy but not marked as spent here.");
}
@@ -891,6 +894,9 @@ UniValue sendmany(const UniValue& params, bool fHelp)
LOCK2(cs_main, pwalletMain->cs_wallet);
+ if (pwalletMain->GetBroadcastTransactions() && !g_connman)
+ throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled");
+
string strAccount = AccountFromValue(params[0]);
UniValue sendTo = params[1].get_obj();
int nMinDepth = 1;
@@ -953,7 +959,7 @@ UniValue sendmany(const UniValue& params, bool fHelp)
bool fCreated = pwalletMain->CreateTransaction(vecSend, wtx, keyChange, nFeeRequired, nChangePosRet, strFailReason);
if (!fCreated)
throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, strFailReason);
- if (!pwalletMain->CommitTransaction(wtx, keyChange))
+ if (!pwalletMain->CommitTransaction(wtx, keyChange, g_connman.get()))
throw JSONRPCError(RPC_WALLET_ERROR, "Transaction commit failed");
return wtx.GetHash().GetHex();
@@ -1087,6 +1093,8 @@ UniValue addwitnessaddress(const UniValue& params, bool fHelp)
throw JSONRPCError(RPC_WALLET_ERROR, "Public key or redeemscript not known to wallet");
}
+ pwalletMain->SetAddressBook(w.result, "", "receive");
+
return CBitcoinAddress(w.result).ToString();
}
@@ -2308,9 +2316,12 @@ UniValue resendwallettransactions(const UniValue& params, bool fHelp)
"Returns array of transaction ids that were re-broadcast.\n"
);
+ if (!g_connman)
+ throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled");
+
LOCK2(cs_main, pwalletMain->cs_wallet);
- std::vector<uint256> txids = pwalletMain->ResendWalletTransactionsBefore(GetTime());
+ std::vector<uint256> txids = pwalletMain->ResendWalletTransactionsBefore(GetTime(), g_connman.get());
UniValue result(UniValue::VARR);
BOOST_FOREACH(const uint256& txid, txids)
{
diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp
index 10aca2e499..4c10ea0edb 100644
--- a/src/wallet/wallet.cpp
+++ b/src/wallet/wallet.cpp
@@ -40,6 +40,7 @@ CFeeRate payTxFee(DEFAULT_TRANSACTION_FEE);
unsigned int nTxConfirmTarget = DEFAULT_TX_CONFIRM_TARGET;
bool bSpendZeroConfChange = DEFAULT_SPEND_ZEROCONF_CHANGE;
bool fSendFreeTransactions = DEFAULT_SEND_FREE_TRANSACTIONS;
+bool fWalletRbf = DEFAULT_WALLET_RBF;
const char * DEFAULT_WALLET_DAT = "wallet.dat";
const uint32_t BIP32_HARDENED_KEY_LIMIT = 0x80000000;
@@ -1453,15 +1454,21 @@ void CWallet::ReacceptWalletTransactions()
}
}
-bool CWalletTx::RelayWalletTransaction()
+bool CWalletTx::RelayWalletTransaction(CConnman* connman)
{
assert(pwallet->GetBroadcastTransactions());
if (!IsCoinBase())
{
if (GetDepthInMainChain() == 0 && !isAbandoned() && InMempool()) {
LogPrintf("Relaying wtx %s\n", GetHash().ToString());
- RelayTransaction((CTransaction)*this);
- return true;
+ if (connman) {
+ CInv inv(MSG_TX, GetHash());
+ connman->ForEachNode([&inv](CNode* pnode)
+ {
+ pnode->PushInventory(inv);
+ });
+ return true;
+ }
}
}
return false;
@@ -1688,7 +1695,7 @@ bool CWalletTx::IsEquivalentTo(const CWalletTx& tx) const
return CTransaction(tx1) == CTransaction(tx2);
}
-std::vector<uint256> CWallet::ResendWalletTransactionsBefore(int64_t nTime)
+std::vector<uint256> CWallet::ResendWalletTransactionsBefore(int64_t nTime, CConnman* connman)
{
std::vector<uint256> result;
@@ -1706,13 +1713,13 @@ std::vector<uint256> CWallet::ResendWalletTransactionsBefore(int64_t nTime)
BOOST_FOREACH(PAIRTYPE(const unsigned int, CWalletTx*)& item, mapSorted)
{
CWalletTx& wtx = *item.second;
- if (wtx.RelayWalletTransaction())
+ if (wtx.RelayWalletTransaction(connman))
result.push_back(wtx.GetHash());
}
return result;
}
-void CWallet::ResendWalletTransactions(int64_t nBestBlockTime)
+void CWallet::ResendWalletTransactions(int64_t nBestBlockTime, CConnman* connman)
{
// Do this infrequently and randomly to avoid giving away
// that these are our transactions.
@@ -1730,7 +1737,7 @@ void CWallet::ResendWalletTransactions(int64_t nBestBlockTime)
// Rebroadcast unconfirmed txes older than 5 minutes before the last
// block was found:
- std::vector<uint256> relayed = ResendWalletTransactionsBefore(nBestBlockTime-5*60);
+ std::vector<uint256> relayed = ResendWalletTransactionsBefore(nBestBlockTime-5*60, connman);
if (!relayed.empty())
LogPrintf("%s: rebroadcast %u unconfirmed transactions\n", __func__, relayed.size());
}
@@ -2209,6 +2216,7 @@ bool CWallet::CreateTransaction(const vector<CRecipient>& vecSend, CWalletTx& wt
nChangePosInOut = nChangePosRequest;
txNew.vin.clear();
txNew.vout.clear();
+ txNew.wit.SetNull();
wtxNew.fFromMe = true;
bool fFirst = true;
@@ -2355,11 +2363,17 @@ bool CWallet::CreateTransaction(const vector<CRecipient>& vecSend, CWalletTx& wt
// Fill vin
//
- // Note how the sequence number is set to max()-1 so that the
- // nLockTime set above actually works.
+ // Note how the sequence number is set to non-maxint so that
+ // the nLockTime set above actually works.
+ //
+ // BIP125 defines opt-in RBF as any nSequence < maxint-1, so
+ // we use the highest possible value in that range (maxint-2)
+ // to avoid conflicting with other possible uses of nSequence,
+ // and in the spirit of "smallest posible change from prior
+ // behavior."
BOOST_FOREACH(const PAIRTYPE(const CWalletTx*,unsigned int)& coin, setCoins)
txNew.vin.push_back(CTxIn(coin.first->GetHash(),coin.second,CScript(),
- std::numeric_limits<unsigned int>::max()-1));
+ std::numeric_limits<unsigned int>::max() - (fWalletRbf ? 2 : 1)));
// Sign
int nIn = 0;
@@ -2447,7 +2461,7 @@ bool CWallet::CreateTransaction(const vector<CRecipient>& vecSend, CWalletTx& wt
/**
* Call after CreateTransaction unless you want to abort
*/
-bool CWallet::CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey)
+bool CWallet::CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey, CConnman* connman)
{
{
LOCK2(cs_main, cs_wallet);
@@ -2481,7 +2495,7 @@ bool CWallet::CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey)
LogPrintf("CommitTransaction(): Error: Transaction not valid\n");
return false;
}
- wtxNew.RelayWalletTransaction();
+ wtxNew.RelayWalletTransaction(connman);
}
}
return true;
@@ -3239,6 +3253,7 @@ std::string CWallet::GetWalletHelpString(bool showDebug)
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("-walletrbf", strprintf(_("Send transactions with full-RBF opt-in enabled (default: %u)"), DEFAULT_WALLET_RBF));
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));
@@ -3479,6 +3494,7 @@ bool CWallet::ParameterInteraction()
nTxConfirmTarget = GetArg("-txconfirmtarget", DEFAULT_TX_CONFIRM_TARGET);
bSpendZeroConfChange = GetBoolArg("-spendzeroconfchange", DEFAULT_SPEND_ZEROCONF_CHANGE);
fSendFreeTransactions = GetBoolArg("-sendfreetransactions", DEFAULT_SEND_FREE_TRANSACTIONS);
+ fWalletRbf = GetBoolArg("-walletrbf", DEFAULT_WALLET_RBF);
return true;
}
diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h
index c06513650c..61aff6e45a 100644
--- a/src/wallet/wallet.h
+++ b/src/wallet/wallet.h
@@ -37,6 +37,7 @@ extern CFeeRate payTxFee;
extern unsigned int nTxConfirmTarget;
extern bool bSpendZeroConfChange;
extern bool fSendFreeTransactions;
+extern bool fWalletRbf;
static const unsigned int DEFAULT_KEYPOOL_SIZE = 100;
//! -paytxfee default
@@ -53,6 +54,8 @@ static const bool DEFAULT_SPEND_ZEROCONF_CHANGE = true;
static const bool DEFAULT_SEND_FREE_TRANSACTIONS = false;
//! -txconfirmtarget default
static const unsigned int DEFAULT_TX_CONFIRM_TARGET = 2;
+//! -walletrbf default
+static const bool DEFAULT_WALLET_RBF = false;
//! Largest (in bytes) free transaction we're willing to create
static const unsigned int MAX_FREE_TRANSACTION_CREATE_SIZE = 1000;
static const bool DEFAULT_WALLETBROADCAST = true;
@@ -401,7 +404,7 @@ public:
int64_t GetTxTime() const;
int GetRequestCount() const;
- bool RelayWalletTransaction();
+ bool RelayWalletTransaction(CConnman* connman);
std::set<uint256> GetConflicts() const;
};
@@ -748,8 +751,8 @@ public:
bool AddToWalletIfInvolvingMe(const CTransaction& tx, const CBlockIndex* pIndex, int posInBlock, bool fUpdate);
int ScanForWalletTransactions(CBlockIndex* pindexStart, bool fUpdate = false);
void ReacceptWalletTransactions();
- void ResendWalletTransactions(int64_t nBestBlockTime);
- std::vector<uint256> ResendWalletTransactionsBefore(int64_t nTime);
+ void ResendWalletTransactions(int64_t nBestBlockTime, CConnman* connman);
+ std::vector<uint256> ResendWalletTransactionsBefore(int64_t nTime, CConnman* connman);
CAmount GetBalance() const;
CAmount GetUnconfirmedBalance() const;
CAmount GetImmatureBalance() const;
@@ -770,7 +773,7 @@ public:
*/
bool CreateTransaction(const std::vector<CRecipient>& vecSend, CWalletTx& wtxNew, CReserveKey& reservekey, CAmount& nFeeRet, int& nChangePosInOut,
std::string& strFailReason, const CCoinControl *coinControl = NULL, bool sign = true);
- bool CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey);
+ bool CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey, CConnman* connman);
bool AddAccountingEntry(const CAccountingEntry&, CWalletDB & pwalletdb);