Improve local node configurability.
MODIFY
WalletWasabi.Gui/Global.cs
WalletWasabi.Gui/Global.cs
@@ -18,6 +18,7 @@ using System.Text;
using System.Threading;
using System.Threading.Tasks;
using WalletWasabi.BitcoinCore;
+using WalletWasabi.BitcoinCore.Endpointing;
using WalletWasabi.CoinJoin.Client.Clients;
using WalletWasabi.Coins;
using WalletWasabi.Crypto;
@@ -151,7 +152,13 @@ namespace WalletWasabi.Gui
var addressManagerFolderPath = Path.Combine(DataDir, "AddressManager");
AddressManagerFilePath = Path.Combine(addressManagerFolderPath, $"AddressManager{Network}.dat");
var addrManTask = InitializeAddressManagerBehaviorAsync();
- var nodeCreationTask = CoreNode.CreateAsync(new CoreNodeParams(Network, EnvironmentHelpers.TryGetDefaultBitcoinCoreDataDir(), tryRestart: false, tryDeleteDataDir: false));
+ var nodeCreationTask = CoreNode.CreateAsync(new CoreNodeParams(
+ Network,
+ EnvironmentHelpers.TryGetDefaultBitcoinCoreDataDir(),
+ tryRestart: false,
+ tryDeleteDataDir: false,
+ EndPointStrategy.Custom(Config.GetBitcoinP2pEndPoint()),
+ EndPointStrategy.Default(Network, EndPointType.Rpc)));
var blocksFolderPath = Path.Combine(DataDir, $"Blocks{Network}");
var connectionParameters = new NodeConnectionParameters { UserAgent = "/Satoshi:0.18.1/" };

MODIFY
WalletWasabi.Tests/Helpers/TestNodeBuilder.cs
WalletWasabi.Tests/Helpers/TestNodeBuilder.cs
@@ -6,6 +6,7 @@ using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
using WalletWasabi.BitcoinCore;
+using WalletWasabi.BitcoinCore.Endpointing;
using WalletWasabi.Helpers;
namespace WalletWasabi.Tests.Helpers
@@ -17,6 +18,8 @@ namespace WalletWasabi.Tests.Helpers
Network.RegTest,
Path.Combine(Global.Instance.DataDir, EnvironmentHelpers.ExtractFileName(callerFilePath), callerMemberName, additionalFolder ?? ""),
tryRestart: true,
- tryDeleteDataDir: true));
+ tryDeleteDataDir: true,
+ EndPointStrategy.Random,
+ EndPointStrategy.Random));
}
}

MODIFY
WalletWasabi/BitcoinCore/Configuration/NetworkTranslator.cs
WalletWasabi/BitcoinCore/Configuration/NetworkTranslator.cs
@@ -9,20 +9,13 @@ namespace WalletWasabi.BitcoinCore.Configuration
{
public static class NetworkTranslator
{
- public static string GetConfigPrefix(Network network, bool mainnetEmpty)
+ public static string GetConfigPrefix(Network network)
{
Guard.NotNull(nameof(network), network);
if (network == Network.Main)
- {
- if (mainnetEmpty)
- {
- return string.Empty;
- }
- else
{
return "main";
}
- }
else if (network == Network.TestNet)
{
return "test";
@@ -37,6 +30,17 @@ namespace WalletWasabi.BitcoinCore.Configuration
}
}
+ public static IEnumerable<string> GetConfigPrefixesWithDots(Network network)
+ {
+ Guard.NotNull(nameof(network), network);
+
+ yield return $"{GetConfigPrefix(network)}.";
+ if (network == Network.Main)
+ {
+ yield return string.Empty;
+ }
+ }
+
public static string GetDataDirPrefix(Network network)
{
Guard.NotNull(nameof(network), network);

MODIFY
WalletWasabi/BitcoinCore/CoreNode.cs
WalletWasabi/BitcoinCore/CoreNode.cs
@@ -14,6 +14,7 @@ using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;
using WalletWasabi.BitcoinCore.Configuration;
+using WalletWasabi.BitcoinCore.Endpointing;
using WalletWasabi.Helpers;
using WalletWasabi.Logging;
@@ -53,63 +54,72 @@ namespace WalletWasabi.BitcoinCore
}
IoHelpers.EnsureDirectoryExists(coreNode.DataDir);
- var pass = Encoders.Hex.EncodeData(RandomUtils.GetBytes(20));
- var creds = new NetworkCredential(pass, pass);
+ var configDic = config.ToDictionary();
+ string rpcUser = null;
+ string rpcPassword = null;
+ EndPoint whitebind = null;
+ string rpcHost = null;
+ int? rpcPort = null;
+ foreach (var networkPrefixWithDot in NetworkTranslator.GetConfigPrefixesWithDots(coreNode.Network))
+ {
+ var ru = configDic.TryGet($"{networkPrefixWithDot}rpcuser");
+ var rp = configDic.TryGet($"{networkPrefixWithDot}rpcpassword");
+ var wbs = configDic.TryGet($"{networkPrefixWithDot}whitebind");
+ var rpst = configDic.TryGet($"{networkPrefixWithDot}rpchost");
+ var rpts = configDic.TryGet($"{networkPrefixWithDot}rpcport");
- var portArray = new int[2];
- var i = 0;
- while (i < portArray.Length)
+ if (ru != null)
{
- var port = RandomUtils.GetUInt32() % 4000;
- port += 10000;
- if (portArray.Any(p => p == port))
+ rpcUser = ru;
+ }
+ if (rp != null)
{
- continue;
+ rpcUser = rp;
}
-
- try
+ if (wbs != null && EndPointParser.TryParse(wbs, coreNode.Network.DefaultPort, out EndPoint wb))
{
- var listener = new TcpListener(IPAddress.Loopback, (int)port);
- listener.Start();
- listener.Stop();
- portArray[i] = (int)port;
- i++;
+ whitebind = wb;
}
- catch (SocketException)
+ if (rpst != null)
{
+ rpcHost = rpst;
+ }
+ if (rpts != null && int.TryParse(rpts, out int rpt))
+ {
+ rpcPort = rpt;
}
}
+ rpcUser ??= Encoders.Hex.EncodeData(RandomUtils.GetBytes(21));
+ rpcPassword ??= Encoders.Hex.EncodeData(RandomUtils.GetBytes(21));
+ var creds = new NetworkCredential(rpcUser, rpcPassword);
- var p2pPort = portArray[0];
- var rpcPort = portArray[1];
- coreNode.P2pEndPoint = new IPEndPoint(IPAddress.Loopback, p2pPort);
- coreNode.RpcEndPoint = new IPEndPoint(IPAddress.Loopback, rpcPort);
+ coreNode.P2pEndPoint = whitebind ?? coreNodeParams.P2pEndPointStrategy.EndPoint;
+ rpcHost ??= coreNodeParams.RpcEndPointStrategy.EndPoint.GetHostOrDefault();
+ rpcPort ??= coreNodeParams.RpcEndPointStrategy.EndPoint.GetPortOrDefault();
+ EndPointParser.TryParse($"{rpcHost}:{rpcPort}", coreNode.Network.RPCPort, out EndPoint rpce);
+ coreNode.RpcEndPoint = rpce;
- coreNode.RpcClient = new RPCClient($"{creds.UserName}:{creds.Password}", coreNode.RpcEndPoint.ToString(rpcPort), coreNode.Network);
+ coreNode.RpcClient = new RPCClient($"{creds.UserName}:{creds.Password}", coreNode.RpcEndPoint.ToString(coreNode.Network.DefaultPort), coreNode.Network);
+ var pidFileName = "bitcoind.pid";
- var configPrefix = NetworkTranslator.GetConfigPrefix(coreNode.Network, mainnetEmpty: false);
+ var configPrefix = NetworkTranslator.GetConfigPrefix(coreNode.Network);
var desiredConfigString =
-@$"regtest = 1
-{configPrefix}.rest = 1
-{configPrefix}.listenonion = 0
-{configPrefix}.server = 1
+@$"{configPrefix}.server = 1
{configPrefix}.txindex = 1
+{configPrefix}.whitebind = {coreNode.P2pEndPoint.ToString(coreNode.Network.DefaultPort)}
{configPrefix}.rpcuser = {coreNode.RpcClient.CredentialString.UserPassword.UserName}
{configPrefix}.rpcpassword = {coreNode.RpcClient.CredentialString.UserPassword.Password}
-{configPrefix}.whitebind = 127.0.0.1:{p2pPort}
+{configPrefix}.rpchost = {coreNode.RpcEndPoint.GetHostOrDefault()}
{configPrefix}.rpcport = {coreNode.RpcEndPoint.GetPortOrDefault()}
-{configPrefix}.printtoconsole = 0
-{configPrefix}.keypool = 10
-{configPrefix}.pid = bitcoind.pid
-{configPrefix}.checklevel = 0
-{configPrefix}.checkblocks = 1";
+{configPrefix}.pid = {pidFileName}";
config.AddOrUpdate(desiredConfigString);
await File.WriteAllTextAsync(configPath, config.ToString());
+ var configFileName = Path.GetFileName(configPath);
coreNode.Bridge = new BitcoindProcessBridge();
- coreNode.Process = coreNode.Bridge.Start($"-conf=bitcoin.conf -datadir={coreNode.DataDir} -debug=1", false);
- string pidFile = Path.Combine(coreNode.DataDir, NetworkTranslator.GetDataDirPrefix(coreNode.Network), "bitcoind.pid");
+ coreNode.Process = coreNode.Bridge.Start($"-{coreNode.Network.ToString().ToLowerInvariant()} -conf={configFileName} -datadir={coreNode.DataDir} -printtoconsole=0", false);
+ string pidFile = Path.Combine(coreNode.DataDir, NetworkTranslator.GetDataDirPrefix(coreNode.Network), pidFileName);
if (!File.Exists(pidFile))
{
IoHelpers.EnsureDirectoryExists(Path.Combine(coreNode.DataDir, NetworkTranslator.GetDataDirPrefix(coreNode.Network)));
@@ -123,8 +133,9 @@ namespace WalletWasabi.BitcoinCore
await coreNode.RpcClient.GetBlockHashAsync(0).ConfigureAwait(false);
break;
}
- catch
+ catch (Exception ex)
{
+ Logger.LogTrace(ex);
}
if (coreNode.Process is null || coreNode.Process.HasExited)
{
@@ -198,7 +209,7 @@ namespace WalletWasabi.BitcoinCore
var foundConfigDic = config.ToDictionary();
- var networkEntry = NetworkTranslator.GetConfigPrefix(network, mainnetEmpty: false);
+ var networkEntry = NetworkTranslator.GetConfigPrefix(network);
var rpcPortString = foundConfigDic.TryGet($"{networkEntry}.rpcport");
var rpcUser = foundConfigDic.TryGet($"{networkEntry}.rpcuser");
var rpcPassword = foundConfigDic.TryGet($"{networkEntry}.rpcpassword");

MODIFY
WalletWasabi/BitcoinCore/CoreNodeParams.cs
WalletWasabi/BitcoinCore/CoreNodeParams.cs
@@ -2,23 +2,34 @@ using NBitcoin;
using System;
using System.Collections.Generic;
using System.Text;
+using WalletWasabi.BitcoinCore.Endpointing;
using WalletWasabi.Helpers;
namespace WalletWasabi.BitcoinCore
{
public class CoreNodeParams
{
- public CoreNodeParams(Network network, string dataDir, bool tryRestart, bool tryDeleteDataDir)
+ public CoreNodeParams(
+ Network network,
+ string dataDir,
+ bool tryRestart,
+ bool tryDeleteDataDir,
+ EndPointStrategy p2pEndPointStrategy,
+ EndPointStrategy rpcEndPointStrategy)
{
Network = Guard.NotNull(nameof(network), network);
DataDir = Guard.NotNullOrEmptyOrWhitespace(nameof(dataDir), dataDir);
TryRestart = tryRestart;
TryDeleteDataDir = tryDeleteDataDir;
+ P2pEndPointStrategy = Guard.NotNull(nameof(p2pEndPointStrategy), p2pEndPointStrategy);
+ RpcEndPointStrategy = Guard.NotNull(nameof(rpcEndPointStrategy), rpcEndPointStrategy);
}
public string DataDir { get; }
public Network Network { get; }
public bool TryRestart { get; }
public bool TryDeleteDataDir { get; }
+ public EndPointStrategy P2pEndPointStrategy { get; }
+ public EndPointStrategy RpcEndPointStrategy { get; }
}
}

ADD
WalletWasabi/BitcoinCore/Endpointing/EndPointStrategy.cs
+using NBitcoin;
+using System;
+using System.Collections.Generic;
+using System.Net;
+using System.Text;
+using WalletWasabi.Helpers;
+
+namespace WalletWasabi.BitcoinCore.Endpointing
+{
+ public class EndPointStrategy
+ {
+ public static EndPointStrategy Random
+ => new EndPointStrategy(EndPointStrategyType.Random, new IPEndPoint(IPAddress.Loopback, PortFinder.GetRandomPorts(1)[0]));
+
+ public static EndPointStrategy Custom(EndPoint endPoint)
+ => new EndPointStrategy(EndPointStrategyType.Custom, endPoint);
+
+ public static EndPointStrategy Default(Network network, EndPointType endPointType)
+ {
+ var port = endPointType == EndPointType.Rpc ? network.RPCPort : network.DefaultPort;
+ return new EndPointStrategy(EndPointStrategyType.Default, new IPEndPoint(IPAddress.Loopback, port));
+ }
+
+ private EndPointStrategy(EndPointStrategyType endPointStrategyType, EndPoint endPoint)
+ {
+ EndPointStrategyType = endPointStrategyType;
+ EndPoint = Guard.NotNull(nameof(endPoint), endPoint);
+ }
+
+ public EndPointStrategyType EndPointStrategyType { get; }
+ public EndPoint EndPoint { get; }
+ }
+}

ADD
WalletWasabi/BitcoinCore/Endpointing/EndPointStrategyType.cs
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace WalletWasabi.BitcoinCore.Endpointing
+{
+ public enum EndPointStrategyType
+ {
+ Default,
+ Custom,
+ Random
+ }
+}

ADD
WalletWasabi/BitcoinCore/Endpointing/EndPointType.cs
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace WalletWasabi.BitcoinCore.Endpointing
+{
+ public enum EndPointType
+ {
+ P2p,
+ Rpc
+ }
+}

ADD
WalletWasabi/BitcoinCore/Endpointing/PortFinder.cs
+using NBitcoin;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Net;
+using System.Net.Sockets;
+using System.Text;
+using WalletWasabi.Logging;
+
+namespace WalletWasabi.BitcoinCore.Endpointing
+{
+ public static class PortFinder
+ {
+ public static int[] GetRandomPorts(int num)
+ {
+ int[] portArray = new int[num];
+ var i = 0;
+ while (i < portArray.Length)
+ {
+ var port = RandomUtils.GetUInt32() % 4000;
+ port += 10000;
+ if (portArray.Any(p => p == port))
+ {
+ continue;
+ }
+
+ try
+ {
+ var listener = new TcpListener(IPAddress.Loopback, (int)port);
+ listener.Start();
+ listener.Stop();
+ portArray[i] = (int)port;
+ i++;
+ }
+ catch (SocketException ex)
+ {
+ Logger.LogTrace(ex);
+ }
+ }
+
+ return portArray;
+ }
+ }
+}

Improve local node configurability.