pub struct FeaturesState { /* private fields */ }Expand description
The program’s global state.
Stored under the STORAGE_ACCOUNT_SEED PDA owned by the feature
management program. Carries the authority pubkey, an optional pending
authority for the two-step transfer handshake, and the set of feature
names that are currently active.
This is the in-memory type. On-disk encoding goes through
FeaturesStateVersioned — the single versioned seam — not a direct borsh
derive on this struct; that is why FeaturesState does not derive
BorshSerialize/BorshDeserialize. The current wire layout is frozen as
FeaturesStateV1, and Self::serialize / Self::deserialize delegate
to the envelope.
Implementations§
Source§impl FeaturesState
impl FeaturesState
Sourcepub fn new(authority: Pubkey) -> Self
pub fn new(authority: Pubkey) -> Self
Create a new state with the given authority, no pending transfer, and an empty entry set.
Genesis intentionally pre-seeds no features — features active from genesis are part of the implementation, not feature flags.
Sourcepub fn propose_transfer(
&mut self,
new_authority: Pubkey,
) -> Result<(), FeatureManagementError>
pub fn propose_transfer( &mut self, new_authority: Pubkey, ) -> Result<(), FeatureManagementError>
Propose a two-step authority transfer.
Sets pending_authority to Some(new_authority). Returns
PendingTransferExists if a previous proposal is still
outstanding — callers must cancel_transfer first. Returns
InvalidTransferTarget if new_authority equals the current
authority (degenerate target; distinct from a signer mismatch,
which is the processor’s Unauthorized).
Caller is responsible for verifying the current-authority signature before invoking this.
Sourcepub fn accept_transfer(&mut self) -> Result<(), FeatureManagementError>
pub fn accept_transfer(&mut self) -> Result<(), FeatureManagementError>
Commit a previously-proposed authority transfer.
On success the authority moves to the pending value and
pending_authority clears. Returns NoPendingTransfer if nothing
is pending.
Contract: the caller MUST have verified that the transaction
signer equals pending_authority() before invoking this. This
method does not re-check; the processor proves signer == pending
via verify_authority(&pending, ...) and then commits here. Code
outside the processor that calls this without the signer check
would let any keypair commit the pending authority.
Sourcepub fn cancel_transfer(&mut self) -> Result<(), FeatureManagementError>
pub fn cancel_transfer(&mut self) -> Result<(), FeatureManagementError>
Cancel a previously-proposed authority transfer.
Clears pending_authority. Returns NoPendingTransfer if nothing
is pending. Caller is responsible for verifying the
current-authority signature before invoking this.
Sourcepub fn serialize(&self) -> Result<Vec<u8>, Error>
pub fn serialize(&self) -> Result<Vec<u8>, Error>
Encode to the on-disk byte layout, through the versioned envelope.
Delegates to FeaturesStateVersioned, which today writes the bare
borsh of FeaturesStateV1 — byte-identical to the pre-envelope format,
so existing accounts read back unchanged.
Sourcepub fn deserialize(data: &[u8]) -> Result<Self, Error>
pub fn deserialize(data: &[u8]) -> Result<Self, Error>
Decode from the on-disk byte layout, through the versioned envelope’s tolerant reader.
Sourcepub fn is_active(&self, feature_name: &str) -> bool
pub fn is_active(&self, feature_name: &str) -> bool
Whether feature_name is active.
Activation is membership: a feature is active iff its name is in
entries. No clock consult.
Sourcepub fn entries(&self) -> &BTreeSet<String>
pub fn entries(&self) -> &BTreeSet<String>
Read-only view of the active entry set. Use enable to mutate so the
MAX_FEATURE_COUNT cap stays enforced.
Sourcepub fn insert_for_test(&mut self, name: String)
pub fn insert_for_test(&mut self, name: String)
Test-only direct insert that skips enable’s validation. Use to
seed fixtures that need specific names. Never call from production
paths.
Not cfg(test)-gated because cross-crate test crates (e.g.
svm-execution’s bank tests) need to construct FeaturesState
fixtures, and cfg(test) from the declaring crate is invisible to
dependent test crates. Documented as fixtures-only and never invoked
by production paths.
Sourcepub fn enable(
&mut self,
names: Vec<String>,
) -> Result<(), FeatureManagementError>
pub fn enable( &mut self, names: Vec<String>, ) -> Result<(), FeatureManagementError>
Add one or more feature names to the active set.
Idempotent: re-submitting an existing name is a no-op (presence is the activation, so a name that is already present is already active). Enforces:
MAX_NAMES_PER_BATCHper-instruction cap →TooManyNames.validate_feature_nameon every name (length, allowed charset, no leading/trailing whitespace) →InvalidFeatureName. Owning the check here keeps it the single source of truth — callers reachingenableoutside the processor path cannot smuggle in malformed names.MAX_FEATURE_COUNTcap on the resulting set size — checked against the post-insert count of distinct new names so a single batch cannot push the registry past the cap →MaxFeatureCountExceeded.
Sourcepub fn schedule(
&mut self,
request_id: u64,
names: Vec<String>,
fire_at_ms: u64,
) -> Result<(), FeatureManagementError>
pub fn schedule( &mut self, request_id: u64, names: Vec<String>, fire_at_ms: u64, ) -> Result<(), FeatureManagementError>
Record a deferred activation request under the caller-chosen
request_id.
Validates names per-name (charset/length) and per-batch
(MAX_NAMES_PER_BATCH) at schedule time, enforces MAX_PENDING_REQUESTS
on the pending entry count, and rejects a request that would push the
serialized state over MAX_FEATURES_STATE_SIZE with
PendingStateTooLarge. It does not enforce the MAX_FEATURE_COUNT
registry cap here — that cap is checked when the schedule fires, via
fire_scheduled → enable. The
fire_at_ms future / horizon check is the caller’s responsibility —
it needs the clock, which this deliberately clock-free state type does
not consult. The request_id is supplied by the caller (it is the
cancel handle and the subscription nonce); a request_id that already
has a pending entry is rejected with RequestAlreadyExists.
Note the count cap and the byte cap interact: MAX_PENDING_REQUESTS
(100) is only reachable with small requests. A request carrying near-max
batches (≈51 KB serialized) exhausts the MAX_FEATURES_STATE_SIZE
(100 KB) budget after one or two entries, at which point
PendingStateTooLarge — not TooManyPendingRequests — is the operative
rejection. The explicit byte check here means that surfaces as a clear
error rather than an opaque SerializationError at save time.
Sourcepub fn fire_scheduled(
&mut self,
request_id: u64,
) -> Result<(), FeatureManagementError>
pub fn fire_scheduled( &mut self, request_id: u64, ) -> Result<(), FeatureManagementError>
Fire a previously-scheduled request: drain its pending entry and activate its names.
Removes pending[request_id] (returning RequestNotFound if absent),
then runs enable on the recorded names — so the same
MAX_FEATURE_COUNT / charset / per-batch validation that a direct
Enable enforces is applied at fire time, not just at schedule time.
Atomicity: the processor saves state after this returns and a failed
enable aborts the whole transaction, rolling back the remove. So the
drain-and-enable is all-or-nothing — a fired schedule never half-drains.
Sourcepub fn cancel(
&mut self,
request_id: u64,
) -> Result<ScheduledRequest, FeatureManagementError>
pub fn cancel( &mut self, request_id: u64, ) -> Result<ScheduledRequest, FeatureManagementError>
Remove a pending scheduled request by id, returning the withdrawn entry.
Returns RequestNotFound if no pending request has that id — already
fired (the one-shot self-removes), already cancelled, or never existed.
Activation is append-only, so a request that has already fired cannot
be undone here.
Sourcepub fn pending(&self) -> &BTreeMap<u64, ScheduledRequest>
pub fn pending(&self) -> &BTreeMap<u64, ScheduledRequest>
Read-only view of the pending scheduled requests, keyed by request_id.
Trait Implementations§
Source§impl Clone for FeaturesState
impl Clone for FeaturesState
Source§fn clone(&self) -> FeaturesState
fn clone(&self) -> FeaturesState
1.0.0 (const: unstable) · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read moreSource§impl Debug for FeaturesState
impl Debug for FeaturesState
impl Eq for FeaturesState
Source§impl From<&FeaturesState> for FeaturesStateV1
impl From<&FeaturesState> for FeaturesStateV1
Source§fn from(s: &FeaturesState) -> Self
fn from(s: &FeaturesState) -> Self
Source§impl From<&FeaturesState> for FeaturesStateVersioned
Defaults to the V1 wire form. A future feature-gated writer would use an
encode(state, feature_active) picker (the StakeInfoVersioned template) to
choose a newer variant; this conversion is the no-feature-context path.
impl From<&FeaturesState> for FeaturesStateVersioned
Defaults to the V1 wire form. A future feature-gated writer would use an
encode(state, feature_active) picker (the StakeInfoVersioned template) to
choose a newer variant; this conversion is the no-feature-context path.
Source§fn from(s: &FeaturesState) -> Self
fn from(s: &FeaturesState) -> Self
Source§impl From<FeaturesState> for FeaturesStateV1
impl From<FeaturesState> for FeaturesStateV1
Source§fn from(s: FeaturesState) -> Self
fn from(s: FeaturesState) -> Self
Source§impl From<FeaturesState> for FeaturesStateVersioned
impl From<FeaturesState> for FeaturesStateVersioned
Source§fn from(s: FeaturesState) -> Self
fn from(s: FeaturesState) -> Self
Source§impl From<FeaturesStateV1> for FeaturesState
impl From<FeaturesStateV1> for FeaturesState
Source§fn from(v: FeaturesStateV1) -> Self
fn from(v: FeaturesStateV1) -> Self
Source§impl From<FeaturesStateVersioned> for FeaturesState
impl From<FeaturesStateVersioned> for FeaturesState
Source§fn from(v: FeaturesStateVersioned) -> Self
fn from(v: FeaturesStateVersioned) -> Self
Source§impl PartialEq for FeaturesState
impl PartialEq for FeaturesState
Source§fn eq(&self, other: &FeaturesState) -> bool
fn eq(&self, other: &FeaturesState) -> bool
self and other values to be equal, and is used by ==.