Description
crates/wallet-core/src/derive.rs only asserts derivation against the single official SEP-0005 test vector plus a handful of hand-picked indices (0, 1, 2, 5). The hardened path m/44'/148'/index' is security-sensitive: a subtle off-by-one in the hardened-offset math or path encoding would silently derive the wrong keypair for some indices while passing every existing test. We need property-based coverage across the full u32 index space (mod hardened bit) and randomized mnemonics to catch that class of bug.
Requirements and Context
- Add
proptest as a dev-dependency of crates/wallet-core (workspace-pinned, dev-only).
- Properties to hold for any valid 12-word mnemonic and any
u32 index: derivation is deterministic (same input -> same output byte-for-byte), different indices from the same seed produce different secrets with overwhelming probability, and the derived public key always round-trips through stellar_strkey encode/decode.
- Include the reserved boundary indices explicitly:
0, 1, u32::MAX, and HARDENED - 1 (0x7fffffff), since these exercise the hardened-offset OR-mask differently than mid-range values.
- Do not weaken
#![deny(clippy::unwrap_used, clippy::expect_used, clippy::panic)] in crates/wallet-core/src/lib.rs — proptest cases run under #[cfg(test)], which already relaxes those lints.
Suggested Execution
Branch: test/wallet-core/sep0005-property-derivation
Implement Changes
- Add
proptest = "1" under [dev-dependencies] in crates/wallet-core/Cargo.toml.
- In
crates/wallet-core/src/derive.rs's #[cfg(test)] mod tests, add a proptest! block generating random mnemonics via bip39::Mnemonic::new seeded from proptest's Rng (or generate random 16-byte entropy and feed it through Mnemonic::from_entropy) and random u32 indices.
- Assert determinism, distinctness across indices, and successful ed25519 keypair construction (
ed25519_dalek::SigningKey::from_bytes) for every generated case.
Test and Commit
proptest case: derivation_is_deterministic_for_any_index (same seed+index twice -> identical secret).
proptest case: distinct_indices_yield_distinct_secrets (two different random indices from the same seed differ).
#[test] fn boundary_indices_derive_without_panic covering 0, 1, u32::MAX, HARDENED - 1.
- Run
cargo test -p octo-wallet-core locally before committing.
Example Commit Message
test(wallet-core): add property-based SEP-0005 derivation coverage
Adds proptest cases exercising the hardened derivation path across randomized
mnemonics and the full u32 index space, plus explicit boundary-index tests, to
catch off-by-one errors the fixed test vector alone can't surface.
Guidelines
- Keep proptest case counts reasonable (the default 256 cases is fine) — this crate's CI budget is tight and ed25519 derivation is not free.
- Do not introduce any
unsafe code; #![forbid(unsafe_code)] is enforced crate-wide.
- Reference this issue with
Closes #<issue-number> in the PR description.
Description
crates/wallet-core/src/derive.rsonly asserts derivation against the single official SEP-0005 test vector plus a handful of hand-picked indices (0, 1, 2, 5). The hardened pathm/44'/148'/index'is security-sensitive: a subtle off-by-one in the hardened-offset math or path encoding would silently derive the wrong keypair for some indices while passing every existing test. We need property-based coverage across the fullu32index space (mod hardened bit) and randomized mnemonics to catch that class of bug.Requirements and Context
proptestas a dev-dependency ofcrates/wallet-core(workspace-pinned, dev-only).u32index: derivation is deterministic (same input -> same output byte-for-byte), different indices from the same seed produce different secrets with overwhelming probability, and the derived public key always round-trips throughstellar_strkeyencode/decode.0,1,u32::MAX, andHARDENED - 1(0x7fffffff), since these exercise the hardened-offset OR-mask differently than mid-range values.#![deny(clippy::unwrap_used, clippy::expect_used, clippy::panic)]incrates/wallet-core/src/lib.rs— proptest cases run under#[cfg(test)], which already relaxes those lints.Suggested Execution
Branch:
test/wallet-core/sep0005-property-derivationImplement Changes
proptest = "1"under[dev-dependencies]incrates/wallet-core/Cargo.toml.crates/wallet-core/src/derive.rs's#[cfg(test)] mod tests, add aproptest!block generating random mnemonics viabip39::Mnemonic::newseeded from proptest'sRng(or generate random 16-byte entropy and feed it throughMnemonic::from_entropy) and randomu32indices.ed25519_dalek::SigningKey::from_bytes) for every generated case.Test and Commit
proptestcase:derivation_is_deterministic_for_any_index(same seed+index twice -> identical secret).proptestcase:distinct_indices_yield_distinct_secrets(two different random indices from the same seed differ).#[test] fn boundary_indices_derive_without_paniccovering0,1,u32::MAX,HARDENED - 1.cargo test -p octo-wallet-corelocally before committing.Example Commit Message
Guidelines
unsafecode;#![forbid(unsafe_code)]is enforced crate-wide.Closes #<issue-number>in the PR description.