mirror of
https://github.com/aljazceru/BTCPayServerPlugins.git
synced 2025-12-17 07:34:24 +01:00
ws update
This commit is contained in:
@@ -2,6 +2,7 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Collections.Immutable;
|
using System.Collections.Immutable;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using NBitcoin;
|
using NBitcoin;
|
||||||
using WalletWasabi.Blockchain.Analysis;
|
using WalletWasabi.Blockchain.Analysis;
|
||||||
@@ -17,6 +18,7 @@ using WalletWasabi.Models;
|
|||||||
using WalletWasabi.WabiSabi;
|
using WalletWasabi.WabiSabi;
|
||||||
using WalletWasabi.WabiSabi.Backend.Rounds;
|
using WalletWasabi.WabiSabi.Backend.Rounds;
|
||||||
using WalletWasabi.WabiSabi.Client;
|
using WalletWasabi.WabiSabi.Client;
|
||||||
|
using WalletWasabi.WabiSabi.Client.CoinJoin.Client;
|
||||||
using WalletWasabi.WabiSabi.Client.StatusChangedEvents;
|
using WalletWasabi.WabiSabi.Client.StatusChangedEvents;
|
||||||
using WalletWasabi.WabiSabi.Models;
|
using WalletWasabi.WabiSabi.Models;
|
||||||
using WalletWasabi.WabiSabi.Models.MultipartyTransaction;
|
using WalletWasabi.WabiSabi.Models.MultipartyTransaction;
|
||||||
@@ -35,28 +37,24 @@ public class BTCPayCoinjoinCoinSelector : IRoundCoinSelector
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async
|
public async
|
||||||
Task<(
|
Task<(ImmutableList<SmartCoin> selected, Func<IEnumerable<AliceClient>, Task<bool>> acceptableRegistered, Func<
|
||||||
ImmutableList<SmartCoin> selected,
|
ImmutableArray<AliceClient>, (IEnumerable<TxOut> outputTxOuts, Dictionary<TxOut, PendingPayment>
|
||||||
Func<IEnumerable<AliceClient>, Task<bool>> acceptableRegistered,
|
batchedPayments), TransactionWithPrecomputedData, RoundState, Task<bool>> acceptableOutputs)>
|
||||||
Func<
|
|
||||||
ImmutableArray<AliceClient>,
|
|
||||||
(IEnumerable<TxOut> outputTxOuts, Dictionary<TxOut, PendingPayment> batchedPayments),
|
|
||||||
TransactionWithPrecomputedData,
|
|
||||||
RoundState,
|
|
||||||
Task<bool>> acceptableOutputs
|
|
||||||
)>
|
|
||||||
SelectCoinsAsync((IEnumerable<SmartCoin> Candidates, IEnumerable<SmartCoin> Ineligible) coinCandidates,
|
SelectCoinsAsync((IEnumerable<SmartCoin> Candidates, IEnumerable<SmartCoin> Ineligible) coinCandidates,
|
||||||
UtxoSelectionParameters utxoSelectionParameters, Money liquidityClue, SecureRandom secureRandom)
|
RoundParameters roundParameters, Money liquidityClue, SecureRandom secureRandom, string coordinatorName)
|
||||||
{
|
{
|
||||||
SmartCoin[] FilterCoinsMore(IEnumerable<SmartCoin> coins)
|
var log = new StringBuilder();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
SmartCoin[] FilterCoinsMore(IEnumerable<SmartCoin> coins)
|
||||||
{
|
{
|
||||||
return coins
|
return coins
|
||||||
.Where(coin => utxoSelectionParameters.AllowedInputScriptTypes.Contains(coin.ScriptType))
|
.Where(coin => roundParameters.AllowedInputTypes.Contains(coin.ScriptType))
|
||||||
.Where(coin => utxoSelectionParameters.AllowedInputAmounts.Contains(coin.Amount))
|
.Where(coin => roundParameters.AllowedInputAmounts.Contains(coin.Amount))
|
||||||
.Where(coin =>
|
.Where(coin =>
|
||||||
{
|
{
|
||||||
var effV = coin.EffectiveValue(utxoSelectionParameters.MiningFeeRate,
|
var effV = coin.EffectiveValue(roundParameters.MiningFeeRate,
|
||||||
utxoSelectionParameters.CoordinationFeeRate);
|
roundParameters.CoordinationFeeRate);
|
||||||
var percentageLeft = (effV.ToDecimal(MoneyUnit.BTC) / coin.Amount.ToDecimal(MoneyUnit.BTC));
|
var percentageLeft = (effV.ToDecimal(MoneyUnit.BTC) / coin.Amount.ToDecimal(MoneyUnit.BTC));
|
||||||
// filter out low value coins where 50% of the value would be eaten up by fees
|
// filter out low value coins where 50% of the value would be eaten up by fees
|
||||||
return effV > Money.Zero && percentageLeft >= 0.5m;
|
return effV > Money.Zero && percentageLeft >= 0.5m;
|
||||||
@@ -69,7 +67,7 @@ public class BTCPayCoinjoinCoinSelector : IRoundCoinSelector
|
|||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (!coin.HdPubKey.Labels.Contains("coinjoin") || coin.HdPubKey.Labels.Contains(utxoSelectionParameters.CoordinatorName))
|
if (!coin.HdPubKey.Labels.Contains("coinjoin") || coin.HdPubKey.Labels.Contains(coordinatorName))
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -78,7 +76,7 @@ public class BTCPayCoinjoinCoinSelector : IRoundCoinSelector
|
|||||||
_wallet.WabisabiStoreSettings.CrossMixBetweenCoordinatorsMode ==
|
_wallet.WabisabiStoreSettings.CrossMixBetweenCoordinatorsMode ==
|
||||||
WabisabiStoreSettings.CrossMixMode.WhenFree)
|
WabisabiStoreSettings.CrossMixMode.WhenFree)
|
||||||
{
|
{
|
||||||
return coin.Amount <= utxoSelectionParameters.CoordinationFeeRate.PlebsDontPayThreshold;
|
return coin.Amount <= roundParameters.CoordinationFeeRate.PlebsDontPayThreshold;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
@@ -93,7 +91,7 @@ public class BTCPayCoinjoinCoinSelector : IRoundCoinSelector
|
|||||||
|
|
||||||
var payments =
|
var payments =
|
||||||
(_wallet.BatchPayments
|
(_wallet.BatchPayments
|
||||||
? await _wallet.DestinationProvider.GetPendingPaymentsAsync(utxoSelectionParameters)
|
? await _wallet.DestinationProvider.GetPendingPaymentsAsync(roundParameters)
|
||||||
: Array.Empty<PendingPayment>()).ToArray();
|
: Array.Empty<PendingPayment>()).ToArray();
|
||||||
|
|
||||||
var maxPerType = new Dictionary<AnonsetType, int>();
|
var maxPerType = new Dictionary<AnonsetType, int>();
|
||||||
@@ -113,7 +111,7 @@ public class BTCPayCoinjoinCoinSelector : IRoundCoinSelector
|
|||||||
maxPerType.TryAdd(AnonsetType.Red, 1);
|
maxPerType.TryAdd(AnonsetType.Red, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
var isLowFee = utxoSelectionParameters.MiningFeeRate.SatoshiPerByte <= _wallet.LowFeeTarget;
|
var isLowFee = roundParameters.MiningFeeRate.SatoshiPerByte <= _wallet.LowFeeTarget;
|
||||||
var consolidationMode = _wallet.ConsolidationMode switch
|
var consolidationMode = _wallet.ConsolidationMode switch
|
||||||
{
|
{
|
||||||
ConsolidationModeType.Always => true,
|
ConsolidationModeType.Always => true,
|
||||||
@@ -122,19 +120,19 @@ public class BTCPayCoinjoinCoinSelector : IRoundCoinSelector
|
|||||||
ConsolidationModeType.WhenLowFeeAndManyUTXO => isLowFee && candidates.Count() > BTCPayWallet.HighAmountOfCoins,
|
ConsolidationModeType.WhenLowFeeAndManyUTXO => isLowFee && candidates.Count() > BTCPayWallet.HighAmountOfCoins,
|
||||||
_ => throw new ArgumentOutOfRangeException()
|
_ => throw new ArgumentOutOfRangeException()
|
||||||
};
|
};
|
||||||
var mixReasons = await _wallet.ShouldMix(utxoSelectionParameters.CoordinatorName, isLowFee, payments.Any());
|
var mixReasons = await _wallet.ShouldMix(coordinatorName, isLowFee, payments.Any());
|
||||||
if (!mixReasons.Any())
|
if (!mixReasons.Any())
|
||||||
{
|
{
|
||||||
throw new CoinJoinClientException(CoinjoinError.NoCoinsEligibleToMix, "ShouldMix returned false, so we will not mix");
|
throw new CoinJoinClientException(CoinjoinError.NoCoinsEligibleToMix, "ShouldMix returned false, so we will not mix");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_wallet.LogDebug($"ShouldMix returned true for {mixReasons.Length} reasons: {string.Join(", ", mixReasons)}");
|
log.AppendLine($"ShouldMix returned true for {mixReasons.Length} reasons: {string.Join(", ", mixReasons)}");
|
||||||
}
|
}
|
||||||
Dictionary<AnonsetType, int> idealMinimumPerType = new Dictionary<AnonsetType, int>()
|
Dictionary<AnonsetType, int> idealMinimumPerType = new Dictionary<AnonsetType, int>()
|
||||||
{{AnonsetType.Red, 1}, {AnonsetType.Orange, 1}, {AnonsetType.Green, 1}};
|
{{AnonsetType.Red, 1}, {AnonsetType.Orange, 1}, {AnonsetType.Green, 1}};
|
||||||
|
|
||||||
var solution = await SelectCoinsInternal(utxoSelectionParameters, candidates,payments,
|
var solution = await SelectCoinsInternal(log,coordinatorName,roundParameters, candidates,payments,
|
||||||
Random.Shared.Next(20, 31),
|
Random.Shared.Next(20, 31),
|
||||||
maxPerType,
|
maxPerType,
|
||||||
idealMinimumPerType,
|
idealMinimumPerType,
|
||||||
@@ -158,7 +156,7 @@ public class BTCPayCoinjoinCoinSelector : IRoundCoinSelector
|
|||||||
throw new CoinJoinClientException(CoinjoinError.NoCoinsEligibleToMix, "ShouldMix returned true only for consolidation, but less than 10 coins were found, so we will not mix");
|
throw new CoinJoinClientException(CoinjoinError.NoCoinsEligibleToMix, "ShouldMix returned true only for consolidation, but less than 10 coins were found, so we will not mix");
|
||||||
}
|
}
|
||||||
|
|
||||||
_wallet.LogTrace(solution.ToString());
|
log.AppendLine(solution.ToString());
|
||||||
|
|
||||||
async Task<bool> AcceptableRegistered(IEnumerable<AliceClient> coins)
|
async Task<bool> AcceptableRegistered(IEnumerable<AliceClient> coins)
|
||||||
{
|
{
|
||||||
@@ -183,7 +181,7 @@ public class BTCPayCoinjoinCoinSelector : IRoundCoinSelector
|
|||||||
var effectiveSumOfRegisteredCoins = coins.Sum(coin => coin.EffectiveValue);
|
var effectiveSumOfRegisteredCoins = coins.Sum(coin => coin.EffectiveValue);
|
||||||
var canHandleAPayment = solution.HandledPayments.Any(payment =>
|
var canHandleAPayment = solution.HandledPayments.Any(payment =>
|
||||||
{
|
{
|
||||||
var cost = payment.ToTxOut().EffectiveCost(utxoSelectionParameters.MiningFeeRate) + payment.Value;
|
var cost = payment.ToTxOut().EffectiveCost(roundParameters.MiningFeeRate) + payment.Value;
|
||||||
return effectiveSumOfRegisteredCoins >= cost;
|
return effectiveSumOfRegisteredCoins >= cost;
|
||||||
});
|
});
|
||||||
if (!canHandleAPayment)
|
if (!canHandleAPayment)
|
||||||
@@ -204,7 +202,7 @@ public class BTCPayCoinjoinCoinSelector : IRoundCoinSelector
|
|||||||
|
|
||||||
if (remainingMixReasons.Count != mixReasons.Length)
|
if (remainingMixReasons.Count != mixReasons.Length)
|
||||||
{
|
{
|
||||||
_wallet.LogDebug($"Some mix reasons were removed due to the difference in registered coins: {string.Join(", ", mixReasons.Except(remainingMixReasons))}. Remaining: {string.Join(", ", remainingMixReasons)}");
|
log.AppendLine($"Some mix reasons were removed due to the difference in registered coins: {string.Join(", ", mixReasons.Except(remainingMixReasons))}. Remaining: {string.Join(", ", remainingMixReasons)}");
|
||||||
}
|
}
|
||||||
|
|
||||||
return remainingMixReasons.Any();
|
return remainingMixReasons.Any();
|
||||||
@@ -267,9 +265,20 @@ public class BTCPayCoinjoinCoinSelector : IRoundCoinSelector
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (solution.Coins.ToImmutableList(), AcceptableRegistered , AcceptableOutputs);
|
return (solution.Coins.ToImmutableList(), AcceptableRegistered , AcceptableOutputs);
|
||||||
|
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
if(log.Length > 0)
|
||||||
|
_wallet.LogInfo(coordinatorName, $"coinselection: {log}");
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<SubsetSolution> SelectCoinsInternal(UtxoSelectionParameters utxoSelectionParameters,
|
private async Task<SubsetSolution> SelectCoinsInternal(
|
||||||
|
StringBuilder log,
|
||||||
|
string coordinatorName,
|
||||||
|
RoundParameters utxoSelectionParameters,
|
||||||
IEnumerable<SmartCoin> coins,
|
IEnumerable<SmartCoin> coins,
|
||||||
IEnumerable<PendingPayment> pendingPayments,
|
IEnumerable<PendingPayment> pendingPayments,
|
||||||
int maxCoins,
|
int maxCoins,
|
||||||
@@ -392,14 +401,14 @@ public class BTCPayCoinjoinCoinSelector : IRoundCoinSelector
|
|||||||
|
|
||||||
if (chance <= rand)
|
if (chance <= rand)
|
||||||
{
|
{
|
||||||
if (_wallet.MinimumDenominationAmount is not null &&
|
var minDenomAmount = Math.Min(_wallet.MinimumDenominationAmount ?? 0, _wallet.AllowedDenominations?.Any() is true? _wallet.AllowedDenominations.Min(): 0);
|
||||||
Money.Coins(solution.LeftoverValue).Satoshi < _wallet.MinimumDenominationAmount)
|
if (minDenomAmount > 0 &&
|
||||||
|
Money.Coins(solution.LeftoverValue).Satoshi < minDenomAmount)
|
||||||
{
|
{
|
||||||
_wallet.LogDebug(
|
log.AppendLine($"leftover value {solution.LeftoverValue} is less than minimum denomination amount {minDenomAmount} so we will try to add more coins");
|
||||||
$"coin selection: leftover value {solution.LeftoverValue} is less than minimum denomination amount {_wallet.MinimumDenominationAmount} so we will try to add more coins");
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
_wallet.LogDebug($"coin selection: no payments left but at {solution.Coins.Count()} coins. random chance to add another coin if: {chance} > {rand} (random 0-100) continue: {chance > rand}");
|
log.AppendLine($"no payments left but at {solution.Coins.Count()} coins. random chance to add another coin if: {chance} > {rand} (random 0-100) continue: {chance > rand}");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -473,9 +482,9 @@ public enum AnonsetType
|
|||||||
|
|
||||||
public class SubsetSolution
|
public class SubsetSolution
|
||||||
{
|
{
|
||||||
private readonly UtxoSelectionParameters _utxoSelectionParameters;
|
private readonly RoundParameters _utxoSelectionParameters;
|
||||||
|
|
||||||
public SubsetSolution(int totalPaymentsGross, IWallet wallet, UtxoSelectionParameters utxoSelectionParameters)
|
public SubsetSolution(int totalPaymentsGross, IWallet wallet, RoundParameters utxoSelectionParameters)
|
||||||
{
|
{
|
||||||
_utxoSelectionParameters = utxoSelectionParameters;
|
_utxoSelectionParameters = utxoSelectionParameters;
|
||||||
TotalPaymentsGross = totalPaymentsGross;
|
TotalPaymentsGross = totalPaymentsGross;
|
||||||
|
|||||||
@@ -2,6 +2,8 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Security.Cryptography;
|
||||||
|
using System.Text;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using BTCPayServer.Client.Models;
|
using BTCPayServer.Client.Models;
|
||||||
@@ -21,6 +23,7 @@ using NBXplorer.DerivationStrategy;
|
|||||||
using NBXplorer.Models;
|
using NBXplorer.Models;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using Newtonsoft.Json.Linq;
|
using Newtonsoft.Json.Linq;
|
||||||
|
using WabiSabi.Crypto.Randomness;
|
||||||
using WalletWasabi.Blockchain.Analysis;
|
using WalletWasabi.Blockchain.Analysis;
|
||||||
using WalletWasabi.Blockchain.Analysis.Clustering;
|
using WalletWasabi.Blockchain.Analysis.Clustering;
|
||||||
using WalletWasabi.Blockchain.Keys;
|
using WalletWasabi.Blockchain.Keys;
|
||||||
@@ -69,6 +72,8 @@ public class BTCPayWallet : IWallet, IDestinationProvider
|
|||||||
StoreRepository storeRepository,
|
StoreRepository storeRepository,
|
||||||
IMemoryCache memoryCache)
|
IMemoryCache memoryCache)
|
||||||
{
|
{
|
||||||
|
WalletId = new WalletWasabi.Wallets.WalletId(new Guid(SHA256.HashData(Encoding.UTF8.GetBytes(storeId)).Take(16)
|
||||||
|
.ToArray()));
|
||||||
KeyChain = keyChain;
|
KeyChain = keyChain;
|
||||||
_walletRepository = walletRepository;
|
_walletRepository = walletRepository;
|
||||||
_btcPayNetworkProvider = btcPayNetworkProvider;
|
_btcPayNetworkProvider = btcPayNetworkProvider;
|
||||||
@@ -88,8 +93,8 @@ public class BTCPayWallet : IWallet, IDestinationProvider
|
|||||||
}
|
}
|
||||||
|
|
||||||
public string StoreId { get; set; }
|
public string StoreId { get; set; }
|
||||||
public List<(DateTimeOffset time, Microsoft.Extensions.Logging.LogLevel level , string message)> LastLogs { get; private set; } = new();
|
public List<(DateTimeOffset time, Microsoft.Extensions.Logging.LogLevel level, string coordinator, string message)> LastLogs { get; private set; } = new();
|
||||||
public void Log(LogLevel logLevel, string logMessage, string callerFilePath = "", string callerMemberName = "",
|
public void Log(LogLevel logLevel, string logMessage, string coordinator, string callerFilePath = "", string callerMemberName = "",
|
||||||
int callerLineNumber = -1)
|
int callerLineNumber = -1)
|
||||||
{
|
{
|
||||||
var ll = logLevel switch
|
var ll = logLevel switch
|
||||||
@@ -103,14 +108,20 @@ public class BTCPayWallet : IWallet, IDestinationProvider
|
|||||||
_ => throw new ArgumentOutOfRangeException(nameof(logLevel))
|
_ => throw new ArgumentOutOfRangeException(nameof(logLevel))
|
||||||
};
|
};
|
||||||
if(LastLogs.FirstOrDefault().message != logMessage)
|
if(LastLogs.FirstOrDefault().message != logMessage)
|
||||||
LastLogs.Insert(0, (DateTimeOffset.Now, ll, logMessage) );
|
LastLogs.Insert(0, (DateTimeOffset.Now, ll, coordinator, logMessage) );
|
||||||
if (LastLogs.Count >= 500)
|
if (LastLogs.Count >= 500)
|
||||||
LastLogs.RemoveLast();
|
LastLogs.RemoveLast();
|
||||||
|
if (coordinator != null)
|
||||||
|
{
|
||||||
|
logMessage = $"[{coordinator}] {logMessage}";
|
||||||
|
}
|
||||||
_logger.Log(ll, logMessage, callerFilePath, callerMemberName, callerLineNumber);
|
_logger.Log(ll, logMessage, callerFilePath, callerMemberName, callerLineNumber);
|
||||||
}
|
}
|
||||||
|
|
||||||
public string WalletName => StoreId;
|
public string WalletName => StoreId;
|
||||||
|
|
||||||
|
public WalletWasabi.Wallets.WalletId WalletId { get; }
|
||||||
|
|
||||||
public bool IsUnderPlebStop => !WabisabiStoreSettings.Active;
|
public bool IsUnderPlebStop => !WabisabiStoreSettings.Active;
|
||||||
|
|
||||||
bool IWallet.IsMixable(string coordinator)
|
bool IWallet.IsMixable(string coordinator)
|
||||||
@@ -122,6 +133,10 @@ public class BTCPayWallet : IWallet, IDestinationProvider
|
|||||||
|
|
||||||
public IKeyChain KeyChain { get; }
|
public IKeyChain KeyChain { get; }
|
||||||
public IDestinationProvider DestinationProvider => this;
|
public IDestinationProvider DestinationProvider => this;
|
||||||
|
public OutputProvider GetOutputProvider(string coordinatorName)
|
||||||
|
{
|
||||||
|
return new OutputProvider(this, SecureRandom.Instance);
|
||||||
|
}
|
||||||
|
|
||||||
public int AnonScoreTarget => WabisabiStoreSettings.PlebMode? 5: WabisabiStoreSettings.AnonymitySetTarget;
|
public int AnonScoreTarget => WabisabiStoreSettings.PlebMode? 5: WabisabiStoreSettings.AnonymitySetTarget;
|
||||||
public ConsolidationModeType ConsolidationMode =>
|
public ConsolidationModeType ConsolidationMode =>
|
||||||
@@ -141,6 +156,9 @@ public class BTCPayWallet : IWallet, IDestinationProvider
|
|||||||
public bool BatchPayments => WabisabiStoreSettings.PlebMode || WabisabiStoreSettings.BatchPayments;
|
public bool BatchPayments => WabisabiStoreSettings.PlebMode || WabisabiStoreSettings.BatchPayments;
|
||||||
public long? MinimumDenominationAmount => WabisabiStoreSettings.PlebMode? 10000 : WabisabiStoreSettings.MinimumDenominationAmount;
|
public long? MinimumDenominationAmount => WabisabiStoreSettings.PlebMode? 10000 : WabisabiStoreSettings.MinimumDenominationAmount;
|
||||||
|
|
||||||
|
public long[]? AllowedDenominations =>
|
||||||
|
WabisabiStoreSettings.PlebMode ? null : WabisabiStoreSettings.AllowedDenominations;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public async Task<IWallet.MixingReason[]> ShouldMix(string coordinatorName, bool? isLowFee = null,
|
public async Task<IWallet.MixingReason[]> ShouldMix(string coordinatorName, bool? isLowFee = null,
|
||||||
@@ -217,8 +235,8 @@ public class BTCPayWallet : IWallet, IDestinationProvider
|
|||||||
|
|
||||||
public double GetPrivacyPercentage(CoinsView coins, int privateThreshold)
|
public double GetPrivacyPercentage(CoinsView coins, int privateThreshold)
|
||||||
{
|
{
|
||||||
var privateAmount = coins.FilterBy(x => x.IsPrivate(this)).TotalAmount();
|
var privateAmount = new CoinsView(coins.Where(x => x.IsPrivate(this))).TotalAmount();
|
||||||
var normalAmount = coins.FilterBy(x => !x.IsPrivate(this)).TotalAmount();
|
var normalAmount = new CoinsView(coins.Where(x => !x.IsPrivate(this))).TotalAmount();
|
||||||
|
|
||||||
var privateDecimalAmount = privateAmount.ToDecimal(MoneyUnit.BTC);
|
var privateDecimalAmount = privateAmount.ToDecimal(MoneyUnit.BTC);
|
||||||
var normalDecimalAmount = normalAmount.ToDecimal(MoneyUnit.BTC);
|
var normalDecimalAmount = normalAmount.ToDecimal(MoneyUnit.BTC);
|
||||||
@@ -339,7 +357,7 @@ public class BTCPayWallet : IWallet, IDestinationProvider
|
|||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
this.LogError($"Could not compute coin candidate: {e.Message}");
|
this.LogError(coordinatorName,$"Could not compute coin candidate: {e.Message}");
|
||||||
return Array.Empty<SmartCoin>();
|
return Array.Empty<SmartCoin>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -543,7 +561,7 @@ public class BTCPayWallet : IWallet, IDestinationProvider
|
|||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
this.LogError($"Failed to analyze anonsets of tx {smartTx.GetHash()}");
|
this.LogError(coordinatorName,$"Failed to analyze anonsets of tx {smartTx.GetHash()}");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -649,7 +667,7 @@ public class BTCPayWallet : IWallet, IDestinationProvider
|
|||||||
|
|
||||||
stopwatch.Stop();
|
stopwatch.Stop();
|
||||||
|
|
||||||
this.LogInfo($"Registered coinjoin result for {StoreId} in {stopwatch.Elapsed}");
|
this.LogInfo(coordinatorName,$"Registered coinjoin result for {StoreId} in {stopwatch.Elapsed}");
|
||||||
_memoryCache.Remove(WabisabiService.GetCacheKey(StoreId) + "cjhistory");
|
_memoryCache.Remove(WabisabiService.GetCacheKey(StoreId) + "cjhistory");
|
||||||
|
|
||||||
break;
|
break;
|
||||||
@@ -657,7 +675,7 @@ public class BTCPayWallet : IWallet, IDestinationProvider
|
|||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
|
|
||||||
this.LogError( "Could not save coinjoin progress! " + e.Message);
|
this.LogError(coordinatorName,"Could not save coinjoin progress! " + e.Message);
|
||||||
// ignored
|
// ignored
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -691,7 +709,7 @@ public async Task<IEnumerable<IDestination>> GetNextDestinationsAsync(int count,
|
|||||||
_btcPayWallet.ReserveAddressAsync(StoreId ,DerivationScheme, "coinjoin"))).ContinueWith(task => task.Result.Select(information => information.Address));
|
_btcPayWallet.ReserveAddressAsync(StoreId ,DerivationScheme, "coinjoin"))).ContinueWith(task => task.Result.Select(information => information.Address));
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<IEnumerable<PendingPayment>> GetPendingPaymentsAsync( UtxoSelectionParameters roundParameters)
|
public async Task<IEnumerable<PendingPayment>> GetPendingPaymentsAsync(RoundParameters roundParameters)
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|
||||||
@@ -715,11 +733,6 @@ public async Task<IEnumerable<IDestination>> GetNextDestinationsAsync(int count,
|
|||||||
|
|
||||||
var payoutBlob = data.GetBlob(_btcPayNetworkJsonSerializerSettings);
|
var payoutBlob = data.GetBlob(_btcPayNetworkJsonSerializerSettings);
|
||||||
var value = new Money(payoutBlob.CryptoAmount.Value, MoneyUnit.BTC);
|
var value = new Money(payoutBlob.CryptoAmount.Value, MoneyUnit.BTC);
|
||||||
if (!roundParameters.AllowedOutputAmounts.Contains(value) ||
|
|
||||||
!roundParameters.AllowedOutputScriptTypes.Contains(bitcoinLikeClaimDestination.Address.ScriptPubKey.GetScriptType()))
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return new PendingPayment()
|
return new PendingPayment()
|
||||||
{
|
{
|
||||||
Identifier = data.Id,
|
Identifier = data.Id,
|
||||||
@@ -738,9 +751,9 @@ public async Task<IEnumerable<IDestination>> GetNextDestinationsAsync(int count,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<ScriptType> GetScriptTypeAsync()
|
public Task<ScriptType[]> GetScriptTypeAsync()
|
||||||
{
|
{
|
||||||
return Task.FromResult(DerivationScheme.GetDerivation(0).ScriptPubKey.GetScriptType());
|
return Task.FromResult(new []{DerivationScheme.GetDerivation(0).ScriptPubKey.GetScriptType()});
|
||||||
}
|
}
|
||||||
|
|
||||||
private Action<(uint256 roundId, uint256 transactionId, int outputIndex)> PaymentSucceeded(string payoutId)
|
private Action<(uint256 roundId, uint256 transactionId, int outputIndex)> PaymentSucceeded(string payoutId)
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ using WalletWasabi.WabiSabi;
|
|||||||
using WalletWasabi.WabiSabi.Backend;
|
using WalletWasabi.WabiSabi.Backend;
|
||||||
using WalletWasabi.WabiSabi.Backend.Rounds.CoinJoinStorage;
|
using WalletWasabi.WabiSabi.Backend.Rounds.CoinJoinStorage;
|
||||||
using WalletWasabi.WabiSabi.Backend.Statistics;
|
using WalletWasabi.WabiSabi.Backend.Statistics;
|
||||||
|
using WalletWasabi.WabiSabi.Client;
|
||||||
using WalletWasabi.WabiSabi.Models;
|
using WalletWasabi.WabiSabi.Models;
|
||||||
using WalletWasabi.WebClients.Wasabi;
|
using WalletWasabi.WebClients.Wasabi;
|
||||||
|
|
||||||
@@ -266,7 +267,14 @@ public class WabisabiCoordinatorService : PeriodicRunner
|
|||||||
{
|
{
|
||||||
instance.WasabiCoordinatorStatusFetcher.OverrideConnected = null;
|
instance.WasabiCoordinatorStatusFetcher.OverrideConnected = null;
|
||||||
}
|
}
|
||||||
_instanceManager.AddCoordinator("Local Coordinator", "local", _ => null, cachedSettings.TermsConditions, cachedSettings.CoordinatorDescription);
|
|
||||||
|
var coinjoinConfig = new CoinJoinConfiguration(WabiSabiCoordinator.Config.CoordinatorIdentifier, 10m, 100m);
|
||||||
|
_instanceManager.AddCoordinator("Local Coordinator",
|
||||||
|
"local", _ => null,
|
||||||
|
cachedSettings.TermsConditions,
|
||||||
|
cachedSettings.CoordinatorDescription,
|
||||||
|
coinjoinConfig
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task StopAsync(CancellationToken cancellationToken)
|
public async Task StopAsync(CancellationToken cancellationToken)
|
||||||
|
|||||||
@@ -49,4 +49,5 @@ public class DiscoveredCoordinator
|
|||||||
public string Name { get; set; }
|
public string Name { get; set; }
|
||||||
public string Relay { get; set; }
|
public string Relay { get; set; }
|
||||||
public string Description { get; set; }
|
public string Description { get; set; }
|
||||||
|
public string? CoinjoinIdentifier { get; set; }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -371,7 +371,7 @@ updateInProgressAnimation(myChart);
|
|||||||
|
|
||||||
RoundState currentRound = null;
|
RoundState currentRound = null;
|
||||||
CoinJoinTracker tracker = null;
|
CoinJoinTracker tracker = null;
|
||||||
if (coordinator.CoinJoinManager.TrackedCoinJoins?.TryGetValue(wallet.WalletName, out tracker) is true &&
|
if (coordinator.CoinJoinManager.TrackedCoinJoins?.TryGetValue(wallet.WalletId, out tracker) is true &&
|
||||||
tracker?.CoinJoinClient?.CurrentRoundId is { } &&
|
tracker?.CoinJoinClient?.CurrentRoundId is { } &&
|
||||||
tracker?.CoinJoinClient?.RoundStatusUpdater?.RoundStates?.TryGetValue(tracker?.CoinJoinClient?.CurrentRoundId, out currentRound) is true)
|
tracker?.CoinJoinClient?.RoundStatusUpdater?.RoundStates?.TryGetValue(tracker?.CoinJoinClient?.CurrentRoundId, out currentRound) is true)
|
||||||
{
|
{
|
||||||
@@ -397,7 +397,7 @@ updateInProgressAnimation(myChart);
|
|||||||
else if (coordinator.WasabiCoordinatorStatusFetcher.Connected)
|
else if (coordinator.WasabiCoordinatorStatusFetcher.Connected)
|
||||||
{
|
{
|
||||||
var roundParameters = coordinator.RoundStateUpdater.RoundStates.LastOrDefault(pair => pair.Value.BlameOf == uint256.Zero).Value?.CoinjoinState.Parameters;
|
var roundParameters = coordinator.RoundStateUpdater.RoundStates.LastOrDefault(pair => pair.Value.BlameOf == uint256.Zero).Value?.CoinjoinState.Parameters;
|
||||||
coordinator.CoinJoinManager.TrackedWallets.TryGetValue(wallet.WalletName, out var trackedWallet);
|
coordinator.CoinJoinManager.TrackedWallets.TryGetValue(wallet.WalletId, out var trackedWallet);
|
||||||
if(trackedWallet is {} && setting.RoundWhenEnabled is not null && roundParameters is not null && !BTCPayWallet.IsRoundOk(roundParameters, setting))
|
if(trackedWallet is {} && setting.RoundWhenEnabled is not null && roundParameters is not null && !BTCPayWallet.IsRoundOk(roundParameters, setting))
|
||||||
{
|
{
|
||||||
<a asp-controller="WabisabiStore" asp-action="UpdateWabisabiStoreSettings" asp-route-storeId="@storeId" class="h6 text-danger">
|
<a asp-controller="WabisabiStore" asp-action="UpdateWabisabiStoreSettings" asp-route-storeId="@storeId" class="h6 text-danger">
|
||||||
@@ -416,7 +416,7 @@ updateInProgressAnimation(myChart);
|
|||||||
@{
|
@{
|
||||||
if (coordinator.CoinPrison is not null)
|
if (coordinator.CoinPrison is not null)
|
||||||
{
|
{
|
||||||
var bannedCoins = coins.Where(coin => coordinator.CoinPrison.TryGetOrRemoveBannedCoin(coin.Outpoint, out _));
|
var bannedCoins = coins.Where(coin => coordinator.CoinPrison.TryGetOrRemoveBannedCoin(coin, out _));
|
||||||
@if (bannedCoins.Any())
|
@if (bannedCoins.Any())
|
||||||
{
|
{
|
||||||
<div class="text-muted">@bannedCoins.Count() banned coins(for disrupting rounds)</div>
|
<div class="text-muted">@bannedCoins.Count() banned coins(for disrupting rounds)</div>
|
||||||
@@ -520,6 +520,7 @@ updateInProgressAnimation(myChart);
|
|||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Time</th>
|
<th>Time</th>
|
||||||
|
<th>Coordinator</th>
|
||||||
<th>Message</th>
|
<th>Message</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
@@ -531,6 +532,9 @@ updateInProgressAnimation(myChart);
|
|||||||
<td>
|
<td>
|
||||||
<small class="text-muted" data-timeago-unixms="@evt.time.ToUnixTimeMilliseconds()">@evt.time.ToTimeAgo()</small>
|
<small class="text-muted" data-timeago-unixms="@evt.time.ToUnixTimeMilliseconds()">@evt.time.ToTimeAgo()</small>
|
||||||
</td>
|
</td>
|
||||||
|
<td>
|
||||||
|
<pre>@evt.coordinator</pre>
|
||||||
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<pre>@evt.message</pre>
|
<pre>@evt.message</pre>
|
||||||
</td>
|
</td>
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
@using BTCPayServer.Services.Stores
|
@using BTCPayServer.Services.Stores
|
||||||
@using Microsoft.AspNetCore.Mvc.TagHelpers
|
@using Microsoft.AspNetCore.Mvc.TagHelpers
|
||||||
@using WalletWasabi.Backend.Controllers
|
@using WalletWasabi.Backend.Controllers
|
||||||
|
@using WalletWasabi.Blockchain.Analysis
|
||||||
@using WalletWasabi.Wallets
|
@using WalletWasabi.Wallets
|
||||||
@model BTCPayServer.Plugins.Wabisabi.WabisabiStoreSettings
|
@model BTCPayServer.Plugins.Wabisabi.WabisabiStoreSettings
|
||||||
@inject WabisabiCoordinatorClientInstanceManager WabisabiCoordinatorClientInstanceManager
|
@inject WabisabiCoordinatorClientInstanceManager WabisabiCoordinatorClientInstanceManager
|
||||||
@@ -96,14 +97,14 @@
|
|||||||
</style>
|
</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)
|
@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">
|
<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>
|
<h4 class="d-none pt-4">Settings cannot be changed while active</h4>
|
||||||
|
|
||||||
</div>
|
</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">
|
||||||
<input class="form-check-input plebModeRadio"
|
<input class="form-check-input plebModeRadio"
|
||||||
@@ -123,8 +124,8 @@
|
|||||||
<p class="text-muted">The world is broken and I need to be vigilant about my bitcoin practices.</p>
|
<p class="text-muted">The world is broken and I need to be vigilant about my bitcoin practices.</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div id="advanced" class="@(Model.PlebMode ? "d-none" : "")">
|
<div id="advanced" class="@(Model.PlebMode ? "d-none" : "")">
|
||||||
<button type="submit" name="command" value="reset" class="btn btn-link">Reset to defaults </button>
|
<button type="submit" name="command" value="reset" class="btn btn-link">Reset to defaults </button>
|
||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
@@ -201,12 +202,32 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-sm-12 col-md-6">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
|
|
||||||
<label asp-for="MinimumDenominationAmount" class="form-label">Minimum denomination (in sats)</label>
|
<label asp-for="MinimumDenominationAmount" class="form-label">Minimum denomination (in sats)</label>
|
||||||
<input type="number" class="form-control" asp-for="MinimumDenominationAmount" placeholder="sats" min="0">
|
<input type="number" class="form-control" asp-for="MinimumDenominationAmount" placeholder="sats" min="0">
|
||||||
<p class="text-muted">Do no use any of the standard denominations below this amount (creates change (which will get remixed) but prevent tiny utxos)</p>
|
<p class="text-muted">Do no use any of the standard denominations below this amount (creates change (which will get remixed) but prevent tiny utxos)</p>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-sm-12 col-md-6">
|
||||||
|
<div class="form-group">
|
||||||
|
|
||||||
|
<label asp-for="AllowedDenominations" class="form-label">Allowed denominations (in sats)</label>
|
||||||
|
<select asp-for="AllowedDenominations" class="form-select" multiple="multiple">
|
||||||
|
@foreach (var denomination in BlockchainAnalyzer.StdDenoms)
|
||||||
|
{
|
||||||
|
<option value="@denomination">@denomination sats</option>
|
||||||
|
}
|
||||||
|
</select>
|
||||||
|
<p>DO NOT USE UNLESS YOU KNOW WHAT YOU ARE DOING</p>
|
||||||
|
<p class="text-muted">Only generate outputs of these denoms. Leave blank to ignore. Generates change. You must match other user's settings to gain any anonymity.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="form-group form-check">
|
<div class="form-group form-check">
|
||||||
<label asp-for="RedCoinIsolation" class="form-check-label">Cautious coinjoin entry mode </label>
|
<label asp-for="RedCoinIsolation" class="form-check-label">Cautious coinjoin entry mode </label>
|
||||||
<input asp-for="RedCoinIsolation" type="checkbox" class="form-check-input"/>
|
<input asp-for="RedCoinIsolation" type="checkbox" class="form-check-input"/>
|
||||||
@@ -307,7 +328,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@for (var index = 0; index < Model.Settings.Count; index++)
|
@for (var index = 0; index < Model.Settings.Count; index++)
|
||||||
|
|||||||
@@ -104,7 +104,7 @@ public class WabisabiCoordinatorClientInstanceManager:IHostedService
|
|||||||
|
|
||||||
|
|
||||||
public void AddCoordinator(string displayName, string name,
|
public void AddCoordinator(string displayName, string name,
|
||||||
Func<IServiceProvider, Uri> fetcher, string termsConditions = null, string description = null)
|
Func<IServiceProvider, Uri> fetcher, string termsConditions = null, string description = null, CoinJoinConfiguration configuration = null)
|
||||||
{
|
{
|
||||||
if (termsConditions is null && name == "zksnacks")
|
if (termsConditions is null && name == "zksnacks")
|
||||||
{
|
{
|
||||||
@@ -171,7 +171,7 @@ public class WabisabiCoordinatorClientInstanceManager:IHostedService
|
|||||||
var instance = new WabisabiCoordinatorClientInstance(
|
var instance = new WabisabiCoordinatorClientInstance(
|
||||||
displayName,
|
displayName,
|
||||||
name, url is null? null: new Uri(url), wasabiHttpClientFactory,_provider.GetService<ILoggerFactory>(), _provider, UTXOLocker,
|
name, url is null? null: new Uri(url), wasabiHttpClientFactory,_provider.GetService<ILoggerFactory>(), _provider, UTXOLocker,
|
||||||
_provider.GetService<WalletProvider>(), termsConditions, description);
|
_provider.GetService<WalletProvider>(), termsConditions, description, configuration);
|
||||||
if (HostedServices.TryAdd(instance.CoordinatorName, instance))
|
if (HostedServices.TryAdd(instance.CoordinatorName, instance))
|
||||||
{
|
{
|
||||||
if(started)
|
if(started)
|
||||||
@@ -299,8 +299,7 @@ public class WabisabiCoordinatorClientInstance:IHostedService
|
|||||||
ILoggerFactory loggerFactory,
|
ILoggerFactory loggerFactory,
|
||||||
IServiceProvider serviceProvider,
|
IServiceProvider serviceProvider,
|
||||||
IUTXOLocker utxoLocker,
|
IUTXOLocker utxoLocker,
|
||||||
WalletProvider walletProvider, string termsConditions, string description, string coordinatorIdentifier = "CoinJoinCoordinatorIdentifier"
|
WalletProvider walletProvider, string termsConditions, string description, CoinJoinConfiguration config)
|
||||||
)
|
|
||||||
{
|
{
|
||||||
|
|
||||||
_utxoLocker = utxoLocker;
|
_utxoLocker = utxoLocker;
|
||||||
@@ -348,7 +347,7 @@ public class WabisabiCoordinatorClientInstance:IHostedService
|
|||||||
|
|
||||||
CoinJoinManager = new CoinJoinManager(coordinatorName, WalletProvider, RoundStateUpdater,
|
CoinJoinManager = new CoinJoinManager(coordinatorName, WalletProvider, RoundStateUpdater,
|
||||||
WasabiHttpClientFactory,
|
WasabiHttpClientFactory,
|
||||||
WasabiCoordinatorStatusFetcher, coordinatorIdentifier, CoinPrison);
|
WasabiCoordinatorStatusFetcher, config, CoinPrison);
|
||||||
CoinJoinManager.StatusChanged += OnStatusChanged;
|
CoinJoinManager.StatusChanged += OnStatusChanged;
|
||||||
|
|
||||||
_hostedServices.Register<RoundStateUpdater>(() => RoundStateUpdater, "RoundStateUpdater");
|
_hostedServices.Register<RoundStateUpdater>(() => RoundStateUpdater, "RoundStateUpdater");
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ using WalletWasabi.Backend.Controllers;
|
|||||||
using WalletWasabi.Blockchain.TransactionBuilding;
|
using WalletWasabi.Blockchain.TransactionBuilding;
|
||||||
using WalletWasabi.Blockchain.TransactionOutputs;
|
using WalletWasabi.Blockchain.TransactionOutputs;
|
||||||
using WalletWasabi.Extensions;
|
using WalletWasabi.Extensions;
|
||||||
|
using WalletWasabi.WabiSabi.Client;
|
||||||
|
|
||||||
namespace BTCPayServer.Plugins.Wabisabi
|
namespace BTCPayServer.Plugins.Wabisabi
|
||||||
{
|
{
|
||||||
@@ -244,7 +245,8 @@ namespace BTCPayServer.Plugins.Wabisabi
|
|||||||
{
|
{
|
||||||
coordSettings.DiscoveredCoordinators.Add(viewModel);
|
coordSettings.DiscoveredCoordinators.Add(viewModel);
|
||||||
await _wabisabiCoordinatorService.UpdateSettings(coordSettings);
|
await _wabisabiCoordinatorService.UpdateSettings(coordSettings);
|
||||||
_instanceManager.AddCoordinator(viewModel.Name, viewModel.Name, provider => viewModel.Uri, viewModel.Description);
|
var config = new CoinJoinConfiguration();
|
||||||
|
_instanceManager.AddCoordinator(viewModel.Name, viewModel.Name, provider => viewModel.Uri, null,viewModel.Description, config);
|
||||||
|
|
||||||
TempData["SuccessMessage"] = $"Coordinator {viewModel.Name } added and started";
|
TempData["SuccessMessage"] = $"Coordinator {viewModel.Name } added and started";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ public class WabisabiStoreSettings
|
|||||||
public CrossMixMode CrossMixBetweenCoordinatorsMode { get; set; } = CrossMixMode.WhenFree;
|
public CrossMixMode CrossMixBetweenCoordinatorsMode { get; set; } = CrossMixMode.WhenFree;
|
||||||
public int FeeRateMedianTimeFrameHours { get; set; }
|
public int FeeRateMedianTimeFrameHours { get; set; }
|
||||||
public long MinimumDenominationAmount { get; set; } = 10000;
|
public long MinimumDenominationAmount { get; set; } = 10000;
|
||||||
|
public long[] AllowedDenominations { get; set; }
|
||||||
public int ExplicitHighestFeeTarget { get; set; } = BTCPayWallet.DefaultExplicitHighestFeeTarget;
|
public int ExplicitHighestFeeTarget { get; set; } = BTCPayWallet.DefaultExplicitHighestFeeTarget;
|
||||||
public int LowFeeTarget { get; set; } = BTCPayWallet.DefaultLowFeeTarget;
|
public int LowFeeTarget { get; set; } = BTCPayWallet.DefaultLowFeeTarget;
|
||||||
|
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ namespace BTCPayServer.Plugins.Wabisabi;
|
|||||||
|
|
||||||
public class WalletProvider : PeriodicRunner,IWalletProvider
|
public class WalletProvider : PeriodicRunner,IWalletProvider
|
||||||
{
|
{
|
||||||
private Dictionary<string, WabisabiStoreSettings>? _cachedSettings;
|
private ConcurrentDictionary<string, WabisabiStoreSettings>? _cachedSettings;
|
||||||
private readonly IServiceProvider _serviceProvider;
|
private readonly IServiceProvider _serviceProvider;
|
||||||
private readonly StoreRepository _storeRepository;
|
private readonly StoreRepository _storeRepository;
|
||||||
private readonly IExplorerClientProvider _explorerClientProvider;
|
private readonly IExplorerClientProvider _explorerClientProvider;
|
||||||
@@ -255,8 +255,8 @@ public class WalletProvider : PeriodicRunner,IWalletProvider
|
|||||||
{
|
{
|
||||||
Task.Run(async () =>
|
Task.Run(async () =>
|
||||||
{
|
{
|
||||||
_cachedSettings =
|
_cachedSettings = new ConcurrentDictionary<string, WabisabiStoreSettings>(
|
||||||
await _storeRepository.GetSettingsAsync<WabisabiStoreSettings>(nameof(WabisabiStoreSettings));
|
(await _storeRepository.GetSettingsAsync<WabisabiStoreSettings>(nameof(WabisabiStoreSettings))));
|
||||||
_initialLoad.SetResult();
|
_initialLoad.SetResult();
|
||||||
}, cancellationToken);
|
}, cancellationToken);
|
||||||
_disposables.Add(_eventAggregator.SubscribeAsync<StoreRemovedEvent>(async @event =>
|
_disposables.Add(_eventAggregator.SubscribeAsync<StoreRemovedEvent>(async @event =>
|
||||||
|
|||||||
Submodule submodules/walletwasabi updated: 10b0aab0fe...3f52c3f7b7
Reference in New Issue
Block a user