Per-account proposal handshake cost grows with interleaved proposal traffic (send + consume) — +128–153% over 300 ops
Maintainers usually reply within 2 days
@zeljkoX is already working on this.
Since Jul 17, 2026.
Assessment
This issue has not been assessed yet.
Description
Environment
guardian.openzeppelin.comv0.15.1 (git 82225104c8a2), Miden testnet- Paired A/B stress runs: identical PRNG seed, 300 guardian-cosigned sends, per-transaction stage timing. Single variable: the receiving wallet either also issued consume (claim) proposals for ~50% of received notes, or none at all.
Observation
With interleaved consumes, the guardian handshake stages grow monotonically over the run; removing consume traffic flattens exactly those stages:
| stage | growth Q1→Q5, claims on | claims off |
|---|---|---|
| proposal-create → signing | +153% | +77% (non-monotonic) |
| signing → sending | +128% | +41% |
| sending → submitting | +43% | 0% |
Everything else was measured flat in both runs (client sync, proving, block wait, local execution, UI). Net effect: median send latency grows from ~11 s to ~19 s across the run, driven by these stages. Since the flattening tracks the traffic-mix knob and not client state, session age (survives page reloads), or network (per-call durations flat), the growth follows per-account proposal co-traffic.
This looks like the non-catastrophic sibling of #303: #312 fixed the multi-minute stall/409-churn form; the steady per-account cost growth under normal interleaved traffic remains.
Where the time goes (narrowing measurements)
- Server per-call response times are flat. Guardian
delta/proposalendpoints answered in ~250–280 ms at the start AND end of the runs; sync RPCs pinned at 201–215 ms. The growth is not server response latency per request. - The number of round-trips per proposal grows. In a heavier run on the same account shape (2026-07-13, 442 ops), client-issued guardian proposal-path calls grew from 7.5 to 12.2 per operation while per-call durations stayed flat — the handshake takes more exchanges, not slower ones.
- Account payload grows.
GetAccountwas the only RPC whose duration grew (305 → 483 ms) over that run, tracking a growing account state payload. - It is not client-session state. Full page reloads of both wallets at ops 100 and 200 produced no reset (+0.8% / +5.9% across the boundaries) — whatever accumulates is durable/server-side, not in-memory client state.
- It is not queue depth at send time. The sender's local tx queue was empty at the start of nearly every op; latency ramps even for ops issued with zero in-flight work.
Suspected mechanism (falsifiable)
Per-proposal work appears to scale with the account's accumulated delta/proposal history: each handshake re-fetches or re-derives per-account state whose size grows with every canonicalized delta, and interleaved consume proposals both add history faster and add a canonicalization cycle (~10–12 s, #316) that overlaps the next send's handshake. Two checks would confirm or kill this: (1) do get_deltas/state-fetch response sizes grow linearly with canonicalized-delta count? (2) does per-proposal server-side work (DB reads, verify passes) scale with history length? If both are flat, the growth must be in the client-side multisig handshake logic instead — we can instrument that side next.
Reproduction
Two guardian-backed accounts on testnet, v0.15.1. Alternate ~300 sends between them; receiver claims ~50% of notes (each claim = a consume proposal on the receiving account). Measure proposal-create → submit per transaction. Expected: those stages roughly double by op ~300. Control: same seed with claims disabled — the submit stage stays flat and the others grow at less than half the rate. Full per-stage dataset (both runs, per-transaction timestamps) available — happy to attach.
References
#303 / #312 · #316 (post-send canonicalization wait) · #317 (scalability umbrella)
- Dominant language
- Rust
- Stars
- 11
- Forks
- 22
- Avg merge
- 3d 15h
- Merged PRs (30d)
- 35
Getting set up
- Ships a Dockerfile or Docker Compose file
- No pull request template
- Read the contributing guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from OpenZeppelin/guardian
-
enhancement
Difficulty 5/5 Over a week Newbie friendliness 35/100
OpenZeppelin/guardian#572 ·
Maintainers usually reply within 2 days
-
bug
Difficulty 4/5 3-5 days Newbie friendliness 38/100
OpenZeppelin/guardian#519 ·
Maintainers usually reply within 2 days
-
enhancement
Difficulty 4/5 3-5 days Newbie friendliness 48/100
OpenZeppelin/guardian#518 ·
Maintainers usually reply within 2 days
-
enhancement
Difficulty 5/5 Over a week Newbie friendliness 30/100
OpenZeppelin/guardian#517 ·
Maintainers usually reply within 2 days
-
bug
Difficulty 4/5 3-5 days Newbie friendliness 52/100
OpenZeppelin/guardian#514 ·
Maintainers usually reply within 2 days
All issues in OpenZeppelin/guardian
Similar issues
-
Difficulty 2/5 1-3 hours Newbie friendliness 69/100
Maintainers usually reply within 1 day
-
A match arm above the largest i64 is compared as 0Possibly taken A pull request linked to this issue is open or already merged. Open
Difficulty 2/5 1-3 hours Newbie friendliness 86/100
Maintainers usually reply within 1 day
-
Difficulty 1/5 Under an hour Newbie friendliness 83/100
DioxusLabs/dioxus#6063 ·
Maintainers usually reply within 1 day
-
`nsh-core` fails to compile when taffy's default `calc` feature is enabled elsewhere in the graphOpen
Difficulty 2/5 1-3 hours Newbie friendliness 76/100
longbridge/nsh#966 ·
Maintainers usually reply within 1 day
-
Windows docs: pin a tested ConPTY version for Kitty graphics; 1.25.260930003 corrupts large uploadsOpen
Difficulty 1/5 1-3 hours Newbie friendliness 88/100
raphamorim/rio#2021 ·
Maintainers usually reply within 1 day