private static bool TryParseNewFormat(string str, out LightningConnectionString connectionString, out string error) { connectionString = null; error = null; var parts = str.Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries); Dictionary <string, string> keyValues = new Dictionary <string, string>(); foreach (var part in parts.Select(p => p.Trim())) { var idx = part.IndexOf('='); if (idx == -1) { error = "The format of the connectionString should a list of key=value delimited by semicolon"; return(false); } var key = part.Substring(0, idx).Trim().ToLowerInvariant(); var value = part.Substring(idx + 1).Trim(); if (keyValues.ContainsKey(key)) { error = $"Duplicate key {key}"; return(false); } keyValues.Add(key, value); } var possibleTypes = String.Join(", ", typeMapping.Select(k => k.Key).ToArray()); LightningConnectionString result = new LightningConnectionString(); var type = Take(keyValues, "type"); if (type == null) { error = $"The key 'type' is mandatory, possible values are {possibleTypes}"; return(false); } if (!typeMapping.TryGetValue(type.ToLowerInvariant(), out var connectionType)) { error = $"The key 'type' is invalid, possible values are {possibleTypes}"; return(false); } result.ConnectionType = connectionType; switch (connectionType) { case LightningConnectionType.Charge: { var server = Take(keyValues, "server"); if (server == null) { error = $"The key 'server' is mandatory for charge connection strings"; return(false); } if (!Uri.TryCreate(server, UriKind.Absolute, out var uri) || (uri.Scheme != "http" && uri.Scheme != "https")) { error = $"The key 'server' should be an URI starting by http:// or https://"; return(false); } parts = uri.UserInfo.Split(':'); if (!string.IsNullOrEmpty(uri.UserInfo) && parts.Length == 2) { result.Username = parts[0]; result.Password = parts[1]; } else { var apiToken = Take(keyValues, "api-token"); if (apiToken == null) { error = "The key 'api-token' is not found"; return(false); } result.Username = "******"; result.Password = apiToken; } result.BaseUri = new UriBuilder(uri) { UserName = "", Password = "" }.Uri; } break; case LightningConnectionType.CLightning: { var server = Take(keyValues, "server"); if (server == null) { error = $"The key 'server' is mandatory for charge connection strings"; return(false); } if (server.StartsWith("//", StringComparison.OrdinalIgnoreCase)) { server = "unix:" + str; } else if (server.StartsWith("/", StringComparison.OrdinalIgnoreCase)) { server = "unix:/" + str; } if (!Uri.TryCreate(server, UriKind.Absolute, out var uri) || (uri.Scheme != "tcp" && uri.Scheme != "unix")) { error = $"The key 'server' should be an URI starting by tcp:// or unix:// or a path to the 'lightning-rpc' unix socket"; return(false); } result.BaseUri = uri; } break; case LightningConnectionType.LndREST: case LightningConnectionType.LndGRPC: { var server = Take(keyValues, "server"); if (server == null) { error = $"The key 'server' is mandatory for lnd connection strings"; return(false); } if (!Uri.TryCreate(server, UriKind.Absolute, out var uri) || (uri.Scheme != "http" && uri.Scheme != "https")) { error = $"The key 'server' should be an URI starting by http:// or https://"; return(false); } parts = uri.UserInfo.Split(':'); if (!string.IsNullOrEmpty(uri.UserInfo) && parts.Length == 2) { result.Username = parts[0]; result.Password = parts[1]; } result.BaseUri = new UriBuilder(uri) { UserName = "", Password = "" }.Uri; var macaroon = Take(keyValues, "macaroon"); if (macaroon != null) { try { result.Macaroon = Encoder.DecodeData(macaroon); } catch { error = $"The key 'macaroon' format should be in hex"; return(false); } } var macaroonFilePath = Take(keyValues, "macaroonfilepath"); if (macaroonFilePath != null) { if (macaroon != null) { error = $"The key 'macaroon' is already specified"; return(false); } if (!macaroonFilePath.EndsWith(".macaroon", StringComparison.OrdinalIgnoreCase)) { error = $"The key 'macaroonfilepath' should point to a .macaroon file"; return(false); } result.MacaroonFilePath = macaroonFilePath; } string securitySet = null; var certthumbprint = Take(keyValues, "certthumbprint"); if (certthumbprint != null) { try { var bytes = Encoders.Hex.DecodeData(certthumbprint.Replace(":", string.Empty)); if (bytes.Length != 32) { error = $"The key 'certthumbprint' has invalid length: it should be the SHA256 of the PEM format of the certificate (32 bytes)"; return(false); } result.CertificateThumbprint = bytes; } catch { error = $"The key 'certthumbprint' has invalid format: it should be the SHA256 of the PEM format of the certificate"; return(false); } securitySet = "certthumbprint"; } var allowinsecureStr = Take(keyValues, "allowinsecure"); if (allowinsecureStr != null) { var allowedValues = new[] { "true", "false" }; if (!allowedValues.Any(v => v.Equals(allowinsecureStr, StringComparison.OrdinalIgnoreCase))) { error = $"The key 'allowinsecure' should be true or false"; return(false); } bool allowInsecure = allowinsecureStr.Equals("true", StringComparison.OrdinalIgnoreCase); if (securitySet != null && allowInsecure) { error = $"The key 'allowinsecure' conflict with '{securitySet}'"; return(false); } result.AllowInsecure = allowInsecure; } if (!result.AllowInsecure && result.BaseUri.Scheme == "http") { error = $"The key 'allowinsecure' is false, but server's Uri is not using https"; return(false); } } break; default: throw new NotSupportedException(connectionType.ToString()); } if (keyValues.Count != 0) { error = $"Unknown keys ({String.Join(", ", keyValues.Select(k => k.Key).ToArray())})"; return(false); } connectionString = result; return(true); }
public static ILightningClient CreateClient(LightningConnectionString connectionString, Network network) { return(new LightningClientFactory(network).Create(connectionString)); }
public static bool TryParse(string str, bool supportLegacy, out LightningConnectionString connectionString) { return(TryParse(str, supportLegacy, out connectionString, out var error)); }
public static bool TryParse(string str, out LightningConnectionString connectionString) { return(TryParse(str, false, out connectionString)); }
private static bool TryParseLegacy(string str, out LightningConnectionString connectionString, out string error) { if (str.StartsWith("/")) { str = "unix:" + str; } var result = new LightningConnectionString(); connectionString = null; error = null; Uri uri; if (!Uri.TryCreate(str, UriKind.Absolute, out uri)) { error = "Invalid URL"; return(false); } var supportedDomains = new string[] { "unix", "tcp", "http", "https" }; if (!supportedDomains.Contains(uri.Scheme)) { var protocols = String.Join(",", supportedDomains); error = $"The url support the following protocols {protocols}"; return(false); } if (uri.Scheme == "unix") { str = uri.AbsoluteUri.Substring("unix:".Length); while (str.Length >= 1 && str[0] == '/') { str = str.Substring(1); } uri = new Uri("unix://" + str, UriKind.Absolute); result.ConnectionType = LightningConnectionType.CLightning; } if (uri.Scheme == "tcp") { result.ConnectionType = LightningConnectionType.CLightning; } if (uri.Scheme == "http" || uri.Scheme == "https") { var parts = uri.UserInfo.Split(':'); if (string.IsNullOrEmpty(uri.UserInfo) || parts.Length != 2) { error = "The url is missing user and password"; return(false); } result.Username = parts[0]; result.Password = parts[1]; result.ConnectionType = LightningConnectionType.Charge; } else if (!string.IsNullOrEmpty(uri.UserInfo)) { error = "The url should not have user information"; return(false); } result.BaseUri = new UriBuilder(uri) { UserName = "", Password = "" }.Uri; result.IsLegacy = true; connectionString = result; return(true); }
public LightningConnectionString Clone() { LightningConnectionString.TryParse(this.ToString(), false, out var result); return(result); }
public ILightningClient Create(LightningConnectionString connectionString) { if (connectionString == null) { throw new ArgumentNullException(nameof(connectionString)); } if (connectionString.ConnectionType == LightningConnectionType.Charge) { if (connectionString.CookieFilePath != null) { return(new ChargeClient(connectionString.BaseUri, connectionString.CookieFilePath, Network, HttpClient)); } else { return(new ChargeClient(connectionString.ToUri(true), Network, HttpClient)); } } else if (connectionString.ConnectionType == LightningConnectionType.CLightning) { return(new CLightningClient(connectionString.ToUri(false), Network)); } else if (connectionString.ConnectionType == LightningConnectionType.LndREST) { return(new LndClient(new LndSwaggerClient(new LndRestSettings(connectionString.BaseUri) { Macaroon = connectionString.Macaroon, MacaroonFilePath = connectionString.MacaroonFilePath, CertificateThumbprint = connectionString.CertificateThumbprint, AllowInsecure = connectionString.AllowInsecure, }, HttpClient), Network)); } else if (connectionString.ConnectionType == LightningConnectionType.Eclair) { var rpcClient = !string.IsNullOrEmpty(connectionString.BitcoinHost) && !string.IsNullOrEmpty(connectionString.BitcoinAuth) ? new RPCClient(connectionString.BitcoinAuth, connectionString.BitcoinHost, Network) { HttpClient = HttpClient } : null; return(new EclairLightningClient(connectionString.BaseUri, connectionString.Password, Network, rpcClient , HttpClient)); } else if (connectionString.ConnectionType == LightningConnectionType.Ptarmigan) { var rpcClient = !string.IsNullOrEmpty(connectionString.BitcoinHost) && !string.IsNullOrEmpty(connectionString.BitcoinAuth) ? new RPCClient(connectionString.BitcoinAuth, connectionString.BitcoinHost, Network) { HttpClient = HttpClient } : null; return(new PtarmiganLightningClient(connectionString.BaseUri, connectionString.ApiToken, Network, rpcClient, HttpClient)); } else { throw new NotSupportedException( $"Unsupported connection string for lightning server ({connectionString.ConnectionType})"); } }