Skip to main content

rialo_s_feature_gate_interface/
lib.rs

1// Copyright (c) Subzero Labs, Inc.
2// SPDX-License-Identifier: Apache-2.0
3// This file is either (a) original to Subzero Labs, Inc. or (b) derived from the Anza codebase and modified by Subzero Labs, Inc.
4
5//! Runtime features.
6//!
7//! Runtime features provide a mechanism for features to be simultaneously activated across the
8//! network. Since validators may choose when to upgrade, features must remain dormant until a
9//! sufficient majority of the network is running a version that would support a given feature.
10//!
11//! Feature activation is accomplished by:
12//! 1. Activation is requested by the feature authority, who issues a transaction to create the
13//!    feature account. The newly created feature account will have the value of
14//!    `Feature::default()`
15//! 2. When the next epoch is entered the runtime will check for new activation requests and
16//!    active them.  When this occurs, the activation slot is recorded in the feature account
17
18pub use rialo_s_sdk_ids::feature::{check_id, id, ID};
19#[cfg(feature = "bincode")]
20use {
21    rialo_s_account::{AccountSharedData, ReadableAccount, WritableAccount},
22    rialo_s_account_info::AccountInfo,
23    rialo_s_instruction::Instruction,
24    rialo_s_program_error::ProgramError,
25    rialo_s_pubkey::Pubkey,
26    rialo_s_rent::Rent,
27    rialo_s_system_interface::instruction as system_instruction,
28};
29
30#[cfg_attr(
31    feature = "serde",
32    derive(serde_derive::Deserialize, serde_derive::Serialize)
33)]
34#[derive(Default, Debug, PartialEq, Eq)]
35pub struct Feature {
36    pub activated_at: Option<u64>,
37}
38
39impl Feature {
40    pub const fn size_of() -> usize {
41        9 // see test_feature_size_of.
42    }
43
44    #[cfg(feature = "bincode")]
45    pub fn from_account_info(account_info: &AccountInfo<'_>) -> Result<Self, ProgramError> {
46        if *account_info.owner != id() {
47            return Err(ProgramError::InvalidAccountOwner);
48        }
49        bincode::deserialize(&account_info.data.borrow())
50            .map_err(|_| ProgramError::InvalidAccountData)
51    }
52}
53
54#[cfg(feature = "bincode")]
55/// Activate a feature
56pub fn activate(feature_id: &Pubkey, funding_address: &Pubkey, rent: &Rent) -> Vec<Instruction> {
57    activate_with_kelvins(
58        feature_id,
59        funding_address,
60        rent.minimum_balance(Feature::size_of()),
61    )
62}
63
64#[cfg(feature = "bincode")]
65pub fn activate_with_kelvins(
66    feature_id: &Pubkey,
67    funding_address: &Pubkey,
68    kelvins: u64,
69) -> Vec<Instruction> {
70    vec![
71        system_instruction::transfer(funding_address, feature_id, kelvins),
72        system_instruction::allocate(feature_id, Feature::size_of() as u64),
73        system_instruction::assign(feature_id, &id()),
74    ]
75}
76
77#[cfg(feature = "bincode")]
78pub fn from_account<T: ReadableAccount>(account: &T) -> Option<Feature> {
79    if account.owner() != &id() {
80        None
81    } else {
82        bincode::deserialize(account.data()).ok()
83    }
84}
85
86#[cfg(feature = "bincode")]
87pub fn to_account(feature: &Feature, account: &mut AccountSharedData) -> Option<()> {
88    bincode::serialize_into(account.data_as_mut_slice(), feature).ok()
89}
90
91#[cfg(feature = "bincode")]
92pub fn create_account(feature: &Feature, kelvins: u64) -> AccountSharedData {
93    let data_len = Feature::size_of().max(bincode::serialized_size(feature).unwrap() as usize);
94    let mut account = AccountSharedData::new(kelvins, data_len, &id());
95    to_account(feature, &mut account).unwrap();
96    account
97}
98
99#[cfg(test)]
100mod test {
101    use super::*;
102
103    #[test]
104    fn test_feature_size_of() {
105        assert_eq!(Feature::size_of() as u64, {
106            let feature = Feature {
107                activated_at: Some(0),
108            };
109            bincode::serialized_size(&feature).unwrap()
110        });
111        assert!(
112            Feature::size_of() >= bincode::serialized_size(&Feature::default()).unwrap() as usize
113        );
114        assert_eq!(Feature::default(), Feature { activated_at: None });
115
116        let features = [
117            Feature {
118                activated_at: Some(0),
119            },
120            Feature {
121                activated_at: Some(u64::MAX),
122            },
123        ];
124        for feature in &features {
125            assert_eq!(
126                Feature::size_of(),
127                bincode::serialized_size(feature).unwrap() as usize
128            );
129        }
130    }
131
132    #[test]
133    fn feature_deserialize_none() {
134        let just_initialized = AccountSharedData::new(42, Feature::size_of(), &id());
135        assert_eq!(
136            from_account(&just_initialized),
137            Some(Feature { activated_at: None })
138        );
139    }
140}