aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--contrib/debian/copyright3
-rw-r--r--depends/hosts/darwin.mk2
-rwxr-xr-xqa/rpc-tests/p2p-compactblocks.py18
-rw-r--r--src/Makefile.bench.include17
-rw-r--r--src/Makefile.qt.include1
-rw-r--r--src/bench/checkblock.cpp55
-rw-r--r--src/bench/data/block413567.rawbin0 -> 999887 bytes
-rw-r--r--src/main.cpp31
-rw-r--r--src/main.h2
-rw-r--r--src/net.cpp31
-rw-r--r--src/net.h3
-rw-r--r--src/protocol.cpp6
-rw-r--r--src/qt/bitcoin.qrc1
-rw-r--r--src/qt/bitcoingui.cpp49
-rw-r--r--src/qt/bitcoingui.h21
-rw-r--r--src/qt/clientmodel.cpp28
-rw-r--r--src/qt/clientmodel.h6
-rw-r--r--src/qt/res/icons/network_disabled.pngbin0 -> 591 bytes
-rw-r--r--src/qt/res/src/network_disabled.svg68
-rw-r--r--src/qt/rpcconsole.cpp30
-rw-r--r--src/qt/rpcconsole.h7
-rw-r--r--src/rpc/client.cpp1
-rw-r--r--src/rpc/net.cpp24
-rw-r--r--src/test/rpc_tests.cpp22
-rw-r--r--src/ui_interface.h3
25 files changed, 398 insertions, 31 deletions
diff --git a/contrib/debian/copyright b/contrib/debian/copyright
index 5cac6e533c..0fa06f1aa9 100644
--- a/contrib/debian/copyright
+++ b/contrib/debian/copyright
@@ -47,7 +47,10 @@ Comment: Site: https://github.com/stephenhutchings/typicons.font
Files: src/qt/res/icons/connect*.png
src/qt/res/src/connect-*.svg
+ src/qt/res/icons/network_disabled.png
+ src/qt/res/src/network_disabled.svg
Copyright: Marco Falke
+ Luke Dashjr
License: Expat
Comment: Inspired by Stephan Hutchings Typicons
diff --git a/depends/hosts/darwin.mk b/depends/hosts/darwin.mk
index 985649619f..4e58bec74e 100644
--- a/depends/hosts/darwin.mk
+++ b/depends/hosts/darwin.mk
@@ -1,4 +1,4 @@
-OSX_MIN_VERSION=10.7
+OSX_MIN_VERSION=10.8
OSX_SDK_VERSION=10.11
OSX_SDK=$(SDK_PATH)/MacOSX$(OSX_SDK_VERSION).sdk
LD64_VERSION=253.9
diff --git a/qa/rpc-tests/p2p-compactblocks.py b/qa/rpc-tests/p2p-compactblocks.py
index 6d1fb3fd9a..1b4c8d90e7 100755
--- a/qa/rpc-tests/p2p-compactblocks.py
+++ b/qa/rpc-tests/p2p-compactblocks.py
@@ -300,8 +300,8 @@ class CompactBlocksTest(BitcoinTestFramework):
assert(segwit_tx_generated) # check that our test is not broken
# Wait until we've seen the block announcement for the resulting tip
- tip = int(self.nodes[0].getbestblockhash(), 16)
- assert(self.test_node.wait_for_block_announcement(tip))
+ tip = int(node.getbestblockhash(), 16)
+ assert(test_node.wait_for_block_announcement(tip))
# Now mine a block, and look at the resulting compact block.
test_node.clear_block_announcement()
@@ -589,8 +589,8 @@ class CompactBlocksTest(BitcoinTestFramework):
assert_equal(int(node.getbestblockhash(), 16), block.sha256)
def test_getblocktxn_handler(self, node, test_node, version):
- # bitcoind won't respond for blocks whose height is more than 15 blocks
- # deep.
+ # bitcoind will not send blocktxn responses for blocks whose height is
+ # more than 10 blocks deep.
MAX_GETBLOCKTXN_DEPTH = 10
chain_height = node.getblockcount()
current_height = chain_height
@@ -623,11 +623,17 @@ class CompactBlocksTest(BitcoinTestFramework):
test_node.last_blocktxn = None
current_height -= 1
- # Next request should be ignored, as we're past the allowed depth.
+ # Next request should send a full block response, as we're past the
+ # allowed depth for a blocktxn response.
block_hash = node.getblockhash(current_height)
msg.block_txn_request = BlockTransactionsRequest(int(block_hash, 16), [0])
+ with mininode_lock:
+ test_node.last_block = None
+ test_node.last_blocktxn = None
test_node.send_and_ping(msg)
with mininode_lock:
+ test_node.last_block.block.calc_sha256()
+ assert_equal(test_node.last_block.block.sha256, int(block_hash, 16))
assert_equal(test_node.last_blocktxn, None)
def test_compactblocks_not_at_tip(self, node, test_node):
@@ -648,6 +654,8 @@ class CompactBlocksTest(BitcoinTestFramework):
node.generate(1)
wait_until(test_node.received_block_announcement, timeout=30)
test_node.clear_block_announcement()
+ with mininode_lock:
+ test_node.last_block = None
test_node.send_message(msg_getdata([CInv(4, int(new_blocks[0], 16))]))
success = wait_until(lambda: test_node.last_block is not None, timeout=30)
assert(success)
diff --git a/src/Makefile.bench.include b/src/Makefile.bench.include
index 9760ad089c..840d33c1b5 100644
--- a/src/Makefile.bench.include
+++ b/src/Makefile.bench.include
@@ -6,11 +6,15 @@ bin_PROGRAMS += bench/bench_bitcoin
BENCH_SRCDIR = bench
BENCH_BINARY = bench/bench_bitcoin$(EXEEXT)
+RAW_TEST_FILES = \
+ bench/data/block413567.raw
+GENERATED_TEST_FILES = $(RAW_TEST_FILES:.raw=.raw.h)
bench_bench_bitcoin_SOURCES = \
bench/bench_bitcoin.cpp \
bench/bench.cpp \
bench/bench.h \
+ bench/checkblock.cpp \
bench/Examples.cpp \
bench/rollingbloom.cpp \
bench/crypto_hash.cpp \
@@ -20,6 +24,8 @@ bench_bench_bitcoin_SOURCES = \
bench/base58.cpp \
bench/lockedpool.cpp
+nodist_bench_bench_bitcoin_SOURCES = $(GENERATED_TEST_FILES)
+
bench_bench_bitcoin_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) $(EVENT_CLFAGS) $(EVENT_PTHREADS_CFLAGS) -I$(builddir)/bench/
bench_bench_bitcoin_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
bench_bench_bitcoin_LDADD = \
@@ -45,10 +51,12 @@ endif
bench_bench_bitcoin_LDADD += $(BOOST_LIBS) $(BDB_LIBS) $(SSL_LIBS) $(CRYPTO_LIBS) $(MINIUPNPC_LIBS) $(EVENT_PTHREADS_LIBS) $(EVENT_LIBS)
bench_bench_bitcoin_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
-CLEAN_BITCOIN_BENCH = bench/*.gcda bench/*.gcno
+CLEAN_BITCOIN_BENCH = bench/*.gcda bench/*.gcno $(GENERATED_TEST_FILES)
CLEANFILES += $(CLEAN_BITCOIN_BENCH)
+bench/checkblock.cpp: bench/data/block413567.raw.h
+
bitcoin_bench: $(BENCH_BINARY)
bench: $(BENCH_BINARY) FORCE
@@ -56,3 +64,10 @@ bench: $(BENCH_BINARY) FORCE
bitcoin_bench_clean : FORCE
rm -f $(CLEAN_BITCOIN_BENCH) $(bench_bench_bitcoin_OBJECTS) $(BENCH_BINARY)
+
+%.raw.h: %.raw
+ @$(MKDIR_P) $(@D)
+ @echo "static unsigned const char $(*F)[] = {" >> $@
+ @$(HEXDUMP) -v -e '8/1 "0x%02x, "' -e '"\n"' $< | $(SED) -e 's/0x ,//g' >> $@
+ @echo "};" >> $@
+ @echo "Generated $@"
diff --git a/src/Makefile.qt.include b/src/Makefile.qt.include
index 1f9a901d75..48abb9b4a2 100644
--- a/src/Makefile.qt.include
+++ b/src/Makefile.qt.include
@@ -271,6 +271,7 @@ RES_ICONS = \
qt/res/icons/key.png \
qt/res/icons/lock_closed.png \
qt/res/icons/lock_open.png \
+ qt/res/icons/network_disabled.png \
qt/res/icons/open.png \
qt/res/icons/overview.png \
qt/res/icons/quit.png \
diff --git a/src/bench/checkblock.cpp b/src/bench/checkblock.cpp
new file mode 100644
index 0000000000..bb596ce7f9
--- /dev/null
+++ b/src/bench/checkblock.cpp
@@ -0,0 +1,55 @@
+// Copyright (c) 2016 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include "bench.h"
+
+#include "chainparams.h"
+#include "main.h"
+#include "consensus/validation.h"
+
+namespace block_bench {
+#include "bench/data/block413567.raw.h"
+}
+
+// These are the two major time-sinks which happen after we have fully received
+// a block off the wire, but before we can relay the block on to peers using
+// compact block relay.
+
+static void DeserializeBlockTest(benchmark::State& state)
+{
+ CDataStream stream((const char*)block_bench::block413567,
+ (const char*)&block_bench::block413567[sizeof(block_bench::block413567)],
+ SER_NETWORK, PROTOCOL_VERSION);
+ char a;
+ stream.write(&a, 1); // Prevent compaction
+
+ while (state.KeepRunning()) {
+ CBlock block;
+ stream >> block;
+ assert(stream.Rewind(sizeof(block_bench::block413567)));
+ }
+}
+
+static void DeserializeAndCheckBlockTest(benchmark::State& state)
+{
+ CDataStream stream((const char*)block_bench::block413567,
+ (const char*)&block_bench::block413567[sizeof(block_bench::block413567)],
+ SER_NETWORK, PROTOCOL_VERSION);
+ char a;
+ stream.write(&a, 1); // Prevent compaction
+
+ Consensus::Params params = Params(CBaseChainParams::MAIN).GetConsensus();
+
+ while (state.KeepRunning()) {
+ CBlock block; // Note that CBlock caches its checked state, so we need to recreate it here
+ stream >> block;
+ assert(stream.Rewind(sizeof(block_bench::block413567)));
+
+ CValidationState state;
+ assert(CheckBlock(block, state, params));
+ }
+}
+
+BENCHMARK(DeserializeBlockTest);
+BENCHMARK(DeserializeAndCheckBlockTest);
diff --git a/src/bench/data/block413567.raw b/src/bench/data/block413567.raw
new file mode 100644
index 0000000000..67d2d5d382
--- /dev/null
+++ b/src/bench/data/block413567.raw
Binary files differ
diff --git a/src/main.cpp b/src/main.cpp
index 14bdd824e9..e868e3c5f9 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -1104,7 +1104,7 @@ int64_t GetTransactionSigOpCost(const CTransaction& tx, const CCoinsViewCache& i
-bool CheckTransaction(const CTransaction& tx, CValidationState &state)
+bool CheckTransaction(const CTransaction& tx, CValidationState &state, bool fCheckDuplicateInputs)
{
// Basic checks that don't depend on any context
if (tx.vin.empty())
@@ -1128,13 +1128,14 @@ bool CheckTransaction(const CTransaction& tx, CValidationState &state)
return state.DoS(100, false, REJECT_INVALID, "bad-txns-txouttotal-toolarge");
}
- // Check for duplicate inputs
- set<COutPoint> vInOutPoints;
- for (const auto& txin : tx.vin)
- {
- if (vInOutPoints.count(txin.prevout))
- return state.DoS(100, false, REJECT_INVALID, "bad-txns-inputs-duplicate");
- vInOutPoints.insert(txin.prevout);
+ // Check for duplicate inputs - note that this check is slow so we skip it in CheckBlock
+ if (fCheckDuplicateInputs) {
+ set<COutPoint> vInOutPoints;
+ for (const auto& txin : tx.vin)
+ {
+ if (!vInOutPoints.insert(txin.prevout).second)
+ return state.DoS(100, false, REJECT_INVALID, "bad-txns-inputs-duplicate");
+ }
}
if (tx.IsCoinBase())
@@ -3461,7 +3462,7 @@ bool CheckBlock(const CBlock& block, CValidationState& state, const Consensus::P
// Check transactions
for (const auto& tx : block.vtx)
- if (!CheckTransaction(tx, state))
+ if (!CheckTransaction(tx, state, false))
return state.Invalid(false, state.GetRejectCode(), state.GetRejectReason(),
strprintf("Transaction check failed (tx hash %s) %s", tx.GetHash().ToString(), state.GetDebugMessage()));
@@ -5494,7 +5495,19 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
}
if (it->second->nHeight < chainActive.Height() - MAX_BLOCKTXN_DEPTH) {
+ // If an older block is requested (should never happen in practice,
+ // but can happen in tests) send a block response instead of a
+ // blocktxn response. Sending a full block response instead of a
+ // small blocktxn response is preferable in the case where a peer
+ // might maliciously send lots of getblocktxn requests to trigger
+ // expensive disk reads, because it will require the peer to
+ // actually receive all the data read from disk over the network.
LogPrint("net", "Peer %d sent us a getblocktxn for a block > %i deep", pfrom->id, MAX_BLOCKTXN_DEPTH);
+ CInv vInv;
+ vInv.type = State(pfrom->GetId())->fWantsCmpctWitness ? MSG_WITNESS_BLOCK : MSG_BLOCK;
+ vInv.hash = req.blockhash;
+ pfrom->vRecvGetData.push_back(vInv);
+ ProcessGetData(pfrom, chainparams.GetConsensus(), connman);
return true;
}
diff --git a/src/main.h b/src/main.h
index 9343330587..21829b6c25 100644
--- a/src/main.h
+++ b/src/main.h
@@ -343,7 +343,7 @@ void UpdateCoins(const CTransaction& tx, CCoinsViewCache& inputs, int nHeight);
/** Transaction validation functions */
/** Context-independent validity checks */
-bool CheckTransaction(const CTransaction& tx, CValidationState& state);
+bool CheckTransaction(const CTransaction& tx, CValidationState& state, bool fCheckDuplicateInputs=true);
namespace Consensus {
diff --git a/src/net.cpp b/src/net.cpp
index e47a8bb168..15c4514f15 100644
--- a/src/net.cpp
+++ b/src/net.cpp
@@ -985,6 +985,12 @@ void CConnman::AcceptConnection(const ListenSocket& hListenSocket) {
return;
}
+ if (!fNetworkActive) {
+ LogPrintf("connection from %s dropped: not accepting new connections\n", addr.ToString());
+ CloseSocket(hSocket);
+ return;
+ }
+
if (!IsSelectableSocket(hSocket))
{
LogPrintf("connection from %s dropped: non-selectable socket\n", addr.ToString());
@@ -1784,6 +1790,9 @@ bool CConnman::OpenNetworkConnection(const CAddress& addrConnect, bool fCountFai
// Initiate outbound network connection
//
boost::this_thread::interruption_point();
+ if (!fNetworkActive) {
+ return false;
+ }
if (!pszDest) {
if (IsLocal(addrConnect) ||
FindNode((CNetAddr)addrConnect) || IsBanned(addrConnect) ||
@@ -2025,8 +2034,30 @@ void Discover(boost::thread_group& threadGroup)
#endif
}
+void CConnman::SetNetworkActive(bool active)
+{
+ if (fDebug) {
+ LogPrint("net", "SetNetworkActive: %s\n", active);
+ }
+
+ if (!active) {
+ fNetworkActive = false;
+
+ LOCK(cs_vNodes);
+ // Close sockets to all nodes
+ BOOST_FOREACH(CNode* pnode, vNodes) {
+ pnode->CloseSocketDisconnect();
+ }
+ } else {
+ fNetworkActive = true;
+ }
+
+ uiInterface.NotifyNetworkActiveChanged(fNetworkActive);
+}
+
CConnman::CConnman(uint64_t nSeed0In, uint64_t nSeed1In) : nSeed0(nSeed0In), nSeed1(nSeed1In)
{
+ fNetworkActive = true;
setBannedIsDirty = false;
fAddressesInitialized = false;
nLastNodeId = 0;
diff --git a/src/net.h b/src/net.h
index 25d9bbc02b..e2d88b478d 100644
--- a/src/net.h
+++ b/src/net.h
@@ -131,6 +131,8 @@ public:
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 GetNetworkActive() const { return fNetworkActive; };
+ void SetNetworkActive(bool active);
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);
@@ -401,6 +403,7 @@ private:
unsigned int nReceiveFloodSize;
std::vector<ListenSocket> vhListenSocket;
+ bool fNetworkActive;
banmap_t setBanned;
CCriticalSection cs_setBanned;
bool setBannedIsDirty;
diff --git a/src/protocol.cpp b/src/protocol.cpp
index 54ad62b1a2..87d6e06848 100644
--- a/src/protocol.cpp
+++ b/src/protocol.cpp
@@ -181,7 +181,11 @@ std::string CInv::GetCommand() const
std::string CInv::ToString() const
{
- return strprintf("%s %s", GetCommand(), hash.ToString());
+ try {
+ return strprintf("%s %s", GetCommand(), hash.ToString());
+ } catch(const std::out_of_range &) {
+ return strprintf("0x%08x %s", type, hash.ToString());
+ }
}
const std::vector<std::string> &getAllNetMessageTypes()
diff --git a/src/qt/bitcoin.qrc b/src/qt/bitcoin.qrc
index ca5b1fa673..451d391237 100644
--- a/src/qt/bitcoin.qrc
+++ b/src/qt/bitcoin.qrc
@@ -52,6 +52,7 @@
<file alias="transaction_abandoned">res/icons/transaction_abandoned.png</file>
<file alias="hd_enabled">res/icons/hd_enabled.png</file>
<file alias="hd_disabled">res/icons/hd_disabled.png</file>
+ <file alias="network_disabled">res/icons/network_disabled.png</file>
</qresource>
<qresource prefix="/movies">
<file alias="spinner-000">res/movies/spinner-000.png</file>
diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp
index ee5102c4f9..b2c9a704ed 100644
--- a/src/qt/bitcoingui.cpp
+++ b/src/qt/bitcoingui.cpp
@@ -86,7 +86,7 @@ BitcoinGUI::BitcoinGUI(const PlatformStyle *_platformStyle, const NetworkStyle *
unitDisplayControl(0),
labelWalletEncryptionIcon(0),
labelWalletHDStatusIcon(0),
- labelConnectionsIcon(0),
+ connectionsControl(0),
labelBlocksIcon(0),
progressBarLabel(0),
progressBar(0),
@@ -199,7 +199,7 @@ BitcoinGUI::BitcoinGUI(const PlatformStyle *_platformStyle, const NetworkStyle *
unitDisplayControl = new UnitDisplayStatusBarControl(platformStyle);
labelWalletEncryptionIcon = new QLabel();
labelWalletHDStatusIcon = new QLabel();
- labelConnectionsIcon = new QLabel();
+ connectionsControl = new NetworkToggleStatusBarControl();
labelBlocksIcon = new QLabel();
if(enableWallet)
{
@@ -210,7 +210,7 @@ BitcoinGUI::BitcoinGUI(const PlatformStyle *_platformStyle, const NetworkStyle *
frameBlocksLayout->addWidget(labelWalletHDStatusIcon);
}
frameBlocksLayout->addStretch();
- frameBlocksLayout->addWidget(labelConnectionsIcon);
+ frameBlocksLayout->addWidget(connectionsControl);
frameBlocksLayout->addStretch();
frameBlocksLayout->addWidget(labelBlocksIcon);
frameBlocksLayout->addStretch();
@@ -469,8 +469,9 @@ void BitcoinGUI::setClientModel(ClientModel *_clientModel)
createTrayIconMenu();
// Keep up to date with client
- setNumConnections(_clientModel->getNumConnections());
+ updateNetworkState();
connect(_clientModel, SIGNAL(numConnectionsChanged(int)), this, SLOT(setNumConnections(int)));
+ connect(_clientModel, SIGNAL(networkActiveChanged(bool)), this, SLOT(setNetworkActive(bool)));
setNumBlocks(_clientModel->getNumBlocks(), _clientModel->getLastBlockDate(), _clientModel->getVerificationProgress(NULL), false);
connect(_clientModel, SIGNAL(numBlocksChanged(int,QDateTime,double,bool)), this, SLOT(setNumBlocks(int,QDateTime,double,bool)));
@@ -489,6 +490,7 @@ void BitcoinGUI::setClientModel(ClientModel *_clientModel)
}
#endif // ENABLE_WALLET
unitDisplayControl->setOptionsModel(_clientModel->getOptionsModel());
+ connectionsControl->setClientModel(_clientModel);
OptionsModel* optionsModel = _clientModel->getOptionsModel();
if(optionsModel)
@@ -698,8 +700,9 @@ void BitcoinGUI::gotoVerifyMessageTab(QString addr)
}
#endif // ENABLE_WALLET
-void BitcoinGUI::setNumConnections(int count)
+void BitcoinGUI::updateNetworkState()
{
+ int count = clientModel->getNumConnections();
QString icon;
switch(count)
{
@@ -709,8 +712,25 @@ void BitcoinGUI::setNumConnections(int count)
case 7: case 8: case 9: icon = ":/icons/connect_3"; break;
default: icon = ":/icons/connect_4"; break;
}
- labelConnectionsIcon->setPixmap(platformStyle->SingleColorIcon(icon).pixmap(STATUSBAR_ICONSIZE,STATUSBAR_ICONSIZE));
- labelConnectionsIcon->setToolTip(tr("%n active connection(s) to Bitcoin network", "", count));
+
+ if (clientModel->getNetworkActive()) {
+ connectionsControl->setToolTip(tr("%n active connection(s) to Bitcoin network", "", count));
+ } else {
+ connectionsControl->setToolTip(tr("Network activity disabled"));
+ icon = ":/icons/network_disabled";
+ }
+
+ connectionsControl->setPixmap(platformStyle->SingleColorIcon(icon).pixmap(STATUSBAR_ICONSIZE,STATUSBAR_ICONSIZE));
+}
+
+void BitcoinGUI::setNumConnections(int count)
+{
+ updateNetworkState();
+}
+
+void BitcoinGUI::setNetworkActive(bool networkActive)
+{
+ updateNetworkState();
}
void BitcoinGUI::setNumBlocks(int count, const QDateTime& blockDate, double nVerificationProgress, bool header)
@@ -1211,3 +1231,18 @@ void UnitDisplayStatusBarControl::onMenuSelection(QAction* action)
optionsModel->setDisplayUnit(action->data());
}
}
+
+void NetworkToggleStatusBarControl::mousePressEvent(QMouseEvent *event)
+{
+ if (clientModel) {
+ clientModel->setNetworkActive(!clientModel->getNetworkActive());
+ }
+}
+
+/** Lets the control know about the Client Model */
+void NetworkToggleStatusBarControl::setClientModel(ClientModel *_clientModel)
+{
+ if (_clientModel) {
+ this->clientModel = _clientModel;
+ }
+}
diff --git a/src/qt/bitcoingui.h b/src/qt/bitcoingui.h
index 0eaa44b263..1b02e77fc4 100644
--- a/src/qt/bitcoingui.h
+++ b/src/qt/bitcoingui.h
@@ -26,6 +26,7 @@ class PlatformStyle;
class RPCConsole;
class SendCoinsRecipient;
class UnitDisplayStatusBarControl;
+class NetworkToggleStatusBarControl;
class WalletFrame;
class WalletModel;
class HelpMessageDialog;
@@ -85,7 +86,7 @@ private:
UnitDisplayStatusBarControl *unitDisplayControl;
QLabel *labelWalletEncryptionIcon;
QLabel *labelWalletHDStatusIcon;
- QLabel *labelConnectionsIcon;
+ NetworkToggleStatusBarControl *connectionsControl;
QLabel *labelBlocksIcon;
QLabel *progressBarLabel;
QProgressBar *progressBar;
@@ -146,6 +147,9 @@ private:
/** Disconnect core signals from GUI client */
void unsubscribeFromCoreSignals();
+ /** Update UI with latest network info from model. */
+ void updateNetworkState();
+
Q_SIGNALS:
/** Signal raised when a URI was entered or dragged to the GUI */
void receivedURI(const QString &uri);
@@ -153,6 +157,8 @@ Q_SIGNALS:
public Q_SLOTS:
/** Set number of connections shown in the UI */
void setNumConnections(int count);
+ /** Set network state shown in the UI */
+ void setNetworkActive(bool networkActive);
/** Set number of blocks and last block date shown in the UI */
void setNumBlocks(int count, const QDateTime& blockDate, double nVerificationProgress, bool headers);
@@ -264,4 +270,17 @@ private Q_SLOTS:
void onMenuSelection(QAction* action);
};
+class NetworkToggleStatusBarControl : public QLabel
+{
+ Q_OBJECT
+
+public:
+ void setClientModel(ClientModel *clientModel);
+protected:
+ void mousePressEvent(QMouseEvent *event);
+
+private:
+ ClientModel *clientModel;
+};
+
#endif // BITCOIN_QT_BITCOINGUI_H
diff --git a/src/qt/clientmodel.cpp b/src/qt/clientmodel.cpp
index f9caca6878..a4bb2f77fe 100644
--- a/src/qt/clientmodel.cpp
+++ b/src/qt/clientmodel.cpp
@@ -145,6 +145,11 @@ void ClientModel::updateNumConnections(int numConnections)
Q_EMIT numConnectionsChanged(numConnections);
}
+void ClientModel::updateNetworkActive(bool networkActive)
+{
+ Q_EMIT networkActiveChanged(networkActive);
+}
+
void ClientModel::updateAlert()
{
Q_EMIT alertsChanged(getStatusBarWarnings());
@@ -167,6 +172,21 @@ enum BlockSource ClientModel::getBlockSource() const
return BLOCK_SOURCE_NONE;
}
+void ClientModel::setNetworkActive(bool active)
+{
+ if (g_connman) {
+ g_connman->SetNetworkActive(active);
+ }
+}
+
+bool ClientModel::getNetworkActive() const
+{
+ if (g_connman) {
+ return g_connman->GetNetworkActive();
+ }
+ return false;
+}
+
QString ClientModel::getStatusBarWarnings() const
{
return QString::fromStdString(GetWarnings("gui"));
@@ -233,6 +253,12 @@ static void NotifyNumConnectionsChanged(ClientModel *clientmodel, int newNumConn
Q_ARG(int, newNumConnections));
}
+static void NotifyNetworkActiveChanged(ClientModel *clientmodel, bool networkActive)
+{
+ QMetaObject::invokeMethod(clientmodel, "updateNetworkActive", Qt::QueuedConnection,
+ Q_ARG(bool, networkActive));
+}
+
static void NotifyAlertChanged(ClientModel *clientmodel)
{
qDebug() << "NotifyAlertChanged";
@@ -273,6 +299,7 @@ void ClientModel::subscribeToCoreSignals()
// Connect signals to client
uiInterface.ShowProgress.connect(boost::bind(ShowProgress, this, _1, _2));
uiInterface.NotifyNumConnectionsChanged.connect(boost::bind(NotifyNumConnectionsChanged, this, _1));
+ uiInterface.NotifyNetworkActiveChanged.connect(boost::bind(NotifyNetworkActiveChanged, this, _1));
uiInterface.NotifyAlertChanged.connect(boost::bind(NotifyAlertChanged, this));
uiInterface.BannedListChanged.connect(boost::bind(BannedListChanged, this));
uiInterface.NotifyBlockTip.connect(boost::bind(BlockTipChanged, this, _1, _2, false));
@@ -284,6 +311,7 @@ void ClientModel::unsubscribeFromCoreSignals()
// Disconnect signals from client
uiInterface.ShowProgress.disconnect(boost::bind(ShowProgress, this, _1, _2));
uiInterface.NotifyNumConnectionsChanged.disconnect(boost::bind(NotifyNumConnectionsChanged, this, _1));
+ uiInterface.NotifyNetworkActiveChanged.disconnect(boost::bind(NotifyNetworkActiveChanged, this, _1));
uiInterface.NotifyAlertChanged.disconnect(boost::bind(NotifyAlertChanged, this));
uiInterface.BannedListChanged.disconnect(boost::bind(BannedListChanged, this));
uiInterface.NotifyBlockTip.disconnect(boost::bind(BlockTipChanged, this, _1, _2, false));
diff --git a/src/qt/clientmodel.h b/src/qt/clientmodel.h
index 3fd8404cbb..a641401425 100644
--- a/src/qt/clientmodel.h
+++ b/src/qt/clientmodel.h
@@ -68,6 +68,10 @@ public:
bool inInitialBlockDownload() const;
//! Return true if core is importing blocks
enum BlockSource getBlockSource() const;
+ //! Return true if network activity in core is enabled
+ bool getNetworkActive() const;
+ //! Toggle network activity state in core
+ void setNetworkActive(bool active);
//! Return warnings to be displayed in status bar
QString getStatusBarWarnings() const;
@@ -91,6 +95,7 @@ Q_SIGNALS:
void numConnectionsChanged(int count);
void numBlocksChanged(int count, const QDateTime& blockDate, double nVerificationProgress, bool header);
void mempoolSizeChanged(long count, size_t mempoolSizeInBytes);
+ void networkActiveChanged(bool networkActive);
void alertsChanged(const QString &warnings);
void bytesChanged(quint64 totalBytesIn, quint64 totalBytesOut);
@@ -103,6 +108,7 @@ Q_SIGNALS:
public Q_SLOTS:
void updateTimer();
void updateNumConnections(int numConnections);
+ void updateNetworkActive(bool networkActive);
void updateAlert();
void updateBanlist();
};
diff --git a/src/qt/res/icons/network_disabled.png b/src/qt/res/icons/network_disabled.png
new file mode 100644
index 0000000000..49f728693d
--- /dev/null
+++ b/src/qt/res/icons/network_disabled.png
Binary files differ
diff --git a/src/qt/res/src/network_disabled.svg b/src/qt/res/src/network_disabled.svg
new file mode 100644
index 0000000000..e95a5eb5bb
--- /dev/null
+++ b/src/qt/res/src/network_disabled.svg
@@ -0,0 +1,68 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ id="svg2"
+ viewBox="0 0 24 24"
+ height="24"
+ width="24"
+ version="1.2">
+ <metadata
+ id="metadata10">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <defs
+ id="defs8" />
+ <g
+ id="g4142"
+ transform="matrix(0,-1,-1,0,23.96,24)">
+ <g
+ id="g4210"
+ transform="matrix(-1,0,0,1,59.86,-106.6)">
+ <g
+ id="g4289"
+ transform="matrix(-1,0,0,1,-16.98,0.8136)">
+ <g
+ id="g4291">
+ <path
+ id="path4293"
+ d="m -65.35,116.3 0,3 0.5,0 c 0.54,0 1,0.5 1,1 l 0,2.6 c -1.15,0.5 -2,1.6 -2,3 0,2 1.59,3.5 3.5,3.5 1.91,0 3.5,-1.5 3.5,-3.5 0,-1.4 -0.85,-2.5 -2,-3 l 0,-2.6 c 0,-2.3 -1.81,-4 -4,-4 z m 1,1.2 c 1.39,0.3 2.5,1.3 2.5,2.8 l 0,3.2 0.34,0.1 c 0.96,0.3 1.66,1.2 1.66,2.3 0,1.4 -1.11,2.5 -2.5,2.5 -1.39,0 -2.5,-1.1 -2.5,-2.5 0,-1.1 0.69,-2 1.66,-2.3 l 0.34,-0.1 0,-3.2 c 0,-0.9 -0.67,-1.5 -1.5,-1.8 z"
+ style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" />
+ <g
+ style="fill:#969696;fill-opacity:1"
+ id="g4295">
+ <path
+ id="path4297"
+ d="m -67.35,106.1 c -1.94,0 -3.5,1.6 -3.5,3.5 0,1.4 0.85,2.5 2,3 l 0,2.7 c 0,2.2 1.79,4 4,4 l 0.5,0 0,-0.5 0,-2.5 -0.5,0 c -0.55,0 -1,-0.5 -1,-1 l 0,-2.7 c 1.15,-0.5 2,-1.6 2,-3 0,-1.9 -1.57,-3.5 -3.5,-3.5 z m 0,1 c 1.37,0 2.5,1.2 2.5,2.5 0,1.1 -0.7,2 -1.66,2.3 l -0.34,0.1 0,3.3 c 0,0.9 0.67,1.5 1.5,1.8 l 0,1 c -1.38,-0.3 -2.5,-1.4 -2.5,-2.8 l 0,-3.3 -0.34,-0.1 c -0.96,-0.3 -1.66,-1.2 -1.66,-2.3 0,-1.3 1.12,-2.5 2.5,-2.5 z"
+ style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" />
+ <path
+ id="path4299"
+ d="m -57.35,106.1 c -1.93,0 -3.5,1.6 -3.5,3.5 0,1.4 0.85,2.5 2,3 l 0,2.7 c 0,0.5 -0.45,1 -1,1 l -4.85,0 3.17,3 1.68,0 c 2.21,0 4,-1.8 4,-4 l 0,-2.7 c 1.15,-0.5 2,-1.6 2,-3 0,-1.9 -1.56,-3.5 -3.5,-3.5 z m 0,1 c 1.38,0 2.5,1.2 2.5,2.5 0,1.1 -0.7,2 -1.66,2.3 l -0.34,0.1 0,3.3 c 0,1.6 -1.35,3 -3,3 l -1.81,0 -2.04,-1 3.85,0 c 1.11,0 2,-0.9 2,-2 l 0,-3.3 -0.34,-0.1 c -0.96,-0.3 -1.66,-1.2 -1.66,-2.3 0,-1.3 1.13,-2.5 2.5,-2.5 z"
+ style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" />
+ </g>
+ <path
+ id="path4301"
+ d="m -69.84,116.3 c -2.19,0 -4,1.7 -4,4 l 0,2.6 c -1.14,0.6 -1.99,1.6 -1.99,3 0,2 1.6,3.5 3.51,3.5 1.91,0 3.5,-1.5 3.5,-3.5 0,-1.4 -0.85,-2.5 -2,-3 l 0,-2.6 c 0,-0.5 0.45,-1 1,-1 l 5.01,0 -3.36,-3 z m 0,1 1.84,0 2.19,1 -4.01,0 c -1.11,0 -2,0.9 -2,2 l 0,3.2 0.34,0.1 c 0.96,0.3 1.66,1.2 1.66,2.3 0,1.4 -1.11,2.5 -2.5,2.5 -1.39,0 -2.51,-1.1 -2.51,-2.5 0,-1.1 0.7,-2 1.66,-2.3 l 0.33,-0.1 0,-0.4 0,-2.8 c 0,-1.7 1.33,-3 3,-3 z"
+ style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" />
+ </g>
+ </g>
+ </g>
+ <path
+ id="path4165"
+ d="m 12,8.77 c -0.84,0 -1.66,0.341 -2.254,0.937 -0.599,0.593 -0.942,1.403 -0.945,2.253 0,0.85 0.337,1.67 0.933,2.26 a 0.6001,0.6001 0 0 0 0,0 c 0.594,0.6 1.424,0.94 2.264,0.94 0.84,0 1.67,-0.34 2.26,-0.94 0.6,-0.59 0.94,-1.41 0.94,-2.26 0,-0.84 -0.34,-1.66 -0.95,-2.253 C 13.66,9.111 12.84,8.77 12,8.77 Z"
+ style="opacity:1;fill:#000000;fill-opacity:1;stroke:none;stroke-width:10;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+ </g>
+ <path d="M 3,3 l 18,18" style="stroke-width: 3; stroke: #000000; stroke-linecap: round;" />
+ <path d="M 21,3 l -18,18" style="stroke-width: 3; stroke: #000000; stroke-linecap: round;" />
+</svg>
diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp
index a9fef731e1..47af6a5724 100644
--- a/src/qt/rpcconsole.cpp
+++ b/src/qt/rpcconsole.cpp
@@ -458,6 +458,9 @@ void RPCConsole::setClientModel(ClientModel *model)
setNumBlocks(model->getNumBlocks(), model->getLastBlockDate(), model->getVerificationProgress(NULL), false);
connect(model, SIGNAL(numBlocksChanged(int,QDateTime,double,bool)), this, SLOT(setNumBlocks(int,QDateTime,double,bool)));
+ updateNetworkState();
+ connect(model, SIGNAL(networkActiveChanged(bool)), this, SLOT(setNetworkActive(bool)));
+
updateTrafficStats(model->getTotalBytesRecv(), model->getTotalBytesSent());
connect(model, SIGNAL(bytesChanged(quint64,quint64)), this, SLOT(updateTrafficStats(quint64, quint64)));
@@ -674,16 +677,30 @@ void RPCConsole::message(int category, const QString &message, bool html)
ui->messagesWidget->append(out);
}
+void RPCConsole::updateNetworkState()
+{
+ QString connections = QString::number(clientModel->getNumConnections()) + " (";
+ connections += tr("In:") + " " + QString::number(clientModel->getNumConnections(CONNECTIONS_IN)) + " / ";
+ connections += tr("Out:") + " " + QString::number(clientModel->getNumConnections(CONNECTIONS_OUT)) + ")";
+
+ if(!clientModel->getNetworkActive()) {
+ connections += " (" + tr("Network activity disabled") + ")";
+ }
+
+ ui->numberOfConnections->setText(connections);
+}
+
void RPCConsole::setNumConnections(int count)
{
if (!clientModel)
return;
- QString connections = QString::number(count) + " (";
- connections += tr("In:") + " " + QString::number(clientModel->getNumConnections(CONNECTIONS_IN)) + " / ";
- connections += tr("Out:") + " " + QString::number(clientModel->getNumConnections(CONNECTIONS_OUT)) + ")";
+ updateNetworkState();
+}
- ui->numberOfConnections->setText(connections);
+void RPCConsole::setNetworkActive(bool networkActive)
+{
+ updateNetworkState();
}
void RPCConsole::setNumBlocks(int count, const QDateTime& blockDate, double nVerificationProgress, bool headers)
@@ -1069,3 +1086,8 @@ void RPCConsole::setTabFocus(enum TabTypes tabType)
{
ui->tabWidget->setCurrentIndex(tabType);
}
+
+void RPCConsole::on_toggleNetworkActiveButton_clicked()
+{
+ clientModel->setNetworkActive(!clientModel->getNetworkActive());
+}
diff --git a/src/qt/rpcconsole.h b/src/qt/rpcconsole.h
index 8e1d878ae5..8c20379a8c 100644
--- a/src/qt/rpcconsole.h
+++ b/src/qt/rpcconsole.h
@@ -61,6 +61,8 @@ protected:
private Q_SLOTS:
void on_lineEdit_returnPressed();
void on_tabWidget_currentChanged(int index);
+ /** toggle network activity */
+ void on_toggleNetworkActiveButton_clicked();
/** open the debug.log from the current datadir */
void on_openDebugLogfileButton_clicked();
/** change the time range of the network traffic graph */
@@ -88,6 +90,8 @@ public Q_SLOTS:
void message(int category, const QString &message, bool html = false);
/** Set number of connections shown in the UI */
void setNumConnections(int count);
+ /** Set network state shown in the UI */
+ void setNetworkActive(bool networkActive);
/** Set number of blocks and last block date shown in the UI */
void setNumBlocks(int count, const QDateTime& blockDate, double nVerificationProgress, bool headers);
/** Set size (number of transactions and memory usage) of the mempool in the UI */
@@ -144,6 +148,9 @@ private:
QMenu *banTableContextMenu;
int consoleFontSize;
QCompleter *autoCompleter;
+
+ /** Update UI with latest network info from model. */
+ void updateNetworkState();
};
#endif // BITCOIN_QT_RPCCONSOLE_H
diff --git a/src/rpc/client.cpp b/src/rpc/client.cpp
index 8370a0f43e..03992a278d 100644
--- a/src/rpc/client.cpp
+++ b/src/rpc/client.cpp
@@ -109,6 +109,7 @@ static const CRPCConvertParam vRPCConvertParams[] =
{ "prioritisetransaction", 2 },
{ "setban", 2 },
{ "setban", 3 },
+ { "setnetworkactive", 0 },
{ "getmempoolancestors", 1 },
{ "getmempooldescendants", 1 },
};
diff --git a/src/rpc/net.cpp b/src/rpc/net.cpp
index bd68abdbb8..f57ba76d3a 100644
--- a/src/rpc/net.cpp
+++ b/src/rpc/net.cpp
@@ -401,6 +401,7 @@ UniValue getnetworkinfo(const JSONRPCRequest& request)
" \"localrelay\": true|false, (bool) true if transaction relay is requested from peers\n"
" \"timeoffset\": xxxxx, (numeric) the time offset\n"
" \"connections\": xxxxx, (numeric) the number of connections\n"
+ " \"networkactive\": true|false, (bool) whether p2p networking is enabled\n"
" \"networks\": [ (array) information per network\n"
" {\n"
" \"name\": \"xxx\", (string) network (ipv4, ipv6 or onion)\n"
@@ -435,8 +436,10 @@ UniValue getnetworkinfo(const JSONRPCRequest& request)
obj.push_back(Pair("localservices", strprintf("%016x", g_connman->GetLocalServices())));
obj.push_back(Pair("localrelay", fRelayTxes));
obj.push_back(Pair("timeoffset", GetTimeOffset()));
- if(g_connman)
+ if (g_connman) {
+ obj.push_back(Pair("networkactive", g_connman->GetNetworkActive()));
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);
@@ -571,6 +574,24 @@ UniValue clearbanned(const JSONRPCRequest& request)
return NullUniValue;
}
+UniValue setnetworkactive(const JSONRPCRequest& request)
+{
+ if (request.fHelp || request.params.size() != 1) {
+ throw runtime_error(
+ "setnetworkactive true|false\n"
+ "Disable/enable all p2p network activity."
+ );
+ }
+
+ if (!g_connman) {
+ throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled");
+ }
+
+ g_connman->SetNetworkActive(request.params[0].get_bool());
+
+ return g_connman->GetNetworkActive();
+}
+
static const CRPCCommand commands[] =
{ // category name actor (function) okSafeMode
// --------------------- ------------------------ ----------------------- ----------
@@ -585,6 +606,7 @@ static const CRPCCommand commands[] =
{ "network", "setban", &setban, true },
{ "network", "listbanned", &listbanned, true },
{ "network", "clearbanned", &clearbanned, true },
+ { "network", "setnetworkactive", &setnetworkactive, true, },
};
void RegisterNetRPCCommands(CRPCTable &t)
diff --git a/src/test/rpc_tests.cpp b/src/test/rpc_tests.cpp
index a3d1a25589..a359598ddc 100644
--- a/src/test/rpc_tests.cpp
+++ b/src/test/rpc_tests.cpp
@@ -84,6 +84,28 @@ BOOST_AUTO_TEST_CASE(rpc_rawparams)
BOOST_CHECK_THROW(CallRPC(string("sendrawtransaction ")+rawtx+" extra"), runtime_error);
}
+BOOST_AUTO_TEST_CASE(rpc_togglenetwork)
+{
+ UniValue r;
+
+ r = CallRPC("getnetworkinfo");
+ bool netState = find_value(r.get_obj(), "networkactive").get_bool();
+ BOOST_CHECK_EQUAL(netState, true);
+
+ BOOST_CHECK_NO_THROW(CallRPC("setnetworkactive false"));
+ r = CallRPC("getnetworkinfo");
+ int numConnection = find_value(r.get_obj(), "connections").get_int();
+ BOOST_CHECK_EQUAL(numConnection, 0);
+
+ netState = find_value(r.get_obj(), "networkactive").get_bool();
+ BOOST_CHECK_EQUAL(netState, false);
+
+ BOOST_CHECK_NO_THROW(CallRPC("setnetworkactive true"));
+ r = CallRPC("getnetworkinfo");
+ netState = find_value(r.get_obj(), "networkactive").get_bool();
+ BOOST_CHECK_EQUAL(netState, true);
+}
+
BOOST_AUTO_TEST_CASE(rpc_rawsign)
{
UniValue r;
diff --git a/src/ui_interface.h b/src/ui_interface.h
index 177ff238db..15b9614f63 100644
--- a/src/ui_interface.h
+++ b/src/ui_interface.h
@@ -85,6 +85,9 @@ public:
/** Number of network connections changed. */
boost::signals2::signal<void (int newNumConnections)> NotifyNumConnectionsChanged;
+ /** Network activity state changed. */
+ boost::signals2::signal<void (bool networkActive)> NotifyNetworkActiveChanged;
+
/**
* Status bar alerts changed.
*/