rialo-feature-management-program-interface 0.8.0-alpha.0

Rialo Feature Management Program Interface
Documentation
// Copyright (c) Subzero Labs, Inc.
// SPDX-License-Identifier: Apache-2.0

//! Compile-time feature registry namespace and subset-assertion machinery.
//!
//! This module reserves the `features` namespace and ships the compile-time
//! helpers that hot-path consumers will lean on once handles start landing.
//! No per-feature `pub const &'static str` handle is declared at this commit
//! — the registry slices are empty placeholders.
//!
//! `KNOWN_FEATURES` will list every feature name the runtime understands.
//! `STICKY_FEATURES` will list the subset whose activation cannot be reversed.
//! The `const _` assertion below enforces `STICKY_FEATURES ⊆ KNOWN_FEATURES`
//! at compile time; once handles land, removing one from `KNOWN_FEATURES`
//! becomes a compile error at the call site rather than a silent runtime
//! no-op.

/// Every feature name the runtime knows about.
///
/// Hot-path gating sites reference entries through the `pub const &'static str`
/// handles declared in this module. The slice itself exists so that:
///
/// * tooling can enumerate the registry,
/// * compile-time assertions on sibling tables (e.g. builtin upgrade registry,
///   compute-budget gating table) can verify that every referenced
///   `feature_name` is known.
pub const KNOWN_FEATURES: &[&str] = &[];

/// Subset of `KNOWN_FEATURES` whose activation is sticky.
///
/// Once a sticky feature activates it must remain active forever; the
/// management program rejects any Upsert that would deactivate one of these.
/// Protocol-version gates, builtin-upgrade gates, and any ABI-extending
/// feature (new syscall, sysvar, builtin instruction handler) must appear
/// here — see the NORTHSTAR protocol-upgrade design doc.
pub const STICKY_FEATURES: &[&str] = &[];

const fn const_str_eq(a: &str, b: &str) -> bool {
    let a = a.as_bytes();
    let b = b.as_bytes();
    if a.len() != b.len() {
        return false;
    }
    let mut i = 0;
    while i < a.len() {
        if a[i] != b[i] {
            return false;
        }
        i += 1;
    }
    true
}

const fn const_slice_contains(haystack: &[&str], needle: &str) -> bool {
    let mut i = 0;
    while i < haystack.len() {
        if const_str_eq(haystack[i], needle) {
            return true;
        }
        i += 1;
    }
    false
}

const fn sticky_is_subset_of_known() -> bool {
    let mut i = 0;
    while i < STICKY_FEATURES.len() {
        if !const_slice_contains(KNOWN_FEATURES, STICKY_FEATURES[i]) {
            return false;
        }
        i += 1;
    }
    true
}

const _: () = assert!(
    sticky_is_subset_of_known(),
    "STICKY_FEATURES must be a subset of KNOWN_FEATURES",
);

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn const_str_eq_matches() {
        assert!(const_str_eq("foo", "foo"));
        assert!(!const_str_eq("foo", "bar"));
        assert!(!const_str_eq("foo", "foobar"));
    }

    #[test]
    fn const_slice_contains_matches() {
        let s: &[&str] = &["a", "b", "c"];
        assert!(const_slice_contains(s, "b"));
        assert!(!const_slice_contains(s, "z"));
    }
}