2019-05-12 04:13:04 +02:00
|
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.Linq;
|
|
|
|
|
using System.Threading;
|
|
|
|
|
using System.Threading.Tasks;
|
|
|
|
|
using BTCPayServer.ModelBinders;
|
|
|
|
|
using BTCPayServer.Models.WalletViewModels;
|
|
|
|
|
using Microsoft.AspNetCore.Mvc;
|
|
|
|
|
using NBitcoin;
|
|
|
|
|
using NBXplorer.Models;
|
|
|
|
|
|
|
|
|
|
namespace BTCPayServer.Controllers
|
|
|
|
|
{
|
|
|
|
|
public partial class WalletsController
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
[NonAction]
|
|
|
|
|
public async Task<CreatePSBTResponse> CreatePSBT(BTCPayNetwork network, DerivationSchemeSettings derivationSettings, WalletSendModel sendModel, CancellationToken cancellationToken)
|
|
|
|
|
{
|
|
|
|
|
var nbx = ExplorerClientProvider.GetExplorerClient(network);
|
|
|
|
|
CreatePSBTRequest psbtRequest = new CreatePSBTRequest();
|
2019-05-21 10:10:07 +02:00
|
|
|
|
|
|
|
|
|
foreach (var transactionOutput in sendModel.Outputs)
|
|
|
|
|
{
|
|
|
|
|
var psbtDestination = new CreatePSBTDestination();
|
|
|
|
|
psbtRequest.Destinations.Add(psbtDestination);
|
|
|
|
|
psbtDestination.Destination = BitcoinAddress.Create(transactionOutput.DestinationAddress, network.NBitcoinNetwork);
|
|
|
|
|
psbtDestination.Amount = Money.Coins(transactionOutput.Amount.Value);
|
|
|
|
|
psbtDestination.SubstractFees = transactionOutput.SubtractFeesFromOutput;
|
|
|
|
|
}
|
|
|
|
|
|
2019-05-12 04:13:04 +02:00
|
|
|
|
if (network.SupportRBF)
|
|
|
|
|
{
|
|
|
|
|
psbtRequest.RBF = !sendModel.DisableRBF;
|
|
|
|
|
}
|
2019-05-21 10:10:07 +02:00
|
|
|
|
|
2019-05-12 04:13:04 +02:00
|
|
|
|
psbtRequest.FeePreference = new FeePreference();
|
|
|
|
|
psbtRequest.FeePreference.ExplicitFeeRate = new FeeRate(Money.Satoshis(sendModel.FeeSatoshiPerByte), 1);
|
|
|
|
|
if (sendModel.NoChange)
|
|
|
|
|
{
|
2019-05-21 10:10:07 +02:00
|
|
|
|
psbtRequest.ExplicitChangeAddress = psbtRequest.Destinations.First().Destination;
|
2019-05-12 04:13:04 +02:00
|
|
|
|
}
|
2019-05-21 10:10:07 +02:00
|
|
|
|
|
2019-05-12 04:13:04 +02:00
|
|
|
|
var psbt = (await nbx.CreatePSBTAsync(derivationSettings.AccountDerivation, psbtRequest, cancellationToken));
|
|
|
|
|
if (psbt == null)
|
|
|
|
|
throw new NotSupportedException("You need to update your version of NBXplorer");
|
2019-07-25 12:38:29 +02:00
|
|
|
|
// Not supported by coldcard, remove when they do support it
|
|
|
|
|
psbt.PSBT.GlobalXPubs.Clear();
|
2019-05-12 04:13:04 +02:00
|
|
|
|
return psbt;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[HttpGet]
|
|
|
|
|
[Route("{walletId}/psbt")]
|
2019-05-19 16:27:18 +02:00
|
|
|
|
public async Task<IActionResult> WalletPSBT([ModelBinder(typeof(WalletIdModelBinder))]
|
2019-05-15 08:00:09 +02:00
|
|
|
|
WalletId walletId, WalletPSBTViewModel vm)
|
2019-05-12 04:13:04 +02:00
|
|
|
|
{
|
2019-05-29 11:43:50 +02:00
|
|
|
|
var network = NetworkProvider.GetNetwork<BTCPayNetwork>(walletId.CryptoCode);
|
2019-12-03 10:57:07 +01:00
|
|
|
|
vm.CryptoCode = network.CryptoCode;
|
2019-05-19 16:27:18 +02:00
|
|
|
|
if (await vm.GetPSBT(network.NBitcoinNetwork) is PSBT psbt)
|
2019-05-15 08:00:09 +02:00
|
|
|
|
{
|
2019-05-19 16:27:18 +02:00
|
|
|
|
vm.Decoded = psbt.ToString();
|
|
|
|
|
vm.PSBT = psbt.ToBase64();
|
2019-05-15 08:00:09 +02:00
|
|
|
|
}
|
2019-11-11 06:22:04 +01:00
|
|
|
|
return View(vm ?? new WalletPSBTViewModel() { CryptoCode = walletId.CryptoCode });
|
2019-05-12 04:13:04 +02:00
|
|
|
|
}
|
|
|
|
|
[HttpPost]
|
|
|
|
|
[Route("{walletId}/psbt")]
|
2019-05-12 06:13:52 +02:00
|
|
|
|
public async Task<IActionResult> WalletPSBT(
|
2019-05-12 04:13:04 +02:00
|
|
|
|
[ModelBinder(typeof(WalletIdModelBinder))]
|
|
|
|
|
WalletId walletId,
|
2019-05-12 06:13:52 +02:00
|
|
|
|
WalletPSBTViewModel vm, string command = null)
|
2019-05-12 04:13:04 +02:00
|
|
|
|
{
|
2019-11-08 12:21:33 +01:00
|
|
|
|
if (command == null)
|
|
|
|
|
return await WalletPSBT(walletId, vm);
|
2019-05-29 11:43:50 +02:00
|
|
|
|
var network = NetworkProvider.GetNetwork<BTCPayNetwork>(walletId.CryptoCode);
|
2019-12-03 10:57:07 +01:00
|
|
|
|
vm.CryptoCode = network.CryptoCode;
|
2019-05-19 16:27:18 +02:00
|
|
|
|
var psbt = await vm.GetPSBT(network.NBitcoinNetwork);
|
2019-05-12 06:13:52 +02:00
|
|
|
|
if (psbt == null)
|
|
|
|
|
{
|
|
|
|
|
ModelState.AddModelError(nameof(vm.PSBT), "Invalid PSBT");
|
|
|
|
|
return View(vm);
|
|
|
|
|
}
|
2019-05-14 18:03:48 +02:00
|
|
|
|
switch (command)
|
2019-05-12 04:13:04 +02:00
|
|
|
|
{
|
2019-11-08 12:21:33 +01:00
|
|
|
|
case "decode":
|
2019-05-14 18:03:48 +02:00
|
|
|
|
vm.Decoded = psbt.ToString();
|
2019-05-19 16:27:18 +02:00
|
|
|
|
ModelState.Remove(nameof(vm.PSBT));
|
|
|
|
|
ModelState.Remove(nameof(vm.FileName));
|
|
|
|
|
ModelState.Remove(nameof(vm.UploadedPSBTFile));
|
|
|
|
|
vm.PSBT = psbt.ToBase64();
|
|
|
|
|
vm.FileName = vm.UploadedPSBTFile?.FileName;
|
2019-05-14 18:03:48 +02:00
|
|
|
|
return View(vm);
|
2019-11-11 06:22:04 +01:00
|
|
|
|
case "vault":
|
|
|
|
|
return ViewVault(walletId, psbt);
|
2019-05-14 18:03:48 +02:00
|
|
|
|
case "ledger":
|
|
|
|
|
return ViewWalletSendLedger(psbt);
|
2019-05-30 16:16:05 +02:00
|
|
|
|
case "update":
|
2019-10-12 13:35:30 +02:00
|
|
|
|
var derivationSchemeSettings = GetDerivationSchemeSettings(walletId);
|
2019-05-30 16:16:05 +02:00
|
|
|
|
psbt = await UpdatePSBT(derivationSchemeSettings, psbt, network);
|
|
|
|
|
if (psbt == null)
|
|
|
|
|
{
|
2019-05-30 17:00:20 +02:00
|
|
|
|
ModelState.AddModelError(nameof(vm.PSBT), "You need to update your version of NBXplorer");
|
2019-05-30 16:16:05 +02:00
|
|
|
|
return View(vm);
|
|
|
|
|
}
|
2019-10-31 04:29:59 +01:00
|
|
|
|
TempData[WellKnownTempData.SuccessMessage] = "PSBT updated!";
|
2019-11-08 12:21:33 +01:00
|
|
|
|
return RedirectToWalletPSBT(walletId, psbt, vm.FileName);
|
2019-05-14 18:03:48 +02:00
|
|
|
|
case "seed":
|
2019-05-15 08:00:09 +02:00
|
|
|
|
return SignWithSeed(walletId, psbt.ToBase64());
|
2019-05-14 18:03:48 +02:00
|
|
|
|
case "broadcast":
|
2019-05-12 06:13:52 +02:00
|
|
|
|
{
|
2019-05-15 08:00:09 +02:00
|
|
|
|
return await WalletPSBTReady(walletId, psbt.ToBase64());
|
2019-05-12 06:13:52 +02:00
|
|
|
|
}
|
2019-05-14 18:03:48 +02:00
|
|
|
|
case "combine":
|
|
|
|
|
ModelState.Remove(nameof(vm.PSBT));
|
|
|
|
|
return View(nameof(WalletPSBTCombine), new WalletPSBTCombineViewModel() { OtherPSBT = psbt.ToBase64() });
|
|
|
|
|
case "save-psbt":
|
|
|
|
|
return FilePSBT(psbt, vm.FileName);
|
|
|
|
|
default:
|
|
|
|
|
return View(vm);
|
2019-05-12 04:13:04 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-05-30 16:16:05 +02:00
|
|
|
|
private async Task<PSBT> UpdatePSBT(DerivationSchemeSettings derivationSchemeSettings, PSBT psbt, BTCPayNetwork network)
|
|
|
|
|
{
|
|
|
|
|
var result = await ExplorerClientProvider.GetExplorerClient(network).UpdatePSBTAsync(new UpdatePSBTRequest()
|
|
|
|
|
{
|
|
|
|
|
PSBT = psbt,
|
|
|
|
|
DerivationScheme = derivationSchemeSettings.AccountDerivation,
|
|
|
|
|
});
|
|
|
|
|
if (result == null)
|
|
|
|
|
return null;
|
|
|
|
|
derivationSchemeSettings.RebaseKeyPaths(result.PSBT);
|
|
|
|
|
return result.PSBT;
|
|
|
|
|
}
|
|
|
|
|
|
2019-05-12 04:13:04 +02:00
|
|
|
|
[HttpGet]
|
|
|
|
|
[Route("{walletId}/psbt/ready")]
|
2019-05-15 08:00:09 +02:00
|
|
|
|
public async Task<IActionResult> WalletPSBTReady(
|
2019-05-12 04:13:04 +02:00
|
|
|
|
[ModelBinder(typeof(WalletIdModelBinder))]
|
2019-05-15 08:00:09 +02:00
|
|
|
|
WalletId walletId, string psbt = null,
|
|
|
|
|
string signingKey = null,
|
|
|
|
|
string signingKeyPath = null)
|
|
|
|
|
{
|
2019-05-29 11:43:50 +02:00
|
|
|
|
var network = NetworkProvider.GetNetwork<BTCPayNetwork>(walletId.CryptoCode);
|
2019-05-15 08:00:09 +02:00
|
|
|
|
var vm = new WalletPSBTReadyViewModel() { PSBT = psbt };
|
|
|
|
|
vm.SigningKey = signingKey;
|
|
|
|
|
vm.SigningKeyPath = signingKeyPath;
|
2019-05-27 14:50:08 +02:00
|
|
|
|
|
2019-10-12 13:35:30 +02:00
|
|
|
|
var derivationSchemeSettings = GetDerivationSchemeSettings(walletId);
|
2019-05-27 14:50:08 +02:00
|
|
|
|
if (derivationSchemeSettings == null)
|
|
|
|
|
return NotFound();
|
2019-07-12 05:57:56 +02:00
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
await FetchTransactionDetails(derivationSchemeSettings, vm, network);
|
|
|
|
|
}
|
|
|
|
|
catch { return BadRequest(); }
|
2019-05-15 08:00:09 +02:00
|
|
|
|
return View(nameof(WalletPSBTReady), vm);
|
|
|
|
|
}
|
|
|
|
|
|
2019-05-30 16:16:05 +02:00
|
|
|
|
private async Task FetchTransactionDetails(DerivationSchemeSettings derivationSchemeSettings, WalletPSBTReadyViewModel vm, BTCPayNetwork network)
|
2019-05-12 04:13:04 +02:00
|
|
|
|
{
|
2019-05-15 08:00:09 +02:00
|
|
|
|
var psbtObject = PSBT.Parse(vm.PSBT, network.NBitcoinNetwork);
|
2019-11-11 06:22:04 +01:00
|
|
|
|
if (!psbtObject.IsAllFinalized())
|
|
|
|
|
psbtObject = await UpdatePSBT(derivationSchemeSettings, psbtObject, network) ?? psbtObject;
|
2019-05-15 08:00:09 +02:00
|
|
|
|
IHDKey signingKey = null;
|
|
|
|
|
RootedKeyPath signingKeyPath = null;
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
signingKey = new BitcoinExtPubKey(vm.SigningKey, network.NBitcoinNetwork);
|
|
|
|
|
}
|
|
|
|
|
catch { }
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
signingKey = signingKey ?? new BitcoinExtKey(vm.SigningKey, network.NBitcoinNetwork);
|
|
|
|
|
}
|
|
|
|
|
catch { }
|
|
|
|
|
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
signingKeyPath = RootedKeyPath.Parse(vm.SigningKeyPath);
|
|
|
|
|
}
|
|
|
|
|
catch { }
|
|
|
|
|
|
|
|
|
|
if (signingKey == null || signingKeyPath == null)
|
|
|
|
|
{
|
2019-05-19 16:27:18 +02:00
|
|
|
|
var signingKeySettings = derivationSchemeSettings.GetSigningAccountKeySettings();
|
2019-05-15 08:00:09 +02:00
|
|
|
|
if (signingKey == null)
|
|
|
|
|
{
|
|
|
|
|
signingKey = signingKeySettings.AccountKey;
|
|
|
|
|
vm.SigningKey = signingKey.ToString();
|
|
|
|
|
}
|
|
|
|
|
if (vm.SigningKeyPath == null)
|
|
|
|
|
{
|
|
|
|
|
signingKeyPath = signingKeySettings.GetRootedKeyPath();
|
|
|
|
|
vm.SigningKeyPath = signingKeyPath?.ToString();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-05-19 16:27:18 +02:00
|
|
|
|
if (psbtObject.IsAllFinalized())
|
|
|
|
|
{
|
|
|
|
|
vm.CanCalculateBalance = false;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
var balanceChange = psbtObject.GetBalance(derivationSchemeSettings.AccountDerivation, signingKey, signingKeyPath);
|
|
|
|
|
vm.BalanceChange = ValueToString(balanceChange, network);
|
|
|
|
|
vm.CanCalculateBalance = true;
|
|
|
|
|
vm.Positive = balanceChange >= Money.Zero;
|
|
|
|
|
}
|
2019-05-15 08:00:09 +02:00
|
|
|
|
|
2019-05-30 17:23:23 +02:00
|
|
|
|
foreach (var input in psbtObject.Inputs)
|
|
|
|
|
{
|
|
|
|
|
var inputVm = new WalletPSBTReadyViewModel.InputViewModel();
|
|
|
|
|
vm.Inputs.Add(inputVm);
|
|
|
|
|
var mine = input.HDKeysFor(derivationSchemeSettings.AccountDerivation, signingKey, signingKeyPath).Any();
|
|
|
|
|
var balanceChange2 = input.GetTxOut()?.Value ?? Money.Zero;
|
|
|
|
|
if (mine)
|
|
|
|
|
balanceChange2 = -balanceChange2;
|
|
|
|
|
inputVm.BalanceChange = ValueToString(balanceChange2, network);
|
|
|
|
|
inputVm.Positive = balanceChange2 >= Money.Zero;
|
|
|
|
|
inputVm.Index = (int)input.Index;
|
|
|
|
|
}
|
|
|
|
|
|
2019-05-15 08:00:09 +02:00
|
|
|
|
foreach (var output in psbtObject.Outputs)
|
|
|
|
|
{
|
|
|
|
|
var dest = new WalletPSBTReadyViewModel.DestinationViewModel();
|
|
|
|
|
vm.Destinations.Add(dest);
|
2019-05-19 16:27:18 +02:00
|
|
|
|
var mine = output.HDKeysFor(derivationSchemeSettings.AccountDerivation, signingKey, signingKeyPath).Any();
|
2019-05-15 08:00:09 +02:00
|
|
|
|
var balanceChange2 = output.Value;
|
|
|
|
|
if (!mine)
|
|
|
|
|
balanceChange2 = -balanceChange2;
|
|
|
|
|
dest.Balance = ValueToString(balanceChange2, network);
|
|
|
|
|
dest.Positive = balanceChange2 >= Money.Zero;
|
|
|
|
|
dest.Destination = output.ScriptPubKey.GetDestinationAddress(network.NBitcoinNetwork)?.ToString() ?? output.ScriptPubKey.ToString();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (psbtObject.TryGetFee(out var fee))
|
|
|
|
|
{
|
2019-05-19 16:27:18 +02:00
|
|
|
|
vm.Destinations.Add(new WalletPSBTReadyViewModel.DestinationViewModel()
|
|
|
|
|
{
|
|
|
|
|
Positive = false,
|
|
|
|
|
Balance = ValueToString(- fee, network),
|
|
|
|
|
Destination = "Mining fees"
|
|
|
|
|
});
|
2019-05-15 08:00:09 +02:00
|
|
|
|
}
|
2019-05-16 05:56:06 +02:00
|
|
|
|
if (psbtObject.TryGetEstimatedFeeRate(out var feeRate))
|
|
|
|
|
{
|
|
|
|
|
vm.FeeRate = feeRate.ToString();
|
|
|
|
|
}
|
2019-05-19 16:27:18 +02:00
|
|
|
|
|
2019-05-30 17:23:23 +02:00
|
|
|
|
var sanityErrors = psbtObject.CheckSanity();
|
|
|
|
|
if (sanityErrors.Count != 0)
|
|
|
|
|
{
|
|
|
|
|
vm.SetErrors(sanityErrors);
|
|
|
|
|
}
|
|
|
|
|
else if (!psbtObject.IsAllFinalized() && !psbtObject.TryFinalize(out var errors))
|
2019-05-19 16:27:18 +02:00
|
|
|
|
{
|
|
|
|
|
vm.SetErrors(errors);
|
|
|
|
|
}
|
2019-05-12 04:13:04 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[HttpPost]
|
|
|
|
|
[Route("{walletId}/psbt/ready")]
|
|
|
|
|
public async Task<IActionResult> WalletPSBTReady(
|
|
|
|
|
[ModelBinder(typeof(WalletIdModelBinder))]
|
|
|
|
|
WalletId walletId, WalletPSBTReadyViewModel vm, string command = null)
|
|
|
|
|
{
|
2019-11-08 12:21:33 +01:00
|
|
|
|
if (command == null)
|
|
|
|
|
return await WalletPSBTReady(walletId, vm.PSBT, vm.SigningKey, vm.SigningKeyPath);
|
2019-05-12 04:13:04 +02:00
|
|
|
|
PSBT psbt = null;
|
2019-05-29 11:43:50 +02:00
|
|
|
|
var network = NetworkProvider.GetNetwork<BTCPayNetwork>(walletId.CryptoCode);
|
2019-05-12 04:13:04 +02:00
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
psbt = PSBT.Parse(vm.PSBT, network.NBitcoinNetwork);
|
2019-10-12 13:35:30 +02:00
|
|
|
|
var derivationSchemeSettings = GetDerivationSchemeSettings(walletId);
|
2019-05-27 14:50:08 +02:00
|
|
|
|
if (derivationSchemeSettings == null)
|
|
|
|
|
return NotFound();
|
|
|
|
|
await FetchTransactionDetails(derivationSchemeSettings, vm, network);
|
2019-05-12 04:13:04 +02:00
|
|
|
|
}
|
|
|
|
|
catch
|
|
|
|
|
{
|
2019-05-19 16:27:18 +02:00
|
|
|
|
vm.GlobalError = "Invalid PSBT";
|
2019-05-12 04:13:04 +02:00
|
|
|
|
return View(vm);
|
|
|
|
|
}
|
|
|
|
|
if (command == "broadcast")
|
|
|
|
|
{
|
|
|
|
|
if (!psbt.IsAllFinalized() && !psbt.TryFinalize(out var errors))
|
|
|
|
|
{
|
2019-05-19 16:27:18 +02:00
|
|
|
|
vm.SetErrors(errors);
|
2019-05-12 04:13:04 +02:00
|
|
|
|
return View(vm);
|
|
|
|
|
}
|
|
|
|
|
var transaction = psbt.ExtractTransaction();
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
var broadcastResult = await ExplorerClientProvider.GetExplorerClient(network).BroadcastAsync(transaction);
|
|
|
|
|
if (!broadcastResult.Success)
|
|
|
|
|
{
|
2019-05-19 16:27:18 +02:00
|
|
|
|
vm.GlobalError = $"RPC Error while broadcasting: {broadcastResult.RPCCode} {broadcastResult.RPCCodeMessage} {broadcastResult.RPCMessage}";
|
2019-05-12 04:13:04 +02:00
|
|
|
|
return View(vm);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
2019-05-19 16:27:18 +02:00
|
|
|
|
vm.GlobalError = "Error while broadcasting: " + ex.Message;
|
2019-05-12 04:13:04 +02:00
|
|
|
|
return View(vm);
|
|
|
|
|
}
|
2019-10-12 13:35:30 +02:00
|
|
|
|
return RedirectToWalletTransaction(walletId, transaction);
|
2019-05-12 04:13:04 +02:00
|
|
|
|
}
|
|
|
|
|
else if (command == "analyze-psbt")
|
|
|
|
|
{
|
2019-11-08 12:21:33 +01:00
|
|
|
|
return RedirectToWalletPSBT(walletId, psbt);
|
2019-05-12 04:13:04 +02:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2019-05-19 16:27:18 +02:00
|
|
|
|
vm.GlobalError = "Unknown command";
|
2019-05-12 04:13:04 +02:00
|
|
|
|
return View(vm);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private IActionResult FilePSBT(PSBT psbt, string fileName)
|
|
|
|
|
{
|
|
|
|
|
return File(psbt.ToBytes(), "application/octet-stream", fileName);
|
|
|
|
|
}
|
2019-05-12 06:13:52 +02:00
|
|
|
|
|
|
|
|
|
[HttpPost]
|
|
|
|
|
[Route("{walletId}/psbt/combine")]
|
|
|
|
|
public async Task<IActionResult> WalletPSBTCombine([ModelBinder(typeof(WalletIdModelBinder))]
|
|
|
|
|
WalletId walletId, WalletPSBTCombineViewModel vm)
|
|
|
|
|
{
|
2019-05-29 11:43:50 +02:00
|
|
|
|
var network = NetworkProvider.GetNetwork<BTCPayNetwork>(walletId.CryptoCode);
|
2019-05-12 06:13:52 +02:00
|
|
|
|
var psbt = await vm.GetPSBT(network.NBitcoinNetwork);
|
|
|
|
|
if (psbt == null)
|
|
|
|
|
{
|
|
|
|
|
ModelState.AddModelError(nameof(vm.PSBT), "Invalid PSBT");
|
|
|
|
|
return View(vm);
|
|
|
|
|
}
|
|
|
|
|
var sourcePSBT = vm.GetSourcePSBT(network.NBitcoinNetwork);
|
|
|
|
|
if (sourcePSBT == null)
|
|
|
|
|
{
|
|
|
|
|
ModelState.AddModelError(nameof(vm.OtherPSBT), "Invalid PSBT");
|
|
|
|
|
return View(vm);
|
|
|
|
|
}
|
|
|
|
|
sourcePSBT = sourcePSBT.Combine(psbt);
|
2019-10-31 04:29:59 +01:00
|
|
|
|
TempData[WellKnownTempData.SuccessMessage] = "PSBT Successfully combined!";
|
2019-11-08 12:21:33 +01:00
|
|
|
|
return RedirectToWalletPSBT(walletId, sourcePSBT);
|
2019-05-12 06:13:52 +02:00
|
|
|
|
}
|
2019-05-12 04:13:04 +02:00
|
|
|
|
}
|
|
|
|
|
}
|