Skip to main content

FeaturesState

Struct FeaturesState 

Source
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

Source

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.

Source

pub fn get_authority(&self) -> &Pubkey

Source

pub fn set_authority(&mut self, new_authority: Pubkey)

Source

pub fn pending_authority(&self) -> Option<&Pubkey>

Source

pub fn set_pending_authority(&mut self, pending: Option<Pubkey>)

Source

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.

Source

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.

Source

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.

Source

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.

Source

pub fn deserialize(data: &[u8]) -> Result<Self, Error>

Decode from the on-disk byte layout, through the versioned envelope’s tolerant reader.

Source

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.

Source

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.

Source

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.

Source

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_BATCH per-instruction cap → TooManyNames.
  • validate_feature_name on every name (length, allowed charset, no leading/trailing whitespace) → InvalidFeatureName. Owning the check here keeps it the single source of truth — callers reaching enable outside the processor path cannot smuggle in malformed names.
  • MAX_FEATURE_COUNT cap 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.
Source

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_scheduledenable. 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.

Source

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.

Source

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.

Source

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

Source§

fn clone(&self) -> FeaturesState

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for FeaturesState

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Eq for FeaturesState

Source§

impl From<&FeaturesState> for FeaturesStateV1

Source§

fn from(s: &FeaturesState) -> Self

Converts to this type from the input type.
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.

Source§

fn from(s: &FeaturesState) -> Self

Converts to this type from the input type.
Source§

impl From<FeaturesState> for FeaturesStateV1

Source§

fn from(s: FeaturesState) -> Self

Converts to this type from the input type.
Source§

impl From<FeaturesState> for FeaturesStateVersioned

Source§

fn from(s: FeaturesState) -> Self

Converts to this type from the input type.
Source§

impl From<FeaturesStateV1> for FeaturesState

Source§

fn from(v: FeaturesStateV1) -> Self

Converts to this type from the input type.
Source§

impl From<FeaturesStateVersioned> for FeaturesState

Source§

fn from(v: FeaturesStateVersioned) -> Self

Converts to this type from the input type.
Source§

impl PartialEq for FeaturesState

Source§

fn eq(&self, other: &FeaturesState) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 (const: unstable) · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, and should not be overridden without very good reason.
Source§

impl StructuralPartialEq for FeaturesState

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.