You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
octo-wallet-core is entirely Stellar: SEP-0005 SLIP-0010 ed25519 derivation at m/44'/148'/index' (derive.rs:16),
strkey addresses, XDR signing. EVM needs a different curve (secp256k1), a different derivation
standard (BIP-32/BIP-44, non-hardened change/index levels), a different coin type (60), and
a different address format (last 20 bytes of the keccak-256 of the uncompressed public key, with an EIP-55 mixed-case checksum).
Create crates/evm-core (octo-evm-core) providing these primitives and an EvmAdapter
implementing ChainAdapter. This issue also owns resolving the alloy MSRV question (see §2)
and its resolution binds #218, #225, #226.
Requirements and context
This crate handles secret key material. It inherits wallet-core's full lint wall and zeroize-on-drop discipline. A panic in this crate can surface key bytes in a backtrace.
Derivation path: m/44'/60'/0'/0/{index} — note the last two levels are non-hardened, unlike
Stellar's all-hardened SEP-0005 path. This is what allows xpub-based address derivation, and it
is also why a leaked child private key plus the xpub compromises every sibling key. Document
that consequence prominently; feat(store): EVM per-customer deposit addresses via HD derivation #220 and feat(wallet): EVM deposit sweep engine #224 depend on understanding it.
Reuse octo-crypto for sealing unchanged. Do not add a second
encryption scheme. The context AAD string must distinguish chains (e.g. "octo:eip155:1") so a
sealed EVM key cannot be opened in a Stellar context.
Test vectors are mandatory, per CONTRIBUTING.md: "crypto and derivation
code must include test vectors."
Security: low-s signature normalisation (EIP-2) is required or signatures are malleable.
Constant-time secret comparison. Never construct a signing key from an uncontrolled byte slice
without validating it is in the curve order.
Suggested execution
Branch: feat/evm-core-keys-and-addresses
Implement changes
Add crates/evm-core; resolve and document the crate-selection decision (alloy-primitives
vs k256 + sha3 + bip32) against MSRV 1.84.1 and cargo deny check in a short ADR in docs/architecture.md.
derive.rs: BIP-32 secp256k1 derivation from the same BIP-39 mnemonic type already used, at m/44'/60'/0'/0/{index}, returning a Zeroizing secret.
address.rs: keccak-256 → 20-byte address; EIP-55 checksum encode; a validate_address that
accepts all-lowercase, all-uppercase, and correctly-checksummed mixed case, and rejects an
incorrect mixed-case checksum (a typo-detection property that silently vanishes if you
lowercase before comparing).
signer.rs: sign a 32-byte digest, produce (r, s, v) with low-s normalisation, and recover
the signer address. Mirror the "no raw-XDR oracle" posture of wallet-core/src/signer.rs: expose typed operations, not
a sign-arbitrary-bytes primitive.
BIP-32/BIP-44 test vectors from BIP-32's own Test Vector 1 and 2, plus a known
mnemonic→address set cross-checked against an independent implementation (MetaMask or ethers.js) — record the source in the test file.
EIP-55 test vectors: every example from the EIP-55 spec, plus the negative cases
(wrong-checksum mixed case must be rejected).
Signature tests: known (digest, key) → (r, s, v) vectors; assert s is always in the lower
half of the curve order; round-trip recovery returns the signing address.
Assert secrets are zeroized on drop.
A test that a seed sealed with an eip155 context fails to open with a stellar context.
Example commit message
feat(evm-core): secp256k1 derivation, EIP-55 addresses, signing
Adds BIP-44 m/44'/60'/0'/0/i derivation, keccak-256 address encoding
with EIP-55 checksum validation, and low-s normalised signing (EIP-2).
Seals reuse octo-crypto with a chain-scoped AAD context so an EVM key
cannot be opened in a Stellar context.
Verified against BIP-32 vectors 1-2 and the full EIP-55 example set.
Refs #217
Guidelines
Security-sensitive: requires maintainer review and will not be merged without test vectors from an
authoritative source. Cite where each vector came from.
Depends on: #213. Blocks: #218, #220, #225.
Description
octo-wallet-coreis entirely Stellar: SEP-0005 SLIP-0010ed25519 derivation at
m/44'/148'/index'(derive.rs:16),strkey addresses, XDR signing. EVM needs a different curve (secp256k1), a different derivation
standard (BIP-32/BIP-44, non-hardened change/index levels), a different coin type (60), and
a different address format (last 20 bytes of the keccak-256 of the uncompressed public key, with an
EIP-55 mixed-case checksum).
Create
crates/evm-core(octo-evm-core) providing these primitives and anEvmAdapterimplementing
ChainAdapter. This issue also owns resolving thealloyMSRV question (see §2)and its resolution binds #218, #225, #226.
Requirements and context
wallet-core's full lint wall andzeroize-on-drop discipline. A panic in this crate can surface key bytes in a backtrace.m/44'/60'/0'/0/{index}— note the last two levels are non-hardened, unlikeStellar's all-hardened SEP-0005 path. This is what allows xpub-based address derivation, and it
is also why a leaked child private key plus the xpub compromises every sibling key. Document
that consequence prominently; feat(store): EVM per-customer deposit addresses via HD derivation #220 and feat(wallet): EVM deposit sweep engine #224 depend on understanding it.
octo-cryptofor sealing unchanged. Do not add a secondencryption scheme. The
contextAAD string must distinguish chains (e.g."octo:eip155:1") so asealed EVM key cannot be opened in a Stellar context.
CONTRIBUTING.md: "crypto and derivationcode must include test vectors."
ssignature normalisation (EIP-2) is required or signatures are malleable.Constant-time secret comparison. Never construct a signing key from an uncontrolled byte slice
without validating it is in the curve order.
Suggested execution
Branch:
feat/evm-core-keys-and-addressesImplement changes
crates/evm-core; resolve and document the crate-selection decision (alloy-primitivesvs
k256+sha3+bip32) against MSRV 1.84.1 andcargo deny checkin a short ADR indocs/architecture.md.derive.rs: BIP-32 secp256k1 derivation from the same BIP-39 mnemonic type already used, atm/44'/60'/0'/0/{index}, returning aZeroizingsecret.address.rs: keccak-256 → 20-byte address; EIP-55 checksum encode; avalidate_addressthataccepts all-lowercase, all-uppercase, and correctly-checksummed mixed case, and rejects an
incorrect mixed-case checksum (a typo-detection property that silently vanishes if you
lowercase before comparing).
signer.rs: sign a 32-byte digest, produce(r, s, v)with low-snormalisation, and recoverthe signer address. Mirror the "no raw-XDR oracle" posture of
wallet-core/src/signer.rs: expose typed operations, nota sign-arbitrary-bytes primitive.
EvmAdapterimplementingChainAdapter, passing feat(chain): Introduce the octo-chain adapter trait and refactor Stellar behind it #213'schain_conformance_suite.Test and commit
mnemonic→address set cross-checked against an independent implementation (MetaMask or
ethers.js) — record the source in the test file.(wrong-checksum mixed case must be rejected).
(digest, key) → (r, s, v)vectors; assertsis always in the lowerhalf of the curve order; round-trip recovery returns the signing address.
eip155context fails to open with astellarcontext.Example commit message
Guidelines
Security-sensitive: requires maintainer review and will not be merged without test vectors from an
authoritative source. Cite where each vector came from.