solana/sdk/src/feature_set.rs

556 lines
21 KiB
Rust
Raw Normal View History

use lazy_static::lazy_static;
2020-09-21 14:03:35 -07:00
use solana_sdk::{
clock::Slot,
2020-09-21 14:03:35 -07:00
hash::{Hash, Hasher},
pubkey::Pubkey,
};
use std::collections::{HashMap, HashSet};
pub mod instructions_sysvar_enabled {
solana_sdk::declare_id!("EnvhHCLvg55P7PDtbvR1NwuTuAeodqpusV3MR5QEK8gs");
}
pub mod secp256k1_program_enabled {
solana_sdk::declare_id!("E3PHP7w8kB7np3CTQ1qQ2tW3KCtjRSXBQgW9vM2mWv2Y");
}
pub mod consistent_recent_blockhashes_sysvar {
solana_sdk::declare_id!("3h1BQWPDS5veRsq6mDBWruEpgPxRJkfwGexg5iiQ9mYg");
}
pub mod deprecate_rewards_sysvar {
solana_sdk::declare_id!("GaBtBJvmS4Arjj5W1NmFcyvPjsHN38UGYDq2MDwbs9Qu");
}
2020-09-25 16:19:03 -07:00
pub mod pico_inflation {
solana_sdk::declare_id!("4RWNif6C2WCNiKVW7otP4G7dkmkHGyKQWRpuZ1pxKU5m");
2020-09-25 16:19:03 -07:00
}
pub mod full_inflation {
pub mod devnet_and_testnet {
solana_sdk::declare_id!("DT4n6ABDqs6w4bnfwrXT9rsprcPf6cdDga1egctaPkLC");
}
// `candidate_example` is an example to follow by a candidate that wishes to enable full
// inflation. There are multiple references to `candidate_example` in this file that need to
// be touched in addition to the following block.
//
// The candidate provides the `enable::id` address and contacts the Solana Foundation to
// receive a `vote::id` address.
//
pub mod candidate_example {
pub mod vote {
// The private key for this address is held by the Solana Foundation
solana_sdk::declare_id!("DummyVoteAddress111111111111111111111111111");
}
pub mod enable {
// The private key for this address is held by candidate_example
solana_sdk::declare_id!("DummyEnab1eAddress1111111111111111111111111");
}
}
pub mod bl {
pub mod vote {
solana_sdk::declare_id!("HRzoLj4jufnYEWosm9kWVgBVFdxAuqB1hu7vLckCuQHa");
}
pub mod enable {
solana_sdk::declare_id!("BLxyQtJPzYZLHyj1p9n5QHUvbPoJt4TtRh7BXbG4M6rR");
}
}
pub mod buburuza {
pub mod vote {
solana_sdk::declare_id!("4qp2VKAPgmi53N7DkobejdbPgkpP2316mSAZnKaWeDtR");
}
pub mod enable {
solana_sdk::declare_id!("BSsRT3AcddKioKwfHqzDNmgMPuzWeHKwocWokj21Xxnf");
}
}
pub mod bunghi {
pub mod vote {
solana_sdk::declare_id!("E9hFUVEz29H8XMXk7ygk7ZpCuEuZQ8DJvJKJSTGu1RM6");
}
pub mod enable {
solana_sdk::declare_id!("5S9JDUb4vKY1CUxLf5oc96ZxjGrephj1jcPeTi62sYmP");
}
}
pub mod certusone {
2021-02-01 20:21:30 -08:00
pub mod vote {
solana_sdk::declare_id!("BzBBveUDymEYoYzcMWNQCx3cd4jQs7puaVFHLtsbB6fm");
2021-02-01 20:21:30 -08:00
}
pub mod enable {
solana_sdk::declare_id!("7XRJcS5Ud5vxGB54JbK9N2vBZVwnwdBNeJW1ibRgD9gx");
2021-02-01 20:21:30 -08:00
}
}
pub mod diman {
pub mod vote {
solana_sdk::declare_id!("9fHeFGjnequiB366D28ELiAQQ6vqzxwxgsATJ5ELxEvd");
}
pub mod enable {
solana_sdk::declare_id!("DimAnioV7WQM2L41fckvg2ei3NLHV2ACy5qoTKpi8Uz5");
}
}
pub mod lowfeevalidation {
pub mod vote {
solana_sdk::declare_id!("DcbTexLyN3fM3Y6UtteiYEpgDPbr3PrapczHYFagTPci");
}
pub mod enable {
solana_sdk::declare_id!("2wftmZhmArxv3eKjoRz4ffw385eZunydQU3Ruku1kvRX");
}
}
pub mod nam {
pub mod vote {
solana_sdk::declare_id!("Hb6tvjY81EmgapxNS4dos1v8Q2RSjQABphu7cnzM4ELa");
}
pub mod enable {
solana_sdk::declare_id!("NamwT9ejvrfcPXrCHEwp7BvUUFKPgVznu66HZUgFD9w");
}
}
pub mod p2pvalidator {
pub mod vote {
solana_sdk::declare_id!("89xUFJyCb3JQ7WbYBK4vza5uyCCTXXv8UQEUCQjo4SbC");
}
pub mod enable {
solana_sdk::declare_id!("C89S2MdjXuP6UmgmqKpszoUahfXLd4xVeikP8vJMioNE");
}
}
pub mod rockx {
pub mod vote {
solana_sdk::declare_id!("8DaPPAGV9mf1YCHzrettgSMFcAT1ePtS3GSGfYka9Rjw");
}
pub mod enable {
solana_sdk::declare_id!("26Bq2mgEJr93MtGTErrHNnhkDYWMoW7r7VB54r9erb5u");
}
}
pub mod sotcsa {
pub mod vote {
solana_sdk::declare_id!("EgoekfqCYoraFE5ZkiECGQ945Y5rGBXh3n85sQPuR85r");
}
pub mod enable {
solana_sdk::declare_id!("6f8Y2dACzRjM9R9RwiLp9HuAxo43QwtztgHm4BKUGyxU");
}
}
pub mod stakeconomy {
pub mod vote {
solana_sdk::declare_id!("JCergKv4GcywaBzn4JHi3sYJfG7mWenTG3QQDNUJiGS4");
}
pub mod enable {
solana_sdk::declare_id!("5NUfXNZUsP1ndyShQJ37H2dgHaEGaUNqgT9zn3BTiwct");
}
}
2021-02-05 08:46:09 -08:00
pub mod w3m {
pub mod vote {
solana_sdk::declare_id!("H44JGZCFs9uViWBeC8LodrbCn8VWjg8GkjtdeRx4LCLM");
}
pub mod enable {
solana_sdk::declare_id!("3dG48jJJT3nDBLiGyFABCpTEacP8JNYzjrmCZFv7mbUU");
}
}
2020-10-08 17:35:48 -07:00
}
pub mod spl_token_v2_multisig_fix {
solana_sdk::declare_id!("E5JiFDQCwyC6QfT9REFyMpfK2mHcmv1GUDySU1Ue7TYv");
}
pub mod bpf_loader2_program {
solana_sdk::declare_id!("DFBnrgThdzH4W6wZ12uGPoWcMnvfZj11EHnxHcVxLPhD");
}
2020-10-28 13:16:13 -07:00
pub mod bpf_compute_budget_balancing {
solana_sdk::declare_id!("HxvjqDSiF5sYdSYuCXsUnS8UeAoWsMT9iGoFP8pgV1mB");
}
2020-09-29 23:29:20 -07:00
pub mod sha256_syscall_enabled {
solana_sdk::declare_id!("D7KfP7bZxpkYtD4Pc38t9htgs1k5k47Yhxe4rp6WDVi8");
}
pub mod no_overflow_rent_distribution {
solana_sdk::declare_id!("4kpdyrcj5jS47CZb2oJGfVxjYbsMm2Kx97gFyZrxxwXz");
}
pub mod ristretto_mul_syscall_enabled {
solana_sdk::declare_id!("HRe7A6aoxgjKzdjbBv6HTy7tJ4YWqE6tVmYCGho6S9Aq");
}
2020-10-09 10:33:12 -07:00
pub mod max_invoke_depth_4 {
solana_sdk::declare_id!("EdM9xggY5y7AhNMskRG8NgGMnaP4JFNsWi8ZZtyT1af5");
}
pub mod max_program_call_depth_64 {
solana_sdk::declare_id!("YCKSgA6XmjtkQrHBQjpyNrX6EMhJPcYcLWMVgWn36iv");
}
pub mod timestamp_correction {
solana_sdk::declare_id!("3zydSLUwuqqsV3wL5wBsaVgyvMox3XTHx7zLEuQf1U2Z");
}
pub mod cumulative_rent_related_fixes {
solana_sdk::declare_id!("FtjnuAtJTWwX3Kx9m24LduNEhzaGuuPfDW6e14SX2Fy5");
}
2020-10-28 12:39:48 -07:00
pub mod sol_log_compute_units_syscall {
solana_sdk::declare_id!("BHuZqHAj7JdZc68wVgZZcy51jZykvgrx4zptR44RyChe");
}
2020-10-15 09:11:54 -07:00
pub mod pubkey_log_syscall_enabled {
solana_sdk::declare_id!("MoqiU1vryuCGQSxFKA1SZ316JdLEFFhoAu6cKUNk7dN");
}
pub mod pull_request_ping_pong_check {
solana_sdk::declare_id!("5RzEHTnf6D7JPZCvwEzjM19kzBsyjSU3HoMfXaQmVgnZ");
}
2020-10-26 11:45:09 -07:00
pub mod timestamp_bounding {
solana_sdk::declare_id!("2cGj3HJYPhBrtQizd7YbBxEsifFs5qhzabyFjUAp6dBa");
2020-10-26 11:45:09 -07:00
}
pub mod stake_program_v2 {
solana_sdk::declare_id!("Gvd9gGJZDHGMNf1b3jkxrfBQSR5etrfTQSBNKCvLSFJN");
}
pub mod rewrite_stake {
solana_sdk::declare_id!("6ap2eGy7wx5JmsWUmQ5sHwEWrFSDUxSti2k5Hbfv5BZG");
}
pub mod filter_stake_delegation_accounts {
solana_sdk::declare_id!("GE7fRxmW46K6EmCD9AMZSbnaJ2e3LfqCZzdHi9hmYAgi");
}
pub mod simple_capitalization {
solana_sdk::declare_id!("9r69RnnxABmpcPFfj1yhg4n9YFR2MNaLdKJCC6v3Speb");
}
2020-12-14 15:35:10 -08:00
pub mod bpf_loader_upgradeable_program {
solana_sdk::declare_id!("FbhK8HN9qvNHvJcoFVHAEUCNkagHvu7DTWzdnLuVQ5u4");
}
pub mod try_find_program_address_syscall_enabled {
solana_sdk::declare_id!("EMsMNadQNhCYDyGpYH5Tx6dGHxiUqKHk782PU5XaWfmi");
}
pub mod warp_timestamp {
solana_sdk::declare_id!("Bfqm7fGk5MBptqa2WHXWFLH7uJvq8hkJcAQPipy2bAMk");
}
pub mod stake_program_v3 {
solana_sdk::declare_id!("Ego6nTu7WsBcZBvVqJQKp6Yku2N3mrfG8oYCfaLZkAeK");
}
2020-12-28 17:14:17 -08:00
pub mod max_cpi_instruction_size_ipv6_mtu {
solana_sdk::declare_id!("5WLtuUJA5VVA1Cc28qULPfGs8anhoBev8uNqaaXeasnf");
}
2020-12-30 18:13:37 -08:00
pub mod limit_cpi_loader_invoke {
solana_sdk::declare_id!("xGbcW7EEC7zMRJ6LaJCob65EJxKryWjwM4rv8f57SRM");
}
pub mod use_loaded_program_accounts {
solana_sdk::declare_id!("FLjgLeg1PJkZimQCVa5sVFtaq6VmSDPw3NvH8iQ3nyHn");
}
pub mod abort_on_all_cpi_failures {
solana_sdk::declare_id!("ED5D5a2hQaECHaMmKpnU48GdsfafdCjkb3pgAw5RKbb2");
}
pub mod use_loaded_executables {
solana_sdk::declare_id!("3Jq7mE2chDpf6oeEDsuGK7orTYEgyQjCPvaRppTNdVGK");
}
pub mod turbine_retransmit_peers_patch {
solana_sdk::declare_id!("5Lu3JnWSFwRYpXzwDMkanWSk6XqSuF2i5fpnVhzB5CTc");
}
pub mod prevent_upgrade_and_invoke {
solana_sdk::declare_id!("BiNjYd8jCYDgAwMqP91uwZs6skWpuHtKrZbckuKESs8N");
}
pub mod track_writable_deescalation {
solana_sdk::declare_id!("HVPSxqskEtRLRT2ZeEMmkmt9FWqoFX4vrN6f5VaadLED");
}
pub mod spl_token_v2_self_transfer_fix {
solana_sdk::declare_id!("BL99GYhdjjcv6ys22C9wPgn2aTVERDbPHHo4NbS3hgp7");
}
pub mod matching_buffer_upgrade_authorities {
solana_sdk::declare_id!("B5PSjDEJvKJEUQSL7q94N7XCEoWJCYum8XfUg7yuugUU");
}
lazy_static! {
2020-09-24 00:22:49 -07:00
/// Map of feature identifiers to user-visible description
pub static ref FEATURE_NAMES: HashMap<Pubkey, &'static str> = [
(instructions_sysvar_enabled::id(), "instructions sysvar"),
(secp256k1_program_enabled::id(), "secp256k1 program"),
(consistent_recent_blockhashes_sysvar::id(), "consistent recentblockhashes sysvar"),
(deprecate_rewards_sysvar::id(), "deprecate unused rewards sysvar"),
(pico_inflation::id(), "pico inflation"),
(full_inflation::devnet_and_testnet::id(), "full inflation on devnet and testnet"),
(spl_token_v2_multisig_fix::id(), "spl-token multisig fix"),
(bpf_loader2_program::id(), "bpf_loader2 program"),
2020-10-28 13:16:13 -07:00
(bpf_compute_budget_balancing::id(), "compute budget balancing"),
(sha256_syscall_enabled::id(), "sha256 syscall"),
(no_overflow_rent_distribution::id(), "no overflow rent distribution"),
(ristretto_mul_syscall_enabled::id(), "ristretto multiply syscall"),
2020-10-09 10:33:12 -07:00
(max_invoke_depth_4::id(), "max invoke call depth 4"),
(max_program_call_depth_64::id(), "max program call depth 64"),
(timestamp_correction::id(), "correct bank timestamps"),
(cumulative_rent_related_fixes::id(), "rent fixes (#10206, #10468, #11342)"),
2020-10-28 12:39:48 -07:00
(sol_log_compute_units_syscall::id(), "sol_log_compute_units syscall (#13243)"),
2020-10-15 09:11:54 -07:00
(pubkey_log_syscall_enabled::id(), "pubkey log syscall"),
(pull_request_ping_pong_check::id(), "ping-pong packet check #12794"),
2020-10-26 11:45:09 -07:00
(timestamp_bounding::id(), "add timestamp-correction bounding #13120"),
(stake_program_v2::id(), "solana_stake_program v2"),
(rewrite_stake::id(), "rewrite stake"),
(filter_stake_delegation_accounts::id(), "filter stake_delegation_accounts #14062"),
(simple_capitalization::id(), "simple capitalization"),
2020-12-14 15:35:10 -08:00
(bpf_loader_upgradeable_program::id(), "upgradeable bpf loader"),
(try_find_program_address_syscall_enabled::id(), "add try_find_program_address syscall"),
(warp_timestamp::id(), "warp timestamp to current, adjust bounding to 50% #14210 & #14531"),
(stake_program_v3::id(), "solana_stake_program v3"),
(max_cpi_instruction_size_ipv6_mtu::id(), "max cross-program invocation size 1280"),
(limit_cpi_loader_invoke::id(), "loader not authorized via CPI"),
(use_loaded_program_accounts::id(), "use loaded program accounts"),
(abort_on_all_cpi_failures::id(), "abort on all CPI failures"),
(use_loaded_executables::id(), "use loaded executable accounts"),
(turbine_retransmit_peers_patch::id(), "turbine retransmit peers patch #14631"),
(prevent_upgrade_and_invoke::id(), "prevent upgrade and invoke in same tx batch"),
2021-01-22 17:08:06 -08:00
(track_writable_deescalation::id(), "track account writable deescalation"),
(spl_token_v2_self_transfer_fix::id(), "spl-token self-transfer fix"),
(matching_buffer_upgrade_authorities::id(), "Upgradeable buffer and program authorities must match"),
(full_inflation::candidate_example::vote::id(), "Community vote allowing candidate_example to enable full inflation"),
(full_inflation::candidate_example::enable::id(), "Full inflation enabled by candidate_example"),
2021-02-01 20:21:30 -08:00
(full_inflation::bl::enable::id(), "Full inflation enabled by BL"),
(full_inflation::bl::vote::id(), "Community vote allowing BL to enable full inflation"),
(full_inflation::buburuza::enable::id(), "Full inflation enabled by buburuza"),
(full_inflation::buburuza::vote::id(), "Community vote allowing buburuza to enable full inflation"),
(full_inflation::bunghi::enable::id(), "Full inflation enabled by bunghi"),
(full_inflation::bunghi::vote::id(), "Community vote allowing bunghi to enable full inflation"),
(full_inflation::certusone::enable::id(), "Full inflation enabled by Certus One"),
(full_inflation::certusone::vote::id(), "Community vote allowing Certus One to enable full inflation"),
(full_inflation::diman::enable::id(), "Full inflation enabled by Diman"),
(full_inflation::diman::vote::id(), "Community vote allowing Diman to enable full inflation"),
(full_inflation::lowfeevalidation::enable::id(), "Full inflation enabled by lowfeevalidation"),
(full_inflation::lowfeevalidation::vote::id(), "Community vote allowing lowfeevalidation to enable full inflation"),
(full_inflation::nam::enable::id(), "Full inflation enabled by Nam"),
(full_inflation::nam::vote::id(), "Community vote allowing Nam to enable full inflation"),
(full_inflation::p2pvalidator::enable::id(), "Full inflation enabled by p2pvalidator"),
(full_inflation::p2pvalidator::vote::id(), "Community vote allowing p2pvalidator to enable full inflation"),
(full_inflation::rockx::enable::id(), "Full inflation enabled by rockx"),
(full_inflation::rockx::vote::id(), "Community vote allowing rockx to enable full inflation"),
(full_inflation::sotcsa::enable::id(), "Full inflation enabled by sotcsa"),
(full_inflation::sotcsa::vote::id(), "Community vote allowing sotcsa to enable full inflation"),
(full_inflation::stakeconomy::enable::id(), "Full inflation enabled by Stakeconomy.com"),
(full_inflation::stakeconomy::vote::id(), "Community vote allowing Stakeconomy.com to enable full inflation"),
2021-02-05 08:46:09 -08:00
(full_inflation::w3m::vote::id(), "Community vote allowing w3m to enable full inflation"),
(full_inflation::w3m::enable::id(), "Full inflation enabled by w3m"),
/*************** ADD NEW FEATURES HERE ***************/
]
.iter()
.cloned()
.collect();
2020-09-24 00:22:49 -07:00
/// Unique identifier of the current software's feature set
pub static ref ID: Hash = {
let mut hasher = Hasher::default();
let mut feature_ids = FEATURE_NAMES.keys().collect::<Vec<_>>();
feature_ids.sort();
for feature in feature_ids {
hasher.hash(feature.as_ref());
}
hasher.result()
};
}
2020-09-21 14:03:35 -07:00
#[derive(Clone, PartialEq, Eq, Hash)]
pub struct FullInflationFeaturePair {
pub vote_id: Pubkey, // Feature that grants the candidate the ability to enable full inflation
pub enable_id: Pubkey, // Feature to enable full inflation by the candidate
}
lazy_static! {
/// Set of feature pairs that once enabled will trigger full inflation
pub static ref FULL_INFLATION_FEATURE_PAIRS: HashSet<FullInflationFeaturePair> = [
FullInflationFeaturePair {
vote_id: full_inflation::candidate_example::vote::id(),
enable_id: full_inflation::candidate_example::enable::id(),
},
2021-02-01 20:21:30 -08:00
FullInflationFeaturePair {
vote_id: full_inflation::bl::vote::id(),
enable_id: full_inflation::bl::enable::id(),
},
FullInflationFeaturePair {
vote_id: full_inflation::bunghi::vote::id(),
enable_id: full_inflation::bunghi::enable::id(),
},
FullInflationFeaturePair {
vote_id: full_inflation::buburuza::vote::id(),
enable_id: full_inflation::buburuza::enable::id(),
},
FullInflationFeaturePair {
vote_id: full_inflation::certusone::vote::id(),
enable_id: full_inflation::certusone::enable::id(),
},
FullInflationFeaturePair {
vote_id: full_inflation::diman::vote::id(),
enable_id: full_inflation::diman::enable::id(),
},
FullInflationFeaturePair {
vote_id: full_inflation::lowfeevalidation::vote::id(),
enable_id: full_inflation::lowfeevalidation::enable::id(),
},
FullInflationFeaturePair {
vote_id: full_inflation::nam::vote::id(),
enable_id: full_inflation::nam::enable::id(),
},
FullInflationFeaturePair {
vote_id: full_inflation::p2pvalidator::vote::id(),
enable_id: full_inflation::p2pvalidator::enable::id(),
},
FullInflationFeaturePair {
vote_id: full_inflation::rockx::vote::id(),
enable_id: full_inflation::rockx::enable::id(),
},
FullInflationFeaturePair {
vote_id: full_inflation::sotcsa::vote::id(),
enable_id: full_inflation::sotcsa::enable::id(),
},
FullInflationFeaturePair {
vote_id: full_inflation::stakeconomy::vote::id(),
enable_id: full_inflation::stakeconomy::enable::id(),
},
2021-02-05 08:46:09 -08:00
FullInflationFeaturePair {
vote_id: full_inflation::w3m::vote::id(),
enable_id: full_inflation::w3m::enable::id(),
},
]
.iter()
.cloned()
.collect();
}
2020-09-24 00:22:49 -07:00
/// `FeatureSet` holds the set of currently active/inactive runtime features
2020-10-20 09:05:45 -07:00
#[derive(AbiExample, Debug, Clone)]
2020-09-21 14:03:35 -07:00
pub struct FeatureSet {
pub active: HashMap<Pubkey, Slot>,
2020-09-21 14:03:35 -07:00
pub inactive: HashSet<Pubkey>,
}
impl Default for FeatureSet {
fn default() -> Self {
2020-09-24 00:22:49 -07:00
// All features disabled
2020-09-21 14:03:35 -07:00
Self {
active: HashMap::new(),
inactive: FEATURE_NAMES.keys().cloned().collect(),
2020-09-21 14:03:35 -07:00
}
}
}
impl FeatureSet {
pub fn is_active(&self, feature_id: &Pubkey) -> bool {
self.active.contains_key(feature_id)
}
pub fn activated_slot(&self, feature_id: &Pubkey) -> Option<Slot> {
self.active.get(feature_id).copied()
}
pub fn cumulative_rent_related_fixes_enabled(&self) -> bool {
self.is_active(&cumulative_rent_related_fixes::id())
}
/// List of enabled features that trigger full inflation
pub fn full_inflation_features_enabled(&self) -> HashSet<Pubkey> {
let mut hash_set = FULL_INFLATION_FEATURE_PAIRS
.iter()
.filter_map(|pair| {
if self.is_active(&pair.vote_id) && self.is_active(&pair.enable_id) {
Some(pair.enable_id)
} else {
None
}
})
.collect::<HashSet<_>>();
if self.is_active(&full_inflation::devnet_and_testnet::id()) {
hash_set.insert(full_inflation::devnet_and_testnet::id());
}
hash_set
}
/// All features enabled, useful for testing
pub fn all_enabled() -> Self {
Self {
active: FEATURE_NAMES.keys().cloned().map(|key| (key, 0)).collect(),
inactive: HashSet::new(),
}
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_full_inflation_features_enabled_devnet_and_testnet() {
let mut feature_set = FeatureSet::default();
assert!(feature_set.full_inflation_features_enabled().is_empty());
feature_set
.active
.insert(full_inflation::devnet_and_testnet::id(), 42);
assert_eq!(
feature_set.full_inflation_features_enabled(),
[full_inflation::devnet_and_testnet::id()]
.iter()
.cloned()
.collect()
);
}
#[test]
fn test_full_inflation_features_enabled() {
// Normal sequence: vote_id then enable_id
let mut feature_set = FeatureSet::default();
assert!(feature_set.full_inflation_features_enabled().is_empty());
feature_set
.active
.insert(full_inflation::candidate_example::vote::id(), 42);
assert!(feature_set.full_inflation_features_enabled().is_empty());
feature_set
.active
.insert(full_inflation::candidate_example::enable::id(), 42);
assert_eq!(
feature_set.full_inflation_features_enabled(),
[full_inflation::candidate_example::enable::id()]
.iter()
.cloned()
.collect()
);
// Backwards sequence: enable_id and then vote_id
let mut feature_set = FeatureSet::default();
assert!(feature_set.full_inflation_features_enabled().is_empty());
feature_set
.active
.insert(full_inflation::candidate_example::enable::id(), 42);
assert!(feature_set.full_inflation_features_enabled().is_empty());
feature_set
.active
.insert(full_inflation::candidate_example::vote::id(), 42);
assert_eq!(
feature_set.full_inflation_features_enabled(),
[full_inflation::candidate_example::enable::id()]
.iter()
.cloned()
.collect()
);
}
}