Commit 1cf17278 authored by Jeremy Bokobza's avatar Jeremy Bokobza

Added NTumblitCommon project

parent 602c13e6
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace NTumbleBit.Common
{
public class CommandLineParser
{
private readonly string[] _Args;
public string[] Args
{
get
{
return _Args;
}
}
public CommandLineParser(string[] args)
{
if(args == null)
throw new ArgumentNullException(nameof(args));
_Args = args;
}
public bool GetBool(string key)
{
return Args.Contains(key);
}
public string GetRequiredString(string key)
{
var start = key + "=";
foreach(var arg in Args)
{
if(arg.StartsWith(start, StringComparison.Ordinal))
{
return arg.Substring(start.Length);
}
}
throw new ConfigException("Required command line " + key + " not found");
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace NTumbleBit.Common
{
public class ConfigException : Exception
{
public ConfigException():base("")
{
}
public ConfigException(string message) : base(message)
{
}
}
}
using NBitcoin;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using System.IO;
using NTumbleBit.Common.Logging;
namespace NTumbleBit.Common
{
public class DefaultDataDirectory
{
public static string GetDefaultDirectory(string appName, Network network)
{
string directory = null;
var home = Environment.GetEnvironmentVariable("HOME");
if(!string.IsNullOrEmpty(home))
{
Logs.Configuration.LogInformation("Using HOME environment variable for initializing application data");
directory = home;
directory = Path.Combine(directory, "." + appName.ToLowerInvariant());
}
else
{
var localAppData = Environment.GetEnvironmentVariable("APPDATA");
if(!string.IsNullOrEmpty(localAppData))
{
Logs.Configuration.LogInformation("Using APPDATA environment variable for initializing application data");
directory = localAppData;
directory = Path.Combine(directory, appName);
}
else
{
throw new DirectoryNotFoundException("Could not find suitable datadir");
}
}
if(!Directory.Exists(directory))
{
Directory.CreateDirectory(directory);
}
directory = Path.Combine(directory, network.Name);
if(!Directory.Exists(directory))
{
Logs.Configuration.LogInformation("Creating data directory");
Directory.CreateDirectory(directory);
}
return directory;
}
}
}
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace NTumbleBit.Common.Logging
{
public class FuncLoggerFactory : ILoggerFactory
{
private Func<string, ILogger> createLogger;
public FuncLoggerFactory(Func<string, ILogger> createLogger)
{
this.createLogger = createLogger;
}
public void AddProvider(ILoggerProvider provider)
{
}
public ILogger CreateLogger(string categoryName)
{
return createLogger(categoryName);
}
public void Dispose()
{
}
}
}
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace NTumbleBit.Common.Logging
{
public class Logs
{
static Logs()
{
Configure(new FuncLoggerFactory(n => new NullLogger()));
}
public static void Configure(ILoggerFactory factory)
{
Configuration = factory.CreateLogger("Configuration");
Main = factory.CreateLogger("Main");
Server = factory.CreateLogger("Server");
}
public static ILogger Main
{
get; set;
}
public static ILogger Server
{
get; set;
}
public static ILogger Configuration
{
get; set;
}
public const int ColumnLength = 16;
}
}
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace NTumbleBit.Common.Logging
{
public class NullLogger : ILogger
{
private class Dispo : IDisposable
{
public void Dispose()
{
}
}
public IDisposable BeginScope<TState>(TState state)
{
return new Dispo();
}
public bool IsEnabled(LogLevel logLevel)
{
return true;
}
public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
{
}
}
}
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard1.6</TargetFramework>
<AssemblyName>NTumbleBit.Common</AssemblyName>
<PackageId>NTumbleBit.Common</PackageId>
<NetStandardImplicitPackageVersion>1.6.1</NetStandardImplicitPackageVersion>
<PackageTargetFallback>$(PackageTargetFallback);dnxcore50</PackageTargetFallback>
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute>
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\NTumbleBit\NTumbleBit.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="1.1.2" />
</ItemGroup>
</Project>
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("NTumbleBit.Common")]
[assembly: AssemblyTrademark("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("53e1d0f4-a0e3-4fc6-820a-b8f0e923a0d5")]
using NBitcoin;
using NBitcoin.RPC;
using NTumbleBit.Common.Logging;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json.Linq;
namespace NTumbleBit.Common
{
public class RPCArgs
{
public Uri Url
{
get; set;
}
public string User
{
get; set;
}
public string Password
{
get; set;
}
public string CookieFile
{
get; set;
}
public RPCClient ConfigureRPCClient(Network network)
{
RPCClient rpcClient = null;
var url = Url;
var usr = User;
var pass = Password;
if(url != null && usr != null && pass != null)
rpcClient = new RPCClient(new System.Net.NetworkCredential(usr, pass), url, network);
if(rpcClient == null)
{
if(url != null && CookieFile != null)
{
try
{
rpcClient = new RPCClient(File.ReadAllText(CookieFile), url, network);
}
catch(IOException)
{
Logs.Configuration.LogWarning("RPC Cookie file not found at " + CookieFile);
}
}
if(rpcClient == null)
{
try
{
rpcClient = new RPCClient(network);
}
catch { }
if(rpcClient == null)
{
Logs.Configuration.LogError("RPC connection settings not configured");
throw new ConfigException();
}
}
}
Logs.Configuration.LogInformation("Testing RPC connection to " + rpcClient.Address.AbsoluteUri);
try
{
rpcClient.SendCommand("whatever");
}
catch(RPCException ex)
{
if(ex.RPCCode != RPCErrorCode.RPC_METHOD_NOT_FOUND)
{
Logs.Configuration.LogError("Received unexpected response from RPC server: " + ex.Message);
throw new ConfigException();
}
}
catch(Exception ex)
{
Logs.Configuration.LogError("Error connecting to RPC " + ex.Message);
throw new ConfigException();
}
Logs.Configuration.LogInformation("RPC connection successfull");
if(rpcClient.GetBlockHash(0) != network.GenesisHash)
{
Logs.Configuration.LogError("The RPC server is not using the chain " + network.Name);
throw new ConfigException();
}
var getInfo = rpcClient.SendCommand(RPCOperations.getinfo);
var version = ((JObject)getInfo.Result)["version"].Value<int>();
if(version < MIN_CORE_VERSION)
{
Logs.Configuration.LogError($"The minimum Bitcoin Core version required is {MIN_CORE_VERSION} (detected: {version})");
throw new ConfigException();
}
Logs.Configuration.LogInformation($"Bitcoin Core version detected: {version}");
return rpcClient;
}
const int MIN_CORE_VERSION = 130100;
public static RPCClient ConfigureRPCClient(TextFileConfiguration confArgs, Network network, string prefix= null)
{
RPCArgs args = Parse(confArgs, network, prefix);
return args.ConfigureRPCClient(network);
}
public static RPCArgs Parse(TextFileConfiguration confArgs, Network network, string prefix = null)
{
prefix = prefix ?? "";
if(prefix != "")
{
if(!prefix.EndsWith("."))
prefix += ".";
}
try
{
var url = confArgs.GetOrDefault<string>(prefix + "rpc.url", network == null ? null : "http://localhost:" + network.RPCPort + "/");
return new RPCArgs()
{
User = confArgs.GetOrDefault<string>(prefix + "rpc.user", null),
Password = confArgs.GetOrDefault<string>(prefix + "rpc.password", null),
CookieFile = confArgs.GetOrDefault<string>(prefix + "rpc.cookiefile", null),
Url = url == null ? null : new Uri(url)
};
}
catch(FormatException)
{
throw new ConfigException("rpc.url is not an url");
}
}
}
}
using NBitcoin;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace NTumbleBit.Common
{
public class ConfigurationException : Exception
{
public ConfigurationException(string message) : base(message)
{
}
}
public class TextFileConfiguration
{
private Dictionary<string, List<string>> _Args;
public TextFileConfiguration(string[] args)
{
_Args = new Dictionary<string, List<string>>();
string noValueParam = null;
Action flushNoValueParam = () =>
{
if(noValueParam != null)
{
Add(noValueParam, "1", false);
noValueParam = null;
}
};
foreach(var arg in args)
{
bool isParamName = arg.StartsWith("-", StringComparison.Ordinal);
if(isParamName)
{
var splitted = arg.Split('=');
if(splitted.Length > 1)
{
var value = String.Join("=", splitted.Skip(1).ToArray());
flushNoValueParam();
Add(splitted[0], value, false);
}
else
{
flushNoValueParam();
noValueParam = splitted[0];
}
}
else
{
if(noValueParam != null)
{
Add(noValueParam, arg, false);
noValueParam = null;
}
}
}
flushNoValueParam();
}
private void Add(string key, string value, bool sourcePriority)
{
key = NormalizeKey(key);
List<string> list;
if(!_Args.TryGetValue(key, out list))
{
list = new List<string>();
_Args.Add(key, list);
}
if(sourcePriority)
list.Insert(0, value);
else
list.Add(value);
}
private static string NormalizeKey(string key)
{
key = key.ToLowerInvariant();
while(key.Length > 0 && key[0] == '-')
{
key = key.Substring(1);
}
key = key.Replace(".", "");
return key;
}
public void MergeInto(TextFileConfiguration destination, bool sourcePriority)
{
foreach(var kv in _Args)
{
foreach(var v in kv.Value)
destination.Add(kv.Key, v, sourcePriority);
}
}
public TextFileConfiguration(Dictionary<string, List<string>> args)
{
_Args = args;
}
public static TextFileConfiguration Parse(string data)
{
Dictionary<string, List<string>> result = new Dictionary<string, List<string>>();
var lines = data.Split(new[] { "\r\n", "\n" }, StringSplitOptions.RemoveEmptyEntries);
int lineCount = -1;
foreach(var l in lines)
{
lineCount++;
var line = l.Trim();
if(line.StartsWith("#", StringComparison.Ordinal))
continue;
var split = line.Split('=');
if(split.Length == 0)
continue;
if(split.Length == 1)
throw new FormatException("Line " + lineCount + ": No value are set");
var key = split[0];
key = NormalizeKey(key);
List<string> values;
if(!result.TryGetValue(key, out values))
{
values = new List<string>();
result.Add(key, values);
}
var value = String.Join("=", split.Skip(1).ToArray());
values.Add(value);
}
return new TextFileConfiguration(result);
}
public bool Contains(string key)
{
List<string> values;
return _Args.TryGetValue(key, out values);
}
public string[] GetAll(string key)
{
List<string> values;
if(!_Args.TryGetValue(key, out values))
return new string[0];
return values.ToArray();
}
private List<Tuple<string, string>> _Aliases = new List<Tuple<string, string>>();
public void AddAlias(string from, string to)
{
from = NormalizeKey(from);
to = NormalizeKey(to);
_Aliases.Add(Tuple.Create(from, to));
}
public T GetOrDefault<T>(string key, T defaultValue)
{
key = NormalizeKey(key);
var aliases = _Aliases
.Where(a => a.Item1 == key || a.Item2 == key)
.Select(a => a.Item1 == key ? a.Item2 : a.Item1)
.ToList();
aliases.Insert(0, key);
foreach(var alias in aliases)
{
List<string> values;
if(!_Args.TryGetValue(alias, out values))
continue;
if(values.Count == 0)
continue;
try
{
return ConvertValue<T>(values[0]);
}
catch(FormatException) { throw new ConfigurationException("Key " + key + " should be of type " + typeof(T).Name); }
}
return defaultValue;
}
private T ConvertValue<T>(string str)
{
if(typeof(T) == typeof(bool))
{
var trueValues = new[] { "1", "true" };
var falseValues = new[] { "0", "false" };
if(trueValues.Contains(str, StringComparer.OrdinalIgnoreCase))
return (T)(object)true;
if(falseValues.Contains(str, StringComparer.OrdinalIgnoreCase))
return (T)(object)false;
throw new FormatException();
}
else if(typeof(T) == typeof(Uri))
return (T)(object)new Uri(str, UriKind.Absolute);
else if(typeof(T) == typeof(string))
return (T)(object)str;
else if(typeof(T) == typeof(int))
{
return (T)(object)int.Parse(str, CultureInfo.InvariantCulture);
}
else
{
throw new NotSupportedException("Configuration value does not support time " + typeof(T).Name);
}
}
//public static String CreateDefaultConfiguration(Network network)
//{
// StringBuilder builder = new StringBuilder();
// builder.AppendLine("#rpc.url=http://localhost:" + network.RPCPort + "/");
// builder.AppendLine("#rpc.user=bitcoinuser");
// builder.AppendLine("#rpc.password=bitcoinpassword");
// builder.AppendLine("#rpc.cookiefile=yourbitcoinfolder/.cookie");
// return builder.ToString();
//}
//public static String CreateClientDefaultConfiguration(Network network)
//{
// StringBuilder builder = new StringBuilder();
// builder.AppendLine("#rpc.url=http://localhost:" + network.RPCPort + "/");
// builder.AppendLine("#rpc.user=bitcoinuser");
// builder.AppendLine("#rpc.password=bitcoinpassword");
// builder.AppendLine("#rpc.cookiefile=yourbitcoinfolder/.cookie");
// return builder.ToString();
//}
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment