mirror of
https://github.com/aljazceru/btcpayserver.git
synced 2025-12-18 14:34:23 +01:00
Refactor Wallet import code (#5638)
* Refactor Wallet import code The code for wallet import was incredibly messy as it evolved over time from various requests. This PR: * splits up each supported format into its own file * Supports taproot descriptors (through a hack until NBitcoin supports it internally) fixes #5518 * Reduces different paths for handling electrum/non-electrum xpubs * Allows plugins to add their own import support formats for onchain wallets. * Update NBitcoin to parse tr descriptors * Fix warnings * Use dedicated type OnChainWalletParsers --------- Co-authored-by: nicolas.dorier <nicolas.dorier@gmail.com>
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using NBitcoin;
|
||||
using NBitcoin.Scripting;
|
||||
@@ -33,7 +34,7 @@ namespace BTCPayServer
|
||||
{
|
||||
throw new FormatException("Custom change paths are not supported.");
|
||||
}
|
||||
return (Parse($"{hd.Extkey}{suffix}"), null);
|
||||
return (Parse($"{hd.Extkey}{suffix}", true, false, false), null);
|
||||
case PubKeyProvider.Origin origin:
|
||||
var innerResult = ExtractFromPkProvider(origin.Inner, suffix);
|
||||
return (innerResult.Item1, new[] { origin.KeyOriginInfo });
|
||||
@@ -47,12 +48,14 @@ namespace BTCPayServer
|
||||
var xpubs = multi.PkProviders.Select(provider => ExtractFromPkProvider(provider));
|
||||
return (
|
||||
Parse(
|
||||
$"{multi.Threshold}-of-{(string.Join('-', xpubs.Select(tuple => tuple.Item1.ToString())))}{(multi.IsSorted ? "" : "-[keeporder]")}"),
|
||||
$"{multi.Threshold}-of-{(string.Join('-', xpubs.Select(tuple => tuple.Item1.ToString())))}{(multi.IsSorted ? "" : "-[keeporder]")}", true ,false, false),
|
||||
xpubs.SelectMany(tuple => tuple.Item2).ToArray());
|
||||
}
|
||||
|
||||
ArgumentNullException.ThrowIfNull(str);
|
||||
str = str.Trim();
|
||||
//nbitcoin output descriptor does not support taproot, so let's check if it is a taproot descriptor and fake until it is supported
|
||||
|
||||
var outputDescriptor = OutputDescriptor.Parse(str, Network);
|
||||
switch (outputDescriptor)
|
||||
{
|
||||
@@ -78,11 +81,13 @@ namespace BTCPayServer
|
||||
sh.Inner is OutputDescriptor.WSH)
|
||||
{
|
||||
var ds = ParseOutputDescriptor(sh.Inner.ToString());
|
||||
return (Parse(ds.Item1 + suffix), ds.Item2);
|
||||
return (Parse(ds.Item1 + suffix, true, false, false), ds.Item2);
|
||||
};
|
||||
throw new FormatException("sh descriptors are only supported with multsig(legacy or p2wsh) and segwit(p2wpkh)");
|
||||
case OutputDescriptor.Tr tr:
|
||||
return ExtractFromPkProvider(tr.InnerPubkey, "-[taproot]");
|
||||
case OutputDescriptor.WPKH wpkh:
|
||||
return ExtractFromPkProvider(wpkh.PkProvider, "");
|
||||
return ExtractFromPkProvider(wpkh.PkProvider);
|
||||
case OutputDescriptor.WSH { Inner: OutputDescriptor.Multi multi }:
|
||||
return ExtractFromMulti(multi);
|
||||
case OutputDescriptor.WSH:
|
||||
@@ -91,36 +96,7 @@ namespace BTCPayServer
|
||||
throw new ArgumentOutOfRangeException(nameof(outputDescriptor));
|
||||
}
|
||||
}
|
||||
|
||||
public DerivationStrategyBase ParseElectrum(string str)
|
||||
{
|
||||
|
||||
ArgumentNullException.ThrowIfNull(str);
|
||||
str = str.Trim();
|
||||
var data = Network.GetBase58CheckEncoder().DecodeData(str);
|
||||
if (data.Length < 4)
|
||||
throw new FormatException();
|
||||
var prefix = Utils.ToUInt32(data, false);
|
||||
|
||||
var standardPrefix = Utils.ToBytes(0x0488b21eU, false);
|
||||
for (int ii = 0; ii < 4; ii++)
|
||||
data[ii] = standardPrefix[ii];
|
||||
var extPubKey = GetBitcoinExtPubKeyByNetwork(Network, data);
|
||||
if (!BtcPayNetwork.ElectrumMapping.TryGetValue(prefix, out var type))
|
||||
{
|
||||
throw new FormatException();
|
||||
}
|
||||
if (type == DerivationType.Segwit)
|
||||
return new DirectDerivationStrategy(extPubKey, true);
|
||||
if (type == DerivationType.Legacy)
|
||||
return new DirectDerivationStrategy(extPubKey, false);
|
||||
if (type == DerivationType.SegwitP2SH)
|
||||
return BtcPayNetwork.NBXplorerNetwork.DerivationStrategyFactory.Parse(extPubKey.ToString() + "-[p2sh]");
|
||||
throw new FormatException();
|
||||
}
|
||||
|
||||
|
||||
public DerivationStrategyBase Parse(string str)
|
||||
public DerivationStrategyBase Parse(string str, bool ignorePrefix = false, bool ignoreBasePrefix = false, bool enforceNetworkPrefix = true)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(str);
|
||||
str = str.Trim();
|
||||
@@ -133,14 +109,6 @@ namespace BTCPayServer
|
||||
str = str.Replace("-[p2sh]", string.Empty, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
return BtcPayNetwork.NBXplorerNetwork.DerivationStrategyFactory.Parse(str);
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
|
||||
var parts = str.Split('-');
|
||||
bool hasLabel = false;
|
||||
for (int i = 0; i < parts.Length; i++)
|
||||
@@ -157,6 +125,7 @@ namespace BTCPayServer
|
||||
hintedLabels.Add(parts[i].Substring(1, parts[i].Length - 2).ToLowerInvariant());
|
||||
continue;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var data = Network.GetBase58CheckEncoder().DecodeData(parts[i]);
|
||||
@@ -170,11 +139,15 @@ namespace BTCPayServer
|
||||
|
||||
var derivationScheme = GetBitcoinExtPubKeyByNetwork(Network, data).ToString();
|
||||
|
||||
if (BtcPayNetwork.ElectrumMapping.TryGetValue(prefix, out var type))
|
||||
if (enforceNetworkPrefix && !BtcPayNetwork.ElectrumMapping.ContainsKey(prefix))
|
||||
throw new FormatException(
|
||||
$"Invalid xpub. Is this really for {BtcPayNetwork.CryptoCode} {Network.ChainName}?");
|
||||
|
||||
if (!ignorePrefix && !hasLabel && BtcPayNetwork.ElectrumMapping.TryGetValue(prefix, out var type))
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case DerivationType.Legacy:
|
||||
case DerivationType.Legacy when !ignoreBasePrefix:
|
||||
hintedLabels.Add("legacy");
|
||||
break;
|
||||
case DerivationType.SegwitP2SH:
|
||||
@@ -182,8 +155,13 @@ namespace BTCPayServer
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
parts[i] = derivationScheme;
|
||||
}
|
||||
catch (FormatException e) when (e.Message.StartsWith("Invalid xpub"))
|
||||
{
|
||||
throw;
|
||||
}
|
||||
catch { continue; }
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user