refactor(b20-asset, activation): remove unreachable announcement guard and redundant AlreadyDeactivated error#3253
Conversation
The is_announcement_active() check in announce() could never fire: begin_announcement() is called after the check (so the flag is always false when the guard runs), and the only re-entry vector — the inner call loop — already rejects the announce selector before dispatch. The flag also had no reset path, leaving the token permanently stuck with in_announcement=true after announce() completed. The selector check in the inner loop is the sole recursion defense, matching the MockB20Asset reference implementation which carries no flag at all. Remove in_announcement, is_announcement_active(), and begin_announcement() from B20AssetToken, and drop the dead guard and call-site from dispatch. Generated with Claude Code Co-Authored-By: Claude <noreply@anthropic.com>
🟡 Heimdall Review Status
|
AlreadyDeactivated and FeatureNotActivated both represent the same underlying state (features[feature] == false). The intended distinction between mutation context (deactivate) and query context (checkActivated) was never implemented: the mock collapses both into FeatureNotActivated. Remove AlreadyDeactivated from the ABI to eliminate the dead declaration and bring the Rust interface in line with the updated IActivationRegistry spec and MockActivationRegistry behavior. Also clarifies set_activated variable names to make the activate/deactivate branch intent explicit. Generated with Claude Code Co-Authored-By: Claude <noreply@anthropic.com>
Generated with Claude Code Co-Authored-By: Claude <noreply@anthropic.com>
| feature, | ||
| })); | ||
|
|
||
| let current_activated_state = self.features.at(&feature).read()?; |
There was a problem hiding this comment.
This is not logic change, making it more readable with clean code principles
| let is_activating_and_already_activated = | ||
| to_activated_state == true && current_activated_state == true; | ||
| let is_deactivating_and_already_deactivated = | ||
| to_activated_state == false && current_activated_state == false; |
There was a problem hiding this comment.
== true / == false comparisons on bool values will trigger clippy::bool_comparison (enabled via clippy::all in the workspace). Idiomatic Rust:
| let is_activating_and_already_activated = | |
| to_activated_state == true && current_activated_state == true; | |
| let is_deactivating_and_already_deactivated = | |
| to_activated_state == false && current_activated_state == false; | |
| let is_activating_and_already_activated = | |
| to_activated_state && current_activated_state; | |
| let is_deactivating_and_already_deactivated = | |
| !to_activated_state && !current_activated_state; |
There was a problem hiding this comment.
Keeping it verbose is a stylstic descion
Review SummaryClean removal of dead code — the analysis in the PR description is thorough and the changes are correct. No remaining references to the removed items ( Finding
|
✅ base-std fork tests: all 602 passedbase/base is fully in sync with the base-std spec. |
Summary
B20 Asset — announcement guard (BOP-292 finding #1)
in_announcement: boolfield,is_announcement_active(), andbegin_announcement()fromB20AssetTokenis_announcement_active()guard andbegin_announcement()call fromannounce()indispatch.rsannouncedoc comment to name the selector check as the sole recursion defenseActivation Registry — redundant error (BOP-292 finding #2)
AlreadyDeactivatedfromactivation/abi.rs— it was declared but never returnedset_activatedvariable names to make the activate/deactivate branch intent explicitContext
Finding #1 — unreachable
is_announcement_active()guardThe
is_announcement_active()check inannounce()was structurally unreachable:begin_announcement()was called after the check, soin_announcementwas alwaysfalsewhen the guard ran.announceselector before dispatch, so a recursive call could never reach the outer function body where the guard lives.end_announcement()or reset, leaving the token permanently stuck within_announcement = trueafterannounce()completed — a one-way latch with no observable effect only because the token is constructed fresh per invocation.Cross-checked against
MockB20Asset.sol: the reference implementation carries no flag at all. Its two actual defenses are the selector check (_checkSelector) and early ID marking before inner calls run. The Rust now matches this exactly.Finding #2 — redundant
AlreadyDeactivatederrorAlreadyDeactivatedandFeatureNotActivatedboth represent the same underlying state —features[feature] == false. The intended distinction (mutation context vs query context) was never implemented:MockActivationRegistry.solcollapses both intoFeatureNotActivated.AlreadyDeactivatedwas not just unused — it was redundant with an existing error. Removed from the Rust ABI to match the updatedIActivationRegistryspec.Test plan
cargo test --manifest-path crates/common/precompiles/Cargo.toml— all 344 tests passis_announcement_active,begin_announcement,in_announcement, orAlreadyDeactivatedin the crate tree