Derive wallet addresses and sign with them (Android) #72

Merged
paulhitt merged 1 commits from feat/wallet-keys-and-address into main 2026-09-04 20:49:36 +00:00
paulhitt commented 2026-09-04 18:19:00 +00:00 (Migrated from gitlab.com)

HdAccount turns a recovery phrase into the BIP-44 path every other Solana
wallet uses, the Base58 address somebody can be paid at, and the key that signs
for it.

The property everything else rests on is that the signing key OWNS the
address that was displayed — same path, same curve, same code. If those ever
disagreed the wallet would sign with a key that does not own the address it
showed. The transaction being rejected by the network is the GOOD outcome; the
bad one is signing for an address somebody else controls. Tested across five
indices, and separately by signing a message and verifying it under the address.

Android can sign, and iOS's constraint does not apply. AccountKdf notes that
a raw Ed25519 seed needs EdECPrivateKeySpec, which Android exposes only from
API 33 — but the engine carries its own RFC 8032, so this works on any
supported device. The wallet is not blocked on a platform floor.

A fresh address per receive, by default rather than on request. Reusing one
lets anybody watching a public chain group every payment somebody ever received
— the single linkability a wallet can actually prevent, because the chain will
not forget.

looksLikeAddress says WELL FORMED, never CORRECT, and the difference is
money. A Solana address carries no checksum, so one mistyped character that
still decodes to 32 bytes is a perfectly valid address nobody holds the key
for. Bitcoin's Base58Check would catch it; this cannot, and neither can the
network. There is a passing test that goes and FINDS such a near-miss, so the
limit is recorded rather than implied — and it fails if the format ever gains a
checksum, which is the point at which the warnings around typed addresses can be
revisited.

A test bug found by the test failing rather than passing: Ed25519.verify
takes (publicKey, signature, message) and I had passed (publicKey, message,
signature). All three are ByteArray, so it compiled perfectly. Noted in the file
— it is a decent argument for asserting the signature verifies at all rather
than trusting that a key which derives must be a key that signs.

12 tests. App builds, every JVM suite green.

Co-Authored-By: Claude Opus 5 noreply@anthropic.com

`HdAccount` turns a recovery phrase into the BIP-44 path every other Solana wallet uses, the Base58 address somebody can be paid at, and the key that signs for it. The property everything else rests on is that the signing key OWNS the address that was displayed — same path, same curve, same code. If those ever disagreed the wallet would sign with a key that does not own the address it showed. The transaction being rejected by the network is the GOOD outcome; the bad one is signing for an address somebody else controls. Tested across five indices, and separately by signing a message and verifying it under the address. Android can sign, and iOS's constraint does not apply. AccountKdf notes that a raw Ed25519 seed needs EdECPrivateKeySpec, which Android exposes only from API 33 — but the engine carries its own RFC 8032, so this works on any supported device. The wallet is not blocked on a platform floor. A fresh address per receive, by default rather than on request. Reusing one lets anybody watching a public chain group every payment somebody ever received — the single linkability a wallet can actually prevent, because the chain will not forget. `looksLikeAddress` says WELL FORMED, never CORRECT, and the difference is money. A Solana address carries no checksum, so one mistyped character that still decodes to 32 bytes is a perfectly valid address nobody holds the key for. Bitcoin's Base58Check would catch it; this cannot, and neither can the network. There is a passing test that goes and FINDS such a near-miss, so the limit is recorded rather than implied — and it fails if the format ever gains a checksum, which is the point at which the warnings around typed addresses can be revisited. A test bug found by the test failing rather than passing: `Ed25519.verify` takes (publicKey, signature, message) and I had passed (publicKey, message, signature). All three are ByteArray, so it compiled perfectly. Noted in the file — it is a decent argument for asserting the signature verifies at all rather than trusting that a key which derives must be a key that signs. 12 tests. App builds, every JVM suite green. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
paulhitt commented 2026-09-04 18:19:00 +00:00 (Migrated from gitlab.com)

assigned to @paulhitt

assigned to @paulhitt
paulhitt commented 2026-09-04 18:19:02 +00:00 (Migrated from gitlab.com)

restored source branch feat/wallet-keys-and-address

restored source branch `feat/wallet-keys-and-address`
paulhitt commented 2026-09-04 20:49:34 +00:00 (Migrated from gitlab.com)

deleted the feat/wallet-engine-primitives branch. This merge request now targets the main branch

deleted the `feat/wallet-engine-primitives` branch. This merge request now targets the `main` branch
paulhitt commented 2026-09-04 20:49:37 +00:00 (Migrated from gitlab.com)

mentioned in commit 9d2dca803b

mentioned in commit 9d2dca803bb548c4c7b87c468df1043657eb0bad
paulhitt (Migrated from gitlab.com) merged commit 9d2dca803b into main 2026-09-04 20:49:37 +00:00
paulhitt commented 2026-09-09 19:01:32 +00:00 (Migrated from gitlab.com)

changed the description

changed the description
Sign in to join this conversation.