Skip to content

feat: add Solana protocol support (GetAddress, SignTx, SignMessage)#3

Closed
BitHighlander wants to merge 9 commits into
masterfrom
feature/solana
Closed

feat: add Solana protocol support (GetAddress, SignTx, SignMessage)#3
BitHighlander wants to merge 9 commits into
masterfrom
feature/solana

Conversation

@BitHighlander

Copy link
Copy Markdown
Owner

Summary

  • Add solana_get_address(), solana_sign_tx(), solana_sign_message() client methods
  • Generate messages_solana_pb2.py using docker_build_pb.sh (protoc 3.5.1 via kktech/firmware:v8)
  • Regenerate messages_pb2.py with Solana MessageType entries (750-755)
  • Add Solana routing in mapping.py
  • Point device-protocol submodule at feature-solana branch (adds messages-solana.proto)

Protocol Messages

ID Message Direction
750 SolanaGetAddress wire_in
751 SolanaAddress wire_out
752 SolanaSignTx wire_in
753 SolanaSignedTx wire_out
754 SolanaSignMessage wire_in
755 SolanaMessageSignature wire_out

Depends on: fix/combined-cleanup being merged first

Test plan

  • docker_build_pb.sh regenerates all pb2 files cleanly
  • solana_get_address returns Base58 address against emulator
  • solana_sign_tx signs a SOL transfer against emulator
  • solana_sign_message produces valid Ed25519 signature

BitHighlander and others added 6 commits March 17, 2026 01:30
Tests for structured TRX transfer, legacy raw_data blind-sign,
TRC-20 USDT transfer, and missing field rejection.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Tests for structured transfer, transfer with comment, legacy raw_tx
blind-sign, deterministic cell hash, and missing field rejection.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Tests for system transfer, token info metadata, message signing,
deterministic signatures, and empty tx rejection.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Use @unittest.skipUnless guards so these tests are skipped gracefully
when protobuf messages are not available in the build, rather than
failing with import/mapping errors.
The MayachainGetAddress class imports fine but may not be registered
in map_class_to_type if MessageType enum lacks the entry. Check the
actual dispatch dict to decide whether to skip the test.
@BitHighlander
BitHighlander changed the base branch from master to develop March 23, 2026 20:13
@BitHighlander
BitHighlander changed the base branch from develop to master March 23, 2026 20:14
@BitHighlander

Copy link
Copy Markdown
Owner Author

Superseded by combined release PR #13 which includes all 7.14.0 content.

BitHighlander added a commit that referenced this pull request Mar 28, 2026
- C31 test_invalid_bip39_word_rejected removed from SECTIONS — will be
  re-added when PR #3 firmware change lands
- Single-frame tests return None from _pick_best_frame — a single
  frame is always setUp noise (wipe confirm), not test content
BitHighlander added a commit that referenced this pull request Mar 28, 2026
Re-added now that PR #3 firmware fix is landing.
BitHighlander added a commit that referenced this pull request Apr 27, 2026
…ANSPORT, split confirm-flow setUp

Three findings from review of PR #14:

#1 (High) test_dylib_confirm_flow used common.KeepKeyTest.setUp which calls
   wipe_device() — the same path the file's pending regression is for.
   Hangs in setUp can't be classified by xfail or interrupted by
   pytest-timeout, so test_features_round_trip ("just Initialize") was
   actually wipe + Initialize. Refactored to construct
   KeepKeyDebuglinkClient directly in setUp (matching test_dylib_screenshot's
   pattern), moved wipe + load_device into the one pending test.

   Tried the reviewer-suggested @pytest.mark.xfail(strict=True) +
   @pytest.mark.timeout combo. pytest-timeout (both signal and thread
   methods) cannot interrupt the C-level kkemu_poll busy-loop — the
   hang locks up the entire test runner instead of failing the test.
   Switched to @unittest.skip with explicit rationale documenting
   exactly that, plus the promotion path: when firmware lands the
   confirm fix, drop the skip; if a future change makes kkemu_poll
   GIL-friendly, switch back to xfail+timeout.

#2 (Medium) tests/config.py treated any non-empty KK_TRANSPORT as
   "explicit" and skipped HID/WebUSB autodetect, but only "dylib" was
   actually handled. A typo like KK_TRANSPORT=dyllib silently fell
   through to UDP with hardware disabled. Now scoped to a
   _KNOWN_TRANSPORTS set; unsupported values raise at config import,
   surfacing typos at test collection time. Verified end-to-end:
   `KK_TRANSPORT=dyllib pytest test_msg_signtx.py` now errors on
   collection with the typo'd value in the message.

#3 (Medium/Low) DylibTransport.ready_to_read appended raw frame bytes
   to read_buffer but DylibTransport._pump_one stripped the leading '?'
   HID marker first. Inconsistent stripping corrupts multi-frame message
   reassembly: _read_headers can scan a stray '?' from one chunk into
   the middle of contiguous payload bytes from another, decoding the
   wrong message-type / length.

   Centralised the read+strip into a private _poll_and_stash helper
   shared by both ready_to_read (no sleep) and _pump_one (sleeps on
   miss). Now the buffer always contains continuation+payload bytes
   only; the leading '?' is stripped at the single point of stashing.
   Trailing HID padding zeros from short messages are still tolerated
   by _read_headers' magic-character search.

Verified locally:

  KK_TRANSPORT=dylib KK_DYLIB=build-emu/lib/libkkemu.dylib \
    pytest tests/test_dylib_screenshot.py tests/test_dylib_confirm_flow.py
  ================== 5 passed, 1 skipped in 0.15s ==================
BitHighlander added a commit that referenced this pull request Apr 27, 2026
…ANSPORT, split confirm-flow setUp

Three findings from review of PR #14:

#1 (High) test_dylib_confirm_flow used common.KeepKeyTest.setUp which calls
   wipe_device() — the same path the file's pending regression is for.
   Hangs in setUp can't be classified by xfail or interrupted by
   pytest-timeout, so test_features_round_trip ("just Initialize") was
   actually wipe + Initialize. Refactored to construct
   KeepKeyDebuglinkClient directly in setUp (matching test_dylib_screenshot's
   pattern), moved wipe + load_device into the one pending test.

   Tried the reviewer-suggested @pytest.mark.xfail(strict=True) +
   @pytest.mark.timeout combo. pytest-timeout (both signal and thread
   methods) cannot interrupt the C-level kkemu_poll busy-loop — the
   hang locks up the entire test runner instead of failing the test.
   Switched to @unittest.skip with explicit rationale documenting
   exactly that, plus the promotion path: when firmware lands the
   confirm fix, drop the skip; if a future change makes kkemu_poll
   GIL-friendly, switch back to xfail+timeout.

#2 (Medium) tests/config.py treated any non-empty KK_TRANSPORT as
   "explicit" and skipped HID/WebUSB autodetect, but only "dylib" was
   actually handled. A typo like KK_TRANSPORT=dyllib silently fell
   through to UDP with hardware disabled. Now scoped to a
   _KNOWN_TRANSPORTS set; unsupported values raise at config import,
   surfacing typos at test collection time. Verified end-to-end:
   `KK_TRANSPORT=dyllib pytest test_msg_signtx.py` now errors on
   collection with the typo'd value in the message.

#3 (Medium/Low) DylibTransport.ready_to_read appended raw frame bytes
   to read_buffer but DylibTransport._pump_one stripped the leading '?'
   HID marker first. Inconsistent stripping corrupts multi-frame message
   reassembly: _read_headers can scan a stray '?' from one chunk into
   the middle of contiguous payload bytes from another, decoding the
   wrong message-type / length.

   Centralised the read+strip into a private _poll_and_stash helper
   shared by both ready_to_read (no sleep) and _pump_one (sleeps on
   miss). Now the buffer always contains continuation+payload bytes
   only; the leading '?' is stripped at the single point of stashing.
   Trailing HID padding zeros from short messages are still tolerated
   by _read_headers' magic-character search.

Verified locally:

  KK_TRANSPORT=dylib KK_DYLIB=build-emu/lib/libkkemu.dylib \
    pytest tests/test_dylib_screenshot.py tests/test_dylib_confirm_flow.py
  ================== 5 passed, 1 skipped in 0.15s ==================
BitHighlander added a commit that referenced this pull request Apr 29, 2026
…ANSPORT, split confirm-flow setUp

Three findings from review of PR #14:

#1 (High) test_dylib_confirm_flow used common.KeepKeyTest.setUp which calls
   wipe_device() — the same path the file's pending regression is for.
   Hangs in setUp can't be classified by xfail or interrupted by
   pytest-timeout, so test_features_round_trip ("just Initialize") was
   actually wipe + Initialize. Refactored to construct
   KeepKeyDebuglinkClient directly in setUp (matching test_dylib_screenshot's
   pattern), moved wipe + load_device into the one pending test.

   Tried the reviewer-suggested @pytest.mark.xfail(strict=True) +
   @pytest.mark.timeout combo. pytest-timeout (both signal and thread
   methods) cannot interrupt the C-level kkemu_poll busy-loop — the
   hang locks up the entire test runner instead of failing the test.
   Switched to @unittest.skip with explicit rationale documenting
   exactly that, plus the promotion path: when firmware lands the
   confirm fix, drop the skip; if a future change makes kkemu_poll
   GIL-friendly, switch back to xfail+timeout.

#2 (Medium) tests/config.py treated any non-empty KK_TRANSPORT as
   "explicit" and skipped HID/WebUSB autodetect, but only "dylib" was
   actually handled. A typo like KK_TRANSPORT=dyllib silently fell
   through to UDP with hardware disabled. Now scoped to a
   _KNOWN_TRANSPORTS set; unsupported values raise at config import,
   surfacing typos at test collection time. Verified end-to-end:
   `KK_TRANSPORT=dyllib pytest test_msg_signtx.py` now errors on
   collection with the typo'd value in the message.

#3 (Medium/Low) DylibTransport.ready_to_read appended raw frame bytes
   to read_buffer but DylibTransport._pump_one stripped the leading '?'
   HID marker first. Inconsistent stripping corrupts multi-frame message
   reassembly: _read_headers can scan a stray '?' from one chunk into
   the middle of contiguous payload bytes from another, decoding the
   wrong message-type / length.

   Centralised the read+strip into a private _poll_and_stash helper
   shared by both ready_to_read (no sleep) and _pump_one (sleeps on
   miss). Now the buffer always contains continuation+payload bytes
   only; the leading '?' is stripped at the single point of stashing.
   Trailing HID padding zeros from short messages are still tolerated
   by _read_headers' magic-character search.

Verified locally:

  KK_TRANSPORT=dylib KK_DYLIB=build-emu/lib/libkkemu.dylib \
    pytest tests/test_dylib_screenshot.py tests/test_dylib_confirm_flow.py
  ================== 5 passed, 1 skipped in 0.15s ==================
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant