btcpayserver/BTCPayServer/Services/WalletFileParsing/WasabiWalletFileParser.cs
Andrew Camilleri c5a0e28420
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>
2024-01-17 18:08:39 +09:00

105 lines
3.4 KiB
C#

#nullable enable
using System;
using System.Linq;
using BTCPayServer;
using NBitcoin;
using NBitcoin.DataEncoders;
using Newtonsoft.Json.Linq;
namespace BTCPayServer.Services.WalletFileParsing;
public class WasabiWalletFileParser : IWalletFileParser
{
public (DerivationSchemeSettings? DerivationSchemeSettings, string? Error) TryParse(BTCPayNetwork network,
string data)
{
try
{
var jobj = JObject.Parse(data);
if (jobj["ExtPubKey"]?.Value<string>() is not string extPubKey)
return (null, null);
var derivationSchemeParser = network.GetDerivationSchemeParser();
var result = new DerivationSchemeSettings()
{
Network = network
};
if (!derivationSchemeParser.TryParseXpub(extPubKey, ref result, out var error))
{
return (null, error);
}
if (jobj["MasterFingerprint"]?.ToString()?.Trim() is string mfpString)
{
try
{
// https://github.com/zkSNACKs/WalletWasabi/pull/1663#issuecomment-508073066
if (uint.TryParse(mfpString, out var fingerprint))
{
result.AccountKeySettings[0].RootFingerprint = new HDFingerprint(fingerprint);
}
else
{
var bytes = Encoders.Hex.DecodeData(mfpString);
var shouldReverseMfp = jobj["ColdCardFirmwareVersion"]?.Value<string>() == "2.1.0";
if (shouldReverseMfp) // Bug in previous version of coldcard
bytes = bytes.Reverse().ToArray();
result.AccountKeySettings[0].RootFingerprint = new HDFingerprint(bytes);
}
}
catch
{
return (null, "MasterFingerprint was not valid");
}
}
if (jobj["AccountKeyPath"]?.Value<string>() is string accountKeyPath)
{
try
{
result.AccountKeySettings[0].AccountKeyPath = new KeyPath(accountKeyPath);
}
catch
{
return (null, "AccountKeyPath was not valid");
}
}
if (jobj["DerivationPath"]?.Value<string>()?.ToLowerInvariant() is string derivationPath)
{
try
{
result.AccountKeySettings[0].AccountKeyPath = new KeyPath(derivationPath);
}
catch
{
return (null, "Derivation path was not valid");
}
}
if (jobj.ContainsKey("ColdCardFirmwareVersion"))
{
result.Source = "ColdCard";
}
else if (jobj.ContainsKey("CoboVaultFirmwareVersion"))
{
result.Source = "CoboVault";
}
else if (jobj.TryGetValue("Source", StringComparison.InvariantCultureIgnoreCase, out var source))
{
result.Source = source.Value<string>();
}
else
result.Source = "WasabiFile";
return (result, null);
}
catch (Exception)
{
return (null, null);
}
}
}