Skip to content

Commit

Permalink
Simd 47 syscall sysvar last restart slot (#31957)
Browse files Browse the repository at this point in the history
* add sysvar and logic for last restart slot

* cleanup

* add test for getting last restart slot from account

* format code

* add some basic rustdoc

* copy+paste error

* feature flag for last_restart_slot

* add to sysvars.md

* updated wording in sysvars.md

* rename sol_get_last_restart_slot_sysvar > sol_get_last_restart_slot

* create sbf C header for sol_get_last_restart_slot

* cleanup imports

* reverted hardened_unpack workaround

* cleanup imports

* cleanup logs + blank lines

* Implementing ui changes for last restart slot, nit

* Some more nit change and implementing the UI for sysvar

* fixing the CI

* Minor clippy fix

* format changes

* changes suggested by mvines and lichtso

* increase timeout in local_cluster test

* fix code format

* use keypair for feature flag from mvines

* delete test.json file

* Revert "increase timeout in local_cluster test"

This reverts commit a67465ae225186be5ebaa46badfe44ecac6d76a8.

* last restart slot should be always less than or equal to current slot

* fixing bug

* changes after  steviez comments

* format issue fixed

* fixing the comment on premature application of future hardfork

* nit change in test

Co-authored-by: steviez <[email protected]>

* reverting sysvar_cache.rs because change was not necessary

---------

Co-authored-by: steve-gg <[email protected]>
Co-authored-by: steviez <[email protected]>
  • Loading branch information
3 people authored Jun 16, 2023
1 parent 987e8ee commit 2ceabd9
Show file tree
Hide file tree
Showing 17 changed files with 393 additions and 9 deletions.
31 changes: 30 additions & 1 deletion account-decoder/src/parse_sysvar.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ use {
slot_hashes::SlotHashes,
slot_history::{self, SlotHistory},
stake_history::{StakeHistory, StakeHistoryEntry},
sysvar::{self, rewards::Rewards},
sysvar::{self, last_restart_slot::LastRestartSlot, rewards::Rewards},
},
};

Expand Down Expand Up @@ -82,6 +82,13 @@ pub fn parse_sysvar(data: &[u8], pubkey: &Pubkey) -> Result<SysvarAccountType, P
.collect();
SysvarAccountType::StakeHistory(stake_history)
})
} else if pubkey == &sysvar::last_restart_slot::id() {
deserialize::<LastRestartSlot>(data)
.ok()
.map(|last_restart_slot| {
let last_restart_slot = last_restart_slot.last_restart_slot;
SysvarAccountType::LastRestartSlot(UiLastRestartSlot { last_restart_slot })
})
} else {
None
}
Expand All @@ -105,6 +112,7 @@ pub enum SysvarAccountType {
SlotHashes(Vec<UiSlotHashEntry>),
SlotHistory(UiSlotHistory),
StakeHistory(Vec<UiStakeHistoryEntry>),
LastRestartSlot(UiLastRestartSlot),
}

#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Default)]
Expand Down Expand Up @@ -218,6 +226,12 @@ pub struct UiStakeHistoryEntry {
pub stake_history: StakeHistoryEntry,
}

#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Default)]
#[serde(rename_all = "camelCase")]
pub struct UiLastRestartSlot {
pub last_restart_slot: Slot,
}

#[cfg(test)]
mod test {
#[allow(deprecated)]
Expand Down Expand Up @@ -334,5 +348,20 @@ mod test {

let bad_data = vec![0; 4];
assert!(parse_sysvar(&bad_data, &sysvar::stake_history::id()).is_err());

let last_restart_slot = LastRestartSlot {
last_restart_slot: 1282,
};
let last_restart_slot_account = create_account_for_test(&last_restart_slot);
assert_eq!(
parse_sysvar(
&last_restart_slot_account.data,
&sysvar::last_restart_slot::id()
)
.unwrap(),
SysvarAccountType::LastRestartSlot(UiLastRestartSlot {
last_restart_slot: 1282
})
);
}
}
10 changes: 9 additions & 1 deletion docs/src/developing/runtime-facilities/sysvars.md
Original file line number Diff line number Diff line change
Expand Up @@ -155,4 +155,12 @@ determining whether epoch rewards distribution has finished.

- Address: `SysvarEpochRewards1111111111111111111111111`
- Layout:
[EpochRewards](https://docs.rs/solana-program/VERSION_FOR_DOCS_RS/solana_program/epoch_rewards/struct.EpochRewards.html)
[EpochRewards](https://docs.rs/solana-program/VERSION_FOR_DOCS_RS/solana_program/epoch_rewards/struct.EpochRewards.html)

## LastRestartSlot

The LastRestartSlot sysvar contains the slot number of the last restart or _0_ (zero) if none ever happened.

- Address: `SysvarLastRestartS1ot1111111111111111111111`
- Layout:
[LastRestartSlot](https://docs.rs/solana-program/VERSION_FOR_DOCS_RS/solana_program/last_restart_slot/struct.LastRestartSlot.html)
35 changes: 34 additions & 1 deletion program-runtime/src/sysvar_cache.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
#[allow(deprecated)]
use solana_sdk::sysvar::{fees::Fees, recent_blockhashes::RecentBlockhashes};
use solana_sdk::sysvar::{
fees::Fees, last_restart_slot::LastRestartSlot, recent_blockhashes::RecentBlockhashes,
};
use {
crate::invoke_context::InvokeContext,
solana_sdk::{
Expand Down Expand Up @@ -33,6 +35,7 @@ pub struct SysvarCache {
#[allow(deprecated)]
recent_blockhashes: Option<Arc<RecentBlockhashes>>,
stake_history: Option<Arc<StakeHistory>>,
last_restart_slot: Option<Arc<LastRestartSlot>>,
}

impl SysvarCache {
Expand Down Expand Up @@ -76,6 +79,16 @@ impl SysvarCache {
self.rent = Some(Arc::new(rent));
}

pub fn get_last_restart_slot(&self) -> Result<Arc<LastRestartSlot>, InstructionError> {
self.last_restart_slot
.clone()
.ok_or(InstructionError::UnsupportedSysvar)
}

pub fn set_last_restart_slot(&mut self, last_restart_slot: LastRestartSlot) {
self.last_restart_slot = Some(Arc::new(last_restart_slot));
}

pub fn get_slot_hashes(&self) -> Result<Arc<SlotHashes>, InstructionError> {
self.slot_hashes
.clone()
Expand Down Expand Up @@ -165,6 +178,13 @@ impl SysvarCache {
}
});
}
if self.last_restart_slot.is_none() {
get_account_data(&LastRestartSlot::id(), &mut |data: &[u8]| {
if let Ok(last_restart_slot) = bincode::deserialize(data) {
self.set_last_restart_slot(last_restart_slot);
}
});
}
}

pub fn reset(&mut self) {
Expand Down Expand Up @@ -258,4 +278,17 @@ pub mod get_sysvar_with_account_check {
)?;
invoke_context.get_sysvar_cache().get_stake_history()
}

pub fn last_restart_slot(
invoke_context: &InvokeContext,
instruction_context: &InstructionContext,
instruction_account_index: IndexOfAccount,
) -> Result<Arc<LastRestartSlot>, InstructionError> {
check_sysvar_account::<LastRestartSlot>(
invoke_context.transaction_context,
instruction_context,
instruction_account_index,
)?;
invoke_context.get_sysvar_cache().get_last_restart_slot()
}
}
17 changes: 17 additions & 0 deletions program-test/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -371,6 +371,15 @@ impl solana_sdk::program_stubs::SyscallStubs for SyscallStubs {
get_sysvar(get_invoke_context().get_sysvar_cache().get_rent(), var_addr)
}

fn sol_get_last_restart_slot(&self, var_addr: *mut u8) -> u64 {
get_sysvar(
get_invoke_context()
.get_sysvar_cache()
.get_last_restart_slot(),
var_addr,
)
}

fn sol_get_return_data(&self) -> Option<(Pubkey, Vec<u8>)> {
let (program_id, data) = get_invoke_context().transaction_context.get_return_data();
Some((*program_id, data.to_vec()))
Expand Down Expand Up @@ -1162,4 +1171,12 @@ impl ProgramTestContext {
self.last_blockhash = blockhash;
Ok(blockhash)
}

/// record a hard fork slot in working bank; should be in the past
pub fn register_hard_fork(&mut self, hard_fork_slot: Slot) {
let bank_forks = self.bank_forks.write().unwrap();
let hard_forks = bank_forks.working_bank().hard_forks();
let mut write = hard_forks.write().unwrap();
write.register(hard_fork_slot);
}
}
104 changes: 104 additions & 0 deletions program-test/tests/sysvar_last_restart_slot.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
use {
solana_program_test::{processor, ProgramTest, ProgramTestContext},
solana_sdk::{
account_info::AccountInfo,
clock::Slot,
entrypoint::ProgramResult,
instruction::{AccountMeta, Instruction},
msg,
pubkey::Pubkey,
signature::Signer,
sysvar::{last_restart_slot, last_restart_slot::LastRestartSlot, Sysvar},
transaction::Transaction,
},
};

// program to check both syscall and sysvar
fn sysvar_last_restart_slot_process_instruction(
_program_id: &Pubkey,
accounts: &[AccountInfo],
input: &[u8],
) -> ProgramResult {
msg!("sysvar_last_restart_slot");
assert_eq!(input.len(), 8);
let expected_last_hardfork_slot = u64::from_le_bytes(input[0..8].try_into().unwrap());

let last_restart_slot = LastRestartSlot::get();
msg!("last restart slot: {:?}", last_restart_slot);
assert_eq!(
last_restart_slot,
Ok(LastRestartSlot {
last_restart_slot: expected_last_hardfork_slot
})
);

let last_restart_slot_account = &accounts[0];
let slot_via_account = LastRestartSlot::from_account_info(last_restart_slot_account)?;
msg!("slot via account: {:?}", slot_via_account);

assert_eq!(
slot_via_account,
LastRestartSlot {
last_restart_slot: expected_last_hardfork_slot
}
);

Ok(())
}

async fn check_with_program(
context: &mut ProgramTestContext,
program_id: Pubkey,
expected_last_restart_slot: u64,
) {
let instructions = vec![Instruction::new_with_bincode(
program_id,
&expected_last_restart_slot.to_le_bytes(),
vec![AccountMeta::new(last_restart_slot::id(), false)],
)];

let transaction = Transaction::new_signed_with_payer(
&instructions,
Some(&context.payer.pubkey()),
&[&context.payer],
context.last_blockhash,
);

context
.banks_client
.process_transaction(transaction)
.await
.unwrap();
}

#[tokio::test]
async fn get_sysvar_last_restart_slot() {
let program_id = Pubkey::new_unique();
let program_test = ProgramTest::new(
"sysvar_last_restart_slot_process",
program_id,
processor!(sysvar_last_restart_slot_process_instruction),
);

let mut context = program_test.start_with_context().await;

check_with_program(&mut context, program_id, 0).await;
context.warp_to_slot(40).unwrap();
context.register_hard_fork(41 as Slot);
check_with_program(&mut context, program_id, 0).await;
context.warp_to_slot(41).unwrap();
check_with_program(&mut context, program_id, 41).await;
// check for value lower than previous hardfork
context.register_hard_fork(40 as Slot);
context.warp_to_slot(45).unwrap();
check_with_program(&mut context, program_id, 41).await;
context.register_hard_fork(43 as Slot);
context.register_hard_fork(47 as Slot);
context.warp_to_slot(46).unwrap();
check_with_program(&mut context, program_id, 43).await;
context.register_hard_fork(50 as Slot);
context.warp_to_slot(48).unwrap();
check_with_program(&mut context, program_id, 47).await;
context.warp_to_slot(50).unwrap();
check_with_program(&mut context, program_id, 50).await;
}
17 changes: 13 additions & 4 deletions programs/bpf_loader/src/syscalls/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ pub use self::{
mem_ops::{SyscallMemcmp, SyscallMemcpy, SyscallMemmove, SyscallMemset},
sysvar::{
SyscallGetClockSysvar, SyscallGetEpochScheduleSysvar, SyscallGetFeesSysvar,
SyscallGetRentSysvar,
SyscallGetLastRestartSlotSysvar, SyscallGetRentSysvar,
},
};
#[allow(deprecated)]
Expand Down Expand Up @@ -37,9 +37,10 @@ use {
disable_cpi_setting_executable_and_rent_epoch, disable_deploy_of_alloc_free_syscall,
disable_fees_sysvar, enable_alt_bn128_syscall, enable_big_mod_exp_syscall,
enable_early_verification_of_account_modifications,
error_on_syscall_bpf_function_hash_collisions, libsecp256k1_0_5_upgrade_enabled,
reject_callx_r10, stop_sibling_instruction_search_at_parent,
stop_truncating_strings_in_syscalls, switch_to_new_elf_parser,
error_on_syscall_bpf_function_hash_collisions, last_restart_slot_sysvar,
libsecp256k1_0_5_upgrade_enabled, reject_callx_r10,
stop_sibling_instruction_search_at_parent, stop_truncating_strings_in_syscalls,
switch_to_new_elf_parser,
},
hash::{Hasher, HASH_BYTES},
instruction::{
Expand Down Expand Up @@ -187,6 +188,7 @@ pub fn create_program_runtime_environment<'a>(
let disable_fees_sysvar = feature_set.is_active(&disable_fees_sysvar::id());
let disable_deploy_of_alloc_free_syscall = reject_deployment_of_broken_elfs
&& feature_set.is_active(&disable_deploy_of_alloc_free_syscall::id());
let last_restart_slot_syscall_enabled = feature_set.is_active(&last_restart_slot_sysvar::id());

let mut result = BuiltinProgram::new_loader(config);

Expand Down Expand Up @@ -263,6 +265,13 @@ pub fn create_program_runtime_environment<'a>(
)?;
result.register_function(b"sol_get_rent_sysvar", SyscallGetRentSysvar::call)?;

register_feature_gated_function!(
result,
last_restart_slot_syscall_enabled,
b"sol_get_last_restart_slot",
SyscallGetLastRestartSlotSysvar::call,
)?;

// Memory ops
result.register_function(b"sol_memcpy_", SyscallMemcpy::call)?;
result.register_function(b"sol_memmove_", SyscallMemmove::call)?;
Expand Down
22 changes: 22 additions & 0 deletions programs/bpf_loader/src/syscalls/sysvar.rs
Original file line number Diff line number Diff line change
Expand Up @@ -112,3 +112,25 @@ declare_syscall!(
)
}
);

declare_syscall!(
/// Get a Last Restart Slot sysvar
SyscallGetLastRestartSlotSysvar,
fn inner_call(
invoke_context: &mut InvokeContext,
var_addr: u64,
_arg2: u64,
_arg3: u64,
_arg4: u64,
_arg5: u64,
memory_mapping: &mut MemoryMapping,
) -> Result<u64, Error> {
get_sysvar(
invoke_context.get_sysvar_cache().get_last_restart_slot(),
var_addr,
invoke_context.get_check_aligned(),
memory_mapping,
invoke_context,
)
}
);
Loading

0 comments on commit 2ceabd9

Please sign in to comment.