Return sysvars via syscalls (#16422)
This commit is contained in:
3
Cargo.lock
generated
3
Cargo.lock
generated
@ -4932,10 +4932,13 @@ version = "1.7.0"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"async-trait",
|
"async-trait",
|
||||||
"base64 0.12.3",
|
"base64 0.12.3",
|
||||||
|
"bincode",
|
||||||
"chrono",
|
"chrono",
|
||||||
"chrono-humanize",
|
"chrono-humanize",
|
||||||
"log 0.4.11",
|
"log 0.4.11",
|
||||||
"mio 0.7.6",
|
"mio 0.7.6",
|
||||||
|
"serde",
|
||||||
|
"serde_derive",
|
||||||
"solana-banks-client",
|
"solana-banks-client",
|
||||||
"solana-banks-server",
|
"solana-banks-server",
|
||||||
"solana-bpf-loader-program",
|
"solana-bpf-loader-program",
|
||||||
|
@ -10,10 +10,13 @@ version = "1.7.0"
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
async-trait = "0.1.42"
|
async-trait = "0.1.42"
|
||||||
base64 = "0.12.3"
|
base64 = "0.12.3"
|
||||||
|
bincode = "1.3.1"
|
||||||
chrono = "0.4.19"
|
chrono = "0.4.19"
|
||||||
chrono-humanize = "0.1.1"
|
chrono-humanize = "0.1.1"
|
||||||
log = "0.4.11"
|
log = "0.4.11"
|
||||||
mio = "0.7.6"
|
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-client = { path = "../banks-client", version = "=1.7.0" }
|
||||||
solana-banks-server = { path = "../banks-server", 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" }
|
solana-bpf-loader-program = { path = "../programs/bpf_loader", version = "=1.7.0" }
|
||||||
|
@ -16,8 +16,9 @@ use {
|
|||||||
solana_sdk::{
|
solana_sdk::{
|
||||||
account::{Account, AccountSharedData, ReadableAccount},
|
account::{Account, AccountSharedData, ReadableAccount},
|
||||||
account_info::AccountInfo,
|
account_info::AccountInfo,
|
||||||
clock::Slot,
|
clock::{Clock, Slot},
|
||||||
entrypoint::ProgramResult,
|
entrypoint::{ProgramResult, SUCCESS},
|
||||||
|
epoch_schedule::EpochSchedule,
|
||||||
feature_set::demote_sysvar_write_locks,
|
feature_set::demote_sysvar_write_locks,
|
||||||
fee_calculator::{FeeCalculator, FeeRateGovernor},
|
fee_calculator::{FeeCalculator, FeeRateGovernor},
|
||||||
genesis_config::{ClusterType, GenesisConfig},
|
genesis_config::{ClusterType, GenesisConfig},
|
||||||
@ -30,10 +31,15 @@ use {
|
|||||||
process_instruction::{
|
process_instruction::{
|
||||||
stable_log, BpfComputeBudget, InvokeContext, ProcessInstructionWithContext,
|
stable_log, BpfComputeBudget, InvokeContext, ProcessInstructionWithContext,
|
||||||
},
|
},
|
||||||
program_error::ProgramError,
|
program_error::{ProgramError, ACCOUNT_BORROW_FAILED, UNSUPPORTED_SYSVAR},
|
||||||
pubkey::Pubkey,
|
pubkey::Pubkey,
|
||||||
rent::Rent,
|
rent::Rent,
|
||||||
signature::{Keypair, Signer},
|
signature::{Keypair, Signer},
|
||||||
|
sysvar::{
|
||||||
|
clock, epoch_schedule,
|
||||||
|
fees::{self, Fees},
|
||||||
|
rent, Sysvar,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
solana_vote_program::vote_state::{VoteState, VoteStateVersions},
|
solana_vote_program::vote_state::{VoteState, VoteStateVersions},
|
||||||
std::{
|
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 {}
|
struct SyscallStubs {}
|
||||||
impl solana_sdk::program_stubs::SyscallStubs for SyscallStubs {
|
impl solana_sdk::program_stubs::SyscallStubs for SyscallStubs {
|
||||||
fn sol_log(&self, message: &str) {
|
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);
|
stable_log::program_success(&logger, &program_id);
|
||||||
Ok(())
|
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> {
|
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();
|
||||||
|
}
|
3
programs/bpf/Cargo.lock
generated
3
programs/bpf/Cargo.lock
generated
@ -3311,10 +3311,13 @@ version = "1.7.0"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"async-trait",
|
"async-trait",
|
||||||
"base64 0.12.3",
|
"base64 0.12.3",
|
||||||
|
"bincode",
|
||||||
"chrono",
|
"chrono",
|
||||||
"chrono-humanize",
|
"chrono-humanize",
|
||||||
"log",
|
"log",
|
||||||
"mio 0.7.7",
|
"mio 0.7.7",
|
||||||
|
"serde",
|
||||||
|
"serde_derive",
|
||||||
"solana-banks-client",
|
"solana-banks-client",
|
||||||
"solana-banks-server",
|
"solana-banks-server",
|
||||||
"solana-bpf-loader-program",
|
"solana-bpf-loader-program",
|
||||||
|
@ -29,6 +29,8 @@ pub fn process_instruction(
|
|||||||
sysvar::clock::id().log();
|
sysvar::clock::id().log();
|
||||||
let clock = Clock::from_account_info(&accounts[2]).unwrap();
|
let clock = Clock::from_account_info(&accounts[2]).unwrap();
|
||||||
assert_ne!(clock, Clock::default());
|
assert_ne!(clock, Clock::default());
|
||||||
|
let got_clock = Clock::get()?;
|
||||||
|
assert_eq!(clock, got_clock);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Epoch Schedule
|
// Epoch Schedule
|
||||||
@ -37,6 +39,8 @@ pub fn process_instruction(
|
|||||||
sysvar::epoch_schedule::id().log();
|
sysvar::epoch_schedule::id().log();
|
||||||
let epoch_schedule = EpochSchedule::from_account_info(&accounts[3]).unwrap();
|
let epoch_schedule = EpochSchedule::from_account_info(&accounts[3]).unwrap();
|
||||||
assert_eq!(epoch_schedule, EpochSchedule::default());
|
assert_eq!(epoch_schedule, EpochSchedule::default());
|
||||||
|
let got_epoch_schedule = EpochSchedule::get()?;
|
||||||
|
assert_eq!(epoch_schedule, got_epoch_schedule);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fees
|
// Fees
|
||||||
@ -44,8 +48,10 @@ pub fn process_instruction(
|
|||||||
msg!("Fees identifier:");
|
msg!("Fees identifier:");
|
||||||
sysvar::fees::id().log();
|
sysvar::fees::id().log();
|
||||||
let fees = Fees::from_account_info(&accounts[4]).unwrap();
|
let fees = Fees::from_account_info(&accounts[4]).unwrap();
|
||||||
let fee_calculator = fees.fee_calculator;
|
let fee_calculator = fees.fee_calculator.clone();
|
||||||
assert_ne!(fee_calculator, FeeCalculator::default());
|
assert_ne!(fee_calculator, FeeCalculator::default());
|
||||||
|
let got_fees = Fees::get()?;
|
||||||
|
assert_eq!(fees, got_fees);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Instructions
|
// Instructions
|
||||||
@ -68,6 +74,8 @@ pub fn process_instruction(
|
|||||||
sysvar::rent::id().log();
|
sysvar::rent::id().log();
|
||||||
let rent = Rent::from_account_info(&accounts[7]).unwrap();
|
let rent = Rent::from_account_info(&accounts[7]).unwrap();
|
||||||
assert_eq!(rent, Rent::default());
|
assert_eq!(rent, Rent::default());
|
||||||
|
let got_rent = Rent::get()?;
|
||||||
|
assert_eq!(rent, got_rent);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Slot Hashes
|
// Slot Hashes
|
||||||
|
@ -15,10 +15,12 @@ use solana_sdk::{
|
|||||||
account_utils::StateMut,
|
account_utils::StateMut,
|
||||||
bpf_loader, bpf_loader_deprecated,
|
bpf_loader, bpf_loader_deprecated,
|
||||||
bpf_loader_upgradeable::{self, UpgradeableLoaderState},
|
bpf_loader_upgradeable::{self, UpgradeableLoaderState},
|
||||||
|
clock::Clock,
|
||||||
entrypoint::{MAX_PERMITTED_DATA_INCREASE, SUCCESS},
|
entrypoint::{MAX_PERMITTED_DATA_INCREASE, SUCCESS},
|
||||||
|
epoch_schedule::EpochSchedule,
|
||||||
feature_set::{
|
feature_set::{
|
||||||
cpi_data_cost, cpi_share_ro_and_exec_accounts, demote_sysvar_write_locks,
|
cpi_data_cost, cpi_share_ro_and_exec_accounts, demote_sysvar_write_locks,
|
||||||
ristretto_mul_syscall_enabled,
|
ristretto_mul_syscall_enabled, sysvar_via_syscall,
|
||||||
},
|
},
|
||||||
hash::{Hasher, HASH_BYTES},
|
hash::{Hasher, HASH_BYTES},
|
||||||
ic_msg,
|
ic_msg,
|
||||||
@ -27,6 +29,8 @@ use solana_sdk::{
|
|||||||
native_loader,
|
native_loader,
|
||||||
process_instruction::{stable_log, ComputeMeter, InvokeContext, Logger},
|
process_instruction::{stable_log, ComputeMeter, InvokeContext, Logger},
|
||||||
pubkey::{Pubkey, PubkeyError, MAX_SEEDS},
|
pubkey::{Pubkey, PubkeyError, MAX_SEEDS},
|
||||||
|
rent::Rent,
|
||||||
|
sysvar::{self, fees::Fees, Sysvar, SysvarId},
|
||||||
};
|
};
|
||||||
use std::{
|
use std::{
|
||||||
alloc::Layout,
|
alloc::Layout,
|
||||||
@ -127,6 +131,19 @@ pub fn register_syscalls(
|
|||||||
.register_syscall_by_name(b"sol_ristretto_mul", SyscallRistrettoMul::call)?;
|
.register_syscall_by_name(b"sol_ristretto_mul", SyscallRistrettoMul::call)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if invoke_context.is_feature_active(&sysvar_via_syscall::id()) {
|
||||||
|
syscall_registry
|
||||||
|
.register_syscall_by_name(b"sol_get_clock_sysvar", SyscallGetClockSysvar::call)?;
|
||||||
|
syscall_registry.register_syscall_by_name(
|
||||||
|
b"sol_get_epoch_schedule_sysvar",
|
||||||
|
SyscallGetEpochScheduleSysvar::call,
|
||||||
|
)?;
|
||||||
|
syscall_registry
|
||||||
|
.register_syscall_by_name(b"sol_get_fees_sysvar", SyscallGetFeesSysvar::call)?;
|
||||||
|
syscall_registry
|
||||||
|
.register_syscall_by_name(b"sol_get_rent_sysvar", SyscallGetRentSysvar::call)?;
|
||||||
|
}
|
||||||
|
|
||||||
syscall_registry
|
syscall_registry
|
||||||
.register_syscall_by_name(b"sol_invoke_signed_c", SyscallInvokeSignedC::call)?;
|
.register_syscall_by_name(b"sol_invoke_signed_c", SyscallInvokeSignedC::call)?;
|
||||||
syscall_registry
|
syscall_registry
|
||||||
@ -137,8 +154,8 @@ pub fn register_syscalls(
|
|||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! bind_feature_gated_syscall_context_object {
|
macro_rules! bind_feature_gated_syscall_context_object {
|
||||||
($vm:expr, $invoke_context:expr, $feature_id:expr, $syscall_context_object:expr $(,)?) => {
|
($vm:expr, $is_feature_active:expr, $syscall_context_object:expr $(,)?) => {
|
||||||
if $invoke_context.is_feature_active($feature_id) {
|
if $is_feature_active {
|
||||||
match $vm.bind_syscall_context_object($syscall_context_object, None) {
|
match $vm.bind_syscall_context_object($syscall_context_object, None) {
|
||||||
Err(EbpfError::SyscallNotRegistered(_)) | Ok(()) => {}
|
Err(EbpfError::SyscallNotRegistered(_)) | Ok(()) => {}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
@ -234,8 +251,7 @@ pub fn bind_syscall_context_objects<'a>(
|
|||||||
|
|
||||||
bind_feature_gated_syscall_context_object!(
|
bind_feature_gated_syscall_context_object!(
|
||||||
vm,
|
vm,
|
||||||
invoke_context,
|
invoke_context.is_feature_active(&ristretto_mul_syscall_enabled::id()),
|
||||||
&ristretto_mul_syscall_enabled::id(),
|
|
||||||
Box::new(SyscallRistrettoMul {
|
Box::new(SyscallRistrettoMul {
|
||||||
cost: 0,
|
cost: 0,
|
||||||
compute_meter: invoke_context.get_compute_meter(),
|
compute_meter: invoke_context.get_compute_meter(),
|
||||||
@ -243,9 +259,44 @@ pub fn bind_syscall_context_objects<'a>(
|
|||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
// Cross-program invocation syscalls
|
let is_sysvar_via_syscall_active = invoke_context.is_feature_active(&sysvar_via_syscall::id());
|
||||||
|
|
||||||
let invoke_context = Rc::new(RefCell::new(invoke_context));
|
let invoke_context = Rc::new(RefCell::new(invoke_context));
|
||||||
|
|
||||||
|
bind_feature_gated_syscall_context_object!(
|
||||||
|
vm,
|
||||||
|
is_sysvar_via_syscall_active,
|
||||||
|
Box::new(SyscallGetClockSysvar {
|
||||||
|
invoke_context: invoke_context.clone(),
|
||||||
|
loader_id,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
bind_feature_gated_syscall_context_object!(
|
||||||
|
vm,
|
||||||
|
is_sysvar_via_syscall_active,
|
||||||
|
Box::new(SyscallGetEpochScheduleSysvar {
|
||||||
|
invoke_context: invoke_context.clone(),
|
||||||
|
loader_id,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
bind_feature_gated_syscall_context_object!(
|
||||||
|
vm,
|
||||||
|
is_sysvar_via_syscall_active,
|
||||||
|
Box::new(SyscallGetFeesSysvar {
|
||||||
|
invoke_context: invoke_context.clone(),
|
||||||
|
loader_id,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
bind_feature_gated_syscall_context_object!(
|
||||||
|
vm,
|
||||||
|
is_sysvar_via_syscall_active,
|
||||||
|
Box::new(SyscallGetRentSysvar {
|
||||||
|
invoke_context: invoke_context.clone(),
|
||||||
|
loader_id,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Cross-program invocation syscalls
|
||||||
vm.bind_syscall_context_object(
|
vm.bind_syscall_context_object(
|
||||||
Box::new(SyscallInvokeSignedC {
|
Box::new(SyscallInvokeSignedC {
|
||||||
callers_keyed_accounts,
|
callers_keyed_accounts,
|
||||||
@ -825,6 +876,136 @@ impl<'a> SyscallObject<BpfError> for SyscallRistrettoMul<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_sysvar<T: std::fmt::Debug + Sysvar + SysvarId>(
|
||||||
|
id: &Pubkey,
|
||||||
|
var_addr: u64,
|
||||||
|
loader_id: &Pubkey,
|
||||||
|
memory_mapping: &MemoryMapping,
|
||||||
|
invoke_context: Rc<RefCell<&mut dyn InvokeContext>>,
|
||||||
|
) -> Result<u64, EbpfError<BpfError>> {
|
||||||
|
let mut invoke_context = invoke_context
|
||||||
|
.try_borrow_mut()
|
||||||
|
.map_err(|_| SyscallError::InvokeContextBorrowFailed)?;
|
||||||
|
|
||||||
|
invoke_context.get_compute_meter().consume(
|
||||||
|
invoke_context.get_bpf_compute_budget().sysvar_base_cost + size_of::<T>() as u64,
|
||||||
|
)?;
|
||||||
|
let var = translate_type_mut::<T>(memory_mapping, var_addr, loader_id)?;
|
||||||
|
|
||||||
|
let sysvar_data = invoke_context.get_sysvar_data(id).ok_or_else(|| {
|
||||||
|
ic_msg!(invoke_context, "Unable to get Sysvar {}", id);
|
||||||
|
SyscallError::InstructionError(InstructionError::UnsupportedSysvar)
|
||||||
|
})?;
|
||||||
|
|
||||||
|
*var = bincode::deserialize(&sysvar_data).map_err(|e| {
|
||||||
|
ic_msg!(invoke_context, "Unable to get Sysvar {}: {:?}", id, e);
|
||||||
|
SyscallError::InstructionError(InstructionError::UnsupportedSysvar)
|
||||||
|
})?;
|
||||||
|
|
||||||
|
Ok(SUCCESS)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get a Clock sysvar
|
||||||
|
struct SyscallGetClockSysvar<'a> {
|
||||||
|
invoke_context: Rc<RefCell<&'a mut dyn InvokeContext>>,
|
||||||
|
loader_id: &'a Pubkey,
|
||||||
|
}
|
||||||
|
impl<'a> SyscallObject<BpfError> for SyscallGetClockSysvar<'a> {
|
||||||
|
fn call(
|
||||||
|
&mut self,
|
||||||
|
var_addr: u64,
|
||||||
|
_arg2: u64,
|
||||||
|
_arg3: u64,
|
||||||
|
_arg4: u64,
|
||||||
|
_arg5: u64,
|
||||||
|
memory_mapping: &MemoryMapping,
|
||||||
|
result: &mut Result<u64, EbpfError<BpfError>>,
|
||||||
|
) {
|
||||||
|
*result = get_sysvar::<Clock>(
|
||||||
|
&sysvar::clock::id(),
|
||||||
|
var_addr,
|
||||||
|
self.loader_id,
|
||||||
|
memory_mapping,
|
||||||
|
self.invoke_context.clone(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// Get a EpochSchedule sysvar
|
||||||
|
struct SyscallGetEpochScheduleSysvar<'a> {
|
||||||
|
invoke_context: Rc<RefCell<&'a mut dyn InvokeContext>>,
|
||||||
|
loader_id: &'a Pubkey,
|
||||||
|
}
|
||||||
|
impl<'a> SyscallObject<BpfError> for SyscallGetEpochScheduleSysvar<'a> {
|
||||||
|
fn call(
|
||||||
|
&mut self,
|
||||||
|
var_addr: u64,
|
||||||
|
_arg2: u64,
|
||||||
|
_arg3: u64,
|
||||||
|
_arg4: u64,
|
||||||
|
_arg5: u64,
|
||||||
|
memory_mapping: &MemoryMapping,
|
||||||
|
result: &mut Result<u64, EbpfError<BpfError>>,
|
||||||
|
) {
|
||||||
|
*result = get_sysvar::<EpochSchedule>(
|
||||||
|
&sysvar::epoch_schedule::id(),
|
||||||
|
var_addr,
|
||||||
|
self.loader_id,
|
||||||
|
memory_mapping,
|
||||||
|
self.invoke_context.clone(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// Get a Fees sysvar
|
||||||
|
struct SyscallGetFeesSysvar<'a> {
|
||||||
|
invoke_context: Rc<RefCell<&'a mut dyn InvokeContext>>,
|
||||||
|
loader_id: &'a Pubkey,
|
||||||
|
}
|
||||||
|
impl<'a> SyscallObject<BpfError> for SyscallGetFeesSysvar<'a> {
|
||||||
|
fn call(
|
||||||
|
&mut self,
|
||||||
|
var_addr: u64,
|
||||||
|
_arg2: u64,
|
||||||
|
_arg3: u64,
|
||||||
|
_arg4: u64,
|
||||||
|
_arg5: u64,
|
||||||
|
memory_mapping: &MemoryMapping,
|
||||||
|
result: &mut Result<u64, EbpfError<BpfError>>,
|
||||||
|
) {
|
||||||
|
*result = get_sysvar::<Fees>(
|
||||||
|
&sysvar::fees::id(),
|
||||||
|
var_addr,
|
||||||
|
self.loader_id,
|
||||||
|
memory_mapping,
|
||||||
|
self.invoke_context.clone(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// Get a Rent sysvar
|
||||||
|
struct SyscallGetRentSysvar<'a> {
|
||||||
|
invoke_context: Rc<RefCell<&'a mut dyn InvokeContext>>,
|
||||||
|
loader_id: &'a Pubkey,
|
||||||
|
}
|
||||||
|
impl<'a> SyscallObject<BpfError> for SyscallGetRentSysvar<'a> {
|
||||||
|
fn call(
|
||||||
|
&mut self,
|
||||||
|
var_addr: u64,
|
||||||
|
_arg2: u64,
|
||||||
|
_arg3: u64,
|
||||||
|
_arg4: u64,
|
||||||
|
_arg5: u64,
|
||||||
|
memory_mapping: &MemoryMapping,
|
||||||
|
result: &mut Result<u64, EbpfError<BpfError>>,
|
||||||
|
) {
|
||||||
|
*result = get_sysvar::<Rent>(
|
||||||
|
&sysvar::rent::id(),
|
||||||
|
var_addr,
|
||||||
|
self.loader_id,
|
||||||
|
memory_mapping,
|
||||||
|
self.invoke_context.clone(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Cross-program invocation syscalls
|
// Cross-program invocation syscalls
|
||||||
|
|
||||||
struct AccountReferences<'a> {
|
struct AccountReferences<'a> {
|
||||||
@ -1739,8 +1920,9 @@ mod tests {
|
|||||||
use solana_rbpf::{memory_region::MemoryRegion, vm::Config};
|
use solana_rbpf::{memory_region::MemoryRegion, vm::Config};
|
||||||
use solana_sdk::{
|
use solana_sdk::{
|
||||||
bpf_loader,
|
bpf_loader,
|
||||||
|
fee_calculator::FeeCalculator,
|
||||||
hash::hashv,
|
hash::hashv,
|
||||||
process_instruction::{MockComputeMeter, MockLogger},
|
process_instruction::{MockComputeMeter, MockInvokeContext, MockLogger},
|
||||||
};
|
};
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
@ -2454,4 +2636,175 @@ mod tests {
|
|||||||
result
|
result
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_syscall_get_sysvar() {
|
||||||
|
// Test clock sysvar
|
||||||
|
{
|
||||||
|
let got_clock = Clock::default();
|
||||||
|
let got_clock_va = 2048;
|
||||||
|
|
||||||
|
let memory_mapping = MemoryMapping::new(
|
||||||
|
vec![MemoryRegion {
|
||||||
|
host_addr: &got_clock as *const _ as u64,
|
||||||
|
vm_addr: got_clock_va,
|
||||||
|
len: size_of::<Clock>() as u64,
|
||||||
|
vm_gap_shift: 63,
|
||||||
|
is_writable: true,
|
||||||
|
}],
|
||||||
|
&DEFAULT_CONFIG,
|
||||||
|
);
|
||||||
|
|
||||||
|
let src_clock = Clock {
|
||||||
|
slot: 1,
|
||||||
|
epoch_start_timestamp: 2,
|
||||||
|
epoch: 3,
|
||||||
|
leader_schedule_epoch: 4,
|
||||||
|
unix_timestamp: 5,
|
||||||
|
};
|
||||||
|
let mut invoke_context = MockInvokeContext::default();
|
||||||
|
let mut data = vec![];
|
||||||
|
bincode::serialize_into(&mut data, &src_clock).unwrap();
|
||||||
|
invoke_context
|
||||||
|
.sysvars
|
||||||
|
.push((sysvar::clock::id(), Some(Rc::new(data))));
|
||||||
|
|
||||||
|
let mut syscall = SyscallGetClockSysvar {
|
||||||
|
invoke_context: Rc::new(RefCell::new(&mut invoke_context)),
|
||||||
|
loader_id: &bpf_loader::id(),
|
||||||
|
};
|
||||||
|
let mut result: Result<u64, EbpfError<BpfError>> = Ok(0);
|
||||||
|
|
||||||
|
syscall.call(got_clock_va, 0, 0, 0, 0, &memory_mapping, &mut result);
|
||||||
|
result.unwrap();
|
||||||
|
assert_eq!(got_clock, src_clock);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test epoch_schedule sysvar
|
||||||
|
{
|
||||||
|
let got_epochschedule = EpochSchedule::default();
|
||||||
|
let got_epochschedule_va = 2048;
|
||||||
|
|
||||||
|
let memory_mapping = MemoryMapping::new(
|
||||||
|
vec![MemoryRegion {
|
||||||
|
host_addr: &got_epochschedule as *const _ as u64,
|
||||||
|
vm_addr: got_epochschedule_va,
|
||||||
|
len: size_of::<EpochSchedule>() as u64,
|
||||||
|
vm_gap_shift: 63,
|
||||||
|
is_writable: true,
|
||||||
|
}],
|
||||||
|
&DEFAULT_CONFIG,
|
||||||
|
);
|
||||||
|
|
||||||
|
let src_epochschedule = EpochSchedule {
|
||||||
|
slots_per_epoch: 1,
|
||||||
|
leader_schedule_slot_offset: 2,
|
||||||
|
warmup: false,
|
||||||
|
first_normal_epoch: 3,
|
||||||
|
first_normal_slot: 4,
|
||||||
|
};
|
||||||
|
let mut invoke_context = MockInvokeContext::default();
|
||||||
|
let mut data = vec![];
|
||||||
|
bincode::serialize_into(&mut data, &src_epochschedule).unwrap();
|
||||||
|
invoke_context
|
||||||
|
.sysvars
|
||||||
|
.push((sysvar::epoch_schedule::id(), Some(Rc::new(data))));
|
||||||
|
|
||||||
|
let mut syscall = SyscallGetEpochScheduleSysvar {
|
||||||
|
invoke_context: Rc::new(RefCell::new(&mut invoke_context)),
|
||||||
|
loader_id: &bpf_loader::id(),
|
||||||
|
};
|
||||||
|
let mut result: Result<u64, EbpfError<BpfError>> = Ok(0);
|
||||||
|
|
||||||
|
syscall.call(
|
||||||
|
got_epochschedule_va,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
&memory_mapping,
|
||||||
|
&mut result,
|
||||||
|
);
|
||||||
|
result.unwrap();
|
||||||
|
assert_eq!(got_epochschedule, src_epochschedule);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test fees sysvar
|
||||||
|
{
|
||||||
|
let got_fees = Fees::default();
|
||||||
|
let got_fees_va = 2048;
|
||||||
|
|
||||||
|
let memory_mapping = MemoryMapping::new(
|
||||||
|
vec![MemoryRegion {
|
||||||
|
host_addr: &got_fees as *const _ as u64,
|
||||||
|
vm_addr: got_fees_va,
|
||||||
|
len: size_of::<Fees>() as u64,
|
||||||
|
vm_gap_shift: 63,
|
||||||
|
is_writable: true,
|
||||||
|
}],
|
||||||
|
&DEFAULT_CONFIG,
|
||||||
|
);
|
||||||
|
|
||||||
|
let src_fees = Fees {
|
||||||
|
fee_calculator: FeeCalculator {
|
||||||
|
lamports_per_signature: 1,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
let mut invoke_context = MockInvokeContext::default();
|
||||||
|
let mut data = vec![];
|
||||||
|
bincode::serialize_into(&mut data, &src_fees).unwrap();
|
||||||
|
invoke_context
|
||||||
|
.sysvars
|
||||||
|
.push((sysvar::fees::id(), Some(Rc::new(data))));
|
||||||
|
|
||||||
|
let mut syscall = SyscallGetFeesSysvar {
|
||||||
|
invoke_context: Rc::new(RefCell::new(&mut invoke_context)),
|
||||||
|
loader_id: &bpf_loader::id(),
|
||||||
|
};
|
||||||
|
let mut result: Result<u64, EbpfError<BpfError>> = Ok(0);
|
||||||
|
|
||||||
|
syscall.call(got_fees_va, 0, 0, 0, 0, &memory_mapping, &mut result);
|
||||||
|
result.unwrap();
|
||||||
|
assert_eq!(got_fees, src_fees);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test rent sysvar
|
||||||
|
{
|
||||||
|
let got_rent = Rent::default();
|
||||||
|
let got_rent_va = 2048;
|
||||||
|
|
||||||
|
let memory_mapping = MemoryMapping::new(
|
||||||
|
vec![MemoryRegion {
|
||||||
|
host_addr: &got_rent as *const _ as u64,
|
||||||
|
vm_addr: got_rent_va,
|
||||||
|
len: size_of::<Rent>() as u64,
|
||||||
|
vm_gap_shift: 63,
|
||||||
|
is_writable: true,
|
||||||
|
}],
|
||||||
|
&DEFAULT_CONFIG,
|
||||||
|
);
|
||||||
|
|
||||||
|
let src_rent = Rent {
|
||||||
|
lamports_per_byte_year: 1,
|
||||||
|
exemption_threshold: 2.0,
|
||||||
|
burn_percent: 3,
|
||||||
|
};
|
||||||
|
let mut invoke_context = MockInvokeContext::default();
|
||||||
|
let mut data = vec![];
|
||||||
|
bincode::serialize_into(&mut data, &src_rent).unwrap();
|
||||||
|
invoke_context
|
||||||
|
.sysvars
|
||||||
|
.push((sysvar::rent::id(), Some(Rc::new(data))));
|
||||||
|
|
||||||
|
let mut syscall = SyscallGetRentSysvar {
|
||||||
|
invoke_context: Rc::new(RefCell::new(&mut invoke_context)),
|
||||||
|
loader_id: &bpf_loader::id(),
|
||||||
|
};
|
||||||
|
let mut result: Result<u64, EbpfError<BpfError>> = Ok(0);
|
||||||
|
|
||||||
|
syscall.call(got_rent_va, 0, 0, 0, 0, &memory_mapping, &mut result);
|
||||||
|
result.unwrap();
|
||||||
|
assert_eq!(got_rent, src_rent);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -2937,6 +2937,8 @@ impl Bank {
|
|||||||
self.feature_set.clone(),
|
self.feature_set.clone(),
|
||||||
bpf_compute_budget,
|
bpf_compute_budget,
|
||||||
&mut timings.details,
|
&mut timings.details,
|
||||||
|
self.rc.accounts.clone(),
|
||||||
|
&self.ancestors,
|
||||||
);
|
);
|
||||||
|
|
||||||
if enable_log_recording {
|
if enable_log_recording {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
instruction_recorder::InstructionRecorder, log_collector::LogCollector,
|
accounts::Accounts, accounts_index::Ancestors, instruction_recorder::InstructionRecorder,
|
||||||
native_loader::NativeLoader, rent_collector::RentCollector,
|
log_collector::LogCollector, native_loader::NativeLoader, rent_collector::RentCollector,
|
||||||
};
|
};
|
||||||
use log::*;
|
use log::*;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
@ -263,6 +263,9 @@ pub struct ThisInvokeContext<'a> {
|
|||||||
instruction_recorder: Option<InstructionRecorder>,
|
instruction_recorder: Option<InstructionRecorder>,
|
||||||
feature_set: Arc<FeatureSet>,
|
feature_set: Arc<FeatureSet>,
|
||||||
pub timings: ExecuteDetailsTimings,
|
pub timings: ExecuteDetailsTimings,
|
||||||
|
account_db: Arc<Accounts>,
|
||||||
|
ancestors: &'a Ancestors,
|
||||||
|
sysvars: Vec<(Pubkey, Option<Rc<Vec<u8>>>)>,
|
||||||
}
|
}
|
||||||
impl<'a> ThisInvokeContext<'a> {
|
impl<'a> ThisInvokeContext<'a> {
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
@ -278,6 +281,8 @@ impl<'a> ThisInvokeContext<'a> {
|
|||||||
executors: Rc<RefCell<Executors>>,
|
executors: Rc<RefCell<Executors>>,
|
||||||
instruction_recorder: Option<InstructionRecorder>,
|
instruction_recorder: Option<InstructionRecorder>,
|
||||||
feature_set: Arc<FeatureSet>,
|
feature_set: Arc<FeatureSet>,
|
||||||
|
account_db: Arc<Accounts>,
|
||||||
|
ancestors: &'a Ancestors,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let mut program_ids = Vec::with_capacity(bpf_compute_budget.max_invoke_depth);
|
let mut program_ids = Vec::with_capacity(bpf_compute_budget.max_invoke_depth);
|
||||||
program_ids.push(*program_id);
|
program_ids.push(*program_id);
|
||||||
@ -297,6 +302,9 @@ impl<'a> ThisInvokeContext<'a> {
|
|||||||
instruction_recorder,
|
instruction_recorder,
|
||||||
feature_set,
|
feature_set,
|
||||||
timings: ExecuteDetailsTimings::default(),
|
timings: ExecuteDetailsTimings::default(),
|
||||||
|
account_db,
|
||||||
|
ancestors,
|
||||||
|
sysvars: vec![],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -416,6 +424,23 @@ impl<'a> InvokeContext for ThisInvokeContext<'a> {
|
|||||||
self.timings.execute_us += execute_us;
|
self.timings.execute_us += execute_us;
|
||||||
self.timings.deserialize_us += deserialize_us;
|
self.timings.deserialize_us += deserialize_us;
|
||||||
}
|
}
|
||||||
|
fn get_sysvar_data(&mut self, id: &Pubkey) -> Option<Rc<Vec<u8>>> {
|
||||||
|
// Try share from cache
|
||||||
|
let mut result =
|
||||||
|
self.sysvars
|
||||||
|
.iter()
|
||||||
|
.find_map(|(key, sysvar)| if id == key { sysvar.clone() } else { None });
|
||||||
|
if result.is_none() {
|
||||||
|
// Load it
|
||||||
|
result = self
|
||||||
|
.account_db
|
||||||
|
.load_slow(self.ancestors, id)
|
||||||
|
.map(|(account, _)| Rc::new(account.data().clone()));
|
||||||
|
// Cache it
|
||||||
|
self.sysvars.push((*id, result.clone()));
|
||||||
|
}
|
||||||
|
result
|
||||||
|
}
|
||||||
}
|
}
|
||||||
pub struct ThisLogger {
|
pub struct ThisLogger {
|
||||||
log_collector: Option<Rc<LogCollector>>,
|
log_collector: Option<Rc<LogCollector>>,
|
||||||
@ -1046,6 +1071,8 @@ impl MessageProcessor {
|
|||||||
bpf_compute_budget: BpfComputeBudget,
|
bpf_compute_budget: BpfComputeBudget,
|
||||||
timings: &mut ExecuteDetailsTimings,
|
timings: &mut ExecuteDetailsTimings,
|
||||||
demote_sysvar_write_locks: bool,
|
demote_sysvar_write_locks: bool,
|
||||||
|
account_db: Arc<Accounts>,
|
||||||
|
ancestors: &Ancestors,
|
||||||
) -> Result<(), InstructionError> {
|
) -> Result<(), InstructionError> {
|
||||||
// Fixup the special instructions key if present
|
// Fixup the special instructions key if present
|
||||||
// before the account pre-values are taken care of
|
// before the account pre-values are taken care of
|
||||||
@ -1076,6 +1103,8 @@ impl MessageProcessor {
|
|||||||
executors,
|
executors,
|
||||||
instruction_recorder,
|
instruction_recorder,
|
||||||
feature_set,
|
feature_set,
|
||||||
|
account_db,
|
||||||
|
ancestors,
|
||||||
);
|
);
|
||||||
let keyed_accounts = Self::create_keyed_accounts(
|
let keyed_accounts = Self::create_keyed_accounts(
|
||||||
message,
|
message,
|
||||||
@ -1124,6 +1153,8 @@ impl MessageProcessor {
|
|||||||
feature_set: Arc<FeatureSet>,
|
feature_set: Arc<FeatureSet>,
|
||||||
bpf_compute_budget: BpfComputeBudget,
|
bpf_compute_budget: BpfComputeBudget,
|
||||||
timings: &mut ExecuteDetailsTimings,
|
timings: &mut ExecuteDetailsTimings,
|
||||||
|
account_db: Arc<Accounts>,
|
||||||
|
ancestors: &Ancestors,
|
||||||
) -> Result<(), TransactionError> {
|
) -> Result<(), TransactionError> {
|
||||||
let demote_sysvar_write_locks = feature_set.is_active(&demote_sysvar_write_locks::id());
|
let demote_sysvar_write_locks = feature_set.is_active(&demote_sysvar_write_locks::id());
|
||||||
for (instruction_index, instruction) in message.instructions.iter().enumerate() {
|
for (instruction_index, instruction) in message.instructions.iter().enumerate() {
|
||||||
@ -1145,6 +1176,8 @@ impl MessageProcessor {
|
|||||||
bpf_compute_budget,
|
bpf_compute_budget,
|
||||||
timings,
|
timings,
|
||||||
demote_sysvar_write_locks,
|
demote_sysvar_write_locks,
|
||||||
|
account_db.clone(),
|
||||||
|
ancestors,
|
||||||
)
|
)
|
||||||
.map_err(|err| TransactionError::InstructionError(instruction_index as u8, err))?;
|
.map_err(|err| TransactionError::InstructionError(instruction_index as u8, err))?;
|
||||||
}
|
}
|
||||||
@ -1184,6 +1217,7 @@ mod tests {
|
|||||||
pre_accounts.push(PreAccount::new(program_id, &account.clone()));
|
pre_accounts.push(PreAccount::new(program_id, &account.clone()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let ancestors = Ancestors::default();
|
||||||
let mut invoke_context = ThisInvokeContext::new(
|
let mut invoke_context = ThisInvokeContext::new(
|
||||||
&program_ids[0],
|
&program_ids[0],
|
||||||
Rent::default(),
|
Rent::default(),
|
||||||
@ -1196,6 +1230,8 @@ mod tests {
|
|||||||
Rc::new(RefCell::new(Executors::default())),
|
Rc::new(RefCell::new(Executors::default())),
|
||||||
None,
|
None,
|
||||||
Arc::new(FeatureSet::all_enabled()),
|
Arc::new(FeatureSet::all_enabled()),
|
||||||
|
Arc::new(Accounts::default()),
|
||||||
|
&ancestors,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Check call depth increases and has a limit
|
// Check call depth increases and has a limit
|
||||||
@ -1748,6 +1784,7 @@ mod tests {
|
|||||||
loaders.push(vec![(mock_system_program_id, account)]);
|
loaders.push(vec![(mock_system_program_id, account)]);
|
||||||
|
|
||||||
let executors = Rc::new(RefCell::new(Executors::default()));
|
let executors = Rc::new(RefCell::new(Executors::default()));
|
||||||
|
let ancestors = Ancestors::default();
|
||||||
|
|
||||||
let from_pubkey = solana_sdk::pubkey::new_rand();
|
let from_pubkey = solana_sdk::pubkey::new_rand();
|
||||||
let to_pubkey = solana_sdk::pubkey::new_rand();
|
let to_pubkey = solana_sdk::pubkey::new_rand();
|
||||||
@ -1776,6 +1813,8 @@ mod tests {
|
|||||||
Arc::new(FeatureSet::all_enabled()),
|
Arc::new(FeatureSet::all_enabled()),
|
||||||
BpfComputeBudget::new(),
|
BpfComputeBudget::new(),
|
||||||
&mut ExecuteDetailsTimings::default(),
|
&mut ExecuteDetailsTimings::default(),
|
||||||
|
Arc::new(Accounts::default()),
|
||||||
|
&ancestors,
|
||||||
);
|
);
|
||||||
assert_eq!(result, Ok(()));
|
assert_eq!(result, Ok(()));
|
||||||
assert_eq!(accounts[0].borrow().lamports, 100);
|
assert_eq!(accounts[0].borrow().lamports, 100);
|
||||||
@ -1802,6 +1841,8 @@ mod tests {
|
|||||||
Arc::new(FeatureSet::all_enabled()),
|
Arc::new(FeatureSet::all_enabled()),
|
||||||
BpfComputeBudget::new(),
|
BpfComputeBudget::new(),
|
||||||
&mut ExecuteDetailsTimings::default(),
|
&mut ExecuteDetailsTimings::default(),
|
||||||
|
Arc::new(Accounts::default()),
|
||||||
|
&ancestors,
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
result,
|
result,
|
||||||
@ -1832,6 +1873,8 @@ mod tests {
|
|||||||
Arc::new(FeatureSet::all_enabled()),
|
Arc::new(FeatureSet::all_enabled()),
|
||||||
BpfComputeBudget::new(),
|
BpfComputeBudget::new(),
|
||||||
&mut ExecuteDetailsTimings::default(),
|
&mut ExecuteDetailsTimings::default(),
|
||||||
|
Arc::new(Accounts::default()),
|
||||||
|
&ancestors,
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
result,
|
result,
|
||||||
@ -1917,6 +1960,7 @@ mod tests {
|
|||||||
loaders.push(vec![(mock_program_id, account)]);
|
loaders.push(vec![(mock_program_id, account)]);
|
||||||
|
|
||||||
let executors = Rc::new(RefCell::new(Executors::default()));
|
let executors = Rc::new(RefCell::new(Executors::default()));
|
||||||
|
let ancestors = Ancestors::default();
|
||||||
|
|
||||||
let from_pubkey = solana_sdk::pubkey::new_rand();
|
let from_pubkey = solana_sdk::pubkey::new_rand();
|
||||||
let to_pubkey = solana_sdk::pubkey::new_rand();
|
let to_pubkey = solana_sdk::pubkey::new_rand();
|
||||||
@ -1948,6 +1992,8 @@ mod tests {
|
|||||||
Arc::new(FeatureSet::all_enabled()),
|
Arc::new(FeatureSet::all_enabled()),
|
||||||
BpfComputeBudget::new(),
|
BpfComputeBudget::new(),
|
||||||
&mut ExecuteDetailsTimings::default(),
|
&mut ExecuteDetailsTimings::default(),
|
||||||
|
Arc::new(Accounts::default()),
|
||||||
|
&ancestors,
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
result,
|
result,
|
||||||
@ -1978,6 +2024,8 @@ mod tests {
|
|||||||
Arc::new(FeatureSet::all_enabled()),
|
Arc::new(FeatureSet::all_enabled()),
|
||||||
BpfComputeBudget::new(),
|
BpfComputeBudget::new(),
|
||||||
&mut ExecuteDetailsTimings::default(),
|
&mut ExecuteDetailsTimings::default(),
|
||||||
|
Arc::new(Accounts::default()),
|
||||||
|
&ancestors,
|
||||||
);
|
);
|
||||||
assert_eq!(result, Ok(()));
|
assert_eq!(result, Ok(()));
|
||||||
|
|
||||||
@ -1993,6 +2041,7 @@ mod tests {
|
|||||||
)],
|
)],
|
||||||
Some(&from_pubkey),
|
Some(&from_pubkey),
|
||||||
);
|
);
|
||||||
|
let ancestors = Ancestors::default();
|
||||||
let result = message_processor.process_message(
|
let result = message_processor.process_message(
|
||||||
&message,
|
&message,
|
||||||
&loaders,
|
&loaders,
|
||||||
@ -2005,6 +2054,8 @@ mod tests {
|
|||||||
Arc::new(FeatureSet::all_enabled()),
|
Arc::new(FeatureSet::all_enabled()),
|
||||||
BpfComputeBudget::new(),
|
BpfComputeBudget::new(),
|
||||||
&mut ExecuteDetailsTimings::default(),
|
&mut ExecuteDetailsTimings::default(),
|
||||||
|
Arc::new(Accounts::default()),
|
||||||
|
&ancestors,
|
||||||
);
|
);
|
||||||
assert_eq!(result, Ok(()));
|
assert_eq!(result, Ok(()));
|
||||||
assert_eq!(accounts[0].borrow().lamports, 80);
|
assert_eq!(accounts[0].borrow().lamports, 80);
|
||||||
@ -2078,6 +2129,7 @@ mod tests {
|
|||||||
];
|
];
|
||||||
let programs: Vec<(_, ProcessInstructionWithContext)> =
|
let programs: Vec<(_, ProcessInstructionWithContext)> =
|
||||||
vec![(callee_program_id, mock_process_instruction)];
|
vec![(callee_program_id, mock_process_instruction)];
|
||||||
|
let ancestors = Ancestors::default();
|
||||||
let mut invoke_context = ThisInvokeContext::new(
|
let mut invoke_context = ThisInvokeContext::new(
|
||||||
&caller_program_id,
|
&caller_program_id,
|
||||||
Rent::default(),
|
Rent::default(),
|
||||||
@ -2094,6 +2146,8 @@ mod tests {
|
|||||||
Rc::new(RefCell::new(Executors::default())),
|
Rc::new(RefCell::new(Executors::default())),
|
||||||
None,
|
None,
|
||||||
Arc::new(FeatureSet::all_enabled()),
|
Arc::new(FeatureSet::all_enabled()),
|
||||||
|
Arc::new(Accounts::default()),
|
||||||
|
&ancestors,
|
||||||
);
|
);
|
||||||
let metas = vec![
|
let metas = vec![
|
||||||
AccountMeta::new(owned_key, false),
|
AccountMeta::new(owned_key, false),
|
||||||
|
@ -82,7 +82,7 @@ pub type UnixTimestamp = i64;
|
|||||||
/// as the network progresses).
|
/// as the network progresses).
|
||||||
///
|
///
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Serialize, Deserialize, Debug, Default, PartialEq)]
|
#[derive(Serialize, Clone, Deserialize, Debug, Default, PartialEq)]
|
||||||
pub struct Clock {
|
pub struct Clock {
|
||||||
/// the current network/bank Slot
|
/// the current network/bank Slot
|
||||||
pub slot: Slot,
|
pub slot: Slot,
|
||||||
|
@ -2,7 +2,10 @@
|
|||||||
|
|
||||||
#![cfg(not(target_arch = "bpf"))]
|
#![cfg(not(target_arch = "bpf"))]
|
||||||
|
|
||||||
use crate::{account_info::AccountInfo, entrypoint::ProgramResult, instruction::Instruction};
|
use crate::{
|
||||||
|
account_info::AccountInfo, entrypoint::ProgramResult, instruction::Instruction,
|
||||||
|
program_error::UNSUPPORTED_SYSVAR,
|
||||||
|
};
|
||||||
use std::sync::{Arc, RwLock};
|
use std::sync::{Arc, RwLock};
|
||||||
|
|
||||||
lazy_static::lazy_static! {
|
lazy_static::lazy_static! {
|
||||||
@ -31,6 +34,19 @@ pub trait SyscallStubs: Sync + Send {
|
|||||||
sol_log("SyscallStubs: sol_invoke_signed() not available");
|
sol_log("SyscallStubs: sol_invoke_signed() not available");
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn sol_get_clock_sysvar(&self, _var_addr: *mut u8) -> u64 {
|
||||||
|
UNSUPPORTED_SYSVAR
|
||||||
|
}
|
||||||
|
fn sol_get_epoch_schedule_sysvar(&self, _var_addr: *mut u8) -> u64 {
|
||||||
|
UNSUPPORTED_SYSVAR
|
||||||
|
}
|
||||||
|
fn sol_get_fees_sysvar(&self, _var_addr: *mut u8) -> u64 {
|
||||||
|
UNSUPPORTED_SYSVAR
|
||||||
|
}
|
||||||
|
fn sol_get_rent_sysvar(&self, _var_addr: *mut u8) -> u64 {
|
||||||
|
UNSUPPORTED_SYSVAR
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct DefaultSyscallStubs {}
|
struct DefaultSyscallStubs {}
|
||||||
@ -61,3 +77,22 @@ pub(crate) fn sol_invoke_signed(
|
|||||||
.unwrap()
|
.unwrap()
|
||||||
.sol_invoke_signed(instruction, account_infos, signers_seeds)
|
.sol_invoke_signed(instruction, account_infos, signers_seeds)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn sol_get_clock_sysvar(var_addr: *mut u8) -> u64 {
|
||||||
|
SYSCALL_STUBS.read().unwrap().sol_get_clock_sysvar(var_addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn sol_get_epoch_schedule_sysvar(var_addr: *mut u8) -> u64 {
|
||||||
|
SYSCALL_STUBS
|
||||||
|
.read()
|
||||||
|
.unwrap()
|
||||||
|
.sol_get_epoch_schedule_sysvar(var_addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn sol_get_fees_sysvar(var_addr: *mut u8) -> u64 {
|
||||||
|
SYSCALL_STUBS.read().unwrap().sol_get_fees_sysvar(var_addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn sol_get_rent_sysvar(var_addr: *mut u8) -> u64 {
|
||||||
|
SYSCALL_STUBS.read().unwrap().sol_get_rent_sysvar(var_addr)
|
||||||
|
}
|
||||||
|
@ -2,8 +2,10 @@
|
|||||||
//!
|
//!
|
||||||
pub use crate::clock::Clock;
|
pub use crate::clock::Clock;
|
||||||
|
|
||||||
use crate::sysvar::Sysvar;
|
use crate::{impl_sysvar_get, program_error::ProgramError, sysvar::Sysvar};
|
||||||
|
|
||||||
crate::declare_sysvar_id!("SysvarC1ock11111111111111111111111111111111", Clock);
|
crate::declare_sysvar_id!("SysvarC1ock11111111111111111111111111111111", Clock);
|
||||||
|
|
||||||
impl Sysvar for Clock {}
|
impl Sysvar for Clock {
|
||||||
|
impl_sysvar_get!(sol_get_clock_sysvar);
|
||||||
|
}
|
||||||
|
@ -1,8 +1,11 @@
|
|||||||
//! This account contains the current cluster rent
|
//! This account contains the current cluster rent
|
||||||
//!
|
//!
|
||||||
pub use crate::epoch_schedule::EpochSchedule;
|
pub use crate::epoch_schedule::EpochSchedule;
|
||||||
use crate::sysvar::Sysvar;
|
|
||||||
|
use crate::{impl_sysvar_get, program_error::ProgramError, sysvar::Sysvar};
|
||||||
|
|
||||||
crate::declare_sysvar_id!("SysvarEpochSchedu1e111111111111111111111111", EpochSchedule);
|
crate::declare_sysvar_id!("SysvarEpochSchedu1e111111111111111111111111", EpochSchedule);
|
||||||
|
|
||||||
impl Sysvar for EpochSchedule {}
|
impl Sysvar for EpochSchedule {
|
||||||
|
impl_sysvar_get!(sol_get_epoch_schedule_sysvar);
|
||||||
|
}
|
||||||
|
@ -1,11 +1,13 @@
|
|||||||
//! This account contains the current cluster fees
|
//! This account contains the current cluster fees
|
||||||
//!
|
//!
|
||||||
use crate::{fee_calculator::FeeCalculator, sysvar::Sysvar};
|
use crate::{
|
||||||
|
fee_calculator::FeeCalculator, impl_sysvar_get, program_error::ProgramError, sysvar::Sysvar,
|
||||||
|
};
|
||||||
|
|
||||||
crate::declare_sysvar_id!("SysvarFees111111111111111111111111111111111", Fees);
|
crate::declare_sysvar_id!("SysvarFees111111111111111111111111111111111", Fees);
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Serialize, Deserialize, Debug, Default)]
|
#[derive(Serialize, Deserialize, Clone, Debug, Default, PartialEq)]
|
||||||
pub struct Fees {
|
pub struct Fees {
|
||||||
pub fee_calculator: FeeCalculator,
|
pub fee_calculator: FeeCalculator,
|
||||||
}
|
}
|
||||||
@ -17,4 +19,6 @@ impl Fees {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Sysvar for Fees {}
|
impl Sysvar for Fees {
|
||||||
|
impl_sysvar_get!(sol_get_fees_sysvar);
|
||||||
|
}
|
||||||
|
@ -47,14 +47,14 @@ macro_rules! declare_sysvar_id(
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
// owner pubkey for sysvar accounts
|
// Owner pubkey for sysvar accounts
|
||||||
crate::declare_id!("Sysvar1111111111111111111111111111111111111");
|
crate::declare_id!("Sysvar1111111111111111111111111111111111111");
|
||||||
|
|
||||||
pub trait SysvarId {
|
pub trait SysvarId {
|
||||||
fn check_id(pubkey: &Pubkey) -> bool;
|
fn check_id(pubkey: &Pubkey) -> bool;
|
||||||
}
|
}
|
||||||
|
|
||||||
// utilities for moving into and out of Accounts
|
// Sysvar utilities
|
||||||
pub trait Sysvar:
|
pub trait Sysvar:
|
||||||
SysvarId + Default + Sized + serde::Serialize + serde::de::DeserializeOwned
|
SysvarId + Default + Sized + serde::Serialize + serde::de::DeserializeOwned
|
||||||
{
|
{
|
||||||
@ -70,6 +70,34 @@ pub trait Sysvar:
|
|||||||
fn to_account_info(&self, account_info: &mut AccountInfo) -> Option<()> {
|
fn to_account_info(&self, account_info: &mut AccountInfo) -> Option<()> {
|
||||||
bincode::serialize_into(&mut account_info.data.borrow_mut()[..], self).ok()
|
bincode::serialize_into(&mut account_info.data.borrow_mut()[..], self).ok()
|
||||||
}
|
}
|
||||||
|
fn get() -> Result<Self, ProgramError> {
|
||||||
|
Err(ProgramError::UnsupportedSysvar)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! impl_sysvar_get {
|
||||||
|
($syscall_name:ident) => {
|
||||||
|
fn get() -> Result<Self, ProgramError> {
|
||||||
|
let mut var = Self::default();
|
||||||
|
let var_addr = &mut var as *mut _ as *mut u8;
|
||||||
|
|
||||||
|
#[cfg(target_arch = "bpf")]
|
||||||
|
let result = unsafe {
|
||||||
|
extern "C" {
|
||||||
|
fn $syscall_name(var_addr: *mut u8) -> u64;
|
||||||
|
}
|
||||||
|
$syscall_name(var_addr)
|
||||||
|
};
|
||||||
|
#[cfg(not(target_arch = "bpf"))]
|
||||||
|
let result = crate::program_stubs::$syscall_name(var_addr);
|
||||||
|
|
||||||
|
match result {
|
||||||
|
crate::entrypoint::SUCCESS => Ok(var),
|
||||||
|
e => Err(e.into()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
@ -2,8 +2,10 @@
|
|||||||
//!
|
//!
|
||||||
pub use crate::rent::Rent;
|
pub use crate::rent::Rent;
|
||||||
|
|
||||||
use crate::sysvar::Sysvar;
|
use crate::{impl_sysvar_get, program_error::ProgramError, sysvar::Sysvar};
|
||||||
|
|
||||||
crate::declare_sysvar_id!("SysvarRent111111111111111111111111111111111", Rent);
|
crate::declare_sysvar_id!("SysvarRent111111111111111111111111111111111", Rent);
|
||||||
|
|
||||||
impl Sysvar for Rent {}
|
impl Sysvar for Rent {
|
||||||
|
impl_sysvar_get!(sol_get_rent_sysvar);
|
||||||
|
}
|
||||||
|
@ -127,6 +127,10 @@ pub mod demote_sysvar_write_locks {
|
|||||||
solana_sdk::declare_id!("86LJYRuq2zgtHuL3FccR6hqFJQMQkFoun4knAxcPiF1P");
|
solana_sdk::declare_id!("86LJYRuq2zgtHuL3FccR6hqFJQMQkFoun4knAxcPiF1P");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub mod sysvar_via_syscall {
|
||||||
|
solana_sdk::declare_id!("7411E6gFQLDhQkdRjmpXwM1hzHMMoYQUjHicmvGPC1Nf");
|
||||||
|
}
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
/// Map of feature identifiers to user-visible description
|
/// Map of feature identifiers to user-visible description
|
||||||
pub static ref FEATURE_NAMES: HashMap<Pubkey, &'static str> = [
|
pub static ref FEATURE_NAMES: HashMap<Pubkey, &'static str> = [
|
||||||
@ -159,6 +163,7 @@ lazy_static! {
|
|||||||
(cpi_data_cost::id(), "charge the compute budget for data passed via CPI"),
|
(cpi_data_cost::id(), "charge the compute budget for data passed via CPI"),
|
||||||
(upgradeable_close_instruction::id(), "close upgradeable buffer accounts"),
|
(upgradeable_close_instruction::id(), "close upgradeable buffer accounts"),
|
||||||
(demote_sysvar_write_locks::id(), "demote builtins and sysvar write locks to readonly #15497"),
|
(demote_sysvar_write_locks::id(), "demote builtins and sysvar write locks to readonly #15497"),
|
||||||
|
(sysvar_via_syscall::id(), "Provide sysvars via syscalls"),
|
||||||
/*************** ADD NEW FEATURES HERE ***************/
|
/*************** ADD NEW FEATURES HERE ***************/
|
||||||
]
|
]
|
||||||
.iter()
|
.iter()
|
||||||
|
@ -7,7 +7,7 @@ use solana_sdk::{
|
|||||||
};
|
};
|
||||||
use std::{cell::RefCell, fmt::Debug, rc::Rc, sync::Arc};
|
use std::{cell::RefCell, fmt::Debug, rc::Rc, sync::Arc};
|
||||||
|
|
||||||
// Prototype of a native loader entry point
|
/// Prototype of a native loader entry point
|
||||||
///
|
///
|
||||||
/// program_id: Program ID of the currently executing program
|
/// program_id: Program ID of the currently executing program
|
||||||
/// keyed_accounts: Accounts passed as part of the instruction
|
/// keyed_accounts: Accounts passed as part of the instruction
|
||||||
@ -68,6 +68,8 @@ pub trait InvokeContext {
|
|||||||
execute_us: u64,
|
execute_us: u64,
|
||||||
deserialize_us: u64,
|
deserialize_us: u64,
|
||||||
);
|
);
|
||||||
|
/// Get sysvar data
|
||||||
|
fn get_sysvar_data(&mut self, id: &Pubkey) -> Option<Rc<Vec<u8>>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Convenience macro to log a message with an `Rc<RefCell<dyn Logger>>`
|
/// Convenience macro to log a message with an `Rc<RefCell<dyn Logger>>`
|
||||||
@ -130,6 +132,8 @@ pub struct BpfComputeBudget {
|
|||||||
pub max_cpi_instruction_size: usize,
|
pub max_cpi_instruction_size: usize,
|
||||||
/// Number of account data bytes per conpute unit charged during a cross-program invocation
|
/// Number of account data bytes per conpute unit charged during a cross-program invocation
|
||||||
pub cpi_bytes_per_unit: u64,
|
pub cpi_bytes_per_unit: u64,
|
||||||
|
/// Base number of compute units consumed to get a sysvar
|
||||||
|
pub sysvar_base_cost: u64,
|
||||||
}
|
}
|
||||||
impl Default for BpfComputeBudget {
|
impl Default for BpfComputeBudget {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
@ -152,6 +156,7 @@ impl BpfComputeBudget {
|
|||||||
log_pubkey_units: 100,
|
log_pubkey_units: 100,
|
||||||
max_cpi_instruction_size: 1280, // IPv6 Min MTU size
|
max_cpi_instruction_size: 1280, // IPv6 Min MTU size
|
||||||
cpi_bytes_per_unit: 250, // ~50MB at 200,000 units
|
cpi_bytes_per_unit: 250, // ~50MB at 200,000 units
|
||||||
|
sysvar_base_cost: 100,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -277,7 +282,9 @@ pub struct MockInvokeContext {
|
|||||||
pub bpf_compute_budget: BpfComputeBudget,
|
pub bpf_compute_budget: BpfComputeBudget,
|
||||||
pub compute_meter: MockComputeMeter,
|
pub compute_meter: MockComputeMeter,
|
||||||
pub programs: Vec<(Pubkey, ProcessInstructionWithContext)>,
|
pub programs: Vec<(Pubkey, ProcessInstructionWithContext)>,
|
||||||
|
pub accounts: Vec<(Pubkey, Rc<RefCell<AccountSharedData>>)>,
|
||||||
pub invoke_depth: usize,
|
pub invoke_depth: usize,
|
||||||
|
pub sysvars: Vec<(Pubkey, Option<Rc<Vec<u8>>>)>,
|
||||||
}
|
}
|
||||||
impl Default for MockInvokeContext {
|
impl Default for MockInvokeContext {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
@ -289,7 +296,9 @@ impl Default for MockInvokeContext {
|
|||||||
remaining: std::i64::MAX as u64,
|
remaining: std::i64::MAX as u64,
|
||||||
},
|
},
|
||||||
programs: vec![],
|
programs: vec![],
|
||||||
|
accounts: vec![],
|
||||||
invoke_depth: 0,
|
invoke_depth: 0,
|
||||||
|
sysvars: vec![],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -336,7 +345,12 @@ impl InvokeContext for MockInvokeContext {
|
|||||||
fn is_feature_active(&self, _feature_id: &Pubkey) -> bool {
|
fn is_feature_active(&self, _feature_id: &Pubkey) -> bool {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
fn get_account(&self, _pubkey: &Pubkey) -> Option<Rc<RefCell<AccountSharedData>>> {
|
fn get_account(&self, pubkey: &Pubkey) -> Option<Rc<RefCell<AccountSharedData>>> {
|
||||||
|
for (key, account) in self.accounts.iter() {
|
||||||
|
if key == pubkey {
|
||||||
|
return Some(account.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
fn update_timing(
|
fn update_timing(
|
||||||
@ -347,4 +361,9 @@ impl InvokeContext for MockInvokeContext {
|
|||||||
_deserialize_us: u64,
|
_deserialize_us: u64,
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
fn get_sysvar_data(&mut self, id: &Pubkey) -> Option<Rc<Vec<u8>>> {
|
||||||
|
self.sysvars
|
||||||
|
.iter()
|
||||||
|
.find_map(|(key, sysvar)| if id == key { sysvar.clone() } else { None })
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user