ws update

This commit is contained in:
Kukks
2024-06-26 09:14:07 +02:00
parent df7c5e7cb7
commit 0969ebb909
11 changed files with 315 additions and 257 deletions

View File

@@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using NBitcoin;
using WalletWasabi.Blockchain.Analysis;
@@ -17,6 +18,7 @@ using WalletWasabi.Models;
using WalletWasabi.WabiSabi;
using WalletWasabi.WabiSabi.Backend.Rounds;
using WalletWasabi.WabiSabi.Client;
using WalletWasabi.WabiSabi.Client.CoinJoin.Client;
using WalletWasabi.WabiSabi.Client.StatusChangedEvents;
using WalletWasabi.WabiSabi.Models;
using WalletWasabi.WabiSabi.Models.MultipartyTransaction;
@@ -35,28 +37,24 @@ public class BTCPayCoinjoinCoinSelector : IRoundCoinSelector
}
public async
Task<(
ImmutableList<SmartCoin> selected,
Func<IEnumerable<AliceClient>, Task<bool>> acceptableRegistered,
Func<
ImmutableArray<AliceClient>,
(IEnumerable<TxOut> outputTxOuts, Dictionary<TxOut, PendingPayment> batchedPayments),
TransactionWithPrecomputedData,
RoundState,
Task<bool>> acceptableOutputs
)>
Task<(ImmutableList<SmartCoin> selected, Func<IEnumerable<AliceClient>, Task<bool>> acceptableRegistered, Func<
ImmutableArray<AliceClient>, (IEnumerable<TxOut> outputTxOuts, Dictionary<TxOut, PendingPayment>
batchedPayments), TransactionWithPrecomputedData, RoundState, Task<bool>> acceptableOutputs)>
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
.Where(coin => utxoSelectionParameters.AllowedInputScriptTypes.Contains(coin.ScriptType))
.Where(coin => utxoSelectionParameters.AllowedInputAmounts.Contains(coin.Amount))
.Where(coin => roundParameters.AllowedInputTypes.Contains(coin.ScriptType))
.Where(coin => roundParameters.AllowedInputAmounts.Contains(coin.Amount))
.Where(coin =>
{
var effV = coin.EffectiveValue(utxoSelectionParameters.MiningFeeRate,
utxoSelectionParameters.CoordinationFeeRate);
var effV = coin.EffectiveValue(roundParameters.MiningFeeRate,
roundParameters.CoordinationFeeRate);
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
return effV > Money.Zero && percentageLeft >= 0.5m;
@@ -69,7 +67,7 @@ public class BTCPayCoinjoinCoinSelector : IRoundCoinSelector
{
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;
}
@@ -78,7 +76,7 @@ public class BTCPayCoinjoinCoinSelector : IRoundCoinSelector
_wallet.WabisabiStoreSettings.CrossMixBetweenCoordinatorsMode ==
WabisabiStoreSettings.CrossMixMode.WhenFree)
{
return coin.Amount <= utxoSelectionParameters.CoordinationFeeRate.PlebsDontPayThreshold;
return coin.Amount <= roundParameters.CoordinationFeeRate.PlebsDontPayThreshold;
}
return false;
@@ -93,7 +91,7 @@ public class BTCPayCoinjoinCoinSelector : IRoundCoinSelector
var payments =
(_wallet.BatchPayments
? await _wallet.DestinationProvider.GetPendingPaymentsAsync(utxoSelectionParameters)
? await _wallet.DestinationProvider.GetPendingPaymentsAsync(roundParameters)
: Array.Empty<PendingPayment>()).ToArray();
var maxPerType = new Dictionary<AnonsetType, int>();
@@ -113,7 +111,7 @@ public class BTCPayCoinjoinCoinSelector : IRoundCoinSelector
maxPerType.TryAdd(AnonsetType.Red, 1);
}
var isLowFee = utxoSelectionParameters.MiningFeeRate.SatoshiPerByte <= _wallet.LowFeeTarget;
var isLowFee = roundParameters.MiningFeeRate.SatoshiPerByte <= _wallet.LowFeeTarget;
var consolidationMode = _wallet.ConsolidationMode switch
{
ConsolidationModeType.Always => true,
@@ -122,19 +120,19 @@ public class BTCPayCoinjoinCoinSelector : IRoundCoinSelector
ConsolidationModeType.WhenLowFeeAndManyUTXO => isLowFee && candidates.Count() > BTCPayWallet.HighAmountOfCoins,
_ => 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())
{
throw new CoinJoinClientException(CoinjoinError.NoCoinsEligibleToMix, "ShouldMix returned false, so we will not mix");
}
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>()
{{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),
maxPerType,
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");
}
_wallet.LogTrace(solution.ToString());
log.AppendLine(solution.ToString());
async Task<bool> AcceptableRegistered(IEnumerable<AliceClient> coins)
{
@@ -183,7 +181,7 @@ public class BTCPayCoinjoinCoinSelector : IRoundCoinSelector
var effectiveSumOfRegisteredCoins = coins.Sum(coin => coin.EffectiveValue);
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;
});
if (!canHandleAPayment)
@@ -204,7 +202,7 @@ public class BTCPayCoinjoinCoinSelector : IRoundCoinSelector
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();
@@ -267,9 +265,20 @@ public class BTCPayCoinjoinCoinSelector : IRoundCoinSelector
}
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<PendingPayment> pendingPayments,
int maxCoins,
@@ -392,14 +401,14 @@ public class BTCPayCoinjoinCoinSelector : IRoundCoinSelector
if (chance <= rand)
{
if (_wallet.MinimumDenominationAmount is not null &&
Money.Coins(solution.LeftoverValue).Satoshi < _wallet.MinimumDenominationAmount)
var minDenomAmount = Math.Min(_wallet.MinimumDenominationAmount ?? 0, _wallet.AllowedDenominations?.Any() is true? _wallet.AllowedDenominations.Min(): 0);
if (minDenomAmount > 0 &&
Money.Coins(solution.LeftoverValue).Satoshi < minDenomAmount)
{
_wallet.LogDebug(
$"coin selection: leftover value {solution.LeftoverValue} is less than minimum denomination amount {_wallet.MinimumDenominationAmount} so we will try to add more coins");
log.AppendLine($"leftover value {solution.LeftoverValue} is less than minimum denomination amount {minDenomAmount} so we will try to add more coins");
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;
}
@@ -473,9 +482,9 @@ public enum AnonsetType
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;
TotalPaymentsGross = totalPaymentsGross;

View File

@@ -2,6 +2,8 @@
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using BTCPayServer.Client.Models;
@@ -21,6 +23,7 @@ using NBXplorer.DerivationStrategy;
using NBXplorer.Models;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using WabiSabi.Crypto.Randomness;
using WalletWasabi.Blockchain.Analysis;
using WalletWasabi.Blockchain.Analysis.Clustering;
using WalletWasabi.Blockchain.Keys;
@@ -69,6 +72,8 @@ public class BTCPayWallet : IWallet, IDestinationProvider
StoreRepository storeRepository,
IMemoryCache memoryCache)
{
WalletId = new WalletWasabi.Wallets.WalletId(new Guid(SHA256.HashData(Encoding.UTF8.GetBytes(storeId)).Take(16)
.ToArray()));
KeyChain = keyChain;
_walletRepository = walletRepository;
_btcPayNetworkProvider = btcPayNetworkProvider;
@@ -88,8 +93,8 @@ public class BTCPayWallet : IWallet, IDestinationProvider
}
public string StoreId { get; set; }
public List<(DateTimeOffset time, Microsoft.Extensions.Logging.LogLevel level , string message)> LastLogs { get; private set; } = new();
public void Log(LogLevel logLevel, string logMessage, string callerFilePath = "", string callerMemberName = "",
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 coordinator, string callerFilePath = "", string callerMemberName = "",
int callerLineNumber = -1)
{
var ll = logLevel switch
@@ -103,14 +108,20 @@ public class BTCPayWallet : IWallet, IDestinationProvider
_ => throw new ArgumentOutOfRangeException(nameof(logLevel))
};
if(LastLogs.FirstOrDefault().message != logMessage)
LastLogs.Insert(0, (DateTimeOffset.Now, ll, logMessage) );
LastLogs.Insert(0, (DateTimeOffset.Now, ll, coordinator, logMessage) );
if (LastLogs.Count >= 500)
LastLogs.RemoveLast();
if (coordinator != null)
{
logMessage = $"[{coordinator}] {logMessage}";
}
_logger.Log(ll, logMessage, callerFilePath, callerMemberName, callerLineNumber);
}
public string WalletName => StoreId;
public WalletWasabi.Wallets.WalletId WalletId { get; }
public bool IsUnderPlebStop => !WabisabiStoreSettings.Active;
bool IWallet.IsMixable(string coordinator)
@@ -122,6 +133,10 @@ public class BTCPayWallet : IWallet, IDestinationProvider
public IKeyChain KeyChain { get; }
public IDestinationProvider DestinationProvider => this;
public OutputProvider GetOutputProvider(string coordinatorName)
{
return new OutputProvider(this, SecureRandom.Instance);
}
public int AnonScoreTarget => WabisabiStoreSettings.PlebMode? 5: WabisabiStoreSettings.AnonymitySetTarget;
public ConsolidationModeType ConsolidationMode =>
@@ -141,6 +156,9 @@ public class BTCPayWallet : IWallet, IDestinationProvider
public bool BatchPayments => WabisabiStoreSettings.PlebMode || WabisabiStoreSettings.BatchPayments;
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,
@@ -217,8 +235,8 @@ public class BTCPayWallet : IWallet, IDestinationProvider
public double GetPrivacyPercentage(CoinsView coins, int privateThreshold)
{
var privateAmount = coins.FilterBy(x => x.IsPrivate(this)).TotalAmount();
var normalAmount = coins.FilterBy(x => !x.IsPrivate(this)).TotalAmount();
var privateAmount = new CoinsView(coins.Where(x => x.IsPrivate(this))).TotalAmount();
var normalAmount = new CoinsView(coins.Where(x => !x.IsPrivate(this))).TotalAmount();
var privateDecimalAmount = privateAmount.ToDecimal(MoneyUnit.BTC);
var normalDecimalAmount = normalAmount.ToDecimal(MoneyUnit.BTC);
@@ -339,7 +357,7 @@ public class BTCPayWallet : IWallet, IDestinationProvider
}
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>();
}
}
@@ -543,7 +561,7 @@ public class BTCPayWallet : IWallet, IDestinationProvider
}
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();
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");
break;
@@ -657,7 +675,7 @@ public class BTCPayWallet : IWallet, IDestinationProvider
catch (Exception e)
{
this.LogError( "Could not save coinjoin progress! " + e.Message);
this.LogError(coordinatorName,"Could not save coinjoin progress! " + e.Message);
// 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));
}
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 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()
{
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)

View File

@@ -33,6 +33,7 @@ using WalletWasabi.WabiSabi;
using WalletWasabi.WabiSabi.Backend;
using WalletWasabi.WabiSabi.Backend.Rounds.CoinJoinStorage;
using WalletWasabi.WabiSabi.Backend.Statistics;
using WalletWasabi.WabiSabi.Client;
using WalletWasabi.WabiSabi.Models;
using WalletWasabi.WebClients.Wasabi;
@@ -266,7 +267,14 @@ public class WabisabiCoordinatorService : PeriodicRunner
{
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)

View File

@@ -49,4 +49,5 @@ public class DiscoveredCoordinator
public string Name { get; set; }
public string Relay { get; set; }
public string Description { get; set; }
public string? CoinjoinIdentifier { get; set; }
}

View File

@@ -371,7 +371,7 @@ updateInProgressAnimation(myChart);
RoundState currentRound = 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?.RoundStatusUpdater?.RoundStates?.TryGetValue(tracker?.CoinJoinClient?.CurrentRoundId, out currentRound) is true)
{
@@ -397,7 +397,7 @@ updateInProgressAnimation(myChart);
else if (coordinator.WasabiCoordinatorStatusFetcher.Connected)
{
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))
{
<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)
{
var bannedCoins = coins.Where(coin => coordinator.CoinPrison.TryGetOrRemoveBannedCoin(coin.Outpoint, out _));
var bannedCoins = coins.Where(coin => coordinator.CoinPrison.TryGetOrRemoveBannedCoin(coin, out _));
@if (bannedCoins.Any())
{
<div class="text-muted">@bannedCoins.Count() banned coins(for disrupting rounds)</div>
@@ -520,6 +520,7 @@ updateInProgressAnimation(myChart);
<thead>
<tr>
<th>Time</th>
<th>Coordinator</th>
<th>Message</th>
</tr>
</thead>
@@ -531,6 +532,9 @@ updateInProgressAnimation(myChart);
<td>
<small class="text-muted" data-timeago-unixms="@evt.time.ToUnixTimeMilliseconds()">@evt.time.ToTimeAgo()</small>
</td>
<td>
<pre>@evt.coordinator</pre>
</td>
<td>
<pre>@evt.message</pre>
</td>

View File

@@ -8,6 +8,7 @@
@using BTCPayServer.Services.Stores
@using Microsoft.AspNetCore.Mvc.TagHelpers
@using WalletWasabi.Backend.Controllers
@using WalletWasabi.Blockchain.Analysis
@using WalletWasabi.Wallets
@model BTCPayServer.Plugins.Wabisabi.WabisabiStoreSettings
@inject WabisabiCoordinatorClientInstanceManager WabisabiCoordinatorClientInstanceManager
@@ -96,14 +97,14 @@
</style>
<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">
<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="form-check">
<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>
</div>
</div>
</div>
<div id="advanced" class="@(Model.PlebMode ? "d-none" : "")">
</div>
<div id="advanced" class="@(Model.PlebMode ? "d-none" : "")">
<button type="submit" name="command" value="reset" class="btn btn-link">Reset to defaults </button>
<div class="row">
@@ -201,12 +202,32 @@
</div>
</div>
</div>
<div class="row">
<div class="col-sm-12 col-md-6">
<div class="form-group">
<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">
<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 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">
<label asp-for="RedCoinIsolation" class="form-check-label">Cautious coinjoin entry mode </label>
<input asp-for="RedCoinIsolation" type="checkbox" class="form-check-input"/>
@@ -307,7 +328,7 @@
</div>
</div>
</div>
</div>
@for (var index = 0; index < Model.Settings.Count; index++)

View File

@@ -104,7 +104,7 @@ public class WabisabiCoordinatorClientInstanceManager:IHostedService
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")
{
@@ -171,7 +171,7 @@ public class WabisabiCoordinatorClientInstanceManager:IHostedService
var instance = new WabisabiCoordinatorClientInstance(
displayName,
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(started)
@@ -299,8 +299,7 @@ public class WabisabiCoordinatorClientInstance:IHostedService
ILoggerFactory loggerFactory,
IServiceProvider serviceProvider,
IUTXOLocker utxoLocker,
WalletProvider walletProvider, string termsConditions, string description, string coordinatorIdentifier = "CoinJoinCoordinatorIdentifier"
)
WalletProvider walletProvider, string termsConditions, string description, CoinJoinConfiguration config)
{
_utxoLocker = utxoLocker;
@@ -348,7 +347,7 @@ public class WabisabiCoordinatorClientInstance:IHostedService
CoinJoinManager = new CoinJoinManager(coordinatorName, WalletProvider, RoundStateUpdater,
WasabiHttpClientFactory,
WasabiCoordinatorStatusFetcher, coordinatorIdentifier, CoinPrison);
WasabiCoordinatorStatusFetcher, config, CoinPrison);
CoinJoinManager.StatusChanged += OnStatusChanged;
_hostedServices.Register<RoundStateUpdater>(() => RoundStateUpdater, "RoundStateUpdater");

View File

@@ -27,6 +27,7 @@ using WalletWasabi.Backend.Controllers;
using WalletWasabi.Blockchain.TransactionBuilding;
using WalletWasabi.Blockchain.TransactionOutputs;
using WalletWasabi.Extensions;
using WalletWasabi.WabiSabi.Client;
namespace BTCPayServer.Plugins.Wabisabi
{
@@ -244,7 +245,8 @@ namespace BTCPayServer.Plugins.Wabisabi
{
coordSettings.DiscoveredCoordinators.Add(viewModel);
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";
}

View File

@@ -31,6 +31,7 @@ public class WabisabiStoreSettings
public CrossMixMode CrossMixBetweenCoordinatorsMode { get; set; } = CrossMixMode.WhenFree;
public int FeeRateMedianTimeFrameHours { get; set; }
public long MinimumDenominationAmount { get; set; } = 10000;
public long[] AllowedDenominations { get; set; }
public int ExplicitHighestFeeTarget { get; set; } = BTCPayWallet.DefaultExplicitHighestFeeTarget;
public int LowFeeTarget { get; set; } = BTCPayWallet.DefaultLowFeeTarget;

View File

@@ -28,7 +28,7 @@ namespace BTCPayServer.Plugins.Wabisabi;
public class WalletProvider : PeriodicRunner,IWalletProvider
{
private Dictionary<string, WabisabiStoreSettings>? _cachedSettings;
private ConcurrentDictionary<string, WabisabiStoreSettings>? _cachedSettings;
private readonly IServiceProvider _serviceProvider;
private readonly StoreRepository _storeRepository;
private readonly IExplorerClientProvider _explorerClientProvider;
@@ -255,8 +255,8 @@ public class WalletProvider : PeriodicRunner,IWalletProvider
{
Task.Run(async () =>
{
_cachedSettings =
await _storeRepository.GetSettingsAsync<WabisabiStoreSettings>(nameof(WabisabiStoreSettings));
_cachedSettings = new ConcurrentDictionary<string, WabisabiStoreSettings>(
(await _storeRepository.GetSettingsAsync<WabisabiStoreSettings>(nameof(WabisabiStoreSettings))));
_initialLoad.SetResult();
}, cancellationToken);
_disposables.Add(_eventAggregator.SubscribeAsync<StoreRemovedEvent>(async @event =>