Skip to content

syncProposals() keeps returning executed proposals as pending (stale cache never pruned) #404

Description

@WiktorStarczewski

Summary

Multisig.syncProposals() in @openzeppelin/miden-multisig-client merges the GUARDIAN response into a persistent this.proposals Map but never prunes it. Once a proposal is executed, the GUARDIAN server drops it from getDeltaProposals (it only returns pending proposals), so a co-signer who did not execute the transaction locally keeps seeing the executed proposal as pending — indefinitely.

Reproduction (2-of-2 multisig, two browsers)

  1. Signer 2 creates a proposal and signs it (1/2).
  2. Signer 1 signs it (2/2) and executes it.
  3. Signer 2 syncs.

Observed: the executed transaction is still listed as a pending proposal on Signer 2, stuck at 1/2.
Expected: it disappears from Signer 2's pending list.

Root cause

syncProposals() iterates the GUARDIAN deltas doing this.proposals.set(...), then returns Array.from(this.proposals.values()). When GUARDIAN returns an empty (or reduced) list, the loop leaves previously-cached proposals in the Map. The signer who executed the tx updated its own cache locally as part of executing; every other signer's cache silently ignores the now-empty response and keeps the executed proposal pinned as pending.

Versions

@openzeppelin/miden-multisig-client, @openzeppelin/guardian-client, and guardian server all on 0.16.x.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions