renounceRole() bypasses signer tracking, causing state inconsistencyThe contract maintains internal signer tracking via s_signerCount, s_signers[], and s_isSigner[]. The custom revokeSigningRole() function properly updates these state variables.
However, the inherited renounceRole() from OpenZeppelin's AccessControl allows signers to renounce their role directly, bypassing the internal tracking update. This causes the AccessControl state and internal tracking to become inconsistent.
Likelihood: Medium
This occurs when any signer calls renounceRole(SIGNING_ROLE, signer) directly
Impact: High
State inconsistency between AccessControl roles and internal signer tracking
getSignerCount() returns incorrect value
getSigners() returns addresses that are no longer actually signers
Could be exploited to exceed the 5-signer limit after re-granting
Explanation: A signer calls renounceRole() directly instead of going through proper channels. The role is revoked at the AccessControl level, but s_signerCount remains unchanged, creating state inconsistency.
Expected: getSignerCount() returns 1 after a signer renounces.
Actual: getSignerCount() still returns 2, creating state inconsistency.
Explanation: Override renounceRole() to block direct renunciation of SIGNING_ROLE. Users must use the proper revokeSigningRole() function which correctly updates all tracking state. This maintains consistency between AccessControl and internal state.
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
Appeals are being carefully reviewed by our judges.
The contest is complete and the rewards are being distributed.