diff --git a/doc/reduce-traffic.md b/doc/reduce-traffic.md --- a/doc/reduce-traffic.md +++ b/doc/reduce-traffic.md @@ -35,3 +35,16 @@ Reducing the maximum connected nodes to a minimum could be desirable if traffic limits are tiny. Keep in mind that bitcoin's trustless model works best if you are connected to a handful of nodes. + +## 4. Turn off transaction relay (`-blocksonly`) + +Forwarding transactions to peers increases the P2P traffic. To only sync blocks +with other peers, you can disable transaction relay. + +Be reminded of the effects of this setting. + +- Fee estimation will no longer work. +- Not relaying other's transactions could hurt your privacy if used while a + wallet is loaded or if you use the node to broadcast transactions. +- It makes block propagation slower because compact block relay can only be + used when transaction relay is enabled. diff --git a/src/init.cpp b/src/init.cpp --- a/src/init.cpp +++ b/src/init.cpp @@ -429,10 +429,11 @@ ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); gArgs.AddArg( "-blocksonly", - strprintf("Whether to operate in a blocks only mode (default: %d)", - DEFAULT_BLOCKSONLY), - ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, - OptionsCategory::OPTIONS); + strprintf( + "Whether to reject transactions from network peers. Transactions " + "from the wallet or RPC are not affected. (default: %u)", + DEFAULT_BLOCKSONLY), + ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); gArgs.AddArg("-conf=", strprintf("Specify configuration file. Relative paths will be " "prefixed by datadir location. (default: %s)", @@ -2287,7 +2288,7 @@ // see Step 2: parameter interactions for more information about these fListen = gArgs.GetBoolArg("-listen", DEFAULT_LISTEN); fDiscover = gArgs.GetBoolArg("-discover", true); - fRelayTxes = !gArgs.GetBoolArg("-blocksonly", DEFAULT_BLOCKSONLY); + g_relay_txes = !gArgs.GetBoolArg("-blocksonly", DEFAULT_BLOCKSONLY); for (const std::string &strAddr : gArgs.GetArgs("-externalip")) { CService addrLocal; diff --git a/src/net.h b/src/net.h --- a/src/net.h +++ b/src/net.h @@ -538,7 +538,7 @@ extern bool fDiscover; extern bool fListen; -extern bool fRelayTxes; +extern bool g_relay_txes; struct LocalServiceInfo { int nScore; diff --git a/src/net.cpp b/src/net.cpp --- a/src/net.cpp +++ b/src/net.cpp @@ -89,7 +89,7 @@ // bool fDiscover = true; bool fListen = true; -bool fRelayTxes = true; +bool g_relay_txes = !DEFAULT_BLOCKSONLY; RecursiveMutex cs_mapLocalHost; std::map mapLocalHost GUARDED_BY(cs_mapLocalHost); static bool vfLimited[NET_MAX] GUARDED_BY(cs_mapLocalHost) = {}; diff --git a/src/net_processing.cpp b/src/net_processing.cpp --- a/src/net_processing.cpp +++ b/src/net_processing.cpp @@ -499,7 +499,7 @@ .Make(NetMsgType::VERSION, PROTOCOL_VERSION, uint64_t(nLocalNodeServices), nTime, addrYou, addrMe, nonce, userAgent(config), - nNodeStartingHeight, ::fRelayTxes)); + nNodeStartingHeight, ::g_relay_txes)); if (fLogIPs) { LogPrint(BCLog::NET, @@ -2547,7 +2547,7 @@ return error("message inv size() = %u", vInv.size()); } - bool fBlocksOnly = !fRelayTxes; + bool fBlocksOnly = !g_relay_txes; // Allow whitelisted peers to send data other than blocks in blocks only // mode if whitelistrelay is true @@ -2852,7 +2852,7 @@ // Stop processing the transaction early if // We are in blocks only mode and peer is either not whitelisted or // whitelistrelay is off - if (!fRelayTxes && !pfrom->HasPermission(PF_RELAY)) { + if (!g_relay_txes && !pfrom->HasPermission(PF_RELAY)) { LogPrint(BCLog::NET, "transaction sent in violation of protocol peer=%d\n", pfrom->GetId()); diff --git a/src/rpc/net.cpp b/src/rpc/net.cpp --- a/src/rpc/net.cpp +++ b/src/rpc/net.cpp @@ -622,7 +622,7 @@ obj.pushKV("localservices", strprintf("%016x", g_rpc_node->connman->GetLocalServices())); } - obj.pushKV("localrelay", fRelayTxes); + obj.pushKV("localrelay", g_relay_txes); obj.pushKV("timeoffset", GetTimeOffset()); if (g_rpc_node->connman) { obj.pushKV("networkactive", g_rpc_node->connman->GetNetworkActive()); diff --git a/test/functional/p2p_blocksonly.py b/test/functional/p2p_blocksonly.py new file mode 100755 --- /dev/null +++ b/test/functional/p2p_blocksonly.py @@ -0,0 +1,64 @@ +#!/usr/bin/env python3 +# Copyright (c) 2019 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 p2p blocksonly""" + +from test_framework.messages import msg_tx, CTransaction, FromHex +from test_framework.mininode import P2PInterface +from test_framework.test_framework import BitcoinTestFramework +from test_framework.util import assert_equal + + +class P2PBlocksOnly(BitcoinTestFramework): + def set_test_params(self): + self.setup_clean_chain = False + self.num_nodes = 1 + self.extra_args = [["-blocksonly"]] + + def run_test(self): + self.nodes[0].add_p2p_connection(P2PInterface()) + + self.log.info('Check that txs from p2p are rejected') + prevtx = self.nodes[0].getblock( + self.nodes[0].getblockhash(1), 2)['tx'][0] + rawtx = self.nodes[0].createrawtransaction( + inputs=[{ + 'txid': prevtx['txid'], + 'vout': 0 + }], + outputs=[{ + self.nodes[0].get_deterministic_priv_key().address: 50 - + 0.00125 + }], + ) + self.log.info(prevtx) + sigtx = self.nodes[0].signrawtransactionwithkey( + hexstring=rawtx, + privkeys=[self.nodes[0].get_deterministic_priv_key().key], + prevtxs=[{ + 'txid': prevtx['txid'], + 'vout': 0, + 'amount': prevtx['vout'][0]['value'], + 'scriptPubKey': prevtx['vout'][0]['scriptPubKey']['hex'], + }], + )['hex'] + assert_equal(self.nodes[0].getnetworkinfo()['localrelay'], False) + with self.nodes[0].assert_debug_log(['transaction sent in violation of protocol peer=0']): + self.nodes[0].p2p.send_message( + msg_tx(FromHex(CTransaction(), sigtx))) + self.nodes[0].p2p.sync_with_ping() + assert_equal(self.nodes[0].getmempoolinfo()['size'], 0) + + self.log.info( + 'Check that txs from rpc are not rejected and relayed to other peers') + assert_equal(self.nodes[0].getpeerinfo()[0]['relaytxes'], True) + txid = self.nodes[0].testmempoolaccept([sigtx])[0]['txid'] + with self.nodes[0].assert_debug_log(['received getdata for: tx {} peer=0'.format(txid)]): + self.nodes[0].sendrawtransaction(sigtx) + self.nodes[0].p2p.wait_for_tx(txid) + assert_equal(self.nodes[0].getmempoolinfo()['size'], 1) + + +if __name__ == '__main__': + P2PBlocksOnly().main() diff --git a/test/functional/test_framework/mininode.py b/test/functional/test_framework/mininode.py --- a/test/functional/test_framework/mininode.py +++ b/test/functional/test_framework/mininode.py @@ -419,6 +419,14 @@ # Message receiving helper methods + def wait_for_tx(self, txid, timeout=60): + def test_function(): + if not self.last_message.get('tx'): + return False + return self.last_message['tx'].tx.rehash() == txid + + wait_until(test_function, timeout=timeout, lock=mininode_lock) + def wait_for_block(self, blockhash, timeout=60): def test_function(): return self.last_message.get( "block") and self.last_message["block"].block.rehash() == blockhash diff --git a/test/functional/test_framework/util.py b/test/functional/test_framework/util.py --- a/test/functional/test_framework/util.py +++ b/test/functional/test_framework/util.py @@ -256,7 +256,7 @@ time.sleep(0.05) # Print the cause of the timeout - predicate_source = inspect.getsourcelines(predicate) + predicate_source = "''''\n" + inspect.getsource(predicate) + "'''" logger.error("wait_until() failed. Predicate: {}".format(predicate_source)) if attempt >= attempts: raise AssertionError("Predicate {} not true after {} attempts".format(