Fix: Some valid taproot PSBT couldn't parsed and show better error message (Fix #5715) (#5993)

This commit is contained in:
Nicolas Dorier 2024-05-21 10:52:55 +09:00 committed by nicolas.dorier
parent cb136cba82
commit fe9e5eb9c9
No known key found for this signature in database
GPG key ID: 6618763EF09186FE
8 changed files with 36 additions and 28 deletions

View file

@ -31,7 +31,7 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="BTCPayServer.Lightning.Common" Version="1.5.1" /> <PackageReference Include="BTCPayServer.Lightning.Common" Version="1.5.1" />
<PackageReference Include="NBitcoin" Version="7.0.34" /> <PackageReference Include="NBitcoin" Version="7.0.37" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" /> <PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View file

@ -4,7 +4,7 @@
<ItemGroup> <ItemGroup>
<FrameworkReference Include="Microsoft.AspNetCore.App" /> <FrameworkReference Include="Microsoft.AspNetCore.App" />
<PackageReference Include="NBXplorer.Client" Version="4.3.0" /> <PackageReference Include="NBXplorer.Client" Version="4.3.1" />
<PackageReference Include="NicolasDorier.StandardConfiguration" Version="2.0.1" /> <PackageReference Include="NicolasDorier.StandardConfiguration" Version="2.0.1" />
</ItemGroup> </ItemGroup>
<ItemGroup Condition="'$(Altcoins)' != 'true'"> <ItemGroup Condition="'$(Altcoins)' != 'true'">

View file

@ -6,7 +6,7 @@
<FrameworkReference Include="Microsoft.AspNetCore.App" /> <FrameworkReference Include="Microsoft.AspNetCore.App" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.8.0" /> <PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.8.0" />
<PackageReference Include="Microsoft.AspNet.WebApi.Client" Version="5.2.9" /> <PackageReference Include="Microsoft.AspNet.WebApi.Client" Version="5.2.9" />
<PackageReference Include="NBitcoin" Version="7.0.34" /> <PackageReference Include="NBitcoin" Version="7.0.37" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" /> <PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="DigitalRuby.ExchangeSharp" Version="1.0.4" /> <PackageReference Include="DigitalRuby.ExchangeSharp" Version="1.0.4" />
</ItemGroup> </ItemGroup>

View file

@ -99,7 +99,7 @@ services:
custom: custom:
nbxplorer: nbxplorer:
image: nicolasdorier/nbxplorer:2.5.0 image: nicolasdorier/nbxplorer:2.5.3
restart: unless-stopped restart: unless-stopped
ports: ports:
- "32838:32838" - "32838:32838"

View file

@ -96,7 +96,7 @@ services:
custom: custom:
nbxplorer: nbxplorer:
image: nicolasdorier/nbxplorer:2.5.0 image: nicolasdorier/nbxplorer:2.5.3
restart: unless-stopped restart: unless-stopped
ports: ports:
- "32838:32838" - "32838:32838"

View file

@ -151,13 +151,12 @@ namespace BTCPayServer.Controllers
WalletId walletId, WalletPSBTViewModel vm, string command = null) WalletId walletId, WalletPSBTViewModel vm, string command = null)
{ {
var network = NetworkProvider.GetNetwork<BTCPayNetwork>(walletId.CryptoCode); var network = NetworkProvider.GetNetwork<BTCPayNetwork>(walletId.CryptoCode);
var psbt = await vm.GetPSBT(network.NBitcoinNetwork); var psbt = await vm.GetPSBT(network.NBitcoinNetwork, ModelState);
vm.BackUrl ??= HttpContext.Request.GetTypedHeaders().Referer?.AbsolutePath; vm.BackUrl ??= HttpContext.Request.GetTypedHeaders().Referer?.AbsolutePath;
if (psbt is null || vm.InvalidPSBT) if (psbt is null || vm.InvalidPSBT)
{ {
ModelState.AddModelError(nameof(vm.PSBT), "Invalid PSBT");
return View("WalletSigningOptions", new WalletSigningOptionsModel return View("WalletSigningOptions", new WalletSigningOptionsModel
{ {
SigningContext = vm.SigningContext, SigningContext = vm.SigningContext,
@ -241,10 +240,9 @@ namespace BTCPayServer.Controllers
vm.NBXSeedAvailable = await CanUseHotWallet() && derivationSchemeSettings.IsHotWallet; vm.NBXSeedAvailable = await CanUseHotWallet() && derivationSchemeSettings.IsHotWallet;
vm.BackUrl ??= HttpContext.Request.GetTypedHeaders().Referer?.AbsolutePath; vm.BackUrl ??= HttpContext.Request.GetTypedHeaders().Referer?.AbsolutePath;
var psbt = await vm.GetPSBT(network.NBitcoinNetwork); var psbt = await vm.GetPSBT(network.NBitcoinNetwork, ModelState);
if (vm.InvalidPSBT) if (vm.InvalidPSBT)
{ {
ModelState.AddModelError(nameof(vm.PSBT), "Invalid PSBT");
return View(vm); return View(vm);
} }
if (psbt is null) if (psbt is null)
@ -477,7 +475,7 @@ namespace BTCPayServer.Controllers
WalletId walletId, WalletPSBTViewModel vm, string command, CancellationToken cancellationToken = default) WalletId walletId, WalletPSBTViewModel vm, string command, CancellationToken cancellationToken = default)
{ {
var network = NetworkProvider.GetNetwork<BTCPayNetwork>(walletId.CryptoCode); var network = NetworkProvider.GetNetwork<BTCPayNetwork>(walletId.CryptoCode);
PSBT psbt = await vm.GetPSBT(network.NBitcoinNetwork); PSBT psbt = await vm.GetPSBT(network.NBitcoinNetwork, ModelState);
if (vm.InvalidPSBT || psbt is null) if (vm.InvalidPSBT || psbt is null)
{ {
if (vm.InvalidPSBT) if (vm.InvalidPSBT)
@ -637,16 +635,14 @@ namespace BTCPayServer.Controllers
WalletId walletId, WalletPSBTCombineViewModel vm) WalletId walletId, WalletPSBTCombineViewModel vm)
{ {
var network = NetworkProvider.GetNetwork<BTCPayNetwork>(walletId.CryptoCode); var network = NetworkProvider.GetNetwork<BTCPayNetwork>(walletId.CryptoCode);
var psbt = await vm.GetPSBT(network.NBitcoinNetwork); var psbt = await vm.GetPSBT(network.NBitcoinNetwork, ModelState);
if (psbt == null) if (psbt == null)
{ {
ModelState.AddModelError(nameof(vm.PSBT), "Invalid PSBT");
return View(vm); return View(vm);
} }
var sourcePSBT = vm.GetSourcePSBT(network.NBitcoinNetwork); var sourcePSBT = vm.GetSourcePSBT(network.NBitcoinNetwork, ModelState);
if (sourcePSBT == null) if (sourcePSBT is null)
{ {
ModelState.AddModelError(nameof(vm.OtherPSBT), "Invalid PSBT");
return View(vm); return View(vm);
} }
sourcePSBT = sourcePSBT.Combine(psbt); sourcePSBT = sourcePSBT.Combine(psbt);

View file

@ -2,6 +2,7 @@ using System;
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using NBitcoin; using NBitcoin;
namespace BTCPayServer.Models.WalletViewModels namespace BTCPayServer.Models.WalletViewModels
@ -17,7 +18,7 @@ namespace BTCPayServer.Models.WalletViewModels
public string BackUrl { get; set; } public string BackUrl { get; set; }
public string ReturnUrl { get; set; } public string ReturnUrl { get; set; }
public PSBT GetSourcePSBT(Network network) public PSBT GetSourcePSBT(Network network, ModelStateDictionary modelState)
{ {
if (!string.IsNullOrEmpty(OtherPSBT)) if (!string.IsNullOrEmpty(OtherPSBT))
{ {
@ -25,12 +26,12 @@ namespace BTCPayServer.Models.WalletViewModels
{ {
return NBitcoin.PSBT.Parse(OtherPSBT, network); return NBitcoin.PSBT.Parse(OtherPSBT, network);
} }
catch catch (Exception ex)
{ } { modelState.AddModelError(nameof(OtherPSBT), ex.Message); }
} }
return null; return null;
} }
public async Task<PSBT> GetPSBT(Network network) public async Task<PSBT> GetPSBT(Network network, ModelStateDictionary modelState)
{ {
if (UploadedPSBTFile != null) if (UploadedPSBTFile != null)
{ {
@ -45,8 +46,9 @@ namespace BTCPayServer.Models.WalletViewModels
{ {
return NBitcoin.PSBT.Load(bytes, network); return NBitcoin.PSBT.Load(bytes, network);
} }
catch catch (FormatException ex)
{ {
modelState.AddModelError(nameof(UploadedPSBTFile), ex.Message);
return null; return null;
} }
} }
@ -56,8 +58,10 @@ namespace BTCPayServer.Models.WalletViewModels
{ {
return NBitcoin.PSBT.Parse(PSBT, network); return NBitcoin.PSBT.Parse(PSBT, network);
} }
catch catch (FormatException ex)
{ } {
modelState.AddModelError(nameof(UploadedPSBTFile), ex.Message);
}
} }
return null; return null;
} }

View file

@ -4,6 +4,7 @@ using System.ComponentModel.DataAnnotations;
using System.IO; using System.IO;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using NBitcoin; using NBitcoin;
namespace BTCPayServer.Models.WalletViewModels namespace BTCPayServer.Models.WalletViewModels
@ -35,9 +36,9 @@ namespace BTCPayServer.Models.WalletViewModels
public IFormFile UploadedPSBTFile { get; set; } public IFormFile UploadedPSBTFile { get; set; }
public async Task<PSBT> GetPSBT(Network network) public async Task<PSBT> GetPSBT(Network network, ModelStateDictionary modelState)
{ {
var psbt = await GetPSBTCore(network); var psbt = await GetPSBTCore(network, modelState);
if (psbt != null) if (psbt != null)
{ {
Decoded = psbt.ToString(); Decoded = psbt.ToString();
@ -52,7 +53,7 @@ namespace BTCPayServer.Models.WalletViewModels
} }
public bool InvalidPSBT { get; set; } public bool InvalidPSBT { get; set; }
async Task<PSBT> GetPSBTCore(Network network) async Task<PSBT> GetPSBTCore(Network network, ModelStateDictionary modelState)
{ {
if (UploadedPSBTFile != null) if (UploadedPSBTFile != null)
{ {
@ -68,16 +69,20 @@ namespace BTCPayServer.Models.WalletViewModels
} }
return NBitcoin.PSBT.Load(bytes, network); return NBitcoin.PSBT.Load(bytes, network);
} }
catch (Exception) catch (Exception ex)
{ {
using var stream = new StreamReader(UploadedPSBTFile.OpenReadStream()); using var stream = new StreamReader(UploadedPSBTFile.OpenReadStream());
PSBT = await stream.ReadToEndAsync(); PSBT = await stream.ReadToEndAsync();
modelState.Remove(nameof(PSBT));
modelState.AddModelError(nameof(PSBT), ex.Message);
InvalidPSBT = true; InvalidPSBT = true;
} }
} }
if (SigningContext != null && !string.IsNullOrEmpty(SigningContext.PSBT)) if (SigningContext != null && !string.IsNullOrEmpty(SigningContext.PSBT))
{ {
PSBT = SigningContext.PSBT; PSBT = SigningContext.PSBT;
modelState.Remove(nameof(PSBT));
InvalidPSBT = false;
} }
if (!string.IsNullOrEmpty(PSBT)) if (!string.IsNullOrEmpty(PSBT))
{ {
@ -86,8 +91,11 @@ namespace BTCPayServer.Models.WalletViewModels
InvalidPSBT = false; InvalidPSBT = false;
return NBitcoin.PSBT.Parse(PSBT, network); return NBitcoin.PSBT.Parse(PSBT, network);
} }
catch catch (Exception ex) when (!InvalidPSBT)
{ InvalidPSBT = true; } {
modelState.AddModelError(nameof(PSBT), ex.Message);
InvalidPSBT = true;
}
} }
return null; return null;
} }