Skip to content
This repository has been archived by the owner on Jan 13, 2025. It is now read-only.

Commit

Permalink
Return sysvars via syscalls (bp #16422) (#16497)
Browse files Browse the repository at this point in the history
* Return sysvars via syscalls (#16422)

(cherry picked from commit fa83f3b)

* bad merge

* Fix branch diffs

* nudge

Co-authored-by: Jack May <jack@solana.com>
  • Loading branch information
mergify[bot] and jackcmay authored Apr 14, 2021
1 parent 31ed985 commit 6da4bec
Show file tree
Hide file tree
Showing 18 changed files with 725 additions and 57 deletions.
3 changes: 3 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions program-test/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,13 @@ version = "1.6.5"
[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.6.5" }
solana-banks-server = { path = "../banks-server", version = "=1.6.5" }
solana-bpf-loader-program = { path = "../programs/bpf_loader", version = "=1.6.5" }
Expand Down
65 changes: 62 additions & 3 deletions program-test/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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},
Expand All @@ -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::{
Expand Down Expand Up @@ -176,6 +182,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) {
Expand Down Expand Up @@ -328,6 +371,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> {
Expand Down
72 changes: 72 additions & 0 deletions program-test/tests/sysvar.rs
Original file line number Diff line number Diff line change
@@ -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();
}
76 changes: 48 additions & 28 deletions programs/bpf/rust/sysvar/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,77 +3,97 @@
extern crate solana_program;
use solana_program::{
account_info::AccountInfo,
clock::DEFAULT_SLOTS_PER_EPOCH,
entrypoint,
entrypoint::ProgramResult,
msg,
program_error::ProgramError,
pubkey::Pubkey,
rent,
sysvar::{
self, clock::Clock, fees::Fees, instructions, rent::Rent, slot_hashes::SlotHashes,
self, clock::Clock, epoch_schedule::EpochSchedule, fees::Fees, instructions,
recent_blockhashes::RecentBlockhashes, rent::Rent, slot_hashes::SlotHashes,
slot_history::SlotHistory, stake_history::StakeHistory, Sysvar,
},
};

entrypoint!(process_instruction);
#[allow(clippy::unnecessary_wraps)]
fn process_instruction(
pub fn process_instruction(
_program_id: &Pubkey,
accounts: &[AccountInfo],
_instruction_data: &[u8],
) -> ProgramResult {
// Clock
msg!("Clock identifier:");
sysvar::clock::id().log();
let clock = Clock::from_account_info(&accounts[2]).unwrap();
assert_eq!(clock.slot, DEFAULT_SLOTS_PER_EPOCH + 1);
{
msg!("Clock identifier:");
sysvar::clock::id().log();
let clock = Clock::from_account_info(&accounts[2]).unwrap();
assert_ne!(clock, Clock::default());
let got_clock = Clock::get()?;
assert_eq!(clock, got_clock);
}

// Epoch Schedule
{
msg!("EpochSchedule identifier:");
sysvar::epoch_schedule::id().log();
let epoch_schedule = EpochSchedule::from_account_info(&accounts[3]).unwrap();
assert_eq!(epoch_schedule, EpochSchedule::default());
let got_epoch_schedule = EpochSchedule::get()?;
assert_eq!(epoch_schedule, got_epoch_schedule);
}

// Fees
msg!("Fees identifier:");
sysvar::fees::id().log();
let fees = Fees::from_account_info(&accounts[3]).unwrap();
let fee_calculator = fees.fee_calculator;
assert_eq!(fee_calculator.lamports_per_signature, 0);
{
msg!("Fees identifier:");
sysvar::fees::id().log();
let fees = Fees::from_account_info(&accounts[4]).unwrap();
let got_fees = Fees::get()?;
assert_eq!(fees, got_fees);
}

// Instructions
msg!("Instructions identifier:");
sysvar::instructions::id().log();
let index = instructions::load_current_index(&accounts[4].try_borrow_data()?);
let index = instructions::load_current_index(&accounts[5].try_borrow_data()?);
assert_eq!(0, index);
msg!(
"instruction {:?}",
instructions::load_instruction_at(index as usize, &accounts[4].try_borrow_data()?)
);

let due = Rent::from_account_info(&accounts[5]).unwrap().due(
rent::DEFAULT_LAMPORTS_PER_BYTE_YEAR * rent::DEFAULT_EXEMPTION_THRESHOLD as u64,
1,
1.0,
);
assert_eq!(due, (0, true));
// Recent Blockhashes
{
msg!("RecentBlockhashes identifier:");
sysvar::recent_blockhashes::id().log();
let recent_blockhashes = RecentBlockhashes::from_account_info(&accounts[6]).unwrap();
assert_ne!(recent_blockhashes, RecentBlockhashes::default());
}

// Rent
{
msg!("Rent identifier:");
sysvar::rent::id().log();
let rent = Rent::from_account_info(&accounts[7]).unwrap();
let got_rent = Rent::get()?;
assert_eq!(rent, got_rent);
}

// Slot Hashes
msg!("SlotHashes identifier:");
sysvar::slot_hashes::id().log();
assert_eq!(
Err(ProgramError::UnsupportedSysvar),
SlotHashes::from_account_info(&accounts[6])
SlotHashes::from_account_info(&accounts[8])
);

// Slot History
msg!("SlotHistory identifier:");
sysvar::slot_history::id().log();
assert_eq!(
Err(ProgramError::UnsupportedSysvar),
SlotHistory::from_account_info(&accounts[7])
SlotHistory::from_account_info(&accounts[9])
);

// Stake History
msg!("StakeHistory identifier:");
sysvar::stake_history::id().log();
let stake_history = StakeHistory::from_account_info(&accounts[8]).unwrap();
assert!(stake_history.len() >= 1);
let _ = StakeHistory::from_account_info(&accounts[10]).unwrap();

Ok(())
}
8 changes: 6 additions & 2 deletions programs/bpf/tests/programs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,10 @@ use solana_sdk::{
pubkey::Pubkey,
signature::{keypair_from_seed, Keypair, Signer},
system_instruction,
sysvar::{clock, fees, rent, slot_hashes, slot_history, stake_history, instructions},
sysvar::{
clock, epoch_schedule, fees, instructions, recent_blockhashes, rent, slot_hashes,
slot_history, stake_history,
},
transaction::{Transaction, TransactionError},
};
use solana_transaction_status::{
Expand Down Expand Up @@ -466,13 +469,14 @@ fn test_program_bpf_sanity() {
AccountMeta::new(mint_keypair.pubkey(), true),
AccountMeta::new(Keypair::new().pubkey(), false),
AccountMeta::new_readonly(clock::id(), false),
AccountMeta::new_readonly(epoch_schedule::id(), false),
AccountMeta::new_readonly(fees::id(), false),
AccountMeta::new_readonly(instructions::id(), false),
AccountMeta::new_readonly(recent_blockhashes::id(), false),
AccountMeta::new_readonly(rent::id(), false),
AccountMeta::new_readonly(slot_hashes::id(), false),
AccountMeta::new_readonly(slot_history::id(), false),
AccountMeta::new_readonly(stake_history::id(), false),

];
let instruction = Instruction::new_with_bytes(program_id, &[1], account_metas);
let result = bank_client.send_and_confirm_instruction(&mint_keypair, instruction);
Expand Down
Loading

0 comments on commit 6da4bec

Please sign in to comment.