mirror of
https://github.com/btcpayserver/btcpayserver.git
synced 2024-11-19 09:54:30 +01:00
d5d0be5824
* Editorconfig: Add space_before_self_closing setting This was a difference between the way dotnet-format and Rider format code. See https://www.jetbrains.com/help/rider/EditorConfig_Index.html * Editorconfig: Keep 4 spaces indentation for Swagger JSON files They are all formatted that way, let's keep it like that. * Apply dotnet-format, mostly white-space related changes
134 lines
5.8 KiB
C#
134 lines
5.8 KiB
C#
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
using BTCPayServer.Controllers;
|
|
using BTCPayServer.Models;
|
|
using BTCPayServer.Models.WalletViewModels;
|
|
using BTCPayServer.Tests.Logging;
|
|
using Microsoft.AspNetCore.Mvc;
|
|
using NBitcoin;
|
|
using NBitpayClient;
|
|
using OpenQA.Selenium;
|
|
using OpenQA.Selenium.Support.Extensions;
|
|
using Xunit;
|
|
using Xunit.Abstractions;
|
|
|
|
namespace BTCPayServer.Tests
|
|
{
|
|
public class PSBTTests : UnitTestBase
|
|
{
|
|
public PSBTTests(ITestOutputHelper helper) : base(helper)
|
|
{
|
|
}
|
|
|
|
[Fact]
|
|
[Trait("Selenium", "Selenium")]
|
|
public async Task CanPlayWithPSBT()
|
|
{
|
|
using var s = CreateSeleniumTester(newDb: true);
|
|
await s.StartAsync();
|
|
|
|
var u1 = s.RegisterNewUser(true);
|
|
var hot = s.CreateNewStore();
|
|
var seed = s.GenerateWallet(isHotWallet: true);
|
|
var cold = s.CreateNewStore();
|
|
s.GenerateWallet(isHotWallet: false, seed: seed.ToString());
|
|
|
|
// Scenario 1: one user has two stores sharing same seed
|
|
// one store is hot wallet, the other not.
|
|
|
|
// Here, the cold wallet create a PSBT, then we switch to hot wallet to sign
|
|
// the PSBT and broadcast
|
|
s.GoToStore(cold.storeId);
|
|
var address = await s.FundStoreWallet();
|
|
Thread.Sleep(1000);
|
|
s.GoToWallet(navPages: Views.Wallets.WalletsNavPages.Send);
|
|
SendAllTo(s, address);
|
|
s.Driver.FindElement(By.Id("SignWithPSBT")).Click();
|
|
|
|
var psbt = ExtractPSBT(s);
|
|
|
|
s.GoToStore(hot.storeId);
|
|
s.GoToWallet(navPages: Views.Wallets.WalletsNavPages.PSBT);
|
|
s.Driver.FindElement(By.Name("PSBT")).SendKeys(psbt);
|
|
s.Driver.FindElement(By.Id("Decode")).Click();
|
|
s.Driver.FindElement(By.Id("SignTransaction")).Click();
|
|
s.Driver.FindElement(By.Id("BroadcastTransaction")).Click();
|
|
s.FindAlertMessage();
|
|
|
|
// Scenario 2: Same as scenario 1, except we create a PSBT from hot wallet, then sign by manually
|
|
// entering the seed on the cold wallet.
|
|
s.GoToWallet(navPages: Views.Wallets.WalletsNavPages.Send);
|
|
SendAllTo(s, address);
|
|
psbt = ExtractPSBT(s);
|
|
|
|
// Let's check it has been signed, then remove the signature.
|
|
// Also remove the hdkeys so we can test the update later
|
|
var psbtParsed = PSBT.Parse(psbt, s.Server.NetworkProvider.BTC.NBitcoinNetwork);
|
|
var signedPSBT = psbtParsed.Clone();
|
|
Assert.True(psbtParsed.Clone().TryFinalize(out _));
|
|
Assert.Single(psbtParsed.Inputs[0].PartialSigs);
|
|
psbtParsed.Inputs[0].PartialSigs.Clear();
|
|
Assert.Single(psbtParsed.Inputs[0].HDKeyPaths);
|
|
psbtParsed.Inputs[0].HDKeyPaths.Clear();
|
|
var skeletonPSBT = psbtParsed;
|
|
|
|
s.GoToStore(cold.storeId);
|
|
s.GoToWallet(navPages: Views.Wallets.WalletsNavPages.PSBT);
|
|
s.Driver.FindElement(By.Name("PSBT")).SendKeys(skeletonPSBT.ToBase64());
|
|
s.Driver.FindElement(By.Id("Decode")).Click();
|
|
s.Driver.FindElement(By.Id("SignTransaction")).Click();
|
|
s.Driver.FindElement(By.Id("SignWithSeed")).Click();
|
|
s.Driver.FindElement(By.Name("SeedOrKey")).SendKeys(seed.ToString());
|
|
s.Driver.FindElement(By.Id("Submit")).Click();
|
|
s.Driver.FindElement(By.Id("BroadcastTransaction")).Click();
|
|
s.FindAlertMessage();
|
|
|
|
// Let's check if the update feature works
|
|
s.GoToWallet(navPages: Views.Wallets.WalletsNavPages.PSBT);
|
|
s.Driver.FindElement(By.Name("PSBT")).SendKeys(skeletonPSBT.ToBase64());
|
|
s.Driver.FindElement(By.Id("Decode")).Click();
|
|
s.Driver.FindElement(By.Id("PSBTOptionsAdvancedHeader")).Click();
|
|
s.Driver.WaitForElement(By.Id("update-psbt")).Click();
|
|
|
|
psbt = ExtractPSBT(s);
|
|
psbtParsed = PSBT.Parse(psbt, s.Server.NetworkProvider.BTC.NBitcoinNetwork);
|
|
Assert.Single(psbtParsed.Inputs[0].HDKeyPaths);
|
|
Assert.Empty(psbtParsed.Inputs[0].PartialSigs);
|
|
|
|
// Let's if we can combine the updated psbt (which has hdkeys, but no sig)
|
|
// with the signed psbt (which has sig, but no hdkeys)
|
|
s.GoToWallet(navPages: Views.Wallets.WalletsNavPages.PSBT);
|
|
s.Driver.FindElement(By.Name("PSBT")).SendKeys(psbtParsed.ToBase64());
|
|
s.Driver.FindElement(By.Id("Decode")).Click();
|
|
s.Driver.FindElement(By.Id("PSBTOptionsAdvancedHeader")).Click();
|
|
s.Driver.WaitForElement(By.Id("combine-psbt")).Click();
|
|
signedPSBT.Inputs[0].HDKeyPaths.Clear();
|
|
s.Driver.FindElement(By.Name("PSBT")).SendKeys(signedPSBT.ToBase64());
|
|
s.Driver.WaitForElement(By.Id("Submit")).Click();
|
|
|
|
psbt = ExtractPSBT(s);
|
|
psbtParsed = PSBT.Parse(psbt, s.Server.NetworkProvider.BTC.NBitcoinNetwork);
|
|
Assert.Single(psbtParsed.Inputs[0].HDKeyPaths);
|
|
Assert.Single(psbtParsed.Inputs[0].PartialSigs);
|
|
}
|
|
|
|
private static void SendAllTo(SeleniumTester s, string address)
|
|
{
|
|
s.Driver.FindElement(By.Name("Outputs[0].DestinationAddress")).SendKeys(address);
|
|
s.Driver.FindElement(By.ClassName("crypto-balance-link")).Click();
|
|
s.Driver.FindElement(By.Id("SignTransaction")).Click();
|
|
}
|
|
|
|
private static string ExtractPSBT(SeleniumTester s)
|
|
{
|
|
var pageSource = s.Driver.PageSource;
|
|
var start = pageSource.IndexOf("id=\"psbt-base64\">");
|
|
start += "id=\"psbt-base64\">".Length;
|
|
var end = pageSource.IndexOf("<", start);
|
|
return pageSource[start..end];
|
|
}
|
|
}
|
|
}
|