diff --git a/BTCPayServerPlugins.sln b/BTCPayServerPlugins.sln index 0d74a68..9a59434 100644 --- a/BTCPayServerPlugins.sln +++ b/BTCPayServerPlugins.sln @@ -53,8 +53,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BTCPayServer.Plugins.FileSe EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BTCPayServer.Plugins.DynamicReports", "Plugins\BTCPayServer.Plugins.DynamicReports\BTCPayServer.Plugins.DynamicReports.csproj", "{BCB4E68D-089F-481E-A3AE-FC9CED6AA34D}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BTCPayServer.Plugins.Vouchers", "Plugins\BTCPayServer.Plugins.Vouchers\BTCPayServer.Plugins.Vouchers.csproj", "{ED061A37-488F-429C-A291-6A5188A47443}" -EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -255,14 +253,6 @@ Global {BCB4E68D-089F-481E-A3AE-FC9CED6AA34D}.Altcoins-Debug|Any CPU.Build.0 = Debug|Any CPU {BCB4E68D-089F-481E-A3AE-FC9CED6AA34D}.Altcoins-Release|Any CPU.ActiveCfg = Debug|Any CPU {BCB4E68D-089F-481E-A3AE-FC9CED6AA34D}.Altcoins-Release|Any CPU.Build.0 = Debug|Any CPU - {ED061A37-488F-429C-A291-6A5188A47443}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {ED061A37-488F-429C-A291-6A5188A47443}.Debug|Any CPU.Build.0 = Debug|Any CPU - {ED061A37-488F-429C-A291-6A5188A47443}.Release|Any CPU.ActiveCfg = Release|Any CPU - {ED061A37-488F-429C-A291-6A5188A47443}.Release|Any CPU.Build.0 = Release|Any CPU - {ED061A37-488F-429C-A291-6A5188A47443}.Altcoins-Debug|Any CPU.ActiveCfg = Debug|Any CPU - {ED061A37-488F-429C-A291-6A5188A47443}.Altcoins-Debug|Any CPU.Build.0 = Debug|Any CPU - {ED061A37-488F-429C-A291-6A5188A47443}.Altcoins-Release|Any CPU.ActiveCfg = Debug|Any CPU - {ED061A37-488F-429C-A291-6A5188A47443}.Altcoins-Release|Any CPU.Build.0 = Debug|Any CPU EndGlobalSection GlobalSection(NestedProjects) = preSolution {B19C9F52-DC47-466D-8B5C-2D202B7B003F} = {9E04ECE9-E304-4FF2-9CBC-83256E6C6962} diff --git a/Plugins/BTCPayServer.Plugins.Vouchers/BTCPayServer.Plugins.Vouchers.csproj b/Plugins/BTCPayServer.Plugins.Vouchers/BTCPayServer.Plugins.Vouchers.csproj deleted file mode 100644 index a4ae82e..0000000 --- a/Plugins/BTCPayServer.Plugins.Vouchers/BTCPayServer.Plugins.Vouchers.csproj +++ /dev/null @@ -1,42 +0,0 @@ - - - - net6.0 - 10 - - - - - Vouchers - Allows you to give users Bitcoin vouchers. - 1.0.0 - - - - true - false - true - - - - - - StaticWebAssetsEnabled=false - false - runtime;native;build;buildTransitive;contentFiles - - - - - - - - - - - - - - - - diff --git a/Plugins/BTCPayServer.Plugins.Vouchers/Program.cs b/Plugins/BTCPayServer.Plugins.Vouchers/Program.cs deleted file mode 100644 index 05e0c31..0000000 --- a/Plugins/BTCPayServer.Plugins.Vouchers/Program.cs +++ /dev/null @@ -1,22 +0,0 @@ -using BTCPayServer.Abstractions.Contracts; -using BTCPayServer.Abstractions.Models; -using BTCPayServer.Abstractions.Services; -using Microsoft.Extensions.DependencyInjection; - -namespace BTCPayServer.Plugins.Vouchers -{ - public class VoucherPlugin : BaseBTCPayServerPlugin - { - public override IBTCPayServerPlugin.PluginDependency[] Dependencies { get; } = - { - new() {Identifier = nameof(BTCPayServer), Condition = ">=1.11.0"} - }; - - public override void Execute(IServiceCollection applicationBuilder) - { - applicationBuilder.AddSingleton(new UIExtension("VoucherNav", - "store-integrations-nav")); - base.Execute(applicationBuilder); - } - } -} \ No newline at end of file diff --git a/Plugins/BTCPayServer.Plugins.Vouchers/Views/Shared/VoucherNav.cshtml b/Plugins/BTCPayServer.Plugins.Vouchers/Views/Shared/VoucherNav.cshtml deleted file mode 100644 index ba797a3..0000000 --- a/Plugins/BTCPayServer.Plugins.Vouchers/Views/Shared/VoucherNav.cshtml +++ /dev/null @@ -1,34 +0,0 @@ -@using BTCPayServer.Abstractions.Contracts -@using BTCPayServer.Abstractions.Extensions -@using BTCPayServer.Client -@using Microsoft.AspNetCore.Mvc.TagHelpers -@inject IScopeProvider ScopeProvider -@{ - var storeId = ScopeProvider.GetCurrentStoreId(); -} -@if (!string.IsNullOrEmpty(storeId)) -{ - -} diff --git a/Plugins/BTCPayServer.Plugins.Vouchers/Views/Voucher/CreateVoucher.cshtml b/Plugins/BTCPayServer.Plugins.Vouchers/Views/Voucher/CreateVoucher.cshtml deleted file mode 100644 index 64e0ca1..0000000 --- a/Plugins/BTCPayServer.Plugins.Vouchers/Views/Voucher/CreateVoucher.cshtml +++ /dev/null @@ -1,40 +0,0 @@ -@using BTCPayServer.Abstractions.Extensions -@using BTCPayServer.TagHelpers -@using Microsoft.AspNetCore.Mvc.TagHelpers -@using BTCPayServer -@using BTCPayServer.Data -@model List -@{ - ViewData.SetActivePage("Voucher", "Create Voucher", "Voucher"); - -} - -
- -
- - - - - -
-
-
-
- - -
-
- - -
-
-
-
- \ No newline at end of file diff --git a/Plugins/BTCPayServer.Plugins.Vouchers/Views/Voucher/ListVouchers.cshtml b/Plugins/BTCPayServer.Plugins.Vouchers/Views/Voucher/ListVouchers.cshtml deleted file mode 100644 index 5b8360b..0000000 --- a/Plugins/BTCPayServer.Plugins.Vouchers/Views/Voucher/ListVouchers.cshtml +++ /dev/null @@ -1,140 +0,0 @@ -@using BTCPayServer.Abstractions.Extensions -@using BTCPayServer.Abstractions.Contracts -@using BTCPayServer.Abstractions.Models -@using BTCPayServer.Client -@using BTCPayServer.Services -@inject IScopeProvider ScopeProvider -@inject DisplayFormatter DisplayFormatter -@model List -@{ - ViewData.SetActivePage("Voucher", "Voucher", "Voucher"); - - var storeId = ScopeProvider.GetCurrentStoreId(); -} - -
- -
- - - - - - - @if (Model.Any()) - { - @foreach (var pp in Model) - { - - } - -
- - - - - - - - - - - @foreach (var pp in Model) - { - - - - - - - - } - -
NameAmountProgressActions
- - @pp.Name - - @DisplayFormatter.Currency(pp.Amount, pp.Currency)@string.Join(", ", pp.PaymentMethods.Select(id => id.ToPrettyString())) -
-
-
-
-
-
-
- - Archive - - } - - - - Full view - - - - - Print view - -
-
- - - - @section PageFootContent { - - } - } - else - { -

- There are no active vouchers. -

- } - - - \ No newline at end of file diff --git a/Plugins/BTCPayServer.Plugins.Vouchers/Views/Voucher/View.cshtml b/Plugins/BTCPayServer.Plugins.Vouchers/Views/Voucher/View.cshtml deleted file mode 100644 index d1ab20f..0000000 --- a/Plugins/BTCPayServer.Plugins.Vouchers/Views/Voucher/View.cshtml +++ /dev/null @@ -1,87 +0,0 @@ -@model BTCPayServer.Plugins.Vouchers.VoucherController.VoucherViewModel -@using BTCPayServer.Components.QRCode -@using BTCPayServer.Services -@inject BTCPayServerEnvironment Env -@inject DisplayFormatter DisplayFormatter -@{ - Layout = null; - ViewData["Title"] = Model.Name; - - string lnurl = null; - if (Model.SupportsLNURL) - { - lnurl = LNURL.LNURL.EncodeBech32(new Uri(Url.Action("GetLNURLForPullPayment", "UILNURL", new {cryptoCode = "BTC", pullPaymentId = Model.Id}, Context.Request.Scheme, Context.Request.Host.ToString()))); - } - - var fullView = Url.Action("ViewPullPayment", "UIPullPayment", new {pullPaymentId = Model.Id}, Context.Request.Scheme, Context.Request.Host.ToString()); -} - - - - - - - - - - -
-
-
- - -
- -

Voucher

-
- - @if (lnurl != null) - { - -

Scan with a Bitcoin Lightning wallet to redeem your voucher

- } - - - - -

Scan or open this link in your browser for the full option list for redemption

- -
-
-
- -
Amount
-
-
@DisplayFormatter.Currency(Model.Amount, Model.Currency, DisplayFormatter.CurrencyFormat.Symbol)
-
- @if (!string.IsNullOrEmpty(Model.Description)) - { -
- @Model.Description -
- } -
- -
-
-
-
- -
- - - - - \ No newline at end of file diff --git a/Plugins/BTCPayServer.Plugins.Vouchers/VoucherController.cs b/Plugins/BTCPayServer.Plugins.Vouchers/VoucherController.cs deleted file mode 100644 index ba7d89b..0000000 --- a/Plugins/BTCPayServer.Plugins.Vouchers/VoucherController.cs +++ /dev/null @@ -1,218 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading; -using System.Threading.Tasks; -using BTCPayServer.Abstractions.Constants; -using BTCPayServer.Abstractions.Extensions; -using BTCPayServer.Abstractions.Models; -using BTCPayServer.Client; -using BTCPayServer.Client.Models; -using BTCPayServer.Controllers; -using BTCPayServer.Data; -using BTCPayServer.HostedServices; -using BTCPayServer.Models.WalletViewModels; -using BTCPayServer.Payments; -using BTCPayServer.Rating; -using BTCPayServer.Services.Rates; -using BTCPayServer.Services.Stores; -using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Mvc; -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.Caching.Memory; -using NBitcoin; -using NBitcoin.DataEncoders; - -namespace BTCPayServer.Plugins.Vouchers; - - - -public class VoucherController : Controller -{ - private readonly PullPaymentHostedService _pullPaymentHostedService; - private readonly UIStorePullPaymentsController _uiStorePullPaymentsController; - private readonly ApplicationDbContextFactory _dbContextFactory; - private readonly IEnumerable _payoutHandlers; - private readonly StoreRepository _storeRepository; - private readonly RateFetcher _rateFetcher; - private readonly BTCPayNetworkProvider _networkProvider; - - public VoucherController(PullPaymentHostedService pullPaymentHostedService, - UIStorePullPaymentsController uiStorePullPaymentsController, - ApplicationDbContextFactory dbContextFactory, - IEnumerable< IPayoutHandler> payoutHandlers, StoreRepository storeRepository, RateFetcher rateFetcher, BTCPayNetworkProvider networkProvider ) - { - _pullPaymentHostedService = pullPaymentHostedService; - _uiStorePullPaymentsController = uiStorePullPaymentsController; - _dbContextFactory = dbContextFactory; - _payoutHandlers = payoutHandlers; - _storeRepository = storeRepository; - _rateFetcher = rateFetcher; - _networkProvider = networkProvider; - } - - [HttpGet("~/plugins/{storeId}/vouchers")] - [Authorize(Policy = Policies.CanModifyStoreSettings, AuthenticationSchemes = AuthenticationSchemes.Cookie)] - public async Task ListVouchers(string storeId) - { - - var now = DateTimeOffset.UtcNow; - await using var ctx = _dbContextFactory.CreateContext(); - var ppsQuery = await ctx.PullPayments - .Include(data => data.Payouts) - .Where(p => p.StoreId == storeId && p.Archived == false) - .OrderByDescending(data => data.Id).ToListAsync(); - - var vouchers = ppsQuery.Select(pp => (PullPayment: pp, Blob: pp.GetBlob())).Where(blob => blob.Blob.Name.StartsWith("Voucher")).ToList(); - - var paymentMethods = await _payoutHandlers.GetSupportedPaymentMethods(HttpContext.GetStoreData()); - if (!paymentMethods.Any()) - { - TempData.SetStatusMessageModel(new StatusMessageModel - { - Message = "You must enable at least one payment method before creating a voucher.", - Severity = StatusMessageModel.StatusSeverity.Error - }); - return RedirectToAction(nameof(UIStoresController.Dashboard), "UIStores", new {storeId}); - } - return View( vouchers.Select(tuple => new VoucherViewModel() - { - Amount = tuple.Blob.Limit, - Currency = tuple.Blob.Currency, - Id = tuple.PullPayment.Id, - Name = tuple.Blob.Name, - PaymentMethods = tuple.Blob.SupportedPaymentMethods, - Progress = _pullPaymentHostedService.CalculatePullPaymentProgress(tuple.PullPayment, now) - }).ToList()); - } - - [HttpGet("~/plugins/{storeId}/vouchers/create")] - [Authorize(Policy = Policies.CanModifyStoreSettings, AuthenticationSchemes = AuthenticationSchemes.Cookie)] - public async Task CreateVoucher(string storeId) - { - var paymentMethods = await _payoutHandlers.GetSupportedPaymentMethods(HttpContext.GetStoreData()); - if (!paymentMethods.Any()) - { - TempData.SetStatusMessageModel(new StatusMessageModel - { - Message = "You must enable at least one payment method before creating a voucher.", - Severity = StatusMessageModel.StatusSeverity.Error - }); - return RedirectToAction(nameof(UIStoresController.Dashboard), "UIStores", new {storeId}); - } - return View(); - } - - [HttpPost("~/plugins/{storeId}/vouchers/create")] - [Authorize(Policy = Policies.CanModifyStoreSettings, AuthenticationSchemes = AuthenticationSchemes.Cookie)] - public async Task CreateVoucher(string storeId, decimal amount, string currency) - { - ModelState.Clear(); - var paymentMethods = await _payoutHandlers.GetSupportedPaymentMethods(HttpContext.GetStoreData()); - if (!paymentMethods.Any()) - { - - TempData[WellKnownTempData.ErrorMessage] = "You must enable at least one payment method before creating a voucher."; - - return RedirectToAction(nameof(UIStoresController.Dashboard), "UIStores", new {storeId}); - } - - if (amount <= 0) - { - TempData[WellKnownTempData.ErrorMessage] = "Amount must be greater than 0"; - return View(); - } - - var storeBLob = HttpContext.GetStoreData().GetStoreBlob(); - currency??=storeBLob.DefaultCurrency; - - var rate = await _rateFetcher.FetchRate(new CurrencyPair("BTC", currency), - storeBLob.GetRateRules(_networkProvider), CancellationToken.None); - if (rate.BidAsk == null) - { - - TempData[WellKnownTempData.ErrorMessage] = "Currency is not supported"; - return View(); - } - - string description = string.Empty; - if (currency!= "BTC") - { - description = $"{amount} {currency} voucher redeemable for {amount * rate.BidAsk.Bid} BTC"; - } - - var pp = await _pullPaymentHostedService.CreatePullPayment(new CreatePullPayment() - { - Amount = amount* rate.BidAsk.Bid, - Currency = "BTC", - Name = "Voucher " + Encoders.Base58.EncodeData(RandomUtils.GetBytes(6)), - Description = description, - StoreId = storeId, - PaymentMethodIds = paymentMethods.ToArray(), - AutoApproveClaims = true - }); - - return RedirectToAction(nameof(View), new {id = pp}); - } - - [HttpGet("~/plugins/vouchers/{id}")] - [AllowAnonymous] - public async Task View(string id) - { - await using var ctx = _dbContextFactory.CreateContext(); - var pp = await ctx.PullPayments - .Include(data => data.Payouts) - .SingleOrDefaultAsync(p => p.Id == id && p.Archived == false); - - if (pp == null) - { - return NotFound(); - } - - var blob = pp.GetBlob(); - if (!blob.Name.StartsWith("Voucher")) - { - return NotFound(); - } - - var now = DateTimeOffset.UtcNow; - var store = await _storeRepository.FindStore(pp.StoreId); - var storeBlob = store.GetStoreBlob(); - var progress = _pullPaymentHostedService.CalculatePullPaymentProgress(pp, now); - return View(new VoucherViewModel() - { - Amount = blob.Limit, - Currency = blob.Currency, - Id = pp.Id, - Name = blob.Name, - PaymentMethods = blob.SupportedPaymentMethods, - Progress = progress, - StoreName = store.StoreName, - BrandColor = storeBlob.BrandColor, - CssFileId = storeBlob.CssFileId, - LogoFileId = storeBlob.LogoFileId, - SupportsLNURL = _pullPaymentHostedService.SupportsLNURL(blob), - Description = blob.Description - }); - } - - - - - public class VoucherViewModel - { - public string Id { get; set; } - public string Name { get; set; } - public decimal Amount { get; set; } - public string Currency { get; set; } - public PaymentMethodId[] PaymentMethods { get; set; } - public PullPaymentsModel.PullPaymentModel.ProgressModel Progress { get; set; } - public string StoreName { get; set; } - public string LogoFileId { get; set; } - public string BrandColor { get; set; } - public string CssFileId { get; set; } - public bool SupportsLNURL { get; set; } - public string Description { get; set; } - } -} \ No newline at end of file diff --git a/Plugins/BTCPayServer.Plugins.Vouchers/_ViewImports.cshtml b/Plugins/BTCPayServer.Plugins.Vouchers/_ViewImports.cshtml deleted file mode 100644 index d897d63..0000000 --- a/Plugins/BTCPayServer.Plugins.Vouchers/_ViewImports.cshtml +++ /dev/null @@ -1,5 +0,0 @@ -@using BTCPayServer.Abstractions.Services -@inject Safe Safe -@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers -@addTagHelper *, BTCPayServer -@addTagHelper *, BTCPayServer.Abstractions \ No newline at end of file diff --git a/Plugins/BTCPayServer.Plugins.Wabisabi/BTCPayCoinjoinCoinSelector.cs b/Plugins/BTCPayServer.Plugins.Wabisabi/BTCPayCoinjoinCoinSelector.cs index 21ac6fc..87bb076 100644 --- a/Plugins/BTCPayServer.Plugins.Wabisabi/BTCPayCoinjoinCoinSelector.cs +++ b/Plugins/BTCPayServer.Plugins.Wabisabi/BTCPayCoinjoinCoinSelector.cs @@ -89,11 +89,22 @@ public class BTCPayCoinjoinCoinSelector : IRoundCoinSelector maxPerType.TryAdd(AnonsetType.Red, 1); } - var solution = SelectCoinsInternal(utxoSelectionParameters, coinCandidates, payments, + var isLowFee = utxoSelectionParameters.MiningFeeRate.SatoshiPerByte <= _wallet.LowFeeTarget; + var consolidationMode = _wallet.ConsolidationMode switch + { + ConsolidationModeType.Always => true, + ConsolidationModeType.Never => false, + ConsolidationModeType.WhenLowFee => isLowFee, + ConsolidationModeType.WhenLowFeeAndManyUTXO => isLowFee && coinCandidates.Count() > BTCPayWallet.HighAmountOfCoins, + _ => throw new ArgumentOutOfRangeException() + }; + + + var solution = await SelectCoinsInternal(utxoSelectionParameters, coinCandidates, payments, Random.Shared.Next(10, 31), maxPerType, new Dictionary() {{AnonsetType.Red, 1}, {AnonsetType.Orange, 1}, {AnonsetType.Green, 1}}, - _wallet.ConsolidationMode, liquidityClue, secureRandom); + consolidationMode, liquidityClue, secureRandom); if (attemptingTobeParanoid && !solution.HandledPayments.Any()) { @@ -112,7 +123,7 @@ public class BTCPayCoinjoinCoinSelector : IRoundCoinSelector return solution.Coins.ToImmutableList(); } - private SubsetSolution SelectCoinsInternal(UtxoSelectionParameters utxoSelectionParameters, + private async Task SelectCoinsInternal(UtxoSelectionParameters utxoSelectionParameters, IEnumerable coins, IEnumerable pendingPayments, int maxCoins, Dictionary maxPerType, Dictionary idealMinimumPerType, @@ -128,11 +139,11 @@ public class BTCPayCoinjoinCoinSelector : IRoundCoinSelector var remainingPendingPayments = new List(pendingPayments); var solution = new SubsetSolution(remainingPendingPayments.Count, _wallet.AnonScoreTarget, utxoSelectionParameters); - var fullyPrivate = remainingCoins.All(coin => coin.CoinColor(_wallet.AnonScoreTarget) == AnonsetType.Green); + + + var fullyPrivate = await _wallet.IsWalletPrivateAsync(new CoinsView(remainingCoins)); var coinjoiningOnlyForPayments = fullyPrivate && remainingPendingPayments.Any(); - var isMixingToOther = !_wallet.WabisabiStoreSettings.PlebMode && - !string.IsNullOrEmpty(_wallet.WabisabiStoreSettings.MixToOtherWallet); - if (fullyPrivate && !coinjoiningOnlyForPayments && !isMixingToOther) + if (fullyPrivate && !coinjoiningOnlyForPayments ) { var rand = Random.Shared.Next(1, 1001); if (rand > _wallet.WabisabiStoreSettings.ExtraJoinProbability) diff --git a/Plugins/BTCPayServer.Plugins.Wabisabi/BTCPayWallet.cs b/Plugins/BTCPayServer.Plugins.Wabisabi/BTCPayWallet.cs index b7e299a..6ad2ed4 100644 --- a/Plugins/BTCPayServer.Plugins.Wabisabi/BTCPayWallet.cs +++ b/Plugins/BTCPayServer.Plugins.Wabisabi/BTCPayWallet.cs @@ -1,12 +1,9 @@ using System; -using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Threading; using System.Threading.Tasks; -using BTCPayServer.Abstractions.Contracts; -using BTCPayServer.Client; using BTCPayServer.Client.Models; using BTCPayServer.Data; using BTCPayServer.HostedServices; @@ -31,9 +28,11 @@ using WalletWasabi.Blockchain.TransactionOutputs; using WalletWasabi.Blockchain.Transactions; using WalletWasabi.Extensions; using WalletWasabi.Models; +using WalletWasabi.WabiSabi; using WalletWasabi.WabiSabi.Backend.Rounds; using WalletWasabi.WabiSabi.Client; using WalletWasabi.Wallets; +using LogLevel = WalletWasabi.Logging.LogLevel; namespace BTCPayServer.Plugins.Wabisabi; @@ -91,13 +90,34 @@ public class BTCPayWallet : IWallet, IDestinationProvider } public string StoreId { get; set; } + public List<(Microsoft.Extensions.Logging.LogLevel, string)> LastLogs { get; private set; } = new(); + public void Log(LogLevel logLevel, string logMessage, string callerFilePath = "", string callerMemberName = "", + int callerLineNumber = -1) + { + var ll = logLevel switch + { + LogLevel.Trace => Microsoft.Extensions.Logging.LogLevel.Trace, + LogLevel.Debug => Microsoft.Extensions.Logging.LogLevel.Debug, + LogLevel.Info => Microsoft.Extensions.Logging.LogLevel.Information, + LogLevel.Warning => Microsoft.Extensions.Logging.LogLevel.Warning, + LogLevel.Error => Microsoft.Extensions.Logging.LogLevel.Error, + LogLevel.Critical => Microsoft.Extensions.Logging.LogLevel.Critical, + _ => throw new ArgumentOutOfRangeException(nameof(logLevel)) + }; + if(LastLogs.FirstOrDefault().Item2 != logMessage) + LastLogs.Insert(0, (ll, logMessage) ); + if (LastLogs.Count >= 100) + LastLogs.RemoveLast(); + + Logger.Log(ll, logMessage, callerFilePath, callerMemberName, callerLineNumber); + } public string WalletName => StoreId; public bool IsUnderPlebStop => !WabisabiStoreSettings.Active; bool IWallet.IsMixable(string coordinator) { - return KeyChain is BTCPayKeyChain {KeysAvailable: true} && WabisabiStoreSettings.Settings.SingleOrDefault( + return WabisabiStoreSettings.Active && KeyChain is BTCPayKeyChain {KeysAvailable: true} && WabisabiStoreSettings.Settings.SingleOrDefault( settings => settings.Coordinator.Equals(coordinator))?.Enabled is true; } @@ -106,20 +126,34 @@ public class BTCPayWallet : IWallet, IDestinationProvider public IDestinationProvider DestinationProvider => this; public int AnonScoreTarget => WabisabiStoreSettings.PlebMode? 5: WabisabiStoreSettings.AnonymitySetTarget; - public bool ConsolidationMode => !WabisabiStoreSettings.PlebMode && WabisabiStoreSettings.ConsolidationMode; + public ConsolidationModeType ConsolidationMode => + WabisabiStoreSettings.PlebMode? ConsolidationModeType.WhenLowFeeAndManyUTXO: WabisabiStoreSettings.ConsolidationMode; public TimeSpan FeeRateMedianTimeFrame => TimeSpan.FromHours(WabisabiStoreSettings.PlebMode? KeyManager.DefaultFeeRateMedianTimeFrameHours: WabisabiStoreSettings.FeeRateMedianTimeFrameHours); + + public const int DefaultExplicitHighestFeeTarget = 75; + public const int DefaultLowFeeTarget = 10; + public const int HighAmountOfCoins = 30; + public int ExplicitHighestFeeTarget => WabisabiStoreSettings.PlebMode? DefaultExplicitHighestFeeTarget: WabisabiStoreSettings.ExplicitHighestFeeTarget; + public int LowFeeTarget => WabisabiStoreSettings.PlebMode? DefaultLowFeeTarget: WabisabiStoreSettings.LowFeeTarget; public bool RedCoinIsolation => !WabisabiStoreSettings.PlebMode &&WabisabiStoreSettings.RedCoinIsolation; public bool BatchPayments => WabisabiStoreSettings.PlebMode || WabisabiStoreSettings.BatchPayments; public long? MinimumDenominationAmount => WabisabiStoreSettings.PlebMode? 10000 : WabisabiStoreSettings.MinimumDenominationAmount; public async Task IsWalletPrivateAsync() { - return !BatchPayments && await GetPrivacyPercentageAsync() >= 1 && (WabisabiStoreSettings.PlebMode || - string.IsNullOrEmpty(WabisabiStoreSettings - .MixToOtherWallet)); + return await IsWalletPrivateAsync(await GetAllCoins()); } - + + public async Task IsWalletPrivateAsync(CoinsView coins) + { + var privacy= GetPrivacyPercentage(coins, AnonScoreTarget); + var mixToOtherWallet = !WabisabiStoreSettings.PlebMode && !string.IsNullOrEmpty(WabisabiStoreSettings + .MixToOtherWallet); + var forceConsolidate = ConsolidationMode == ConsolidationModeType.WhenLowFeeAndManyUTXO && coins.Available().Confirmed().Count() > HighAmountOfCoins; + return !BatchPayments && privacy >= 1 && !mixToOtherWallet && !forceConsolidate; + } + public async Task GetPrivacyPercentageAsync() { return GetPrivacyPercentage(await GetAllCoins(), AnonScoreTarget); @@ -270,7 +304,7 @@ public class BTCPayWallet : IWallet, IDestinationProvider } catch (Exception e) { - Logger.LogError(e, "Could not compute coin candidate"); + this.LogError($"Could not compute coin candidate: {e.Message}"); return Array.Empty(); } } @@ -356,10 +390,8 @@ public class BTCPayWallet : IWallet, IDestinationProvider public async Task> GetTransactionsAsync() { return Array.Empty(); - } - public class CoinjoinData { public class CoinjoinDataCoin diff --git a/Plugins/BTCPayServer.Plugins.Wabisabi/Coordinator/CoordinatorExtensions.cs b/Plugins/BTCPayServer.Plugins.Wabisabi/Coordinator/CoordinatorExtensions.cs index 08f7c91..30b848e 100644 --- a/Plugins/BTCPayServer.Plugins.Wabisabi/Coordinator/CoordinatorExtensions.cs +++ b/Plugins/BTCPayServer.Plugins.Wabisabi/Coordinator/CoordinatorExtensions.cs @@ -1,9 +1,10 @@ using BTCPayServer.Abstractions.Contracts; using BTCPayServer.Abstractions.Services; using BTCPayServer.Configuration; +using BTCPayServer.Plugins.Wabisabi.Coordinator; using BTCPayServer.Services; using Microsoft.Extensions.DependencyInjection; -using WalletWasabi.Affiliation; +using WalletWasabi.WabiSabi.Backend; using WalletWasabi.WabiSabi.Models.Serialization; namespace WalletWasabi.Backend.Controllers; @@ -14,6 +15,7 @@ public static class CoordinatorExtensions { services.AddSingleton(); + services.AddSingleton(); services.AddTransient(provider => { var s = provider.GetRequiredService(); diff --git a/Plugins/BTCPayServer.Plugins.Wabisabi/Coordinator/WabisabiCoordinatorService.cs b/Plugins/BTCPayServer.Plugins.Wabisabi/Coordinator/WabisabiCoordinatorService.cs index f5cc3d6..3ba2bb7 100644 --- a/Plugins/BTCPayServer.Plugins.Wabisabi/Coordinator/WabisabiCoordinatorService.cs +++ b/Plugins/BTCPayServer.Plugins.Wabisabi/Coordinator/WabisabiCoordinatorService.cs @@ -27,6 +27,7 @@ using WalletWasabi.BitcoinCore.Rpc; using WalletWasabi.Cache; using WalletWasabi.Services; using WalletWasabi.WabiSabi; +using WalletWasabi.WabiSabi.Backend; using WalletWasabi.WabiSabi.Backend.Rounds.CoinJoinStorage; using WalletWasabi.WabiSabi.Backend.Statistics; @@ -53,7 +54,7 @@ public class WabisabiCoordinatorService : PeriodicRunner WabisabiCoordinatorClientInstanceManager instanceManager, IHttpClientFactory httpClientFactory, IServiceProvider serviceProvider, - ILogger logger ) : base(TimeSpan.FromMinutes(15)) + ILogger logger, WabiSabiConfig.CoordinatorScriptResolver coordinatorScriptResolver) : base(TimeSpan.FromMinutes(15)) { _settingsRepository = settingsRepository; _dataDirectories = dataDirectories; @@ -62,6 +63,7 @@ public class WabisabiCoordinatorService : PeriodicRunner _instanceManager = instanceManager; _httpClientFactory = httpClientFactory; _logger = logger; + _coordinatorScriptResolver = coordinatorScriptResolver; _socks5HttpClientHandler = serviceProvider.GetRequiredService(); IdempotencyRequestCache = new(memoryCache); } @@ -69,6 +71,7 @@ public class WabisabiCoordinatorService : PeriodicRunner private WabisabiCoordinatorSettings cachedSettings; private readonly Socks5HttpClientHandler _socks5HttpClientHandler; + private readonly WabiSabiConfig.CoordinatorScriptResolver _coordinatorScriptResolver; public async Task GetSettings() { @@ -211,7 +214,7 @@ public class WabisabiCoordinatorService : PeriodicRunner var rpc = new BtcPayRpcClient(explorerClient.RPCClient, _memoryCache, explorerClient); WabiSabiCoordinator = new WabiSabiCoordinator(coordinatorParameters, rpc, coinJoinIdStore, coinJoinScriptStore, - _httpClientFactory); + _httpClientFactory, _coordinatorScriptResolver); HostedServices.Register(() => WabiSabiCoordinator, "WabiSabi Coordinator"); var settings = await GetSettings(); diff --git a/Plugins/BTCPayServer.Plugins.Wabisabi/Coordinator/WabisabiScriptResolver.cs b/Plugins/BTCPayServer.Plugins.Wabisabi/Coordinator/WabisabiScriptResolver.cs new file mode 100644 index 0000000..0e4c68c --- /dev/null +++ b/Plugins/BTCPayServer.Plugins.Wabisabi/Coordinator/WabisabiScriptResolver.cs @@ -0,0 +1,119 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net.Http; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using BTCPayServer.Services.Stores; +using BTCPayServer.Services.Wallets; +using NBitcoin; +using Newtonsoft.Json.Linq; +using WalletWasabi.WabiSabi.Backend; + +namespace BTCPayServer.Plugins.Wabisabi.Coordinator; + +public class WabisabiScriptResolver: WabiSabiConfig.CoordinatorScriptResolver +{ + private readonly IHttpClientFactory _httpClientFactory; + private readonly StoreRepository _storeRepository; + private readonly BTCPayNetworkProvider _networkProvider; + private readonly BTCPayWalletProvider _walletProvider; + + public WabisabiScriptResolver(IHttpClientFactory httpClientFactory, + StoreRepository storeRepository, + BTCPayNetworkProvider networkProvider, + BTCPayWalletProvider walletProvider) + { + _httpClientFactory = httpClientFactory; + _storeRepository = storeRepository; + _networkProvider = networkProvider; + _walletProvider = walletProvider; + } + + private static async Task GetRedirectedUrl(HttpClient client, string url, + CancellationToken cancellationToken) + { + var redirectedUrl = url; + using var response = await client.PostAsync(url, new FormUrlEncodedContent(Array.Empty>()), cancellationToken).ConfigureAwait(false); + using var content = response.Content; + // ... Read the response to see if we have the redirected url + if (response.StatusCode == System.Net.HttpStatusCode.Found) + { + var headers = response.Headers; + if (headers.Location != null) + { + redirectedUrl = new Uri(new Uri(url), headers.Location.ToString()).ToString(); + } + } + + return redirectedUrl; + } + + public override async Task