@@ -32,7 +32,9 @@ Moonlight/Phoenix/contract owners approve controller operations.
3232 bytes, and salt.
3333- A ready operation must be removed from pending proposals and tombstoned before
3434 the wrapper attempts target execution.
35- - Expired proposals are pruned and cannot become ready.
35+ - Expired proposals cannot become ready. Direct confirmation returns the
36+ explicit expired error without mutating the pending operation; later
37+ proposal/cancel lifecycle calls prune expired pending operations.
3638- Authority changes that remove owners or change threshold clear stale pending
3739 operations.
3840
@@ -60,7 +62,8 @@ Phoenix owners and threshold `2`, then deploy `proxy_counter` with
6062The native property test exercises the raw controller state machine across
6163random owners, ids, targets, TTLs, tombstone windows, and timings. It checks
6264atomicity for non-owner proposals, non-owner confirmations, duplicate
63- confirmations, one-of-three pending behavior, expiry pruning, ready-state
65+ confirmations, same-id/different-target rejection, one-of-three pending
66+ behavior, expired confirmation rejection without mutation, ready-state
6467tombstoning, tombstone replay rejection, and successful re-use only after the
6568tombstone has expired.
6669
@@ -71,11 +74,14 @@ Phoenix. Phoenix owners approve by Schnorr-signed `AuthorizedAction`.
7174Moonlight and contract owners may be accepted through observed runtime context
7275when the host exposes it.
7376
74- The Forge wrapper verifies signatures before consuming nonce state, but only
75- consumes after the controller accepts the proposal/confirmation/maintenance
76- operation. This avoids burning a valid signature on duplicate confirmations,
77- wrong threshold membership, invalid targets, expired proposals, or invalid
78- maintenance payloads.
77+ The Forge wrapper verifies signatures before consuming nonce state. Threshold
78+ maintenance calls receive a ` MultisigQuorum ` witness from that verification
79+ path rather than a caller-supplied list of principals, so composing contracts
80+ cannot accidentally satisfy owner management by naming owners without proving
81+ authorization. The wrapper consumes nonce state only after the controller
82+ accepts the proposal/confirmation/maintenance operation. This avoids burning a
83+ valid signature on duplicate confirmations, wrong threshold membership,
84+ invalid targets, expired proposals, or invalid maintenance payloads.
7985
8086Execution is automatic at threshold. There is no separate ` execute ` step. This
8187is simpler and avoids a ready-operation queue, but systems that require a final
@@ -87,10 +93,10 @@ A target execution failure emits `multisig/operation_executed` with
8793same logical call requires a new salt. This avoids accidental replay, but
8894clients must surface failed execution clearly.
8995
90- ` propose ` can count as a confirmation for an existing operation with the same
91- operation id. This is acceptable because the signed payload is the exact
92- operation id, but client UX should prefer the explicit ` confirm ` method after a
93- proposal already exists.
96+ ` propose ` can count as a confirmation for an existing operation only when the
97+ supplied target matches the stored target for the operation id. Client UX
98+ should still prefer the explicit ` confirm ` method after a proposal already
99+ exists.
94100
95101## Residual Risks
96102
0 commit comments