feat(kaspa): verify hardware tx inputs via on-device refTx#12515
feat(kaspa): verify hardware tx inputs via on-device refTx#12515ByteZhang1024 wants to merge 35 commits into
Conversation
…127) (#12297) * feat: add receive estimated arrival time utils * feat: redesign receive page layout with arrival time and share dialog * fix: polish receive page layout per design feedback * fix: refine receive copy icon layout and share card address styling * feat: add network badge and emphasized subtitle to receive share card * fix: treat whitespace-only banner href as non-pressable on receive page * fix: remove receive page vertical padding on small screens * chore: wire up receive page i18n keys * feat: append token standard tag to receive network name * fix: use whiteA1 cell background on receive cards in dark mode * fix: contain-fit receive share preview so dialog actions stay visible * fix: add space between receive arrival time value and unit * fix: center receive QR token logo on native * feat: pre-generate receive share image and fix copy address label * fix: use accent share dialog actions and dismiss dialog before system share * fix: use secondary share dialog buttons with DotHor more icon * fix: center share image token logo on native * refactor: simplify receive share internals per cleanup review * fix: use medium share dialog buttons on gtMd screens * chore: add receive page locale strings * fix: calibrate receive arrival times against Binance deposit policy
…12470) * feat: add Overview first tab for aggregated token details (OK-57944) Aggregated (multi-chain) tokens now open on a new "Overview" tab showing the cross-chain total balance, Send/Receive/Swap/Buy actions with per-action network resolution, an Earn banner with the whole-symbol max APY, and a per-chain asset allocation list. Tapping an allocation row jumps to that chain's tab and auto-enables the network if needed. Removes the per-token last-active-tab memory so the page always defaults to Overview. Extracts shared helpers (balance hero, swap navigation, fiat widget, aggregate token details/sort hook) reused by the header and chain tabs. * fix: keep aggregate Overview total consistent with home list (OK-57944) The home aggregate row only sums members on enabled networks (aggCell over aggMembership), but the Overview backfill fetched every member with an address — including disabled-network members the home never counted — inflating the Overview total above the home figure. Skip disabled-network members in the backfill (they keep the enable- network placeholder row and can still be filled in after enabling), and emit tap-time member seeds only for aggMembership networks so a stale sub-cell can never seed a balance the home row does not count. * fix: address Overview review feedback (OK-57944) - Unmark failed/empty backfill members so a later run retries instead of leaving the row on its placeholder forever. - Move the footer market-data context write out of the promise body so usePromiseResult's stale-run protection applies during fast tab switches, and include the price source currency. - Fingerprint aggregate member networkId/address pairs into the Earn block cache key so wallets with different member sets don't reuse each other's cached protocols. - Check fiat-buy support for the selected member before opening the widget; unsupported networks toast instead of failing generically.
* feat: support Spark Savings in Earn (OK-57595) * feat: support Bitway Earn integration(OK-56813) * fix: complete Spark queued withdraw integration (OK-57595) * fix: align Bitway assets with server scope (OK-56813) * fix: unify Earn withdrawal path readiness (OK-57595) OK-56813 * fix: preserve staking transaction simulation * fix: guard Earn withdrawal confirmation (OK-57595) OK-56813
* feat: add OKX-style TradingView settings gallery * refactor: split TradingView settings gallery components * fix: address TradingView settings review feedback * fix: stabilize tradingview settings confirmation * fix: match tradingview sub-indicator limit behavior
* fix: resolve iOS WalletConnect loading hang * fix: preserve WalletConnect gate lifecycle * fix: clear stale WalletConnect pairing payload * fix: cancel WalletConnect pairing on rapid modal close * fix: enable native background runtime for local iOS * fix: deduplicate WalletConnect modal in split view * fix: resolve CI lint failure * fix: start background Metro with iOS dev commands * fix: isolate Metro cache by native bg mode * fix: avoid dual Metro OOM in native dev * fix: preserve dialog safe area in iOS portals * fix: preserve pending WalletConnect approvals * fix: scope dialog inset fallback to WalletConnect * fix: harden WalletConnect connect attempt lifecycle (#12472) Review fixes for #12424: cancel superseded connect attempts, guard the finally CloseModal by attempt ownership, abort with the open-time uri snapshot instead of an empty wildcard, disconnect late-approved orphan sessions, clear the pairing store for interleaved sessions, scope the dialog inset fallback to iOS, and stop logging pairing uri symKeys. * fix: serialize WalletConnect connect attempts and scope terminal events (#12479) Round-2 review fixes for #12424: a generation counter serializes concurrent connectToWallet supersede transitions so stale calls yield instead of racing a second live attempt, and open/close/modal-state events carry the attemptId so terminal events only clear the pairing they belong to (stale close(A) delivered after open(B) no longer drops B back to loading). * fix: align Android debug runtime layout and scope modal close by attempt (#12480) Round-3 review fixes for #12424: Android debug commands now set ENABLE_NATIVE_BACKGROUND_THREAD=true so Gradle, Metro and the native runner agree on the dual-runtime layout, and the WalletConnect modal container only acts on attempt-scoped closes that match the pairing it is showing. * fix: match WalletConnect connect results to their attempt (#12483) Round-4 review fix for #12424: WalletConnectConnectSuccess/ConnectError now carry the attemptId and the native modal's module-level listeners drop events from superseded attempts, so a stale error can no longer reject the newer attempt's AppKit connection. * fix: guard pairing ownership reset and serialize web modal transitions (#12484) Round-5 review fixes for #12424: the native close effect clears pairing ownership only when the request that opened the closing modal is still the latest generation, and the web modal serializes open/close transitions through a generation-bound queue with pending/attributed ref split so stale closes are blamed on the superseded attempt. * fix: stop logging pairing uri in default connect modal callback (#12486) Round-6 review fix for #12424: the default updateConnectModalUri callback no longer logs the full wc: uri (its query carries the pairing symKey); the SDK still receives the uri via the pairingUri cache replay on display_uri registration.
…251 OK-57730 OK-57545) (#12487) * fix: remove order id from swap history detail(OK-57251) * fix: unify swap network logo to circular(OK-57730) * fix: disable swap action button when no provider supports trade(OK-57545)
…continuation) (#12478) * chore: add main+bg concurrent AsyncStorage write test tooling Follow-up to the AsyncStorage write-forwarder fix (#12287). Adds a way to exercise and regression-check the dual-runtime no-clobber guarantee that the forwarder provides on iOS. - ServiceDevSetting: dev-only bg-runtime AsyncStorage helpers (demoAsyncStorageBgMultiSet/MultiGet/MultiRemove). Reached via backgroundApiProxy RPC so they execute bg-local, producing genuine bg-origin writes. Guarded to a fixed test-key prefix so the dev RPC can never touch real storage keys. - AsyncStorageDevSettings: "Concurrent main+bg write test" button that interleaves main-origin writes (forwarded to bg on iOS) with bg-origin writes, then reads back from both runtimes and reports any dropped or mismatched key. Existing buttons are untouched. - asyncStorageWriteForwarderTypes.test.ts: unit tests for the request-status fence serialize/parse layer (round-trip, enum/type validation, malformed input) that the timeout / bg-restart replay safety depends on. * chore: raise web allScriptRawBytes startup budget to 90.5MiB The Web startup graph budget check failed on this branch at allScriptRawBytes 90.03 MiB / 90.00 MiB (over by ~29 KiB); every other budget in the report passes. This overage is not from this branch's dev-only test tooling: the same budget passed when the branch was at its own commit (base d343fc1). It only exceeded after the two `Merge branch 'x'` commits pulled newer x content into the bundle, so this is x-side cumulative graph growth surfacing here. Raise allScriptRawBytes from 90.00 MiB (94371840) to 90.5 MiB (94896128), matching the existing half-MiB style used by sourceSizeBytes. Budget owners can re-tighten after trimming the web startup graph in x. * fix: drain all concurrent writes before cleanup in AsyncStorage dev test Addresses review feedback: the concurrent main+bg write test used Promise.all(writeTasks), which rejects on the first failed write while the other writes — especially the in-flight bg-origin RPCs — keep running. The catch/finally then ran multiRemove(allKeys) cleanup immediately, so a late bg write could land after cleanup and leave dirty test keys, making the next run start unclean and the ERROR/FAIL result unreliable. Use Promise.allSettled so the whole write path converges before read verification and cleanup, and surface the rejected-write count (writeFail) in the result; PASS now also requires zero write failures. * fix: route AsyncStorage dev-test cleanup through bg-direct remove Addresses review feedback: the concurrent test's clean-slate and finally cleanup used appStorage.multiRemove(allKeys), i.e. the main-runtime write path that is itself under test. If the forwarder/transport is unhealthy — the exact failure this tool diagnoses — that cleanup goes through the same broken path and cannot be trusted to clear bg-origin keys, so the next run may start dirty and distort PASS/FAIL. Route both setup and teardown through demoAsyncStorageBgMultiRemove (direct on the single-writer bg runtime), which authoritatively clears both main-forwarded and bg-origin keys from the shared native store regardless of forwarder health. * fix: isolate AsyncStorage dev-test runs with per-run id and unmount guard Addresses review feedback: the concurrent test reused a fixed keyspace with no run id or cancel semantics. If the dialog is closed mid-run and reopened, the old and new component instances operated on the same keys — the old run's finally cleanup could delete the new run's data, and reads could pick up a stale run's values, yielding false PASS/FAIL. - Namespace every key with a unique per-run runId so overlapping runs use disjoint keyspaces; an old run's cleanup and reads only ever touch its own keys. - Track mount state via a ref and skip result/toast/state updates once the instance has unmounted, so a run that outlives its dialog cannot surface UI for (or over) a newer instance. * chore: lazy-load AsyncStorageDevSettings dev panel --------- Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Zhao <Charon.Dian@gmail.com>
* feat: add in-app featured changelog preview via deeplink Ops-only tool so operations staff can verify a dashboard-configured Featured Changelog inside a production build. Reached only via the onekey-wallet://preview-featured-changelog?version=X.Y.Z deeplink (no in-app entry point; JS-only, hot-updatable). - ServiceAppUpdate.previewFeaturedChangelog overrides the request version header so the server's real update-selection pipeline runs for the target version, then normalizes and returns without touching any atom/cache (zero side effects on the real update flow). - axiosInterceptor preserves a per-request version header override. - showFeaturedChangelogDialog accepts injected previewData: renders off the payload, forces the force-lock off, keeps the already-upgraded CTA live while making the not-upgraded CTA inert, and suppresses the onClose refetch and whatsNewClosed analytics in preview. - New deeplink path + AppUpdateModal screen (FeaturedChangelogPreview). * fix: route featured changelog preview QR through in-app scanner The in-app QR scanner parses through ServiceScanQRCode (bg), where no handler matched preview-featured-changelog, so the DEEPLINK fallback won and the UI showed only the raw Info/Copy dialog. Add a dedicated scan handler + UI case (mirroring updatePreview/rewardCenter) that opens the FeaturedChangelogPreview page with the version param. Also improve the preview page after live desktop verification against the test-env utility: clearer no-match guidance (the server treats the requested version as the client's CURRENT version and serves the newest release above it, so the featured changelog must be configured on the version the server actually selects), and auto-fetch on deeplink arrival keyed by param value. * feat: fetch featured changelog preview via dedicated endpoint The production /app-update response only attaches featuredChangelog to the release its selection pipeline currently delivers, so previewing any other version was impossible (and forcing it in prod would require creating a real higher release row — which would actually ship). Switch the preview to the new read-only /utility/v1/app-update/featured-changelog-preview endpoint (direct lookup by version, same collection and locale resolution as production, zero side effects), and keep the real app-update request as a best-effort online-comparison signal so operators can see which release the pipeline would deliver right now and whether it carries a featured changelog. * fix: make featured carousel close button clickable on web The arrow hot-zone is a full-height absolute strip relying on pointerEvents="box-none" to stay click-transparent. 'box-none' has no CSS equivalent — on web/desktop it computes to 'auto', turning the strip into a solid click layer that swallowed the top-right close button (verified via elementsFromPoint: a 72x240 div above the button). Use CSS 'none' on web with the arrow button explicitly restoring 'auto'; keep native's real 'box-none'. Affects the production featured dialog on desktop/web too, not just the ops preview. * fix: drop side-effecting online-comparison call from changelog preview Cross-review (Codex) found the preview's secondary "online comparison" request to the real /utility/v1/app-update writes to shared server state: getLatestAppVersion does a grayscale `$inc reachedGrayScalePeopleNumbers` when the selected release is under grayscale rollout, so previewing would consume real grayscale slots — violating the preview's zero-side-effect guarantee. Remove that secondary request entirely; the read-only /featured-changelog-preview endpoint already returns exactly the requested version. This also removes: - the now-dead x-onekey-request-version interceptor carve-out (nothing sets that per-request header anymore); - the version-mixing vector (previewData no longer carries an online release's storeUrl/downloadUrl/updateStrategy alongside another version's featured content) — IFeaturedChangelogPreviewData trimmed to featuredChangelog + latestVersion. Also invalidate the cached fetch when the operator edits the version input, so a preview button can never render a previously-fetched version. --------- Co-authored-by: morizon <sidmorizon@outlook.com>
…OK-58002) (#12469) * style: compact wallet banner height and redesign Tron resource card - Reduce banner item height 108->88 and image 60->56 - Redesign Tron resource card as compact full-width stacked rows (ring + name + amount) - Sync native header height estimates (312->292, 522->502) - Extract shared BANNER_ITEM_HEIGHT / TRON_CARD_WIDTH and getResourcePercentage helper * fix: cap wallet banner title lines to fit compact card height Title allowed 3 lines while the 88pt card only has a 56pt content box, so long or localized titles could spill past the card border. Cap to 1 line when a description is present, 2 otherwise. * fix: reserve banner icon footprint so long titles do not collide The decorative icon is anchored bottom-right, so at the compact 88pt card height it rises into the title's line band (icon y 48-72 vs title y 34-58) and would overlap a long title. Reserve 24pt icon + 8pt gap on the text column in the icon branch.
* docs: optimize agent context loading * docs: remove obsolete native and sentry skills * docs: remove generic React best-practice skills * chore: enforce agent context budgets * chore: run agent context lint concurrently * fix: remove stale skill references * fix: restore skill tooling behavior * fix: validate YAML block scalar modifiers * fix: support YAML block indentation indicators * fix: handle YAML frontmatter variants * fix: parse agent context YAML correctly * fix: align yaml dependency version * fix: harden agent context validation * fix: count complete skill file lines * fix: validate skill invocation flag
* refactor: migrate desktop and webembed to rspack v2 * fix: unblock rspack migration checks * fix: add jiti loader to desktop rspack workflows * fix: strengthen web-embed compatibility checks * fix: restore desktop rspack defaults * fix: match spaced script end tags * fix: harden web-embed script extraction * fix: harden web-embed production finalization * fix: finalize desktop production assets * fix: align node engine with rspack * fix: align web embed sentry release
* feat: open external links in native in-app browser (OK-57922) * feat: present iOS in-app browser as page sheet with close button * refactor: simplify external link open paths * fix: keep web firmware tool on system browser in error toast (OK-57922) * fix: honor backend useSystemBrowser flag on banner links (OK-57922) * fix: keep desktop perp history links in webview modal and widen social host handoff (OK-57922) * refactor: centralize webusb host policy and dedupe system-browser fallback * fix: report in-app open analytics only after successful presentation (OK-57922) * fix: keep notification openInBrowser mode on the system browser (OK-57922) * feat: present ios in-app browser full screen (OK-57922)
* feat: unify OneKey ID and Keyless OAuth Unify OneKey ID and Keyless Wallet OAuth sessions around the Keyless Supabase OAuth flow: new OneKey ID login uses Google/Apple OAuth, while legacy email OTP remains a low-priority fallback for existing accounts. Keyless Wallet and OneKey ID share the OAuth session model, so upgrade paths work for users who already have Keyless, OneKey ID, both, or neither. - separate LegacyEmailSupabase and KeylessOAuth session sources; read active tokens from the matching Supabase session instead of storing OAuth access tokens - serialize all auth-state writes (source + prime atom) through authStateWriteMutex with in-lock rechecks; scope invalid-token cleanup to the matching session source - delete the legacy keyless refresh-token blob only on definitive GoTrue rejections; persist rotated tokens before any fallible network call; guard passcode-change rekey under the exchange mutex - seal Supabase sessions with a non-extractable device key + AES-256-GCM on ext/web/dev-desktop; re-read before resealing a rotated session; never pin a transient device-key failure into plaintext fallback - relay ext action-popup OAuth to the expand tab and resume it across hash-only tab reuse - classify the linked keyless logout by the device-local authSessionSource, never server-side account identities - forbid creating a OneKey ID via email OTP (shouldCreateUser=false), keep passcode-cancel from opening the browser OAuth, and stop logging OTP auth responses * fix: address #12507 review findings (transient sealed-session read, pinned request token) (#12511) * fix: treat transient sealed-session device-key failure as retryable * fix: pin snapshotted request token header in callApiFetchPrimeUserInfo --------- Co-authored-by: Claude <noreply@anthropic.com> * fix: gate keyless teardown and invalid-token events on auth-state generation * fix: gate the post-lock invalid-token session sweep on auth-state generation * fix: pass invalid-token request token via WeakMap stash instead of error property * fix: serialize session-slot deletions on a bg-owned generation-gated queue * fix: replace slot re-reading signOut with snapshotted server revocation in gated session clear --------- Co-authored-by: Claude <noreply@anthropic.com>
|
Review the following changes in direct dependencies. Learn more about Socket for GitHub.
|
| }; | ||
| } | ||
|
|
||
| public refTxFetchFailed({ |
There was a problem hiding this comment.
当 refTx 获取失败时,调用方会执行这个方法,但这里没有 @LogToLocal(),因此它只是返回对象,不会进入日志管线。结果是 blind-sign 降级发生后没有任何可追踪记录,和调用方注释声明的诊断能力不一致。
请为该方法加上 @LogToLocal(),确保失败事件实际落到本地日志。
| inputs: (j.inputs ?? []).map((input) => ({ | ||
| prevTxId: input.previous_outpoint_hash, | ||
| outputIndex: Number(input.previous_outpoint_index ?? 0), | ||
| sequenceNumber: input.sequence ?? 0, |
There was a problem hiding this comment.
🟡 P2 buildPrevTx 读取的多个字段在代理响应中并不存在,静默降级为 0
问题
JSON 分支通过 IKaspaRestTx 读取前序交易的 version / lock_time / gas 以及每个 input 的 sequence,但该数据实际来自 sendProxyRequest<IKaspaGetTransactionResponse>(api.kaspa.org 的 /transactions/{id}),其响应体并不包含这些字段。于是 sequenceNumber: input.sequence ?? 0、version、lockTime、gas、script_public_key_version 恒为 0,与真实前序交易无关。
影响
设备会用 refTx 重算前序交易的 txId 来校验每个输入。对标准 OneKey 交易(version/lockTime/gas/sequence 恰好都为 0)能对上;但一旦被花费 UTXO 的前序交易带有非零 sequence/lockTime/version(来自其他钱包、矿池 coinbase 等),重算出的 txId 与 input 的 prevTxId 不一致,设备将中途拒签。此时 refTxs 已成功构建,代码不会再回退盲签,而是直接 throw convertDeviceError(response.payload),导致该 UTXO 在硬件钱包上无法花费(可用性/资金问题),且与 catch 分支的 fallback 是不同代码路径。
建议
明确这些字段无法从该 REST 端点取得(从 IKaspaRestTx 移除误导性可选字段或加注释),并在设备因 refTx 校验失败(response.success === false 且 refTxs 非空)时回退到不带 refTxs 的盲签,避免硬失败。
Generated by Claude Code
Co-authored-by: Claude <noreply@anthropic.com>
* ci: add minimal KMS fetch verification workflow * ci: validate decrypted KMS test payload --------- Co-authored-by: Zhao <Charon.Dian@gmail.com>
* fix: consolidate pending issue fixes (OK-57753, OK-57836, OK-57752, OK-57547, OK-57451, OK-54672, OK-57787, OK-56469, OK-56028) (#12461) * fix: stabilize Swap Pro account warning (OK-57753) * fix: center editable DeFi percentage value (OK-57836) * fix: keep Market network labels unshrunk (OK-57547) * fix: keep Limit history available when disconnected (OK-57451) * fix: use decimal keyboards for trading inputs (OK-54672) * fix: prepare Swap Pro before navigation (OK-57752) * fix: clear stale Stock balance on Swap exit (OK-57787) * fix: align Earn provider header breakpoint (OK-56469) * fix: expand Private Send quote toggle target (OK-56028) * fix: handle Stock market data failures * fix: preserve stock balance and swap pro account scope * feat: align swap k-line and same-chain token selection (OK-57650, OK-57751) (#12436) * feat: align swap k-line chart controls (OK-57650) * fix: hide duplicate swap chart interval selector OK-57650 * chore: update translations * feat: support same-chain token selection (OK-57751) * chore: update translations * fix: resolve Jira UI issues(OK-57565, OK-52118, OK-56004, OK-55516, OK-56209, OK-57648, OK-57924) (#12468) * fix: honor Pendle tooltip text styles OK-57565 * fix: make borrow detail tabs scrollable OK-52118 * fix: deduplicate same-symbol K-line tokens OK-56004 * fix: hide estimated swap savings OK-55516 * fix: hide swap keyboard accessory on iPad OK-56470 * fix: keep extension side panel on Trade OK-57350 * fix: guard swap derive selector by network OK-56209 * fix: align swap slippage boundaries OK-54577 * fix: preserve WebDapp route through modals OK-57648 * fix: use OneKey token metadata for limits OK-57550 * revert: restore iPad swap keyboard accessories OK-56470 * revert: restore extension Perps home navigation OK-57350 * revert: restore previous swap slippage rules OK-54577 * fix: align limit order metadata with Market detail OK-57550 * fix: avoid blocking limit order status refresh * fix: keep borrow detail tab visible OK-52118 * revert: remove OK-57550 from PR scope * fix: stabilize Earn protocol balances OK-57924 * fix: preserve supported K-line side OK-56004 * fix: tighten WebDapp route fallback OK-57648 * fix: address route and K-line edge cases OK-57648 OK-56004 * fix: ignore network filter for market stocks(OK-57973) (#12490) * fix: adjust market chart height(OK-56353, OK-57794) (#12180) * fix: adjust market chart height(OK-56353, OK-57794) * fix: limit market sub indicators on mobile only * fix: remove perps mobile chart touch bridge * fix: tune mobile chart scroll sensitivity * fix: translate Perps fallback strings (#12499) * feat: add attempt-level firmware update tracking(OK-57543) (#12481) * feat: add attempt-level firmware update tracking(OK-57543) - add firmwareUpdateFailedAttempt event: report each real update-task failure (before user retry) with deviceType/transportType/updateFlow/ firmwareVersions/attempt/errorCode, so attempt-level failure rate is measurable even when a later retry succeeds - add retryCount & durationMs to firmwareUpdateResult (v1 UI flow and v2 service flow), enabling retry-recovery-rate and USB-vs-BLE duration analysis - user exit (FirmwareUpdateExit/FirmwareUpdateTasksClear) is not counted as a failed attempt * fix: make tracking-info reading in UI never reject to avoid flipping a successful update into failed state * fix: exclude retry wait from firmware duration * fix: track every firmware update attempt * fix: isolate firmware update tracking workflows --------- Co-authored-by: Leon <lixiao.dev@gmail.com> * fix: correct Swap and Market display states (OK-57526, OK-57987, OK-57998) (#12494) * fix: align Swap K-line toolbar spacing (OK-57526) * fix: hide unavailable token audit UI (OK-57987) * fix: enrich Swap history network metadata (OK-57998) * chore: simplify Swap and DeFi readiness checks * fix: align swap k-line skeleton layout OK-57526 * fix: show unavailable mobile audit state (OK-57987) * fix: prevent incorrect swap recipient fallback * fix: refine Japanese Perps translations (#12505) * fix: prevent mobile isolated label clipping * fix: widen mobile margin mode selector * fix: refine Japanese Perps translations --------- Co-authored-by: Zhao <Charon.Dian@gmail.com> Co-authored-by: limichange <limichange@hotmail.com> Co-authored-by: Kahn <0xkahnchan@gmail.com> Co-authored-by: LeoShang <biubiu520520@gmail.com>
#12521) * fix: surface keyless OAuth session persist failures in OneKey ID login * feat: log OneKey ID login failures and fingerprint non-JSON auth error pages * fix: classify intercepted auth error-page body instead of logging raw content --------- Co-authored-by: Claude <noreply@anthropic.com>
| // Only trust version-0 txs: the REST API returns bad fields for non-standard | ||
| // txs (a v1 tx's subnetwork_id mirrors its txid prefix), which would make the | ||
| // device hard-reject. Bail → caller blind-signs. TODO: handle v1 via wasm SDK. | ||
| if (refTxs.some((tx) => tx.version !== 0)) { |
There was a problem hiding this comment.
🟠 P1 v0 gate 恒不触发:/transactions/search 响应缺少共识字段,被静默置 0
问题
collectRefTxsByApi 把 client.getTransactions() 返回的 IKaspaGetTransactionResponse(来自 /transactions/search)直接 JSON.stringify 后喂给 buildPrevTx,而后者按 IKaspaRestTx 解析。但这两个类型并不一致:IKaspaGetTransactionResponse / IKaspaGetTransactionInput / IKaspaGetTransactionOutput(packages/core/src/chains/kaspa/sdkKaspa/types/clientRestApi.ts)根本没有 version、lock_time、gas、payload、逐 input 的 sequence、逐 output 的 script_public_key_version 这些字段(input 侧只有 sig_op_count)。
于是 buildPrevTx 里所有这些字段全部走 ?? 0 / ?? '' 默认值:
version: Number(j.version ?? 0), // 恒为 0:响应无 version
sequenceNumber: input.sequence ?? 0, // 恒为 0:input 无 sequence
lockTime: j.lock_time ?? 0, // 恒为 0
scriptVersion: Number(output.script_public_key_version ?? 0), // 恒为 0因为 version 永远解析为 0,本 commit 新增的 refTxs.some((tx) => tx.version !== 0) 判定永远为 false,v0 gate 形同虚设——一笔真实的 v1 prev tx 会被当作 v0 构造并下发到设备。
影响
设备会用 refTx 字段重算 prev-tx id 并与 input.prevTxId 比对。对于任何 version != 0、或 v0 但 sequence/lock_time/scriptPublicKey.version 非 0 的 prev tx,下发的字段被置 0 → 设备重算出的 txid 不匹配 → 硬拒绝。而 sdk.kaspaSignTransaction(KeyringHardware.ts 约 284 行)在 try/catch 之外,这个拒绝不会被捕获、没有 blind-sign 兜底 → 用户直接签不了、无法转账。标准支付 tx(各字段恰为 0)能过,纯属巧合;本 commit 声称的"只信任 v0"保护实际不生效。
建议
不要用 version 作判据(该端点根本给不出可靠的共识字段)。改为自校验:在 buildPrevTx 后本地按共识规则重算 prev-tx id,只保留重算 id 等于 input.prevTxId 的 refTx,其余整批丢弃 → 走 blind-sign;或至少把 sdk.kaspaSignTransaction 也纳入兜底:设备因 prevtx 不匹配拒绝时,去掉 refTxs 重签一次。
// 伪代码:自校验,避免依赖 REST 端点是否返回 version/sequence
const verified = refTxs.filter((rt) => recomputeKaspaTxId(rt) === rt.txId);
if (verified.length !== refTxs.length) throw new OneKeyLocalError('refTx id mismatch');Generated by Claude Code
| let timer: ReturnType<typeof setTimeout> | undefined; | ||
| try { | ||
| const [txs = []] = await Promise.race([ | ||
| this.backgroundApi.serviceAccountProfile.sendProxyRequest< |
There was a problem hiding this comment.
🟡 P2 Promise.race 超时后败方 sendProxyRequest 未处理 → unhandledRejection
问题
getTransactions 用 Promise.race([sendProxyRequest(...), timeoutPromise])。当 30s 超时先 reject 时,sendProxyRequest 这个 promise 仍在挂起。如果它随后(比如 31s 时网络失败)自行 reject,就是一个没有任何 .catch 接管的 rejection。
const [txs = []] = await Promise.race([
this.backgroundApi.serviceAccountProfile.sendProxyRequest<...>({ ... }), // 败方无 catch
new Promise<never>((_resolve, reject) => { timer = setTimeout(() => reject(...), 30s); }),
]);这是上一轮已提出的问题,重构进 ClientKaspa 后依旧存在。
影响
在 RN/Hermes 与 Node 下会触发 unhandledRejection 警告,严重时被全局处理器上报为 Sentry 噪音或告警;虽不影响本次签名结果(已走 blind-sign 兜底),但污染错误监控、掩盖真实问题。
建议
给败方挂一个 no-op .catch,吞掉超时后迟到的 rejection:
const req = this.backgroundApi.serviceAccountProfile.sendProxyRequest<...>({ ... });
req.catch(() => {}); // 防止超时胜出后成为 unhandledRejection
const [txs = []] = await Promise.race([req, timeoutPromise]);Generated by Claude Code
…8n (#12523) * fix: update disabled network toast copy in swap token selector | OK-57913 * feat: add stocks FAQ section on desktop stocks tab | OK-57157 * fix: keep no-provider button disabled for amount-less error quotes | OK-57545 * fix: unify swap tab label with history title translation key | OK-58055 * fix: align swap and bridge word translations with history title | OK-58055 * fix: address review - case-insensitive quote pair identity and hi_IN status copy * chore: sync perp funding label translations from Lokalise
| if (trimmed.startsWith('{')) { | ||
| const j = JSON.parse(trimmed) as IKaspaRestTx; | ||
| return { | ||
| txId, |
There was a problem hiding this comment.
当 /transactions/search 响应没有 version 时,当前 proxy 类型也不保证该字段存在,这里的 ?? 0 会把未知版本直接构造成 v0。后面的 version !== 0 检查因此无法拦截不完整或非标准前序交易,错误字段会继续发送给设备,并可能导致 txid 重算不一致后硬拒绝签名。
请在构建 refTx 前要求 version 明确存在且严格等于 0;字段缺失或版本不支持时直接抛错,不要用默认值通过 v0 gate。
| const j = JSON.parse(trimmed) as IKaspaRestTx; | ||
| return { | ||
| txId, | ||
| version: Number(j.version ?? 0), |
There was a problem hiding this comment.
当 UTXO 来自 v1 或具有非默认 lock time/sequence 的交易时,/transactions/search 的响应并不提供这些共识字段;这里用 ?? 0 补出的 refTx 内容会与保留的 transaction_id 不一致。后面的 v0 gate 也无法识别这种情况,因为缺失的 version 已经被改写为 0。
设备收到该 refTx 后会重算出不同 txid 并拒绝签名,预期的 blind-sign 回退不会发生。请改用能返回 raw consensus transaction 或完整共识字段的数据源,并在发送设备前本地重算、校验 txid;校验不通过时不要附加该 refTx。
| ).filter(Boolean); | ||
| let refTxs: IKaspaRefTransaction[] | undefined; | ||
| try { | ||
| const built = await (this.vault as Vault).collectRefTxsByApi(prevTxids); |
There was a problem hiding this comment.
当签名的是 KRC20 commit 时,它不会进入上面的 reveal 分支,因此这里仍会请求全部 refTx;但 commit 的 P2SH 输出不满足 1.1.32 SDK 的 streaming 条件,SDK 最终必然使用 legacy blind-sign,这批 refTx 不会被使用。代理缓慢时,用户会在硬件确认前无意义地等待最长 30 秒。
请在发起请求前按 SDK 的 streamable-script 条件增加 guard,至少对带 commitScriptPubKey 的 KRC20 commit 直接跳过 refTx 获取。
| outputs: tx.outputs.map((output) => ({ | ||
| satoshis: Number(output.amount), | ||
| script: output.script_public_key, | ||
| scriptVersion: 0, |
There was a problem hiding this comment.
当前序交易输出金额超过 Number.MAX_SAFE_INTEGER sompi 时,这里的 Number(output.amount) 会静默舍入。舍入后的金额会通过 KaspaTxAckPrevOutput 发给设备,使设备重建出的前序交易与 transaction_id 不一致并拒绝当前签名;由于 refTx 获取已经成功,这条路径也不会自动回退到 blind-sign。
请保持 uint64 的无损表示,例如改为 output.amount.toString();@onekeyfe/hd-core 1.1.32 已支持用十进制字符串编码该字段。
| ).filter(Boolean); | ||
| let refTxs: IKaspaRefTransaction[] | undefined; | ||
| try { | ||
| const built = await (this.vault as Vault).collectRefTxsByApi(prevTxids); |
There was a problem hiding this comment.
当签名 KRC20 commit 时,它不会进入上面的 reveal 分支,因此这里仍会请求全部 refTx;但 commit 的 P2SH 输出不满足 SDK 1.1.32 的 streaming 条件,SDK 最终必然使用 legacy blind-sign,这批 refTx 不会被使用。代理缓慢时,用户会在硬件确认前无意义地等待最长 30 秒。
请在请求前增加能力 guard,至少在 encodedTx.commitScriptPubKey 存在时直接跳过 refTx 获取。
… states (#12527) * fix: keep OAuth session on transient auth errors and refine OTP error states * docs: correct stale email field comments in prime user info * fix: align bind flow with apiOAuthLogin commit boundary --------- Co-authored-by: Claude <noreply@anthropic.com>
…iagnostics (#12526) * fix: harden OneKey ID OAuth login guard, surfacing and intermediary diagnostics * fix: register web OAuth callback route to preserve authorization code in popup URL * fix: guard fallback toast error marker against primitive throwables * fix: block corrupt keyless slot before server login and bound error-body reads * fix: keep temp OAuth session on transient failures in legacy bind flows --------- Co-authored-by: Claude <noreply@anthropic.com>
* feat: add tradingview native clock to market detail * feat: render market kline in tradingview native * fix: render native market chart immediately * refactor: share tradingview chart controls * feat: connect tradingview native chart controls * feat: render tradingview native chart on mobile * chore: apply code quality cleanups * feat: add opt-in tradingview native market chart * feat: add pan and zoom to tradingview native chart * refactor: replace deprecated runOnUI usage * feat: tune tradingview native pan inertia * fix: reduce native chart candle spacing * fix: remove unused chart control type imports
Bump @onekeyfe/hd-* to 1.1.32-alpha.4, which adds the streaming Kaspa sign protocol with previous-transaction (refTx) verification: the device recomputes each spent input's tx id from its previous transaction and checks the amount/script on-device. - KeyringHardware: describe outputs by both script (legacy blind-sign) and address/addressN (streaming), so signing degrades gracefully — blind-sign without refTxs, stream with on-device verification when present; change outputs go by BIP-32 path so the device shows KASPA_PAYTOCHANGE. - Vault.collectRefTxsByApi: fetch the spent inputs' previous transactions from the kaspa REST API via the network-aware RPC proxy, mapped to KaspaRefTransaction (buildPrevTx handles consensus-hex and REST JSON); capped at a 30s timeout. - Any fetch failure/timeout/incomplete result falls back to blind signing and is logged via transaction.send.refTxFetchFailed for diagnosis. - collectTxsByApi kept as groundwork for preferring the backend raw-transaction/list once it supports kaspa.
- Move the previous-transaction (refTx) RPC access into a per-chain proxy client (vaults/impls/kaspa/sdkKaspa/ClientKaspa.ts), matching the ClientSol convention; the Vault just maps the result to KaspaRefTransaction. - Batch-fetch via POST /transactions/search (one upstream request for all txids) instead of one GET per txid; capped at a 30s timeout. - Only trust version-0 prev txs: the kaspa REST API returns unreliable fields for non-standard (version-1) txs, which would make the device recompute a mismatching prev-tx id and hard-reject — skip refTxs and blind-sign those. - Add @LogToLocal to the refTx failure log and trim it to network + error.
- hd-* (root + apps/cli) 1.1.32-alpha.4 → 1.1.32 now that the formal SDK is published; hwk-* stays at 1.1.31 (independent stack, unaffected). - Fix the oxlint import-order errors, the prettier blank line and the cspell PAYTOCHANGE warning that blocked CI lint on ClientKaspa.ts / KeyringHardware.ts.
Runtime behaviour is unchanged: the REST API doesn't return the omitted fields, so they resolved to 0 before and are written as 0 now. - Delete the kaspa-core-lib consensus-hex branch of buildPrevTx (and its Transaction import): the only caller always passes REST JSON, so the branch never ran. A future wRPC/WebSocket source would be written fresh rather than reuse it. - Delete IKaspaRestTx, an all-optional shadow of IKaspaGetTransactionResponse. The JSON.stringify → JSON.parse round-trip existed only to bridge the two types; buildPrevTx now takes the response object directly. - Declare the fields the REST API actually returns (version, payload) on IKaspaGetTransactionResponse, plus inputs: null for coinbase transactions (verified against api.kaspa.org), so no cast is needed. - Stop reading lock_time/gas/sequence/script_public_key_version, which the REST API never returns: write 0 explicitly and say why, instead of `?? 0` on absent fields.
acb098c to
8279d2b
Compare
Bump @onekeyfe/hd-* to 1.1.32-alpha.4, which adds the streaming Kaspa sign protocol with previous-transaction (refTx) verification: the device recomputes each spent input's tx id from its previous transaction and checks the amount/script on-device.