Skip to main content

rialo_feature_management_interface/
features.rs

1// Copyright (c) Subzero Labs, Inc.
2// SPDX-License-Identifier: Apache-2.0
3
4//! Compile-time feature registry namespace.
5//!
6//! This module owns the `features` namespace: each feature name the runtime
7//! understands is declared here as a `pub const &'static str` handle, and
8//! `KNOWN_FEATURES` lists every one of them.
9//!
10//! Sibling tables (e.g. the builtin-upgrade registry, the compute-budget gating
11//! table) carry their own compile-time assertions that every `feature_name`
12//! they reference is a member of `KNOWN_FEATURES`.
13
14/// Enables the `StakeInfo` schema version 2 on-disk layout.
15///
16/// While active, the bank-level `native-staking` writers — genesis self-bond
17/// and epoch-reward compounding — emit the V2 stake-account format; before
18/// activation they emit V1. The instruction-handler write path
19/// (`write_stake_info`) runs inside the program runtime, which sees the Solana
20/// `feature_set` rather than this runtime `ActiveFeatures` flag, so it keeps
21/// emitting V1 for now; the versioned reader decodes both, so V1 and V2
22/// records coexist. (The V2-only field `last_reward_slot` has no consumer yet,
23/// so a handler rewriting a V2 record back to V1 is currently lossless in
24/// practice; feature-aware handler emission lands with that consumer.)
25/// Append-only: like all feature activation this cannot be reversed, and once
26/// any account has been written in the new format the storage-format bump is
27/// effectively permanent. This is the worked example for the writer-gated
28/// storage-format migration template.
29pub const ENABLE_STAKE_INFO_V2: &str = "ENABLE_STAKE_INFO_V2";
30
31// ---------------------------------------------------------------------------
32// Test-only protocol-upgrade demonstrators.
33//
34// The seven `TEST_*` handles below gate the end-to-end protocol-upgrade
35// demonstrators (the `test-upgrade` builtin's V1/V2 lineage, the
36// `rlo_test_gated` syscall, and a standalone scheduled-activation target).
37// They are members of `KNOWN_FEATURES` so the runtime, deploy-time import
38// check, and builtin-upgrade registry recognise them — but **nothing Upserts
39// them in production**, so the gated handlers, builtin versions, and syscall
40// binding ship as inert dead code. A managed test network opts in by
41// `Enable`-ing them, which is how the `scripts/test/rialo-cli-tests`
42// feature-upgrade suite drives every upgrade surface against a live node.
43// ---------------------------------------------------------------------------
44
45/// Enables version 1 (the baseline) of the `test-upgrade` demonstrator builtin.
46///
47/// Until active, the demonstrator program is unregistered and uncallable. Once
48/// active, version 0 of the builtin (handling `Increment`) registers via the
49/// `ACTIVE_FEATURE_BUILTINS` table in `svm-execution`.
50pub const TEST_UPGRADE_BUILTIN_V1: &str = "TEST_UPGRADE_BUILTIN_V1";
51
52/// Enables version 2 of the `test-upgrade` demonstrator builtin.
53///
54/// Activation swaps the builtin entrypoint to V2, which adds the `IncrementBy`
55/// instruction, soft-deprecates `Increment`, and lazily migrates the storage
56/// account from the V1 to the V2 on-disk layout on the next write.
57pub const TEST_UPGRADE_BUILTIN_V2: &str = "TEST_UPGRADE_BUILTIN_V2";
58
59/// Enables the `rlo_test_gated` system call at link time.
60///
61/// While inactive, a RISC-V program importing the syscall is rejected at deploy
62/// (the import is known-but-gated-off) and would fail to link; once active the
63/// import binds and the call succeeds. `rlo_test_gated` ships in the release
64/// binary (it is the production-resident, `KNOWN_FEATURES`-registered gated
65/// demonstrator), but nothing activates this feature in production.
66pub const TEST_GATED_SYSCALL: &str = "TEST_GATED_SYSCALL";
67
68/// Standalone target for the scheduled-activation demonstrator.
69///
70/// Gates no runtime behaviour; the scheduled-activation CLI test `ScheduleEnable`s
71/// it and observes the timed `Scheduled` → `Active` transition (and the drained
72/// pending entry) to confirm the one-shot scheduler fires.
73pub const TEST_SCHEDULED: &str = "TEST_SCHEDULED";
74
75/// Probe features for the authority-transfer demonstrator. Gate no runtime
76/// behaviour; the authority-transfer CLI test enables them to prove that a
77/// feature can be activated before/after an authority transfer (and that the
78/// old authority can no longer activate features after the transfer).
79pub const TEST_AUTHORITY_PROBE_1: &str = "TEST_AUTHORITY_PROBE_1";
80/// See [`TEST_AUTHORITY_PROBE_1`].
81pub const TEST_AUTHORITY_PROBE_2: &str = "TEST_AUTHORITY_PROBE_2";
82
83/// Target for the schedule-cancellation demonstrator. Gates no runtime
84/// behaviour; the test `ScheduleEnable`s it, then `Cancel`s the pending request
85/// and confirms it never activates. Registered (like the others) so that, were
86/// the cancel to fail and the schedule to fire, the resulting active set would
87/// still be a subset of `KNOWN_FEATURES` and not halt the node.
88pub const TEST_SCHEDULED_CANCEL: &str = "TEST_SCHEDULED_CANCEL";
89
90/// Every feature name the runtime knows about.
91///
92/// Hot-path gating sites reference entries through the `pub const &'static str`
93/// handles declared in this module. The slice itself exists so that:
94///
95/// * tooling can enumerate the registry,
96/// * compile-time assertions on sibling tables (e.g. builtin upgrade registry,
97///   compute-budget gating table) can verify that every referenced
98///   `feature_name` is known.
99pub const KNOWN_FEATURES: &[&str] = &[
100    ENABLE_STAKE_INFO_V2,
101    TEST_UPGRADE_BUILTIN_V1,
102    TEST_UPGRADE_BUILTIN_V2,
103    TEST_GATED_SYSCALL,
104    TEST_SCHEDULED,
105    TEST_AUTHORITY_PROBE_1,
106    TEST_AUTHORITY_PROBE_2,
107    TEST_SCHEDULED_CANCEL,
108];
109
110/// Const-evaluable string equality.
111///
112/// `==` on `&str` is not usable in `const fn` on stable, so sibling registry
113/// tables in other crates (e.g. the builtin-upgrade and storage-format
114/// writer-gate registries in `svm-execution`) compose this byte-compare into
115/// their own compile-time assertions instead of each carrying a private copy.
116pub const fn const_str_eq(a: &str, b: &str) -> bool {
117    let a = a.as_bytes();
118    let b = b.as_bytes();
119    if a.len() != b.len() {
120        return false;
121    }
122    let mut i = 0;
123    while i < a.len() {
124        if a[i] != b[i] {
125            return false;
126        }
127        i += 1;
128    }
129    true
130}
131
132const fn const_slice_contains(haystack: &[&str], needle: &str) -> bool {
133    let mut i = 0;
134    while i < haystack.len() {
135        if const_str_eq(haystack[i], needle) {
136            return true;
137        }
138        i += 1;
139    }
140    false
141}
142
143/// Returns `true` if `name` appears in [`KNOWN_FEATURES`].
144///
145/// Const-evaluable so sibling tables in other crates (e.g. the compute-budget
146/// gating table in `svm-execution`) can assert at compile time that every
147/// feature name they reference is known to the runtime.
148pub const fn is_known_feature(name: &str) -> bool {
149    const_slice_contains(KNOWN_FEATURES, name)
150}
151
152#[cfg(test)]
153mod tests {
154    use super::*;
155
156    #[test]
157    fn const_str_eq_matches() {
158        assert!(const_str_eq("foo", "foo"));
159        assert!(!const_str_eq("foo", "bar"));
160        assert!(!const_str_eq("foo", "foobar"));
161    }
162
163    #[test]
164    fn const_slice_contains_matches() {
165        let s: &[&str] = &["a", "b", "c"];
166        assert!(const_slice_contains(s, "b"));
167        assert!(!const_slice_contains(s, "z"));
168    }
169
170    #[test]
171    fn is_known_feature_matches() {
172        assert!(is_known_feature(ENABLE_STAKE_INFO_V2));
173        assert!(!is_known_feature("definitely_not_a_real_feature"));
174    }
175
176    #[test]
177    fn test_upgrade_demonstrator_features_are_known() {
178        for f in [
179            TEST_UPGRADE_BUILTIN_V1,
180            TEST_UPGRADE_BUILTIN_V2,
181            TEST_GATED_SYSCALL,
182            TEST_SCHEDULED,
183            TEST_AUTHORITY_PROBE_1,
184            TEST_AUTHORITY_PROBE_2,
185            TEST_SCHEDULED_CANCEL,
186        ] {
187            assert!(is_known_feature(f), "{f} must be a known feature");
188            assert!(
189                crate::validate_feature_name(f),
190                "{f} must be a valid feature name"
191            );
192        }
193    }
194}