Implement mock for displayaddress.
MODIFY
WalletWasabi.Tests/HwiTests/NoDeviceConnectedTests/IMockHwiProcessBridge.cs
WalletWasabi.Tests/HwiTests/NoDeviceConnectedTests/IMockHwiProcessBridge.cs
@@ -100,7 +100,7 @@ namespace WalletWasabi.Tests.HwiTests.NoDeviceConnectedTests
return Task.FromResult((response, code));
}
}
- else if (CompareArguments(arguments, "--device-path \"webusb: 001:4\" --device-type \"trezor\" getxpub", true))
+ else if (CompareArguments(arguments, "--device-path \"webusb: 001:4\" --device-type \"trezor\" getxpub m/84h/0h/0h", false))
{
if (Model == HardwareWalletModels.TrezorT)
{
@@ -109,30 +109,77 @@ namespace WalletWasabi.Tests.HwiTests.NoDeviceConnectedTests
return Task.FromResult((response, code));
}
}
+ else if (CompareArguments(arguments, "--device-path \"webusb: 001:4\" --device-type \"trezor\" getxpub m/84h/0h/0h/1", false))
+ {
+ if (Model == HardwareWalletModels.TrezorT)
+ {
+ var response = "{\"xpub\": \"xpub6FJS1ne3STcKdQ9JLXNzZXidmCNZ9dxLiy7WVvsRkcmxjJsrDKJKEAXq4MGyEBM3vHEw2buqXezfNK5SNBrkwK7Fxjz1TW6xzRr2pUyMWFu\"}\r\n";
+ var code = 0;
+ return Task.FromResult((response, code));
+ }
+ }
+ else if (CompareArguments(out bool t1, arguments, "--device-path \"webusb: 001:4\" --device-type \"trezor\" displayaddress --path m/84h/0h/0h --wpkh", false))
+ {
+ if (Model == HardwareWalletModels.TrezorT)
+ {
+ string response = t1
+ ? "{\"address\": \"tb1q7zqqsmqx5ymhd7qn73lm96w5yqdkrmx7rtzlxy\"}\r\n"
+ : "{\"address\": \"bc1q7zqqsmqx5ymhd7qn73lm96w5yqdkrmx7fdevah\"}\r\n";
+ var code = 0;
+ return Task.FromResult((response, code));
+ }
+ }
+ else if (CompareArguments(out bool t2, arguments, "--device-path \"webusb: 001:4\" --device-type \"trezor\" displayaddress --path m/84h/0h/0h/1 --wpkh", false))
+ {
+ if (Model == HardwareWalletModels.TrezorT)
+ {
+ string response = t2
+ ? "{\"address\": \"tb1qmaveee425a5xjkjcv7m6d4gth45jvtnjqhj3l6\"}\r\n"
+ : "{\"address\": \"bc1qmaveee425a5xjkjcv7m6d4gth45jvtnj23fzyf\"}\r\n";
+ var code = 0;
+ return Task.FromResult((response, code));
+ }
+ }
throw new NotImplementedException($"Mocking is not implemented for '{arguments}'");
}
- private static bool CompareArguments(string arguments, string desired, bool useStartWith = false)
+ private static bool CompareArguments(out bool isTestNet, string arguments, string desired, bool useStartWith = false)
{
var testnetDesired = $"--testnet {desired}";
+ isTestNet = false;
if (useStartWith)
{
- if (arguments.StartsWith(desired, StringComparison.OrdinalIgnoreCase) || arguments.StartsWith(testnetDesired, StringComparison.OrdinalIgnoreCase))
+ if (arguments.StartsWith(desired, StringComparison.Ordinal))
+ {
+ return true;
+ }
+
+ if (arguments.StartsWith(testnetDesired, StringComparison.Ordinal))
{
+ isTestNet = true;
return true;
}
}
else
{
- if (arguments == desired || arguments == testnetDesired)
+ if (arguments == desired)
+ {
+ return true;
+ }
+
+ if (arguments == testnetDesired)
{
+ isTestNet = true;
return true;
}
}
return false;
}
+
+ private static bool CompareArguments(string arguments, string desired, bool useStartWith = false)
+ => CompareArguments(out _, arguments, desired, useStartWith);
}
}

MODIFY
WalletWasabi.Tests/HwiTests/NoDeviceConnectedTests/MockedDeviceTests.cs
WalletWasabi.Tests/HwiTests/NoDeviceConnectedTests/MockedDeviceTests.cs
@@ -65,10 +65,42 @@ namespace WalletWasabi.Tests.HwiTests.NoDeviceConnectedTests
Assert.Equal("The PIN has already been sent to this device", sendpin.Message);
Assert.Equal(HwiErrorCode.DeviceAlreadyUnlocked, sendpin.ErrorCode);
- KeyPath keyPath = KeyManager.DefaultAccountKeyPath;
- ExtPubKey xpub = await client.GetXpubAsync(deviceType, devicePath, keyPath, cts.Token);
- var expecteXpub = NBitcoinHelpers.BetterParseExtPubKey("xpub6DHjDx4gzLV37gJWMxYJAqyKRGN46MT61RHVizdU62cbVUYu9L95cXKzX62yJ2hPbN11EeprS8sSn8kj47skQBrmycCMzFEYBQSntVKFQ5M");
- Assert.Equal(expecteXpub, xpub);
+ KeyPath keyPath1 = KeyManager.DefaultAccountKeyPath;
+ KeyPath keyPath2 = KeyManager.DefaultAccountKeyPath.Derive(1);
+ ExtPubKey xpub1 = await client.GetXpubAsync(deviceType, devicePath, keyPath1, cts.Token);
+ ExtPubKey xpub2 = await client.GetXpubAsync(deviceType, devicePath, keyPath2, cts.Token);
+ var expecteXpub1 = NBitcoinHelpers.BetterParseExtPubKey("xpub6DHjDx4gzLV37gJWMxYJAqyKRGN46MT61RHVizdU62cbVUYu9L95cXKzX62yJ2hPbN11EeprS8sSn8kj47skQBrmycCMzFEYBQSntVKFQ5M");
+ var expecteXpub2 = NBitcoinHelpers.BetterParseExtPubKey("xpub6FJS1ne3STcKdQ9JLXNzZXidmCNZ9dxLiy7WVvsRkcmxjJsrDKJKEAXq4MGyEBM3vHEw2buqXezfNK5SNBrkwK7Fxjz1TW6xzRr2pUyMWFu");
+ Assert.Equal(expecteXpub1, xpub1);
+ Assert.Equal(expecteXpub2, xpub2);
+
+ BitcoinWitPubKeyAddress address1 = await client.DisplayAddressAsync(deviceType, devicePath, keyPath1, cts.Token);
+ BitcoinWitPubKeyAddress address2 = await client.DisplayAddressAsync(deviceType, devicePath, keyPath2, cts.Token);
+
+ BitcoinAddress expectedAddress1;
+ BitcoinAddress expectedAddress2;
+ if (network == Network.Main)
+ {
+ expectedAddress1 = BitcoinAddress.Create("bc1q7zqqsmqx5ymhd7qn73lm96w5yqdkrmx7fdevah", Network.Main);
+ expectedAddress2 = BitcoinAddress.Create("bc1qmaveee425a5xjkjcv7m6d4gth45jvtnj23fzyf", Network.Main);
+ }
+ else if (network == Network.TestNet)
+ {
+ expectedAddress1 = BitcoinAddress.Create("tb1q7zqqsmqx5ymhd7qn73lm96w5yqdkrmx7rtzlxy", Network.TestNet);
+ expectedAddress2 = BitcoinAddress.Create("tb1qmaveee425a5xjkjcv7m6d4gth45jvtnjqhj3l6", Network.TestNet);
+ }
+ else if (network == Network.RegTest)
+ {
+ expectedAddress1 = BitcoinAddress.Create("bcrt1q7zqqsmqx5ymhd7qn73lm96w5yqdkrmx7pzmj3d", Network.RegTest);
+ expectedAddress2 = BitcoinAddress.Create("bcrt1qmaveee425a5xjkjcv7m6d4gth45jvtnjz7tugn", Network.RegTest);
+ }
+ else
+ {
+ throw new NotSupportedException($"{network} not supported.");
+ }
+
+ Assert.Equal(expectedAddress1, address1);
+ Assert.Equal(expectedAddress2, address2);
}
}

MODIFY
WalletWasabi/Extensions/NBitcoinExtensions.cs
WalletWasabi/Extensions/NBitcoinExtensions.cs
@@ -283,5 +283,19 @@ namespace NBitcoin
return toStringBuilder.ToString();
}
+
+ public static BitcoinWitPubKeyAddress TransformToNetworkNetwork(this BitcoinWitPubKeyAddress me, Network desiredNetwork)
+ {
+ Network originalNetwork = me.Network;
+
+ if (originalNetwork == desiredNetwork)
+ {
+ return me;
+ }
+
+ var newAddress = new BitcoinWitPubKeyAddress(me.Hash, desiredNetwork);
+
+ return newAddress;
+ }
}
}

MODIFY
WalletWasabi/Hwi2/HwiClient.cs
WalletWasabi/Hwi2/HwiClient.cs
@@ -92,10 +92,11 @@ namespace WalletWasabi.Hwi2
public async Task<ExtPubKey> GetXpubAsync(HardwareWalletVendors deviceType, string devicePath, KeyPath keyPath, CancellationToken cancel)
{
+ string keyPathString = keyPath.ToString(true, "h");
var response = await SendCommandAsync(
options: new[] { HwiOption.DevicePath(devicePath), HwiOption.DeviceType(deviceType) },
command: HwiCommands.GetXpub,
- commandArguments: keyPath.ToString(true, "h"),
+ commandArguments: keyPathString,
cancel).ConfigureAwait(false);
var extPubKey = HwiParser.ParseExtPubKey(response);
@@ -111,7 +112,9 @@ namespace WalletWasabi.Hwi2
commandArguments: $"--path {keyPath.ToString(true, "h")} --wpkh",
cancel).ConfigureAwait(false);
- var address = HwiParser.ParseAddress(response, Network);
+ var address = HwiParser.ParseAddress(response, Network) as BitcoinWitPubKeyAddress;
+
+ address = address.TransformToNetworkNetwork(Network);
return address as BitcoinWitPubKeyAddress;
}

MODIFY
WalletWasabi/Hwi2/Parsers/HwiParser.cs
WalletWasabi/Hwi2/Parsers/HwiParser.cs
@@ -162,6 +162,12 @@ namespace WalletWasabi.Hwi2.Parsers
public static BitcoinAddress ParseAddress(string json, Network network)
{
+ // HWI does not support regtest, so the parsing would fail here.
+ if (network == Network.RegTest)
+ {
+ network = Network.TestNet;
+ }
+
if (JsonHelpers.TryParseJToken(json, out JToken token))
{
var addressString = token["address"]?.ToString()?.Trim() ?? null;

Implement mock for displayaddress.