Allow flexible derivation scheme for the store

This commit is contained in:
NicolasDorier 2017-10-05 00:05:38 +09:00
parent 51ef3ec656
commit 8b4e572e16
5 changed files with 139 additions and 47 deletions

View File

@ -45,9 +45,9 @@ namespace BTCPayServer.Tests
await store.UpdateStore(StoreId, new StoreViewModel()
{
ExtPubKey = extKey.Neuter().ToString() + "-[legacy]",
DerivationScheme = extKey.Neuter().ToString() + "-[legacy]",
SpeedPolicy = SpeedPolicy.MediumSpeed
});
}, "Save");
Assert.IsType<ViewResult>(await store.RequestPairing(pairingCode.ToString()));
await store.Pair(pairingCode.ToString(), StoreId);
}

View File

@ -10,6 +10,7 @@ using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using NBitcoin;
using NBitpayClient;
using NBXplorer.DerivationStrategy;
using System;
using System.Collections.Generic;
using System.Linq;
@ -28,6 +29,7 @@ namespace BTCPayServer.Controllers
UserManager<ApplicationUser> userManager,
AccessTokenController tokenController,
BTCPayWallet wallet,
Network network,
IHostingEnvironment env)
{
_Repo = repo;
@ -36,7 +38,9 @@ namespace BTCPayServer.Controllers
_TokenController = tokenController;
_Wallet = wallet;
_Env = env;
_Network = network;
}
Network _Network;
BTCPayWallet _Wallet;
AccessTokenController _TokenController;
StoreRepository _Repo;
@ -106,7 +110,7 @@ namespace BTCPayServer.Controllers
vm.StoreName = store.StoreName;
vm.StoreWebsite = store.StoreWebsite;
vm.SpeedPolicy = store.SpeedPolicy;
vm.ExtPubKey = store.DerivationStrategy;
vm.DerivationScheme = store.DerivationStrategy;
vm.StatusMessage = StatusMessage;
return View(vm);
}
@ -114,7 +118,7 @@ namespace BTCPayServer.Controllers
[HttpPost]
[ValidateAntiForgeryToken]
[Route("{storeId}")]
public async Task<IActionResult> UpdateStore(string storeId, StoreViewModel model)
public async Task<IActionResult> UpdateStore(string storeId, StoreViewModel model, string command)
{
if(!ModelState.IsValid)
{
@ -124,48 +128,64 @@ namespace BTCPayServer.Controllers
if(store == null)
return NotFound();
bool needUpdate = false;
if(store.SpeedPolicy != model.SpeedPolicy)
if(command == "Save")
{
needUpdate = true;
store.SpeedPolicy = model.SpeedPolicy;
}
if(store.StoreName != model.StoreName)
{
needUpdate = true;
store.StoreName = model.StoreName;
}
if(store.StoreWebsite != model.StoreWebsite)
{
needUpdate = true;
store.StoreWebsite = model.StoreWebsite;
}
if(store.DerivationStrategy != model.ExtPubKey)
{
needUpdate = true;
try
bool needUpdate = false;
if(store.SpeedPolicy != model.SpeedPolicy)
{
await _Wallet.TrackAsync(model.ExtPubKey);
store.DerivationStrategy = model.ExtPubKey;
needUpdate = true;
store.SpeedPolicy = model.SpeedPolicy;
}
catch
if(store.StoreName != model.StoreName)
{
ModelState.AddModelError(nameof(model.ExtPubKey), "Invalid Derivation Scheme");
return View(model);
needUpdate = true;
store.StoreName = model.StoreName;
}
if(store.StoreWebsite != model.StoreWebsite)
{
needUpdate = true;
store.StoreWebsite = model.StoreWebsite;
}
}
if(needUpdate)
{
await _Repo.UpdateStore(store);
StatusMessage = "Store successfully updated";
}
if(store.DerivationStrategy != model.DerivationScheme)
{
needUpdate = true;
try
{
await _Wallet.TrackAsync(model.DerivationScheme);
store.DerivationStrategy = model.DerivationScheme;
}
catch
{
ModelState.AddModelError(nameof(model.DerivationScheme), "Invalid Derivation Scheme");
return View(model);
}
}
return RedirectToAction(nameof(UpdateStore), new
if(needUpdate)
{
await _Repo.UpdateStore(store);
StatusMessage = "Store successfully updated";
}
return RedirectToAction(nameof(UpdateStore), new
{
storeId = storeId
});
}
else
{
storeId = storeId
});
var facto = new DerivationStrategyFactory(_Network);
var scheme = facto.Parse(model.DerivationScheme);
var line = scheme.GetLineFor(DerivationFeature.Deposit);
for(int i = 0; i < 10; i++)
{
var address = line.Derive((uint)i);
model.AddressSamples.Add((line.Path.Derive((uint)i).ToString(), address.ScriptPubKey.GetDestinationAddress(_Network).ToString()));
}
return View(model);
}
}
[HttpGet]

View File

@ -27,8 +27,8 @@ namespace BTCPayServer.Models.StoreViewModels
set;
}
[ExtPubKeyValidator]
public string ExtPubKey
[DerivationStrategyValidator]
public string DerivationScheme
{
get; set;
}
@ -39,6 +39,11 @@ namespace BTCPayServer.Models.StoreViewModels
get; set;
}
public List<(string KeyPath, string Address)> AddressSamples
{
get; set;
} = new List<(string KeyPath, string Address)>();
public string StatusMessage
{
get; set;

View File

@ -1,4 +1,5 @@
using NBitcoin;
using NBXplorer.DerivationStrategy;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
@ -6,7 +7,7 @@ using System.Text;
namespace BTCPayServer.Validations
{
public class ExtPubKeyValidatorAttribute : ValidationAttribute
public class DerivationStrategyValidatorAttribute : ValidationAttribute
{
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
@ -19,7 +20,7 @@ namespace BTCPayServer.Validations
return new ValidationResult("No Network specified");
try
{
new BitcoinExtPubKey((string)value, network);
new DerivationStrategyFactory(network).Parse((string)value);
return ValidationResult.Success;
}
catch(Exception ex)

View File

@ -36,11 +36,77 @@
<span asp-validation-for="SpeedPolicy" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="ExtPubKey"></label>
<input asp-for="ExtPubKey" class="form-control" />
<span asp-validation-for="ExtPubKey" class="text-danger"></span>
<h5>Derivation Scheme</h5>
@if(Model.AddressSamples.Count == 0)
{
<span>The DerivationScheme represents the destination of the funds received by your invoice. It is generated by your wallet software. Please, verify that you are generating the right addresses by clicking on 'Check ExtPubKey'</span>
}
</div>
<button type="submit" class="btn btn-default">Save</button>
<div class="form-group">
<input asp-for="DerivationScheme" class="form-control" />
<span asp-validation-for="DerivationScheme" class="text-danger"></span>
</div>
<div class="form-group">
@if(Model.AddressSamples.Count == 0)
{
<table class="table">
<thead class="thead-inverse">
<tr>
<th>Address type</th>
<th>Example</th>
</tr>
</thead>
<tbody>
<tr>
<td>P2WPKH</td>
<td>xpub</td>
</tr>
<tr>
<td>P2SH-P2WPKH</td>
<td>xpub-[p2sh]</td>
</tr>
<tr>
<td>P2SH</td>
<td>xpub-[legacy]</td>
</tr>
<tr>
<td>Multi-sig P2WSH</td>
<td>2-of-xpub1-xpub2</td>
</tr>
<tr>
<td>Multi-sig P2SH-P2WSH</td>
<td>2-of-xpub1-xpub2-[p2sh]</td>
</tr>
<tr>
<td>Multi-sig P2SH</td>
<td>2-of-xpub1-xpub2-[legacy]</td>
</tr>
</tbody>
</table>
}
else
{
<table class="table">
<thead class="thead-inverse">
<tr>
<th>Key path</th>
<th>Address</th>
</tr>
</thead>
<tbody>
@foreach(var sample in Model.AddressSamples)
{
<tr>
<td>@sample.KeyPath</td>
<td>@sample.Address</td>
</tr>
}
</tbody>
</table>
}
</div>
<button name="command" type="submit" class="btn btn-success" value="Save">Save</button>
<button name="command" type="submit" class="btn btn-default" value="Check">Check ExtPubKey</button>
</form>
</div>
</div>