Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
191 commits
Select commit Hold shift + click to select a range
6447ace
feat(transport): add DylibTransport for in-process libkkemu testing
BitHighlander Apr 26, 2026
2add091
test(dylib): screenshot regression for ringbuf capacity + canvas sema…
BitHighlander Apr 27, 2026
d4eda86
fix(dylib): address PR #14 review — strip-? consistency, narrow KK_TR…
BitHighlander Apr 27, 2026
e88ff15
feat(zcash): seed_fingerprint client + tests
BitHighlander Apr 29, 2026
69d28d6
test(zcash): split helper tests + cover client wrappers
BitHighlander Apr 29, 2026
1688716
Merge pull request #15 from BitHighlander/feat/zcash-seed-fingerprint
BitHighlander Apr 29, 2026
3335e6f
chore: defer planning test gates
BitHighlander Apr 30, 2026
a39dad4
test(eth): regression for EIP-1559 chunked-data signing bug (firmware…
BitHighlander Apr 28, 2026
7ecc099
test(eth): drop requires_message gate that probe-skips this test
BitHighlander Apr 28, 2026
cc0f4ae
test(ci): install pycryptodome so eth-utils.keccak has a backend
BitHighlander Apr 28, 2026
61ea6ab
test(eth): drop msg arg from assertEqual (custom 2-arg overload)
BitHighlander Apr 28, 2026
43e3b54
test(eth): gate EIP-1559 chunked-data regression on firmware 7.14.1+
BitHighlander Apr 29, 2026
fcdf6bf
release: python-keepkey 7.14.1
BitHighlander Apr 30, 2026
38b57f7
feat: add message-signing protocol bindings
BitHighlander Apr 30, 2026
297cba3
feat(7.14.2): XRP THORChain memo support + EVM depositWithExpiry reco…
BitHighlander May 15, 2026
bf870e6
Merge pull request #18 from BitHighlander/release/7.14.2-python-keepkey
BitHighlander May 15, 2026
eee4804
feat(hive): add Hive blockchain support (#19)
BitHighlander May 24, 2026
04119f3
fix(hive): regenerate messages_hive_pb2.py with old-style descriptor …
BitHighlander May 24, 2026
e338df0
fix(tests): port alpha CI test fixes to feature/hive baseline
BitHighlander May 24, 2026
4e7034e
test: skip legacy sighash test — firmware requires full tx digests
BitHighlander May 24, 2026
e717f10
test: skip all legacy sighash PCZT tests — firmware requires full tx …
BitHighlander May 24, 2026
e3fb2ff
test(hive): vendored SLIP-0048 multi-key + account-op device tests
BitHighlander Jun 26, 2026
e9e4a2e
test(hive): parse serialized_tx and bind every field by position
BitHighlander Jun 26, 2026
7388ae2
Merge pull request #21 from BitHighlander/feat/hive-tests
BitHighlander Jun 26, 2026
8ac46ac
test(hive): drop unsupported msg arg from assertEqual calls
BitHighlander Jun 27, 2026
ae1d39c
Merge pull request #22 from BitHighlander/fix/hive-tests-assertequal
BitHighlander Jun 27, 2026
bdfb2d1
test(insight): EVM clear-signing metadata vectors + tx-hash binding t…
BitHighlander Jun 29, 2026
2acc77f
test(insight): activate clear-signing tests on 7.15.0 + expand report…
BitHighlander Jun 29, 2026
ce4bdca
Merge pull request #24 from BitHighlander/feat/insight-clearsign-tests
BitHighlander Jun 29, 2026
206114d
test(eth): signing-guard regression tests (EIP-1559 consistency + con…
BitHighlander Jun 29, 2026
027146f
test(0x): enable AdvancedMode for transformERC20 blind-sign
BitHighlander Jun 29, 2026
a79ce7b
Merge pull request #26 from BitHighlander/fix/0x-transformerc20-advan…
BitHighlander Jun 29, 2026
531756a
test(eth): transformERC20 needs AdvancedMode at 7.15 (calldata > init…
BitHighlander Jun 29, 2026
79ff6b1
test(eth): transformERC20 clear-signs without AdvancedMode (revert 7.…
BitHighlander Jun 29, 2026
3a72308
Merge pull request #25 from BitHighlander/test/eth-signing-guards-master
BitHighlander Jun 29, 2026
5a08e0c
Merge remote-tracking branch 'upstream/master' into HEAD
BitHighlander Jun 30, 2026
452ca98
test(thor): point eth swap/add-liquidity vectors at the firmware-pinn…
BitHighlander Jun 29, 2026
88da246
feat(clearsign): LoadClearsignSigner trust path + release-protocol pr…
BitHighlander Jul 2, 2026
c346561
test/report: fix stale zcash tests + report generator for the 7.15 PDF
BitHighlander Jul 2, 2026
b46a8b0
test: skip emulator-only uniswap approve; alias injection cases; repo…
BitHighlander Jul 2, 2026
8e8ee81
test: requires_message probe no longer false-skips required-field mes…
BitHighlander Jul 2, 2026
e152271
feat(clearsign): human-readable who/what/why — STRING + TOKEN_AMOUNT …
BitHighlander Jul 2, 2026
0ab2bea
test(clearsign): full hex-free flow suite — all 7 real-world payloads
BitHighlander Jul 2, 2026
6680f8f
feat(clearsign): CLEARSIGN_FLOWS catalog — python-keepkey as the comp…
BitHighlander Jul 2, 2026
683e247
fix(test): batch assert — KeepKeyTest.assertEqual has no msg param
BitHighlander Jul 2, 2026
2b924bf
feat(clearsign): 51-flow reference catalog — 50+ real tx types, hex-free
BitHighlander Jul 2, 2026
1545299
fix(report): Decode line renders real values (scaled amounts, 0x.. ad…
BitHighlander Jul 2, 2026
e728e31
fix(bip85): gate tests on 7.15.0, not 7.14.0 — BIP-85 landed in 7.15
BitHighlander Jul 2, 2026
3d20db3
report: 10 fixes to the 7.15 PDF test report
BitHighlander Jul 3, 2026
5307888
clearsign v2: static-schema serializer, tests, and PDF report coverage
BitHighlander Jul 3, 2026
4ceec46
fix(tron): gate legacy dummy-raw_data tests behind AdvancedMode
BitHighlander Jul 6, 2026
99f1e06
fix(solana): split versioned-v0 test into static-verified and ALT-opa…
BitHighlander Jul 6, 2026
3b8418b
report: 10 fixes to the 7.15 PDF test report — release-gate coverage …
BitHighlander Jul 3, 2026
c101137
fix(clearsign): shorten erc4337 innerCall disclosure to fit 32-byte v…
BitHighlander Jul 3, 2026
1674346
Merge fix/pdf-report-coverage-gaps-v3: v2 static-schema harness into …
BitHighlander Jul 7, 2026
e0587c0
test(solana): gate versioned-v0 tests on requires_firmware(7.15.0)
BitHighlander Jul 7, 2026
4c3f158
test(ton): enable AdvancedMode for TonSignTx tests
BitHighlander Jul 8, 2026
560b897
test(mayachain): use the real Maya ETH Router v4 in eth swap/liquidit…
BitHighlander Jul 8, 2026
02fa3ea
test(hive): negative coverage for SLIP-48 path enforcement + memo limit
BitHighlander Jul 15, 2026
35555d7
test(hive): role-exact path enforcement per operation
BitHighlander Jul 15, 2026
8e0607f
feat(hive): sign_message (HiveSignMessage 1614/1615) + signBuffer con…
BitHighlander Jul 15, 2026
591dba8
test(hive): drop msg arg from assertEqual (KeepKeyTest override takes…
BitHighlander Jul 15, 2026
ed8cbdf
test(hive): review follow-ups — owner' rejected for signBuffer, >128B…
BitHighlander Jul 15, 2026
a4332c7
feat(hive): sign_operations (HiveSignOperations 1616/1617) + parsed-o…
BitHighlander Jul 15, 2026
15d95ec
test(hive): oversize tx must actually exceed the 2048 proto cap
BitHighlander Jul 15, 2026
a322ef1
fix(review): regenerate bindings for the post-#36 protocol; verify Ma…
BitHighlander Jul 16, 2026
03af8d1
fix(review): reference decoder must reject 0x80 like firmware; pin co…
BitHighlander Jul 16, 2026
25df24c
test(clearsign): make the icon reference decoder exact, like the firm…
BitHighlander Jul 16, 2026
c475482
fix(review): correct the public icon contract; fix the Maya recovery …
BitHighlander Jul 16, 2026
7231374
test(hive): expect non-printable messages to be rejected
BitHighlander Jul 17, 2026
78bd6ca
test: unchecked SPL transfer/approve require AdvancedMode; native ETH…
BitHighlander Jul 17, 2026
536bfb6
test(solana): CreateAccount/SetAuthority require AdvancedMode; StakeA…
BitHighlander Jul 17, 2026
71d6086
test(maya): un-skip native signtx — digest-verified, no frozen vector…
BitHighlander Jul 18, 2026
d39ab36
test(thorchain): Avalanche router deposit clear-signs; unpinned chain…
BitHighlander Jul 18, 2026
8586652
chore(submodule): declare device-protocol from keepkey/up-release-pro…
BitHighlander Jul 18, 2026
4a1de6e
test(solana): version-gate unchecked-SPL AdvancedMode tests to 7.15.0
BitHighlander Jul 18, 2026
38acf57
test(hive): device tests for the phase-3 clear-sign op table
BitHighlander Jul 19, 2026
e98228f
test(hive): serialize assets with the wire symbols hived uses
BitHighlander Jul 21, 2026
a37a96a
Merge pull request #198 from keepkey/feat/hive-clearsign-ops-phase3
BitHighlander Jul 21, 2026
a010285
test(report): put the phase-2/3 Hive ops in SECTIONS so they get scre…
BitHighlander Jul 21, 2026
59f7c85
test(report): G36 is rejection-only — drop its screenshot hint
BitHighlander Jul 21, 2026
b257df2
test(osmosis): device tests for the confirm screens, with screenshot …
BitHighlander Jul 21, 2026
3a9e1b7
fix(osmosis): the MsgSend client path was dead code with three bugs
BitHighlander Jul 21, 2026
0594d57
test(osmosis): derive the osmo1 address from the device, not a literal
BitHighlander Jul 21, 2026
52e1fb5
test(osmosis): osmosis_get_address already returns the string
BitHighlander Jul 21, 2026
1a69741
fix(osmosis): fence MsgSend to uosmo — the firmware signs a denom it …
BitHighlander Jul 21, 2026
bf188c5
test(ethereum): cover typed-hash policy gate
BitHighlander Jul 22, 2026
87f4c1a
test(osmosis): cover direct-wire review invariants
BitHighlander Jul 22, 2026
887fb15
test(osmosis): verify denomination binding
BitHighlander Jul 23, 2026
892ae04
test(ethereum): gate typed-hash policy coverage on 7.15
BitHighlander Jul 25, 2026
9ce1aeb
fix(clearsign): reject unauthenticated signer persistence
BitHighlander Jul 25, 2026
951913f
test(zcash): enforce RC18 PCZT signing contract
BitHighlander Jul 26, 2026
a578e19
chore(rc18): repin protocol contract
BitHighlander Jul 26, 2026
737361d
test(zcash): satisfy PCZT action preflight
BitHighlander Jul 26, 2026
addc024
docs(rc18): align audit report with Orchard release
BitHighlander Jul 26, 2026
7a8ff4f
test(clearsign): opt runtime identities into advanced mode
BitHighlander Jul 30, 2026
c406a1b
test(clearsign): preserve loaded signer during flows
BitHighlander Jul 30, 2026
729bc62
test(zcash): cover NU6.3 Ironwood signing
BitHighlander Jul 30, 2026
d88a073
Merge pull request #200 from keepkey/agent/zcash-ironwood
BitHighlander Jul 30, 2026
879cdd4
feat(solana): carry verified recipient owner hints
BitHighlander Jul 31, 2026
ceb5345
test x402 payments on Solana and EVM
BitHighlander Jul 31, 2026
f2de46c
Merge pull request #202 from keepkey/agent/x402-solana-metadata
BitHighlander Jul 31, 2026
6b1999c
chore: pin portable canonical device protocol
BitHighlander Jul 31, 2026
c2eb9d9
fix(ci): gate upstream staging pull requests
BitHighlander Jul 31, 2026
98a83ef
Merge pull request #203 from keepkey/agent/pin-portable-device-protocol
BitHighlander Jul 31, 2026
bde3700
test: GetAddress returns BIP-86 taproot addresses
BitHighlander Jul 31, 2026
50b107d
test: cross-check P2TR signing against an independent implementation
BitHighlander Jul 31, 2026
2443547
test: gate taproot tests on a capability, not a firmware version
BitHighlander Aug 1, 2026
5d75801
test: cover production Taproot signing paths
BitHighlander Aug 2, 2026
e11a2a1
test(report): bind Taproot release evidence
BitHighlander Aug 2, 2026
f8311c2
fix(ci): request reviews safely for fork PRs
BitHighlander Aug 2, 2026
e6e7f56
Merge pull request #205 from BitHighlander/agent/copilot-review-fork-…
BitHighlander Aug 2, 2026
45a18d6
Merge remote-tracking branch 'upstream/reconcile/upstream-sync' into …
BitHighlander Aug 2, 2026
eac1ad2
chore: pin canonical Taproot protocol staging
BitHighlander Aug 2, 2026
b6e8f01
Merge pull request #204 from BitHighlander/agent/taproot-tests-staging
BitHighlander Aug 2, 2026
bd75235
test(taproot): prove tampered prevout rejection
BitHighlander Aug 2, 2026
9c58e67
Merge pull request #206 from BitHighlander/agent/taproot-prevout-proof
BitHighlander Aug 2, 2026
7888d7f
test(rng): prove entropy audit budget policy
BitHighlander Aug 3, 2026
7e35103
Merge pull request #207 from keepkey/agent/rc23-entropy-c27
BitHighlander Aug 3, 2026
84b4a25
test(taproot): require physical signing confirmations
BitHighlander Aug 3, 2026
7826459
Merge pull request #208 from BitHighlander/fix/taproot-confirmation-s…
BitHighlander Aug 3, 2026
cc70aa7
test(taproot): assert the serialized transaction, not just the signature
BitHighlander Aug 4, 2026
1f2eecd
report(taproot): fail the report when required tests only skip
BitHighlander Aug 4, 2026
58d4e02
feat(reset): drive and verify on-device dice-entropy collection
BitHighlander Aug 4, 2026
ee9369f
test(reset): assert an aborted reset disarms EntropyAck
BitHighlander Aug 4, 2026
a8e4126
Merge pull request #210 from keepkey/feat/dice-entropy
BitHighlander Aug 4, 2026
604cfc0
merge: sync fork master with upstream keepkey/master
BitHighlander Aug 5, 2026
b44f1b3
test(reset): display_random is accepted and ignored
BitHighlander Aug 5, 2026
74768b0
report: catalog the 7.15 seed-generation evidence and state the repor…
BitHighlander Aug 9, 2026
5761392
report: say which Zcash shielded tests never touch a device
BitHighlander Aug 9, 2026
3bbf996
test(zcash): sign a shielded transaction on an actual device
BitHighlander Aug 9, 2026
417a613
fix(report): K8 named a storage test that no longer exists
BitHighlander Aug 12, 2026
f558eef
test(eth): pin the multi-byte chain_id EIP-1559 regression
BitHighlander Aug 15, 2026
040a9e5
test(reset): version-gate the display_random tests to 7.15.0
BitHighlander Aug 15, 2026
2cf5edc
fix(review): address all four technical findings on #212
BitHighlander Aug 15, 2026
3e68c86
Merge pull request #212 from keepkey/release/7.15-pyk-reconcile
BitHighlander Aug 15, 2026
99906e4
test: cover the disclosure behaviour 7.14.2 adds
BitHighlander Aug 20, 2026
8c1492b
merge: fold the 7.14.2 disclosure tests into the 7.15 line
BitHighlander Aug 20, 2026
811520e
test(uniswap): run the liquidity tests on the emulator instead of ski…
BitHighlander Aug 21, 2026
9f3b176
Merge pull request #32 from BitHighlander/fix/unskip-uniswap-liquidity
BitHighlander Aug 21, 2026
a5effba
test(atlas): catalogue the four 7.15 areas the report could not see
BitHighlander Aug 21, 2026
295dac4
test: match four assertions to the 7.14.2 policies that superseded them
BitHighlander Aug 21, 2026
cf9dcf6
Merge pull request #33 from BitHighlander/fix/715-atlas-coverage
BitHighlander Aug 21, 2026
1530421
test: gate the bitcoin-only suite on the bitcoin-only product
BitHighlander Aug 21, 2026
1ed34a7
Merge pull request #34 from BitHighlander/fix/715-bitcoin-only-guard
BitHighlander Aug 21, 2026
32e12ab
fix(report): recognise any test_ module, not just test_msg_/test_sign…
BitHighlander Aug 21, 2026
d5560b5
Merge pull request #35 from BitHighlander/fix/715-report-module-prefix
BitHighlander Aug 21, 2026
00b4e16
test: power-cycle skips where the harness does not own the emulator
BitHighlander Aug 21, 2026
cedad1c
Merge pull request #36 from BitHighlander/fix/715-power-cycle-portabi…
BitHighlander Aug 21, 2026
19e73c8
feat(solana): KKSOLSW1 lookup-table account attestation tests
BitHighlander Aug 21, 2026
a5df311
Merge pull request #37 from BitHighlander/feat/kksolsw1-tests
BitHighlander Aug 21, 2026
169fcec
fix(tests): power-cycle helper must run on Python 3.6, which CI uses
BitHighlander Aug 21, 2026
ba05282
Merge pull request #38 from BitHighlander/fix/715-py36-power-cycle
BitHighlander Aug 21, 2026
27957e8
feat(tokens): cap the built-in token table at 500 entries — frees 23,…
BitHighlander Aug 21, 2026
a5a4025
Revert "regenerate messages_solana_pb2 with a modern protoc"
BitHighlander Aug 21, 2026
af148d8
Merge pull request #40 from BitHighlander/fix/revert-pb2-regen
BitHighlander Aug 21, 2026
1f2bd84
regen(pb2): messages_solana with the pinned image, for the KKSOLSW1 tags
BitHighlander Aug 21, 2026
eaf386a
Merge pull request #41 from BitHighlander/feat/pb2-kksolsw1-docker
BitHighlander Aug 21, 2026
ef34f35
Merge pull request #39 from BitHighlander/feat/715-token-flash-budget
BitHighlander Aug 21, 2026
c57bb85
fix(report): stop declaring screens seven tests cannot draw
BitHighlander Aug 21, 2026
1dacea4
Merge pull request #42 from BitHighlander/fix/report-screenshot-audit
BitHighlander Aug 21, 2026
178fefd
fix(report): the console summary counted the wrong skips
BitHighlander Aug 21, 2026
099a830
Merge pull request #43 from BitHighlander/fix/report-count-reconcile
BitHighlander Aug 21, 2026
668d90a
fix(report): count distinct tests, and assert the catalog is well-formed
BitHighlander Aug 21, 2026
66e5c39
Merge pull request #44 from BitHighlander/fix/catalog-structural-audit
BitHighlander Aug 21, 2026
9039762
feat(report): catalog KKSOLSW1, and make a skip fail where the featur…
BitHighlander Aug 21, 2026
bb1d77a
Merge pull request #45 from BitHighlander/feat/catalog-kksolsw1
BitHighlander Aug 21, 2026
aaa8bd9
test(I6): disabling AdvancedMode must revoke the signer, not suspend it
BitHighlander Aug 21, 2026
9fad463
Merge pull request #46 from BitHighlander/fix/advanced-mode-revokes
BitHighlander Aug 21, 2026
bfe065d
fix(atlas): E16b claimed x402 clear-signs; the test asserts it is REF…
BitHighlander Aug 21, 2026
c4563ad
Merge pull request #47 from BitHighlander/fix/atlas-e16b-tells-the-truth
BitHighlander Aug 21, 2026
e58eba5
feat(eip712): python client for the device-driven walk
BitHighlander Aug 21, 2026
3b8fde1
test(eip712): the walk signs, and its hashes match the published spec
BitHighlander Aug 21, 2026
78392bf
Merge pull request #48 from BitHighlander/feat/eip712-streaming-client
BitHighlander Aug 21, 2026
18cf383
feat(atlas): section TD, structured EIP-712, with hardware evidence
BitHighlander Aug 21, 2026
b8c4302
Merge pull request #49 from BitHighlander/feat/atlas-section-td
BitHighlander Aug 21, 2026
7ae7f0e
test(storage): argue the V20 bump, and assert 18/19 stay unreadable
BitHighlander Aug 22, 2026
fa4898d
Merge pull request #50 from BitHighlander/feat/u5-storage-v20
BitHighlander Aug 22, 2026
83c3ed2
fix(test): LAST_SHIPPED stays 17 until 7.16 actually ships
BitHighlander Aug 22, 2026
0abf93e
Merge pull request #51 from BitHighlander/fix/u5-last-shipped-stays-17
BitHighlander Aug 22, 2026
02889da
fix(tests): gate on firmware capability, not on host bindings or sour…
BitHighlander Aug 22, 2026
f9849f6
Merge pull request #52 from BitHighlander/fix/capability-gates-not-so…
BitHighlander Aug 22, 2026
388e631
fix(ci): a crashed emulator now fails in seconds instead of hanging 3…
BitHighlander Aug 22, 2026
1e3ff05
fix(ci): the emulator image is five months stale, and a test was kill…
BitHighlander Aug 22, 2026
073f2ea
fix(ci): build the emulator from source instead of pulling a stale tag
BitHighlander Aug 22, 2026
39450f3
fix(ci): do not recurse trezor-firmware's vendor tree
BitHighlander Aug 22, 2026
e294164
fix(tests): Failure_UnexpectedMessage lives in types_pb2, and run fro…
BitHighlander Aug 22, 2026
594e366
fix(tests): derive the storage version from the tree, not from one br…
BitHighlander Aug 22, 2026
006142d
Merge pull request #53 from BitHighlander/fix/ci-hang-and-false-green
BitHighlander Aug 22, 2026
7e9fe85
fix(atlas): repoint three catalog entries, and correct what they claim
BitHighlander Aug 22, 2026
999e776
Merge pull request #54 from BitHighlander/fix/atlas-storage-gate-names
BitHighlander Aug 22, 2026
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
158 changes: 140 additions & 18 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
# and runs the full python integration test suite against it.
#
# Stage 1: GATE (seconds)
# └─ lint basic Python syntax check
# └─ lint Python syntax + deterministic protocol contract tests
#
# Stage 2: TEST (gated by Stage 1)
# └─ integration full pytest suite against emulator
Expand All @@ -13,9 +13,15 @@ name: CI

on:
push:
branches: [master, develop, 'feature/**', 'fix/**', 'hotfix/**']
branches: [master, develop, reconcile/upstream-sync, 'feature/**', 'fix/**', 'hotfix/**']
pull_request:
branches: [master, develop]
branches: [master, develop, reconcile/upstream-sync]

# One run per ref: a new push supersedes the old instead of both burning a
# runner to completion.
concurrency:
group: ci-${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true

jobs:
# ═══════════════════════════════════════════════════════════
Expand All @@ -34,13 +40,26 @@ jobs:
- name: Syntax check
run: python -m py_compile keepkeylib/*.py

- name: Install contract-test dependencies
run: |
pip install "protobuf>=3.20,<4" mnemonic ecdsa pytest

- name: Run deterministic Zcash PCZT contract tests
env:
PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION: python
run: |
python -m pytest -q \
tests/test_msg_zcash_sign_pczt.py \
tests/test_zcash_seed_fingerprint_helper.py

- name: Lint summary
run: |
echo "## 🔑 KeepKey python-keepkey — Lint" >> "$GITHUB_STEP_SUMMARY"
echo "" >> "$GITHUB_STEP_SUMMARY"
echo "| Check | Status |" >> "$GITHUB_STEP_SUMMARY"
echo "|-------|--------|" >> "$GITHUB_STEP_SUMMARY"
echo "| Syntax | ✅ PASS |" >> "$GITHUB_STEP_SUMMARY"
echo "| Zcash PCZT contract | ✅ PASS |" >> "$GITHUB_STEP_SUMMARY"

# ═══════════════════════════════════════════════════════════
# STAGE 2: TEST — pull published emulator, run pytest
Expand All @@ -49,26 +68,74 @@ jobs:
integration:
needs: [lint]
runs-on: ubuntu-latest
timeout-minutes: 30
timeout-minutes: 15

services:
kkemu:
image: kktech/kkemu:latest
ports:
- 11044:11044/udp
- 11045:11045/udp
- 5000:5000
# NO published emulator image. This job BUILDS one from current firmware.
#
# It used to pull kktech/kkemu:latest -- a floating tag whose image was
# five months and six minor versions stale. That single fact caused every
# symptom we chased: 80 tests gating on requires_firmware("7.15.0") skipped
# silently, and one unskipped test drove a ctime() path that segfaults on
# the old image and does not exist in current firmware.
#
# Publishing a fresher image would only reset that clock. Building from
# source removes the class: the emulator under test is, by construction,
# the firmware the tests were written against.

steps:
- uses: actions/checkout@v4
with:
submodules: recursive
path: python-keepkey

# python-keepkey is a SUBMODULE of the firmware repo, so the firmware is
# where the emulator lives. alpha is the fork's integration branch.
- name: Checkout firmware
uses: actions/checkout@v4
with:
repository: BitHighlander/keepkey-firmware
ref: alpha
path: keepkey-firmware

# NOT `submodules: recursive`. trezor-firmware carries a micropython
# vendor tree whose lib/lwip lives on git.savannah.gnu.org, which serves
# dumb HTTP and cannot do the shallow clone actions/checkout requests --
# it fails the whole job. The firmware repo's own CI inits exactly these
# paths, non-recursively, for the same reason.
- name: Init the submodules the emulator build needs
working-directory: keepkey-firmware
run: |
git submodule update --init --depth 1 deps/crypto/trezor-firmware
git submodule update --init --depth 1 deps/device-protocol
git submodule update --init --depth 1 deps/googletest
git submodule update --init --depth 1 deps/qrenc/QR-Code-generator
git submodule update --init --depth 1 deps/sca-hardening/SecAESSTM32

# Test THIS checkout of python-keepkey, not the one the firmware pins.
- name: Overlay this python-keepkey onto the firmware tree
run: |
rm -rf keepkey-firmware/deps/python-keepkey
cp -a python-keepkey keepkey-firmware/deps/python-keepkey

- name: Build the emulator
timeout-minutes: 20
working-directory: keepkey-firmware
run: |
docker build -t kkemu-ci -f scripts/emulator/Dockerfile .

- name: Start the emulator
run: |
docker run -d --name kkemu \
-p 11044:11044/udp -p 11045:11045/udp -p 5000:5000 kkemu-ci
sleep 3
docker logs kkemu | head -5

- uses: actions/setup-python@v5
with:
python-version: '3.11'

- name: Install dependencies
working-directory: python-keepkey
run: |
pip install --upgrade pip
pip install "protobuf>=3.20,<4"
Expand All @@ -88,20 +155,67 @@ jobs:
sleep 1
done

# "The emulator answered a ping" is not "the emulator is the right
# firmware". CI ran a 7.16-era suite against a 7.10.0 image for five
# months: 80 tests gate on requires_firmware("7.15.0") and silently
# SKIPPED, while one unskipped test drove a code path that segfaults in
# 7.10.0 and is already fixed in 7.15 -- which reads as a product failure
# but is only a stale image. A floating tag cannot tell you that. This
# can, and it fails closed.
- name: Assert the emulator is not older than the suite
timeout-minutes: 2
env:
KK_TRANSPORT_MAIN: "127.0.0.1:11044"
KK_TRANSPORT_DEBUG: "127.0.0.1:11045"
KK_MIN_FW: "7.15.0"
KK_UDP_TIMEOUT: "20"
working-directory: keepkey-firmware/deps/python-keepkey/tests
run: |
python - <<'PY'
import os, sys
sys.path.insert(0, '..')
import config
from keepkeylib.client import KeepKeyDebuglinkClient
c = KeepKeyDebuglinkClient(config.TRANSPORT(*config.TRANSPORT_ARGS,
**config.TRANSPORT_KWARGS))
c.set_debuglink(config.DEBUG_TRANSPORT(*config.DEBUG_TRANSPORT_ARGS,
**config.DEBUG_TRANSPORT_KWARGS))
c.init_device()
f = c.features
got = (f.major_version, f.minor_version, f.patch_version)
floor = tuple(int(x) for x in os.environ['KK_MIN_FW'].split('.'))
print('emulator firmware %d.%d.%d, floor %s' %
(got + (os.environ['KK_MIN_FW'],)))
if got < floor:
sys.exit('FATAL: the emulator image predates the tests that run '
'against it. Republish kktech/kkemu from current '
'firmware and pin the new digest above.')
PY

# Step-level timeout, deliberately: a JOB-level timeout ends the job as
# "cancelled", which reads as an infra blip. A step timeout is a FAILURE.
- name: Run integration tests
timeout-minutes: 8
env:
KK_TRANSPORT_MAIN: "127.0.0.1:11044"
KK_TRANSPORT_DEBUG: "127.0.0.1:11045"
PYTHONPATH: "${{ github.workspace }}/keepkeylib:${{ github.workspace }}"
PYTHONPATH: "${{ github.workspace }}/keepkey-firmware/deps/python-keepkey"
# A crashed emulator now raises instead of blocking in recv() forever.
KK_UDP_TIMEOUT: "45"
run: |
cd tests
# From the OVERLAID copy, not the standalone checkout: the
# storage-version-gate tests assert against lib/firmware/storage.c,
# which they find by walking UP. Run them as a sibling of the
# firmware and they resolve; run them standalone and they fail
# claiming the sources are missing.
cd keepkey-firmware/deps/python-keepkey/tests
pytest -v --junitxml=junit.xml 2>&1 | tee pytest-output.txt
echo "${PIPESTATUS[0]}" > status

- name: Test summary
if: always()
run: |
XML="tests/junit.xml"
XML="keepkey-firmware/deps/python-keepkey/tests/junit.xml"
echo "## 🔑 KeepKey python-keepkey — Integration Tests" >> "$GITHUB_STEP_SUMMARY"
echo "" >> "$GITHUB_STEP_SUMMARY"

Expand Down Expand Up @@ -148,15 +262,23 @@ jobs:
echo "---" >> "$GITHUB_STEP_SUMMARY"
echo "*KeepKey python-keepkey CI*" >> "$GITHUB_STEP_SUMMARY"

- name: Upload test results
# NO check_name. With one, this action publishes a SEPARATE check run
# via the Checks API, and its require_tests default of 'false' means an
# absent junit.xml -- which is exactly what a killed pytest leaves behind
# -- reports conclusion:success with zero duration. That green check sat
# on top of a job timing out at 30 minutes for at least six merges.
# annotate_only keeps the inline annotations without minting a check.
- name: Annotate test results
uses: mikepenz/action-junit-report@v4
if: always()
with:
report_paths: tests/junit.xml
check_name: Integration Tests
report_paths: keepkey-firmware/deps/python-keepkey/tests/junit.xml
annotate_only: true
require_tests: true
fail_on_failure: true

- name: Fail on test failure
if: always()
run: |
STATUS=$(cat tests/status 2>/dev/null || echo "1")
STATUS=$(cat keepkey-firmware/deps/python-keepkey/tests/status 2>/dev/null || echo "1")
[ "$STATUS" = "0" ] || exit 1
6 changes: 5 additions & 1 deletion .github/workflows/copilot-review.yml
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
name: Request Copilot Review

on:
pull_request:
# This workflow never checks out or executes pull-request code. Using the
# base-repository context is therefore safe and is required for cross-fork
# PRs, whose pull_request GITHUB_TOKEN is always downgraded to read-only.
pull_request_target:
types: [opened, reopened, ready_for_review, synchronize]

jobs:
request-copilot-review:
if: github.event.pull_request.draft == false
runs-on: ubuntu-latest
permissions:
pull-requests: write
Expand Down
2 changes: 1 addition & 1 deletion .gitmodules
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[submodule "device-protocol"]
path = device-protocol
url = https://github.com/keepkey/device-protocol.git
branch = master
branch = up/release-protocol
[submodule "keepkeylib/eth/ethereum-lists"]
path = keepkeylib/eth/ethereum-lists
url = https://github.com/keepkey/ethereum-lists.git
Expand Down
2 changes: 1 addition & 1 deletion build_pb.sh
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ CURDIR=$(pwd)
cd "device-protocol"

echo "Building with protoc version: $(protoc --version)"
for i in messages messages-ethereum messages-eos messages-nano messages-cosmos messages-ripple messages-binance messages-tendermint messages-thorchain messages-osmosis messages-mayachain messages-solana messages-tron messages-ton messages-zcash types ; do
for i in messages messages-ethereum messages-eos messages-nano messages-cosmos messages-ripple messages-binance messages-hive messages-tendermint messages-thorchain messages-osmosis messages-mayachain messages-solana messages-tron messages-ton messages-zcash types ; do
protoc --python_out=$CURDIR/keepkeylib/ -I/usr/include -I. $i.proto
i=${i/-/_}
sed -i -Ee 's/^import ([^.]+_pb2)/from . import \1/' $CURDIR/keepkeylib/"$i"_pb2.py
Expand Down
81 changes: 81 additions & 0 deletions keepkeylib/clearsign_abi.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
"""
Minimal, deterministic Solidity ABI encoder for STATIC types only.

Used to build REAL calldata for the clear-sign flow catalog from a function
signature + argument values, instead of hand-typing hex (which is how bugs
get shipped in a signing test suite). Selectors are always derived from
keccak256(signature) here — never trusted from an external source — so a
wrong/hallucinated selector fails loudly instead of silently producing a
plausible-looking but wrong test vector.

Deliberately does NOT support dynamic types (string, bytes, T[], tuples with
dynamic members) — those need offset/length ABI encoding that's easy to get
subtly wrong by hand. Calls with dynamic types are hand-built at the call
site (see clearsign_catalog.py's multicall/handleOps entries) using the
primitives here (_word/_addr_word) plus an explicit comment that the layout
is a representative simplification, not a literal captured mainnet tx.
"""

from .signed_metadata import keccak256


def parse_signature(signature):
"""'supply(address,uint256,address,uint16)' -> ('supply', ['address', 'uint256', 'address', 'uint16'])"""
name, rest = signature.split('(', 1)
rest = rest.rsplit(')', 1)[0]
types = [t.strip() for t in rest.split(',')] if rest.strip() else []
return name, types


def selector(signature):
"""4-byte function selector, always computed — never trusted as input."""
return keccak256(signature.encode('ascii'))[:4]


def _word(value):
if isinstance(value, str) and value.startswith('0x'):
value = int(value, 16)
return int(value).to_bytes(32, 'big')


def _addr_word(address):
if isinstance(address, str):
address = bytes.fromhex(address[2:] if address.startswith('0x') else address)
assert len(address) == 20, 'address must be 20 bytes, got %d' % len(address)
return b'\x00' * 12 + address


def encode_static_args(types, values):
"""ABI-encode STATIC Solidity types into concatenated 32-byte words.
Raises on any dynamic type (string/bytes/arrays) — build those by hand."""
assert len(types) == len(values), (
'arg count mismatch: %d types, %d values' % (len(types), len(values)))
out = bytearray()
for typ, val in zip(types, values):
if typ == 'address':
out += _addr_word(val)
elif typ.startswith('uint') or typ.startswith('int'):
digits = typ[4:] if typ.startswith('uint') else typ[3:]
bits = int(digits) if digits else 256
n = int(val)
assert 0 <= n < (1 << bits), 'value %r out of range for %s' % (val, typ)
out += n.to_bytes(32, 'big')
elif typ == 'bool':
out += (1 if val else 0).to_bytes(32, 'big')
elif typ.startswith('bytes') and typ != 'bytes' and not typ.endswith('[]'):
n = int(typ[5:])
b = val if isinstance(val, (bytes, bytearray)) else bytes.fromhex(
val[2:] if val.startswith('0x') else val)
assert len(b) == n, 'bytes%d value has wrong length' % n
out += b.ljust(32, b'\x00') # bytesN is left-aligned per ABI spec
else:
raise ValueError(
'dynamic/unsupported type %r — build this call by hand '
'(see module docstring)' % typ)
return bytes(out)


def build_calldata(signature, values):
"""selector(signature) + ABI-encoded static args, in one call."""
_, types = parse_signature(signature)
return selector(signature) + encode_static_args(types, values)
Loading
Loading