Port BPFLoader2 activation to FeatureSet and rework built-in program activation
This commit is contained in:
parent
6071d0d206
commit
31696a1d72
|
@ -19,7 +19,6 @@ members = [
|
|||
"perf",
|
||||
"validator",
|
||||
"genesis",
|
||||
"genesis-programs",
|
||||
"gossip",
|
||||
"install",
|
||||
"keygen",
|
||||
|
|
|
@ -45,10 +45,10 @@ serde_derive = "1.0.103"
|
|||
serde_json = "1.0.56"
|
||||
solana-account-decoder = { path = "../account-decoder", version = "1.4.0" }
|
||||
solana-banks-server = { path = "../banks-server", version = "1.4.0" }
|
||||
solana-bpf-loader-program = { path = "../programs/bpf_loader", version = "1.4.0" }
|
||||
solana-clap-utils = { path = "../clap-utils", version = "1.4.0" }
|
||||
solana-client = { path = "../client", version = "1.4.0" }
|
||||
solana-faucet = { path = "../faucet", version = "1.4.0" }
|
||||
solana-genesis-programs = { path = "../genesis-programs", version = "1.4.0" }
|
||||
solana-ledger = { path = "../ledger", version = "1.4.0" }
|
||||
solana-logger = { path = "../logger", version = "1.4.0" }
|
||||
solana-merkle-tree = { path = "../merkle-tree", version = "1.4.0" }
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
use solana_runtime::{
|
||||
bank::{Builtin, Builtins, Entrypoint},
|
||||
feature_set,
|
||||
};
|
||||
use solana_sdk::{genesis_config::ClusterType, pubkey::Pubkey};
|
||||
|
||||
/// Builtin programs that are always available
|
||||
fn genesis_builtins(cluster_type: ClusterType) -> Vec<Builtin> {
|
||||
let builtins = if cluster_type != ClusterType::MainnetBeta {
|
||||
vec![
|
||||
solana_bpf_loader_deprecated_program!(),
|
||||
solana_bpf_loader_program!(),
|
||||
]
|
||||
} else {
|
||||
// Remove this `else` block and the `cluster_type` argument to this function once
|
||||
// `feature_set::bpf_loader2_program::id()` is active on Mainnet Beta
|
||||
vec![solana_bpf_loader_deprecated_program!()]
|
||||
};
|
||||
|
||||
builtins
|
||||
.into_iter()
|
||||
.map(|b| Builtin::new(&b.0, b.1, Entrypoint::Loader(b.2)))
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Builtin programs activated dynamically by feature
|
||||
fn feature_builtins() -> Vec<(Builtin, Pubkey)> {
|
||||
let builtins = vec![(
|
||||
solana_bpf_loader_program!(),
|
||||
feature_set::bpf_loader2_program::id(),
|
||||
)];
|
||||
|
||||
builtins
|
||||
.into_iter()
|
||||
.map(|(b, p)| (Builtin::new(&b.0, b.1, Entrypoint::Loader(b.2)), p))
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub(crate) fn get(cluster_type: ClusterType) -> Builtins {
|
||||
Builtins {
|
||||
genesis_builtins: genesis_builtins(cluster_type),
|
||||
feature_builtins: feature_builtins(),
|
||||
}
|
||||
}
|
|
@ -6,11 +6,15 @@
|
|||
//! command-line tools to spin up validators and a Rust library
|
||||
//!
|
||||
|
||||
#[macro_use]
|
||||
extern crate solana_bpf_loader_program;
|
||||
|
||||
pub mod accounts_background_service;
|
||||
pub mod accounts_hash_verifier;
|
||||
pub mod banking_stage;
|
||||
pub mod bigtable_upload_service;
|
||||
pub mod broadcast_stage;
|
||||
mod builtins;
|
||||
pub mod cache_block_time_service;
|
||||
pub mod cluster_info_vote_listener;
|
||||
pub mod commitment_service;
|
||||
|
|
|
@ -69,6 +69,7 @@ impl TestValidator {
|
|||
&Pubkey::new_rand(),
|
||||
42,
|
||||
bootstrap_validator_lamports,
|
||||
solana_sdk::genesis_config::ClusterType::Development,
|
||||
);
|
||||
genesis_config.rent.lamports_per_byte_year = 1;
|
||||
genesis_config.rent.exemption_threshold = 1.0;
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
use crate::{
|
||||
broadcast_stage::BroadcastStageType,
|
||||
builtins,
|
||||
cache_block_time_service::{CacheBlockTimeSender, CacheBlockTimeService},
|
||||
cluster_info::{ClusterInfo, Node},
|
||||
cluster_info_vote_listener::VoteTracker,
|
||||
|
@ -791,6 +792,7 @@ fn new_banks_from_ledger(
|
|||
new_hard_forks: config.new_hard_forks.clone(),
|
||||
frozen_accounts: config.frozen_accounts.clone(),
|
||||
debug_keys: config.debug_keys.clone(),
|
||||
additional_builtins: Some(builtins::get(genesis_config.cluster_type)),
|
||||
..blockstore_processor::ProcessOptions::default()
|
||||
};
|
||||
|
||||
|
|
|
@ -89,6 +89,7 @@ mod tests {
|
|||
vec![accounts_dir.path().to_path_buf()],
|
||||
&[],
|
||||
None,
|
||||
None,
|
||||
);
|
||||
bank0.freeze();
|
||||
let mut bank_forks = BankForks::new(bank0);
|
||||
|
@ -143,6 +144,7 @@ mod tests {
|
|||
CompressionType::Bzip2,
|
||||
old_genesis_config,
|
||||
None,
|
||||
None,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
|
|
|
@ -1,25 +0,0 @@
|
|||
[package]
|
||||
name = "solana-genesis-programs"
|
||||
version = "1.4.0"
|
||||
description = "Solana genesis programs"
|
||||
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
||||
repository = "https://github.com/solana-labs/solana"
|
||||
license = "Apache-2.0"
|
||||
homepage = "https://solana.com/"
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
log = { version = "0.4.8" }
|
||||
solana-bpf-loader-program = { path = "../programs/bpf_loader", version = "1.4.0" }
|
||||
solana-budget-program = { path = "../programs/budget", version = "1.4.0" }
|
||||
solana-exchange-program = { path = "../programs/exchange", version = "1.4.0" }
|
||||
solana-runtime = { path = "../runtime", version = "1.4.0" }
|
||||
solana-sdk = { path = "../sdk", version = "1.4.0" }
|
||||
solana-vest-program = { path = "../programs/vest", version = "1.4.0" }
|
||||
|
||||
[lib]
|
||||
crate-type = ["lib"]
|
||||
name = "solana_genesis_programs"
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
targets = ["x86_64-unknown-linux-gnu"]
|
|
@ -1,190 +0,0 @@
|
|||
#[macro_use]
|
||||
extern crate solana_bpf_loader_program;
|
||||
#[macro_use]
|
||||
extern crate solana_budget_program;
|
||||
#[macro_use]
|
||||
extern crate solana_exchange_program;
|
||||
#[macro_use]
|
||||
extern crate solana_vest_program;
|
||||
|
||||
use solana_runtime::bank::{Bank, EnteredEpochCallback};
|
||||
use solana_sdk::{
|
||||
clock::{Epoch, GENESIS_EPOCH},
|
||||
entrypoint_native::{ErasedProcessInstructionWithContext, ProcessInstructionWithContext},
|
||||
genesis_config::ClusterType,
|
||||
pubkey::Pubkey,
|
||||
};
|
||||
|
||||
enum Program {
|
||||
Native((String, Pubkey)),
|
||||
BuiltinLoader((String, Pubkey, ProcessInstructionWithContext)),
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for Program {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
#[derive(Debug)]
|
||||
enum Program {
|
||||
Native((String, Pubkey)),
|
||||
BuiltinLoader((String, Pubkey, String)),
|
||||
}
|
||||
let program = match self {
|
||||
crate::Program::Native((string, pubkey)) => Program::Native((string.clone(), *pubkey)),
|
||||
crate::Program::BuiltinLoader((string, pubkey, instruction)) => {
|
||||
let erased: ErasedProcessInstructionWithContext = *instruction;
|
||||
Program::BuiltinLoader((string.clone(), *pubkey, format!("{:p}", erased)))
|
||||
}
|
||||
};
|
||||
write!(f, "{:?}", program)
|
||||
}
|
||||
}
|
||||
|
||||
// given cluster type, return the entire set of enabled programs
|
||||
fn get_programs(cluster_type: ClusterType) -> Vec<(Program, Epoch)> {
|
||||
match cluster_type {
|
||||
ClusterType::Development => vec![
|
||||
// Programs used for testing
|
||||
Program::BuiltinLoader(solana_bpf_loader_program!()),
|
||||
Program::BuiltinLoader(solana_bpf_loader_deprecated_program!()),
|
||||
Program::Native(solana_vest_program!()),
|
||||
Program::Native(solana_budget_program!()),
|
||||
Program::Native(solana_exchange_program!()),
|
||||
]
|
||||
.into_iter()
|
||||
.map(|program| (program, GENESIS_EPOCH))
|
||||
.collect::<Vec<_>>(),
|
||||
|
||||
ClusterType::Devnet => vec![
|
||||
(
|
||||
Program::BuiltinLoader(solana_bpf_loader_deprecated_program!()),
|
||||
GENESIS_EPOCH,
|
||||
),
|
||||
(Program::Native(solana_vest_program!()), GENESIS_EPOCH),
|
||||
(Program::Native(solana_budget_program!()), GENESIS_EPOCH),
|
||||
(Program::Native(solana_exchange_program!()), GENESIS_EPOCH),
|
||||
(Program::BuiltinLoader(solana_bpf_loader_program!()), 400),
|
||||
],
|
||||
|
||||
ClusterType::Testnet => vec![
|
||||
(
|
||||
Program::BuiltinLoader(solana_bpf_loader_deprecated_program!()),
|
||||
GENESIS_EPOCH,
|
||||
),
|
||||
(Program::BuiltinLoader(solana_bpf_loader_program!()), 89),
|
||||
],
|
||||
|
||||
ClusterType::MainnetBeta => vec![
|
||||
(
|
||||
Program::BuiltinLoader(solana_bpf_loader_deprecated_program!()),
|
||||
34,
|
||||
),
|
||||
(
|
||||
Program::BuiltinLoader(solana_bpf_loader_program!()),
|
||||
// The epoch of std::u64::MAX is a placeholder and is expected
|
||||
// to be reduced in a future cluster update.
|
||||
Epoch::MAX,
|
||||
),
|
||||
],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_native_programs_for_genesis(cluster_type: ClusterType) -> Vec<(String, Pubkey)> {
|
||||
let mut native_programs = vec![];
|
||||
for (program, start_epoch) in get_programs(cluster_type) {
|
||||
if let Program::Native((string, key)) = program {
|
||||
if start_epoch == GENESIS_EPOCH {
|
||||
native_programs.push((string, key));
|
||||
}
|
||||
}
|
||||
}
|
||||
native_programs
|
||||
}
|
||||
|
||||
pub fn get_entered_epoch_callback(cluster_type: ClusterType) -> EnteredEpochCallback {
|
||||
Box::new(move |bank: &mut Bank, initial: bool| {
|
||||
// Be careful to add arbitrary logic here; this should be idempotent and can be called
|
||||
// at arbitrary point in an epoch not only epoch boundaries.
|
||||
// This is because this closure need to be executed immediately after snapshot restoration,
|
||||
// in addition to usual epoch boundaries
|
||||
// In other words, this callback initializes some skip(serde) fields, regardless
|
||||
// frozen or not
|
||||
|
||||
for (program, start_epoch) in get_programs(cluster_type) {
|
||||
let should_populate =
|
||||
initial && bank.epoch() >= start_epoch || !initial && bank.epoch() == start_epoch;
|
||||
if should_populate {
|
||||
match program {
|
||||
Program::Native((name, program_id)) => {
|
||||
bank.add_native_program(&name, &program_id);
|
||||
}
|
||||
Program::BuiltinLoader((
|
||||
name,
|
||||
program_id,
|
||||
process_instruction_with_context,
|
||||
)) => {
|
||||
bank.add_builtin_loader(
|
||||
&name,
|
||||
program_id,
|
||||
process_instruction_with_context,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use std::collections::HashSet;
|
||||
|
||||
fn do_test_uniqueness(programs: Vec<(Program, Epoch)>) {
|
||||
let mut unique_ids = HashSet::new();
|
||||
let mut unique_names = HashSet::new();
|
||||
let mut prev_start_epoch = GENESIS_EPOCH;
|
||||
for (program, next_start_epoch) in programs {
|
||||
assert!(next_start_epoch >= prev_start_epoch);
|
||||
match program {
|
||||
Program::Native((name, id)) => {
|
||||
assert!(unique_ids.insert(id));
|
||||
assert!(unique_names.insert(name));
|
||||
}
|
||||
Program::BuiltinLoader((name, id, _)) => {
|
||||
assert!(unique_ids.insert(id));
|
||||
assert!(unique_names.insert(name));
|
||||
}
|
||||
}
|
||||
prev_start_epoch = next_start_epoch;
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_uniqueness() {
|
||||
do_test_uniqueness(get_programs(ClusterType::Development));
|
||||
do_test_uniqueness(get_programs(ClusterType::Devnet));
|
||||
do_test_uniqueness(get_programs(ClusterType::Testnet));
|
||||
do_test_uniqueness(get_programs(ClusterType::MainnetBeta));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_development_programs() {
|
||||
assert_eq!(get_programs(ClusterType::Development).len(), 5);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_native_development_programs() {
|
||||
assert_eq!(
|
||||
get_native_programs_for_genesis(ClusterType::Development).len(),
|
||||
3
|
||||
);
|
||||
}
|
||||
#[test]
|
||||
fn test_softlaunch_programs() {
|
||||
assert!(!get_programs(ClusterType::MainnetBeta).is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_debug() {
|
||||
assert!(!format!("{:?}", get_programs(ClusterType::Development)).is_empty());
|
||||
}
|
||||
}
|
|
@ -15,15 +15,17 @@ chrono = "0.4"
|
|||
serde = "1.0.112"
|
||||
serde_json = "1.0.56"
|
||||
serde_yaml = "0.8.13"
|
||||
solana-budget-program = { path = "../programs/budget", version = "1.4.0" }
|
||||
solana-clap-utils = { path = "../clap-utils", version = "1.4.0" }
|
||||
solana-genesis-programs = { path = "../genesis-programs", version = "1.4.0" }
|
||||
solana-exchange-program = { path = "../programs/exchange", version = "1.4.0" }
|
||||
solana-ledger = { path = "../ledger", version = "1.4.0" }
|
||||
solana-logger = { path = "../logger", version = "1.4.0" }
|
||||
solana-runtime = { path = "../runtime", version = "1.4.0" }
|
||||
solana-sdk = { path = "../sdk", version = "1.4.0" }
|
||||
solana-stake-program = { path = "../programs/stake", version = "1.4.0" }
|
||||
solana-vote-program = { path = "../programs/vote", version = "1.4.0" }
|
||||
solana-version = { path = "../version", version = "1.4.0" }
|
||||
solana-vest-program = { path = "../programs/vest", version = "1.4.0" }
|
||||
solana-vote-program = { path = "../programs/vote", version = "1.4.0" }
|
||||
tempfile = "3.1.0"
|
||||
|
||||
[[bin]]
|
||||
|
|
|
@ -1,5 +1,12 @@
|
|||
//! A command-line executable for generating the chain's genesis config.
|
||||
|
||||
#[macro_use]
|
||||
extern crate solana_budget_program;
|
||||
#[macro_use]
|
||||
extern crate solana_exchange_program;
|
||||
#[macro_use]
|
||||
extern crate solana_vest_program;
|
||||
|
||||
use clap::{crate_description, crate_name, value_t, value_t_or_exit, App, Arg, ArgMatches};
|
||||
use solana_clap_utils::{
|
||||
input_parsers::{cluster_type_of, pubkey_of, pubkeys_of, unix_timestamp_from_rfc3339_datetime},
|
||||
|
@ -462,8 +469,15 @@ fn main() -> Result<(), Box<dyn error::Error>> {
|
|||
matches.is_present("enable_warmup_epochs"),
|
||||
);
|
||||
|
||||
let native_instruction_processors =
|
||||
solana_genesis_programs::get_native_programs_for_genesis(cluster_type);
|
||||
let native_instruction_processors = if cluster_type == ClusterType::Development {
|
||||
vec![
|
||||
solana_vest_program!(),
|
||||
solana_budget_program!(),
|
||||
solana_exchange_program!(),
|
||||
]
|
||||
} else {
|
||||
vec![]
|
||||
};
|
||||
|
||||
let mut genesis_config = GenesisConfig {
|
||||
native_instruction_processors,
|
||||
|
@ -526,7 +540,9 @@ fn main() -> Result<(), Box<dyn error::Error>> {
|
|||
}
|
||||
|
||||
solana_stake_program::add_genesis_accounts(&mut genesis_config);
|
||||
solana_runtime::genesis_utils::add_feature_accounts(&mut genesis_config);
|
||||
if genesis_config.cluster_type == ClusterType::Development {
|
||||
solana_runtime::genesis_utils::activate_all_features(&mut genesis_config);
|
||||
}
|
||||
|
||||
if let Some(files) = matches.values_of("primordial_accounts_file") {
|
||||
for file in files {
|
||||
|
|
|
@ -32,7 +32,6 @@ serde = "1.0.112"
|
|||
serde_bytes = "0.11.4"
|
||||
sha2 = "0.8.2"
|
||||
solana-transaction-status = { path = "../transaction-status", version = "1.4.0" }
|
||||
solana-genesis-programs = { path = "../genesis-programs", version = "1.4.0" }
|
||||
solana-logger = { path = "../logger", version = "1.4.0" }
|
||||
solana-measure = { path = "../measure", version = "1.4.0" }
|
||||
solana-merkle-tree = { path = "../merkle-tree", version = "1.4.0" }
|
||||
|
|
|
@ -13,7 +13,7 @@ use solana_runtime::{
|
|||
snapshot_utils,
|
||||
};
|
||||
use solana_sdk::{clock::Slot, genesis_config::GenesisConfig, hash::Hash};
|
||||
use std::{fs, path::PathBuf, process, result, sync::Arc};
|
||||
use std::{fs, path::PathBuf, process, result};
|
||||
|
||||
pub type LoadResult = result::Result<
|
||||
(BankForks, LeaderScheduleCache, Option<(Slot, Hash)>),
|
||||
|
@ -66,6 +66,7 @@ pub fn load(
|
|||
compression,
|
||||
genesis_config,
|
||||
process_options.debug_keys.clone(),
|
||||
process_options.additional_builtins.as_ref(),
|
||||
)
|
||||
.expect("Load from snapshot failed");
|
||||
|
||||
|
@ -84,9 +85,8 @@ pub fn load(
|
|||
|
||||
return to_loadresult(
|
||||
blockstore_processor::process_blockstore_from_root(
|
||||
genesis_config,
|
||||
blockstore,
|
||||
Arc::new(deserialized_bank),
|
||||
deserialized_bank,
|
||||
&process_options,
|
||||
&VerifyRecyclers::default(),
|
||||
transaction_status_sender,
|
||||
|
|
|
@ -16,7 +16,7 @@ use solana_metrics::{datapoint_error, inc_new_counter_debug};
|
|||
use solana_rayon_threadlimit::get_thread_count;
|
||||
use solana_runtime::{
|
||||
bank::{
|
||||
Bank, InnerInstructionsList, TransactionBalancesSet, TransactionProcessResult,
|
||||
Bank, Builtins, InnerInstructionsList, TransactionBalancesSet, TransactionProcessResult,
|
||||
TransactionResults,
|
||||
},
|
||||
bank_forks::BankForks,
|
||||
|
@ -318,14 +318,7 @@ pub struct ProcessOptions {
|
|||
pub new_hard_forks: Option<Vec<Slot>>,
|
||||
pub frozen_accounts: Vec<Pubkey>,
|
||||
pub debug_keys: Option<Arc<HashSet<Pubkey>>>,
|
||||
}
|
||||
|
||||
fn initiate_callback(mut bank: &mut Arc<Bank>, genesis_config: &GenesisConfig) {
|
||||
Arc::get_mut(&mut bank)
|
||||
.unwrap()
|
||||
.initiate_entered_epoch_callback(solana_genesis_programs::get_entered_epoch_callback(
|
||||
genesis_config.cluster_type,
|
||||
));
|
||||
pub additional_builtins: Option<Builtins>,
|
||||
}
|
||||
|
||||
pub fn process_blockstore(
|
||||
|
@ -344,55 +337,43 @@ pub fn process_blockstore(
|
|||
}
|
||||
|
||||
// Setup bank for slot 0
|
||||
let mut bank0 = Arc::new(Bank::new_with_paths(
|
||||
let bank0 = Bank::new_with_paths(
|
||||
&genesis_config,
|
||||
account_paths,
|
||||
&opts.frozen_accounts,
|
||||
opts.debug_keys.clone(),
|
||||
));
|
||||
initiate_callback(&mut bank0, genesis_config);
|
||||
opts.additional_builtins.as_ref(),
|
||||
);
|
||||
let bank0 = Arc::new(bank0);
|
||||
info!("processing ledger for slot 0...");
|
||||
let recyclers = VerifyRecyclers::default();
|
||||
process_bank_0(&bank0, blockstore, &opts, &recyclers)?;
|
||||
do_process_blockstore_from_root(
|
||||
genesis_config,
|
||||
blockstore,
|
||||
bank0,
|
||||
&opts,
|
||||
&recyclers,
|
||||
None,
|
||||
false,
|
||||
)
|
||||
do_process_blockstore_from_root(blockstore, bank0, &opts, &recyclers, None)
|
||||
}
|
||||
|
||||
// Process blockstore from a known root bank
|
||||
pub fn process_blockstore_from_root(
|
||||
genesis_config: &GenesisConfig,
|
||||
pub(crate) fn process_blockstore_from_root(
|
||||
blockstore: &Blockstore,
|
||||
bank: Arc<Bank>,
|
||||
bank: Bank,
|
||||
opts: &ProcessOptions,
|
||||
recyclers: &VerifyRecyclers,
|
||||
transaction_status_sender: Option<TransactionStatusSender>,
|
||||
) -> BlockstoreProcessorResult {
|
||||
do_process_blockstore_from_root(
|
||||
genesis_config,
|
||||
blockstore,
|
||||
bank,
|
||||
Arc::new(bank),
|
||||
opts,
|
||||
recyclers,
|
||||
transaction_status_sender,
|
||||
true,
|
||||
)
|
||||
}
|
||||
|
||||
fn do_process_blockstore_from_root(
|
||||
genesis_config: &GenesisConfig,
|
||||
blockstore: &Blockstore,
|
||||
mut bank: Arc<Bank>,
|
||||
bank: Arc<Bank>,
|
||||
opts: &ProcessOptions,
|
||||
recyclers: &VerifyRecyclers,
|
||||
transaction_status_sender: Option<TransactionStatusSender>,
|
||||
enable_callback: bool,
|
||||
) -> BlockstoreProcessorResult {
|
||||
info!("processing ledger from slot {}...", bank.slot());
|
||||
let allocated = thread_mem_usage::Allocatedp::default();
|
||||
|
@ -404,10 +385,6 @@ fn do_process_blockstore_from_root(
|
|||
let now = Instant::now();
|
||||
let mut root = start_slot;
|
||||
|
||||
if enable_callback {
|
||||
initiate_callback(&mut bank, genesis_config);
|
||||
}
|
||||
|
||||
if let Some(ref new_hard_forks) = opts.new_hard_forks {
|
||||
let hard_forks = bank.hard_forks();
|
||||
|
||||
|
@ -2662,8 +2639,7 @@ pub mod tests {
|
|||
blockstore.set_roots(&[3, 5]).unwrap();
|
||||
|
||||
// Set up bank1
|
||||
let mut bank0 = Arc::new(Bank::new(&genesis_config));
|
||||
initiate_callback(&mut bank0, &genesis_config);
|
||||
let bank0 = Arc::new(Bank::new(&genesis_config));
|
||||
let opts = ProcessOptions {
|
||||
poh_verify: true,
|
||||
..ProcessOptions::default()
|
||||
|
@ -2684,16 +2660,8 @@ pub mod tests {
|
|||
bank1.squash();
|
||||
|
||||
// Test process_blockstore_from_root() from slot 1 onwards
|
||||
let (bank_forks, _leader_schedule) = do_process_blockstore_from_root(
|
||||
&genesis_config,
|
||||
&blockstore,
|
||||
bank1,
|
||||
&opts,
|
||||
&recyclers,
|
||||
None,
|
||||
false,
|
||||
)
|
||||
.unwrap();
|
||||
let (bank_forks, _leader_schedule) =
|
||||
do_process_blockstore_from_root(&blockstore, bank1, &opts, &recyclers, None).unwrap();
|
||||
|
||||
assert_eq!(frozen_bank_slots(&bank_forks), vec![5, 6]);
|
||||
assert_eq!(bank_forks.working_bank().slot(), 6);
|
||||
|
@ -2857,7 +2825,7 @@ pub mod tests {
|
|||
genesis_config: &GenesisConfig,
|
||||
account_paths: Vec<PathBuf>,
|
||||
) -> EpochSchedule {
|
||||
let bank = Bank::new_with_paths(&genesis_config, account_paths, &[], None);
|
||||
let bank = Bank::new_with_paths(&genesis_config, account_paths, &[], None, None);
|
||||
*bank.epoch_schedule()
|
||||
}
|
||||
|
||||
|
@ -3211,95 +3179,4 @@ pub mod tests {
|
|||
8
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_process_blockstore_feature_activations_since_genesis() {
|
||||
let GenesisConfigInfo { genesis_config, .. } = create_genesis_config(123);
|
||||
|
||||
let (ledger_path, _blockhash) = create_new_tmp_ledger!(&genesis_config);
|
||||
let blockstore = Blockstore::open(&ledger_path).unwrap();
|
||||
|
||||
let opts = ProcessOptions::default();
|
||||
let (bank_forks, _leader_schedule) =
|
||||
process_blockstore(&genesis_config, &blockstore, vec![], opts).unwrap();
|
||||
|
||||
assert_eq!(bank_forks.working_bank().slot(), 0);
|
||||
assert_eq!(
|
||||
bank_forks.working_bank().builtin_loader_ids(),
|
||||
vec![
|
||||
solana_sdk::bpf_loader::id(),
|
||||
solana_sdk::bpf_loader_deprecated::id()
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_process_blockstore_feature_activations_from_snapshot() {
|
||||
let GenesisConfigInfo { genesis_config, .. } = create_genesis_config(123);
|
||||
|
||||
let (ledger_path, _blockhash) = create_new_tmp_ledger!(&genesis_config);
|
||||
let blockstore = Blockstore::open(&ledger_path).unwrap();
|
||||
|
||||
// Set up bank1
|
||||
let mut bank0 = Arc::new(Bank::new(&genesis_config));
|
||||
initiate_callback(&mut bank0, &genesis_config);
|
||||
let recyclers = VerifyRecyclers::default();
|
||||
let opts = ProcessOptions::default();
|
||||
process_bank_0(&bank0, &blockstore, &opts, &recyclers).unwrap();
|
||||
let restored_slot = genesis_config.epoch_schedule.get_first_slot_in_epoch(1);
|
||||
let mut bank1 = Bank::new_from_parent(&bank0, &Pubkey::default(), restored_slot);
|
||||
bank1.squash();
|
||||
|
||||
// this is similar to snapshot deserialization
|
||||
bank1.reset_callback_and_message_processor();
|
||||
assert_eq!(bank1.builtin_loader_ids(), vec![]);
|
||||
|
||||
let bank1 = Arc::new(bank1);
|
||||
let (bank_forks, _leader_schedule) = process_blockstore_from_root(
|
||||
&genesis_config,
|
||||
&blockstore,
|
||||
bank1,
|
||||
&opts,
|
||||
&recyclers,
|
||||
None,
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(bank_forks.working_bank().slot(), restored_slot);
|
||||
assert_eq!(
|
||||
bank_forks.working_bank().builtin_loader_ids(),
|
||||
vec![
|
||||
solana_sdk::bpf_loader::id(),
|
||||
solana_sdk::bpf_loader_deprecated::id()
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_process_blockstore_feature_activations_into_epoch_with_activation() {
|
||||
let GenesisConfigInfo {
|
||||
mut genesis_config, ..
|
||||
} = create_genesis_config(123);
|
||||
|
||||
genesis_config.cluster_type = solana_sdk::genesis_config::ClusterType::MainnetBeta;
|
||||
let (ledger_path, _blockhash) = create_new_tmp_ledger!(&genesis_config);
|
||||
let blockstore = Blockstore::open(&ledger_path).unwrap();
|
||||
|
||||
let opts = ProcessOptions::default();
|
||||
let (bank_forks, _leader_schedule) =
|
||||
process_blockstore(&genesis_config, &blockstore, vec![], opts).unwrap();
|
||||
let bank0 = bank_forks.working_bank();
|
||||
assert_eq!(bank0.builtin_loader_ids(), vec![]);
|
||||
|
||||
let restored_slot = genesis_config.epoch_schedule.get_first_slot_in_epoch(34);
|
||||
let bank1 = Bank::new_from_parent(&bank0, &Pubkey::default(), restored_slot);
|
||||
|
||||
assert_eq!(bank0.slot(), 0);
|
||||
assert_eq!(bank0.builtin_loader_ids(), vec![]);
|
||||
|
||||
assert_eq!(bank1.slot(), restored_slot);
|
||||
assert_eq!(
|
||||
bank1.builtin_loader_ids(),
|
||||
vec![solana_sdk::bpf_loader_deprecated::id()]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,7 +19,6 @@ solana-client = { path = "../client", version = "1.4.0" }
|
|||
solana-download-utils = { path = "../download-utils", version = "1.4.0" }
|
||||
solana-faucet = { path = "../faucet", version = "1.4.0" }
|
||||
solana-exchange-program = { path = "../programs/exchange", version = "1.4.0" }
|
||||
solana-genesis-programs = { path = "../genesis-programs", version = "1.4.0" }
|
||||
solana-ledger = { path = "../ledger", version = "1.4.0" }
|
||||
solana-logger = { path = "../logger", version = "1.4.0" }
|
||||
solana-runtime = { path = "../runtime", version = "1.4.0" }
|
||||
|
|
|
@ -13,7 +13,8 @@ use solana_core::{
|
|||
};
|
||||
use solana_ledger::create_new_tmp_ledger;
|
||||
use solana_runtime::genesis_utils::{
|
||||
create_genesis_config_with_vote_accounts, GenesisConfigInfo, ValidatorVoteKeypairs,
|
||||
create_genesis_config_with_vote_accounts_and_cluster_type, GenesisConfigInfo,
|
||||
ValidatorVoteKeypairs,
|
||||
};
|
||||
use solana_sdk::{
|
||||
client::SyncClient,
|
||||
|
@ -155,10 +156,11 @@ impl LocalCluster {
|
|||
mut genesis_config,
|
||||
mint_keypair,
|
||||
..
|
||||
} = create_genesis_config_with_vote_accounts(
|
||||
} = create_genesis_config_with_vote_accounts_and_cluster_type(
|
||||
config.cluster_lamports,
|
||||
&keys_in_genesis,
|
||||
stakes_in_genesis,
|
||||
config.cluster_type,
|
||||
);
|
||||
genesis_config.ticks_per_slot = config.ticks_per_slot;
|
||||
genesis_config.epoch_schedule = EpochSchedule::custom(
|
||||
|
@ -166,19 +168,7 @@ impl LocalCluster {
|
|||
config.stakers_slot_offset,
|
||||
!config.skip_warmup_slots,
|
||||
);
|
||||
genesis_config.cluster_type = config.cluster_type;
|
||||
genesis_config.poh_config = config.poh_config.clone();
|
||||
|
||||
match genesis_config.cluster_type {
|
||||
ClusterType::MainnetBeta | ClusterType::Testnet => {
|
||||
genesis_config.native_instruction_processors =
|
||||
solana_genesis_programs::get_native_programs_for_genesis(
|
||||
genesis_config.cluster_type,
|
||||
)
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
|
||||
genesis_config
|
||||
.native_instruction_processors
|
||||
.extend_from_slice(&config.native_instruction_processors);
|
||||
|
|
|
@ -759,6 +759,7 @@ fn test_mainnet_beta_cluster_type() {
|
|||
&solana_sdk::system_program::id(),
|
||||
&solana_stake_program::id(),
|
||||
&solana_vote_program::id(),
|
||||
&solana_sdk::bpf_loader_deprecated::id(),
|
||||
]
|
||||
.iter()
|
||||
{
|
||||
|
@ -774,13 +775,7 @@ fn test_mainnet_beta_cluster_type() {
|
|||
}
|
||||
|
||||
// Programs that are not available at epoch 0
|
||||
for program_id in [
|
||||
&solana_sdk::bpf_loader::id(),
|
||||
&solana_sdk::bpf_loader_deprecated::id(),
|
||||
&solana_vest_program::id(),
|
||||
]
|
||||
.iter()
|
||||
{
|
||||
for program_id in [&solana_sdk::bpf_loader::id(), &solana_vest_program::id()].iter() {
|
||||
assert_eq!(
|
||||
(
|
||||
program_id,
|
||||
|
|
|
@ -36,7 +36,13 @@ fn bench_has_duplicates(bencher: &mut Bencher) {
|
|||
#[bench]
|
||||
fn test_accounts_create(bencher: &mut Bencher) {
|
||||
let (genesis_config, _) = create_genesis_config(10_000);
|
||||
let bank0 = Bank::new_with_paths(&genesis_config, vec![PathBuf::from("bench_a0")], &[], None);
|
||||
let bank0 = Bank::new_with_paths(
|
||||
&genesis_config,
|
||||
vec![PathBuf::from("bench_a0")],
|
||||
&[],
|
||||
None,
|
||||
None,
|
||||
);
|
||||
bencher.iter(|| {
|
||||
let mut pubkeys: Vec<Pubkey> = vec![];
|
||||
deposit_many(&bank0, &mut pubkeys, 1000);
|
||||
|
@ -51,6 +57,7 @@ fn test_accounts_squash(bencher: &mut Bencher) {
|
|||
vec![PathBuf::from("bench_a1")],
|
||||
&[],
|
||||
None,
|
||||
None,
|
||||
));
|
||||
let mut pubkeys: Vec<Pubkey> = vec![];
|
||||
deposit_many(&bank1, &mut pubkeys, 250_000);
|
||||
|
|
|
@ -10,7 +10,7 @@ use crate::{
|
|||
accounts_db::{ErrorCounters, SnapshotStorages},
|
||||
accounts_index::Ancestors,
|
||||
blockhash_queue::BlockhashQueue,
|
||||
builtins::*,
|
||||
builtins,
|
||||
epoch_stakes::{EpochStakes, NodeVoteAccounts},
|
||||
feature::Feature,
|
||||
feature_set::{self, FeatureSet},
|
||||
|
@ -137,6 +137,7 @@ pub enum Entrypoint {
|
|||
Program(ProcessInstruction),
|
||||
Loader(ProcessInstructionWithContext),
|
||||
}
|
||||
#[derive(Clone)]
|
||||
pub struct Builtin {
|
||||
pub name: String,
|
||||
pub id: Pubkey,
|
||||
|
@ -186,6 +187,15 @@ impl CowCachedExecutors {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Builtins {
|
||||
/// Builtin programs that are always available
|
||||
pub genesis_builtins: Vec<Builtin>,
|
||||
|
||||
/// Builtin programs activated dynamically by feature
|
||||
pub feature_builtins: Vec<(Builtin, Pubkey)>,
|
||||
}
|
||||
|
||||
const MAX_CACHED_EXECUTORS: usize = 100; // 10 MB assuming programs are around 100k
|
||||
|
||||
/// LFU Cache of executors
|
||||
|
@ -327,9 +337,6 @@ impl StatusCacheRc {
|
|||
}
|
||||
}
|
||||
|
||||
pub type EnteredEpochCallback = Box<dyn Fn(&mut Bank, bool) + Sync + Send>;
|
||||
type WrappedEnteredEpochCallback = Arc<RwLock<Option<EnteredEpochCallback>>>;
|
||||
|
||||
pub type TransactionProcessResult = (Result<()>, Option<HashAgeKind>);
|
||||
pub struct TransactionResults {
|
||||
pub fee_collection_results: Vec<Result<()>>,
|
||||
|
@ -559,9 +566,8 @@ pub struct Bank {
|
|||
/// The Message processor
|
||||
message_processor: MessageProcessor,
|
||||
|
||||
/// Callback to be notified when a bank enters a new Epoch
|
||||
/// (used to adjust cluster features over time)
|
||||
entered_epoch_callback: WrappedEnteredEpochCallback,
|
||||
/// Builtin programs activated dynamically by feature
|
||||
feature_builtins: Arc<Vec<(Builtin, Pubkey)>>,
|
||||
|
||||
/// Last time when the cluster info vote listener has synced with this bank
|
||||
pub last_vote_sync: AtomicU64,
|
||||
|
@ -594,7 +600,7 @@ impl Default for BlockhashQueue {
|
|||
|
||||
impl Bank {
|
||||
pub fn new(genesis_config: &GenesisConfig) -> Self {
|
||||
Self::new_with_paths(&genesis_config, Vec::new(), &[], None)
|
||||
Self::new_with_paths(&genesis_config, Vec::new(), &[], None, None)
|
||||
}
|
||||
|
||||
pub fn new_with_paths(
|
||||
|
@ -602,6 +608,7 @@ impl Bank {
|
|||
paths: Vec<PathBuf>,
|
||||
frozen_account_pubkeys: &[Pubkey],
|
||||
debug_keys: Option<Arc<HashSet<Pubkey>>>,
|
||||
additional_builtins: Option<&Builtins>,
|
||||
) -> Self {
|
||||
let mut bank = Self::default();
|
||||
bank.transaction_debug_keys = debug_keys;
|
||||
|
@ -610,7 +617,7 @@ impl Bank {
|
|||
|
||||
bank.rc.accounts = Arc::new(Accounts::new(paths, &genesis_config.cluster_type));
|
||||
bank.process_genesis_config(genesis_config);
|
||||
bank.finish_init(genesis_config);
|
||||
bank.finish_init(genesis_config, additional_builtins);
|
||||
|
||||
// Freeze accounts after process_genesis_config creates the initial append vecs
|
||||
Arc::get_mut(&mut Arc::get_mut(&mut bank.rc.accounts).unwrap().accounts_db)
|
||||
|
@ -697,7 +704,7 @@ impl Bank {
|
|||
tick_height: AtomicU64::new(parent.tick_height.load(Relaxed)),
|
||||
signature_count: AtomicU64::new(0),
|
||||
message_processor: parent.message_processor.clone(),
|
||||
entered_epoch_callback: parent.entered_epoch_callback.clone(),
|
||||
feature_builtins: parent.feature_builtins.clone(),
|
||||
hard_forks: parent.hard_forks.clone(),
|
||||
last_vote_sync: AtomicU64::new(parent.last_vote_sync.load(Relaxed)),
|
||||
rewards: None,
|
||||
|
@ -725,7 +732,7 @@ impl Bank {
|
|||
|
||||
// Following code may touch AccountsDB, requiring proper ancestors
|
||||
if parent.epoch() < new.epoch() {
|
||||
new.apply_feature_activations(false, false);
|
||||
new.apply_feature_activations(false);
|
||||
}
|
||||
|
||||
new.update_slot_hashes();
|
||||
|
@ -747,7 +754,7 @@ impl Bank {
|
|||
/// * Freezes the new bank, assuming that the user will `Bank::new_from_parent` from this bank
|
||||
pub fn warp_from_parent(parent: &Arc<Bank>, collector_id: &Pubkey, slot: Slot) -> Self {
|
||||
let mut new = Bank::new_from_parent(parent, collector_id, slot);
|
||||
new.apply_feature_activations(true, true);
|
||||
new.apply_feature_activations(true);
|
||||
new.update_epoch_stakes(new.epoch_schedule().get_epoch(slot));
|
||||
new.tick_height.store(new.max_tick_height(), Relaxed);
|
||||
new.freeze();
|
||||
|
@ -761,6 +768,7 @@ impl Bank {
|
|||
genesis_config: &GenesisConfig,
|
||||
fields: BankFieldsToDeserialize,
|
||||
debug_keys: Option<Arc<HashSet<Pubkey>>>,
|
||||
additional_builtins: Option<&Builtins>,
|
||||
) -> Self {
|
||||
fn new<T: Default>() -> T {
|
||||
T::default()
|
||||
|
@ -803,7 +811,7 @@ impl Bank {
|
|||
epoch_stakes: fields.epoch_stakes,
|
||||
is_delta: AtomicBool::new(fields.is_delta),
|
||||
message_processor: new(),
|
||||
entered_epoch_callback: new(),
|
||||
feature_builtins: new(),
|
||||
last_vote_sync: new(),
|
||||
rewards: new(),
|
||||
skip_drop: new(),
|
||||
|
@ -816,7 +824,7 @@ impl Bank {
|
|||
transaction_debug_keys: debug_keys,
|
||||
feature_set: new(),
|
||||
};
|
||||
bank.finish_init(genesis_config);
|
||||
bank.finish_init(genesis_config, additional_builtins);
|
||||
|
||||
// Sanity assertions between bank snapshot and genesis config
|
||||
// Consider removing from serializable bank state
|
||||
|
@ -2967,19 +2975,29 @@ impl Bank {
|
|||
self.rc.accounts.clone()
|
||||
}
|
||||
|
||||
pub fn set_bank_rc(&mut self, bank_rc: BankRc, status_cache_rc: StatusCacheRc) {
|
||||
self.rc = bank_rc;
|
||||
self.src = status_cache_rc;
|
||||
}
|
||||
|
||||
pub fn finish_init(&mut self, genesis_config: &GenesisConfig) {
|
||||
fn finish_init(
|
||||
&mut self,
|
||||
genesis_config: &GenesisConfig,
|
||||
additional_builtins: Option<&Builtins>,
|
||||
) {
|
||||
self.rewards_pool_pubkeys =
|
||||
Arc::new(genesis_config.rewards_pools.keys().cloned().collect());
|
||||
self.apply_feature_activations(true, false);
|
||||
}
|
||||
|
||||
pub fn set_parent(&mut self, parent: &Arc<Bank>) {
|
||||
self.rc.parent = RwLock::new(Some(parent.clone()));
|
||||
let mut builtins = builtins::get();
|
||||
if let Some(additional_builtins) = additional_builtins {
|
||||
builtins
|
||||
.genesis_builtins
|
||||
.extend_from_slice(&additional_builtins.genesis_builtins);
|
||||
builtins
|
||||
.feature_builtins
|
||||
.extend_from_slice(&additional_builtins.feature_builtins);
|
||||
}
|
||||
for builtin in builtins.genesis_builtins {
|
||||
self.add_builtin(&builtin.name, builtin.id, builtin.entrypoint);
|
||||
}
|
||||
self.feature_builtins = Arc::new(builtins.feature_builtins);
|
||||
|
||||
self.apply_feature_activations(true);
|
||||
}
|
||||
|
||||
pub fn set_inflation(&self, inflation: Inflation) {
|
||||
|
@ -2990,19 +3008,6 @@ impl Bank {
|
|||
self.hard_forks.clone()
|
||||
}
|
||||
|
||||
pub fn initiate_entered_epoch_callback(
|
||||
&mut self,
|
||||
entered_epoch_callback: EnteredEpochCallback,
|
||||
) {
|
||||
{
|
||||
let mut callback_w = self.entered_epoch_callback.write().unwrap();
|
||||
assert!(callback_w.is_none(), "Already callback has been initiated");
|
||||
*callback_w = Some(entered_epoch_callback);
|
||||
}
|
||||
// immediately fire the callback as initial invocation
|
||||
self.reinvoke_entered_epoch_callback(true);
|
||||
}
|
||||
|
||||
pub fn get_account(&self, pubkey: &Pubkey) -> Option<Account> {
|
||||
self.get_account_modified_slot(pubkey)
|
||||
.map(|(acc, _slot)| acc)
|
||||
|
@ -3558,7 +3563,7 @@ impl Bank {
|
|||
|
||||
// This is called from snapshot restore AND for each epoch boundary
|
||||
// The entire code path herein must be idempotent
|
||||
fn apply_feature_activations(&mut self, init_finish_or_warp: bool, initiate_callback: bool) {
|
||||
fn apply_feature_activations(&mut self, init_finish_or_warp: bool) {
|
||||
let new_feature_activations = self.compute_active_feature_set(!init_finish_or_warp);
|
||||
|
||||
if new_feature_activations.contains(&feature_set::pico_inflation::id()) {
|
||||
|
@ -3571,8 +3576,7 @@ impl Bank {
|
|||
self.apply_spl_token_v2_multisig_fix();
|
||||
}
|
||||
|
||||
self.ensure_builtins(init_finish_or_warp, &new_feature_activations);
|
||||
self.reinvoke_entered_epoch_callback(initiate_callback);
|
||||
self.ensure_feature_builtins(init_finish_or_warp, &new_feature_activations);
|
||||
self.recheck_cross_program_support();
|
||||
self.recheck_compute_budget();
|
||||
self.reconfigure_token2_native_mint();
|
||||
|
@ -3623,32 +3627,21 @@ impl Bank {
|
|||
newly_activated
|
||||
}
|
||||
|
||||
fn ensure_builtins(&mut self, init_or_warp: bool, new_feature_activations: &HashSet<Pubkey>) {
|
||||
for (program, start_epoch) in get_cluster_builtins(self.cluster_type()) {
|
||||
let should_populate = init_or_warp && self.epoch() >= start_epoch
|
||||
|| !init_or_warp && self.epoch() == start_epoch;
|
||||
if should_populate {
|
||||
self.add_builtin(&program.name, program.id, program.entrypoint);
|
||||
}
|
||||
}
|
||||
|
||||
for (program, feature) in get_feature_builtins() {
|
||||
fn ensure_feature_builtins(
|
||||
&mut self,
|
||||
init_or_warp: bool,
|
||||
new_feature_activations: &HashSet<Pubkey>,
|
||||
) {
|
||||
let feature_builtins = self.feature_builtins.clone();
|
||||
for (builtin, feature) in feature_builtins.iter() {
|
||||
let should_populate = init_or_warp && self.feature_set.is_active(&feature)
|
||||
|| !init_or_warp && new_feature_activations.contains(&feature);
|
||||
if should_populate {
|
||||
self.add_builtin(&program.name, program.id, program.entrypoint);
|
||||
self.add_builtin(&builtin.name, builtin.id, builtin.entrypoint);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn reinvoke_entered_epoch_callback(&mut self, initiate: bool) {
|
||||
if let Some(entered_epoch_callback) =
|
||||
self.entered_epoch_callback.clone().read().unwrap().as_ref()
|
||||
{
|
||||
entered_epoch_callback(self, initiate)
|
||||
}
|
||||
}
|
||||
|
||||
fn recheck_cross_program_support(&mut self) {
|
||||
if ClusterType::MainnetBeta == self.cluster_type() {
|
||||
self.set_cross_program_support(self.epoch() >= 63);
|
||||
|
@ -3769,22 +3762,6 @@ impl Bank {
|
|||
.is_active(&feature_set::consistent_recent_blockhashes_sysvar::id()),
|
||||
}
|
||||
}
|
||||
|
||||
// only used for testing
|
||||
pub fn builtin_loader_ids(&self) -> Vec<Pubkey> {
|
||||
self.message_processor.builtin_loader_ids()
|
||||
}
|
||||
|
||||
// only used for testing
|
||||
pub fn builtin_program_ids(&self) -> Vec<Pubkey> {
|
||||
self.message_processor.builtin_program_ids()
|
||||
}
|
||||
|
||||
// only used for testing
|
||||
pub fn reset_callback_and_message_processor(&mut self) {
|
||||
self.entered_epoch_callback = WrappedEnteredEpochCallback::default();
|
||||
self.message_processor = MessageProcessor::default();
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Bank {
|
||||
|
@ -3845,7 +3822,7 @@ mod tests {
|
|||
vote_instruction,
|
||||
vote_state::{self, Vote, VoteInit, VoteState, MAX_LOCKOUT_HISTORY},
|
||||
};
|
||||
use std::{result, sync::atomic::Ordering::SeqCst, time::Duration};
|
||||
use std::{result, time::Duration};
|
||||
|
||||
#[test]
|
||||
fn test_hash_age_kind_is_durable_nonce() {
|
||||
|
@ -6943,45 +6920,6 @@ mod tests {
|
|||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_bank_entered_epoch_callback() {
|
||||
let (genesis_config, _) = create_genesis_config(500);
|
||||
let mut bank0 = Arc::new(Bank::new(&genesis_config));
|
||||
let callback_count = Arc::new(AtomicU64::new(0));
|
||||
|
||||
Arc::get_mut(&mut bank0)
|
||||
.unwrap()
|
||||
.initiate_entered_epoch_callback({
|
||||
let callback_count = callback_count.clone();
|
||||
Box::new(move |_, _| {
|
||||
callback_count.fetch_add(1, SeqCst);
|
||||
})
|
||||
});
|
||||
|
||||
// set_entered_eepoc_callbak fires the initial call
|
||||
assert_eq!(callback_count.load(SeqCst), 1);
|
||||
|
||||
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(SeqCst), 1);
|
||||
|
||||
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(SeqCst), 2);
|
||||
|
||||
callback_count.store(0, 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(SeqCst), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_is_delta_true() {
|
||||
let (genesis_config, mint_keypair) = create_genesis_config(500);
|
||||
|
@ -8406,7 +8344,6 @@ mod tests {
|
|||
})
|
||||
.collect()
|
||||
} else {
|
||||
use std::collections::HashSet;
|
||||
let mut inserted = HashSet::new();
|
||||
(0..num_keys)
|
||||
.map(|_| {
|
||||
|
@ -8740,7 +8677,7 @@ mod tests {
|
|||
bank.message_processor.set_cross_program_support(false);
|
||||
|
||||
// simulate bank is just after deserialized from snapshot
|
||||
bank.finish_init(&genesis_config);
|
||||
bank.finish_init(&genesis_config, None);
|
||||
|
||||
assert_eq!(bank.message_processor.get_cross_program_support(), true);
|
||||
}
|
||||
|
|
|
@ -1,76 +1,37 @@
|
|||
use crate::{
|
||||
bank::{Builtin, Entrypoint},
|
||||
bank::{Builtin, Builtins, Entrypoint},
|
||||
feature_set, system_instruction_processor,
|
||||
};
|
||||
use solana_sdk::{
|
||||
clock::{Epoch, GENESIS_EPOCH},
|
||||
genesis_config::ClusterType,
|
||||
pubkey::Pubkey,
|
||||
system_program,
|
||||
};
|
||||
use solana_sdk::{pubkey::Pubkey, system_program};
|
||||
|
||||
use log::*;
|
||||
|
||||
/// Builtin programs that should be active for the given cluster_type
|
||||
///
|
||||
/// Old style. Use `get_feature_builtins()` instead
|
||||
pub fn get_cluster_builtins(cluster_type: ClusterType) -> Vec<(Builtin, Epoch)> {
|
||||
trace!("get_cluster_builtins: {:?}", cluster_type);
|
||||
let mut builtins = vec![];
|
||||
|
||||
builtins.extend(
|
||||
vec![
|
||||
Builtin::new(
|
||||
"system_program",
|
||||
system_program::id(),
|
||||
Entrypoint::Program(system_instruction_processor::process_instruction),
|
||||
),
|
||||
Builtin::new(
|
||||
"config_program",
|
||||
solana_config_program::id(),
|
||||
Entrypoint::Program(solana_config_program::config_processor::process_instruction),
|
||||
),
|
||||
Builtin::new(
|
||||
"stake_program",
|
||||
solana_stake_program::id(),
|
||||
Entrypoint::Program(solana_stake_program::stake_instruction::process_instruction),
|
||||
),
|
||||
Builtin::new(
|
||||
"vote_program",
|
||||
solana_vote_program::id(),
|
||||
Entrypoint::Program(solana_vote_program::vote_instruction::process_instruction),
|
||||
),
|
||||
]
|
||||
.into_iter()
|
||||
.map(|program| (program, GENESIS_EPOCH))
|
||||
.collect::<Vec<_>>(),
|
||||
);
|
||||
|
||||
// repurpose Testnet for test_get_builtins because the Development is overloaded...
|
||||
#[cfg(test)]
|
||||
if cluster_type == ClusterType::Testnet {
|
||||
use solana_sdk::account::KeyedAccount;
|
||||
use solana_sdk::instruction::InstructionError;
|
||||
use std::str::FromStr;
|
||||
fn mock_ix_processor(
|
||||
_pubkey: &Pubkey,
|
||||
_ka: &[KeyedAccount],
|
||||
_data: &[u8],
|
||||
) -> std::result::Result<(), InstructionError> {
|
||||
Err(InstructionError::Custom(42))
|
||||
}
|
||||
let program_id = Pubkey::from_str("7saCc6X5a2syoYANA5oUUnPZLcLMfKoSjiDhFU5fbpoK").unwrap();
|
||||
builtins.push((
|
||||
Builtin::new("mock", program_id, Entrypoint::Program(mock_ix_processor)),
|
||||
2,
|
||||
));
|
||||
}
|
||||
|
||||
builtins
|
||||
/// Builtin programs that are always available
|
||||
fn genesis_builtins() -> Vec<Builtin> {
|
||||
vec![
|
||||
Builtin::new(
|
||||
"system_program",
|
||||
system_program::id(),
|
||||
Entrypoint::Program(system_instruction_processor::process_instruction),
|
||||
),
|
||||
Builtin::new(
|
||||
"vote_program",
|
||||
solana_vote_program::id(),
|
||||
Entrypoint::Program(solana_vote_program::vote_instruction::process_instruction),
|
||||
),
|
||||
Builtin::new(
|
||||
"stake_program",
|
||||
solana_stake_program::id(),
|
||||
Entrypoint::Program(solana_stake_program::stake_instruction::process_instruction),
|
||||
),
|
||||
Builtin::new(
|
||||
"config_program",
|
||||
solana_config_program::id(),
|
||||
Entrypoint::Program(solana_config_program::config_processor::process_instruction),
|
||||
),
|
||||
]
|
||||
}
|
||||
|
||||
/// Builtin programs that are activated dynamically by feature
|
||||
pub fn get_feature_builtins() -> Vec<(Builtin, Pubkey)> {
|
||||
/// Builtin programs activated dynamically by feature
|
||||
fn feature_builtins() -> Vec<(Builtin, Pubkey)> {
|
||||
vec![(
|
||||
Builtin::new(
|
||||
"secp256k1_program",
|
||||
|
@ -81,108 +42,9 @@ pub fn get_feature_builtins() -> Vec<(Builtin, Pubkey)> {
|
|||
)]
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::bank::Bank;
|
||||
use solana_sdk::genesis_config::create_genesis_config;
|
||||
use std::{collections::HashSet, str::FromStr, sync::Arc};
|
||||
|
||||
fn do_test_uniqueness(builtins: Vec<(Builtin, Epoch)>) {
|
||||
let mut unique_ids = HashSet::new();
|
||||
let mut unique_names = HashSet::new();
|
||||
let mut prev_start_epoch = 0;
|
||||
for (builtin, next_start_epoch) in builtins {
|
||||
assert!(next_start_epoch >= prev_start_epoch);
|
||||
assert!(unique_ids.insert(builtin.name));
|
||||
assert!(unique_names.insert(builtin.id));
|
||||
prev_start_epoch = next_start_epoch;
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_uniqueness() {
|
||||
do_test_uniqueness(get_cluster_builtins(ClusterType::Development));
|
||||
do_test_uniqueness(get_cluster_builtins(ClusterType::Devnet));
|
||||
do_test_uniqueness(get_cluster_builtins(ClusterType::Testnet));
|
||||
do_test_uniqueness(get_cluster_builtins(ClusterType::MainnetBeta));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_builtins() {
|
||||
let mock_program_id =
|
||||
Pubkey::from_str("7saCc6X5a2syoYANA5oUUnPZLcLMfKoSjiDhFU5fbpoK").unwrap();
|
||||
|
||||
let (mut genesis_config, _mint_keypair) = create_genesis_config(100_000);
|
||||
genesis_config.cluster_type = ClusterType::Testnet;
|
||||
let bank0 = Arc::new(Bank::new(&genesis_config));
|
||||
|
||||
let restored_slot1 = genesis_config.epoch_schedule.get_first_slot_in_epoch(2);
|
||||
let bank1 = Arc::new(Bank::new_from_parent(
|
||||
&bank0,
|
||||
&Pubkey::default(),
|
||||
restored_slot1,
|
||||
));
|
||||
|
||||
let restored_slot2 = genesis_config.epoch_schedule.get_first_slot_in_epoch(3);
|
||||
let bank2 = Arc::new(Bank::new_from_parent(
|
||||
&bank1,
|
||||
&Pubkey::default(),
|
||||
restored_slot2,
|
||||
));
|
||||
|
||||
let warped_slot = genesis_config.epoch_schedule.get_first_slot_in_epoch(999);
|
||||
let warped_bank = Arc::new(Bank::warp_from_parent(
|
||||
&bank0,
|
||||
&Pubkey::default(),
|
||||
warped_slot,
|
||||
));
|
||||
|
||||
assert_eq!(bank0.slot(), 0);
|
||||
assert_eq!(
|
||||
bank0.builtin_program_ids(),
|
||||
vec![
|
||||
system_program::id(),
|
||||
solana_config_program::id(),
|
||||
solana_stake_program::id(),
|
||||
solana_vote_program::id(),
|
||||
]
|
||||
);
|
||||
|
||||
assert_eq!(bank1.slot(), restored_slot1);
|
||||
assert_eq!(
|
||||
bank1.builtin_program_ids(),
|
||||
vec![
|
||||
system_program::id(),
|
||||
solana_config_program::id(),
|
||||
solana_stake_program::id(),
|
||||
solana_vote_program::id(),
|
||||
mock_program_id,
|
||||
]
|
||||
);
|
||||
|
||||
assert_eq!(bank2.slot(), restored_slot2);
|
||||
assert_eq!(
|
||||
bank2.builtin_program_ids(),
|
||||
vec![
|
||||
system_program::id(),
|
||||
solana_config_program::id(),
|
||||
solana_stake_program::id(),
|
||||
solana_vote_program::id(),
|
||||
mock_program_id,
|
||||
]
|
||||
);
|
||||
|
||||
assert_eq!(warped_bank.slot(), warped_slot);
|
||||
assert_eq!(
|
||||
warped_bank.builtin_program_ids(),
|
||||
vec![
|
||||
system_program::id(),
|
||||
solana_config_program::id(),
|
||||
solana_stake_program::id(),
|
||||
solana_vote_program::id(),
|
||||
mock_program_id,
|
||||
]
|
||||
);
|
||||
pub(crate) fn get() -> Builtins {
|
||||
Builtins {
|
||||
genesis_builtins: genesis_builtins(),
|
||||
feature_builtins: feature_builtins(),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,6 +25,10 @@ pub mod spl_token_v2_multisig_fix {
|
|||
solana_sdk::declare_id!("E5JiFDQCwyC6QfT9REFyMpfK2mHcmv1GUDySU1Ue7TYv");
|
||||
}
|
||||
|
||||
pub mod bpf_loader2_program {
|
||||
solana_sdk::declare_id!("DFBnrgThdzH4W6wZ12uGPoWcMnvfZj11EHnxHcVxLPhD");
|
||||
}
|
||||
|
||||
lazy_static! {
|
||||
/// Map of feature identifiers to user-visible description
|
||||
pub static ref FEATURE_NAMES: HashMap<Pubkey, &'static str> = [
|
||||
|
@ -33,6 +37,7 @@ lazy_static! {
|
|||
(consistent_recent_blockhashes_sysvar::id(), "consistent recentblockhashes sysvar"),
|
||||
(pico_inflation::id(), "pico-inflation"),
|
||||
(spl_token_v2_multisig_fix::id(), "spl-token multisig fix"),
|
||||
(bpf_loader2_program::id(), "bpf_loader2 program"),
|
||||
/*************** ADD NEW FEATURES HERE ***************/
|
||||
]
|
||||
.iter()
|
||||
|
|
|
@ -53,6 +53,20 @@ pub fn create_genesis_config_with_vote_accounts(
|
|||
mint_lamports: u64,
|
||||
voting_keypairs: &[impl Borrow<ValidatorVoteKeypairs>],
|
||||
stakes: Vec<u64>,
|
||||
) -> GenesisConfigInfo {
|
||||
create_genesis_config_with_vote_accounts_and_cluster_type(
|
||||
mint_lamports,
|
||||
voting_keypairs,
|
||||
stakes,
|
||||
ClusterType::Development,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn create_genesis_config_with_vote_accounts_and_cluster_type(
|
||||
mint_lamports: u64,
|
||||
voting_keypairs: &[impl Borrow<ValidatorVoteKeypairs>],
|
||||
stakes: Vec<u64>,
|
||||
cluster_type: ClusterType,
|
||||
) -> GenesisConfigInfo {
|
||||
assert!(!voting_keypairs.is_empty());
|
||||
assert_eq!(voting_keypairs.len(), stakes.len());
|
||||
|
@ -64,6 +78,7 @@ pub fn create_genesis_config_with_vote_accounts(
|
|||
&voting_keypairs[0].borrow().stake_keypair.pubkey(),
|
||||
stakes[0],
|
||||
BOOTSTRAP_VALIDATOR_LAMPORTS,
|
||||
cluster_type,
|
||||
);
|
||||
|
||||
for (validator_voting_keypairs, stake) in voting_keypairs[1..].iter().zip(&stakes[1..]) {
|
||||
|
@ -105,24 +120,23 @@ pub fn create_genesis_config_with_leader(
|
|||
&Pubkey::new_rand(),
|
||||
bootstrap_validator_stake_lamports,
|
||||
BOOTSTRAP_VALIDATOR_LAMPORTS,
|
||||
ClusterType::Development,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn add_feature_accounts(genesis_config: &mut GenesisConfig) {
|
||||
if genesis_config.cluster_type == ClusterType::Development {
|
||||
// Activate all features at genesis in development mode
|
||||
for feature_id in FeatureSet::default().inactive {
|
||||
let feature = Feature {
|
||||
activated_at: Some(0),
|
||||
};
|
||||
genesis_config.accounts.insert(
|
||||
feature_id,
|
||||
feature.create_account(std::cmp::max(
|
||||
genesis_config.rent.minimum_balance(Feature::size_of()),
|
||||
1,
|
||||
)),
|
||||
);
|
||||
}
|
||||
pub fn activate_all_features(genesis_config: &mut GenesisConfig) {
|
||||
// Activate all features at genesis in development mode
|
||||
for feature_id in FeatureSet::default().inactive {
|
||||
let feature = Feature {
|
||||
activated_at: Some(0),
|
||||
};
|
||||
genesis_config.accounts.insert(
|
||||
feature_id,
|
||||
feature.create_account(std::cmp::max(
|
||||
genesis_config.rent.minimum_balance(Feature::size_of()),
|
||||
1,
|
||||
)),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -133,6 +147,7 @@ pub fn create_genesis_config_with_leader_ex(
|
|||
bootstrap_validator_staking_pubkey: &Pubkey,
|
||||
bootstrap_validator_stake_lamports: u64,
|
||||
bootstrap_validator_lamports: u64,
|
||||
cluster_type: ClusterType,
|
||||
) -> GenesisConfigInfo {
|
||||
let mint_keypair = Keypair::new();
|
||||
let bootstrap_validator_vote_account = vote_state::create_account(
|
||||
|
@ -179,11 +194,14 @@ pub fn create_genesis_config_with_leader_ex(
|
|||
accounts,
|
||||
fee_rate_governor,
|
||||
rent,
|
||||
cluster_type,
|
||||
..GenesisConfig::default()
|
||||
};
|
||||
|
||||
solana_stake_program::add_genesis_accounts(&mut genesis_config);
|
||||
add_feature_accounts(&mut genesis_config);
|
||||
if genesis_config.cluster_type == ClusterType::Development {
|
||||
activate_all_features(&mut genesis_config);
|
||||
}
|
||||
|
||||
GenesisConfigInfo {
|
||||
genesis_config,
|
||||
|
|
|
@ -759,16 +759,6 @@ impl MessageProcessor {
|
|||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// only used for testing
|
||||
pub fn builtin_loader_ids(&self) -> Vec<Pubkey> {
|
||||
self.loaders.iter().map(|a| a.0).collect::<Vec<_>>()
|
||||
}
|
||||
|
||||
// only used for testing
|
||||
pub fn builtin_program_ids(&self) -> Vec<Pubkey> {
|
||||
self.programs.iter().map(|a| a.0).collect::<Vec<_>>()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
|
|
@ -4,7 +4,7 @@ use {
|
|||
accounts_db::{AccountStorageEntry, AccountsDB, AppendVecId, BankHashInfo},
|
||||
accounts_index::Ancestors,
|
||||
append_vec::AppendVec,
|
||||
bank::{Bank, BankFieldsToDeserialize, BankRc},
|
||||
bank::{Bank, BankFieldsToDeserialize, BankRc, Builtins},
|
||||
blockhash_queue::BlockhashQueue,
|
||||
epoch_stakes::EpochStakes,
|
||||
message_processor::MessageProcessor,
|
||||
|
@ -125,6 +125,7 @@ pub(crate) fn bank_from_stream<R, P>(
|
|||
genesis_config: &GenesisConfig,
|
||||
frozen_account_pubkeys: &[Pubkey],
|
||||
debug_keys: Option<Arc<HashSet<Pubkey>>>,
|
||||
additional_builtins: Option<&Builtins>,
|
||||
) -> std::result::Result<Bank, Error>
|
||||
where
|
||||
R: Read,
|
||||
|
@ -142,6 +143,7 @@ where
|
|||
account_paths,
|
||||
append_vecs_path,
|
||||
debug_keys,
|
||||
additional_builtins,
|
||||
)?;
|
||||
Ok(bank)
|
||||
}};
|
||||
|
@ -227,6 +229,7 @@ fn reconstruct_bank_from_fields<E, P>(
|
|||
account_paths: &[PathBuf],
|
||||
append_vecs_path: P,
|
||||
debug_keys: Option<Arc<HashSet<Pubkey>>>,
|
||||
additional_builtins: Option<&Builtins>,
|
||||
) -> Result<Bank, Error>
|
||||
where
|
||||
E: Into<AccountStorageEntry>,
|
||||
|
@ -241,7 +244,13 @@ where
|
|||
accounts_db.freeze_accounts(&bank_fields.ancestors, frozen_account_pubkeys);
|
||||
|
||||
let bank_rc = BankRc::new(Accounts::new_empty(accounts_db), bank_fields.slot);
|
||||
let bank = Bank::new_from_fields(bank_rc, genesis_config, bank_fields, debug_keys);
|
||||
let bank = Bank::new_from_fields(
|
||||
bank_rc,
|
||||
genesis_config,
|
||||
bank_fields,
|
||||
debug_keys,
|
||||
additional_builtins,
|
||||
);
|
||||
|
||||
Ok(bank)
|
||||
}
|
||||
|
|
|
@ -212,6 +212,7 @@ fn test_bank_serialize_style(serde_style: SerdeStyle) {
|
|||
&genesis_config,
|
||||
&[],
|
||||
None,
|
||||
None,
|
||||
)
|
||||
.unwrap();
|
||||
dbank.src = ref_sc;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use crate::{
|
||||
bank::{Bank, BankSlotDelta},
|
||||
bank::{Bank, BankSlotDelta, Builtins},
|
||||
bank_forks::CompressionType,
|
||||
hardened_unpack::{unpack_snapshot, UnpackError},
|
||||
serde_snapshot::{
|
||||
|
@ -574,6 +574,7 @@ pub fn bank_from_archive<P: AsRef<Path>>(
|
|||
compression: CompressionType,
|
||||
genesis_config: &GenesisConfig,
|
||||
debug_keys: Option<Arc<HashSet<Pubkey>>>,
|
||||
additional_builtins: Option<&Builtins>,
|
||||
) -> Result<Bank> {
|
||||
// Untar the snapshot into a temp directory under `snapshot_config.snapshot_path()`
|
||||
let unpack_dir = tempfile::tempdir_in(snapshot_path)?;
|
||||
|
@ -595,6 +596,7 @@ pub fn bank_from_archive<P: AsRef<Path>>(
|
|||
unpacked_accounts_dir,
|
||||
genesis_config,
|
||||
debug_keys,
|
||||
additional_builtins,
|
||||
)?;
|
||||
|
||||
if !bank.verify_snapshot_bank() {
|
||||
|
@ -753,6 +755,7 @@ fn rebuild_bank_from_snapshots<P>(
|
|||
append_vecs_path: P,
|
||||
genesis_config: &GenesisConfig,
|
||||
debug_keys: Option<Arc<HashSet<Pubkey>>>,
|
||||
additional_builtins: Option<&Builtins>,
|
||||
) -> Result<Bank>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
|
@ -785,6 +788,7 @@ where
|
|||
genesis_config,
|
||||
frozen_account_pubkeys,
|
||||
debug_keys,
|
||||
additional_builtins,
|
||||
),
|
||||
}?)
|
||||
})?;
|
||||
|
|
Loading…
Reference in New Issue