btcpayserver/BTCPayServer.Tests/TestAccount.cs

393 lines
16 KiB
C#
Raw Normal View History

2017-09-13 08:47:34 +02:00
using BTCPayServer.Controllers;
using System.Linq;
2017-09-13 08:47:34 +02:00
using BTCPayServer.Models.AccountViewModels;
2017-09-13 16:50:36 +02:00
using BTCPayServer.Models.StoreViewModels;
2017-10-20 21:06:37 +02:00
using BTCPayServer.Services.Invoices;
2017-09-13 08:47:34 +02:00
using Microsoft.AspNetCore.Mvc;
using NBitcoin;
using NBitpayClient;
using System;
using System.Collections.Generic;
2020-03-29 17:28:22 +02:00
using System.Net.Http;
2017-09-13 08:47:34 +02:00
using System.Text;
using System.Threading.Tasks;
2020-03-29 17:28:22 +02:00
using Amazon.S3.Model;
2017-09-13 08:47:34 +02:00
using Xunit;
2017-11-06 09:31:02 +01:00
using NBXplorer.DerivationStrategy;
using BTCPayServer.Payments;
using BTCPayServer.Payments.Lightning;
2018-08-22 17:24:33 +02:00
using BTCPayServer.Tests.Logging;
using BTCPayServer.Lightning;
using BTCPayServer.Lightning.CLightning;
using BTCPayServer.Data;
2019-10-12 13:35:30 +02:00
using Microsoft.AspNetCore.Identity;
using NBXplorer.Models;
2020-03-16 08:36:55 +01:00
using BTCPayServer.Client;
2020-04-07 12:32:30 +02:00
using BTCPayServer.Events;
2020-03-29 17:28:22 +02:00
using BTCPayServer.Services;
using BTCPayServer.Services.Stores;
using BTCPayServer.Services.Wallets;
using NBitcoin.Payment;
using Newtonsoft.Json.Linq;
2017-09-13 08:47:34 +02:00
namespace BTCPayServer.Tests
{
public class TestAccount
{
ServerTester parent;
2020-03-29 17:28:22 +02:00
public TestAccount(ServerTester parent)
{
this.parent = parent;
BitPay = new Bitpay(new Key(), parent.PayTester.ServerUri);
}
2017-09-13 08:47:34 +02:00
2020-01-06 13:57:32 +01:00
public void GrantAccess(bool isAdmin = false)
{
2020-01-06 13:57:32 +01:00
GrantAccessAsync(isAdmin).GetAwaiter().GetResult();
}
2020-03-19 11:11:15 +01:00
public async Task MakeAdmin(bool isAdmin = true)
2019-10-12 13:35:30 +02:00
{
var userManager = parent.PayTester.GetService<UserManager<ApplicationUser>>();
var u = await userManager.FindByIdAsync(UserId);
2020-03-19 11:11:15 +01:00
if (isAdmin)
await userManager.AddToRoleAsync(u, Roles.ServerAdmin);
else
await userManager.RemoveFromRoleAsync(u, Roles.ServerAdmin);
IsAdmin = true;
2019-10-12 13:35:30 +02:00
}
2020-03-25 16:57:54 +01:00
public Task<BTCPayServerClient> CreateClient()
2020-03-20 17:14:47 +01:00
{
2020-03-29 17:28:22 +02:00
return Task.FromResult(new BTCPayServerClient(parent.PayTester.ServerUri, RegisterDetails.Email,
RegisterDetails.Password));
2020-03-20 17:14:47 +01:00
}
2020-03-16 08:36:55 +01:00
public async Task<BTCPayServerClient> CreateClient(params string[] permissions)
{
var manageController = parent.PayTester.GetController<ManageController>(UserId, StoreId, IsAdmin);
var x = Assert.IsType<RedirectToActionResult>(await manageController.AddApiKey(
new ManageController.AddApiKeyViewModel()
{
2020-03-29 17:28:22 +02:00
PermissionValues =
permissions.Select(s =>
new ManageController.AddApiKeyViewModel.PermissionValueItem()
{
Permission = s, Value = true
}).ToList(),
2020-03-16 08:36:55 +01:00
StoreMode = ManageController.AddApiKeyViewModel.ApiKeyStoreMode.AllStores
}));
var statusMessage = manageController.TempData.GetStatusMessageModel();
Assert.NotNull(statusMessage);
var apiKey = statusMessage.Html.Substring(statusMessage.Html.IndexOf("<code>") + 6);
apiKey = apiKey.Substring(0, apiKey.IndexOf("</code>"));
return new BTCPayServerClient(parent.PayTester.ServerUri, apiKey);
}
2020-01-06 13:57:32 +01:00
public void Register(bool isAdmin = false)
{
2020-01-06 13:57:32 +01:00
RegisterAsync(isAdmin).GetAwaiter().GetResult();
}
2020-03-29 17:28:22 +02:00
2020-01-06 13:57:32 +01:00
public async Task GrantAccessAsync(bool isAdmin = false)
{
2020-01-06 13:57:32 +01:00
await RegisterAsync(isAdmin);
await CreateStoreAsync();
var store = this.GetController<StoresController>();
var pairingCode = BitPay.RequestClientAuthorization("test", Facade.Merchant);
Assert.IsType<ViewResult>(await store.RequestPairing(pairingCode.ToString()));
await store.Pair(pairingCode.ToString(), StoreId);
}
2020-03-27 06:17:31 +01:00
public BTCPayServerClient CreateClientFromAPIKey(string apiKey)
{
return new BTCPayServerClient(parent.PayTester.ServerUri, apiKey);
}
public void CreateStore()
{
CreateStoreAsync().GetAwaiter().GetResult();
}
public void SetNetworkFeeMode(NetworkFeeMode mode)
2019-01-06 07:04:30 +01:00
{
ModifyStore((store) =>
{
store.NetworkFeeMode = mode;
});
}
2020-03-29 17:28:22 +02:00
2019-01-06 07:04:30 +01:00
public void ModifyStore(Action<StoreViewModel> modify)
{
var storeController = GetController<StoresController>();
StoreViewModel store = (StoreViewModel)((ViewResult)storeController.UpdateStore()).Model;
2019-01-06 07:04:30 +01:00
modify(store);
storeController.UpdateStore(store).GetAwaiter().GetResult();
}
public T GetController<T>(bool setImplicitStore = true) where T : Controller
2018-04-03 09:53:55 +02:00
{
2019-10-12 13:35:30 +02:00
var controller = parent.PayTester.GetController<T>(UserId, setImplicitStore ? StoreId : null, IsAdmin);
return controller;
2018-04-03 09:53:55 +02:00
}
public async Task CreateStoreAsync()
{
var store = this.GetController<UserStoresController>();
2020-03-29 17:28:22 +02:00
await store.CreateStore(new CreateStoreViewModel() {Name = "Test Store"});
StoreId = store.CreatedStoreId;
2018-07-19 12:31:17 +02:00
parent.Stores.Add(StoreId);
}
public BTCPayNetwork SupportedNetwork { get; set; }
2020-04-07 12:32:30 +02:00
public WalletId RegisterDerivationScheme(string crytoCode, ScriptPubKeyType segwit = ScriptPubKeyType.Legacy, bool importKeysToNBX = false)
{
return RegisterDerivationSchemeAsync(crytoCode, segwit, importKeysToNBX).GetAwaiter().GetResult();
}
2020-03-29 17:28:22 +02:00
2020-04-07 12:32:30 +02:00
public async Task<WalletId> RegisterDerivationSchemeAsync(string cryptoCode, ScriptPubKeyType segwit = ScriptPubKeyType.Legacy,
2020-03-29 17:28:22 +02:00
bool importKeysToNBX = false)
{
SupportedNetwork = parent.NetworkProvider.GetNetwork<BTCPayNetwork>(cryptoCode);
2018-04-29 19:33:42 +02:00
var store = parent.PayTester.GetController<StoresController>(UserId, StoreId);
GenerateWalletResponseV = await parent.ExplorerClient.GenerateWalletAsync(new GenerateWalletRequest()
{
2020-04-07 12:32:30 +02:00
ScriptPubKeyType = segwit,
SavePrivateKeys = importKeysToNBX,
});
2020-03-29 17:28:22 +02:00
await store.AddDerivationScheme(StoreId,
new DerivationSchemeViewModel()
{
Enabled = true,
CryptoCode = cryptoCode,
Network = SupportedNetwork,
RootFingerprint = GenerateWalletResponseV.AccountKeyPath.MasterFingerprint.ToString(),
RootKeyPath = SupportedNetwork.GetRootKeyPath(),
Source = "NBXplorer",
AccountKey = GenerateWalletResponseV.AccountHDKey.Neuter().ToWif(),
DerivationSchemeFormat = "BTCPay",
KeyPath = GenerateWalletResponseV.AccountKeyPath.KeyPath.ToString(),
DerivationScheme = DerivationScheme.ToString(),
Confirmation = true
}, cryptoCode);
return new WalletId(StoreId, cryptoCode);
}
2020-01-06 13:57:32 +01:00
public async Task EnablePayJoin()
{
var storeController = parent.PayTester.GetController<StoresController>(UserId, StoreId);
var storeVM =
Assert.IsType<StoreViewModel>(Assert
.IsType<ViewResult>(storeController.UpdateStore()).Model);
2020-01-06 13:57:32 +01:00
storeVM.PayJoinEnabled = true;
2020-01-06 13:57:32 +01:00
Assert.Equal(nameof(storeController.UpdateStore),
2020-01-06 13:57:32 +01:00
Assert.IsType<RedirectToActionResult>(
await storeController.UpdateStore(storeVM)).ActionName);
2020-01-06 13:57:32 +01:00
}
public GenerateWalletResponse GenerateWalletResponseV { get; set; }
public DerivationStrategyBase DerivationScheme
{
get
{
return GenerateWalletResponseV.DerivationScheme;
}
}
2017-11-06 09:31:02 +01:00
2020-01-06 13:57:32 +01:00
private async Task RegisterAsync(bool isAdmin = false)
{
var account = parent.PayTester.GetController<AccountController>();
RegisterDetails = new RegisterViewModel()
{
Email = Guid.NewGuid() + "@toto.com",
ConfirmPassword = "Kitten0@",
Password = "Kitten0@",
2020-01-06 13:57:32 +01:00
IsAdmin = isAdmin
};
await account.Register(RegisterDetails);
UserId = account.RegisteredUserId;
IsAdmin = account.RegisteredAdmin;
}
2017-09-13 08:47:34 +02:00
2020-03-29 17:28:22 +02:00
public RegisterViewModel RegisterDetails { get; set; }
public Bitpay BitPay
{
2020-03-29 17:28:22 +02:00
get;
set;
}
2020-03-29 17:28:22 +02:00
public string UserId
{
2020-03-29 17:28:22 +02:00
get;
set;
}
2017-09-13 08:47:34 +02:00
public string StoreId
{
2020-03-29 17:28:22 +02:00
get;
set;
}
2020-03-29 17:28:22 +02:00
2019-10-12 13:35:30 +02:00
public bool IsAdmin { get; internal set; }
2018-07-01 09:10:17 +02:00
public void RegisterLightningNode(string cryptoCode, LightningConnectionType connectionType)
{
RegisterLightningNodeAsync(cryptoCode, connectionType).GetAwaiter().GetResult();
}
public async Task RegisterLightningNodeAsync(string cryptoCode, LightningConnectionType connectionType)
{
2018-04-29 19:33:42 +02:00
var storeController = this.GetController<StoresController>();
2018-07-01 14:41:06 +02:00
string connectionString = null;
if (connectionType == LightningConnectionType.Charge)
2018-07-01 14:41:06 +02:00
connectionString = "type=charge;server=" + parent.MerchantCharge.Client.Uri.AbsoluteUri;
else if (connectionType == LightningConnectionType.CLightning)
2020-03-29 17:28:22 +02:00
connectionString = "type=clightning;server=" +
((CLightningClient)parent.MerchantLightningD).Address.AbsoluteUri;
else if (connectionType == LightningConnectionType.LndREST)
connectionString = $"type=lnd-rest;server={parent.MerchantLnd.Swagger.BaseUrl};allowinsecure=true";
else
throw new NotSupportedException(connectionType.ToString());
2020-03-29 17:28:22 +02:00
await storeController.AddLightningNode(StoreId,
new LightningNodeViewModel() {ConnectionString = connectionString, SkipPortTest = true}, "save", "BTC");
if (storeController.ModelState.ErrorCount != 0)
Assert.False(true, storeController.ModelState.FirstOrDefault().Value.Errors[0].ErrorMessage);
}
2020-03-29 17:28:22 +02:00
public async Task<Coin> ReceiveUTXO(Money value, BTCPayNetwork network)
{
var cashCow = parent.ExplorerNode;
var btcPayWallet = parent.PayTester.GetService<BTCPayWalletProvider>().GetWallet(network);
var address = (await btcPayWallet.ReserveAddressAsync(this.DerivationScheme)).Address;
2020-04-07 12:32:30 +02:00
await parent.WaitForEvent<NewOnChainTransactionEvent>(async () =>
{
await cashCow.SendToAddressAsync(address, value);
});
2020-04-08 10:10:42 +02:00
int i = 0;
while (i <30)
{
var result = (await btcPayWallet.GetUnspentCoins(DerivationScheme))
.FirstOrDefault(c => c.ScriptPubKey == address.ScriptPubKey)?.Coin;
if (result != null)
{
return result;
}
await Task.Delay(1000);
i++;
}
Assert.False(true);
2020-03-29 17:28:22 +02:00
}
public async Task<BitcoinAddress> GetNewAddress(BTCPayNetwork network)
{
var cashCow = parent.ExplorerNode;
var btcPayWallet = parent.PayTester.GetService<BTCPayWalletProvider>().GetWallet(network);
var address = (await btcPayWallet.ReserveAddressAsync(this.DerivationScheme)).Address;
return address;
}
public async Task<PSBT> Sign(PSBT psbt)
{
var btcPayWallet = parent.PayTester.GetService<BTCPayWalletProvider>()
.GetWallet(psbt.Network.NetworkSet.CryptoCode);
var explorerClient = parent.PayTester.GetService<ExplorerClientProvider>()
.GetExplorerClient(psbt.Network.NetworkSet.CryptoCode);
psbt = (await explorerClient.UpdatePSBTAsync(new UpdatePSBTRequest()
{
DerivationScheme = DerivationScheme, PSBT = psbt
})).PSBT;
return psbt.SignAll(this.DerivationScheme, GenerateWalletResponseV.AccountHDKey,
GenerateWalletResponseV.AccountKeyPath);
}
2020-04-08 08:20:19 +02:00
public async Task<PSBT> SubmitPayjoin(Invoice invoice, PSBT psbt, string expectedError = null, bool senderError= false)
2020-03-29 17:28:22 +02:00
{
var endpoint = GetPayjoinEndpoint(invoice, psbt.Network);
2020-04-07 12:32:30 +02:00
if (endpoint == null)
{
return null;
}
2020-03-29 17:28:22 +02:00
var pjClient = parent.PayTester.GetService<PayjoinClient>();
var storeRepository = parent.PayTester.GetService<StoreRepository>();
var store = await storeRepository.FindStore(StoreId);
var settings = store.GetSupportedPaymentMethods(parent.NetworkProvider).OfType<DerivationSchemeSettings>()
.First();
Logs.Tester.LogInformation($"Proposing {psbt.GetGlobalTransaction().GetHash()}");
2020-04-08 08:20:19 +02:00
if (expectedError is null && !senderError)
2020-03-29 17:28:22 +02:00
{
var proposed = await pjClient.RequestPayjoin(endpoint, settings, psbt, default);
Logs.Tester.LogInformation($"Proposed payjoin is {proposed.GetGlobalTransaction().GetHash()}");
2020-03-29 17:28:22 +02:00
Assert.NotNull(proposed);
return proposed;
}
else
{
2020-04-08 08:20:19 +02:00
if (senderError)
{
await Assert.ThrowsAsync<PayjoinSenderException>(async () => await pjClient.RequestPayjoin(endpoint, settings, psbt, default));
}
else
{
var ex = await Assert.ThrowsAsync<PayjoinReceiverException>(async () => await pjClient.RequestPayjoin(endpoint, settings, psbt, default));
Assert.Equal(expectedError, ex.ErrorCode);
}
2020-03-29 17:28:22 +02:00
return null;
}
}
public async Task<Transaction> SubmitPayjoin(Invoice invoice, Transaction transaction, BTCPayNetwork network,
string expectedError = null)
{
var response =
await SubmitPayjoinCore(transaction.ToHex(), invoice, network.NBitcoinNetwork, expectedError);
if (response == null)
return null;
var signed = Transaction.Parse(await response.Content.ReadAsStringAsync(), network.NBitcoinNetwork);
return signed;
}
async Task<HttpResponseMessage> SubmitPayjoinCore(string content, Invoice invoice, Network network,
string expectedError)
{
var endpoint = GetPayjoinEndpoint(invoice, network);
var response = await parent.PayTester.HttpClient.PostAsync(endpoint,
new StringContent(content, Encoding.UTF8, "text/plain"));
if (expectedError != null)
{
Assert.False(response.IsSuccessStatusCode);
var error = JObject.Parse(await response.Content.ReadAsStringAsync());
Assert.Equal(expectedError, error["errorCode"].Value<string>());
return null;
}
else
{
if (!response.IsSuccessStatusCode)
{
var error = JObject.Parse(await response.Content.ReadAsStringAsync());
Assert.True(false,
$"Error: {error["errorCode"].Value<string>()}: {error["message"].Value<string>()}");
}
}
return response;
}
private static Uri GetPayjoinEndpoint(Invoice invoice, Network network)
{
var parsedBip21 = new BitcoinUrlBuilder(
invoice.CryptoInfo.First(c => c.CryptoCode == network.NetworkSet.CryptoCode).PaymentUrls.BIP21,
network);
return parsedBip21.UnknowParameters.TryGetValue($"{PayjoinClient.BIP21EndpointKey}", out var uri) ? new Uri(uri, UriKind.Absolute) : null;
2020-03-29 17:28:22 +02:00
}
2018-07-01 09:10:17 +02:00
}
2017-09-13 08:47:34 +02:00
}