0xMiden / 0xMiden/wallet

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

Aberta
#784 1 comentário 0 reações 1 responsável Reivindicada por @WiktorStarczewski Ver no GitHub
Linguagem predominante
TypeScript
Estrelas
5
Forks
28
Merge médio
9h 50min
PRs com merge (30d)
123

Descrição

## 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.

Guia de contribuição

Abrir o guia de contribuição

Avaliação

Esta issue ainda não foi avaliada.

Receba novas issues na sua caixa de entrada

Um resumo curto de issues do GitHub para quem está começando.