From decd2a5d75bbf731698fa280eb6ec57ee290714d Mon Sep 17 00:00:00 2001 From: Marcos Date: Wed, 25 Oct 2023 18:06:54 +0200 Subject: [PATCH 01/13] feat: Bitcoin Output descriptors for wallets --- src/Helpers/WalletParser.cs | 103 ++++++ src/Pages/Wallets.razor | 63 ++++ .../Helpers/WalletParserTests.cs | 322 +++++++++++++++++- 3 files changed, 487 insertions(+), 1 deletion(-) diff --git a/src/Helpers/WalletParser.cs b/src/Helpers/WalletParser.cs index de8a6bec..4fe42610 100644 --- a/src/Helpers/WalletParser.cs +++ b/src/Helpers/WalletParser.cs @@ -1,6 +1,9 @@ +using System.Text; +using Humanizer; using NBitcoin; using NBitcoin.Scripting; using NBXplorer.DerivationStrategy; +using NodeGuard.Data.Models; namespace NodeGuard.Helpers; @@ -105,4 +108,104 @@ DerivationStrategyBase Parse(string str) return strategy; } } + + public static string GetOutputDescriptor(Wallet wallet) + { + var network = Network.GetNetwork(Constants.BITCOIN_NETWORK); + OutputDescriptor outputDescriptor = null; + PubKeyProvider pubKeyProvider; + + if (wallet.IsHotWallet) + { + var key = wallet.Keys.FirstOrDefault(); + pubKeyProvider = PubKeyProvider.NewHD( + new BitcoinExtPubKey( + ExtPubKey.Parse(key.XPUB, network), + network + ), + new KeyPath(""), + key.Path.EndsWith("'") ? PubKeyProvider.DeriveType.HARDENED : PubKeyProvider.DeriveType.UNHARDENED + ); + + if (!wallet.IsBIP39Imported) + { + var internalKey = wallet.Keys.FirstOrDefault(k => k.InternalWalletId != null); + var internalBytes = GetMasterFingerprint(internalKey.MasterFingerprint); + var rootedKeyPath = new RootedKeyPath( + new HDFingerprint(internalBytes), + KeyPath.Parse(internalKey.Path) + ); + pubKeyProvider = PubKeyProvider.NewOrigin(rootedKeyPath, pubKeyProvider); + } + + switch (wallet.WalletAddressType) + { + case WalletAddressType.NativeSegwit: + outputDescriptor = OutputDescriptor.NewWPKH(pubKeyProvider, network); + break; + case WalletAddressType.NestedSegwit: + outputDescriptor = OutputDescriptor.NewWPKH(pubKeyProvider, network); + outputDescriptor = OutputDescriptor.NewSH(outputDescriptor, network); + break; + case WalletAddressType.Legacy: + outputDescriptor = OutputDescriptor.NewPKH(pubKeyProvider, network); + break; + case WalletAddressType.Taproot: + throw new NotImplementedException(); + } + } + else + { + var pubKeyProviders = new List(); + foreach (var k in wallet.Keys) + { + var rootedKeyPath = new RootedKeyPath( + new HDFingerprint(GetMasterFingerprint(k.MasterFingerprint)), + KeyPath.Parse(k.Path) + ); + pubKeyProvider = PubKeyProvider.NewOrigin( + rootedKeyPath, + PubKeyProvider.NewHD( + new BitcoinExtPubKey( + ExtPubKey.Parse(k.XPUB, network), + network + ), + new KeyPath(""), + k.Path.EndsWith("'") ? PubKeyProvider.DeriveType.HARDENED : PubKeyProvider.DeriveType.UNHARDENED + ) + ); + pubKeyProviders.Add(pubKeyProvider); + } + outputDescriptor = OutputDescriptor.NewMulti( + (uint)wallet.MofN, + pubKeyProviders, + !wallet.IsUnSortedMultiSig, + network); + + switch (wallet.WalletAddressType) + { + case WalletAddressType.NativeSegwit: + outputDescriptor = OutputDescriptor.NewWSH(outputDescriptor, network); + break; + case WalletAddressType.NestedSegwit: + outputDescriptor = OutputDescriptor.NewSH(outputDescriptor, network); + break; + case WalletAddressType.Legacy: + break; + case WalletAddressType.Taproot: + throw new NotImplementedException(); + } + } + + return outputDescriptor is not null ? outputDescriptor.ToString() : throw new Exception("Something went wrong"); + } + + public static byte[] GetMasterFingerprint(string masterFingerprint) + { + var internalBytes = Enumerable.Range(0, masterFingerprint.Length) + .Where(x => x % 2 == 0) + .Select(x => Convert.ToByte(masterFingerprint.Substring(x, 2), 16)) + .ToArray(); + return internalBytes; + } } \ No newline at end of file diff --git a/src/Pages/Wallets.razor b/src/Pages/Wallets.razor index 3e812871..118a1fda 100644 --- a/src/Pages/Wallets.razor +++ b/src/Pages/Wallets.razor @@ -20,6 +20,7 @@ @inject ILocalStorageService LocalStorageService @inject ISchedulerFactory SchedulerFactory @inject IPriceConversionService PriceConversionService +@inject IInternalWalletRepository InternalWalletRepository @attribute [Authorize(Roles = "NodeManager, FinanceManager, Superadmin")] @@ -78,6 +79,7 @@ Get address Transfer funds Rescan wallet + Export output descriptor @@ -512,6 +514,36 @@ + + + + @($"Export wallet") + + +

Copy the output descriptor below and use it to import this wallet in other systems.

+

Only segwit descriptors for single sig(WPKH) or multisigs (WSH) are supported.

+

Singlesig: wpkh([…/84'/0'/0']xpub…/0/*)

+

Multisig: wsh(sortedmulti(2,[…/48'/0'/0'/2']xpub…/0/*,[…/48'/0'/0'/2']xpub…/0/*))

+ + @(StringHelper.TruncateHeadAndTail(_outputDescriptorContentModal, 25)) + + + @{ + foreach(var k in _keysContentModal) + { +

+ @($"{k.Name}: {k.Path}") + +

+ } + } +
+ + + +
+
+ + @code { @@ -547,6 +580,10 @@ private List _selectedWalletKeysPlusInternalWalletKey = new(); private List _selectedFinanceManagerAvailableKeys = new(); private Key? _selectedWalletKey; + + private Modal _exportOutputDescriptorModal; + private string _outputDescriptorContentModal; + private HashSet _keysContentModal = new(); private Modal _textModalRef; private string _textModalTitle = string.Empty; @@ -1402,4 +1439,30 @@ } } + private async Task LoadAndOpenExportOutputDescriptor(Wallet contextItem) + { + await CleanTextModal(); + if (contextItem != null) + { + try + { + _outputDescriptorContentModal = WalletParser.GetOutputDescriptor(contextItem); + _keysContentModal = contextItem.Keys as HashSet ?? new HashSet(); + await _exportOutputDescriptorModal.Show(); + } + catch (Exception e) + { + ToastService.ShowError("Error while getting the wallet descriptor"); + } + } + } + + private async Task CloseExportOutputDescriptorModal() + { + await _exportOutputDescriptorModal.Close(CloseReason.UserClosing); + + _outputDescriptorContentModal = string.Empty; + _keysContentModal = new HashSet(); + } + } \ No newline at end of file diff --git a/test/NodeGuard.Tests/Helpers/WalletParserTests.cs b/test/NodeGuard.Tests/Helpers/WalletParserTests.cs index f99a8376..e79f0208 100644 --- a/test/NodeGuard.Tests/Helpers/WalletParserTests.cs +++ b/test/NodeGuard.Tests/Helpers/WalletParserTests.cs @@ -1,8 +1,9 @@ using FluentAssertions; using NBitcoin; using NBXplorer.DerivationStrategy; -using Nodeguard; +using NodeGuard.Data.Models; using NodeGuard.Helpers; +using NSubstitute.ExceptionExtensions; using Key = NodeGuard.Data.Models.Key; namespace NodeGuard.Tests; @@ -149,4 +150,323 @@ public void ParseOutputDescriptor_InvalidSegwitSingleSig(string descriptor, stri } + // Testing GetOutputDescriptor method + [Fact] + public void GetOutputDescriptor_NativeSegwits() + { + Environment.SetEnvironmentVariable("BITCOIN_NETWORK", "mainnet"); + + // Testing NodeGuard created Native Segwit hot wallet + var wallet1HotWalletCreated = new Wallet() + { + IsHotWallet = true, + InternalWalletId = 1, + WalletAddressType = WalletAddressType.NativeSegwit, + MofN = 1, + InternalWalletSubDerivationPath = "0", + Keys = new List() { + new() { + XPUB = "xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB", + Path = "48'/0'/0'", + MasterFingerprint = "ed0210c8", + InternalWalletId = 1, + } + } + }; + var outputDescriptor1 = WalletParser.GetOutputDescriptor(wallet1HotWalletCreated); + outputDescriptor1.Should().Be("wpkh([ed0210c8/48'/0'/0']xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/*')#j646efh8"); + + // Testing NodeGuard imported Native Segwit hot wallet + var wallet1HotWalletImported = new Wallet() + { + IsBIP39Imported = true, + IsHotWallet = true, + WalletAddressType = WalletAddressType.NativeSegwit, + MofN = 1, + Keys = new List() { + new() { + XPUB = "xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB", + Path = "48'/0'/0'", + MasterFingerprint = "ed0210c8", + } + } + }; + var outputDescriptor2 = WalletParser.GetOutputDescriptor(wallet1HotWalletImported); + outputDescriptor2.Should().Be("wpkh(xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/*')#s032ytxu"); + + // Testing NodeGuard created Native Segwit cold wallet + var wallet1ColdWallet = new Wallet() + { + IsHotWallet = false, + WalletAddressType = WalletAddressType.NativeSegwit, + MofN = 2, + Keys = new List() + { + new() + { + XPUB = "xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB", + Path = "48'/0'/0", + MasterFingerprint = "ed0210c8" + }, + new() + { + XPUB = "xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH", + Path = "48'/0'/1", + MasterFingerprint = "ed0210c8" + }, + new() + { + XPUB = "xpub6CUGRUonZSQ4TWtTMmzXdrXDtypWKiKrhko4egpiMZbpiaQL2jkwSB1icqYh2cfDfVxdx4df189oLKnC5fSwqPfgyP3hooxujYzAu3fDVmz", + Path = "48'/0'/2", + MasterFingerprint = "ed0210c8" + } + } + }; + var outputDescriptor3 = WalletParser.GetOutputDescriptor(wallet1ColdWallet); + outputDescriptor3.Should().Be( + "wsh(sortedmulti(2," + + "[ed0210c8/48'/0'/0]xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/*," + + "[ed0210c8/48'/0'/1]xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH/*," + + "[ed0210c8/48'/0'/2]xpub6CUGRUonZSQ4TWtTMmzXdrXDtypWKiKrhko4egpiMZbpiaQL2jkwSB1icqYh2cfDfVxdx4df189oLKnC5fSwqPfgyP3hooxujYzAu3fDVmz/*))#rm8zkn09" + ); + } + + [Fact] + public void GetOutputDescriptor_NestedSegwits() + { + Environment.SetEnvironmentVariable("BITCOIN_NETWORK", "mainnet"); + + // Testing NodeGuard created Nested Segwit hot wallet + var wallet1HotWalletCreated = new Wallet() + { + IsHotWallet = true, + InternalWalletId = 1, + WalletAddressType = WalletAddressType.NestedSegwit, + MofN = 1, + InternalWalletSubDerivationPath = "0", + Keys = new List() { + new() { + XPUB = "xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB", + Path = "48'/0'/0'", + MasterFingerprint = "ed0210c8", + InternalWalletId = 1, + } + } + }; + var outputDescriptor1 = WalletParser.GetOutputDescriptor(wallet1HotWalletCreated); + outputDescriptor1.Should().Be("sh(wpkh([ed0210c8/48'/0'/0']xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/*'))#7run0eul"); + + // Testing NodeGuard imported Nested Segwit hot wallet + var wallet1HotWalletImported = new Wallet() + { + IsBIP39Imported = true, + IsHotWallet = true, + WalletAddressType = WalletAddressType.NestedSegwit, + MofN = 1, + Keys = new List() { + new() { + XPUB = "xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB", + Path = "48'/0'/0'", + MasterFingerprint = "ed0210c8", + } + } + }; + var outputDescriptor2 = WalletParser.GetOutputDescriptor(wallet1HotWalletImported); + outputDescriptor2.Should().Be("sh(wpkh(xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/*'))#m766kd29"); + + // Testing NodeGuard created Nested Segwit cold wallet + var wallet1ColdWallet = new Wallet() + { + IsHotWallet = false, + WalletAddressType = WalletAddressType.NestedSegwit, + MofN = 2, + Keys = new List() + { + new() + { + XPUB = "xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB", + Path = "48'/0'/0", + MasterFingerprint = "ed0210c8" + }, + new() + { + XPUB = "xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH", + Path = "48'/0'/1", + MasterFingerprint = "ed0210c8" + }, + new() + { + XPUB = "xpub6CUGRUonZSQ4TWtTMmzXdrXDtypWKiKrhko4egpiMZbpiaQL2jkwSB1icqYh2cfDfVxdx4df189oLKnC5fSwqPfgyP3hooxujYzAu3fDVmz", + Path = "48'/0'/2", + MasterFingerprint = "ed0210c8" + } + } + }; + var outputDescriptor3 = WalletParser.GetOutputDescriptor(wallet1ColdWallet); + outputDescriptor3.Should().Be( + "sh(sortedmulti(2," + + "[ed0210c8/48'/0'/0]xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/*," + + "[ed0210c8/48'/0'/1]xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH/*," + + "[ed0210c8/48'/0'/2]xpub6CUGRUonZSQ4TWtTMmzXdrXDtypWKiKrhko4egpiMZbpiaQL2jkwSB1icqYh2cfDfVxdx4df189oLKnC5fSwqPfgyP3hooxujYzAu3fDVmz/*))#9y9xajh7" + ); + } + + [Fact] + public void GetOutputDescriptor_Legacy() + { + Environment.SetEnvironmentVariable("BITCOIN_NETWORK", "mainnet"); + + // Testing NodeGuard created Legacy hot wallet + var wallet1HotWalletCreated = new Wallet() + { + IsHotWallet = true, + InternalWalletId = 1, + WalletAddressType = WalletAddressType.Legacy, + MofN = 1, + InternalWalletSubDerivationPath = "0", + Keys = new List() + { + new() + { + XPUB = "xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB", + Path = "48'/0'/0'", + MasterFingerprint = "ed0210c8", + InternalWalletId = 1, + } + } + }; + var outputDescriptor1 = WalletParser.GetOutputDescriptor(wallet1HotWalletCreated); + outputDescriptor1.Should().Be("pkh([ed0210c8/48'/0'/0']xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/*')#tvnkfe78"); + + // Testing NodeGuard imported Legacy hot wallet + var wallet1HotWalletImported = new Wallet() + { + IsBIP39Imported = true, + IsHotWallet = true, + WalletAddressType = WalletAddressType.Legacy, + MofN = 1, + Keys = new List() + { + new() + { + XPUB = "xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB", + Path = "48'/0'/0'", + MasterFingerprint = "ed0210c8", + } + } + }; + var outputDescriptor2 = WalletParser.GetOutputDescriptor(wallet1HotWalletImported); + outputDescriptor2.Should().Be("pkh(xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/*')#6n7l5kek"); + + // Testing NodeGuard created Legacy cold wallet + var wallet1ColdWallet = new Wallet() + { + IsHotWallet = false, + WalletAddressType = WalletAddressType.Legacy, + MofN = 2, + Keys = new List() + { + new() + { + XPUB = "xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB", + Path = "48'/0'/0", + MasterFingerprint = "ed0210c8" + }, + new() + { + XPUB = "xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH", + Path = "48'/0'/1", + MasterFingerprint = "ed0210c8" + }, + new() + { + XPUB = "xpub6CUGRUonZSQ4TWtTMmzXdrXDtypWKiKrhko4egpiMZbpiaQL2jkwSB1icqYh2cfDfVxdx4df189oLKnC5fSwqPfgyP3hooxujYzAu3fDVmz", + Path = "48'/0'/2", + MasterFingerprint = "ed0210c8" + } + } + }; + var outputDescriptor3 = WalletParser.GetOutputDescriptor(wallet1ColdWallet); + outputDescriptor3.Should().Be( + "sortedmulti(2," + + "[ed0210c8/48'/0'/0]xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/*," + + "[ed0210c8/48'/0'/1]xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH/*," + + "[ed0210c8/48'/0'/2]xpub6CUGRUonZSQ4TWtTMmzXdrXDtypWKiKrhko4egpiMZbpiaQL2jkwSB1icqYh2cfDfVxdx4df189oLKnC5fSwqPfgyP3hooxujYzAu3fDVmz/*)#eeg5es07" + ); + } + + [Fact] + public void GetOutputDescriptor_Taproot() + { + Environment.SetEnvironmentVariable("BITCOIN_NETWORK", "mainnet"); + + // Testing NodeGuard created Taproot hot wallet + var wallet1HotWalletCreated = new Wallet() + { + IsHotWallet = true, + InternalWalletId = 1, + WalletAddressType = WalletAddressType.Taproot, + MofN = 1, + InternalWalletSubDerivationPath = "0", + Keys = new List() { + new() { + XPUB = "xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB", + Path = "48'/0'/0'", + MasterFingerprint = "ed0210c8", + InternalWalletId = 1, + } + } + }; + var taprootFunction = () => WalletParser.GetOutputDescriptor(wallet1HotWalletCreated); + taprootFunction.Should().Throw(); + + // Testing NodeGuard imported Taproot hot wallet + var wallet1HotWalletImported = new Wallet() + { + IsBIP39Imported = true, + IsHotWallet = true, + WalletAddressType = WalletAddressType.Taproot, + MofN = 1, + Keys = new List() { + new() { + XPUB = "xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB", + Path = "48'/0'/0'", + MasterFingerprint = "ed0210c8", + } + } + }; + taprootFunction = () => WalletParser.GetOutputDescriptor(wallet1HotWalletImported); + taprootFunction.Should().Throw(); + + // Testing NodeGuard created Taproot cold wallet + var wallet1ColdWallet = new Wallet() + { + IsHotWallet = false, + WalletAddressType = WalletAddressType.Taproot, + MofN = 2, + Keys = new List() + { + new() + { + XPUB = "xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB", + Path = "48'/0'/0", + MasterFingerprint = "ed0210c8" + }, + new() + { + XPUB = "xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH", + Path = "48'/0'/1", + MasterFingerprint = "ed0210c8" + }, + new() + { + XPUB = "xpub6CUGRUonZSQ4TWtTMmzXdrXDtypWKiKrhko4egpiMZbpiaQL2jkwSB1icqYh2cfDfVxdx4df189oLKnC5fSwqPfgyP3hooxujYzAu3fDVmz", + Path = "48'/0'/2", + MasterFingerprint = "ed0210c8" + } + } + }; + taprootFunction = () => WalletParser.GetOutputDescriptor(wallet1ColdWallet); + taprootFunction.Should().Throw(); + } } \ No newline at end of file From 6d85aba5183afa9ccaf7b0993d520ca50df71bc9 Mon Sep 17 00:00:00 2001 From: Marcos Date: Thu, 26 Oct 2023 07:28:47 +0200 Subject: [PATCH 02/13] fix: add bitcoinNetwork as a parameter to the function --- src/Helpers/WalletParser.cs | 4 +-- src/Pages/Wallets.razor | 2 +- .../Helpers/WalletParserTests.cs | 32 +++++++------------ 3 files changed, 15 insertions(+), 23 deletions(-) diff --git a/src/Helpers/WalletParser.cs b/src/Helpers/WalletParser.cs index 4fe42610..a1eaaa37 100644 --- a/src/Helpers/WalletParser.cs +++ b/src/Helpers/WalletParser.cs @@ -109,9 +109,9 @@ DerivationStrategyBase Parse(string str) } } - public static string GetOutputDescriptor(Wallet wallet) + public static string GetOutputDescriptor(Wallet wallet, string bitcoinNetwork) { - var network = Network.GetNetwork(Constants.BITCOIN_NETWORK); + var network = Network.GetNetwork(bitcoinNetwork); OutputDescriptor outputDescriptor = null; PubKeyProvider pubKeyProvider; diff --git a/src/Pages/Wallets.razor b/src/Pages/Wallets.razor index 118a1fda..2132b6e6 100644 --- a/src/Pages/Wallets.razor +++ b/src/Pages/Wallets.razor @@ -1446,7 +1446,7 @@ { try { - _outputDescriptorContentModal = WalletParser.GetOutputDescriptor(contextItem); + _outputDescriptorContentModal = WalletParser.GetOutputDescriptor(contextItem, Constants.BITCOIN_NETWORK); _keysContentModal = contextItem.Keys as HashSet ?? new HashSet(); await _exportOutputDescriptorModal.Show(); } diff --git a/test/NodeGuard.Tests/Helpers/WalletParserTests.cs b/test/NodeGuard.Tests/Helpers/WalletParserTests.cs index e79f0208..855ddbe2 100644 --- a/test/NodeGuard.Tests/Helpers/WalletParserTests.cs +++ b/test/NodeGuard.Tests/Helpers/WalletParserTests.cs @@ -154,8 +154,6 @@ public void ParseOutputDescriptor_InvalidSegwitSingleSig(string descriptor, stri [Fact] public void GetOutputDescriptor_NativeSegwits() { - Environment.SetEnvironmentVariable("BITCOIN_NETWORK", "mainnet"); - // Testing NodeGuard created Native Segwit hot wallet var wallet1HotWalletCreated = new Wallet() { @@ -173,7 +171,7 @@ public void GetOutputDescriptor_NativeSegwits() } } }; - var outputDescriptor1 = WalletParser.GetOutputDescriptor(wallet1HotWalletCreated); + var outputDescriptor1 = WalletParser.GetOutputDescriptor(wallet1HotWalletCreated, "mainnet"); outputDescriptor1.Should().Be("wpkh([ed0210c8/48'/0'/0']xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/*')#j646efh8"); // Testing NodeGuard imported Native Segwit hot wallet @@ -191,7 +189,7 @@ public void GetOutputDescriptor_NativeSegwits() } } }; - var outputDescriptor2 = WalletParser.GetOutputDescriptor(wallet1HotWalletImported); + var outputDescriptor2 = WalletParser.GetOutputDescriptor(wallet1HotWalletImported, "mainnet"); outputDescriptor2.Should().Be("wpkh(xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/*')#s032ytxu"); // Testing NodeGuard created Native Segwit cold wallet @@ -222,7 +220,7 @@ public void GetOutputDescriptor_NativeSegwits() } } }; - var outputDescriptor3 = WalletParser.GetOutputDescriptor(wallet1ColdWallet); + var outputDescriptor3 = WalletParser.GetOutputDescriptor(wallet1ColdWallet, "mainnet"); outputDescriptor3.Should().Be( "wsh(sortedmulti(2," + "[ed0210c8/48'/0'/0]xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/*," + @@ -234,8 +232,6 @@ public void GetOutputDescriptor_NativeSegwits() [Fact] public void GetOutputDescriptor_NestedSegwits() { - Environment.SetEnvironmentVariable("BITCOIN_NETWORK", "mainnet"); - // Testing NodeGuard created Nested Segwit hot wallet var wallet1HotWalletCreated = new Wallet() { @@ -253,7 +249,7 @@ public void GetOutputDescriptor_NestedSegwits() } } }; - var outputDescriptor1 = WalletParser.GetOutputDescriptor(wallet1HotWalletCreated); + var outputDescriptor1 = WalletParser.GetOutputDescriptor(wallet1HotWalletCreated, "mainnet"); outputDescriptor1.Should().Be("sh(wpkh([ed0210c8/48'/0'/0']xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/*'))#7run0eul"); // Testing NodeGuard imported Nested Segwit hot wallet @@ -271,7 +267,7 @@ public void GetOutputDescriptor_NestedSegwits() } } }; - var outputDescriptor2 = WalletParser.GetOutputDescriptor(wallet1HotWalletImported); + var outputDescriptor2 = WalletParser.GetOutputDescriptor(wallet1HotWalletImported, "mainnet"); outputDescriptor2.Should().Be("sh(wpkh(xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/*'))#m766kd29"); // Testing NodeGuard created Nested Segwit cold wallet @@ -302,7 +298,7 @@ public void GetOutputDescriptor_NestedSegwits() } } }; - var outputDescriptor3 = WalletParser.GetOutputDescriptor(wallet1ColdWallet); + var outputDescriptor3 = WalletParser.GetOutputDescriptor(wallet1ColdWallet, "mainnet"); outputDescriptor3.Should().Be( "sh(sortedmulti(2," + "[ed0210c8/48'/0'/0]xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/*," + @@ -314,8 +310,6 @@ public void GetOutputDescriptor_NestedSegwits() [Fact] public void GetOutputDescriptor_Legacy() { - Environment.SetEnvironmentVariable("BITCOIN_NETWORK", "mainnet"); - // Testing NodeGuard created Legacy hot wallet var wallet1HotWalletCreated = new Wallet() { @@ -335,7 +329,7 @@ public void GetOutputDescriptor_Legacy() } } }; - var outputDescriptor1 = WalletParser.GetOutputDescriptor(wallet1HotWalletCreated); + var outputDescriptor1 = WalletParser.GetOutputDescriptor(wallet1HotWalletCreated, "mainnet"); outputDescriptor1.Should().Be("pkh([ed0210c8/48'/0'/0']xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/*')#tvnkfe78"); // Testing NodeGuard imported Legacy hot wallet @@ -355,7 +349,7 @@ public void GetOutputDescriptor_Legacy() } } }; - var outputDescriptor2 = WalletParser.GetOutputDescriptor(wallet1HotWalletImported); + var outputDescriptor2 = WalletParser.GetOutputDescriptor(wallet1HotWalletImported, "mainnet"); outputDescriptor2.Should().Be("pkh(xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/*')#6n7l5kek"); // Testing NodeGuard created Legacy cold wallet @@ -386,7 +380,7 @@ public void GetOutputDescriptor_Legacy() } } }; - var outputDescriptor3 = WalletParser.GetOutputDescriptor(wallet1ColdWallet); + var outputDescriptor3 = WalletParser.GetOutputDescriptor(wallet1ColdWallet, "mainnet"); outputDescriptor3.Should().Be( "sortedmulti(2," + "[ed0210c8/48'/0'/0]xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/*," + @@ -398,8 +392,6 @@ public void GetOutputDescriptor_Legacy() [Fact] public void GetOutputDescriptor_Taproot() { - Environment.SetEnvironmentVariable("BITCOIN_NETWORK", "mainnet"); - // Testing NodeGuard created Taproot hot wallet var wallet1HotWalletCreated = new Wallet() { @@ -417,7 +409,7 @@ public void GetOutputDescriptor_Taproot() } } }; - var taprootFunction = () => WalletParser.GetOutputDescriptor(wallet1HotWalletCreated); + var taprootFunction = () => WalletParser.GetOutputDescriptor(wallet1HotWalletCreated, "mainnet"); taprootFunction.Should().Throw(); // Testing NodeGuard imported Taproot hot wallet @@ -435,7 +427,7 @@ public void GetOutputDescriptor_Taproot() } } }; - taprootFunction = () => WalletParser.GetOutputDescriptor(wallet1HotWalletImported); + taprootFunction = () => WalletParser.GetOutputDescriptor(wallet1HotWalletImported, "mainnet"); taprootFunction.Should().Throw(); // Testing NodeGuard created Taproot cold wallet @@ -466,7 +458,7 @@ public void GetOutputDescriptor_Taproot() } } }; - taprootFunction = () => WalletParser.GetOutputDescriptor(wallet1ColdWallet); + taprootFunction = () => WalletParser.GetOutputDescriptor(wallet1ColdWallet, "mainnet"); taprootFunction.Should().Throw(); } } \ No newline at end of file From 371dcb35fb10a24bc96dd9fcf7753a23f2f47e45 Mon Sep 17 00:00:00 2001 From: Marcos Date: Thu, 26 Oct 2023 10:48:21 +0200 Subject: [PATCH 03/13] fix(WalletParser.cs): change KeyPath from empty string to "/0" to correctly derive child keys from the root key --- src/Helpers/WalletParser.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Helpers/WalletParser.cs b/src/Helpers/WalletParser.cs index a1eaaa37..2cc0e67c 100644 --- a/src/Helpers/WalletParser.cs +++ b/src/Helpers/WalletParser.cs @@ -123,7 +123,7 @@ public static string GetOutputDescriptor(Wallet wallet, string bitcoinNetwork) ExtPubKey.Parse(key.XPUB, network), network ), - new KeyPath(""), + new KeyPath("/0"), key.Path.EndsWith("'") ? PubKeyProvider.DeriveType.HARDENED : PubKeyProvider.DeriveType.UNHARDENED ); @@ -170,7 +170,7 @@ public static string GetOutputDescriptor(Wallet wallet, string bitcoinNetwork) ExtPubKey.Parse(k.XPUB, network), network ), - new KeyPath(""), + new KeyPath("/0"), k.Path.EndsWith("'") ? PubKeyProvider.DeriveType.HARDENED : PubKeyProvider.DeriveType.UNHARDENED ) ); From dcc9eb8d6371719b2dcbb3b0dbd4fe3f8223ee89 Mon Sep 17 00:00:00 2001 From: Marcos Date: Thu, 26 Oct 2023 10:49:08 +0200 Subject: [PATCH 04/13] fix(WalletParser.cs): remove unnecessary conditional statement for deriving key type The conditional statement for deriving the key type based on the path ending with "'" is unnecessary and can be removed. The key type is set to `PubKeyProvider.DeriveType.UNHARDENED` for both cases. --- src/Helpers/WalletParser.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Helpers/WalletParser.cs b/src/Helpers/WalletParser.cs index 2cc0e67c..680f03cc 100644 --- a/src/Helpers/WalletParser.cs +++ b/src/Helpers/WalletParser.cs @@ -124,7 +124,7 @@ public static string GetOutputDescriptor(Wallet wallet, string bitcoinNetwork) network ), new KeyPath("/0"), - key.Path.EndsWith("'") ? PubKeyProvider.DeriveType.HARDENED : PubKeyProvider.DeriveType.UNHARDENED + PubKeyProvider.DeriveType.UNHARDENED ); if (!wallet.IsBIP39Imported) @@ -171,7 +171,7 @@ public static string GetOutputDescriptor(Wallet wallet, string bitcoinNetwork) network ), new KeyPath("/0"), - k.Path.EndsWith("'") ? PubKeyProvider.DeriveType.HARDENED : PubKeyProvider.DeriveType.UNHARDENED + PubKeyProvider.DeriveType.UNHARDENED ) ); pubKeyProviders.Add(pubKeyProvider); From c270d969ea030811ebcd1950bbb89d76d20a69bc Mon Sep 17 00:00:00 2001 From: Marcos Date: Thu, 26 Oct 2023 11:08:57 +0200 Subject: [PATCH 05/13] fix: rearrange tests --- .../Helpers/WalletParserTests.cs | 60 ++++++++++++------- 1 file changed, 40 insertions(+), 20 deletions(-) diff --git a/test/NodeGuard.Tests/Helpers/WalletParserTests.cs b/test/NodeGuard.Tests/Helpers/WalletParserTests.cs index 855ddbe2..88bc1168 100644 --- a/test/NodeGuard.Tests/Helpers/WalletParserTests.cs +++ b/test/NodeGuard.Tests/Helpers/WalletParserTests.cs @@ -154,6 +154,7 @@ public void ParseOutputDescriptor_InvalidSegwitSingleSig(string descriptor, stri [Fact] public void GetOutputDescriptor_NativeSegwits() { + // Arrange // Testing NodeGuard created Native Segwit hot wallet var wallet1HotWalletCreated = new Wallet() { @@ -171,8 +172,6 @@ public void GetOutputDescriptor_NativeSegwits() } } }; - var outputDescriptor1 = WalletParser.GetOutputDescriptor(wallet1HotWalletCreated, "mainnet"); - outputDescriptor1.Should().Be("wpkh([ed0210c8/48'/0'/0']xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/*')#j646efh8"); // Testing NodeGuard imported Native Segwit hot wallet var wallet1HotWalletImported = new Wallet() @@ -189,8 +188,6 @@ public void GetOutputDescriptor_NativeSegwits() } } }; - var outputDescriptor2 = WalletParser.GetOutputDescriptor(wallet1HotWalletImported, "mainnet"); - outputDescriptor2.Should().Be("wpkh(xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/*')#s032ytxu"); // Testing NodeGuard created Native Segwit cold wallet var wallet1ColdWallet = new Wallet() @@ -220,7 +217,15 @@ public void GetOutputDescriptor_NativeSegwits() } } }; + + // Act + var outputDescriptor1 = WalletParser.GetOutputDescriptor(wallet1HotWalletCreated, "mainnet"); + var outputDescriptor2 = WalletParser.GetOutputDescriptor(wallet1HotWalletImported, "mainnet"); var outputDescriptor3 = WalletParser.GetOutputDescriptor(wallet1ColdWallet, "mainnet"); + + // Assert + outputDescriptor1.Should().Be("wpkh([ed0210c8/48'/0'/0']xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/*')#j646efh8"); + outputDescriptor2.Should().Be("wpkh(xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/*')#s032ytxu"); outputDescriptor3.Should().Be( "wsh(sortedmulti(2," + "[ed0210c8/48'/0'/0]xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/*," + @@ -232,6 +237,7 @@ public void GetOutputDescriptor_NativeSegwits() [Fact] public void GetOutputDescriptor_NestedSegwits() { + // Arrange // Testing NodeGuard created Nested Segwit hot wallet var wallet1HotWalletCreated = new Wallet() { @@ -249,8 +255,6 @@ public void GetOutputDescriptor_NestedSegwits() } } }; - var outputDescriptor1 = WalletParser.GetOutputDescriptor(wallet1HotWalletCreated, "mainnet"); - outputDescriptor1.Should().Be("sh(wpkh([ed0210c8/48'/0'/0']xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/*'))#7run0eul"); // Testing NodeGuard imported Nested Segwit hot wallet var wallet1HotWalletImported = new Wallet() @@ -267,8 +271,6 @@ public void GetOutputDescriptor_NestedSegwits() } } }; - var outputDescriptor2 = WalletParser.GetOutputDescriptor(wallet1HotWalletImported, "mainnet"); - outputDescriptor2.Should().Be("sh(wpkh(xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/*'))#m766kd29"); // Testing NodeGuard created Nested Segwit cold wallet var wallet1ColdWallet = new Wallet() @@ -297,8 +299,16 @@ public void GetOutputDescriptor_NestedSegwits() MasterFingerprint = "ed0210c8" } } - }; + }; + + //Act + var outputDescriptor1 = WalletParser.GetOutputDescriptor(wallet1HotWalletCreated, "mainnet"); + var outputDescriptor2 = WalletParser.GetOutputDescriptor(wallet1HotWalletImported, "mainnet"); var outputDescriptor3 = WalletParser.GetOutputDescriptor(wallet1ColdWallet, "mainnet"); + + // Assert + outputDescriptor1.Should().Be("sh(wpkh([ed0210c8/48'/0'/0']xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/*'))#7run0eul"); + outputDescriptor2.Should().Be("sh(wpkh(xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/*'))#m766kd29"); outputDescriptor3.Should().Be( "sh(sortedmulti(2," + "[ed0210c8/48'/0'/0]xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/*," + @@ -310,6 +320,7 @@ public void GetOutputDescriptor_NestedSegwits() [Fact] public void GetOutputDescriptor_Legacy() { + // Arrange // Testing NodeGuard created Legacy hot wallet var wallet1HotWalletCreated = new Wallet() { @@ -329,8 +340,6 @@ public void GetOutputDescriptor_Legacy() } } }; - var outputDescriptor1 = WalletParser.GetOutputDescriptor(wallet1HotWalletCreated, "mainnet"); - outputDescriptor1.Should().Be("pkh([ed0210c8/48'/0'/0']xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/*')#tvnkfe78"); // Testing NodeGuard imported Legacy hot wallet var wallet1HotWalletImported = new Wallet() @@ -349,8 +358,6 @@ public void GetOutputDescriptor_Legacy() } } }; - var outputDescriptor2 = WalletParser.GetOutputDescriptor(wallet1HotWalletImported, "mainnet"); - outputDescriptor2.Should().Be("pkh(xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/*')#6n7l5kek"); // Testing NodeGuard created Legacy cold wallet var wallet1ColdWallet = new Wallet() @@ -380,7 +387,15 @@ public void GetOutputDescriptor_Legacy() } } }; + + // Act + var outputDescriptor1 = WalletParser.GetOutputDescriptor(wallet1HotWalletCreated, "mainnet"); + var outputDescriptor2 = WalletParser.GetOutputDescriptor(wallet1HotWalletImported, "mainnet"); var outputDescriptor3 = WalletParser.GetOutputDescriptor(wallet1ColdWallet, "mainnet"); + + // Assert + outputDescriptor1.Should().Be("pkh([ed0210c8/48'/0'/0']xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/*')#tvnkfe78"); + outputDescriptor2.Should().Be("pkh(xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/*')#6n7l5kek"); outputDescriptor3.Should().Be( "sortedmulti(2," + "[ed0210c8/48'/0'/0]xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/*," + @@ -392,6 +407,7 @@ public void GetOutputDescriptor_Legacy() [Fact] public void GetOutputDescriptor_Taproot() { + // Arrange // Testing NodeGuard created Taproot hot wallet var wallet1HotWalletCreated = new Wallet() { @@ -409,8 +425,6 @@ public void GetOutputDescriptor_Taproot() } } }; - var taprootFunction = () => WalletParser.GetOutputDescriptor(wallet1HotWalletCreated, "mainnet"); - taprootFunction.Should().Throw(); // Testing NodeGuard imported Taproot hot wallet var wallet1HotWalletImported = new Wallet() @@ -427,8 +441,6 @@ public void GetOutputDescriptor_Taproot() } } }; - taprootFunction = () => WalletParser.GetOutputDescriptor(wallet1HotWalletImported, "mainnet"); - taprootFunction.Should().Throw(); // Testing NodeGuard created Taproot cold wallet var wallet1ColdWallet = new Wallet() @@ -458,7 +470,15 @@ public void GetOutputDescriptor_Taproot() } } }; - taprootFunction = () => WalletParser.GetOutputDescriptor(wallet1ColdWallet, "mainnet"); - taprootFunction.Should().Throw(); + + // Act + var taprootFunction1 = () => WalletParser.GetOutputDescriptor(wallet1HotWalletCreated, "mainnet"); + var taprootFunction2 = () => WalletParser.GetOutputDescriptor(wallet1HotWalletImported, "mainnet"); + var taprootFunction3 = () => WalletParser.GetOutputDescriptor(wallet1ColdWallet, "mainnet"); + + // Assert + taprootFunction1.Should().Throw(); + taprootFunction2.Should().Throw(); + taprootFunction3.Should().Throw(); } -} \ No newline at end of file +} From 74582af34367a5de048cd5f19305c5c809344292 Mon Sep 17 00:00:00 2001 From: Marcos Date: Thu, 26 Oct 2023 11:14:15 +0200 Subject: [PATCH 06/13] fix(WalletParserTests.cs): update assertions in WalletParserTests to use 'Contain' instead of 'Be' for output descriptors to improve test flexibility and maintainability --- .../Helpers/WalletParserTests.cs | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/test/NodeGuard.Tests/Helpers/WalletParserTests.cs b/test/NodeGuard.Tests/Helpers/WalletParserTests.cs index 88bc1168..3e5c0bcb 100644 --- a/test/NodeGuard.Tests/Helpers/WalletParserTests.cs +++ b/test/NodeGuard.Tests/Helpers/WalletParserTests.cs @@ -224,13 +224,13 @@ public void GetOutputDescriptor_NativeSegwits() var outputDescriptor3 = WalletParser.GetOutputDescriptor(wallet1ColdWallet, "mainnet"); // Assert - outputDescriptor1.Should().Be("wpkh([ed0210c8/48'/0'/0']xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/*')#j646efh8"); - outputDescriptor2.Should().Be("wpkh(xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/*')#s032ytxu"); - outputDescriptor3.Should().Be( + outputDescriptor1.Should().Contain("wpkh([ed0210c8/48'/0'/0']xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/*')#"); + outputDescriptor2.Should().Contain("wpkh(xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/*')#"); + outputDescriptor3.Should().Contain( "wsh(sortedmulti(2," + "[ed0210c8/48'/0'/0]xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/*," + "[ed0210c8/48'/0'/1]xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH/*," + - "[ed0210c8/48'/0'/2]xpub6CUGRUonZSQ4TWtTMmzXdrXDtypWKiKrhko4egpiMZbpiaQL2jkwSB1icqYh2cfDfVxdx4df189oLKnC5fSwqPfgyP3hooxujYzAu3fDVmz/*))#rm8zkn09" + "[ed0210c8/48'/0'/2]xpub6CUGRUonZSQ4TWtTMmzXdrXDtypWKiKrhko4egpiMZbpiaQL2jkwSB1icqYh2cfDfVxdx4df189oLKnC5fSwqPfgyP3hooxujYzAu3fDVmz/*))#" ); } @@ -307,13 +307,13 @@ public void GetOutputDescriptor_NestedSegwits() var outputDescriptor3 = WalletParser.GetOutputDescriptor(wallet1ColdWallet, "mainnet"); // Assert - outputDescriptor1.Should().Be("sh(wpkh([ed0210c8/48'/0'/0']xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/*'))#7run0eul"); - outputDescriptor2.Should().Be("sh(wpkh(xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/*'))#m766kd29"); - outputDescriptor3.Should().Be( + outputDescriptor1.Should().Contain("sh(wpkh([ed0210c8/48'/0'/0']xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/*'))#"); + outputDescriptor2.Should().Contain("sh(wpkh(xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/*'))#"); + outputDescriptor3.Should().Contain( "sh(sortedmulti(2," + "[ed0210c8/48'/0'/0]xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/*," + "[ed0210c8/48'/0'/1]xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH/*," + - "[ed0210c8/48'/0'/2]xpub6CUGRUonZSQ4TWtTMmzXdrXDtypWKiKrhko4egpiMZbpiaQL2jkwSB1icqYh2cfDfVxdx4df189oLKnC5fSwqPfgyP3hooxujYzAu3fDVmz/*))#9y9xajh7" + "[ed0210c8/48'/0'/2]xpub6CUGRUonZSQ4TWtTMmzXdrXDtypWKiKrhko4egpiMZbpiaQL2jkwSB1icqYh2cfDfVxdx4df189oLKnC5fSwqPfgyP3hooxujYzAu3fDVmz/*))#" ); } @@ -394,13 +394,13 @@ public void GetOutputDescriptor_Legacy() var outputDescriptor3 = WalletParser.GetOutputDescriptor(wallet1ColdWallet, "mainnet"); // Assert - outputDescriptor1.Should().Be("pkh([ed0210c8/48'/0'/0']xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/*')#tvnkfe78"); - outputDescriptor2.Should().Be("pkh(xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/*')#6n7l5kek"); - outputDescriptor3.Should().Be( + outputDescriptor1.Should().Contain("pkh([ed0210c8/48'/0'/0']xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/*')#"); + outputDescriptor2.Should().Contain("pkh(xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/*')#"); + outputDescriptor3.Should().Contain( "sortedmulti(2," + "[ed0210c8/48'/0'/0]xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/*," + "[ed0210c8/48'/0'/1]xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH/*," + - "[ed0210c8/48'/0'/2]xpub6CUGRUonZSQ4TWtTMmzXdrXDtypWKiKrhko4egpiMZbpiaQL2jkwSB1icqYh2cfDfVxdx4df189oLKnC5fSwqPfgyP3hooxujYzAu3fDVmz/*)#eeg5es07" + "[ed0210c8/48'/0'/2]xpub6CUGRUonZSQ4TWtTMmzXdrXDtypWKiKrhko4egpiMZbpiaQL2jkwSB1icqYh2cfDfVxdx4df189oLKnC5fSwqPfgyP3hooxujYzAu3fDVmz/*)#" ); } From 4e70aa4e009b036d7c8dbfb37eab034b6ff06d20 Mon Sep 17 00:00:00 2001 From: Marcos Date: Thu, 26 Oct 2023 11:18:44 +0200 Subject: [PATCH 07/13] fix(WalletParserTests.cs): update output descriptors in WalletParserTests to match expected values The output descriptors in the WalletParserTests were updated to match the expected values. The changes include: - Updating the output descriptor for `outputDescriptor1` to contain the correct derivation path and extended public key for the first account. - Updating the output descriptor for `outputDescriptor2` to contain the correct derivation path and extended public key for the second account. - Updating the output descriptor for `outputDescriptor3` to contain the correct derivation paths and extended public keys for the multi-signature account. These changes were made to ensure that the tests pass and the output descriptors are correctly generated. --- .../Helpers/WalletParserTests.cs | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/test/NodeGuard.Tests/Helpers/WalletParserTests.cs b/test/NodeGuard.Tests/Helpers/WalletParserTests.cs index 3e5c0bcb..34cfa121 100644 --- a/test/NodeGuard.Tests/Helpers/WalletParserTests.cs +++ b/test/NodeGuard.Tests/Helpers/WalletParserTests.cs @@ -224,13 +224,13 @@ public void GetOutputDescriptor_NativeSegwits() var outputDescriptor3 = WalletParser.GetOutputDescriptor(wallet1ColdWallet, "mainnet"); // Assert - outputDescriptor1.Should().Contain("wpkh([ed0210c8/48'/0'/0']xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/*')#"); - outputDescriptor2.Should().Contain("wpkh(xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/*')#"); + outputDescriptor1.Should().Contain("wpkh([ed0210c8/48'/0'/0']xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/0/*)#"); + outputDescriptor2.Should().Contain("wpkh(xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/0/*)#"); outputDescriptor3.Should().Contain( "wsh(sortedmulti(2," + - "[ed0210c8/48'/0'/0]xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/*," + - "[ed0210c8/48'/0'/1]xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH/*," + - "[ed0210c8/48'/0'/2]xpub6CUGRUonZSQ4TWtTMmzXdrXDtypWKiKrhko4egpiMZbpiaQL2jkwSB1icqYh2cfDfVxdx4df189oLKnC5fSwqPfgyP3hooxujYzAu3fDVmz/*))#" + "[ed0210c8/48'/0'/0]xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/0/*," + + "[ed0210c8/48'/0'/1]xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH/0/*," + + "[ed0210c8/48'/0'/2]xpub6CUGRUonZSQ4TWtTMmzXdrXDtypWKiKrhko4egpiMZbpiaQL2jkwSB1icqYh2cfDfVxdx4df189oLKnC5fSwqPfgyP3hooxujYzAu3fDVmz/0/*))#" ); } @@ -307,13 +307,13 @@ public void GetOutputDescriptor_NestedSegwits() var outputDescriptor3 = WalletParser.GetOutputDescriptor(wallet1ColdWallet, "mainnet"); // Assert - outputDescriptor1.Should().Contain("sh(wpkh([ed0210c8/48'/0'/0']xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/*'))#"); - outputDescriptor2.Should().Contain("sh(wpkh(xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/*'))#"); + outputDescriptor1.Should().Contain("sh(wpkh([ed0210c8/48'/0'/0']xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/0/*))#"); + outputDescriptor2.Should().Contain("sh(wpkh(xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/0/*))#"); outputDescriptor3.Should().Contain( "sh(sortedmulti(2," + - "[ed0210c8/48'/0'/0]xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/*," + - "[ed0210c8/48'/0'/1]xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH/*," + - "[ed0210c8/48'/0'/2]xpub6CUGRUonZSQ4TWtTMmzXdrXDtypWKiKrhko4egpiMZbpiaQL2jkwSB1icqYh2cfDfVxdx4df189oLKnC5fSwqPfgyP3hooxujYzAu3fDVmz/*))#" + "[ed0210c8/48'/0'/0]xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/0/*," + + "[ed0210c8/48'/0'/1]xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH/0/*," + + "[ed0210c8/48'/0'/2]xpub6CUGRUonZSQ4TWtTMmzXdrXDtypWKiKrhko4egpiMZbpiaQL2jkwSB1icqYh2cfDfVxdx4df189oLKnC5fSwqPfgyP3hooxujYzAu3fDVmz/0/*))#" ); } @@ -394,13 +394,13 @@ public void GetOutputDescriptor_Legacy() var outputDescriptor3 = WalletParser.GetOutputDescriptor(wallet1ColdWallet, "mainnet"); // Assert - outputDescriptor1.Should().Contain("pkh([ed0210c8/48'/0'/0']xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/*')#"); - outputDescriptor2.Should().Contain("pkh(xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/*')#"); + outputDescriptor1.Should().Contain("pkh([ed0210c8/48'/0'/0']xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/0/*)#"); + outputDescriptor2.Should().Contain("pkh(xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/0/*)#"); outputDescriptor3.Should().Contain( "sortedmulti(2," + - "[ed0210c8/48'/0'/0]xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/*," + - "[ed0210c8/48'/0'/1]xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH/*," + - "[ed0210c8/48'/0'/2]xpub6CUGRUonZSQ4TWtTMmzXdrXDtypWKiKrhko4egpiMZbpiaQL2jkwSB1icqYh2cfDfVxdx4df189oLKnC5fSwqPfgyP3hooxujYzAu3fDVmz/*)#" + "[ed0210c8/48'/0'/0]xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/0/*," + + "[ed0210c8/48'/0'/1]xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH/0/*," + + "[ed0210c8/48'/0'/2]xpub6CUGRUonZSQ4TWtTMmzXdrXDtypWKiKrhko4egpiMZbpiaQL2jkwSB1icqYh2cfDfVxdx4df189oLKnC5fSwqPfgyP3hooxujYzAu3fDVmz/0/*)#" ); } From 1b9a04929ac80b359c8e6f99101321e21390832c Mon Sep 17 00:00:00 2001 From: Marcos Date: Thu, 26 Oct 2023 11:39:53 +0200 Subject: [PATCH 08/13] fix: properly deriving and adding the masterfingerprint to the imported wallets --- src/Helpers/WalletParser.cs | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/src/Helpers/WalletParser.cs b/src/Helpers/WalletParser.cs index 680f03cc..c34a69b9 100644 --- a/src/Helpers/WalletParser.cs +++ b/src/Helpers/WalletParser.cs @@ -126,18 +126,13 @@ public static string GetOutputDescriptor(Wallet wallet, string bitcoinNetwork) new KeyPath("/0"), PubKeyProvider.DeriveType.UNHARDENED ); + var fingerprint = GetMasterFingerprint(key.MasterFingerprint); + var rootedKeyPath = new RootedKeyPath( + new HDFingerprint(fingerprint), + KeyPath.Parse(key.Path) + ); + pubKeyProvider = PubKeyProvider.NewOrigin(rootedKeyPath, pubKeyProvider); - if (!wallet.IsBIP39Imported) - { - var internalKey = wallet.Keys.FirstOrDefault(k => k.InternalWalletId != null); - var internalBytes = GetMasterFingerprint(internalKey.MasterFingerprint); - var rootedKeyPath = new RootedKeyPath( - new HDFingerprint(internalBytes), - KeyPath.Parse(internalKey.Path) - ); - pubKeyProvider = PubKeyProvider.NewOrigin(rootedKeyPath, pubKeyProvider); - } - switch (wallet.WalletAddressType) { case WalletAddressType.NativeSegwit: From 6a662e3be96cf73b8493b1cb9e0278140c11334f Mon Sep 17 00:00:00 2001 From: Marcos Date: Thu, 26 Oct 2023 11:49:34 +0200 Subject: [PATCH 09/13] refactor(WalletParserTests.cs): Remove unused code and fix variable names for clarity The changes in this commit remove unused code and fix variable names in the WalletParserTests.cs file. The following changes were made: - Removed the unused wallet1HotWalletImported variable for Native Segwit, Nested Segwit, Legacy, and Taproot hot wallets. - Renamed the wallet1ColdWallet variable to walletColdWallet for clarity. - Updated the variable names in the GetOutputDescriptor method calls to reflect the changes. These changes improve the readability and maintainability of the code. --- .../Helpers/WalletParserTests.cs | 88 ++----------------- 1 file changed, 7 insertions(+), 81 deletions(-) diff --git a/test/NodeGuard.Tests/Helpers/WalletParserTests.cs b/test/NodeGuard.Tests/Helpers/WalletParserTests.cs index 34cfa121..48f286e4 100644 --- a/test/NodeGuard.Tests/Helpers/WalletParserTests.cs +++ b/test/NodeGuard.Tests/Helpers/WalletParserTests.cs @@ -172,22 +172,6 @@ public void GetOutputDescriptor_NativeSegwits() } } }; - - // Testing NodeGuard imported Native Segwit hot wallet - var wallet1HotWalletImported = new Wallet() - { - IsBIP39Imported = true, - IsHotWallet = true, - WalletAddressType = WalletAddressType.NativeSegwit, - MofN = 1, - Keys = new List() { - new() { - XPUB = "xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB", - Path = "48'/0'/0'", - MasterFingerprint = "ed0210c8", - } - } - }; // Testing NodeGuard created Native Segwit cold wallet var wallet1ColdWallet = new Wallet() @@ -220,13 +204,11 @@ public void GetOutputDescriptor_NativeSegwits() // Act var outputDescriptor1 = WalletParser.GetOutputDescriptor(wallet1HotWalletCreated, "mainnet"); - var outputDescriptor2 = WalletParser.GetOutputDescriptor(wallet1HotWalletImported, "mainnet"); - var outputDescriptor3 = WalletParser.GetOutputDescriptor(wallet1ColdWallet, "mainnet"); + var outputDescriptor2 = WalletParser.GetOutputDescriptor(wallet1ColdWallet, "mainnet"); // Assert outputDescriptor1.Should().Contain("wpkh([ed0210c8/48'/0'/0']xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/0/*)#"); - outputDescriptor2.Should().Contain("wpkh(xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/0/*)#"); - outputDescriptor3.Should().Contain( + outputDescriptor2.Should().Contain( "wsh(sortedmulti(2," + "[ed0210c8/48'/0'/0]xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/0/*," + "[ed0210c8/48'/0'/1]xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH/0/*," + @@ -256,22 +238,6 @@ public void GetOutputDescriptor_NestedSegwits() } }; - // Testing NodeGuard imported Nested Segwit hot wallet - var wallet1HotWalletImported = new Wallet() - { - IsBIP39Imported = true, - IsHotWallet = true, - WalletAddressType = WalletAddressType.NestedSegwit, - MofN = 1, - Keys = new List() { - new() { - XPUB = "xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB", - Path = "48'/0'/0'", - MasterFingerprint = "ed0210c8", - } - } - }; - // Testing NodeGuard created Nested Segwit cold wallet var wallet1ColdWallet = new Wallet() { @@ -303,13 +269,11 @@ public void GetOutputDescriptor_NestedSegwits() //Act var outputDescriptor1 = WalletParser.GetOutputDescriptor(wallet1HotWalletCreated, "mainnet"); - var outputDescriptor2 = WalletParser.GetOutputDescriptor(wallet1HotWalletImported, "mainnet"); - var outputDescriptor3 = WalletParser.GetOutputDescriptor(wallet1ColdWallet, "mainnet"); + var outputDescriptor2 = WalletParser.GetOutputDescriptor(wallet1ColdWallet, "mainnet"); // Assert outputDescriptor1.Should().Contain("sh(wpkh([ed0210c8/48'/0'/0']xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/0/*))#"); - outputDescriptor2.Should().Contain("sh(wpkh(xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/0/*))#"); - outputDescriptor3.Should().Contain( + outputDescriptor2.Should().Contain( "sh(sortedmulti(2," + "[ed0210c8/48'/0'/0]xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/0/*," + "[ed0210c8/48'/0'/1]xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH/0/*," + @@ -341,24 +305,6 @@ public void GetOutputDescriptor_Legacy() } }; - // Testing NodeGuard imported Legacy hot wallet - var wallet1HotWalletImported = new Wallet() - { - IsBIP39Imported = true, - IsHotWallet = true, - WalletAddressType = WalletAddressType.Legacy, - MofN = 1, - Keys = new List() - { - new() - { - XPUB = "xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB", - Path = "48'/0'/0'", - MasterFingerprint = "ed0210c8", - } - } - }; - // Testing NodeGuard created Legacy cold wallet var wallet1ColdWallet = new Wallet() { @@ -390,13 +336,11 @@ public void GetOutputDescriptor_Legacy() // Act var outputDescriptor1 = WalletParser.GetOutputDescriptor(wallet1HotWalletCreated, "mainnet"); - var outputDescriptor2 = WalletParser.GetOutputDescriptor(wallet1HotWalletImported, "mainnet"); - var outputDescriptor3 = WalletParser.GetOutputDescriptor(wallet1ColdWallet, "mainnet"); + var outputDescriptor2 = WalletParser.GetOutputDescriptor(wallet1ColdWallet, "mainnet"); // Assert outputDescriptor1.Should().Contain("pkh([ed0210c8/48'/0'/0']xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/0/*)#"); - outputDescriptor2.Should().Contain("pkh(xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/0/*)#"); - outputDescriptor3.Should().Contain( + outputDescriptor2.Should().Contain( "sortedmulti(2," + "[ed0210c8/48'/0'/0]xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/0/*," + "[ed0210c8/48'/0'/1]xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH/0/*," + @@ -426,22 +370,6 @@ public void GetOutputDescriptor_Taproot() } }; - // Testing NodeGuard imported Taproot hot wallet - var wallet1HotWalletImported = new Wallet() - { - IsBIP39Imported = true, - IsHotWallet = true, - WalletAddressType = WalletAddressType.Taproot, - MofN = 1, - Keys = new List() { - new() { - XPUB = "xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB", - Path = "48'/0'/0'", - MasterFingerprint = "ed0210c8", - } - } - }; - // Testing NodeGuard created Taproot cold wallet var wallet1ColdWallet = new Wallet() { @@ -473,12 +401,10 @@ public void GetOutputDescriptor_Taproot() // Act var taprootFunction1 = () => WalletParser.GetOutputDescriptor(wallet1HotWalletCreated, "mainnet"); - var taprootFunction2 = () => WalletParser.GetOutputDescriptor(wallet1HotWalletImported, "mainnet"); - var taprootFunction3 = () => WalletParser.GetOutputDescriptor(wallet1ColdWallet, "mainnet"); + var taprootFunction2 = () => WalletParser.GetOutputDescriptor(wallet1ColdWallet, "mainnet"); // Assert taprootFunction1.Should().Throw(); taprootFunction2.Should().Throw(); - taprootFunction3.Should().Throw(); } } From 4f4330c403ce05ee3e86393a79265b38190ced72 Mon Sep 17 00:00:00 2001 From: Marcos Date: Fri, 27 Oct 2023 11:07:51 +0200 Subject: [PATCH 10/13] refactor(WalletParser.cs): change GetOutputDescriptor method to an extension method to improve code organization and readability refactor(Wallets.razor): update usage of GetOutputDescriptor method to use it as an extension method on the Wallet object for better code organization and readability refactor(WalletParserTests.cs): update tests to use GetOutputDescriptor method as an extension method on the Wallet object for better code organization and readability --- src/Helpers/WalletParser.cs | 2 +- src/Pages/Wallets.razor | 2 +- .../NodeGuard.Tests/Helpers/WalletParserTests.cs | 16 ++++++++-------- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/Helpers/WalletParser.cs b/src/Helpers/WalletParser.cs index c34a69b9..5f75c9e4 100644 --- a/src/Helpers/WalletParser.cs +++ b/src/Helpers/WalletParser.cs @@ -109,7 +109,7 @@ DerivationStrategyBase Parse(string str) } } - public static string GetOutputDescriptor(Wallet wallet, string bitcoinNetwork) + public static string GetOutputDescriptor(this Wallet wallet, string bitcoinNetwork) { var network = Network.GetNetwork(bitcoinNetwork); OutputDescriptor outputDescriptor = null; diff --git a/src/Pages/Wallets.razor b/src/Pages/Wallets.razor index 98b7fa42..4dcc86aa 100644 --- a/src/Pages/Wallets.razor +++ b/src/Pages/Wallets.razor @@ -1662,7 +1662,7 @@ { try { - _outputDescriptorContentModal = WalletParser.GetOutputDescriptor(contextItem, Constants.BITCOIN_NETWORK); + _outputDescriptorContentModal = contextItem.GetOutputDescriptor(Constants.BITCOIN_NETWORK); _keysContentModal = contextItem.Keys as HashSet ?? new HashSet(); await _exportOutputDescriptorModal.Show(); } diff --git a/test/NodeGuard.Tests/Helpers/WalletParserTests.cs b/test/NodeGuard.Tests/Helpers/WalletParserTests.cs index 48f286e4..14c10bf6 100644 --- a/test/NodeGuard.Tests/Helpers/WalletParserTests.cs +++ b/test/NodeGuard.Tests/Helpers/WalletParserTests.cs @@ -203,8 +203,8 @@ public void GetOutputDescriptor_NativeSegwits() }; // Act - var outputDescriptor1 = WalletParser.GetOutputDescriptor(wallet1HotWalletCreated, "mainnet"); - var outputDescriptor2 = WalletParser.GetOutputDescriptor(wallet1ColdWallet, "mainnet"); + var outputDescriptor1 = wallet1HotWalletCreated.GetOutputDescriptor("mainnet"); + var outputDescriptor2 = wallet1ColdWallet.GetOutputDescriptor("mainnet"); // Assert outputDescriptor1.Should().Contain("wpkh([ed0210c8/48'/0'/0']xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/0/*)#"); @@ -268,8 +268,8 @@ public void GetOutputDescriptor_NestedSegwits() }; //Act - var outputDescriptor1 = WalletParser.GetOutputDescriptor(wallet1HotWalletCreated, "mainnet"); - var outputDescriptor2 = WalletParser.GetOutputDescriptor(wallet1ColdWallet, "mainnet"); + var outputDescriptor1 = wallet1HotWalletCreated.GetOutputDescriptor("mainnet"); + var outputDescriptor2 = wallet1ColdWallet.GetOutputDescriptor("mainnet"); // Assert outputDescriptor1.Should().Contain("sh(wpkh([ed0210c8/48'/0'/0']xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/0/*))#"); @@ -335,8 +335,8 @@ public void GetOutputDescriptor_Legacy() }; // Act - var outputDescriptor1 = WalletParser.GetOutputDescriptor(wallet1HotWalletCreated, "mainnet"); - var outputDescriptor2 = WalletParser.GetOutputDescriptor(wallet1ColdWallet, "mainnet"); + var outputDescriptor1 = wallet1HotWalletCreated.GetOutputDescriptor("mainnet"); + var outputDescriptor2 = wallet1ColdWallet.GetOutputDescriptor("mainnet"); // Assert outputDescriptor1.Should().Contain("pkh([ed0210c8/48'/0'/0']xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/0/*)#"); @@ -400,8 +400,8 @@ public void GetOutputDescriptor_Taproot() }; // Act - var taprootFunction1 = () => WalletParser.GetOutputDescriptor(wallet1HotWalletCreated, "mainnet"); - var taprootFunction2 = () => WalletParser.GetOutputDescriptor(wallet1ColdWallet, "mainnet"); + var taprootFunction1 = () => wallet1HotWalletCreated.GetOutputDescriptor("mainnet"); + var taprootFunction2 = () => wallet1ColdWallet.GetOutputDescriptor("mainnet"); // Assert taprootFunction1.Should().Throw(); From 22fe2bed640388b59e95972d9c558b11559e3f22 Mon Sep 17 00:00:00 2001 From: Marcos Date: Fri, 27 Oct 2023 11:16:16 +0200 Subject: [PATCH 11/13] chore(WalletParser.cs): add documentation for GetOutputDescriptor method to improve code readability and maintainability chore(WalletParser.cs): add documentation for GetMasterFingerprint method to improve code readability and maintainability --- src/Helpers/WalletParser.cs | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/Helpers/WalletParser.cs b/src/Helpers/WalletParser.cs index 5f75c9e4..83a4d745 100644 --- a/src/Helpers/WalletParser.cs +++ b/src/Helpers/WalletParser.cs @@ -109,6 +109,19 @@ DerivationStrategyBase Parse(string str) } } + /// + /// Generates an output descriptor for a given wallet based on its type and the Bitcoin network it's associated with. + /// + /// The wallet for which the output descriptor is to be generated. + /// The Bitcoin network associated with the wallet. + /// A string representation of the output descriptor. + /// Thrown when the wallet address type is Taproot, which is not currently supported. + /// Thrown when the output descriptor could not be generated for some reason. + /// + /// This method first determines the network based on the provided string. It then checks if the wallet is a hot wallet or not. + /// If it is, it generates the output descriptor based on the first key in the wallet and the wallet's address type. + /// If it's not a hot wallet, it generates a multi-signature output descriptor based on all the keys in the wallet and the wallet's address type. + /// public static string GetOutputDescriptor(this Wallet wallet, string bitcoinNetwork) { var network = Network.GetNetwork(bitcoinNetwork); @@ -195,6 +208,14 @@ public static string GetOutputDescriptor(this Wallet wallet, string bitcoinNetwo return outputDescriptor is not null ? outputDescriptor.ToString() : throw new Exception("Something went wrong"); } + /// + /// Converts a hexadecimal string representation of a master fingerprint into a byte array. + /// + /// The hexadecimal string representation of the master fingerprint. + /// A byte array that represents the master fingerprint. + /// + /// This method works by iterating over the input string two characters at a time (since each byte in a hexadecimal string is represented by two characters), converting those two characters into a byte, and then adding that byte to the output array. + /// public static byte[] GetMasterFingerprint(string masterFingerprint) { var internalBytes = Enumerable.Range(0, masterFingerprint.Length) From 4cb8a298f5e702c82205e9f0f5c2b823e7cb8f5c Mon Sep 17 00:00:00 2001 From: Marcos Date: Fri, 27 Oct 2023 11:26:21 +0200 Subject: [PATCH 12/13] fix: enforce the checksum in the tests --- .../Helpers/WalletParserTests.cs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/test/NodeGuard.Tests/Helpers/WalletParserTests.cs b/test/NodeGuard.Tests/Helpers/WalletParserTests.cs index 14c10bf6..918face5 100644 --- a/test/NodeGuard.Tests/Helpers/WalletParserTests.cs +++ b/test/NodeGuard.Tests/Helpers/WalletParserTests.cs @@ -207,12 +207,12 @@ public void GetOutputDescriptor_NativeSegwits() var outputDescriptor2 = wallet1ColdWallet.GetOutputDescriptor("mainnet"); // Assert - outputDescriptor1.Should().Contain("wpkh([ed0210c8/48'/0'/0']xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/0/*)#"); - outputDescriptor2.Should().Contain( + outputDescriptor1.Should().Be("wpkh([ed0210c8/48'/0'/0']xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/0/*)#yt9sugwc"); + outputDescriptor2.Should().Be( "wsh(sortedmulti(2," + "[ed0210c8/48'/0'/0]xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/0/*," + "[ed0210c8/48'/0'/1]xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH/0/*," + - "[ed0210c8/48'/0'/2]xpub6CUGRUonZSQ4TWtTMmzXdrXDtypWKiKrhko4egpiMZbpiaQL2jkwSB1icqYh2cfDfVxdx4df189oLKnC5fSwqPfgyP3hooxujYzAu3fDVmz/0/*))#" + "[ed0210c8/48'/0'/2]xpub6CUGRUonZSQ4TWtTMmzXdrXDtypWKiKrhko4egpiMZbpiaQL2jkwSB1icqYh2cfDfVxdx4df189oLKnC5fSwqPfgyP3hooxujYzAu3fDVmz/0/*))#8qt3h45m" ); } @@ -272,12 +272,12 @@ public void GetOutputDescriptor_NestedSegwits() var outputDescriptor2 = wallet1ColdWallet.GetOutputDescriptor("mainnet"); // Assert - outputDescriptor1.Should().Contain("sh(wpkh([ed0210c8/48'/0'/0']xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/0/*))#"); - outputDescriptor2.Should().Contain( + outputDescriptor1.Should().Be("sh(wpkh([ed0210c8/48'/0'/0']xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/0/*))#u4qug9gg"); + outputDescriptor2.Should().Be( "sh(sortedmulti(2," + "[ed0210c8/48'/0'/0]xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/0/*," + "[ed0210c8/48'/0'/1]xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH/0/*," + - "[ed0210c8/48'/0'/2]xpub6CUGRUonZSQ4TWtTMmzXdrXDtypWKiKrhko4egpiMZbpiaQL2jkwSB1icqYh2cfDfVxdx4df189oLKnC5fSwqPfgyP3hooxujYzAu3fDVmz/0/*))#" + "[ed0210c8/48'/0'/2]xpub6CUGRUonZSQ4TWtTMmzXdrXDtypWKiKrhko4egpiMZbpiaQL2jkwSB1icqYh2cfDfVxdx4df189oLKnC5fSwqPfgyP3hooxujYzAu3fDVmz/0/*))#acnwnep3" ); } @@ -339,12 +339,12 @@ public void GetOutputDescriptor_Legacy() var outputDescriptor2 = wallet1ColdWallet.GetOutputDescriptor("mainnet"); // Assert - outputDescriptor1.Should().Contain("pkh([ed0210c8/48'/0'/0']xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/0/*)#"); - outputDescriptor2.Should().Contain( + outputDescriptor1.Should().Be("pkh([ed0210c8/48'/0'/0']xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/0/*)#p7sn6nw0"); + outputDescriptor2.Should().Be( "sortedmulti(2," + "[ed0210c8/48'/0'/0]xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/0/*," + "[ed0210c8/48'/0'/1]xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH/0/*," + - "[ed0210c8/48'/0'/2]xpub6CUGRUonZSQ4TWtTMmzXdrXDtypWKiKrhko4egpiMZbpiaQL2jkwSB1icqYh2cfDfVxdx4df189oLKnC5fSwqPfgyP3hooxujYzAu3fDVmz/0/*)#" + "[ed0210c8/48'/0'/2]xpub6CUGRUonZSQ4TWtTMmzXdrXDtypWKiKrhko4egpiMZbpiaQL2jkwSB1icqYh2cfDfVxdx4df189oLKnC5fSwqPfgyP3hooxujYzAu3fDVmz/0/*)#uqv67sle" ); } From 5ff98c0618c2393ba013cd52bbffa439595951c8 Mon Sep 17 00:00:00 2001 From: Marcos Date: Fri, 27 Oct 2023 18:17:32 +0200 Subject: [PATCH 13/13] refactor: show derivation scheme instead of derivation path --- src/Pages/Wallets.razor | 23 ++++++----------------- 1 file changed, 6 insertions(+), 17 deletions(-) diff --git a/src/Pages/Wallets.razor b/src/Pages/Wallets.razor index df88c660..59d32163 100644 --- a/src/Pages/Wallets.razor +++ b/src/Pages/Wallets.razor @@ -708,23 +708,12 @@ @($"Export wallet") -

Copy the output descriptor below and use it to import this wallet in other systems.

-

Only segwit descriptors for single sig(WPKH) or multisigs (WSH) are supported.

-

Singlesig: wpkh([…/84'/0'/0']xpub…/0/*)

-

Multisig: wsh(sortedmulti(2,[…/48'/0'/0'/2']xpub…/0/*,[…/48'/0'/0'/2']xpub…/0/*))

- @(StringHelper.TruncateHeadAndTail(_outputDescriptorContentModal, 25)) + @("Output descriptor: " + StringHelper.TruncateHeadAndTail(_outputDescriptorContentModal, 25)) - @{ - foreach(var k in _keysContentModal) - { -

- @($"{k.Name}: {k.Path}") - -

- } - } + @("Wallet derivation strategy (NBITCOIN): " + StringHelper.TruncateHeadAndTail(_derivationScheme, 25)) +
@@ -770,7 +759,7 @@ private Modal _exportOutputDescriptorModal; private string _outputDescriptorContentModal; - private HashSet _keysContentModal = new(); + private string _derivationScheme; private Modal _textModalRef; private string _textModalTitle = string.Empty; @@ -1663,7 +1652,7 @@ try { _outputDescriptorContentModal = contextItem.GetOutputDescriptor(Constants.BITCOIN_NETWORK); - _keysContentModal = contextItem.Keys as HashSet ?? new HashSet(); + _derivationScheme = contextItem.GetDerivationStrategy().ToString(); await _exportOutputDescriptorModal.Show(); } catch (Exception e) @@ -1678,7 +1667,7 @@ await _exportOutputDescriptorModal.Close(CloseReason.UserClosing); _outputDescriptorContentModal = string.Empty; - _keysContentModal = new HashSet(); + _derivationScheme = string.Empty; } } \ No newline at end of file