Skip to content

Commit d35783c

Browse files
committed
Simulate subaddress generate duplication
1 parent 76e2f11 commit d35783c

2 files changed

Lines changed: 121 additions & 65 deletions

File tree

BTCPayServer.Plugins.IntegrationTests/Monero/IntegrationTestUtils.cs

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ public static class IntegrationTestUtils
2727
private static readonly string ContainerWalletDir =
2828
Environment.GetEnvironmentVariable("BTCPAY_XMR_WALLET_DAEMON_WALLETDIR") ?? "/wallet";
2929

30-
public static async Task CleanUpAsync(PlaywrightTester playwrightTester)
30+
public static async Task CleanUpAsync(PlaywrightTester playwrightTester, bool dropDatabase = true)
3131
{
3232
MoneroRpcProvider moneroRpcProvider = playwrightTester.Server.PayTester.GetService<MoneroRpcProvider>();
3333
if (moneroRpcProvider.IsAvailable("XMR"))
@@ -38,16 +38,22 @@ public static async Task CleanUpAsync(PlaywrightTester playwrightTester)
3838
if (RunsInContainer)
3939
{
4040
DeleteWalletInContainer();
41-
await DropDatabaseAsync(
42-
"btcpayserver",
43-
"Host=postgres;Port=5432;Username=postgres;Database=postgres");
41+
if (dropDatabase)
42+
{
43+
await DropDatabaseAsync(
44+
"btcpayserver",
45+
"Host=postgres;Port=5432;Username=postgres;Database=postgres");
46+
}
4447
}
4548
else
4649
{
4750
await RemoveWalletFromLocalDocker();
48-
await DropDatabaseAsync(
49-
"btcpayserver",
50-
"Host=localhost;Port=39372;Username=postgres;Database=postgres");
51+
if (dropDatabase)
52+
{
53+
await DropDatabaseAsync(
54+
"btcpayserver",
55+
"Host=localhost;Port=39372;Username=postgres;Database=postgres");
56+
}
5157
}
5258
}
5359

BTCPayServer.Plugins.IntegrationTests/Monero/MoneroPluginIntegrationTest.cs

Lines changed: 108 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
using BTCPayServer.Plugins.Monero.Services;
33
using BTCPayServer.Rating;
44
using BTCPayServer.Services.Rates;
5+
using BTCPayServer.Tests;
56
using BTCPayServer.Tests.Mocks;
67

78
using Xunit;
@@ -17,56 +18,7 @@ public async Task ShouldEnableMoneroPluginSuccessfully()
1718
await using var s = CreatePlaywrightTester();
1819
await s.StartAsync();
1920

20-
if (s.Server.PayTester.MockRates)
21-
{
22-
var rateProviderFactory = s.Server.PayTester.GetService<RateProviderFactory>();
23-
rateProviderFactory.Providers.Clear();
24-
25-
var coinAverageMock = new MockRateProvider();
26-
coinAverageMock.ExchangeRates.Add(new PairRate(CurrencyPair.Parse("BTC_USD"), new BidAsk(5000m)));
27-
coinAverageMock.ExchangeRates.Add(new PairRate(CurrencyPair.Parse("BTC_EUR"), new BidAsk(4000m)));
28-
coinAverageMock.ExchangeRates.Add(new PairRate(CurrencyPair.Parse("XMR_BTC"), new BidAsk(4500m)));
29-
rateProviderFactory.Providers.Add("coingecko", coinAverageMock);
30-
31-
var kraken = new MockRateProvider();
32-
kraken.ExchangeRates.Add(new PairRate(CurrencyPair.Parse("BTC_USD"), new BidAsk(0.1m)));
33-
kraken.ExchangeRates.Add(new PairRate(CurrencyPair.Parse("XMR_USD"), new BidAsk(0.1m)));
34-
kraken.ExchangeRates.Add(new PairRate(CurrencyPair.Parse("XMR_BTC"), new BidAsk(0.1m)));
35-
rateProviderFactory.Providers.Add("kraken", kraken);
36-
}
37-
38-
await s.RegisterNewUser(true);
39-
await s.CreateNewStore(preferredExchange: "Kraken");
40-
await s.Page.Locator("a.nav-link[href*='monerolike/XMR']").ClickAsync();
41-
await s.Page.Locator("input#PrimaryAddress")
42-
.FillAsync(
43-
"43Pnj6ZKGFTJhaLhiecSFfLfr64KPJZw7MyGH73T6PTDekBBvsTAaWEUSM4bmJqDuYLizhA13jQkMRPpz9VXBCBqQQb6y5L");
44-
await s.Page.Locator("input#PrivateViewKey")
45-
.FillAsync("1bfa03b0c78aa6bc8292cf160ec9875657d61e889c41d0ebe5c54fd3a2c4b40e");
46-
await s.Page.Locator("input#RestoreHeight").FillAsync("0");
47-
await s.Page.ClickAsync("button[name='command'][value='set-wallet-details']");
48-
var message = await s.Page
49-
.GetByText("View-only wallet created. The wallet will soon become available.")
50-
.InnerTextAsync();
51-
Assert.Contains("View-only wallet created", message);
52-
await s.Page.CheckAsync("#Enabled");
53-
await s.Page.SelectOptionAsync("#SettlementConfirmationThresholdChoice", "2");
54-
await s.Page.ClickAsync("#SaveButton");
55-
56-
// Set rate provider
57-
await s.Page.Locator("#menu-item-General").ClickAsync();
58-
await s.Page.Locator("#menu-item-Rates").ClickAsync();
59-
await s.Page.FillAsync("#DefaultCurrencyPairs", "BTC_USD,XMR_USD,XMR_BTC");
60-
await s.Page.SelectOptionAsync("#PrimarySource_PreferredExchange", "kraken");
61-
await s.Page.Locator("#page-primary").ClickAsync();
62-
63-
// Generate a new invoice
64-
await s.Page.Locator("a.nav-link[href*='invoices']").ClickAsync();
65-
await s.Page.Locator("#page-primary").ClickAsync();
66-
await s.Page.FillAsync("#Amount", "4.20");
67-
await s.Page.FillAsync("#BuyerEmail", "monero@monero.com");
68-
await Task.Delay(TimeSpan.FromSeconds(25)); // wallet-rpc needs some time to sync. refactor this later
69-
await s.Page.Locator("#page-primary").ClickAsync();
21+
await SetupWalletAndGenerateInvoice(s);
7022

7123
// View the invoice
7224
var href = await s.Page.Locator("a[href^='/i/']").GetAttributeAsync("href");
@@ -104,6 +56,46 @@ await s.Page.Locator("input#PrivateViewKey")
10456
await IntegrationTestUtils.CleanUpAsync(s);
10557
}
10658

59+
[Fact]
60+
public async Task ShouldAdvanceSubaddressIndexWhenOutOfSync()
61+
{
62+
await using var s = CreatePlaywrightTester();
63+
await s.StartAsync();
64+
65+
await SetupWalletAndGenerateInvoice(s);
66+
67+
await IntegrationTestUtils.CleanUpAsync(s, false);
68+
69+
MoneroRpcProvider moneroRpcProvider = s.Server.PayTester.GetService<MoneroRpcProvider>();
70+
await moneroRpcProvider.WalletRpcClients["XMR"]
71+
.SendCommandAsync<GenerateFromKeysRequest, GenerateFromKeysResponse>("generate_from_keys",
72+
new GenerateFromKeysRequest
73+
{
74+
PrimaryAddress =
75+
"43Pnj6ZKGFTJhaLhiecSFfLfr64KPJZw7MyGH73T6PTDekBBvsTAaWEUSM4bmJqDuYLizhA13jQkMRPpz9VXBCBqQQb6y5L",
76+
PrivateViewKey = "1bfa03b0c78aa6bc8292cf160ec9875657d61e889c41d0ebe5c54fd3a2c4b40e",
77+
WalletFileName = "wallet",
78+
Password = ""
79+
});
80+
81+
await s.Page.Locator("a.nav-link[href*='monerolike/XMR']").ClickAsync();
82+
83+
var invoiceCount = Random.Shared.Next(1, 11); // 1 to 10 invoices
84+
for (int i = 0; i < invoiceCount; i++)
85+
{
86+
await s.Page.Locator("a.nav-link[href*='invoices']").ClickAsync();
87+
await s.Page.Locator("#page-primary").ClickAsync();
88+
await s.Page.FillAsync("#Amount", "4.20");
89+
await s.Page.FillAsync("#BuyerEmail", $"monero{i}@monero.com"); // make email unique
90+
await s.Page.Locator("#page-primary").ClickAsync();
91+
}
92+
93+
// Fails to view the last invoice
94+
await s.Page.Locator("a[href^='/i/']").GetAttributeAsync("href");
95+
96+
await IntegrationTestUtils.CleanUpAsync(s);
97+
}
98+
10799
[Fact]
108100
public async Task ShouldFailWhenWrongPrimaryAddress()
109101
{
@@ -135,20 +127,24 @@ public async Task ShouldFailWhenWalletFileAlreadyExists()
135127
await s.StartAsync();
136128

137129
MoneroRpcProvider moneroRpcProvider = s.Server.PayTester.GetService<MoneroRpcProvider>();
138-
await moneroRpcProvider.WalletRpcClients["XMR"].SendCommandAsync<GenerateFromKeysRequest, GenerateFromKeysResponse>("generate_from_keys", new GenerateFromKeysRequest
139-
{
140-
PrimaryAddress = "43Pnj6ZKGFTJhaLhiecSFfLfr64KPJZw7MyGH73T6PTDekBBvsTAaWEUSM4bmJqDuYLizhA13jQkMRPpz9VXBCBqQQb6y5L",
141-
PrivateViewKey = "1bfa03b0c78aa6bc8292cf160ec9875657d61e889c41d0ebe5c54fd3a2c4b40e",
142-
WalletFileName = "wallet",
143-
Password = ""
144-
});
130+
await moneroRpcProvider.WalletRpcClients["XMR"]
131+
.SendCommandAsync<GenerateFromKeysRequest, GenerateFromKeysResponse>("generate_from_keys",
132+
new GenerateFromKeysRequest
133+
{
134+
PrimaryAddress =
135+
"43Pnj6ZKGFTJhaLhiecSFfLfr64KPJZw7MyGH73T6PTDekBBvsTAaWEUSM4bmJqDuYLizhA13jQkMRPpz9VXBCBqQQb6y5L",
136+
PrivateViewKey = "1bfa03b0c78aa6bc8292cf160ec9875657d61e889c41d0ebe5c54fd3a2c4b40e",
137+
WalletFileName = "wallet",
138+
Password = ""
139+
});
145140
await moneroRpcProvider.CloseWallet("XMR");
146141

147142
await s.RegisterNewUser(true);
148143
await s.CreateNewStore();
149144
await s.Page.Locator("a.nav-link[href*='monerolike/XMR']").ClickAsync();
150145
await s.Page.Locator("input#PrimaryAddress")
151-
.FillAsync("43Pnj6ZKGFTJhaLhiecSFfLfr64KPJZw7MyGH73T6PTDekBBvsTAaWEUSM4bmJqDuYLizhA13jQkMRPpz9VXBCBqQQb6y5L");
146+
.FillAsync(
147+
"43Pnj6ZKGFTJhaLhiecSFfLfr64KPJZw7MyGH73T6PTDekBBvsTAaWEUSM4bmJqDuYLizhA13jQkMRPpz9VXBCBqQQb6y5L");
152148
await s.Page.Locator("input#PrivateViewKey")
153149
.FillAsync("1bfa03b0c78aa6bc8292cf160ec9875657d61e889c41d0ebe5c54fd3a2c4b40e");
154150
await s.Page.Locator("input#RestoreHeight").FillAsync("0");
@@ -198,4 +194,58 @@ public async Task ShouldLoadViewWalletWithPasswordOnStartUpIfExists()
198194

199195
await IntegrationTestUtils.CleanUpAsync(s);
200196
}
197+
198+
static async Task SetupWalletAndGenerateInvoice(PlaywrightTester s)
199+
{
200+
if (s.Server.PayTester.MockRates)
201+
{
202+
var rateProviderFactory = s.Server.PayTester.GetService<RateProviderFactory>();
203+
rateProviderFactory.Providers.Clear();
204+
205+
var coinAverageMock = new MockRateProvider();
206+
coinAverageMock.ExchangeRates.Add(new PairRate(CurrencyPair.Parse("BTC_USD"), new BidAsk(5000m)));
207+
coinAverageMock.ExchangeRates.Add(new PairRate(CurrencyPair.Parse("BTC_EUR"), new BidAsk(4000m)));
208+
coinAverageMock.ExchangeRates.Add(new PairRate(CurrencyPair.Parse("XMR_BTC"), new BidAsk(4500m)));
209+
rateProviderFactory.Providers.Add("coingecko", coinAverageMock);
210+
211+
var kraken = new MockRateProvider();
212+
kraken.ExchangeRates.Add(new PairRate(CurrencyPair.Parse("BTC_USD"), new BidAsk(0.1m)));
213+
kraken.ExchangeRates.Add(new PairRate(CurrencyPair.Parse("XMR_USD"), new BidAsk(0.1m)));
214+
kraken.ExchangeRates.Add(new PairRate(CurrencyPair.Parse("XMR_BTC"), new BidAsk(0.1m)));
215+
rateProviderFactory.Providers.Add("kraken", kraken);
216+
}
217+
218+
await s.RegisterNewUser(true);
219+
await s.CreateNewStore(preferredExchange: "Kraken");
220+
await s.Page.Locator("a.nav-link[href*='monerolike/XMR']").ClickAsync();
221+
await s.Page.Locator("input#PrimaryAddress")
222+
.FillAsync(
223+
"43Pnj6ZKGFTJhaLhiecSFfLfr64KPJZw7MyGH73T6PTDekBBvsTAaWEUSM4bmJqDuYLizhA13jQkMRPpz9VXBCBqQQb6y5L");
224+
await s.Page.Locator("input#PrivateViewKey")
225+
.FillAsync("1bfa03b0c78aa6bc8292cf160ec9875657d61e889c41d0ebe5c54fd3a2c4b40e");
226+
await s.Page.Locator("input#RestoreHeight").FillAsync("0");
227+
await s.Page.ClickAsync("button[name='command'][value='set-wallet-details']");
228+
var message = await s.Page
229+
.GetByText("View-only wallet created. The wallet will soon become available.")
230+
.InnerTextAsync();
231+
Assert.Contains("View-only wallet created", message);
232+
await s.Page.CheckAsync("#Enabled");
233+
await s.Page.SelectOptionAsync("#SettlementConfirmationThresholdChoice", "2");
234+
await s.Page.ClickAsync("#SaveButton");
235+
236+
// Set rate provider
237+
await s.Page.Locator("#menu-item-General").ClickAsync();
238+
await s.Page.Locator("#menu-item-Rates").ClickAsync();
239+
await s.Page.FillAsync("#DefaultCurrencyPairs", "BTC_USD,XMR_USD,XMR_BTC");
240+
await s.Page.SelectOptionAsync("#PrimarySource_PreferredExchange", "kraken");
241+
await s.Page.Locator("#page-primary").ClickAsync();
242+
243+
// Generate a new invoice
244+
await s.Page.Locator("a.nav-link[href*='invoices']").ClickAsync();
245+
await s.Page.Locator("#page-primary").ClickAsync();
246+
await s.Page.FillAsync("#Amount", "4.20");
247+
await s.Page.FillAsync("#BuyerEmail", "monero@monero.com");
248+
await Task.Delay(TimeSpan.FromSeconds(25)); // wallet-rpc needs some time to sync. refactor this later
249+
await s.Page.Locator("#page-primary").ClickAsync();
250+
}
201251
}

0 commit comments

Comments
 (0)