init: introduce a new option to enable/disable private broadcast

Co-authored-by: brunoerg <brunoely.gc@gmail.com>
Co-authored-by: Lőrinc <pap.lorinc@gmail.com>
This commit is contained in:
Vasil Dimov
2023-12-14 14:11:05 +01:00
parent d6ee490e0a
commit 94aaa5d31b
3 changed files with 64 additions and 3 deletions

View File

@@ -670,6 +670,15 @@ void SetupServerArgs(ArgsManager& argsman, bool can_listen_ipc)
OptionsCategory::NODE_RELAY);
argsman.AddArg("-minrelaytxfee=<amt>", strprintf("Fees (in %s/kvB) smaller than this are considered zero fee for relaying, mining and transaction creation (default: %s)",
CURRENCY_UNIT, FormatMoney(DEFAULT_MIN_RELAY_TX_FEE)), ArgsManager::ALLOW_ANY, OptionsCategory::NODE_RELAY);
argsman.AddArg("-privatebroadcast",
strprintf(
"Broadcast transactions submitted via sendrawtransaction RPC using short-lived "
"connections through the Tor or I2P networks, without putting them in the mempool first. "
"Transactions submitted through the wallet are not affected by this option "
"(default: %u)",
DEFAULT_PRIVATE_BROADCAST),
ArgsManager::ALLOW_ANY,
OptionsCategory::NODE_RELAY);
argsman.AddArg("-whitelistforcerelay", strprintf("Add 'forcerelay' permission to whitelisted peers with default permissions. This will relay transactions even if the transactions were already in the mempool. (default: %d)", DEFAULT_WHITELISTFORCERELAY), ArgsManager::ALLOW_ANY, OptionsCategory::NODE_RELAY);
argsman.AddArg("-whitelistrelay", strprintf("Add 'relay' permission to whitelisted peers with default permissions. This will accept relayed transactions even when not relaying transactions (default: %d)", DEFAULT_WHITELISTRELAY), ArgsManager::ALLOW_ANY, OptionsCategory::NODE_RELAY);
@@ -1732,13 +1741,13 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
}
}
const bool listenonion{args.GetBoolArg("-listenonion", DEFAULT_LISTEN_ONION)};
if (onion_proxy.IsValid()) {
SetProxy(NET_ONION, onion_proxy);
} else {
// If -listenonion is set, then we will (try to) connect to the Tor control port
// later from the torcontrol thread and may retrieve the onion proxy from there.
const bool listenonion_disabled{!args.GetBoolArg("-listenonion", DEFAULT_LISTEN_ONION)};
if (onlynet_used_with_onion && listenonion_disabled) {
if (onlynet_used_with_onion && !listenonion) {
return InitError(
_("Outbound connections restricted to Tor (-onlynet=onion) but the proxy for "
"reaching the Tor network is not provided: none of -proxy, -onion or "
@@ -2119,7 +2128,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
connOptions.onion_binds.push_back(onion_service_target);
}
if (args.GetBoolArg("-listenonion", DEFAULT_LISTEN_ONION)) {
if (listenonion) {
if (connOptions.onion_binds.size() > 1) {
InitWarning(strprintf(_("More than one onion bind address is provided. Using %s "
"for the automatically created Tor onion service."),
@@ -2192,6 +2201,32 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
conflict->ToStringAddrPort()));
}
if (args.GetBoolArg("-privatebroadcast", DEFAULT_PRIVATE_BROADCAST)) {
// If -listenonion is set, then NET_ONION may not be reachable now
// but may become reachable later, thus only error here if it is not
// reachable and will not become reachable for sure.
const bool onion_may_become_reachable{listenonion && (!args.IsArgSet("-onlynet") || onlynet_used_with_onion)};
if (!g_reachable_nets.Contains(NET_I2P) &&
!g_reachable_nets.Contains(NET_ONION) &&
!onion_may_become_reachable) {
return InitError(_("Private broadcast of own transactions requested (-privatebroadcast), "
"but none of Tor or I2P networks is reachable"));
}
if (!connOptions.m_use_addrman_outgoing) {
return InitError(_("Private broadcast of own transactions requested (-privatebroadcast), "
"but -connect is also configured. They are incompatible because the "
"private broadcast needs to open new connections to randomly "
"chosen Tor or I2P peers. Consider using -maxconnections=0 -addnode=... "
"instead"));
}
if (!proxyRandomize && (g_reachable_nets.Contains(NET_ONION) || onion_may_become_reachable)) {
InitWarning(_("Private broadcast of own transactions requested (-privatebroadcast) and "
"-proxyrandomize is disabled. Tor circuits for private broadcast connections "
"may be correlated to other connections over Tor. For maximum privacy set "
"-proxyrandomize=1."));
}
}
if (!node.connman->Start(scheduler, connOptions)) {
return false;
}

View File

@@ -83,6 +83,8 @@ static const std::string DEFAULT_MAX_UPLOAD_TARGET{"0M"};
static const bool DEFAULT_BLOCKSONLY = false;
/** -peertimeout default */
static const int64_t DEFAULT_PEER_CONNECT_TIMEOUT = 60;
/** Default for -privatebroadcast. */
static constexpr bool DEFAULT_PRIVATE_BROADCAST{false};
/** Number of file descriptors required for message capture **/
static const int NUM_FDS_MESSAGE_CAPTURE = 1;
/** Interval for ASMap Health Check **/

View File

@@ -411,6 +411,29 @@ class ConfArgsTest(BitcoinTestFramework):
self.restart_node(0, extra_args=[connect_arg, '-dnsseed', '-proxy=localhost:1080'])
self.stop_node(0)
def test_privatebroadcast(self):
self.log.info("Test that an invalid usage of -privatebroadcast throws an init error")
self.stop_node(0)
# -privatebroadcast init error: Tor/I2P not reachable at startup
self.nodes[0].assert_start_raises_init_error(
extra_args=["-privatebroadcast"],
expected_msg=(
"Error: Private broadcast of own transactions requested (-privatebroadcast), "
"but none of Tor or I2P networks is reachable"))
# -privatebroadcast init error: incompatible with -connect
self.nodes[0].assert_start_raises_init_error(
extra_args=["-privatebroadcast", "-connect=127.0.0.1:8333", "-onion=127.0.0.1:9050"],
expected_msg=(
"Error: Private broadcast of own transactions requested (-privatebroadcast), but -connect is also configured. "
"They are incompatible because the private broadcast needs to open new connections to randomly "
"chosen Tor or I2P peers. Consider using -maxconnections=0 -addnode=... instead"))
# Warning case: private broadcast allowed, but -proxyrandomize=0 triggers a privacy warning
self.start_node(0, extra_args=["-privatebroadcast", "-onion=127.0.0.1:9050", "-proxyrandomize=0"])
self.stop_node(0, expected_stderr=(
"Warning: Private broadcast of own transactions requested (-privatebroadcast) and "
"-proxyrandomize is disabled. Tor circuits for private broadcast connections may "
"be correlated to other connections over Tor. For maximum privacy set -proxyrandomize=1."))
def test_ignored_conf(self):
self.log.info('Test error is triggered when the datadir in use contains a bitcoin.conf file that would be ignored '
'because a conflicting -conf file argument is passed.')
@@ -496,6 +519,7 @@ class ConfArgsTest(BitcoinTestFramework):
self.test_seed_peers()
self.test_networkactive()
self.test_connect_with_seednode()
self.test_privatebroadcast()
self.test_dir_config()
self.test_negated_config()