public void LoadArgs(IConfiguration conf) { NetworkType = DefaultConfiguration.GetNetworkType(conf); var defaultSettings = BTCPayDefaultSettings.GetDefaultSettings(NetworkType); DataDir = conf.GetOrDefault <string>("datadir", defaultSettings.DefaultDataDirectory); Logs.Configuration.LogInformation("Network: " + NetworkType.ToString()); var supportedChains = conf.GetOrDefault <string>("chains", "btc") .Split(',', StringSplitOptions.RemoveEmptyEntries) .Select(t => t.ToUpperInvariant()); NetworkProvider = new BTCPayNetworkProvider(NetworkType).Filter(supportedChains.ToArray()); foreach (var chain in supportedChains) { if (NetworkProvider.GetNetwork(chain) == null) { throw new ConfigException($"Invalid chains \"{chain}\""); } } var validChains = new List <string>(); foreach (var net in NetworkProvider.GetAll()) { NBXplorerConnectionSetting setting = new NBXplorerConnectionSetting(); setting.CryptoCode = net.CryptoCode; setting.ExplorerUri = conf.GetOrDefault <Uri>($"{net.CryptoCode}.explorer.url", net.NBXplorerNetwork.DefaultSettings.DefaultUrl); setting.CookieFile = conf.GetOrDefault <string>($"{net.CryptoCode}.explorer.cookiefile", net.NBXplorerNetwork.DefaultSettings.DefaultCookieFile); NBXplorerConnectionSettings.Add(setting); var lightning = conf.GetOrDefault <string>($"{net.CryptoCode}.lightning", string.Empty); if (lightning.Length != 0) { if (!LightningConnectionString.TryParse(lightning, out var connectionString, out var error)) { throw new ConfigException($"Invalid setting {net.CryptoCode}.lightning, you need to pass either " + $"the absolute path to the unix socket of a running CLightning instance (eg. /root/.lightning/lightning-rpc), " + $"or the url to a charge server with crendetials (eg. https://apitoken@API_TOKEN_SECRET:charge.example.com/)"); } InternalLightningByCryptoCode.Add(net.CryptoCode, connectionString); } } Logs.Configuration.LogInformation("Supported chains: " + String.Join(',', supportedChains.ToArray())); PostgresConnectionString = conf.GetOrDefault <string>("postgres", null); BundleJsCss = conf.GetOrDefault <bool>("bundlejscss", true); ExternalUrl = conf.GetOrDefault <Uri>("externalurl", null); RootPath = conf.GetOrDefault <string>("rootpath", "/"); if (!RootPath.StartsWith("/", StringComparison.InvariantCultureIgnoreCase)) { RootPath = "/" + RootPath; } var old = conf.GetOrDefault <Uri>("internallightningnode", null); if (old != null) { throw new ConfigException($"internallightningnode should not be used anymore, use btclightning instead"); } }
public async Task <IActionResult> UpdateLightningNetworkPaymentMethod(string cryptoCode, [FromBody] LightningNetworkPaymentMethodData paymentMethodData) { var id = new PaymentMethodId(cryptoCode, PaymentTypes.LightningLike); if (!GetNetwork(cryptoCode, out var network)) { return(NotFound()); } var internalLightning = GetInternalLightningNode(network.CryptoCode); if (string.IsNullOrEmpty(paymentMethodData?.ConnectionString)) { ModelState.AddModelError(nameof(LightningNetworkPaymentMethodData.ConnectionString), "Missing connectionString"); } if (!ModelState.IsValid) { return(this.CreateValidationError(ModelState)); } PaymentMethodId paymentMethodId = new PaymentMethodId(network.CryptoCode, PaymentTypes.LightningLike); Payments.Lightning.LightningSupportedPaymentMethod paymentMethod = null; if (!string.IsNullOrEmpty(paymentMethodData.ConnectionString)) { if (!LightningConnectionString.TryParse(paymentMethodData.ConnectionString, false, out var connectionString, out var error)) { ModelState.AddModelError(nameof(paymentMethodData.ConnectionString), $"Invalid URL ({error})"); return(this.CreateValidationError(ModelState)); } if (connectionString.ConnectionType == LightningConnectionType.LndGRPC) { ModelState.AddModelError(nameof(paymentMethodData.ConnectionString), $"BTCPay does not support gRPC connections"); return(this.CreateValidationError(ModelState)); } bool isInternalNode = connectionString.IsInternalNode(internalLightning); if (connectionString.BaseUri.Scheme == "http") { if (!isInternalNode && !connectionString.AllowInsecure) { ModelState.AddModelError(nameof(paymentMethodData.ConnectionString), "The url must be HTTPS"); return(this.CreateValidationError(ModelState)); } } if (connectionString.MacaroonFilePath != null) { if (!CanUseInternalLightning()) { ModelState.AddModelError(nameof(paymentMethodData.ConnectionString), "You are not authorized to use macaroonfilepath"); return(this.CreateValidationError(ModelState)); } if (!System.IO.File.Exists(connectionString.MacaroonFilePath)) { ModelState.AddModelError(nameof(paymentMethodData.ConnectionString), "The macaroonfilepath file does not exist"); return(this.CreateValidationError(ModelState)); } if (!System.IO.Path.IsPathRooted(connectionString.MacaroonFilePath)) { ModelState.AddModelError(nameof(paymentMethodData.ConnectionString), "The macaroonfilepath should be fully rooted"); return(this.CreateValidationError(ModelState)); } } if (isInternalNode && !CanUseInternalLightning()) { ModelState.AddModelError(nameof(paymentMethodData.ConnectionString), "Unauthorized url"); return(this.CreateValidationError(ModelState)); } paymentMethod = new Payments.Lightning.LightningSupportedPaymentMethod() { CryptoCode = paymentMethodId.CryptoCode }; paymentMethod.SetLightningUrl(connectionString); } var store = Store; var storeBlob = store.GetStoreBlob(); store.SetSupportedPaymentMethod(id, paymentMethod); storeBlob.SetExcluded(id, !paymentMethodData.Enabled); store.SetStoreBlob(storeBlob); await _storeRepository.UpdateStore(store); return(Ok(GetExistingLightningLikePaymentMethod(cryptoCode, store))); }
public async Task <IActionResult> UpdateLightningNetworkPaymentMethod(string storeId, string cryptoCode, [FromBody] LightningNetworkPaymentMethodData paymentMethodData) { var paymentMethodId = new PaymentMethodId(cryptoCode, PaymentTypes.LightningLike); if (!GetNetwork(cryptoCode, out var network)) { return(NotFound()); } if (string.IsNullOrEmpty(paymentMethodData.ConnectionString)) { ModelState.AddModelError(nameof(LightningNetworkPaymentMethodData.ConnectionString), "Missing connectionString"); } if (!ModelState.IsValid) { return(this.CreateValidationError(ModelState)); } LightningSupportedPaymentMethod?paymentMethod = null; if (!string.IsNullOrEmpty(paymentMethodData !.ConnectionString)) { if (paymentMethodData.ConnectionString == LightningSupportedPaymentMethod.InternalNode) { if (!await CanUseInternalLightning()) { ModelState.AddModelError(nameof(paymentMethodData.ConnectionString), $"You are not authorized to use the internal lightning node"); return(this.CreateValidationError(ModelState)); } paymentMethod = new Payments.Lightning.LightningSupportedPaymentMethod() { CryptoCode = paymentMethodId.CryptoCode }; paymentMethod.SetInternalNode(); } else { if (!LightningConnectionString.TryParse(paymentMethodData.ConnectionString, false, out var connectionString, out var error)) { ModelState.AddModelError(nameof(paymentMethodData.ConnectionString), $"Invalid URL ({error})"); return(this.CreateValidationError(ModelState)); } if (connectionString.ConnectionType == LightningConnectionType.LndGRPC) { ModelState.AddModelError(nameof(paymentMethodData.ConnectionString), $"BTCPay does not support gRPC connections"); return(this.CreateValidationError(ModelState)); } if (!await CanManageServer() && !connectionString.IsSafe()) { ModelState.AddModelError(nameof(paymentMethodData.ConnectionString), $"You do not have 'btcpay.server.canmodifyserversettings' rights, so the connection string should not contain 'cookiefilepath', 'macaroondirectorypath', 'macaroonfilepath', and should not point to a local ip or to a dns name ending with '.internal', '.local', '.lan' or '.'."); return(this.CreateValidationError(ModelState)); } paymentMethod = new Payments.Lightning.LightningSupportedPaymentMethod() { CryptoCode = paymentMethodId.CryptoCode }; paymentMethod.SetLightningUrl(connectionString); } } var store = Store; var storeBlob = store.GetStoreBlob(); store.SetSupportedPaymentMethod(paymentMethodId, paymentMethod); storeBlob.SetExcluded(paymentMethodId, !paymentMethodData.Enabled); store.SetStoreBlob(storeBlob); await _storeRepository.UpdateStore(store); return(Ok(GetExistingLightningLikePaymentMethod(cryptoCode, store))); }
public async Task <IActionResult> AddLightningNode(string storeId, LightningNodeViewModel vm, string command, string cryptoCode) { vm.CryptoCode = cryptoCode; var store = HttpContext.GetStoreData(); if (store == null) { return(NotFound()); } var network = vm.CryptoCode == null ? null : _ExplorerProvider.GetNetwork(vm.CryptoCode); var internalLightning = GetInternalLighningNode(network.CryptoCode); vm.InternalLightningNode = internalLightning?.ToUri(true)?.AbsoluteUri; if (network == null) { ModelState.AddModelError(nameof(vm.CryptoCode), "Invalid network"); return(View(vm)); } PaymentMethodId paymentMethodId = new PaymentMethodId(network.CryptoCode, PaymentTypes.LightningLike); Payments.Lightning.LightningSupportedPaymentMethod paymentMethod = null; if (!string.IsNullOrEmpty(vm.Url)) { if (!LightningConnectionString.TryParse(vm.Url, out var connectionString, out var error)) { ModelState.AddModelError(nameof(vm.Url), $"Invalid URL ({error})"); return(View(vm)); } var internalDomain = internalLightning?.ToUri(false)?.DnsSafeHost; bool isLocal = (internalDomain == "127.0.0.1" || internalDomain == "localhost"); bool isInternalNode = connectionString.ConnectionType == LightningConnectionType.CLightning || connectionString.BaseUri.DnsSafeHost == internalDomain || isLocal; if (connectionString.BaseUri.Scheme == "http" && !isLocal) { if (!isInternalNode || (isInternalNode && !CanUseInternalLightning())) { ModelState.AddModelError(nameof(vm.Url), "The url must be HTTPS"); return(View(vm)); } } if (isInternalNode && !CanUseInternalLightning()) { ModelState.AddModelError(nameof(vm.Url), "Unauthorized url"); return(View(vm)); } paymentMethod = new Payments.Lightning.LightningSupportedPaymentMethod() { CryptoCode = paymentMethodId.CryptoCode }; paymentMethod.SetLightningUrl(connectionString); } if (command == "save") { store.SetSupportedPaymentMethod(paymentMethodId, paymentMethod); await _Repo.UpdateStore(store); StatusMessage = $"Lightning node modified ({network.CryptoCode})"; return(RedirectToAction(nameof(UpdateStore), new { storeId = storeId })); } else // if(command == "test") { if (paymentMethod == null) { ModelState.AddModelError(nameof(vm.Url), "Missing url parameter"); return(View(vm)); } var handler = (LightningLikePaymentHandler)_ServiceProvider.GetRequiredService <IPaymentMethodHandler <Payments.Lightning.LightningSupportedPaymentMethod> >(); try { var info = await handler.Test(paymentMethod, network); if (!vm.SkipPortTest) { using (CancellationTokenSource cts = new CancellationTokenSource(TimeSpan.FromSeconds(20))) { await handler.TestConnection(info, cts.Token); } } vm.StatusMessage = $"Connection to the lightning node succeed ({info})"; } catch (Exception ex) { vm.StatusMessage = $"Error: {ex.Message}"; return(View(vm)); } return(View(vm)); } }
public void LoadArgs(IConfiguration conf) { NetworkType = DefaultConfiguration.GetNetworkType(conf); var defaultSettings = BTCPayDefaultSettings.GetDefaultSettings(NetworkType); DataDir = conf.GetOrDefault <string>("datadir", defaultSettings.DefaultDataDirectory); Logs.Configuration.LogInformation("Network: " + NetworkType.ToString()); var supportedChains = conf.GetOrDefault <string>("chains", "btc") .Split(',', StringSplitOptions.RemoveEmptyEntries) .Select(t => t.ToUpperInvariant()); NetworkProvider = new BTCPayNetworkProvider(NetworkType).Filter(supportedChains.ToArray()); foreach (var chain in supportedChains) { if (NetworkProvider.GetNetwork(chain) == null) { throw new ConfigException($"Invalid chains \"{chain}\""); } } var validChains = new List <string>(); foreach (var net in NetworkProvider.GetAll()) { NBXplorerConnectionSetting setting = new NBXplorerConnectionSetting(); setting.CryptoCode = net.CryptoCode; setting.ExplorerUri = conf.GetOrDefault <Uri>($"{net.CryptoCode}.explorer.url", net.NBXplorerNetwork.DefaultSettings.DefaultUrl); setting.CookieFile = conf.GetOrDefault <string>($"{net.CryptoCode}.explorer.cookiefile", net.NBXplorerNetwork.DefaultSettings.DefaultCookieFile); NBXplorerConnectionSettings.Add(setting); { var lightning = conf.GetOrDefault <string>($"{net.CryptoCode}.lightning", string.Empty); if (lightning.Length != 0) { if (!LightningConnectionString.TryParse(lightning, true, out var connectionString, out var error)) { throw new ConfigException($"Invalid setting {net.CryptoCode}.lightning, " + Environment.NewLine + $"If you have a c-lightning server use: 'type=clightning;server=/root/.lightning/lightning-rpc', " + Environment.NewLine + $"If you have a lightning charge server: 'type=charge;server=https://charge.example.com;api-token=yourapitoken'" + Environment.NewLine + $"If you have a lnd server: 'type=lnd-rest;server=https://lnd:[email protected];macaroon=abf239...;certthumbprint=2abdf302...'" + Environment.NewLine + $" lnd server: 'type=lnd-rest;server=https://lnd:[email protected];macaroonfilepath=/root/.lnd/admin.macaroon;certthumbprint=2abdf302...'" + Environment.NewLine + error); } if (connectionString.IsLegacy) { Logs.Configuration.LogWarning($"Setting {net.CryptoCode}.lightning is a deprecated format, it will work now, but please replace it for future versions with '{connectionString.ToString()}'"); } InternalLightningByCryptoCode.Add(net.CryptoCode, connectionString); } } void externalLnd <T>(string code, string lndType) { var lightning = conf.GetOrDefault <string>(code, string.Empty); if (lightning.Length != 0) { if (!LightningConnectionString.TryParse(lightning, false, out var connectionString, out var error)) { throw new ConfigException($"Invalid setting {code}, " + Environment.NewLine + $"lnd server: 'type={lndType};server=https://lnd.example.com;macaroon=abf239...;certthumbprint=2abdf302...'" + Environment.NewLine + $"lnd server: 'type={lndType};server=https://lnd.example.com;macaroonfilepath=/root/.lnd/admin.macaroon;certthumbprint=2abdf302...'" + Environment.NewLine + error); } var instanceType = typeof(T); ExternalServicesByCryptoCode.Add(net.CryptoCode, (ExternalService)Activator.CreateInstance(instanceType, connectionString)); } }; externalLnd <ExternalLndGrpc>($"{net.CryptoCode}.external.lnd.grpc", "lnd-grpc"); externalLnd <ExternalLndRest>($"{net.CryptoCode}.external.lnd.rest", "lnd-rest"); var spark = conf.GetOrDefault <string>($"{net.CryptoCode}.external.spark", string.Empty); if (spark.Length != 0) { if (!SparkConnectionString.TryParse(spark, out var connectionString)) { throw new ConfigException($"Invalid setting {net.CryptoCode}.external.spark, " + Environment.NewLine + $"Valid example: 'server=https://btcpay.example.com/spark/btc/;cookiefile=/etc/clightning_bitcoin_spark/.cookie'"); } ExternalServicesByCryptoCode.Add(net.CryptoCode, new ExternalSpark(connectionString)); } var charge = conf.GetOrDefault <string>($"{net.CryptoCode}.external.charge", string.Empty); if (charge.Length != 0) { if (!LightningConnectionString.TryParse(charge, false, out var chargeConnectionString, out var chargeError)) { LightningConnectionString.TryParse("type=charge;" + charge, false, out chargeConnectionString, out chargeError); } if (chargeConnectionString == null || chargeConnectionString.ConnectionType != LightningConnectionType.Charge) { throw new ConfigException($"Invalid setting {net.CryptoCode}.external.charge, " + Environment.NewLine + $"lightning charge server: 'type=charge;server=https://charge.example.com;api-token=2abdf302...'" + Environment.NewLine + $"lightning charge server: 'type=charge;server=https://charge.example.com;cookiefilepath=/root/.charge/.cookie'" + Environment.NewLine + chargeError ?? string.Empty); } ExternalServicesByCryptoCode.Add(net.CryptoCode, new ExternalCharge(chargeConnectionString)); } } Logs.Configuration.LogInformation("Supported chains: " + String.Join(',', supportedChains.ToArray())); var services = conf.GetOrDefault <string>("externalservices", null); if (services != null) { foreach (var service in services.Split(new[] { ';', ',' }) .Select(p => p.Split(':')) .Where(p => p.Length == 2) .Select(p => (Name: p[0], Link: p[1]))) { ExternalServices.AddOrReplace(service.Name, service.Link); } } PostgresConnectionString = conf.GetOrDefault <string>("postgres", null); MySQLConnectionString = conf.GetOrDefault <string>("mysql", null); BundleJsCss = conf.GetOrDefault <bool>("bundlejscss", true); ExternalUrl = conf.GetOrDefault <Uri>("externalurl", null); var sshSettings = ParseSSHConfiguration(conf); if ((!string.IsNullOrEmpty(sshSettings.Password) || !string.IsNullOrEmpty(sshSettings.KeyFile)) && !string.IsNullOrEmpty(sshSettings.Server)) { int waitTime = 0; while (!string.IsNullOrEmpty(sshSettings.KeyFile) && !File.Exists(sshSettings.KeyFile)) { if (waitTime++ < 5) { System.Threading.Thread.Sleep(1000); } else { throw new ConfigException($"sshkeyfile does not exist"); } } if (sshSettings.Port > ushort.MaxValue || sshSettings.Port < ushort.MinValue) { throw new ConfigException($"ssh port is invalid"); } if (!string.IsNullOrEmpty(sshSettings.Password) && !string.IsNullOrEmpty(sshSettings.KeyFile)) { throw new ConfigException($"sshpassword or sshkeyfile should be provided, but not both"); } try { sshSettings.CreateConnectionInfo(); } catch { throw new ConfigException($"sshkeyfilepassword is invalid"); } SSHSettings = sshSettings; } var fingerPrints = conf.GetOrDefault <string>("sshtrustedfingerprints", ""); if (!string.IsNullOrEmpty(fingerPrints)) { foreach (var fingerprint in fingerPrints.Split(';', StringSplitOptions.RemoveEmptyEntries)) { if (!SSHFingerprint.TryParse(fingerprint, out var f)) { throw new ConfigException($"Invalid ssh fingerprint format {fingerprint}"); } TrustedFingerprints.Add(f); } } RootPath = conf.GetOrDefault <string>("rootpath", "/"); if (!RootPath.StartsWith("/", StringComparison.InvariantCultureIgnoreCase)) { RootPath = "/" + RootPath; } var old = conf.GetOrDefault <Uri>("internallightningnode", null); if (old != null) { throw new ConfigException($"internallightningnode is deprecated and should not be used anymore, use btclightning instead"); } LogFile = GetDebugLog(conf); if (!string.IsNullOrEmpty(LogFile)) { Logs.Configuration.LogInformation("LogFile: " + LogFile); Logs.Configuration.LogInformation("Log Level: " + GetDebugLogLevel(conf)); } DisableRegistration = conf.GetOrDefault <bool>("disable-registration", true); }
public async Task <IActionResult> SetupLightningNode(string storeId, LightningNodeViewModel vm, string command, string cryptoCode) { vm.CryptoCode = cryptoCode; var store = HttpContext.GetStoreData(); if (store == null) { return(NotFound()); } vm.CanUseInternalNode = CanUseInternalLightning(); if (vm.CryptoCode == null) { ModelState.AddModelError(nameof(vm.CryptoCode), "Invalid network"); return(View(vm)); } var network = _ExplorerProvider.GetNetwork(vm.CryptoCode); var paymentMethodId = new PaymentMethodId(network.CryptoCode, PaymentTypes.LightningLike); LightningSupportedPaymentMethod?paymentMethod = null; if (vm.LightningNodeType == LightningNodeType.Internal) { if (!CanUseInternalLightning()) { ModelState.AddModelError(nameof(vm.ConnectionString), "You are not authorized to use the internal lightning node"); return(View(vm)); } paymentMethod = new LightningSupportedPaymentMethod { CryptoCode = paymentMethodId.CryptoCode }; paymentMethod.SetInternalNode(); } else { if (string.IsNullOrEmpty(vm.ConnectionString)) { ModelState.AddModelError(nameof(vm.ConnectionString), "Please provide a connection string"); return(View(vm)); } if (!LightningConnectionString.TryParse(vm.ConnectionString, false, out var connectionString, out var error)) { ModelState.AddModelError(nameof(vm.ConnectionString), $"Invalid URL ({error})"); return(View(vm)); } if (connectionString.ConnectionType == LightningConnectionType.LndGRPC) { ModelState.AddModelError(nameof(vm.ConnectionString), $"BTCPay does not support gRPC connections"); return(View(vm)); } if (!User.IsInRole(Roles.ServerAdmin) && !connectionString.IsSafe()) { ModelState.AddModelError(nameof(vm.ConnectionString), "You are not a server admin, so the connection string should not contain 'cookiefilepath', 'macaroondirectorypath', 'macaroonfilepath', and should not point to a local ip or to a dns name ending with '.internal', '.local', '.lan' or '.'."); return(View(vm)); } paymentMethod = new LightningSupportedPaymentMethod { CryptoCode = paymentMethodId.CryptoCode }; paymentMethod.SetLightningUrl(connectionString); } switch (command) { case "save": var lnurl = new PaymentMethodId(vm.CryptoCode, PaymentTypes.LNURLPay); store.SetSupportedPaymentMethod(paymentMethodId, paymentMethod); store.SetSupportedPaymentMethod(lnurl, new LNURLPaySupportedPaymentMethod() { CryptoCode = vm.CryptoCode, UseBech32Scheme = true, EnableForStandardInvoices = false, LUD12Enabled = false }); await _Repo.UpdateStore(store); TempData[WellKnownTempData.SuccessMessage] = $"{network.CryptoCode} Lightning node updated."; return(RedirectToAction(nameof(LightningSettings), new { storeId, cryptoCode })); case "test": var handler = _ServiceProvider.GetRequiredService <LightningLikePaymentHandler>(); try { var info = await handler.GetNodeInfo(paymentMethod, network, new InvoiceLogs(), Request.IsOnion(), true); if (!vm.SkipPortTest) { using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(20)); await handler.TestConnection(info.First(), cts.Token); } TempData[WellKnownTempData.SuccessMessage] = $"Connection to the Lightning node successful. Your node address: {info.First()}"; } catch (Exception ex) { TempData[WellKnownTempData.ErrorMessage] = ex.Message; return(View(vm)); } return(View(vm)); default: return(View(vm)); } }
BuildModel(EditLightningNodeExternalServiceDataViewModel viewModel, ExternalServiceData mainModel) { var failureViewModel = new EditLightningNodeExternalServiceDataViewModel(viewModel); failureViewModel.CryptoCodes = new SelectList(_nbXplorerOptions.Cryptos?.ToList() ?? new List <string>(), viewModel.CryptoCode); //current External Service data var externalServiceData = mainModel; if (!ModelState.IsValid) { return(null, failureViewModel); } //current External Service data externalServiceData.Set((LightningNodeExternalServiceData)viewModel); var lightningNodeService = new LightningNodeService(externalServiceData, _nbXplorerClientProvider, _nbXplorerSummaryProvider, _socketFactory); if (!string.IsNullOrEmpty(viewModel.ConnectionString)) { if (!LightningConnectionString.TryParse(viewModel.ConnectionString, false, out var connectionString, out var error)) { ModelState.AddModelError(nameof(viewModel.ConnectionString), $"Invalid URL ({error})"); return(null, failureViewModel); } if (connectionString.ConnectionType == LightningConnectionType.LndGRPC) { ModelState.AddModelError(nameof(viewModel.ConnectionString), $"BTCPay does not support gRPC connections"); return(null, failureViewModel); } var canUseInternalLightning = await IsAdmin(); bool isInternalNode = connectionString.ConnectionType == LightningConnectionType.CLightning || connectionString.BaseUri.DnsSafeHost.StartsWith("lnd_") || (connectionString.BaseUri.DnsSafeHost.Equals("127.0.0.1", StringComparison.InvariantCultureIgnoreCase) || connectionString.BaseUri.DnsSafeHost.Equals("localhost", StringComparison.InvariantCultureIgnoreCase)); if (connectionString.BaseUri.Scheme == "http") { if (!isInternalNode) { ModelState.AddModelError(nameof(viewModel.ConnectionString), "The url must be HTTPS"); return(null, failureViewModel); } } if (connectionString.MacaroonFilePath != null) { if (!canUseInternalLightning) { ModelState.AddModelError(nameof(viewModel.ConnectionString), "You are not authorized to use macaroonfilepath"); return(null, failureViewModel); } if (!System.IO.File.Exists(connectionString.MacaroonFilePath)) { ModelState.AddModelError(nameof(viewModel.ConnectionString), "The macaroonfilepath file does not exist"); return(null, failureViewModel); } if (!System.IO.Path.IsPathRooted(connectionString.MacaroonFilePath)) { ModelState.AddModelError(nameof(viewModel.ConnectionString), "The macaroonfilepath should be fully rooted"); return(null, failureViewModel); } } if (isInternalNode && !canUseInternalLightning) { ModelState.AddModelError(nameof(viewModel.ConnectionString), "Unauthorized url"); return(null, failureViewModel); } } if (!await lightningNodeService.TestAccess(Request.IsOnion())) { ModelState.AddModelError(String.Empty, "Could not connect with current settings"); return(null, failureViewModel); } return(externalServiceData, null); }
public static bool IsInternalNode(this LightningConnectionString connectionString, LightningConnectionString internalLightning) { var internalDomain = internalLightning?.BaseUri?.DnsSafeHost; return(connectionString.ConnectionType == LightningConnectionType.CLightning || connectionString.BaseUri.DnsSafeHost == internalDomain || (internalDomain == "127.0.0.1" || internalDomain == "localhost")); }
public ExternalLndRest(LightningConnectionString connectionString) : base(connectionString, "lnd-rest") { }
public void LoadArgs(IConfiguration conf) { NetworkType = DefaultConfiguration.GetNetworkType(conf); var defaultSettings = BTCPayDefaultSettings.GetDefaultSettings(NetworkType); DataDir = conf.GetOrDefault <string>("datadir", defaultSettings.DefaultDataDirectory); Logs.Configuration.LogInformation("Network: " + NetworkType.ToString()); var supportedChains = conf.GetOrDefault <string>("chains", "btc") .Split(',', StringSplitOptions.RemoveEmptyEntries) .Select(t => t.ToUpperInvariant()); NetworkProvider = new BTCPayNetworkProvider(NetworkType).Filter(supportedChains.ToArray()); foreach (var chain in supportedChains) { if (NetworkProvider.GetNetwork(chain) == null) { throw new ConfigException($"Invalid chains \"{chain}\""); } } var validChains = new List <string>(); foreach (var net in NetworkProvider.GetAll()) { NBXplorerConnectionSetting setting = new NBXplorerConnectionSetting(); setting.CryptoCode = net.CryptoCode; setting.ExplorerUri = conf.GetOrDefault <Uri>($"{net.CryptoCode}.explorer.url", net.NBXplorerNetwork.DefaultSettings.DefaultUrl); setting.CookieFile = conf.GetOrDefault <string>($"{net.CryptoCode}.explorer.cookiefile", net.NBXplorerNetwork.DefaultSettings.DefaultCookieFile); NBXplorerConnectionSettings.Add(setting); { var lightning = conf.GetOrDefault <string>($"{net.CryptoCode}.lightning", string.Empty); if (lightning.Length != 0) { if (!LightningConnectionString.TryParse(lightning, true, out var connectionString, out var error)) { throw new ConfigException($"Invalid setting {net.CryptoCode}.lightning, " + Environment.NewLine + $"If you have a lightning server use: 'type=clightning;server=/root/.lightning/lightning-rpc', " + Environment.NewLine + $"If you have a lightning charge server: 'type=charge;server=https://charge.example.com;api-token=yourapitoken'" + Environment.NewLine + $"If you have a lnd server: 'type=lnd-rest;server=https://lnd:[email protected];macaroon=abf239...;certthumbprint=2abdf302...'" + Environment.NewLine + $" lnd server: 'type=lnd-rest;server=https://lnd:[email protected];macaroonfilepath=/root/.lnd/admin.macaroon;certthumbprint=2abdf302...'" + Environment.NewLine + error); } if (connectionString.IsLegacy) { Logs.Configuration.LogWarning($"Setting {net.CryptoCode}.lightning will work but use an deprecated format, please replace it by '{connectionString.ToString()}'"); } InternalLightningByCryptoCode.Add(net.CryptoCode, connectionString); } } { var lightning = conf.GetOrDefault <string>($"{net.CryptoCode}.external.lnd.grpc", string.Empty); if (lightning.Length != 0) { if (!LightningConnectionString.TryParse(lightning, false, out var connectionString, out var error)) { throw new ConfigException($"Invalid setting {net.CryptoCode}.external.lnd.grpc, " + Environment.NewLine + $"lnd server: 'type=lnd-grpc;server=https://lnd.example.com;macaroon=abf239...;certthumbprint=2abdf302...'" + Environment.NewLine + $"lnd server: 'type=lnd-grpc;server=https://lnd.example.com;macaroonfilepath=/root/.lnd/admin.macaroon;certthumbprint=2abdf302...'" + Environment.NewLine + error); } ExternalServicesByCryptoCode.Add(net.CryptoCode, new ExternalLNDGRPC(connectionString)); } } } Logs.Configuration.LogInformation("Supported chains: " + String.Join(',', supportedChains.ToArray())); PostgresConnectionString = conf.GetOrDefault <string>("postgres", null); BundleJsCss = conf.GetOrDefault <bool>("bundlejscss", true); ExternalUrl = conf.GetOrDefault <Uri>("externalurl", null); RootPath = conf.GetOrDefault <string>("rootpath", "/"); if (!RootPath.StartsWith("/", StringComparison.InvariantCultureIgnoreCase)) { RootPath = "/" + RootPath; } var old = conf.GetOrDefault <Uri>("internallightningnode", null); if (old != null) { throw new ConfigException($"internallightningnode should not be used anymore, use btclightning instead"); } }
public ExternalLndGrpc(LightningConnectionString connectionString) : base(connectionString, "lnd-grpc") { }
public ExternalLnd(LightningConnectionString connectionString, string type) { ConnectionString = connectionString; Type = type; }
public async Task <IActionResult> AddLightningNode(string storeId, LightningNodeViewModel vm, string command, string cryptoCode) { vm.CryptoCode = cryptoCode; var store = HttpContext.GetStoreData(); if (store == null) { return(NotFound()); } var network = vm.CryptoCode == null ? null : _ExplorerProvider.GetNetwork(vm.CryptoCode); if (network == null) { ModelState.AddModelError(nameof(vm.CryptoCode), "Invalid network"); return(View(vm)); } PaymentMethodId paymentMethodId = new PaymentMethodId(network.CryptoCode, PaymentTypes.LightningLike); Payments.Lightning.LightningSupportedPaymentMethod paymentMethod = null; if (vm.ConnectionString == LightningSupportedPaymentMethod.InternalNode) { if (!CanUseInternalLightning()) { ModelState.AddModelError(nameof(vm.ConnectionString), $"You are not authorized to use the internal lightning node"); return(View(vm)); } paymentMethod = new Payments.Lightning.LightningSupportedPaymentMethod() { CryptoCode = paymentMethodId.CryptoCode }; paymentMethod.SetInternalNode(); } else if (!string.IsNullOrEmpty(vm.ConnectionString)) { if (!LightningConnectionString.TryParse(vm.ConnectionString, false, out var connectionString, out var error)) { ModelState.AddModelError(nameof(vm.ConnectionString), $"Invalid URL ({error})"); return(View(vm)); } if (connectionString.ConnectionType == LightningConnectionType.LndGRPC) { ModelState.AddModelError(nameof(vm.ConnectionString), $"BTCPay does not support gRPC connections"); return(View(vm)); } if (!User.IsInRole(Roles.ServerAdmin) && !connectionString.IsSafe()) { ModelState.AddModelError(nameof(vm.ConnectionString), $"You are not a server admin, so the connection string should not contain 'cookiefilepath', 'macaroondirectorypath', 'macaroonfilepath', and should not point to a local ip or to a dns name ending with '.internal', '.local', '.lan' or '.'."); return(View(vm)); } paymentMethod = new Payments.Lightning.LightningSupportedPaymentMethod() { CryptoCode = paymentMethodId.CryptoCode }; paymentMethod.SetLightningUrl(connectionString); } switch (command) { case "save": var storeBlob = store.GetStoreBlob(); storeBlob.SetExcluded(paymentMethodId, !vm.Enabled); storeBlob.Hints.Lightning = false; store.SetStoreBlob(storeBlob); store.SetSupportedPaymentMethod(paymentMethodId, paymentMethod); await _Repo.UpdateStore(store); TempData[WellKnownTempData.SuccessMessage] = $"Lightning node modified ({network.CryptoCode})"; return(RedirectToAction(nameof(UpdateStore), new { storeId = storeId })); case "test" when paymentMethod == null: ModelState.AddModelError(nameof(vm.ConnectionString), "Missing url parameter"); return(View(vm)); case "test": var handler = _ServiceProvider.GetRequiredService <LightningLikePaymentHandler>(); try { var info = await handler.GetNodeInfo(this.Request.IsOnion(), paymentMethod, network); if (!vm.SkipPortTest) { using (CancellationTokenSource cts = new CancellationTokenSource(TimeSpan.FromSeconds(20))) { await handler.TestConnection(info, cts.Token); } } TempData[WellKnownTempData.SuccessMessage] = $"Connection to the lightning node succeeded. Your node address: {info}"; } catch (Exception ex) { TempData[WellKnownTempData.ErrorMessage] = ex.Message; return(View(vm)); } return(View(vm)); default: return(View(vm)); } }
public static IServiceCollection AddBTCPayServer(this IServiceCollection services, IConfiguration configuration) { services.AddSingleton <MvcNewtonsoftJsonOptions>(o => o.GetRequiredService <IOptions <MvcNewtonsoftJsonOptions> >().Value); services.AddDbContext <ApplicationDbContext>((provider, o) => { var factory = provider.GetRequiredService <ApplicationDbContextFactory>(); factory.ConfigureBuilder(o); }); services.AddHttpClient(); services.AddHttpClient(nameof(ExplorerClientProvider), httpClient => { httpClient.Timeout = Timeout.InfiniteTimeSpan; }); services.AddSingleton <BTCPayNetworkJsonSerializerSettings>(); services.AddPayJoinServices(); #if ALTCOINS services.AddMoneroLike(); services.AddEthereumLike(); #endif services.TryAddSingleton <SettingsRepository>(); services.TryAddSingleton <ISettingsRepository>(provider => provider.GetService <SettingsRepository>()); services.TryAddSingleton <LabelFactory>(); services.TryAddSingleton <TorServices>(); services.TryAddSingleton <SocketFactory>(); services.TryAddSingleton <LightningClientFactoryService>(); services.TryAddSingleton <InvoicePaymentNotification>(); services.TryAddSingleton <BTCPayServerOptions>(o => o.GetRequiredService <IOptions <BTCPayServerOptions> >().Value); // Don't move this StartupTask, we depend on it being right here services.AddStartupTask <MigrationStartupTask>(); // services.AddStartupTask <BlockExplorerLinkStartupTask>(); services.TryAddSingleton <InvoiceRepository>(o => { var dbContext = o.GetRequiredService <ApplicationDbContextFactory>(); return(new InvoiceRepository(dbContext, o.GetRequiredService <BTCPayNetworkProvider>(), o.GetService <EventAggregator>())); }); services.AddSingleton <BTCPayServerEnvironment>(); services.TryAddSingleton <TokenRepository>(); services.TryAddSingleton <WalletRepository>(); services.TryAddSingleton <EventAggregator>(); services.TryAddSingleton <PaymentRequestService>(); services.TryAddSingleton <U2FService>(); services.AddSingleton <ApplicationDbContextFactory>(); services.AddOptions <BTCPayServerOptions>().Configure( (options) => { options.LoadArgs(configuration); }); services.AddOptions <DataDirectories>().Configure( (options) => { options.Configure(configuration); }); services.AddOptions <DatabaseOptions>().Configure <IOptions <DataDirectories> >( (options, datadirs) => { var postgresConnectionString = configuration["postgres"]; var mySQLConnectionString = configuration["mysql"]; var sqliteFileName = configuration["sqlitefile"]; if (!string.IsNullOrEmpty(postgresConnectionString)) { options.DatabaseType = DatabaseType.Postgres; options.ConnectionString = postgresConnectionString; } else if (!string.IsNullOrEmpty(mySQLConnectionString)) { options.DatabaseType = DatabaseType.MySQL; options.ConnectionString = mySQLConnectionString; } else if (!string.IsNullOrEmpty(sqliteFileName)) { var connStr = "Data Source=" + (Path.IsPathRooted(sqliteFileName) ? sqliteFileName : Path.Combine(datadirs.Value.DataDir, sqliteFileName)); options.DatabaseType = DatabaseType.Sqlite; options.ConnectionString = connStr; } else { throw new InvalidOperationException("No database option was configured."); } }); services.AddOptions <NBXplorerOptions>().Configure <BTCPayNetworkProvider>( (options, btcPayNetworkProvider) => { foreach (BTCPayNetwork btcPayNetwork in btcPayNetworkProvider.GetAll().OfType <BTCPayNetwork>()) { NBXplorerConnectionSetting setting = new NBXplorerConnectionSetting { CryptoCode = btcPayNetwork.CryptoCode, ExplorerUri = configuration.GetOrDefault <Uri>( $"{btcPayNetwork.CryptoCode}.explorer.url", btcPayNetwork.NBXplorerNetwork.DefaultSettings.DefaultUrl), CookieFile = configuration.GetOrDefault <string>( $"{btcPayNetwork.CryptoCode}.explorer.cookiefile", btcPayNetwork.NBXplorerNetwork.DefaultSettings.DefaultCookieFile) }; options.NBXplorerConnectionSettings.Add(setting); } }); services.AddOptions <LightningNetworkOptions>().Configure <BTCPayNetworkProvider>( (options, btcPayNetworkProvider) => { foreach (var net in btcPayNetworkProvider.GetAll().OfType <BTCPayNetwork>()) { var lightning = configuration.GetOrDefault <string>($"{net.CryptoCode}.lightning", string.Empty); if (lightning.Length != 0) { if (!LightningConnectionString.TryParse(lightning, true, out var connectionString, out var error)) { Logs.Configuration.LogWarning($"Invalid setting {net.CryptoCode}.lightning, " + Environment.NewLine + $"If you have a c-lightning server use: 'type=clightning;server=/root/.lightning/lightning-rpc', " + Environment.NewLine + $"If you have a lightning charge server: 'type=charge;server=https://charge.example.com;api-token=yourapitoken'" + Environment.NewLine + $"If you have a lnd server: 'type=lnd-rest;server=https://lnd:[email protected];macaroon=abf239...;certthumbprint=2abdf302...'" + Environment.NewLine + $" lnd server: 'type=lnd-rest;server=https://lnd:[email protected];macaroonfilepath=/root/.lnd/admin.macaroon;certthumbprint=2abdf302...'" + Environment.NewLine + $"If you have an eclair server: 'type=eclair;server=http://eclair.com:4570;password=eclairpassword;bitcoin-host=bitcoind:37393;bitcoin-auth=bitcoinrpcuser:bitcoinrpcpassword" + Environment.NewLine + $" eclair server: 'type=eclair;server=http://eclair.com:4570;password=eclairpassword;bitcoin-host=bitcoind:37393" + Environment.NewLine + $"Error: {error}" + Environment.NewLine + "This service will not be exposed through BTCPay Server"); } else { if (connectionString.IsLegacy) { Logs.Configuration.LogWarning( $"Setting {net.CryptoCode}.lightning is a deprecated format, it will work now, but please replace it for future versions with '{connectionString.ToString()}'"); } options.InternalLightningByCryptoCode.Add(net.CryptoCode, connectionString); } } } });
public ExternalLNDGRPC(LightningConnectionString connectionString) { ConnectionString = connectionString; }
public async Task <IActionResult> AddLightningNode(string storeId, LightningNodeViewModel vm, string command, string bitcoinCode) { vm.bitcoinCode = bitcoinCode; var store = HttpContext.GetStoreData(); if (store == null) { return(NotFound()); } var network = vm.bitcoinCode == null ? null : _ExplorerProvider.GetNetwork(vm.bitcoinCode); var internalLightning = GetInternalLighningNode(network.bitcoinCode); vm.InternalLightningNode = internalLightning?.ToString(); if (network == null) { ModelState.AddModelError(nameof(vm.bitcoinCode), "Invalid network"); return(View(vm)); } PaymentMethodId paymentMethodId = new PaymentMethodId(network.bitcoinCode, PaymentTypes.LightningLike); Payments.Lightning.LightningSupportedPaymentMethod paymentMethod = null; if (!string.IsNullOrEmpty(vm.ConnectionString)) { if (!LightningConnectionString.TryParse(vm.ConnectionString, false, out var connectionString, out var error)) { ModelState.AddModelError(nameof(vm.ConnectionString), $"Invalid URL ({error})"); return(View(vm)); } if (connectionString.ConnectionType == LightningConnectionType.LndGRPC) { ModelState.AddModelError(nameof(vm.ConnectionString), $"BTCPay does not support gRPC connections"); return(View(vm)); } bool isInternalNode = connectionString.IsInternalNode(internalLightning); if (connectionString.BaseUri.Scheme == "http") { if (!isInternalNode) { ModelState.AddModelError(nameof(vm.ConnectionString), "The url must be HTTPS"); return(View(vm)); } } if (connectionString.MacaroonFilePath != null) { if (!CanUseInternalLightning()) { ModelState.AddModelError(nameof(vm.ConnectionString), "You are not authorized to use macaroonfilepath"); return(View(vm)); } if (!System.IO.File.Exists(connectionString.MacaroonFilePath)) { ModelState.AddModelError(nameof(vm.ConnectionString), "The macaroonfilepath file does not exist"); return(View(vm)); } if (!System.IO.Path.IsPathRooted(connectionString.MacaroonFilePath)) { ModelState.AddModelError(nameof(vm.ConnectionString), "The macaroonfilepath should be fully rooted"); return(View(vm)); } } if (isInternalNode && !CanUseInternalLightning()) { ModelState.AddModelError(nameof(vm.ConnectionString), "Unauthorized url"); return(View(vm)); } paymentMethod = new Payments.Lightning.LightningSupportedPaymentMethod() { bitcoinCode = paymentMethodId.bitcoinCode }; paymentMethod.SetLightningUrl(connectionString); } switch (command) { case "save": var storeBlob = store.GetStoreBlob(); storeBlob.SetExcluded(paymentMethodId, !vm.Enabled); store.SetStoreBlob(storeBlob); store.SetSupportedPaymentMethod(paymentMethodId, paymentMethod); await _Repo.UpdateStore(store); TempData[WellKnownTempData.SuccessMessage] = $"Lightning node modified ({network.bitcoinCode})"; return(RedirectToAction(nameof(UpdateStore), new { storeId = storeId })); case "test" when paymentMethod == null: ModelState.AddModelError(nameof(vm.ConnectionString), "Missing url parameter"); return(View(vm)); case "test": var handler = _ServiceProvider.GetRequiredService <LightningLikePaymentHandler>(); try { var info = await handler.GetNodeInfo(this.Request.IsOnion(), paymentMethod, network); if (!vm.SkipPortTest) { using (CancellationTokenSource cts = new CancellationTokenSource(TimeSpan.FromSeconds(20))) { await handler.TestConnection(info, cts.Token); } } TempData[WellKnownTempData.SuccessMessage] = $"Connection to the lightning node succeeded ({info})"; } catch (Exception ex) { TempData[WellKnownTempData.ErrorMessage] = ex.Message; return(View(vm)); } return(View(vm)); default: return(View(vm)); } }
public void LoadArgs(IConfiguration conf) { NetworkType = DefaultConfiguration.GetNetworkType(conf); var defaultSettings = BTCPayDefaultSettings.GetDefaultSettings(NetworkType); DataDir = conf.GetOrDefault <string>("datadir", defaultSettings.DefaultDataDirectory); Logs.Configuration.LogInformation("Network: " + NetworkType.ToString()); var supportedChains = conf.GetOrDefault <string>("chains", "btc") .Split(',', StringSplitOptions.RemoveEmptyEntries) .Select(t => t.ToUpperInvariant()); NetworkProvider = new BTCPayNetworkProvider(NetworkType).Filter(supportedChains.ToArray()); foreach (var chain in supportedChains) { if (NetworkProvider.GetNetwork(chain) == null) { throw new ConfigException($"Invalid chains \"{chain}\""); } } var validChains = new List <string>(); foreach (var net in NetworkProvider.GetAll()) { NBXplorerConnectionSetting setting = new NBXplorerConnectionSetting(); setting.CryptoCode = net.CryptoCode; setting.ExplorerUri = conf.GetOrDefault <Uri>($"{net.CryptoCode}.explorer.url", net.NBXplorerNetwork.DefaultSettings.DefaultUrl); setting.CookieFile = conf.GetOrDefault <string>($"{net.CryptoCode}.explorer.cookiefile", net.NBXplorerNetwork.DefaultSettings.DefaultCookieFile); NBXplorerConnectionSettings.Add(setting); { var lightning = conf.GetOrDefault <string>($"{net.CryptoCode}.lightning", string.Empty); if (lightning.Length != 0) { if (!LightningConnectionString.TryParse(lightning, true, out var connectionString, out var error)) { throw new ConfigException($"Invalid setting {net.CryptoCode}.lightning, " + Environment.NewLine + $"If you have a lightning server use: 'type=clightning;server=/root/.lightning/lightning-rpc', " + Environment.NewLine + $"If you have a lightning charge server: 'type=charge;server=https://charge.example.com;api-token=yourapitoken'" + Environment.NewLine + $"If you have a lnd server: 'type=lnd-rest;server=https://lnd:[email protected];macaroon=abf239...;certthumbprint=2abdf302...'" + Environment.NewLine + $" lnd server: 'type=lnd-rest;server=https://lnd:[email protected];macaroonfilepath=/root/.lnd/admin.macaroon;certthumbprint=2abdf302...'" + Environment.NewLine + error); } if (connectionString.IsLegacy) { Logs.Configuration.LogWarning($"Setting {net.CryptoCode}.lightning will work but use an deprecated format, please replace it by '{connectionString.ToString()}'"); } InternalLightningByCryptoCode.Add(net.CryptoCode, connectionString); } } { var lightning = conf.GetOrDefault <string>($"{net.CryptoCode}.external.lnd.grpc", string.Empty); if (lightning.Length != 0) { if (!LightningConnectionString.TryParse(lightning, false, out var connectionString, out var error)) { throw new ConfigException($"Invalid setting {net.CryptoCode}.external.lnd.grpc, " + Environment.NewLine + $"lnd server: 'type=lnd-grpc;server=https://lnd.example.com;macaroon=abf239...;certthumbprint=2abdf302...'" + Environment.NewLine + $"lnd server: 'type=lnd-grpc;server=https://lnd.example.com;macaroonfilepath=/root/.lnd/admin.macaroon;certthumbprint=2abdf302...'" + Environment.NewLine + error); } ExternalServicesByCryptoCode.Add(net.CryptoCode, new ExternalLNDGRPC(connectionString)); } } } Logs.Configuration.LogInformation("Supported chains: " + String.Join(',', supportedChains.ToArray())); PostgresConnectionString = conf.GetOrDefault <string>("postgres", null); BundleJsCss = conf.GetOrDefault <bool>("bundlejscss", true); ExternalUrl = conf.GetOrDefault <Uri>("externalurl", null); var sshSettings = ParseSSHConfiguration(conf); if ((!string.IsNullOrEmpty(sshSettings.Password) || !string.IsNullOrEmpty(sshSettings.KeyFile)) && !string.IsNullOrEmpty(sshSettings.Server)) { int waitTime = 0; while (!string.IsNullOrEmpty(sshSettings.KeyFile) && !File.Exists(sshSettings.KeyFile)) { if (waitTime++ < 5) { System.Threading.Thread.Sleep(1000); } else { throw new ConfigException($"sshkeyfile does not exist"); } } if (sshSettings.Port > ushort.MaxValue || sshSettings.Port < ushort.MinValue) { throw new ConfigException($"ssh port is invalid"); } if (!string.IsNullOrEmpty(sshSettings.Password) && !string.IsNullOrEmpty(sshSettings.KeyFile)) { throw new ConfigException($"sshpassword or sshkeyfile should be provided, but not both"); } try { sshSettings.CreateConnectionInfo(); } catch { throw new ConfigException($"sshkeyfilepassword is invalid"); } SSHSettings = sshSettings; } var fingerPrints = conf.GetOrDefault <string>("sshtrustedfingerprints", ""); if (!string.IsNullOrEmpty(fingerPrints)) { foreach (var fingerprint in fingerPrints.Split(';', StringSplitOptions.RemoveEmptyEntries)) { if (!SSHFingerprint.TryParse(fingerprint, out var f)) { throw new ConfigException($"Invalid ssh fingerprint format {fingerprint}"); } TrustedFingerprints.Add(f); } } RootPath = conf.GetOrDefault <string>("rootpath", "/"); if (!RootPath.StartsWith("/", StringComparison.InvariantCultureIgnoreCase)) { RootPath = "/" + RootPath; } var old = conf.GetOrDefault <Uri>("internallightningnode", null); if (old != null) { throw new ConfigException($"internallightningnode should not be used anymore, use btclightning instead"); } }
public void LoadArgs(IConfiguration conf) { NetworkType = DefaultConfiguration.GetNetworkType(conf); DataDir = conf.GetDataDir(NetworkType); Logs.Configuration.LogInformation("Network: " + NetworkType.ToString()); if (conf.GetOrDefault <bool>("launchsettings", false) && NetworkType != NetworkType.Regtest) { throw new ConfigException($"You need to run BTCPayServer with the run.sh or run.ps1 script"); } var supportedChains = conf.GetOrDefault <string>("chains", "btc") .Split(',', StringSplitOptions.RemoveEmptyEntries) .Select(t => t.ToUpperInvariant()).ToHashSet(); var networkProvider = new BTCPayNetworkProvider(NetworkType); var filtered = networkProvider.Filter(supportedChains.ToArray()); var elementsBased = filtered.GetAll().OfType <ElementsBTCPayNetwork>(); var parentChains = elementsBased.Select(network => network.NetworkCryptoCode.ToUpperInvariant()).Distinct(); var allSubChains = networkProvider.GetAll().OfType <ElementsBTCPayNetwork>() .Where(network => parentChains.Contains(network.NetworkCryptoCode)).Select(network => network.CryptoCode.ToUpperInvariant()); supportedChains.AddRange(allSubChains); NetworkProvider = networkProvider.Filter(supportedChains.ToArray()); foreach (var chain in supportedChains) { if (NetworkProvider.GetNetwork <BTCPayNetworkBase>(chain) == null) { throw new ConfigException($"Invalid chains \"{chain}\""); } } var validChains = new List <string>(); foreach (var net in NetworkProvider.GetAll().OfType <BTCPayNetwork>()) { NBXplorerConnectionSetting setting = new NBXplorerConnectionSetting(); setting.CryptoCode = net.CryptoCode; setting.ExplorerUri = conf.GetOrDefault <Uri>($"{net.CryptoCode}.explorer.url", net.NBXplorerNetwork.DefaultSettings.DefaultUrl); setting.CookieFile = conf.GetOrDefault <string>($"{net.CryptoCode}.explorer.cookiefile", net.NBXplorerNetwork.DefaultSettings.DefaultCookieFile); NBXplorerConnectionSettings.Add(setting); { var lightning = conf.GetOrDefault <string>($"{net.CryptoCode}.lightning", string.Empty); if (lightning.Length != 0) { if (!LightningConnectionString.TryParse(lightning, true, out var connectionString, out var error)) { Logs.Configuration.LogWarning($"Invalid setting {net.CryptoCode}.lightning, " + Environment.NewLine + $"If you have a c-lightning server use: 'type=clightning;server=/root/.lightning/lightning-rpc', " + Environment.NewLine + $"If you have a lightning charge server: 'type=charge;server=https://charge.example.com;api-token=yourapitoken'" + Environment.NewLine + $"If you have a lnd server: 'type=lnd-rest;server=https://lnd:[email protected];macaroon=abf239...;certthumbprint=2abdf302...'" + Environment.NewLine + $" lnd server: 'type=lnd-rest;server=https://lnd:[email protected];macaroonfilepath=/root/.lnd/admin.macaroon;certthumbprint=2abdf302...'" + Environment.NewLine + $"If you have an eclair server: 'type=eclair;server=http://eclair.com:4570;password=eclairpassword;bitcoin-host=bitcoind:37393;bitcoin-auth=bitcoinrpcuser:bitcoinrpcpassword" + Environment.NewLine + $" eclair server: 'type=eclair;server=http://eclair.com:4570;password=eclairpassword;bitcoin-host=bitcoind:37393" + Environment.NewLine + $"Error: {error}" + Environment.NewLine + "This service will not be exposed through BTCPay Server"); } else { if (connectionString.IsLegacy) { Logs.Configuration.LogWarning($"Setting {net.CryptoCode}.lightning is a deprecated format, it will work now, but please replace it for future versions with '{connectionString.ToString()}'"); } InternalLightningByCryptoCode.Add(net.CryptoCode, connectionString); } } } ExternalServices.Load(net.CryptoCode, conf); }
public async Task <IActionResult> AddLightningNode(string storeId, LightningNodeViewModel vm, string command, string cryptoCode) { vm.CryptoCode = cryptoCode; var store = HttpContext.GetStoreData(); if (store == null) { return(NotFound()); } var network = vm.CryptoCode == null ? null : _ExplorerProvider.GetNetwork(vm.CryptoCode); var internalLightning = GetInternalLighningNode(network.CryptoCode); vm.InternalLightningNode = internalLightning?.ToString(); if (network == null) { ModelState.AddModelError(nameof(vm.CryptoCode), "Red inválida"); return(View(vm)); } PaymentMethodId paymentMethodId = new PaymentMethodId(network.CryptoCode, PaymentTypes.LightningLike); Payments.Lightning.LightningSupportedPaymentMethod paymentMethod = null; if (!string.IsNullOrEmpty(vm.ConnectionString)) { if (!LightningConnectionString.TryParse(vm.ConnectionString, false, out var connectionString, out var error)) { ModelState.AddModelError(nameof(vm.ConnectionString), $"URL invalida ({error})"); return(View(vm)); } if (connectionString.ConnectionType == LightningConnectionType.LndGRPC) { ModelState.AddModelError(nameof(vm.ConnectionString), $"BTCPay no admite conexiones gRPC"); return(View(vm)); } var internalDomain = internalLightning?.BaseUri?.DnsSafeHost; bool isInternalNode = connectionString.ConnectionType == LightningConnectionType.CLightning || connectionString.BaseUri.DnsSafeHost == internalDomain || (internalDomain == "127.0.0.1" || internalDomain == "localhost"); if (connectionString.BaseUri.Scheme == "http") { if (!isInternalNode) { ModelState.AddModelError(nameof(vm.ConnectionString), "La url debe ser HTTPS"); return(View(vm)); } } if (connectionString.MacaroonFilePath != null) { if (!CanUseInternalLightning()) { ModelState.AddModelError(nameof(vm.ConnectionString), "Usted no está autorizado para usar Macaroonfilepath"); return(View(vm)); } if (!System.IO.File.Exists(connectionString.MacaroonFilePath)) { ModelState.AddModelError(nameof(vm.ConnectionString), "El archivo macaroonfilepath no existe"); return(View(vm)); } if (!System.IO.Path.IsPathRooted(connectionString.MacaroonFilePath)) { ModelState.AddModelError(nameof(vm.ConnectionString), "El macaroonfilepath debe estar completamente enraizado."); return(View(vm)); } } if (isInternalNode && !CanUseInternalLightning()) { ModelState.AddModelError(nameof(vm.ConnectionString), "URL no autorizada"); return(View(vm)); } paymentMethod = new Payments.Lightning.LightningSupportedPaymentMethod() { CryptoCode = paymentMethodId.CryptoCode }; paymentMethod.SetLightningUrl(connectionString); } switch (command) { case "save": var storeBlob = store.GetStoreBlob(); storeBlob.SetExcluded(paymentMethodId, !vm.Enabled); store.SetStoreBlob(storeBlob); store.SetSupportedPaymentMethod(paymentMethodId, paymentMethod); await _Repo.UpdateStore(store); StatusMessage = $"Lightning nodo modificado ({network.CryptoCode})"; return(RedirectToAction(nameof(UpdateStore), new { storeId = storeId })); case "test" when paymentMethod == null: ModelState.AddModelError(nameof(vm.ConnectionString), "Falta el parámetro url"); return(View(vm)); case "test": var handler = (LightningLikePaymentHandler)_ServiceProvider.GetRequiredService <IPaymentMethodHandler <Payments.Lightning.LightningSupportedPaymentMethod> >(); try { var info = await handler.GetNodeInfo(this.Request.IsOnion(), paymentMethod, network); if (!vm.SkipPortTest) { using (CancellationTokenSource cts = new CancellationTokenSource(TimeSpan.FromSeconds(20))) { await handler.TestConnection(info, cts.Token); } } vm.StatusMessage = $"La conexión al nodo del Lightning tuvo éxito ({info})"; } catch (Exception ex) { vm.StatusMessage = $"Error: {ex.Message}"; return(View(vm)); } return(View(vm)); default: return(View(vm)); } }