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-config-program",
|
||||||
"solana-frozen-abi 1.16.0",
|
"solana-frozen-abi 1.16.0",
|
||||||
"solana-frozen-abi-macro 1.16.0",
|
"solana-frozen-abi-macro 1.16.0",
|
||||||
|
"solana-loader-v3-program",
|
||||||
"solana-logger 1.16.0",
|
"solana-logger 1.16.0",
|
||||||
"solana-measure",
|
"solana-measure",
|
||||||
"solana-metrics",
|
"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-interface = { path = "geyser-plugin-interface", version = "=1.16.0" }
|
||||||
solana-geyser-plugin-manager = { path = "geyser-plugin-manager", 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-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-ledger = { path = "ledger", version = "=1.16.0" }
|
||||||
solana-local-cluster = { path = "local-cluster", version = "=1.16.0" }
|
solana-local-cluster = { path = "local-cluster", version = "=1.16.0" }
|
||||||
solana-logger = { path = "logger", 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 progress = ConfirmationProgress::new(last_entry_hash);
|
||||||
|
|
||||||
let mut m = Measure::start("process_single_slot");
|
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(
|
if process_single_slot(
|
||||||
blockstore,
|
blockstore,
|
||||||
&bank,
|
&bank,
|
||||||
|
|
|
@ -272,6 +272,12 @@ impl solana_frozen_abi::abi_example::AbiExample for LoadedPrograms {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub enum LoadedProgramMatchCriteria {
|
||||||
|
DeployedOnOrAfterSlot(Slot),
|
||||||
|
Closed,
|
||||||
|
NoCriteria,
|
||||||
|
}
|
||||||
|
|
||||||
impl LoadedPrograms {
|
impl LoadedPrograms {
|
||||||
/// Refill the cache with a single entry. It's typically called during transaction loading,
|
/// 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`.
|
/// when the cache doesn't contain the entry corresponding to program `key`.
|
||||||
|
@ -346,16 +352,31 @@ impl LoadedPrograms {
|
||||||
self.remove_programs_with_no_entries();
|
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
|
/// Extracts a subset of the programs relevant to a transaction batch
|
||||||
/// and returns which program accounts the accounts DB needs to load.
|
/// and returns which program accounts the accounts DB needs to load.
|
||||||
pub fn extract<S: WorkingSlot>(
|
pub fn extract<S: WorkingSlot>(
|
||||||
&self,
|
&self,
|
||||||
working_slot: &S,
|
working_slot: &S,
|
||||||
keys: impl Iterator<Item = Pubkey>,
|
keys: impl Iterator<Item = (Pubkey, LoadedProgramMatchCriteria)>,
|
||||||
) -> (HashMap<Pubkey, Arc<LoadedProgram>>, Vec<Pubkey>) {
|
) -> (HashMap<Pubkey, Arc<LoadedProgram>>, Vec<Pubkey>) {
|
||||||
let mut missing = Vec::new();
|
let mut missing = Vec::new();
|
||||||
let found = keys
|
let found = keys
|
||||||
.filter_map(|key| {
|
.filter_map(|(key, match_criteria)| {
|
||||||
if let Some(second_level) = self.entries.get(&key) {
|
if let Some(second_level) = self.entries.get(&key) {
|
||||||
for entry in second_level.iter().rev() {
|
for entry in second_level.iter().rev() {
|
||||||
let current_slot = working_slot.current_slot();
|
let current_slot = working_slot.current_slot();
|
||||||
|
@ -374,6 +395,11 @@ impl LoadedPrograms {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !Self::matches_loaded_program(entry, &match_criteria) {
|
||||||
|
missing.push(key);
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
if current_slot >= entry.effective_slot {
|
if current_slot >= entry.effective_slot {
|
||||||
return Some((key, entry.clone()));
|
return Some((key, entry.clone()));
|
||||||
}
|
}
|
||||||
|
@ -501,7 +527,8 @@ impl LoadedPrograms {
|
||||||
mod tests {
|
mod tests {
|
||||||
use {
|
use {
|
||||||
crate::loaded_programs::{
|
crate::loaded_programs::{
|
||||||
BlockRelation, ForkGraph, LoadedProgram, LoadedProgramType, LoadedPrograms, WorkingSlot,
|
BlockRelation, ForkGraph, LoadedProgram, LoadedProgramMatchCriteria, LoadedProgramType,
|
||||||
|
LoadedPrograms, WorkingSlot,
|
||||||
},
|
},
|
||||||
percentage::Percentage,
|
percentage::Percentage,
|
||||||
solana_rbpf::vm::BuiltInProgram,
|
solana_rbpf::vm::BuiltInProgram,
|
||||||
|
@ -1153,7 +1180,13 @@ mod tests {
|
||||||
let working_slot = TestWorkingSlot::new(22, &[0, 10, 20, 22]);
|
let working_slot = TestWorkingSlot::new(22, &[0, 10, 20, 22]);
|
||||||
let (found, missing) = cache.extract(
|
let (found, missing) = cache.extract(
|
||||||
&working_slot,
|
&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));
|
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 mut working_slot = TestWorkingSlot::new(16, &[0, 5, 11, 15, 16, 18, 19, 23]);
|
||||||
let (found, missing) = cache.extract(
|
let (found, missing) = cache.extract(
|
||||||
&working_slot,
|
&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));
|
assert!(match_slot(&found, &program1, 0));
|
||||||
|
@ -1181,7 +1220,13 @@ mod tests {
|
||||||
working_slot.update_slot(18);
|
working_slot.update_slot(18);
|
||||||
let (found, missing) = cache.extract(
|
let (found, missing) = cache.extract(
|
||||||
&working_slot,
|
&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));
|
assert!(match_slot(&found, &program1, 0));
|
||||||
|
@ -1196,7 +1241,13 @@ mod tests {
|
||||||
working_slot.update_slot(23);
|
working_slot.update_slot(23);
|
||||||
let (found, missing) = cache.extract(
|
let (found, missing) = cache.extract(
|
||||||
&working_slot,
|
&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));
|
assert!(match_slot(&found, &program1, 0));
|
||||||
|
@ -1211,7 +1262,13 @@ mod tests {
|
||||||
let working_slot = TestWorkingSlot::new(11, &[0, 5, 11, 15, 16]);
|
let working_slot = TestWorkingSlot::new(11, &[0, 5, 11, 15, 16]);
|
||||||
let (found, missing) = cache.extract(
|
let (found, missing) = cache.extract(
|
||||||
&working_slot,
|
&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));
|
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 working_slot = TestWorkingSlot::new(19, &[0, 5, 11, 15, 16, 18, 19, 21, 23]);
|
||||||
let (found, missing) = cache.extract(
|
let (found, missing) = cache.extract(
|
||||||
&working_slot,
|
&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));
|
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 working_slot = TestWorkingSlot::new(21, &[0, 5, 11, 15, 16, 18, 19, 21, 23]);
|
||||||
let (found, missing) = cache.extract(
|
let (found, missing) = cache.extract(
|
||||||
&working_slot,
|
&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));
|
assert!(match_slot(&found, &program1, 0));
|
||||||
|
@ -1285,7 +1354,13 @@ mod tests {
|
||||||
let working_slot = TestWorkingSlot::new(22, &[0, 10, 20, 22]);
|
let working_slot = TestWorkingSlot::new(22, &[0, 10, 20, 22]);
|
||||||
let (found, missing) = cache.extract(
|
let (found, missing) = cache.extract(
|
||||||
&working_slot,
|
&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.
|
// 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 working_slot = TestWorkingSlot::new(27, &[0, 5, 11, 25, 27]);
|
||||||
let (found, _missing) = cache.extract(
|
let (found, _missing) = cache.extract(
|
||||||
&working_slot,
|
&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));
|
assert!(match_slot(&found, &program1, 0));
|
||||||
|
@ -1328,7 +1409,13 @@ mod tests {
|
||||||
let working_slot = TestWorkingSlot::new(27, &[0, 5, 11, 25, 27]);
|
let working_slot = TestWorkingSlot::new(27, &[0, 5, 11, 25, 27]);
|
||||||
let (found, missing) = cache.extract(
|
let (found, missing) = cache.extract(
|
||||||
&working_slot,
|
&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));
|
assert!(match_slot(&found, &program1, 0));
|
||||||
|
@ -1339,6 +1426,81 @@ mod tests {
|
||||||
assert!(missing.contains(&program3));
|
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]
|
#[test]
|
||||||
fn test_prune_expired() {
|
fn test_prune_expired() {
|
||||||
let mut cache = LoadedPrograms::default();
|
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 working_slot = TestWorkingSlot::new(12, &[0, 5, 11, 12, 15, 16, 18, 19, 21, 23]);
|
||||||
let (found, missing) = cache.extract(
|
let (found, missing) = cache.extract(
|
||||||
&working_slot,
|
&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
|
// 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 working_slot = TestWorkingSlot::new(15, &[0, 5, 11, 15, 16, 18, 19, 21, 23]);
|
||||||
let (found, missing) = cache.extract(
|
let (found, missing) = cache.extract(
|
||||||
&working_slot,
|
&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));
|
assert!(match_slot(&found, &program2, 11));
|
||||||
|
@ -1462,7 +1634,10 @@ mod tests {
|
||||||
cache.prune(&fork_graph, 10);
|
cache.prune(&fork_graph, 10);
|
||||||
|
|
||||||
let working_slot = TestWorkingSlot::new(20, &[0, 10, 20]);
|
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
|
// The cache should have the program deployed at slot 0
|
||||||
assert_eq!(
|
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 {
|
unsafe {
|
||||||
let data = data
|
let data = data
|
||||||
.get(0..LoaderV3State::program_data_offset())
|
.get(0..LoaderV3State::program_data_offset())
|
||||||
|
|
|
@ -5113,6 +5113,18 @@ dependencies = [
|
||||||
"trees",
|
"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]]
|
[[package]]
|
||||||
name = "solana-logger"
|
name = "solana-logger"
|
||||||
version = "1.15.2"
|
version = "1.15.2"
|
||||||
|
@ -5608,6 +5620,7 @@ dependencies = [
|
||||||
"solana-config-program",
|
"solana-config-program",
|
||||||
"solana-frozen-abi 1.16.0",
|
"solana-frozen-abi 1.16.0",
|
||||||
"solana-frozen-abi-macro 1.16.0",
|
"solana-frozen-abi-macro 1.16.0",
|
||||||
|
"solana-loader-v3-program",
|
||||||
"solana-measure",
|
"solana-measure",
|
||||||
"solana-metrics",
|
"solana-metrics",
|
||||||
"solana-perf",
|
"solana-perf",
|
||||||
|
|
|
@ -49,6 +49,7 @@ solana-compute-budget-program = { workspace = true }
|
||||||
solana-config-program = { workspace = true }
|
solana-config-program = { workspace = true }
|
||||||
solana-frozen-abi = { workspace = true }
|
solana-frozen-abi = { workspace = true }
|
||||||
solana-frozen-abi-macro = { workspace = true }
|
solana-frozen-abi-macro = { workspace = true }
|
||||||
|
solana-loader-v3-program = { workspace = true }
|
||||||
solana-measure = { workspace = true }
|
solana-measure = { workspace = true }
|
||||||
solana-metrics = { workspace = true }
|
solana-metrics = { workspace = true }
|
||||||
solana-perf = { workspace = true }
|
solana-perf = { workspace = true }
|
||||||
|
|
|
@ -136,6 +136,7 @@ use {
|
||||||
inflation::Inflation,
|
inflation::Inflation,
|
||||||
instruction::{CompiledInstruction, TRANSACTION_LEVEL_STACK_HEIGHT},
|
instruction::{CompiledInstruction, TRANSACTION_LEVEL_STACK_HEIGHT},
|
||||||
lamports::LamportsError,
|
lamports::LamportsError,
|
||||||
|
loader_v3,
|
||||||
message::{AccountKeys, SanitizedMessage},
|
message::{AccountKeys, SanitizedMessage},
|
||||||
native_loader,
|
native_loader,
|
||||||
native_token::{sol_to_lamports, LAMPORTS_PER_SOL},
|
native_token::{sol_to_lamports, LAMPORTS_PER_SOL},
|
||||||
|
@ -257,6 +258,7 @@ pub struct BankRc {
|
||||||
|
|
||||||
#[cfg(RUSTC_WITH_SPECIALIZATION)]
|
#[cfg(RUSTC_WITH_SPECIALIZATION)]
|
||||||
use solana_frozen_abi::abi_example::AbiExample;
|
use solana_frozen_abi::abi_example::AbiExample;
|
||||||
|
use solana_program_runtime::loaded_programs::LoadedProgramMatchCriteria;
|
||||||
|
|
||||||
#[cfg(RUSTC_WITH_SPECIALIZATION)]
|
#[cfg(RUSTC_WITH_SPECIALIZATION)]
|
||||||
impl AbiExample for BankRc {
|
impl AbiExample for BankRc {
|
||||||
|
@ -794,6 +796,7 @@ impl PartialEq for Bank {
|
||||||
fee_structure: _,
|
fee_structure: _,
|
||||||
incremental_snapshot_persistence: _,
|
incremental_snapshot_persistence: _,
|
||||||
loaded_programs_cache: _,
|
loaded_programs_cache: _,
|
||||||
|
check_program_modification_slot: _,
|
||||||
// Ignore new fields explicitly if they do not impact PartialEq.
|
// Ignore new fields explicitly if they do not impact PartialEq.
|
||||||
// Adding ".." will remove compile-time checks that if a new field
|
// Adding ".." will remove compile-time checks that if a new field
|
||||||
// is added to the struct, this PartialEq is accordingly updated.
|
// 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 loaded_programs_cache: Arc<RwLock<LoadedPrograms>>,
|
||||||
|
|
||||||
|
pub check_program_modification_slot: bool,
|
||||||
|
|
||||||
/// true when the bank's freezing or destruction has completed
|
/// true when the bank's freezing or destruction has completed
|
||||||
bank_freeze_or_destruction_incremented: AtomicBool,
|
bank_freeze_or_destruction_incremented: AtomicBool,
|
||||||
}
|
}
|
||||||
|
@ -1269,6 +1274,7 @@ impl Bank {
|
||||||
accounts_data_size_delta_off_chain: AtomicI64::new(0),
|
accounts_data_size_delta_off_chain: AtomicI64::new(0),
|
||||||
fee_structure: FeeStructure::default(),
|
fee_structure: FeeStructure::default(),
|
||||||
loaded_programs_cache: Arc::<RwLock<LoadedPrograms>>::default(),
|
loaded_programs_cache: Arc::<RwLock<LoadedPrograms>>::default(),
|
||||||
|
check_program_modification_slot: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
bank.bank_created();
|
bank.bank_created();
|
||||||
|
@ -1567,6 +1573,7 @@ impl Bank {
|
||||||
accounts_data_size_delta_off_chain: AtomicI64::new(0),
|
accounts_data_size_delta_off_chain: AtomicI64::new(0),
|
||||||
fee_structure: parent.fee_structure.clone(),
|
fee_structure: parent.fee_structure.clone(),
|
||||||
loaded_programs_cache: parent.loaded_programs_cache.clone(),
|
loaded_programs_cache: parent.loaded_programs_cache.clone(),
|
||||||
|
check_program_modification_slot: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
let (_, ancestors_time_us) = measure_us!({
|
let (_, ancestors_time_us) = measure_us!({
|
||||||
|
@ -1893,6 +1900,7 @@ impl Bank {
|
||||||
accounts_data_size_delta_off_chain: AtomicI64::new(0),
|
accounts_data_size_delta_off_chain: AtomicI64::new(0),
|
||||||
fee_structure: FeeStructure::default(),
|
fee_structure: FeeStructure::default(),
|
||||||
loaded_programs_cache: Arc::<RwLock<LoadedPrograms>>::default(),
|
loaded_programs_cache: Arc::<RwLock<LoadedPrograms>>::default(),
|
||||||
|
check_program_modification_slot: false,
|
||||||
};
|
};
|
||||||
bank.bank_created();
|
bank.bank_created();
|
||||||
|
|
||||||
|
@ -4110,6 +4118,36 @@ impl Bank {
|
||||||
let _ = self.executor_cache.write().unwrap().remove(pubkey);
|
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
|
#[allow(dead_code)] // Preparation for BankExecutorCache rework
|
||||||
fn load_program(&self, pubkey: &Pubkey) -> Result<Arc<LoadedProgram>> {
|
fn load_program(&self, pubkey: &Pubkey) -> Result<Arc<LoadedProgram>> {
|
||||||
let program = if let Some(program) = self.get_account_with_fixed_root(pubkey) {
|
let program = if let Some(program) = self.get_account_with_fixed_root(pubkey) {
|
||||||
|
@ -4386,10 +4424,31 @@ impl Bank {
|
||||||
&self,
|
&self,
|
||||||
program_accounts_map: &HashMap<Pubkey, &Pubkey>,
|
program_accounts_map: &HashMap<Pubkey, &Pubkey>,
|
||||||
) -> HashMap<Pubkey, Arc<LoadedProgram>> {
|
) -> 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) = {
|
let (mut loaded_programs_for_txs, missing_programs) = {
|
||||||
// Lock the global cache to figure out which programs need to be loaded
|
// Lock the global cache to figure out which programs need to be loaded
|
||||||
let loaded_programs_cache = self.loaded_programs_cache.read().unwrap();
|
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
|
// Load missing programs while global cache is unlocked
|
||||||
|
|
|
@ -66,6 +66,7 @@ pub struct BankForks {
|
||||||
pub accounts_hash_interval_slots: Slot,
|
pub accounts_hash_interval_slots: Slot,
|
||||||
last_accounts_hash_slot: Slot,
|
last_accounts_hash_slot: Slot,
|
||||||
in_vote_only_mode: Arc<AtomicBool>,
|
in_vote_only_mode: Arc<AtomicBool>,
|
||||||
|
highest_slot_at_startup: Slot,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Index<u64> for BankForks {
|
impl Index<u64> for BankForks {
|
||||||
|
@ -182,10 +183,14 @@ impl BankForks {
|
||||||
accounts_hash_interval_slots: std::u64::MAX,
|
accounts_hash_interval_slots: std::u64::MAX,
|
||||||
last_accounts_hash_slot: root,
|
last_accounts_hash_slot: root,
|
||||||
in_vote_only_mode: Arc::new(AtomicBool::new(false)),
|
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 bank = Arc::new(bank);
|
||||||
let prev = self.banks.insert(bank.slot(), bank.clone());
|
let prev = self.banks.insert(bank.slot(), bank.clone());
|
||||||
assert!(prev.is_none());
|
assert!(prev.is_none());
|
||||||
|
@ -197,6 +202,11 @@ impl BankForks {
|
||||||
bank
|
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>> {
|
pub fn remove(&mut self, slot: Slot) -> Option<Arc<Bank>> {
|
||||||
let bank = self.banks.remove(&slot)?;
|
let bank = self.banks.remove(&slot)?;
|
||||||
for parent in bank.proper_ancestors() {
|
for parent in bank.proper_ancestors() {
|
||||||
|
|
Loading…
Reference in New Issue