InvokeContext: Add get_sysvar() helper to sdk (backport #17360) (#17368)

* Add get_sysvar() helper to sdk

(cherry picked from commit 2c99b23ad7)

# Conflicts:
#	runtime/src/message_processor.rs
#	sdk/src/process_instruction.rs

* Resolve conflicts

Co-authored-by: Michael Vines <mvines@gmail.com>
This commit is contained in:
mergify[bot]
2021-05-21 03:42:20 +00:00
committed by GitHub
parent b1d294de75
commit 3f6964d264
3 changed files with 58 additions and 29 deletions

View File

@ -29,7 +29,7 @@ use solana_sdk::{
keccak, keccak,
keyed_account::KeyedAccount, keyed_account::KeyedAccount,
native_loader, native_loader,
process_instruction::{stable_log, ComputeMeter, InvokeContext, Logger}, process_instruction::{self, stable_log, ComputeMeter, InvokeContext, Logger},
pubkey::{Pubkey, PubkeyError, MAX_SEEDS}, pubkey::{Pubkey, PubkeyError, MAX_SEEDS},
rent::Rent, rent::Rent,
sysvar::{self, fees::Fees, Sysvar, SysvarId}, sysvar::{self, fees::Fees, Sysvar, SysvarId},
@ -960,8 +960,8 @@ fn get_sysvar<T: std::fmt::Debug + Sysvar + SysvarId>(
memory_mapping: &MemoryMapping, memory_mapping: &MemoryMapping,
invoke_context: Rc<RefCell<&mut dyn InvokeContext>>, invoke_context: Rc<RefCell<&mut dyn InvokeContext>>,
) -> Result<u64, EbpfError<BpfError>> { ) -> Result<u64, EbpfError<BpfError>> {
let mut invoke_context = invoke_context let invoke_context = invoke_context
.try_borrow_mut() .try_borrow()
.map_err(|_| SyscallError::InvokeContextBorrowFailed)?; .map_err(|_| SyscallError::InvokeContextBorrowFailed)?;
invoke_context.get_compute_meter().consume( invoke_context.get_compute_meter().consume(
@ -974,15 +974,8 @@ fn get_sysvar<T: std::fmt::Debug + Sysvar + SysvarId>(
invoke_context.is_feature_active(&enforce_aligned_host_addrs::id()), invoke_context.is_feature_active(&enforce_aligned_host_addrs::id()),
)?; )?;
let sysvar_data = invoke_context.get_sysvar_data(id).ok_or_else(|| { *var = process_instruction::get_sysvar::<T>(*invoke_context, id)
ic_msg!(invoke_context, "Unable to get Sysvar {}", id); .map_err(SyscallError::InstructionError)?;
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) Ok(SUCCESS)
} }

View File

@ -270,7 +270,8 @@ pub struct ThisInvokeContext<'a> {
pub timings: ExecuteDetailsTimings, pub timings: ExecuteDetailsTimings,
account_db: Arc<Accounts>, account_db: Arc<Accounts>,
ancestors: &'a Ancestors, ancestors: &'a Ancestors,
sysvars: Vec<(Pubkey, Option<Rc<Vec<u8>>>)>, #[allow(clippy::type_complexity)]
sysvars: RefCell<Vec<(Pubkey, Option<Rc<Vec<u8>>>)>>,
} }
impl<'a> ThisInvokeContext<'a> { impl<'a> ThisInvokeContext<'a> {
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
@ -309,7 +310,7 @@ impl<'a> ThisInvokeContext<'a> {
timings: ExecuteDetailsTimings::default(), timings: ExecuteDetailsTimings::default(),
account_db, account_db,
ancestors, ancestors,
sysvars: vec![], sysvars: RefCell::new(vec![]),
} }
} }
} }
@ -429,22 +430,25 @@ 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>>> { fn get_sysvar_data(&self, id: &Pubkey) -> Option<Rc<Vec<u8>>> {
// Try share from cache if let Ok(mut sysvars) = self.sysvars.try_borrow_mut() {
let mut result = // Try share from cache
self.sysvars let mut result = sysvars
.iter() .iter()
.find_map(|(key, sysvar)| if id == key { sysvar.clone() } else { None }); .find_map(|(key, sysvar)| if id == key { sysvar.clone() } else { None });
if result.is_none() { if result.is_none() {
// Load it // Load it
result = self result = self
.account_db .account_db
.load_slow(self.ancestors, id) .load_slow(self.ancestors, id)
.map(|(account, _)| Rc::new(account.data().clone())); .map(|(account, _)| Rc::new(account.data().clone()));
// Cache it // Cache it
self.sysvars.push((*id, result.clone())); sysvars.push((*id, result.clone()));
}
result
} else {
None
} }
result
} }
} }
pub struct ThisLogger { pub struct ThisLogger {

View File

@ -4,6 +4,7 @@ use solana_sdk::{
keyed_account::KeyedAccount, keyed_account::KeyedAccount,
message::Message, message::Message,
pubkey::Pubkey, pubkey::Pubkey,
sysvar::Sysvar,
}; };
use std::{cell::RefCell, fmt::Debug, rc::Rc, sync::Arc}; use std::{cell::RefCell, fmt::Debug, rc::Rc, sync::Arc};
@ -69,7 +70,7 @@ pub trait InvokeContext {
deserialize_us: u64, deserialize_us: u64,
); );
/// Get sysvar data /// Get sysvar data
fn get_sysvar_data(&mut self, id: &Pubkey) -> Option<Rc<Vec<u8>>>; fn get_sysvar_data(&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>>`
@ -102,6 +103,21 @@ macro_rules! ic_msg {
}; };
} }
pub fn get_sysvar<T: Sysvar>(
invoke_context: &dyn InvokeContext,
id: &Pubkey,
) -> Result<T, InstructionError> {
let sysvar_data = invoke_context.get_sysvar_data(id).ok_or_else(|| {
ic_msg!(invoke_context, "Unable to get sysvar {}", id);
InstructionError::UnsupportedSysvar
})?;
bincode::deserialize(&sysvar_data).map_err(|err| {
ic_msg!(invoke_context, "Unable to get sysvar {}: {:?}", id, err);
InstructionError::UnsupportedSysvar
})
}
#[derive(Clone, Copy, Debug, AbiExample)] #[derive(Clone, Copy, Debug, AbiExample)]
pub struct BpfComputeBudget { pub struct BpfComputeBudget {
/// Number of compute units that an instruction is allowed. Compute units /// Number of compute units that an instruction is allowed. Compute units
@ -302,6 +318,22 @@ impl Default for MockInvokeContext {
} }
} }
} }
pub fn mock_set_sysvar<T: Sysvar>(
mock_invoke_context: &mut MockInvokeContext,
id: Pubkey,
sysvar: T,
) -> Result<(), InstructionError> {
let mut data = Vec::with_capacity(T::size_of());
bincode::serialize_into(&mut data, &sysvar).map_err(|err| {
ic_msg!(mock_invoke_context, "Unable to serialize sysvar: {:?}", err);
InstructionError::GenericError
})?;
mock_invoke_context.sysvars.push((id, Some(Rc::new(data))));
Ok(())
}
impl InvokeContext for MockInvokeContext { impl InvokeContext for MockInvokeContext {
fn push(&mut self, _key: &Pubkey) -> Result<(), InstructionError> { fn push(&mut self, _key: &Pubkey) -> Result<(), InstructionError> {
self.invoke_depth = self.invoke_depth.saturating_add(1); self.invoke_depth = self.invoke_depth.saturating_add(1);
@ -361,7 +393,7 @@ impl InvokeContext for MockInvokeContext {
_deserialize_us: u64, _deserialize_us: u64,
) { ) {
} }
fn get_sysvar_data(&mut self, id: &Pubkey) -> Option<Rc<Vec<u8>>> { fn get_sysvar_data(&self, id: &Pubkey) -> Option<Rc<Vec<u8>>> {
self.sysvars self.sysvars
.iter() .iter()
.find_map(|(key, sysvar)| if id == key { sysvar.clone() } else { None }) .find_map(|(key, sysvar)| if id == key { sysvar.clone() } else { None })