Fix: deploy program on last slot of epoch during environment change (#101)
* Fix: deploy program on last slot of epoch during environment change * solana-runtime: deploy at last epoch slot test * disable deployment of sol_alloc_free * Move tx-batch-constructor to its own function * use new_from_cache --------- Co-authored-by: Alessandro Decina <alessandro.d@gmail.com>
This commit is contained in:
parent
36c66f5111
commit
91b1ee3df6
|
@ -21,11 +21,12 @@ use {
|
|||
},
|
||||
solana_runtime::bank::Bank,
|
||||
solana_sdk::{
|
||||
account::AccountSharedData,
|
||||
account::{create_account_shared_data_for_test, AccountSharedData},
|
||||
account_utils::StateMut,
|
||||
bpf_loader_upgradeable::{self, UpgradeableLoaderState},
|
||||
pubkey::Pubkey,
|
||||
slot_history::Slot,
|
||||
sysvar,
|
||||
transaction_context::{IndexOfAccount, InstructionAccount},
|
||||
},
|
||||
std::{
|
||||
|
@ -510,13 +511,16 @@ pub fn program(ledger_path: &Path, matches: &ArgMatches<'_>) {
|
|||
program_id, // ID of the loaded program. It can modify accounts with the same owner key
|
||||
AccountSharedData::new(0, 0, &loader_id),
|
||||
));
|
||||
transaction_accounts.push((
|
||||
sysvar::epoch_schedule::id(),
|
||||
create_account_shared_data_for_test(bank.epoch_schedule()),
|
||||
));
|
||||
let interpreted = matches.value_of("mode").unwrap() != "jit";
|
||||
with_mock_invoke_context!(invoke_context, transaction_context, transaction_accounts);
|
||||
|
||||
// Adding `DELAY_VISIBILITY_SLOT_OFFSET` to slots to accommodate for delay visibility of the program
|
||||
let slot = bank.slot() + DELAY_VISIBILITY_SLOT_OFFSET;
|
||||
let mut loaded_programs =
|
||||
LoadedProgramsForTxBatch::new(slot, bank.get_runtime_environments_for_slot(slot));
|
||||
bank.new_program_cache_for_tx_batch_for_slot(bank.slot() + DELAY_VISIBILITY_SLOT_OFFSET);
|
||||
for key in cached_account_keys {
|
||||
loaded_programs.replenish(key, bank.load_program(&key, false, bank.epoch()));
|
||||
debug!("Loaded program {}", key);
|
||||
|
|
|
@ -2,7 +2,9 @@ use {
|
|||
crate::{
|
||||
compute_budget::ComputeBudget,
|
||||
ic_msg,
|
||||
loaded_programs::{LoadedProgram, LoadedProgramType, LoadedProgramsForTxBatch},
|
||||
loaded_programs::{
|
||||
LoadedProgram, LoadedProgramType, LoadedProgramsForTxBatch, ProgramRuntimeEnvironments,
|
||||
},
|
||||
log_collector::LogCollector,
|
||||
stable_log,
|
||||
sysvar_cache::SysvarCache,
|
||||
|
@ -17,8 +19,10 @@ use {
|
|||
vm::{Config, ContextObject, EbpfVm},
|
||||
},
|
||||
solana_sdk::{
|
||||
account::AccountSharedData,
|
||||
account::{create_account_shared_data_for_test, AccountSharedData},
|
||||
bpf_loader_deprecated,
|
||||
clock::Slot,
|
||||
epoch_schedule::EpochSchedule,
|
||||
feature_set::FeatureSet,
|
||||
hash::Hash,
|
||||
instruction::{AccountMeta, InstructionError},
|
||||
|
@ -26,6 +30,7 @@ use {
|
|||
pubkey::Pubkey,
|
||||
saturating_add_assign,
|
||||
stable_layout::stable_instruction::StableInstruction,
|
||||
sysvar,
|
||||
transaction_context::{
|
||||
IndexOfAccount, InstructionAccount, TransactionAccount, TransactionContext,
|
||||
},
|
||||
|
@ -209,6 +214,17 @@ impl<'a> InvokeContext<'a> {
|
|||
.or_else(|| self.programs_loaded_for_tx_batch.find(pubkey))
|
||||
}
|
||||
|
||||
pub fn get_environments_for_slot(
|
||||
&self,
|
||||
effective_slot: Slot,
|
||||
) -> Result<&ProgramRuntimeEnvironments, InstructionError> {
|
||||
let epoch_schedule = self.sysvar_cache.get_epoch_schedule()?;
|
||||
let epoch = epoch_schedule.get_epoch(effective_slot);
|
||||
Ok(self
|
||||
.programs_loaded_for_tx_batch
|
||||
.get_environments_for_epoch(epoch))
|
||||
}
|
||||
|
||||
/// Push a stack frame onto the invocation stack
|
||||
pub fn push(&mut self) -> Result<(), InstructionError> {
|
||||
let instruction_context = self
|
||||
|
@ -713,6 +729,18 @@ pub fn mock_process_instruction<F: FnMut(&mut InvokeContext), G: FnMut(&mut Invo
|
|||
program_indices.insert(0, transaction_accounts.len() as IndexOfAccount);
|
||||
let processor_account = AccountSharedData::new(0, 0, &native_loader::id());
|
||||
transaction_accounts.push((*loader_id, processor_account));
|
||||
let pop_epoch_schedule_account = if !transaction_accounts
|
||||
.iter()
|
||||
.any(|(key, _)| *key == sysvar::epoch_schedule::id())
|
||||
{
|
||||
transaction_accounts.push((
|
||||
sysvar::epoch_schedule::id(),
|
||||
create_account_shared_data_for_test(&EpochSchedule::default()),
|
||||
));
|
||||
true
|
||||
} else {
|
||||
false
|
||||
};
|
||||
with_mock_invoke_context!(invoke_context, transaction_context, transaction_accounts);
|
||||
let mut programs_loaded_for_tx_batch = LoadedProgramsForTxBatch::default();
|
||||
programs_loaded_for_tx_batch.replenish(
|
||||
|
@ -731,6 +759,9 @@ pub fn mock_process_instruction<F: FnMut(&mut InvokeContext), G: FnMut(&mut Invo
|
|||
assert_eq!(result, expected_result);
|
||||
post_adjustments(&mut invoke_context);
|
||||
let mut transaction_accounts = transaction_context.deconstruct_without_keys().unwrap();
|
||||
if pop_epoch_schedule_account {
|
||||
transaction_accounts.pop();
|
||||
}
|
||||
transaction_accounts.pop();
|
||||
transaction_accounts
|
||||
}
|
||||
|
|
|
@ -618,19 +618,61 @@ pub struct LoadedProgramsForTxBatch {
|
|||
entries: HashMap<Pubkey, Arc<LoadedProgram>>,
|
||||
slot: Slot,
|
||||
pub environments: ProgramRuntimeEnvironments,
|
||||
/// Anticipated replacement for `environments` at the next epoch.
|
||||
///
|
||||
/// This is `None` during most of an epoch, and only `Some` around the boundaries (at the end and beginning of an epoch).
|
||||
/// More precisely, it starts with the recompilation phase a few hundred slots before the epoch boundary,
|
||||
/// and it ends with the first rerooting after the epoch boundary.
|
||||
/// Needed when a program is deployed at the last slot of an epoch, becomes effective in the next epoch.
|
||||
/// So needs to be compiled with the environment for the next epoch.
|
||||
pub upcoming_environments: Option<ProgramRuntimeEnvironments>,
|
||||
/// The epoch of the last rerooting
|
||||
pub latest_root_epoch: Epoch,
|
||||
pub hit_max_limit: bool,
|
||||
}
|
||||
|
||||
impl LoadedProgramsForTxBatch {
|
||||
pub fn new(slot: Slot, environments: ProgramRuntimeEnvironments) -> Self {
|
||||
pub fn new(
|
||||
slot: Slot,
|
||||
environments: ProgramRuntimeEnvironments,
|
||||
upcoming_environments: Option<ProgramRuntimeEnvironments>,
|
||||
latest_root_epoch: Epoch,
|
||||
) -> Self {
|
||||
Self {
|
||||
entries: HashMap::new(),
|
||||
slot,
|
||||
environments,
|
||||
upcoming_environments,
|
||||
latest_root_epoch,
|
||||
hit_max_limit: false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_from_cache<FG: ForkGraph>(
|
||||
slot: Slot,
|
||||
epoch: Epoch,
|
||||
cache: &ProgramCache<FG>,
|
||||
) -> Self {
|
||||
Self {
|
||||
entries: HashMap::new(),
|
||||
slot,
|
||||
environments: cache.get_environments_for_epoch(epoch).clone(),
|
||||
upcoming_environments: cache.get_upcoming_environments_for_epoch(epoch),
|
||||
latest_root_epoch: cache.latest_root_epoch,
|
||||
hit_max_limit: false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the current environments depending on the given epoch
|
||||
pub fn get_environments_for_epoch(&self, epoch: Epoch) -> &ProgramRuntimeEnvironments {
|
||||
if epoch != self.latest_root_epoch {
|
||||
if let Some(upcoming_environments) = self.upcoming_environments.as_ref() {
|
||||
return upcoming_environments;
|
||||
}
|
||||
}
|
||||
&self.environments
|
||||
}
|
||||
|
||||
/// Refill the cache with a single entry. It's typically called during transaction loading, and
|
||||
/// transaction processing (for program management instructions).
|
||||
/// It replaces the existing entry (if any) with the provided entry. The return value contains
|
||||
|
@ -710,6 +752,17 @@ impl<FG: ForkGraph> ProgramCache<FG> {
|
|||
&self.environments
|
||||
}
|
||||
|
||||
/// Returns the upcoming environments depending on the given epoch
|
||||
pub fn get_upcoming_environments_for_epoch(
|
||||
&self,
|
||||
epoch: Epoch,
|
||||
) -> Option<ProgramRuntimeEnvironments> {
|
||||
if epoch == self.latest_root_epoch {
|
||||
return self.upcoming_environments.clone();
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
/// Insert a single entry. It's typically called during transaction loading,
|
||||
/// when the cache doesn't contain the entry corresponding to program `key`.
|
||||
pub fn assign_program(&mut self, key: Pubkey, entry: Arc<LoadedProgram>) -> bool {
|
||||
|
@ -2057,7 +2110,7 @@ mod tests {
|
|||
(program3, (LoadedProgramMatchCriteria::NoCriteria, 3)),
|
||||
(program4, (LoadedProgramMatchCriteria::NoCriteria, 4)),
|
||||
];
|
||||
let mut extracted = LoadedProgramsForTxBatch::new(22, cache.environments.clone());
|
||||
let mut extracted = LoadedProgramsForTxBatch::new(22, cache.environments.clone(), None, 0);
|
||||
cache.extract(&mut missing, &mut extracted, true);
|
||||
|
||||
assert!(match_slot(&extracted, &program1, 20, 22));
|
||||
|
@ -2073,7 +2126,7 @@ mod tests {
|
|||
(program3, (LoadedProgramMatchCriteria::NoCriteria, 1)),
|
||||
(program4, (LoadedProgramMatchCriteria::NoCriteria, 1)),
|
||||
];
|
||||
let mut extracted = LoadedProgramsForTxBatch::new(15, cache.environments.clone());
|
||||
let mut extracted = LoadedProgramsForTxBatch::new(15, cache.environments.clone(), None, 0);
|
||||
cache.extract(&mut missing, &mut extracted, true);
|
||||
|
||||
assert!(match_slot(&extracted, &program1, 0, 15));
|
||||
|
@ -2096,7 +2149,7 @@ mod tests {
|
|||
(program3, (LoadedProgramMatchCriteria::NoCriteria, 1)),
|
||||
(program4, (LoadedProgramMatchCriteria::NoCriteria, 1)),
|
||||
];
|
||||
let mut extracted = LoadedProgramsForTxBatch::new(18, cache.environments.clone());
|
||||
let mut extracted = LoadedProgramsForTxBatch::new(18, cache.environments.clone(), None, 0);
|
||||
cache.extract(&mut missing, &mut extracted, true);
|
||||
|
||||
assert!(match_slot(&extracted, &program1, 0, 18));
|
||||
|
@ -2114,7 +2167,7 @@ mod tests {
|
|||
(program3, (LoadedProgramMatchCriteria::NoCriteria, 1)),
|
||||
(program4, (LoadedProgramMatchCriteria::NoCriteria, 1)),
|
||||
];
|
||||
let mut extracted = LoadedProgramsForTxBatch::new(23, cache.environments.clone());
|
||||
let mut extracted = LoadedProgramsForTxBatch::new(23, cache.environments.clone(), None, 0);
|
||||
cache.extract(&mut missing, &mut extracted, true);
|
||||
|
||||
assert!(match_slot(&extracted, &program1, 0, 23));
|
||||
|
@ -2132,7 +2185,7 @@ mod tests {
|
|||
(program3, (LoadedProgramMatchCriteria::NoCriteria, 1)),
|
||||
(program4, (LoadedProgramMatchCriteria::NoCriteria, 1)),
|
||||
];
|
||||
let mut extracted = LoadedProgramsForTxBatch::new(11, cache.environments.clone());
|
||||
let mut extracted = LoadedProgramsForTxBatch::new(11, cache.environments.clone(), None, 0);
|
||||
cache.extract(&mut missing, &mut extracted, true);
|
||||
|
||||
assert!(match_slot(&extracted, &program1, 0, 11));
|
||||
|
@ -2170,7 +2223,7 @@ mod tests {
|
|||
(program3, (LoadedProgramMatchCriteria::NoCriteria, 1)),
|
||||
(program4, (LoadedProgramMatchCriteria::NoCriteria, 1)),
|
||||
];
|
||||
let mut extracted = LoadedProgramsForTxBatch::new(21, cache.environments.clone());
|
||||
let mut extracted = LoadedProgramsForTxBatch::new(21, cache.environments.clone(), None, 0);
|
||||
cache.extract(&mut missing, &mut extracted, true);
|
||||
|
||||
// Since the fork was pruned, we should not find the entry deployed at slot 20.
|
||||
|
@ -2187,7 +2240,7 @@ mod tests {
|
|||
(program3, (LoadedProgramMatchCriteria::NoCriteria, 1)),
|
||||
(program4, (LoadedProgramMatchCriteria::NoCriteria, 1)),
|
||||
];
|
||||
let mut extracted = LoadedProgramsForTxBatch::new(27, cache.environments.clone());
|
||||
let mut extracted = LoadedProgramsForTxBatch::new(27, cache.environments.clone(), None, 0);
|
||||
cache.extract(&mut missing, &mut extracted, true);
|
||||
|
||||
assert!(match_slot(&extracted, &program1, 0, 27));
|
||||
|
@ -2219,7 +2272,7 @@ mod tests {
|
|||
(program3, (LoadedProgramMatchCriteria::NoCriteria, 1)),
|
||||
(program4, (LoadedProgramMatchCriteria::NoCriteria, 1)),
|
||||
];
|
||||
let mut extracted = LoadedProgramsForTxBatch::new(23, cache.environments.clone());
|
||||
let mut extracted = LoadedProgramsForTxBatch::new(23, cache.environments.clone(), None, 0);
|
||||
cache.extract(&mut missing, &mut extracted, true);
|
||||
|
||||
assert!(match_slot(&extracted, &program1, 0, 23));
|
||||
|
@ -2274,7 +2327,7 @@ mod tests {
|
|||
(program2, (LoadedProgramMatchCriteria::NoCriteria, 1)),
|
||||
(program3, (LoadedProgramMatchCriteria::NoCriteria, 1)),
|
||||
];
|
||||
let mut extracted = LoadedProgramsForTxBatch::new(12, cache.environments.clone());
|
||||
let mut extracted = LoadedProgramsForTxBatch::new(12, cache.environments.clone(), None, 0);
|
||||
cache.extract(&mut missing, &mut extracted, true);
|
||||
|
||||
assert!(match_slot(&extracted, &program1, 0, 12));
|
||||
|
@ -2294,7 +2347,7 @@ mod tests {
|
|||
),
|
||||
(program3, (LoadedProgramMatchCriteria::NoCriteria, 1)),
|
||||
];
|
||||
let mut extracted = LoadedProgramsForTxBatch::new(12, cache.environments.clone());
|
||||
let mut extracted = LoadedProgramsForTxBatch::new(12, cache.environments.clone(), None, 0);
|
||||
cache.extract(&mut missing, &mut extracted, true);
|
||||
|
||||
assert!(match_slot(&extracted, &program2, 11, 12));
|
||||
|
@ -2360,7 +2413,7 @@ mod tests {
|
|||
(program2, (LoadedProgramMatchCriteria::NoCriteria, 1)),
|
||||
(program3, (LoadedProgramMatchCriteria::NoCriteria, 1)),
|
||||
];
|
||||
let mut extracted = LoadedProgramsForTxBatch::new(19, cache.environments.clone());
|
||||
let mut extracted = LoadedProgramsForTxBatch::new(19, cache.environments.clone(), None, 0);
|
||||
cache.extract(&mut missing, &mut extracted, true);
|
||||
|
||||
assert!(match_slot(&extracted, &program1, 0, 19));
|
||||
|
@ -2374,7 +2427,7 @@ mod tests {
|
|||
(program2, (LoadedProgramMatchCriteria::NoCriteria, 1)),
|
||||
(program3, (LoadedProgramMatchCriteria::NoCriteria, 1)),
|
||||
];
|
||||
let mut extracted = LoadedProgramsForTxBatch::new(27, cache.environments.clone());
|
||||
let mut extracted = LoadedProgramsForTxBatch::new(27, cache.environments.clone(), None, 0);
|
||||
cache.extract(&mut missing, &mut extracted, true);
|
||||
|
||||
assert!(match_slot(&extracted, &program1, 0, 27));
|
||||
|
@ -2388,7 +2441,7 @@ mod tests {
|
|||
(program2, (LoadedProgramMatchCriteria::NoCriteria, 1)),
|
||||
(program3, (LoadedProgramMatchCriteria::NoCriteria, 1)),
|
||||
];
|
||||
let mut extracted = LoadedProgramsForTxBatch::new(22, cache.environments.clone());
|
||||
let mut extracted = LoadedProgramsForTxBatch::new(22, cache.environments.clone(), None, 0);
|
||||
cache.extract(&mut missing, &mut extracted, true);
|
||||
|
||||
assert!(match_slot(&extracted, &program1, 20, 22));
|
||||
|
@ -2469,7 +2522,7 @@ mod tests {
|
|||
cache.prune(10, 0);
|
||||
|
||||
let mut missing = vec![(program1, (LoadedProgramMatchCriteria::NoCriteria, 1))];
|
||||
let mut extracted = LoadedProgramsForTxBatch::new(20, cache.environments.clone());
|
||||
let mut extracted = LoadedProgramsForTxBatch::new(20, cache.environments.clone(), None, 0);
|
||||
cache.extract(&mut missing, &mut extracted, true);
|
||||
|
||||
// The cache should have the program deployed at slot 0
|
||||
|
@ -2513,7 +2566,7 @@ mod tests {
|
|||
(program1, (LoadedProgramMatchCriteria::NoCriteria, 1)),
|
||||
(program2, (LoadedProgramMatchCriteria::NoCriteria, 1)),
|
||||
];
|
||||
let mut extracted = LoadedProgramsForTxBatch::new(20, cache.environments.clone());
|
||||
let mut extracted = LoadedProgramsForTxBatch::new(20, cache.environments.clone(), None, 0);
|
||||
cache.extract(&mut missing, &mut extracted, true);
|
||||
|
||||
assert!(match_slot(&extracted, &program1, 0, 20));
|
||||
|
@ -2523,7 +2576,7 @@ mod tests {
|
|||
(program1, (LoadedProgramMatchCriteria::NoCriteria, 1)),
|
||||
(program2, (LoadedProgramMatchCriteria::NoCriteria, 1)),
|
||||
];
|
||||
let mut extracted = LoadedProgramsForTxBatch::new(6, cache.environments.clone());
|
||||
let mut extracted = LoadedProgramsForTxBatch::new(6, cache.environments.clone(), None, 0);
|
||||
cache.extract(&mut missing, &mut extracted, true);
|
||||
|
||||
assert!(match_slot(&extracted, &program1, 5, 6));
|
||||
|
@ -2537,7 +2590,7 @@ mod tests {
|
|||
(program1, (LoadedProgramMatchCriteria::NoCriteria, 1)),
|
||||
(program2, (LoadedProgramMatchCriteria::NoCriteria, 1)),
|
||||
];
|
||||
let mut extracted = LoadedProgramsForTxBatch::new(20, cache.environments.clone());
|
||||
let mut extracted = LoadedProgramsForTxBatch::new(20, cache.environments.clone(), None, 0);
|
||||
cache.extract(&mut missing, &mut extracted, true);
|
||||
|
||||
assert!(match_slot(&extracted, &program1, 0, 20));
|
||||
|
@ -2547,7 +2600,7 @@ mod tests {
|
|||
(program1, (LoadedProgramMatchCriteria::NoCriteria, 1)),
|
||||
(program2, (LoadedProgramMatchCriteria::NoCriteria, 1)),
|
||||
];
|
||||
let mut extracted = LoadedProgramsForTxBatch::new(6, cache.environments.clone());
|
||||
let mut extracted = LoadedProgramsForTxBatch::new(6, cache.environments.clone(), None, 0);
|
||||
cache.extract(&mut missing, &mut extracted, true);
|
||||
|
||||
assert!(match_slot(&extracted, &program1, 0, 6));
|
||||
|
@ -2561,7 +2614,7 @@ mod tests {
|
|||
(program1, (LoadedProgramMatchCriteria::NoCriteria, 1)),
|
||||
(program2, (LoadedProgramMatchCriteria::NoCriteria, 1)),
|
||||
];
|
||||
let mut extracted = LoadedProgramsForTxBatch::new(20, cache.environments.clone());
|
||||
let mut extracted = LoadedProgramsForTxBatch::new(20, cache.environments.clone(), None, 0);
|
||||
cache.extract(&mut missing, &mut extracted, true);
|
||||
|
||||
assert!(match_slot(&extracted, &program1, 0, 20));
|
||||
|
|
|
@ -51,7 +51,7 @@ use {
|
|||
rc::Rc,
|
||||
sync::{atomic::Ordering, Arc},
|
||||
},
|
||||
syscalls::create_program_runtime_environment_v1,
|
||||
syscalls::{create_program_runtime_environment_v1, morph_into_deployment_environment_v1},
|
||||
};
|
||||
|
||||
pub const DEFAULT_LOADER_COMPUTE_UNITS: u64 = 570;
|
||||
|
@ -106,11 +106,16 @@ macro_rules! deploy_program {
|
|||
$account_size:expr, $slot:expr, $drop:expr, $new_programdata:expr $(,)?) => {{
|
||||
let mut load_program_metrics = LoadProgramMetrics::default();
|
||||
let mut register_syscalls_time = Measure::start("register_syscalls_time");
|
||||
let deployment_program_runtime_environment = create_program_runtime_environment_v1(
|
||||
&$invoke_context.feature_set,
|
||||
$invoke_context.get_compute_budget(),
|
||||
true, /* deployment */
|
||||
false, /* debugging_features */
|
||||
let deployment_slot: Slot = $slot;
|
||||
let environments = $invoke_context.get_environments_for_slot(
|
||||
deployment_slot.saturating_add(DELAY_VISIBILITY_SLOT_OFFSET)
|
||||
).map_err(|e| {
|
||||
// This will never fail since the epoch schedule is already configured.
|
||||
ic_msg!($invoke_context, "Failed to get runtime environment: {}", e);
|
||||
InstructionError::ProgramEnvironmentSetupFailure
|
||||
})?;
|
||||
let deployment_program_runtime_environment = morph_into_deployment_environment_v1(
|
||||
environments.program_runtime_v1.clone(),
|
||||
).map_err(|e| {
|
||||
ic_msg!($invoke_context, "Failed to register syscalls: {}", e);
|
||||
InstructionError::ProgramEnvironmentSetupFailure
|
||||
|
@ -143,7 +148,7 @@ macro_rules! deploy_program {
|
|||
$loader_key,
|
||||
$account_size,
|
||||
$slot,
|
||||
$invoke_context.programs_modified_by_tx.environments.program_runtime_v1.clone(),
|
||||
environments.program_runtime_v1.clone(),
|
||||
true,
|
||||
)?;
|
||||
if let Some(old_entry) = $invoke_context.find_program_in_cache(&$program_id) {
|
||||
|
@ -1536,6 +1541,7 @@ mod tests {
|
|||
},
|
||||
account_utils::StateMut,
|
||||
clock::Clock,
|
||||
epoch_schedule::EpochSchedule,
|
||||
instruction::{AccountMeta, InstructionError},
|
||||
pubkey::Pubkey,
|
||||
rent::Rent,
|
||||
|
@ -3723,7 +3729,10 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_program_usage_count_on_upgrade() {
|
||||
let transaction_accounts = vec![];
|
||||
let transaction_accounts = vec![(
|
||||
sysvar::epoch_schedule::id(),
|
||||
create_account_for_test(&EpochSchedule::default()),
|
||||
)];
|
||||
with_mock_invoke_context!(invoke_context, transaction_context, transaction_accounts);
|
||||
let program_id = Pubkey::new_unique();
|
||||
let env = Arc::new(BuiltinProgram::new_mock());
|
||||
|
@ -3763,7 +3772,10 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_program_usage_count_on_non_upgrade() {
|
||||
let transaction_accounts = vec![];
|
||||
let transaction_accounts = vec![(
|
||||
sysvar::epoch_schedule::id(),
|
||||
create_account_for_test(&EpochSchedule::default()),
|
||||
)];
|
||||
with_mock_invoke_context!(invoke_context, transaction_context, transaction_accounts);
|
||||
let program_id = Pubkey::new_unique();
|
||||
let env = Arc::new(BuiltinProgram::new_mock());
|
||||
|
|
|
@ -239,6 +239,24 @@ macro_rules! register_feature_gated_function {
|
|||
};
|
||||
}
|
||||
|
||||
pub fn morph_into_deployment_environment_v1(
|
||||
from: Arc<BuiltinProgram<InvokeContext>>,
|
||||
) -> Result<BuiltinProgram<InvokeContext>, Error> {
|
||||
let mut config = *from.get_config();
|
||||
config.reject_broken_elfs = true;
|
||||
|
||||
let mut result = FunctionRegistry::<BuiltinFunction<InvokeContext>>::default();
|
||||
|
||||
for (key, (name, value)) in from.get_function_registry().iter() {
|
||||
// Deployment of programs with sol_alloc_free is disabled. So do not register the syscall.
|
||||
if name != *b"sol_alloc_free_" {
|
||||
result.register_function(key, name, value)?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(BuiltinProgram::new_loader(config, result))
|
||||
}
|
||||
|
||||
pub fn create_program_runtime_environment_v1<'a>(
|
||||
feature_set: &FeatureSet,
|
||||
compute_budget: &ComputeBudget,
|
||||
|
|
|
@ -405,17 +405,21 @@ pub fn process_instruction_deploy(
|
|||
let deployment_slot = state.slot;
|
||||
let effective_slot = deployment_slot.saturating_add(DELAY_VISIBILITY_SLOT_OFFSET);
|
||||
|
||||
let environments = invoke_context
|
||||
.get_environments_for_slot(effective_slot)
|
||||
.map_err(|err| {
|
||||
// This will never fail since the epoch schedule is already configured.
|
||||
ic_logger_msg!(log_collector, "Failed to get runtime environment {}", err);
|
||||
InstructionError::InvalidArgument
|
||||
})?;
|
||||
|
||||
let mut load_program_metrics = LoadProgramMetrics {
|
||||
program_id: buffer.get_key().to_string(),
|
||||
..LoadProgramMetrics::default()
|
||||
};
|
||||
let executor = LoadedProgram::new(
|
||||
&loader_v4::id(),
|
||||
invoke_context
|
||||
.programs_modified_by_tx
|
||||
.environments
|
||||
.program_runtime_v2
|
||||
.clone(),
|
||||
environments.program_runtime_v2.clone(),
|
||||
deployment_slot,
|
||||
effective_slot,
|
||||
programdata,
|
||||
|
|
|
@ -33,10 +33,6 @@
|
|||
//! It offers a high-level API that signs transactions
|
||||
//! on behalf of the caller, and a low-level API for when they have
|
||||
//! already been signed and verified.
|
||||
#[cfg(feature = "dev-context-only-utils")]
|
||||
use solana_accounts_db::accounts_db::{
|
||||
ACCOUNTS_DB_CONFIG_FOR_BENCHMARKS, ACCOUNTS_DB_CONFIG_FOR_TESTING,
|
||||
};
|
||||
#[allow(deprecated)]
|
||||
use solana_sdk::recent_blockhashes_account;
|
||||
pub use solana_sdk::reward_type::RewardType;
|
||||
|
@ -199,6 +195,13 @@ use {
|
|||
time::{Duration, Instant},
|
||||
},
|
||||
};
|
||||
#[cfg(feature = "dev-context-only-utils")]
|
||||
use {
|
||||
solana_accounts_db::accounts_db::{
|
||||
ACCOUNTS_DB_CONFIG_FOR_BENCHMARKS, ACCOUNTS_DB_CONFIG_FOR_TESTING,
|
||||
},
|
||||
solana_program_runtime::loaded_programs::LoadedProgramsForTxBatch,
|
||||
};
|
||||
|
||||
/// params to `verify_accounts_hash`
|
||||
struct VerifyAccountsHashConfig {
|
||||
|
@ -7881,6 +7884,14 @@ impl Bank {
|
|||
pub fn update_accounts_hash_for_tests(&self) -> AccountsHash {
|
||||
self.update_accounts_hash(CalcAccountsHashDataSource::IndexForTests, false, false)
|
||||
}
|
||||
|
||||
pub fn new_program_cache_for_tx_batch_for_slot(&self, slot: Slot) -> LoadedProgramsForTxBatch {
|
||||
LoadedProgramsForTxBatch::new_from_cache(
|
||||
slot,
|
||||
self.epoch_schedule.get_epoch(slot),
|
||||
&self.program_cache.read().unwrap(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// Compute how much an account has changed size. This function is useful when the data size delta
|
||||
|
|
|
@ -13921,3 +13921,115 @@ fn test_check_execution_status_and_charge_fee() {
|
|||
}
|
||||
});
|
||||
}
|
||||
#[test]
|
||||
fn test_deploy_last_epoch_slot() {
|
||||
solana_logger::setup();
|
||||
|
||||
// Bank Setup
|
||||
let (mut genesis_config, mint_keypair) = create_genesis_config(1_000_000 * LAMPORTS_PER_SOL);
|
||||
genesis_config
|
||||
.accounts
|
||||
.remove(&feature_set::reject_callx_r10::id());
|
||||
let mut bank = Bank::new_for_tests(&genesis_config);
|
||||
bank.activate_feature(&feature_set::reject_callx_r10::id());
|
||||
|
||||
// go to the last slot in the epoch
|
||||
let (bank, bank_forks) = bank.wrap_with_bank_forks_for_tests();
|
||||
let slots_in_epoch = bank.epoch_schedule().get_slots_in_epoch(0);
|
||||
let bank = new_bank_from_parent_with_bank_forks(
|
||||
&bank_forks,
|
||||
bank,
|
||||
&Pubkey::default(),
|
||||
slots_in_epoch - 1,
|
||||
);
|
||||
eprintln!("now at slot {} epoch {}", bank.slot(), bank.epoch());
|
||||
|
||||
// deploy a program
|
||||
let payer_keypair = Keypair::new();
|
||||
let program_keypair = Keypair::new();
|
||||
let mut file = File::open("../programs/bpf_loader/test_elfs/out/noop_aligned.so").unwrap();
|
||||
let mut elf = Vec::new();
|
||||
file.read_to_end(&mut elf).unwrap();
|
||||
let min_program_balance =
|
||||
bank.get_minimum_balance_for_rent_exemption(UpgradeableLoaderState::size_of_program());
|
||||
let min_buffer_balance = bank
|
||||
.get_minimum_balance_for_rent_exemption(UpgradeableLoaderState::size_of_buffer(elf.len()));
|
||||
let min_programdata_balance = bank.get_minimum_balance_for_rent_exemption(
|
||||
UpgradeableLoaderState::size_of_programdata(elf.len()),
|
||||
);
|
||||
let buffer_address = Pubkey::new_unique();
|
||||
let (programdata_address, _) = Pubkey::find_program_address(
|
||||
&[program_keypair.pubkey().as_ref()],
|
||||
&bpf_loader_upgradeable::id(),
|
||||
);
|
||||
let upgrade_authority_keypair = Keypair::new();
|
||||
|
||||
let buffer_account = {
|
||||
let mut account = AccountSharedData::new(
|
||||
min_buffer_balance,
|
||||
UpgradeableLoaderState::size_of_buffer(elf.len()),
|
||||
&bpf_loader_upgradeable::id(),
|
||||
);
|
||||
account
|
||||
.set_state(&UpgradeableLoaderState::Buffer {
|
||||
authority_address: Some(upgrade_authority_keypair.pubkey()),
|
||||
})
|
||||
.unwrap();
|
||||
account
|
||||
.data_as_mut_slice()
|
||||
.get_mut(UpgradeableLoaderState::size_of_buffer_metadata()..)
|
||||
.unwrap()
|
||||
.copy_from_slice(&elf);
|
||||
account
|
||||
};
|
||||
|
||||
let payer_base_balance = LAMPORTS_PER_SOL;
|
||||
let deploy_fees = {
|
||||
let fee_calculator = genesis_config.fee_rate_governor.create_fee_calculator();
|
||||
3 * fee_calculator.lamports_per_signature
|
||||
};
|
||||
let min_payer_balance = min_program_balance
|
||||
.saturating_add(min_programdata_balance)
|
||||
.saturating_sub(min_buffer_balance.saturating_add(deploy_fees));
|
||||
bank.store_account(
|
||||
&payer_keypair.pubkey(),
|
||||
&AccountSharedData::new(
|
||||
payer_base_balance.saturating_add(min_payer_balance),
|
||||
0,
|
||||
&system_program::id(),
|
||||
),
|
||||
);
|
||||
bank.store_account(&buffer_address, &buffer_account);
|
||||
bank.store_account(&program_keypair.pubkey(), &AccountSharedData::default());
|
||||
bank.store_account(&programdata_address, &AccountSharedData::default());
|
||||
let message = Message::new(
|
||||
&bpf_loader_upgradeable::deploy_with_max_program_len(
|
||||
&payer_keypair.pubkey(),
|
||||
&program_keypair.pubkey(),
|
||||
&buffer_address,
|
||||
&upgrade_authority_keypair.pubkey(),
|
||||
min_program_balance,
|
||||
elf.len(),
|
||||
)
|
||||
.unwrap(),
|
||||
Some(&payer_keypair.pubkey()),
|
||||
);
|
||||
let signers = &[&payer_keypair, &program_keypair, &upgrade_authority_keypair];
|
||||
let transaction = Transaction::new(signers, message.clone(), bank.last_blockhash());
|
||||
let ret = bank.process_transaction(&transaction);
|
||||
assert!(ret.is_ok(), "ret: {:?}", ret);
|
||||
goto_end_of_slot(bank.clone());
|
||||
|
||||
// go to the first slot in the new epoch
|
||||
let bank =
|
||||
new_bank_from_parent_with_bank_forks(&bank_forks, bank, &Pubkey::default(), slots_in_epoch);
|
||||
eprintln!("now at slot {} epoch {}", bank.slot(), bank.epoch());
|
||||
|
||||
let instruction = Instruction::new_with_bytes(program_keypair.pubkey(), &[], Vec::new());
|
||||
let message = Message::new(&[instruction], Some(&mint_keypair.pubkey()));
|
||||
let binding = mint_keypair.insecure_clone();
|
||||
let signers = vec![&binding];
|
||||
let transaction = Transaction::new(&signers, message, bank.last_blockhash());
|
||||
let result_with_feature_enabled = bank.process_transaction(&transaction);
|
||||
assert_eq!(result_with_feature_enabled, Ok(()));
|
||||
}
|
||||
|
|
|
@ -510,9 +510,10 @@ impl<FG: ForkGraph> TransactionBatchProcessor<FG> {
|
|||
// Initialize our local cache.
|
||||
let is_first_round = loaded_programs_for_txs.is_none();
|
||||
if is_first_round {
|
||||
loaded_programs_for_txs = Some(LoadedProgramsForTxBatch::new(
|
||||
loaded_programs_for_txs = Some(LoadedProgramsForTxBatch::new_from_cache(
|
||||
self.slot,
|
||||
program_cache.get_environments_for_epoch(self.epoch).clone(),
|
||||
self.epoch,
|
||||
&program_cache,
|
||||
));
|
||||
}
|
||||
// Submit our last completed loading task.
|
||||
|
@ -523,9 +524,10 @@ impl<FG: ForkGraph> TransactionBatchProcessor<FG> {
|
|||
// This branch is taken when there is an error in assigning a program to a
|
||||
// cache slot. It is not possible to mock this error for SVM unit
|
||||
// tests purposes.
|
||||
let mut ret = LoadedProgramsForTxBatch::new(
|
||||
let mut ret = LoadedProgramsForTxBatch::new_from_cache(
|
||||
self.slot,
|
||||
program_cache.get_environments_for_epoch(self.epoch).clone(),
|
||||
self.epoch,
|
||||
&program_cache,
|
||||
);
|
||||
ret.hit_max_limit = true;
|
||||
return ret;
|
||||
|
@ -630,6 +632,8 @@ impl<FG: ForkGraph> TransactionBatchProcessor<FG> {
|
|||
let mut programs_modified_by_tx = LoadedProgramsForTxBatch::new(
|
||||
self.slot,
|
||||
programs_loaded_for_tx_batch.environments.clone(),
|
||||
programs_loaded_for_tx_batch.upcoming_environments.clone(),
|
||||
programs_loaded_for_tx_batch.latest_root_epoch,
|
||||
);
|
||||
let mut process_message_time = Measure::start("process_message_time");
|
||||
let process_result = MessageProcessor::process_message(
|
||||
|
|
Loading…
Reference in New Issue