Repository navigation
Grouped-transition proofs do not bind the supplied transition to the recorded action #5327
Description
Problem
The grouped-transition client proof verifier authenticates the action at the caller-supplied group_state_transition_info.action_id and the signer's recorded participation, but does not independently establish that the supplied transition's contents correspond to that action.
The grouped branches pass the declared ID to Drive::verify_action_signer_and_total_power. Basic structure validation recomputes the ID for a proposer; for a co-signer it only checks whether the transition kind supports group actions.
This leaves a gap between "participation in the named action is proven" and the stronger ExecutionProved claim for the supplied transition. It affects the shared grouped-token pattern, including grouped mint-to-pool and burn-from-pool, rather than a change introduced by #5314.
Practical risk and evidence boundary
The important scenario is an application verifying a transition received from an untrusted party. A genuine proof for an existing action may be paired with a different transition retaining that action ID, signer and other required lookup fields. If an application interprets the result as proof that those supplied operation parameters executed, it may make an incorrect off-chain decision.
On the normal SDK path the verifier receives the client's own locally constructed request, so a DAPI response cannot simply substitute that request's amount. Historical-document verification also compares operation fields in the branches that use it. Risk therefore depends on the transition kind, history configuration and consuming application's trust boundary.
This is a code-level finding. An end-to-end substitution regression and a concrete affected application have not yet been demonstrated. It is not evidence of an ability to mint or spend without authorization: block execution validates group-operation contents separately, and these client verifiers are not called by block-processing paths.
Binding and compatibility questions
- Recomputing an ID from every co-signer's own identity and nonce would reject legitimate votes: the action ID was created using the proposer's identity and nonce. Establish the correct content binding for both roles.
- For the pool mint/burn kinds, the ID commits to token ID, proposer identity, nonce, amount and the serialized-actions digest. It does not commit to the anchor, Orchard proof, binding signature or public note. Define what the execution guarantee promises for these excluded fields; exact object identity and authenticated group participation are different guarantees.
Verification and scope
- Add a regression using an authentic proof and a substituted transition, establishing which production verifier branches accept it and which additional checks prevent it. Do not rely on a forged root or quorum signature.
- Preserve valid proposer/co-signer flows and distinguish recorded participation from the requested operation's execution.
- Review the appropriate content-binding fix and guarantee contract before implementation. Do not automatically downgrade SDK execution guarantees or introduce consensus/storage changes as part of this tracking split.
Extracted from the unresolved second part of #5177. The independent active/closed status problem was fixed by #5314, merged on 2026-10-07. Originally found while auditing #4760.
Activity