Skip to content

Commit a568a0f

Browse files
somebodyLi424778940z
authored andcommitted
ci: add onekey device
1 parent 05b0d06 commit a568a0f

File tree

8 files changed

+86
-65
lines changed

8 files changed

+86
-65
lines changed

.github/actions/install-sim/action.yml

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,14 @@ runs:
1717
apt-get update
1818
apt-get install -y libsdl2-image-2.0-0 libusb-1.0-0
1919
tar -xvf trezor-firmware.tar.gz
20-
20+
21+
- if: startsWith(inputs.device, 'onekey-')
22+
shell: bash
23+
run: |
24+
apt-get update
25+
apt-get install -y libsdl2-image-2.0-0 libusb-1.0-0
26+
tar -xvf onekey-firmware.tar.gz
27+
2128
- if: inputs.device == 'coldcard'
2229
shell: bash
2330
run: |

.github/workflows/ci.yml

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ jobs:
4444
hwilib/devices/__init__.py
4545
hwilib/devices/keepkey.py
4646
hwilib/devices/ledger.py
47+
hwilib/devices/onekey.py
4748
hwilib/devices/trezor.py
4849
hwilib/errors.py
4950
hwilib/_script.py
@@ -153,7 +154,9 @@ jobs:
153154
- { name: 'jade', archive: 'jade', paths: 'test/work/jade/simulator' }
154155
- { name: 'ledger', archive: 'speculos', paths: 'test/work/speculos' }
155156
- { name: 'keepkey', archive: 'keepkey-firmware', paths: 'test/work/keepkey-firmware/bin' }
156-
157+
- { name: 'onekey-1', archive: 'onekey-firmware', paths: 'test/work/onekey-firmware' }
158+
- { name: 'onekey-t', archive: 'onekey-firmware', paths: 'test/work/onekey-firmware' }
159+
157160
steps:
158161
- uses: actions/checkout@v4
159162

@@ -218,6 +221,8 @@ jobs:
218221
- 'ledger'
219222
- 'ledger-legacy'
220223
- 'keepkey'
224+
- 'onekey-1'
225+
- 'onekey-t'
221226
script:
222227
- name: 'Wheel'
223228
install: 'pip install dist/*.whl'
@@ -232,8 +237,11 @@ jobs:
232237
env:
233238
DEVICE: '--${{ matrix.device }}'
234239

235-
container: python:${{ matrix.python-version }}
236-
240+
container:
241+
image: python:${{ matrix.python-version }}
242+
volumes:
243+
- ${{ github.workspace }}:${{ github.workspace }}
244+
options: --workdir ${{ github.workspace }}
237245
steps:
238246
- uses: actions/checkout@v4
239247

@@ -287,10 +295,15 @@ jobs:
287295
- 'ledger'
288296
- 'ledger-legacy'
289297
- 'keepkey'
298+
- 'onekey-1'
299+
- 'onekey-t'
290300
interface: [ 'library', 'cli', 'stdin' ]
291301

292-
container: python:${{ matrix.python-version }}
293-
302+
container:
303+
image: python:${{ matrix.python-version }}
304+
volumes:
305+
- ${{ github.workspace }}:${{ github.workspace }}
306+
options: --workdir ${{ github.workspace }}
294307
steps:
295308
- uses: actions/checkout@v4
296309

hwilib/devices/keepkey.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -160,8 +160,7 @@ def __init__(self, path: str, password: Optional[str] = None, expert: bool = Fal
160160
if path.startswith("udp"):
161161
model.default_mapping.register(KeepkeyDebugLinkState)
162162

163-
super(KeepkeyClient, self).__init__(path, password, expert, chain, KEEPKEY_HID_IDS, KEEPKEY_WEBUSB_IDS, KEEPKEY_SIMULATOR_PATH, model)
164-
self.type = 'Keepkey'
163+
super(KeepkeyClient, self).__init__(path, password, expert, chain, KEEPKEY_HID_IDS, KEEPKEY_WEBUSB_IDS, KEEPKEY_SIMULATOR_PATH, model, device_type="Keepkey")
165164

166165
def can_sign_taproot(self) -> bool:
167166
"""

hwilib/devices/onekey.py

Lines changed: 16 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,14 @@
55
"""
66

77

8-
import sys
98
from ..common import Chain
109
from ..errors import (
1110
DEVICE_NOT_INITIALIZED,
1211
DeviceNotReadyError,
1312
common_err_msgs,
1413
handle_errors,
1514
)
16-
from .trezorlib import protobuf, debuglink
15+
from .trezorlib import protobuf
1716
from .trezorlib.transport import (
1817
udp,
1918
webusb,
@@ -35,6 +34,7 @@
3534
Optional,
3635
Sequence,
3736
)
37+
import copy
3838

3939
py_enumerate = enumerate # Need to use the enumerate built-in but there's another function already named that
4040

@@ -223,8 +223,8 @@ def __init__(
223223
self.serial_no = serial_no
224224
self.boardloader_version = boardloader_version
225225

226-
227-
DEFAULT_MAPPING.register(OnekeyFeatures)
226+
ONEKEY_MAPPING = copy.deepcopy(DEFAULT_MAPPING)
227+
ONEKEY_MAPPING.register(OnekeyFeatures)
228228

229229
USB_IDS = {(0x1209, 0x4F4A), (0x1209, 0x4F4B), }
230230

@@ -233,15 +233,15 @@ def __init__(
233233
minimum_version=(2, 11, 0),
234234
vendors=VENDORS,
235235
usb_ids=USB_IDS,
236-
default_mapping=DEFAULT_MAPPING,
236+
default_mapping=ONEKEY_MAPPING,
237237
)
238238

239239
ONEKEY_TOUCH = TrezorModel(
240240
name="T",
241241
minimum_version=(4, 2, 0),
242242
vendors=VENDORS,
243243
usb_ids=USB_IDS,
244-
default_mapping=DEFAULT_MAPPING,
244+
default_mapping=ONEKEY_MAPPING,
245245
)
246246

247247
ONEKEYS = (ONEKEY_LEGACY, ONEKEY_TOUCH)
@@ -256,29 +256,14 @@ def model_by_name(name: str) -> Optional[TrezorModel]:
256256

257257
# ===============overwrite methods for onekey device begin============
258258

259+
def retrieval_version(self: object):
260+
version = (*map(int, self.features.onekey_version.split(".")), )
261+
return version
259262

260-
def _refresh_features(self: object, features: Features) -> None:
261-
"""Update internal fields based on passed-in Features message."""
262-
if not self.model:
263-
self.model = model_by_name(features.model or "1")
264-
if self.model is None:
265-
raise RuntimeError("Unsupported OneKey model")
266-
267-
if features.vendor not in self.model.vendors:
268-
raise RuntimeError("Unsupported device")
269-
self.features = features
270-
self.version = (*map(int, self.features.onekey_version.split(".")), )
271-
self.check_firmware_version(warn_only=True)
272-
if self.features.session_id is not None:
273-
self.session_id = self.features.session_id
274-
self.features.session_id = None
275-
276-
def button_request(self: object, code: Optional[int]) -> None:
277-
if not self.prompt_shown:
278-
print("Please confirm action on your OneKey device", file=sys.stderr)
279-
if not self.always_prompt:
280-
self.prompt_shown = True
281-
263+
def ensure_model(self: object, features):
264+
assert self.model is not None, "Unsupported OneKey model"
265+
# Correct the correct model
266+
self.model = model_by_name(features.model or "1")
282267

283268
# ===============overwrite methods for onekey device end============
284269

@@ -291,12 +276,9 @@ def __init__(
291276
expert: bool = False,
292277
chain: Chain = Chain.MAIN,
293278
) -> None:
294-
super().__init__(path, password, expert, chain, webusb_ids=USB_IDS, sim_path=ONEKEY_EMULATOR_PATH)
295-
self.client._refresh_features = MethodType(_refresh_features, self.client)
296-
if not isinstance(self.client.ui, debuglink.DebugUI):
297-
self.client.ui.button_request = MethodType(button_request, self.client.ui)
298-
self.type = "OneKey"
299-
279+
super().__init__(path, password, expert, chain, webusb_ids=USB_IDS, sim_path=ONEKEY_EMULATOR_PATH, model=ONEKEY_LEGACY, device_type="OneKey")
280+
self.client.retrieval_version = MethodType(retrieval_version, self.client)
281+
self.client.ensure_model = MethodType(ensure_model, self.client)
300282

301283
def enumerate(
302284
password: Optional[str] = None, expert: bool = False, chain: Chain = Chain.MAIN

hwilib/devices/trezor.py

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -229,16 +229,17 @@ def get_word(type: messages.WordRequestType) -> str:
229229

230230

231231
class PassphraseUI:
232-
def __init__(self, passphrase: str) -> None:
232+
def __init__(self, passphrase: str, device_type: str) -> None:
233233
self.passphrase = passphrase
234234
self.pinmatrix_shown = False
235235
self.prompt_shown = False
236236
self.always_prompt = False
237237
self.return_passphrase = True
238+
self.device_type = device_type
238239

239240
def button_request(self, code: Optional[int]) -> None:
240241
if not self.prompt_shown:
241-
print("Please confirm action on your Trezor device", file=sys.stderr)
242+
print(f"Please confirm action on your {self.device_type} device", file=sys.stderr)
242243
if not self.always_prompt:
243244
self.prompt_shown = True
244245

@@ -288,7 +289,8 @@ def __init__(
288289
hid_ids: Set[Tuple[int, int]] = HID_IDS,
289290
webusb_ids: Set[Tuple[int, int]] = WEBUSB_IDS,
290291
sim_path: str = SIMULATOR_PATH,
291-
model: Optional[TrezorModel] = None
292+
model: Optional[TrezorModel] = None,
293+
device_type: str = "Trezor"
292294
) -> None:
293295
if password is None:
294296
password = ""
@@ -301,14 +303,14 @@ def __init__(
301303
self.simulator = True
302304
self.client.use_passphrase(password)
303305
else:
304-
self.client = Trezor(transport=transport, ui=PassphraseUI(password), model=model, _init_device=False)
306+
self.client = Trezor(transport=transport, ui=PassphraseUI(password, device_type), model=model, _init_device=False)
305307

306308
# if it wasn't able to find a client, throw an error
307309
if not self.client:
308310
raise IOError("no Device")
309311

310312
self.password = password
311-
self.type = 'Trezor'
313+
self.type = device_type
312314

313315
def _prepare_device(self) -> None:
314316
self.coin_name = 'Bitcoin' if self.chain == Chain.MAIN else 'Testnet'

hwilib/devices/trezorlib/client.py

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
import logging
1818
import os
1919
import warnings
20-
from typing import TYPE_CHECKING, Any, Optional
20+
from typing import TYPE_CHECKING, Any, Optional, Tuple
2121

2222
from mnemonic import Mnemonic
2323

@@ -259,25 +259,31 @@ def call(self, msg: "MessageType", check_fw: bool = True) -> "MessageType":
259259
raise exceptions.TrezorFailure(resp)
260260
else:
261261
return resp
262+
263+
def retrieval_version(self) -> Tuple[int, int, int]:
262264

263-
def _refresh_features(self, features: messages.Features) -> None:
264-
"""Update internal fields based on passed-in Features message."""
265-
265+
version = (
266+
self.features.major_version,
267+
self.features.minor_version,
268+
self.features.patch_version,
269+
)
270+
return version
271+
272+
def ensure_model(self, features):
266273
if not self.model:
267274
# Trezor Model One bootloader 1.8.0 or older does not send model name
268275
self.model = models.by_name(features.model or "1")
269276
if self.model is None:
270277
raise RuntimeError("Unsupported Trezor model")
278+
279+
def _refresh_features(self, features: messages.Features) -> None:
280+
"""Update internal fields based on passed-in Features message."""
271281

282+
self.ensure_model(features)
272283
if features.vendor not in self.model.vendors:
273284
raise RuntimeError("Unsupported device")
274-
275285
self.features = features
276-
self.version = (
277-
self.features.major_version,
278-
self.features.minor_version,
279-
self.features.patch_version,
280-
)
286+
self.version = self.retrieval_version()
281287
self.check_firmware_version(warn_only=True)
282288
if self.features.session_id is not None:
283289
self.session_id = self.features.session_id

test/README.md

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -80,12 +80,17 @@ $ pipenv run script/cibuild
8080

8181
### Dependencies
8282

83-
In order to build the Onekey emulator, the [Nix](https://nixos.org) will need to be installed:
83+
In order to build the Onekey emulator, the following packages will need to be installed:
8484

8585
```
86-
sh <(curl -L https://nixos.org/nix/install)
86+
build-essential curl git python3 python3-pip libsdl2-dev libsdl2-image-dev gcc-arm-none-eabi libnewlib-arm-none-eabi gcc-multilib
8787
```
8888

89+
For onekey Touch `Rust` needs to be installed:
90+
91+
```
92+
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
93+
```
8994
### Building
9095

9196
Clone the repository:
@@ -95,20 +100,26 @@ $ git clone --recursive https://github.com/OneKeyHQ/firmware.git onekey-firmware
95100
```
96101

97102
For the Onekey Legacy firmware emulator:
103+
98104
```
99105
$ git checkout bixin_dev
100106
$ cd onekey-firmware
101-
$ nix-shell
102107
$ poetry install
103108
$ export EMULATOR=1 DEBUG_LINK=1
104109
$ poetry run script/setup
105110
$ poetry run script/cibuild
106111
```
112+
107113
For the Onekey Touch emulator:
114+
108115
```
109-
$ git checkout touch
116+
$ rustup update
117+
$ rustup toolchain uninstall nightly
118+
$ rustup toolchain install nightly
119+
$ rustup default nightly
110120
$ cd onekey-firmware
111-
$ nix-shell
121+
$ git checkout touch
122+
$ git submodule update --init --recursive vendor/lvgl_mp
112123
$ poetry install
113124
$ cd core
114125
$ poetry run make build_unix

test/test_onekey.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
from hwilib.devices.trezorlib.transport.udp import UdpTransport
1616
from hwilib.devices.trezorlib.debuglink import TrezorClientDebugLink, load_device_by_mnemonic
1717
from hwilib.devices.trezorlib import device
18-
from hwilib.devices.onekey import _refresh_features
18+
from hwilib.devices.onekey import retrieval_version, ensure_model, ONEKEY_LEGACY
1919
from test_device import (
2020
Bitcoind,
2121
DeviceEmulator,
@@ -86,8 +86,9 @@ def start(self):
8686
time.sleep(1)
8787
# Setup the emulator
8888
wirelink = UdpTransport.enumerate("127.0.0.1:54935")[0]
89-
client = TrezorClientDebugLink(wirelink)
90-
client._refresh_features = MethodType(_refresh_features, client)
89+
client = TrezorClientDebugLink(wirelink, model=ONEKEY_LEGACY)
90+
client.retrieval_version = MethodType(retrieval_version, client)
91+
client.ensure_model = MethodType(ensure_model, client)
9192
client.init_device()
9293
device.wipe(client)
9394
load_device_by_mnemonic(client=client, mnemonic='alcohol woman abuse must during monitor noble actual mixed trade anger aisle', pin='', passphrase_protection=False, label='test') # From Trezor device tests

0 commit comments

Comments
 (0)