Skip to content

Commit

Permalink
ci: add onekey device
Browse files Browse the repository at this point in the history
  • Loading branch information
somebodyLi committed Dec 6, 2023
1 parent 80ca9df commit d7df45f
Show file tree
Hide file tree
Showing 8 changed files with 86 additions and 65 deletions.
9 changes: 8 additions & 1 deletion .github/actions/install-sim/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,14 @@ runs:
apt-get update
apt-get install -y libsdl2-image-2.0-0 libusb-1.0-0
tar -xvf trezor-firmware.tar.gz
- if: startsWith(inputs.device, 'onekey-')
shell: bash
run: |
apt-get update
apt-get install -y libsdl2-image-2.0-0 libusb-1.0-0
tar -xvf onekey-firmware.tar.gz
- if: inputs.device == 'coldcard'
shell: bash
run: |
Expand Down
23 changes: 18 additions & 5 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ jobs:
hwilib/devices/__init__.py
hwilib/devices/keepkey.py
hwilib/devices/ledger.py
hwilib/devices/onekey.py
hwilib/devices/trezor.py
hwilib/errors.py
hwilib/_script.py
Expand Down Expand Up @@ -153,7 +154,9 @@ jobs:
- { name: 'jade', archive: 'jade', paths: 'test/work/jade/simulator' }
- { name: 'ledger', archive: 'speculos', paths: 'test/work/speculos' }
- { name: 'keepkey', archive: 'keepkey-firmware', paths: 'test/work/keepkey-firmware/bin' }

- { name: 'onekey-1', archive: 'onekey-firmware', paths: 'test/work/onekey-firmware' }
- { name: 'onekey-t', archive: 'onekey-firmware', paths: 'test/work/onekey-firmware' }

steps:
- uses: actions/checkout@v4

Expand Down Expand Up @@ -218,6 +221,8 @@ jobs:
- 'ledger'
- 'ledger-legacy'
- 'keepkey'
- 'onekey-1'
- 'onekey-t'
script:
- name: 'Wheel'
install: 'pip install dist/*.whl'
Expand All @@ -232,8 +237,11 @@ jobs:
env:
DEVICE: '--${{ matrix.device }}'

container: python:${{ matrix.python-version }}

container:
image: python:${{ matrix.python-version }}
volumes:
- ${{ github.workspace }}:${{ github.workspace }}
options: --workdir ${{ github.workspace }}
steps:
- uses: actions/checkout@v4

Expand Down Expand Up @@ -286,10 +294,15 @@ jobs:
- 'ledger'
- 'ledger-legacy'
- 'keepkey'
- 'onekey-1'
- 'onekey-t'
interface: [ 'library', 'cli', 'stdin' ]

container: python:${{ matrix.python-version }}

container:
image: python:${{ matrix.python-version }}
volumes:
- ${{ github.workspace }}:${{ github.workspace }}
options: --workdir ${{ github.workspace }}
steps:
- uses: actions/checkout@v4

Expand Down
3 changes: 1 addition & 2 deletions hwilib/devices/keepkey.py
Original file line number Diff line number Diff line change
Expand Up @@ -159,8 +159,7 @@ def __init__(self, path: str, password: Optional[str] = None, expert: bool = Fal
if path.startswith("udp"):
model.default_mapping.register(KeepkeyDebugLinkState)

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

def can_sign_taproot(self) -> bool:
"""
Expand Down
50 changes: 16 additions & 34 deletions hwilib/devices/onekey.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,14 @@
"""


import sys
from ..common import Chain
from ..errors import (
DEVICE_NOT_INITIALIZED,
DeviceNotReadyError,
common_err_msgs,
handle_errors,
)
from .trezorlib import protobuf, debuglink
from .trezorlib import protobuf
from .trezorlib.transport import (
udp,
webusb,
Expand All @@ -35,6 +34,7 @@
Optional,
Sequence,
)
import copy

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

Expand Down Expand Up @@ -223,8 +223,8 @@ def __init__(
self.serial_no = serial_no
self.boardloader_version = boardloader_version


DEFAULT_MAPPING.register(OnekeyFeatures)
ONEKEY_MAPPING = copy.deepcopy(DEFAULT_MAPPING)
ONEKEY_MAPPING.register(OnekeyFeatures)

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

Expand All @@ -233,15 +233,15 @@ def __init__(
minimum_version=(2, 11, 0),
vendors=VENDORS,
usb_ids=USB_IDS,
default_mapping=DEFAULT_MAPPING,
default_mapping=ONEKEY_MAPPING,
)

ONEKEY_TOUCH = TrezorModel(
name="T",
minimum_version=(4, 2, 0),
vendors=VENDORS,
usb_ids=USB_IDS,
default_mapping=DEFAULT_MAPPING,
default_mapping=ONEKEY_MAPPING,
)

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

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

def retrieval_version(self: object):
version = (*map(int, self.features.onekey_version.split(".")), )
return version

def _refresh_features(self: object, features: Features) -> None:
"""Update internal fields based on passed-in Features message."""
if not self.model:
self.model = model_by_name(features.model or "1")
if self.model is None:
raise RuntimeError("Unsupported OneKey model")

if features.vendor not in self.model.vendors:
raise RuntimeError("Unsupported device")
self.features = features
self.version = (*map(int, self.features.onekey_version.split(".")), )
self.check_firmware_version(warn_only=True)
if self.features.session_id is not None:
self.session_id = self.features.session_id
self.features.session_id = None

def button_request(self: object, code: Optional[int]) -> None:
if not self.prompt_shown:
print("Please confirm action on your OneKey device", file=sys.stderr)
if not self.always_prompt:
self.prompt_shown = True

def ensure_model(self: object, features):
assert self.model is not None, "Unsupported OneKey model"
# Correct the correct model
self.model = model_by_name(features.model or "1")

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

Expand All @@ -291,12 +276,9 @@ def __init__(
expert: bool = False,
chain: Chain = Chain.MAIN,
) -> None:
super().__init__(path, password, expert, chain, webusb_ids=USB_IDS, sim_path=ONEKEY_EMULATOR_PATH)
self.client._refresh_features = MethodType(_refresh_features, self.client)
if not isinstance(self.client.ui, debuglink.DebugUI):
self.client.ui.button_request = MethodType(button_request, self.client.ui)
self.type = "OneKey"

super().__init__(path, password, expert, chain, webusb_ids=USB_IDS, sim_path=ONEKEY_EMULATOR_PATH, model=ONEKEY_LEGACY, device_type="OneKey")
self.client.retrieval_version = MethodType(retrieval_version, self.client)
self.client.ensure_model = MethodType(ensure_model, self.client)

def enumerate(
password: Optional[str] = None, expert: bool = False, chain: Chain = Chain.MAIN
Expand Down
12 changes: 7 additions & 5 deletions hwilib/devices/trezor.py
Original file line number Diff line number Diff line change
Expand Up @@ -229,16 +229,17 @@ def get_word(type: messages.WordRequestType) -> str:


class PassphraseUI:
def __init__(self, passphrase: str) -> None:
def __init__(self, passphrase: str, device_type: str) -> None:
self.passphrase = passphrase
self.pinmatrix_shown = False
self.prompt_shown = False
self.always_prompt = False
self.return_passphrase = True
self.device_type = device_type

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

Expand Down Expand Up @@ -288,7 +289,8 @@ def __init__(
hid_ids: Set[Tuple[int, int]] = HID_IDS,
webusb_ids: Set[Tuple[int, int]] = WEBUSB_IDS,
sim_path: str = SIMULATOR_PATH,
model: Optional[TrezorModel] = None
model: Optional[TrezorModel] = None,
device_type: str = "Trezor"
) -> None:
if password is None:
password = ""
Expand All @@ -301,14 +303,14 @@ def __init__(
self.simulator = True
self.client.use_passphrase(password)
else:
self.client = Trezor(transport=transport, ui=PassphraseUI(password), model=model, _init_device=False)
self.client = Trezor(transport=transport, ui=PassphraseUI(password, device_type), model=model, _init_device=False)

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

self.password = password
self.type = 'Trezor'
self.type = device_type

def _prepare_device(self) -> None:
self.coin_name = 'Bitcoin' if self.chain == Chain.MAIN else 'Testnet'
Expand Down
26 changes: 16 additions & 10 deletions hwilib/devices/trezorlib/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
import logging
import os
import warnings
from typing import TYPE_CHECKING, Any, Optional
from typing import TYPE_CHECKING, Any, Optional, Tuple

from mnemonic import Mnemonic

Expand Down Expand Up @@ -259,25 +259,31 @@ def call(self, msg: "MessageType", check_fw: bool = True) -> "MessageType":
raise exceptions.TrezorFailure(resp)
else:
return resp

def retrieval_version(self) -> Tuple[int, int, int]:

def _refresh_features(self, features: messages.Features) -> None:
"""Update internal fields based on passed-in Features message."""

version = (
self.features.major_version,
self.features.minor_version,
self.features.patch_version,
)
return version

def ensure_model(self, features):
if not self.model:
# Trezor Model One bootloader 1.8.0 or older does not send model name
self.model = models.by_name(features.model or "1")
if self.model is None:
raise RuntimeError("Unsupported Trezor model")

def _refresh_features(self, features: messages.Features) -> None:
"""Update internal fields based on passed-in Features message."""

self.ensure_model(features)
if features.vendor not in self.model.vendors:
raise RuntimeError("Unsupported device")

self.features = features
self.version = (
self.features.major_version,
self.features.minor_version,
self.features.patch_version,
)
self.version = self.retrieval_version()
self.check_firmware_version(warn_only=True)
if self.features.session_id is not None:
self.session_id = self.features.session_id
Expand Down
21 changes: 16 additions & 5 deletions test/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,12 +80,17 @@ $ pipenv run script/cibuild

### Dependencies

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

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

For onekey Touch `Rust` needs to be installed:

```
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
```
### Building

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

For the Onekey Legacy firmware emulator:

```
$ git checkout bixin_dev
$ cd onekey-firmware
$ nix-shell
$ poetry install
$ export EMULATOR=1 DEBUG_LINK=1
$ poetry run script/setup
$ poetry run script/cibuild
```

For the Onekey Touch emulator:

```
$ git checkout touch
$ rustup update
$ rustup toolchain uninstall nightly
$ rustup toolchain install nightly
$ rustup default nightly
$ cd onekey-firmware
$ nix-shell
$ git checkout touch
$ git submodule update --init --recursive vendor/lvgl_mp
$ poetry install
$ cd core
$ poetry run make build_unix
Expand Down
7 changes: 4 additions & 3 deletions test/test_onekey.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
from hwilib.devices.trezorlib.transport.udp import UdpTransport
from hwilib.devices.trezorlib.debuglink import TrezorClientDebugLink, load_device_by_mnemonic
from hwilib.devices.trezorlib import device
from hwilib.devices.onekey import _refresh_features
from hwilib.devices.onekey import retrieval_version, ensure_model, ONEKEY_LEGACY
from test_device import (
Bitcoind,
DeviceEmulator,
Expand Down Expand Up @@ -86,8 +86,9 @@ def start(self):
time.sleep(1)
# Setup the emulator
wirelink = UdpTransport.enumerate("127.0.0.1:54935")[0]
client = TrezorClientDebugLink(wirelink)
client._refresh_features = MethodType(_refresh_features, client)
client = TrezorClientDebugLink(wirelink, model=ONEKEY_LEGACY)
client.retrieval_version = MethodType(retrieval_version, client)
client.ensure_model = MethodType(ensure_model, client)
client.init_device()
device.wipe(client)
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
Expand Down

0 comments on commit d7df45f

Please sign in to comment.