diff --git a/BTCPayServerPlugins.sln.DotSettings.user b/BTCPayServerPlugins.sln.DotSettings.user
index 0cc0f59..053c8d0 100644
--- a/BTCPayServerPlugins.sln.DotSettings.user
+++ b/BTCPayServerPlugins.sln.DotSettings.user
@@ -15,4 +15,33 @@
</SessionState>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
True
\ No newline at end of file
diff --git a/Plugins/BTCPayServer.Plugins.NIP05/BTCPayServer.Plugins.NIP05.csproj b/Plugins/BTCPayServer.Plugins.NIP05/BTCPayServer.Plugins.NIP05.csproj
index 0692222..01e9c73 100644
--- a/Plugins/BTCPayServer.Plugins.NIP05/BTCPayServer.Plugins.NIP05.csproj
+++ b/Plugins/BTCPayServer.Plugins.NIP05/BTCPayServer.Plugins.NIP05.csproj
@@ -11,7 +11,7 @@
Nostr
NIP5 addresses, Zap support, Nostr Wallet Connect Lightning support
- 1.1.6
+ 1.1.7
true
@@ -36,7 +36,7 @@
-
+
diff --git a/Plugins/BTCPayServer.Plugins.Wabisabi/BTCPayServer.Plugins.Wabisabi.csproj b/Plugins/BTCPayServer.Plugins.Wabisabi/BTCPayServer.Plugins.Wabisabi.csproj
index 50a3acd..f006d43 100644
--- a/Plugins/BTCPayServer.Plugins.Wabisabi/BTCPayServer.Plugins.Wabisabi.csproj
+++ b/Plugins/BTCPayServer.Plugins.Wabisabi/BTCPayServer.Plugins.Wabisabi.csproj
@@ -13,7 +13,7 @@
Coinjoin
Allows you to integrate your btcpayserver store with coinjoins.
- 1.0.81
+ 1.0.82
true
@@ -44,7 +44,7 @@
-
+
diff --git a/Plugins/BTCPayServer.Plugins.Wabisabi/Coordinator/WabisabiCoordinatorService.cs b/Plugins/BTCPayServer.Plugins.Wabisabi/Coordinator/WabisabiCoordinatorService.cs
index e1eed77..869787c 100644
--- a/Plugins/BTCPayServer.Plugins.Wabisabi/Coordinator/WabisabiCoordinatorService.cs
+++ b/Plugins/BTCPayServer.Plugins.Wabisabi/Coordinator/WabisabiCoordinatorService.cs
@@ -268,6 +268,7 @@ public class WabisabiCoordinatorService : PeriodicRunner
var network = _clientProvider.GetExplorerClient("BTC").Network.NBitcoinNetwork;
var s = await GetSettings();
+ Uri uri = s.UriToAdvertise;
if (s.Enabled && !string.IsNullOrEmpty(s.NostrIdentity) && s.NostrRelay is not null &&
s.UriToAdvertise is not null)
{
@@ -276,29 +277,44 @@ public class WabisabiCoordinatorService : PeriodicRunner
if(s.UriToAdvertise.Scheme.StartsWith("http", StringComparison.InvariantCultureIgnoreCase))
{
//make sure the end is with plugins/wabisabi-coordinator/
- var uri = new UriBuilder(s.UriToAdvertise);
-
- uri.Path = uri.Path.Replace("plugins/wabisabi-coordinator/", "").TrimEnd('/') + "/plugins/wabisabi-coordinator/";
+ var uriB = new UriBuilder(uri);
+ uriB.Path = uriB.Path.Replace("plugins/wabisabi-coordinator", "").TrimEnd('/') + "/plugins/wabisabi-coordinator/";
+ uri= uriB.Uri;
}
+
//verify the url
IWasabiHttpClientFactory factory = null;
- if (s.UriToAdvertise.Scheme == "nostr" &&
- s.UriToAdvertise.Host.FromNIP19Note() is NIP19.NosteProfileNote nostrProfileNote)
+ if (uri.Scheme == "nostr" &&
+ uri.AbsolutePath.FromNIP19Note() is NIP19.NosteProfileNote nostrProfileNote)
{
factory = new NostrWabisabiClientFactory(null, nostrProfileNote);
}
else
{
- factory = new WasabiHttpClientFactory(null, () => s.UriToAdvertise);
+ factory = new WasabiHttpClientFactory(null, () => uri);
}
try
{
- var handler = factory.NewWabiSabiApiRequestHandler(Mode.SingleCircuitPerLifetime);
- var resp = await handler.GetStatusAsync(RoundStateRequest.Empty, cancel);
+ if (factory is IHostedService hs)
+ {
+ await hs.StartAsync(cancel);
+ }
+ var handler = factory.NewWabiSabiApiRequestHandler(Mode.DefaultCircuit);
+
+ var cts = CancellationTokenSource.CreateLinkedTokenSource(cancel);
+ cts.CancelAfter(TimeSpan.FromSeconds(10));
+ var resp = await handler.GetStatusAsync(RoundStateRequest.Empty, cts.Token);
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "Could not connect to the coordinator at {0}", uri);
+ await Task.Delay(TimeSpan.FromMinutes(1), cancel);
+ TriggerRound();
+ return;
}
finally
{
@@ -309,13 +325,13 @@ public class WabisabiCoordinatorService : PeriodicRunner
}
- _logger.LogInformation("Publishing coordinator discovery event with url {0}", s.UriToAdvertise);
+ _logger.LogInformation("Publishing coordinator discovery event with url {0}", uri);
await Nostr.Publish(s.NostrRelay,
new[]
{
- await Nostr.CreateCoordinatorDiscoveryEvent(network, k, s.UriToAdvertise,
+ await Nostr.CreateCoordinatorDiscoveryEvent(network, k, uri,
s.CoordinatorDescription)
},s.UriToAdvertise.IsOnion()? _socks5HttpClientHandler: null, cancel);
}
diff --git a/Plugins/BTCPayServer.Plugins.Wabisabi/NostrWabiSabiApiClient.cs b/Plugins/BTCPayServer.Plugins.Wabisabi/NostrWabiSabiApiClient.cs
index 0cb867b..0fc5213 100644
--- a/Plugins/BTCPayServer.Plugins.Wabisabi/NostrWabiSabiApiClient.cs
+++ b/Plugins/BTCPayServer.Plugins.Wabisabi/NostrWabiSabiApiClient.cs
@@ -1,4 +1,5 @@
using System;
+using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
@@ -31,7 +32,7 @@ public class NostrWabiSabiApiClient : IWabiSabiApiRequestHandler, IHostedService
private readonly ECXOnlyPubKey _coordinatorKey;
private readonly INamedCircuit _circuit;
private string _coordinatorKeyHex => _coordinatorKey.ToHex();
- private readonly string _coordinatorFilterId;
+ // private readonly string _coordinatorFilterId;
public NostrWabiSabiApiClient(Uri relay, WebProxy webProxy , ECXOnlyPubKey coordinatorKey, INamedCircuit? circuit)
{
@@ -39,66 +40,79 @@ public class NostrWabiSabiApiClient : IWabiSabiApiRequestHandler, IHostedService
_webProxy = webProxy;
_coordinatorKey = coordinatorKey;
_circuit = circuit;
- _coordinatorFilterId = new Guid().ToString();
+ // _coordinatorFilterId = new Guid().ToString();
}
+ private CancellationTokenSource _cts;
public async Task StartAsync(CancellationToken cancellationToken)
{
if (!_circuit.IsActive)
{
Dispose();
}
-
+ _cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
if (_circuit is OneOffCircuit)
{
return;
// we dont bootstrap, we do it on demand for a request instead
}
- await Init(cancellationToken, _circuit);
+ _ = Init(_cts.Token, _circuit);
}
+
private async Task Init(CancellationToken cancellationToken, INamedCircuit circuit)
{
- _client = CreateClient(_relay, _webProxy, circuit);
-
- _ = _client.ListenForMessages();
- var filter = new NostrSubscriptionFilter()
+ while (cancellationToken.IsCancellationRequested == false)
{
- Authors = new[] {_coordinatorKey.ToHex()},
- Kinds = new[] {RoundStateKind, CommunicationKind},
- Since = DateTimeOffset.UtcNow.Subtract(TimeSpan.FromHours(1)),
- Limit = 0
- };
- await _client.CreateSubscription(_coordinatorFilterId, new[] {filter}, cancellationToken);
- _client.EventsReceived += EventsReceived;
+ _client = CreateClient(_relay, _webProxy, circuit);
- await _client.ConnectAndWaitUntilConnected(cancellationToken);
- _circuit.IsolationIdChanged += (_, _) =>
- {
- _client.Dispose();
- _ = StartAsync(CancellationToken.None);
- };
- }
+ await _client.ConnectAndWaitUntilConnected(cancellationToken);
- private RoundStateResponse _lastRoundState { get; set; }
- private TaskCompletionSource _lastRoundStateTask = new();
-
-
- private void EventsReceived(object sender, (string subscriptionId, NostrEvent[] events) e)
- {
- if (e.subscriptionId == _coordinatorFilterId)
- {
- var roundState = e.events.Where(evt => evt.Kind == RoundStateKind).MaxBy(@event => @event.CreatedAt);
- if (roundState != null)
+ _circuit.IsolationIdChanged += (_, _) =>
{
- _lastRoundState = Deserialize(roundState.Content);
- _lastRoundStateTask.TrySetResult();
- }
+ Dispose();
+ _ = StartAsync(CancellationToken.None);
+ };
+
+
+ var subscriptions = _client.SubscribeForEvents(new[]
+ {
+ new NostrSubscriptionFilter()
+ {
+ Authors = new[] {_coordinatorKey.ToHex()},
+ Kinds = new[] {RoundStateKind},
+ Limit = 1
+ }
+ }, false, cancellationToken);
+
+ await HandleStateEvents(subscriptions);
+
}
}
+ private async Task HandleStateEvents(IAsyncEnumerable subscriptions)
+ {
+ await foreach (var evt in subscriptions)
+ {
+ if (evt.Kind != RoundStateKind)
+ continue;
+ if (_lastRoundStateEvent is not null && evt.CreatedAt <= _lastRoundStateEvent.CreatedAt)
+ continue;
+ _lastRoundStateEvent = evt;
+
+ _lastRoundState = Deserialize(evt.Content);
+ _lastRoundStateTask.TrySetResult();
+
+ }
+ }
+
+ private NostrEvent _lastRoundStateEvent { get; set; }
+ private RoundStateResponse _lastRoundState { get; set; }
+ private readonly TaskCompletionSource _lastRoundStateTask = new();
+
+
private async Task SendAndWaitForReply(RemoteAction action, TRequest request,
CancellationToken cancellationToken)
{
@@ -129,37 +143,19 @@ public class NostrWabiSabiApiClient : IWabiSabiApiRequestHandler, IHostedService
Request = request
}),
PublicKey = pubkey.ToHex(),
- Kind = 4,
+ Kind = CommunicationKind,
CreatedAt = DateTimeOffset.Now
};
evt.SetTag("p", _coordinatorKeyHex);
- await evt.EncryptNip04EventAsync(newKey);
- evt.Kind = CommunicationKind;
- await evt.ComputeIdAndSignAsync(newKey);
- var tcs = new TaskCompletionSource(cancellationToken);
-
- void OnClientEventsReceived(object sender, (string subscriptionId, NostrEvent[] events) e)
- {
- foreach (var nostrEvent in e.events)
- {
- if (nostrEvent.PublicKey != _coordinatorKeyHex) continue;
- var replyToEvent = evt.GetTaggedData("e");
- var replyToUser = evt.GetTaggedData("p");
- if (replyToEvent.All(s => s != evt.Id) || replyToUser.All(s => s != evt.PublicKey)) continue;
- if (!nostrEvent.Verify()) continue;
- _client.EventsReceived -= OnClientEventsReceived;
- tcs.TrySetResult(nostrEvent);
- break;
- }
- }
-
- _client.EventsReceived += OnClientEventsReceived;
+ await evt.EncryptNip04EventAsync(newKey, null, true);
+ evt = await evt.ComputeIdAndSignAsync(newKey, false);
+
try
{
- var replyEvent = await tcs.Task;
- replyEvent.Kind = 4;
- var response = await replyEvent.DecryptNip04EventAsync(newKey);
+
+ var replyEvent = await _client.SendEventAndWaitForReply(evt, cancellationToken);
+ var response = await replyEvent.DecryptNip04EventAsync(newKey, null, true);
var jobj = JObject.Parse(response);
if (jobj.TryGetValue("error", out var errorJson))
{
@@ -205,7 +201,6 @@ public class NostrWabiSabiApiClient : IWabiSabiApiRequestHandler, IHostedService
}
catch (OperationCanceledException e)
{
- _client.EventsReceived -= OnClientEventsReceived;
_circuit.IncrementIsolationId();
throw;
}
@@ -283,6 +278,10 @@ public class NostrWabiSabiApiClient : IWabiSabiApiRequestHandler, IHostedService
public void Dispose()
{
_client?.Dispose();
+ _cts?.Cancel();
+ _client = null;
+ _cts = null;
+
}
public static NostrClient CreateClient(Uri relay, WebProxy webProxy, INamedCircuit namedCircuit )
diff --git a/Plugins/BTCPayServer.Plugins.Wabisabi/NostrWabisabiApiServer.cs b/Plugins/BTCPayServer.Plugins.Wabisabi/NostrWabisabiApiServer.cs
index 7c3d7e3..0871a7b 100644
--- a/Plugins/BTCPayServer.Plugins.Wabisabi/NostrWabisabiApiServer.cs
+++ b/Plugins/BTCPayServer.Plugins.Wabisabi/NostrWabisabiApiServer.cs
@@ -1,6 +1,8 @@
using System;
+using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
+using System.Text.Json;
using System.Threading;
using System.Threading.Channels;
using System.Threading.Tasks;
@@ -20,20 +22,21 @@ using WalletWasabi.WabiSabi.Backend.Rounds;
using WalletWasabi.WabiSabi.Crypto;
using WalletWasabi.WabiSabi.Models;
using WalletWasabi.WabiSabi.Models.Serialization;
+using JsonSerializer = Newtonsoft.Json.JsonSerializer;
namespace BTCPayServer.Plugins.Wabisabi;
-public class NostrWabisabiApiServer: IHostedService
+public class NostrWabisabiApiServer : IHostedService
{
public static int RoundStateKind = 15750;
public static int CommunicationKind = 25750;
private readonly Arena _arena;
private WabisabiCoordinatorSettings _coordinatorSettings;
- private NostrClient _client;
+ private NostrClient _client;
private readonly ILogger _logger;
private readonly string _coordinatorFilterId;
- private Channel PendingEvents { get; } = Channel.CreateUnbounded();
+ // private Channel PendingEvents { get; } = Channel.CreateUnbounded();
public NostrWabisabiApiServer(Arena arena, WabisabiCoordinatorSettings coordinatorSettings,
ILogger logger)
{
@@ -44,6 +47,7 @@ public class NostrWabisabiApiServer: IHostedService
_serializer = JsonSerializer.Create(JsonSerializationOptions.Default.Settings);
}
+ private CancellationTokenSource _cts;
public async Task StartAsync(CancellationToken cancellationToken)
{
@@ -52,33 +56,26 @@ public class NostrWabisabiApiServer: IHostedService
_logger.LogInformation("NOSTR SERVER: No Nostr relay/identity configured, skipping");
return;
}
-
+
_client = new NostrClient(_coordinatorSettings.NostrRelay);
- await _client.Connect(cancellationToken);
- _logger.LogInformation($"NOSTR SERVER: CONNECTED TO {_coordinatorSettings.NostrRelay}");
- var filter = new NostrSubscriptionFilter()
- {
- ReferencedPublicKeys = new[] {_coordinatorSettings.GetPubKey().ToHex()},
- Kinds = new[] { CommunicationKind},
- Limit = 0
- };
- await _client.CreateSubscription(_coordinatorFilterId, new[] {filter}, cancellationToken);
- _client.EventsReceived += EventsReceived;
await _client.ConnectAndWaitUntilConnected(cancellationToken);
- _ = RoutinelyUpdateRoundEvent(cancellationToken);
- _ = ProcessRequests(cancellationToken);
-
- }
+ _cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
+ _logger.LogInformation($"NOSTR SERVER: CONNECTED TO {_coordinatorSettings.NostrRelay}");
- private void EventsReceived(object sender, (string subscriptionId, NostrEvent[] events) e)
- {
- if (e.subscriptionId != _coordinatorFilterId) return;
- var requests = e.events.Where(evt =>
- evt.Kind == CommunicationKind &&
- evt.GetTaggedData("p").Any(s => s == _coordinatorSettings.GetPubKey().ToHex()) && evt.Verify());
- foreach (var request in requests)
- PendingEvents.Writer.TryWrite(request);
+ _evtSubscriptions = _client.SubscribeForEvents(new[]
+ {
+ new NostrSubscriptionFilter()
+ {
+ ReferencedPublicKeys = new[] {_coordinatorSettings.GetPubKey().ToHex()},
+ Kinds = new[] {CommunicationKind},
+ Limit = 0
+ }
+ }, false, _cts.Token);
+
+
+ _ = RoutinelyUpdateRoundEvent(_cts.Token);
+ _ = ProcessRequests(_cts.Token);
}
@@ -86,155 +83,169 @@ public class NostrWabisabiApiServer: IHostedService
{
while (!cancellationToken.IsCancellationRequested)
{
- var response = await _arena.GetStatusAsync(RoundStateRequest.Empty, cancellationToken);
+ try
+ {
+
+ var response = await _arena.GetStatusAsync(RoundStateRequest.Empty, cancellationToken);
var nostrEvent = new NostrEvent()
{
Kind = RoundStateKind,
- PublicKey = _coordinatorSettings.GetPubKey().ToHex(),
- CreatedAt = DateTimeOffset.Now,
- Content = Serialize(response)
+ Content =Serialize(response)
};
- await _client.PublishEvent(nostrEvent, cancellationToken);
+
+
+ nostrEvent = await nostrEvent.ComputeIdAndSignAsync(_coordinatorSettings.GetKey());
+ await _client.SendEventsAndWaitUntilReceived(new[] {nostrEvent}, cancellationToken);
_logger.LogDebug($"NOSTR SERVER: PUBLISHED ROUND STATE {nostrEvent.Id}");
await Task.Delay(1000, cancellationToken);
+
+ }
+ catch (Exception e)
+ {
+ Console.WriteLine(e);
+ }
}
}
private async Task ProcessRequests(CancellationToken cancellationToken)
{
- while (!cancellationToken.IsCancellationRequested &&
- await PendingEvents.Reader.WaitToReadAsync(cancellationToken) &&
- PendingEvents.Reader.TryRead(out var evt))
+ await foreach (var evt in _evtSubscriptions.WithCancellation(cancellationToken))
{
- evt.Kind = 4;
- var content = JObject.Parse(await evt.DecryptNip04EventAsync(_coordinatorSettings.GetKey()));
- if (content.TryGetValue("action", out var actionJson) &&
- actionJson.Value(actionJson) is { } actionString &&
- Enum.TryParse(actionString, out var action) &&
- content.ContainsKey("request"))
+ try
{
- try
+ var content =
+ JObject.Parse(await evt.DecryptNip04EventAsync(_coordinatorSettings.GetKey(), null, true));
+ if (content.TryGetValue("action", out var actionJson) &&
+ actionJson.Value(actionJson) is { } actionString &&
+ Enum.TryParse(actionString, out var action) &&
+ content.ContainsKey("request"))
{
- _logger.LogDebug($"NOSTR SERVER: Received request {evt.Id} {action}");
- switch (action)
+ try
{
- case RemoteAction.GetStatus:
- // ignored as we use a dedicated public event for this to not spam
- break;
- case RemoteAction.RegisterInput:
- var registerInputRequest =
- content["request"].ToObject(_serializer);
- var registerInputResponse =
- await _arena.RegisterInputAsync(registerInputRequest, CancellationToken.None);
- await Reply(evt, registerInputResponse, CancellationToken.None);
- break;
- case RemoteAction.RegisterOutput:
- var registerOutputRequest =
- content["request"].ToObject(_serializer);
- await _arena.RegisterOutputAsync(registerOutputRequest, CancellationToken.None);
- break;
- case RemoteAction.RemoveInput:
- var removeInputRequest = content["request"].ToObject(_serializer);
- await _arena.RemoveInputAsync(removeInputRequest, CancellationToken.None);
- break;
- case RemoteAction.ConfirmConnection:
- var connectionConfirmationRequest =
- content["request"].ToObject(_serializer);
- var connectionConfirmationResponse =
- await _arena.ConfirmConnectionAsync(connectionConfirmationRequest,
- CancellationToken.None);
- await Reply(evt, connectionConfirmationResponse, CancellationToken.None);
- break;
- case RemoteAction.ReissueCredential:
- var reissueCredentialRequest =
- content["request"].ToObject(_serializer);
- var reissueCredentialResponse =
- await _arena.ReissuanceAsync(reissueCredentialRequest, CancellationToken.None);
- await Reply(evt, reissueCredentialResponse, CancellationToken.None);
- break;
- case RemoteAction.SignTransaction:
- var transactionSignaturesRequest =
- content["request"].ToObject(_serializer);
- await _arena.SignTransactionAsync(transactionSignaturesRequest, CancellationToken.None);
- break;
- case RemoteAction.ReadyToSign:
- var readyToSignRequest =
- content["request"].ToObject(_serializer);
- await _arena.ReadyToSignAsync(readyToSignRequest, CancellationToken.None);
- break;
+ _logger.LogDebug($"NOSTR SERVER: Received request {evt.Id} {action}");
+ switch (action)
+ {
+ case RemoteAction.GetStatus:
+ // ignored as we use a dedicated public event for this to not spam
+ break;
+ case RemoteAction.RegisterInput:
+ var registerInputRequest =
+ content["request"].ToObject(_serializer);
+ var registerInputResponse =
+ await _arena.RegisterInputAsync(registerInputRequest, CancellationToken.None);
+ await Reply(evt, registerInputResponse, CancellationToken.None);
+ break;
+ case RemoteAction.RegisterOutput:
+ var registerOutputRequest =
+ content["request"].ToObject(_serializer);
+ await _arena.RegisterOutputAsync(registerOutputRequest, CancellationToken.None);
+ break;
+ case RemoteAction.RemoveInput:
+ var removeInputRequest = content["request"].ToObject(_serializer);
+ await _arena.RemoveInputAsync(removeInputRequest, CancellationToken.None);
+ break;
+ case RemoteAction.ConfirmConnection:
+ var connectionConfirmationRequest =
+ content["request"].ToObject(_serializer);
+ var connectionConfirmationResponse =
+ await _arena.ConfirmConnectionAsync(connectionConfirmationRequest,
+ CancellationToken.None);
+ await Reply(evt, connectionConfirmationResponse, CancellationToken.None);
+ break;
+ case RemoteAction.ReissueCredential:
+ var reissueCredentialRequest =
+ content["request"].ToObject(_serializer);
+ var reissueCredentialResponse =
+ await _arena.ReissuanceAsync(reissueCredentialRequest, CancellationToken.None);
+ await Reply(evt, reissueCredentialResponse, CancellationToken.None);
+ break;
+ case RemoteAction.SignTransaction:
+ var transactionSignaturesRequest =
+ content["request"].ToObject(_serializer);
+ await _arena.SignTransactionAsync(transactionSignaturesRequest, CancellationToken.None);
+ break;
+ case RemoteAction.ReadyToSign:
+ var readyToSignRequest =
+ content["request"].ToObject(_serializer);
+ await _arena.ReadyToSignAsync(readyToSignRequest, CancellationToken.None);
+ break;
+ }
+ }
+ catch (Exception ex)
+ {
+ object response = ex switch
+ {
+ WabiSabiProtocolException wabiSabiProtocolException => new
+ {
+ Error = new Error(Type: ProtocolConstants.ProtocolViolationType,
+ ErrorCode: wabiSabiProtocolException.ErrorCode.ToString(),
+ Description: wabiSabiProtocolException.Message,
+ ExceptionData: wabiSabiProtocolException.ExceptionData ??
+ EmptyExceptionData.Instance)
+ },
+ WabiSabiCryptoException wabiSabiCryptoException => new
+ {
+ Error = new Error(Type: ProtocolConstants.ProtocolViolationType,
+ ErrorCode: WabiSabiProtocolErrorCode.CryptoException.ToString(),
+ Description: wabiSabiCryptoException.Message,
+ ExceptionData: EmptyExceptionData.Instance)
+ },
+ _ => new
+ {
+ Error = new Error(Type: "unknown", ErrorCode: ex.GetType().Name,
+ Description: ex.Message, ExceptionData: EmptyExceptionData.Instance)
+ }
+ };
+
+ await Reply(evt, response, CancellationToken.None);
}
}
- catch (Exception ex)
- {
- object response = ex switch
- {
- WabiSabiProtocolException wabiSabiProtocolException => new
- {
- Error = new Error(Type: ProtocolConstants.ProtocolViolationType,
- ErrorCode: wabiSabiProtocolException.ErrorCode.ToString(),
- Description: wabiSabiProtocolException.Message,
- ExceptionData: wabiSabiProtocolException.ExceptionData ??
- EmptyExceptionData.Instance)
- },
- WabiSabiCryptoException wabiSabiCryptoException => new
- {
- Error = new Error(Type: ProtocolConstants.ProtocolViolationType,
- ErrorCode: WabiSabiProtocolErrorCode.CryptoException.ToString(),
- Description: wabiSabiCryptoException.Message,
- ExceptionData: EmptyExceptionData.Instance)
- },
- _ => new
- {
- Error = new Error(Type: "unknown", ErrorCode: ex.GetType().Name,
- Description: ex.Message, ExceptionData: EmptyExceptionData.Instance)
- }
- };
-
- await Reply(evt, response, CancellationToken.None);
- }
}
-
+ catch (Exception e)
+ {
+ _logger.LogError(e, "NOSTR SERVER: Failed to process event {0}", evt.Id);
+ }
}
}
private readonly JsonSerializer _serializer;
+ private IAsyncEnumerable _evtSubscriptions;
-
- private async Task Reply(NostrEvent originaltEvent,TResponse response,
+ private async Task Reply(NostrEvent originaltEvent, TResponse response,
CancellationToken cancellationToken)
{
-
_logger.LogDebug($"NOSTR SERVER: REPLYING TO {originaltEvent.Id} WITH {response}");
var evt = new NostrEvent()
{
Content = Serialize(response),
PublicKey = _coordinatorSettings.GetPubKey().ToHex(),
- Kind = 4,
+ Kind = CommunicationKind,
CreatedAt = DateTimeOffset.Now
};
evt.SetTag("p", originaltEvent.PublicKey);
evt.SetTag("e", originaltEvent.Id);
- await evt.EncryptNip04EventAsync(_coordinatorSettings.GetKey());
- evt.Kind = CommunicationKind;
+ await evt.EncryptNip04EventAsync(_coordinatorSettings.GetKey(), null, true);
await evt.ComputeIdAndSignAsync(_coordinatorSettings.GetKey());
await _client.PublishEvent(evt, cancellationToken);
}
public async Task StopAsync(CancellationToken cancellationToken)
{
- if (_client is not null)
- {
-
- await _client.CloseSubscription(_coordinatorFilterId, cancellationToken);
- _client.EventsReceived -= EventsReceived;
- _client = null;
- }
+ if(_cts is not null)
+ await _cts?.CancelAsync();
+
+ _client?.Dispose();
}
private static string Serialize(T obj)
- => JsonConvert.SerializeObject(obj, JsonSerializationOptions.Default.Settings);
+ {
+ var raw = JsonConvert.SerializeObject(obj, JsonSerializationOptions.Default.Settings);
+ return raw;
+
+ }
+
private enum RemoteAction
{
RegisterInput,
diff --git a/Plugins/BTCPayServer.Plugins.Wabisabi/WabisabiCoordinatorClientInstance.cs b/Plugins/BTCPayServer.Plugins.Wabisabi/WabisabiCoordinatorClientInstance.cs
index f92e2e4..58336b3 100644
--- a/Plugins/BTCPayServer.Plugins.Wabisabi/WabisabiCoordinatorClientInstance.cs
+++ b/Plugins/BTCPayServer.Plugins.Wabisabi/WabisabiCoordinatorClientInstance.cs
@@ -194,7 +194,7 @@ public class NostrWabisabiClientFactory: IWasabiHttpClientFactory, IHostedServic
public IWabiSabiApiRequestHandler NewWabiSabiApiRequestHandler(Mode mode, ICircuit circuit = null)
{
- if (mode == Mode.DefaultCircuit || _socks5HttpClientHandler.Proxy is null)
+ if (mode == Mode.DefaultCircuit || _socks5HttpClientHandler?.Proxy is null)
{
circuit = DefaultCircuit.Instance;
}
@@ -209,10 +209,16 @@ public class NostrWabisabiClientFactory: IWasabiHttpClientFactory, IHostedServic
var result = _clients.GetOrAdd(namedCircuit.Name, name =>
{
var result = new NostrWabiSabiApiClient(new Uri(_nostrProfileNote.Relays.First()),
- _socks5HttpClientHandler.Proxy as WebProxy, NostrExtensions.ParsePubKey(_nostrProfileNote.PubKey),
+ _socks5HttpClientHandler?.Proxy as WebProxy, NostrExtensions.ParsePubKey(_nostrProfileNote.PubKey),
namedCircuit);
+ if (_started)
+ {
+
+ result.StartAsync(CancellationToken.None).ConfigureAwait(false).GetAwaiter().GetResult();
+ }
return result;
});
+
return result;
}
}
@@ -287,7 +293,7 @@ public class WabisabiCoordinatorClientInstance:IHostedService
}
else if (coordinator.Scheme == "nostr" &&
- coordinator.Host.FromNIP19Note() is NIP19.NosteProfileNote nostrProfileNote)
+ coordinator.AbsolutePath.TrimEnd('/').FromNIP19Note() is NIP19.NosteProfileNote nostrProfileNote)
{
var factory = new NostrWabisabiClientFactory(socks5HttpClientHandler, nostrProfileNote);
WasabiHttpClientFactory = factory;
diff --git a/Plugins/BTCPayServer.Plugins.Wabisabi/WabisabiStoreController.cs b/Plugins/BTCPayServer.Plugins.Wabisabi/WabisabiStoreController.cs
index e8e9e01..e34d7ef 100644
--- a/Plugins/BTCPayServer.Plugins.Wabisabi/WabisabiStoreController.cs
+++ b/Plugins/BTCPayServer.Plugins.Wabisabi/WabisabiStoreController.cs
@@ -108,35 +108,37 @@ namespace BTCPayServer.Plugins.Wabisabi
vm.FeeRateMedianTimeFrameHours = Math.Max(0, vm.FeeRateMedianTimeFrameHours);
ModelState.Clear();
+ var coordx = string.Join(string.Empty,pieces.Skip(1).ToArray());
WabisabiCoordinatorSettings coordSettings;
switch (actualCommand)
{
case "reset":
var newS = new WabisabiStoreSettings();
newS.Settings = vm.Settings;
- await _WabisabiService.SetWabisabiForStore(storeId, vm, commandIndex);
+ await _WabisabiService.SetWabisabiForStore(storeId, vm, coordx);
TempData["SuccessMessage"] = $"Advanced settings reset to default";
return RedirectToAction(nameof(UpdateWabisabiStoreSettings), new {storeId});
case "accept-terms":
- var coord = vm.Settings.SingleOrDefault(settings => settings.Coordinator == commandIndex);
+ var coord = vm.Settings.SingleOrDefault(settings => settings.Coordinator == coordx);
coord.RoundWhenEnabled = null;
- await _WabisabiService.SetWabisabiForStore(storeId, vm, commandIndex);
- TempData["SuccessMessage"] = $"{commandIndex} terms accepted";
+ await _WabisabiService.SetWabisabiForStore(storeId, vm, coordx);
+ TempData["SuccessMessage"] = $"{coordx} terms accepted";
return RedirectToAction(nameof(UpdateWabisabiStoreSettings), new {storeId});
case "remove-coordinator":
if (!(await _authorizationService.AuthorizeAsync(User, null,
new PolicyRequirement(Policies.CanModifyServerSettings))).Succeeded)
{ return View(vm);
}
+
coordSettings = await _wabisabiCoordinatorService.GetSettings();
if (coordSettings.DiscoveredCoordinators.RemoveAll(discoveredCoordinator =>
- discoveredCoordinator.Name == commandIndex) > 0)
+ discoveredCoordinator.Name == coordx) > 0)
{
- TempData["SuccessMessage"] = $"Coordinator {commandIndex} stopped and removed";
+ TempData["SuccessMessage"] = $"Coordinator {coordx} stopped and removed";
await _wabisabiCoordinatorService.UpdateSettings(coordSettings);
- await _instanceManager.RemoveCoordinator(commandIndex);
+ await _instanceManager.RemoveCoordinator(coordx);
return RedirectToAction(nameof(UpdateWabisabiStoreSettings), new {storeId});
}
else