mirror of
https://github.com/aljazceru/BTCPayServerPlugins.git
synced 2025-12-17 07:34:24 +01:00
Improve everything
This commit is contained in:
@@ -71,17 +71,43 @@ public class BTCPayCoinjoinCoinSelector : IRoundCoinSelector
|
|||||||
_wallet.BatchPayments
|
_wallet.BatchPayments
|
||||||
? await _wallet.DestinationProvider.GetPendingPaymentsAsync(utxoSelectionParameters)
|
? await _wallet.DestinationProvider.GetPendingPaymentsAsync(utxoSelectionParameters)
|
||||||
: Array.Empty<PendingPayment>();
|
: Array.Empty<PendingPayment>();
|
||||||
var minCoins = new Dictionary<AnonsetType, int>();
|
|
||||||
|
var maxPerType = new Dictionary<AnonsetType, int>();
|
||||||
|
|
||||||
|
var attemptingTobeParanoid = payments.Any() && _wallet.WabisabiStoreSettings.ParanoidPayments;
|
||||||
|
var attemptingToMixToOtherWallet = string.IsNullOrEmpty(_wallet.WabisabiStoreSettings.MixToOtherWallet);
|
||||||
|
selectCoins:
|
||||||
|
maxPerType.Clear();
|
||||||
|
if (attemptingTobeParanoid || attemptingToMixToOtherWallet)
|
||||||
|
{
|
||||||
|
maxPerType.Add(AnonsetType.Red,0);
|
||||||
|
maxPerType.Add(AnonsetType.Orange,0);
|
||||||
|
}
|
||||||
|
|
||||||
if (_wallet.RedCoinIsolation)
|
if (_wallet.RedCoinIsolation)
|
||||||
{
|
{
|
||||||
minCoins.Add(AnonsetType.Red, 1);
|
maxPerType.TryAdd(AnonsetType.Red, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
var solution = SelectCoinsInternal(utxoSelectionParameters, coinCandidates, payments,
|
var solution = SelectCoinsInternal(utxoSelectionParameters, coinCandidates, payments,
|
||||||
Random.Shared.Next(10, 31),
|
Random.Shared.Next(10, 31),
|
||||||
minCoins,
|
maxPerType,
|
||||||
new Dictionary<AnonsetType, int>() {{AnonsetType.Red, 1}, {AnonsetType.Orange, 1}, {AnonsetType.Green, 1}},
|
new Dictionary<AnonsetType, int>() {{AnonsetType.Red, 1}, {AnonsetType.Orange, 1}, {AnonsetType.Green, 1}},
|
||||||
_wallet.ConsolidationMode, liquidityClue, secureRandom);
|
_wallet.ConsolidationMode, liquidityClue, secureRandom);
|
||||||
|
|
||||||
|
if (attemptingTobeParanoid && !solution.HandledPayments.Any())
|
||||||
|
{
|
||||||
|
attemptingTobeParanoid = false;
|
||||||
|
payments = Array.Empty<PendingPayment>();
|
||||||
|
goto selectCoins;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (attemptingToMixToOtherWallet && !solution.Coins.Any())
|
||||||
|
{
|
||||||
|
// check that we have enough coins to mix to other wallet
|
||||||
|
attemptingToMixToOtherWallet = false;
|
||||||
|
goto selectCoins;
|
||||||
|
}
|
||||||
_logger.LogTrace(solution.ToString());
|
_logger.LogTrace(solution.ToString());
|
||||||
return solution.Coins.ToImmutableList();
|
return solution.Coins.ToImmutableList();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,7 +13,7 @@
|
|||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<Product>Wabisabi Coinjoin</Product>
|
<Product>Wabisabi Coinjoin</Product>
|
||||||
<Description>Allows you to integrate your btcpayserver store with coinjoins.</Description>
|
<Description>Allows you to integrate your btcpayserver store with coinjoins.</Description>
|
||||||
<Version>1.0.63</Version>
|
<Version>1.0.64</Version>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<!-- Plugin development properties -->
|
<!-- Plugin development properties -->
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ using BTCPayServer.Payments.PayJoin;
|
|||||||
using BTCPayServer.Services;
|
using BTCPayServer.Services;
|
||||||
using BTCPayServer.Services.Stores;
|
using BTCPayServer.Services.Stores;
|
||||||
using BTCPayServer.Services.Wallets;
|
using BTCPayServer.Services.Wallets;
|
||||||
|
using LinqKit;
|
||||||
using Microsoft.Extensions.Caching.Memory;
|
using Microsoft.Extensions.Caching.Memory;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using NBitcoin;
|
using NBitcoin;
|
||||||
@@ -92,7 +93,7 @@ public class BTCPayWallet : IWallet, IDestinationProvider
|
|||||||
public string StoreId { get; set; }
|
public string StoreId { get; set; }
|
||||||
|
|
||||||
public string WalletName => StoreId;
|
public string WalletName => StoreId;
|
||||||
public bool IsUnderPlebStop => false;
|
public bool IsUnderPlebStop => !WabisabiStoreSettings.Active;
|
||||||
|
|
||||||
bool IWallet.IsMixable(string coordinator)
|
bool IWallet.IsMixable(string coordinator)
|
||||||
{
|
{
|
||||||
@@ -114,7 +115,9 @@ public class BTCPayWallet : IWallet, IDestinationProvider
|
|||||||
|
|
||||||
public async Task<bool> IsWalletPrivateAsync()
|
public async Task<bool> IsWalletPrivateAsync()
|
||||||
{
|
{
|
||||||
return !BatchPayments && await GetPrivacyPercentageAsync()>= 1;
|
return !BatchPayments && await GetPrivacyPercentageAsync() >= 1 && (WabisabiStoreSettings.PlebMode ||
|
||||||
|
string.IsNullOrEmpty(WabisabiStoreSettings
|
||||||
|
.MixToOtherWallet));
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<double> GetPrivacyPercentageAsync()
|
public async Task<double> GetPrivacyPercentageAsync()
|
||||||
@@ -127,11 +130,18 @@ public class BTCPayWallet : IWallet, IDestinationProvider
|
|||||||
await _savingProgress;
|
await _savingProgress;
|
||||||
|
|
||||||
var utxos = await _btcPayWallet.GetUnspentCoins(DerivationScheme);
|
var utxos = await _btcPayWallet.GetUnspentCoins(DerivationScheme);
|
||||||
var utxoLabels = await GetUtxoLabels(_walletRepository, StoreId,utxos);
|
var utxoLabels = await GetUtxoLabels(_memoryCache ,_walletRepository, StoreId,utxos, false);
|
||||||
await _smartifier.LoadCoins(utxos.ToList(), 1, utxoLabels);
|
await _smartifier.LoadCoins(utxos.ToList(), 1, utxoLabels);
|
||||||
var coins = await Task.WhenAll(_smartifier.Coins.Where(pair => utxos.Any(data => data.OutPoint == pair.Key))
|
var coins = await Task.WhenAll(_smartifier.Coins.Where(pair => utxos.Any(data => data.OutPoint == pair.Key))
|
||||||
.Select(pair => pair.Value));
|
.Select(pair => pair.Value));
|
||||||
|
|
||||||
|
|
||||||
|
foreach (var c in coins)
|
||||||
|
{
|
||||||
|
var utxo = utxos.Single(coin => coin.OutPoint == c.Outpoint);
|
||||||
|
c.Height = utxo.Confirmations > 0 ? new Height((uint) utxo.Confirmations) : Height.Mempool;
|
||||||
|
}
|
||||||
|
|
||||||
return new CoinsView(coins);
|
return new CoinsView(coins);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -164,6 +174,25 @@ public class BTCPayWallet : IWallet, IDestinationProvider
|
|||||||
var coordSettings = WabisabiStoreSettings.Settings.Find(settings => settings.Coordinator == coordinatorName && settings.Enabled);
|
var coordSettings = WabisabiStoreSettings.Settings.Find(settings => settings.Coordinator == coordinatorName && settings.Enabled);
|
||||||
return coordSettings is not null && IsRoundOk(roundParameters, coordSettings);
|
return coordSettings is not null && IsRoundOk(roundParameters, coordSettings);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task CompletedCoinjoin(CoinJoinTracker finishedCoinJoin)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
|
||||||
|
var successfulCoinJoinResult = (await finishedCoinJoin.CoinJoinTask) as SuccessfulCoinJoinResult;
|
||||||
|
|
||||||
|
await RegisterCoinjoinTransaction(successfulCoinJoinResult,
|
||||||
|
finishedCoinJoin.CoinJoinClient.CoordinatorName);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
public static bool IsRoundOk(RoundParameters roundParameters, WabisabiStoreCoordinatorSettings coordSettings)
|
public static bool IsRoundOk(RoundParameters roundParameters, WabisabiStoreCoordinatorSettings coordSettings)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
@@ -183,20 +212,15 @@ public class BTCPayWallet : IWallet, IDestinationProvider
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
|
||||||
await _savingProgress;
|
await _savingProgress;
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (IsUnderPlebStop)
|
if (IsUnderPlebStop)
|
||||||
{
|
{
|
||||||
return Array.Empty<SmartCoin>();
|
return Array.Empty<SmartCoin>();
|
||||||
}
|
}
|
||||||
|
|
||||||
var utxos = await _btcPayWallet.GetUnspentCoins(DerivationScheme, true, CancellationToken.None);
|
var utxos = await _btcPayWallet.GetUnspentCoins(DerivationScheme, true, CancellationToken.None);
|
||||||
var utxoLabels = await GetUtxoLabels(_walletRepository, StoreId,utxos);
|
var utxoLabels = await GetUtxoLabels(_memoryCache, _walletRepository, StoreId,utxos, false);
|
||||||
if (!WabisabiStoreSettings.PlebMode)
|
if (!WabisabiStoreSettings.PlebMode)
|
||||||
{
|
{
|
||||||
if (WabisabiStoreSettings.InputLabelsAllowed?.Any() is true)
|
if (WabisabiStoreSettings.InputLabelsAllowed?.Any() is true)
|
||||||
@@ -239,7 +263,7 @@ public class BTCPayWallet : IWallet, IDestinationProvider
|
|||||||
foreach (SmartCoin c in resultX)
|
foreach (SmartCoin c in resultX)
|
||||||
{
|
{
|
||||||
var utxo = utxos.Single(coin => coin.OutPoint == c.Outpoint);
|
var utxo = utxos.Single(coin => coin.OutPoint == c.Outpoint);
|
||||||
c.Height = new Height((uint) utxo.Confirmations);
|
c.Height = utxo.Confirmations > 0 ? new Height((uint) utxo.Confirmations) : Height.Mempool;
|
||||||
}
|
}
|
||||||
|
|
||||||
return resultX;
|
return resultX;
|
||||||
@@ -251,12 +275,30 @@ public class BTCPayWallet : IWallet, IDestinationProvider
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async Task<Dictionary<OutPoint, (HashSet<string> labels, double anonset, CoinjoinData coinjoinData)>> GetUtxoLabels(WalletRepository walletRepository, string storeId ,ReceivedCoin[] utxos)
|
|
||||||
{
|
|
||||||
var walletTransactionsInfoAsync = await walletRepository.GetWalletTransactionsInfo(new WalletId(storeId, "BTC"),
|
|
||||||
utxos.SelectMany(GetWalletObjectsQuery.Get).Distinct().ToArray());
|
|
||||||
|
|
||||||
var utxoLabels = utxos.Select(coin =>
|
|
||||||
|
public static async Task<Dictionary<OutPoint, (HashSet<string> labels, double anonset, CoinjoinData coinjoinData)>> GetUtxoLabels(IMemoryCache memoryCache, WalletRepository walletRepository, string storeId ,ReceivedCoin[] utxos, bool isDepth)
|
||||||
|
{
|
||||||
|
var utxoToQuery = utxos.ToArray();
|
||||||
|
var cacheResult = new Dictionary<OutPoint, (HashSet<string> labels, double anonset, CoinjoinData coinjoinData)>();
|
||||||
|
foreach (var utxo in utxoToQuery)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (memoryCache.TryGetValue<(HashSet<string> labels, double anonset, CoinjoinData coinjoinData)>(
|
||||||
|
$"wabisabi_{utxo.OutPoint}_utxo", out var cacheVariant ) )
|
||||||
|
{
|
||||||
|
if (!cacheResult.TryAdd(utxo.OutPoint, cacheVariant))
|
||||||
|
{
|
||||||
|
//wtf!
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
utxoToQuery = utxoToQuery.Where(utxo => !cacheResult.ContainsKey(utxo.OutPoint)).ToArray();
|
||||||
|
var walletTransactionsInfoAsync = await walletRepository.GetWalletTransactionsInfo(new WalletId(storeId, "BTC"),
|
||||||
|
utxoToQuery.SelectMany(GetWalletObjectsQuery.Get).Distinct().ToArray());
|
||||||
|
|
||||||
|
var utxoLabels = utxoToQuery.Select(coin =>
|
||||||
{
|
{
|
||||||
walletTransactionsInfoAsync.TryGetValue(coin.OutPoint.Hash.ToString(), out var info1);
|
walletTransactionsInfoAsync.TryGetValue(coin.OutPoint.Hash.ToString(), out var info1);
|
||||||
walletTransactionsInfoAsync.TryGetValue(coin.Address.ToString(), out var info2);
|
walletTransactionsInfoAsync.TryGetValue(coin.Address.ToString(), out var info2);
|
||||||
@@ -268,25 +310,23 @@ public class BTCPayWallet : IWallet, IDestinationProvider
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (coin.OutPoint, info);
|
return (coin.OutPoint, info);
|
||||||
}).Where(tuple => tuple.info is not null)
|
}).Where(tuple => tuple.info is not null).DistinctBy(tuple => tuple.OutPoint)
|
||||||
.ToDictionary(tuple => tuple.OutPoint, tuple => tuple.info);
|
.ToDictionary(tuple => tuple.OutPoint, pair =>
|
||||||
return utxoLabels.ToDictionary(pair => pair.Key, pair =>
|
|
||||||
{
|
{
|
||||||
|
|
||||||
var labels = new HashSet<string>();
|
var labels = new HashSet<string>();
|
||||||
if (pair.Value.LabelColors.Any())
|
if (pair.info.LabelColors.Any())
|
||||||
{
|
{
|
||||||
labels.AddRange((pair.Value.LabelColors.Select(pair => pair.Key)));
|
labels.AddRange((pair.info.LabelColors.Select(pair => pair.Key)));
|
||||||
}
|
}
|
||||||
if (pair.Value.Attachments.Any() is true)
|
if (pair.info.Attachments.Any() is true)
|
||||||
{
|
{
|
||||||
labels.AddRange((pair.Value.Attachments.Select(attachment => attachment.Id)));
|
labels.AddRange((pair.info.Attachments.Select(attachment => attachment.Id)));
|
||||||
}
|
}
|
||||||
var cjData = pair.Value.Attachments
|
var cjData = pair.info.Attachments
|
||||||
.FirstOrDefault(attachment => attachment.Type == "coinjoin")?.Data
|
.FirstOrDefault(attachment => attachment.Type == "coinjoin")?.Data
|
||||||
?.ToObject<CoinjoinData>();
|
?.ToObject<CoinjoinData>();
|
||||||
|
|
||||||
var explicitAnonset = pair.Value.Attachments.FirstOrDefault(attachment => attachment.Type == "anonset")
|
var explicitAnonset = pair.info.Attachments.FirstOrDefault(attachment => attachment.Type == "anonset")
|
||||||
?.Id;
|
?.Id;
|
||||||
double anonset = 1;
|
double anonset = 1;
|
||||||
if (!string.IsNullOrEmpty(explicitAnonset))
|
if (!string.IsNullOrEmpty(explicitAnonset))
|
||||||
@@ -294,7 +334,7 @@ public class BTCPayWallet : IWallet, IDestinationProvider
|
|||||||
anonset = double.Parse(explicitAnonset);
|
anonset = double.Parse(explicitAnonset);
|
||||||
}else if (cjData is not null)
|
}else if (cjData is not null)
|
||||||
{
|
{
|
||||||
var utxo = cjData.CoinsOut.FirstOrDefault(dataCoin => dataCoin.Outpoint == pair.Key.ToString());
|
var utxo = cjData.CoinsOut.FirstOrDefault(dataCoin => dataCoin.Outpoint == pair.OutPoint.ToString());
|
||||||
if (utxo is not null)
|
if (utxo is not null)
|
||||||
{
|
{
|
||||||
anonset = utxo.AnonymitySet;
|
anonset = utxo.AnonymitySet;
|
||||||
@@ -305,6 +345,11 @@ public class BTCPayWallet : IWallet, IDestinationProvider
|
|||||||
return (labels, anonset, cjData);
|
return (labels, anonset, cjData);
|
||||||
|
|
||||||
});
|
});
|
||||||
|
foreach (var pair in utxoLabels)
|
||||||
|
{
|
||||||
|
memoryCache.Set($"wabisabi_{pair.Key.Hash}_utxo", pair.Value, isDepth? TimeSpan.FromMinutes(10): TimeSpan.FromMinutes(5));
|
||||||
|
}
|
||||||
|
return utxoLabels.Concat(cacheResult).ToDictionary(pair => pair.Key, pair => pair.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -389,7 +434,7 @@ public class BTCPayWallet : IWallet, IDestinationProvider
|
|||||||
{
|
{
|
||||||
coin.HdPubKey.SetKeyState(KeyState.Used);
|
coin.HdPubKey.SetKeyState(KeyState.Used);
|
||||||
coin.SpenderTransaction = smartTx;
|
coin.SpenderTransaction = smartTx;
|
||||||
smartTx.TryAddWalletInput(coin);
|
smartTx.TryAddWalletInput(SmartCoin.Clone(coin));
|
||||||
});
|
});
|
||||||
result.Outputs.ForEach(s =>
|
result.Outputs.ForEach(s =>
|
||||||
{
|
{
|
||||||
@@ -422,7 +467,7 @@ public class BTCPayWallet : IWallet, IDestinationProvider
|
|||||||
Round = result.RoundId.ToString(),
|
Round = result.RoundId.ToString(),
|
||||||
CoordinatorName = coordinatorName,
|
CoordinatorName = coordinatorName,
|
||||||
Transaction = txHash.ToString(),
|
Transaction = txHash.ToString(),
|
||||||
CoinsIn = smartTx.WalletInputs.Select(coin => new CoinjoinData.CoinjoinDataCoin()
|
CoinsIn = result.Coins.Select(coin => new CoinjoinData.CoinjoinDataCoin()
|
||||||
{
|
{
|
||||||
AnonymitySet = coin.AnonymitySet,
|
AnonymitySet = coin.AnonymitySet,
|
||||||
PayoutId = null,
|
PayoutId = null,
|
||||||
@@ -503,6 +548,12 @@ public class BTCPayWallet : IWallet, IDestinationProvider
|
|||||||
|
|
||||||
}
|
}
|
||||||
_smartifier.SmartTransactions.AddOrReplace(txHash, Task.FromResult(smartTx));
|
_smartifier.SmartTransactions.AddOrReplace(txHash, Task.FromResult(smartTx));
|
||||||
|
smartTx.WalletOutputs.ForEach(coin =>
|
||||||
|
{
|
||||||
|
|
||||||
|
_smartifier.Coins.AddOrReplace(coin.Outpoint, Task.FromResult(coin));
|
||||||
|
});
|
||||||
|
|
||||||
//
|
//
|
||||||
// var kp = await ExplorerClient.GetMetadataAsync<RootedKeyPath>(DerivationScheme,
|
// var kp = await ExplorerClient.GetMetadataAsync<RootedKeyPath>(DerivationScheme,
|
||||||
// WellknownMetadataKeys.AccountKeyPath);
|
// WellknownMetadataKeys.AccountKeyPath);
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ public static class CoordinatorExtensions
|
|||||||
services.AddTransient(provider =>
|
services.AddTransient(provider =>
|
||||||
{
|
{
|
||||||
var s = provider.GetRequiredService<WabisabiCoordinatorService>();
|
var s = provider.GetRequiredService<WabisabiCoordinatorService>();
|
||||||
if (!s.Started)
|
if (!s.Started )
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,11 +3,14 @@ using System.Collections.Concurrent;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using WalletWasabi.Blockchain.TransactionOutputs;
|
||||||
|
|
||||||
namespace BTCPayServer.Plugins.Wabisabi;
|
namespace BTCPayServer.Plugins.Wabisabi;
|
||||||
|
|
||||||
public static class Extensions
|
public static class Extensions
|
||||||
{
|
{
|
||||||
|
|
||||||
public static string ToSentenceCase(this string str)
|
public static string ToSentenceCase(this string str)
|
||||||
{
|
{
|
||||||
return Regex.Replace(str, "[a-z][A-Z]", m => m.Value[0] + " " + char.ToLower(m.Value[1]));
|
return Regex.Replace(str, "[a-z][A-Z]", m => m.Value[0] + " " + char.ToLower(m.Value[1]));
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ using BTCPayServer.Abstractions.Contracts;
|
|||||||
using BTCPayServer.Payments.PayJoin;
|
using BTCPayServer.Payments.PayJoin;
|
||||||
using BTCPayServer.Services;
|
using BTCPayServer.Services;
|
||||||
using BTCPayServer.Services.Wallets;
|
using BTCPayServer.Services.Wallets;
|
||||||
|
using Microsoft.Extensions.Caching.Memory;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using NBitcoin;
|
using NBitcoin;
|
||||||
using NBXplorer;
|
using NBXplorer;
|
||||||
@@ -24,6 +25,7 @@ namespace BTCPayServer.Plugins.Wabisabi;
|
|||||||
|
|
||||||
public class Smartifier
|
public class Smartifier
|
||||||
{
|
{
|
||||||
|
private readonly IMemoryCache _memoryCache;
|
||||||
private readonly ILogger _logger;
|
private readonly ILogger _logger;
|
||||||
private readonly WalletRepository _walletRepository;
|
private readonly WalletRepository _walletRepository;
|
||||||
private readonly ExplorerClient _explorerClient;
|
private readonly ExplorerClient _explorerClient;
|
||||||
@@ -32,11 +34,13 @@ public class Smartifier
|
|||||||
private readonly IUTXOLocker _utxoLocker;
|
private readonly IUTXOLocker _utxoLocker;
|
||||||
|
|
||||||
public Smartifier(
|
public Smartifier(
|
||||||
|
IMemoryCache memoryCache,
|
||||||
ILogger logger,
|
ILogger logger,
|
||||||
WalletRepository walletRepository,
|
WalletRepository walletRepository,
|
||||||
ExplorerClient explorerClient, DerivationStrategyBase derivationStrategyBase, string storeId,
|
ExplorerClient explorerClient, DerivationStrategyBase derivationStrategyBase, string storeId,
|
||||||
IUTXOLocker utxoLocker, RootedKeyPath accountKeyPath)
|
IUTXOLocker utxoLocker, RootedKeyPath accountKeyPath)
|
||||||
{
|
{
|
||||||
|
_memoryCache = memoryCache;
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
_walletRepository = walletRepository;
|
_walletRepository = walletRepository;
|
||||||
_explorerClient = explorerClient;
|
_explorerClient = explorerClient;
|
||||||
@@ -44,7 +48,32 @@ public class Smartifier
|
|||||||
_storeId = storeId;
|
_storeId = storeId;
|
||||||
_utxoLocker = utxoLocker;
|
_utxoLocker = utxoLocker;
|
||||||
_accountKeyPath = accountKeyPath;
|
_accountKeyPath = accountKeyPath;
|
||||||
|
|
||||||
|
_ = LoadInitialTxs();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task LoadInitialTxs()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var txsBulk = await _explorerClient.GetTransactionsAsync(DerivationScheme);
|
||||||
|
foreach (var transactionInformation in txsBulk.ConfirmedTransactions.Transactions.Concat(txsBulk.UnconfirmedTransactions.Transactions))
|
||||||
|
{
|
||||||
|
TransactionInformations.AddOrReplace(transactionInformation.TransactionId,
|
||||||
|
new Lazy<Task<TransactionInformation>>(() => Task.FromResult(transactionInformation)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
|
||||||
|
_loadInitialTxs.TrySetResult();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private TaskCompletionSource _loadInitialTxs = new();
|
||||||
|
|
||||||
public readonly ConcurrentDictionary<uint256, Lazy<Task<TransactionInformation>>> TransactionInformations = new();
|
public readonly ConcurrentDictionary<uint256, Lazy<Task<TransactionInformation>>> TransactionInformations = new();
|
||||||
public readonly ConcurrentDictionary<uint256, Task<SmartTransaction>> SmartTransactions = new();
|
public readonly ConcurrentDictionary<uint256, Task<SmartTransaction>> SmartTransactions = new();
|
||||||
public readonly ConcurrentDictionary<OutPoint, Task<SmartCoin>> Coins = new();
|
public readonly ConcurrentDictionary<OutPoint, Task<SmartCoin>> Coins = new();
|
||||||
@@ -95,6 +124,8 @@ public class Smartifier
|
|||||||
public async Task LoadCoins(List<ReceivedCoin> coins, int current ,
|
public async Task LoadCoins(List<ReceivedCoin> coins, int current ,
|
||||||
Dictionary<OutPoint, (HashSet<string> labels, double anonset, BTCPayWallet.CoinjoinData coinjoinData)> utxoLabels)
|
Dictionary<OutPoint, (HashSet<string> labels, double anonset, BTCPayWallet.CoinjoinData coinjoinData)> utxoLabels)
|
||||||
{
|
{
|
||||||
|
|
||||||
|
await _loadInitialTxs.Task;
|
||||||
coins = coins.Where(data => data is not null).ToList();
|
coins = coins.Where(data => data is not null).ToList();
|
||||||
if (current > 3)
|
if (current > 3)
|
||||||
{
|
{
|
||||||
@@ -168,7 +199,7 @@ public class Smartifier
|
|||||||
};
|
};
|
||||||
}).Where(receivedCoin => receivedCoin is not null).ToList();
|
}).Where(receivedCoin => receivedCoin is not null).ToList();
|
||||||
|
|
||||||
await LoadCoins(inputsToLoad,current+1, await BTCPayWallet.GetUtxoLabels(_walletRepository, _storeId, inputsToLoad.ToArray()));
|
await LoadCoins(inputsToLoad,current+1, await BTCPayWallet.GetUtxoLabels( _memoryCache ,_walletRepository, _storeId, inputsToLoad.ToArray(), true ));
|
||||||
foreach (MatchedOutput input in unsmartTx.Inputs)
|
foreach (MatchedOutput input in unsmartTx.Inputs)
|
||||||
{
|
{
|
||||||
if (!ourSpentUtxos.TryGetValue(input, out var outputtxin))
|
if (!ourSpentUtxos.TryGetValue(input, out var outputtxin))
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
@using BTCPayServer.Common
|
@using BTCPayServer.Common
|
||||||
@using BTCPayServer.Plugins.Wabisabi
|
@using BTCPayServer.Plugins.Wabisabi
|
||||||
@using NBitcoin
|
@using NBitcoin
|
||||||
|
@using Org.BouncyCastle.Asn1.Ocsp
|
||||||
@using WalletWasabi.Extensions
|
@using WalletWasabi.Extensions
|
||||||
@using WalletWasabi.WabiSabi.Backend.Rounds
|
@using WalletWasabi.WabiSabi.Backend.Rounds
|
||||||
@using WalletWasabi.WabiSabi.Client
|
@using WalletWasabi.WabiSabi.Client
|
||||||
@@ -100,7 +101,7 @@
|
|||||||
|
|
||||||
function getColor(isPrivate, score, maxScore) {
|
function getColor(isPrivate, score, maxScore) {
|
||||||
let normalizedScore = Math.min(Math.max(score, 0), maxScore) / maxScore;
|
let normalizedScore = Math.min(Math.max(score, 0), maxScore) / maxScore;
|
||||||
return isPrivate ? `rgb(0, ${Math.floor(255 * normalizedScore)}, 0)` : `rgb(255, ${Math.floor(128 * normalizedScore)}, 0)`;
|
return isPrivate ? `rgb(81, 177, 62)` : `rgb(255, ${Math.floor(128 * normalizedScore)}, 0)`;
|
||||||
}
|
}
|
||||||
|
|
||||||
function prepareDatasets(data) {
|
function prepareDatasets(data) {
|
||||||
@@ -122,8 +123,8 @@ function prepareDatasets(data) {
|
|||||||
id: "inprogresscoins",
|
id: "inprogresscoins",
|
||||||
label: "In Progress Coins",
|
label: "In Progress Coins",
|
||||||
data: inProgressCoins.map(coin => coin.value),
|
data: inProgressCoins.map(coin => coin.value),
|
||||||
backgroundColor: inProgressCoins.map(() => "rgba(81, 177, 62,1)"),
|
backgroundColor: inProgressCoins.map(() => "rgb(56, 151, 37)"),
|
||||||
alternativeBackgroundColor: inProgressCoins.map(() => "rgba(30, 122, 68,1)"),
|
alternativeBackgroundColor: inProgressCoins.map(() => "rgb(28, 113, 11)"),
|
||||||
borderColor: "transparent",
|
borderColor: "transparent",
|
||||||
borderWidth: 3,
|
borderWidth: 3,
|
||||||
coins: inProgressCoins
|
coins: inProgressCoins
|
||||||
@@ -286,16 +287,21 @@ updateInProgressAnimation(myChart);
|
|||||||
}
|
}
|
||||||
<header>
|
<header>
|
||||||
<h4>Coinjoin stats</h4>
|
<h4>Coinjoin stats</h4>
|
||||||
<a asp-controller="WabisabiStore" asp-action="UpdateWabisabiStoreSettings" asp-route-storeId="@storeId" class="fw-semibold">
|
<div class="d-flex gap-1">
|
||||||
Configure coinjoin settings
|
<a asp-controller="WabisabiStore" asp-action="ToggleActive" asp-route-storeId="@storeId" asp-route-returnUrl="@Context.Request.GetCurrentUrl()" class="fw-semibold">
|
||||||
|
@(settings.Active ? "Deactivate" : "Activate")
|
||||||
</a>
|
</a>
|
||||||
|
-
|
||||||
|
<a asp-controller="WabisabiStore" asp-action="UpdateWabisabiStoreSettings" asp-route-storeId="@storeId" class="fw-semibold">
|
||||||
|
Configure
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
</header>
|
</header>
|
||||||
<div class="w-100">
|
<div class="w-100">
|
||||||
|
|
||||||
@if (coins.Any())
|
@if (coins.Any())
|
||||||
{
|
{
|
||||||
<div class="d-flex justify-content-center" style="max-height: 400px; "> <canvas id="cjchart" class="mb-4"></canvas></div>
|
<div class="d-flex justify-content-center mb-4" style="max-height: 400px; "> <canvas id="cjchart"></canvas></div>
|
||||||
|
|
||||||
}
|
}
|
||||||
@* <div> *@
|
@* <div> *@
|
||||||
@@ -508,10 +514,15 @@ updateInProgressAnimation(myChart);
|
|||||||
</tr>
|
</tr>
|
||||||
@if (!tracker.CoinJoinClient.CoinsToRegister.IsEmpty)
|
@if (!tracker.CoinJoinClient.CoinsToRegister.IsEmpty)
|
||||||
{
|
{
|
||||||
|
var statement = $"Registered {tracker.CoinJoinClient.CoinsInCriticalPhase.Count()} inputs ({tracker.CoinJoinClient.CoinsInCriticalPhase.Sum(coin => coin.Amount.ToDecimal(MoneyUnit.BTC))} BTC)";
|
||||||
|
if (tracker.CoinJoinClient.CoinsInCriticalPhase.Count() != tracker.CoinJoinClient.CoinsToRegister.Count())
|
||||||
|
{
|
||||||
|
statement += $" / {tracker.CoinJoinClient.CoinsToRegister.Count()} inputs ({tracker.CoinJoinClient.CoinsToRegister.Sum(coin => coin.Amount.ToDecimal(MoneyUnit.BTC))} BTC)";
|
||||||
|
}
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="">Your inputs</th>
|
<th scope="">Your inputs</th>
|
||||||
<td class="">
|
<td class="">
|
||||||
<span class="w-100">Registered @tracker.CoinJoinClient.CoinsInCriticalPhase.Count() inputs (@tracker.CoinJoinClient.CoinsInCriticalPhase.Sum(coin => coin.Amount.ToDecimal(MoneyUnit.BTC)) BTC) / @tracker.CoinJoinClient.CoinsToRegister.Count() inputs (@tracker.CoinJoinClient.CoinsToRegister.Sum(coin => coin.Amount.ToDecimal(MoneyUnit.BTC)) BTC) </span>
|
<span class="w-100">@statement</span>
|
||||||
@if (tracker.BannedCoins.Any())
|
@if (tracker.BannedCoins.Any())
|
||||||
{
|
{
|
||||||
<span class="w-100 text-danger">but got @tracker.BannedCoins.Count() inputs (@tracker.BannedCoins.Sum(coin => coin.Coin.Amount.ToDecimal(MoneyUnit.BTC)) BTC) banned</span>
|
<span class="w-100 text-danger">but got @tracker.BannedCoins.Count() inputs (@tracker.BannedCoins.Sum(coin => coin.Coin.Amount.ToDecimal(MoneyUnit.BTC)) BTC) banned</span>
|
||||||
@@ -528,9 +539,11 @@ updateInProgressAnimation(myChart);
|
|||||||
</tr>
|
</tr>
|
||||||
if (tracker.CoinJoinClient.OutputTxOuts is { } outputs)
|
if (tracker.CoinJoinClient.OutputTxOuts is { } outputs)
|
||||||
{
|
{
|
||||||
|
var statement = $"{outputs.outputTxOuts.Count()} outputs ({outputs.outputTxOuts.Sum(coin => coin.Value.ToDecimal(MoneyUnit.BTC))} BTC {(outputs.batchedPayments.Any()? $"{outputs.batchedPayments.Count()} batched payments": "")}";
|
||||||
|
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="">Your outputs</th>
|
<th scope="">Your outputs</th>
|
||||||
<td >@outputs.outputTxOuts.Count() outputs (@outputs.outputTxOuts.Sum(coin => coin.Value.ToDecimal(MoneyUnit.BTC)) BTC, @outputs.batchedPayments.Count() batched payments)</td>
|
<td >@statement</td>
|
||||||
</tr>
|
</tr>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,7 +35,7 @@
|
|||||||
|
|
||||||
@if (ViewData.ModelState.TryGetValue("config", out var error) && error.Errors.Any())
|
@if (ViewData.ModelState.TryGetValue("config", out var error) && error.Errors.Any())
|
||||||
{
|
{
|
||||||
<span class="text-danger">@string.Join("\n", error.Errors)</span>
|
<span class="text-danger">@string.Join("\n", error.Errors.Select(modelError => modelError.ErrorMessage))</span>
|
||||||
}
|
}
|
||||||
<textarea rows="10" cols="40" class="form-control" id="config" name="config" >
|
<textarea rows="10" cols="40" class="form-control" id="config" name="config" >
|
||||||
@Html.Raw(ViewBag.Config)
|
@Html.Raw(ViewBag.Config)
|
||||||
|
|||||||
@@ -44,19 +44,22 @@
|
|||||||
<vc:icon symbol="info"/>
|
<vc:icon symbol="info"/>
|
||||||
</a>
|
</a>
|
||||||
</h3>
|
</h3>
|
||||||
<div>
|
<div class="d-flex align-items-center gap-1 ">
|
||||||
|
<div class="d-flex align-items-center">
|
||||||
|
<input asp-for="Active" type="checkbox" class="btcpay-toggle me-2"/>
|
||||||
|
<label asp-for="Active" class="form-label mb-0 me-1"></label>
|
||||||
|
</div>
|
||||||
<button name="command" type="submit" value="save" class="btn btn-primary mt-3 mt-sm-0">Save</button>
|
<button name="command" type="submit" value="save" class="btn btn-primary mt-3 mt-sm-0">Save</button>
|
||||||
<a asp-action="ListCoinjoins" asp-route-storeId="@storeId" class="btn btn-secondary mt-3 mt-sm-0" role="button">
|
<a asp-action="ListCoinjoins" asp-route-storeId="@storeId" class="btn btn-secondary mt-3 mt-sm-0" role="button">
|
||||||
Coinjoin History
|
Coinjoin History
|
||||||
</a>
|
</a>
|
||||||
<button type="button" class="btn btn-secondary mt-3 mt-sm-0" permission="@Policies.CanModifyServerSettings"
|
<button type="button" class="btn btn-secondary mt-3 mt-sm-0" permission="@Policies.CanModifyServerSettings"
|
||||||
data-bs-toggle="modal" data-bs-target="#discover-prompt">
|
data-bs-toggle="modal" data-bs-target="#discover-prompt">
|
||||||
Add Coordinator
|
Add Coordinator
|
||||||
</button>
|
</button>
|
||||||
<a asp-controller="WabisabiCoordinatorConfig" asp-action="UpdateWabisabiSettings" class="btn btn-secondary mt-3 mt-sm-0" permission="@Policies.CanModifyServerSettings">Coordinator</a>
|
<a asp-controller="WabisabiCoordinatorConfig" asp-action="UpdateWabisabiSettings" class="btn btn-secondary mt-3 mt-sm-0" permission="@Policies.CanModifyServerSettings">Coordinator</a>
|
||||||
@* <a class="btn btn-secondary mt-3 mt-sm-0" href="https://gist.github.com/nopara73/bb17e89d7dc9af536ca41f50f705d329" rel="noreferrer noopener" target="_blank">Enable Discreet payments - Coming soon</a> *@
|
|
||||||
|
|
||||||
|
@* <a class="btn btn-secondary mt-3 mt-sm-0" href="https://gist.github.com/nopara73/bb17e89d7dc9af536ca41f50f705d329" rel="noreferrer noopener" target="_blank">Enable Discreet payments - Coming soon</a> *@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@{
|
@{
|
||||||
@@ -80,9 +83,26 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
<style>
|
||||||
|
#blocker:hover{
|
||||||
|
background-color: rgba(128,128,128, 0.5);
|
||||||
|
}
|
||||||
|
#blocker:hover h4{
|
||||||
|
display: block !important;
|
||||||
|
left: 0;
|
||||||
|
position: fixed;
|
||||||
|
width: 100%
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
<div class="@(anyEnabled ? "" : "d-none") card card-body coordinator-settings">
|
<div class="@(anyEnabled ? "" : "d-none") card card-body coordinator-settings">
|
||||||
|
@if (Model.Active && anyEnabled)
|
||||||
|
{
|
||||||
|
<div class="position-absolute w-100 h-100 text-center rounded" id="blocker" style=" left: 0; top: 0; z-index: 1">
|
||||||
|
<h4 class="d-none pt-4">Settings cannot be changed while active</h4>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
}
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-sm-12 col-md-6">
|
<div class="col-sm-12 col-md-6">
|
||||||
<div class="form-check">
|
<div class="form-check">
|
||||||
@@ -140,6 +160,11 @@
|
|||||||
<input asp-for="BatchPayments" type="checkbox" class="form-check-input"/>
|
<input asp-for="BatchPayments" type="checkbox" class="form-check-input"/>
|
||||||
<p class="text-muted">Batch your pending payments (on-chain payouts awaiting payment) inside coinjoins.</p>
|
<p class="text-muted">Batch your pending payments (on-chain payouts awaiting payment) inside coinjoins.</p>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="form-group form-check">
|
||||||
|
<label asp-for="ParanoidPayments" class="form-check-label">Paranoid payments</label>
|
||||||
|
<input asp-for="ParanoidPayments" type="checkbox" class="form-check-input"/>
|
||||||
|
<p class="text-muted">Only batch payments with fully private coins.</p>
|
||||||
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label asp-for="CrossMixBetweenCoordinatorsMode" class="form-label">Mix funds between different coordinators</label>
|
<label asp-for="CrossMixBetweenCoordinatorsMode" class="form-label">Mix funds between different coordinators</label>
|
||||||
<select asp-for="CrossMixBetweenCoordinatorsMode" class="form-select">
|
<select asp-for="CrossMixBetweenCoordinatorsMode" class="form-select">
|
||||||
|
|||||||
@@ -324,32 +324,6 @@ public class WabisabiCoordinatorClientInstance:IHostedService
|
|||||||
_logger.LogTrace(coinJoinStatusEventArgs.CoinJoinProgressEventArgs.GetType() + " :" +
|
_logger.LogTrace(coinJoinStatusEventArgs.CoinJoinProgressEventArgs.GetType() + " :" +
|
||||||
e.Wallet.WalletName);
|
e.Wallet.WalletName);
|
||||||
break;
|
break;
|
||||||
case CompletedEventArgs completedEventArgs:
|
|
||||||
|
|
||||||
var result = completedEventArgs.CoinJoinResult;
|
|
||||||
|
|
||||||
if (completedEventArgs.CompletionStatus == CompletionStatus.Success && result is SuccessfulCoinJoinResult successfulCoinJoinResult)
|
|
||||||
{
|
|
||||||
Task.Run(async () =>
|
|
||||||
{
|
|
||||||
|
|
||||||
var wallet = (BTCPayWallet) e.Wallet;
|
|
||||||
await wallet.RegisterCoinjoinTransaction(successfulCoinJoinResult, CoordinatorName);
|
|
||||||
|
|
||||||
});
|
|
||||||
}
|
|
||||||
else if(result is DisruptedCoinJoinResult disruptedCoinJoinResult )
|
|
||||||
{
|
|
||||||
Task.Run(async () =>
|
|
||||||
{
|
|
||||||
// _logger.LogInformation("unlocking coins because round failed");
|
|
||||||
await _utxoLocker.TryUnlock(
|
|
||||||
disruptedCoinJoinResult.SignedCoins.Select(coin => coin.Outpoint).ToArray());
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
_logger.LogTrace("Coinjoin complete! :" + e.Wallet.WalletName);
|
|
||||||
break;
|
|
||||||
case LoadedEventArgs loadedEventArgs:
|
case LoadedEventArgs loadedEventArgs:
|
||||||
stopWhenAllMixed = !((BTCPayWallet)loadedEventArgs.Wallet).BatchPayments;
|
stopWhenAllMixed = !((BTCPayWallet)loadedEventArgs.Wallet).BatchPayments;
|
||||||
_ = CoinJoinManager.StartAsync(loadedEventArgs.Wallet, stopWhenAllMixed, false, CancellationToken.None);
|
_ = CoinJoinManager.StartAsync(loadedEventArgs.Wallet, stopWhenAllMixed, false, CancellationToken.None);
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ using BTCPayServer.Abstractions.Services;
|
|||||||
using BTCPayServer.Common;
|
using BTCPayServer.Common;
|
||||||
using BTCPayServer.Services.Stores;
|
using BTCPayServer.Services.Stores;
|
||||||
using Microsoft.AspNetCore.Builder;
|
using Microsoft.AspNetCore.Builder;
|
||||||
|
using Microsoft.Extensions.Caching.Memory;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using NBitcoin;
|
using NBitcoin;
|
||||||
@@ -60,7 +61,8 @@ public class WabisabiPlugin : BaseBTCPayServerPlugin
|
|||||||
utxoLocker,
|
utxoLocker,
|
||||||
provider.GetRequiredService<EventAggregator>(),
|
provider.GetRequiredService<EventAggregator>(),
|
||||||
provider.GetRequiredService<ILogger<WalletProvider>>(),
|
provider.GetRequiredService<ILogger<WalletProvider>>(),
|
||||||
provider.GetRequiredService<BTCPayNetworkProvider>()
|
provider.GetRequiredService<BTCPayNetworkProvider>(),
|
||||||
|
provider.GetRequiredService<IMemoryCache>()
|
||||||
));
|
));
|
||||||
applicationBuilder.AddWabisabiCoordinator();
|
applicationBuilder.AddWabisabiCoordinator();
|
||||||
applicationBuilder.AddSingleton<IWalletProvider>(provider => provider.GetRequiredService<WalletProvider>());
|
applicationBuilder.AddSingleton<IWalletProvider>(provider => provider.GetRequiredService<WalletProvider>());
|
||||||
|
|||||||
@@ -82,13 +82,6 @@ namespace BTCPayServer.Plugins.Wabisabi
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (wabisabiSettings.Settings.All(settings => !settings.Enabled))
|
|
||||||
{
|
|
||||||
|
|
||||||
await _storeRepository.UpdateSetting<WabisabiStoreSettings>(storeId, nameof(WabisabiStoreSettings), null!);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var res = await GetWabisabiForStore(storeId);
|
var res = await GetWabisabiForStore(storeId);
|
||||||
foreach (var wabisabiStoreCoordinatorSettings in wabisabiSettings.Settings)
|
foreach (var wabisabiStoreCoordinatorSettings in wabisabiSettings.Settings)
|
||||||
{
|
{
|
||||||
@@ -114,7 +107,7 @@ namespace BTCPayServer.Plugins.Wabisabi
|
|||||||
|
|
||||||
}
|
}
|
||||||
await _storeRepository.UpdateSetting(storeId, nameof(WabisabiStoreSettings), wabisabiSettings!);
|
await _storeRepository.UpdateSetting(storeId, nameof(WabisabiStoreSettings), wabisabiSettings!);
|
||||||
}
|
|
||||||
_memoryCache.Remove(GetCacheKey(storeId));
|
_memoryCache.Remove(GetCacheKey(storeId));
|
||||||
await _walletProvider.SettingsUpdated(storeId, wabisabiSettings);
|
await _walletProvider.SettingsUpdated(storeId, wabisabiSettings);
|
||||||
// var existingProcessor = (await _payoutProcessorService.GetProcessors(new PayoutProcessorService.PayoutProcessorQuery()
|
// var existingProcessor = (await _payoutProcessorService.GetProcessors(new PayoutProcessorService.PayoutProcessorQuery()
|
||||||
|
|||||||
@@ -57,6 +57,24 @@ namespace BTCPayServer.Plugins.Wabisabi
|
|||||||
_socks5HttpClientHandler = serviceProvider.GetRequiredService<Socks5HttpClientHandler>();
|
_socks5HttpClientHandler = serviceProvider.GetRequiredService<Socks5HttpClientHandler>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[HttpGet("toggle-active")]
|
||||||
|
public async Task<IActionResult> ToggleActive(string storeId, string returnUrl)
|
||||||
|
{
|
||||||
|
var settings = await _WabisabiService.GetWabisabiForStore(storeId);
|
||||||
|
if (settings is null)
|
||||||
|
{
|
||||||
|
return NotFound();
|
||||||
|
}
|
||||||
|
settings.Active = !settings.Active;
|
||||||
|
|
||||||
|
await _WabisabiService.SetWabisabiForStore(storeId, settings);
|
||||||
|
TempData["SuccessMessage"] = $"Coinjoin is now {(settings.Active ? "active" : "inactive")}";
|
||||||
|
returnUrl = Url.EnsureLocal(returnUrl, HttpContext.Request);
|
||||||
|
if(returnUrl is null)
|
||||||
|
return RedirectToAction(nameof(UpdateWabisabiStoreSettings), new {storeId});
|
||||||
|
return Redirect(returnUrl);
|
||||||
|
}
|
||||||
|
|
||||||
[HttpGet("")]
|
[HttpGet("")]
|
||||||
public async Task<IActionResult> UpdateWabisabiStoreSettings(string storeId)
|
public async Task<IActionResult> UpdateWabisabiStoreSettings(string storeId)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,6 +1,4 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
|
||||||
using BTCPayServer.Client.JsonConverters;
|
|
||||||
using NBitcoin;
|
using NBitcoin;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
@@ -9,7 +7,7 @@ namespace BTCPayServer.Plugins.Wabisabi;
|
|||||||
public class WabisabiStoreSettings
|
public class WabisabiStoreSettings
|
||||||
{
|
{
|
||||||
public List<WabisabiStoreCoordinatorSettings> Settings { get; set; } = new();
|
public List<WabisabiStoreCoordinatorSettings> Settings { get; set; } = new();
|
||||||
|
public bool Active { get; set; } = true;
|
||||||
|
|
||||||
public string MixToOtherWallet { get; set; }
|
public string MixToOtherWallet { get; set; }
|
||||||
|
|
||||||
@@ -22,6 +20,7 @@ public class WabisabiStoreSettings
|
|||||||
public int AnonymitySetTarget { get; set; } = 5;
|
public int AnonymitySetTarget { get; set; } = 5;
|
||||||
|
|
||||||
public bool BatchPayments { get; set; } = true;
|
public bool BatchPayments { get; set; } = true;
|
||||||
|
public bool ParanoidPayments { get; set; } = false;
|
||||||
public int ExtraJoinProbability { get; set; } = 0;
|
public int ExtraJoinProbability { get; set; } = 0;
|
||||||
public CrossMixMode CrossMixBetweenCoordinatorsMode { get; set; } = CrossMixMode.WhenFree;
|
public CrossMixMode CrossMixBetweenCoordinatorsMode { get; set; } = CrossMixMode.WhenFree;
|
||||||
public int FeeRateMedianTimeFrameHours { get; set; }
|
public int FeeRateMedianTimeFrameHours { get; set; }
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ public class WalletProvider : PeriodicRunner,IWalletProvider
|
|||||||
private readonly EventAggregator _eventAggregator;
|
private readonly EventAggregator _eventAggregator;
|
||||||
private readonly ILogger<WalletProvider> _logger;
|
private readonly ILogger<WalletProvider> _logger;
|
||||||
private readonly BTCPayNetworkProvider _networkProvider;
|
private readonly BTCPayNetworkProvider _networkProvider;
|
||||||
|
private readonly IMemoryCache _memoryCache;
|
||||||
|
|
||||||
public WalletProvider(
|
public WalletProvider(
|
||||||
IServiceProvider serviceProvider,
|
IServiceProvider serviceProvider,
|
||||||
@@ -46,7 +47,8 @@ public class WalletProvider : PeriodicRunner,IWalletProvider
|
|||||||
IUTXOLocker utxoLocker,
|
IUTXOLocker utxoLocker,
|
||||||
EventAggregator eventAggregator,
|
EventAggregator eventAggregator,
|
||||||
ILogger<WalletProvider> logger,
|
ILogger<WalletProvider> logger,
|
||||||
BTCPayNetworkProvider networkProvider) : base(TimeSpan.FromMinutes(5))
|
BTCPayNetworkProvider networkProvider,
|
||||||
|
IMemoryCache memoryCache) : base(TimeSpan.FromMinutes(5))
|
||||||
{
|
{
|
||||||
UtxoLocker = utxoLocker;
|
UtxoLocker = utxoLocker;
|
||||||
_serviceProvider = serviceProvider;
|
_serviceProvider = serviceProvider;
|
||||||
@@ -56,6 +58,7 @@ public class WalletProvider : PeriodicRunner,IWalletProvider
|
|||||||
_eventAggregator = eventAggregator;
|
_eventAggregator = eventAggregator;
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
_networkProvider = networkProvider;
|
_networkProvider = networkProvider;
|
||||||
|
_memoryCache = memoryCache;
|
||||||
}
|
}
|
||||||
|
|
||||||
public readonly ConcurrentDictionary<string, Lazy<Task<IWallet>>> LoadedWallets = new();
|
public readonly ConcurrentDictionary<string, Lazy<Task<IWallet>>> LoadedWallets = new();
|
||||||
@@ -102,7 +105,7 @@ public class WalletProvider : PeriodicRunner,IWalletProvider
|
|||||||
var accountKeyPath2 = await explorerClient.GetMetadataAsync<RootedKeyPath>(derivationStrategy,
|
var accountKeyPath2 = await explorerClient.GetMetadataAsync<RootedKeyPath>(derivationStrategy,
|
||||||
WellknownMetadataKeys.AccountKeyPath);
|
WellknownMetadataKeys.AccountKeyPath);
|
||||||
accountKeyPath = accountKeyPath2 ?? accountKeyPath;
|
accountKeyPath = accountKeyPath2 ?? accountKeyPath;
|
||||||
var smartifier = new Smartifier(_logger,_serviceProvider.GetRequiredService<WalletRepository>(),
|
var smartifier = new Smartifier(_memoryCache,_logger,_serviceProvider.GetRequiredService<WalletRepository>(),
|
||||||
explorerClient, derivationStrategy, name, UtxoLocker, accountKeyPath);
|
explorerClient, derivationStrategy, name, UtxoLocker, accountKeyPath);
|
||||||
if (masterKey is null || accountKey is null || accountKeyPath is null)
|
if (masterKey is null || accountKey is null || accountKeyPath is null)
|
||||||
{
|
{
|
||||||
@@ -113,7 +116,7 @@ public class WalletProvider : PeriodicRunner,IWalletProvider
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var smartifier = new Smartifier(_logger,_serviceProvider.GetRequiredService<WalletRepository>(), explorerClient,
|
var smartifier = new Smartifier(_memoryCache, _logger,_serviceProvider.GetRequiredService<WalletRepository>(), explorerClient,
|
||||||
derivationStrategy, name, UtxoLocker, accountKeyPath);
|
derivationStrategy, name, UtxoLocker, accountKeyPath);
|
||||||
keychain = new BTCPayKeyChain(explorerClient, derivationStrategy, null, null, smartifier);
|
keychain = new BTCPayKeyChain(explorerClient, derivationStrategy, null, null, smartifier);
|
||||||
}
|
}
|
||||||
@@ -242,7 +245,7 @@ public class WalletProvider : PeriodicRunner,IWalletProvider
|
|||||||
public async Task SettingsUpdated(string storeId, WabisabiStoreSettings wabisabiSettings)
|
public async Task SettingsUpdated(string storeId, WabisabiStoreSettings wabisabiSettings)
|
||||||
{
|
{
|
||||||
|
|
||||||
if (wabisabiSettings.Settings.All(settings => !settings.Enabled))
|
if (wabisabiSettings.Settings.All(settings => !settings.Enabled) || !wabisabiSettings.Active)
|
||||||
{
|
{
|
||||||
_cachedSettings?.Remove(storeId);
|
_cachedSettings?.Remove(storeId);
|
||||||
await UnloadWallet(storeId);
|
await UnloadWallet(storeId);
|
||||||
|
|||||||
Submodule submodules/walletwasabi updated: 2ffaba5965...4d34360b6f
Reference in New Issue
Block a user