Auto load wallet on start up if available and deprecate password
Co-authored-by: Deverick <5827364+deverickapollo@users.noreply.github.com>
This commit is contained in:
parent
50d3a23c7b
commit
f30d55072e
25 changed files with 320 additions and 95 deletions
4
.gitattributes
vendored
4
.gitattributes
vendored
|
|
@ -6,4 +6,6 @@
|
||||||
|
|
||||||
# Denote all files that are truly binary and should not be modified.
|
# Denote all files that are truly binary and should not be modified.
|
||||||
*.png binary
|
*.png binary
|
||||||
*.jpg binary
|
*.jpg binary
|
||||||
|
*.keys binary
|
||||||
|
wallet binary
|
||||||
|
|
@ -9,6 +9,7 @@
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Docker.DotNet" Version="3.125.15" />
|
<PackageReference Include="Docker.DotNet" Version="3.125.15" />
|
||||||
|
<PackageReference Include="Mono.Posix.NETStandard" Version="1.0.0" />
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.14.1" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.14.1" />
|
||||||
<PackageReference Include="Microsoft.Playwright" Version="1.52.0" />
|
<PackageReference Include="Microsoft.Playwright" Version="1.52.0" />
|
||||||
<PackageReference Include="xunit" Version="2.9.3" />
|
<PackageReference Include="xunit" Version="2.9.3" />
|
||||||
|
|
@ -32,6 +33,12 @@
|
||||||
<ProjectReference Include="..\Plugins\Monero\BTCPayServer.Plugins.Monero.csproj" />
|
<ProjectReference Include="..\Plugins\Monero\BTCPayServer.Plugins.Monero.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Content Include="Resources\**\*">
|
||||||
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
|
</Content>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemDefinitionGroup>
|
<ItemDefinitionGroup>
|
||||||
<ProjectReference>
|
<ProjectReference>
|
||||||
<Properties>StaticWebAssetsEnabled=false</Properties>
|
<Properties>StaticWebAssetsEnabled=false</Properties>
|
||||||
|
|
|
||||||
|
|
@ -5,20 +5,26 @@ using BTCPayServer.Tests;
|
||||||
|
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
|
using Mono.Unix.Native;
|
||||||
|
|
||||||
using Npgsql;
|
using Npgsql;
|
||||||
|
|
||||||
|
using static Mono.Unix.Native.Syscall;
|
||||||
|
|
||||||
namespace BTCPayServer.Plugins.IntegrationTests.Monero;
|
namespace BTCPayServer.Plugins.IntegrationTests.Monero;
|
||||||
|
|
||||||
public static class IntegrationTestUtils
|
public static class IntegrationTestUtils
|
||||||
{
|
{
|
||||||
|
|
||||||
private static readonly ILogger Logger = LoggerFactory
|
private static readonly ILogger Logger = LoggerFactory
|
||||||
.Create(builder => builder.AddConsole())
|
.Create(builder => builder.AddConsole())
|
||||||
.CreateLogger("IntegrationTestUtils");
|
.CreateLogger("IntegrationTestUtils");
|
||||||
|
|
||||||
|
private static readonly string ContainerWalletDir =
|
||||||
|
Environment.GetEnvironmentVariable("BTCPAY_XMR_WALLET_DAEMON_WALLETDIR") ?? "/wallet";
|
||||||
|
|
||||||
public static async Task CleanUpAsync(PlaywrightTester playwrightTester)
|
public static async Task CleanUpAsync(PlaywrightTester playwrightTester)
|
||||||
{
|
{
|
||||||
MoneroRPCProvider moneroRpcProvider = playwrightTester.Server.PayTester.GetService<MoneroRPCProvider>();
|
MoneroRpcProvider moneroRpcProvider = playwrightTester.Server.PayTester.GetService<MoneroRpcProvider>();
|
||||||
if (moneroRpcProvider.IsAvailable("XMR"))
|
if (moneroRpcProvider.IsAvailable("XMR"))
|
||||||
{
|
{
|
||||||
await moneroRpcProvider.CloseWallet("XMR");
|
await moneroRpcProvider.CloseWallet("XMR");
|
||||||
|
|
@ -26,7 +32,7 @@ public static class IntegrationTestUtils
|
||||||
|
|
||||||
if (playwrightTester.Server.PayTester.InContainer)
|
if (playwrightTester.Server.PayTester.InContainer)
|
||||||
{
|
{
|
||||||
moneroRpcProvider.DeleteWallet();
|
DeleteWalletInContainer();
|
||||||
await DropDatabaseAsync(
|
await DropDatabaseAsync(
|
||||||
"btcpayserver",
|
"btcpayserver",
|
||||||
"Host=postgres;Port=5432;Username=postgres;Database=postgres");
|
"Host=postgres;Port=5432;Username=postgres;Database=postgres");
|
||||||
|
|
@ -62,6 +68,101 @@ public static class IntegrationTestUtils
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static async Task CopyWalletFilesToMoneroRpcDirAsync(PlaywrightTester playwrightTester, String walletDir)
|
||||||
|
{
|
||||||
|
Logger.LogInformation("Starting to copy wallet files");
|
||||||
|
if (playwrightTester.Server.PayTester.InContainer)
|
||||||
|
{
|
||||||
|
CopyWalletFilesInContainer(walletDir);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
await CopyWalletFilesToLocalDocker(walletDir);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void CopyWalletFilesInContainer(String walletDir)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
CopyWalletFile("wallet", walletDir);
|
||||||
|
CopyWalletFile("wallet.keys", walletDir);
|
||||||
|
CopyWalletFile("password", walletDir);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Logger.LogError(ex, "Failed to copy wallet files to the Monero directory.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void CopyWalletFile(string name, string walletDir)
|
||||||
|
{
|
||||||
|
var resourceWalletDir = Path.Combine(AppContext.BaseDirectory, "Resources", walletDir);
|
||||||
|
|
||||||
|
var src = Path.Combine(resourceWalletDir, name);
|
||||||
|
var dst = Path.Combine(ContainerWalletDir, name);
|
||||||
|
|
||||||
|
if (!File.Exists(src))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
File.Copy(src, dst, overwrite: true);
|
||||||
|
|
||||||
|
// monero ownership
|
||||||
|
if (chown(dst, 980, 980) == 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Logger.LogError("chown failed for {File}. errno={Errno}", dst, Stdlib.GetLastError());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static async Task CopyWalletFilesToLocalDocker(String walletDir)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var fullWalletDir = Path.Combine(AppContext.BaseDirectory, "Resources", walletDir);
|
||||||
|
|
||||||
|
await RunProcessAsync("docker",
|
||||||
|
$"cp \"{Path.Combine(fullWalletDir, "wallet")}\" xmr_wallet:/wallet/wallet");
|
||||||
|
|
||||||
|
await RunProcessAsync("docker",
|
||||||
|
$"cp \"{Path.Combine(fullWalletDir, "wallet.keys")}\" xmr_wallet:/wallet/wallet.keys");
|
||||||
|
|
||||||
|
await RunProcessAsync("docker",
|
||||||
|
$"cp \"{Path.Combine(fullWalletDir, "password")}\" xmr_wallet:/wallet/password");
|
||||||
|
|
||||||
|
await RunProcessAsync("docker",
|
||||||
|
"exec xmr_wallet chown monero:monero /wallet/wallet /wallet/wallet.keys /wallet/password");
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Logger.LogError(ex, "Failed to copy wallet files to the Monero directory.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static async Task RunProcessAsync(string fileName, string args)
|
||||||
|
{
|
||||||
|
var psi = new ProcessStartInfo
|
||||||
|
{
|
||||||
|
FileName = fileName,
|
||||||
|
Arguments = args,
|
||||||
|
RedirectStandardOutput = true,
|
||||||
|
RedirectStandardError = true,
|
||||||
|
UseShellExecute = false
|
||||||
|
};
|
||||||
|
|
||||||
|
using var process = Process.Start(psi)!;
|
||||||
|
await process.WaitForExitAsync();
|
||||||
|
|
||||||
|
if (process.ExitCode != 0)
|
||||||
|
{
|
||||||
|
throw new Exception(await process.StandardError.ReadToEndAsync());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static async Task RemoveWalletFromLocalDocker()
|
private static async Task RemoveWalletFromLocalDocker()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
|
|
@ -101,4 +202,33 @@ public static class IntegrationTestUtils
|
||||||
Logger.LogError(ex, "Wallet cleanup via Docker failed.");
|
Logger.LogError(ex, "Wallet cleanup via Docker failed.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void DeleteWalletInContainer()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var walletFile = Path.Combine(ContainerWalletDir, "wallet");
|
||||||
|
var keysFile = walletFile + ".keys";
|
||||||
|
var passwordFile = Path.Combine(ContainerWalletDir, "password");
|
||||||
|
|
||||||
|
if (File.Exists(walletFile))
|
||||||
|
{
|
||||||
|
File.Delete(walletFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (File.Exists(keysFile))
|
||||||
|
{
|
||||||
|
File.Delete(keysFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (File.Exists(passwordFile))
|
||||||
|
{
|
||||||
|
File.Delete(passwordFile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Logger.LogError(ex, "Failed to delete wallet files in directory {Dir}", ContainerWalletDir);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -44,7 +44,6 @@ public class MoneroPluginIntegrationTest(ITestOutputHelper helper) : MoneroAndBi
|
||||||
await s.Page.Locator("input#PrivateViewKey")
|
await s.Page.Locator("input#PrivateViewKey")
|
||||||
.FillAsync("1bfa03b0c78aa6bc8292cf160ec9875657d61e889c41d0ebe5c54fd3a2c4b40e");
|
.FillAsync("1bfa03b0c78aa6bc8292cf160ec9875657d61e889c41d0ebe5c54fd3a2c4b40e");
|
||||||
await s.Page.Locator("input#RestoreHeight").FillAsync("0");
|
await s.Page.Locator("input#RestoreHeight").FillAsync("0");
|
||||||
await s.Page.Locator("input#WalletPassword").FillAsync("pass123");
|
|
||||||
await s.Page.ClickAsync("button[name='command'][value='set-wallet-details']");
|
await s.Page.ClickAsync("button[name='command'][value='set-wallet-details']");
|
||||||
await s.Page.CheckAsync("#Enabled");
|
await s.Page.CheckAsync("#Enabled");
|
||||||
await s.Page.SelectOptionAsync("#SettlementConfirmationThresholdChoice", "2");
|
await s.Page.SelectOptionAsync("#SettlementConfirmationThresholdChoice", "2");
|
||||||
|
|
@ -119,7 +118,6 @@ public class MoneroPluginIntegrationTest(ITestOutputHelper helper) : MoneroAndBi
|
||||||
await s.Page.Locator("input#PrivateViewKey")
|
await s.Page.Locator("input#PrivateViewKey")
|
||||||
.FillAsync("1bfa03b0c78aa6bc8292cf160ec9875657d61e889c41d0ebe5c54fd3a2c4b40e");
|
.FillAsync("1bfa03b0c78aa6bc8292cf160ec9875657d61e889c41d0ebe5c54fd3a2c4b40e");
|
||||||
await s.Page.Locator("input#RestoreHeight").FillAsync("0");
|
await s.Page.Locator("input#RestoreHeight").FillAsync("0");
|
||||||
await s.Page.Locator("input#WalletPassword").FillAsync("pass123");
|
|
||||||
await s.Page.ClickAsync("button[name='command'][value='set-wallet-details']");
|
await s.Page.ClickAsync("button[name='command'][value='set-wallet-details']");
|
||||||
var errorText = await s.Page
|
var errorText = await s.Page
|
||||||
.Locator("div.validation-summary-errors li")
|
.Locator("div.validation-summary-errors li")
|
||||||
|
|
@ -136,12 +134,12 @@ public class MoneroPluginIntegrationTest(ITestOutputHelper helper) : MoneroAndBi
|
||||||
await using var s = CreatePlaywrightTester();
|
await using var s = CreatePlaywrightTester();
|
||||||
await s.StartAsync();
|
await s.StartAsync();
|
||||||
|
|
||||||
MoneroRPCProvider moneroRpcProvider = s.Server.PayTester.GetService<MoneroRPCProvider>();
|
MoneroRpcProvider moneroRpcProvider = s.Server.PayTester.GetService<MoneroRpcProvider>();
|
||||||
await moneroRpcProvider.WalletRpcClients["XMR"].SendCommandAsync<GenerateFromKeysRequest, GenerateFromKeysResponse>("generate_from_keys", new GenerateFromKeysRequest
|
await moneroRpcProvider.WalletRpcClients["XMR"].SendCommandAsync<GenerateFromKeysRequest, GenerateFromKeysResponse>("generate_from_keys", new GenerateFromKeysRequest
|
||||||
{
|
{
|
||||||
PrimaryAddress = "43Pnj6ZKGFTJhaLhiecSFfLfr64KPJZw7MyGH73T6PTDekBBvsTAaWEUSM4bmJqDuYLizhA13jQkMRPpz9VXBCBqQQb6y5L",
|
PrimaryAddress = "43Pnj6ZKGFTJhaLhiecSFfLfr64KPJZw7MyGH73T6PTDekBBvsTAaWEUSM4bmJqDuYLizhA13jQkMRPpz9VXBCBqQQb6y5L",
|
||||||
PrivateViewKey = "1bfa03b0c78aa6bc8292cf160ec9875657d61e889c41d0ebe5c54fd3a2c4b40e",
|
PrivateViewKey = "1bfa03b0c78aa6bc8292cf160ec9875657d61e889c41d0ebe5c54fd3a2c4b40e",
|
||||||
WalletFileName = "view_wallet",
|
WalletFileName = "wallet",
|
||||||
Password = ""
|
Password = ""
|
||||||
});
|
});
|
||||||
await moneroRpcProvider.CloseWallet("XMR");
|
await moneroRpcProvider.CloseWallet("XMR");
|
||||||
|
|
@ -154,13 +152,49 @@ public class MoneroPluginIntegrationTest(ITestOutputHelper helper) : MoneroAndBi
|
||||||
await s.Page.Locator("input#PrivateViewKey")
|
await s.Page.Locator("input#PrivateViewKey")
|
||||||
.FillAsync("1bfa03b0c78aa6bc8292cf160ec9875657d61e889c41d0ebe5c54fd3a2c4b40e");
|
.FillAsync("1bfa03b0c78aa6bc8292cf160ec9875657d61e889c41d0ebe5c54fd3a2c4b40e");
|
||||||
await s.Page.Locator("input#RestoreHeight").FillAsync("0");
|
await s.Page.Locator("input#RestoreHeight").FillAsync("0");
|
||||||
await s.Page.Locator("input#WalletPassword").FillAsync("pass123");
|
|
||||||
await s.Page.ClickAsync("button[name='command'][value='set-wallet-details']");
|
await s.Page.ClickAsync("button[name='command'][value='set-wallet-details']");
|
||||||
var errorText = await s.Page
|
var errorText = await s.Page
|
||||||
.Locator("div.validation-summary-errors li")
|
.Locator("div.validation-summary-errors li")
|
||||||
.InnerTextAsync();
|
.InnerTextAsync();
|
||||||
|
|
||||||
Assert.Equal("Could not generate view wallet from keys: Wallet already exists.", errorText);
|
Assert.Equal("Could not generate view wallet from keys: Wallet already exists.", errorText);
|
||||||
|
await IntegrationTestUtils.CleanUpAsync(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task ShouldLoadViewWalletOnStartUpIfExists()
|
||||||
|
{
|
||||||
|
await using var s = CreatePlaywrightTester();
|
||||||
|
await IntegrationTestUtils.CopyWalletFilesToMoneroRpcDirAsync(s, "wallet");
|
||||||
|
await s.StartAsync();
|
||||||
|
await s.RegisterNewUser(true);
|
||||||
|
await s.CreateNewStore();
|
||||||
|
await s.Page.Locator("a.nav-link[href*='monerolike/XMR']").ClickAsync();
|
||||||
|
|
||||||
|
var walletRpcIsAvailable = await s.Page
|
||||||
|
.Locator("li.list-group-item:text('Wallet RPC available: True')")
|
||||||
|
.InnerTextAsync();
|
||||||
|
|
||||||
|
Assert.Contains("Wallet RPC available: True", walletRpcIsAvailable);
|
||||||
|
|
||||||
|
await IntegrationTestUtils.CleanUpAsync(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task ShouldLoadViewWalletWithPasswordOnStartUpIfExists()
|
||||||
|
{
|
||||||
|
await using var s = CreatePlaywrightTester();
|
||||||
|
await IntegrationTestUtils.CopyWalletFilesToMoneroRpcDirAsync(s, "wallet_password");
|
||||||
|
await s.StartAsync();
|
||||||
|
await s.RegisterNewUser(true);
|
||||||
|
await s.CreateNewStore();
|
||||||
|
await s.Page.Locator("a.nav-link[href*='monerolike/XMR']").ClickAsync();
|
||||||
|
|
||||||
|
var walletRpcIsAvailable = await s.Page
|
||||||
|
.Locator("li.list-group-item:text('Wallet RPC available: True')")
|
||||||
|
.InnerTextAsync();
|
||||||
|
|
||||||
|
Assert.Contains("Wallet RPC available: True", walletRpcIsAvailable);
|
||||||
|
|
||||||
await IntegrationTestUtils.CleanUpAsync(s);
|
await IntegrationTestUtils.CleanUpAsync(s);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
BIN
BTCPayServer.Plugins.IntegrationTests/Resources/wallet/wallet
Normal file
BIN
BTCPayServer.Plugins.IntegrationTests/Resources/wallet/wallet
Normal file
Binary file not shown.
Binary file not shown.
|
|
@ -0,0 +1 @@
|
||||||
|
pass123
|
||||||
Binary file not shown.
Binary file not shown.
|
|
@ -1,6 +1,6 @@
|
||||||
MIT License
|
MIT License
|
||||||
|
|
||||||
Copyright (c) 2017-2025 btcpayserver
|
Copyright (c) 2017-2026 btcpayserver
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"Identifier": "BTCPayServer.Plugins.Monero",
|
"Identifier": "BTCPayServer.Plugins.Monero",
|
||||||
"Name": "BTCPay Server: Monero support plugin",
|
"Name": "BTCPay Server: Monero support plugin",
|
||||||
"Version": "1.0.1.0",
|
"Version": "1.1.0",
|
||||||
"Description": "This plugin extends BTCPay Server to enable users to receive payments via Monero.",
|
"Description": "This plugin extends BTCPay Server to enable users to receive payments via Monero.",
|
||||||
"SystemPlugin": false,
|
"SystemPlugin": false,
|
||||||
"Dependencies": [
|
"Dependencies": [
|
||||||
|
|
|
||||||
|
|
@ -33,12 +33,12 @@ namespace BTCPayServer.Plugins.Monero.Controllers
|
||||||
{
|
{
|
||||||
private readonly MoneroLikeConfiguration _MoneroLikeConfiguration;
|
private readonly MoneroLikeConfiguration _MoneroLikeConfiguration;
|
||||||
private readonly StoreRepository _StoreRepository;
|
private readonly StoreRepository _StoreRepository;
|
||||||
private readonly MoneroRPCProvider _MoneroRpcProvider;
|
private readonly MoneroRpcProvider _MoneroRpcProvider;
|
||||||
private readonly PaymentMethodHandlerDictionary _handlers;
|
private readonly PaymentMethodHandlerDictionary _handlers;
|
||||||
private IStringLocalizer StringLocalizer { get; }
|
private IStringLocalizer StringLocalizer { get; }
|
||||||
|
|
||||||
public UIMoneroLikeStoreController(MoneroLikeConfiguration moneroLikeConfiguration,
|
public UIMoneroLikeStoreController(MoneroLikeConfiguration moneroLikeConfiguration,
|
||||||
StoreRepository storeRepository, MoneroRPCProvider moneroRpcProvider,
|
StoreRepository storeRepository, MoneroRpcProvider moneroRpcProvider,
|
||||||
PaymentMethodHandlerDictionary handlers,
|
PaymentMethodHandlerDictionary handlers,
|
||||||
IStringLocalizer stringLocalizer)
|
IStringLocalizer stringLocalizer)
|
||||||
{
|
{
|
||||||
|
|
@ -219,9 +219,8 @@ namespace BTCPayServer.Plugins.Monero.Controllers
|
||||||
{
|
{
|
||||||
PrimaryAddress = viewModel.PrimaryAddress,
|
PrimaryAddress = viewModel.PrimaryAddress,
|
||||||
PrivateViewKey = viewModel.PrivateViewKey,
|
PrivateViewKey = viewModel.PrivateViewKey,
|
||||||
WalletFileName = "view_wallet",
|
WalletFileName = "wallet",
|
||||||
RestoreHeight = viewModel.RestoreHeight,
|
RestoreHeight = viewModel.RestoreHeight
|
||||||
Password = viewModel.WalletPassword
|
|
||||||
});
|
});
|
||||||
if (response?.Error != null)
|
if (response?.Error != null)
|
||||||
{
|
{
|
||||||
|
|
@ -286,7 +285,7 @@ namespace BTCPayServer.Plugins.Monero.Controllers
|
||||||
|
|
||||||
public class MoneroLikePaymentMethodViewModel : IValidatableObject
|
public class MoneroLikePaymentMethodViewModel : IValidatableObject
|
||||||
{
|
{
|
||||||
public MoneroRPCProvider.MoneroLikeSummary Summary { get; set; }
|
public MoneroRpcProvider.MoneroLikeSummary Summary { get; set; }
|
||||||
public string CryptoCode { get; set; }
|
public string CryptoCode { get; set; }
|
||||||
public string NewAccountLabel { get; set; }
|
public string NewAccountLabel { get; set; }
|
||||||
public long AccountIndex { get; set; }
|
public long AccountIndex { get; set; }
|
||||||
|
|
@ -300,8 +299,6 @@ namespace BTCPayServer.Plugins.Monero.Controllers
|
||||||
public string PrivateViewKey { get; set; }
|
public string PrivateViewKey { get; set; }
|
||||||
[Display(Name = "Restore Height")]
|
[Display(Name = "Restore Height")]
|
||||||
public int RestoreHeight { get; set; }
|
public int RestoreHeight { get; set; }
|
||||||
[Display(Name = "Wallet Password")]
|
|
||||||
public string WalletPassword { get; set; }
|
|
||||||
[Display(Name = "Consider the invoice settled when the payment transaction …")]
|
[Display(Name = "Consider the invoice settled when the payment transaction …")]
|
||||||
public MoneroLikeSettlementThresholdChoice SettlementConfirmationThresholdChoice { get; set; }
|
public MoneroLikeSettlementThresholdChoice SettlementConfirmationThresholdChoice { get; set; }
|
||||||
[Display(Name = "Required Confirmations"), Range(0, 100)]
|
[Display(Name = "Required Confirmations"), Range(0, 100)]
|
||||||
|
|
|
||||||
|
|
@ -75,9 +75,10 @@ public class MoneroPlugin : BaseBTCPayServerPlugin
|
||||||
PreAuthenticate = true
|
PreAuthenticate = true
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
services.AddSingleton<MoneroRPCProvider>();
|
services.AddSingleton<MoneroRpcProvider>();
|
||||||
services.AddHostedService<MoneroLikeSummaryUpdaterHostedService>();
|
services.AddHostedService<MoneroLikeSummaryUpdaterHostedService>();
|
||||||
services.AddHostedService<MoneroListener>();
|
services.AddHostedService<MoneroListener>();
|
||||||
|
services.AddHostedService<MoneroLoadUpService>();
|
||||||
services.AddSingleton(provider =>
|
services.AddSingleton(provider =>
|
||||||
(IPaymentMethodHandler)ActivatorUtilities.CreateInstance(provider, typeof(MoneroLikePaymentMethodHandler), network));
|
(IPaymentMethodHandler)ActivatorUtilities.CreateInstance(provider, typeof(MoneroLikePaymentMethodHandler), network));
|
||||||
services.AddSingleton(provider =>
|
services.AddSingleton(provider =>
|
||||||
|
|
|
||||||
|
|
@ -17,11 +17,11 @@ namespace BTCPayServer.Plugins.Monero.Payments
|
||||||
private readonly MoneroLikeSpecificBtcPayNetwork _network;
|
private readonly MoneroLikeSpecificBtcPayNetwork _network;
|
||||||
public MoneroLikeSpecificBtcPayNetwork Network => _network;
|
public MoneroLikeSpecificBtcPayNetwork Network => _network;
|
||||||
public JsonSerializer Serializer { get; }
|
public JsonSerializer Serializer { get; }
|
||||||
private readonly MoneroRPCProvider _moneroRpcProvider;
|
private readonly MoneroRpcProvider _moneroRpcProvider;
|
||||||
|
|
||||||
public PaymentMethodId PaymentMethodId { get; }
|
public PaymentMethodId PaymentMethodId { get; }
|
||||||
|
|
||||||
public MoneroLikePaymentMethodHandler(MoneroLikeSpecificBtcPayNetwork network, MoneroRPCProvider moneroRpcProvider)
|
public MoneroLikePaymentMethodHandler(MoneroLikeSpecificBtcPayNetwork network, MoneroRpcProvider moneroRpcProvider)
|
||||||
{
|
{
|
||||||
PaymentMethodId = PaymentTypes.CHAIN.GetPaymentMethodId(network.CryptoCode);
|
PaymentMethodId = PaymentTypes.CHAIN.GetPaymentMethodId(network.CryptoCode);
|
||||||
_network = network;
|
_network = network;
|
||||||
|
|
|
||||||
10
Plugins/Monero/RPC/Models/OpenWalletRequest.cs
Normal file
10
Plugins/Monero/RPC/Models/OpenWalletRequest.cs
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
namespace BTCPayServer.Plugins.Monero.RPC.Models
|
||||||
|
{
|
||||||
|
public class OpenWalletRequest
|
||||||
|
{
|
||||||
|
[JsonProperty("filename")] public string Filename { get; set; }
|
||||||
|
[JsonProperty("password")] public string Password { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
12
Plugins/Monero/RPC/Models/OpenWalletResponse.cs
Normal file
12
Plugins/Monero/RPC/Models/OpenWalletResponse.cs
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
namespace BTCPayServer.Plugins.Monero.RPC.Models
|
||||||
|
{
|
||||||
|
public class OpenWalletResponse
|
||||||
|
{
|
||||||
|
[JsonProperty("id")] public string Id { get; set; }
|
||||||
|
[JsonProperty("jsonrpc")] public string Jsonrpc { get; set; }
|
||||||
|
[JsonProperty("result")] public object Result { get; set; }
|
||||||
|
[JsonProperty("error")] public ErrorResponse Error { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -12,13 +12,13 @@ namespace BTCPayServer.Plugins.Monero.Services
|
||||||
{
|
{
|
||||||
public class MoneroLikeSummaryUpdaterHostedService : IHostedService
|
public class MoneroLikeSummaryUpdaterHostedService : IHostedService
|
||||||
{
|
{
|
||||||
private readonly MoneroRPCProvider _MoneroRpcProvider;
|
private readonly MoneroRpcProvider _MoneroRpcProvider;
|
||||||
private readonly MoneroLikeConfiguration _moneroLikeConfiguration;
|
private readonly MoneroLikeConfiguration _moneroLikeConfiguration;
|
||||||
|
|
||||||
public Logs Logs { get; }
|
public Logs Logs { get; }
|
||||||
|
|
||||||
private CancellationTokenSource _Cts;
|
private CancellationTokenSource _Cts;
|
||||||
public MoneroLikeSummaryUpdaterHostedService(MoneroRPCProvider moneroRpcProvider, MoneroLikeConfiguration moneroLikeConfiguration, Logs logs)
|
public MoneroLikeSummaryUpdaterHostedService(MoneroRpcProvider moneroRpcProvider, MoneroLikeConfiguration moneroLikeConfiguration, Logs logs)
|
||||||
{
|
{
|
||||||
_MoneroRpcProvider = moneroRpcProvider;
|
_MoneroRpcProvider = moneroRpcProvider;
|
||||||
_moneroLikeConfiguration = moneroLikeConfiguration;
|
_moneroLikeConfiguration = moneroLikeConfiguration;
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,7 @@ namespace BTCPayServer.Plugins.Monero.Services
|
||||||
{
|
{
|
||||||
private readonly InvoiceRepository _invoiceRepository;
|
private readonly InvoiceRepository _invoiceRepository;
|
||||||
private readonly EventAggregator _eventAggregator;
|
private readonly EventAggregator _eventAggregator;
|
||||||
private readonly MoneroRPCProvider _moneroRpcProvider;
|
private readonly MoneroRpcProvider _moneroRpcProvider;
|
||||||
private readonly MoneroLikeConfiguration _MoneroLikeConfiguration;
|
private readonly MoneroLikeConfiguration _MoneroLikeConfiguration;
|
||||||
private readonly BTCPayNetworkProvider _networkProvider;
|
private readonly BTCPayNetworkProvider _networkProvider;
|
||||||
private readonly ILogger<MoneroListener> _logger;
|
private readonly ILogger<MoneroListener> _logger;
|
||||||
|
|
@ -39,7 +39,7 @@ namespace BTCPayServer.Plugins.Monero.Services
|
||||||
|
|
||||||
public MoneroListener(InvoiceRepository invoiceRepository,
|
public MoneroListener(InvoiceRepository invoiceRepository,
|
||||||
EventAggregator eventAggregator,
|
EventAggregator eventAggregator,
|
||||||
MoneroRPCProvider moneroRpcProvider,
|
MoneroRpcProvider moneroRpcProvider,
|
||||||
MoneroLikeConfiguration moneroLikeConfiguration,
|
MoneroLikeConfiguration moneroLikeConfiguration,
|
||||||
BTCPayNetworkProvider networkProvider,
|
BTCPayNetworkProvider networkProvider,
|
||||||
ILogger<MoneroListener> logger,
|
ILogger<MoneroListener> logger,
|
||||||
|
|
@ -62,12 +62,12 @@ namespace BTCPayServer.Plugins.Monero.Services
|
||||||
{
|
{
|
||||||
base.SubscribeToEvents();
|
base.SubscribeToEvents();
|
||||||
Subscribe<MoneroEvent>();
|
Subscribe<MoneroEvent>();
|
||||||
Subscribe<MoneroRPCProvider.MoneroDaemonStateChange>();
|
Subscribe<MoneroRpcProvider.MoneroDaemonStateChange>();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override async Task ProcessEvent(object evt, CancellationToken cancellationToken)
|
protected override async Task ProcessEvent(object evt, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
if (evt is MoneroRPCProvider.MoneroDaemonStateChange stateChange)
|
if (evt is MoneroRpcProvider.MoneroDaemonStateChange stateChange)
|
||||||
{
|
{
|
||||||
if (_moneroRpcProvider.IsAvailable(stateChange.CryptoCode))
|
if (_moneroRpcProvider.IsAvailable(stateChange.CryptoCode))
|
||||||
{
|
{
|
||||||
|
|
|
||||||
78
Plugins/Monero/Services/MoneroLoadUpService.cs
Normal file
78
Plugins/Monero/Services/MoneroLoadUpService.cs
Normal file
|
|
@ -0,0 +1,78 @@
|
||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
using BTCPayServer.Plugins.Monero.RPC.Models;
|
||||||
|
|
||||||
|
using Microsoft.Extensions.Hosting;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
|
namespace BTCPayServer.Plugins.Monero.Services;
|
||||||
|
|
||||||
|
public class MoneroLoadUpService : IHostedService
|
||||||
|
{
|
||||||
|
private const string CryptoCode = "XMR";
|
||||||
|
private readonly ILogger<MoneroLoadUpService> _logger;
|
||||||
|
private readonly MoneroRpcProvider _moneroRpcProvider;
|
||||||
|
|
||||||
|
public MoneroLoadUpService(ILogger<MoneroLoadUpService> logger, MoneroRpcProvider moneroRpcProvider)
|
||||||
|
{
|
||||||
|
_moneroRpcProvider = moneroRpcProvider;
|
||||||
|
_logger = logger;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Obsolete("Remove optional password parameter")]
|
||||||
|
public async Task StartAsync(CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_logger.LogInformation("Attempt to load existing wallet");
|
||||||
|
|
||||||
|
string walletDir = _moneroRpcProvider.GetWalletDirectory(CryptoCode);
|
||||||
|
if (!string.IsNullOrEmpty(walletDir))
|
||||||
|
{
|
||||||
|
string password = await TryToGetPassword(walletDir, cancellationToken);
|
||||||
|
|
||||||
|
await _moneroRpcProvider.WalletRpcClients[CryptoCode]
|
||||||
|
.SendCommandAsync<OpenWalletRequest, OpenWalletResponse>("open_wallet",
|
||||||
|
new OpenWalletRequest { Filename = "wallet", Password = password }, cancellationToken);
|
||||||
|
|
||||||
|
await _moneroRpcProvider.UpdateSummary(CryptoCode);
|
||||||
|
_logger.LogInformation("Existing wallet successfully loaded");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_logger.LogInformation("No wallet directory configured, skipping wallet migration");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError("Failed to load {CryptoCode} wallet. Error Message: {ErrorMessage}", CryptoCode,
|
||||||
|
ex.Message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Obsolete("Password is obsolete due to the inability to fully separate the password file from the wallet file.")]
|
||||||
|
private async Task<string> TryToGetPassword(string walletDir, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
string password = "";
|
||||||
|
string passwordFile = Path.Combine(walletDir, "password");
|
||||||
|
if (File.Exists(passwordFile))
|
||||||
|
{
|
||||||
|
password = await File.ReadAllTextAsync(passwordFile, cancellationToken);
|
||||||
|
password = password.Trim();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_logger.LogInformation("No password file found - ignoring");
|
||||||
|
}
|
||||||
|
|
||||||
|
return password;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task StopAsync(CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using System.Collections.Immutable;
|
using System.Collections.Immutable;
|
||||||
using System.IO;
|
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
|
@ -9,32 +8,25 @@ using BTCPayServer.Plugins.Monero.Configuration;
|
||||||
using BTCPayServer.Plugins.Monero.RPC;
|
using BTCPayServer.Plugins.Monero.RPC;
|
||||||
using BTCPayServer.Plugins.Monero.RPC.Models;
|
using BTCPayServer.Plugins.Monero.RPC.Models;
|
||||||
|
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
|
|
||||||
using NBitcoin;
|
using NBitcoin;
|
||||||
|
|
||||||
namespace BTCPayServer.Plugins.Monero.Services
|
namespace BTCPayServer.Plugins.Monero.Services
|
||||||
{
|
{
|
||||||
public class MoneroRPCProvider
|
public class MoneroRpcProvider
|
||||||
{
|
{
|
||||||
private readonly MoneroLikeConfiguration _moneroLikeConfiguration;
|
private readonly MoneroLikeConfiguration _moneroLikeConfiguration;
|
||||||
private readonly EventAggregator _eventAggregator;
|
private readonly EventAggregator _eventAggregator;
|
||||||
public ImmutableDictionary<string, JsonRpcClient> DaemonRpcClients;
|
public ImmutableDictionary<string, JsonRpcClient> DaemonRpcClients;
|
||||||
public ImmutableDictionary<string, JsonRpcClient> WalletRpcClients;
|
public ImmutableDictionary<string, JsonRpcClient> WalletRpcClients;
|
||||||
private readonly ILogger<MoneroRPCProvider> _logger;
|
|
||||||
|
|
||||||
private readonly ConcurrentDictionary<string, MoneroLikeSummary> _summaries = new();
|
public ConcurrentDictionary<string, MoneroLikeSummary> Summaries { get; } = new();
|
||||||
|
|
||||||
public ConcurrentDictionary<string, MoneroLikeSummary> Summaries => _summaries;
|
public MoneroRpcProvider(MoneroLikeConfiguration moneroLikeConfiguration,
|
||||||
|
|
||||||
public MoneroRPCProvider(MoneroLikeConfiguration moneroLikeConfiguration,
|
|
||||||
EventAggregator eventAggregator,
|
EventAggregator eventAggregator,
|
||||||
ILogger<MoneroRPCProvider> logger,
|
|
||||||
IHttpClientFactory httpClientFactory)
|
IHttpClientFactory httpClientFactory)
|
||||||
{
|
{
|
||||||
_moneroLikeConfiguration = moneroLikeConfiguration;
|
_moneroLikeConfiguration = moneroLikeConfiguration;
|
||||||
_eventAggregator = eventAggregator;
|
_eventAggregator = eventAggregator;
|
||||||
_logger = logger;
|
|
||||||
DaemonRpcClients =
|
DaemonRpcClients =
|
||||||
_moneroLikeConfiguration.MoneroLikeConfigurationItems.ToImmutableDictionary(pair => pair.Key,
|
_moneroLikeConfiguration.MoneroLikeConfigurationItems.ToImmutableDictionary(pair => pair.Key,
|
||||||
pair => new JsonRpcClient(pair.Value.DaemonRpcUri, pair.Value.Username, pair.Value.Password,
|
pair => new JsonRpcClient(pair.Value.DaemonRpcUri, pair.Value.Username, pair.Value.Password,
|
||||||
|
|
@ -49,7 +41,7 @@ namespace BTCPayServer.Plugins.Monero.Services
|
||||||
public bool IsAvailable(string cryptoCode)
|
public bool IsAvailable(string cryptoCode)
|
||||||
{
|
{
|
||||||
cryptoCode = cryptoCode.ToUpperInvariant();
|
cryptoCode = cryptoCode.ToUpperInvariant();
|
||||||
return _summaries.ContainsKey(cryptoCode) && IsAvailable(_summaries[cryptoCode]);
|
return Summaries.ContainsKey(cryptoCode) && IsAvailable(Summaries[cryptoCode]);
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool IsAvailable(MoneroLikeSummary summary)
|
private bool IsAvailable(MoneroLikeSummary summary)
|
||||||
|
|
@ -69,43 +61,12 @@ namespace BTCPayServer.Plugins.Monero.Services
|
||||||
"close_wallet", JsonRpcClient.NoRequestModel.Instance);
|
"close_wallet", JsonRpcClient.NoRequestModel.Instance);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void DeleteWallet()
|
public string GetWalletDirectory(string cryptoCode)
|
||||||
{
|
{
|
||||||
if (!_moneroLikeConfiguration.MoneroLikeConfigurationItems.TryGetValue("XMR", out var configItem))
|
cryptoCode = cryptoCode.ToUpperInvariant();
|
||||||
{
|
return !_moneroLikeConfiguration.MoneroLikeConfigurationItems.TryGetValue(cryptoCode, out var configItem)
|
||||||
_logger.LogWarning("DeleteWallet: No XMR configuration found.");
|
? null
|
||||||
return;
|
: configItem.WalletDirectory;
|
||||||
}
|
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(configItem.WalletDirectory))
|
|
||||||
{
|
|
||||||
_logger.LogWarning("DeleteWallet: WalletDirectory is null or empty for XMR configuration.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var walletFile = Path.Combine(configItem.WalletDirectory, "view_wallet");
|
|
||||||
var keysFile = walletFile + ".keys";
|
|
||||||
var passwordFile = Path.Combine(configItem.WalletDirectory, "password");
|
|
||||||
|
|
||||||
if (File.Exists(walletFile))
|
|
||||||
{
|
|
||||||
File.Delete(walletFile);
|
|
||||||
}
|
|
||||||
if (File.Exists(keysFile))
|
|
||||||
{
|
|
||||||
File.Delete(keysFile);
|
|
||||||
}
|
|
||||||
if (File.Exists(passwordFile))
|
|
||||||
{
|
|
||||||
File.Delete(passwordFile);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
_logger.LogError(ex, "Failed to delete wallet files in directory {Dir}",
|
|
||||||
configItem.WalletDirectory);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<MoneroLikeSummary> UpdateSummary(string cryptoCode)
|
public async Task<MoneroLikeSummary> UpdateSummary(string cryptoCode)
|
||||||
|
|
@ -146,9 +107,9 @@ namespace BTCPayServer.Plugins.Monero.Services
|
||||||
summary.WalletAvailable = false;
|
summary.WalletAvailable = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
var changed = !_summaries.ContainsKey(cryptoCode) || IsAvailable(cryptoCode) != IsAvailable(summary);
|
var changed = !Summaries.ContainsKey(cryptoCode) || IsAvailable(cryptoCode) != IsAvailable(summary);
|
||||||
|
|
||||||
_summaries.AddOrReplace(cryptoCode, summary);
|
Summaries.AddOrReplace(cryptoCode, summary);
|
||||||
if (changed)
|
if (changed)
|
||||||
{
|
{
|
||||||
_eventAggregator.Publish(new MoneroDaemonStateChange() { Summary = summary, CryptoCode = cryptoCode });
|
_eventAggregator.Publish(new MoneroDaemonStateChange() { Summary = summary, CryptoCode = cryptoCode });
|
||||||
|
|
@ -9,9 +9,9 @@ namespace BTCPayServer.Plugins.Monero.Services
|
||||||
{
|
{
|
||||||
public class MoneroSyncSummaryProvider : ISyncSummaryProvider
|
public class MoneroSyncSummaryProvider : ISyncSummaryProvider
|
||||||
{
|
{
|
||||||
private readonly MoneroRPCProvider _moneroRpcProvider;
|
private readonly MoneroRpcProvider _moneroRpcProvider;
|
||||||
|
|
||||||
public MoneroSyncSummaryProvider(MoneroRPCProvider moneroRpcProvider)
|
public MoneroSyncSummaryProvider(MoneroRpcProvider moneroRpcProvider)
|
||||||
{
|
{
|
||||||
_moneroRpcProvider = moneroRpcProvider;
|
_moneroRpcProvider = moneroRpcProvider;
|
||||||
}
|
}
|
||||||
|
|
@ -42,6 +42,6 @@ namespace BTCPayServer.Plugins.Monero.Services
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public MoneroRPCProvider.MoneroLikeSummary Summary { get; set; }
|
public MoneroRpcProvider.MoneroLikeSummary Summary { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -52,11 +52,6 @@
|
||||||
<input type="number" class="form-control" asp-for="RestoreHeight" required>
|
<input type="number" class="form-control" asp-for="RestoreHeight" required>
|
||||||
<span asp-validation-for="RestoreHeight" class="text-danger"></span>
|
<span asp-validation-for="RestoreHeight" class="text-danger"></span>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group p-2">
|
|
||||||
<label asp-for="WalletPassword" class="form-label">Wallet Password</label>
|
|
||||||
<input type="password" class="form-control" asp-for="WalletPassword" required>
|
|
||||||
<span asp-validation-for="WalletPassword" class="text-danger"></span>
|
|
||||||
</div>
|
|
||||||
<div class="card-footer text-right">
|
<div class="card-footer text-right">
|
||||||
<button name="command" value="set-wallet-details" class="btn btn-secondary" type="submit">Set Wallet Details</button>
|
<button name="command" value="set-wallet-details" class="btn btn-secondary" type="submit">Set Wallet Details</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,5 @@
|
||||||
@using BTCPayServer
|
|
||||||
@using BTCPayServer.Data
|
|
||||||
@using BTCPayServer.Plugins.Monero.Services
|
@using BTCPayServer.Plugins.Monero.Services
|
||||||
@using Microsoft.AspNetCore.Identity
|
@inject MoneroRpcProvider MoneroRpcProvider
|
||||||
@inject MoneroRPCProvider MoneroRpcProvider
|
|
||||||
@inject SignInManager<ApplicationUser> SignInManager;
|
@inject SignInManager<ApplicationUser> SignInManager;
|
||||||
|
|
||||||
@if (SignInManager.IsSignedIn(User) && User.IsInRole(Roles.ServerAdmin) && MoneroRpcProvider.Summaries.Any())
|
@if (SignInManager.IsSignedIn(User) && User.IsInRole(Roles.ServerAdmin) && MoneroRpcProvider.Summaries.Any())
|
||||||
|
|
|
||||||
|
|
@ -4,10 +4,10 @@
|
||||||
|
|
||||||
We currently support the following versions of the Monero plugin for BTCPayServer:
|
We currently support the following versions of the Monero plugin for BTCPayServer:
|
||||||
|
|
||||||
| Version | Supported |
|
| Version | Supported |
|
||||||
| ------- | ------------------ |
|
|---------|-------------|
|
||||||
| 2.x | ✅ Yes |
|
| 1.1.x | ✅ Yes |
|
||||||
| 1.x | ❌ No |
|
| 1.0.x | ❌ No |
|
||||||
|
|
||||||
## Reporting a Vulnerability
|
## Reporting a Vulnerability
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1 +1 @@
|
||||||
Subproject commit 335c906b811b8f131a5973ef3e9cdcac99017654
|
Subproject commit 3f52aa7fd9faf8bb99343572b3dae305037db2ba
|
||||||
Loading…
Add table
Reference in a new issue