Skip to content

Commit

Permalink
genesis: Make "cluster-type" aware of features (#2587)
Browse files Browse the repository at this point in the history
* genesis: Make "cluster-type" aware of features

#### Problem

The `--cluster-type` parameter in solana-genesis mentions cluster
features, but refers to things like epoch length and  hashes per tick.
This behavior is confusing because it doesn't include the feature set.

#### Summary of changes

For "mainnet-beta", "testnet", and "devnet", clone the cluster feature
set, and deactivate the appropriate features.

* Allow deactivating individual features with --cluster-type
  • Loading branch information
joncinque authored Aug 15, 2024
1 parent ecb44d7 commit 6476d5f
Show file tree
Hide file tree
Showing 3 changed files with 100 additions and 15 deletions.
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions genesis/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ solana-cli-config = { workspace = true }
solana-entry = { workspace = true }
solana-ledger = { workspace = true }
solana-logger = { workspace = true }
solana-rpc-client = { workspace = true }
solana-rpc-client-api = { workspace = true }
solana-runtime = { workspace = true }
solana-sdk = { workspace = true }
solana-stake-program = { workspace = true }
Expand Down
111 changes: 96 additions & 15 deletions genesis/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,23 @@ use {
cluster_type_of, pubkey_of, pubkeys_of, unix_timestamp_from_rfc3339_datetime,
},
input_validators::{
is_pubkey, is_pubkey_or_keypair, is_rfc3339_datetime, is_slot, is_valid_percentage,
is_pubkey, is_pubkey_or_keypair, is_rfc3339_datetime, is_slot, is_url_or_moniker,
is_valid_percentage, normalize_to_url_if_moniker,
},
},
solana_entry::poh::compute_hashes_per_tick,
solana_genesis::{genesis_accounts::add_genesis_accounts, Base64Account},
solana_ledger::{blockstore::create_new_ledger, blockstore_options::LedgerColumnOptions},
solana_rpc_client::rpc_client::RpcClient,
solana_rpc_client_api::request::MAX_MULTIPLE_ACCOUNTS,
solana_sdk::{
account::{Account, AccountSharedData, ReadableAccount, WritableAccount},
bpf_loader_upgradeable::UpgradeableLoaderState,
clock,
commitment_config::CommitmentConfig,
epoch_schedule::EpochSchedule,
feature,
feature_set::FEATURE_NAMES,
fee_calculator::FeeRateGovernor,
genesis_config::{ClusterType, GenesisConfig},
inflation::Inflation,
Expand Down Expand Up @@ -106,6 +112,70 @@ pub fn load_genesis_accounts(file: &str, genesis_config: &mut GenesisConfig) ->
Ok(lamports)
}

fn check_rpc_genesis_hash(
cluster_type: &ClusterType,
rpc_client: &RpcClient,
) -> Result<(), Box<dyn std::error::Error>> {
if let Some(genesis_hash) = cluster_type.get_genesis_hash() {
let rpc_genesis_hash = rpc_client.get_genesis_hash()?;
if rpc_genesis_hash != genesis_hash {
return Err(format!(
"The genesis hash for the specified cluster {cluster_type:?} does not match the \
genesis hash reported by the specified RPC. Cluster genesis hash: \
{genesis_hash}, RPC reported genesis hash: {rpc_genesis_hash}"
)
.into());
}
}
Ok(())
}

fn features_to_deactivate_for_cluster(
cluster_type: &ClusterType,
matches: &ArgMatches<'_>,
) -> Result<Vec<Pubkey>, Box<dyn error::Error>> {
let mut features_to_deactivate = pubkeys_of(matches, "deactivate_feature").unwrap_or_default();
if cluster_type == &ClusterType::Development {
return Ok(features_to_deactivate);
}

// if we're here, the cluster type must be one of "mainnet-beta", "testnet", or "devnet"
assert!(matches!(
cluster_type,
ClusterType::MainnetBeta | ClusterType::Testnet | ClusterType::Devnet
));
let json_rpc_url = normalize_to_url_if_moniker(
matches
.value_of("json_rpc_url")
.unwrap_or(matches.value_of("cluster_type").unwrap()),
);
let rpc_client = RpcClient::new_with_commitment(json_rpc_url, CommitmentConfig::confirmed());
check_rpc_genesis_hash(cluster_type, &rpc_client)?;
for feature_ids in FEATURE_NAMES
.keys()
.cloned()
.collect::<Vec<Pubkey>>()
.chunks(MAX_MULTIPLE_ACCOUNTS)
{
rpc_client
.get_multiple_accounts(feature_ids)
.map_err(|err| format!("Failed to fetch: {err}"))?
.into_iter()
.zip(feature_ids)
.for_each(|(maybe_account, feature_id)| {
if maybe_account
.as_ref()
.and_then(feature::from_account)
.and_then(|feature| feature.activated_at)
.is_none()
{
features_to_deactivate.push(*feature_id);
}
});
}
Ok(features_to_deactivate)
}

#[allow(clippy::cognitive_complexity)]
fn main() -> Result<(), Box<dyn error::Error>> {
let default_faucet_pubkey = solana_cli_config::Config::default().keypair_path;
Expand Down Expand Up @@ -407,6 +477,20 @@ fn main() -> Result<(), Box<dyn error::Error>> {
.possible_values(&["pico", "full", "none"])
.help("Selects inflation"),
)
.arg(
Arg::with_name("json_rpc_url")
.short("u")
.long("url")
.value_name("URL_OR_MONIKER")
.takes_value(true)
.global(true)
.validator(is_url_or_moniker)
.help(
"URL for Solana's JSON RPC or moniker (or their first letter): \
[mainnet-beta, testnet, devnet, localhost]. Used for cloning \
feature sets",
),
)
.get_matches();

let ledger_path = PathBuf::from(matches.value_of("ledger_path").unwrap());
Expand Down Expand Up @@ -480,12 +564,11 @@ fn main() -> Result<(), Box<dyn error::Error>> {
let cluster_type = cluster_type_of(&matches, "cluster_type").unwrap();

// Get the features to deactivate if provided
let features_to_deactivate = pubkeys_of(&matches, "deactivate_feature").unwrap_or_default();

if cluster_type != ClusterType::Development && !features_to_deactivate.is_empty() {
eprintln!("Error: The --deativate-feature argument cannot be used with --cluster-type={cluster_type:?}");
std::process::exit(1);
}
let features_to_deactivate = features_to_deactivate_for_cluster(&cluster_type, &matches)
.unwrap_or_else(|e| {
eprintln!("{e}");
std::process::exit(1);
});

match matches.value_of("hashes_per_tick").unwrap() {
"auto" => match cluster_type {
Expand Down Expand Up @@ -594,14 +677,12 @@ fn main() -> Result<(), Box<dyn error::Error>> {
}

solana_stake_program::add_genesis_accounts(&mut genesis_config);
if genesis_config.cluster_type == ClusterType::Development {
solana_runtime::genesis_utils::activate_all_features(&mut genesis_config);
if !features_to_deactivate.is_empty() {
solana_runtime::genesis_utils::deactivate_features(
&mut genesis_config,
&features_to_deactivate,
);
}
solana_runtime::genesis_utils::activate_all_features(&mut genesis_config);
if !features_to_deactivate.is_empty() {
solana_runtime::genesis_utils::deactivate_features(
&mut genesis_config,
&features_to_deactivate,
);
}

if let Some(files) = matches.values_of("primordial_accounts_file") {
Expand Down

0 comments on commit 6476d5f

Please sign in to comment.