ColdCard Kata
MODIFY
WalletWasabi.Tests/HwiTests/DeviceConnectedTests/Katas.cs
WalletWasabi.Tests/HwiTests/DeviceConnectedTests/Katas.cs
@@ -166,5 +166,90 @@ namespace WalletWasabi.Tests.HwiTests.DeviceConnectedTests
psbt.AddKeyPath(sendToPubKey, rootKeyPath2, sendToAddress.ScriptPubKey);
return psbt;
}
+
+ [Fact]
+ public async Task ColdCardKataAsync()
+ {
+ // --- USER INTERACTIONS ---
+ //
+ // Connect an already initialized device and unlock it.
+ // Run this test.
+ // Coldcard screen => "Receiving...": refuse
+ // signtx request: refuse
+ // signtx request: confirm
+ //
+ // --- USER INTERACTIONS ---
+
+ var network = Network.Main;
+ var client = new HwiClient(network);
+ using (var cts = new CancellationTokenSource(ReasonableRequestTimeout))
+ {
+ var enumerate = await client.EnumerateAsync(cts.Token);
+ Assert.Single(enumerate);
+ HwiEnumerateEntry entry = enumerate.Single();
+ Assert.NotNull(entry.Path);
+ Assert.Equal(HardwareWalletVendors.Coldcard, entry.Type);
+ Assert.True(entry.Fingerprint.HasValue);
+
+ string devicePath = entry.Path;
+ HardwareWalletVendors deviceType = entry.Type.Value;
+ HDFingerprint fingerprint = entry.Fingerprint.Value;
+
+ // ColdCard doesn't support it.
+ await Assert.ThrowsAsync<HwiException>(async () => await client.SetupAsync(deviceType, devicePath, false, cts.Token));
+
+ // ColdCard doesn't support it.
+ await Assert.ThrowsAsync<HwiException>(async () => await client.RestoreAsync(deviceType, devicePath, false, cts.Token));
+
+ // ColdCard doesn't support it.
+ await Assert.ThrowsAsync<HwiException>(async () => await client.PromptPinAsync(deviceType, devicePath, cts.Token));
+ // ColdCard doesn't support it.
+ await Assert.ThrowsAsync<HwiException>(async () => await client.SendPinAsync(deviceType, devicePath, 1111, cts.Token));
+
+ 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);
+ Assert.NotNull(xpub1);
+ Assert.NotNull(xpub2);
+ Assert.NotEqual(xpub1, xpub2);
+
+ // ColdCard just display the address. There is no confirm/refuse action.
+
+ BitcoinWitPubKeyAddress address1 = await client.DisplayAddressAsync(deviceType, devicePath, keyPath1, cts.Token);
+ BitcoinWitPubKeyAddress address2 = await client.DisplayAddressAsync(fingerprint, keyPath2, cts.Token);
+ Assert.NotNull(address1);
+ Assert.NotNull(address2);
+ Assert.NotEqual(address1, address2);
+ var expectedAddress1 = xpub1.PubKey.GetAddress(ScriptPubKeyType.Segwit, network);
+ var expectedAddress2 = xpub2.PubKey.GetAddress(ScriptPubKeyType.Segwit, network);
+ Assert.Equal(expectedAddress1, address1);
+ Assert.Equal(expectedAddress2, address2);
+
+ // If Coldcard is displaying the address it will say it is in another operation.
+ // When the PSBT sent ColdCard will display Receiving and stuck there.
+ // So here the user should press X to get back to the main menu.
+
+ // USER: REFUSE
+ PSBT psbt = BuildPsbt(network, fingerprint, xpub1, keyPath1);
+ var ex = await Assert.ThrowsAsync<HwiException>(async () => await client.SignTxAsync(deviceType, devicePath, psbt, cts.Token));
+ Assert.Equal(HwiErrorCode.DeviceBusy, ex.ErrorCode);
+
+ await Task.Delay(2000);
+
+ // USER: REFUSE
+ ex = await Assert.ThrowsAsync<HwiException>(async () => await client.SignTxAsync(deviceType, devicePath, psbt, cts.Token));
+ Assert.Equal(HwiErrorCode.ActionCanceled, ex.ErrorCode);
+
+ // USER: CONFIRM
+ PSBT signedPsbt = await client.SignTxAsync(deviceType, devicePath, psbt, cts.Token);
+
+ Transaction signeTx = signedPsbt.GetOriginalTransaction();
+ Assert.Equal(psbt.GetOriginalTransaction().GetHash(), signeTx.GetHash());
+
+ var checkResult = signeTx.Check();
+ Assert.Equal(TransactionCheckResult.Success, checkResult);
+ }
+ }
}
}

ColdCard Kata