mirror of
https://github.com/aljazceru/BTCPayServerPlugins.git
synced 2026-01-31 13:34:52 +01:00
fixes
This commit is contained in:
@@ -13,7 +13,7 @@
|
||||
<PropertyGroup>
|
||||
<Product>Wabisabi Coinjoin</Product>
|
||||
<Description>Allows you to integrate your btcpayserver store with coinjoins.</Description>
|
||||
<Version>1.0.15</Version>
|
||||
<Version>1.0.16</Version>
|
||||
</PropertyGroup>
|
||||
|
||||
<!-- Plugin development properties -->
|
||||
@@ -43,7 +43,7 @@
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="NNostr.Client" Version="0.0.21" />
|
||||
<PackageReference Include="NNostr.Client" Version="0.0.23" />
|
||||
</ItemGroup>
|
||||
<Target Name="DeleteExampleFile" AfterTargets="Publish">
|
||||
<RemoveDir Directories="$(PublishDir)\Microservices" />
|
||||
|
||||
@@ -110,7 +110,8 @@ public class BTCPayWallet : IWallet, IDestinationProvider
|
||||
|
||||
public int AnonScoreTarget => WabisabiStoreSettings.PlebMode? 2: WabisabiStoreSettings.AnonymitySetTarget;
|
||||
public bool ConsolidationMode => !WabisabiStoreSettings.PlebMode && WabisabiStoreSettings.ConsolidationMode;
|
||||
public TimeSpan FeeRateMedianTimeFrame { get; } = TimeSpan.FromHours(KeyManager.DefaultFeeRateMedianTimeFrameHours);
|
||||
public TimeSpan FeeRateMedianTimeFrame => TimeSpan.FromHours(WabisabiStoreSettings.PlebMode?
|
||||
KeyManager.DefaultFeeRateMedianTimeFrameHours: WabisabiStoreSettings.FeeRateMedianTimeFrameHours);
|
||||
public bool RedCoinIsolation => !WabisabiStoreSettings.PlebMode &&WabisabiStoreSettings.RedCoinIsolation;
|
||||
public bool BatchPayments => WabisabiStoreSettings.PlebMode || WabisabiStoreSettings.BatchPayments;
|
||||
|
||||
|
||||
@@ -3,11 +3,13 @@ using System.IO;
|
||||
using System.Net.Http;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using BTCPayServer;
|
||||
using BTCPayServer.Abstractions.Contracts;
|
||||
using BTCPayServer.Common;
|
||||
using BTCPayServer.Configuration;
|
||||
using BTCPayServer.Plugins.Wabisabi;
|
||||
using BTCPayServer.Services;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
using Microsoft.Extensions.Caching.Memory;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Options;
|
||||
@@ -33,6 +35,7 @@ public class WabisabiCoordinatorService : PeriodicRunner
|
||||
private readonly IMemoryCache _memoryCache;
|
||||
private readonly WabisabiCoordinatorClientInstanceManager _instanceManager;
|
||||
private readonly IHttpClientFactory _httpClientFactory;
|
||||
private readonly LinkGenerator _linkGenerator;
|
||||
|
||||
public readonly IdempotencyRequestCache IdempotencyRequestCache;
|
||||
|
||||
@@ -44,7 +47,8 @@ public class WabisabiCoordinatorService : PeriodicRunner
|
||||
IOptions<DataDirectories> dataDirectories, IExplorerClientProvider clientProvider, IMemoryCache memoryCache,
|
||||
WabisabiCoordinatorClientInstanceManager instanceManager,
|
||||
IHttpClientFactory httpClientFactory,
|
||||
IServiceProvider serviceProvider) : base(TimeSpan.FromMinutes(15))
|
||||
IServiceProvider serviceProvider,
|
||||
LinkGenerator linkGenerator) : base(TimeSpan.FromMinutes(15))
|
||||
{
|
||||
_settingsRepository = settingsRepository;
|
||||
_dataDirectories = dataDirectories;
|
||||
@@ -52,6 +56,7 @@ public class WabisabiCoordinatorService : PeriodicRunner
|
||||
_memoryCache = memoryCache;
|
||||
_instanceManager = instanceManager;
|
||||
_httpClientFactory = httpClientFactory;
|
||||
_linkGenerator = linkGenerator;
|
||||
_socks5HttpClientHandler = serviceProvider.GetRequiredService<Socks5HttpClientHandler>();
|
||||
IdempotencyRequestCache = new(memoryCache);
|
||||
}
|
||||
@@ -196,12 +201,13 @@ public class WabisabiCoordinatorService : PeriodicRunner
|
||||
s.UriToAdvertise is not null)
|
||||
{
|
||||
|
||||
var uri = new Uri(s.UriToAdvertise, "plugins/wabisabi-coordinator/wabisabi");
|
||||
await Nostr.Publish(s.NostrRelay,
|
||||
new[]
|
||||
{
|
||||
await Nostr.CreateCoordinatorDiscoveryEvent(network, s.NostrIdentity, s.UriToAdvertise,
|
||||
await Nostr.CreateCoordinatorDiscoveryEvent(network, s.NostrIdentity, uri,
|
||||
s.CoordinatorDescription)
|
||||
}, _socks5HttpClientHandler, cancel);
|
||||
},s.UriToAdvertise.IsOnion()? _socks5HttpClientHandler: null, cancel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ public class WabisabiCoordinatorSettings
|
||||
public bool Enabled { get; set; } = false;
|
||||
|
||||
public string NostrIdentity { get; set; }
|
||||
public Uri NostrRelay { get; set; } = new Uri("wss://relay.nostr.info");
|
||||
public Uri NostrRelay { get; set; } = new Uri("wss://nostr.mutinywallet.com");
|
||||
|
||||
public List<DiscoveredCoordinator> DiscoveredCoordinators { get; set; } = new();
|
||||
|
||||
@@ -45,5 +45,6 @@ public class DiscoveredCoordinator
|
||||
{
|
||||
public Uri Uri { get; set; }
|
||||
public string Name { get; set; }
|
||||
public string Relay { get; set; }
|
||||
public string Description { get; set; }
|
||||
}
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Nodes;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using BTCPayServer.Services;
|
||||
@@ -23,26 +25,37 @@ public class Nostr
|
||||
public static async Task Publish(
|
||||
Uri relayUri,
|
||||
NostrEvent[] evts,
|
||||
Socks5HttpClientHandler httpClientHandler,
|
||||
Socks5HttpClientHandler? httpClientHandler,
|
||||
CancellationToken cancellationToken )
|
||||
{
|
||||
|
||||
if (!evts.Any())
|
||||
return;
|
||||
var ct = CancellationTokenSource
|
||||
.CreateLinkedTokenSource(cancellationToken, new CancellationTokenSource(TimeSpan.FromSeconds(30)).Token)
|
||||
.CreateLinkedTokenSource(cancellationToken, new CancellationTokenSource(TimeSpan.FromMinutes(1)).Token)
|
||||
.Token;
|
||||
var client = new NostrClient(relayUri);
|
||||
var client = new NostrClient(relayUri, socket => socket.Options.Proxy = httpClientHandler?.Proxy);
|
||||
await client.ConnectAndWaitUntilConnected(ct);
|
||||
_ = client.ListenForMessages();
|
||||
var tcs = new TaskCompletionSource();
|
||||
|
||||
var ids = evts.Select(evt => evt.Id).ToHashSet();
|
||||
|
||||
|
||||
client.InvalidMessageReceived += (sender, tuple) =>
|
||||
{
|
||||
Console.WriteLine(tuple);
|
||||
|
||||
};
|
||||
client.OkReceived += (sender, tuple) =>
|
||||
{
|
||||
if (ids.RemoveWhere(s => s == tuple.eventId)> 0 && !ids.Any())
|
||||
{
|
||||
tcs.TrySetResult();
|
||||
}
|
||||
};
|
||||
client.EventsReceived += (sender, tuple) =>
|
||||
{
|
||||
if (ids.RemoveWhere(s => tuple.events.Any(@event => @event.Id == s)) > 0 && !ids.Any())
|
||||
{
|
||||
tcs.TrySetResult();
|
||||
tcs.TrySetResult();
|
||||
}
|
||||
};
|
||||
await client.CreateSubscription("ack", new[]
|
||||
@@ -86,18 +99,25 @@ public class Nostr
|
||||
}
|
||||
|
||||
public static async Task<List<DiscoveredCoordinator>> Discover(
|
||||
Socks5HttpClientHandler? httpClientHandler,
|
||||
Uri relayUri,
|
||||
Network currentNetwork,
|
||||
string ourPubKey,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
using var nostrClient = new NostrClient(relayUri);
|
||||
|
||||
var nostrClient = new NostrClient(relayUri, socket => socket.Options.Proxy = httpClientHandler?.Proxy);
|
||||
await nostrClient.CreateSubscription("nostr-wabisabi-coordinators",
|
||||
new[]
|
||||
{
|
||||
new NostrSubscriptionFilter()
|
||||
{
|
||||
Kinds = new[] {Kind}, Since = DateTimeOffset.UtcNow.Subtract(TimeSpan.FromHours(1)),
|
||||
ExtensionData = new Dictionary<string, JsonElement>()
|
||||
{
|
||||
["type"] = JsonSerializer.SerializeToElement(TypeTagValue),
|
||||
["network"] = JsonSerializer.SerializeToElement(currentNetwork.Name.ToLower())
|
||||
}
|
||||
}
|
||||
}, cancellationToken);
|
||||
var cts = new CancellationTokenSource(TimeSpan.FromMinutes(1));
|
||||
@@ -107,12 +127,9 @@ public class Nostr
|
||||
var tcs = new TaskCompletionSource();
|
||||
Stopwatch stopwatch = new();
|
||||
stopwatch.Start();
|
||||
nostrClient.MessageReceived += (sender, s) =>
|
||||
nostrClient.EoseReceived += (sender, s) =>
|
||||
{
|
||||
if (JArray.Parse(s).FirstOrDefault()?.Value<string>() == "EOSE")
|
||||
{
|
||||
tcs.SetResult();
|
||||
}
|
||||
tcs.SetResult();
|
||||
};
|
||||
nostrClient.EventsReceived += (sender, tuple) =>
|
||||
{
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
@using BTCPayServer.Client
|
||||
@using Microsoft.AspNetCore.Mvc.TagHelpers
|
||||
@using BTCPayServer.Abstractions.Contracts
|
||||
@using BTCPayServer.Abstractions.TagHelpers
|
||||
@using Newtonsoft.Json
|
||||
@using WalletWasabi.Backend.Controllers
|
||||
@model WalletWasabi.Backend.Controllers.DiscoveredCoordinator
|
||||
@inject IScopeProvider ScopeProvider
|
||||
|
||||
|
||||
|
||||
|
||||
<button type="button" class="btn btn-secondary mt-2" permission="@Policies.CanModifyServerSettings"
|
||||
data-bs-toggle="modal" data-bs-target="#discover-prompt">
|
||||
Add Coordinator
|
||||
</button>
|
||||
<div class="modal fade" id="discover-prompt" permission="@Policies.CanModifyServerSettings">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">Add Coordinator</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<ul class="nav nav-tabs" id="myTab" role="tablist">
|
||||
<li class="nav-item" role="presentation">
|
||||
<button class="nav-link active" id="nostr-tab" data-bs-toggle="tab" data-bs-target="#nostr-tab-pane" type="button" role="tab" aria-selected="true">Nostr</button>
|
||||
</li>
|
||||
<li class="nav-item" role="presentation">
|
||||
<button class="nav-link" id="manual-tab" data-bs-toggle="tab" data-bs-target="#manual-tab-pane" type="button" role="tab" aria-selected="false">Manual</button>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="tab-content ">
|
||||
<form asp-action="AddCoordinator" asp-route-storeId="@ScopeProvider.GetCurrentStoreId()"
|
||||
class="tab-pane fade show active " id="nostr-tab-pane" role="tabpanel" aria-labelledby="home-tab" tabindex="0">
|
||||
|
||||
<div class="modal-body">
|
||||
<div class="form-group">
|
||||
<label class="form-label">Nostr Relay</label>
|
||||
<input type="url" class="form-control" asp-for="Relay" required="required">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button name="command" type="submit" value="discover" class="btn btn-primary">Discover</button>
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
|
||||
</div>
|
||||
</form>
|
||||
<form asp-action="AddCoordinator" asp-route-storeId="@ScopeProvider.GetCurrentStoreId()"
|
||||
class="tab-pane fade" id="manual-tab-pane" role="tabpanel" tabindex="0">
|
||||
|
||||
<div class="modal-body">
|
||||
<div class="form-group">
|
||||
<label asp-for="Name" class="form-label">Coordinator Name</label>
|
||||
<input asp-for="Name" type="text" required class="form-control"/>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label asp-for="Uri" class="form-label">Coordinator URL</label>
|
||||
<input asp-for="Uri" type="url" required class="form-control"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button name="command" type="submit" class="btn btn-primary">Add</button>
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@if (TempData.TryGetValue("DiscoveredCoordinators" , out var v)&& v is string vs)
|
||||
{
|
||||
var discoveredCoordinators = JsonConvert.DeserializeObject<List<DiscoveredCoordinator>>(vs);
|
||||
foreach (var coordinator in discoveredCoordinators)
|
||||
{
|
||||
<partial model="@coordinator" name="Wabisabi/AddCoordinator"/>
|
||||
}
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
@using BTCPayServer.Client
|
||||
@using Microsoft.AspNetCore.Mvc.TagHelpers
|
||||
@using BTCPayServer.Abstractions.Contracts
|
||||
@model WalletWasabi.Backend.Controllers.DiscoveredCoordinator
|
||||
@inject IScopeProvider ScopeProvider
|
||||
|
||||
<div class="mt-4" permission="@Policies.CanModifyServerSettings">
|
||||
<button class="btn btn-link mt-4" type="button" data-bs-toggle="collapse" data-bs-target="#manual-coordinator">
|
||||
Add coordinator manually
|
||||
</button>
|
||||
<div class="row collapse" id="manual-coordinator">
|
||||
<div class="col col-xl-6 mb-4">
|
||||
<form asp-action="AddCoordinator" asp-controller="WabisabiStore" asp-route-storeId="@ScopeProvider.GetCurrentStoreId()" method="post" class="card ">
|
||||
|
||||
<div class="card-body">
|
||||
<h4 class="card-title">Add coordinator manually</h4>
|
||||
|
||||
<div class="form-group">
|
||||
<label asp-for="Name" class="form-label">Coordinator Name</label>
|
||||
<input asp-for="Name" type="text" required class="form-control"/>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label asp-for="Uri" class="form-label">Coordinator URL</label>
|
||||
<input asp-for="Uri" type="url" required class="form-control"/>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="card-footer">
|
||||
|
||||
<button class="btn btn-secondary" type="submit">Add</button>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -13,7 +13,7 @@
|
||||
@inject WabisabiService WabisabiService;
|
||||
@inject WalletProvider WalletProvider;
|
||||
@inject WabisabiCoordinatorClientInstanceManager WabisabiCoordinatorClientInstanceManager
|
||||
@inject ContentSecurityPolicies contentSecurityPolicies
|
||||
|
||||
@inject IExplorerClientProvider ExplorerClientProvider
|
||||
|
||||
@{
|
||||
@@ -31,10 +31,9 @@
|
||||
var storeId = ScopeProvider.GetCurrentStoreId();
|
||||
// var methods = await Client.GetStoreOnChainPaymentMethods(storeId, true);
|
||||
// var method = methods.FirstOrDefault(data => data.CryptoCode == "BTC");
|
||||
var nonce = RandomUtils.GetUInt256().ToString().Substring(0, 32);
|
||||
contentSecurityPolicies.Add("script-src", $"'nonce-{nonce}'");
|
||||
contentSecurityPolicies.AllowUnsafeHashes();
|
||||
var mainnet = ExplorerClientProvider.GetExplorerClient("BTC").Network.NBitcoinNetwork.ChainName == ChainName.Mainnet;
|
||||
// var nonce = RandomUtils.GetUInt256().ToString().Substring(0, 32);
|
||||
// contentSecurityPolicies.Add("script-src", $"'nonce-{nonce}'");
|
||||
// contentSecurityPolicies.AllowUnsafeHashes();
|
||||
|
||||
}
|
||||
@if (available)
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
@using BTCPayServer.Plugins.Wabisabi
|
||||
@using BTCPayServer.Abstractions.Contracts
|
||||
@using BTCPayServer.Security
|
||||
@using NBitcoin
|
||||
@using System.Security.Claims
|
||||
@using BTCPayServer
|
||||
@using BTCPayServer.Client
|
||||
@using BTCPayServer.Configuration
|
||||
@using BTCPayServer.Services.Stores
|
||||
@using Microsoft.AspNetCore.Mvc.TagHelpers
|
||||
@using WalletWasabi.Backend.Controllers
|
||||
@@ -14,6 +14,7 @@
|
||||
@inject StoreRepository StoreRepository
|
||||
@inject WalletProvider WalletProvider
|
||||
@inject BTCPayNetworkProvider BtcPayNetworkProvider
|
||||
@inject BTCPayServerOptions BtcPayServerOptions
|
||||
@{
|
||||
var storeId = _scopeProvider.GetCurrentStoreId();
|
||||
Layout = "../Shared/_NavLayout.cshtml";
|
||||
@@ -43,6 +44,14 @@
|
||||
</div>
|
||||
<form method="post">
|
||||
@{
|
||||
if (BtcPayServerOptions.SocksEndpoint is null)
|
||||
{
|
||||
<div class="alert alert-danger d-flex align-items-center" role="alert">
|
||||
<vc:icon symbol="warning"/>
|
||||
<span class="ms-3">TOR is not configured on this BTCPay Server instance. All communication will be over clearnet and therefore not private!</span>
|
||||
</div>
|
||||
}
|
||||
|
||||
var wallet = await WalletProvider.GetWalletAsync(storeId);
|
||||
if (wallet is BTCPayWallet btcPayWallet)
|
||||
{
|
||||
@@ -98,6 +107,13 @@
|
||||
|
||||
<p class="text-muted">Scores your coinjoined utxos based on how many other utxos in the coinjoin (and other previous coinjoin rounds) had the same value.<br/> Anonset score computation is not an exact science, and when using coordinators with massive liquidity, is not that important as all rounds (past, present, future) contribute to your privacy.</p>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
|
||||
<label asp-for="FeeRateMedianTimeFrameHours" class="form-label">Mining fee limits in hours</label>
|
||||
<input type="number" class="form-control" asp-for="FeeRateMedianTimeFrameHours" placeholder="hours" min="0">
|
||||
|
||||
<p class="text-muted">Only coinjoin if the mining fee is below the median of the specified number of hours</p>
|
||||
</div>
|
||||
<div class="form-group form-check">
|
||||
<label asp-for="ConsolidationMode" class="form-check-label">Coinsolidation mode</label>
|
||||
<input asp-for="ConsolidationMode" type="checkbox" class="form-check-input"/>
|
||||
@@ -217,9 +233,11 @@
|
||||
</p>
|
||||
}
|
||||
|
||||
@if (coordinator.RoundStateUpdater.AnyRound)
|
||||
// make sure there is a round that is not a blame round
|
||||
@if (coordinator.RoundStateUpdater.AnyRound && coordinator.RoundStateUpdater.RoundStates.Any(pair => pair.Value.BlameOf == uint256.Zero))
|
||||
{
|
||||
var round = coordinator.RoundStateUpdater.RoundStates.Last().Value.CoinjoinState.Parameters;
|
||||
var round = coordinator.RoundStateUpdater.RoundStates.Last(pair => pair.Value.BlameOf == uint256.Zero).Value;
|
||||
var roundParameters = round.CoinjoinState.Parameters;
|
||||
|
||||
<div class="modal modal-lg fade" id="config-@s.Coordinator">
|
||||
<div class="modal-dialog">
|
||||
@@ -234,7 +252,7 @@
|
||||
<tr>
|
||||
<th scope="row">Fee charged</th>
|
||||
@{
|
||||
var fee = $"{round.CoordinationFeeRate.Rate * 100}% + Free remixing {(round.CoordinationFeeRate.PlebsDontPayThreshold <= 0 ? string.Empty : $"+ Free under {round.CoordinationFeeRate.PlebsDontPayThreshold.ToDecimal(MoneyUnit.BTC)} BTC")}";
|
||||
var fee = $"{roundParameters.CoordinationFeeRate.Rate * 100}% + Free remixing {(roundParameters.CoordinationFeeRate.PlebsDontPayThreshold <= 0 ? string.Empty : $"+ Free under {roundParameters.CoordinationFeeRate.PlebsDontPayThreshold.ToDecimal(MoneyUnit.BTC)} BTC")}";
|
||||
}
|
||||
<td>
|
||||
@(fee)
|
||||
@@ -242,30 +260,30 @@
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">Allowed input amounts</th>
|
||||
<td>@round.AllowedInputAmounts.Min.ToDecimal(MoneyUnit.BTC) BTC - @round.AllowedInputAmounts.Max.ToDecimal(MoneyUnit.BTC) BTC</td>
|
||||
<td>@roundParameters.AllowedInputAmounts.Min.ToDecimal(MoneyUnit.BTC) BTC - @roundParameters.AllowedInputAmounts.Max.ToDecimal(MoneyUnit.BTC) BTC</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">Allowed input types</th>
|
||||
<td>@string.Join(", ", round.AllowedInputTypes)</td>
|
||||
<td>@string.Join(", ", roundParameters.AllowedInputTypes)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">Allowed output amounts</th>
|
||||
<td>@round.AllowedOutputAmounts.Min.ToDecimal(MoneyUnit.BTC) BTC - @round.AllowedOutputAmounts.Max.ToDecimal(MoneyUnit.BTC) BTC</td>
|
||||
<td>@roundParameters.AllowedOutputAmounts.Min.ToDecimal(MoneyUnit.BTC) BTC - @roundParameters.AllowedOutputAmounts.Max.ToDecimal(MoneyUnit.BTC) BTC</td>
|
||||
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">Allowed output types</th>
|
||||
|
||||
<td>@string.Join(", ", round.AllowedOutputTypes)</td>
|
||||
<td>@string.Join(", ", roundParameters.AllowedOutputTypes)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">Minimum inputs</th><td>@round.MinInputCountByRound</td>
|
||||
<th scope="row">Minimum inputs</th><td>@roundParameters.MinInputCountByRound</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">Maximum inputs</th><td>@round.MaxInputCountByRound</td>
|
||||
<th scope="row">Maximum inputs</th><td>@roundParameters.MaxInputCountByRound</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">Maximum round registration time</th><td>@round.StandardInputRegistrationTimeout.ToString()</td>
|
||||
<th scope="row">Maximum round registration time</th><td>@roundParameters.StandardInputRegistrationTimeout.ToString()</td>
|
||||
</tr>
|
||||
|
||||
</table>
|
||||
@@ -281,7 +299,7 @@
|
||||
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]))
|
||||
@if (Model.Settings[index].RoundWhenEnabled is not null && !BTCPayWallet.IsRoundOk(roundParameters, 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>
|
||||
}
|
||||
@@ -300,7 +318,7 @@
|
||||
@{
|
||||
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)">
|
||||
<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"/>
|
||||
@@ -339,23 +357,15 @@
|
||||
|
||||
|
||||
<button name="command" type="submit" value="save" class="btn btn-primary mt-2">Save</button>
|
||||
<a asp-controller="WabisabiCoordinatorConfig" asp-action="UpdateWabisabiSettings" class="btn btn-secondary mt-2" permission="@Policies.CanModifyServerSettings">Coordinator runner</a>
|
||||
<a asp-controller="WabisabiStore" asp-action="ListCoinjoins" class="btn btn-secondary mt-2" asp-route-storeId="@storeId">Coinjoins</a>
|
||||
<button name="command" type="submit" value="discover" class="btn btn-secondary mt-2" permission="@Policies.CanModifyServerSettings">Discover coordinators over Nostr</button>
|
||||
<a class="btn btn-secondary mt-2" href="https://gist.github.com/nopara73/bb17e89d7dc9af536ca41f50f705d329" rel="noreferrer noopener" target="_blank">Enable Discreet payments - Coming soon</a>
|
||||
|
||||
|
||||
</form>
|
||||
|
||||
@if (ViewBag.DiscoveredCoordinators is List<DiscoveredCoordinator> discoveredCoordinators)
|
||||
{
|
||||
foreach (var coordinator in discoveredCoordinators)
|
||||
{
|
||||
<partial model="@coordinator" name="Wabisabi/AddCoordinator"/>
|
||||
}
|
||||
}
|
||||
|
||||
<partial name="Wabisabi/AddManualCoordinator" model="@(new DiscoveredCoordinator())"/>
|
||||
<a asp-controller="WabisabiCoordinatorConfig" asp-action="UpdateWabisabiSettings" class="btn btn-secondary mt-2" permission="@Policies.CanModifyServerSettings">Coordinator runner</a>
|
||||
|
||||
<partial name="Wabisabi/AddCoordinatorPrompt" model="@(new DiscoveredCoordinator())"/>
|
||||
<a class="btn btn-secondary mt-2" href="https://gist.github.com/nopara73/bb17e89d7dc9af536ca41f50f705d329" rel="noreferrer noopener" target="_blank">Enable Discreet payments - Coming soon</a>
|
||||
|
||||
|
||||
@section PageFootContent {
|
||||
<partial name="_ValidationScriptsPartial"/>
|
||||
|
||||
@@ -12,6 +12,7 @@ using BTCPayServer.Abstractions.Contracts;
|
||||
using BTCPayServer.Client;
|
||||
using BTCPayServer.Client.Models;
|
||||
using BTCPayServer.Common;
|
||||
using BTCPayServer.Configuration;
|
||||
using BTCPayServer.Data;
|
||||
using BTCPayServer.Filters;
|
||||
using BTCPayServer.Models.WalletViewModels;
|
||||
@@ -26,6 +27,7 @@ using NBitcoin;
|
||||
using NBitcoin.Payment;
|
||||
using NBitcoin.Secp256k1;
|
||||
using NBXplorer;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using NNostr.Client;
|
||||
using Org.BouncyCastle.Security;
|
||||
@@ -46,6 +48,7 @@ namespace BTCPayServer.Plugins.Wabisabi
|
||||
private readonly IExplorerClientProvider _explorerClientProvider;
|
||||
private readonly WabisabiCoordinatorService _wabisabiCoordinatorService;
|
||||
private readonly IAuthorizationService _authorizationService;
|
||||
private readonly BTCPayServerOptions _options;
|
||||
private readonly WabisabiCoordinatorClientInstanceManager _instanceManager;
|
||||
private readonly Socks5HttpClientHandler _socks5HttpClientHandler;
|
||||
|
||||
@@ -55,7 +58,8 @@ namespace BTCPayServer.Plugins.Wabisabi
|
||||
WabisabiCoordinatorService wabisabiCoordinatorService,
|
||||
WabisabiCoordinatorClientInstanceManager instanceManager,
|
||||
IAuthorizationService authorizationService,
|
||||
IServiceProvider serviceProvider)
|
||||
IServiceProvider serviceProvider,
|
||||
BTCPayServerOptions options)
|
||||
{
|
||||
_WabisabiService = WabisabiService;
|
||||
_walletProvider = walletProvider;
|
||||
@@ -63,6 +67,7 @@ namespace BTCPayServer.Plugins.Wabisabi
|
||||
_explorerClientProvider = explorerClientProvider;
|
||||
_wabisabiCoordinatorService = wabisabiCoordinatorService;
|
||||
_authorizationService = authorizationService;
|
||||
_options = options;
|
||||
_instanceManager = instanceManager;
|
||||
_socks5HttpClientHandler = serviceProvider.GetRequiredService<Socks5HttpClientHandler>();
|
||||
}
|
||||
@@ -91,8 +96,8 @@ namespace BTCPayServer.Plugins.Wabisabi
|
||||
var pieces = command.Split(":");
|
||||
var actualCommand = pieces[0];
|
||||
var commandIndex = pieces.Length > 1 ? pieces[1] : null;
|
||||
var coordinator = pieces.Length > 2 ? pieces[2] : null;
|
||||
vm.AnonymitySetTarget = Math.Max(2, vm.AnonymitySetTarget);
|
||||
vm.AnonymitySetTarget = Math.Max(0, vm.AnonymitySetTarget);
|
||||
vm.FeeRateMedianTimeFrameHours = Math.Max(0, vm.FeeRateMedianTimeFrameHours);
|
||||
ModelState.Clear();
|
||||
|
||||
WabisabiCoordinatorSettings coordSettings;
|
||||
@@ -106,41 +111,6 @@ namespace BTCPayServer.Plugins.Wabisabi
|
||||
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 ??
|
||||
coordSettings?.NostrRelay.ToString();
|
||||
var network = _explorerClientProvider.GetExplorerClient("BTC").Network.NBitcoinNetwork;
|
||||
|
||||
if (Uri.TryCreate(relay, UriKind.Absolute, out var relayUri))
|
||||
{
|
||||
|
||||
if (network.ChainName == ChainName.Regtest)
|
||||
{
|
||||
var evts = new List<NostrEvent>();
|
||||
for (int i = 0; i < SecureRandom.Shared.Next(1, 10); i++)
|
||||
{
|
||||
ECPrivKey.TryCreate(new ReadOnlySpan<byte>(RandomNumberGenerator.GetBytes(32)),
|
||||
out var key);
|
||||
evts.Add(await Nostr.CreateCoordinatorDiscoveryEvent(network, key.ToHex(),
|
||||
new Uri($"https://{Guid.NewGuid()}.com"), "fake regtest coord test"));
|
||||
}
|
||||
|
||||
await Nostr.Publish(relayUri, evts.ToArray(),_socks5HttpClientHandler ,CancellationToken.None);
|
||||
}
|
||||
|
||||
ViewBag.DiscoveredCoordinators = await Nostr.Discover(relayUri,
|
||||
network,
|
||||
coordSettings.Key?.CreateXOnlyPubKey().ToHex(), CancellationToken.None);
|
||||
}
|
||||
else
|
||||
{
|
||||
TempData["ErrorMessage"] = $"No relay uri was provided";
|
||||
}
|
||||
|
||||
return View(vm);
|
||||
|
||||
case "remove-coordinator":
|
||||
if (!(await _authorizationService.AuthorizeAsync(User, null,
|
||||
new PolicyRequirement(Policies.CanModifyServerSettings))).Succeeded)
|
||||
@@ -197,11 +167,64 @@ var network = _explorerClientProvider.GetExplorerClient("BTC").Network.NBitcoinN
|
||||
|
||||
[HttpPost("add-coordinator")]
|
||||
[Authorize(AuthenticationSchemes = AuthenticationSchemes.Cookie, Policy = Policies.CanModifyServerSettings)]
|
||||
public async Task<IActionResult> AddCoordinator(string storeId, [FromForm] DiscoveredCoordinator viewModel)
|
||||
public async Task<IActionResult> AddCoordinator(string storeId, [FromForm] DiscoveredCoordinator viewModel, string command )
|
||||
{
|
||||
|
||||
var coordSettings = await _wabisabiCoordinatorService.GetSettings();
|
||||
if (command == "discover")
|
||||
{
|
||||
|
||||
var network = _explorerClientProvider.GetExplorerClient("BTC").Network.NBitcoinNetwork;
|
||||
|
||||
if (Uri.TryCreate(viewModel.Relay, UriKind.Absolute, out var relayUri))
|
||||
{
|
||||
|
||||
if (network.ChainName == ChainName.Regtest)
|
||||
{
|
||||
var eventsTomake = Random.Shared.Next(1, 5);
|
||||
var evts = new List<NostrEvent>();
|
||||
for (var i = 0; i < eventsTomake; i++)
|
||||
{
|
||||
ECPrivKey.TryCreate(new ReadOnlySpan<byte>(RandomNumberGenerator.GetBytes(32)),
|
||||
out var key);
|
||||
evts.Add(await Nostr.CreateCoordinatorDiscoveryEvent(network, key.ToHex(),
|
||||
new Uri($"https://{Guid.NewGuid()}.com"), "fake regtest coord test"));
|
||||
}
|
||||
|
||||
await Nostr.Publish(relayUri, evts.ToArray(),null,CancellationToken.None);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var result = await Nostr.Discover(
|
||||
_socks5HttpClientHandler, relayUri,
|
||||
network,
|
||||
coordSettings.Key?.CreateXOnlyPubKey().ToHex(), CancellationToken.None);
|
||||
if(result.Any())
|
||||
TempData["DiscoveredCoordinators"] = JsonConvert.SerializeObject(result);
|
||||
else
|
||||
TempData["ErrorMessage"] = $"No coordinators found.";
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
|
||||
TempData["ErrorMessage"] = $"Could not discover coordinators: {e.Message}";
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
TempData["ErrorMessage"] = $"No relay uri was provided";
|
||||
}
|
||||
|
||||
return RedirectToAction(nameof(UpdateWabisabiStoreSettings), new {storeId});
|
||||
}
|
||||
|
||||
|
||||
if (viewModel.Name is not null && viewModel.Uri is not null && coordSettings.DiscoveredCoordinators.All(discoveredCoordinator =>
|
||||
if (viewModel.Name is not null &&
|
||||
viewModel.Uri is not null &&
|
||||
!new []{"local", "zksnacks"}.Contains(viewModel.Name.ToLowerInvariant()) &&
|
||||
coordSettings.DiscoveredCoordinators.All(discoveredCoordinator =>
|
||||
discoveredCoordinator.Name != viewModel.Name && discoveredCoordinator.Uri != viewModel.Uri))
|
||||
{
|
||||
coordSettings.DiscoveredCoordinators.Add(viewModel);
|
||||
|
||||
@@ -24,6 +24,7 @@ public class WabisabiStoreSettings
|
||||
public bool BatchPayments { get; set; } = true;
|
||||
public int ExtraJoinProbability { get; set; } = 0;
|
||||
public CrossMixMode CrossMixBetweenCoordinatorsMode { get; set; } = CrossMixMode.WhenFree;
|
||||
public int FeeRateMedianTimeFrameHours { get; set; }
|
||||
|
||||
public enum CrossMixMode
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user