btcpayserver/BTCPayServer/HostedServices/NBXplorerWaiter.cs

234 lines
8.2 KiB
C#
Raw Normal View History

2020-06-28 21:44:35 -05:00
using System;
2020-06-28 17:55:27 +09:00
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
2020-06-28 17:55:27 +09:00
using BTCPayServer.Events;
using BTCPayServer.Logging;
using Microsoft.Extensions.Hosting;
2020-06-28 17:55:27 +09:00
using Microsoft.Extensions.Logging;
using NBXplorer;
using NBXplorer.Models;
namespace BTCPayServer.HostedServices
{
public enum NBXplorerState
{
NotConnected,
Synching,
Ready
}
2018-01-08 04:14:35 +09:00
public class NBXplorerDashboard
{
public class NBXplorerSummary
{
public BTCPayNetworkBase Network { get; set; }
2018-01-08 04:14:35 +09:00
public NBXplorerState State { get; set; }
public StatusResult Status { get; set; }
2018-01-13 01:05:38 +09:00
public string Error { get; set; }
2018-01-08 04:14:35 +09:00
}
readonly ConcurrentDictionary<string, NBXplorerSummary> _Summaries = new ConcurrentDictionary<string, NBXplorerSummary>();
public void Publish(BTCPayNetworkBase network, NBXplorerState state, StatusResult status, string error)
2018-01-08 04:14:35 +09:00
{
2018-01-13 01:05:38 +09:00
var summary = new NBXplorerSummary() { Network = network, State = state, Status = status, Error = error };
2019-12-24 08:20:44 +01:00
_Summaries.AddOrUpdate(network.CryptoCode.ToUpperInvariant(), summary, (k, v) => summary);
2018-01-08 04:14:35 +09:00
}
public bool IsFullySynched()
{
return _Summaries.All(s => s.Value.Status?.IsFullySynched is true);
2018-01-08 04:14:35 +09:00
}
public bool IsFullySynched(string cryptoCode, out NBXplorerSummary summary)
{
2020-06-28 17:55:27 +09:00
return _Summaries.TryGetValue(cryptoCode.ToUpperInvariant(), out summary) &&
summary.Status?.IsFullySynched is true;
}
2018-10-26 23:07:39 +09:00
public NBXplorerSummary Get(string cryptoCode)
{
2019-05-07 13:58:55 +09:00
_Summaries.TryGetValue(cryptoCode.ToUpperInvariant(), out var summary);
2018-10-26 23:07:39 +09:00
return summary;
}
2018-01-08 04:14:35 +09:00
public IEnumerable<NBXplorerSummary> GetAll()
{
return _Summaries.Values;
}
}
public class NBXplorerWaiters : IHostedService
{
readonly List<NBXplorerWaiter> _Waiters = new List<NBXplorerWaiter>();
2021-11-22 17:16:08 +09:00
public NBXplorerWaiters(NBXplorerDashboard dashboard, ExplorerClientProvider explorerClientProvider, EventAggregator eventAggregator, Logs logs)
{
2018-01-08 04:14:35 +09:00
foreach (var explorer in explorerClientProvider.GetAll())
{
2021-11-22 17:16:08 +09:00
_Waiters.Add(new NBXplorerWaiter(dashboard, explorer.Item1, explorer.Item2, eventAggregator, logs));
}
}
public Task StartAsync(CancellationToken cancellationToken)
{
return Task.WhenAll(_Waiters.Select(w => w.StartAsync(cancellationToken)).ToArray());
}
public Task StopAsync(CancellationToken cancellationToken)
{
return Task.WhenAll(_Waiters.Select(w => w.StopAsync(cancellationToken)).ToArray());
}
}
public class NBXplorerWaiter : IHostedService
{
2021-11-22 17:16:08 +09:00
public NBXplorerWaiter(NBXplorerDashboard dashboard, BTCPayNetwork network, ExplorerClient client, EventAggregator aggregator, Logs logs)
{
2021-11-22 17:16:08 +09:00
this.Logs = logs;
_Network = network;
_Client = client;
_Aggregator = aggregator;
2018-01-08 04:14:35 +09:00
_Dashboard = dashboard;
_Dashboard.Publish(_Network, State, null, null);
}
readonly NBXplorerDashboard _Dashboard;
2021-11-22 17:16:08 +09:00
public Logs Logs { get; }
readonly BTCPayNetwork _Network;
readonly EventAggregator _Aggregator;
readonly ExplorerClient _Client;
2018-01-10 02:07:42 +09:00
CancellationTokenSource _Cts;
Task _Loop;
public Task StartAsync(CancellationToken cancellationToken)
{
2018-01-10 02:07:42 +09:00
_Cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
_Loop = StartLoop(_Cts.Token);
return Task.CompletedTask;
}
2018-01-10 02:07:42 +09:00
private async Task StartLoop(CancellationToken cancellation)
{
2018-01-10 02:07:42 +09:00
Logs.PayServer.LogInformation($"Starting listening NBXplorer ({_Network.CryptoCode})");
try
{
2018-01-10 02:07:42 +09:00
while (!cancellation.IsCancellationRequested)
{
try
{
while (await StepAsync(cancellation))
{
2018-01-10 02:07:42 +09:00
}
await Task.Delay(PollInterval, cancellation);
}
catch (Exception ex) when (!cancellation.IsCancellationRequested)
{
Logs.PayServer.LogError(ex, $"Unhandled exception in NBXplorerWaiter ({_Network.CryptoCode})");
await Task.Delay(TimeSpan.FromSeconds(10), cancellation);
}
}
}
2018-01-10 02:07:42 +09:00
catch when (cancellation.IsCancellationRequested) { }
}
2018-01-10 02:07:42 +09:00
private async Task<bool> StepAsync(CancellationToken cancellation)
{
var oldState = State;
2018-01-13 01:05:38 +09:00
string error = null;
StatusResult status = null;
2018-01-10 02:07:42 +09:00
try
{
2018-01-10 02:07:42 +09:00
switch (State)
{
case NBXplorerState.NotConnected:
status = await _Client.GetStatusAsync(cancellation);
if (status != null)
{
if (status.IsFullySynched)
{
State = NBXplorerState.Ready;
}
else
{
State = NBXplorerState.Synching;
}
}
break;
case NBXplorerState.Synching:
status = await _Client.GetStatusAsync(cancellation);
if (status == null)
{
State = NBXplorerState.NotConnected;
}
else if (status.IsFullySynched)
{
State = NBXplorerState.Ready;
}
2018-01-10 02:07:42 +09:00
break;
case NBXplorerState.Ready:
status = await _Client.GetStatusAsync(cancellation);
if (status == null)
{
State = NBXplorerState.NotConnected;
}
else if (!status.IsFullySynched)
{
State = NBXplorerState.Synching;
}
2018-01-10 02:07:42 +09:00
break;
}
}
2018-01-13 01:05:38 +09:00
catch (Exception ex) when (!cancellation.IsCancellationRequested)
2018-01-10 02:07:42 +09:00
{
2018-01-13 01:05:38 +09:00
error = ex.Message;
}
2020-06-28 17:55:27 +09:00
if (status == null && error == null)
2018-01-13 01:05:38 +09:00
error = $"{_Network.CryptoCode}: NBXplorer does not support this cryptocurrency";
2017-12-17 11:07:11 +09:00
2020-06-28 17:55:27 +09:00
if (status != null && error == null)
2018-01-13 01:05:38 +09:00
{
if (status.NetworkType != _Network.NBitcoinNetwork.ChainName)
error = $"{_Network.CryptoCode}: NBXplorer is on a different ChainType (actual: {status.NetworkType}, expected: {_Network.NBitcoinNetwork.ChainName})";
2018-01-10 02:07:42 +09:00
}
2018-01-13 01:05:38 +09:00
if (error != null)
2018-01-10 02:07:42 +09:00
{
State = NBXplorerState.NotConnected;
2018-01-13 01:05:38 +09:00
status = null;
2018-05-10 11:56:46 +09:00
Logs.PayServer.LogError($"{_Network.CryptoCode}: NBXplorer error `{error}`");
2018-01-10 02:07:42 +09:00
}
2018-01-13 01:05:38 +09:00
_Dashboard.Publish(_Network, State, status, error);
if (oldState != State)
{
2017-12-17 11:07:11 +09:00
if (State == NBXplorerState.Synching)
{
2018-01-10 02:07:42 +09:00
PollInterval = TimeSpan.FromSeconds(10);
2017-12-17 11:07:11 +09:00
}
else
{
2018-01-10 02:07:42 +09:00
PollInterval = TimeSpan.FromMinutes(1);
2017-12-17 11:07:11 +09:00
}
_Aggregator.Publish(new NBXplorerStateChangedEvent(_Network, oldState, State));
}
return oldState != State;
}
2018-01-10 02:07:42 +09:00
public TimeSpan PollInterval { get; set; } = TimeSpan.FromMinutes(1.0);
public NBXplorerState State { get; private set; }
public Task StopAsync(CancellationToken cancellationToken)
{
2018-01-10 02:07:42 +09:00
_Cts.Cancel();
return _Loop;
}
}
}