1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
//! Context for instruction execution.
//!
//! Caches sysvars, program id, and funder/recipients for account sets to use.
use crate::{
account_set::{CanAddLamports, CanFundRent},
prelude::*,
};
use pinocchio::sysvars::{clock::Clock, rent::Rent, Sysvar};
use std::cell::Cell;
/// Additional context given to [`crate::instruction::StarFrameInstruction`]s, enabling programs to cache and retrieve helpful information during instruction execution.
#[derive(Debug)]
pub struct Context {
/// The program id of the currently executing program.
program_id: &'static Pubkey,
// Rent cache to avoid repeated `Rent::get()` calls
rent_cache: Cell<Option<Rent>>,
// Clock cache to avoid repeated `Clock::get()` calls
clock_cache: Cell<Option<Clock>>,
// Cached recipient for rent. Usually set during `AccountSetValidate`
recipient: Option<Box<dyn CanAddLamports>>,
// Cached funder for rent. Usually set during `AccountSetValidate`
funder: Option<Box<dyn CanFundRent>>,
}
impl Default for Context {
fn default() -> Self {
static ZERO_PUBKEY: Pubkey = Pubkey::new_from_array([0; 32]);
Self::new(&ZERO_PUBKEY)
}
}
impl Context {
/// Create a new context from a program id.
#[must_use]
pub fn new(program_id: &'static Pubkey) -> Self {
Self {
program_id,
rent_cache: Cell::new(None),
clock_cache: Cell::new(None),
recipient: None,
funder: None,
}
}
/// Get the program id of the currently executing program.
pub fn current_program_id(&self) -> &Pubkey {
self.program_id
}
/// Gets the rent sysvar from the cache, populating the cache with a call to `Rent::get()` if empty.
pub fn get_rent(&self) -> Result<Rent> {
match self.rent_cache.get() {
None => {
let new_rent = Rent::get()?;
self.rent_cache.set(Some(new_rent));
Ok(new_rent)
}
Some(rent) => Ok(rent),
}
}
/// Gets the clock sysvar from the cache, populating the cache with a call to `Clock::get()` if empty.
pub fn get_clock(&self) -> Result<Clock> {
match self.clock_cache.get() {
None => {
let new_clock = Clock::get()?;
self.clock_cache.set(Some(new_clock));
Ok(new_clock)
}
Some(clock) => Ok(clock),
}
}
/// Gets the cached funder for rent if it has been set.
pub fn get_funder(&self) -> Option<&dyn CanFundRent> {
self.funder.as_ref().map(std::convert::AsRef::as_ref)
}
/// Sets the funder for rent.
pub fn set_funder(&mut self, funder: Box<dyn CanFundRent>) {
self.funder.replace(funder);
}
/// Gets the cached recipient for rent if it has been set.
pub fn get_recipient(&self) -> Option<&dyn CanAddLamports> {
self.recipient.as_ref().map(std::convert::AsRef::as_ref)
}
/// Sets the recipient for rent.
pub fn set_recipient(&mut self, recipient: Box<dyn CanAddLamports>) {
self.recipient.replace(recipient);
}
}