Simd 47 syscall sysvar last restart slot (#31957)

* 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 a67465ae22.

* 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 <steven@solana.com>

* reverting sysvar_cache.rs because change was not necessary

---------

Co-authored-by: steve-gg <grooviegermanikus@gmail.com>
Co-authored-by: steviez <steven@solana.com>
This commit is contained in:
galactus 2023-06-16 22:14:02 +02:00 committed by GitHub
parent 987e8eeeaf
commit 2ceabd9368
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 393 additions and 9 deletions

View File

@ -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},
},
};
@ -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
}
@ -105,6 +112,7 @@ pub enum SysvarAccountType {
SlotHashes(Vec<UiSlotHashEntry>),
SlotHistory(UiSlotHistory),
StakeHistory(Vec<UiStakeHistoryEntry>),
LastRestartSlot(UiLastRestartSlot),
}
#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Default)]
@ -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)]
@ -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
})
);
}
}

View File

@ -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)

View File

@ -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::{
@ -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 {
@ -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()
@ -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) {
@ -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()
}
}

View File

@ -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()))
@ -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);
}
}

View File

@ -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;
}

View File

@ -6,7 +6,7 @@ pub use self::{
mem_ops::{SyscallMemcmp, SyscallMemcpy, SyscallMemmove, SyscallMemset},
sysvar::{
SyscallGetClockSysvar, SyscallGetEpochScheduleSysvar, SyscallGetFeesSysvar,
SyscallGetRentSysvar,
SyscallGetLastRestartSlotSysvar, SyscallGetRentSysvar,
},
};
#[allow(deprecated)]
@ -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::{
@ -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);
@ -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)?;

View File

@ -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,
)
}
);

View File

@ -156,7 +156,7 @@ use {
slot_history::{Check, SlotHistory},
stake::state::Delegation,
system_transaction,
sysvar::{self, Sysvar, SysvarId},
sysvar::{self, last_restart_slot::LastRestartSlot, Sysvar, SysvarId},
timing::years_as_slots,
transaction::{
self, MessageHash, Result, SanitizedTransaction, Transaction, TransactionError,
@ -1447,6 +1447,7 @@ impl Bank {
bank.update_rent();
bank.update_epoch_schedule();
bank.update_recent_blockhashes();
bank.update_last_restart_slot();
bank.fill_missing_sysvar_cache_entries();
bank
}
@ -1744,6 +1745,7 @@ impl Bank {
new.update_stake_history(Some(parent_epoch));
new.update_clock(Some(parent_epoch));
new.update_fees();
new.update_last_restart_slot()
});
let (_, fill_sysvar_cache_time_us) = measure_us!(new.fill_missing_sysvar_cache_entries());
@ -2415,6 +2417,35 @@ impl Bank {
});
}
pub fn update_last_restart_slot(&self) {
let feature_flag = self
.feature_set
.is_active(&feature_set::last_restart_slot_sysvar::id());
if feature_flag {
let last_restart_slot = {
let slot = self.slot;
let hard_forks = self.hard_forks();
let hard_forks_r = hard_forks.read().unwrap();
// Only consider hard forks <= this bank's slot to avoid prematurely applying
// a hard fork that is set to occur in the future.
hard_forks_r
.iter()
.rev()
.find(|(hard_fork, _)| *hard_fork <= slot)
.map(|(slot, _)| *slot)
.unwrap_or(0)
};
self.update_sysvar_account(&sysvar::last_restart_slot::id(), |account| {
create_account(
&LastRestartSlot { last_restart_slot },
self.inherit_specially_retained_account_fields(account),
)
});
}
}
pub fn set_sysvar_for_tests<T>(&self, sysvar: &T)
where
T: Sysvar + SysvarId,

View File

@ -0,0 +1,10 @@
//! Information about the last restart slot (hard fork).
use {crate::clock::Slot, solana_sdk_macro::CloneZeroed};
#[repr(C)]
#[derive(Serialize, Deserialize, Debug, CloneZeroed, PartialEq, Eq, Default)]
pub struct LastRestartSlot {
/// The last restart `Slot`.
pub last_restart_slot: Slot,
}

View File

@ -495,6 +495,7 @@ pub mod incinerator;
pub mod instruction;
pub mod keccak;
pub mod lamports;
pub mod last_restart_slot;
pub mod loader_instruction;
pub mod loader_upgradeable_instruction;
pub mod loader_v4;

View File

@ -54,7 +54,9 @@ pub trait SyscallStubs: Sync + Send {
fn sol_get_epoch_rewards_sysvar(&self, _var_addr: *mut u8) -> u64 {
UNSUPPORTED_SYSVAR
}
fn sol_get_last_restart_slot(&self, _var_addr: *mut u8) -> u64 {
UNSUPPORTED_SYSVAR
}
/// # Safety
unsafe fn sol_memcpy(&self, dst: *mut u8, src: *const u8, n: usize) {
// cannot be overlapping
@ -154,6 +156,13 @@ pub(crate) fn sol_get_rent_sysvar(var_addr: *mut u8) -> u64 {
SYSCALL_STUBS.read().unwrap().sol_get_rent_sysvar(var_addr)
}
pub(crate) fn sol_get_last_restart_slot(var_addr: *mut u8) -> u64 {
SYSCALL_STUBS
.read()
.unwrap()
.sol_get_last_restart_slot(var_addr)
}
pub(crate) fn sol_memcpy(dst: *mut u8, src: *const u8, n: usize) {
unsafe {
SYSCALL_STUBS.read().unwrap().sol_memcpy(dst, src, n);

View File

@ -50,6 +50,7 @@ define_syscall!(fn sol_get_clock_sysvar(addr: *mut u8) -> u64);
define_syscall!(fn sol_get_epoch_schedule_sysvar(addr: *mut u8) -> u64);
define_syscall!(fn sol_get_fees_sysvar(addr: *mut u8) -> u64);
define_syscall!(fn sol_get_rent_sysvar(addr: *mut u8) -> u64);
define_syscall!(fn sol_get_last_restart_slot(addr: *mut u8) -> u64);
define_syscall!(fn sol_memcpy_(dst: *mut u8, src: *const u8, n: u64));
define_syscall!(fn sol_memmove_(dst: *mut u8, src: *const u8, n: u64));
define_syscall!(fn sol_memcmp_(s1: *const u8, s2: *const u8, n: u64, result: *mut i32));

View File

@ -0,0 +1,52 @@
//! Information about the last restart slot (hard fork).
//!
//! The _last restart sysvar_ provides access to the last restart slot kept in the
//! bank fork for the slot on the fork that executes the current transaction.
//! In case there was no fork it returns _0_.
//!
//! [`LastRestartSlot`] implements [`Sysvar::get`] and can be loaded efficiently without
//! passing the sysvar account ID to the program.
//!
//! See also the Solana [SIMD proposal][simd].
//!
//! [simd]: https://github.com/solana-foundation/solana-improvement-documents/blob/main/proposals/0047-syscall-and-sysvar-for-last-restart-slot.md
//!
//! # Examples
//!
//! Accessing via on-chain program directly:
//!
//! ```no_run
//! # use solana_program::{
//! # account_info::{AccountInfo, next_account_info},
//! # entrypoint::ProgramResult,
//! # msg,
//! # pubkey::Pubkey,
//! # sysvar::Sysvar,
//! # last_restart_slot::LastRestartSlot,
//! # };
//!
//! fn process_instruction(
//! program_id: &Pubkey,
//! accounts: &[AccountInfo],
//! instruction_data: &[u8],
//! ) -> ProgramResult {
//!
//! let last_restart_slot = LastRestartSlot::get();
//! msg!("last restart slot: {:?}", last_restart_slot);
//!
//! Ok(())
//! }
//! ```
//!
pub use crate::last_restart_slot::LastRestartSlot;
use crate::{impl_sysvar_get, program_error::ProgramError, sysvar::Sysvar};
crate::declare_sysvar_id!(
"SysvarLastRestartS1ot1111111111111111111111",
LastRestartSlot
);
impl Sysvar for LastRestartSlot {
impl_sysvar_get!(sol_get_last_restart_slot);
}

View File

@ -91,6 +91,7 @@ pub mod epoch_rewards;
pub mod epoch_schedule;
pub mod fees;
pub mod instructions;
pub mod last_restart_slot;
pub mod recent_blockhashes;
pub mod rent;
pub mod rewards;
@ -113,6 +114,7 @@ lazy_static! {
stake_history::id(),
instructions::id(),
epoch_rewards::id(),
last_restart_slot::id(),
];
}

View File

@ -0,0 +1,21 @@
#pragma once
/**
* @brief Solana Last Restart Slot system call
*/
#include <sol/types.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* Get Last Restart Slot
*/
@SYSCALL u64 sol_get_last_restart_slot(uint8_t *result);
#ifdef __cplusplus
}
#endif
/**@}*/

View File

@ -0,0 +1,30 @@
#pragma once
/**
* @brief Solana Last Restart Slot system call
*/
#include <sol/types.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* Get Last Restart Slot
*/
/* DO NOT MODIFY THIS GENERATED FILE. INSTEAD CHANGE sdk/sbf/c/inc/sol/inc/last_restart_slot.inc AND RUN `cargo run --bin gen-headers` */
#ifndef SOL_SBFV2
u64 sol_get_last_restart_slot(uint8_t *result);
#else
typedef u64(*sol_get_last_restart_slot_pointer_type)(uint8_t *result);
static u64 sol_get_last_restart_slot(uint8_t *result arg1) {
sol_get_last_restart_slot_pointer_type sol_get_last_restart_slot_pointer = (sol_get_last_restart_slot_pointer_type) 411697201;
return sol_get_last_restart_slot_pointer(arg1);
}
#endif
#ifdef __cplusplus
}
#endif
/**@}*/

View File

@ -668,6 +668,10 @@ pub mod checked_arithmetic_in_fee_validation {
solana_sdk::declare_id!("5Pecy6ie6XGm22pc9d4P9W5c31BugcFBuy6hsP2zkETv");
}
pub mod last_restart_slot_sysvar {
solana_sdk::declare_id!("HooKD5NC9QNxk25QuzCssB8ecrEzGt6eXEPBUxWp1LaR");
}
lazy_static! {
/// Map of feature identifiers to user-visible description
pub static ref FEATURE_NAMES: HashMap<Pubkey, &'static str> = [
@ -830,6 +834,7 @@ lazy_static! {
(vote_state_add_vote_latency::id(), "replace Lockout with LandedVote (including vote latency) in vote state #31264"),
(checked_arithmetic_in_fee_validation::id(), "checked arithmetic in fee validation #31273"),
(bpf_account_data_direct_mapping::id(), "use memory regions to map account data into the rbpf vm instead of copying the data"),
(last_restart_slot_sysvar::id(), "enable new sysvar last_restart_slot"),
/*************** ADD NEW FEATURES HERE ***************/
]
.iter()