Return sysvars via syscalls (#16422)
This commit is contained in:
@ -10,10 +10,13 @@ version = "1.7.0"
|
||||
[dependencies]
|
||||
async-trait = "0.1.42"
|
||||
base64 = "0.12.3"
|
||||
bincode = "1.3.1"
|
||||
chrono = "0.4.19"
|
||||
chrono-humanize = "0.1.1"
|
||||
log = "0.4.11"
|
||||
mio = "0.7.6"
|
||||
serde = "1.0.112"
|
||||
serde_derive = "1.0.103"
|
||||
solana-banks-client = { path = "../banks-client", version = "=1.7.0" }
|
||||
solana-banks-server = { path = "../banks-server", version = "=1.7.0" }
|
||||
solana-bpf-loader-program = { path = "../programs/bpf_loader", version = "=1.7.0" }
|
||||
|
@ -16,8 +16,9 @@ use {
|
||||
solana_sdk::{
|
||||
account::{Account, AccountSharedData, ReadableAccount},
|
||||
account_info::AccountInfo,
|
||||
clock::Slot,
|
||||
entrypoint::ProgramResult,
|
||||
clock::{Clock, Slot},
|
||||
entrypoint::{ProgramResult, SUCCESS},
|
||||
epoch_schedule::EpochSchedule,
|
||||
feature_set::demote_sysvar_write_locks,
|
||||
fee_calculator::{FeeCalculator, FeeRateGovernor},
|
||||
genesis_config::{ClusterType, GenesisConfig},
|
||||
@ -30,10 +31,15 @@ use {
|
||||
process_instruction::{
|
||||
stable_log, BpfComputeBudget, InvokeContext, ProcessInstructionWithContext,
|
||||
},
|
||||
program_error::ProgramError,
|
||||
program_error::{ProgramError, ACCOUNT_BORROW_FAILED, UNSUPPORTED_SYSVAR},
|
||||
pubkey::Pubkey,
|
||||
rent::Rent,
|
||||
signature::{Keypair, Signer},
|
||||
sysvar::{
|
||||
clock, epoch_schedule,
|
||||
fees::{self, Fees},
|
||||
rent, Sysvar,
|
||||
},
|
||||
},
|
||||
solana_vote_program::vote_state::{VoteState, VoteStateVersions},
|
||||
std::{
|
||||
@ -181,6 +187,43 @@ macro_rules! processor {
|
||||
};
|
||||
}
|
||||
|
||||
fn get_sysvar<T: Default + Sysvar + Sized + serde::de::DeserializeOwned>(
|
||||
id: &Pubkey,
|
||||
var_addr: *mut u8,
|
||||
) -> u64 {
|
||||
let invoke_context = get_invoke_context();
|
||||
|
||||
let sysvar_data = match invoke_context.get_sysvar_data(id).ok_or_else(|| {
|
||||
ic_msg!(invoke_context, "Unable to get Sysvar {}", id);
|
||||
UNSUPPORTED_SYSVAR
|
||||
}) {
|
||||
Ok(sysvar_data) => sysvar_data,
|
||||
Err(err) => return err,
|
||||
};
|
||||
|
||||
let var: T = match bincode::deserialize(&sysvar_data) {
|
||||
Ok(sysvar_data) => sysvar_data,
|
||||
Err(_) => return UNSUPPORTED_SYSVAR,
|
||||
};
|
||||
|
||||
unsafe {
|
||||
*(var_addr as *mut _ as *mut T) = var;
|
||||
}
|
||||
|
||||
if invoke_context
|
||||
.get_compute_meter()
|
||||
.try_borrow_mut()
|
||||
.map_err(|_| ACCOUNT_BORROW_FAILED)
|
||||
.unwrap()
|
||||
.consume(invoke_context.get_bpf_compute_budget().sysvar_base_cost + T::size_of() as u64)
|
||||
.is_err()
|
||||
{
|
||||
panic!("Exceeded compute budget");
|
||||
}
|
||||
|
||||
SUCCESS
|
||||
}
|
||||
|
||||
struct SyscallStubs {}
|
||||
impl solana_sdk::program_stubs::SyscallStubs for SyscallStubs {
|
||||
fn sol_log(&self, message: &str) {
|
||||
@ -333,6 +376,22 @@ impl solana_sdk::program_stubs::SyscallStubs for SyscallStubs {
|
||||
stable_log::program_success(&logger, &program_id);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn sol_get_clock_sysvar(&self, var_addr: *mut u8) -> u64 {
|
||||
get_sysvar::<Clock>(&clock::id(), var_addr)
|
||||
}
|
||||
|
||||
fn sol_get_epoch_schedule_sysvar(&self, var_addr: *mut u8) -> u64 {
|
||||
get_sysvar::<EpochSchedule>(&epoch_schedule::id(), var_addr)
|
||||
}
|
||||
|
||||
fn sol_get_fees_sysvar(&self, var_addr: *mut u8) -> u64 {
|
||||
get_sysvar::<Fees>(&fees::id(), var_addr)
|
||||
}
|
||||
|
||||
fn sol_get_rent_sysvar(&self, var_addr: *mut u8) -> u64 {
|
||||
get_sysvar::<Rent>(&rent::id(), var_addr)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn find_file(filename: &str) -> Option<PathBuf> {
|
||||
|
72
program-test/tests/sysvar.rs
Normal file
72
program-test/tests/sysvar.rs
Normal file
@ -0,0 +1,72 @@
|
||||
use {
|
||||
solana_program_test::{processor, ProgramTest},
|
||||
solana_sdk::{
|
||||
account_info::AccountInfo,
|
||||
clock::Clock,
|
||||
entrypoint::ProgramResult,
|
||||
epoch_schedule::EpochSchedule,
|
||||
fee_calculator::FeeCalculator,
|
||||
instruction::Instruction,
|
||||
msg,
|
||||
pubkey::Pubkey,
|
||||
rent::Rent,
|
||||
signature::Signer,
|
||||
sysvar::{fees::Fees, Sysvar},
|
||||
transaction::Transaction,
|
||||
},
|
||||
};
|
||||
|
||||
// Process instruction to invoke into another program
|
||||
fn sysvar_getter_process_instruction(
|
||||
_program_id: &Pubkey,
|
||||
_accounts: &[AccountInfo],
|
||||
_input: &[u8],
|
||||
) -> ProgramResult {
|
||||
msg!("sysvar_getter");
|
||||
|
||||
let clock = Clock::get()?;
|
||||
assert_eq!(42, clock.slot);
|
||||
|
||||
let epoch_schedule = EpochSchedule::get()?;
|
||||
assert_eq!(epoch_schedule, EpochSchedule::default());
|
||||
|
||||
let fees = Fees::get()?;
|
||||
assert_eq!(
|
||||
fees.fee_calculator,
|
||||
FeeCalculator {
|
||||
lamports_per_signature: 5000
|
||||
}
|
||||
);
|
||||
|
||||
let rent = Rent::get()?;
|
||||
assert_eq!(rent, Rent::default());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn get_sysvar() {
|
||||
let program_id = Pubkey::new_unique();
|
||||
let program_test = ProgramTest::new(
|
||||
"sysvar_getter",
|
||||
program_id,
|
||||
processor!(sysvar_getter_process_instruction),
|
||||
);
|
||||
|
||||
let mut context = program_test.start_with_context().await;
|
||||
context.warp_to_slot(42).unwrap();
|
||||
let instructions = vec![Instruction::new_with_bincode(program_id, &(), vec![])];
|
||||
|
||||
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();
|
||||
}
|
Reference in New Issue
Block a user