private static void ThrowIfError(string responseString, IEnumerable <HwiOption> options) { if (HwiParser.TryParseErrors(responseString, options, out HwiException error)) { throw error; } }
public void TryParseVersionTests(string input, bool isParsable) { Version expectedVersion = new(1, 1, 2); Assert.Equal(isParsable, HwiParser.TryParseVersion(input, out Version? actualVersion)); if (isParsable) { Assert.Equal(expectedVersion, actualVersion); } }
private async Task <string> SendCommandAsync(IEnumerable <HwiOption> options, HwiCommands?command, string commandArguments, bool openConsole, CancellationToken cancel, bool isRecursion = false) { string arguments = HwiParser.ToArgumentString(Network, options, command, commandArguments); try { (string responseString, int exitCode) = await Bridge.SendCommandAsync(arguments, openConsole, cancel).ConfigureAwait(false); ThrowIfError(responseString, options, arguments, exitCode); return(responseString); } catch (Exception ex) when(ex is OperationCanceledException || ex is TaskCanceledException || ex is TimeoutException) { throw new OperationCanceledException($"'hwi {arguments}' operation is canceled.", ex); } //// HWI is inconsistent with error codes here. catch (HwiException ex) when(ex.ErrorCode == HwiErrorCode.DeviceConnError || ex.ErrorCode == HwiErrorCode.DeviceNotReady) { // Probably didn't find device with specified fingerprint. // Enumerate and call again, but not forever. if (isRecursion || !options.Any(x => x.Type == HwiOptions.Fingerprint)) { throw; } IEnumerable <HwiEnumerateEntry> hwiEntries = await EnumerateAsync(cancel, isRecursion : true); // Trezor T won't give Fingerprint info so we'll assume that the first device that doesn't give fingerprint is what we need. HwiEnumerateEntry firstNoFingerprintEntry = hwiEntries.Where(x => x.Fingerprint is null).FirstOrDefault(); if (firstNoFingerprintEntry is null) { throw; } // Build options without fingerprint with device model and device path. var newOptions = BuildOptions(firstNoFingerprintEntry.Model, firstNoFingerprintEntry.Path, fingerprint: null, options.Where(x => x.Type != HwiOptions.Fingerprint).ToArray()); return(await SendCommandAsync(newOptions, command, arguments, openConsole, cancel, isRecursion : true)); } catch (HwiException ex) when(Network != Network.Main && ex.ErrorCode == HwiErrorCode.UnknownError && ex.Message?.Contains("DataError: Forbidden key path") is true) { // Trezor only accepts KeyPath 84'/1' on TestNet from v2.3.1. We fake that we are on MainNet to ensure compatibility. string fixedArguments = HwiParser.ToArgumentString(Network.Main, options, command, commandArguments); (string responseString, int exitCode) = await Bridge.SendCommandAsync(fixedArguments, openConsole, cancel).ConfigureAwait(false); ThrowIfError(responseString, options, fixedArguments, exitCode); return(responseString); } }
public async Task <Version> GetVersionAsync(CancellationToken cancel) { string responseString = await SendCommandAsync( options : new[] { HwiOption.Version }, command : null, commandArguments : null, openConsole : false, cancel).ConfigureAwait(false); var version = HwiParser.ParseVersion(responseString); return(version); }
public async Task <IEnumerable <HwiEnumerateEntry> > EnumerateAsync(CancellationToken cancel, bool isRecursion = false) { string responseString = await SendCommandAsync( options : null, command : HwiCommands.Enumerate, commandArguments : null, openConsole : false, cancel, isRecursion).ConfigureAwait(false); IEnumerable <HwiEnumerateEntry> response = HwiParser.ParseHwiEnumerateResponse(responseString); return(response); }
private async Task <ExtPubKey> GetXpubImplAsync(HardwareWalletModels?deviceType, string devicePath, HDFingerprint?fingerprint, KeyPath keyPath, CancellationToken cancel) { string keyPathString = keyPath.ToString(true, "h"); var response = await SendCommandAsync( options : BuildOptions(deviceType, devicePath, fingerprint), command : HwiCommands.GetXpub, commandArguments : keyPathString, openConsole : false, cancel).ConfigureAwait(false); var extPubKey = HwiParser.ParseExtPubKey(response); return(extPubKey); }
private async Task <BitcoinWitPubKeyAddress> DisplayAddressImplAsync(HardwareWalletModels?deviceType, string devicePath, HDFingerprint?fingerprint, KeyPath keyPath, CancellationToken cancel) { var response = await SendCommandAsync( options : BuildOptions(deviceType, devicePath, fingerprint), command : HwiCommands.DisplayAddress, commandArguments : $"--path {keyPath.ToString(true, "h")} --wpkh", openConsole : false, cancel).ConfigureAwait(false); var address = HwiParser.ParseAddress(response, Network) as BitcoinWitPubKeyAddress; address = address.TransformToNetworkNetwork(Network); return(address); }
private static void ThrowIfError(string responseString, IEnumerable <HwiOption> options, string arguments, int exitCode) { if (exitCode != 0) { if (HwiParser.TryParseErrors(responseString, options, out HwiException error)) { throw error; } throw new HwiException(HwiErrorCode.UnknownError, $"'hwi {arguments}' exited with incorrect exit code: {exitCode}."); } if (HwiParser.TryParseErrors(responseString, options, out HwiException error2)) { throw error2; } }
private async Task <PSBT> SignTxImplAsync(HardwareWalletModels?deviceType, string devicePath, HDFingerprint?fingerprint, PSBT psbt, CancellationToken cancel) { var psbtString = psbt.ToBase64(); var response = await SendCommandAsync( options : BuildOptions(deviceType, devicePath, fingerprint), command : HwiCommands.SignTx, commandArguments : psbtString, openConsole : false, cancel).ConfigureAwait(false); PSBT signedPsbt = HwiParser.ParsePsbt(response, Network); if (!signedPsbt.IsAllFinalized()) { signedPsbt.Finalize(); } return(signedPsbt); }
public async Task ColdCardMk1MockTestsAsync(Network network) { var client = new HwiClient(network, new HwiProcessBridgeMock(HardwareWalletModels.Coldcard)); using var cts = new CancellationTokenSource(ReasonableRequestTimeout); IEnumerable <HwiEnumerateEntry> enumerate = await client.EnumerateAsync(cts.Token); Assert.Single(enumerate); HwiEnumerateEntry entry = enumerate.Single(); Assert.Equal(HardwareWalletModels.Coldcard, entry.Model); string rawPath = "\\\\\\\\?\\\\hid#vid_d13e&pid_cc10&mi_00#7&1b239988&0&0000#{4d1e55b2-f16f-11cf-88cb-001111000030}"; string normalizedPath = HwiParser.NormalizeRawDevicePath(rawPath); Assert.Equal(normalizedPath, entry.Path); Assert.Null(entry.NeedsPassphraseSent); Assert.Null(entry.NeedsPinSent); Assert.Null(entry.Error); Assert.Null(entry.Code); Assert.Equal("a3d0d797", entry.Fingerprint.ToString()); Assert.True(entry.IsInitialized()); var deviceType = entry.Model; var devicePath = entry.Path; HDFingerprint fingerprint = entry.Fingerprint.Value; var wipe = await Assert.ThrowsAsync <HwiException>(async() => await client.WipeAsync(deviceType, devicePath, cts.Token)); Assert.Equal("The Coldcard does not support wiping via software", wipe.Message); Assert.Equal(HwiErrorCode.UnavailableAction, wipe.ErrorCode); // ColdCard doesn't support it. var setup = await Assert.ThrowsAsync <HwiException>(async() => await client.SetupAsync(deviceType, devicePath, false, cts.Token)); Assert.Equal("The Coldcard does not support software setup", setup.Message); Assert.Equal(HwiErrorCode.UnavailableAction, setup.ErrorCode); // ColdCard doesn't support it. var restore = await Assert.ThrowsAsync <HwiException>(async() => await client.RestoreAsync(deviceType, devicePath, false, cts.Token)); Assert.Equal("The Coldcard does not support restoring via software", restore.Message); Assert.Equal(HwiErrorCode.UnavailableAction, restore.ErrorCode); // ColdCard doesn't support it. var promptpin = await Assert.ThrowsAsync <HwiException>(async() => await client.PromptPinAsync(deviceType, devicePath, cts.Token)); Assert.Equal("The Coldcard does not need a PIN sent from the host", promptpin.Message); Assert.Equal(HwiErrorCode.UnavailableAction, promptpin.ErrorCode); // ColdCard doesn't support it. var sendpin = await Assert.ThrowsAsync <HwiException>(async() => await client.SendPinAsync(deviceType, devicePath, 1111, cts.Token)); Assert.Equal("The Coldcard does not need a PIN sent from the host", sendpin.Message); Assert.Equal(HwiErrorCode.UnavailableAction, sendpin.ErrorCode); 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 NotSupportedNetworkException(network); } Assert.Equal(expectedAddress1, address1); Assert.Equal(expectedAddress2, address2); }
public async Task TrezorOneMockTestsAsync(Network network) { var client = new HwiClient(network, new HwiProcessBridgeMock(HardwareWalletModels.Trezor_1)); using var cts = new CancellationTokenSource(ReasonableRequestTimeout); IEnumerable <HwiEnumerateEntry> enumerate = await client.EnumerateAsync(cts.Token); Assert.Single(enumerate); HwiEnumerateEntry entry = enumerate.Single(); Assert.Equal(HardwareWalletModels.Trezor_1, entry.Model); string rawPath = "hid:\\\\\\\\?\\\\hid#vid_534c&pid_0001&mi_00#7&6f0b727&0&0000#{4d1e55b2-f16f-11cf-88cb-001111000030}"; string normalizedPath = HwiParser.NormalizeRawDevicePath(rawPath); Assert.Equal(normalizedPath, entry.Path); Assert.False(entry.NeedsPassphraseSent); Assert.True(entry.NeedsPinSent); Assert.Equal("Could not open client or get fingerprint information: Trezor is locked. Unlock by using 'promptpin' and then 'sendpin'.", entry.Error); Assert.Equal(HwiErrorCode.DeviceNotReady, entry.Code); Assert.Null(entry.Fingerprint); Assert.True(entry.IsInitialized()); var deviceType = entry.Model; var devicePath = entry.Path; await client.WipeAsync(deviceType, devicePath, cts.Token); await client.SetupAsync(deviceType, devicePath, false, cts.Token); await client.RestoreAsync(deviceType, devicePath, false, cts.Token); var promptpin = await Assert.ThrowsAsync <HwiException>(async() => await client.PromptPinAsync(deviceType, devicePath, cts.Token)); Assert.Equal("The PIN has already been sent to this device", promptpin.Message); Assert.Equal(HwiErrorCode.DeviceAlreadyUnlocked, promptpin.ErrorCode); var sendpin = await Assert.ThrowsAsync <HwiException>(async() => await client.SendPinAsync(deviceType, devicePath, 1111, cts.Token)); Assert.Equal("The PIN has already been sent to this device", sendpin.Message); Assert.Equal(HwiErrorCode.DeviceAlreadyUnlocked, sendpin.ErrorCode); 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 NotSupportedNetworkException(network); } Assert.Equal(expectedAddress1, address1); Assert.Equal(expectedAddress2, address2); }
public Task <(string response, int exitCode)> SendCommandAsync(string arguments, bool openConsole, CancellationToken cancel, Action <StreamWriter>?standardInputWriter = null) { if (openConsole) { throw new NotImplementedException($"Cannot mock {nameof(openConsole)} mode."); } string model; string rawPath; model = Model switch { HardwareWalletModels.Trezor_T => "trezor_t", HardwareWalletModels.Trezor_1 => "trezor_1", HardwareWalletModels.Coldcard => "coldcard", HardwareWalletModels.Ledger_Nano_S => "ledger_nano_s", HardwareWalletModels.Ledger_Nano_X => "ledger_nano_x", _ => throw new NotImplementedException("Mock missing.") }; rawPath = Model switch { HardwareWalletModels.Trezor_T => "webusb: 001:4", HardwareWalletModels.Trezor_1 => "hid:\\\\\\\\?\\\\hid#vid_534c&pid_0001&mi_00#7&6f0b727&0&0000#{4d1e55b2-f16f-11cf-88cb-001111000030}", HardwareWalletModels.Coldcard => @"\\\\?\\hid#vid_d13e&pid_cc10&mi_00#7&1b239988&0&0000#{4d1e55b2-f16f-11cf-88cb-001111000030}", HardwareWalletModels.Ledger_Nano_S => "\\\\\\\\?\\\\hid#vid_2c97&pid_0001&mi_00#7&e45ae20&0&0000#{4d1e55b2-f16f-11cf-88cb-001111000030}", HardwareWalletModels.Ledger_Nano_X => "\\\\\\\\?\\\\hid#vid_2c97&pid_0001&mi_00#7&e45ae20&0&0000#{4d1e55b2-f16f-11cf-88cb-001111000030}", _ => throw new NotImplementedException("Mock missing.") }; string path = HwiParser.NormalizeRawDevicePath(rawPath); string devicePathAndTypeArgumentString = $"--device-path \"{path}\" --device-type \"{model}\""; const string SuccessTrueResponse = "{\"success\": true}\r\n"; string?response = null; int code = 0; if (CompareArguments(arguments, "enumerate")) { response = Model switch { HardwareWalletModels.Trezor_T => $"[{{\"model\": \"{model}\", \"path\": \"{rawPath}\", \"needs_pin_sent\": false, \"needs_passphrase_sent\": false, \"error\": \"Not initialized\"}}]", HardwareWalletModels.Trezor_1 => $"[{{\"model\": \"{model}\", \"path\": \"{rawPath}\", \"needs_pin_sent\": true, \"needs_passphrase_sent\": false, \"error\": \"Could not open client or get fingerprint information: Trezor is locked. Unlock by using 'promptpin' and then 'sendpin'.\", \"code\": -12}}]\r\n", HardwareWalletModels.Coldcard => $"[{{\"model\": \"{model}\", \"path\": \"{rawPath}\", \"needs_passphrase\": false, \"fingerprint\": \"a3d0d797\"}}]\r\n", HardwareWalletModels.Ledger_Nano_S => $"[{{\"model\": \"{model}\", \"path\": \"{rawPath}\", \"fingerprint\": \"4054d6f6\", \"needs_pin_sent\": false, \"needs_passphrase_sent\": false}}]\r\n", HardwareWalletModels.Ledger_Nano_X => $"[{{\"model\": \"{model}\", \"path\": \"{rawPath}\", \"fingerprint\": \"4054d6f6\", \"needs_pin_sent\": false, \"needs_passphrase_sent\": false}}]\r\n", _ => throw new NotImplementedException($"Mock missing for {model}") }; } else if (CompareArguments(arguments, $"{devicePathAndTypeArgumentString} wipe")) { response = Model switch { HardwareWalletModels.Trezor_T or HardwareWalletModels.Trezor_1 => SuccessTrueResponse, HardwareWalletModels.Coldcard => "{\"error\": \"The Coldcard does not support wiping via software\", \"code\": -9}\r\n", HardwareWalletModels.Ledger_Nano_S => "{\"error\": \"The Ledger Nano S does not support wiping via software\", \"code\": -9}\r\n", HardwareWalletModels.Ledger_Nano_X => "{\"error\": \"The Ledger Nano X does not support wiping via software\", \"code\": -9}\r\n", _ => throw new NotImplementedException("Mock missing.") }; } else if (CompareArguments(arguments, $"{devicePathAndTypeArgumentString} setup")) { response = Model switch { HardwareWalletModels.Trezor_T or HardwareWalletModels.Trezor_1 => "{\"error\": \"setup requires interactive mode\", \"code\": -9}", HardwareWalletModels.Coldcard => "{\"error\": \"The Coldcard does not support software setup\", \"code\": -9}\r\n", HardwareWalletModels.Ledger_Nano_S => "{\"error\": \"The Ledger Nano S does not support software setup\", \"code\": -9}\r\n", HardwareWalletModels.Ledger_Nano_X => "{\"error\": \"The Ledger Nano X does not support software setup\", \"code\": -9}\r\n", _ => throw new NotImplementedException("Mock missing.") }; } else if (CompareArguments(arguments, $"{devicePathAndTypeArgumentString} --interactive setup")) { response = Model switch { HardwareWalletModels.Trezor_T or HardwareWalletModels.Trezor_1 => SuccessTrueResponse, HardwareWalletModels.Coldcard => "{\"error\": \"The Coldcard does not support software setup\", \"code\": -9}\r\n", HardwareWalletModels.Ledger_Nano_S => "{\"error\": \"The Ledger Nano S does not support software setup\", \"code\": -9}\r\n", HardwareWalletModels.Ledger_Nano_X => "{\"error\": \"The Ledger Nano X does not support software setup\", \"code\": -9}\r\n", _ => throw new NotImplementedException("Mock missing.") }; } else if (CompareArguments(arguments, $"{devicePathAndTypeArgumentString} --interactive restore")) { response = Model switch { HardwareWalletModels.Trezor_T or HardwareWalletModels.Trezor_1 => SuccessTrueResponse, HardwareWalletModels.Coldcard => "{\"error\": \"The Coldcard does not support restoring via software\", \"code\": -9}\r\n", HardwareWalletModels.Ledger_Nano_S => "{\"error\": \"The Ledger Nano S does not support restoring via software\", \"code\": -9}\r\n", HardwareWalletModels.Ledger_Nano_X => "{\"error\": \"The Ledger Nano X does not support restoring via software\", \"code\": -9}\r\n", _ => throw new NotImplementedException("Mock missing.") }; } else if (CompareArguments(arguments, $"{devicePathAndTypeArgumentString} promptpin")) { response = Model switch { HardwareWalletModels.Trezor_T or HardwareWalletModels.Trezor_1 => "{\"error\": \"The PIN has already been sent to this device\", \"code\": -11}", HardwareWalletModels.Coldcard => "{\"error\": \"The Coldcard does not need a PIN sent from the host\", \"code\": -9}\r\n", HardwareWalletModels.Ledger_Nano_S => "{\"error\": \"The Ledger Nano S does not need a PIN sent from the host\", \"code\": -9}\r\n", HardwareWalletModels.Ledger_Nano_X => "{\"error\": \"The Ledger Nano X does not need a PIN sent from the host\", \"code\": -9}\r\n", _ => throw new NotImplementedException("Mock missing.") }; } else if (CompareArguments(arguments, $"{devicePathAndTypeArgumentString} sendpin", true)) { response = Model switch { HardwareWalletModels.Trezor_T or HardwareWalletModels.Trezor_1 => "{\"error\": \"The PIN has already been sent to this device\", \"code\": -11}", HardwareWalletModels.Coldcard => "{\"error\": \"The Coldcard does not need a PIN sent from the host\", \"code\": -9}\r\n", HardwareWalletModels.Ledger_Nano_S => "{\"error\": \"The Ledger Nano S does not need a PIN sent from the host\", \"code\": -9}\r\n", HardwareWalletModels.Ledger_Nano_X => "{\"error\": \"The Ledger Nano X does not need a PIN sent from the host\", \"code\": -9}\r\n", _ => throw new NotImplementedException("Mock missing.") }; } else if (CompareGetXbpubArguments(arguments, out string?xpub)) { if (Model is HardwareWalletModels.Trezor_T or HardwareWalletModels.Coldcard or HardwareWalletModels.Trezor_1 or HardwareWalletModels.Ledger_Nano_S or HardwareWalletModels.Ledger_Nano_X) { response = $"{{\"xpub\": \"{xpub}\"}}\r\n"; } } else if (CompareArguments(out bool t1, arguments, $"{devicePathAndTypeArgumentString} displayaddress --path m/84h/0h/0h --addr-type wit", false)) { if (Model is HardwareWalletModels.Trezor_T or HardwareWalletModels.Coldcard or HardwareWalletModels.Trezor_1 or HardwareWalletModels.Ledger_Nano_S or HardwareWalletModels.Ledger_Nano_X) { response = t1 ? "{\"address\": \"tb1q7zqqsmqx5ymhd7qn73lm96w5yqdkrmx7rtzlxy\"}\r\n" : "{\"address\": \"bc1q7zqqsmqx5ymhd7qn73lm96w5yqdkrmx7fdevah\"}\r\n"; } }
public Task <(string response, int exitCode)> SendCommandAsync(string arguments, bool openConsole, CancellationToken cancel, Action <StreamWriter>?standardInputWriter = null) { if (openConsole) { throw new NotImplementedException($"Cannot mock {nameof(openConsole)} mode."); } string model; string rawPath; if (Model == HardwareWalletModels.Trezor_T) { model = "trezor_t"; rawPath = "webusb: 001:4"; } else if (Model == HardwareWalletModels.Trezor_1) { model = "trezor_1"; rawPath = "hid:\\\\\\\\?\\\\hid#vid_534c&pid_0001&mi_00#7&6f0b727&0&0000#{4d1e55b2-f16f-11cf-88cb-001111000030}"; } else if (Model == HardwareWalletModels.Coldcard) { model = "coldcard"; rawPath = @"\\\\?\\hid#vid_d13e&pid_cc10&mi_00#7&1b239988&0&0000#{4d1e55b2-f16f-11cf-88cb-001111000030}"; } else if (Model == HardwareWalletModels.Ledger_Nano_S) { model = "ledger_nano_s"; rawPath = "\\\\\\\\?\\\\hid#vid_2c97&pid_0001&mi_00#7&e45ae20&0&0000#{4d1e55b2-f16f-11cf-88cb-001111000030}"; } else { throw new NotImplementedException("Mock missing."); } string path = HwiParser.NormalizeRawDevicePath(rawPath); string devicePathAndTypeArgumentString = $"--device-path \"{path}\" --device-type \"{model}\""; const string SuccessTrueResponse = "{\"success\": true}\r\n"; string?response = null; int code = 0; if (CompareArguments(arguments, "enumerate")) { if (Model == HardwareWalletModels.Trezor_T) { response = $"[{{\"model\": \"{model}\", \"path\": \"{rawPath}\", \"needs_pin_sent\": false, \"needs_passphrase_sent\": false, \"error\": \"Not initialized\"}}]"; } else if (Model == HardwareWalletModels.Trezor_1) { response = $"[{{\"model\": \"{model}\", \"path\": \"{rawPath}\", \"needs_pin_sent\": true, \"needs_passphrase_sent\": false, \"error\": \"Could not open client or get fingerprint information: Trezor is locked. Unlock by using 'promptpin' and then 'sendpin'.\", \"code\": -12}}]\r\n"; } else if (Model == HardwareWalletModels.Coldcard) { response = $"[{{\"model\": \"{model}\", \"path\": \"{rawPath}\", \"needs_passphrase\": false, \"fingerprint\": \"a3d0d797\"}}]\r\n"; } else if (Model == HardwareWalletModels.Ledger_Nano_S) { response = $"[{{\"model\": \"{model}\", \"path\": \"{rawPath}\", \"fingerprint\": \"4054d6f6\", \"needs_pin_sent\": false, \"needs_passphrase_sent\": false}}]\r\n"; } } else if (CompareArguments(arguments, $"{devicePathAndTypeArgumentString} wipe")) { if (Model is HardwareWalletModels.Trezor_T or HardwareWalletModels.Trezor_1) { response = SuccessTrueResponse; } else if (Model == HardwareWalletModels.Coldcard) { response = "{\"error\": \"The Coldcard does not support wiping via software\", \"code\": -9}\r\n"; } else if (Model == HardwareWalletModels.Ledger_Nano_S) { response = "{\"error\": \"The Ledger Nano S does not support wiping via software\", \"code\": -9}\r\n"; } }