Files
btcpayserver-monero-plugin/Plugins/Monero/Services/MoneroRPCProvider.cs
nicolas.dorier c24ef96fd4 Init commit
2025-01-08 18:27:19 +09:00

136 lines
5.3 KiB
C#

using System;
using System.Collections.Concurrent;
using System.Collections.Immutable;
using System.Net.Http;
using System.Threading.Tasks;
using Amazon.Runtime;
using BTCPayServer.Plugins.Monero.Configuration;
using BTCPayServer.Plugins.Monero.RPC;
using BTCPayServer.Plugins.Monero.RPC.Models;
using BTCPayServer.Services;
using NBitcoin;
namespace BTCPayServer.Plugins.Monero.Services
{
public class MoneroRPCProvider
{
private readonly MoneroLikeConfiguration _moneroLikeConfiguration;
private readonly EventAggregator _eventAggregator;
private readonly BTCPayServerEnvironment environment;
public ImmutableDictionary<string, JsonRpcClient> DaemonRpcClients;
public ImmutableDictionary<string, JsonRpcClient> WalletRpcClients;
private readonly ConcurrentDictionary<string, MoneroLikeSummary> _summaries =
new ConcurrentDictionary<string, MoneroLikeSummary>();
public ConcurrentDictionary<string, MoneroLikeSummary> Summaries => _summaries;
public MoneroRPCProvider(MoneroLikeConfiguration moneroLikeConfiguration, EventAggregator eventAggregator, IHttpClientFactory httpClientFactory, BTCPayServerEnvironment environment)
{
_moneroLikeConfiguration = moneroLikeConfiguration;
_eventAggregator = eventAggregator;
this.environment = environment;
DaemonRpcClients =
_moneroLikeConfiguration.MoneroLikeConfigurationItems.ToImmutableDictionary(pair => pair.Key,
pair => new JsonRpcClient(pair.Value.DaemonRpcUri, pair.Value.Username, pair.Value.Password, httpClientFactory.CreateClient($"{pair.Key}client")));
WalletRpcClients =
_moneroLikeConfiguration.MoneroLikeConfigurationItems.ToImmutableDictionary(pair => pair.Key,
pair => new JsonRpcClient(pair.Value.InternalWalletRpcUri, "", "", httpClientFactory.CreateClient($"{pair.Key}client")));
}
public bool IsAvailable(string cryptoCode)
{
cryptoCode = cryptoCode.ToUpperInvariant();
return _summaries.ContainsKey(cryptoCode) && IsAvailable(_summaries[cryptoCode]);
}
private bool IsAvailable(MoneroLikeSummary summary)
{
return summary.Synced &&
summary.WalletAvailable;
}
public async Task<MoneroLikeSummary> UpdateSummary(string cryptoCode)
{
if (!DaemonRpcClients.TryGetValue(cryptoCode.ToUpperInvariant(), out var daemonRpcClient) ||
!WalletRpcClients.TryGetValue(cryptoCode.ToUpperInvariant(), out var walletRpcClient))
{
return null;
}
var summary = new MoneroLikeSummary();
try
{
var daemonResult =
await daemonRpcClient.SendCommandAsync<JsonRpcClient.NoRequestModel, GetInfoResponse>("get_info",
JsonRpcClient.NoRequestModel.Instance);
summary.TargetHeight = daemonResult.TargetHeight.GetValueOrDefault(0);
summary.CurrentHeight = daemonResult.Height;
summary.TargetHeight = summary.TargetHeight == 0 ? summary.CurrentHeight : summary.TargetHeight;
summary.Synced = !daemonResult.BusySyncing;
summary.UpdatedAt = DateTime.UtcNow;
summary.DaemonAvailable = true;
}
catch
{
summary.DaemonAvailable = false;
}
bool walletCreated = false;
retry:
try
{
var walletResult =
await walletRpcClient.SendCommandAsync<JsonRpcClient.NoRequestModel, GetHeightResponse>(
"get_height", JsonRpcClient.NoRequestModel.Instance);
summary.WalletHeight = walletResult.Height;
summary.WalletAvailable = true;
}
catch when (environment.CheatMode && !walletCreated)
{
await walletRpcClient.SendCommandAsync<CreateWalletRequest, JsonRpcClient.NoRequestModel>("create_wallet",
new()
{
Filename = "wallet",
Password = "",
Language = "English"
});
walletCreated = true;
goto retry;
}
catch
{
summary.WalletAvailable = false;
}
var changed = !_summaries.ContainsKey(cryptoCode) || IsAvailable(cryptoCode) != IsAvailable(summary);
_summaries.AddOrReplace(cryptoCode, summary);
if (changed)
{
_eventAggregator.Publish(new MoneroDaemonStateChange() { Summary = summary, CryptoCode = cryptoCode });
}
return summary;
}
public class MoneroDaemonStateChange
{
public string CryptoCode { get; set; }
public MoneroLikeSummary Summary { get; set; }
}
public class MoneroLikeSummary
{
public bool Synced { get; set; }
public long CurrentHeight { get; set; }
public long WalletHeight { get; set; }
public long TargetHeight { get; set; }
public DateTime UpdatedAt { get; set; }
public bool DaemonAvailable { get; set; }
public bool WalletAvailable { get; set; }
}
}
}