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}