Skip to content

Commit d0f12f5

Browse files
committed
Merge #311: Update firmware build scripts and tests to work with new firmwares
4a8f88e tests: Non-device tests do not need emulators compiled (Andrew Chow) 9b31a96 tests: Only send kill signal if process isn't already dead (Andrew Chow) 9ab7373 tests: Run each device's test suite individually (Andrew Chow) 3f82e60 test, trezor: report model T in tests correctly (Andrew Chow) a741211 tests: move setUp and tearDown to DeviceTestCase (Andrew Chow) e5808ac tests, ledger: Have speculos do button presses internally (Andrew Chow) b8ba161 tests: use Travis' default xcode version (Andrew Chow) 4a4bf70 tests: Use new speculos cli args (Andrew Chow) cb13862 tests: checkout Trezor T v2.2.0 (Andrew Chow) 83b60ad tests: coldcard simulator use --ms instead of -m (Andrew Chow) cd0720f tests: Use p2sh-segwit address type for the p2sh-segwit import (Andrew Chow) 5355055 update Trezor 1 build script (Andrew Chow) 4b1c06f Update keepkey build script (Andrew Chow) Pull request description: For the past 2 weeks, travis has been failing because hardware wallet vendors have been updating their firmware (and emulators) in a way that was incompatible with the HWI tests. I've finally gotten around to fixing that. Things that were updated: * Keepkey build script. They switched to Python 3 and require a newer version of nanopb. Some other build commands had to be modified. * Trezor 1 build script. `script/setup` needs to be run in the pipenv. * Trezor T master branch incompatibility. Trezor T has made some major change that makes HWI incompatible with their current master branch (and probably future firmware versions too). For now, we checkout the 2.2.0 (latest as of this PR) firmware for testing. * Speculos command line arguments. Speculos changed their command line argument names. * Speculos patched to just do the key presses internally. Instead of having us to run another thread to send the correct key presses at the right time, we modify speculos to do that itself. * Coldcard command line arguments. Coldcard changed some command line argument names. * Some ismine thing is "broken" in Core, added a workaround for that. * Each devices tests are run with their own test runner. Global emulators will only be run for their device's tests. * MacOS build will use travis' defaul xcode version so that we don't run into issues with it being outdated and no longer supported by travis Top commit has no ACKs. Tree-SHA512: 0a5b6eb759a57f05254814d3c90e786989e41474d15b95393c08ef68e515fa31847129cef0933e0c801b7dea882abf3674438c0483ca57824024e25e6a566c45
2 parents 31ccc02 + 4a8f88e commit d0f12f5

11 files changed

+156
-249
lines changed

.travis.yml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,9 @@ jobs:
6969
script: mypy hwilib/base58.py
7070
- name: Run non-device tests only
7171
stage: test
72+
install:
73+
- pip install poetry
74+
- poetry install
7275
script: cd test; poetry run ./run_tests.py
7376
- name: With process_commands interface
7477
stage: test
@@ -112,7 +115,6 @@ jobs:
112115
- name: macOS binary distribution (no tests)
113116
stage: test
114117
os: osx
115-
osx_image: xcode7.3
116118
language: generic
117119
addons:
118120
artifacts:
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
From bd251085fa85ad20e90fa055e7487b5c73d3dc7d Mon Sep 17 00:00:00 2001
2+
From: Andrew Chow <achow101-github@achow101.com>
3+
Date: Thu, 12 Mar 2020 17:25:09 -0400
4+
Subject: [PATCH] Do button presses within seproxyhal
5+
6+
---
7+
mcu/seproxyhal.py | 34 ++++++++++++++++++++++++++++++++++
8+
1 file changed, 34 insertions(+)
9+
10+
diff --git a/mcu/seproxyhal.py b/mcu/seproxyhal.py
11+
index 8ed3fec..1a6bfb6 100644
12+
--- a/mcu/seproxyhal.py
13+
+++ b/mcu/seproxyhal.py
14+
@@ -2,6 +2,7 @@ import binascii
15+
import logging
16+
import os
17+
import select
18+
+import struct
19+
import sys
20+
import time
21+
import threading
22+
@@ -144,6 +145,39 @@ class SeProxyHal:
23+
#self.logger.debug(f"DISPLAY_STATUS {data!r}")
24+
screen.display_status(data)
25+
self._queue_event_packet(SephTag.DISPLAY_PROCESSED_EVENT)
26+
+ data_type, _, x, y, = struct.unpack('bbhh', data[:6])
27+
+ if data_type == 7:
28+
+ if y == 12 or y ==28:
29+
+ self.line = data[28:].decode()
30+
+ elif y == 26:
31+
+ self.line += data[28:].decode()
32+
+
33+
+ if y == 26 or y == 28:
34+
+ if self.line.startswith('Address') or self.line.startswith('Message hash') or self.line.startswith('Reviewoutput') or self.line.startswith('Amount') or self.line.startswith('Fees') or self.line.startswith('Confirmtransaction'):
35+
+ self.handle_button(2, True)
36+
+ self.handle_button(2, False)
37+
+ if self.line.startswith('Message hash'):
38+
+ self.seen_msg_hash = True
39+
+ elif self.line == 'Approve' or self.line.startswith('Accept'):
40+
+ self.handle_button(1, True)
41+
+ self.handle_button(2, True)
42+
+ self.handle_button(1, False)
43+
+ self.handle_button(2, False)
44+
+ elif self.line == 'Signmessage':
45+
+ if self.seen_msg_hash:
46+
+ self.handle_button(1, True)
47+
+ self.handle_button(2, True)
48+
+ self.handle_button(1, False)
49+
+ self.handle_button(2, False)
50+
+ self.seen_msg_hash = False
51+
+ else:
52+
+ self.handle_button(2, True)
53+
+ self.handle_button(2, False)
54+
+ self.handle_button(2, True)
55+
+ self.handle_button(2, False)
56+
+ elif self.line == 'Cancel' or self.line == 'Reject':
57+
+ self.handle_button(1, True)
58+
+ self.handle_button(1, False)
59+
60+
elif tag == SephTag.SCREEN_DISPLAY_RAW_STATUS:
61+
self.logger.debug("SephTag.SCREEN_DISPLAY_RAW_STATUS")
62+
--
63+
2.25.2
64+

test/data/speculos-screen-text.patch

Lines changed: 0 additions & 55 deletions
This file was deleted.

test/run_tests.py

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -56,13 +56,15 @@
5656
args = parser.parse_args()
5757

5858
# Run tests
59+
success = True
5960
suite = unittest.TestSuite()
6061
suite.addTests(unittest.defaultTestLoader.loadTestsFromTestCase(TestDescriptor))
6162
suite.addTests(unittest.defaultTestLoader.loadTestsFromTestCase(TestSegwitAddress))
6263
suite.addTests(unittest.defaultTestLoader.loadTestsFromTestCase(TestPSBT))
6364
suite.addTests(unittest.defaultTestLoader.loadTestsFromTestCase(TestBase58))
6465
if sys.platform.startswith("linux"):
6566
suite.addTests(unittest.defaultTestLoader.loadTestsFromTestCase(TestUdevRulesInstaller))
67+
success = unittest.TextTestRunner(stream=sys.stdout, verbosity=2).run(suite).wasSuccessful()
6668

6769
if args.all:
6870
# Default all true unless overridden
@@ -86,17 +88,16 @@
8688
rpc, userpass = start_bitcoind(args.bitcoind)
8789

8890
if args.bitbox:
89-
suite.addTest(digitalbitbox_test_suite(args.bitbox_path, rpc, userpass, args.interface))
91+
success &= digitalbitbox_test_suite(args.bitbox_path, rpc, userpass, args.interface)
9092
if args.coldcard:
91-
suite.addTest(coldcard_test_suite(args.coldcard_path, rpc, userpass, args.interface))
93+
success &= coldcard_test_suite(args.coldcard_path, rpc, userpass, args.interface)
9294
if args.trezor:
93-
suite.addTest(trezor_test_suite(args.trezor_path, rpc, userpass, args.interface))
95+
success &= trezor_test_suite(args.trezor_path, rpc, userpass, args.interface)
9496
if args.trezor_t:
95-
suite.addTest(trezor_test_suite(args.trezor_t_path, rpc, userpass, args.interface, True))
97+
success &= trezor_test_suite(args.trezor_t_path, rpc, userpass, args.interface, True)
9698
if args.keepkey:
97-
suite.addTest(keepkey_test_suite(args.keepkey_path, rpc, userpass, args.interface))
99+
success &= keepkey_test_suite(args.keepkey_path, rpc, userpass, args.interface)
98100
if args.ledger:
99-
suite.addTest(ledger_test_suite(args.ledger_path, rpc, userpass, args.interface))
101+
success &= ledger_test_suite(args.ledger_path, rpc, userpass, args.interface)
100102

101-
result = unittest.TextTestRunner(stream=sys.stdout, verbosity=2).run(suite)
102-
sys.exit(not result.wasSuccessful())
103+
sys.exit(not success)

test/setup_environment.sh

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ trezor_setup_needed=false
1212
if [ ! -d "trezor-firmware" ]; then
1313
git clone --recursive https://github.com/trezor/trezor-firmware.git
1414
cd trezor-firmware
15+
git checkout core/v2.2.0
1516
trezor_setup_needed=true
1617
else
1718
cd trezor-firmware
@@ -36,8 +37,8 @@ fi
3637
cd legacy
3738
export EMULATOR=1 TREZOR_TRANSPORT_V1=1 DEBUG_LINK=1 HEADLESS=1
3839
if [ "$trezor_setup_needed" == true ] ; then
39-
script/setup
40-
pipenv install
40+
pipenv sync
41+
pipenv run script/setup
4142
fi
4243
pipenv run script/cibuild
4344
# Delete any emulator.img file
@@ -147,18 +148,14 @@ fi
147148

148149
# Build the simulator. This is cached, but it is also fast
149150
if [ "$keepkey_setup_needed" == true ] ; then
150-
git clone https://github.com/nanopb/nanopb.git -b nanopb-0.2.9.2
151+
git clone https://github.com/nanopb/nanopb.git -b nanopb-0.3.9.4
151152
fi
152-
# This needs py2, so make a pipenv
153-
export PIPENV_IGNORE_VIRTUALENVS=1
154-
pipenv --python 2.7
155-
pipenv install protobuf
156153
cd nanopb/generator/proto
157-
pipenv run make
154+
make
158155
cd ../../../
159156
export PATH=$PATH:`pwd`/nanopb/generator
160-
pipenv run cmake -C cmake/caches/emulator.cmake . -DNANOPB_DIR=nanopb/ -DKK_HAVE_STRLCAT=OFF -DKK_HAVE_STRLCPY=OFF
161-
pipenv run make -j$(nproc) kkemu
157+
cmake -C cmake/caches/emulator.cmake . -DNANOPB_DIR=nanopb/ -DPROTOC_BINARY=/usr/bin/protoc
158+
make -j$(nproc) kkemu
162159
# Delete any emulator.img file
163160
find . -name "emulator.img" -exec rm {} \;
164161
cd ..
@@ -187,7 +184,7 @@ else
187184
fi
188185
fi
189186
# Apply patch to get screen info
190-
git am ../../data/speculos-screen-text.patch
187+
git am ../../data/speculos-auto-button.patch
191188

192189
# Build the simulator. This is cached, but it is also fast
193190
mkdir -p build

test/test_coldcard.py

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import os
66
import signal
77
import subprocess
8+
import sys
89
import time
910
import unittest
1011

@@ -13,7 +14,7 @@
1314

1415
def coldcard_test_suite(simulator, rpc, userpass, interface):
1516
# Start the Coldcard simulator
16-
coldcard_proc = subprocess.Popen(['python3', os.path.basename(simulator), '-m'], cwd=os.path.dirname(simulator), stdout=subprocess.DEVNULL, preexec_fn=os.setsid)
17+
coldcard_proc = subprocess.Popen(['python3', os.path.basename(simulator), '--ms'], cwd=os.path.dirname(simulator), stdout=subprocess.DEVNULL, preexec_fn=os.setsid)
1718
# Wait for simulator to be up
1819
while True:
1920
try:
@@ -31,8 +32,9 @@ def coldcard_test_suite(simulator, rpc, userpass, interface):
3132
# Cleanup
3233

3334
def cleanup_simulator():
34-
os.killpg(os.getpgid(coldcard_proc.pid), signal.SIGTERM)
35-
os.waitpid(os.getpgid(coldcard_proc.pid), 0)
35+
if coldcard_proc.poll() is None:
36+
os.killpg(os.getpgid(coldcard_proc.pid), signal.SIGTERM)
37+
os.waitpid(os.getpgid(coldcard_proc.pid), 0)
3638
atexit.register(cleanup_simulator)
3739

3840
# Coldcard specific management command tests
@@ -101,7 +103,11 @@ def test_getxpub(self):
101103
suite.addTest(DeviceTestCase.parameterize(TestDisplayAddress, rpc, userpass, 'coldcard', 'coldcard', '/tmp/ckcc-simulator.sock', '0f056943', 'tpubDDpWvmUrPZrhSPmUzCMBHffvC3HyMAPnWDSAQNBTnj1iZeJa7BZQEttFiP4DS4GCcXQHezdXhn86Hj6LHX5EDstXPWrMaSneRWM8yUf6NFd', interface=interface))
102104
suite.addTest(DeviceTestCase.parameterize(TestSignMessage, rpc, userpass, 'coldcard', 'coldcard', '/tmp/ckcc-simulator.sock', '0f056943', 'tpubDDpWvmUrPZrhSPmUzCMBHffvC3HyMAPnWDSAQNBTnj1iZeJa7BZQEttFiP4DS4GCcXQHezdXhn86Hj6LHX5EDstXPWrMaSneRWM8yUf6NFd', interface=interface))
103105
suite.addTest(DeviceTestCase.parameterize(TestSignTx, rpc, userpass, 'coldcard', 'coldcard', '/tmp/ckcc-simulator.sock', '0f056943', 'tpubDDpWvmUrPZrhSPmUzCMBHffvC3HyMAPnWDSAQNBTnj1iZeJa7BZQEttFiP4DS4GCcXQHezdXhn86Hj6LHX5EDstXPWrMaSneRWM8yUf6NFd', interface=interface))
104-
return suite
106+
107+
result = unittest.TextTestRunner(stream=sys.stdout, verbosity=2).run(suite)
108+
cleanup_simulator()
109+
atexit.unregister(cleanup_simulator)
110+
return result.wasSuccessful()
105111

106112
if __name__ == '__main__':
107113
parser = argparse.ArgumentParser(description='Test Coldcard implementation')
@@ -113,5 +119,4 @@ def test_getxpub(self):
113119
# Start bitcoind
114120
rpc, userpass = start_bitcoind(args.bitcoind)
115121

116-
suite = coldcard_test_suite(args.simulator, rpc, userpass, args.interface)
117-
unittest.TextTestRunner(verbosity=2).run(suite)
122+
sys.exit(not coldcard_test_suite(args.simulator, rpc, userpass, args.interface))

test/test_device.py

Lines changed: 5 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -113,13 +113,13 @@ def __str__(self):
113113
def __repr__(self):
114114
return '{}: {}'.format(self.full_type, super().__repr__())
115115

116-
class TestDeviceConnect(DeviceTestCase):
117116
def setUp(self):
118117
self.emulator.start()
119118

120119
def tearDown(self):
121120
self.emulator.stop()
122121

122+
class TestDeviceConnect(DeviceTestCase):
123123
def test_enumerate(self):
124124
enum_res = self.do_command(self.get_password_args() + ['enumerate'])
125125
found = False
@@ -175,9 +175,6 @@ def setUp(self):
175175
self.dev_args.append('--testnet')
176176
self.emulator.start()
177177

178-
def tearDown(self):
179-
self.emulator.stop()
180-
181178
def test_getkeypool_bad_args(self):
182179
result = self.do_command(self.dev_args + ['getkeypool', '--sh_wpkh', '--wpkh', '0', '20'])
183180
self.assertIn('error', result)
@@ -205,9 +202,9 @@ def test_getkeypool(self):
205202
import_result = self.wrpc.importmulti(keypool_desc)
206203
self.assertTrue(import_result[0]['success'])
207204
for i in range(0, 21):
208-
addr_info = self.wrpc.getaddressinfo(self.wrpc.getnewaddress())
205+
addr_info = self.wrpc.getaddressinfo(self.wrpc.getnewaddress('', 'p2sh-segwit'))
209206
self.assertEqual(addr_info['hdkeypath'], "m/49'/1'/0'/0/{}".format(i))
210-
addr_info = self.wrpc.getaddressinfo(self.wrpc.getrawchangeaddress())
207+
addr_info = self.wrpc.getaddressinfo(self.wrpc.getrawchangeaddress('p2sh-segwit'))
211208
self.assertEqual(addr_info['hdkeypath'], "m/49'/1'/0'/1/{}".format(i))
212209

213210
keypool_desc = self.do_command(self.dev_args + ['getkeypool', '--wpkh', '0', '20'])
@@ -223,9 +220,9 @@ def test_getkeypool(self):
223220
import_result = self.wrpc.importmulti(keypool_desc)
224221
self.assertTrue(import_result[0]['success'])
225222
for i in range(0, 21):
226-
addr_info = self.wrpc.getaddressinfo(self.wrpc.getnewaddress())
223+
addr_info = self.wrpc.getaddressinfo(self.wrpc.getnewaddress('', 'p2sh-segwit'))
227224
self.assertEqual(addr_info['hdkeypath'], "m/49'/1'/3'/0/{}".format(i))
228-
addr_info = self.wrpc.getaddressinfo(self.wrpc.getrawchangeaddress())
225+
addr_info = self.wrpc.getaddressinfo(self.wrpc.getrawchangeaddress('p2sh-segwit'))
229226
self.assertEqual(addr_info['hdkeypath'], "m/49'/1'/3'/1/{}".format(i))
230227
keypool_desc = self.do_command(self.dev_args + ['getkeypool', '--wpkh', '--account', '3', '0', '20'])
231228
import_result = self.wrpc.importmulti(keypool_desc)
@@ -289,9 +286,6 @@ def setUp(self):
289286
self.dev_args.append('--testnet')
290287
self.emulator.start()
291288

292-
def tearDown(self):
293-
self.emulator.stop()
294-
295289
def _generate_and_finalize(self, unknown_inputs, psbt):
296290
if not unknown_inputs:
297291
# Just do the normal signing process to test "all inputs" case
@@ -464,12 +458,6 @@ def test_big_tx(self):
464458
pass
465459

466460
class TestDisplayAddress(DeviceTestCase):
467-
def setUp(self):
468-
self.emulator.start()
469-
470-
def tearDown(self):
471-
self.emulator.stop()
472-
473461
def test_display_address_bad_args(self):
474462
result = self.do_command(self.dev_args + ['displayaddress', '--sh_wpkh', '--wpkh', '--path', 'm/49h/1h/0h/0/0'])
475463
self.assertIn('error', result)
@@ -544,12 +532,6 @@ def test_display_address_descriptor(self):
544532
self.assertEqual(result['code'], -7)
545533

546534
class TestSignMessage(DeviceTestCase):
547-
def setUp(self):
548-
self.emulator.start()
549-
550-
def tearDown(self):
551-
self.emulator.stop()
552-
553535
def test_sign_msg(self):
554536
self.do_command(self.dev_args + ['signmessage', '"Message signing test"', 'm/44h/1h/0h/0/0'])
555537

test/test_digitalbitbox.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import json
66
import os
77
import subprocess
8+
import sys
89
import time
910
import unittest
1011

@@ -147,7 +148,11 @@ def test_getxpub(self):
147148
suite.addTest(DeviceTestCase.parameterize(TestGetKeypool, rpc, userpass, type, full_type, path, fingerprint, master_xpub, '0000', interface=interface))
148149
suite.addTest(DeviceTestCase.parameterize(TestSignTx, rpc, userpass, type, full_type, path, fingerprint, master_xpub, '0000', interface=interface))
149150
suite.addTest(DeviceTestCase.parameterize(TestSignMessage, rpc, userpass, type, full_type, path, fingerprint, master_xpub, '0000', interface=interface))
150-
return suite
151+
152+
result = unittest.TextTestRunner(stream=sys.stdout, verbosity=2).run(suite)
153+
cleanup_simulator()
154+
atexit.unregister(cleanup_simulator)
155+
return result.wasSuccessful()
151156

152157
if __name__ == '__main__':
153158
parser = argparse.ArgumentParser(description='Test Digital Bitbox implementation')
@@ -159,5 +164,4 @@ def test_getxpub(self):
159164
# Start bitcoind
160165
rpc, userpass = start_bitcoind(args.bitcoind)
161166

162-
suite = digitalbitbox_test_suite(args.simulator, rpc, userpass, args.interface)
163-
unittest.TextTestRunner(verbosity=2).run(suite)
167+
sys.exit(not digitalbitbox_test_suite(args.simulator, rpc, userpass, args.interface))

0 commit comments

Comments
 (0)