Skip to content

feat: add Nano address validation and Chain::Nano - #80

Closed
dhyabi2 wants to merge 1 commit into
tinyhumansai:mainfrom
dhyabi2:add-nano-address
Closed

dhyabi2 wants to merge 1 commit into
tinyhumansai:mainfrom
dhyabi2:add-nano-address

Conversation

@dhyabi2

@dhyabi2 dhyabi2 commented Oct 12, 2026

Copy link
Copy Markdown

An agent wallet that cannot recognise a Nano address cannot be asked to pay one. Today "nano".parse::<Chain>() returns UnknownChain("nano"), and address::validate has no Nano arm.

This adds Nano beside the existing chains. Nothing about them changes.

  • tinywallet-bus: Chain::Nano (as_str/serde "nano", alias "xno", in Chain::ALL).
  • tinywallet-crypto: address::nano behind a default-on nano gate. It provides validate, decode (returns the 32-byte ed25519 key) and encode, shaped like the Solana module. An address is nano_ (or legacy xrb_) plus 60 base32 chars: 4 zero pad bits and the key, then a byte-reversed 5-byte blake2b checksum. It rejects a wrong prefix, wrong length, characters outside the alphabet, nonzero padding and a bad checksum. Adds blake2 0.11, which uses the same digest 0.11 as the existing sha2. No signing crates are added.
  • address::validate dispatches to it, the module build enables nano, and the README, lib docs and examples/basic.rs are updated.

Public API: additive. Chain is #[non_exhaustive].

Behavior note: Nano has no asset::Network or client queries yet, so networks_are_distinct_and_cover_every_chain now skips Chain::Nano, with a comment saying why. key::derive(Chain::Nano, ..) returns the existing ChainNotCompiled.

Test vectors: the genesis account (its decoded key equals the published E89208DD...A65093BA), the all-zero burn address and a live account. Every checksum was checked independently with Python's hashlib.blake2b(digest_size=5).

What I ran:

  • cargo fmt --all -- --check: clean.
  • cargo test --all-features: 825 passed / 3 ignored on main c154d14; 842 passed / 0 failed / 3 ignored after.
  • bash scripts/test-features.sh: 1641 passed / 0 failed / 6 ignored (--locked ok); cargo fmt --all -- --check clean.
  • cargo doc with -D warnings, check-bus-boundaries.py, nano-only gated build and tests: all clean.
  • cargo clippy --all-targets --all-features -- -D warnings: my local clippy 0.1.97 fails on unmodified main as well, with unknown lint: clippy::unused_async_trait_impl in tinywallet-module. With only that lint allowed, it is clean.

This is the primitive. An x402 nano:mainnet payer leg would follow only if you want it.

Add Chain::Nano to the bus vocabulary ("nano", alias "xno") and an
address::nano module in tinywallet-crypto behind a default-on `nano`
gate. A Nano address is `nano_` (or legacy `xrb_`) plus 60 base32
characters: 4 zero padding bits and a 32-byte ed25519 public key,
then a byte-reversed 5-byte blake2b checksum. validate, decode and
encode mirror the Solana module; address::validate dispatches to it.

The asset catalog and chain client do not cover Nano, so the
network-coverage test skips it explicitly.
@coderabbitai

coderabbitai Bot commented Oct 12, 2026

Copy link
Copy Markdown

Important

  • 🔍 Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

⚙️ Run configuration
  • Configuration used: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 2c2d3928-f585-44f3-b8dd-342654550919

  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

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

@senamakel senamakel closed this Oct 12, 2026
@tinysweeper

tinysweeper Bot commented Oct 12, 2026 •

Copy link
Copy Markdown

Tiny Sweeper review

Tiny Sweeper completed its review; deterministic results follow.

State: Incomplete
Priority: none
Reviewed head: 811eae6a4d6f
Updated: 2026-10-12T20:50:59Z

Review snapshot

Change surface Files Review signal Count
Production 6 Active findings 0
Tests 4 Noted findings 0
Documentation 1 Resolved findings 0
Configuration 3 Pending checks/questions 5

Completeness: Incomplete
Test assessment: Test coverage is assessed from changed tests and lane evidence; execution is not claimed without trusted check data.

What changed

The diff introduces Nano address support across the crate hierarchy. A new `crates/tinywallet-crypto/src/address/nano.rs` module implements decode, encode, validate, base32 packing/unpacking, and a blake2b-40 checksum, gated behind a new `nano` feature backed by an optional `blake2` dependency. `Chain::Nano` is added to the bus enum with ALL, as_str, and FromStr support including the `xno` alias. The root and crypto crates add the `nano` feature to defaults; the module crate forwards it. Dispatch in `crates/tinywallet-crypto/src/address/mod.rs#pub fn validate(chain: Chain, address: &str) -> Result<String>` gains a Nano arm, and the unused-variable allow-list is extended. Tests are added in `nano/nano_tests.rs` and `crates/tinywallet-bus/src/chain/mod_tests.rs`, the FIXTURES table in `crates/tinywallet-crypto/src/address/mod_tests.rs` grows to five chains, and the asset network table test skips Nano. Documentation in README.md, src/lib.rs, and examples/basic.rs is updated.

Features

  • Added — Nano address validate/decode/encode module: Provides the core Nano address functionality: validate returns the trimmed address string, decode returns the 32-byte ed25519 public key, and encode renders a key back to a nano_ address. It enforces the nano_/xrb_ prefix, 60-character body length, the Nano base32 alphabet, zero padding bits, and a byte-reversed blake2b-40 checksum. (crates/tinywallet-crypto/src/address/nano.rs, crates/tinywallet-crypto/src/address/nano/nano_tests.rs)
  • Added — Chain::Nano variant: Adds Nano to the bus chain enum so chain-generic callers can name it. It is included in Chain::ALL, maps to the string "nano" via as_str, and parses from "nano", "xno", and "NANO". (crates/tinywallet-bus/src/chain/mod.rs#pub enum Chain {, crates/tinywallet-bus/src/chain/mod.rs#impl Chain {, crates/tinywallet-bus/src/chain/mod.rs#impl FromStr for Chain {)
  • Modified — address::validate dispatch to Nano: The chain-generic dispatcher now routes Chain::Nano to nano::validate when the nano feature is enabled, and falls through to None (producing ChainNotCompiled) otherwise. The unused-variable allow cfg is broadened to include the nano feature. (crates/tinywallet-crypto/src/address/mod.rs#pub fn validate(chain: Chain, address: &str) -> Result<String> {, crates/tinywallet-crypto/src/address/mod.rs#pub mod tron;)
  • Added — nano feature gate and blake2 dependency: Adds a `nano` feature to the root crate and tinywallet-crypto, backed by an optional blake2 dependency for the checksum. The root default feature set, the crypto default set, and the module crate's forwarded features all include nano, so it is on by default. (Cargo.toml#evm = ["tinywallet-crypto/evm"], crates/tinywallet-crypto/Cargo.toml#evm = [], crates/tinywallet-crypto/Cargo.toml#hex = { version = "0.4", optional = true }, crates/tinywallet-module/Cargo.toml#tinywallet = { path = "../..", default-features = false, features = [)
  • Modified — Asset catalog test skips Nano: The asset network-table test now iterates Chain::ALL filtered to exclude Chain::Nano, reflecting that Nano has address support only and no catalog network or client queries yet. (crates/tinywallet-crypto/src/asset/mod_tests.rs#fn networks_are_distinct_and_cover_every_chain() {)
  • Modified — Documentation and example updates: README.md, src/lib.rs, and the address module docs now list Nano with its blake2b checksumming, and the feature table and CI check loop include nano. examples/basic.rs adds a Nano fixture to the chain-generic example, and its required-features gains nano. (README.md#Agent-friendly multi-chain wallet primitives in Rust., README.md#because it is easy to assume otherwise:, README.md#must error, never validate):, README.md#not pay for the others' parsers., src/lib.rs, examples/basic.rs#fn main() {)

Tests

  • unit — Verifies Nano chain parsing accepts "nano", "xno", and "NANO", that Chain::Nano.as_str() equals "nano", and that Chain::ALL contains Nano.: The tests lane considered the alias and round-trip behavior pinned by this test. I did not execute it. (crates/tinywallet-bus/src/chain/mod_tests.rs#fn an_unknown_name_is_reported_with_the_input() {)

Findings

No active actionable findings.

Could not review: Cargo.toml, crates/tinywallet-crypto/Cargo.toml, crates/tinywallet-crypto/src/address/mod_tests.rs, crates/tinywallet-crypto/src/address/nano.rs, crates/tinywallet-crypto/src/address/nano/nano_tests.rs

Before merge

  • Complete the critique review for Cargo.toml, crates/tinywallet-crypto/Cargo.toml, crates/tinywallet-crypto/src/address/mod_tests.rs, crates/tinywallet-crypto/src/address/nano.rs, crates/tinywallet-crypto/src/address/nano/nano_tests.rs.

How this fits together

flowchart LR
  n0["networks"]:::impacted
  n1["catalog"]:::impacted
  n2["...work_lists_exactly_one_native_asset_first"]:::impacted
  n3["...token_contract_is_valid_for_its_own_chain"]:::impacted
  n4["...ts_carry_no_contract_and_tokens_always_do"]:::impacted
  n2 -->|calls| n0
  n2 -->|tests| n0
  n2 -->|calls| n1
  n2 -->|tests| n1
  n3 -->|calls| n0
  n3 -->|tests| n0
  n3 -->|calls| n1
  n3 -->|tests| n1
  n4 -->|calls| n0
  n4 -->|tests| n0
  n4 -->|calls| n1
  n4 -->|tests| n1
  classDef changed fill:#0d4429,stroke:#238636,color:#e6edf3
  classDef impacted fill:#161b22,stroke:#6e7681,color:#c9d1d9
  classDef flagged fill:#5a1e02,stroke:#d93f0b,color:#ffffff
  classDef blocking fill:#67060c,stroke:#f85149,color:#ffffff
Loading
Agent review details

critique

  • Conclusion: Success
  • Scope reviewed: incomplete; unanswered: Cargo.toml, crates/tinywallet-crypto/Cargo.toml, crates/tinywallet-crypto/src/address/mod_tests.rs, crates/tinywallet-crypto/src/address/nano.rs, crates/tinywallet-crypto/src/address/nano/nano_tests.rs
  • Lane summary: Reviewed 9 files; 0 findings. 5 files could not be reviewed: Cargo.toml, crates/tinywallet-crypto/Cargo.toml, crates/tinywallet-crypto/src/address/mod_tests.rs, crates/tinywallet-crypto/src/address/nano.rs, crates/tinywallet-crypto/src/address/nano/nano_tests.rs. _The code index is behind this pull request (indexed at `8fd9b113131c`), so retrieved context may be out of date._

security

  • Conclusion: Success
  • Scope reviewed: all assigned evidence
  • Positive: The security lane reviewed 13 files and reported zero findings; README.md was not security-reviewed as prose or tabular data.
  • Lane summary: Reviewed 13 files; 0 findings. 1 file was not security-reviewed: README.md (prose or tabular data). _The code index is behind this pull request (indexed at `8fd9b113131c`), so retrieved context may be out of date._

tests

  • Conclusion: Success
  • Scope reviewed: all assigned evidence
  • Positive: The Nano module is covered by a dedicated test file with real mainnet vectors and adversarial cases; the tests lane found no reportable defect and stated that no claimed invariant in the diff is left unpinned.
  • Positive: The checksum mismatch, nonzero padding-bit, and alphabet exclusion behaviors each have targeted negative tests that isolate the failing check from valid key/checksum state.
  • Positive: The legacy xrb_ prefix is explicitly tested to resolve to the same decoded key as the nano_ prefix, confirming alias equivalence.
  • Lane summary: The Nano module is well covered by its own test file: real mainnet vectors (genesis and burn), the legacy `xrb_` prefix, trimming, empty input, checksum mismatch on both the key and checksum halves, foreign/missing/case-wrong prefixes, wrong length, out-of-alphabet characters, nonzero padding bits, encode/decode round-trips (including an all-0xff boundary vector), and a differential `validate` vs `decode` agreement test. Speculative checks before I answer: (1) whether any of the wallet's `match chain` sites use exhaustive matches that the new `Chain::Nano` variant would break — `Chain` is `#[non_exhaustive]`, so downstream crates cannot write an exhaustive match and `crates/tinywallet-crypto/src/address/mod.rs` already falls through with `_ => None`; (2) whether the `asset` catalog's excluded-Nano test masks a real gap — the diff comment states Nano has no catalog network yet, and the surrounding unit tests pin the other four chains, so this is a documented scope limit rather than untested behaviour; (3) whether `unpack` has an overflow path — trait bounds plus the >1 padding check make it unreachable. I found no reportable defect, and I'm satisfied the Nano feature's behaviours that can regress silently are pinned by the tests in `nano/nano_tests.rs`. The listed 'neither' files (Cargo.toml, README.md, crate manifests, the module manifest) are wiring and documentation; their effects are exercised by the same tests, e.g. `current_gate_enables_every_chain` in address/mod_tests.rs. One residual note that is not a finding: I could not inspect the wallet test-support `sample_address` match sites for Nano coverage, but those live in tinywallet-web3 and are not touched by this diff. No claimed invariant in the diff (checksumming, padding bits, prefix handling) is left unpinned by a test. _The code index is behind this pull request (indexed at `8fd9b113131c`), so retrieved context may be out of date._

commits

  • Conclusion: Neutral
  • Scope reviewed: all assigned evidence
  • Lane summary: Nothing sensitive found in what this pull request commits.

description

  • Conclusion: Success
  • Scope reviewed: all assigned evidence
  • Lane summary: The change adds a Nano address module to tinywallet-crypto and a Chain::Nano variant to tinywallet-bus, and the description matches the diff closely: the feature gate, dispatch arm, README/lib docs/example updates, the blake2 dependency, and the two test adjustments (asset network table skip and the new nano test file) are all described. I found one inconsistency: the asset catalog is skipped for Chain::Nano while key::derive(Chain::Nano, ..) is stated to fall through to ChainNotCompiled, but neither is a defect the diff introduces. Nothing here is a concrete misbehaviour in the new code — the byte-reversal, padding-bit and length checks line up with Nano's published format — so I have no reportable defects; I was unable to run the test suite from this view, so the claimed 842-passing result is unverified. _The code index is behind this pull request (indexed at `8fd9b113131c`), so retrieved context may be out of date._
Evidence and run details
  • Models: ladder/vectors-oai3, deepseek-v4.1-flash,
  • Spend: $0.012477
  • Tokens: 1335138 input · 168131 output · 320640 cached · 981 embedding
Head State Pass summary
811eae6a4d6f incomplete 0 active finding(s), 0 resolved finding(s) (at 2026-10-12T20:50:59Z)

tinysweeper 0.1.2

@tinysweeper tinysweeper 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.

tinysweeper found nothing blocking, but could not review everything, so this is not an approval: Cargo.toml, crates/tinywallet-crypto/Cargo.toml, crates/tinywallet-crypto/src/address/mod_tests.rs, crates/tinywallet-crypto/src/address/nano.rs, crates/tinywallet-crypto/src/address/nano/nano_tests.rs.

             $0.0125 · 1,335,138 in / 168,131 out · 320,640 cached (24%) · ladder/vectors-oai3, deepseek-v4.1-flash,  · 981 embedded
critique:    $0.0050 · 598,781 in   / 71,850 out  · 159,616 cached (27%) · deepseek-v4.1-flash
security:    $0.0071 · 620,564 in   / 92,380 out  · 150,528 cached (24%) · deepseek-v4.1-flash,
tests:       $0.0001 · 50,062 in    / 791 out     · 4,352 cached (9%)    · deepseek-v4.1-flash
description: $0.0001 · 53,388 in    / 973 out     · 6,144 cached (12%)   · deepseek-v4.1-flash

@tinysweeper tinysweeper Bot added the priority: p3 Whenever. Cosmetic, a nicety, or a cleanup with no user visible effect. label Oct 12, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

priority: p3 Whenever. Cosmetic, a nicety, or a cleanup with no user visible effect.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants