Make sure we pick up existing Bitcoin Core running.
MODIFY
WalletWasabi.Gui/Global.cs
WalletWasabi.Gui/Global.cs
@@ -650,7 +650,6 @@ namespace WalletWasabi.Gui
if (Config.StopLocalBitcoinCoreOnShutdown && BitcoinCoreNode != null)
{
await BitcoinCoreNode.TryStopAsync().ConfigureAwait(false);
- Logger.LogInfo($"{nameof(BitcoinCoreNode)} is stopped.");
}
if (TorManager != null)

MODIFY
WalletWasabi/BitcoinCore/CoreNode.cs
WalletWasabi/BitcoinCore/CoreNode.cs
@@ -15,6 +15,7 @@ using System.Threading;
using System.Threading.Tasks;
using WalletWasabi.BitcoinCore.Configuration;
using WalletWasabi.BitcoinCore.Endpointing;
+using WalletWasabi.BitcoinCore.Processes;
using WalletWasabi.Helpers;
using WalletWasabi.Logging;
@@ -25,7 +26,7 @@ namespace WalletWasabi.BitcoinCore
public EndPoint P2pEndPoint { get; private set; }
public EndPoint RpcEndPoint { get; private set; }
public RPCClient RpcClient { get; private set; }
- private BitcoindProcessBridge Bridge { get; set; }
+ private BitcoindRpcProcessBridge Bridge { get; set; }
public string DataDir { get; private set; }
public Network Network { get; private set; }
@@ -140,27 +141,16 @@ namespace WalletWasabi.BitcoinCore
}
var configFileName = Path.GetFileName(configPath);
- coreNode.Bridge = new BitcoindProcessBridge();
- using var process = coreNode.Bridge.Start($"{NetworkTranslator.GetCommandLineArguments(coreNode.Network)} -conf={configFileName} -datadir={coreNode.DataDir} -printtoconsole=0", false);
-
- var pidFile = new PidFile(coreNode.DataDir, coreNode.Network);
- await pidFile.SerializeAsync(process.Id).ConfigureAwait(false);
-
- while (true)
+ // If it isn't already running, then we run it.
+ if (await coreNode.RpcClient.TestAsync().ConfigureAwait(false) is null)
{
- try
- {
- await coreNode.RpcClient.GetBlockchainInfoAsync().ConfigureAwait(false);
- break;
+ Logger.LogInfo("Bitcoin Core is already running.");
}
- catch (Exception ex)
+ else
{
- Logger.LogTrace(ex);
- if (process is null || process.HasExited)
- {
- throw ex;
- }
- }
+ coreNode.Bridge = new BitcoindRpcProcessBridge(coreNode.RpcClient, coreNode.DataDir, printToConsole: false);
+ await coreNode.Bridge.StartAsync().ConfigureAwait(false);
+ Logger.LogInfo("Started Bitcoin Core.");
}
return coreNode;
@@ -199,39 +189,30 @@ namespace WalletWasabi.BitcoinCore
public async Task<bool> TryStopAsync()
{
- var pidFile = new PidFile(DataDir, Network);
- try
- {
- var reasonableCoreShutdownTimeout = TimeSpan.FromSeconds(21);
-
- using CancellationTokenSource cts = new CancellationTokenSource(reasonableCoreShutdownTimeout);
- var pid = await pidFile.TryReadAsync().ConfigureAwait(false);
-
- if (pid.HasValue)
+ Exception exThrown = null;
+ if (Bridge != null)
{
- using Process process = Process.GetProcessById(pid.Value);
try
{
- await RpcClient.StopAsync().ConfigureAwait(false);
+ await Bridge.StopAsync().ConfigureAwait(false);
+ Logger.LogInfo("Stopped.");
+ return true;
}
catch (Exception ex)
{
- process.Kill();
- Logger.LogDebug(ex);
+ exThrown = ex;
}
- await process.WaitForExitAsync(cts.Token).ConfigureAwait(false);
- return true;
}
- }
- catch (Exception ex)
+
+ Logger.LogInfo("Did not stop Bitcoin Core. Reason:");
+ if (exThrown is null)
{
- Logger.LogWarning(ex);
+ Logger.LogInfo("Bitcoin Core was started externally.");
}
- finally
+ else
{
- pidFile.TryDelete();
+ Logger.LogWarning(exThrown);
}
-
return false;
}
}

RENAME
WalletWasabi/BitcoinCore/BitcoindException.cs
WalletWasabi/BitcoinCore/Processes/BitcoindException.cs
@@ -2,7 +2,7 @@ using System;
using System.Collections.Generic;
using System.Text;
-namespace WalletWasabi.BitcoinCore
+namespace WalletWasabi.BitcoinCore.Processes
{
public class BitcoindException : Exception
{

RENAME
WalletWasabi/BitcoinCore/BitcoindProcessBridge.cs
WalletWasabi/BitcoinCore/Processes/BitcoindProcessBridge.cs
using System;
using System.Collections.Generic;
-using System.Diagnostics;
-using System.IO;
-using System.Runtime.InteropServices;
using System.Text;
-using System.Threading;
-using System.Threading.Tasks;
-using WalletWasabi.Helpers;
-using WalletWasabi.Interfaces;
-using WalletWasabi.Logging;
using WalletWasabi.Microservices;
-namespace WalletWasabi.BitcoinCore
+namespace WalletWasabi.BitcoinCore.Processes
{
public class BitcoindProcessBridge : ProcessBridge
{

ADD
WalletWasabi/BitcoinCore/Processes/BitcoindRpcProcessBridge.cs
+using NBitcoin;
+using NBitcoin.RPC;
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.IO;
+using System.Runtime.InteropServices;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+using WalletWasabi.BitcoinCore.Configuration;
+using WalletWasabi.Helpers;
+using WalletWasabi.Interfaces;
+using WalletWasabi.Logging;
+using WalletWasabi.Microservices;
+
+namespace WalletWasabi.BitcoinCore.Processes
+{
+ public class BitcoindRpcProcessBridge : BitcoindProcessBridge
+ {
+ public Network Network { get; }
+ public RPCClient RpcClient { get; set; }
+ public string DataDir { get; }
+ public bool PrintToConsole { get; }
+ public PidFile PidFile { get; }
+
+ public BitcoindRpcProcessBridge(RPCClient rpc, string dataDir, bool printToConsole) : base()
+ {
+ RpcClient = Guard.NotNull(nameof(rpc), rpc);
+ Network = RpcClient.Network;
+ DataDir = Guard.NotNullOrEmptyOrWhitespace(nameof(dataDir), dataDir);
+ PrintToConsole = printToConsole;
+ PidFile = new PidFile(DataDir, Network);
+ }
+
+ public async Task StartAsync()
+ {
+ var ptcv = PrintToConsole ? 1 : 0;
+ using var process = Start($"{NetworkTranslator.GetCommandLineArguments(Network)} -datadir={DataDir} -printtoconsole={ptcv}", false);
+
+ await PidFile.SerializeAsync(process.Id).ConfigureAwait(false);
+
+ while (true)
+ {
+ var ex = await RpcClient.TestAsync().ConfigureAwait(false);
+ if (ex is null)
+ {
+ break;
+ }
+
+ if (process is null || process.HasExited)
+ {
+ throw ex;
+ }
+ }
+ }
+
+ public async Task StopAsync()
+ {
+ try
+ {
+ var reasonableCoreShutdownTimeout = TimeSpan.FromSeconds(21);
+
+ using CancellationTokenSource cts = new CancellationTokenSource(reasonableCoreShutdownTimeout);
+ var pid = await PidFile.TryReadAsync().ConfigureAwait(false);
+
+ if (pid.HasValue)
+ {
+ using Process process = Process.GetProcessById(pid.Value);
+ try
+ {
+ await RpcClient.StopAsync().ConfigureAwait(false);
+ }
+ catch (Exception ex)
+ {
+ process.Kill();
+ Logger.LogDebug(ex);
+ }
+ await process.WaitForExitAsync(cts.Token).ConfigureAwait(false);
+ }
+ }
+ finally
+ {
+ PidFile.TryDelete();
+ }
+ }
+ }
+}

RENAME
WalletWasabi/BitcoinCore/Configuration/PidFile.cs
WalletWasabi/BitcoinCore/Processes/PidFile.cs
@@ -4,10 +4,11 @@ using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Threading.Tasks;
+using WalletWasabi.BitcoinCore.Configuration;
using WalletWasabi.Helpers;
using WalletWasabi.Logging;
-namespace WalletWasabi.BitcoinCore.Configuration
+namespace WalletWasabi.BitcoinCore.Processes
{
public class PidFile
{

MODIFY
WalletWasabi/Extensions/NBitcoinExtensions.cs
WalletWasabi/Extensions/NBitcoinExtensions.cs
@@ -28,14 +28,14 @@ namespace NBitcoin
using var listener = node.CreateListener();
var getdata = new GetDataPayload(new InventoryVector(node.AddSupportedOptions(InventoryType.MSG_BLOCK), hash));
- await node.SendMessageAsync(getdata);
+ await node.SendMessageAsync(getdata).ConfigureAwait(false);
cancellationToken.ThrowIfCancellationRequested();
// Bitcoin Core processes the messages sequentially and does not send a NOTFOUND message if the remote node is pruned and the data not available.
// A good way to get any feedback about whether the node knows the block or not is to send a ping request.
// If block is not known by the remote node, the pong will be sent immediately, else it will be sent after the block download.
ulong pingNonce = RandomUtils.GetUInt64();
- await node.SendMessageAsync(new PingPayload() { Nonce = pingNonce });
+ await node.SendMessageAsync(new PingPayload() { Nonce = pingNonce }).ConfigureAwait(false);
while (true)
{
cancellationToken.ThrowIfCancellationRequested();
@@ -244,9 +244,21 @@ namespace NBitcoin
return Encoders.Base58Check.EncodeData(version.Concat(data).ToArray());
}
- public static async Task StopAsync(this RPCClient rpc)
+ /// <summary>
+ /// If null is returned, no exception is thrown, so the test was successful.
+ /// </summary>
+ public static async Task<Exception> TestAsync(this RPCClient rpc)
{
- await rpc.SendCommandAsync("stop");
+ try
+ {
+ await rpc.GetBlockchainInfoAsync().ConfigureAwait(false);
+ }
+ catch (Exception ex)
+ {
+ return ex;
+ }
+
+ return null;
}
public static SmartTransaction ExtractSmartTransaction(this PSBT psbt)

Make sure we pick up existing Bitcoin Core running.