Skip to content

Address review from #10: unrepresentable chain mismatch, serde/derive - #11

Merged
senamakel merged 1 commit into
mainfrom
tinywallet-review-fixes
Aug 11, 2026
Merged

Address review from #10: unrepresentable chain mismatch, serde/derive#11
senamakel merged 1 commit into
mainfrom
tinywallet-review-fixes

Conversation

@senamakel

@senamakel senamakel commented Aug 11, 2026

Copy link
Copy Markdown
Member

Follow-up to #10, which merged before these landed. CodeRabbit raised two findings on it, both correct; this is the fix for each.

A request could name one chain and carry another 🟠

SigningRequest and AttachRequest deserialized chain and transaction independently, so a mismatched pair was accepted and had to be caught at runtime.

The suggestion was to reject mismatches during validation, or drop the duplicate field. I took the second — validation leaves a state that has to be detected, and detection is what a future code path forgets.

TransactionSpec::chain() is now the single source of truth and neither request carries chain. The disagreement is unrepresentable rather than rejected.

This deleted code rather than adding it: the module's mismatched() helper, its UnsupportedChain failure variant, and that variant's wire error name existed only to catch this.

chain() is infallible, which is worth explaining since TransactionSpec is #[non_exhaustive]. That attribute binds only downstream crates, and a downstream crate calls the method rather than matching the enum — so there is no wildcard to write, and adding a variant becomes a compile error in the file that has to handle it. My first attempt returned Result with an UnknownChain error; the coverage gate caught the wildcard as permanently unreachable, which was the right signal that the Result was unnecessary.

The rejection tests you asked for could not be written — the state no longer compiles. Instead:

  • every_transaction_names_its_own_chain pins the variant→chain mapping for all four.
  • The loader E2E's contradictory-request case became a tampered Tron transaction whose recomputed txID disagrees with the claimed one — a real compromised-node scenario — asserting the InvalidInput wire name.

serde feature missing serde/derive 🟡

Correct, and invisible locally: cargo check --features serde passed here, because this crate's dev-dependency on serde with derive unified the feature into the lib build. A consumer taking tinywallet with serde alone gets no derive and fails to compile Chain.

I confirmed the mechanism with cargo tree rather than just applying the patch, and the Cargo comment records it so the apparent redundancy is not "cleaned up" later. wire now depends on plain serde, since the derive comes from the one place that owns it.

Also here

  • fee_rate_sat_vbfee_sat. It always carried an absolute fee, not a rate; the module was already mapping it to Transfer::fee. Caught while wiring up the host, before anything depended on the wrong name.
  • The coverage gate's vendor exclusion is now anchored at the workspace root. Unanchored, it matched any path containing /vendor/ — and this repo is itself vendored inside other checkouts, so it silently excluded the crate's own sources when built from one. It only did the right thing on CI by accident of the runner's path.

Verification

274 tests pass, clippy clean workspace-wide at the strict bar, wire/mod.rs at 100% line coverage, and the real-module E2E still signs EVM, multi-input Bitcoin and Solana byte-identically to the library.

Summary by CodeRabbit

  • New Features

    • Transaction requests now determine their blockchain automatically from the transaction details.
    • Bitcoin transaction fees are specified as an absolute amount in satoshis.
  • Bug Fixes

    • Improved validation rejects malformed transactions with inconsistent transaction IDs.
    • Unknown transaction types are rejected with a clearer invalid-input error.

…quests

The `chain` field was removed from `SigningRequest` and `AttachRequest` because it duplicated information already present in the `TransactionSpec` variant, creating a state where the two could disagree. The chain is now read from the transaction spec itself via a new `TransactionSpec::chain()` method, making the disagreement unrepresentable. The `UnsupportedChain` error variant was removed along with the `mismatched` function, replaced by a simpler `unknown_kind` refusal for unrecognized transaction shapes. The Bitcoin transaction spec field `fee_rate_sat_vb` was renamed to `fee_sat` to reflect that the fee is absolute rather than rate-based.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
@coderabbitai

coderabbitai Bot commented Aug 11, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The wire contract removes request-level chain fields. TransactionSpec now provides chain mapping, and wallet dispatch matches transaction variants directly. Tests, feature definitions, and coverage filtering were updated.

Changes

Transaction request flow

Layer / File(s) Summary
Wire contract and feature definitions
src/wire/mod.rs, Cargo.toml
Requests derive chain context from TransactionSpec. Bitcoin uses fee_sat. The serde feature enables derive support.
Variant-based wallet dispatch
crates/tinywallet-module/src/service/mod.rs
Signing and attachment paths match transaction variants directly. Unknown variants return invalid input.
Contract, integration, and CI validation
src/wire/test.rs, crates/tinywallet-module/src/service/test.rs, crates/tinywallet-module/tests/module_e2e.rs, .github/workflows/ci.yml
Tests cover chain mapping, self-describing requests, malformed transactions, the new Bitcoin fee field, and anchored vendor coverage filtering.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant SigningRequest
  participant WalletService
  participant TransactionSpec
  Client->>SigningRequest: submit transaction specification
  SigningRequest->>WalletService: provide signing or attachment request
  WalletService->>TransactionSpec: match transaction variant
  TransactionSpec-->>WalletService: select chain-specific path
  WalletService-->>Client: return signed transaction or validation failure
Loading

Possibly related PRs

  • tinyhumansai/tinywallet#3: Introduced transaction module fields refined here, including Bitcoin fee representation and chain-specific dispatch.
  • tinyhumansai/tinywallet#10: Introduced the wire contract refined here by removing request-level chain fields.

Suggested labels: priority: p2

Poem

I’m a rabbit with a tidy request,
No extra chain tag to detest.
Each transaction knows its way,
Tests keep malformed paths at bay.
Fee fields hop to a clearer nest!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main changes: removing representable chain mismatches and correcting serde derive configuration.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@senamakel
senamakel merged commit 8e7548e into main Aug 11, 2026
10 of 11 checks passed

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
crates/tinywallet-module/tests/module_e2e.rs (1)

259-287: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Assert the transaction-ID mismatch branch in the focused service test.

verify_transfer checks expected_txid before recipient validation, so the current decodable fixture reaches that branch. Match Failure::InvalidInput and assert the message contains "txID does not match sha256(raw_data)" instead of checking only "InvalidInput". A valid protobuf fixture is not required because verify_transfer does not parse protobuf.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/tinywallet-module/tests/module_e2e.rs` around lines 259 - 287, Update
refuses_a_request_the_module_cannot_build to assert the underlying
Failure::InvalidInput result and verify its message contains "txID does not
match sha256(raw_data)". Keep the existing wire-name assertion if appropriate,
but replace the insufficient generic error check with the focused transaction-ID
mismatch assertion.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@crates/tinywallet-module/src/service/mod.rs`:
- Around line 108-109: Update the comment above build_unsigned to remove the
stale chain_of reference and state that the function matches TransactionSpec
directly, preserving the explanation that unrecognised shapes are rejected
before selecting a variant.

---

Outside diff comments:
In `@crates/tinywallet-module/tests/module_e2e.rs`:
- Around line 259-287: Update refuses_a_request_the_module_cannot_build to
assert the underlying Failure::InvalidInput result and verify its message
contains "txID does not match sha256(raw_data)". Keep the existing wire-name
assertion if appropriate, but replace the insufficient generic error check with
the focused transaction-ID mismatch assertion.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 602b74ae-4813-442e-aa42-6405b64b2c00

📥 Commits

Reviewing files that changed from the base of the PR and between 92c5b8a and b5df6e7.

📒 Files selected for processing (7)
  • .github/workflows/ci.yml
  • Cargo.toml
  • crates/tinywallet-module/src/service/mod.rs
  • crates/tinywallet-module/src/service/test.rs
  • crates/tinywallet-module/tests/module_e2e.rs
  • src/wire/mod.rs
  • src/wire/test.rs

Comment on lines +108 to +109
// `chain_of` runs first so an unrecognised shape is refused before any
// arm is tried; the chain itself then comes from the variant.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Remove the stale chain_of reference.

Line 108 says that chain_of runs first. build_unsigned no longer calls chain_of. State that the function matches TransactionSpec directly.

Proposed fix
-    // `chain_of` runs first so an unrecognised shape is refused before any
-    // arm is tried; the chain itself then comes from the variant.
+    // Match the transaction variant directly. Unknown variants are refused.
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// `chain_of` runs first so an unrecognised shape is refused before any
// arm is tried; the chain itself then comes from the variant.
// Match the transaction variant directly. Unknown variants are refused.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/tinywallet-module/src/service/mod.rs` around lines 108 - 109, Update
the comment above build_unsigned to remove the stale chain_of reference and
state that the function matches TransactionSpec directly, preserving the
explanation that unrecognised shapes are rejected before selecting a variant.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant