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");
            }
        }
Beispiel #2
0
        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)));
        }
Beispiel #3
0
        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));
            }
        }
Beispiel #5
0
        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));
            }
        }
Beispiel #7
0
        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);
        }
Beispiel #8
0
        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"));
        }
Beispiel #9
0
 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");
            }
        }
Beispiel #11
0
 public ExternalLndGrpc(LightningConnectionString connectionString) : base(connectionString, "lnd-grpc")
 {
 }
Beispiel #12
0
 public ExternalLnd(LightningConnectionString connectionString, string type)
 {
     ConnectionString = connectionString;
     Type             = type;
 }
Beispiel #13
0
        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;
 }
Beispiel #16
0
        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);
            }
Beispiel #19
0
        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));
            }
        }