Repository navigation
7.15 stack tests and report fixes - #230
Merged
BitHighlander merged 37 commits intoOct 9, 2026
Merged
BitHighlander merged 37 commits into
BitHighlander merged 37 commits into
Conversation
Firmware now decrypts each Orchard output, so the fixture is the zcash-test-vectors note 0 with its real ciphertext (Ironwood V3 too).
Bindings regenerated with protoc 3.5.1, as before; the descriptor differs only by Features.Capability CAPABILITY_TRON_TRC20_REVIEW = 25.
Gated on 7.15.0 and the tron-trc20-review capability, so 7.15 builds without the trusted token table skip. Checks the screen text and verifies the signature over sha256(raw_data) against the account's address.
Address screens wrap at their own widths and pitches beside a QR code, so shows() takes a pitch, ignores the blank top row the QR border may cover, and stops the edge check at the wrap limit. shows_address() tries each layout in app_layout.c; assert_shows_address() runs a show_display call and requires one screen to show the whole value.
A Hive STM key lost its tail on the device and no test noticed. Each show_display test now checks the screen against a known vector or the show_display=False response. Adds Hive, THORChain, Maya, Osmosis and EOS show tests, and stops the TON and Tron show tests swallowing errors.
7.14 and the 7.15 blocks before the fix drop the last characters of a 52-character cosmosvaloper1 address. Covers delegate, undelegate, redelegate (both validators) and rewards; nested display checks now restore the enclosing capture.
Before firmware 88ae1c3be DebugLinkState was built in the shared response arena, so reading the screen during a confirm wiped a response the handler had already built; on a 7.14.3 emulator the cosmos, thor, maya, osmosis, solana, ripple, eos, tron and ton responses came back empty and an xpub confirm wedged. Older firmware now runs the call unwatched, and the TON and Tron show tests gate on 7.15.0, not 7.14.2.
The fixture attested two accounts for a message that loads one, which firmware now refuses to show. Add the partial/padded-list case.
7.14.3 now builds DebugLinkState outside the response arena, so the bitcoin-only report checks every shown address as 7.15 does.
The Groestlcoin checks made the whole test full-firmware only, so the bitcoin-only report never displayed a bech32 address.
A real multi-receiver Unified Address holding the output's Orchard receiver is shown verbatim, in both pools. A valid address with another Orchard receiver is refused before any output screen. Without one the device shows the Orchard-only address as "Orchard address". Repins device-protocol for ZcashPCZTAction.user_address.
eip712_corpus.json is the firmware repository's corpus (42 documents, one per protocol and shape, written from each protocol's source). Each must sign on the device with the expected domain separator and message hash, recover to the device's address, and end on a screen that names the whole primary type. Gated on 7.15.0 and structured EIP-712. The host encoder now accepts struct type names with ':' up to 47 characters, as the firmware does, so Hyperliquid's HyperliquidTransaction:* actions can be sent.
From erc20-unlimited-approve-review and erc20-unlimited-permit-review, 7.15 signs an unlimited approve after an UNLIMITED approval screen with the full spender, and shows an unlimited permit value as UNLIMITED on its own screen. The refusal tests now run only on firmware that lacks those capabilities (requires_capability_absent). The corpus gains three max approvals: the Uniswap default Permit2 PermitSingle (uint160 max), an EIP-2612 USDC permit of 2^256-1 and a DAI permit with allowed=true. They run where the firmware reports the capability, and before 7.16 their value screen must read UNLIMITED.
Each corpus document is now its own test (test_doc_<id>), so one slow document no longer exhausts a single test's timeout and a refusal names the document. The 7.15.0 gate is unchanged. New documents: a Seaport bundle of 10 offer and 6 consideration items, an order with 12 consideration items, a 32-item bundle, Permit2 batches of 20 and 32, a Snapshot vote whose reason holds a newline, a tab and a CR, and a Safe transaction whose data is an unlimited USDC approve, which must show the UNLIMITED warning first.
An execTransaction that embeds approve(spender, 2^256-1) shows the UNLIMITED approval screen, with the full spender, before the inner review or the blind screen. Declining it signs nothing, a finite approve shows no warning, and a dirty spender word is refused.
From NU6.3 every Orchard spend is paired with a zero-valued output whose ciphertext is random bytes (ZIP 326). This bundle is built by the orchard 0.16.0 crate for the "all" seed, so its rk values are the device's own. The device must sign both wallet spends, show only the change and the fee, and show no 0 ZEC output.
Wallets pad an Orchard bundle with a dummy zero-valued output to a random address whose ciphertext decrypts. The device showed it as a 0 ZEC send. Like Keystone and Ledger it now shows only the change and the fee. The bundle is a real NU6.2 one built by the orchard 0.16.0 crate.
From NU6.3 Orchard funds can only leave the Orchard pool. ZIP 318 moves them to Ironwood with a v6 transfer that has both bundles, after 16-action note-preparation transactions. zcash_sign_pczt takes the Ironwood actions of a crossing transaction and streams them after the Orchard ones. The tests sign a transfer and a preparation transaction built by librustzcash's zcash_pool_migration. Repins device-protocol for the crossing fields.
Outputs proven to pay the account's internal address are shown as one "Change back to your wallet" total. The fabricated, padding and migration transactions now show that total instead of per-output screens, and the 16-action preparation transaction takes three confirmations instead of 32. A new real NU6.2 bundle (orchard 0.16.0) checks that a self-send to the account's external address and a payment to another account's change address are still shown, and that a zero-valued memo-only send with the address the user entered is shown at 0 ZEC.
Native and token deposits to THORChain's BSC and Base routers and Maya's Arbitrum router clear-sign on their own chain, and each signature recovers to the device's address. The same router address on another chain is refused.
A V3DutchOrder witness references seven struct types and nests six frames deep (witness.baseOutputs[i].curve.relativeAmounts). Add three filled Arbitrum orders: one output, one with a fee output, and one with a negative curve amount. Each document was rebuilt from the order the UniswapX API returned, and the swapper's own signature recovers from its digest.
A wallet pays a TEX address from shielded funds in two transactions: one to its own one-time address m/44'/133'/account'/2/index, then a transparent-only one from there to the TEX recipient. Both are built by librustzcash for the "all" seed. Step 1 shows the output as a transfer to your one-time address when its path is sent, and as a plain t1 send without it. Step 2 has no shielded action; it shows the recipient as tex1... and its signature verifies under librustzcash's sighash, in v6 and v5. Shielded bundle claims, a non-empty Sapling digest, another account's path and change 3 are refused. zcash_sign_pczt accepts no actions when there is a transparent input. Repins device-protocol for is_tex and the one-time output path.
EOSIO key types are K1=0, R1=1, WA=2. parse_authorization sent type 1 for every address_n key, so an updateauth or newaccount built here put the user's K1 key bytes on chain as an R1 key nobody holds, which can freeze the account. Send type 0; a raw PUB_R1_ key still sends 1. Every firmware accepts type 0 for a derived key, and firmware that checks the type refuses anything else. The updateauth, newaccount and JSON newaccount digests are re-derived with an independent EOSIO serializer; the old digests come out of the same serializer with type 1.
A Safe MultiSend's data or a Snapshot proposal's body is often several KB, past the 1024-byte EthereumTypedDataValueAck. eip712_stream.value_ack() answers a ValueRequest whole up to 1 KB, and past that in 1 KB chunks: the first names the total length, each later one echoes the offset the device asked for. It chunks only for firmware that reports CAPABILITY_EIP712_CHUNKED_VALUES; older firmware would hash the first chunk alone, so without it a long value is refused on the host. Pin device-protocol at feat/eip712-chunked-values and regenerate the bindings. Add a real Safe MultiSend (4,900 B of data, digest equals its safeTxHash) and a real Snapshot proposal (3,929 B body, digest equals its proposal id) to the corpus, and device tests for boundary lengths, UTF-8 split across chunks, misframed chunks, a part declined mid-value and an unlimited approve heading chunked SafeTx data.
Olympus DAO's Safe batched approve(WETH, max) to the CoW vault relayer with a setPreSignature swap order (nonce 1802). The device names the spender and WETH on the UNLIMITED screen; the digest is its safeTxHash. Gated on eip712-chunked-values, the block that brings the warning.
Evmos and Injective sign EIP-712 with a string verifyingContract and salt; the firmware refused them. Adds an Evmos MsgSend (evmosjs eip712 helpers) and Injective's EIP-712 v2 MsgSend fixture (injective-ts eip712.spec.ts), plus the Arbitrum V3 Dutch order with 16-point curves, the most the decay library accepts. Hashes match eth-sig-util. Gated on eip712-chunked-values, the block that brings the fixes.
The port pair is only known to be free when it is picked; another process can bind it before kkemu does, and the reboot test then failed at random. Pick a fresh pair and boot again when kkemu reports it could not bind.
A 0.02 RUNE fee was printed as "Fee: 2000000 rune". Fails on the old firmware with exactly that text.
A forced line break before "to" paged the last characters of a 44-char address onto a screen of their own.
- drop K8, K8b and K9: no such tests exist in this firmware, so they rendered as pending inside a passing section - unlimited approvals sign after a warning from 7.15; say so, and capture the warning screen instead of withholding it - read section L from the bitcoin-only JUnit beside the full one, where those tests actually run, and update the L3 and L11 text now that both pass
K8, K8b and K9 are staged controls: a complete release requires them, and validate_junit exempts them only while the firmware reports storage-v19-kdf missing. Restore the rows, and in the PDF show a staged control as withheld when its capability is missing instead of pending.
Hash-only signing is no longer the only EIP-712 path: the streamed protocol in section TD reads the document. E16 is the blind path for hash-only hosts; E16b proves the old JSON endpoint stays refused.
This was referenced Oct 8, 2026
BitHighlander
merged commit Oct 9, 2026
8d62850
into
keepkey:reconcile/upstream-sync
3 of 5 checks passed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Moves #197 forward 37 commits for the 7.14.3 and 7.15 firmware stack.
Fast-forward only. All seven firmware blocks pin 8d62850 and are green on CI.