Add GenesisBlock::OperatingMode to control how cluster features are activated (#6430)

This commit is contained in:
Michael Vines 2019-10-23 11:50:10 -07:00 committed by GitHub
parent 35d6196384
commit 35cc74ef25
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 256 additions and 46 deletions

6
Cargo.lock generated
View File

@ -3460,6 +3460,7 @@ dependencies = [
name = "solana-genesis-programs" name = "solana-genesis-programs"
version = "0.20.0" version = "0.20.0"
dependencies = [ dependencies = [
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
"solana-bpf-loader-api 0.20.0", "solana-bpf-loader-api 0.20.0",
"solana-bpf-loader-program 0.20.0", "solana-bpf-loader-program 0.20.0",
"solana-budget-api 0.20.0", "solana-budget-api 0.20.0",
@ -3470,6 +3471,7 @@ dependencies = [
"solana-exchange-program 0.20.0", "solana-exchange-program 0.20.0",
"solana-move-loader-api 0.20.0", "solana-move-loader-api 0.20.0",
"solana-move-loader-program 0.20.0", "solana-move-loader-program 0.20.0",
"solana-runtime 0.20.0",
"solana-sdk 0.20.0", "solana-sdk 0.20.0",
"solana-stake-api 0.20.0", "solana-stake-api 0.20.0",
"solana-stake-program 0.20.0", "solana-stake-program 0.20.0",
@ -3561,6 +3563,7 @@ dependencies = [
"serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_derive 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", "serde_derive 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)",
"solana-budget-api 0.20.0", "solana-budget-api 0.20.0",
"solana-genesis-programs 0.20.0",
"solana-logger 0.20.0", "solana-logger 0.20.0",
"solana-measure 0.20.0", "solana-measure 0.20.0",
"solana-merkle-tree 0.20.0", "solana-merkle-tree 0.20.0",
@ -3618,10 +3621,12 @@ dependencies = [
"solana-bench-exchange 0.20.0", "solana-bench-exchange 0.20.0",
"solana-bench-tps 0.20.0", "solana-bench-tps 0.20.0",
"solana-client 0.20.0", "solana-client 0.20.0",
"solana-config-api 0.20.0",
"solana-core 0.20.0", "solana-core 0.20.0",
"solana-drone 0.20.0", "solana-drone 0.20.0",
"solana-exchange-api 0.20.0", "solana-exchange-api 0.20.0",
"solana-exchange-program 0.20.0", "solana-exchange-program 0.20.0",
"solana-genesis-programs 0.20.0",
"solana-ledger 0.20.0", "solana-ledger 0.20.0",
"solana-logger 0.20.0", "solana-logger 0.20.0",
"solana-move-loader-api 0.20.0", "solana-move-loader-api 0.20.0",
@ -3632,6 +3637,7 @@ dependencies = [
"solana-stake-api 0.20.0", "solana-stake-api 0.20.0",
"solana-storage-api 0.20.0", "solana-storage-api 0.20.0",
"solana-storage-program 0.20.0", "solana-storage-program 0.20.0",
"solana-vest-api 0.20.0",
"solana-vote-api 0.20.0", "solana-vote-api 0.20.0",
"symlink 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "symlink 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)",

View File

@ -393,6 +393,12 @@ fn get_bank_forks(
verify_ledger: bool, verify_ledger: bool,
dev_halt_at_slot: Option<Slot>, dev_halt_at_slot: Option<Slot>,
) -> (BankForks, Vec<BankForksInfo>, LeaderScheduleCache) { ) -> (BankForks, Vec<BankForksInfo>, LeaderScheduleCache) {
let process_options = blocktree_processor::ProcessOptions {
verify_ledger,
dev_halt_at_slot,
..blocktree_processor::ProcessOptions::default()
};
if let Some(snapshot_config) = snapshot_config.as_ref() { if let Some(snapshot_config) = snapshot_config.as_ref() {
info!( info!(
"Initializing snapshot path: {:?}", "Initializing snapshot path: {:?}",
@ -417,13 +423,10 @@ fn get_bank_forks(
.expect("Load from snapshot failed"); .expect("Load from snapshot failed");
return blocktree_processor::process_blocktree_from_root( return blocktree_processor::process_blocktree_from_root(
genesis_block,
blocktree, blocktree,
Arc::new(deserialized_bank), Arc::new(deserialized_bank),
&blocktree_processor::ProcessOptions { &process_options,
verify_ledger,
dev_halt_at_slot,
..blocktree_processor::ProcessOptions::default()
},
) )
.expect("processing blocktree after loading snapshot failed"); .expect("processing blocktree after loading snapshot failed");
} else { } else {
@ -438,11 +441,7 @@ fn get_bank_forks(
&genesis_block, &genesis_block,
&blocktree, &blocktree,
account_paths, account_paths,
blocktree_processor::ProcessOptions { process_options,
verify_ledger,
dev_halt_at_slot,
..blocktree_processor::ProcessOptions::default()
},
) )
.expect("process_blocktree failed") .expect("process_blocktree failed")
} }

View File

@ -9,7 +9,7 @@ use solana_sdk::{
clock, clock,
epoch_schedule::EpochSchedule, epoch_schedule::EpochSchedule,
fee_calculator::FeeCalculator, fee_calculator::FeeCalculator,
genesis_block::GenesisBlock, genesis_block::{GenesisBlock, OperatingMode},
native_token::sol_to_lamports, native_token::sol_to_lamports,
poh_config::PohConfig, poh_config::PohConfig,
pubkey::Pubkey, pubkey::Pubkey,
@ -285,6 +285,11 @@ fn main() -> Result<(), Box<dyn error::Error>> {
.takes_value(true) .takes_value(true)
.help("The location of keypairs for primordial accounts and balance"), .help("The location of keypairs for primordial accounts and balance"),
) )
.arg(
Arg::with_name("development")
.long("dev")
.help("Configure the cluster for development mode where all features are available at epoch 0"),
)
.get_matches(); .get_matches();
let bootstrap_leader_keypair_file = matches.value_of("bootstrap_leader_keypair_file").unwrap(); let bootstrap_leader_keypair_file = matches.value_of("bootstrap_leader_keypair_file").unwrap();
@ -376,14 +381,21 @@ fn main() -> Result<(), Box<dyn error::Error>> {
} }
} }
let operating_mode = if matches.is_present("development") {
OperatingMode::Development
} else {
OperatingMode::SoftLaunch
};
let native_instruction_processors = solana_genesis_programs::get(operating_mode, 0).unwrap();
let mut genesis_block = GenesisBlock { let mut genesis_block = GenesisBlock {
accounts, accounts,
native_instruction_processors: solana_genesis_programs::get(), native_instruction_processors,
ticks_per_slot, ticks_per_slot,
epoch_schedule, epoch_schedule,
fee_calculator, fee_calculator,
rent_calculator, rent_calculator,
poh_config, poh_config,
operating_mode,
..GenesisBlock::default() ..GenesisBlock::default()
}; };

View File

@ -9,6 +9,7 @@ homepage = "https://solana.com/"
edition = "2018" edition = "2018"
[dependencies] [dependencies]
log = { version = "0.4.8" }
solana-bpf-loader-api = { path = "../programs/bpf_loader_api", version = "0.20.0" } solana-bpf-loader-api = { path = "../programs/bpf_loader_api", version = "0.20.0" }
solana-bpf-loader-program = { path = "../programs/bpf_loader_program", version = "0.20.0" } solana-bpf-loader-program = { path = "../programs/bpf_loader_program", version = "0.20.0" }
solana-budget-api = { path = "../programs/budget_api", version = "0.20.0" } solana-budget-api = { path = "../programs/budget_api", version = "0.20.0" }
@ -19,6 +20,7 @@ solana-exchange-api = { path = "../programs/exchange_api", version = "0.20.0" }
solana-exchange-program = { path = "../programs/exchange_program", version = "0.20.0" } solana-exchange-program = { path = "../programs/exchange_program", version = "0.20.0" }
solana-move-loader-program = { path = "../programs/move_loader_program", version = "0.20.0", optional = true } solana-move-loader-program = { path = "../programs/move_loader_program", version = "0.20.0", optional = true }
solana-move-loader-api = { path = "../programs/move_loader_api", version = "0.20.0", optional = true } solana-move-loader-api = { path = "../programs/move_loader_api", version = "0.20.0", optional = true }
solana-runtime = { path = "../runtime", version = "0.20.0" }
solana-sdk = { path = "../sdk", version = "0.20.0" } solana-sdk = { path = "../sdk", version = "0.20.0" }
solana-stake-api = { path = "../programs/stake_api", version = "0.20.0" } solana-stake-api = { path = "../programs/stake_api", version = "0.20.0" }
solana-stake-program = { path = "../programs/stake_program", version = "0.20.0" } solana-stake-program = { path = "../programs/stake_program", version = "0.20.0" }

View File

@ -1,5 +1,7 @@
use solana_sdk::pubkey::Pubkey; use solana_sdk::{
use solana_sdk::system_program::solana_system_program; clock::Epoch, genesis_block::OperatingMode, pubkey::Pubkey,
system_program::solana_system_program,
};
#[macro_use] #[macro_use]
extern crate solana_bpf_loader_program; extern crate solana_bpf_loader_program;
@ -21,43 +23,102 @@ extern crate solana_vest_program;
#[macro_use] #[macro_use]
extern crate solana_vote_program; extern crate solana_vote_program;
pub fn get() -> Vec<(String, Pubkey)> { use log::*;
vec![ use solana_runtime::bank::{Bank, EnteredEpochCallback};
solana_system_program(),
solana_bpf_loader_program!(), pub fn get(operating_mode: OperatingMode, epoch: Epoch) -> Option<Vec<(String, Pubkey)>> {
solana_budget_program!(), match operating_mode {
solana_config_program!(), OperatingMode::Development => {
solana_exchange_program!(), if epoch == 0 {
#[cfg(feature = "move")] Some(vec![
solana_move_loader_program!(), // Enable all SoftLaunch programs
solana_stake_program!(), solana_system_program(),
solana_storage_program!(), solana_bpf_loader_program!(),
solana_vest_program!(), solana_config_program!(),
solana_vote_program!(), solana_stake_program!(),
] solana_storage_program!(),
solana_vest_program!(),
solana_vote_program!(),
// Programs that are only available in Development mode
solana_budget_program!(),
solana_exchange_program!(),
#[cfg(feature = "move")]
solana_move_loader_program!(),
])
} else {
None
}
}
OperatingMode::SoftLaunch => {
if epoch == 0 {
// Voting and Staking only at epoch 0
Some(vec![solana_stake_program!(), solana_vote_program!()])
} else if epoch == std::u64::MAX - 1 {
// System program and Archivers are activated next
//
// The epoch of std::u64::MAX - 1 is a placeholder and is expected to be reduced in
// a future hard fork.
Some(vec![
solana_config_program!(),
solana_storage_program!(),
solana_system_program(),
solana_vest_program!(),
])
} else if epoch == std::u64::MAX {
// Finally 3rd party BPF programs are available
//
// The epoch of std::u64::MAX is a placeholder and is expected to be reduced in a
// future hard fork.
Some(vec![solana_bpf_loader_program!()])
} else {
None
}
}
}
}
pub fn get_entered_epoch_callback(operating_mode: OperatingMode) -> EnteredEpochCallback {
Box::new(move |bank: &mut Bank| {
info!(
"Entering epoch {} with operating_mode {:?}",
bank.epoch(),
operating_mode
);
if let Some(new_programs) = get(operating_mode, bank.epoch()) {
for (name, program_id) in new_programs.iter() {
info!("Registering {} at {}", name, program_id);
bank.register_native_instruction_processor(name, program_id);
}
}
})
} }
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*;
use std::collections::HashSet; use std::collections::HashSet;
#[test] #[test]
fn test_id_uniqueness() { fn test_id_uniqueness() {
let mut unique = HashSet::new(); let mut unique = HashSet::new();
let ids = vec![ let ids = get(OperatingMode::Development, 0).unwrap();
solana_budget_api::id(),
solana_config_api::id(),
solana_exchange_api::id(),
#[cfg(feature = "move")]
solana_move_loader_api::id(),
solana_sdk::bpf_loader::id(),
solana_sdk::native_loader::id(),
solana_sdk::system_program::id(),
solana_stake_api::id(),
solana_storage_api::id(),
solana_vest_api::id(),
solana_vote_api::id(),
];
assert!(ids.into_iter().all(move |id| unique.insert(id))); assert!(ids.into_iter().all(move |id| unique.insert(id)));
} }
#[test]
fn test_development_programs() {
assert_eq!(get(OperatingMode::Development, 0).unwrap().len(), 9);
assert_eq!(get(OperatingMode::Development, 1), None);
}
#[test]
fn test_softlaunch_programs() {
assert_eq!(
get(OperatingMode::SoftLaunch, 0),
Some(vec![solana_stake_program!(), solana_vote_program!(),])
);
assert_eq!(get(OperatingMode::SoftLaunch, 1), None);
assert!(get(OperatingMode::SoftLaunch, std::u64::MAX - 1).is_some());
assert!(get(OperatingMode::SoftLaunch, std::u64::MAX).is_some());
}
} }

View File

@ -26,6 +26,7 @@ rayon = "1.2.0"
reed-solomon-erasure = { package = "solana-reed-solomon-erasure", version = "4.0.1-3", features = ["simd-accel"] } reed-solomon-erasure = { package = "solana-reed-solomon-erasure", version = "4.0.1-3", features = ["simd-accel"] }
serde = "1.0.101" serde = "1.0.101"
serde_derive = "1.0.101" serde_derive = "1.0.101"
solana-genesis-programs = { path = "../genesis_programs", version = "0.20.0" }
solana-logger = { path = "../logger", version = "0.20.0" } solana-logger = { path = "../logger", version = "0.20.0" }
solana-measure = { path = "../measure", version = "0.20.0" } solana-measure = { path = "../measure", version = "0.20.0" }
solana-merkle-tree = { path = "../merkle-tree", version = "0.20.0" } solana-merkle-tree = { path = "../merkle-tree", version = "0.20.0" }

View File

@ -206,13 +206,14 @@ pub fn process_blocktree(
// Setup bank for slot 0 // Setup bank for slot 0
let bank0 = Arc::new(Bank::new_with_paths(&genesis_block, account_paths)); let bank0 = Arc::new(Bank::new_with_paths(&genesis_block, account_paths));
info!("processing ledger from bank 0..."); info!("processing ledger for bank 0...");
process_bank_0(&bank0, blocktree, &opts)?; process_bank_0(&bank0, blocktree, &opts)?;
process_blocktree_from_root(blocktree, bank0, &opts) process_blocktree_from_root(genesis_block, blocktree, bank0, &opts)
} }
// Process blocktree from a known root bank // Process blocktree from a known root bank
pub fn process_blocktree_from_root( pub fn process_blocktree_from_root(
genesis_block: &GenesisBlock,
blocktree: &Blocktree, blocktree: &Blocktree,
bank: Arc<Bank>, bank: Arc<Bank>,
opts: &ProcessOptions, opts: &ProcessOptions,
@ -224,6 +225,10 @@ pub fn process_blocktree_from_root(
let now = Instant::now(); let now = Instant::now();
let mut rooted_path = vec![start_slot]; let mut rooted_path = vec![start_slot];
bank.set_entered_epoch_callback(solana_genesis_programs::get_entered_epoch_callback(
genesis_block.operating_mode,
));
blocktree blocktree
.set_roots(&[start_slot]) .set_roots(&[start_slot])
.expect("Couldn't set root on startup"); .expect("Couldn't set root on startup");
@ -1699,7 +1704,7 @@ pub mod tests {
// Test process_blocktree_from_root() from slot 1 onwards // Test process_blocktree_from_root() from slot 1 onwards
let (bank_forks, bank_forks_info, _) = let (bank_forks, bank_forks_info, _) =
process_blocktree_from_root(&blocktree, bank1, &opts).unwrap(); process_blocktree_from_root(&genesis_block, &blocktree, bank1, &opts).unwrap();
assert_eq!(bank_forks_info.len(), 1); // One fork assert_eq!(bank_forks_info.len(), 1); // One fork
assert_eq!( assert_eq!(

View File

@ -13,11 +13,13 @@ log = "0.4.8"
rand = "0.6.5" rand = "0.6.5"
solana-bench-exchange = { path = "../bench-exchange", version = "0.20.0" } solana-bench-exchange = { path = "../bench-exchange", version = "0.20.0" }
solana-bench-tps = { path = "../bench-tps", version = "0.20.0" } solana-bench-tps = { path = "../bench-tps", version = "0.20.0" }
solana-config-api = { path = "../programs/config_api", version = "0.20.0" }
solana-core = { path = "../core", version = "0.20.0" } solana-core = { path = "../core", version = "0.20.0" }
solana-client = { path = "../client", version = "0.20.0" } solana-client = { path = "../client", version = "0.20.0" }
solana-drone = { path = "../drone", version = "0.20.0" } solana-drone = { path = "../drone", version = "0.20.0" }
solana-exchange-api = { path = "../programs/exchange_api", version = "0.20.0" } solana-exchange-api = { path = "../programs/exchange_api", version = "0.20.0" }
solana-exchange-program = { path = "../programs/exchange_program", version = "0.20.0" } solana-exchange-program = { path = "../programs/exchange_program", version = "0.20.0" }
solana-genesis-programs = { path = "../genesis_programs", version = "0.20.0" }
solana-ledger = { path = "../ledger", version = "0.20.0" } solana-ledger = { path = "../ledger", version = "0.20.0" }
solana-logger = { path = "../logger", version = "0.20.0" } solana-logger = { path = "../logger", version = "0.20.0" }
solana-move-loader-api = { path = "../programs/move_loader_api", version = "0.20.0", optional = true } solana-move-loader-api = { path = "../programs/move_loader_api", version = "0.20.0", optional = true }
@ -27,6 +29,7 @@ solana-sdk = { path = "../sdk", version = "0.20.0" }
solana-stake-api = { path = "../programs/stake_api", version = "0.20.0" } solana-stake-api = { path = "../programs/stake_api", version = "0.20.0" }
solana-storage-api = { path = "../programs/storage_api", version = "0.20.0" } solana-storage-api = { path = "../programs/storage_api", version = "0.20.0" }
solana-storage-program = { path = "../programs/storage_program", version = "0.20.0" } solana-storage-program = { path = "../programs/storage_program", version = "0.20.0" }
solana-vest-api = { path = "../programs/vest_api", version = "0.20.0" }
solana-vote-api = { path = "../programs/vote_api", version = "0.20.0" } solana-vote-api = { path = "../programs/vote_api", version = "0.20.0" }
symlink = "0.1.0" symlink = "0.1.0"
tempfile = "3.1.0" tempfile = "3.1.0"

View File

@ -14,7 +14,7 @@ use solana_sdk::{
client::SyncClient, client::SyncClient,
clock::{DEFAULT_SLOTS_PER_EPOCH, DEFAULT_SLOTS_PER_SEGMENT, DEFAULT_TICKS_PER_SLOT}, clock::{DEFAULT_SLOTS_PER_EPOCH, DEFAULT_SLOTS_PER_SEGMENT, DEFAULT_TICKS_PER_SLOT},
epoch_schedule::EpochSchedule, epoch_schedule::EpochSchedule,
genesis_block::GenesisBlock, genesis_block::{GenesisBlock, OperatingMode},
message::Message, message::Message,
poh_config::PohConfig, poh_config::PohConfig,
pubkey::Pubkey, pubkey::Pubkey,
@ -74,6 +74,7 @@ pub struct ClusterConfig {
pub slots_per_segment: u64, pub slots_per_segment: u64,
pub stakers_slot_offset: u64, pub stakers_slot_offset: u64,
pub native_instruction_processors: Vec<(String, Pubkey)>, pub native_instruction_processors: Vec<(String, Pubkey)>,
pub operating_mode: OperatingMode,
pub poh_config: PohConfig, pub poh_config: PohConfig,
} }
@ -90,6 +91,7 @@ impl Default for ClusterConfig {
slots_per_segment: DEFAULT_SLOTS_PER_SEGMENT, slots_per_segment: DEFAULT_SLOTS_PER_SEGMENT,
stakers_slot_offset: DEFAULT_SLOTS_PER_EPOCH, stakers_slot_offset: DEFAULT_SLOTS_PER_EPOCH,
native_instruction_processors: vec![], native_instruction_processors: vec![],
operating_mode: OperatingMode::Development,
poh_config: PohConfig::default(), poh_config: PohConfig::default(),
} }
} }
@ -142,7 +144,19 @@ impl LocalCluster {
genesis_block.slots_per_segment = config.slots_per_segment; genesis_block.slots_per_segment = config.slots_per_segment;
genesis_block.epoch_schedule = genesis_block.epoch_schedule =
EpochSchedule::custom(config.slots_per_epoch, config.stakers_slot_offset, true); EpochSchedule::custom(config.slots_per_epoch, config.stakers_slot_offset, true);
genesis_block.operating_mode = config.operating_mode;
genesis_block.poh_config = config.poh_config.clone(); genesis_block.poh_config = config.poh_config.clone();
match genesis_block.operating_mode {
OperatingMode::SoftLaunch => {
genesis_block.native_instruction_processors =
solana_genesis_programs::get(genesis_block.operating_mode, 0).unwrap()
}
// create_genesis_block_with_leader() assumes OperatingMode::Development so do
// nothing...
OperatingMode::Development => (),
}
genesis_block genesis_block
.native_instruction_processors .native_instruction_processors
.extend_from_slice(&config.native_instruction_processors); .extend_from_slice(&config.native_instruction_processors);

View File

@ -5,6 +5,7 @@ use crate::{
}; };
use log::*; use log::*;
use serial_test_derive::serial; use serial_test_derive::serial;
use solana_client::thin_client::create_client;
use solana_core::{ use solana_core::{
broadcast_stage::BroadcastStageType, gossip_service::discover_cluster, broadcast_stage::BroadcastStageType, gossip_service::discover_cluster,
validator::ValidatorConfig, validator::ValidatorConfig,
@ -15,6 +16,7 @@ use solana_sdk::{
client::SyncClient, client::SyncClient,
clock, clock,
epoch_schedule::{EpochSchedule, MINIMUM_SLOTS_PER_EPOCH}, epoch_schedule::{EpochSchedule, MINIMUM_SLOTS_PER_EPOCH},
genesis_block::OperatingMode,
poh_config::PohConfig, poh_config::PohConfig,
}; };
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
@ -296,6 +298,43 @@ fn test_listener_startup() {
assert_eq!(cluster_nodes.len(), 4); assert_eq!(cluster_nodes.len(), 4);
} }
#[test]
#[serial]
fn test_softlaunch_operating_mode() {
solana_logger::setup();
let config = ClusterConfig {
operating_mode: OperatingMode::SoftLaunch,
node_stakes: vec![100; 1],
cluster_lamports: 1_000,
validator_configs: vec![ValidatorConfig::default(); 1],
..ClusterConfig::default()
};
let cluster = LocalCluster::new(&config);
let (cluster_nodes, _) = discover_cluster(&cluster.entry_point_info.gossip, 1).unwrap();
assert_eq!(cluster_nodes.len(), 1);
let client = create_client(
cluster.entry_point_info.client_facing_addr(),
solana_core::cluster_info::VALIDATOR_PORT_RANGE,
);
// Programs that are not available at soft launch
for program_id in [
&solana_config_api::id(),
&solana_sdk::bpf_loader::id(),
&solana_sdk::system_program::id(),
&solana_vest_api::id(),
]
.iter()
{
assert_eq!(
(program_id, client.get_account(program_id).unwrap()),
(program_id, None)
);
}
}
#[allow(unused_attributes)] #[allow(unused_attributes)]
#[test] #[test]
#[serial] #[serial]

View File

@ -26,6 +26,7 @@ default_arg --bootstrap-storage-keypair "$SOLANA_CONFIG_DIR"/bootstrap-leader/st
default_arg --ledger "$SOLANA_CONFIG_DIR"/bootstrap-leader default_arg --ledger "$SOLANA_CONFIG_DIR"/bootstrap-leader
default_arg --mint "$SOLANA_CONFIG_DIR"/mint-keypair.json default_arg --mint "$SOLANA_CONFIG_DIR"/mint-keypair.json
default_arg --hashes-per-tick auto default_arg --hashes-per-tick auto
default_arg --dev
$solana_genesis "${args[@]}" $solana_genesis "${args[@]}"
( (

View File

@ -151,6 +151,8 @@ impl StatusCacheRc {
} }
} }
pub type EnteredEpochCallback = Box<dyn Fn(&mut Bank) -> () + Sync + Send>;
/// Manager for the state of all accounts and programs after processing its entries. /// Manager for the state of all accounts and programs after processing its entries.
#[derive(Default, Deserialize, Serialize)] #[derive(Default, Deserialize, Serialize)]
pub struct Bank { pub struct Bank {
@ -252,6 +254,11 @@ pub struct Bank {
/// The Message processor /// The Message processor
message_processor: MessageProcessor, message_processor: MessageProcessor,
/// Callback to be notified when a bank enters a new Epoch
/// (used to adjust cluster features over time)
#[serde(skip)]
entered_epoch_callback: Arc<RwLock<Option<EnteredEpochCallback>>>,
} }
impl Default for BlockhashQueue { impl Default for BlockhashQueue {
@ -339,6 +346,7 @@ impl Bank {
tick_height: AtomicU64::new(parent.tick_height.load(Ordering::Relaxed)), tick_height: AtomicU64::new(parent.tick_height.load(Ordering::Relaxed)),
signature_count: AtomicU64::new(0), signature_count: AtomicU64::new(0),
message_processor: MessageProcessor::default(), message_processor: MessageProcessor::default(),
entered_epoch_callback: parent.entered_epoch_callback.clone(),
}; };
datapoint_debug!( datapoint_debug!(
@ -348,6 +356,15 @@ impl Bank {
); );
let leader_schedule_epoch = epoch_schedule.get_leader_schedule_epoch(slot); let leader_schedule_epoch = epoch_schedule.get_leader_schedule_epoch(slot);
if parent.epoch() < new.epoch() {
if let Some(entered_epoch_callback) =
parent.entered_epoch_callback.read().unwrap().as_ref()
{
entered_epoch_callback(&mut new)
}
}
// update epoch_stakes cache // update epoch_stakes cache
// if my parent didn't populate for this staker's epoch, we've // if my parent didn't populate for this staker's epoch, we've
// crossed a boundary // crossed a boundary
@ -1290,6 +1307,13 @@ impl Bank {
self.rc.parent = RwLock::new(Some(parent.clone())); self.rc.parent = RwLock::new(Some(parent.clone()));
} }
pub fn set_entered_epoch_callback(&self, entered_epoch_callback: EnteredEpochCallback) {
std::mem::replace(
&mut *self.entered_epoch_callback.write().unwrap(),
Some(entered_epoch_callback),
);
}
pub fn get_account(&self, pubkey: &Pubkey) -> Option<Account> { pub fn get_account(&self, pubkey: &Pubkey) -> Option<Account> {
self.rc self.rc
.accounts .accounts
@ -2773,6 +2797,41 @@ mod tests {
); );
} }
#[test]
fn test_bank_entered_epoch_callback() {
let (genesis_block, _) = create_genesis_block(500);
let bank0 = Arc::new(Bank::new(&genesis_block));
let callback_count = Arc::new(AtomicU64::new(0));
bank0.set_entered_epoch_callback({
let callback_count = callback_count.clone();
//Box::new(move |_bank: &mut Bank| {
Box::new(move |_| {
callback_count.fetch_add(1, Ordering::SeqCst);
})
});
let _bank1 =
Bank::new_from_parent(&bank0, &Pubkey::default(), bank0.get_slots_in_epoch(0) - 1);
// No callback called while within epoch 0
assert_eq!(callback_count.load(Ordering::SeqCst), 0);
let _bank1 = Bank::new_from_parent(&bank0, &Pubkey::default(), bank0.get_slots_in_epoch(0));
// Callback called as bank1 is in epoch 1
assert_eq!(callback_count.load(Ordering::SeqCst), 1);
callback_count.store(0, Ordering::SeqCst);
let _bank1 = Bank::new_from_parent(
&bank0,
&Pubkey::default(),
std::u64::MAX / bank0.ticks_per_slot - 1,
);
// If the new bank jumps ahead multiple epochs the callback is still only called once.
// This was done to keep the callback implementation simpler as new bank will never jump
// cross multiple epochs in a real deployment.
assert_eq!(callback_count.load(Ordering::SeqCst), 1);
}
#[test] #[test]
fn test_is_delta_true() { fn test_is_delta_true() {
let (genesis_block, mint_keypair) = create_genesis_block(500); let (genesis_block, mint_keypair) = create_genesis_block(500);

View File

@ -21,6 +21,12 @@ use std::{
path::Path, path::Path,
}; };
#[derive(Serialize, Deserialize, Debug, Clone, Copy)]
pub enum OperatingMode {
SoftLaunch, // Cluster features incrementally enabled over time
Development, // All features (including experimental features) available immediately from genesis
}
#[derive(Serialize, Deserialize, Debug, Clone)] #[derive(Serialize, Deserialize, Debug, Clone)]
pub struct GenesisBlock { pub struct GenesisBlock {
pub accounts: Vec<(Pubkey, Account)>, pub accounts: Vec<(Pubkey, Account)>,
@ -33,6 +39,7 @@ pub struct GenesisBlock {
pub rent_calculator: RentCalculator, pub rent_calculator: RentCalculator,
pub inflation: Inflation, pub inflation: Inflation,
pub epoch_schedule: EpochSchedule, pub epoch_schedule: EpochSchedule,
pub operating_mode: OperatingMode,
} }
// useful for basic tests // useful for basic tests
@ -63,6 +70,7 @@ impl Default for GenesisBlock {
fee_calculator: FeeCalculator::default(), fee_calculator: FeeCalculator::default(),
rent_calculator: RentCalculator::default(), rent_calculator: RentCalculator::default(),
epoch_schedule: EpochSchedule::default(), epoch_schedule: EpochSchedule::default(),
operating_mode: OperatingMode::Development,
} }
} }
} }