Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
e60ce4f
ci(circleci): stop gating python-keepkey on the firmware's C++ suite
BitHighlander Aug 23, 2026
c73750c
fix(thorchain): expose version-gated send denoms
BitHighlander Aug 23, 2026
55adaad
test(thorchain): defer denom emulator coverage
BitHighlander Aug 23, 2026
be1975c
docs(osmosis): correct the uosmo restriction rationale
pastaghost Aug 23, 2026
70f3055
fix(zcash): validate the transparent signature list
pastaghost Aug 23, 2026
44d82ef
ci(bitcoin-only): actually run the product-boundary suite
pastaghost Aug 23, 2026
5f872cc
fix(tests): the display-disclosure suite was passing vacuously
BitHighlander Aug 23, 2026
34a1c6c
fix(tests): a v6 fixture must use the real empty-Orchard-bundle digest
BitHighlander Aug 23, 2026
b91d87b
test: an oversized multisig signature must be refused
BitHighlander Aug 23, 2026
3305b80
fix(signing): cover ZIP-229 and ambiguous message acks
BitHighlander Aug 23, 2026
e79c6b8
test(bitcoin-only): gate unsupported 7.15 handlers
BitHighlander Aug 26, 2026
c697a25
test(report): respect Bitcoin-only feature boundaries
BitHighlander Aug 26, 2026
b532d98
Merge pull request #220 from keepkey/fix/7.15-bitcoin-only-capability…
BitHighlander Aug 26, 2026
5b03b26
merge: reconcile 7.15 tests after 7.14.3 gate
BitHighlander Aug 27, 2026
29e47e9
Merge commit '86831be' into prepare/715-pyk-reconcile
BitHighlander Aug 27, 2026
7d32a39
ci: run reconciliation branches
BitHighlander Aug 27, 2026
c9cc7d3
merge: carry 7.14.3 report selector fix into 7.15
BitHighlander Aug 27, 2026
1067497
merge: carry canonical Solana stake authorize test into 7.15
BitHighlander Aug 27, 2026
32b7216
merge: carry screenshot audit fixes into 7.15
BitHighlander Aug 27, 2026
ba2f86f
merge: carry versioned report catalog into 7.15
BitHighlander Aug 27, 2026
eb26dcb
merge: carry report provenance inputs into 7.15
BitHighlander Aug 27, 2026
68173d8
fix(7.15): preserve session policy and valid auth fixtures
BitHighlander Aug 27, 2026
96c5805
test(7.15): align Solana wire case and OLED baselines
BitHighlander Aug 27, 2026
57642ad
ci(7.15): test against the matching firmware branch
BitHighlander Aug 27, 2026
c4fb8bf
test(7.15): assert fail-closed signing contracts
BitHighlander Aug 27, 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
29 changes: 22 additions & 7 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,10 @@ jobs:
# Move python-keepkey out of the way
mv .pykk ../

# This companion branch gates firmware PR #604, not the default
# firmware branch. Keep the target explicit and fail if it moves.
git clone --depth 1 -b release/7.14.3-bitcoin-only \
# This companion branch gates the fork's 7.15 firmware PR, not the
# default firmware branch. Keep the target explicit; the checkout
# then replaces its python-keepkey submodule with CIRCLE_SHA1 below.
git clone --depth 1 -b release/7.15 \
https://github.com/BitHighlander/keepkey-firmware.git .

# Match firmware CI's build set. A recursive init reaches optional
Expand Down Expand Up @@ -68,18 +69,32 @@ jobs:
command: |
pushd ./scripts/emulator
set +e # don’t exit on first failure
docker-compose up --build firmware-unit
docker-compose up --build python-keepkey
set -e

# Collect JUnit / pytest XML results
mkdir -p ../../test-reports
docker cp "$(docker-compose ps -q firmware-unit)":/kkemu/test-reports/. ../../test-reports/
docker cp "$(docker-compose ps -q python-keepkey)":/kkemu/test-reports/. ../../test-reports/
popd

# Fail job if either container reported non-zero status
[ "$(cat test-reports/python-keepkey/status)$(cat test-reports/firmware-unit/status)" = "00" ] || exit 1
# Fail the job on this repo's OWN result.
#
# The firmware's C++ firmware-unit suite used to run here and gated
# this job. No change in THIS repository can affect firmware C++, and
# the firmware repo already runs that suite in its own CI, so all it
# did was fail python-keepkey for reasons no python change caused: a
# token-table change cannot go green here until the matching firmware
# change reaches the branch this clones, which is a release away.
#
# Read the status file defensively -- it is written by the container,
# and a crash before it exists must FAIL rather than silently pass an
# empty-string comparison.
STATUS_FILE=test-reports/python-keepkey/status
if [ ! -f "$STATUS_FILE" ]; then
echo "no status file at $STATUS_FILE -- the suite did not finish"
exit 1
fi
[ "$(cat "$STATUS_FILE")" = "0" ] || exit 1

- store_test_results:
path: test-reports
Expand Down
218 changes: 216 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,15 @@
# └─ lint Python syntax + deterministic protocol contract tests
#
# Stage 2: TEST (gated by Stage 1)
# └─ integration full pytest suite against emulator
# ├─ integration full pytest suite against the regular emulator
# └─ integration-btc bitcoin-only product boundary against a
# -DKK_BITCOIN_ONLY=ON emulator

name: CI

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

Expand Down Expand Up @@ -322,3 +324,215 @@ jobs:
run: |
STATUS=$(cat keepkey-firmware/deps/python-keepkey/tests/status 2>/dev/null || echo "1")
[ "$STATUS" = "0" ] || exit 1

# ═══════════════════════════════════════════════════════════
# STAGE 2b: TEST — the OTHER shipping product
# ═══════════════════════════════════════════════════════════

integration-btc:
needs: [lint]
runs-on: ubuntu-latest
timeout-minutes: 15

# KK_BITCOIN_ONLY=ON is a second shipping product, not a build flavour:
# coins.def keeps only Bitcoin and Testnet, messagemap.def drops every
# altcoin handler, ZCASH_PRIVACY is forced OFF, and transaction.c takes a
# BITCOIN_ONLY arm on the OP_RETURN path.
#
# tests/test_msg_bitcoin_only_variant.py asserts all of that, and its
# setUp() calls requires_bitcoinOnly() -- so against the regular emulator
# the `integration` job runs it as ELEVEN SKIPS. Skips are green. Without
# this job the advertised bitcoin-only coverage is never executed by any
# required check, which is the exact condition that file was written to
# end. The step below therefore fails closed on a skip, not just on a
# failure.

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

- name: Checkout firmware
uses: actions/checkout@v4
with:
repository: BitHighlander/keepkey-firmware
ref: alpha
path: keepkey-firmware

# Same non-recursive init as the regular job: trezor-firmware's
# micropython vendor tree pulls lib/lwip from git.savannah.gnu.org,
# which cannot serve the shallow clone actions/checkout asks for.
- 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

- 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

# scripts/emulator/Dockerfile forwards ARG coinsupport into the cmake
# invocation, so this is the same emulator build with the product flag
# the shipping bitcoin-only image is built with.
- name: Build the bitcoin-only emulator
timeout-minutes: 20
working-directory: keepkey-firmware
run: |
docker build -t kkemu-btc-ci \
--build-arg coinsupport=-DKK_BITCOIN_ONLY=ON \
-f scripts/emulator/Dockerfile .

- name: Start the emulator
run: |
docker run -d --name kkemu-btc \
-p 11044:11044/udp -p 11045:11045/udp -p 5000:5000 kkemu-btc-ci
sleep 3
docker logs kkemu-btc | 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"
pip install -e .
pip install pytest semver rlp requests eth-keys pycryptodome

- name: Wait for emulator
run: |
echo "Waiting for emulator bridge on port 5000..."
for i in $(seq 1 30); do
if curl -sf -X POST http://localhost:5000/exchange/main \
-H 'Content-Type: application/json' \
-d '{"data":""}' > /dev/null 2>&1; then
echo "Emulator ready after ${i}s"
break
fi
sleep 1
done

# A bitcoin-only emulator that reports "Emulator" instead of
# "EmulatorBTC" makes requires_bitcoinOnly() skip the whole file, and a
# regular emulator built by a broken --build-arg does the same. Assert
# the variant BEFORE pytest so that failure is named, not silent.
- name: Assert the emulator really is the bitcoin-only product
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, variant %r' %
(got + (f.firmware_variant,)))
if got < floor:
sys.exit('FATAL: the emulator image predates the tests that run '
'against it.')
if f.firmware_variant not in ('KeepKeyBTC', 'EmulatorBTC'):
sys.exit('FATAL: firmware_variant is %r, so requires_bitcoinOnly() '
'would skip every test in this job. The -DKK_BITCOIN_ONLY=ON '
'build arg did not take effect.' % (f.firmware_variant,))
PY

- name: Run the bitcoin-only product-boundary tests
timeout-minutes: 8
env:
KK_TRANSPORT_MAIN: "127.0.0.1:11044"
KK_TRANSPORT_DEBUG: "127.0.0.1:11045"
PYTHONPATH: "${{ github.workspace }}/keepkey-firmware/deps/python-keepkey"
KK_UDP_TIMEOUT: "45"
run: |
cd keepkey-firmware/deps/python-keepkey/tests
pytest -v --junitxml=junit-btc.xml test_msg_bitcoin_only_variant.py \
2>&1 | tee pytest-btc-output.txt
echo "${PIPESTATUS[0]}" > status-btc

# The whole reason this job exists. `pytest` exits 0 on a fully skipped
# module, so a green run proves nothing unless the skip count is zero.
- name: Fail if the product-boundary tests skipped
if: always()
run: |
XML="keepkey-firmware/deps/python-keepkey/tests/junit-btc.xml"
if [ ! -f "$XML" ]; then
echo "::error::no junit-btc.xml -- the suite crashed before completion"
exit 1
fi
python3 - "$XML" <<'PY'
import sys, xml.etree.ElementTree as ET
tree = ET.parse(sys.argv[1])
cases = list(tree.iter('testcase'))
skipped = [c for c in cases if c.find('skipped') is not None]
print('bitcoin-only boundary: %d tests, %d skipped' %
(len(cases), len(skipped)))
if not cases:
sys.exit('FATAL: collected zero tests.')
for c in skipped:
print('::error::SKIPPED %s: %s' %
(c.get('name'), c.find('skipped').get('message', '')))
if skipped:
sys.exit('FATAL: %d of %d bitcoin-only tests skipped. A skip here '
'means the variant went unaudited, which is the failure '
'this job exists to catch.' % (len(skipped), len(cases)))
PY

- name: Bitcoin-only summary
if: always()
run: |
XML="keepkey-firmware/deps/python-keepkey/tests/junit-btc.xml"
echo "## 🔑 KeepKey python-keepkey — Bitcoin-only product boundary" >> "$GITHUB_STEP_SUMMARY"
echo "" >> "$GITHUB_STEP_SUMMARY"
if [ ! -f "$XML" ]; then
echo "❌ **No test results found** — suite may have crashed." >> "$GITHUB_STEP_SUMMARY"
else
TOTAL=$(grep -oP 'tests="\K[0-9]+' "$XML" | head -1)
FAILED=$(grep -oP 'failures="\K[0-9]+' "$XML" | head -1)
ERRORS=$(grep -oP 'errors="\K[0-9]+' "$XML" | head -1)
SKIPPED=$(grep -oP 'skipped="\K[0-9]+' "$XML" | head -1)
TOTAL=${TOTAL:-0}; FAILED=${FAILED:-0}; ERRORS=${ERRORS:-0}; SKIPPED=${SKIPPED:-0}
PASSED=$((TOTAL - FAILED - ERRORS - SKIPPED))
echo "| Metric | Count |" >> "$GITHUB_STEP_SUMMARY"
echo "|--------|-------|" >> "$GITHUB_STEP_SUMMARY"
echo "| Total | $TOTAL |" >> "$GITHUB_STEP_SUMMARY"
echo "| ✅ Passed | $PASSED |" >> "$GITHUB_STEP_SUMMARY"
echo "| ⏭️ Skipped (must be 0) | $SKIPPED |" >> "$GITHUB_STEP_SUMMARY"
echo "| ❌ Failed | $FAILED |" >> "$GITHUB_STEP_SUMMARY"
echo "| 💥 Errors | $ERRORS |" >> "$GITHUB_STEP_SUMMARY"
fi

- name: Annotate test results
uses: mikepenz/action-junit-report@v4
if: always()
with:
report_paths: keepkey-firmware/deps/python-keepkey/tests/junit-btc.xml
annotate_only: true
require_tests: true
fail_on_failure: true

- name: Fail on test failure
if: always()
run: |
STATUS=$(cat keepkey-firmware/deps/python-keepkey/tests/status-btc 2>/dev/null || echo "1")
[ "$STATUS" = "0" ] || exit 1
Loading
Loading