Rename BpfComputeBudget (#18768)

This commit is contained in:
Jack May
2021-07-22 10:18:51 -07:00
committed by GitHub
parent cbe2ed47e2
commit 7fc4cfebc8
9 changed files with 280 additions and 168 deletions

View File

@ -1,9 +1,6 @@
#![cfg(feature = "full")]
use crate::{
process_instruction::BpfComputeBudget,
transaction::{Transaction, TransactionError},
};
use crate::transaction::{Transaction, TransactionError};
use borsh::{BorshDeserialize, BorshSchema, BorshSerialize};
use solana_sdk::{
borsh::try_from_slice_unchecked,
@ -32,70 +29,126 @@ pub enum ComputeBudgetInstruction {
/// allowed to consume.
RequestUnits(u64),
}
/// Create a `ComputeBudgetInstruction::RequestUnits` `Instruction`
pub fn request_units(units: u64) -> Instruction {
Instruction::new_with_borsh(id(), &ComputeBudgetInstruction::RequestUnits(units), vec![])
impl ComputeBudgetInstruction {
/// Create a `ComputeBudgetInstruction::RequestUnits` `Instruction`
pub fn request_units(units: u64) -> Instruction {
Instruction::new_with_borsh(id(), &ComputeBudgetInstruction::RequestUnits(units), vec![])
}
}
pub fn process_request(
compute_budget: &mut BpfComputeBudget,
tx: &Transaction,
) -> Result<(), TransactionError> {
let error = TransactionError::InstructionError(0, InstructionError::InvalidInstructionData);
// Compute budget instruction must be in 1st or 2nd instruction (avoid nonce marker)
for instruction in tx.message().instructions.iter().take(2) {
if check_id(instruction.program_id(&tx.message().account_keys)) {
let ComputeBudgetInstruction::RequestUnits(units) =
try_from_slice_unchecked::<ComputeBudgetInstruction>(&instruction.data)
.map_err(|_| error.clone())?;
if units > MAX_UNITS {
return Err(error);
}
compute_budget.max_units = units;
#[derive(Clone, Copy, Debug, AbiExample, PartialEq)]
pub struct ComputeBudget {
/// Number of compute units that an instruction is allowed. Compute units
/// are consumed by program execution, resources they use, etc...
pub max_units: u64,
/// Number of compute units consumed by a log_u64 call
pub log_64_units: u64,
/// Number of compute units consumed by a create_program_address call
pub create_program_address_units: u64,
/// Number of compute units consumed by an invoke call (not including the cost incurred by
/// the called program)
pub invoke_units: u64,
/// Maximum cross-program invocation depth allowed
pub max_invoke_depth: usize,
/// Base number of compute units consumed to call SHA256
pub sha256_base_cost: u64,
/// Incremental number of units consumed by SHA256 (based on bytes)
pub sha256_byte_cost: u64,
/// Maximum BPF to BPF call depth
pub max_call_depth: usize,
/// Size of a stack frame in bytes, must match the size specified in the LLVM BPF backend
pub stack_frame_size: usize,
/// Number of compute units consumed by logging a `Pubkey`
pub log_pubkey_units: u64,
/// Maximum cross-program invocation instruction size
pub max_cpi_instruction_size: usize,
/// Number of account data bytes per conpute unit charged during a cross-program invocation
pub cpi_bytes_per_unit: u64,
/// Base number of compute units consumed to get a sysvar
pub sysvar_base_cost: u64,
/// Number of compute units consumed to call secp256k1_recover
pub secp256k1_recover_cost: u64,
/// Optional program heap region size, if `None` then loader default
pub heap_size: Option<usize>,
}
impl Default for ComputeBudget {
fn default() -> Self {
Self::new()
}
}
impl ComputeBudget {
pub fn new() -> Self {
ComputeBudget {
max_units: 200_000,
log_64_units: 100,
create_program_address_units: 1500,
invoke_units: 1000,
max_invoke_depth: 4,
sha256_base_cost: 85,
sha256_byte_cost: 1,
max_call_depth: 64,
stack_frame_size: 4_096,
log_pubkey_units: 100,
max_cpi_instruction_size: 1280, // IPv6 Min MTU size
cpi_bytes_per_unit: 250, // ~50MB at 200,000 units
sysvar_base_cost: 100,
secp256k1_recover_cost: 25_000,
heap_size: None,
}
}
Ok(())
pub fn process_transaction(&mut self, tx: &Transaction) -> Result<(), TransactionError> {
let error = TransactionError::InstructionError(0, InstructionError::InvalidInstructionData);
// Compute budget instruction must be in 1st or 2nd instruction (avoid nonce marker)
for instruction in tx.message().instructions.iter().take(2) {
if check_id(instruction.program_id(&tx.message().account_keys)) {
let ComputeBudgetInstruction::RequestUnits(units) =
try_from_slice_unchecked::<ComputeBudgetInstruction>(&instruction.data)
.map_err(|_| error.clone())?;
if units > MAX_UNITS {
return Err(error);
}
self.max_units = units;
}
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{
compute_budget, hash::Hash, message::Message, pubkey::Pubkey, signature::Keypair,
signer::Signer,
};
use crate::{hash::Hash, message::Message, pubkey::Pubkey, signature::Keypair, signer::Signer};
#[test]
fn test_process_request() {
fn test_process_transaction() {
let payer_keypair = Keypair::new();
let mut compute_budget = BpfComputeBudget::default();
let mut compute_budget = ComputeBudget::default();
let tx = Transaction::new(
&[&payer_keypair],
Message::new(&[], Some(&payer_keypair.pubkey())),
Hash::default(),
);
process_request(&mut compute_budget, &tx).unwrap();
assert_eq!(compute_budget, BpfComputeBudget::default());
compute_budget.process_transaction(&tx).unwrap();
assert_eq!(compute_budget, ComputeBudget::default());
let tx = Transaction::new(
&[&payer_keypair],
Message::new(
&[
compute_budget::request_units(1),
ComputeBudgetInstruction::request_units(1),
Instruction::new_with_bincode(Pubkey::new_unique(), &0, vec![]),
],
Some(&payer_keypair.pubkey()),
),
Hash::default(),
);
process_request(&mut compute_budget, &tx).unwrap();
compute_budget.process_transaction(&tx).unwrap();
assert_eq!(
compute_budget,
BpfComputeBudget {
ComputeBudget {
max_units: 1,
..BpfComputeBudget::default()
..ComputeBudget::default()
}
);
@ -103,14 +156,14 @@ mod tests {
&[&payer_keypair],
Message::new(
&[
compute_budget::request_units(MAX_UNITS + 1),
ComputeBudgetInstruction::request_units(MAX_UNITS + 1),
Instruction::new_with_bincode(Pubkey::new_unique(), &0, vec![]),
],
Some(&payer_keypair.pubkey()),
),
Hash::default(),
);
let result = process_request(&mut compute_budget, &tx);
let result = compute_budget.process_transaction(&tx);
assert_eq!(
result,
Err(TransactionError::InstructionError(
@ -124,18 +177,18 @@ mod tests {
Message::new(
&[
Instruction::new_with_bincode(Pubkey::new_unique(), &0, vec![]),
compute_budget::request_units(MAX_UNITS),
ComputeBudgetInstruction::request_units(MAX_UNITS),
],
Some(&payer_keypair.pubkey()),
),
Hash::default(),
);
process_request(&mut compute_budget, &tx).unwrap();
compute_budget.process_transaction(&tx).unwrap();
assert_eq!(
compute_budget,
BpfComputeBudget {
ComputeBudget {
max_units: MAX_UNITS,
..BpfComputeBudget::default()
..ComputeBudget::default()
}
);
}

View File

@ -1,3 +1,5 @@
#![cfg(feature = "full")]
use crate::{
account::{ReadableAccount, WritableAccount},
account_utils::State as AccountUtilsState,

View File

@ -1,5 +1,8 @@
#![cfg(feature = "full")]
use solana_sdk::{
account::AccountSharedData,
compute_budget::ComputeBudget,
instruction::{CompiledInstruction, Instruction, InstructionError},
keyed_account::{create_keyed_accounts_unified, KeyedAccount},
pubkey::Pubkey,
@ -76,6 +79,8 @@ pub trait InvokeContext {
/// Get this invocation's logger
fn get_logger(&self) -> Rc<RefCell<dyn Logger>>;
/// Get this invocation's compute budget
#[allow(deprecated)]
#[deprecated(since = "1.8.0", note = "please use `get_compute_budget` instead")]
fn get_bpf_compute_budget(&self) -> &BpfComputeBudget;
/// Get this invocation's compute meter
fn get_compute_meter(&self) -> Rc<RefCell<dyn ComputeMeter>>;
@ -100,6 +105,8 @@ pub trait InvokeContext {
);
/// Get sysvar data
fn get_sysvar_data(&self, id: &Pubkey) -> Option<Rc<Vec<u8>>>;
/// Get this invocation's compute budget
fn get_compute_budget(&self) -> &ComputeBudget;
}
/// Convenience macro to log a message with an `Rc<RefCell<dyn Logger>>`
@ -147,7 +154,8 @@ pub fn get_sysvar<T: Sysvar>(
})
}
#[derive(Clone, Copy, Debug, AbiExample, PartialEq)]
#[deprecated(since = "1.8.0", note = "please use `ComputeBudget` instead")]
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct BpfComputeBudget {
/// Number of compute units that an instruction is allowed. Compute units
/// are consumed by program execution, resources they use, etc...
@ -182,30 +190,60 @@ pub struct BpfComputeBudget {
/// Optional program heap region size, if `None` then loader default
pub heap_size: Option<usize>,
}
impl Default for BpfComputeBudget {
fn default() -> Self {
Self::new()
#[allow(deprecated)]
impl From<ComputeBudget> for BpfComputeBudget {
fn from(item: ComputeBudget) -> Self {
BpfComputeBudget {
max_units: item.max_units,
log_64_units: item.log_64_units,
create_program_address_units: item.create_program_address_units,
invoke_units: item.invoke_units,
max_invoke_depth: item.max_invoke_depth,
sha256_base_cost: item.sha256_base_cost,
sha256_byte_cost: item.sha256_byte_cost,
max_call_depth: item.max_call_depth,
stack_frame_size: item.stack_frame_size,
log_pubkey_units: item.log_pubkey_units,
max_cpi_instruction_size: item.max_cpi_instruction_size,
cpi_bytes_per_unit: item.cpi_bytes_per_unit,
sysvar_base_cost: item.sysvar_base_cost,
secp256k1_recover_cost: item.secp256k1_recover_cost,
heap_size: item.heap_size,
}
}
}
#[allow(deprecated)]
impl From<BpfComputeBudget> for ComputeBudget {
fn from(item: BpfComputeBudget) -> Self {
ComputeBudget {
max_units: item.max_units,
log_64_units: item.log_64_units,
create_program_address_units: item.create_program_address_units,
invoke_units: item.invoke_units,
max_invoke_depth: item.max_invoke_depth,
sha256_base_cost: item.sha256_base_cost,
sha256_byte_cost: item.sha256_byte_cost,
max_call_depth: item.max_call_depth,
stack_frame_size: item.stack_frame_size,
log_pubkey_units: item.log_pubkey_units,
max_cpi_instruction_size: item.max_cpi_instruction_size,
cpi_bytes_per_unit: item.cpi_bytes_per_unit,
sysvar_base_cost: item.sysvar_base_cost,
secp256k1_recover_cost: item.secp256k1_recover_cost,
heap_size: item.heap_size,
}
}
}
#[allow(deprecated)]
impl Default for BpfComputeBudget {
fn default() -> Self {
ComputeBudget::default().into()
}
}
#[allow(deprecated)]
impl BpfComputeBudget {
pub fn new() -> Self {
BpfComputeBudget {
max_units: 200_000,
log_64_units: 100,
create_program_address_units: 1500,
invoke_units: 1000,
max_invoke_depth: 4,
sha256_base_cost: 85,
sha256_byte_cost: 1,
max_call_depth: 64,
stack_frame_size: 4_096,
log_pubkey_units: 100,
max_cpi_instruction_size: 1280, // IPv6 Min MTU size
cpi_bytes_per_unit: 250, // ~50MB at 200,000 units
sysvar_base_cost: 100,
secp256k1_recover_cost: 25_000,
heap_size: None,
}
BpfComputeBudget::default()
}
}
@ -336,9 +374,12 @@ impl Logger for MockLogger {
}
}
#[allow(deprecated)]
pub struct MockInvokeContext<'a> {
pub invoke_stack: Vec<InvokeContextStackFrame<'a>>,
pub logger: MockLogger,
pub compute_budget: ComputeBudget,
#[allow(deprecated)]
pub bpf_compute_budget: BpfComputeBudget,
pub compute_meter: MockComputeMeter,
pub programs: Vec<(Pubkey, ProcessInstructionWithContext)>,
@ -348,11 +389,13 @@ pub struct MockInvokeContext<'a> {
}
impl<'a> MockInvokeContext<'a> {
pub fn new(keyed_accounts: Vec<KeyedAccount<'a>>) -> Self {
let bpf_compute_budget = BpfComputeBudget::default();
let compute_budget = ComputeBudget::default();
let mut invoke_context = MockInvokeContext {
invoke_stack: Vec::with_capacity(bpf_compute_budget.max_invoke_depth),
invoke_stack: Vec::with_capacity(compute_budget.max_invoke_depth),
logger: MockLogger::default(),
bpf_compute_budget,
compute_budget,
#[allow(deprecated)]
bpf_compute_budget: compute_budget.into(),
compute_meter: MockComputeMeter {
remaining: std::i64::MAX as u64,
},
@ -442,7 +485,9 @@ impl<'a> InvokeContext for MockInvokeContext<'a> {
fn get_logger(&self) -> Rc<RefCell<dyn Logger>> {
Rc::new(RefCell::new(self.logger.clone()))
}
#[allow(deprecated)]
fn get_bpf_compute_budget(&self) -> &BpfComputeBudget {
#[allow(deprecated)]
&self.bpf_compute_budget
}
fn get_compute_meter(&self) -> Rc<RefCell<dyn ComputeMeter>> {
@ -477,4 +522,7 @@ impl<'a> InvokeContext for MockInvokeContext<'a> {
.iter()
.find_map(|(key, sysvar)| if id == key { sysvar.clone() } else { None })
}
fn get_compute_budget(&self) -> &ComputeBudget {
&self.compute_budget
}
}