btcpayserver/BTCPayServer/DerivationSchemeSettings.cs

189 lines
6.4 KiB
C#
Raw Normal View History

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using BTCPayServer.Payments;
using NBitcoin;
using NBXplorer.DerivationStrategy;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
namespace BTCPayServer
{
public class DerivationSchemeSettings : ISupportedPaymentMethod
{
public static DerivationSchemeSettings Parse(string derivationStrategy, BTCPayNetwork network)
{
if (network == null)
throw new ArgumentNullException(nameof(network));
if (derivationStrategy == null)
throw new ArgumentNullException(nameof(derivationStrategy));
var result = new NBXplorer.DerivationStrategy.DerivationStrategyFactory(network.NBitcoinNetwork).Parse(derivationStrategy);
return new DerivationSchemeSettings(result, network) { AccountOriginal = derivationStrategy.Trim() };
}
2019-05-08 17:40:30 +02:00
public static bool TryParseFromJson(string config, BTCPayNetwork network, out DerivationSchemeSettings strategy)
{
if (network == null)
throw new ArgumentNullException(nameof(network));
if (config == null)
throw new ArgumentNullException(nameof(config));
strategy = null;
try
{
strategy = network.NBXplorerNetwork.Serializer.ToObject<DerivationSchemeSettings>(config);
strategy.Network = network;
}
catch { }
return strategy != null;
}
2019-05-08 17:40:30 +02:00
public static bool TryParseFromColdcard(string coldcardExport, BTCPayNetwork network, out DerivationSchemeSettings settings)
{
settings = null;
if (coldcardExport == null)
throw new ArgumentNullException(nameof(coldcardExport));
if (network == null)
throw new ArgumentNullException(nameof(network));
var result = new DerivationSchemeSettings();
result.Source = "Coldcard";
2019-05-09 09:05:18 +02:00
var derivationSchemeParser = new DerivationSchemeParser(network);
2019-05-08 17:40:30 +02:00
JObject jobj = null;
try
{
jobj = JObject.Parse(coldcardExport);
jobj = (JObject)jobj["keystore"];
}
catch
{
return false;
}
if (jobj.ContainsKey("xpub"))
{
try
{
result.AccountOriginal = jobj["xpub"].Value<string>().Trim();
result.AccountDerivation = derivationSchemeParser.ParseElectrum(result.AccountOriginal);
if (result.AccountDerivation is DirectDerivationStrategy direct && !direct.Segwit)
result.AccountOriginal = null; // Saving this would be confusing for user, as xpub of electrum is legacy derivation, but for btcpay, it is segwit derivation
2019-05-08 17:40:30 +02:00
}
catch
{
return false;
}
}
else
{
return false;
}
if (jobj.ContainsKey("label"))
{
try
{
result.Label = jobj["label"].Value<string>();
}
catch { return false; }
}
if (jobj.ContainsKey("ckcc_xfp"))
{
try
{
result.RootFingerprint = new HDFingerprint(jobj["ckcc_xfp"].Value<uint>());
}
catch { return false; }
}
if (jobj.ContainsKey("derivation"))
{
try
{
result.AccountKeyPath = new KeyPath(jobj["derivation"].Value<string>());
}
catch { return false; }
}
settings = result;
settings.Network = network;
return true;
}
public DerivationSchemeSettings()
{
}
public DerivationSchemeSettings(DerivationStrategyBase derivationStrategy, BTCPayNetwork network)
{
if (network == null)
throw new ArgumentNullException(nameof(network));
if (derivationStrategy == null)
throw new ArgumentNullException(nameof(derivationStrategy));
AccountDerivation = derivationStrategy;
Network = network;
}
[JsonIgnore]
public BTCPayNetwork Network { get; set; }
public string Source { get; set; }
public KeyPath AccountKeyPath { get; set; }
public DerivationStrategyBase AccountDerivation { get; set; }
public string AccountOriginal { get; set; }
public HDFingerprint? RootFingerprint { get; set; }
public BitcoinExtPubKey ExplicitAccountKey { get; set; }
[JsonIgnore]
public BitcoinExtPubKey AccountKey
{
get
{
return ExplicitAccountKey ?? new BitcoinExtPubKey(AccountDerivation.GetExtPubKeys().First(), Network.NBitcoinNetwork);
}
}
public IEnumerable<NBXplorer.Models.PSBTRebaseKeyRules> GetPSBTRebaseKeyRules()
{
if (AccountKey != null && AccountKeyPath != null && RootFingerprint is HDFingerprint fp)
{
yield return new NBXplorer.Models.PSBTRebaseKeyRules()
{
AccountKey = AccountKey,
AccountKeyPath = AccountKeyPath,
MasterFingerprint = fp
};
}
}
public string Label { get; set; }
[JsonIgnore]
public PaymentMethodId PaymentId => new PaymentMethodId(Network.CryptoCode, PaymentTypes.BTCLike);
public override string ToString()
{
return AccountDerivation.ToString();
}
public string ToPrettyString()
{
return !string.IsNullOrEmpty(Label) ? Label :
!String.IsNullOrEmpty(AccountOriginal) ? AccountOriginal :
ToString();
}
public string ToJson()
{
return Network.NBXplorerNetwork.Serializer.ToString(this);
}
public void RebaseKeyPaths(PSBT psbt)
{
foreach (var rebase in GetPSBTRebaseKeyRules())
{
psbt.RebaseKeyPaths(rebase.AccountKey, rebase.AccountKeyPath, rebase.MasterFingerprint);
}
}
}
}