mirror of
https://github.com/aljazceru/BTCPayServerPlugins.git
synced 2025-12-19 08:34:26 +01:00
add coordinator failsafe
This commit is contained in:
@@ -162,6 +162,21 @@ public class BTCPayWallet : IWallet, IDestinationProvider
|
||||
return _coinSelector;
|
||||
}
|
||||
|
||||
public bool IsRoundOk(RoundParameters roundParameters, string coordinatorName)
|
||||
{
|
||||
var coordSettings = WabisabiStoreSettings.Settings.Find(settings => settings.Coordinator == coordinatorName && settings.Enabled);
|
||||
return coordSettings is not null && IsRoundOk(roundParameters, coordSettings);
|
||||
}
|
||||
public static bool IsRoundOk(RoundParameters roundParameters, WabisabiStoreCoordinatorSettings coordSettings)
|
||||
{
|
||||
if (coordSettings.RoundWhenEnabled is null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return roundParameters.CoordinationFeeRate.Rate <= coordSettings.RoundWhenEnabled.CoordinationFeeRate &&
|
||||
roundParameters.CoordinationFeeRate.PlebsDontPayThreshold <= coordSettings.RoundWhenEnabled.PlebsDontPayThreshold &&
|
||||
roundParameters.MinInputCountByRound <= coordSettings.RoundWhenEnabled.MinInputCountByRound;
|
||||
}
|
||||
public async Task<IEnumerable<SmartCoin>> GetCoinjoinCoinCandidatesAsync(string coordinatorName)
|
||||
{
|
||||
try
|
||||
|
||||
@@ -43,8 +43,6 @@
|
||||
</div>
|
||||
<form method="post">
|
||||
@{
|
||||
|
||||
|
||||
var wallet = await WalletProvider.GetWalletAsync(storeId);
|
||||
if (wallet is BTCPayWallet btcPayWallet)
|
||||
{
|
||||
@@ -283,19 +281,39 @@
|
||||
data-bs-toggle="modal" data-bs-target="#config-@s.Coordinator">
|
||||
Coordinator Config
|
||||
</a>
|
||||
@if (Model.Settings[index].RoundWhenEnabled is not null && !BTCPayWallet.IsRoundOk(round, Model.Settings[index]))
|
||||
{
|
||||
<div class="alert alert-danger w-100 mb-0 p-1">Round fees/parameters changed. Coinjoins will not occur unless you accept the new parameters.<button class="btn btn-link alert-link p-0" name="command" type="submit" value="accept-terms:@s.Coordinator"> Accept new terms</button></div>
|
||||
}
|
||||
}
|
||||
|
||||
<a class="px-2 w-100 cursor-pointer"
|
||||
data-bs-toggle="modal" data-bs-target="#terms-@s.Coordinator"
|
||||
style="
|
||||
right: 0;
|
||||
text-align: right;
|
||||
">
|
||||
By enabling this coordinator, you agree to their terms and conditions.
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group form-check form">
|
||||
<input asp-for="Settings[index].Enabled" type="checkbox" class="form-check-input form-control-lg toggle-settings" data-coordinator="@s.Coordinator" disabled="@(!coordinator.WasabiCoordinatorStatusFetcher.Connected)"/>
|
||||
<a class="px-2 position-absolute bottom-0 cursor-pointer"
|
||||
data-bs-toggle="modal" data-bs-target="#terms-@s.Coordinator"
|
||||
style="
|
||||
right: 0;
|
||||
text-align: right;
|
||||
">
|
||||
By enabling this coordinator, you agree to their terms and conditions.
|
||||
</a>
|
||||
@{
|
||||
var canEnable = coordinator.WasabiCoordinatorStatusFetcher.Connected && coordinator.RoundStateUpdater.AnyRound;
|
||||
}
|
||||
<div class="form-group form-check form" data-bs-toggle="tooltip" title="@(!canEnable ? "You cannot enable this coordinator until it is connected and a round has been seen" : string.Empty)">
|
||||
@if (Model.Settings[index].RoundWhenEnabled is not null)
|
||||
{
|
||||
<input type="hidden" asp-for="Settings[index].RoundWhenEnabled.CoordinationFeeRate"/>
|
||||
<input type="hidden" asp-for="Settings[index].RoundWhenEnabled.PlebsDontPayThreshold"/>
|
||||
<input type="hidden" asp-for="Settings[index].RoundWhenEnabled.MinInputCountByRound"/>
|
||||
}
|
||||
|
||||
<input asp-for="Settings[index].Enabled"
|
||||
|
||||
type="checkbox" class="form-check-input form-control-lg toggle-settings"
|
||||
data-coordinator="@s.Coordinator"
|
||||
disabled="@(!Model.Settings[index].Enabled && !canEnable)"/>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
@@ -343,7 +361,7 @@
|
||||
<partial name="_ValidationScriptsPartial"/>
|
||||
}
|
||||
|
||||
<script type="text/javascript" >
|
||||
<script type="text/javascript">
|
||||
|
||||
function handlePlebModeChange(evt){
|
||||
const isPlebMode = evt.target.value === "true";
|
||||
|
||||
@@ -52,10 +52,8 @@ namespace BTCPayServer.Plugins.Wabisabi
|
||||
return res;
|
||||
}
|
||||
|
||||
public async Task SetWabisabiForStore(string storeId, WabisabiStoreSettings wabisabiSettings)
|
||||
public async Task SetWabisabiForStore(string storeId, WabisabiStoreSettings wabisabiSettings, string termsCoord = null)
|
||||
{
|
||||
var paybatching = wabisabiSettings.PlebMode || wabisabiSettings.BatchPayments &&
|
||||
wabisabiSettings.Settings.Any(settings => settings.Enabled);
|
||||
foreach (var setting in wabisabiSettings.Settings)
|
||||
{
|
||||
if (setting.Enabled) continue;
|
||||
@@ -70,7 +68,31 @@ namespace BTCPayServer.Plugins.Wabisabi
|
||||
}
|
||||
else
|
||||
{
|
||||
await _storeRepository.UpdateSetting<WabisabiStoreSettings>(storeId, nameof(WabisabiStoreSettings), wabisabiSettings!);
|
||||
var res = await _storeRepository.GetSettingAsync<WabisabiStoreSettings>(storeId, nameof(WabisabiStoreSettings));
|
||||
foreach (var wabisabiStoreCoordinatorSettings in wabisabiSettings.Settings)
|
||||
{
|
||||
if (!wabisabiStoreCoordinatorSettings.Enabled)
|
||||
{
|
||||
wabisabiStoreCoordinatorSettings.RoundWhenEnabled = null;
|
||||
}else if (
|
||||
(termsCoord == wabisabiStoreCoordinatorSettings.Coordinator ||
|
||||
res?.Settings?.Find(settings =>
|
||||
settings.Coordinator == wabisabiStoreCoordinatorSettings.Coordinator)?.RoundWhenEnabled is null) &&
|
||||
_coordinatorClientInstanceManager.HostedServices.TryGetValue(wabisabiStoreCoordinatorSettings.Coordinator, out var coordinator))
|
||||
{
|
||||
var round = coordinator.RoundStateUpdater.RoundStates.LastOrDefault();
|
||||
wabisabiStoreCoordinatorSettings.RoundWhenEnabled = new LastCoordinatorRoundConfig()
|
||||
{
|
||||
CoordinationFeeRate = round.Value.CoinjoinState.Parameters.CoordinationFeeRate.Rate,
|
||||
PlebsDontPayThreshold = round.Value.CoinjoinState.Parameters.CoordinationFeeRate
|
||||
.PlebsDontPayThreshold,
|
||||
MinInputCountByRound = round.Value.CoinjoinState.Parameters.MinInputCountByRound,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
await _storeRepository.UpdateSetting(storeId, nameof(WabisabiStoreSettings), wabisabiSettings!);
|
||||
}
|
||||
|
||||
await _walletProvider.SettingsUpdated(storeId, wabisabiSettings);
|
||||
|
||||
@@ -93,12 +93,20 @@ namespace BTCPayServer.Plugins.Wabisabi
|
||||
var commandIndex = pieces.Length > 1 ? pieces[1] : null;
|
||||
var coordinator = pieces.Length > 2 ? pieces[2] : null;
|
||||
vm.AnonymitySetTarget = Math.Max(2, vm.AnonymitySetTarget);
|
||||
var coord = vm.Settings.SingleOrDefault(settings => settings.Coordinator == coordinator);
|
||||
ModelState.Clear();
|
||||
|
||||
WabisabiCoordinatorSettings coordSettings;
|
||||
switch (actualCommand)
|
||||
{
|
||||
case "accept-terms":
|
||||
|
||||
var coord = vm.Settings.SingleOrDefault(settings => settings.Coordinator == commandIndex);
|
||||
coord.RoundWhenEnabled = null;
|
||||
|
||||
await _WabisabiService.SetWabisabiForStore(storeId, vm, commandIndex);
|
||||
TempData["SuccessMessage"] = $"{commandIndex} terms accepted";
|
||||
return RedirectToAction(nameof(UpdateWabisabiStoreSettings), new {storeId});
|
||||
|
||||
case "discover":
|
||||
coordSettings = await _wabisabiCoordinatorService.GetSettings();
|
||||
var relay = commandIndex ??
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using BTCPayServer.Client.JsonConverters;
|
||||
using NBitcoin;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace BTCPayServer.Plugins.Wabisabi;
|
||||
|
||||
@@ -32,8 +35,17 @@ public class WabisabiStoreSettings
|
||||
|
||||
public class WabisabiStoreCoordinatorSettings
|
||||
{
|
||||
|
||||
public string Coordinator { get; set; }
|
||||
public bool Enabled { get; set; } = false;
|
||||
|
||||
|
||||
public LastCoordinatorRoundConfig RoundWhenEnabled { get; set; }
|
||||
}
|
||||
|
||||
public class LastCoordinatorRoundConfig
|
||||
{
|
||||
|
||||
public decimal CoordinationFeeRate { get; set; }
|
||||
[JsonConverter(typeof(MoneyJsonConverter))]
|
||||
public Money PlebsDontPayThreshold { get; set; }
|
||||
public int MinInputCountByRound { get; set; }
|
||||
}
|
||||
|
||||
@@ -59,7 +59,7 @@ Scientist mode allows you to configure the following:
|
||||
We realize that the weakest link in these coinjoin protocols is the centralized coordinator aspect, and so have opted to support multiple coordinators, in parallel, from the get-go. You can discover additional coordinators over Nostr, or you can add a coordinator manually by using the link at the bottom.
|
||||

|
||||
|
||||
Please be cautious as some coordinators may be malicious in nature. Once a coordinator has been added and a coinjoin round has been discovered, you can click on "Coordinator Config" to see what their fees and round requirements are set to, but be aware that a coordinator can change these at will. Additional safeguards will be implemented soon.
|
||||
Please be cautious as some coordinators may be malicious in nature. Once a coordinator has been added and a coinjoin round has been discovered, you can click on "Coordinator Config" to see what their fees and round requirements are set to, but be aware that a coordinator can change these at will. The plugin tracks if the minimum inputs per round, the coordination fee or the free threshold has changed and will not join rounds that are worse off than the one visible when you enabled the coordinator. You can accept the new terms by clicking on "Accept new terms" and the plugin will join rounds using the new parameters.
|
||||
|
||||

|
||||
|
||||
|
||||
Submodule submodules/walletwasabi updated: 85a7a0c2c9...a8c17d21f2
Reference in New Issue
Block a user