<commit_before>﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using NBitcoin.RPC;
using System.IO;
using NTumbleBit.Logging;
using NTumbleBit.Services;
using NTumbleBit.ClassicTumbler;
using NBitcoin;
using NTumbleBit.Configuration;
using NTumbleBit.ClassicTumbler.Client;
using NTumbleBit.ClassicTumbler.CLI;
using System.Text;
using System.Net;
using System.Threading;
using NTumbleBit.Tor;

namespace NTumbleBit.ClassicTumbler.Server
{
	public class TumblerRuntime : IDisposable
	{
		public static TumblerRuntime FromConfiguration(TumblerConfiguration conf, ClientInteraction interaction)
		{
			return FromConfigurationAsync(conf, interaction).GetAwaiter().GetResult();
		}
		public static async Task<TumblerRuntime> FromConfigurationAsync(TumblerConfiguration conf, ClientInteraction interaction)
		{
			if(conf == null)
				throw new ArgumentNullException("conf");
			TumblerRuntime runtime = new TumblerRuntime();
			runtime.Cooperative = conf.Cooperative;
			runtime.ClassicTumblerParameters = Serializer.Clone(conf.ClassicTumblerParameters);
			runtime.Network = conf.Network;
			RPCClient rpcClient = null;
			try
			{
				rpcClient = conf.RPC.ConfigureRPCClient(conf.Network);
			}
			catch
			{
				throw new ConfigException("Please, fix rpc settings in " + conf.ConfigurationFile);
			}

			bool torConfigured = false;			
			if(conf.TorSettings != null)
			{
				Exception error = null;
				try
				{
					await conf.TorSettings.SetupAsync(interaction).ConfigureAwait(false);
					Logs.Configuration.LogInformation("Successfully authenticated to Tor");
					var torRSA = Path.Combine(conf.DataDir, "Tor.rsa");


					string privateKey = null;
					if(File.Exists(torRSA))
						privateKey = File.ReadAllText(torRSA, Encoding.UTF8);

					IPEndPoint routable = GetLocalEndpoint(conf);
					runtime.TorConnection = conf.TorSettings.CreateTorClient2();
					runtime._Resources.Add(runtime.TorConnection);

					await runtime.TorConnection.ConnectAsync().ConfigureAwait(false);
					await runtime.TorConnection.AuthenticateAsync().ConfigureAwait(false);
					var result = await runtime.TorConnection.RegisterHiddenServiceAsync(routable, conf.TorSettings.VirtualPort, privateKey).ConfigureAwait(false);
					if(privateKey == null)
					{
						File.WriteAllText(torRSA, result.PrivateKey, Encoding.UTF8);
						Logs.Configuration.LogWarning($"Tor RSA private key generated to {torRSA}");
					}

					runtime.TorUri = result.HiddenServiceUri;
					Logs.Configuration.LogInformation($"Tor configured on {runtime.TorUri.AbsoluteUri}");
					torConfigured = true;
				}
				catch(ConfigException ex)
				{
					error = ex;
				}
				catch(TorException ex)
				{
					error = ex;
				}
				catch(ClientInteractionException)
				{
				}
				if(error != null)
					Logs.Configuration.LogWarning("Error while configuring Tor hidden service: " + error.Message);
			}

			if(!torConfigured)
				Logs.Configuration.LogWarning("The tumbler is not configured as a Tor Hidden service");

			var rsaFile = Path.Combine(conf.DataDir, "Tumbler.pem");
			if(!File.Exists(rsaFile))
			{
				Logs.Configuration.LogWarning("RSA private key not found, please backup it. Creating...");
				runtime.TumblerKey = new RsaKey();
				File.WriteAllBytes(rsaFile, runtime.TumblerKey.ToBytes());
				Logs.Configuration.LogInformation("RSA key saved (" + rsaFile + ")");
			}
			else
			{
				Logs.Configuration.LogInformation("RSA private key found (" + rsaFile + ")");
				runtime.TumblerKey = new RsaKey(File.ReadAllBytes(rsaFile));
			}

			var voucherFile = Path.Combine(conf.DataDir, "Voucher.pem");
			if(!File.Exists(voucherFile))
			{
				Logs.Configuration.LogWarning("Creation of Voucher Key");
				runtime.VoucherKey = new RsaKey();
				File.WriteAllBytes(voucherFile, runtime.VoucherKey.ToBytes());
				Logs.Configuration.LogInformation("RSA key saved (" + voucherFile + ")");
			}
			else
			{
				Logs.Configuration.LogInformation("Voucher key found (" + voucherFile + ")");
				runtime.VoucherKey = new RsaKey(File.ReadAllBytes(voucherFile));
			}

			runtime.ClassicTumblerParameters.ServerKey = runtime.TumblerKey.PubKey;
			runtime.ClassicTumblerParameters.VoucherKey = runtime.VoucherKey.PubKey;
			runtime.ClassicTumblerParametersHash = runtime.ClassicTumblerParameters.GetHash();

			if(runtime.TorUri != null)
				runtime.TumblerUris.Add(runtime.CreateTumblerUri(runtime.TorUri));

			foreach(var url in conf.GetUrls())
				runtime.TumblerUris.Add(runtime.CreateTumblerUri(new Uri(url, UriKind.Absolute)));


			Logs.Configuration.LogInformation("");
			Logs.Configuration.LogInformation($"--------------------------------");
			var uris = String.Join(Environment.NewLine, runtime.TumblerUris.ToArray().Select(u => u.AbsoluteUri).ToArray());
			Logs.Configuration.LogInformation($"Shareable URIs of the running tumbler are:");
			foreach(var uri in runtime.TumblerUris)
			{
				Logs.Configuration.LogInformation(uri.AbsoluteUri);
			}
			Logs.Configuration.LogInformation($"--------------------------------");
			Logs.Configuration.LogInformation("");

			var dbreeze = new DBreezeRepository(Path.Combine(conf.DataDir, "db2"));
			runtime.Repository = dbreeze;
			runtime._Resources.Add(dbreeze);
			runtime.Tracker = new Tracker(dbreeze, runtime.Network);
			runtime.Services = ExternalServices.CreateFromRPCClient(rpcClient, dbreeze, runtime.Tracker);
			return runtime;
		}
<commit_msg>Cleanup TumblerRuntime if configure failed<commit_after>﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using NBitcoin.RPC;
using System.IO;
using NTumbleBit.Logging;
using NTumbleBit.Services;
using NTumbleBit.ClassicTumbler;
using NBitcoin;
using NTumbleBit.Configuration;
using NTumbleBit.ClassicTumbler.Client;
using NTumbleBit.ClassicTumbler.CLI;
using System.Text;
using System.Net;
using System.Threading;
using NTumbleBit.Tor;

namespace NTumbleBit.ClassicTumbler.Server
{
	public class TumblerRuntime : IDisposable
	{
		public static TumblerRuntime FromConfiguration(TumblerConfiguration conf, ClientInteraction interaction)
		{
			return FromConfigurationAsync(conf, interaction).GetAwaiter().GetResult();
		}
		public static async Task<TumblerRuntime> FromConfigurationAsync(TumblerConfiguration conf, ClientInteraction interaction)
		{
			if(conf == null)
				throw new ArgumentNullException("conf");
			TumblerRuntime runtime = new TumblerRuntime();
			await runtime.ConfigureAsync(conf, interaction).ConfigureAwait(false);
			return runtime;
		}
		public async Task ConfigureAsync(TumblerConfiguration conf, ClientInteraction interaction)
		{
			try
			{
				await ConfigureAsyncCore(conf, interaction).ConfigureAwait(false);
			}
			catch
			{
				Dispose();
				throw;
			}
		}
		async Task ConfigureAsyncCore(TumblerConfiguration conf, ClientInteraction interaction)
		{
			Cooperative = conf.Cooperative;
			ClassicTumblerParameters = Serializer.Clone(conf.ClassicTumblerParameters);
			Network = conf.Network;
			RPCClient rpcClient = null;
			try
			{
				rpcClient = conf.RPC.ConfigureRPCClient(conf.Network);
			}
			catch
			{
				throw new ConfigException("Please, fix rpc settings in " + conf.ConfigurationFile);
			}

			bool torConfigured = false;			
			if(conf.TorSettings != null)
			{
				Exception error = null;
				try
				{
					await conf.TorSettings.SetupAsync(interaction).ConfigureAwait(false);
					Logs.Configuration.LogInformation("Successfully authenticated to Tor");
					var torRSA = Path.Combine(conf.DataDir, "Tor.rsa");


					string privateKey = null;
					if(File.Exists(torRSA))
						privateKey = File.ReadAllText(torRSA, Encoding.UTF8);

					IPEndPoint routable = GetLocalEndpoint(conf);
					TorConnection = conf.TorSettings.CreateTorClient2();
					_Resources.Add(TorConnection);

					await TorConnection.ConnectAsync().ConfigureAwait(false);
					await TorConnection.AuthenticateAsync().ConfigureAwait(false);
					var result = await TorConnection.RegisterHiddenServiceAsync(routable, conf.TorSettings.VirtualPort, privateKey).ConfigureAwait(false);
					if(privateKey == null)
					{
						File.WriteAllText(torRSA, result.PrivateKey, Encoding.UTF8);
						Logs.Configuration.LogWarning($"Tor RSA private key generated to {torRSA}");
					}

					TorUri = result.HiddenServiceUri;
					Logs.Configuration.LogInformation($"Tor configured on {TorUri.AbsoluteUri}");
					torConfigured = true;
				}
				catch(ConfigException ex)
				{
					error = ex;
				}
				catch(TorException ex)
				{
					error = ex;
				}
				catch(ClientInteractionException)
				{
				}
				if(error != null)
					Logs.Configuration.LogWarning("Error while configuring Tor hidden service: " + error.Message);
			}

			if(!torConfigured)
				Logs.Configuration.LogWarning("The tumbler is not configured as a Tor Hidden service");

			var rsaFile = Path.Combine(conf.DataDir, "Tumbler.pem");
			if(!File.Exists(rsaFile))
			{
				Logs.Configuration.LogWarning("RSA private key not found, please backup it. Creating...");
				TumblerKey = new RsaKey();
				File.WriteAllBytes(rsaFile, TumblerKey.ToBytes());
				Logs.Configuration.LogInformation("RSA key saved (" + rsaFile + ")");
			}
			else
			{
				Logs.Configuration.LogInformation("RSA private key found (" + rsaFile + ")");
				TumblerKey = new RsaKey(File.ReadAllBytes(rsaFile));
			}

			var voucherFile = Path.Combine(conf.DataDir, "Voucher.pem");
			if(!File.Exists(voucherFile))
			{
				Logs.Configuration.LogWarning("Creation of Voucher Key");
				VoucherKey = new RsaKey();
				File.WriteAllBytes(voucherFile, VoucherKey.ToBytes());
				Logs.Configuration.LogInformation("RSA key saved (" + voucherFile + ")");
			}
			else
			{
				Logs.Configuration.LogInformation("Voucher key found (" + voucherFile + ")");
				VoucherKey = new RsaKey(File.ReadAllBytes(voucherFile));
			}

			ClassicTumblerParameters.ServerKey = TumblerKey.PubKey;
			ClassicTumblerParameters.VoucherKey = VoucherKey.PubKey;
			ClassicTumblerParametersHash = ClassicTumblerParameters.GetHash();

			if(TorUri != null)
				TumblerUris.Add(CreateTumblerUri(TorUri));

			foreach(var url in conf.GetUrls())
				TumblerUris.Add(CreateTumblerUri(new Uri(url, UriKind.Absolute)));


			Logs.Configuration.LogInformation("");
			Logs.Configuration.LogInformation($"--------------------------------");
			var uris = String.Join(Environment.NewLine, TumblerUris.ToArray().Select(u => u.AbsoluteUri).ToArray());
			Logs.Configuration.LogInformation($"Shareable URIs of the running tumbler are:");
			foreach(var uri in TumblerUris)
			{
				Logs.Configuration.LogInformation(uri.AbsoluteUri);
			}
			Logs.Configuration.LogInformation($"--------------------------------");
			Logs.Configuration.LogInformation("");

			var dbreeze = new DBreezeRepository(Path.Combine(conf.DataDir, "db2"));
			Repository = dbreeze;
			_Resources.Add(dbreeze);
			Tracker = new Tracker(dbreeze, Network);
			Services = ExternalServices.CreateFromRPCClient(rpcClient, dbreeze, Tracker);
		}

{'fraction_non_alphanumeric': 0.12115748670332642, 'fraction_numerical': 0.0007211755160912287, 'mean_word_length': 4.133734382230449, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 7, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4733744', 'n_tokens_mistral': 4060, 'n_tokens_neox': 3347, 'n_words': 615}