feat(express): verify created addresses against client-supplied trusted keychains#9109
feat(express): verify created addresses against client-supplied trusted keychains#9109rajangarg047 wants to merge 1 commit into
Conversation
| async function verifyCreatedAddressesWithTrustedKeys( | ||
| coin: ReturnType<BitGo['coin']>, | ||
| wallet: Wallet, | ||
| result: any, |
There was a problem hiding this comment.
is there a better typing we can use or?
There was a problem hiding this comment.
Good call — replaced result: any with a CreateAddressResult type (single address or { addresses: [...] }) whose fields reuse the DeriveAddressOptions types for the values we feed back into coin.deriveAddress. Done in d92773f.
| } | ||
| let derived; | ||
| try { | ||
| derived = await coin.deriveAddress({ |
There was a problem hiding this comment.
do we need to try catch here? does the dervieAddress not return a good error messaging? If it does, we can avoid doing let since we're not reassigning the var
There was a problem hiding this comment.
The try/catch is intentional: the caller opted in by supplying trustedKeychains, so a derivation failure (e.g. an unsupported coin/wallet) must surface as a 400 client error rather than deriveAddress's default 500 — that's what the "unsupported coin → 400" test asserts.
I did drop the let though: the mismatch check now lives inside the try block so derived is a const, and the catch rethrows ApiResponseError unchanged so the 400 mapping only applies to genuine derivation errors. d92773f.
d92773f to
16a192b
Compare
…ed keychains Add an opt-in "bring your own trusted keys" check to address creation. When the createAddress request includes trustedKeychains (public key material the caller holds independently), Express locally re-derives each newly created address via coin.deriveAddress and fails the request if it does not match what the service returned — an independent verification folded into the create call, with no client-side round-trip. Unlike the SDK's built-in check (which verifies using keychains fetched from the same service), this uses caller-supplied keys, so a match is an independent trust guarantee. It is purely per-request opt-in: requests without trustedKeychains are unchanged, so other clients/coins are unaffected, and no server config is needed. The caller opted in, so an unsupported coin/wallet surfaces a clear 400 rather than silently skipping. The createAddress result is modeled as a CreateAddressResult type (reusing DeriveAddressOptions types) instead of any, and derivation failures and mismatches alike map to 400. Token (e.g. SPL) deposit addresses derive differently and are not verified here yet (follows WCN-1054). WCN-1055 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
16a192b to
e5129b5
Compare
Summary
Adds an opt-in "bring your own trusted keys" check to address creation. When a
createAddressrequest includestrustedKeychains(public key material the caller holds independently), Express locally re-derives each newly created address viacoin.deriveAddressand fails the request (400) if it doesn't match the address the service returned — an independent verification folded into the create call, no client-side round-trip.Why this (and how it differs from the SDK's existing check)
wallet.createAddressalready verifies the new address — but using keychains it fetches from the same service (an integrity check; circular). This feature verifies against caller-supplied keys, so a match is an independent trust guarantee.Key properties:
trustedKeychainsare completely unchanged — so other clients/coins are unaffected and no server config/flag is needed.Scope note
Token (e.g. SPL) deposit addresses derive differently (associated token account) and are not verified here yet — that path depends on token derivation (WCN-1054, PR #9080) and is a fast-follow. Token-address creates (
onToken) are skipped rather than mismatched.Changes
trustedKeychainsto thecreateAddressbody (reuses theaddress/derivekeychain union codec).handleV2CreateAddressrunsverifyCreatedAddressesWithTrustedKeyswhen the field is present.Context
WCN-1055 (FR-465 Phase 2) — requested by Bullish. Chose the client-supplied-keys approach (vs an operator keystore) so it's a single transparent create call with the full independent-trust guarantee and no server-side key management.
Test plan
tscclean (express src);eslint0 errorsonToken→ skipped🤖 Generated with Claude Code