2018-02-25 16:48:12 +01:00
|
|
|
|
using System;
|
2019-12-18 14:28:03 +01:00
|
|
|
|
using Microsoft.AspNetCore.Authorization;
|
2018-02-25 16:48:12 +01:00
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.Globalization;
|
2019-05-08 20:37:37 +02:00
|
|
|
|
using System.IO;
|
2018-02-25 16:48:12 +01:00
|
|
|
|
using System.Linq;
|
|
|
|
|
using System.Net.WebSockets;
|
|
|
|
|
using System.Text;
|
|
|
|
|
using System.Threading;
|
|
|
|
|
using System.Threading.Tasks;
|
|
|
|
|
using BTCPayServer.Data;
|
2020-01-18 06:12:27 +01:00
|
|
|
|
using BTCPayServer.Events;
|
2019-05-08 20:37:37 +02:00
|
|
|
|
using BTCPayServer.Models;
|
2018-02-25 16:48:12 +01:00
|
|
|
|
using BTCPayServer.Models.StoreViewModels;
|
|
|
|
|
using BTCPayServer.Payments;
|
|
|
|
|
using BTCPayServer.Services;
|
2018-05-07 05:17:46 +02:00
|
|
|
|
using LedgerWallet;
|
2019-05-09 09:11:09 +02:00
|
|
|
|
using Microsoft.AspNetCore.Http;
|
2018-02-25 16:48:12 +01:00
|
|
|
|
using Microsoft.AspNetCore.Mvc;
|
|
|
|
|
using NBitcoin;
|
|
|
|
|
using NBXplorer.DerivationStrategy;
|
2019-11-29 21:24:52 +01:00
|
|
|
|
using NBXplorer.Models;
|
2018-02-25 16:48:12 +01:00
|
|
|
|
using Newtonsoft.Json;
|
2019-11-11 06:22:04 +01:00
|
|
|
|
using Newtonsoft.Json.Linq;
|
2018-02-25 16:48:12 +01:00
|
|
|
|
|
|
|
|
|
namespace BTCPayServer.Controllers
|
|
|
|
|
{
|
|
|
|
|
public partial class StoresController
|
|
|
|
|
{
|
|
|
|
|
[HttpGet]
|
2018-03-20 18:48:11 +01:00
|
|
|
|
[Route("{storeId}/derivations/{cryptoCode}")]
|
2019-12-18 14:28:03 +01:00
|
|
|
|
public async Task<IActionResult> AddDerivationScheme(string storeId, string cryptoCode)
|
2018-02-25 16:48:12 +01:00
|
|
|
|
{
|
2018-04-29 19:33:42 +02:00
|
|
|
|
var store = HttpContext.GetStoreData();
|
2018-02-25 16:48:12 +01:00
|
|
|
|
if (store == null)
|
|
|
|
|
return NotFound();
|
2018-04-10 12:07:57 +02:00
|
|
|
|
var network = cryptoCode == null ? null : _ExplorerProvider.GetNetwork(cryptoCode);
|
|
|
|
|
if (network == null)
|
|
|
|
|
{
|
|
|
|
|
return NotFound();
|
|
|
|
|
}
|
2018-03-20 19:05:51 +01:00
|
|
|
|
|
2018-02-25 16:48:12 +01:00
|
|
|
|
DerivationSchemeViewModel vm = new DerivationSchemeViewModel();
|
2018-03-20 18:48:11 +01:00
|
|
|
|
vm.CryptoCode = cryptoCode;
|
2018-04-12 04:48:33 +02:00
|
|
|
|
vm.RootKeyPath = network.GetRootKeyPath();
|
2019-05-25 04:45:36 +02:00
|
|
|
|
vm.Network = network;
|
2019-12-18 14:28:03 +01:00
|
|
|
|
var derivation = GetExistingDerivationStrategy(vm.CryptoCode, store);
|
|
|
|
|
if (derivation != null)
|
|
|
|
|
{
|
|
|
|
|
vm.DerivationScheme = derivation.AccountDerivation.ToString();
|
|
|
|
|
vm.Config = derivation.ToJson();
|
|
|
|
|
}
|
|
|
|
|
vm.Enabled = !store.GetStoreBlob().IsExcluded(new PaymentMethodId(vm.CryptoCode, PaymentTypes.BTCLike));
|
|
|
|
|
vm.CanUseHotWallet = await CanUseHotWallet();
|
2018-02-25 16:48:12 +01:00
|
|
|
|
return View(vm);
|
|
|
|
|
}
|
|
|
|
|
|
2019-05-09 18:05:37 +02:00
|
|
|
|
class GetXPubs
|
|
|
|
|
{
|
|
|
|
|
public BitcoinExtPubKey ExtPubKey { get; set; }
|
|
|
|
|
public DerivationStrategyBase DerivationScheme { get; set; }
|
|
|
|
|
public HDFingerprint RootFingerprint { get; set; }
|
|
|
|
|
public string Source { get; set; }
|
|
|
|
|
}
|
|
|
|
|
|
2018-10-31 16:19:25 +01:00
|
|
|
|
[HttpGet]
|
|
|
|
|
[Route("{storeId}/derivations/{cryptoCode}/ledger/ws")]
|
|
|
|
|
public async Task<IActionResult> AddDerivationSchemeLedger(
|
|
|
|
|
string storeId,
|
|
|
|
|
string cryptoCode,
|
|
|
|
|
string command,
|
2018-12-26 06:04:00 +01:00
|
|
|
|
string keyPath = "")
|
2018-10-31 16:19:25 +01:00
|
|
|
|
{
|
|
|
|
|
if (!HttpContext.WebSockets.IsWebSocketRequest)
|
|
|
|
|
return NotFound();
|
|
|
|
|
|
|
|
|
|
var webSocket = await HttpContext.WebSockets.AcceptWebSocketAsync();
|
2019-05-10 07:36:25 +02:00
|
|
|
|
var hw = new LedgerHardwareWalletService(webSocket);
|
2018-10-31 16:19:25 +01:00
|
|
|
|
object result = null;
|
2019-05-29 11:43:50 +02:00
|
|
|
|
var network = _NetworkProvider.GetNetwork<BTCPayNetwork>(cryptoCode);
|
2018-10-31 16:19:25 +01:00
|
|
|
|
|
|
|
|
|
using (var normalOperationTimeout = new CancellationTokenSource())
|
|
|
|
|
{
|
|
|
|
|
normalOperationTimeout.CancelAfter(TimeSpan.FromMinutes(30));
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
if (command == "test")
|
|
|
|
|
{
|
|
|
|
|
result = await hw.Test(normalOperationTimeout.Token);
|
|
|
|
|
}
|
|
|
|
|
if (command == "getxpub")
|
|
|
|
|
{
|
2018-12-26 06:04:00 +01:00
|
|
|
|
var k = KeyPath.Parse(keyPath);
|
|
|
|
|
if (k.Indexes.Length == 0)
|
|
|
|
|
throw new FormatException("Invalid key path");
|
2019-05-09 18:05:37 +02:00
|
|
|
|
|
|
|
|
|
var getxpubResult = new GetXPubs();
|
|
|
|
|
getxpubResult.ExtPubKey = await hw.GetExtPubKey(network, k, normalOperationTimeout.Token);
|
|
|
|
|
var segwit = network.NBitcoinNetwork.Consensus.SupportSegwit;
|
2019-12-24 08:20:44 +01:00
|
|
|
|
var derivation = network.NBXplorerNetwork.DerivationStrategyFactory.CreateDirectDerivationStrategy(getxpubResult.ExtPubKey, new DerivationStrategyOptions()
|
2019-05-09 18:05:37 +02:00
|
|
|
|
{
|
2019-11-15 10:53:20 +01:00
|
|
|
|
ScriptPubKeyType = segwit ? ScriptPubKeyType.SegwitP2SH : ScriptPubKeyType.Legacy
|
2019-05-09 18:05:37 +02:00
|
|
|
|
});
|
|
|
|
|
getxpubResult.DerivationScheme = derivation;
|
|
|
|
|
getxpubResult.RootFingerprint = (await hw.GetExtPubKey(network, new KeyPath(), normalOperationTimeout.Token)).ExtPubKey.PubKey.GetHDFingerPrint();
|
|
|
|
|
getxpubResult.Source = hw.Device;
|
2018-10-31 16:19:25 +01:00
|
|
|
|
result = getxpubResult;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
catch (OperationCanceledException)
|
|
|
|
|
{ result = new LedgerTestResult() { Success = false, Error = "Timeout" }; }
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{ result = new LedgerTestResult() { Success = false, Error = ex.Message }; }
|
|
|
|
|
finally { hw.Dispose(); }
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
if (result != null)
|
|
|
|
|
{
|
|
|
|
|
UTF8Encoding UTF8NOBOM = new UTF8Encoding(false);
|
2019-05-09 18:05:37 +02:00
|
|
|
|
var bytes = UTF8NOBOM.GetBytes(JsonConvert.SerializeObject(result, network.NBXplorerNetwork.JsonSerializerSettings));
|
2020-01-12 07:32:26 +01:00
|
|
|
|
using var cts = new CancellationTokenSource(2000);
|
|
|
|
|
await webSocket.SendAsync(new ArraySegment<byte>(bytes), WebSocketMessageType.Text, true, cts.Token);
|
2018-10-31 16:19:25 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
catch { }
|
|
|
|
|
finally
|
|
|
|
|
{
|
|
|
|
|
await webSocket.CloseSocket();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return new EmptyResult();
|
|
|
|
|
}
|
|
|
|
|
|
2019-05-08 16:39:11 +02:00
|
|
|
|
private DerivationSchemeSettings GetExistingDerivationStrategy(string cryptoCode, StoreData store)
|
2018-03-20 19:05:51 +01:00
|
|
|
|
{
|
|
|
|
|
var id = new PaymentMethodId(cryptoCode, PaymentTypes.BTCLike);
|
|
|
|
|
var existing = store.GetSupportedPaymentMethods(_NetworkProvider)
|
2019-05-08 16:39:11 +02:00
|
|
|
|
.OfType<DerivationSchemeSettings>()
|
2018-03-20 19:05:51 +01:00
|
|
|
|
.FirstOrDefault(d => d.PaymentId == id);
|
|
|
|
|
return existing;
|
|
|
|
|
}
|
|
|
|
|
|
2018-02-25 16:48:12 +01:00
|
|
|
|
[HttpPost]
|
2018-03-20 18:48:11 +01:00
|
|
|
|
[Route("{storeId}/derivations/{cryptoCode}")]
|
2019-05-08 20:37:37 +02:00
|
|
|
|
public async Task<IActionResult> AddDerivationScheme(string storeId, DerivationSchemeViewModel vm,
|
|
|
|
|
string cryptoCode)
|
2018-02-25 16:48:12 +01:00
|
|
|
|
{
|
2018-03-20 18:48:11 +01:00
|
|
|
|
vm.CryptoCode = cryptoCode;
|
2018-04-29 19:33:42 +02:00
|
|
|
|
var store = HttpContext.GetStoreData();
|
2018-02-25 16:48:12 +01:00
|
|
|
|
if (store == null)
|
|
|
|
|
return NotFound();
|
|
|
|
|
|
2018-03-20 18:48:11 +01:00
|
|
|
|
var network = cryptoCode == null ? null : _ExplorerProvider.GetNetwork(cryptoCode);
|
2018-02-25 16:48:12 +01:00
|
|
|
|
if (network == null)
|
|
|
|
|
{
|
2018-03-20 18:48:11 +01:00
|
|
|
|
return NotFound();
|
2018-02-25 16:48:12 +01:00
|
|
|
|
}
|
2019-05-08 20:37:37 +02:00
|
|
|
|
|
2019-05-25 04:45:36 +02:00
|
|
|
|
vm.Network = network;
|
2018-04-12 04:48:33 +02:00
|
|
|
|
vm.RootKeyPath = network.GetRootKeyPath();
|
2019-05-08 20:37:37 +02:00
|
|
|
|
DerivationSchemeSettings strategy = null;
|
2019-11-11 06:22:04 +01:00
|
|
|
|
|
2018-02-25 16:48:12 +01:00
|
|
|
|
var wallet = _WalletProvider.GetWallet(network);
|
|
|
|
|
if (wallet == null)
|
|
|
|
|
{
|
2018-03-20 18:48:11 +01:00
|
|
|
|
return NotFound();
|
2018-02-25 16:48:12 +01:00
|
|
|
|
}
|
|
|
|
|
|
2019-05-09 09:11:09 +02:00
|
|
|
|
if (!string.IsNullOrEmpty(vm.Config))
|
|
|
|
|
{
|
|
|
|
|
if (!DerivationSchemeSettings.TryParseFromJson(vm.Config, network, out strategy))
|
|
|
|
|
{
|
2019-11-07 10:20:17 +01:00
|
|
|
|
TempData.SetStatusMessageModel(new StatusMessageModel()
|
2019-05-09 09:11:09 +02:00
|
|
|
|
{
|
|
|
|
|
Severity = StatusMessageModel.StatusSeverity.Error,
|
|
|
|
|
Message = "Config file was not in the correct format"
|
2019-11-07 10:20:17 +01:00
|
|
|
|
});
|
2019-05-09 09:11:09 +02:00
|
|
|
|
vm.Confirmation = false;
|
2019-12-16 09:35:41 +01:00
|
|
|
|
return View(nameof(AddDerivationScheme),vm);
|
2019-05-09 09:11:09 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-05-08 20:37:37 +02:00
|
|
|
|
if (vm.ColdcardPublicFile != null)
|
2018-02-25 16:48:12 +01:00
|
|
|
|
{
|
2019-05-09 09:11:09 +02:00
|
|
|
|
if (!DerivationSchemeSettings.TryParseFromColdcard(await ReadAllText(vm.ColdcardPublicFile), network, out strategy))
|
2018-02-25 16:48:12 +01:00
|
|
|
|
{
|
2019-11-07 10:20:17 +01:00
|
|
|
|
TempData.SetStatusMessageModel(new StatusMessageModel()
|
2019-05-08 20:37:37 +02:00
|
|
|
|
{
|
2019-05-09 09:11:09 +02:00
|
|
|
|
Severity = StatusMessageModel.StatusSeverity.Error,
|
|
|
|
|
Message = "Coldcard public file was not in the correct format"
|
2019-11-07 10:20:17 +01:00
|
|
|
|
});
|
2019-05-09 09:11:09 +02:00
|
|
|
|
vm.Confirmation = false;
|
2019-12-16 09:35:41 +01:00
|
|
|
|
return View(nameof(AddDerivationScheme),vm);
|
2018-02-25 16:48:12 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
2019-05-08 20:37:37 +02:00
|
|
|
|
else
|
2018-02-25 16:48:12 +01:00
|
|
|
|
{
|
2019-05-08 20:37:37 +02:00
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
if (!string.IsNullOrEmpty(vm.DerivationScheme))
|
|
|
|
|
{
|
2019-05-09 09:11:09 +02:00
|
|
|
|
var newStrategy = ParseDerivationStrategy(vm.DerivationScheme, null, network);
|
|
|
|
|
if (newStrategy.AccountDerivation != strategy?.AccountDerivation)
|
|
|
|
|
{
|
2019-05-12 17:13:55 +02:00
|
|
|
|
var accountKey = string.IsNullOrEmpty(vm.AccountKey) ? null : new BitcoinExtPubKey(vm.AccountKey, network.NBitcoinNetwork);
|
|
|
|
|
if (accountKey != null)
|
|
|
|
|
{
|
|
|
|
|
var accountSettings = newStrategy.AccountKeySettings.FirstOrDefault(a => a.AccountKey == accountKey);
|
|
|
|
|
if (accountSettings != null)
|
|
|
|
|
{
|
|
|
|
|
accountSettings.AccountKeyPath = vm.KeyPath == null ? null : KeyPath.Parse(vm.KeyPath);
|
|
|
|
|
accountSettings.RootFingerprint = string.IsNullOrEmpty(vm.RootFingerprint) ? (HDFingerprint?)null : new HDFingerprint(NBitcoin.DataEncoders.Encoders.Hex.DecodeData(vm.RootFingerprint));
|
|
|
|
|
}
|
|
|
|
|
}
|
2019-05-09 09:11:09 +02:00
|
|
|
|
strategy = newStrategy;
|
2019-05-09 18:05:37 +02:00
|
|
|
|
strategy.Source = vm.Source;
|
2019-05-09 09:11:09 +02:00
|
|
|
|
vm.DerivationScheme = strategy.AccountDerivation.ToString();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
strategy = null;
|
2019-05-08 20:37:37 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
catch
|
|
|
|
|
{
|
|
|
|
|
ModelState.AddModelError(nameof(vm.DerivationScheme), "Invalid Derivation Scheme");
|
|
|
|
|
vm.Confirmation = false;
|
2019-12-16 09:35:41 +01:00
|
|
|
|
return View(nameof(AddDerivationScheme),vm);
|
2019-05-08 20:37:37 +02:00
|
|
|
|
}
|
2018-02-25 16:48:12 +01:00
|
|
|
|
}
|
2019-05-08 20:37:37 +02:00
|
|
|
|
|
2019-05-09 09:11:09 +02:00
|
|
|
|
var oldConfig = vm.Config;
|
|
|
|
|
vm.Config = strategy == null ? null : strategy.ToJson();
|
|
|
|
|
|
|
|
|
|
PaymentMethodId paymentMethodId = new PaymentMethodId(network.CryptoCode, PaymentTypes.BTCLike);
|
|
|
|
|
var exisingStrategy = store.GetSupportedPaymentMethods(_NetworkProvider)
|
|
|
|
|
.Where(c => c.PaymentId == paymentMethodId)
|
|
|
|
|
.OfType<DerivationSchemeSettings>()
|
|
|
|
|
.FirstOrDefault();
|
2018-10-12 06:17:38 +02:00
|
|
|
|
var storeBlob = store.GetStoreBlob();
|
|
|
|
|
var wasExcluded = storeBlob.GetExcludedPaymentMethods().Match(paymentMethodId);
|
|
|
|
|
var willBeExcluded = !vm.Enabled;
|
2018-08-01 08:59:29 +02:00
|
|
|
|
|
2018-10-12 06:17:38 +02:00
|
|
|
|
var showAddress = // Show addresses if:
|
2019-11-11 06:22:04 +01:00
|
|
|
|
// - If the user is testing the hint address in confirmation screen
|
2019-05-08 20:37:37 +02:00
|
|
|
|
(vm.Confirmation && !string.IsNullOrWhiteSpace(vm.HintAddress)) ||
|
2019-05-09 09:11:09 +02:00
|
|
|
|
// - The user is clicking on continue after changing the config
|
|
|
|
|
(!vm.Confirmation && oldConfig != vm.Config) ||
|
|
|
|
|
// - The user is clickingon continue without changing config nor enabling/disabling
|
|
|
|
|
(!vm.Confirmation && oldConfig == vm.Config && willBeExcluded == wasExcluded);
|
2018-08-01 08:59:29 +02:00
|
|
|
|
|
2018-11-16 17:09:28 +01:00
|
|
|
|
showAddress = showAddress && strategy != null;
|
2018-08-01 08:59:29 +02:00
|
|
|
|
if (!showAddress)
|
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
if (strategy != null)
|
2019-05-08 16:39:11 +02:00
|
|
|
|
await wallet.TrackAsync(strategy.AccountDerivation);
|
2019-05-09 09:11:09 +02:00
|
|
|
|
store.SetSupportedPaymentMethod(paymentMethodId, strategy);
|
2019-05-08 16:39:11 +02:00
|
|
|
|
storeBlob.SetExcluded(paymentMethodId, willBeExcluded);
|
2018-08-01 08:59:29 +02:00
|
|
|
|
store.SetStoreBlob(storeBlob);
|
|
|
|
|
}
|
|
|
|
|
catch
|
|
|
|
|
{
|
|
|
|
|
ModelState.AddModelError(nameof(vm.DerivationScheme), "Invalid Derivation Scheme");
|
|
|
|
|
return View(vm);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
await _Repo.UpdateStore(store);
|
2020-01-18 06:12:27 +01:00
|
|
|
|
_EventAggregator.Publish(new WalletChangedEvent()
|
|
|
|
|
{
|
|
|
|
|
WalletId = new WalletId(storeId, cryptoCode)
|
|
|
|
|
});
|
|
|
|
|
|
2019-05-09 09:11:09 +02:00
|
|
|
|
if (willBeExcluded != wasExcluded)
|
|
|
|
|
{
|
|
|
|
|
var label = willBeExcluded ? "disabled" : "enabled";
|
2019-10-31 04:29:59 +01:00
|
|
|
|
TempData[WellKnownTempData.SuccessMessage] = $"On-Chain payments for {network.CryptoCode} has been {label}.";
|
2019-05-09 09:11:09 +02:00
|
|
|
|
}
|
2019-10-21 06:24:13 +02:00
|
|
|
|
else
|
|
|
|
|
{
|
2019-10-31 04:29:59 +01:00
|
|
|
|
TempData[WellKnownTempData.SuccessMessage] = $"Derivation settings for {network.CryptoCode} has been modified.";
|
2019-10-21 06:24:13 +02:00
|
|
|
|
}
|
2019-11-11 06:22:04 +01:00
|
|
|
|
return RedirectToAction(nameof(UpdateStore), new { storeId = storeId });
|
2018-08-01 08:59:29 +02:00
|
|
|
|
}
|
|
|
|
|
else if (!string.IsNullOrEmpty(vm.HintAddress))
|
2018-03-24 12:40:26 +01:00
|
|
|
|
{
|
|
|
|
|
BitcoinAddress address = null;
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
address = BitcoinAddress.Create(vm.HintAddress, network.NBitcoinNetwork);
|
|
|
|
|
}
|
|
|
|
|
catch
|
|
|
|
|
{
|
|
|
|
|
ModelState.AddModelError(nameof(vm.HintAddress), "Invalid hint address");
|
|
|
|
|
return ShowAddresses(vm, strategy);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
try
|
|
|
|
|
{
|
2019-05-09 09:11:09 +02:00
|
|
|
|
var newStrategy = ParseDerivationStrategy(vm.DerivationScheme, address.ScriptPubKey, network);
|
|
|
|
|
if (newStrategy.AccountDerivation != strategy.AccountDerivation)
|
|
|
|
|
{
|
|
|
|
|
strategy.AccountDerivation = newStrategy.AccountDerivation;
|
|
|
|
|
strategy.AccountOriginal = null;
|
|
|
|
|
}
|
2018-03-24 12:40:26 +01:00
|
|
|
|
}
|
|
|
|
|
catch
|
|
|
|
|
{
|
|
|
|
|
ModelState.AddModelError(nameof(vm.HintAddress), "Impossible to find a match with this address");
|
|
|
|
|
return ShowAddresses(vm, strategy);
|
|
|
|
|
}
|
2019-05-08 20:37:37 +02:00
|
|
|
|
|
2018-03-24 12:40:26 +01:00
|
|
|
|
vm.HintAddress = "";
|
2019-10-31 04:29:59 +01:00
|
|
|
|
TempData[WellKnownTempData.SuccessMessage] =
|
2019-05-08 20:37:37 +02:00
|
|
|
|
"Address successfully found, please verify that the rest is correct and click on \"Confirm\"";
|
2018-03-24 12:40:26 +01:00
|
|
|
|
ModelState.Remove(nameof(vm.HintAddress));
|
|
|
|
|
ModelState.Remove(nameof(vm.DerivationScheme));
|
2018-02-25 16:48:12 +01:00
|
|
|
|
}
|
2019-05-08 20:37:37 +02:00
|
|
|
|
|
2018-08-01 08:59:29 +02:00
|
|
|
|
return ShowAddresses(vm, strategy);
|
2018-03-24 12:40:26 +01:00
|
|
|
|
}
|
2019-05-09 09:11:09 +02:00
|
|
|
|
|
2019-11-29 21:24:52 +01:00
|
|
|
|
[HttpPost]
|
|
|
|
|
[Route("{storeId}/derivations/{cryptoCode}/generatenbxwallet")]
|
2019-12-05 21:07:41 +01:00
|
|
|
|
public async Task<IActionResult> GenerateNBXWallet(string storeId, string cryptoCode,
|
|
|
|
|
GenerateWalletRequest request)
|
2019-11-29 21:24:52 +01:00
|
|
|
|
{
|
2019-12-18 14:28:03 +01:00
|
|
|
|
if (!await CanUseHotWallet())
|
2019-12-16 09:32:43 +01:00
|
|
|
|
{
|
|
|
|
|
return NotFound();
|
|
|
|
|
}
|
|
|
|
|
|
2019-11-29 21:24:52 +01:00
|
|
|
|
var network = _NetworkProvider.GetNetwork<BTCPayNetwork>(cryptoCode);
|
2019-12-05 21:07:41 +01:00
|
|
|
|
var client = _ExplorerProvider.GetExplorerClient(cryptoCode);
|
|
|
|
|
var response = await client.GenerateWalletAsync(request);
|
2019-12-29 16:43:55 +01:00
|
|
|
|
if (response == null)
|
|
|
|
|
{
|
|
|
|
|
TempData.SetStatusMessageModel(new StatusMessageModel()
|
|
|
|
|
{
|
|
|
|
|
Severity = StatusMessageModel.StatusSeverity.Error,
|
|
|
|
|
Html = "There was an error generating your wallet. Is your node available?"
|
|
|
|
|
});
|
|
|
|
|
return RedirectToAction("AddDerivationScheme", new {storeId, cryptoCode});
|
|
|
|
|
}
|
2019-12-05 21:07:41 +01:00
|
|
|
|
var store = HttpContext.GetStoreData();
|
|
|
|
|
var result = await AddDerivationScheme(storeId,
|
|
|
|
|
new DerivationSchemeViewModel()
|
|
|
|
|
{
|
|
|
|
|
Confirmation = false,
|
|
|
|
|
Network = network,
|
|
|
|
|
RootFingerprint = response.AccountKeyPath.MasterFingerprint.ToString(),
|
|
|
|
|
RootKeyPath = network.GetRootKeyPath(),
|
|
|
|
|
CryptoCode = cryptoCode,
|
|
|
|
|
DerivationScheme = response.DerivationScheme.ToString(),
|
|
|
|
|
Source = "NBXplorer",
|
|
|
|
|
AccountKey = response.AccountHDKey.Neuter().ToWif(),
|
|
|
|
|
DerivationSchemeFormat = "BTCPay",
|
|
|
|
|
KeyPath = response.AccountKeyPath.KeyPath.ToString(),
|
|
|
|
|
Enabled = !store.GetStoreBlob()
|
|
|
|
|
.IsExcluded(new PaymentMethodId(cryptoCode, PaymentTypes.BTCLike))
|
|
|
|
|
}, cryptoCode);
|
2019-12-16 09:32:43 +01:00
|
|
|
|
|
2019-12-05 21:07:41 +01:00
|
|
|
|
TempData.SetStatusMessageModel(new StatusMessageModel()
|
|
|
|
|
{
|
|
|
|
|
Severity = StatusMessageModel.StatusSeverity.Success,
|
|
|
|
|
Html = !string.IsNullOrEmpty(request.ExistingMnemonic)
|
|
|
|
|
? "Your wallet has been imported."
|
|
|
|
|
: $"Your wallet has been generated. Please store your seed securely! <br/><code>{response.Mnemonic}</code>"
|
|
|
|
|
});
|
|
|
|
|
return result;
|
2019-11-29 21:24:52 +01:00
|
|
|
|
}
|
|
|
|
|
|
2019-12-18 14:28:03 +01:00
|
|
|
|
private async Task<bool> CanUseHotWallet()
|
|
|
|
|
{
|
|
|
|
|
var isAdmin = (await _authorizationService.AuthorizeAsync(User, BTCPayServer.Security.Policies.CanModifyServerSettings.Key)).Succeeded;
|
|
|
|
|
if (isAdmin)
|
|
|
|
|
return true;
|
|
|
|
|
var policies = await _settingsRepository.GetSettingAsync<PoliciesSettings>();
|
|
|
|
|
return policies?.AllowHotWalletForAll is true;
|
|
|
|
|
}
|
|
|
|
|
|
2019-05-09 09:11:09 +02:00
|
|
|
|
private async Task<string> ReadAllText(IFormFile file)
|
|
|
|
|
{
|
|
|
|
|
using (var stream = new StreamReader(file.OpenReadStream()))
|
|
|
|
|
{
|
|
|
|
|
return await stream.ReadToEndAsync();
|
|
|
|
|
}
|
|
|
|
|
}
|
2018-03-24 12:40:26 +01:00
|
|
|
|
|
2019-12-24 08:20:44 +01:00
|
|
|
|
private IActionResult
|
|
|
|
|
ShowAddresses(DerivationSchemeViewModel vm, DerivationSchemeSettings strategy)
|
2018-03-24 12:40:26 +01:00
|
|
|
|
{
|
2019-05-08 16:39:11 +02:00
|
|
|
|
vm.DerivationScheme = strategy.AccountDerivation.ToString();
|
2019-08-17 08:14:31 +02:00
|
|
|
|
var deposit = new NBXplorer.KeyPathTemplates(null).GetKeyPathTemplate(DerivationFeature.Deposit);
|
2018-03-24 12:40:26 +01:00
|
|
|
|
if (!string.IsNullOrEmpty(vm.DerivationScheme))
|
2018-02-25 16:48:12 +01:00
|
|
|
|
{
|
2019-08-17 08:14:31 +02:00
|
|
|
|
var line = strategy.AccountDerivation.GetLineFor(deposit);
|
2018-02-25 16:48:12 +01:00
|
|
|
|
|
2018-03-24 12:40:26 +01:00
|
|
|
|
for (int i = 0; i < 10; i++)
|
|
|
|
|
{
|
2019-12-10 13:22:46 +01:00
|
|
|
|
var keyPath = deposit.GetKeyPath((uint)i);
|
|
|
|
|
var rootedKeyPath = vm.GetAccountKeypath()?.Derive(keyPath);
|
2019-12-24 08:20:44 +01:00
|
|
|
|
var derivation = line.Derive((uint)i);
|
|
|
|
|
var address = strategy.Network.NBXplorerNetwork.CreateAddress(strategy.AccountDerivation,
|
|
|
|
|
line.KeyPathTemplate.GetKeyPath((uint)i),
|
|
|
|
|
derivation.ScriptPubKey).ToString();
|
|
|
|
|
vm.AddressSamples.Add((keyPath.ToString(), address, rootedKeyPath));
|
2018-02-25 16:48:12 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
2018-03-24 12:40:26 +01:00
|
|
|
|
vm.Confirmation = true;
|
2019-05-25 05:53:03 +02:00
|
|
|
|
ModelState.Remove(nameof(vm.Config)); // Remove the cached value
|
2019-12-16 09:38:00 +01:00
|
|
|
|
return View(nameof(AddDerivationScheme),vm);
|
2018-02-25 16:48:12 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|