Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
.directory
.vscode



Expand Down
34 changes: 34 additions & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
{
"version": "0.2.0",
"configurations": [
{
"name": "demo",
"type": "debugpy",
"request": "launch",
"program": "demo.py",
"console": "integratedTerminal",
"justMyCode": false,
"preLaunchTask": "Poetry Install"
},
{
"name": "Python: Pytest",
"type": "debugpy",
"request": "launch",
"module": "pytest",
"args": [
"-vvv",
"--log-cli-level=0"
],
"console": "integratedTerminal",
"preLaunchTask": "Poetry Install"
},
{
"name": "Python: Current File",
"type": "debugpy",
"request": "launch",
"program": "${file}",
"console": "integratedTerminal",
"preLaunchTask": "Poetry Install"
}
]
}
41 changes: 41 additions & 0 deletions .vscode/tasks.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
{
"version": "2.0.0",
"tasks": [
{
"label": "Install Poetry",
"type": "process",
"command": "${workspaceFolder}/.venv/bin/python",
"windows": {
"command": "${workspaceFolder}\\.venv\\Scripts\\python.exe"
},
"args": [
"-m",
"pip",
"install",
"poetry"
],
"options": {
"cwd": "${workspaceFolder}"
},
"problemMatcher": []
},
{
"label": "Poetry Install",
"type": "process",
"command": "${workspaceFolder}/.venv/bin/python",
"windows": {
"command": "${workspaceFolder}\\.venv\\Scripts\\python.exe"
},
"args": [
"-m",
"poetry",
"install"
],
"options": {
"cwd": "${workspaceFolder}"
},
"problemMatcher": [],
"dependsOn": "Install Poetry"
}
]
}
20 changes: 20 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Agent Guidelines

- Never use `getattr` or `setattr`.
- Use type hints.
- Write clean code. If you're writing many `if` statements, you're probably doing it wrong.
- Avoid keyword-only `*` in method/function signatures unless explicitly requested.
- Before you commit, run pre-commit `ruff-format`, then commit and push the changes (use a dedicated branch for each session). If pre-commit returns errors, fix them. For pre-commit to work, `cd` into the current project and activate the environment.
- Ensure git hooks can resolve `python`: run commit/pre-commit commands with the project venv first on `PATH`, e.g. `PATH=\"$(poetry env info -p)/bin:$PATH\" poetry run pre-commit run ruff-format --files <files>` and `PATH=\"$(poetry env info -p)/bin:$PATH\" git commit -m \"<message>\"`.

## App Run + GUI Interaction Notes

- Launch the app demo with `DISPLAY=desktop:0 poetry run python demo.py`.
- For GUI pytests where you want to see windows on the running X server, force the display and Qt platform:
- `DISPLAY=desktop:0 QT_QPA_PLATFORM=xcb poetry run pytest <test> -vv -s`
- Screenshot the X server via a tiny PyQt6 script using `QGuiApplication` + `primaryScreen().grabWindow(0)`.
- Best practice for clicks:
- Ensure window focus (`xdotool windowactivate --sync <id>`).
- Use `--clearmodifiers` and/or explicit `mousedown`/`mouseup`.
- If a button ignores clicks, try small coordinate offsets or absolute screen coords.
- Keyboard fallback: tab to focus, then `Return` or `space`.
4 changes: 1 addition & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@
* This provides an abstraction layer ontop of hwi, such that only bdk is needed from the outside
* Supported are
- Coldcard, Coldcard Q, Bitbox02, Blockstream Jade, Trezor Safe, Foundation Passport, Keystone, Ledger, Specter DIY
- Blockstream Jade can be connected via USB and Bluetooth


* It also provides
- AddressTypes, which are the commonly used bitcoin output descriptor templates
- seed_tools.derive_spk_provider to derive xpubs from seeds for all AddressTypes (bdk does not support multisig templates currently https://github.com/bitcoindevkit/bdk/issues/1020)
- SoftwareSigner which can sign single and multisig PSBTs, this doesn't do any security checks, so only use it on testnet
- HWIQuick to list the connected devices without the need to unlock them (this however only works with all devices after initialization)


### Demo
Expand Down Expand Up @@ -67,5 +67,3 @@ pre-commit install
```shell
pre-commit run --all-files
```


23 changes: 18 additions & 5 deletions bitcoin_usb/device.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
from hwilib.devices.bitbox02 import Bitbox02Client, CLINoiseConfig
from hwilib.devices.bitbox02_lib import bitbox02
from hwilib.devices.bitbox02_lib.communication import devices as bitbox02devices
from hwilib.devices.jadepy.jade import DEFAULT_BLE_DEVICE_NAME
from hwilib.devices.trezor import TrezorClient
from hwilib.hwwclient import HardwareWalletClient
from hwilib.psbt import PSBT
Expand All @@ -27,6 +28,7 @@

from bitcoin_usb.dialogs import Worker
from bitcoin_usb.i18n import translate
from bitcoin_usb.jade_ble_client import JadeBleClient
from bitcoin_usb.util import run_device_task, run_script

from .address_types import (
Expand Down Expand Up @@ -284,11 +286,22 @@ def write_down_seed_ask_until_success(cls, client: Bitbox02Client) -> bool | Non
return False

def _init_client(self):
self.client = hwi_commands.get_client(
device_type=self.selected_device["type"],
device_path=self.selected_device["path"],
chain=bdknetwork_to_chain(self.network),
)
if (
self.selected_device.get("type") == "jade"
and self.selected_device.get("transport") == "bluetooth"
):
self.client = JadeBleClient(
device_name=self.selected_device.get("bluetooth_name", DEFAULT_BLE_DEVICE_NAME),
serial_number=self.selected_device.get("bluetooth_serial_number"),
device_address=self.selected_device.get("bluetooth_address"),
chain=bdknetwork_to_chain(self.network),
)
else:
self.client = hwi_commands.get_client(
device_type=self.selected_device["type"],
device_path=self.selected_device["path"],
chain=bdknetwork_to_chain(self.network),
)

if isinstance(self.client, TrezorClient):
self.client.client.refresh_features()
Expand Down
Loading