0xMiden / 0xMiden/wallet

Pass ChainAnchor through to executeRequest so guardian co-signatures cannot be raced

未關閉
#784 1 則留言 0 個 reaction 已指派 1 人 已被 @WiktorStarczewski 認領 在 GitHub 檢視
主要語言
TypeScript
星號
5
分支
28
平均合併
9 小時 50 分鐘
30 天內合併 PR
123

描述

## Problem

A guardian-backed send can fail with `transaction is unauthorized with summary TransactionSummary { ... }` even though nothing is wrong with the wallet, the key, or the threshold.

The guardian binds its co-signature to a `TransactionSummary` that pins specific account state. The wallet then executes locally. If the chain advances between those two moments, the state the signature was bound to no longer matches the state at execution, and the kernel rejects the transaction as unauthorized.

This is a race whose probability scales with the width of that window. A 300-op devnet stress run measured it directly:

| proposal-to-execution window | failure rate |
| --- | --- |
| 0.66-0.91s | 4.5% |
| 1.70-17.62s | 35.3% |

The window is not constant. Under sustained load the guardian's round-trip time grew from ~650ms to ~1374ms over 2.3 hours, and the observed send failure rate tracked it, ending at 26%. Devnet block time is ~2.97s, so the window becomes a meaningful fraction of a block interval and the race stops being rare.

## The fix

The SDK already supports pinning execution to a reference block, and the proposal already carries the anchor — the wallet just does not pass it through.

- `AnchoredOptions` is accepted by `executeRequest` (`@miden-sdk/miden-sdk`)
- `chainAnchor` is set on the proposal metadata by `@openzeppelin/miden-multisig-client`
- `chainAnchorFromBase64` is publicly exported
- `src/offscreen/main.ts` calls `executeRequest(accountId, tr)` with no options argument

Threading the proposal's anchor into that call pins execution to the same reference block the guardian signed against, which removes the mismatch rather than retrying through it.

Worth verifying as part of the work: pinning to a captured block is not free — an anchor that is too old may be rejected by the node — so this likely needs a bound on anchor age, and interacts with how long a retry may wait before re-executing.

## Relationship to #781

[#781](https://github.com/0xMiden/wallet/pull/781) added an automatic retry for this failure. That is a **backstop, not a cure**: it recovers the send by re-running the whole proposal cycle, and it is explicitly documented in `src/lib/miden/transaction/index.ts` as standing in until the anchor is threaded through. This issue is that cure.

The two are complements rather than substitutes — the anchor narrows the window, and a retry covers whatever race remains — but the retry should not be the only thing standing between users and this failure.

貢獻指南

開啟貢獻指南

研究方向

The issue is in `src/offscreen/main.ts` where `executeRequest` is called. First, locate the proposal metadata to extract the `chainAnchor`. Then, modify the call to pass an `AnchoredOptions` object containing the anchor. Verify the fix by checking that the guardian co-signature race no longer occurs, and consider adding a bound on anchor age as noted. The related PR #781 provides context on the retry mechanism.

由索引模型根據 Issue 內容生成。

評估

技術堆疊
nodejs, typescript
領域
backend, blockchain
Issue 類型
缺陷
難度
3/5
預估耗時
1-2 天
活躍度
活躍
描述清晰度
描述清楚
新手友好度
65/100

把新 issue 寄到你的電子郵件信箱

精選適合新手參與的 GitHub issue 摘要。