Implement Trezor T wipe
MODIFY
WalletWasabi.Tests/HwiTests/DeviceConnectedTests/UserWorkflowTests.cs
WalletWasabi.Tests/HwiTests/DeviceConnectedTests/UserWorkflowTests.cs
@@ -24,8 +24,18 @@ namespace WalletWasabi.Tests.HwiTests.DeviceConnectedTests
[Fact]
public async Task TrezorTTestsAsync()
{
- // USER: Connect a Trezor T
+ // --- USER INTERACTIONS ---
+ //
+ // Connect a the device.
+ // Run this test.
+ // First wipe request: confirm.
+ // Second wipe request: pull out the device.
+ //
+ // --- 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);
@@ -41,6 +51,22 @@ namespace WalletWasabi.Tests.HwiTests.DeviceConnectedTests
Assert.NotEmpty(entry.Error);
Assert.Equal(HwiErrorCode.NotInitialized, entry.Code);
Assert.Null(entry.Fingerprint);
+
+ devicePath = entry.Path;
+ deviceType = entry.Type.Value;
+ }
+
+ 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));
}
}
}

MODIFY
WalletWasabi/Hwi2/HwiClient.cs
WalletWasabi/Hwi2/HwiClient.cs
@@ -40,7 +40,7 @@ namespace WalletWasabi.Hwi2
#region Commands
- private async Task<string> SendCommandAsync(IEnumerable<HwiOptions> options, HwiCommands? command, CancellationToken cancel)
+ private async Task<string> SendCommandAsync(IEnumerable<HwiOption> options, HwiCommands? command, CancellationToken cancel)
{
string arguments = HwiParser.ToArgumentString(Network, options, command);
@@ -71,9 +71,18 @@ 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()) },
+ command: HwiCommands.Wipe,
+ cancel).ConfigureAwait(false);
+ }
+
public async Task<Version> GetVersionAsync(CancellationToken cancel)
{
- string responseString = await SendCommandAsync(options: new[] { HwiOptions.Version }, command: null, cancel).ConfigureAwait(false);
+ string responseString = await SendCommandAsync(options: new[] { new HwiOption(HwiOptions.Version) }, command: null, cancel).ConfigureAwait(false);
var version = HwiParser.ParseVersion(responseString);
return version;
@@ -81,7 +90,7 @@ namespace WalletWasabi.Hwi2
public async Task<string> GetHelpAsync(CancellationToken cancel)
{
- string responseString = await SendCommandAsync(options: new[] { HwiOptions.Help }, command: null, cancel).ConfigureAwait(false);
+ string responseString = await SendCommandAsync(options: new[] { new HwiOption(HwiOptions.Help) }, command: null, cancel).ConfigureAwait(false);
return responseString;
}

ADD
WalletWasabi/Hwi2/Models/HwiOption.cs
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace WalletWasabi.Hwi2.Models
+{
+ public class HwiOption
+ {
+ public HwiOption(HwiOptions type, string argument = null)
+ {
+ Type = type;
+ Arguments = argument;
+ }
+
+ public HwiOptions Type { get; }
+ public string Arguments { get; }
+ }
+}

MODIFY
WalletWasabi/Hwi2/Parsers/HwiParser.cs
WalletWasabi/Hwi2/Parsers/HwiParser.cs
@@ -44,6 +44,7 @@ namespace WalletWasabi.Hwi2.Parsers
var errToken = token["error"];
var codeToken = token["code"];
+ var successToken = token["success"];
string err = "";
if (errToken != null)
@@ -71,6 +72,10 @@ namespace WalletWasabi.Hwi2.Parsers
{
error = new HwiException(HwiErrorCode.UnknownError, err);
}
+ else if (successToken != null && successToken.Value<bool>() == false)
+ {
+ error = new HwiException(HwiErrorCode.UnknownError, "");
+ }
return error != null;
}
@@ -242,17 +247,40 @@ namespace WalletWasabi.Hwi2.Parsers
throw new FormatException($"Cannot parse version from HWI's response. Response: {hwiResponse}.");
}
- public static string ToArgumentString(Network network, IEnumerable<HwiOptions> options, HwiCommands? command)
+ public static string ToArgumentString(Network network, IEnumerable<HwiOption> options, HwiCommands? command)
{
- options = options ?? Enumerable.Empty<HwiOptions>();
- var fullOptions = new List<HwiOptions>(options);
+ options = options ?? Enumerable.Empty<HwiOption>();
+ var fullOptions = new List<HwiOption>(options);
if (network != Network.Main)
{
- fullOptions.Add(HwiOptions.TestNet);
+ fullOptions.Add(new HwiOption(HwiOptions.TestNet));
}
- var optionsString = string.Join(" --", fullOptions.Select(x => x.ToString().ToLowerInvariant()));
+ var optionsString = string.Join(" --", fullOptions.Select(x =>
+ {
+ string optionString;
+ if (x.Type == HwiOptions.DeviceType)
+ {
+ optionString = "device-type";
+ }
+ else if (x.Type == HwiOptions.DevicePath)
+ {
+ optionString = "device-path";
+ }
+ else
+ {
+ optionString = x.Type.ToString().ToLowerInvariant();
+ }
+ if (string.IsNullOrWhiteSpace(x.Arguments))
+ {
+ return optionString;
+ }
+ else
+ {
+ return $"{optionString} \"{x.Arguments}\"";
+ }
+ }));
optionsString = string.IsNullOrWhiteSpace(optionsString) ? "" : $"--{optionsString}";
var argumentBuilder = new StringBuilder(optionsString);
if (command != null)

Implement Trezor T wipe