|
| 1 | +# python-keepkey consolidation receipt — 2026-09-09 |
| 2 | + |
| 3 | +Single shared head for the 7.14.2 / 7.14.3 / 7.15 develop-flow release program. |
| 4 | + |
| 5 | +## Why |
| 6 | + |
| 7 | +Six python-keepkey heads were in play. The same test changes had been |
| 8 | +cherry-picked onto several of them, and each copy was gated at whatever release |
| 9 | +its branch was cut for — so the version gate recorded which branch a test lived |
| 10 | +on rather than which firmware carries the fix. The report atlas |
| 11 | +(`scripts/generate-test-report.py`), which defines what the release PDF must |
| 12 | +contain, had forked three ways along with them. |
| 13 | + |
| 14 | +## Prior identities |
| 15 | + |
| 16 | +| Head | Pinned by | Relation to canonical `9c3982035` | |
| 17 | +| --- | --- | --- | |
| 18 | +| `9c3982035` | upstream fw #475/#476 | canonical (`keepkey:reconcile/upstream-sync`, PR #197 → master) | |
| 19 | +| `7f46aa207` | fork fw `release/7.14.3-bitcoin-only` | +4, 0 behind | |
| 20 | +| `5dae186a3` | fork fw `audit/7143-scope-repair` | +9, 0 behind | |
| 21 | +| `08e491c60` | fork fw `release/7.15` | +8, 0 behind | |
| 22 | +| `6268e38a3` | fork fw `audit/715-scope-repair` | +13, 0 behind | |
| 23 | +| `d3b26aee6` | fork fw `release/7.14.2`, `audit/7142-scope-repair` | **diverged**: 66 behind / 7 ahead | |
| 24 | +| `2ed835472` | fork fw `develop`, `alpha` | **diverged**: 50 behind / 52 ahead | |
| 25 | + |
| 26 | +## Assembly |
| 27 | + |
| 28 | +Base `5dae186a3`, merged `6268e38a3`. Merge base `7f46aa207`. Two conflicts, |
| 29 | +one line each; nothing else conflicted. |
| 30 | + |
| 31 | +Duplicate pairs across the two tips: `test(reset)` dice grouping and |
| 32 | +`test(storage)` CRC framing are byte-identical (equal patch-ids) and deduped on |
| 33 | +merge. `test(ping)` message presence and `test(ripple)` displayed-address |
| 34 | +differed **only** in the gate string. |
| 35 | + |
| 36 | +### Conflict resolutions |
| 37 | + |
| 38 | +Both resolved to `7.14.2`, below either side's value: |
| 39 | + |
| 40 | +- `tests/test_msg_ping.py::test_protected_ping_preserves_message_presence_after_debug_read` |
| 41 | + — three copies existed, gated `7.14.2` / `7.14.3` / `7.15.0`. `fsm_msgPing` is |
| 42 | + byte-identical between the 7.14.2 and 7.14.3 candidates, so the fix is on all |
| 43 | + three products and the lowest gate is the correct one. |
| 44 | +- `tests/test_msg_ripple_get_address.py` — same shape. The response-arena fix is |
| 45 | + present in `lib/firmware/fsm_msg_ripple.h` on the 7.14.2 candidate |
| 46 | + (`8c13ed24f`) as well. The comment claiming "older release backports are |
| 47 | + separate" was false and was rewritten; `1ce4d3961` (7.14.3) and `885609fbe` |
| 48 | + (7.15) reach the same end state. |
| 49 | + |
| 50 | +`tests/test_msg_ripple_sign_tx.py` was **not** a duplicate: `0f4c839db` asserts |
| 51 | +memo rejection below 7.15 and self-skips above it, `fb968836b` un-skips the |
| 52 | +THORChain memo test at 7.15.0. Complementary, auto-merged, both kept. |
| 53 | + |
| 54 | +## Coverage proof — nothing dropped |
| 55 | + |
| 56 | +Every test function on all six prior heads was diffed against the consolidated |
| 57 | +head. Three gaps were found and each is a deliberate supersession, not a loss: |
| 58 | + |
| 59 | +- `d3b26aee6` EOS work: `tests/test_msg_eos_signtx.py` and |
| 60 | + `tests/unit/test_eos_updateauth_vector.py` are **blob-identical** to the |
| 61 | + consolidated head. Its `test_msg_signing_boundaries.py` is superseded by the |
| 62 | + class-based rewrite, which is a strict superset (adds |
| 63 | + `test_clear_session_aborts_every_txrequest_stage`, |
| 64 | + `test_invalid_multisig_outputs_never_serialize_or_sign`). |
| 65 | +- `d3b26aee6` Zcash: 6 older PCZT tests replaced on canonical's line by 12 |
| 66 | + stricter ones, including `test_ironwood_v6_metadata_is_forwarded_exactly` |
| 67 | + (the NU/branch-id rot fix). Consolidated blob equals canonical blob exactly. |
| 68 | +- `test_full_715_accepts_pre_release_solana_lut_skip` was replaced by `08e491c` |
| 69 | + with two stricter tests: `test_full_7143_accepts_unimplemented_solana_lut_skip` |
| 70 | + and `test_full_715_requires_solana_lut_coverage`. |
| 71 | + |
| 72 | +`2ed835472` (fork develop/alpha) is the one head **not** covered — see exclusions. |
| 73 | + |
| 74 | +## Report atlas |
| 75 | + |
| 76 | +Single atlas at blob `b88c99396`. `MUST_RUN_MODULES['test_msg_solana_lut_attestation']` |
| 77 | +resolves to `7.15.0`, not canonical's stale `7.16.0`: `lib/firmware/solana.c` |
| 78 | +and `fsm_msg_solana.h` on `bd5e509cd` (7.15) carry the LUT attestation path, |
| 79 | +while `0fe01bc1b` (7.14.3) and `8c13ed24f` (7.14.2) do not. Canonical's floor |
| 80 | +would have let the 7.15 product silently skip its own LUT coverage. |
| 81 | + |
| 82 | +## Defect fixed in the same pass |
| 83 | + |
| 84 | +`keepkeylib/eth/ethereum_tokens.py` was fail-open. `build()` verified neither |
| 85 | +that the vetted `ethereum-lists` source was present nor that the scan produced |
| 86 | +anything. Measured: on a non-recursive checkout the old path yields **0 tokens |
| 87 | +and raises nothing**, so the firmware would build green with an empty token |
| 88 | +table and the device would show raw addresses and unknown decimals for every |
| 89 | +ERC-20. Restored the fail-closed form already present on the fork develop line, |
| 90 | +plus its regression test — which fails without the change (control run). |
| 91 | + |
| 92 | +The sibling `return` → `continue` in `add_tokens()` is corrected but is |
| 93 | +**latent, not live**: the currently pinned `ethereum-lists` has no non-file |
| 94 | +entries in any scanned directory, and the table is 1378 tokens either way. |
| 95 | + |
| 96 | +## Checks executed |
| 97 | + |
| 98 | +Offline suites on the consolidated head, real `ethereum-lists` checked out: |
| 99 | + |
| 100 | +- `tests/test_token_table_generators.py` — 5 passed, 0 skipped |
| 101 | +- `tests/test_report_variant_validation.py` — 4 passed |
| 102 | +- `tests/unit`, `tests/test_network_policy.py` — 4 passed, 2 subtests |
| 103 | + (`PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION=python`; the local env has a |
| 104 | + protobuf ≠ 3.20.3) |
| 105 | +- Control: the token guard test fails without the fix, passes with it. |
| 106 | + |
| 107 | +Device/emulator suites were **not** run here. No firmware branch is re-pinned by |
| 108 | +this change, so no CI was dispatched. |
| 109 | + |
| 110 | +## Exclusions |
| 111 | + |
| 112 | +- **`2ed835472` (fork develop/alpha) is not merged.** It diverges 50/52 with |
| 113 | + zero patch-id equivalence in either direction. It carries four fail-closed |
| 114 | + library changes and eight test functions that exist nowhere else, all |
| 115 | + `requires_firmware("7.16.0")`-shaped: the EIP-712 `MAX_IDENTIFIER_BYTES = 31` |
| 116 | + identifier/duplicate-member validation, the `SolanaSignTx` field-13 |
| 117 | + `clearsign_certificate` binding, and the WETH uniswap entry. Re-pinning |
| 118 | + develop straight to this head would silently revert them **and** delete their |
| 119 | + guarding tests in the same change. That is its own unit. |
| 120 | +- The report-atlas union with develop (J4 streamed-calldata commitment, section |
| 121 | + K seed-generation hardening, `screenshot_count_audit` frame-count gate, |
| 122 | + the GH #516 uniswap must-run entry) is deferred with it. |
| 123 | +- `TD4` is a genuine policy contradiction, not a merge conflict: canonical has |
| 124 | + `test_advanced_mode_gates_the_endpoint`, develop has |
| 125 | + `test_advanced_mode_is_not_required_for_structured_review`. Needs a decision, |
| 126 | + not a resolution. |
| 127 | + |
| 128 | +## Sequencing constraint |
| 129 | + |
| 130 | +The collapsed gates are satisfied by any firmware reporting ≥ 7.14.2, including |
| 131 | +release branches that have **not** taken the `audit/*-scope-repair` fixes. |
| 132 | +Against those builds these tests will fail rather than skip. That failure is |
| 133 | +correct — they are real regressions — but the release branches must take the |
| 134 | +audit fixes **before** any firmware branch re-pins to this head, or the |
| 135 | +consolidation will be blamed for the red. |
0 commit comments