Implement SetupAsync
MODIFY
WalletWasabi.Tests/HwiTests/DeviceConnectedTests/Katas.cs
WalletWasabi.Tests/HwiTests/DeviceConnectedTests/Katas.cs
@@ -13,6 +13,7 @@ using Xunit;
namespace WalletWasabi.Tests.HwiTests.DeviceConnectedTests
{
/// <summary>
+ /// Kata tests are intended to be run one by one.
/// A kata is a type of test that requires user interaction.
/// User interaction shall be defined in the beginning of the each kata.
/// </summary>
@@ -21,7 +22,7 @@ namespace WalletWasabi.Tests.HwiTests.DeviceConnectedTests
#region SharedVariables
// Bottleneck: user action on device.
- public TimeSpan ReasonableRequestTimeout { get; } = TimeSpan.FromMinutes(1);
+ public TimeSpan ReasonableRequestTimeout { get; } = TimeSpan.FromMinutes(3);
#endregion SharedVariables
@@ -30,23 +31,23 @@ namespace WalletWasabi.Tests.HwiTests.DeviceConnectedTests
{
// --- USER INTERACTIONS ---
//
- // Connect a the device.
+ // Connect a the device and unlock if it's not wiped.
// Run this test.
// Wipe request: confirm.
// Wipe request: confirm.
- // Wipe request: pull out the device.
+ // Setup request: refuse.
+ // Setup request: confirm and setup.
//
// --- USER INTERACTIONS ---
var client = new HwiClient(Network.Main);
- string devicePath;
- HardwareWalletVendors deviceType;
using (var cts = new CancellationTokenSource(ReasonableRequestTimeout))
{
var enumerate = await client.EnumerateAsync(cts.Token);
HwiEnumerateEntry entry = enumerate.Single();
- devicePath = entry.Path;
- deviceType = entry.Type.Value;
+
+ string devicePath = entry.Path;
+ HardwareWalletVendors deviceType = entry.Type.Value;
// User should confirm the device action here.
await client.WipeAsync(deviceType, devicePath, cts.Token);
@@ -64,19 +65,15 @@ namespace WalletWasabi.Tests.HwiTests.DeviceConnectedTests
Assert.NotEmpty(entry.Error);
Assert.Equal(HwiErrorCode.NotInitialized, entry.Code);
Assert.Null(entry.Fingerprint);
- }
-
- using (var cts = new CancellationTokenSource(ReasonableRequestTimeout))
- {
- await Assert.ThrowsAsync<ArgumentNullException>(async () => await client.WipeAsync(deviceType, null, cts.Token));
- await Assert.ThrowsAsync<ArgumentException>(async () => await client.WipeAsync(deviceType, "", cts.Token));
- await Assert.ThrowsAsync<ArgumentException>(async () => await client.WipeAsync(deviceType, " ", cts.Token));
// User should confirm the device action here.
await client.WipeAsync(deviceType, devicePath, cts.Token);
- // User should make it fail by plug out the device.
- await Assert.ThrowsAsync<HwiException>(async () => await client.WipeAsync(deviceType, devicePath, cts.Token));
+ // User should refuse the device action here.
+ await Assert.ThrowsAsync<HwiException>(async () => await client.SetupAsync(deviceType, devicePath, cts.Token));
+
+ // User should confirm the device action and setup a new device here.
+ await client.SetupAsync(deviceType, devicePath, cts.Token);
}
}
}

MODIFY
WalletWasabi.Tests/HwiTests/NoDeviceConnectedTests/HwiDefaultResponseTests.cs
WalletWasabi.Tests/HwiTests/NoDeviceConnectedTests/HwiDefaultResponseTests.cs
@@ -86,6 +86,26 @@ namespace WalletWasabi.Tests.HwiTests.NoDeviceConnectedTests
}
}
+ [Theory]
+ [MemberData(nameof(GetHwiClientConfigurationCombinationValues))]
+ public async Task ThrowArgumentExceptionsForWrongDevicePathAsync(HwiClient client)
+ {
+ var wrongDeviePaths = new[] { "", " " };
+ using (var cts = new CancellationTokenSource(ReasonableRequestTimeout))
+ {
+ foreach (HardwareWalletVendors deviceType in Enum.GetValues(typeof(HardwareWalletVendors)))
+ {
+ foreach (var wrongDevicePath in wrongDeviePaths)
+ {
+ await Assert.ThrowsAsync<ArgumentException>(async () => await client.WipeAsync(deviceType, wrongDevicePath, cts.Token));
+ await Assert.ThrowsAsync<ArgumentException>(async () => await client.SetupAsync(deviceType, wrongDevicePath, cts.Token));
+ }
+ await Assert.ThrowsAsync<ArgumentNullException>(async () => await client.WipeAsync(deviceType, null, cts.Token));
+ await Assert.ThrowsAsync<ArgumentNullException>(async () => await client.SetupAsync(deviceType, null, cts.Token));
+ }
+ }
+ }
+
[Theory]
[MemberData(nameof(GetHwiClientConfigurationCombinationValues))]
public async Task CanCallAsynchronouslyAsync(HwiClient client)

MODIFY
WalletWasabi.Tests/HwiTests/NoDeviceConnectedTests/IMockHwiProcessBridge.cs
WalletWasabi.Tests/HwiTests/NoDeviceConnectedTests/IMockHwiProcessBridge.cs
@@ -37,6 +37,24 @@ namespace WalletWasabi.Tests.HwiTests.NoDeviceConnectedTests
return Task.FromResult((response, code));
}
}
+ else if (arguments == "--device-path \"webusb: 001:4\" --device-type \"trezor\" setup" || arguments == "--testnet --device-path \"webusb: 001:4\" --device-type \"trezor\" setup")
+ {
+ if (Model == HardwareWalletModels.TrezorT)
+ {
+ var response = "{\"error\": \"setup requires interactive mode\", \"code\": -9}";
+ var code = 0;
+ return Task.FromResult((response, code));
+ }
+ }
+ else if (arguments == "--device-path \"webusb: 001:4\" --device-type \"trezor\" --interactive setup" || arguments == "--testnet --device-path \"webusb: 001:4\" --device-type \"trezor\" --interactive setup")
+ {
+ if (Model == HardwareWalletModels.TrezorT)
+ {
+ var response = "{\"success\": \"true\"\r\n}";
+ var code = 0;
+ return Task.FromResult((response, code));
+ }
+ }
throw new NotImplementedException($"Mocking is not implemented for '{arguments}'");
}

MODIFY
WalletWasabi.Tests/HwiTests/NoDeviceConnectedTests/MockedDeviceTests.cs
WalletWasabi.Tests/HwiTests/NoDeviceConnectedTests/MockedDeviceTests.cs
@@ -43,11 +43,9 @@ namespace WalletWasabi.Tests.HwiTests.NoDeviceConnectedTests
Assert.Null(entry.Fingerprint);
var deviceType = entry.Type.Value;
- await Assert.ThrowsAsync<ArgumentNullException>(async () => await client.WipeAsync(deviceType, null, cts.Token));
- await Assert.ThrowsAsync<ArgumentException>(async () => await client.WipeAsync(deviceType, "", cts.Token));
- await Assert.ThrowsAsync<ArgumentException>(async () => await client.WipeAsync(deviceType, " ", cts.Token));
await client.WipeAsync(deviceType, entry.Path, cts.Token);
+ await client.SetupAsync(deviceType, entry.Path, cts.Token);
}
}

MODIFY
WalletWasabi/Hwi2/HwiClient.cs
WalletWasabi/Hwi2/HwiClient.cs
@@ -73,16 +73,23 @@ namespace WalletWasabi.Hwi2
public async Task WipeAsync(HardwareWalletVendors deviceType, string devicePath, CancellationToken cancel)
{
- devicePath = Guard.NotNullOrEmptyOrWhitespace(nameof(devicePath), devicePath, trim: true);
await SendCommandAsync(
- options: new[] { new HwiOption(HwiOptions.DevicePath, devicePath), new HwiOption(HwiOptions.DeviceType, deviceType.ToString().ToLowerInvariant()) },
+ options: new[] { HwiOption.DevicePath(devicePath), HwiOption.DeviceType(deviceType) },
command: HwiCommands.Wipe,
cancel).ConfigureAwait(false);
}
+ public async Task SetupAsync(HardwareWalletVendors deviceType, string devicePath, CancellationToken cancel)
+ {
+ await SendCommandAsync(
+ options: new[] { HwiOption.DevicePath(devicePath), HwiOption.DeviceType(deviceType), HwiOption.Interactive },
+ command: HwiCommands.Setup,
+ cancel).ConfigureAwait(false);
+ }
+
public async Task<Version> GetVersionAsync(CancellationToken cancel)
{
- string responseString = await SendCommandAsync(options: new[] { new HwiOption(HwiOptions.Version) }, command: null, cancel).ConfigureAwait(false);
+ string responseString = await SendCommandAsync(options: new[] { HwiOption.Version }, command: null, cancel).ConfigureAwait(false);
var version = HwiParser.ParseVersion(responseString);
return version;
@@ -90,7 +97,7 @@ namespace WalletWasabi.Hwi2
public async Task<string> GetHelpAsync(CancellationToken cancel)
{
- string responseString = await SendCommandAsync(options: new[] { new HwiOption(HwiOptions.Help) }, command: null, cancel).ConfigureAwait(false);
+ string responseString = await SendCommandAsync(options: new[] { HwiOption.Help }, command: null, cancel).ConfigureAwait(false);
return responseString;
}

MODIFY
WalletWasabi/Hwi2/Models/HwiOption.cs
WalletWasabi/Hwi2/Models/HwiOption.cs
+using NBitcoin;
using System;
using System.Collections.Generic;
using System.Text;
+using WalletWasabi.Helpers;
+using WalletWasabi.Hwi2.Parsers;
namespace WalletWasabi.Hwi2.Models
{
public class HwiOption
{
- public HwiOption(HwiOptions type, string argument = null)
+ public static HwiOption DevicePath(string devicePath)
+ {
+ devicePath = Guard.NotNullOrEmptyOrWhitespace(nameof(devicePath), devicePath, trim: true);
+ return new HwiOption(HwiOptions.DevicePath, devicePath);
+ }
+
+ public static HwiOption DeviceType(HardwareWalletVendors deviceType) => new HwiOption(HwiOptions.DeviceType, deviceType.ToHwiFriendlyString());
+
+ public static HwiOption Debug => new HwiOption(HwiOptions.Debug);
+
+ public static HwiOption Fingerprint(HDFingerprint fingerprint) => new HwiOption(HwiOptions.Fingerprint, fingerprint.ToString());
+
+ public static HwiOption Help => new HwiOption(HwiOptions.Help);
+ public static HwiOption Interactive => new HwiOption(HwiOptions.Interactive);
+
+ public static HwiOption Password(string password) => new HwiOption(HwiOptions.Password, password);
+
+ public static HwiOption TestNet => new HwiOption(HwiOptions.TestNet);
+ public static HwiOption Version => new HwiOption(HwiOptions.Version);
+
+ private HwiOption(HwiOptions type, string argument = null)
{
Type = type;
Arguments = argument;

MODIFY
WalletWasabi/Hwi2/Parsers/HwiParser.cs
WalletWasabi/Hwi2/Parsers/HwiParser.cs
@@ -257,7 +257,7 @@ namespace WalletWasabi.Hwi2.Parsers
if (network != Network.Main)
{
- fullOptions.Insert(0, new HwiOption(HwiOptions.TestNet));
+ fullOptions.Insert(0, HwiOption.TestNet);
}
var optionsString = string.Join(" --", fullOptions.Select(x =>
@@ -298,5 +298,10 @@ namespace WalletWasabi.Hwi2.Parsers
var arguments = argumentBuilder.ToString().Trim();
return arguments;
}
+
+ public static string ToHwiFriendlyString(this HardwareWalletVendors me)
+ {
+ return me.ToString().ToLowerInvariant();
+ }
}
}

Implement SetupAsync