Skip to content

Commit 91b1ee3

Browse files
pgarg66alessandrod
andauthored
Fix: deploy program on last slot of epoch during environment change (pyth-network#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>
1 parent 36c66f5 commit 91b1ee3

File tree

9 files changed

+296
-47
lines changed

9 files changed

+296
-47
lines changed

ledger-tool/src/program.rs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,12 @@ use {
2121
},
2222
solana_runtime::bank::Bank,
2323
solana_sdk::{
24-
account::AccountSharedData,
24+
account::{create_account_shared_data_for_test, AccountSharedData},
2525
account_utils::StateMut,
2626
bpf_loader_upgradeable::{self, UpgradeableLoaderState},
2727
pubkey::Pubkey,
2828
slot_history::Slot,
29+
sysvar,
2930
transaction_context::{IndexOfAccount, InstructionAccount},
3031
},
3132
std::{
@@ -510,13 +511,16 @@ pub fn program(ledger_path: &Path, matches: &ArgMatches<'_>) {
510511
program_id, // ID of the loaded program. It can modify accounts with the same owner key
511512
AccountSharedData::new(0, 0, &loader_id),
512513
));
514+
transaction_accounts.push((
515+
sysvar::epoch_schedule::id(),
516+
create_account_shared_data_for_test(bank.epoch_schedule()),
517+
));
513518
let interpreted = matches.value_of("mode").unwrap() != "jit";
514519
with_mock_invoke_context!(invoke_context, transaction_context, transaction_accounts);
515520

516521
// Adding `DELAY_VISIBILITY_SLOT_OFFSET` to slots to accommodate for delay visibility of the program
517-
let slot = bank.slot() + DELAY_VISIBILITY_SLOT_OFFSET;
518522
let mut loaded_programs =
519-
LoadedProgramsForTxBatch::new(slot, bank.get_runtime_environments_for_slot(slot));
523+
bank.new_program_cache_for_tx_batch_for_slot(bank.slot() + DELAY_VISIBILITY_SLOT_OFFSET);
520524
for key in cached_account_keys {
521525
loaded_programs.replenish(key, bank.load_program(&key, false, bank.epoch()));
522526
debug!("Loaded program {}", key);

program-runtime/src/invoke_context.rs

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@ use {
22
crate::{
33
compute_budget::ComputeBudget,
44
ic_msg,
5-
loaded_programs::{LoadedProgram, LoadedProgramType, LoadedProgramsForTxBatch},
5+
loaded_programs::{
6+
LoadedProgram, LoadedProgramType, LoadedProgramsForTxBatch, ProgramRuntimeEnvironments,
7+
},
68
log_collector::LogCollector,
79
stable_log,
810
sysvar_cache::SysvarCache,
@@ -17,15 +19,18 @@ use {
1719
vm::{Config, ContextObject, EbpfVm},
1820
},
1921
solana_sdk::{
20-
account::AccountSharedData,
22+
account::{create_account_shared_data_for_test, AccountSharedData},
2123
bpf_loader_deprecated,
24+
clock::Slot,
25+
epoch_schedule::EpochSchedule,
2226
feature_set::FeatureSet,
2327
hash::Hash,
2428
instruction::{AccountMeta, InstructionError},
2529
native_loader,
2630
pubkey::Pubkey,
2731
saturating_add_assign,
2832
stable_layout::stable_instruction::StableInstruction,
33+
sysvar,
2934
transaction_context::{
3035
IndexOfAccount, InstructionAccount, TransactionAccount, TransactionContext,
3136
},
@@ -209,6 +214,17 @@ impl<'a> InvokeContext<'a> {
209214
.or_else(|| self.programs_loaded_for_tx_batch.find(pubkey))
210215
}
211216

217+
pub fn get_environments_for_slot(
218+
&self,
219+
effective_slot: Slot,
220+
) -> Result<&ProgramRuntimeEnvironments, InstructionError> {
221+
let epoch_schedule = self.sysvar_cache.get_epoch_schedule()?;
222+
let epoch = epoch_schedule.get_epoch(effective_slot);
223+
Ok(self
224+
.programs_loaded_for_tx_batch
225+
.get_environments_for_epoch(epoch))
226+
}
227+
212228
/// Push a stack frame onto the invocation stack
213229
pub fn push(&mut self) -> Result<(), InstructionError> {
214230
let instruction_context = self
@@ -713,6 +729,18 @@ pub fn mock_process_instruction<F: FnMut(&mut InvokeContext), G: FnMut(&mut Invo
713729
program_indices.insert(0, transaction_accounts.len() as IndexOfAccount);
714730
let processor_account = AccountSharedData::new(0, 0, &native_loader::id());
715731
transaction_accounts.push((*loader_id, processor_account));
732+
let pop_epoch_schedule_account = if !transaction_accounts
733+
.iter()
734+
.any(|(key, _)| *key == sysvar::epoch_schedule::id())
735+
{
736+
transaction_accounts.push((
737+
sysvar::epoch_schedule::id(),
738+
create_account_shared_data_for_test(&EpochSchedule::default()),
739+
));
740+
true
741+
} else {
742+
false
743+
};
716744
with_mock_invoke_context!(invoke_context, transaction_context, transaction_accounts);
717745
let mut programs_loaded_for_tx_batch = LoadedProgramsForTxBatch::default();
718746
programs_loaded_for_tx_batch.replenish(
@@ -731,6 +759,9 @@ pub fn mock_process_instruction<F: FnMut(&mut InvokeContext), G: FnMut(&mut Invo
731759
assert_eq!(result, expected_result);
732760
post_adjustments(&mut invoke_context);
733761
let mut transaction_accounts = transaction_context.deconstruct_without_keys().unwrap();
762+
if pop_epoch_schedule_account {
763+
transaction_accounts.pop();
764+
}
734765
transaction_accounts.pop();
735766
transaction_accounts
736767
}

program-runtime/src/loaded_programs.rs

Lines changed: 73 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -618,19 +618,61 @@ pub struct LoadedProgramsForTxBatch {
618618
entries: HashMap<Pubkey, Arc<LoadedProgram>>,
619619
slot: Slot,
620620
pub environments: ProgramRuntimeEnvironments,
621+
/// Anticipated replacement for `environments` at the next epoch.
622+
///
623+
/// This is `None` during most of an epoch, and only `Some` around the boundaries (at the end and beginning of an epoch).
624+
/// More precisely, it starts with the recompilation phase a few hundred slots before the epoch boundary,
625+
/// and it ends with the first rerooting after the epoch boundary.
626+
/// Needed when a program is deployed at the last slot of an epoch, becomes effective in the next epoch.
627+
/// So needs to be compiled with the environment for the next epoch.
628+
pub upcoming_environments: Option<ProgramRuntimeEnvironments>,
629+
/// The epoch of the last rerooting
630+
pub latest_root_epoch: Epoch,
621631
pub hit_max_limit: bool,
622632
}
623633

624634
impl LoadedProgramsForTxBatch {
625-
pub fn new(slot: Slot, environments: ProgramRuntimeEnvironments) -> Self {
635+
pub fn new(
636+
slot: Slot,
637+
environments: ProgramRuntimeEnvironments,
638+
upcoming_environments: Option<ProgramRuntimeEnvironments>,
639+
latest_root_epoch: Epoch,
640+
) -> Self {
626641
Self {
627642
entries: HashMap::new(),
628643
slot,
629644
environments,
645+
upcoming_environments,
646+
latest_root_epoch,
630647
hit_max_limit: false,
631648
}
632649
}
633650

651+
pub fn new_from_cache<FG: ForkGraph>(
652+
slot: Slot,
653+
epoch: Epoch,
654+
cache: &ProgramCache<FG>,
655+
) -> Self {
656+
Self {
657+
entries: HashMap::new(),
658+
slot,
659+
environments: cache.get_environments_for_epoch(epoch).clone(),
660+
upcoming_environments: cache.get_upcoming_environments_for_epoch(epoch),
661+
latest_root_epoch: cache.latest_root_epoch,
662+
hit_max_limit: false,
663+
}
664+
}
665+
666+
/// Returns the current environments depending on the given epoch
667+
pub fn get_environments_for_epoch(&self, epoch: Epoch) -> &ProgramRuntimeEnvironments {
668+
if epoch != self.latest_root_epoch {
669+
if let Some(upcoming_environments) = self.upcoming_environments.as_ref() {
670+
return upcoming_environments;
671+
}
672+
}
673+
&self.environments
674+
}
675+
634676
/// Refill the cache with a single entry. It's typically called during transaction loading, and
635677
/// transaction processing (for program management instructions).
636678
/// It replaces the existing entry (if any) with the provided entry. The return value contains
@@ -710,6 +752,17 @@ impl<FG: ForkGraph> ProgramCache<FG> {
710752
&self.environments
711753
}
712754

755+
/// Returns the upcoming environments depending on the given epoch
756+
pub fn get_upcoming_environments_for_epoch(
757+
&self,
758+
epoch: Epoch,
759+
) -> Option<ProgramRuntimeEnvironments> {
760+
if epoch == self.latest_root_epoch {
761+
return self.upcoming_environments.clone();
762+
}
763+
None
764+
}
765+
713766
/// Insert a single entry. It's typically called during transaction loading,
714767
/// when the cache doesn't contain the entry corresponding to program `key`.
715768
pub fn assign_program(&mut self, key: Pubkey, entry: Arc<LoadedProgram>) -> bool {
@@ -2057,7 +2110,7 @@ mod tests {
20572110
(program3, (LoadedProgramMatchCriteria::NoCriteria, 3)),
20582111
(program4, (LoadedProgramMatchCriteria::NoCriteria, 4)),
20592112
];
2060-
let mut extracted = LoadedProgramsForTxBatch::new(22, cache.environments.clone());
2113+
let mut extracted = LoadedProgramsForTxBatch::new(22, cache.environments.clone(), None, 0);
20612114
cache.extract(&mut missing, &mut extracted, true);
20622115

20632116
assert!(match_slot(&extracted, &program1, 20, 22));
@@ -2073,7 +2126,7 @@ mod tests {
20732126
(program3, (LoadedProgramMatchCriteria::NoCriteria, 1)),
20742127
(program4, (LoadedProgramMatchCriteria::NoCriteria, 1)),
20752128
];
2076-
let mut extracted = LoadedProgramsForTxBatch::new(15, cache.environments.clone());
2129+
let mut extracted = LoadedProgramsForTxBatch::new(15, cache.environments.clone(), None, 0);
20772130
cache.extract(&mut missing, &mut extracted, true);
20782131

20792132
assert!(match_slot(&extracted, &program1, 0, 15));
@@ -2096,7 +2149,7 @@ mod tests {
20962149
(program3, (LoadedProgramMatchCriteria::NoCriteria, 1)),
20972150
(program4, (LoadedProgramMatchCriteria::NoCriteria, 1)),
20982151
];
2099-
let mut extracted = LoadedProgramsForTxBatch::new(18, cache.environments.clone());
2152+
let mut extracted = LoadedProgramsForTxBatch::new(18, cache.environments.clone(), None, 0);
21002153
cache.extract(&mut missing, &mut extracted, true);
21012154

21022155
assert!(match_slot(&extracted, &program1, 0, 18));
@@ -2114,7 +2167,7 @@ mod tests {
21142167
(program3, (LoadedProgramMatchCriteria::NoCriteria, 1)),
21152168
(program4, (LoadedProgramMatchCriteria::NoCriteria, 1)),
21162169
];
2117-
let mut extracted = LoadedProgramsForTxBatch::new(23, cache.environments.clone());
2170+
let mut extracted = LoadedProgramsForTxBatch::new(23, cache.environments.clone(), None, 0);
21182171
cache.extract(&mut missing, &mut extracted, true);
21192172

21202173
assert!(match_slot(&extracted, &program1, 0, 23));
@@ -2132,7 +2185,7 @@ mod tests {
21322185
(program3, (LoadedProgramMatchCriteria::NoCriteria, 1)),
21332186
(program4, (LoadedProgramMatchCriteria::NoCriteria, 1)),
21342187
];
2135-
let mut extracted = LoadedProgramsForTxBatch::new(11, cache.environments.clone());
2188+
let mut extracted = LoadedProgramsForTxBatch::new(11, cache.environments.clone(), None, 0);
21362189
cache.extract(&mut missing, &mut extracted, true);
21372190

21382191
assert!(match_slot(&extracted, &program1, 0, 11));
@@ -2170,7 +2223,7 @@ mod tests {
21702223
(program3, (LoadedProgramMatchCriteria::NoCriteria, 1)),
21712224
(program4, (LoadedProgramMatchCriteria::NoCriteria, 1)),
21722225
];
2173-
let mut extracted = LoadedProgramsForTxBatch::new(21, cache.environments.clone());
2226+
let mut extracted = LoadedProgramsForTxBatch::new(21, cache.environments.clone(), None, 0);
21742227
cache.extract(&mut missing, &mut extracted, true);
21752228

21762229
// Since the fork was pruned, we should not find the entry deployed at slot 20.
@@ -2187,7 +2240,7 @@ mod tests {
21872240
(program3, (LoadedProgramMatchCriteria::NoCriteria, 1)),
21882241
(program4, (LoadedProgramMatchCriteria::NoCriteria, 1)),
21892242
];
2190-
let mut extracted = LoadedProgramsForTxBatch::new(27, cache.environments.clone());
2243+
let mut extracted = LoadedProgramsForTxBatch::new(27, cache.environments.clone(), None, 0);
21912244
cache.extract(&mut missing, &mut extracted, true);
21922245

21932246
assert!(match_slot(&extracted, &program1, 0, 27));
@@ -2219,7 +2272,7 @@ mod tests {
22192272
(program3, (LoadedProgramMatchCriteria::NoCriteria, 1)),
22202273
(program4, (LoadedProgramMatchCriteria::NoCriteria, 1)),
22212274
];
2222-
let mut extracted = LoadedProgramsForTxBatch::new(23, cache.environments.clone());
2275+
let mut extracted = LoadedProgramsForTxBatch::new(23, cache.environments.clone(), None, 0);
22232276
cache.extract(&mut missing, &mut extracted, true);
22242277

22252278
assert!(match_slot(&extracted, &program1, 0, 23));
@@ -2274,7 +2327,7 @@ mod tests {
22742327
(program2, (LoadedProgramMatchCriteria::NoCriteria, 1)),
22752328
(program3, (LoadedProgramMatchCriteria::NoCriteria, 1)),
22762329
];
2277-
let mut extracted = LoadedProgramsForTxBatch::new(12, cache.environments.clone());
2330+
let mut extracted = LoadedProgramsForTxBatch::new(12, cache.environments.clone(), None, 0);
22782331
cache.extract(&mut missing, &mut extracted, true);
22792332

22802333
assert!(match_slot(&extracted, &program1, 0, 12));
@@ -2294,7 +2347,7 @@ mod tests {
22942347
),
22952348
(program3, (LoadedProgramMatchCriteria::NoCriteria, 1)),
22962349
];
2297-
let mut extracted = LoadedProgramsForTxBatch::new(12, cache.environments.clone());
2350+
let mut extracted = LoadedProgramsForTxBatch::new(12, cache.environments.clone(), None, 0);
22982351
cache.extract(&mut missing, &mut extracted, true);
22992352

23002353
assert!(match_slot(&extracted, &program2, 11, 12));
@@ -2360,7 +2413,7 @@ mod tests {
23602413
(program2, (LoadedProgramMatchCriteria::NoCriteria, 1)),
23612414
(program3, (LoadedProgramMatchCriteria::NoCriteria, 1)),
23622415
];
2363-
let mut extracted = LoadedProgramsForTxBatch::new(19, cache.environments.clone());
2416+
let mut extracted = LoadedProgramsForTxBatch::new(19, cache.environments.clone(), None, 0);
23642417
cache.extract(&mut missing, &mut extracted, true);
23652418

23662419
assert!(match_slot(&extracted, &program1, 0, 19));
@@ -2374,7 +2427,7 @@ mod tests {
23742427
(program2, (LoadedProgramMatchCriteria::NoCriteria, 1)),
23752428
(program3, (LoadedProgramMatchCriteria::NoCriteria, 1)),
23762429
];
2377-
let mut extracted = LoadedProgramsForTxBatch::new(27, cache.environments.clone());
2430+
let mut extracted = LoadedProgramsForTxBatch::new(27, cache.environments.clone(), None, 0);
23782431
cache.extract(&mut missing, &mut extracted, true);
23792432

23802433
assert!(match_slot(&extracted, &program1, 0, 27));
@@ -2388,7 +2441,7 @@ mod tests {
23882441
(program2, (LoadedProgramMatchCriteria::NoCriteria, 1)),
23892442
(program3, (LoadedProgramMatchCriteria::NoCriteria, 1)),
23902443
];
2391-
let mut extracted = LoadedProgramsForTxBatch::new(22, cache.environments.clone());
2444+
let mut extracted = LoadedProgramsForTxBatch::new(22, cache.environments.clone(), None, 0);
23922445
cache.extract(&mut missing, &mut extracted, true);
23932446

23942447
assert!(match_slot(&extracted, &program1, 20, 22));
@@ -2469,7 +2522,7 @@ mod tests {
24692522
cache.prune(10, 0);
24702523

24712524
let mut missing = vec![(program1, (LoadedProgramMatchCriteria::NoCriteria, 1))];
2472-
let mut extracted = LoadedProgramsForTxBatch::new(20, cache.environments.clone());
2525+
let mut extracted = LoadedProgramsForTxBatch::new(20, cache.environments.clone(), None, 0);
24732526
cache.extract(&mut missing, &mut extracted, true);
24742527

24752528
// The cache should have the program deployed at slot 0
@@ -2513,7 +2566,7 @@ mod tests {
25132566
(program1, (LoadedProgramMatchCriteria::NoCriteria, 1)),
25142567
(program2, (LoadedProgramMatchCriteria::NoCriteria, 1)),
25152568
];
2516-
let mut extracted = LoadedProgramsForTxBatch::new(20, cache.environments.clone());
2569+
let mut extracted = LoadedProgramsForTxBatch::new(20, cache.environments.clone(), None, 0);
25172570
cache.extract(&mut missing, &mut extracted, true);
25182571

25192572
assert!(match_slot(&extracted, &program1, 0, 20));
@@ -2523,7 +2576,7 @@ mod tests {
25232576
(program1, (LoadedProgramMatchCriteria::NoCriteria, 1)),
25242577
(program2, (LoadedProgramMatchCriteria::NoCriteria, 1)),
25252578
];
2526-
let mut extracted = LoadedProgramsForTxBatch::new(6, cache.environments.clone());
2579+
let mut extracted = LoadedProgramsForTxBatch::new(6, cache.environments.clone(), None, 0);
25272580
cache.extract(&mut missing, &mut extracted, true);
25282581

25292582
assert!(match_slot(&extracted, &program1, 5, 6));
@@ -2537,7 +2590,7 @@ mod tests {
25372590
(program1, (LoadedProgramMatchCriteria::NoCriteria, 1)),
25382591
(program2, (LoadedProgramMatchCriteria::NoCriteria, 1)),
25392592
];
2540-
let mut extracted = LoadedProgramsForTxBatch::new(20, cache.environments.clone());
2593+
let mut extracted = LoadedProgramsForTxBatch::new(20, cache.environments.clone(), None, 0);
25412594
cache.extract(&mut missing, &mut extracted, true);
25422595

25432596
assert!(match_slot(&extracted, &program1, 0, 20));
@@ -2547,7 +2600,7 @@ mod tests {
25472600
(program1, (LoadedProgramMatchCriteria::NoCriteria, 1)),
25482601
(program2, (LoadedProgramMatchCriteria::NoCriteria, 1)),
25492602
];
2550-
let mut extracted = LoadedProgramsForTxBatch::new(6, cache.environments.clone());
2603+
let mut extracted = LoadedProgramsForTxBatch::new(6, cache.environments.clone(), None, 0);
25512604
cache.extract(&mut missing, &mut extracted, true);
25522605

25532606
assert!(match_slot(&extracted, &program1, 0, 6));
@@ -2561,7 +2614,7 @@ mod tests {
25612614
(program1, (LoadedProgramMatchCriteria::NoCriteria, 1)),
25622615
(program2, (LoadedProgramMatchCriteria::NoCriteria, 1)),
25632616
];
2564-
let mut extracted = LoadedProgramsForTxBatch::new(20, cache.environments.clone());
2617+
let mut extracted = LoadedProgramsForTxBatch::new(20, cache.environments.clone(), None, 0);
25652618
cache.extract(&mut missing, &mut extracted, true);
25662619

25672620
assert!(match_slot(&extracted, &program1, 0, 20));

0 commit comments

Comments
 (0)