rialo_feature_management_interface/lib.rs
1// Copyright (c) Subzero Labs, Inc.
2// SPDX-License-Identifier: Apache-2.0
3
4//! Feature Management Program Interface
5//!
6//! This program provides a system-level mechanism for managing feature flags.
7//! Activation is presence-based and append-only: a feature is active once its
8//! name is added to the on-chain set, and names are never removed.
9
10#![no_std]
11
12extern crate alloc;
13
14use alloc::vec::Vec;
15
16use rialo_s_pubkey::Pubkey;
17
18pub mod error;
19pub mod features;
20pub mod instruction;
21pub mod state;
22
23rialo_s_pubkey::declare_id!("Feature1111111111111111111111111111111111111");
24
25/// Storage account PDA seed for the features map
26pub const STORAGE_ACCOUNT_SEED: &[u8] = b"features_storage";
27
28/// Derive the storage account address
29pub fn get_storage_account_address() -> (rialo_s_pubkey::Pubkey, u8) {
30 rialo_s_pubkey::Pubkey::find_program_address(&[STORAGE_ACCOUNT_SEED], &id())
31}
32
33/// PDA seed for the **scheduler authority**: a program-owned pubkey that
34/// signs all `ScheduleEnable` subscriptions and the fired
35/// `FireScheduledEnable` transactions.
36///
37/// This keeps scheduled activations stable across human-authority
38/// changes: the subscription data account is a PDA of
39/// `(scheduler_authority, request_id)`, so a subsequent
40/// `UpdateAuthority` / two-step accept does not orphan in-flight
41/// schedules — the program can still derive the same subscription
42/// address (to `Cancel` it) and the matcher's fired transaction still
43/// satisfies `FireScheduledEnable`'s authority check (signer ==
44/// scheduler PDA, independent of the human authority). See SUB-2605.
45pub const SCHEDULER_AUTHORITY_SEED: &[u8] = b"features_scheduler";
46
47/// Derive the scheduler-authority PDA.
48pub fn get_scheduler_authority_address() -> (rialo_s_pubkey::Pubkey, u8) {
49 rialo_s_pubkey::Pubkey::find_program_address(&[SCHEDULER_AUTHORITY_SEED], &id())
50}
51
52/// Maximum length for a feature name
53pub const MAX_FEATURE_NAME_LENGTH: usize = 512;
54
55/// Maximum number of features allowed in the system
56pub const MAX_FEATURE_COUNT: usize = 10000;
57
58/// Maximum number of names a single `Enable` instruction may carry.
59///
60/// Bounded by the ~64 KB transaction-size limit: 100 × 512-byte names
61/// (`MAX_FEATURE_NAME_LENGTH`) plus borsh length prefixes ≈ 51.6 KB, well
62/// inside the envelope with comfortable room for transaction headers,
63/// signatures, and other instructions in the same tx. Raising the cap
64/// requires re-validating the worst-case payload against the tx-size limit.
65pub const MAX_NAMES_PER_BATCH: usize = 100;
66
67/// Maximum total size of serialized features state (100 KB)
68pub const MAX_FEATURES_STATE_SIZE: usize = 100 * 1024;
69
70/// Maximum number of outstanding `ScheduleEnable` requests in the pending set.
71///
72/// Bounds the on-chain pending map (and its share of `MAX_FEATURES_STATE_SIZE`)
73/// and the number of live one-shot subscriptions the program can have
74/// registered at once.
75pub const MAX_PENDING_REQUESTS: usize = 100;
76
77/// Maximum lead time for a `ScheduleEnable`, in milliseconds (~100 years).
78///
79/// `fire_at_ms` must be in the future and within this horizon of the current
80/// block time. Mirrors the sanity bound the windowed-era schema enforced —
81/// catches fat-fingered far-future timestamps (e.g. seconds-vs-millis) that
82/// would otherwise pin a subscription effectively forever.
83pub const MAX_SCHEDULE_HORIZON_MS: u64 = 100 * 365 * 24 * 60 * 60 * 1000;
84
85/// Validates a feature name
86///
87/// Feature names must:
88/// - Not be empty
89/// - Not exceed MAX_FEATURE_NAME_LENGTH
90/// - Contain only alphanumeric characters, underscores, and hyphens
91/// - Not start or end with whitespace
92pub fn validate_feature_name(name: &str) -> bool {
93 !name.is_empty()
94 && name.len() <= MAX_FEATURE_NAME_LENGTH
95 && name
96 .chars()
97 .all(|c| c.is_alphanumeric() || c == '_' || c == '-')
98 && !name.starts_with(char::is_whitespace)
99 && !name.ends_with(char::is_whitespace)
100}
101
102/// Create genesis storage data with initial authority
103///
104/// Creates the initial FeaturesState with the provided authority and serializes it
105/// for use in genesis configuration.
106///
107/// # Arguments
108/// * `authority` - The pubkey that will have authority over the feature management system
109///
110/// # Returns
111/// A `Vec<u8>` containing the serialized FeaturesState
112pub fn genesis_storage(authority: Pubkey) -> Vec<u8> {
113 use state::FeaturesState;
114
115 let state = FeaturesState::new(authority);
116 state
117 .serialize()
118 .expect("Failed to serialize genesis state")
119}