Compare commits

..

4 Commits

Author SHA1 Message Date
1860333250 Make Ln address pass an invoiceId in the context to resolve breaking change 2023-08-25 10:27:57 +02:00
e93cf36a98 FIx permission misalignment on create pull payments
We have explicit permissions for pull payment creation, even allow them to be created through the invoices, but the create ui and cta were blocked behind  canmodify store permission.
2023-08-24 16:54:15 +02:00
156a6633bb Show app view link in nav when not enoguh permission to modify 2023-08-24 16:35:20 +02:00
c41e528872 BUmp LNURL 2023-08-24 16:35:19 +02:00
527 changed files with 7168 additions and 10846 deletions

3
.gitignore vendored
View File

@ -298,5 +298,4 @@ Packed Plugins
Plugins/packed
BTCPayServer/wwwroot/swagger/v1/openapi.json
BTCPayServer/appsettings.dev.json
BTCPayServer.Tests/monero_wallet
BTCPayServer/appsettings.dev.json

View File

@ -31,11 +31,11 @@
<None Include="icon.png" Pack="true" PackagePath="\" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="HtmlSanitizer" Version="8.0.723" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="8.0.0" />
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="8.0.0" />
<PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="8.0.0-beta.2" />
<PackageReference Include="HtmlSanitizer" Version="5.0.372" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="6.0.9" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="6.0.9" />
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="6.0.7" />
<PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="6.0.1" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\BTCPayServer.Client\BTCPayServer.Client.csproj" />

View File

@ -84,7 +84,6 @@ namespace BTCPayServer.Abstractions.Contracts
.UseNpgsql(_options.Value.ConnectionString, o =>
{
o.EnableRetryOnFailure(10);
o.SetPostgresVersion(12, 0);
if (!string.IsNullOrEmpty(_schemaPrefix))
{
o.MigrationsHistoryTable(_schemaPrefix);

View File

@ -20,15 +20,6 @@ namespace BTCPayServer.Abstractions.Extensions
Relative
}
public static void SetBlazorAllowed(this ViewDataDictionary viewData, bool allowed)
{
viewData["BlazorAllowed"] = allowed;
}
public static bool IsBlazorAllowed(this ViewDataDictionary viewData)
{
return viewData["BlazorAllowed"] is not false;
}
public static void SetActivePage<T>(this ViewDataDictionary viewData, T activePage, string title = null, string activeId = null)
where T : IConvertible
{

View File

@ -5,6 +5,7 @@ using System.Reflection;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Primitives;
using Newtonsoft.Json.Linq;
using Npgsql.Internal.TypeHandlers.GeometricHandlers;
namespace BTCPayServer.Abstractions.Form;
@ -104,7 +105,31 @@ public class Form
}
}
public void SetValues(JObject values)
{
var fields = GetAllFields().ToDictionary(k => k.FullName, k => k.Field);
SetValues(fields, new List<string>(), values);
}
private void SetValues(Dictionary<string, Field> fields, List<string> path, JObject values)
{
foreach (var prop in values.Properties())
{
List<string> propPath = new List<string>(path.Count + 1);
propPath.AddRange(path);
propPath.Add(prop.Name);
if (prop.Value.Type == JTokenType.Object)
{
SetValues(fields, propPath, (JObject)prop.Value);
}
else if (prop.Value.Type == JTokenType.String)
{
var fullName = string.Join('_', propPath.Where(s => !string.IsNullOrEmpty(s)));
if (fields.TryGetValue(fullName, out var f) && !f.Constant)
f.Value = prop.Value.Value<string>();
}
}
}
}

View File

@ -1,5 +1,5 @@
using System.Web;
using Ganss.Xss;
using Ganss.XSS;
using Microsoft.AspNetCore.Html;
using Microsoft.AspNetCore.Mvc.Rendering;

View File

@ -2,13 +2,12 @@ using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Razor.TagHelpers;
using System;
using System.Linq;
using Microsoft.Extensions.Logging;
namespace BTCPayServer.Abstractions.TagHelpers;
[HtmlTargetElement(Attributes = "[permission]")]
[HtmlTargetElement(Attributes = "[not-permission]")]
[HtmlTargetElement(Attributes = "[not-permission]" )]
public class PermissionTagHelper : TagHelper
{
private readonly IAuthorizationService _authorizationService;
@ -23,72 +22,29 @@ public class PermissionTagHelper : TagHelper
public string Permission { get; set; }
public string NotPermission { get; set; }
public string PermissionResource { get; set; }
public bool AndMode { get; set; } = false;
public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
{
var permissions = Permission?.Split(',', StringSplitOptions.RemoveEmptyEntries) ?? Array.Empty<string>();
var notPermissions = NotPermission?.Split(',', StringSplitOptions.RemoveEmptyEntries) ?? Array.Empty<string>();
if (!permissions.Any() && !notPermissions.Any())
if (string.IsNullOrEmpty(Permission) && string.IsNullOrEmpty(NotPermission))
return;
if (_httpContextAccessor.HttpContext is null)
return;
bool shouldRender = true; // Assume tag should be rendered unless a check fails
// Process 'Permission' - User must have these permissions
if (permissions.Any())
var expectedResult = !string.IsNullOrEmpty(Permission);
var key = $"{Permission??NotPermission}_{PermissionResource}";
if (!_httpContextAccessor.HttpContext.Items.TryGetValue(key, out var o) ||
o is not AuthorizationResult res)
{
bool finalResult = AndMode;
foreach (var perm in permissions)
{
var key = $"{perm}_{PermissionResource}";
AuthorizationResult res = await GetOrAddAuthorizationResult(key, perm);
if (AndMode)
finalResult &= res.Succeeded;
else
finalResult |= res.Succeeded;
if (!AndMode && finalResult) break;
}
shouldRender = finalResult;
res = await _authorizationService.AuthorizeAsync(_httpContextAccessor.HttpContext.User,
PermissionResource,
Permission);
_httpContextAccessor.HttpContext.Items.Add(key, res);
}
// Process 'NotPermission' - User must not have these permissions
if (shouldRender && notPermissions.Any())
{
foreach (var notPerm in notPermissions)
{
var key = $"{notPerm}_{PermissionResource}";
AuthorizationResult res = await GetOrAddAuthorizationResult(key, notPerm);
if (res.Succeeded) // If the user has a 'NotPermission', they should not see the tag
{
shouldRender = false;
break;
}
}
}
if (!shouldRender)
if (expectedResult != res.Succeeded)
{
output.SuppressOutput();
}
}
private async Task<AuthorizationResult> GetOrAddAuthorizationResult(string key, string permission)
{
if (!_httpContextAccessor.HttpContext.Items.TryGetValue(key, out var cachedResult))
{
var res = await _authorizationService.AuthorizeAsync(_httpContextAccessor.HttpContext.User,
PermissionResource, permission);
_httpContextAccessor.HttpContext.Items[key] = res;
return res;
}
return cachedResult as AuthorizationResult;
}
}

View File

@ -16,7 +16,7 @@
<Platforms>AnyCPU</Platforms>
</PropertyGroup>
<PropertyGroup>
<Version Condition=" '$(Version)' == '' ">1.7.3</Version>
<Version Condition=" '$(Version)' == '' ">1.7.2</Version>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
<PublishRepositoryUrl>true</PublishRepositoryUrl>
@ -30,9 +30,9 @@
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0" PrivateAssets="All" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="BTCPayServer.Lightning.Common" Version="1.5.1" />
<PackageReference Include="NBitcoin" Version="7.0.31" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="BTCPayServer.Lightning.Common" Version="1.3.21" />
<PackageReference Include="NBitcoin" Version="7.0.24" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
</ItemGroup>
<ItemGroup>
<None Include="icon.png" Pack="true" PackagePath="\" />

View File

@ -28,8 +28,6 @@ namespace BTCPayServer.Client.Models
public PosViewType DefaultView { get; set; }
public bool ShowCustomAmount { get; set; } = false;
public bool ShowDiscount { get; set; } = true;
public bool ShowSearch { get; set; } = true;
public bool ShowCategories { get; set; } = true;
public bool EnableTips { get; set; } = true;
public string CustomAmountPayButtonText { get; set; } = null;
public string FixedAmountPayButtonText { get; set; } = null;
@ -39,9 +37,9 @@ namespace BTCPayServer.Client.Models
public string RedirectUrl { get; set; } = null;
public bool? RedirectAutomatically { get; set; } = null;
public bool? RequiresRefundEmail { get; set; } = null;
public bool? Archived { get; set; } = null;
public string FormId { get; set; } = null;
public string EmbeddedCSS { get; set; } = null;
public CheckoutType? CheckoutType { get; set; } = null;
}
public enum CrowdfundResetEvery
@ -80,7 +78,6 @@ namespace BTCPayServer.Client.Models
public bool? DisplayPerksValue { get; set; } = null;
public bool? DisplayPerksRanking { get; set; } = null;
public bool? SortPerksByPopularity { get; set; } = null;
public bool? Archived { get; set; } = null;
public string[] Sounds { get; set; } = null;
public string[] AnimationColors { get; set; } = null;
}

View File

@ -1,12 +1,12 @@
namespace BTCPayServer.Client.Models;
public enum InvoiceExceptionStatus
namespace BTCPayServer.Client.Models
{
None,
PaidLate,
PaidPartial,
Marked,
Invalid,
PaidOver
public enum InvoiceExceptionStatus
{
None,
PaidLate,
PaidPartial,
Marked,
Invalid,
PaidOver
}
}

View File

@ -1,12 +1,8 @@
using Newtonsoft.Json;
namespace BTCPayServer.Client.Models
{
public class LNURLPayPaymentMethodBaseData
{
public bool UseBech32Scheme { get; set; }
[JsonProperty("lud12Enabled")]
public bool LUD12Enabled { get; set; }
public LNURLPayPaymentMethodBaseData()

View File

@ -16,12 +16,11 @@ namespace BTCPayServer.Client.Models
{
}
public LNURLPayPaymentMethodData(string cryptoCode, bool enabled, bool useBech32Scheme, bool lud12Enabled)
public LNURLPayPaymentMethodData(string cryptoCode, bool enabled, bool useBech32Scheme)
{
Enabled = enabled;
CryptoCode = cryptoCode;
UseBech32Scheme = useBech32Scheme;
LUD12Enabled = lud12Enabled;
}
}
}

View File

@ -7,7 +7,7 @@ namespace BTCPayServer.Client.Models
public class PaymentRequestData : PaymentRequestBaseData
{
[JsonConverter(typeof(StringEnumConverter))]
public PaymentRequestStatus Status { get; set; }
public PaymentRequestData.PaymentRequestStatus Status { get; set; }
[JsonConverter(typeof(NBitcoin.JsonConverters.DateTimeToUnixTimeConverter))]
public DateTimeOffset CreatedTime { get; set; }
public string Id { get; set; }
@ -16,8 +16,7 @@ namespace BTCPayServer.Client.Models
{
Pending = 0,
Completed = 1,
Expired = 2,
Processing = 3
Expired = 2
}
}
}

View File

@ -9,8 +9,6 @@ namespace BTCPayServer.Client.Models
public string AppType { get; set; }
public string Name { get; set; }
public string StoreId { get; set; }
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
public bool? Archived { get; set; }
[JsonConverter(typeof(NBitcoin.JsonConverters.DateTimeToUnixTimeConverter))]
public DateTimeOffset Created { get; set; }
}
@ -21,8 +19,6 @@ namespace BTCPayServer.Client.Models
public string DefaultView { get; set; }
public bool ShowCustomAmount { get; set; }
public bool ShowDiscount { get; set; }
public bool ShowSearch { get; set; }
public bool ShowCategories { get; set; }
public bool EnableTips { get; set; }
public string Currency { get; set; }
public object Items { get; set; }

View File

@ -37,20 +37,14 @@ namespace BTCPayServer.Client.Models
public bool AnyoneCanCreateInvoice { get; set; }
public string DefaultCurrency { get; set; }
public bool RequiresRefundEmail { get; set; }
[JsonConverter(typeof(StringEnumConverter))]
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
public CheckoutType? CheckoutType { get; set; }
public CheckoutType CheckoutType { get; set; }
public bool LightningAmountInSatoshi { get; set; }
public bool LightningPrivateRouteHints { get; set; }
public bool OnChainWithLnInvoiceFallback { get; set; }
public bool LazyPaymentMethods { get; set; }
public bool RedirectAutomatically { get; set; }
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
public bool Archived { get; set; }
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
public bool ShowRecommendedFee { get; set; } = true;
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
@ -76,17 +70,6 @@ namespace BTCPayServer.Client.Models
public bool PayJoinEnabled { get; set; }
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
public bool? AutoDetectLanguage { get; set; }
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
public bool? ShowPayInWalletButton { get; set; }
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
public bool? ShowStoreHeader { get; set; }
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
public bool? CelebratePayment { get; set; }
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
public bool? PlaySoundOnPayment { get; set; }
public InvoiceData.ReceiptOptions Receipt { get; set; }

View File

@ -11,7 +11,8 @@ namespace BTCPayServer.Client.Models
{
public bool Everything { get; set; } = true;
public string[] SpecificEvents { get; set; } = Array.Empty<string>();
[JsonProperty(ItemConverterType = typeof(Newtonsoft.Json.Converters.StringEnumConverter))]
public WebhookEventType[] SpecificEvents { get; set; } = Array.Empty<WebhookEventType>();
}
public bool Enabled { get; set; } = true;

View File

@ -9,7 +9,7 @@ namespace BTCPayServer.Client.Models
{
public class WebhookEvent
{
public static readonly JsonSerializerSettings DefaultSerializerSettings;
public readonly static JsonSerializerSettings DefaultSerializerSettings;
static WebhookEvent()
{
DefaultSerializerSettings = new JsonSerializerSettings();
@ -45,7 +45,8 @@ namespace BTCPayServer.Client.Models
}
}
public bool IsRedelivery { get; set; }
public string Type { get; set; }
[JsonConverter(typeof(StringEnumConverter))]
public WebhookEventType Type { get; set; }
[JsonConverter(typeof(NBitcoin.JsonConverters.DateTimeToUnixTimeConverter))]
public DateTimeOffset Timestamp { get; set; }
[JsonExtensionData]

View File

@ -1,20 +1,13 @@
namespace BTCPayServer.Client.Models;
public static class WebhookEventType
namespace BTCPayServer.Client.Models
{
public const string InvoiceCreated = nameof(InvoiceCreated);
public const string InvoiceReceivedPayment = nameof(InvoiceReceivedPayment);
public const string InvoiceProcessing = nameof(InvoiceProcessing);
public const string InvoiceExpired = nameof(InvoiceExpired);
public const string InvoiceSettled = nameof(InvoiceSettled);
public const string InvoiceInvalid = nameof(InvoiceInvalid);
public const string InvoicePaymentSettled = nameof(InvoicePaymentSettled);
public const string PayoutCreated = nameof(PayoutCreated);
public const string PayoutApproved = nameof(PayoutApproved);
public const string PayoutUpdated = nameof(PayoutUpdated);
public const string PaymentRequestUpdated = nameof(PaymentRequestUpdated);
public const string PaymentRequestCreated = nameof(PaymentRequestCreated);
public const string PaymentRequestArchived = nameof(PaymentRequestArchived);
public const string PaymentRequestStatusChanged = nameof(PaymentRequestStatusChanged);
public enum WebhookEventType
{
InvoiceCreated,
InvoiceReceivedPayment,
InvoiceProcessing,
InvoiceExpired,
InvoiceSettled,
InvoiceInvalid,
InvoicePaymentSettled,
}
}

View File

@ -1,64 +1,31 @@
using System;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using Newtonsoft.Json.Linq;
namespace BTCPayServer.Client.Models
{
public class WebhookPayoutEvent : StoreWebhookEvent
{
public WebhookPayoutEvent(string evtType, string storeId)
{
if (!evtType.StartsWith("payout", StringComparison.InvariantCultureIgnoreCase))
throw new ArgumentException("Invalid event type", nameof(evtType));
Type = evtType;
StoreId = storeId;
}
[JsonProperty(Order = 2)] public string PayoutId { get; set; }
[JsonProperty(Order = 3)] public string PullPaymentId { get; set; }
[JsonProperty(Order = 4)] [JsonConverter(typeof(StringEnumConverter))]public PayoutState PayoutState { get; set; }
}
public class WebhookPaymentRequestEvent : StoreWebhookEvent
{
public WebhookPaymentRequestEvent(string evtType, string storeId)
{
if (!evtType.StartsWith("paymentrequest", StringComparison.InvariantCultureIgnoreCase))
throw new ArgumentException("Invalid event type", nameof(evtType));
Type = evtType;
StoreId = storeId;
}
[JsonProperty(Order = 2)] public string PaymentRequestId { get; set; }
[JsonProperty(Order = 3)] [JsonConverter(typeof(StringEnumConverter))]public PaymentRequestData.PaymentRequestStatus Status { get; set; }
}
public abstract class StoreWebhookEvent : WebhookEvent
{
[JsonProperty(Order = 1)] public string StoreId { get; set; }
}
public class WebhookInvoiceEvent : StoreWebhookEvent
public class WebhookInvoiceEvent : WebhookEvent
{
public WebhookInvoiceEvent()
{
}
public WebhookInvoiceEvent(string evtType, string storeId)
{
if (!evtType.StartsWith("invoice", StringComparison.InvariantCultureIgnoreCase))
throw new ArgumentException("Invalid event type", nameof(evtType));
Type = evtType;
StoreId = storeId;
public WebhookInvoiceEvent(WebhookEventType evtType)
{
this.Type = evtType;
}
[JsonProperty(Order = 1)] public string StoreId { get; set; }
[JsonProperty(Order = 2)] public string InvoiceId { get; set; }
[JsonProperty(Order = 3)] public JObject Metadata { get; set; }
}
public class WebhookInvoiceSettledEvent : WebhookInvoiceEvent
{
public WebhookInvoiceSettledEvent(string storeId) : base(WebhookEventType.InvoiceSettled, storeId)
public WebhookInvoiceSettledEvent()
{
}
public WebhookInvoiceSettledEvent(WebhookEventType evtType) : base(evtType)
{
}
@ -67,7 +34,11 @@ namespace BTCPayServer.Client.Models
public class WebhookInvoiceInvalidEvent : WebhookInvoiceEvent
{
public WebhookInvoiceInvalidEvent(string storeId) : base(WebhookEventType.InvoiceInvalid, storeId)
public WebhookInvoiceInvalidEvent()
{
}
public WebhookInvoiceInvalidEvent(WebhookEventType evtType) : base(evtType)
{
}
@ -76,7 +47,11 @@ namespace BTCPayServer.Client.Models
public class WebhookInvoiceProcessingEvent : WebhookInvoiceEvent
{
public WebhookInvoiceProcessingEvent(string storeId) : base(WebhookEventType.InvoiceProcessing, storeId)
public WebhookInvoiceProcessingEvent()
{
}
public WebhookInvoiceProcessingEvent(WebhookEventType evtType) : base(evtType)
{
}
@ -85,7 +60,11 @@ namespace BTCPayServer.Client.Models
public class WebhookInvoiceReceivedPaymentEvent : WebhookInvoiceEvent
{
public WebhookInvoiceReceivedPaymentEvent(string type, string storeId) : base(type, storeId)
public WebhookInvoiceReceivedPaymentEvent()
{
}
public WebhookInvoiceReceivedPaymentEvent(WebhookEventType evtType) : base(evtType)
{
}
@ -97,14 +76,22 @@ namespace BTCPayServer.Client.Models
public class WebhookInvoicePaymentSettledEvent : WebhookInvoiceReceivedPaymentEvent
{
public WebhookInvoicePaymentSettledEvent(string storeId) : base(WebhookEventType.InvoicePaymentSettled, storeId)
public WebhookInvoicePaymentSettledEvent()
{
}
public WebhookInvoicePaymentSettledEvent(WebhookEventType evtType) : base(evtType)
{
}
}
public class WebhookInvoiceExpiredEvent : WebhookInvoiceEvent
{
public WebhookInvoiceExpiredEvent(string storeId) : base(WebhookEventType.InvoiceExpired, storeId)
public WebhookInvoiceExpiredEvent()
{
}
public WebhookInvoiceExpiredEvent(WebhookEventType evtType) : base(evtType)
{
}

View File

@ -19,7 +19,6 @@ namespace BTCPayServer.Client
public const string CanModifyStoreWebhooks = "btcpay.store.webhooks.canmodifywebhooks";
public const string CanModifyStoreSettingsUnscoped = "btcpay.store.canmodifystoresettings:";
public const string CanViewStoreSettings = "btcpay.store.canviewstoresettings";
public const string CanViewReports = "btcpay.store.canviewreports";
public const string CanViewInvoices = "btcpay.store.canviewinvoices";
public const string CanCreateInvoice = "btcpay.store.cancreateinvoice";
public const string CanModifyInvoices = "btcpay.store.canmodifyinvoices";
@ -34,11 +33,7 @@ namespace BTCPayServer.Client
public const string CanManageUsers = "btcpay.server.canmanageusers";
public const string CanDeleteUser = "btcpay.user.candeleteuser";
public const string CanManagePullPayments = "btcpay.store.canmanagepullpayments";
public const string CanArchivePullPayments = "btcpay.store.canarchivepullpayments";
public const string CanManagePayouts = "btcpay.store.canmanagepayouts";
public const string CanViewPayouts = "btcpay.store.canviewpayouts";
public const string CanCreatePullPayments = "btcpay.store.cancreatepullpayments";
public const string CanViewPullPayments = "btcpay.store.canviewpullpayments";
public const string CanCreateNonApprovedPullPayments = "btcpay.store.cancreatenonapprovedpullpayments";
public const string CanViewCustodianAccounts = "btcpay.store.canviewcustodianaccounts";
public const string CanManageCustodianAccounts = "btcpay.store.canmanagecustodianaccounts";
@ -57,7 +52,6 @@ namespace BTCPayServer.Client
yield return CanModifyServerSettings;
yield return CanModifyStoreSettings;
yield return CanViewStoreSettings;
yield return CanViewReports;
yield return CanViewPaymentRequests;
yield return CanModifyPaymentRequests;
yield return CanModifyProfile;
@ -75,9 +69,7 @@ namespace BTCPayServer.Client
yield return CanViewLightningInvoiceInStore;
yield return CanCreateLightningInvoiceInStore;
yield return CanManagePullPayments;
yield return CanArchivePullPayments;
yield return CanCreatePullPayments;
yield return CanViewPullPayments;
yield return CanCreateNonApprovedPullPayments;
yield return CanViewCustodianAccounts;
yield return CanManageCustodianAccounts;
@ -85,8 +77,6 @@ namespace BTCPayServer.Client
yield return CanWithdrawFromCustodianAccounts;
yield return CanTradeCustodianAccount;
yield return CanManageUsers;
yield return CanManagePayouts;
yield return CanViewPayouts;
}
}
public static bool IsValidPolicy(string policy)
@ -260,13 +250,11 @@ namespace BTCPayServer.Client
Policies.CanViewStoreSettings,
Policies.CanModifyStoreWebhooks,
Policies.CanModifyPaymentRequests,
Policies.CanManagePayouts,
Policies.CanUseLightningNodeInStore);
PolicyHasChild(policyMap,Policies.CanManageUsers, Policies.CanCreateUser);
PolicyHasChild(policyMap,Policies.CanManagePullPayments, Policies.CanCreatePullPayments, Policies.CanArchivePullPayments);
PolicyHasChild(policyMap,Policies.CanManagePullPayments, Policies.CanCreatePullPayments);
PolicyHasChild(policyMap,Policies.CanCreatePullPayments, Policies.CanCreateNonApprovedPullPayments);
PolicyHasChild(policyMap, Policies.CanCreateNonApprovedPullPayments, Policies.CanViewPullPayments);
PolicyHasChild(policyMap,Policies.CanModifyPaymentRequests, Policies.CanViewPaymentRequests);
PolicyHasChild(policyMap,Policies.CanModifyProfile, Policies.CanViewProfile);
PolicyHasChild(policyMap,Policies.CanUseLightningNodeInStore, Policies.CanViewLightningInvoiceInStore, Policies.CanCreateLightningInvoiceInStore);
@ -277,8 +265,7 @@ namespace BTCPayServer.Client
PolicyHasChild(policyMap, Policies.CanUseInternalLightningNode, Policies.CanCreateLightningInvoiceInternalNode, Policies.CanViewLightningInvoiceInternalNode);
PolicyHasChild(policyMap, Policies.CanManageCustodianAccounts, Policies.CanViewCustodianAccounts);
PolicyHasChild(policyMap, Policies.CanModifyInvoices, Policies.CanViewInvoices, Policies.CanCreateInvoice, Policies.CanCreateLightningInvoiceInStore);
PolicyHasChild(policyMap, Policies.CanViewStoreSettings, Policies.CanViewInvoices, Policies.CanViewPaymentRequests, Policies.CanViewReports, Policies.CanViewPullPayments, Policies.CanViewPayouts);
PolicyHasChild(policyMap, Policies.CanManagePayouts, Policies.CanViewPayouts);
PolicyHasChild(policyMap, Policies.CanViewStoreSettings, Policies.CanViewInvoices, Policies.CanViewPaymentRequests);
var missingPolicies = Policies.AllPolicies.ToHashSet();
//recurse through the tree to see which policies are not included in the tree

View File

@ -0,0 +1,28 @@
using NBitcoin;
using NBXplorer;
namespace BTCPayServer
{
public partial class BTCPayNetworkProvider
{
public void InitAlthash()
{
var nbxplorerNetwork = NBXplorerNetworkProvider.GetFromCryptoCode("HTML");
Add(new BTCPayNetwork()
{
CryptoCode = nbxplorerNetwork.CryptoCode,
DisplayName = "Htmlcoin",
BlockExplorerLink = NetworkType == ChainName.Mainnet ? "https://explorer.htmlcoin.com/api/tx/{0}" : "https://explorer.htmlcoin.com/api/tx/{0}",
NBXplorerNetwork = nbxplorerNetwork,
DefaultRateRules = new[]
{
"HTML_X = HTML_USD",
"HTML_USD = hitbtc(HTML_USD)"
},
CryptoImagePath = "imlegacy/althash.png",
DefaultSettings = BTCPayDefaultSettings.GetDefaultSettings(NetworkType),
CoinType = NetworkType == ChainName.Mainnet ? new KeyPath("172'") : new KeyPath("1'")
});
}
}
}

View File

@ -0,0 +1,30 @@
using NBitcoin;
namespace BTCPayServer
{
public partial class BTCPayNetworkProvider
{
public void InitArgoneum()
{
var nbxplorerNetwork = NBXplorerNetworkProvider.GetFromCryptoCode("AGM");
Add(new BTCPayNetwork()
{
CryptoCode = nbxplorerNetwork.CryptoCode,
DisplayName = "Argoneum",
BlockExplorerLink = NetworkType == ChainName.Mainnet
? "https://chainz.cryptoid.info/agm/tx.dws?{0}"
: "https://chainz.cryptoid.info/agm-test/tx.dws?{0}",
NBXplorerNetwork = nbxplorerNetwork,
DefaultRateRules = new[]
{
"AGM_X = AGM_BTC * BTC_X",
"AGM_BTC = argoneum(AGM_BTC)"
},
CryptoImagePath = "imlegacy/argoneum.png",
DefaultSettings = BTCPayDefaultSettings.GetDefaultSettings(NetworkType),
CoinType = NetworkType == ChainName.Mainnet ? new KeyPath("421'")
: new KeyPath("1'")
});
}
}
}

View File

@ -0,0 +1,28 @@
using NBitcoin;
namespace BTCPayServer
{
public partial class BTCPayNetworkProvider
{
public void InitBGold()
{
var nbxplorerNetwork = NBXplorerNetworkProvider.GetFromCryptoCode("BTG");
Add(new BTCPayNetwork()
{
CryptoCode = nbxplorerNetwork.CryptoCode,
DisplayName = "BGold",
BlockExplorerLink = NetworkType == ChainName.Mainnet ? "https://btgexplorer.com/tx/{0}" : "https://testnet.btgexplorer.com/tx/{0}",
NBXplorerNetwork = nbxplorerNetwork,
DefaultRateRules = new[]
{
"BTG_X = BTG_BTC * BTC_X",
"BTG_BTC = gate(BTG_BTC)",
},
CryptoImagePath = "imlegacy/btg.svg",
LightningImagePath = "imlegacy/btg-lightning.svg",
DefaultSettings = BTCPayDefaultSettings.GetDefaultSettings(NetworkType),
CoinType = NetworkType == ChainName.Mainnet ? new KeyPath("156'") : new KeyPath("1'")
});
}
}
}

View File

@ -0,0 +1,28 @@
using NBitcoin;
using NBXplorer;
namespace BTCPayServer
{
public partial class BTCPayNetworkProvider
{
public void InitBPlus()
{
var nbxplorerNetwork = NBXplorerNetworkProvider.GetFromCryptoCode("XBC");
Add(new BTCPayNetwork()
{
CryptoCode = nbxplorerNetwork.CryptoCode,
DisplayName = "BPlus",
BlockExplorerLink = NetworkType == ChainName.Mainnet ? "https://chainz.cryptoid.info/xbc/tx.dws?{0}" : "https://chainz.cryptoid.info/xbc/tx.dws?{0}",
NBXplorerNetwork = nbxplorerNetwork,
DefaultRateRules = new[]
{
"XBC_X = XBC_BTC * BTC_X",
"XBC_BTC = cryptopia(XBC_BTC)"
},
CryptoImagePath = "imlegacy/xbc.png",
DefaultSettings = BTCPayDefaultSettings.GetDefaultSettings(NetworkType),
CoinType = NetworkType == ChainName.Mainnet ? new KeyPath("65'") : new KeyPath("1'")
});
}
}
}

View File

@ -0,0 +1,29 @@
using NBitcoin;
using NBXplorer;
namespace BTCPayServer
{
public partial class BTCPayNetworkProvider
{
public void InitBitcore()
{
var nbxplorerNetwork = NBXplorerNetworkProvider.GetFromCryptoCode("BTX");
Add(new BTCPayNetwork()
{
CryptoCode = nbxplorerNetwork.CryptoCode,
DisplayName = "BitCore",
BlockExplorerLink = NetworkType == ChainName.Mainnet ? "https://explorer.bitcore.cc/tx/{0}" : "https://explorer.bitcore.cc/tx/{0}",
NBXplorerNetwork = nbxplorerNetwork,
DefaultRateRules = new[]
{
"BTX_X = BTX_BTC * BTC_X",
"BTX_BTC = graviex(BTX_BTC)"
},
CryptoImagePath = "imlegacy/bitcore.svg",
LightningImagePath = "imlegacy/bitcore-lightning.svg",
DefaultSettings = BTCPayDefaultSettings.GetDefaultSettings(NetworkType),
CoinType = NetworkType == ChainName.Mainnet ? new KeyPath("160'") : new KeyPath("1'")
});
}
}
}

View File

@ -0,0 +1,32 @@
using NBitcoin;
namespace BTCPayServer
{
public partial class BTCPayNetworkProvider
{
public void InitDash()
{
//not needed: NBitcoin.Altcoins.Dash.Instance.EnsureRegistered();
var nbxplorerNetwork = NBXplorerNetworkProvider.GetFromCryptoCode("DASH");
Add(new BTCPayNetwork()
{
CryptoCode = nbxplorerNetwork.CryptoCode,
DisplayName = "Dash",
BlockExplorerLink = NetworkType == ChainName.Mainnet
? "https://insight.dash.org/insight/tx/{0}"
: "https://testnet-insight.dashevo.org/insight/tx/{0}",
NBXplorerNetwork = nbxplorerNetwork,
DefaultRateRules = new[]
{
"DASH_X = DASH_BTC * BTC_X",
"DASH_BTC = bitfinex(DSH_BTC)"
},
CryptoImagePath = "imlegacy/dash.png",
DefaultSettings = BTCPayDefaultSettings.GetDefaultSettings(NetworkType),
//https://github.com/satoshilabs/slips/blob/master/slip-0044.md
CoinType = NetworkType == ChainName.Mainnet ? new KeyPath("5'")
: new KeyPath("1'")
});
}
}
}

View File

@ -0,0 +1,28 @@
using NBitcoin;
using NBXplorer;
namespace BTCPayServer
{
public partial class BTCPayNetworkProvider
{
public void InitDogecoin()
{
var nbxplorerNetwork = NBXplorerNetworkProvider.GetFromCryptoCode("DOGE");
Add(new BTCPayNetwork()
{
CryptoCode = nbxplorerNetwork.CryptoCode,
DisplayName = "Dogecoin",
BlockExplorerLink = NetworkType == ChainName.Mainnet ? "https://dogechain.info/tx/{0}" : "https://dogechain.info/tx/{0}",
NBXplorerNetwork = nbxplorerNetwork,
DefaultRateRules = new[]
{
"DOGE_X = DOGE_BTC * BTC_X",
"DOGE_BTC = bittrex(DOGE_BTC)"
},
CryptoImagePath = "imlegacy/dogecoin.png",
DefaultSettings = BTCPayDefaultSettings.GetDefaultSettings(NetworkType),
CoinType = NetworkType == ChainName.Mainnet ? new KeyPath("3'") : new KeyPath("1'")
});
}
}
}

View File

@ -0,0 +1,28 @@
using NBitcoin;
using NBXplorer;
namespace BTCPayServer
{
public partial class BTCPayNetworkProvider
{
public void InitFeathercoin()
{
var nbxplorerNetwork = NBXplorerNetworkProvider.GetFromCryptoCode("FTC");
Add(new BTCPayNetwork()
{
CryptoCode = nbxplorerNetwork.CryptoCode,
DisplayName = "Feathercoin",
BlockExplorerLink = NetworkType == ChainName.Mainnet ? "https://explorer.feathercoin.com/tx/{0}" : "https://explorer.feathercoin.com/tx/{0}",
NBXplorerNetwork = nbxplorerNetwork,
DefaultRateRules = new[]
{
"FTC_X = FTC_BTC * BTC_X",
"FTC_BTC = bittrex(FTC_BTC)"
},
CryptoImagePath = "imlegacy/feathercoin.png",
DefaultSettings = BTCPayDefaultSettings.GetDefaultSettings(NetworkType),
CoinType = NetworkType == ChainName.Mainnet ? new KeyPath("8'") : new KeyPath("1'")
});
}
}
}

View File

@ -0,0 +1,33 @@
using NBitcoin;
namespace BTCPayServer
{
public partial class BTCPayNetworkProvider
{
public void InitGroestlcoin()
{
var nbxplorerNetwork = NBXplorerNetworkProvider.GetFromCryptoCode("GRS");
Add(new BTCPayNetwork()
{
CryptoCode = nbxplorerNetwork.CryptoCode,
DisplayName = "Groestlcoin",
BlockExplorerLink = NetworkType == ChainName.Mainnet
? "https://chainz.cryptoid.info/grs/tx.dws?{0}.htm"
: "https://chainz.cryptoid.info/grs-test/tx.dws?{0}.htm",
NBXplorerNetwork = nbxplorerNetwork,
DefaultRateRules = new[]
{
"GRS_X = GRS_BTC * BTC_X",
"GRS_BTC = bittrex(GRS_BTC)"
},
CryptoImagePath = "imlegacy/groestlcoin.png",
LightningImagePath = "imlegacy/groestlcoin-lightning.svg",
DefaultSettings = BTCPayDefaultSettings.GetDefaultSettings(NetworkType),
CoinType = NetworkType == ChainName.Mainnet ? new KeyPath("17'") : new KeyPath("1'"),
SupportRBF = true,
SupportPayJoin = true,
VaultSupported = true
});
}
}
}

View File

@ -0,0 +1,46 @@
using System.Collections.Generic;
using NBitcoin;
using NBXplorer;
namespace BTCPayServer
{
public partial class BTCPayNetworkProvider
{
public void InitLitecoin()
{
var nbxplorerNetwork = NBXplorerNetworkProvider.GetFromCryptoCode("LTC");
Add(new BTCPayNetwork()
{
CryptoCode = nbxplorerNetwork.CryptoCode,
DisplayName = "Litecoin",
BlockExplorerLink = NetworkType == ChainName.Mainnet
? "https://live.blockcypher.com/ltc/tx/{0}/"
: "http://explorer.litecointools.com/tx/{0}",
NBXplorerNetwork = nbxplorerNetwork,
DefaultRateRules = new[]
{
"LTC_X = LTC_BTC * BTC_X",
"LTC_BTC = coingecko(LTC_BTC)"
},
CryptoImagePath = "imlegacy/litecoin.svg",
LightningImagePath = "imlegacy/litecoin-lightning.svg",
DefaultSettings = BTCPayDefaultSettings.GetDefaultSettings(NetworkType),
CoinType = NetworkType == ChainName.Mainnet ? new KeyPath("2'") : new KeyPath("1'"),
//https://github.com/pooler/electrum-ltc/blob/0d6989a9d2fb2edbea421c116e49d1015c7c5a91/electrum_ltc/constants.py
ElectrumMapping = NetworkType == ChainName.Mainnet
? new Dictionary<uint, DerivationType>()
{
{0x0488b21eU, DerivationType.Legacy },
{0x049d7cb2U, DerivationType.SegwitP2SH },
{0x04b24746U, DerivationType.Segwit },
}
: new Dictionary<uint, DerivationType>()
{
{0x043587cfU, DerivationType.Legacy },
{0x044a5262U, DerivationType.SegwitP2SH },
{0x045f1cf6U, DerivationType.Segwit }
}
});
}
}
}

View File

@ -0,0 +1,29 @@
using NBitcoin;
using NBXplorer;
namespace BTCPayServer
{
public partial class BTCPayNetworkProvider
{
public void InitMonacoin()
{
var nbxplorerNetwork = NBXplorerNetworkProvider.GetFromCryptoCode("MONA");
Add(new BTCPayNetwork()
{
CryptoCode = nbxplorerNetwork.CryptoCode,
DisplayName = "Monacoin",
BlockExplorerLink = NetworkType == ChainName.Mainnet ? "https://mona.insight.monaco-ex.org/insight/tx/{0}" : "https://testnet-mona.insight.monaco-ex.org/insight/tx/{0}",
NBXplorerNetwork = nbxplorerNetwork,
DefaultRateRules = new[]
{
"MONA_X = MONA_BTC * BTC_X",
"MONA_BTC = bittrex(MONA_BTC)"
},
CryptoImagePath = "imlegacy/monacoin.png",
LightningImagePath = "imlegacy/mona-lightning.svg",
DefaultSettings = BTCPayDefaultSettings.GetDefaultSettings(NetworkType),
CoinType = NetworkType == ChainName.Mainnet ? new KeyPath("22'") : new KeyPath("1'")
});
}
}
}

View File

@ -0,0 +1,28 @@
using NBitcoin;
using NBXplorer;
namespace BTCPayServer
{
public partial class BTCPayNetworkProvider
{
public void InitPolis()
{
var nbxplorerNetwork = NBXplorerNetworkProvider.GetFromCryptoCode("POLIS");
Add(new BTCPayNetwork()
{
CryptoCode = nbxplorerNetwork.CryptoCode,
DisplayName = "Polis",
BlockExplorerLink = NetworkType == ChainName.Mainnet ? "https://blockbook.polispay.org/tx/{0}" : "https://blockbook.polispay.org/tx/{0}",
NBXplorerNetwork = nbxplorerNetwork,
DefaultRateRules = new[]
{
"POLIS_X = POLIS_BTC * BTC_X",
"POLIS_BTC = polispay(POLIS_BTC)"
},
CryptoImagePath = "imlegacy/polis.png",
DefaultSettings = BTCPayDefaultSettings.GetDefaultSettings(NetworkType),
CoinType = NetworkType == ChainName.Mainnet ? new KeyPath("1997'") : new KeyPath("1'")
});
}
}
}

View File

@ -0,0 +1,28 @@
using NBitcoin;
using NBXplorer;
namespace BTCPayServer
{
public partial class BTCPayNetworkProvider
{
public void InitUfo()
{
var nbxplorerNetwork = NBXplorerNetworkProvider.GetFromCryptoCode("UFO");
Add(new BTCPayNetwork()
{
CryptoCode = nbxplorerNetwork.CryptoCode,
DisplayName = "Ufo",
BlockExplorerLink = NetworkType == ChainName.Mainnet ? "https://chainz.cryptoid.info/ufo/tx.dws?{0}" : "https://chainz.cryptoid.info/ufo/tx.dws?{0}",
NBXplorerNetwork = nbxplorerNetwork,
DefaultRateRules = new[]
{
"UFO_X = UFO_BTC * BTC_X",
"UFO_BTC = coinexchange(UFO_BTC)"
},
CryptoImagePath = "imlegacy/ufo.png",
DefaultSettings = BTCPayDefaultSettings.GetDefaultSettings(NetworkType),
CoinType = NetworkType == ChainName.Mainnet ? new KeyPath("202'") : new KeyPath("1'")
});
}
}
}

View File

@ -0,0 +1,28 @@
using NBitcoin;
using NBXplorer;
namespace BTCPayServer
{
public partial class BTCPayNetworkProvider
{
public void InitViacoin()
{
var nbxplorerNetwork = NBXplorerNetworkProvider.GetFromCryptoCode("VIA");
Add(new BTCPayNetwork()
{
CryptoCode = nbxplorerNetwork.CryptoCode,
DisplayName = "Viacoin",
BlockExplorerLink = NetworkType == ChainName.Mainnet ? "https://explorer.viacoin.org/tx/{0}" : "https://explorer.viacoin.org/tx/{0}",
NBXplorerNetwork = nbxplorerNetwork,
DefaultRateRules = new[]
{
"VIA_X = VIA_BTC * BTC_X",
"VIA_BTC = bittrex(VIA_BTC)"
},
CryptoImagePath = "imlegacy/viacoin.png",
DefaultSettings = BTCPayDefaultSettings.GetDefaultSettings(NetworkType),
CoinType = NetworkType == ChainName.Mainnet ? new KeyPath("14'") : new KeyPath("1'")
});
}
}
}

View File

@ -0,0 +1,37 @@
#if ALTCOINS
using NBitcoin;
using NBitcoin.Altcoins;
using NBitcoin.Altcoins.Elements;
using NBXplorer;
namespace BTCPayServer
{
public partial class BTCPayNetworkProvider
{
public void InitLiquid()
{
var nbxplorerNetwork = NBXplorerNetworkProvider.GetFromCryptoCode("LBTC");
Add(new ElementsBTCPayNetwork()
{
AssetId = NetworkType == ChainName.Mainnet ? ElementsParams<Liquid>.PeggedAssetId : ElementsParams<Liquid.LiquidRegtest>.PeggedAssetId,
CryptoCode = "LBTC",
NetworkCryptoCode = "LBTC",
DisplayName = "Liquid Bitcoin",
DefaultRateRules = new[]
{
"LBTC_X = LBTC_BTC * BTC_X",
"LBTC_BTC = 1",
},
BlockExplorerLink = NetworkType == ChainName.Mainnet ? "https://liquid.network/tx/{0}" : "https://liquid.network/testnet/tx/{0}",
NBXplorerNetwork = nbxplorerNetwork,
CryptoImagePath = "imlegacy/liquid.png",
DefaultSettings = BTCPayDefaultSettings.GetDefaultSettings(NetworkType),
CoinType = NetworkType == ChainName.Mainnet ? new KeyPath("1776'") : new KeyPath("1'"),
SupportRBF = true
});
}
}
}
#endif

View File

@ -0,0 +1,83 @@
#if ALTCOINS
using NBitcoin;
namespace BTCPayServer
{
public partial class BTCPayNetworkProvider
{
public void InitLiquidAssets()
{
var nbxplorerNetwork = NBXplorerNetworkProvider.GetFromCryptoCode("LBTC");
Add(new ElementsBTCPayNetwork()
{
CryptoCode = "USDt",
NetworkCryptoCode = "LBTC",
ShowSyncSummary = false,
DefaultRateRules = new[]
{
"USDT_UST = 1",
"USDT_X = USDT_BTC * BTC_X",
"USDT_BTC = bitfinex(UST_BTC)",
},
AssetId = new uint256("ce091c998b83c78bb71a632313ba3760f1763d9cfcffae02258ffa9865a37bd2"),
DisplayName = "Liquid Tether",
BlockExplorerLink = NetworkType == ChainName.Mainnet ? "https://liquid.network/tx/{0}" : "https://liquid.network/testnet/tx/{0}",
NBXplorerNetwork = nbxplorerNetwork,
CryptoImagePath = "imlegacy/liquid-tether.svg",
DefaultSettings = BTCPayDefaultSettings.GetDefaultSettings(NetworkType),
CoinType = NetworkType == ChainName.Mainnet ? new KeyPath("1776'") : new KeyPath("1'"),
SupportRBF = true,
SupportLightning = false
});
Add(new ElementsBTCPayNetwork()
{
CryptoCode = "ETB",
NetworkCryptoCode = "LBTC",
ShowSyncSummary = false,
DefaultRateRules = new[]
{
"ETB_X = ETB_BTC * BTC_X",
"ETB_BTC = bitpay(ETB_BTC)"
},
Divisibility = 2,
AssetId = new uint256("aa775044c32a7df391902b3659f46dfe004ccb2644ce2ddc7dba31e889391caf"),
DisplayName = "Ethiopian Birr",
BlockExplorerLink = NetworkType == ChainName.Mainnet ? "https://liquid.network/tx/{0}" : "https://liquid.network/testnet/tx/{0}",
NBXplorerNetwork = nbxplorerNetwork,
CryptoImagePath = "imlegacy/etb.png",
DefaultSettings = BTCPayDefaultSettings.GetDefaultSettings(NetworkType),
CoinType = NetworkType == ChainName.Mainnet ? new KeyPath("1776'") : new KeyPath("1'"),
SupportRBF = true,
SupportLightning = false
});
Add(new ElementsBTCPayNetwork()
{
CryptoCode = "LCAD",
NetworkCryptoCode = "LBTC",
ShowSyncSummary = false,
DefaultRateRules = new[]
{
"LCAD_CAD = 1",
"LCAD_X = CAD_BTC * BTC_X",
"LCAD_BTC = bylls(CAD_BTC)",
"CAD_BTC = LCAD_BTC"
},
AssetId = new uint256("0e99c1a6da379d1f4151fb9df90449d40d0608f6cb33a5bcbfc8c265f42bab0a"),
DisplayName = "Liquid CAD",
BlockExplorerLink = NetworkType == ChainName.Mainnet ? "https://liquid.network/tx/{0}" : "https://liquid.network/testnet/tx/{0}",
NBXplorerNetwork = nbxplorerNetwork,
CryptoImagePath = "imlegacy/lcad.png",
DefaultSettings = BTCPayDefaultSettings.GetDefaultSettings(NetworkType),
CoinType = NetworkType == ChainName.Mainnet ? new KeyPath("1776'") : new KeyPath("1'"),
SupportRBF = true,
SupportLightning = false
});
}
}
}
#endif

View File

@ -0,0 +1,50 @@
#if ALTCOINS
using System;
using System.Collections.Generic;
using System.Linq;
using BTCPayServer.Common;
using NBitcoin;
using NBXplorer;
using NBXplorer.Models;
namespace BTCPayServer
{
public class ElementsBTCPayNetwork : BTCPayNetwork
{
public string NetworkCryptoCode { get; set; }
public uint256 AssetId { get; set; }
public override bool ReadonlyWallet { get; set; } = true;
public override IEnumerable<(MatchedOutput matchedOutput, OutPoint outPoint)> GetValidOutputs(
NewTransactionEvent evtOutputs)
{
return evtOutputs.Outputs.Where(output =>
output.Value is AssetMoney assetMoney && assetMoney.AssetId == AssetId).Select(output =>
{
var outpoint = new OutPoint(evtOutputs.TransactionData.TransactionHash, output.Index);
return (output, outpoint);
});
}
public override List<TransactionInformation> FilterValidTransactions(List<TransactionInformation> transactionInformationSet)
{
return transactionInformationSet.FindAll(information =>
information.Outputs.Any(output =>
output.Value is AssetMoney assetMoney && assetMoney.AssetId == AssetId) ||
information.Inputs.Any(output =>
output.Value is AssetMoney assetMoney && assetMoney.AssetId == AssetId));
}
public override PaymentUrlBuilder GenerateBIP21(string cryptoInfoAddress, decimal? cryptoInfoDue)
{
//precision 0: 10 = 0.00000010
//precision 2: 10 = 0.00001000
//precision 8: 10 = 10
var money = cryptoInfoDue / (decimal)Math.Pow(10, 8 - Divisibility);
var builder = base.GenerateBIP21(cryptoInfoAddress, money);
builder.QueryParams.Add("assetid", AssetId.ToString());
return builder;
}
}
}
#endif

View File

@ -0,0 +1,18 @@
#if ALTCOINS
using System.Collections.Generic;
using System.Linq;
namespace BTCPayServer
{
public static class LiquidExtensions
{
public static IEnumerable<string> GetAllElementsSubChains(this BTCPayNetworkProvider networkProvider, BTCPayNetworkProvider unfiltered)
{
var elementsBased = networkProvider.GetAll().OfType<ElementsBTCPayNetwork>();
var parentChains = elementsBased.Select(network => network.NetworkCryptoCode.ToUpperInvariant()).Distinct();
return unfiltered.GetAll().OfType<ElementsBTCPayNetwork>()
.Where(network => parentChains.Contains(network.NetworkCryptoCode)).Select(network => network.CryptoCode.ToUpperInvariant());
}
}
}
#endif

View File

@ -0,0 +1,28 @@
using NBitcoin;
namespace BTCPayServer
{
public partial class BTCPayNetworkProvider
{
public void InitMonero()
{
Add(new MoneroLikeSpecificBtcPayNetwork()
{
CryptoCode = "XMR",
DisplayName = "Monero",
Divisibility = 12,
BlockExplorerLink =
NetworkType == ChainName.Mainnet
? "https://www.exploremonero.com/transaction/{0}"
: "https://testnet.xmrchain.net/tx/{0}",
DefaultRateRules = new[]
{
"XMR_X = XMR_BTC * BTC_X",
"XMR_BTC = kraken(XMR_BTC)"
},
CryptoImagePath = "/imlegacy/monero.svg",
UriScheme = "monero"
});
}
}
}

View File

@ -0,0 +1,8 @@
namespace BTCPayServer
{
public class MoneroLikeSpecificBtcPayNetwork : BTCPayNetworkBase
{
public int MaxTrackedConfirmation = 10;
public string UriScheme { get; set; }
}
}

View File

@ -0,0 +1,29 @@
using NBitcoin;
namespace BTCPayServer
{
public partial class BTCPayNetworkProvider
{
// Change this if you want another zcash coin
public void InitZcash()
{
Add(new ZcashLikeSpecificBtcPayNetwork()
{
CryptoCode = "ZEC",
DisplayName = "Zcash",
Divisibility = 8,
BlockExplorerLink =
NetworkType == ChainName.Mainnet
? "https://www.exploreZcash.com/transaction/{0}"
: "https://testnet.xmrchain.net/tx/{0}",
DefaultRateRules = new[]
{
"ZEC_X = ZEC_BTC * BTC_X",
"ZEC_BTC = kraken(ZEC_BTC)"
},
CryptoImagePath = "/imlegacy/zcash.png",
UriScheme = "zcash"
});
}
}
}

View File

@ -0,0 +1,8 @@
namespace BTCPayServer
{
public class ZcashLikeSpecificBtcPayNetwork : BTCPayNetworkBase
{
public int MaxTrackedConfirmation = 10;
public string UriScheme { get; set; }
}
}

View File

@ -4,8 +4,6 @@ using System.Globalization;
using System.IO;
using System.Linq;
using BTCPayServer.Common;
using Microsoft.AspNetCore.HttpOverrides;
using Microsoft.Extensions.DependencyInjection;
using NBitcoin;
using NBXplorer;
using NBXplorer.Models;
@ -64,31 +62,6 @@ namespace BTCPayServer
public KeyPath CoinType { get; set; }
public Dictionary<uint, DerivationType> ElectrumMapping = new Dictionary<uint, DerivationType>();
public BTCPayNetwork SetDefaultElectrumMapping(ChainName chainName)
{
//https://github.com/spesmilo/electrum/blob/11733d6bc271646a00b69ff07657119598874da4/electrum/constants.py
ElectrumMapping = chainName == ChainName.Mainnet
? new Dictionary<uint, DerivationType>()
{
{0x0488b21eU, DerivationType.Legacy }, // xpub
{0x049d7cb2U, DerivationType.SegwitP2SH }, // ypub
{0x04b24746U, DerivationType.Segwit }, //zpub
}
: new Dictionary<uint, DerivationType>()
{
{0x043587cfU, DerivationType.Legacy}, // tpub
{0x044a5262U, DerivationType.SegwitP2SH}, // upub
{0x045f1cf6U, DerivationType.Segwit} // vpub
};
if (!NBitcoinNetwork.Consensus.SupportSegwit)
{
ElectrumMapping =
ElectrumMapping
.Where(kv => kv.Value == DerivationType.Legacy)
.ToDictionary(k => k.Key, k => k.Value);
}
return this;
}
public virtual bool WalletSupported { get; set; } = true;
public virtual bool ReadonlyWallet { get; set; } = false;
@ -134,8 +107,25 @@ namespace BTCPayServer
public abstract class BTCPayNetworkBase
{
private string _blockExplorerLink;
public bool ShowSyncSummary { get; set; } = true;
public string CryptoCode { get; set; }
public string BlockExplorerLink
{
get => _blockExplorerLink;
set
{
if (string.IsNullOrEmpty(BlockExplorerLinkDefault))
{
BlockExplorerLinkDefault = value;
}
_blockExplorerLink = value;
}
}
public string BlockExplorerLinkDefault { get; set; }
public string DisplayName { get; set; }
public int Divisibility { get; set; } = 8;
public bool IsBTC
@ -163,8 +153,5 @@ namespace BTCPayServer
{
return NBitcoin.JsonConverters.Serializer.ToString(obj, null);
}
[Obsolete("Use TransactionLinkProviders service instead")]
public string BlockExplorerLink { get; set; }
}
}

View File

@ -0,0 +1,44 @@
using System.Collections.Generic;
using NBitcoin;
using NBXplorer;
namespace BTCPayServer
{
public partial class BTCPayNetworkProvider
{
public void InitBitcoin()
{
var nbxplorerNetwork = NBXplorerNetworkProvider.GetFromCryptoCode("BTC");
Add(new BTCPayNetwork()
{
CryptoCode = nbxplorerNetwork.CryptoCode,
DisplayName = "Bitcoin",
BlockExplorerLink = NetworkType == ChainName.Mainnet ? "https://mempool.space/tx/{0}" :
NetworkType == Bitcoin.Instance.Signet.ChainName ? "https://mempool.space/signet/tx/{0}"
: "https://mempool.space/testnet/tx/{0}",
NBXplorerNetwork = nbxplorerNetwork,
CryptoImagePath = "imlegacy/bitcoin.svg",
LightningImagePath = "imlegacy/bitcoin-lightning.svg",
DefaultSettings = BTCPayDefaultSettings.GetDefaultSettings(NetworkType),
CoinType = NetworkType == ChainName.Mainnet ? new KeyPath("0'") : new KeyPath("1'"),
SupportRBF = true,
SupportPayJoin = true,
VaultSupported = true,
//https://github.com/spesmilo/electrum/blob/11733d6bc271646a00b69ff07657119598874da4/electrum/constants.py
ElectrumMapping = NetworkType == ChainName.Mainnet
? new Dictionary<uint, DerivationType>()
{
{0x0488b21eU, DerivationType.Legacy }, // xpub
{0x049d7cb2U, DerivationType.SegwitP2SH }, // ypub
{0x04b24746U, DerivationType.Segwit }, //zpub
}
: new Dictionary<uint, DerivationType>()
{
{0x043587cfU, DerivationType.Legacy}, // tpub
{0x044a5262U, DerivationType.SegwitP2SH}, // upub
{0x045f1cf6U, DerivationType.Segwit} // vpub
}
});
}
}
}

View File

@ -1,15 +1,8 @@
using System;
using System.Collections.Generic;
using System.Linq;
using BTCPayServer.Configuration;
using BTCPayServer.Logging;
using Microsoft.AspNetCore.DataProtection.KeyManagement;
using Microsoft.AspNetCore.HttpOverrides;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using NBitcoin;
using NBXplorer;
using StandardConfiguration;
namespace BTCPayServer
{
@ -26,39 +19,92 @@ namespace BTCPayServer
}
}
public ChainName NetworkType { get; private set; }
public BTCPayNetworkProvider(
IEnumerable<BTCPayNetworkBase> networks,
SelectedChains selectedChains,
NBXplorerNetworkProvider nbxplorerNetworkProvider,
Logs logs,
IConfiguration configuration)
BTCPayNetworkProvider(BTCPayNetworkProvider unfiltered, string[] cryptoCodes)
{
#if !ALTCOINS
var onlyBTC = networks.Count() == 1 && networks.First().IsBTC;
if (!onlyBTC)
throw new ConfigException($"This build of BTCPay Server does not support altcoins");
NetworkType = unfiltered.NetworkType;
_NBXplorerNetworkProvider = new NBXplorerNetworkProvider(unfiltered.NetworkType);
_Networks = new Dictionary<string, BTCPayNetworkBase>();
cryptoCodes = cryptoCodes.Select(c => c.ToUpperInvariant()).ToArray();
foreach (var network in unfiltered._Networks)
{
if (cryptoCodes.Contains(network.Key))
{
_Networks.Add(network.Key, network.Value);
}
}
}
public ChainName NetworkType { get; private set; }
public BTCPayNetworkProvider(ChainName networkType)
{
_NBXplorerNetworkProvider = new NBXplorerNetworkProvider(networkType);
NetworkType = networkType;
InitBitcoin();
#if ALTCOINS
InitLiquid();
InitLiquidAssets();
InitLitecoin();
InitBitcore();
InitDogecoin();
InitBGold();
InitMonacoin();
InitDash();
InitFeathercoin();
InitAlthash();
InitGroestlcoin();
InitViacoin();
InitMonero();
InitZcash();
// InitArgoneum();//their rate source is down 9/15/20.
// InitMonetaryUnit(); Not supported from Bittrex from 11/23/2022, dead shitcoin
// Assume that electrum mappings are same as BTC if not specified
foreach (var network in _Networks.Values.OfType<BTCPayNetwork>())
{
if (network.ElectrumMapping.Count == 0)
{
network.ElectrumMapping = GetNetwork<BTCPayNetwork>("BTC").ElectrumMapping;
if (!network.NBitcoinNetwork.Consensus.SupportSegwit)
{
network.ElectrumMapping =
network.ElectrumMapping
.Where(kv => kv.Value == DerivationType.Legacy)
.ToDictionary(k => k.Key, k => k.Value);
}
}
}
// Disabled because of https://twitter.com/Cryptopia_NZ/status/1085084168852291586
//InitBPlus();
//InitUfo();
#endif
}
_NBXplorerNetworkProvider = nbxplorerNetworkProvider;
NetworkType = nbxplorerNetworkProvider.NetworkType;
foreach (var network in networks)
{
_Networks.Add(network.CryptoCode.ToUpperInvariant(), network);
}
foreach (var chain in selectedChains.ExplicitlySelected)
{
if (GetNetwork<BTCPayNetworkBase>(chain) == null)
throw new ConfigException($"Invalid chains \"{chain}\"");
}
logs.Configuration.LogInformation(
"Supported chains: " + String.Join(',', _Networks.Select(n => n.Key).ToArray()));
/// <summary>
/// Keep only the specified crypto
/// </summary>
/// <param name="cryptoCodes">Crypto to support</param>
/// <returns></returns>
public BTCPayNetworkProvider Filter(string[] cryptoCodes)
{
return new BTCPayNetworkProvider(this, cryptoCodes);
}
public BTCPayNetwork BTC => GetNetwork<BTCPayNetwork>("BTC");
public BTCPayNetworkBase DefaultNetwork => BTC ?? GetAll().First();
public void Add(BTCPayNetwork network)
{
if (network.NBitcoinNetwork == null)
return;
Add(network as BTCPayNetworkBase);
}
public void Add(BTCPayNetworkBase network)
{
_Networks.Add(network.CryptoCode.ToUpperInvariant(), network);
}
public IEnumerable<BTCPayNetworkBase> GetAll()
{
return _Networks.Values.ToArray();

View File

@ -4,13 +4,10 @@
<ItemGroup>
<FrameworkReference Include="Microsoft.AspNetCore.App" />
<PackageReference Include="NBXplorer.Client" Version="4.2.6" />
<PackageReference Include="NBXplorer.Client" Version="4.2.5" />
<PackageReference Include="NicolasDorier.StandardConfiguration" Version="2.0.1" />
</ItemGroup>
<ItemGroup Condition="'$(Altcoins)' != 'true'">
<Compile Remove="Altcoins\**\*.cs"></Compile>
</ItemGroup>
<ItemGroup>
<Folder Include="Altcoins\" />
</ItemGroup>
</Project>

View File

@ -1,49 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using BTCPayServer.Logging;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json.Bson;
namespace BTCPayServer
{
public class SelectedChains
{
HashSet<string> chains = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
bool all = false;
public SelectedChains(IConfiguration configuration, Logs logs)
{
foreach (var chain in (configuration["chains"] ?? "btc")
.Split(',', StringSplitOptions.RemoveEmptyEntries)
.Select(t => t.ToUpperInvariant()))
{
if (new[] { "ETH", "USDT20", "FAU" }.Contains(chain, StringComparer.OrdinalIgnoreCase))
{
logs.Configuration.LogWarning($"'{chain}' is not anymore supported, please remove it from 'chains'");
continue;
}
if (chain == "*")
{
all = true;
continue;
}
chains.Add(chain);
}
if (chains.Count == 0)
chains.Add("BTC");
if (all)
chains.Clear();
}
public bool Contains(string cryptoCode)
{
return all || chains.Contains(cryptoCode);
}
public void Add(string cryptoCode)
{
chains.Add(cryptoCode);
}
public IEnumerable<string> ExplicitlySelected => chains;
}
}

View File

@ -1,13 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="../Build/Version.csproj" Condition="Exists('../Build/Version.csproj')" />
<Import Project="../Build/Common.csproj" />
<ItemGroup>
<FrameworkReference Include="Microsoft.AspNetCore.App" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.0">
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="6.0.9">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="8.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="6.0.9" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\BTCPayServer.Abstractions\BTCPayServer.Abstractions.csproj" />

View File

@ -1,6 +1,5 @@
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Internal;
namespace BTCPayServer.Data
{

View File

@ -14,7 +14,6 @@ namespace BTCPayServer.Data
public DateTimeOffset Created { get; set; }
public bool TagAllInvoices { get; set; }
public string Settings { get; set; }
public bool Archived { get; set; }
internal static void OnModelCreating(ModelBuilder builder)
{

View File

@ -30,6 +30,9 @@ namespace BTCPayServer.Data
public List<PendingInvoiceData> PendingInvoices { get; set; }
public List<InvoiceSearchData> InvoiceSearchData { get; set; }
public List<RefundData> Refunds { get; set; }
public string CurrentRefundId { get; set; }
[ForeignKey("Id,CurrentRefundId")]
public RefundData CurrentRefund { get; set; }
internal static void OnModelCreating(ModelBuilder builder, DatabaseFacade databaseFacade)
@ -39,6 +42,8 @@ namespace BTCPayServer.Data
.WithMany(a => a.Invoices).OnDelete(DeleteBehavior.Cascade);
builder.Entity<InvoiceData>().HasIndex(o => o.StoreDataId);
builder.Entity<InvoiceData>().HasIndex(o => o.OrderId);
builder.Entity<InvoiceData>()
.HasOne(o => o.CurrentRefund);
builder.Entity<InvoiceData>().HasIndex(o => o.Created);
if (databaseFacade.IsNpgsql())

View File

@ -1,6 +1,5 @@
using System.ComponentModel.DataAnnotations;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Internal;
namespace BTCPayServer.Data
{
@ -14,6 +13,7 @@ namespace BTCPayServer.Data
public PullPaymentData PullPaymentData { get; set; }
public InvoiceData InvoiceData { get; set; }
internal static void OnModelCreating(ModelBuilder builder)
{
builder.Entity<RefundData>()

View File

@ -48,7 +48,6 @@ namespace BTCPayServer.Data
public IEnumerable<StoreSettingData> Settings { get; set; }
public IEnumerable<FormData> Forms { get; set; }
public IEnumerable<StoreRole> StoreRoles { get; set; }
public bool Archived { get; set; }
internal static void OnModelCreating(ModelBuilder builder, DatabaseFacade databaseFacade)
{

View File

@ -1,12 +1,14 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
namespace BTCPayServer.Data
{
public class WalletObjectData : IEqualityComparer<WalletObjectData>
public class WalletObjectData
{
public class Types
{
@ -86,30 +88,9 @@ namespace BTCPayServer.Data
if (databaseFacade.IsNpgsql())
{
builder.Entity<WalletObjectData>()
.Property(o => o.Data)
.HasColumnType("JSONB");
.Property(o => o.Data)
.HasColumnType("JSONB");
}
}
public bool Equals(WalletObjectData x, WalletObjectData y)
{
if (ReferenceEquals(x, y)) return true;
if (ReferenceEquals(x, null)) return false;
if (ReferenceEquals(y, null)) return false;
if (x.GetType() != y.GetType()) return false;
return string.Equals(x.WalletId, y.WalletId, StringComparison.InvariantCultureIgnoreCase) &&
string.Equals(x.Type, y.Type, StringComparison.InvariantCultureIgnoreCase) &&
string.Equals(x.Id, y.Id, StringComparison.InvariantCultureIgnoreCase);
}
public int GetHashCode(WalletObjectData obj)
{
HashCode hashCode = new HashCode();
hashCode.Add(obj.WalletId, StringComparer.InvariantCultureIgnoreCase);
hashCode.Add(obj.Type, StringComparer.InvariantCultureIgnoreCase);
hashCode.Add(obj.Id, StringComparer.InvariantCultureIgnoreCase);
return hashCode.ToHashCode();
}
}
}

Some files were not shown because too many files have changed in this diff Show More