mirror of
https://github.com/btcpayserver/btcpayserver.git
synced 2025-02-21 22:11:48 +01:00
Better validation of inputs when setting on-chain payment method by API
This commit is contained in:
parent
bbe95d780f
commit
8e927eee73
5 changed files with 74 additions and 16 deletions
|
@ -3314,6 +3314,12 @@ namespace BTCPayServer.Tests
|
|||
await viewOnlyClient.SendHttpRequest($"api/v1/stores/{store.Id}/payment-methods/onchain/BTC/preview", new JObject() { ["config"] = xpub.ToString() }, HttpMethod.Post);
|
||||
|
||||
var method = await client.UpdateStorePaymentMethod(store.Id, "BTC-CHAIN", new UpdatePaymentMethodRequest() { Enabled = true, Config = JValue.CreateString(xpub.ToString())});
|
||||
var method2 = await client.UpdateStorePaymentMethod(store.Id, "BTC-CHAIN", new UpdatePaymentMethodRequest() { Enabled = true, Config = new JObject() { ["derivationScheme"] = xpub.ToString(), ["label"] = "test", ["accountKeyPath"] = "aaaaaaaa/84'/0'/0'" } });
|
||||
Assert.Equal("aaaaaaaa", method2.Config["accountKeySettings"][0]["rootFingerprint"].ToString());
|
||||
Assert.Equal("84'/0'/0'", method2.Config["accountKeySettings"][0]["accountKeyPath"].ToString());
|
||||
var method3 = await client.UpdateStorePaymentMethod(store.Id, "BTC-CHAIN", new UpdatePaymentMethodRequest() { Enabled = true, Config = new JObject() { ["derivationScheme"] = xpub.ToString() } });
|
||||
|
||||
Assert.Equal(method.ToJson(), method3.ToJson());
|
||||
|
||||
Assert.Equal(firstAddress, (await viewOnlyClient.PreviewStoreOnChainPaymentMethodAddresses(store.Id, "BTC")).Addresses.First().Address);
|
||||
await AssertHttpError(403, async () =>
|
||||
|
@ -3378,7 +3384,6 @@ namespace BTCPayServer.Tests
|
|||
|
||||
Assert.Equal(24, generateResponse.Mnemonic.Words.Length);
|
||||
Assert.Equal(Wordlist.Japanese, generateResponse.Mnemonic.WordList);
|
||||
|
||||
}
|
||||
|
||||
[Fact(Timeout = 60 * 2 * 1000)]
|
||||
|
|
|
@ -408,6 +408,7 @@ public partial class UIStoresController
|
|||
var client = _explorerProvider.GetExplorerClient(network);
|
||||
|
||||
var handler = _handlers.GetBitcoinHandler(cryptoCode);
|
||||
|
||||
var vm = new WalletSettingsViewModel
|
||||
{
|
||||
StoreId = storeId,
|
||||
|
@ -417,18 +418,18 @@ public partial class UIStoresController
|
|||
Network = network,
|
||||
IsHotWallet = derivation.IsHotWallet,
|
||||
Source = derivation.Source,
|
||||
RootFingerprint = derivation.GetSigningAccountKeySettings().RootFingerprint.ToString(),
|
||||
DerivationScheme = derivation.AccountDerivation.ToString(),
|
||||
RootFingerprint = derivation.GetSigningAccountKeySettingsOrDefault()?.RootFingerprint.ToString(),
|
||||
DerivationScheme = derivation.AccountDerivation?.ToString(),
|
||||
DerivationSchemeInput = derivation.AccountOriginal,
|
||||
KeyPath = derivation.GetSigningAccountKeySettings().AccountKeyPath?.ToString(),
|
||||
KeyPath = derivation.GetSigningAccountKeySettingsOrDefault()?.AccountKeyPath?.ToString(),
|
||||
UriScheme = network.NBitcoinNetwork.UriScheme,
|
||||
Label = derivation.Label,
|
||||
SelectedSigningKey = derivation.SigningKey.ToString(),
|
||||
SelectedSigningKey = derivation.SigningKey?.ToString(),
|
||||
NBXSeedAvailable = derivation.IsHotWallet &&
|
||||
canUseHotWallet &&
|
||||
!string.IsNullOrEmpty(await client.GetMetadataAsync<string>(derivation.AccountDerivation,
|
||||
WellknownMetadataKeys.MasterHDKey)),
|
||||
AccountKeys = derivation.AccountKeySettings
|
||||
AccountKeys = (derivation.AccountKeySettings ?? [])
|
||||
.Select(e => new WalletSettingsAccountKeyViewModel
|
||||
{
|
||||
AccountKey = e.AccountKey.ToString(),
|
||||
|
@ -441,7 +442,7 @@ public partial class UIStoresController
|
|||
CanUseHotWallet = canUseHotWallet,
|
||||
CanUseRPCImport = rpcImport,
|
||||
StoreName = store.StoreName,
|
||||
CanSetupMultiSig = derivation.AccountKeySettings.Length > 1,
|
||||
CanSetupMultiSig = (derivation.AccountKeySettings ?? []).Length > 1,
|
||||
IsMultiSigOnServer = derivation.IsMultiSigOnServer,
|
||||
DefaultIncludeNonWitnessUtxo = derivation.DefaultIncludeNonWitnessUtxo
|
||||
};
|
||||
|
|
|
@ -80,7 +80,12 @@ namespace BTCPayServer
|
|||
|
||||
public AccountKeySettings GetSigningAccountKeySettings()
|
||||
{
|
||||
return AccountKeySettings.Single(a => a.AccountKey == SigningKey);
|
||||
return (AccountKeySettings ?? []).Single(a => a.AccountKey == SigningKey);
|
||||
}
|
||||
|
||||
public AccountKeySettings GetSigningAccountKeySettingsOrDefault()
|
||||
{
|
||||
return (AccountKeySettings ?? []).SingleOrDefault(a => a.AccountKey == SigningKey);
|
||||
}
|
||||
|
||||
public AccountKeySettings[] AccountKeySettings { get; set; }
|
||||
|
|
|
@ -23,6 +23,7 @@ using NBXplorer.DerivationStrategy;
|
|||
using NBXplorer.Models;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using static Org.BouncyCastle.Math.EC.ECCurve;
|
||||
using StoreData = BTCPayServer.Data.StoreData;
|
||||
|
||||
namespace BTCPayServer.Payments.Bitcoin
|
||||
|
@ -39,7 +40,7 @@ namespace BTCPayServer.Payments.Bitcoin
|
|||
private readonly NBXplorerDashboard _dashboard;
|
||||
private readonly WalletRepository _walletRepository;
|
||||
private readonly Services.Wallets.BTCPayWalletProvider _WalletProvider;
|
||||
|
||||
|
||||
public JsonSerializer Serializer { get; }
|
||||
public PaymentMethodId PaymentMethodId { get; private set; }
|
||||
public BTCPayNetwork Network => _Network;
|
||||
|
@ -233,16 +234,50 @@ namespace BTCPayServer.Payments.Bitcoin
|
|||
return null;
|
||||
return GetAccountDerivation(value, network);
|
||||
}
|
||||
class AlternativeConfig
|
||||
{
|
||||
[JsonProperty]
|
||||
public DerivationStrategyBase DerivationScheme { get; set; }
|
||||
[JsonProperty]
|
||||
public string Label { get; set; }
|
||||
[JsonProperty]
|
||||
public RootedKeyPath AccountKeyPath { get; set; }
|
||||
|
||||
public DerivationSchemeSettings ToDerivationSchemeSettings(BTCPayNetwork network)
|
||||
{
|
||||
if (DerivationScheme is null)
|
||||
return null;
|
||||
var scheme = new DerivationSchemeSettings(DerivationScheme, network);
|
||||
scheme.AccountOriginal = DerivationScheme.ToString();
|
||||
scheme.Label = Label;
|
||||
if (AccountKeyPath is not null && scheme.AccountKeySettings is [{ } ak, ..])
|
||||
{
|
||||
ak.RootFingerprint = AccountKeyPath.MasterFingerprint;
|
||||
ak.AccountKeyPath = AccountKeyPath.KeyPath;
|
||||
}
|
||||
return scheme;
|
||||
}
|
||||
}
|
||||
public Task ValidatePaymentMethodConfig(PaymentMethodConfigValidationContext validationContext)
|
||||
{
|
||||
var parser = Network.GetDerivationSchemeParser();
|
||||
DerivationSchemeSettings settings = new DerivationSchemeSettings();
|
||||
if (parser.TryParseXpub(validationContext.Config.ToString(), ref settings))
|
||||
if (validationContext.Config is JValue { Type: JTokenType.String, Value: string config }
|
||||
&& parser.TryParseXpub(config, ref settings))
|
||||
{
|
||||
validationContext.Config = JToken.FromObject(settings, Serializer);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
var res = validationContext.Config.ToObject<DerivationSchemeSettings>(Serializer);
|
||||
DerivationSchemeSettings res = null;
|
||||
try
|
||||
{
|
||||
res = validationContext.Config.ToObject<AlternativeConfig>(Serializer)?.ToDerivationSchemeSettings(Network);
|
||||
if (res != null)
|
||||
validationContext.Config = JToken.FromObject(res, Serializer);
|
||||
}
|
||||
catch { }
|
||||
|
||||
res ??= validationContext.Config.ToObject<DerivationSchemeSettings>(Serializer);
|
||||
if (res is null)
|
||||
{
|
||||
validationContext.ModelState.AddModelError(nameof(validationContext.Config), "Invalid derivation scheme settings");
|
||||
|
@ -252,6 +287,18 @@ namespace BTCPayServer.Payments.Bitcoin
|
|||
{
|
||||
validationContext.ModelState.AddModelError(nameof(res.AccountDerivation), "Invalid account derivation");
|
||||
}
|
||||
if (res.AccountKeySettings is null)
|
||||
{
|
||||
validationContext.ModelState.AddModelError(nameof(res.AccountKeySettings), "Invalid AccountKeySettings");
|
||||
}
|
||||
if (res.SigningKey is null)
|
||||
{
|
||||
validationContext.ModelState.AddModelError(nameof(res.SigningKey), "Invalid SigningKey");
|
||||
}
|
||||
if (res.GetSigningAccountKeySettingsOrDefault() is null)
|
||||
{
|
||||
validationContext.ModelState.AddModelError(nameof(res.AccountKeySettings), "AccountKeySettings doesn't include the SigningKey");
|
||||
}
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
"/api/v1/stores/{storeId}/payment-methods": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"Store Payment Methods"
|
||||
"Store (Payment Methods)"
|
||||
],
|
||||
"summary": "Get store payment methods",
|
||||
"description": "View information about the stores' configured payment methods",
|
||||
|
@ -79,7 +79,7 @@
|
|||
"/api/v1/stores/{storeId}/payment-methods/{paymentMethodId}": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"Store Payment Methods"
|
||||
"Store (Payment Methods)"
|
||||
],
|
||||
"summary": "Get store payment method",
|
||||
"description": "View information about the stores' configured payment method",
|
||||
|
@ -144,7 +144,7 @@
|
|||
},
|
||||
"put": {
|
||||
"tags": [
|
||||
"Store Payment Methods"
|
||||
"Store (Payment Methods)"
|
||||
],
|
||||
"summary": "Update store's payment method",
|
||||
"description": "Update information about the stores' configured payment method",
|
||||
|
@ -209,7 +209,7 @@
|
|||
},
|
||||
"delete": {
|
||||
"tags": [
|
||||
"Store Payment Methods"
|
||||
"Store (Payment Methods)"
|
||||
],
|
||||
"summary": "Delete store's payment method",
|
||||
"description": "Delete information about the stores' configured payment method",
|
||||
|
@ -369,7 +369,7 @@
|
|||
},
|
||||
"tags": [
|
||||
{
|
||||
"name": "Store Payment Methods",
|
||||
"name": "Store (Payment Methods)",
|
||||
"description": "Store Payment Methods operations"
|
||||
}
|
||||
]
|
||||
|
|
Loading…
Add table
Reference in a new issue