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/// Maximum length for a feature name
34pub const MAX_FEATURE_NAME_LENGTH: usize = 512;
35
36/// Maximum number of features allowed in the system
37pub const MAX_FEATURE_COUNT: usize = 10000;
38
39/// Maximum number of names a single `Enable` instruction may carry.
40///
41/// Bounded by the ~64 KB transaction-size limit: 100 × 512-byte names
42/// (`MAX_FEATURE_NAME_LENGTH`) plus borsh length prefixes ≈ 51.6 KB, well
43/// inside the envelope with comfortable room for transaction headers,
44/// signatures, and other instructions in the same tx. Raising the cap
45/// requires re-validating the worst-case payload against the tx-size limit.
46pub const MAX_NAMES_PER_BATCH: usize = 100;
47
48/// Maximum total size of serialized features state (100 KB)
49pub const MAX_FEATURES_STATE_SIZE: usize = 100 * 1024;
50
51/// Maximum number of outstanding `ScheduleEnable` requests in the pending set.
52///
53/// Bounds the on-chain pending map (and its share of `MAX_FEATURES_STATE_SIZE`)
54/// and the number of live one-shot subscriptions the program can have
55/// registered at once.
56pub const MAX_PENDING_REQUESTS: usize = 100;
57
58/// Maximum lead time for a `ScheduleEnable`, in milliseconds (~100 years).
59///
60/// `fire_at_ms` must be in the future and within this horizon of the current
61/// block time. Mirrors the sanity bound the windowed-era schema enforced —
62/// catches fat-fingered far-future timestamps (e.g. seconds-vs-millis) that
63/// would otherwise pin a subscription effectively forever.
64pub const MAX_SCHEDULE_HORIZON_MS: u64 = 100 * 365 * 24 * 60 * 60 * 1000;
65
66/// Validates a feature name
67///
68/// Feature names must:
69/// - Not be empty
70/// - Not exceed MAX_FEATURE_NAME_LENGTH
71/// - Contain only alphanumeric characters, underscores, and hyphens
72/// - Not start or end with whitespace
73pub fn validate_feature_name(name: &str) -> bool {
74 !name.is_empty()
75 && name.len() <= MAX_FEATURE_NAME_LENGTH
76 && name
77 .chars()
78 .all(|c| c.is_alphanumeric() || c == '_' || c == '-')
79 && !name.starts_with(char::is_whitespace)
80 && !name.ends_with(char::is_whitespace)
81}
82
83/// Create genesis storage data with initial authority
84///
85/// Creates the initial FeaturesState with the provided authority and serializes it
86/// for use in genesis configuration.
87///
88/// # Arguments
89/// * `authority` - The pubkey that will have authority over the feature management system
90///
91/// # Returns
92/// A `Vec<u8>` containing the serialized FeaturesState
93pub fn genesis_storage(authority: Pubkey) -> Vec<u8> {
94 use state::FeaturesState;
95
96 let state = FeaturesState::new(authority);
97 state
98 .serialize()
99 .expect("Failed to serialize genesis state")
100}