2017-09-13 08:47:34 +02:00
using BTCPayServer.Logging ;
using System.Linq ;
using Microsoft.Extensions.Logging ;
using NBitcoin ;
using System ;
using System.Collections.Generic ;
using System.IO ;
using System.Net ;
2017-09-22 18:31:29 +02:00
using Microsoft.Extensions.Configuration ;
2018-08-13 02:43:59 +02:00
using BTCPayServer.SSH ;
2018-08-30 04:50:39 +02:00
using BTCPayServer.Lightning ;
2018-11-07 14:29:35 +01:00
using Serilog.Events ;
2017-09-13 08:47:34 +02:00
namespace BTCPayServer.Configuration
{
2018-01-12 08:00:31 +01:00
public class NBXplorerConnectionSetting
{
public string CryptoCode { get ; internal set ; }
public Uri ExplorerUri { get ; internal set ; }
public string CookieFile { get ; internal set ; }
}
2017-10-27 10:53:04 +02:00
public class BTCPayServerOptions
{
2018-04-19 09:54:25 +02:00
public NetworkType NetworkType
2017-10-27 10:53:04 +02:00
{
get ; set ;
}
public string ConfigurationFile
2018-11-07 14:29:35 +01:00
{
get ;
private set ;
2018-12-20 14:40:32 +01:00
}
2018-11-07 14:29:35 +01:00
public string LogFile
2017-10-27 10:53:04 +02:00
{
get ;
private set ;
}
public string DataDir
{
get ;
private set ;
}
2019-03-17 16:03:02 +01:00
public EndPoint SocksEndpoint { get ; set ; }
2019-05-14 17:46:43 +02:00
2018-01-12 08:00:31 +01:00
public List < NBXplorerConnectionSetting > NBXplorerConnectionSettings
{
get ;
set ;
} = new List < NBXplorerConnectionSetting > ( ) ;
2019-01-06 16:43:55 +01:00
public bool DisableRegistration
{
get ;
private set ;
}
2018-11-07 14:29:35 +01:00
public static string GetDebugLog ( IConfiguration configuration )
{
return configuration . GetValue < string > ( "debuglog" , null ) ;
}
public static LogEventLevel GetDebugLogLevel ( IConfiguration configuration )
{
var raw = configuration . GetValue ( "debugloglevel" , nameof ( LogEventLevel . Debug ) ) ;
2018-12-20 14:40:32 +01:00
return ( LogEventLevel ) Enum . Parse ( typeof ( LogEventLevel ) , raw , true ) ;
2018-11-07 14:29:35 +01:00
}
2017-10-27 10:53:04 +02:00
public void LoadArgs ( IConfiguration conf )
{
2018-04-19 09:54:25 +02:00
NetworkType = DefaultConfiguration . GetNetworkType ( conf ) ;
2019-05-14 17:46:43 +02:00
DataDir = conf . GetDataDir ( NetworkType ) ;
2018-04-19 09:54:25 +02:00
Logs . Configuration . LogInformation ( "Network: " + NetworkType . ToString ( ) ) ;
2017-09-13 08:47:34 +02:00
2019-05-14 17:55:15 +02:00
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" ) ;
2018-01-11 14:52:28 +01:00
var supportedChains = conf . GetOrDefault < string > ( "chains" , "btc" )
. Split ( ',' , StringSplitOptions . RemoveEmptyEntries )
2018-01-12 03:54:57 +01:00
. Select ( t = > t . ToUpperInvariant ( ) ) ;
2018-04-19 09:54:25 +02:00
NetworkProvider = new BTCPayNetworkProvider ( NetworkType ) . Filter ( supportedChains . ToArray ( ) ) ;
2018-02-25 16:48:12 +01:00
foreach ( var chain in supportedChains )
{
2019-05-29 11:43:50 +02:00
if ( NetworkProvider . GetNetwork < BTCPayNetworkBase > ( chain ) = = null )
2018-02-25 16:48:12 +01:00
throw new ConfigException ( $"Invalid chains \" { chain } \ "" ) ;
}
2018-01-11 14:52:28 +01:00
var validChains = new List < string > ( ) ;
2019-05-29 11:43:50 +02:00
foreach ( var net in NetworkProvider . GetAll ( ) . OfType < BTCPayNetwork > ( ) )
2018-01-07 18:36:41 +01:00
{
2018-02-25 16:48:12 +01:00
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 ) ;
2018-10-27 15:49:39 +02:00
2018-03-20 18:09:25 +01:00
{
2018-07-23 04:53:39 +02:00
var lightning = conf . GetOrDefault < string > ( $"{net.CryptoCode}.lightning" , string . Empty ) ;
if ( lightning . Length ! = 0 )
2018-03-20 18:09:25 +01:00
{
2018-07-23 04:53:39 +02:00
if ( ! LightningConnectionString . TryParse ( lightning , true , out var connectionString , out var error ) )
{
2019-02-22 10:24:27 +01:00
Logs . Configuration . LogWarning ( $"Invalid setting {net.CryptoCode}.lightning, " + Environment . NewLine +
2019-01-18 15:23:47 +01:00
$"If you have a c-lightning server use: 'type=clightning;server=/root/.lightning/lightning-rpc', " + Environment . NewLine +
2018-07-23 04:53:39 +02:00
$"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:lnd@lnd.example.com;macaroon=abf239...;certthumbprint=2abdf302...'" + Environment . NewLine +
$" lnd server: 'type=lnd-rest;server=https://lnd:lnd@lnd.example.com;macaroonfilepath=/root/.lnd/admin.macaroon;certthumbprint=2abdf302...'" + Environment . NewLine +
2019-05-20 03:13:11 +02:00
$"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 +
2019-02-22 10:24:27 +01:00
$"Error: {error}" + Environment . NewLine +
"This service will not be exposed through BTCPay Server" ) ;
2018-07-23 04:53:39 +02:00
}
2019-02-22 10:24:27 +01:00
else
2018-07-23 04:53:39 +02:00
{
2019-02-22 10:24:27 +01:00
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 ) ;
2018-07-23 04:53:39 +02:00
}
2018-07-01 08:45:08 +02:00
}
2018-07-23 04:53:39 +02:00
}
2019-03-01 05:20:21 +01:00
ExternalServices . Load ( net . CryptoCode , conf ) ;
2018-01-07 18:36:41 +01:00
}
2018-01-12 03:54:57 +01:00
Logs . Configuration . LogInformation ( "Supported chains: " + String . Join ( ',' , supportedChains . ToArray ( ) ) ) ;
2018-02-25 16:48:12 +01:00
2018-12-07 10:42:39 +01:00
var services = conf . GetOrDefault < string > ( "externalservices" , null ) ;
2018-12-20 14:40:32 +01:00
if ( services ! = null )
2018-12-07 10:42:39 +01:00
{
2019-01-23 05:31:00 +01:00
foreach ( var service in services . Split ( new [ ] { ';' , ',' } , StringSplitOptions . RemoveEmptyEntries )
. Select ( p = > ( p , SeparatorIndex : p . IndexOf ( ':' , StringComparison . OrdinalIgnoreCase ) ) )
. Where ( p = > p . SeparatorIndex ! = - 1 )
. Select ( p = > ( Name : p . p . Substring ( 0 , p . SeparatorIndex ) ,
Link : p . p . Substring ( p . SeparatorIndex + 1 ) ) ) )
2018-12-07 10:42:39 +01:00
{
2019-03-01 06:46:32 +01:00
if ( Uri . TryCreate ( service . Link , UriKind . RelativeOrAbsolute , out var uri ) )
OtherExternalServices . AddOrReplace ( service . Name , uri ) ;
2018-12-07 10:42:39 +01:00
}
}
2017-10-27 10:53:04 +02:00
PostgresConnectionString = conf . GetOrDefault < string > ( "postgres" , null ) ;
2018-10-27 16:15:21 +02:00
MySQLConnectionString = conf . GetOrDefault < string > ( "mysql" , null ) ;
2018-02-21 07:48:25 +01:00
BundleJsCss = conf . GetOrDefault < bool > ( "bundlejscss" , true ) ;
2019-05-13 10:00:58 +02:00
AllowAdminRegistration = conf . GetOrDefault < bool > ( "allow-admin-registration" , false ) ;
2019-03-17 12:49:26 +01:00
TorrcFile = conf . GetOrDefault < string > ( "torrcfile" , null ) ;
2019-03-31 06:16:05 +02:00
var socksEndpointString = conf . GetOrDefault < string > ( "socksendpoint" , null ) ;
if ( ! string . IsNullOrEmpty ( socksEndpointString ) )
{
if ( ! Utils . TryParseEndpoint ( socksEndpointString , 9050 , out var endpoint ) )
throw new ConfigException ( "Invalid value for socksendpoint" ) ;
SocksEndpoint = endpoint ;
}
2018-04-05 08:50:23 +02:00
2018-08-13 02:43:59 +02:00
var sshSettings = ParseSSHConfiguration ( conf ) ;
2018-08-12 16:23:26 +02:00
if ( ( ! string . IsNullOrEmpty ( sshSettings . Password ) | | ! string . IsNullOrEmpty ( sshSettings . KeyFile ) ) & & ! string . IsNullOrEmpty ( sshSettings . Server ) )
2018-08-12 14:38:45 +02:00
{
2018-08-13 02:43:59 +02:00
int waitTime = 0 ;
while ( ! string . IsNullOrEmpty ( sshSettings . KeyFile ) & & ! File . Exists ( sshSettings . KeyFile ) )
{
2018-10-27 15:49:39 +02:00
if ( waitTime + + < 5 )
2018-08-13 02:43:59 +02:00
System . Threading . Thread . Sleep ( 1000 ) ;
else
throw new ConfigException ( $"sshkeyfile does not exist" ) ;
}
2018-10-27 15:49:39 +02:00
2018-08-12 14:38:45 +02:00
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 ( ) ;
2019-08-25 05:25:42 +02:00
SSHSettings = sshSettings ;
}
catch ( NotSupportedException ex )
{
Logs . Configuration . LogWarning ( $"The SSH key is not supported ({ex.Message}), try to generate the key with ssh-keygen using \" - m PEM \ ". Skipping SSH configuration..." ) ;
2018-08-12 14:38:45 +02:00
}
catch
{
throw new ConfigException ( $"sshkeyfilepassword is invalid" ) ;
}
}
2018-08-12 16:23:26 +02:00
var fingerPrints = conf . GetOrDefault < string > ( "sshtrustedfingerprints" , "" ) ;
if ( ! string . IsNullOrEmpty ( fingerPrints ) )
{
2018-08-13 02:43:59 +02:00
foreach ( var fingerprint in fingerPrints . Split ( ';' , StringSplitOptions . RemoveEmptyEntries ) )
2018-08-12 16:23:26 +02:00
{
2018-08-13 02:43:59 +02:00
if ( ! SSHFingerprint . TryParse ( fingerprint , out var f ) )
throw new ConfigException ( $"Invalid ssh fingerprint format {fingerprint}" ) ;
2019-08-27 16:30:25 +02:00
SSHSettings ? . TrustedFingerprints . Add ( f ) ;
2018-08-12 16:23:26 +02:00
}
}
2018-04-05 08:50:23 +02:00
RootPath = conf . GetOrDefault < string > ( "rootpath" , "/" ) ;
2018-04-09 07:31:39 +02:00
if ( ! RootPath . StartsWith ( "/" , StringComparison . InvariantCultureIgnoreCase ) )
RootPath = "/" + RootPath ;
2018-03-20 18:09:25 +01:00
var old = conf . GetOrDefault < Uri > ( "internallightningnode" , null ) ;
2018-07-23 04:53:39 +02:00
if ( old ! = null )
2019-01-18 15:23:47 +01:00
throw new ConfigException ( $"internallightningnode is deprecated and should not be used anymore, use btclightning instead" ) ;
2018-11-07 14:29:35 +01:00
LogFile = GetDebugLog ( conf ) ;
if ( ! string . IsNullOrEmpty ( LogFile ) )
2019-10-31 04:29:59 +01:00
{
if ( ! Path . IsPathRooted ( LogFile ) )
LogFile = Path . Combine ( DataDir , LogFile ) ;
}
if ( ! string . IsNullOrEmpty ( LogFile ) )
2018-11-07 14:29:35 +01:00
{
Logs . Configuration . LogInformation ( "LogFile: " + LogFile ) ;
Logs . Configuration . LogInformation ( "Log Level: " + GetDebugLogLevel ( conf ) ) ;
}
2019-01-06 16:43:55 +01:00
DisableRegistration = conf . GetOrDefault < bool > ( "disable-registration" , true ) ;
2017-10-27 10:53:04 +02:00
}
2018-08-12 16:23:26 +02:00
2018-08-13 02:43:59 +02:00
private SSHSettings ParseSSHConfiguration ( IConfiguration conf )
2018-08-12 16:23:26 +02:00
{
2018-08-13 02:43:59 +02:00
var settings = new SSHSettings ( ) ;
settings . Server = conf . GetOrDefault < string > ( "sshconnection" , null ) ;
if ( settings . Server ! = null )
2018-08-12 16:23:26 +02:00
{
2018-08-13 02:43:59 +02:00
var parts = settings . Server . Split ( ':' ) ;
if ( parts . Length = = 2 & & int . TryParse ( parts [ 1 ] , out int port ) )
{
settings . Port = port ;
settings . Server = parts [ 0 ] ;
}
else
{
settings . Port = 22 ;
}
2018-08-12 16:23:26 +02:00
2018-08-13 02:43:59 +02:00
parts = settings . Server . Split ( '@' ) ;
if ( parts . Length = = 2 )
{
settings . Username = parts [ 0 ] ;
settings . Server = parts [ 1 ] ;
}
else
{
settings . Username = "root" ;
}
2018-08-12 16:23:26 +02:00
}
2018-08-13 02:43:59 +02:00
settings . Password = conf . GetOrDefault < string > ( "sshpassword" , "" ) ;
settings . KeyFile = conf . GetOrDefault < string > ( "sshkeyfile" , "" ) ;
2019-09-20 11:51:14 +02:00
settings . AuthorizedKeysFile = conf . GetOrDefault < string > ( "sshauthorizedkeys" , "" ) ;
2018-08-13 02:43:59 +02:00
settings . KeyFilePassword = conf . GetOrDefault < string > ( "sshkeyfilepassword" , "" ) ;
return settings ;
2018-08-12 16:23:26 +02:00
}
2018-04-05 08:50:23 +02:00
public string RootPath { get ; set ; }
2018-03-20 18:09:25 +01:00
public Dictionary < string , LightningConnectionString > InternalLightningByCryptoCode { get ; set ; } = new Dictionary < string , LightningConnectionString > ( ) ;
2018-12-07 10:42:39 +01:00
2019-03-01 06:46:32 +01:00
public Dictionary < string , Uri > OtherExternalServices { get ; set ; } = new Dictionary < string , Uri > ( ) ;
2019-03-01 05:20:21 +01:00
public ExternalServices ExternalServices { get ; set ; } = new ExternalServices ( ) ;
2018-02-26 10:58:02 +01:00
2018-02-25 16:48:12 +01:00
public BTCPayNetworkProvider NetworkProvider { get ; set ; }
2017-10-27 10:53:04 +02:00
public string PostgresConnectionString
{
get ;
set ;
}
2018-10-27 16:15:21 +02:00
public string MySQLConnectionString
{
get ;
set ;
}
2018-02-21 07:48:25 +01:00
public bool BundleJsCss
{
get ;
set ;
}
2019-05-13 10:00:58 +02:00
public bool AllowAdminRegistration { get ; set ; }
2018-08-12 14:38:45 +02:00
public SSHSettings SSHSettings
{
get ;
set ;
}
2019-03-17 12:49:26 +01:00
public string TorrcFile { get ; set ; }
2017-10-27 10:53:04 +02:00
}
2017-09-13 08:47:34 +02:00
}