2024-04-04 09:31:04 +02:00
2020-06-29 04:44:35 +02:00
using System ;
2024-01-17 10:08:39 +01:00
using System.Collections.Concurrent ;
2017-09-13 08:47:34 +02:00
using System.Collections.Generic ;
2023-11-21 10:55:02 +01:00
using System.ComponentModel.DataAnnotations ;
2020-06-28 10:55:27 +02:00
using System.Globalization ;
2021-01-06 15:51:13 +01:00
using System.IO ;
2020-06-28 10:55:27 +02:00
using System.Linq ;
2024-12-04 13:56:45 +01:00
using System.Linq.Expressions ;
2020-06-28 10:55:27 +02:00
using System.Net ;
using System.Net.WebSockets ;
2020-07-27 06:27:47 +02:00
using System.Reflection ;
2020-06-28 10:55:27 +02:00
using System.Security.Claims ;
2017-09-13 08:47:34 +02:00
using System.Text ;
2024-01-17 10:08:39 +01:00
using System.Text.RegularExpressions ;
2017-11-06 09:31:02 +01:00
using System.Threading ;
2020-06-28 10:55:27 +02:00
using System.Threading.Tasks ;
2024-10-07 10:38:02 +02:00
using BTCPayServer.Abstractions.Contracts ;
using BTCPayServer.Abstractions.Services ;
2021-12-31 08:59:02 +01:00
using BTCPayServer.BIP78.Sender ;
2020-06-28 10:55:27 +02:00
using BTCPayServer.Configuration ;
using BTCPayServer.Data ;
2023-05-28 16:44:10 +02:00
using BTCPayServer.HostedServices ;
2020-06-28 10:55:27 +02:00
using BTCPayServer.Lightning ;
2018-04-03 04:50:41 +02:00
using BTCPayServer.Models ;
2020-07-15 19:51:01 +02:00
using BTCPayServer.Models.StoreViewModels ;
2023-12-21 02:29:28 +01:00
using BTCPayServer.NTag424 ;
2020-06-28 10:55:27 +02:00
using BTCPayServer.Payments ;
using BTCPayServer.Payments.Bitcoin ;
2024-04-04 09:31:04 +02:00
using BTCPayServer.Payments.Lightning ;
2024-05-01 03:22:07 +02:00
using BTCPayServer.Payouts ;
2022-06-29 16:18:02 +02:00
using BTCPayServer.Security ;
2024-10-14 07:11:00 +02:00
using BTCPayServer.Services ;
2020-06-28 10:55:27 +02:00
using BTCPayServer.Services.Invoices ;
2023-07-24 02:24:32 +02:00
using BTCPayServer.Services.Reporting ;
2020-06-28 10:55:27 +02:00
using BTCPayServer.Services.Wallets ;
using Microsoft.AspNetCore.Http ;
2024-04-04 09:31:04 +02:00
using Microsoft.AspNetCore.HttpOverrides ;
2020-07-15 19:51:01 +02:00
using Microsoft.AspNetCore.Mvc ;
2020-06-28 10:55:27 +02:00
using Microsoft.Extensions.Configuration ;
2023-05-28 16:44:10 +02:00
using Microsoft.Extensions.DependencyInjection ;
2020-06-28 10:55:27 +02:00
using NBitcoin ;
using NBitcoin.Payment ;
2024-01-17 10:08:39 +01:00
using NBXplorer.DerivationStrategy ;
2020-06-28 10:55:27 +02:00
using NBXplorer.Models ;
2021-12-31 08:36:38 +01:00
using Newtonsoft.Json ;
2021-10-25 08:18:02 +02:00
using InvoiceCryptoInfo = BTCPayServer . Services . Invoices . InvoiceCryptoInfo ;
2017-09-13 08:47:34 +02:00
namespace BTCPayServer
{
2017-10-27 10:53:04 +02:00
public static class Extensions
{
2024-04-04 09:31:04 +02:00
/// <summary>
/// Outputs a serializer which will serialize default and null members.
/// This is useful for discovering the API.
/// </summary>
/// <param name="settings"></param>
/// <returns></returns>
public static JsonSerializer ForAPI ( this JsonSerializer settings )
{
var clone = new JsonSerializer ( )
{
CheckAdditionalContent = settings . CheckAdditionalContent ,
ConstructorHandling = settings . ConstructorHandling ,
ContractResolver = settings . ContractResolver ,
Culture = settings . Culture ,
DateFormatHandling = settings . DateFormatHandling ,
DateFormatString = settings . DateFormatString ,
DateParseHandling = settings . DateParseHandling ,
DateTimeZoneHandling = settings . DateTimeZoneHandling ,
DefaultValueHandling = settings . DefaultValueHandling ,
EqualityComparer = settings . EqualityComparer ,
FloatFormatHandling = settings . FloatFormatHandling ,
FloatParseHandling = settings . FloatParseHandling ,
Formatting = settings . Formatting ,
MaxDepth = settings . MaxDepth ,
MetadataPropertyHandling = settings . MetadataPropertyHandling ,
Context = settings . Context ,
MissingMemberHandling = settings . MissingMemberHandling ,
NullValueHandling = settings . NullValueHandling ,
ObjectCreationHandling = settings . ObjectCreationHandling ,
PreserveReferencesHandling = settings . PreserveReferencesHandling ,
ReferenceLoopHandling = settings . ReferenceLoopHandling ,
StringEscapeHandling = settings . StringEscapeHandling ,
TraceWriter = settings . TraceWriter ,
TypeNameAssemblyFormatHandling = settings . TypeNameAssemblyFormatHandling ,
SerializationBinder = settings . SerializationBinder ,
TypeNameHandling = settings . TypeNameHandling ,
ReferenceResolver = settings . ReferenceResolver
} ;
foreach ( var conv in settings . Converters )
clone . Converters . Add ( conv ) ;
clone . NullValueHandling = NullValueHandling . Include ;
clone . DefaultValueHandling = DefaultValueHandling . Include ;
return clone ;
}
2024-01-17 10:08:39 +01:00
public static DerivationSchemeParser GetDerivationSchemeParser ( this BTCPayNetwork network )
{
2024-01-18 04:31:59 +01:00
return new DerivationSchemeParser ( network ) ;
2024-01-17 10:08:39 +01:00
}
public static bool TryParseXpub ( this DerivationSchemeParser derivationSchemeParser , string xpub ,
2024-01-24 09:49:15 +01:00
ref DerivationSchemeSettings derivationSchemeSettings , bool electrum = false )
2024-01-17 10:08:39 +01:00
{
2024-01-24 09:49:15 +01:00
if ( ! electrum )
{
var isOD = Regex . Match ( xpub , @"\(.*?\)" ) . Success ;
try
{
var result = derivationSchemeParser . ParseOutputDescriptor ( xpub ) ;
derivationSchemeSettings . AccountOriginal = xpub . Trim ( ) ;
derivationSchemeSettings . AccountDerivation = result . Item1 ;
derivationSchemeSettings . AccountKeySettings = result . Item2 . Select ( ( path , i ) = > new AccountKeySettings ( )
{
RootFingerprint = path ? . MasterFingerprint ,
AccountKeyPath = path ? . KeyPath ,
AccountKey = result . Item1 . GetExtPubKeys ( ) . ElementAt ( i ) . GetWif ( derivationSchemeParser . Network )
} ) . ToArray ( ) ;
return true ;
}
catch ( Exception )
{
if ( isOD )
{
return false ;
} // otherwise continue and try to parse input as xpub
}
}
2024-01-17 10:08:39 +01:00
try
{
// Extract fingerprint and account key path from export formats that contain them.
// Possible formats: [fingerprint/account_key_path]xpub, [fingerprint]xpub, xpub
HDFingerprint ? rootFingerprint = null ;
KeyPath accountKeyPath = null ;
var derivationRegex = new Regex ( @"^(?:\[(\w+)(?:\/(.*?))?\])?(\w+)$" , RegexOptions . IgnoreCase ) ;
var match = derivationRegex . Match ( xpub . Trim ( ) ) ;
if ( match . Success )
{
if ( ! string . IsNullOrEmpty ( match . Groups [ 1 ] . Value ) )
rootFingerprint = HDFingerprint . Parse ( match . Groups [ 1 ] . Value ) ;
if ( ! string . IsNullOrEmpty ( match . Groups [ 2 ] . Value ) )
accountKeyPath = KeyPath . Parse ( match . Groups [ 2 ] . Value ) ;
if ( ! string . IsNullOrEmpty ( match . Groups [ 3 ] . Value ) )
xpub = match . Groups [ 3 ] . Value ;
}
derivationSchemeSettings . AccountOriginal = xpub . Trim ( ) ;
2024-01-24 09:49:15 +01:00
derivationSchemeSettings . AccountDerivation = electrum ? derivationSchemeParser . ParseElectrum ( derivationSchemeSettings . AccountOriginal ) : derivationSchemeParser . Parse ( derivationSchemeSettings . AccountOriginal ) ;
2024-01-17 10:08:39 +01:00
derivationSchemeSettings . AccountKeySettings = derivationSchemeSettings . AccountDerivation . GetExtPubKeys ( )
2024-01-24 09:49:15 +01:00
. Select ( key = > new AccountKeySettings
{
AccountKey = key . GetWif ( derivationSchemeParser . Network )
} ) . ToArray ( ) ;
2024-01-17 10:08:39 +01:00
if ( derivationSchemeSettings . AccountDerivation is DirectDerivationStrategy direct & & ! direct . Segwit )
2024-01-24 09:49:15 +01:00
derivationSchemeSettings . AccountOriginal = null ; // Saving this would be confusing for user, as xpub of electrum is legacy derivation, but for btcpay, it is segwit derivation
2024-01-17 10:08:39 +01:00
// apply initial matches if there were no results from parsing
if ( rootFingerprint ! = null & & derivationSchemeSettings . AccountKeySettings [ 0 ] . RootFingerprint = = null )
{
derivationSchemeSettings . AccountKeySettings [ 0 ] . RootFingerprint = rootFingerprint ;
}
if ( accountKeyPath ! = null & & derivationSchemeSettings . AccountKeySettings [ 0 ] . AccountKeyPath = = null )
{
derivationSchemeSettings . AccountKeySettings [ 0 ] . AccountKeyPath = accountKeyPath ;
}
return true ;
}
2024-01-24 09:49:15 +01:00
catch ( Exception )
2024-01-17 10:08:39 +01:00
{
return false ;
}
}
2024-01-24 09:49:15 +01:00
2023-12-21 02:29:28 +01:00
public static CardKey CreatePullPaymentCardKey ( this IssuerKey issuerKey , byte [ ] uid , int version , string pullPaymentId )
{
var data = Encoding . UTF8 . GetBytes ( pullPaymentId ) ;
return issuerKey . CreateCardKey ( uid , version , data ) ;
}
2023-07-19 11:47:32 +02:00
public static DateTimeOffset TruncateMilliSeconds ( this DateTimeOffset dt ) = > new ( dt . Year , dt . Month , dt . Day , dt . Hour , dt . Minute , dt . Second , 0 , dt . Offset ) ;
public static decimal? GetDue ( this InvoiceCryptoInfo invoiceCryptoInfo )
{
if ( invoiceCryptoInfo is null )
return null ;
if ( decimal . TryParse ( invoiceCryptoInfo . Due , NumberStyles . Any , CultureInfo . InvariantCulture , out var v ) )
return v ;
return null ;
}
2023-02-14 09:03:12 +01:00
public static Task < BufferizedFormFile > Bufferize ( this IFormFile formFile )
{
return BufferizedFormFile . Bufferize ( formFile ) ;
}
2023-02-06 10:17:17 +01:00
/// <summary>
/// Unescape Uri string for %2F
/// See details at: https://github.com/dotnet/aspnetcore/issues/14170#issuecomment-533342396
/// </summary>
/// <param name="uriString">The Uri string.</param>
/// <returns>Unescaped back slash Uri string.</returns>
public static string UnescapeBackSlashUriString ( string uriString )
{
if ( uriString = = null )
{
return null ;
}
return uriString . Replace ( "%2f" , "%2F" ) . Replace ( "%2F" , "/" ) ;
}
2022-07-15 05:37:47 +02:00
public static bool IsValidEmail ( this string email )
{
if ( string . IsNullOrEmpty ( email ) )
{
return false ;
}
return MailboxAddressValidator . TryParse ( email , out var ma ) & & ma . ToString ( ) = = ma . Address ;
}
2023-01-06 14:18:07 +01:00
2020-06-17 14:43:56 +02:00
public static bool TryGetPayjoinEndpoint ( this BitcoinUrlBuilder bip21 , out Uri endpoint )
{
2022-01-07 04:32:00 +01:00
endpoint = bip21 . UnknownParameters . TryGetValue ( $"{PayjoinClient.BIP21EndpointKey}" , out var uri ) ? new Uri ( uri , UriKind . Absolute ) : null ;
2020-06-17 14:43:56 +02:00
return endpoint ! = null ;
}
2021-03-02 03:11:58 +01:00
2024-12-09 10:14:40 +01:00
[Obsolete("Use GetServerUri(this ILightningClient client, string connectionString) instead")]
public static Uri GetServerUri ( this ILightningClient client ) = > GetServerUri ( client , client . ToString ( ) ) ;
public static Uri GetServerUri ( this ILightningClient client , string connectionString )
2020-05-29 02:00:13 +02:00
{
2024-12-09 10:14:40 +01:00
if ( client is IExtendedLightningClient { ServerUri : { } uri } )
return uri ;
var kv = client . ExtractValues ( connectionString ) ;
2023-11-21 10:55:02 +01:00
return ! kv . TryGetValue ( "server" , out var server ) ? null : new Uri ( server , UriKind . Absolute ) ;
}
2024-12-09 10:14:40 +01:00
[Obsolete("Use GetDisplayName(this ILightningClient client, string connectionString) instead")]
public static string GetDisplayName ( this ILightningClient client ) = > GetDisplayName ( client , client . ToString ( ) ) ;
public static string GetDisplayName ( this ILightningClient client , string connectionString )
2023-11-21 10:55:02 +01:00
{
2024-12-09 10:14:40 +01:00
if ( client is IExtendedLightningClient { DisplayName : { } displayName } )
return displayName ;
var kv = client . ExtractValues ( connectionString ) ;
if ( ! kv . TryGetValue ( "type" , out var type ) )
return "???" ;
2023-12-20 11:26:24 +01:00
var lncType = typeof ( LightningConnectionType ) ;
var fields = lncType . GetFields ( BindingFlags . Public | BindingFlags . Static ) ;
var field = fields . FirstOrDefault ( f = > f . GetValue ( lncType ) ? . ToString ( ) = = type ) ;
2023-11-21 10:55:02 +01:00
if ( field = = null ) return type ;
DisplayAttribute attr = field . GetCustomAttribute < DisplayAttribute > ( ) ;
return attr ? . Name ? ? type ;
}
2024-12-09 10:14:40 +01:00
private static bool TryParseLegacy ( string str , out Dictionary < string , string > connectionString )
{
if ( str . StartsWith ( "/" ) )
{
str = "unix:" + str ;
}
Dictionary < string , string > dictionary = new Dictionary < string , string > ( ) ;
connectionString = null ;
if ( ! Uri . TryCreate ( str , UriKind . Absolute , out Uri result ) )
{
return false ;
}
if ( ! new string [ 4 ] { "unix" , "tcp" , "http" , "https" } . Contains ( result . Scheme ) )
{
return false ;
}
if ( result . Scheme = = "unix" )
{
str = result . AbsoluteUri . Substring ( "unix:" . Length ) ;
while ( str . Length > = 1 & & str [ 0 ] = = '/' )
{
str = str . Substring ( 1 ) ;
}
result = new Uri ( "unix://" + str , UriKind . Absolute ) ;
dictionary . Add ( "type" , "clightning" ) ;
}
if ( result . Scheme = = "tcp" )
{
dictionary . Add ( "type" , "clightning" ) ;
}
if ( result . Scheme = = "http" | | result . Scheme = = "https" )
{
string [ ] array = result . UserInfo . Split ( ':' ) ;
if ( string . IsNullOrEmpty ( result . UserInfo ) | | array . Length ! = 2 )
{
return false ;
}
dictionary . Add ( "type" , "charge" ) ;
dictionary . Add ( "username" , array [ 0 ] ) ;
dictionary . Add ( "password" , array [ 1 ] ) ;
if ( result . Scheme = = "http" )
{
dictionary . Add ( "allowinsecure" , "true" ) ;
}
}
else if ( ! string . IsNullOrEmpty ( result . UserInfo ) )
{
return false ;
}
dictionary . Add ( "server" , new UriBuilder ( result )
{
UserName = "" ,
Password = ""
} . Uri . ToString ( ) ) ;
connectionString = dictionary ;
return true ;
}
static Dictionary < string , string > ExtractValues ( this ILightningClient client , string connectionString )
{
ArgumentNullException . ThrowIfNull ( connectionString ) ;
if ( TryParseLegacy ( connectionString , out var legacy ) )
return legacy ;
string [ ] source = connectionString . Split ( new char [ 1 ] { ';' } , StringSplitOptions . RemoveEmptyEntries ) ;
var kv = new Dictionary < string , string > ( ) ;
foreach ( string item in source . Select ( ( string p ) = > p . Trim ( ) ) )
{
int num = item . IndexOf ( '=' ) ;
if ( num = = - 1 )
continue ;
string text = item . Substring ( 0 , num ) . Trim ( ) . ToLowerInvariant ( ) ;
string value = item . Substring ( num + 1 ) . Trim ( ) ;
kv . TryAdd ( text , value ) ;
}
return kv ;
}
[Obsolete("Use IsSafe(this ILightningClient client, string connectionString) instead")]
public static bool IsSafe ( this ILightningClient client ) = > IsSafe ( client , client . ToString ( ) ) ;
public static bool IsSafe ( this ILightningClient client , string connectionString )
2023-11-21 10:55:02 +01:00
{
2024-12-09 10:14:40 +01:00
var kv = client . ExtractValues ( connectionString ) ;
2023-12-20 11:26:24 +01:00
if ( kv . TryGetValue ( "cookiefilepath" , out _ ) | |
kv . TryGetValue ( "macaroondirectorypath" , out _ ) | |
kv . TryGetValue ( "macaroonfilepath" , out _ ) )
2021-03-02 03:11:58 +01:00
return false ;
2020-05-29 02:00:13 +02:00
2023-11-21 10:55:02 +01:00
if ( ! kv . TryGetValue ( "server" , out var server ) )
{
return true ;
}
var uri = new Uri ( server , UriKind . Absolute ) ;
2021-03-02 03:11:58 +01:00
if ( uri . Scheme . Equals ( "unix" , StringComparison . OrdinalIgnoreCase ) )
return false ;
2023-12-20 11:26:24 +01:00
if ( ! Utils . TryParseEndpoint ( uri . DnsSafeHost , 80 , out _ ) )
2021-03-02 03:11:58 +01:00
return false ;
2023-11-21 10:55:02 +01:00
return ! IsLocalNetwork ( uri . DnsSafeHost ) ;
2020-05-29 02:00:13 +02:00
}
2020-06-28 10:55:27 +02:00
2019-10-04 15:55:11 +02:00
public static IQueryable < TEntity > Where < TEntity > ( this Microsoft . EntityFrameworkCore . DbSet < TEntity > obj , System . Linq . Expressions . Expression < Func < TEntity , bool > > predicate ) where TEntity : class
2019-10-04 10:17:11 +02:00
{
2019-10-04 15:55:11 +02:00
return System . Linq . Queryable . Where ( obj , predicate ) ;
2019-10-04 10:17:11 +02:00
}
2019-10-04 15:55:11 +02:00
2018-04-18 11:23:39 +02:00
public static string PrettyPrint ( this TimeSpan expiration )
{
StringBuilder builder = new StringBuilder ( ) ;
if ( expiration . Days > = 1 )
builder . Append ( expiration . Days . ToString ( CultureInfo . InvariantCulture ) ) ;
if ( expiration . Hours > = 1 )
builder . Append ( expiration . Hours . ToString ( "00" , CultureInfo . InvariantCulture ) ) ;
2021-12-27 05:46:31 +01:00
builder . Append ( CultureInfo . InvariantCulture , $"{expiration.Minutes.ToString(" 00 ", CultureInfo.InvariantCulture)}:{expiration.Seconds.ToString(" 00 ", CultureInfo.InvariantCulture)}" ) ;
2018-04-18 11:23:39 +02:00
return builder . ToString ( ) ;
}
2023-01-06 14:18:07 +01:00
2018-03-17 18:26:33 +01:00
public static decimal RoundUp ( decimal value , int precision )
{
2023-06-16 03:47:58 +02:00
try
2018-03-17 18:26:33 +01:00
{
2023-06-16 03:47:58 +02:00
for ( int i = 0 ; i < precision ; i + + )
{
value = value * 10 m ;
}
value = Math . Ceiling ( value ) ;
for ( int i = 0 ; i < precision ; i + + )
{
value = value / 10 m ;
}
return value ;
2018-03-17 18:26:33 +01:00
}
2023-06-16 03:47:58 +02:00
catch ( OverflowException )
2018-03-17 18:26:33 +01:00
{
2023-06-16 03:47:58 +02:00
return value ;
2018-03-17 18:26:33 +01:00
}
}
2019-10-31 04:29:59 +01:00
2024-10-14 07:11:00 +02:00
#nullable enable
2024-10-18 09:06:51 +02:00
public static IServiceCollection AddDefaultTranslations ( this IServiceCollection services , params string [ ] keyValues )
2024-10-14 07:11:00 +02:00
{
2024-10-18 09:06:51 +02:00
return services . AddDefaultTranslations ( keyValues . Select ( k = > KeyValuePair . Create < string , string? > ( k , string . Empty ) ) . ToArray ( ) ) ;
2024-10-14 07:11:00 +02:00
}
2024-10-14 14:13:43 +02:00
public static IServiceCollection AddDefaultPrettyName ( this IServiceCollection services , PaymentMethodId paymentMethodId , string defaultPrettyName )
{
2024-10-20 04:49:36 +02:00
services . AddSingleton < PrettyNameProvider . UntranslatedPrettyName > ( new PrettyNameProvider . UntranslatedPrettyName ( paymentMethodId , defaultPrettyName ) ) ;
return services . AddDefaultTranslations ( KeyValuePair . Create < string , string? > ( PrettyNameProvider . GetTranslationKey ( paymentMethodId ) , defaultPrettyName ) ) ;
2024-10-14 14:13:43 +02:00
}
2024-10-18 09:06:51 +02:00
public static IServiceCollection AddDefaultTranslations ( this IServiceCollection services , params KeyValuePair < string , string? > [ ] keyValues )
2024-10-14 07:11:00 +02:00
{
2024-10-18 09:06:51 +02:00
services . AddSingleton < IDefaultTranslationProvider > ( new InMemoryDefaultTranslationProvider ( keyValues ) ) ;
2024-10-14 07:11:00 +02:00
return services ;
}
#nullable restore
2024-10-07 11:19:19 +02:00
public static IServiceCollection AddUIExtension ( this IServiceCollection services , string location , string partialViewName )
2024-10-07 10:38:02 +02:00
{
2024-10-07 14:33:53 +02:00
#pragma warning disable CS0618 // Type or member is obsolete
2024-10-07 11:19:19 +02:00
services . AddSingleton < IUIExtension > ( new UIExtension ( partialViewName , location ) ) ;
2024-10-07 14:33:53 +02:00
#pragma warning restore CS0618 // Type or member is obsolete
2024-10-07 10:38:02 +02:00
return services ;
}
2023-07-24 02:24:32 +02:00
public static IServiceCollection AddReportProvider < T > ( this IServiceCollection services )
where T : ReportProvider
{
services . AddSingleton < T > ( ) ;
services . AddSingleton < ReportProvider , T > ( ) ;
return services ;
}
2023-05-28 16:44:10 +02:00
public static IServiceCollection AddScheduledTask < T > ( this IServiceCollection services , TimeSpan every )
where T : class , IPeriodicTask
{
services . AddSingleton < T > ( ) ;
services . AddTransient < ScheduledTask > ( o = > new ScheduledTask ( typeof ( T ) , every ) ) ;
return services ;
}
2018-02-12 19:27:36 +01:00
public static async Task CloseSocket ( this WebSocket webSocket )
{
try
{
if ( webSocket . State = = WebSocketState . Open )
{
2022-01-14 09:50:29 +01:00
using CancellationTokenSource cts = new CancellationTokenSource ( ) ;
cts . CancelAfter ( 5000 ) ;
await webSocket . CloseAsync ( WebSocketCloseStatus . NormalClosure , "Closing" , cts . Token ) ;
2018-02-12 19:27:36 +01:00
}
}
catch { }
finally { try { webSocket . Dispose ( ) ; } catch { } }
}
2020-03-29 17:28:22 +02:00
2024-04-04 09:31:04 +02:00
public static IEnumerable < BitcoinLikePaymentData > GetAllBitcoinPaymentData ( this InvoiceEntity invoice , BitcoinLikePaymentHandler handler , bool accountedOnly )
2020-03-29 17:28:22 +02:00
{
2021-05-14 09:16:19 +02:00
return invoice . GetPayments ( accountedOnly )
2024-09-17 10:28:58 +02:00
. Select ( p = > p . GetDetails < BitcoinLikePaymentData > ( handler ) )
. Where ( p = > p is not null ) ;
2020-03-29 17:28:22 +02:00
}
public static async Task < Dictionary < uint256 , TransactionResult > > GetTransactions ( this BTCPayWallet client , uint256 [ ] hashes , bool includeOffchain = false , CancellationToken cts = default ( CancellationToken ) )
2017-11-06 09:31:02 +01:00
{
hashes = hashes . Distinct ( ) . ToArray ( ) ;
var transactions = hashes
2020-03-29 17:28:22 +02:00
. Select ( async o = > await client . GetTransactionAsync ( o , includeOffchain , cts ) )
2017-11-06 09:31:02 +01:00
. ToArray ( ) ;
await Task . WhenAll ( transactions ) . ConfigureAwait ( false ) ;
return transactions . Select ( t = > t . Result ) . Where ( t = > t ! = null ) . ToDictionary ( o = > o . Transaction . GetHash ( ) ) ;
}
2020-06-28 10:55:27 +02:00
2020-03-29 17:28:22 +02:00
public static async Task < PSBT > UpdatePSBT ( this ExplorerClientProvider explorerClientProvider , DerivationSchemeSettings derivationSchemeSettings , PSBT psbt )
{
var result = await explorerClientProvider . GetExplorerClient ( psbt . Network . NetworkSet . CryptoCode ) . UpdatePSBTAsync ( new UpdatePSBTRequest ( )
{
PSBT = psbt ,
2020-09-24 14:43:04 +02:00
DerivationScheme = derivationSchemeSettings . AccountDerivation ,
AlwaysIncludeNonWitnessUTXO = true
2020-03-29 17:28:22 +02:00
} ) ;
if ( result = = null )
return null ;
derivationSchemeSettings . RebaseKeyPaths ( result . PSBT ) ;
return result . PSBT ;
}
2020-06-28 10:55:27 +02:00
2018-07-12 10:38:21 +02:00
public static void SetHeaderOnStarting ( this HttpResponse resp , string name , string value )
{
if ( resp . HasStarted )
return ;
resp . OnStarting ( ( ) = >
{
SetHeader ( resp , name , value ) ;
return Task . CompletedTask ;
} ) ;
}
public static void SetHeader ( this HttpResponse resp , string name , string value )
{
var existing = resp . Headers [ name ] . FirstOrDefault ( ) ;
if ( existing ! = null & & value = = null )
resp . Headers . Remove ( name ) ;
else
resp . Headers [ name ] = value ;
}
2019-04-04 07:28:11 +02:00
public static bool IsLocalNetwork ( string server )
{
2021-12-28 09:39:54 +01:00
ArgumentNullException . ThrowIfNull ( server ) ;
2019-04-04 07:28:11 +02:00
if ( Uri . CheckHostName ( server ) = = UriHostNameType . Dns )
{
return server . EndsWith ( ".internal" , StringComparison . OrdinalIgnoreCase ) | |
server . EndsWith ( ".local" , StringComparison . OrdinalIgnoreCase ) | |
server . EndsWith ( ".lan" , StringComparison . OrdinalIgnoreCase ) | |
server . IndexOf ( '.' , StringComparison . OrdinalIgnoreCase ) = = - 1 ;
}
2020-06-28 10:55:27 +02:00
if ( IPAddress . TryParse ( server , out var ip ) )
2019-04-04 07:28:11 +02:00
{
2019-06-12 10:40:49 +02:00
return ip . IsLocal ( ) | | ip . IsRFC1918 ( ) ;
2019-04-04 07:28:11 +02:00
}
return false ;
}
2024-04-04 09:31:04 +02:00
#nullable enable
public static LNURLPayPaymentHandler GetLNURLHandler ( this PaymentMethodHandlerDictionary handlers , BTCPayNetwork network )
{
return handlers . GetLNURLHandler ( network . CryptoCode ) ;
}
public static LNURLPayPaymentHandler GetLNURLHandler ( this PaymentMethodHandlerDictionary handlers , string cryptoCode )
{
var pmi = PaymentTypes . LNURL . GetPaymentMethodId ( cryptoCode ) ;
var h = ( LNURLPayPaymentHandler ) handlers [ pmi ] ;
return h ;
}
public static LightningLikePaymentHandler GetLightningHandler ( this PaymentMethodHandlerDictionary handlers , BTCPayNetwork network )
{
return handlers . GetLightningHandler ( network . CryptoCode ) ;
}
public static LightningLikePaymentHandler GetLightningHandler ( this PaymentMethodHandlerDictionary handlers , string cryptoCode )
{
var pmi = PaymentTypes . LN . GetPaymentMethodId ( cryptoCode ) ;
var h = ( LightningLikePaymentHandler ) handlers [ pmi ] ;
return h ;
}
2019-04-04 07:28:11 +02:00
2024-04-04 09:31:04 +02:00
public static BitcoinLikePaymentHandler ? TryGetBitcoinHandler ( this PaymentMethodHandlerDictionary handlers , BTCPayNetwork network )
2024-10-07 02:37:56 +02:00
= > handlers . TryGetBitcoinHandler ( network . CryptoCode ) ;
2024-04-04 09:31:04 +02:00
public static BitcoinLikePaymentHandler ? TryGetBitcoinHandler ( this PaymentMethodHandlerDictionary handlers , string cryptoCode )
2024-10-07 02:37:56 +02:00
= > handlers . TryGetBitcoinHandler ( PaymentTypes . CHAIN . GetPaymentMethodId ( cryptoCode ) ) ;
public static BitcoinLikePaymentHandler ? TryGetBitcoinHandler ( this PaymentMethodHandlerDictionary handlers , PaymentMethodId paymentMethodId )
2024-04-04 09:31:04 +02:00
{
2024-10-07 02:37:56 +02:00
if ( handlers . TryGetValue ( paymentMethodId , out var h ) & & h is BitcoinLikePaymentHandler b )
2024-04-04 09:31:04 +02:00
return b ;
return null ;
}
public static BitcoinLikePaymentHandler GetBitcoinHandler ( this PaymentMethodHandlerDictionary handlers , BTCPayNetwork network )
2024-10-07 02:37:56 +02:00
= > handlers . GetBitcoinHandler ( network . CryptoCode ) ;
2024-04-04 09:31:04 +02:00
public static BitcoinLikePaymentHandler GetBitcoinHandler ( this PaymentMethodHandlerDictionary handlers , string cryptoCode )
{
var pmi = PaymentTypes . CHAIN . GetPaymentMethodId ( cryptoCode ) ;
var h = ( BitcoinLikePaymentHandler ) handlers [ pmi ] ;
return h ;
}
2024-05-01 03:22:07 +02:00
public static BTCPayNetwork ? TryGetNetwork < TId , THandler > ( this HandlersDictionary < TId , THandler > handlers , TId id )
where THandler : IHandler < TId >
where TId : notnull
2024-04-04 09:31:04 +02:00
{
2024-05-01 03:22:07 +02:00
if ( id is not null & &
handlers . TryGetValue ( id , out var value ) & &
2024-04-04 09:31:04 +02:00
value is IHasNetwork { Network : var n } )
{
return n ;
}
return null ;
}
2024-05-01 03:22:07 +02:00
public static BTCPayNetwork GetNetwork < TId , THandler > ( this HandlersDictionary < TId , THandler > handlers , TId id )
where THandler : IHandler < TId >
where TId : notnull
2024-04-04 09:31:04 +02:00
{
2024-05-01 03:22:07 +02:00
return TryGetNetwork ( handlers , id ) ? ? throw new KeyNotFoundException ( $"Network for {id} is not found" ) ;
2024-04-04 09:31:04 +02:00
}
public static LightningPaymentMethodConfig ? GetLightningConfig ( this PaymentMethodHandlerDictionary handlers , Data . StoreData store , BTCPayNetwork network )
{
var config = store . GetPaymentMethodConfig ( PaymentTypes . LN . GetPaymentMethodId ( network . CryptoCode ) ) ;
if ( config is null )
return null ;
return handlers . GetLightningHandler ( network ) . ParsePaymentMethodConfig ( config ) ;
}
public static DerivationStrategyBase ? GetDerivationStrategy ( this PaymentMethodHandlerDictionary handlers , InvoiceEntity invoice , BTCPayNetworkBase network )
{
var pmi = PaymentTypes . CHAIN . GetPaymentMethodId ( network . CryptoCode ) ;
if ( ! handlers . TryGetValue ( pmi , out var handler ) )
return null ;
var prompt = invoice . GetPaymentPrompt ( pmi ) ;
if ( prompt ? . Details is null )
return null ;
var details = ( BitcoinPaymentPromptDetails ) handler . ParsePaymentPromptDetails ( prompt . Details ) ;
return details . AccountDerivation ;
}
#nullable restore
2020-02-19 09:35:23 +01:00
public static bool IsOnion ( this Uri uri )
{
2020-02-24 16:10:07 +01:00
if ( uri = = null | | ! uri . IsAbsoluteUri )
return false ;
return uri . DnsSafeHost . EndsWith ( ".onion" , StringComparison . OrdinalIgnoreCase ) ;
2020-02-19 09:35:23 +01:00
}
2018-04-27 19:09:24 +02:00
public static string GetSIN ( this ClaimsPrincipal principal )
2017-10-27 10:53:04 +02:00
{
2019-10-12 13:35:30 +02:00
return principal . Claims . Where ( c = > c . Type = = Security . Bitpay . BitpayClaims . SIN ) . Select ( c = > c . Value ) . FirstOrDefault ( ) ;
2018-04-27 19:51:20 +02:00
}
2018-04-29 13:32:43 +02:00
public static void SetIsBitpayAPI ( this HttpContext ctx , bool value )
{
NBitcoin . Extensions . TryAdd ( ctx . Items , "IsBitpayAPI" , value ) ;
}
public static bool GetIsBitpayAPI ( this HttpContext ctx )
{
return ctx . Items . TryGetValue ( "IsBitpayAPI" , out object obj ) & &
obj is bool b & & b ;
}
2018-04-30 15:28:00 +02:00
public static void SetBitpayAuth ( this HttpContext ctx , ( string Signature , String Id , String Authorization ) value )
{
NBitcoin . Extensions . TryAdd ( ctx . Items , "BitpayAuth" , value ) ;
}
2019-06-11 11:40:47 +02:00
public static bool TryGetBitpayAuth ( this HttpContext ctx , out ( string Signature , String Id , String Authorization ) result )
2018-04-30 15:28:00 +02:00
{
2019-06-11 11:40:47 +02:00
if ( ctx . Items . TryGetValue ( "BitpayAuth" , out object obj ) )
{
result = ( ( string Signature , String Id , String Authorization ) ) obj ;
return true ;
}
result = default ;
return false ;
2018-04-30 15:28:00 +02:00
}
2021-12-31 08:59:02 +01:00
2021-12-31 08:36:38 +01:00
public static UserPrefsCookie GetUserPrefsCookie ( this HttpContext ctx )
{
var prefCookie = new UserPrefsCookie ( ) ;
ctx . Request . Cookies . TryGetValue ( nameof ( UserPrefsCookie ) , out var strPrefCookie ) ;
if ( ! string . IsNullOrEmpty ( strPrefCookie ) )
{
try
{
prefCookie = JsonConvert . DeserializeObject < UserPrefsCookie > ( strPrefCookie ) ;
}
catch { /* ignore cookie deserialization failures */ }
}
return prefCookie ;
}
2022-02-01 02:42:31 +01:00
public static void DeleteUserPrefsCookie ( this HttpContext ctx )
{
ctx . Response . Cookies . Delete ( nameof ( UserPrefsCookie ) ) ;
}
2021-12-31 08:36:38 +01:00
private static void SetCurrentStoreId ( this HttpContext ctx , string storeId )
{
var prefCookie = ctx . GetUserPrefsCookie ( ) ;
if ( prefCookie . CurrentStoreId ! = storeId )
{
prefCookie . CurrentStoreId = storeId ;
ctx . Response . Cookies . Append ( nameof ( UserPrefsCookie ) , JsonConvert . SerializeObject ( prefCookie ) ) ;
}
}
2018-04-29 13:48:17 +02:00
2022-06-29 16:18:02 +02:00
public static string GetCurrentStoreId ( this HttpContext ctx )
{
return ctx . GetImplicitStoreId ( ) ? ? ctx . GetUserPrefsCookie ( ) ? . CurrentStoreId ;
}
2018-04-29 13:32:43 +02:00
public static StoreData GetStoreData ( this HttpContext ctx )
{
return ctx . Items . TryGet ( "BTCPAY.STOREDATA" ) as StoreData ;
}
2021-12-31 08:59:02 +01:00
2018-04-29 13:32:43 +02:00
public static void SetStoreData ( this HttpContext ctx , StoreData storeData )
{
ctx . Items [ "BTCPAY.STOREDATA" ] = storeData ;
2021-12-31 08:59:02 +01:00
2021-12-31 08:36:38 +01:00
SetCurrentStoreId ( ctx , storeData . Id ) ;
2018-04-29 13:32:43 +02:00
}
2020-03-19 11:11:15 +01:00
public static StoreData [ ] GetStoresData ( this HttpContext ctx )
{
return ctx . Items . TryGet ( "BTCPAY.STORESDATA" ) as StoreData [ ] ;
}
2021-12-31 08:59:02 +01:00
2020-03-19 11:11:15 +01:00
public static void SetStoresData ( this HttpContext ctx , StoreData [ ] storeData )
{
ctx . Items [ "BTCPAY.STORESDATA" ] = storeData ;
}
2020-07-15 19:51:01 +02:00
2021-12-16 17:37:19 +01:00
public static InvoiceEntity GetInvoiceData ( this HttpContext ctx )
{
return ctx . Items . TryGet ( "BTCPAY.INVOICEDATA" ) as InvoiceEntity ;
}
2021-12-31 08:59:02 +01:00
2021-12-16 17:37:19 +01:00
public static void SetInvoiceData ( this HttpContext ctx , InvoiceEntity invoiceEntity )
{
ctx . Items [ "BTCPAY.INVOICEDATA" ] = invoiceEntity ;
}
public static PaymentRequestData GetPaymentRequestData ( this HttpContext ctx )
{
return ctx . Items . TryGet ( "BTCPAY.PAYMENTREQUESTDATA" ) as PaymentRequestData ;
}
2021-12-31 08:59:02 +01:00
2021-12-16 17:37:19 +01:00
public static void SetPaymentRequestData ( this HttpContext ctx , PaymentRequestData paymentRequestData )
{
ctx . Items [ "BTCPAY.PAYMENTREQUESTDATA" ] = paymentRequestData ;
}
public static AppData GetAppData ( this HttpContext ctx )
{
return ctx . Items . TryGet ( "BTCPAY.APPDATA" ) as AppData ;
}
2021-12-31 08:59:02 +01:00
2021-12-16 17:37:19 +01:00
public static void SetAppData ( this HttpContext ctx , AppData appData )
{
ctx . Items [ "BTCPAY.APPDATA" ] = appData ;
}
2022-02-10 04:35:21 +01:00
public static bool SupportChain ( this IConfiguration conf , string cryptoCode )
{
var supportedChains = conf . GetOrDefault < string > ( "chains" , "btc" )
. Split ( ',' , StringSplitOptions . RemoveEmptyEntries )
. Select ( t = > t . ToUpperInvariant ( ) ) . ToHashSet ( ) ;
return supportedChains . Contains ( cryptoCode . ToUpperInvariant ( ) ) ;
}
2024-12-04 13:56:45 +01:00
class ParameterReplacer : ExpressionVisitor
{
private Dictionary < string , ParameterExpression > _Parameters ;
protected override Expression VisitLambda < T > ( Expression < T > node )
{
_Parameters = node . Parameters . ToDictionary ( p = > p . Name ) ;
return base . VisitLambda ( node ) ;
}
protected override Expression VisitParameter ( ParameterExpression node )
{
return _Parameters [ node . Name ] ;
}
}
public static TExpr ReplaceParameterRef < TExpr > ( this TExpr expression ) where TExpr : Expression
= > ( TExpr ) new ParameterReplacer ( ) . Visit ( expression ) ;
2020-07-15 19:51:01 +02:00
public static IActionResult RedirectToRecoverySeedBackup ( this Controller controller , RecoverySeedBackupViewModel vm )
{
2021-12-16 17:37:19 +01:00
var redirectVm = new PostRedirectViewModel
2020-07-15 19:51:01 +02:00
{
2022-01-07 04:32:00 +01:00
AspController = "UIHome" ,
2020-07-15 19:51:01 +02:00
AspAction = "RecoverySeedBackup" ,
2022-02-10 04:24:28 +01:00
FormParameters =
2020-07-15 19:51:01 +02:00
{
2022-02-10 04:24:28 +01:00
{ "cryptoCode" , vm . CryptoCode } ,
{ "mnemonic" , vm . Mnemonic } ,
{ "passphrase" , vm . Passphrase } ,
{ "isStored" , vm . IsStored ? "true" : "false" } ,
{ "requireConfirm" , vm . RequireConfirm ? "true" : "false" } ,
{ "returnUrl" , vm . ReturnUrl }
2020-07-15 19:51:01 +02:00
}
} ;
return controller . View ( "PostRedirect" , redirectVm ) ;
}
2020-07-27 06:27:47 +02:00
2024-12-09 10:14:40 +01:00
public static string RemoveUserInfo ( this Uri uri )
= > string . IsNullOrEmpty ( uri . UserInfo ) ? uri . ToString ( ) : uri . ToString ( ) . Replace ( uri . UserInfo , "***" ) ;
2021-01-06 15:51:13 +01:00
public static DataDirectories Configure ( this DataDirectories dataDirectories , IConfiguration configuration )
{
var networkType = DefaultConfiguration . GetNetworkType ( configuration ) ;
var defaultSettings = BTCPayDefaultSettings . GetDefaultSettings ( networkType ) ;
dataDirectories . DataDir = configuration [ "datadir" ] ? ? defaultSettings . DefaultDataDirectory ;
dataDirectories . PluginDir = configuration [ "plugindir" ] ? ? defaultSettings . DefaultPluginDirectory ;
2021-12-31 08:59:02 +01:00
dataDirectories . StorageDir = Path . Combine ( dataDirectories . DataDir , Storage . Services . Providers . FileSystemStorage . FileSystemFileProviderService . LocalStorageDirectoryName ) ;
2021-01-06 15:51:13 +01:00
dataDirectories . TempStorageDir = Path . Combine ( dataDirectories . StorageDir , "tmp" ) ;
2022-03-31 11:54:25 +02:00
dataDirectories . TempDir = Path . Combine ( dataDirectories . DataDir , "tmp" ) ;
2024-07-24 13:16:20 +02:00
dataDirectories . LangsDir = Path . Combine ( dataDirectories . DataDir , "Langs" ) ;
2021-01-06 15:51:13 +01:00
return dataDirectories ;
}
2020-07-27 06:27:47 +02:00
private static object Private ( this object obj , string privateField ) = > obj ? . GetType ( ) . GetField ( privateField , BindingFlags . Instance | BindingFlags . NonPublic ) ? . GetValue ( obj ) ;
private static T Private < T > ( this object obj , string privateField ) = > ( T ) obj ? . GetType ( ) . GetField ( privateField , BindingFlags . Instance | BindingFlags . NonPublic ) ? . GetValue ( obj ) ;
2017-10-27 10:53:04 +02:00
}
2017-09-13 08:47:34 +02:00
}