Check program modification slots during cold start (#31331)
This commit is contained in:
parent
aafcac27d8
commit
94dc8fed55
|
@ -6699,6 +6699,7 @@ dependencies = [
|
|||
"solana-config-program",
|
||||
"solana-frozen-abi 1.16.0",
|
||||
"solana-frozen-abi-macro 1.16.0",
|
||||
"solana-loader-v3-program",
|
||||
"solana-logger 1.16.0",
|
||||
"solana-measure",
|
||||
"solana-metrics",
|
||||
|
|
|
@ -319,6 +319,7 @@ solana-genesis-utils = { path = "genesis-utils", version = "=1.16.0" }
|
|||
solana-geyser-plugin-interface = { path = "geyser-plugin-interface", version = "=1.16.0" }
|
||||
solana-geyser-plugin-manager = { path = "geyser-plugin-manager", version = "=1.16.0" }
|
||||
solana-gossip = { path = "gossip", version = "=1.16.0" }
|
||||
solana-loader-v3-program = { path = "programs/loader-v3", version = "=1.16.0" }
|
||||
solana-ledger = { path = "ledger", version = "=1.16.0" }
|
||||
solana-local-cluster = { path = "local-cluster", version = "=1.16.0" }
|
||||
solana-logger = { path = "logger", version = "=1.16.0" }
|
||||
|
|
|
@ -1434,7 +1434,7 @@ fn load_frozen_forks(
|
|||
let mut progress = ConfirmationProgress::new(last_entry_hash);
|
||||
|
||||
let mut m = Measure::start("process_single_slot");
|
||||
let bank = bank_forks.write().unwrap().insert(bank);
|
||||
let bank = bank_forks.write().unwrap().insert_from_ledger(bank);
|
||||
if process_single_slot(
|
||||
blockstore,
|
||||
&bank,
|
||||
|
|
|
@ -272,6 +272,12 @@ impl solana_frozen_abi::abi_example::AbiExample for LoadedPrograms {
|
|||
}
|
||||
}
|
||||
|
||||
pub enum LoadedProgramMatchCriteria {
|
||||
DeployedOnOrAfterSlot(Slot),
|
||||
Closed,
|
||||
NoCriteria,
|
||||
}
|
||||
|
||||
impl LoadedPrograms {
|
||||
/// Refill the cache with a single entry. It's typically called during transaction loading,
|
||||
/// when the cache doesn't contain the entry corresponding to program `key`.
|
||||
|
@ -346,16 +352,31 @@ impl LoadedPrograms {
|
|||
self.remove_programs_with_no_entries();
|
||||
}
|
||||
|
||||
fn matches_loaded_program(
|
||||
program: &Arc<LoadedProgram>,
|
||||
criteria: &LoadedProgramMatchCriteria,
|
||||
) -> bool {
|
||||
match criteria {
|
||||
LoadedProgramMatchCriteria::DeployedOnOrAfterSlot(slot) => {
|
||||
program.deployment_slot >= *slot
|
||||
}
|
||||
LoadedProgramMatchCriteria::Closed => {
|
||||
matches!(program.program, LoadedProgramType::Closed)
|
||||
}
|
||||
LoadedProgramMatchCriteria::NoCriteria => true,
|
||||
}
|
||||
}
|
||||
|
||||
/// Extracts a subset of the programs relevant to a transaction batch
|
||||
/// and returns which program accounts the accounts DB needs to load.
|
||||
pub fn extract<S: WorkingSlot>(
|
||||
&self,
|
||||
working_slot: &S,
|
||||
keys: impl Iterator<Item = Pubkey>,
|
||||
keys: impl Iterator<Item = (Pubkey, LoadedProgramMatchCriteria)>,
|
||||
) -> (HashMap<Pubkey, Arc<LoadedProgram>>, Vec<Pubkey>) {
|
||||
let mut missing = Vec::new();
|
||||
let found = keys
|
||||
.filter_map(|key| {
|
||||
.filter_map(|(key, match_criteria)| {
|
||||
if let Some(second_level) = self.entries.get(&key) {
|
||||
for entry in second_level.iter().rev() {
|
||||
let current_slot = working_slot.current_slot();
|
||||
|
@ -374,6 +395,11 @@ impl LoadedPrograms {
|
|||
return None;
|
||||
}
|
||||
|
||||
if !Self::matches_loaded_program(entry, &match_criteria) {
|
||||
missing.push(key);
|
||||
return None;
|
||||
}
|
||||
|
||||
if current_slot >= entry.effective_slot {
|
||||
return Some((key, entry.clone()));
|
||||
}
|
||||
|
@ -501,7 +527,8 @@ impl LoadedPrograms {
|
|||
mod tests {
|
||||
use {
|
||||
crate::loaded_programs::{
|
||||
BlockRelation, ForkGraph, LoadedProgram, LoadedProgramType, LoadedPrograms, WorkingSlot,
|
||||
BlockRelation, ForkGraph, LoadedProgram, LoadedProgramMatchCriteria, LoadedProgramType,
|
||||
LoadedPrograms, WorkingSlot,
|
||||
},
|
||||
percentage::Percentage,
|
||||
solana_rbpf::vm::BuiltInProgram,
|
||||
|
@ -1153,7 +1180,13 @@ mod tests {
|
|||
let working_slot = TestWorkingSlot::new(22, &[0, 10, 20, 22]);
|
||||
let (found, missing) = cache.extract(
|
||||
&working_slot,
|
||||
vec![program1, program2, program3, program4].into_iter(),
|
||||
vec![
|
||||
(program1, LoadedProgramMatchCriteria::NoCriteria),
|
||||
(program2, LoadedProgramMatchCriteria::NoCriteria),
|
||||
(program3, LoadedProgramMatchCriteria::NoCriteria),
|
||||
(program4, LoadedProgramMatchCriteria::NoCriteria),
|
||||
]
|
||||
.into_iter(),
|
||||
);
|
||||
|
||||
assert!(match_slot(&found, &program1, 20));
|
||||
|
@ -1166,7 +1199,13 @@ mod tests {
|
|||
let mut working_slot = TestWorkingSlot::new(16, &[0, 5, 11, 15, 16, 18, 19, 23]);
|
||||
let (found, missing) = cache.extract(
|
||||
&working_slot,
|
||||
vec![program1, program2, program3, program4].into_iter(),
|
||||
vec![
|
||||
(program1, LoadedProgramMatchCriteria::NoCriteria),
|
||||
(program2, LoadedProgramMatchCriteria::NoCriteria),
|
||||
(program3, LoadedProgramMatchCriteria::NoCriteria),
|
||||
(program4, LoadedProgramMatchCriteria::NoCriteria),
|
||||
]
|
||||
.into_iter(),
|
||||
);
|
||||
|
||||
assert!(match_slot(&found, &program1, 0));
|
||||
|
@ -1181,7 +1220,13 @@ mod tests {
|
|||
working_slot.update_slot(18);
|
||||
let (found, missing) = cache.extract(
|
||||
&working_slot,
|
||||
vec![program1, program2, program3, program4].into_iter(),
|
||||
vec![
|
||||
(program1, LoadedProgramMatchCriteria::NoCriteria),
|
||||
(program2, LoadedProgramMatchCriteria::NoCriteria),
|
||||
(program3, LoadedProgramMatchCriteria::NoCriteria),
|
||||
(program4, LoadedProgramMatchCriteria::NoCriteria),
|
||||
]
|
||||
.into_iter(),
|
||||
);
|
||||
|
||||
assert!(match_slot(&found, &program1, 0));
|
||||
|
@ -1196,7 +1241,13 @@ mod tests {
|
|||
working_slot.update_slot(23);
|
||||
let (found, missing) = cache.extract(
|
||||
&working_slot,
|
||||
vec![program1, program2, program3, program4].into_iter(),
|
||||
vec![
|
||||
(program1, LoadedProgramMatchCriteria::NoCriteria),
|
||||
(program2, LoadedProgramMatchCriteria::NoCriteria),
|
||||
(program3, LoadedProgramMatchCriteria::NoCriteria),
|
||||
(program4, LoadedProgramMatchCriteria::NoCriteria),
|
||||
]
|
||||
.into_iter(),
|
||||
);
|
||||
|
||||
assert!(match_slot(&found, &program1, 0));
|
||||
|
@ -1211,7 +1262,13 @@ mod tests {
|
|||
let working_slot = TestWorkingSlot::new(11, &[0, 5, 11, 15, 16]);
|
||||
let (found, missing) = cache.extract(
|
||||
&working_slot,
|
||||
vec![program1, program2, program3, program4].into_iter(),
|
||||
vec![
|
||||
(program1, LoadedProgramMatchCriteria::NoCriteria),
|
||||
(program2, LoadedProgramMatchCriteria::NoCriteria),
|
||||
(program3, LoadedProgramMatchCriteria::NoCriteria),
|
||||
(program4, LoadedProgramMatchCriteria::NoCriteria),
|
||||
]
|
||||
.into_iter(),
|
||||
);
|
||||
|
||||
assert!(match_slot(&found, &program1, 0));
|
||||
|
@ -1235,7 +1292,13 @@ mod tests {
|
|||
let working_slot = TestWorkingSlot::new(19, &[0, 5, 11, 15, 16, 18, 19, 21, 23]);
|
||||
let (found, missing) = cache.extract(
|
||||
&working_slot,
|
||||
vec![program1, program2, program3, program4].into_iter(),
|
||||
vec![
|
||||
(program1, LoadedProgramMatchCriteria::NoCriteria),
|
||||
(program2, LoadedProgramMatchCriteria::NoCriteria),
|
||||
(program3, LoadedProgramMatchCriteria::NoCriteria),
|
||||
(program4, LoadedProgramMatchCriteria::NoCriteria),
|
||||
]
|
||||
.into_iter(),
|
||||
);
|
||||
|
||||
assert!(match_slot(&found, &program1, 0));
|
||||
|
@ -1250,7 +1313,13 @@ mod tests {
|
|||
let working_slot = TestWorkingSlot::new(21, &[0, 5, 11, 15, 16, 18, 19, 21, 23]);
|
||||
let (found, missing) = cache.extract(
|
||||
&working_slot,
|
||||
vec![program1, program2, program3, program4].into_iter(),
|
||||
vec![
|
||||
(program1, LoadedProgramMatchCriteria::NoCriteria),
|
||||
(program2, LoadedProgramMatchCriteria::NoCriteria),
|
||||
(program3, LoadedProgramMatchCriteria::NoCriteria),
|
||||
(program4, LoadedProgramMatchCriteria::NoCriteria),
|
||||
]
|
||||
.into_iter(),
|
||||
);
|
||||
|
||||
assert!(match_slot(&found, &program1, 0));
|
||||
|
@ -1285,7 +1354,13 @@ mod tests {
|
|||
let working_slot = TestWorkingSlot::new(22, &[0, 10, 20, 22]);
|
||||
let (found, missing) = cache.extract(
|
||||
&working_slot,
|
||||
vec![program1, program2, program3, program4].into_iter(),
|
||||
vec![
|
||||
(program1, LoadedProgramMatchCriteria::NoCriteria),
|
||||
(program2, LoadedProgramMatchCriteria::NoCriteria),
|
||||
(program3, LoadedProgramMatchCriteria::NoCriteria),
|
||||
(program4, LoadedProgramMatchCriteria::NoCriteria),
|
||||
]
|
||||
.into_iter(),
|
||||
);
|
||||
|
||||
// Since the fork was pruned, we should not find the entry deployed at slot 20.
|
||||
|
@ -1299,7 +1374,13 @@ mod tests {
|
|||
let working_slot = TestWorkingSlot::new(27, &[0, 5, 11, 25, 27]);
|
||||
let (found, _missing) = cache.extract(
|
||||
&working_slot,
|
||||
vec![program1, program2, program3, program4].into_iter(),
|
||||
vec![
|
||||
(program1, LoadedProgramMatchCriteria::NoCriteria),
|
||||
(program2, LoadedProgramMatchCriteria::NoCriteria),
|
||||
(program3, LoadedProgramMatchCriteria::NoCriteria),
|
||||
(program4, LoadedProgramMatchCriteria::NoCriteria),
|
||||
]
|
||||
.into_iter(),
|
||||
);
|
||||
|
||||
assert!(match_slot(&found, &program1, 0));
|
||||
|
@ -1328,7 +1409,13 @@ mod tests {
|
|||
let working_slot = TestWorkingSlot::new(27, &[0, 5, 11, 25, 27]);
|
||||
let (found, missing) = cache.extract(
|
||||
&working_slot,
|
||||
vec![program1, program2, program3, program4].into_iter(),
|
||||
vec![
|
||||
(program1, LoadedProgramMatchCriteria::NoCriteria),
|
||||
(program2, LoadedProgramMatchCriteria::NoCriteria),
|
||||
(program3, LoadedProgramMatchCriteria::NoCriteria),
|
||||
(program4, LoadedProgramMatchCriteria::NoCriteria),
|
||||
]
|
||||
.into_iter(),
|
||||
);
|
||||
|
||||
assert!(match_slot(&found, &program1, 0));
|
||||
|
@ -1339,6 +1426,81 @@ mod tests {
|
|||
assert!(missing.contains(&program3));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_extract_using_deployment_slot() {
|
||||
let mut cache = LoadedPrograms::default();
|
||||
|
||||
// Fork graph created for the test
|
||||
// 0
|
||||
// / \
|
||||
// 10 5
|
||||
// | |
|
||||
// 20 11
|
||||
// | | \
|
||||
// 22 15 25
|
||||
// | |
|
||||
// 16 27
|
||||
// |
|
||||
// 19
|
||||
// |
|
||||
// 23
|
||||
|
||||
let mut fork_graph = TestForkGraphSpecific::default();
|
||||
fork_graph.insert_fork(&[0, 10, 20, 22]);
|
||||
fork_graph.insert_fork(&[0, 5, 11, 15, 16, 19, 21, 23]);
|
||||
fork_graph.insert_fork(&[0, 5, 11, 25, 27]);
|
||||
|
||||
let program1 = Pubkey::new_unique();
|
||||
assert!(!cache.replenish(program1, new_test_loaded_program(0, 1)).0);
|
||||
assert!(!cache.replenish(program1, new_test_loaded_program(20, 21)).0);
|
||||
|
||||
let program2 = Pubkey::new_unique();
|
||||
assert!(!cache.replenish(program2, new_test_loaded_program(5, 6)).0);
|
||||
assert!(!cache.replenish(program2, new_test_loaded_program(11, 12)).0);
|
||||
|
||||
let program3 = Pubkey::new_unique();
|
||||
assert!(!cache.replenish(program3, new_test_loaded_program(25, 26)).0);
|
||||
|
||||
// Testing fork 0 - 5 - 11 - 15 - 16 - 19 - 21 - 23 with current slot at 19
|
||||
let working_slot = TestWorkingSlot::new(12, &[0, 5, 11, 12, 15, 16, 18, 19, 21, 23]);
|
||||
let (found, missing) = cache.extract(
|
||||
&working_slot,
|
||||
vec![
|
||||
(program1, LoadedProgramMatchCriteria::NoCriteria),
|
||||
(program2, LoadedProgramMatchCriteria::NoCriteria),
|
||||
(program3, LoadedProgramMatchCriteria::NoCriteria),
|
||||
]
|
||||
.into_iter(),
|
||||
);
|
||||
|
||||
assert!(match_slot(&found, &program1, 0));
|
||||
assert!(match_slot(&found, &program2, 11));
|
||||
|
||||
assert!(missing.contains(&program3));
|
||||
|
||||
// Test the same fork, but request the program modified at a later slot than what's in the cache.
|
||||
let (found, missing) = cache.extract(
|
||||
&working_slot,
|
||||
vec![
|
||||
(
|
||||
program1,
|
||||
LoadedProgramMatchCriteria::DeployedOnOrAfterSlot(5),
|
||||
),
|
||||
(
|
||||
program2,
|
||||
LoadedProgramMatchCriteria::DeployedOnOrAfterSlot(5),
|
||||
),
|
||||
(program3, LoadedProgramMatchCriteria::NoCriteria),
|
||||
]
|
||||
.into_iter(),
|
||||
);
|
||||
|
||||
assert!(match_slot(&found, &program2, 11));
|
||||
|
||||
assert!(missing.contains(&program1));
|
||||
assert!(missing.contains(&program3));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_prune_expired() {
|
||||
let mut cache = LoadedPrograms::default();
|
||||
|
@ -1389,7 +1551,12 @@ mod tests {
|
|||
let working_slot = TestWorkingSlot::new(12, &[0, 5, 11, 12, 15, 16, 18, 19, 21, 23]);
|
||||
let (found, missing) = cache.extract(
|
||||
&working_slot,
|
||||
vec![program1, program2, program3].into_iter(),
|
||||
vec![
|
||||
(program1, LoadedProgramMatchCriteria::NoCriteria),
|
||||
(program2, LoadedProgramMatchCriteria::NoCriteria),
|
||||
(program3, LoadedProgramMatchCriteria::NoCriteria),
|
||||
]
|
||||
.into_iter(),
|
||||
);
|
||||
|
||||
// Program1 deployed at slot 11 should not be expired yet
|
||||
|
@ -1403,7 +1570,12 @@ mod tests {
|
|||
let working_slot = TestWorkingSlot::new(15, &[0, 5, 11, 15, 16, 18, 19, 21, 23]);
|
||||
let (found, missing) = cache.extract(
|
||||
&working_slot,
|
||||
vec![program1, program2, program3].into_iter(),
|
||||
vec![
|
||||
(program1, LoadedProgramMatchCriteria::NoCriteria),
|
||||
(program2, LoadedProgramMatchCriteria::NoCriteria),
|
||||
(program3, LoadedProgramMatchCriteria::NoCriteria),
|
||||
]
|
||||
.into_iter(),
|
||||
);
|
||||
|
||||
assert!(match_slot(&found, &program2, 11));
|
||||
|
@ -1462,7 +1634,10 @@ mod tests {
|
|||
cache.prune(&fork_graph, 10);
|
||||
|
||||
let working_slot = TestWorkingSlot::new(20, &[0, 10, 20]);
|
||||
let (found, _missing) = cache.extract(&working_slot, vec![program1].into_iter());
|
||||
let (found, _missing) = cache.extract(
|
||||
&working_slot,
|
||||
vec![(program1, LoadedProgramMatchCriteria::NoCriteria)].into_iter(),
|
||||
);
|
||||
|
||||
// The cache should have the program deployed at slot 0
|
||||
assert_eq!(
|
||||
|
|
|
@ -37,7 +37,7 @@ use {
|
|||
},
|
||||
};
|
||||
|
||||
fn get_state(data: &[u8]) -> Result<&LoaderV3State, InstructionError> {
|
||||
pub fn get_state(data: &[u8]) -> Result<&LoaderV3State, InstructionError> {
|
||||
unsafe {
|
||||
let data = data
|
||||
.get(0..LoaderV3State::program_data_offset())
|
||||
|
|
|
@ -5113,6 +5113,18 @@ dependencies = [
|
|||
"trees",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "solana-loader-v3-program"
|
||||
version = "1.16.0"
|
||||
dependencies = [
|
||||
"log",
|
||||
"rand 0.7.3",
|
||||
"solana-measure",
|
||||
"solana-program-runtime",
|
||||
"solana-sdk 1.16.0",
|
||||
"solana_rbpf",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "solana-logger"
|
||||
version = "1.15.2"
|
||||
|
@ -5608,6 +5620,7 @@ dependencies = [
|
|||
"solana-config-program",
|
||||
"solana-frozen-abi 1.16.0",
|
||||
"solana-frozen-abi-macro 1.16.0",
|
||||
"solana-loader-v3-program",
|
||||
"solana-measure",
|
||||
"solana-metrics",
|
||||
"solana-perf",
|
||||
|
|
|
@ -49,6 +49,7 @@ solana-compute-budget-program = { workspace = true }
|
|||
solana-config-program = { workspace = true }
|
||||
solana-frozen-abi = { workspace = true }
|
||||
solana-frozen-abi-macro = { workspace = true }
|
||||
solana-loader-v3-program = { workspace = true }
|
||||
solana-measure = { workspace = true }
|
||||
solana-metrics = { workspace = true }
|
||||
solana-perf = { workspace = true }
|
||||
|
|
|
@ -136,6 +136,7 @@ use {
|
|||
inflation::Inflation,
|
||||
instruction::{CompiledInstruction, TRANSACTION_LEVEL_STACK_HEIGHT},
|
||||
lamports::LamportsError,
|
||||
loader_v3,
|
||||
message::{AccountKeys, SanitizedMessage},
|
||||
native_loader,
|
||||
native_token::{sol_to_lamports, LAMPORTS_PER_SOL},
|
||||
|
@ -257,6 +258,7 @@ pub struct BankRc {
|
|||
|
||||
#[cfg(RUSTC_WITH_SPECIALIZATION)]
|
||||
use solana_frozen_abi::abi_example::AbiExample;
|
||||
use solana_program_runtime::loaded_programs::LoadedProgramMatchCriteria;
|
||||
|
||||
#[cfg(RUSTC_WITH_SPECIALIZATION)]
|
||||
impl AbiExample for BankRc {
|
||||
|
@ -794,6 +796,7 @@ impl PartialEq for Bank {
|
|||
fee_structure: _,
|
||||
incremental_snapshot_persistence: _,
|
||||
loaded_programs_cache: _,
|
||||
check_program_modification_slot: _,
|
||||
// Ignore new fields explicitly if they do not impact PartialEq.
|
||||
// Adding ".." will remove compile-time checks that if a new field
|
||||
// is added to the struct, this PartialEq is accordingly updated.
|
||||
|
@ -1049,6 +1052,8 @@ pub struct Bank {
|
|||
|
||||
pub loaded_programs_cache: Arc<RwLock<LoadedPrograms>>,
|
||||
|
||||
pub check_program_modification_slot: bool,
|
||||
|
||||
/// true when the bank's freezing or destruction has completed
|
||||
bank_freeze_or_destruction_incremented: AtomicBool,
|
||||
}
|
||||
|
@ -1269,6 +1274,7 @@ impl Bank {
|
|||
accounts_data_size_delta_off_chain: AtomicI64::new(0),
|
||||
fee_structure: FeeStructure::default(),
|
||||
loaded_programs_cache: Arc::<RwLock<LoadedPrograms>>::default(),
|
||||
check_program_modification_slot: false,
|
||||
};
|
||||
|
||||
bank.bank_created();
|
||||
|
@ -1567,6 +1573,7 @@ impl Bank {
|
|||
accounts_data_size_delta_off_chain: AtomicI64::new(0),
|
||||
fee_structure: parent.fee_structure.clone(),
|
||||
loaded_programs_cache: parent.loaded_programs_cache.clone(),
|
||||
check_program_modification_slot: false,
|
||||
};
|
||||
|
||||
let (_, ancestors_time_us) = measure_us!({
|
||||
|
@ -1893,6 +1900,7 @@ impl Bank {
|
|||
accounts_data_size_delta_off_chain: AtomicI64::new(0),
|
||||
fee_structure: FeeStructure::default(),
|
||||
loaded_programs_cache: Arc::<RwLock<LoadedPrograms>>::default(),
|
||||
check_program_modification_slot: false,
|
||||
};
|
||||
bank.bank_created();
|
||||
|
||||
|
@ -4110,6 +4118,36 @@ impl Bank {
|
|||
let _ = self.executor_cache.write().unwrap().remove(pubkey);
|
||||
}
|
||||
|
||||
fn program_modification_slot(&self, pubkey: &Pubkey) -> Result<Slot> {
|
||||
let program = self
|
||||
.get_account_with_fixed_root(pubkey)
|
||||
.ok_or(TransactionError::ProgramAccountNotFound)?;
|
||||
if bpf_loader_upgradeable::check_id(program.owner()) {
|
||||
if let Ok(UpgradeableLoaderState::Program {
|
||||
programdata_address,
|
||||
}) = program.state()
|
||||
{
|
||||
let programdata = self
|
||||
.get_account_with_fixed_root(&programdata_address)
|
||||
.ok_or(TransactionError::ProgramAccountNotFound)?;
|
||||
if let Ok(UpgradeableLoaderState::ProgramData {
|
||||
slot,
|
||||
upgrade_authority_address: _,
|
||||
}) = programdata.state()
|
||||
{
|
||||
return Ok(slot);
|
||||
}
|
||||
}
|
||||
Err(TransactionError::ProgramAccountNotFound)
|
||||
} else if loader_v3::check_id(program.owner()) {
|
||||
let state = solana_loader_v3_program::get_state(program.data())
|
||||
.map_err(|_| TransactionError::ProgramAccountNotFound)?;
|
||||
Ok(state.slot)
|
||||
} else {
|
||||
Ok(0)
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)] // Preparation for BankExecutorCache rework
|
||||
fn load_program(&self, pubkey: &Pubkey) -> Result<Arc<LoadedProgram>> {
|
||||
let program = if let Some(program) = self.get_account_with_fixed_root(pubkey) {
|
||||
|
@ -4386,10 +4424,31 @@ impl Bank {
|
|||
&self,
|
||||
program_accounts_map: &HashMap<Pubkey, &Pubkey>,
|
||||
) -> HashMap<Pubkey, Arc<LoadedProgram>> {
|
||||
let programs_and_slots: Vec<(Pubkey, LoadedProgramMatchCriteria)> =
|
||||
if self.check_program_modification_slot {
|
||||
program_accounts_map
|
||||
.keys()
|
||||
.map(|pubkey| {
|
||||
(
|
||||
*pubkey,
|
||||
self.program_modification_slot(pubkey)
|
||||
.map_or(LoadedProgramMatchCriteria::Closed, |slot| {
|
||||
LoadedProgramMatchCriteria::DeployedOnOrAfterSlot(slot)
|
||||
}),
|
||||
)
|
||||
})
|
||||
.collect()
|
||||
} else {
|
||||
program_accounts_map
|
||||
.keys()
|
||||
.map(|pubkey| (*pubkey, LoadedProgramMatchCriteria::NoCriteria))
|
||||
.collect()
|
||||
};
|
||||
|
||||
let (mut loaded_programs_for_txs, missing_programs) = {
|
||||
// Lock the global cache to figure out which programs need to be loaded
|
||||
let loaded_programs_cache = self.loaded_programs_cache.read().unwrap();
|
||||
loaded_programs_cache.extract(self, program_accounts_map.keys().cloned())
|
||||
loaded_programs_cache.extract(self, programs_and_slots.into_iter())
|
||||
};
|
||||
|
||||
// Load missing programs while global cache is unlocked
|
||||
|
|
|
@ -66,6 +66,7 @@ pub struct BankForks {
|
|||
pub accounts_hash_interval_slots: Slot,
|
||||
last_accounts_hash_slot: Slot,
|
||||
in_vote_only_mode: Arc<AtomicBool>,
|
||||
highest_slot_at_startup: Slot,
|
||||
}
|
||||
|
||||
impl Index<u64> for BankForks {
|
||||
|
@ -182,10 +183,14 @@ impl BankForks {
|
|||
accounts_hash_interval_slots: std::u64::MAX,
|
||||
last_accounts_hash_slot: root,
|
||||
in_vote_only_mode: Arc::new(AtomicBool::new(false)),
|
||||
highest_slot_at_startup: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn insert(&mut self, bank: Bank) -> Arc<Bank> {
|
||||
pub fn insert(&mut self, mut bank: Bank) -> Arc<Bank> {
|
||||
bank.check_program_modification_slot =
|
||||
self.root.load(Ordering::Relaxed) < self.highest_slot_at_startup;
|
||||
|
||||
let bank = Arc::new(bank);
|
||||
let prev = self.banks.insert(bank.slot(), bank.clone());
|
||||
assert!(prev.is_none());
|
||||
|
@ -197,6 +202,11 @@ impl BankForks {
|
|||
bank
|
||||
}
|
||||
|
||||
pub fn insert_from_ledger(&mut self, bank: Bank) -> Arc<Bank> {
|
||||
self.highest_slot_at_startup = std::cmp::max(self.highest_slot_at_startup, bank.slot());
|
||||
self.insert(bank)
|
||||
}
|
||||
|
||||
pub fn remove(&mut self, slot: Slot) -> Option<Arc<Bank>> {
|
||||
let bank = self.banks.remove(&slot)?;
|
||||
for parent in bank.proper_ancestors() {
|
||||
|
|
Loading…
Reference in New Issue