Summary
In PrivilegesManager, all set_or_update_* methods gate on is_account_permanently_registered (permanent state only), while the corresponding get_* methods layer reads as ephemeral delta → newly-registered (delta) → permanent. The net effect: you cannot register an account and adjust its privileges in the same execution — the setter rejects with AccountIsNotPermanentlyRegistered even though the account is registered (ephemerally) and the getter would return its body. This is asymmetric with both the getters in this file and with the Registry/StateManager read models.
Affected file(s)
src/inscriptive/privileges_manager/privileges_manager.rs
Location
Setters (all use is_account_permanently_registered):
set_or_update_account_liveness_flag — privileges_manager.rs:651-666
set_or_update_account_hierarchy — :669-684
set_or_update_account_txfee_exemptions — :687-702
set_or_update_account_reserved_flag_1 — :705-720
set_or_update_account_reserved_flag_2 — :723-738
set_or_update_account_can_deploy_liquidity — :741-756
set_or_update_account_can_deploy_contract — :759-774
set_or_update_contract_liveness_flag — :777-792
set_or_update_contract_immutability — :795-810
set_or_update_contract_tax_exemptions — :813-828
Representative:
pub fn set_or_update_account_liveness_flag(
&mut self,
account_key: AccountKey,
liveness_flag: LivenessFlag,
) -> Result<(), PMUpdateAccountError> {
if !self.is_account_permanently_registered(account_key) { // <-- permanent only
return Err(PMUpdateAccountError::AccountIsNotPermanentlyRegistered(account_key));
}
self.delta.updated_account_liveness_flags.insert(account_key, liveness_flag);
Ok(())
}
Getters (layer ephemeral → new-register → permanent), e.g. get_account_liveness_flag — :478-491:
pub fn get_account_liveness_flag(&self, account_key: AccountKey) -> Option<LivenessFlag> {
if let Some(value) = self.delta.updated_account_liveness_flags.get(&account_key) {
return Some(value.clone());
}
if let Some(body) = self.delta.new_accounts_to_register.get(&account_key) {
return Some(body.liveness_flag.clone());
}
self.in_memory_accounts.get(&account_key).map(|body| body.liveness_flag.clone())
}
Root cause / analysis
There are two reasonable intended behaviors, and the current code matches neither cleanly:
- (A) "Privileges are set at registration only." Then the setters should not exist for newly-registered accounts, and the register path should require the full body — which it does (
register_account takes a complete PrivilegesManagerAccountBody). Under this reading, the setters correctly reject ephemeral accounts. But then the getters' middle arm (delta.new_accounts_to_register) is redundant with reading the body, and the asymmetry is just confusing rather than wrong.
- (B) "Privileges are mutable at any time." Then the setters should use
is_account_registered (ephemeral or permanent), matching the registry's is_account_registered (registry.rs:534-537) and the getter layering. Under this reading, the current setters are buggy: they reject a legitimate same-execution register+configure flow.
The getter layering strongly implies (B) was intended, which makes the setters wrong. Either way, the contract should be made explicit and symmetric.
Note: Registry exposes both is_account_permanently_registered and is_account_registered (the union) precisely so callers can pick the semantics; PrivilegesManager exposes is_account_permanently_registered and is_account_ephemerally_registered separately but no union helper, which is why the setters reach for the permanent-only check.
Impact
- Functional limitation: same-execution register + privilege-update is impossible; forces a two-transaction dance that may not match the protocol's execution model.
- Confusing API: getters see ephemeral registrations, setters don't. A future maintainer will reasonably assume (B) and be surprised.
- Possibly masking a real bug if a caller relies on the union semantics.
Summary
In
PrivilegesManager, allset_or_update_*methods gate onis_account_permanently_registered(permanent state only), while the correspondingget_*methods layer reads as ephemeral delta → newly-registered (delta) → permanent. The net effect: you cannot register an account and adjust its privileges in the same execution — the setter rejects withAccountIsNotPermanentlyRegisteredeven though the account is registered (ephemerally) and the getter would return its body. This is asymmetric with both the getters in this file and with theRegistry/StateManagerread models.Affected file(s)
src/inscriptive/privileges_manager/privileges_manager.rsLocation
Setters (all use
is_account_permanently_registered):set_or_update_account_liveness_flag—privileges_manager.rs:651-666set_or_update_account_hierarchy—:669-684set_or_update_account_txfee_exemptions—:687-702set_or_update_account_reserved_flag_1—:705-720set_or_update_account_reserved_flag_2—:723-738set_or_update_account_can_deploy_liquidity—:741-756set_or_update_account_can_deploy_contract—:759-774set_or_update_contract_liveness_flag—:777-792set_or_update_contract_immutability—:795-810set_or_update_contract_tax_exemptions—:813-828Representative:
Getters (layer ephemeral → new-register → permanent), e.g.
get_account_liveness_flag—:478-491:Root cause / analysis
There are two reasonable intended behaviors, and the current code matches neither cleanly:
register_accounttakes a completePrivilegesManagerAccountBody). Under this reading, the setters correctly reject ephemeral accounts. But then the getters' middle arm (delta.new_accounts_to_register) is redundant with reading the body, and the asymmetry is just confusing rather than wrong.is_account_registered(ephemeral or permanent), matching the registry'sis_account_registered(registry.rs:534-537) and the getter layering. Under this reading, the current setters are buggy: they reject a legitimate same-execution register+configure flow.The getter layering strongly implies (B) was intended, which makes the setters wrong. Either way, the contract should be made explicit and symmetric.
Note:
Registryexposes bothis_account_permanently_registeredandis_account_registered(the union) precisely so callers can pick the semantics;PrivilegesManagerexposesis_account_permanently_registeredandis_account_ephemerally_registeredseparately but no union helper, which is why the setters reach for the permanent-only check.Impact