DogeOS Chikyu adversarial fund-loss audit — 2026-10-01
Remediation update: Both source findings have been addressed in local frontend and relayer worktrees. See REMEDIATION.md for changes, regression results and rollout limits. The audit below describes the pre-fix behavior.
Scope and conclusion
This review covers the new /dogeos DogeOS Chikyu integration (chain ID 6281971): the browser's wallet/key derivation and JoinSplit V2 send/recovery paths, the EVM relayer's fee and submission journals, and the deployed-contract source and tests. It does not cover native Dogecoin UTXO transfers. Chikyu is configured as a testnet; no mainnet DogeOS deployment is enabled in the reviewed network record.
I found one high-severity conditional loss-of-access path and one medium-severity reorganization/reconciliation defect. I did not reproduce a contract-level unauthorized withdrawal or an automatic loss of vault funds. The high-severity path becomes irreversible if a non-deterministic wallet's first derived spending signature is lost after receiving notes. The reorg defect leaves funds in the pool in the reproduction, but falsely reports a completed withdrawal and blocks normal recovery.
Findings
D1 — High: non-deterministic wallet signatures can strand received notes
Location: privacy-frontend/src/base-app/hooks/useBaseWallet.ts:110-125, privacy-frontend/src/base-app/helpers/baseDerivation.ts:88-135, privacy-frontend/src/base-app/helpers/storage.ts:57-71,168-211.
connectWallet requests the derivation EIP-712 signature once, hashes the raw signature bytes into the spending seed, and marks the wallet connected. There is no determinism check. The DogeOS integration plan explicitly requires a two-signature D8 gate because embedded/MPC wallets may sign the same EIP-712 challenge non-deterministically (privacy-protocol/DOGEOS_MYDOGE_JOINSPLIT_PLAN.md:301-304). If the wallet produces signature A on initial connect and B after a restart, both valid for the same account and challenge, the application derives two different private accounts. A note delivered to A cannot be spent with B. Encrypted local caches and remote recovery records are also keyed to the signature-derived account; they do not restore A from B. The raw signature is kept in a React ref for the session, not as a recoverable wallet secret.
Reproduction: derivation-signature-reproduction.cjs constructs two distinct, valid EIP-712 signatures by the same test account for one DogeOS derivation challenge. ethers.verifyTypedData recovers the same account from both; the exact SHA-256-over-raw-signature operation used by baseDerivation.ts yields different seeds. It passes locally. This establishes the failure mode, not that any particular production MyDoge device currently signs this way. The MyDoge V3 device qualification matrix is still blank, including row 5 for signature determinism (privacy-frontend/docs/dogeos-mydoge-qualification.md).
Fix before accepting deposits: Fail closed when two signatures of the identical locally constructed challenge differ; preserve a signature fingerprint across sessions and reject changed derivation before exposing a receive address. Provide a durable, user-controlled recovery method for the original seed/signature, because a later wallet implementation change could otherwise strand already received notes. Qualify actual MyDoge injected, WalletConnect, and embedded/social wallet paths on devices.
D2 — Medium: an included withdrawal reorgs out but relayer replays stale success
Location: stealthos-relayer/src/relayer/evm-pool-v2/journal.js:117-139, stealthos-relayer/src/relayer/transaction-handler.js:1285-1323, stealthos-relayer/src/relayer/evm-submission-journal.js:87-148.
The settlement journal returns a cached confirmed row before checking a receipt. The transaction handler then returns status: "success" with its old hash. On Chikyu, the underlying submission journal permits the next signer nonce after canonical L2 inclusion, before L1 finality, and explicitly handles a later missing receipt as a reorg. These two journals disagree about what confirmed means.
Reproduction: run-relayer-reorg-reproduction.ps1 runs a test against the real handler: submit a DogeOS withdrawal with a canonical receipt while the finalized head lags; remove the receipt and reset the signer nonce to simulate a reorg; retry the same withdrawal. The handler returns the old hash as confirmed without reading the receipt or rebroadcasting. A second withdrawal is then blocked by the submission journal's unresolved nonce gate. The test passes and the source checkout remains unchanged.
Impact: The original withdrawal is not paid on the simulated canonical chain, yet the app can report payment complete. Its notes remain in the pool; the reproduction does not show irreversible loss or a double spend. The user can nevertheless act on a false payment confirmation, and normal retries do not clear the condition.
Fix: Treat a settlement as final only after the configured finality criterion and a canonical receipt check. For an included but unfinalized attempt, return a pending/reconciliation state and retain the durable signed bytes, nullifier claims, gas reservation, and signer nonce gate. On reorg, rebroadcast or reconcile the original signed attempt; never allocate a new nonce merely because a timeout elapsed. A retry with the same nullifiers but different calldata must locate the outstanding original attempt rather than opening a competing settlement.
Verification and limits
- Foundry JoinSplit/DogeOS selection: 157 passed, 0 failed, 10 skipped (
contracts-baseline.log). The skipped tests require fork conditions or external setup; they are not proof of deployed bytecode behavior. - EVM spend-policy, real handler, submission journal, and pool tests: 55 passed (
submission-baseline.log). Additional quote, route, registry, gateway, fee, and journal tests: 27 passed (relayer-no-db.log). - Selected frontend suites including DogeOS configuration, authentication, recovery, settlement, and note sync: 155 suites / 1,158 tests passed (
frontend-baseline.log). - The PostgreSQL-backed engine handle tests could not run because this workspace has no configured
ENGINE_DATA_TEST_POSTGRES_URLor local server. Their failed setup is recorded inrelayer-baseline.log; it is an environment limitation, not a product failure. - No live MyDoge device signing, Chikyu RPC finality, deployed bytecode, or end-to-end funded transfer was verified in this audit. No transaction was broadcast by the reproduction tests.
The contract tests and source review show useful protections, including exact ERC-20 deposit balance accounting and fail-closed proof/domain checks. Those checks do not mitigate D1 or D2, which occur in key derivation and the relayer state machine.