From 2ff983647f3b3d3fc0b84ee5a591d642e1127ee4 Mon Sep 17 00:00:00 2001 From: Jack May Date: Tue, 29 Sep 2020 01:36:46 -0700 Subject: [PATCH] Move process_instruction defs to runtime (#12507) --- genesis-programs/.gitignore | 2 - programs/bpf/benches/bpf_loader.rs | 6 +- programs/bpf/tests/programs.rs | 13 +-- programs/bpf_loader/src/lib.rs | 15 ++- programs/bpf_loader/src/syscalls.rs | 6 +- runtime/src/bank.rs | 22 ++-- runtime/src/lib.rs | 1 + runtime/src/message_processor.rs | 10 +- runtime/src/native_loader.rs | 3 +- runtime/src/process_instruction.rs | 130 ++++++++++++++++++++++++ sdk/src/entrypoint_native.rs | 151 +--------------------------- 11 files changed, 176 insertions(+), 183 deletions(-) delete mode 100644 genesis-programs/.gitignore create mode 100644 runtime/src/process_instruction.rs diff --git a/genesis-programs/.gitignore b/genesis-programs/.gitignore deleted file mode 100644 index 5404b132d..000000000 --- a/genesis-programs/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -/target/ -/farf/ diff --git a/programs/bpf/benches/bpf_loader.rs b/programs/bpf/benches/bpf_loader.rs index 7c8885ed1..b134451e5 100644 --- a/programs/bpf/benches/bpf_loader.rs +++ b/programs/bpf/benches/bpf_loader.rs @@ -14,15 +14,15 @@ use solana_runtime::{ bank_client::BankClient, genesis_utils::{create_genesis_config, GenesisConfigInfo}, loader_utils::load_program, + process_instruction::{ + ComputeBudget, ComputeMeter, Executor, InvokeContext, Logger, ProcessInstruction, + }, }; use solana_sdk::{ account::Account, bpf_loader, client::SyncClient, entrypoint::SUCCESS, - entrypoint_native::{ - ComputeBudget, ComputeMeter, Executor, InvokeContext, Logger, ProcessInstruction, - }, instruction::{AccountMeta, CompiledInstruction, Instruction, InstructionError}, message::Message, pubkey::Pubkey, diff --git a/programs/bpf/tests/programs.rs b/programs/bpf/tests/programs.rs index ec92ca5ee..3d9fac224 100644 --- a/programs/bpf/tests/programs.rs +++ b/programs/bpf/tests/programs.rs @@ -13,6 +13,9 @@ use solana_runtime::{ bank_client::BankClient, genesis_utils::{create_genesis_config, GenesisConfigInfo}, loader_utils::load_program, + process_instruction::{ + ComputeBudget, ComputeMeter, Executor, InvokeContext, Logger, ProcessInstruction, + }, }; use solana_sdk::{ account::{Account, KeyedAccount}, @@ -20,9 +23,7 @@ use solana_sdk::{ client::SyncClient, clock::{DEFAULT_SLOTS_PER_EPOCH, MAX_PROCESSING_AGE}, entrypoint::{MAX_PERMITTED_DATA_INCREASE, SUCCESS}, - entrypoint_native::{ - ComputeBudget, ComputeMeter, Executor, InvokeContext, Logger, ProcessInstruction, - }, + instruction::{AccountMeta, CompiledInstruction, Instruction, InstructionError}, message::Message, pubkey::Pubkey, @@ -629,12 +630,12 @@ fn assert_instruction_count() { ("solana_bpf_rust_128bit", 543), ("solana_bpf_rust_alloc", 19082), ("solana_bpf_rust_dep_crate", 2), - ("solana_bpf_rust_external_spend", 473), + ("solana_bpf_rust_external_spend", 477), ("solana_bpf_rust_iter", 723), ("solana_bpf_rust_many_args", 231), - ("solana_bpf_rust_noop", 447), + ("solana_bpf_rust_noop", 451), ("solana_bpf_rust_param_passing", 54), - ("solana_bpf_rust_sanity", 2211), + ("solana_bpf_rust_sanity", 2215), ]); } diff --git a/programs/bpf_loader/src/lib.rs b/programs/bpf_loader/src/lib.rs index f0e1a548c..ff133a6c5 100644 --- a/programs/bpf_loader/src/lib.rs +++ b/programs/bpf_loader/src/lib.rs @@ -16,12 +16,12 @@ use solana_rbpf::{ memory_region::MemoryRegion, vm::{EbpfVm, Executable, InstructionMeter}, }; +use solana_runtime::process_instruction::{ComputeMeter, Executor, InvokeContext}; use solana_sdk::{ account::{is_executable, next_keyed_account, KeyedAccount}, bpf_loader, bpf_loader_deprecated, decode_error::DecodeError, entrypoint::SUCCESS, - entrypoint_native::{ComputeMeter, Executor, InvokeContext}, instruction::InstructionError, loader_instruction::LoaderInstruction, program_utils::limited_deserialize, @@ -270,14 +270,13 @@ impl Executor for BPFExecutor { mod tests { use super::*; use rand::Rng; - use solana_runtime::message_processor::{Executors, ThisInvokeContext}; + use solana_runtime::{ + message_processor::{Executors, ThisInvokeContext}, + process_instruction::{ComputeBudget, Logger, ProcessInstruction}, + }; use solana_sdk::{ - account::Account, - entrypoint_native::{ComputeBudget, Logger, ProcessInstruction}, - instruction::CompiledInstruction, - instruction::Instruction, - message::Message, - rent::Rent, + account::Account, instruction::CompiledInstruction, instruction::Instruction, + message::Message, rent::Rent, }; use std::{cell::RefCell, fs::File, io::Read, ops::Range, rc::Rc}; diff --git a/programs/bpf_loader/src/syscalls.rs b/programs/bpf_loader/src/syscalls.rs index e9f85ba16..827b62850 100644 --- a/programs/bpf_loader/src/syscalls.rs +++ b/programs/bpf_loader/src/syscalls.rs @@ -6,14 +6,16 @@ use solana_rbpf::{ memory_region::{translate_addr, MemoryRegion}, vm::{EbpfVm, SyscallObject}, }; -use solana_runtime::message_processor::MessageProcessor; +use solana_runtime::{ + message_processor::MessageProcessor, + process_instruction::{ComputeMeter, InvokeContext, Logger}, +}; use solana_sdk::{ account::Account, account::KeyedAccount, account_info::AccountInfo, bpf_loader, bpf_loader_deprecated, entrypoint::{MAX_PERMITTED_DATA_INCREASE, SUCCESS}, - entrypoint_native::{ComputeMeter, InvokeContext, Logger}, instruction::{AccountMeta, Instruction, InstructionError}, message::Message, program_error::ProgramError, diff --git a/runtime/src/bank.rs b/runtime/src/bank.rs index 85aef4663..fb23493ce 100644 --- a/runtime/src/bank.rs +++ b/runtime/src/bank.rs @@ -18,6 +18,9 @@ use crate::{ log_collector::LogCollector, message_processor::{Executors, MessageProcessor}, nonce_utils, + process_instruction::{ + ComputeBudget, Executor, ProcessInstruction, ProcessInstructionWithContext, + }, rent_collector::RentCollector, stakes::Stakes, status_cache::{SlotDelta, StatusCache}, @@ -38,9 +41,6 @@ use solana_sdk::{ Epoch, Slot, SlotCount, SlotIndex, UnixTimestamp, DEFAULT_TICKS_PER_SECOND, MAX_PROCESSING_AGE, MAX_RECENT_BLOCKHASHES, SECONDS_PER_DAY, }, - entrypoint_native::{ - ComputeBudget, Executor, ProcessInstruction, ProcessInstructionWithContext, - }, epoch_info::EpochInfo, epoch_schedule::EpochSchedule, fee_calculator::{FeeCalculator, FeeConfig, FeeRateGovernor}, @@ -211,7 +211,6 @@ pub struct Builtins { const MAX_CACHED_EXECUTORS: usize = 100; // 10 MB assuming programs are around 100k /// LFU Cache of executors -#[derive(AbiExample)] struct CachedExecutors { max: usize, executors: HashMap)>, @@ -224,6 +223,17 @@ impl Default for CachedExecutors { } } } + +#[cfg(RUSTC_WITH_SPECIALIZATION)] +impl AbiExample for CachedExecutors { + fn example() -> Self { + // Delegate AbiExample impl to Default before going deep and stuck with + // not easily impl-able Arc due to rust's coherence issue + // This is safe because CachedExecutors isn't serializable by definition. + Self::default() + } +} + impl Clone for CachedExecutors { fn clone(&self) -> Self { let mut executors = HashMap::new(); @@ -3816,13 +3826,13 @@ mod tests { genesis_utils::{ create_genesis_config_with_leader, GenesisConfigInfo, BOOTSTRAP_VALIDATOR_LAMPORTS, }, + process_instruction::InvokeContext, status_cache::MAX_CACHE_ENTRIES, }; use solana_sdk::{ account::KeyedAccount, account_utils::StateMut, clock::{DEFAULT_SLOTS_PER_EPOCH, DEFAULT_TICKS_PER_SLOT}, - entrypoint_native::InvokeContext, epoch_schedule::MINIMUM_SLOTS_PER_EPOCH, genesis_config::create_genesis_config, instruction::{AccountMeta, CompiledInstruction, Instruction, InstructionError}, @@ -8750,7 +8760,7 @@ mod tests { _pubkey: &Pubkey, _ka: &[KeyedAccount], _data: &[u8], - _context: &mut dyn solana_sdk::entrypoint_native::InvokeContext, + _context: &mut dyn InvokeContext, ) -> std::result::Result<(), InstructionError> { Ok(()) } diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 11e975f5e..b3a6a3ef2 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -23,6 +23,7 @@ pub mod log_collector; pub mod message_processor; mod native_loader; pub mod nonce_utils; +pub mod process_instruction; pub mod rent_collector; pub mod serde_snapshot; pub mod snapshot_package; diff --git a/runtime/src/message_processor.rs b/runtime/src/message_processor.rs index 654526d43..7aae214cb 100644 --- a/runtime/src/message_processor.rs +++ b/runtime/src/message_processor.rs @@ -3,6 +3,10 @@ use crate::{ instruction_recorder::InstructionRecorder, log_collector::LogCollector, native_loader::NativeLoader, + process_instruction::{ + ComputeBudget, ComputeMeter, ErasedProcessInstruction, ErasedProcessInstructionWithContext, + Executor, InvokeContext, Logger, ProcessInstruction, ProcessInstructionWithContext, + }, rent_collector::RentCollector, }; use log::*; @@ -10,10 +14,6 @@ use serde::{Deserialize, Serialize}; use solana_sdk::{ account::{create_keyed_readonly_accounts, Account, KeyedAccount}, clock::Epoch, - entrypoint_native::{ - ComputeBudget, ComputeMeter, ErasedProcessInstruction, ErasedProcessInstructionWithContext, - Executor, InvokeContext, Logger, ProcessInstruction, ProcessInstructionWithContext, - }, instruction::{CompiledInstruction, Instruction, InstructionError}, message::Message, native_loader, @@ -1694,7 +1694,7 @@ mod tests { _pubkey: &Pubkey, _ka: &[KeyedAccount], _data: &[u8], - _context: &mut dyn solana_sdk::entrypoint_native::InvokeContext, + _context: &mut dyn InvokeContext, ) -> std::result::Result<(), InstructionError> { Ok(()) } diff --git a/runtime/src/native_loader.rs b/runtime/src/native_loader.rs index 3b032ad7b..8d4a817c1 100644 --- a/runtime/src/native_loader.rs +++ b/runtime/src/native_loader.rs @@ -1,4 +1,5 @@ //! Native loader +use crate::process_instruction::{InvokeContext, LoaderEntrypoint}; #[cfg(unix)] use libloading::os::unix::*; #[cfg(windows)] @@ -8,7 +9,7 @@ use num_derive::{FromPrimitive, ToPrimitive}; use solana_sdk::{ account::{next_keyed_account, KeyedAccount}, decode_error::DecodeError, - entrypoint_native::{InvokeContext, LoaderEntrypoint, ProgramEntrypoint}, + entrypoint_native::ProgramEntrypoint, instruction::InstructionError, pubkey::Pubkey, }; diff --git a/runtime/src/process_instruction.rs b/runtime/src/process_instruction.rs new file mode 100644 index 000000000..d57945c8a --- /dev/null +++ b/runtime/src/process_instruction.rs @@ -0,0 +1,130 @@ +use solana_sdk::{ + account::{Account, KeyedAccount}, + instruction::{CompiledInstruction, Instruction, InstructionError}, + message::Message, + pubkey::Pubkey, +}; +use std::{cell::RefCell, rc::Rc, sync::Arc}; + +// Prototype of a native loader entry point +/// +/// program_id: Program ID of the currently executing program +/// keyed_accounts: Accounts passed as part of the instruction +/// instruction_data: Instruction data +/// invoke_context: Invocation context +pub type LoaderEntrypoint = unsafe extern "C" fn( + program_id: &Pubkey, + keyed_accounts: &[KeyedAccount], + instruction_data: &[u8], + invoke_context: &dyn InvokeContext, +) -> Result<(), InstructionError>; + +pub type ProcessInstruction = fn(&Pubkey, &[KeyedAccount], &[u8]) -> Result<(), InstructionError>; +pub type ProcessInstructionWithContext = + fn(&Pubkey, &[KeyedAccount], &[u8], &mut dyn InvokeContext) -> Result<(), InstructionError>; + +// These are just type aliases for work around of Debug-ing above function pointers +pub type ErasedProcessInstructionWithContext = fn( + &'static Pubkey, + &'static [KeyedAccount<'static>], + &'static [u8], + &'static mut dyn InvokeContext, +) -> Result<(), InstructionError>; + +pub type ErasedProcessInstruction = fn( + &'static Pubkey, + &'static [KeyedAccount<'static>], + &'static [u8], +) -> Result<(), InstructionError>; + +/// Invocation context passed to loaders +pub trait InvokeContext { + /// Push a program ID on to the invocation stack + fn push(&mut self, key: &Pubkey) -> Result<(), InstructionError>; + /// Pop a program ID off of the invocation stack + fn pop(&mut self); + /// Verify and update PreAccount state based on program execution + fn verify_and_update( + &mut self, + message: &Message, + instruction: &CompiledInstruction, + accounts: &[Rc>], + ) -> Result<(), InstructionError>; + /// Get the program ID of the currently executing program + fn get_caller(&self) -> Result<&Pubkey, InstructionError>; + /// Get a list of built-in programs + fn get_programs(&self) -> &[(Pubkey, ProcessInstruction)]; + /// Get this invocation's logger + fn get_logger(&self) -> Rc>; + /// Are cross program invocations supported + fn is_cross_program_supported(&self) -> bool; + /// Get this invocation's compute budget + fn get_compute_budget(&self) -> ComputeBudget; + /// Get this invocation's compute meter + fn get_compute_meter(&self) -> Rc>; + /// Loaders may need to do work in order to execute a program. Cache + /// the work that can be re-used across executions + fn add_executor(&mut self, pubkey: &Pubkey, executor: Arc); + /// Get the completed loader work that can be re-used across executions + fn get_executor(&mut self, pubkey: &Pubkey) -> Option>; + /// Record invoked instruction + fn record_instruction(&self, instruction: &Instruction); +} + +#[derive(Clone, Copy, Debug)] +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 call + pub log_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 incured by + /// the called program) + pub invoke_units: u64, + /// Maximum cross-program invocation depth allowed including the orignal caller + pub max_invoke_depth: usize, +} +impl Default for ComputeBudget { + fn default() -> Self { + // Tuned for ~1ms + ComputeBudget { + max_units: 200_000, + log_units: 100, + log_64_units: 100, + create_program_address_units: 1500, + invoke_units: 1000, + max_invoke_depth: 2, + } + } +} + +/// Compute meter +pub trait ComputeMeter { + /// Consume compute units + fn consume(&mut self, amount: u64) -> Result<(), InstructionError>; + /// Get the number of remaining compute units + fn get_remaining(&self) -> u64; +} + +/// Log messages +pub trait Logger { + fn log_enabled(&self) -> bool; + /// Log a message + fn log(&mut self, message: &str); +} + +/// Program executor +pub trait Executor: Send + Sync { + /// Execute the program + fn execute( + &self, + program_id: &Pubkey, + keyed_accounts: &[KeyedAccount], + instruction_data: &[u8], + invoke_context: &mut dyn InvokeContext, + ) -> Result<(), InstructionError>; +} diff --git a/sdk/src/entrypoint_native.rs b/sdk/src/entrypoint_native.rs index dda391183..3990a7fbd 100644 --- a/sdk/src/entrypoint_native.rs +++ b/sdk/src/entrypoint_native.rs @@ -1,14 +1,6 @@ //! @brief Solana Native program entry point -#[cfg(RUSTC_WITH_SPECIALIZATION)] -use crate::abi_example::AbiExample; -use crate::{ - account::{Account, KeyedAccount}, - instruction::{CompiledInstruction, Instruction, InstructionError}, - message::Message, - pubkey::Pubkey, -}; -use std::{cell::RefCell, rc::Rc, sync::Arc}; +use crate::{account::KeyedAccount, instruction::InstructionError, pubkey::Pubkey}; // Prototype of a native program entry point /// @@ -21,19 +13,6 @@ pub type ProgramEntrypoint = unsafe extern "C" fn( instruction_data: &[u8], ) -> Result<(), InstructionError>; -// Prototype of a native loader entry point -/// -/// program_id: Program ID of the currently executing program -/// keyed_accounts: Accounts passed as part of the instruction -/// instruction_data: Instruction data -/// invoke_context: Invocation context -pub type LoaderEntrypoint = unsafe extern "C" fn( - program_id: &Pubkey, - keyed_accounts: &[KeyedAccount], - instruction_data: &[u8], - invoke_context: &dyn InvokeContext, -) -> Result<(), InstructionError>; - #[rustversion::since(1.46.0)] #[macro_export] macro_rules! declare_name { @@ -178,131 +157,3 @@ macro_rules! declare_program( } ) ); - -pub type ProcessInstruction = fn(&Pubkey, &[KeyedAccount], &[u8]) -> Result<(), InstructionError>; -pub type ProcessInstructionWithContext = - fn(&Pubkey, &[KeyedAccount], &[u8], &mut dyn InvokeContext) -> Result<(), InstructionError>; - -// These are just type aliases for work around of Debug-ing above function pointers -pub type ErasedProcessInstructionWithContext = fn( - &'static Pubkey, - &'static [KeyedAccount<'static>], - &'static [u8], - &'static mut dyn InvokeContext, -) -> Result<(), InstructionError>; - -pub type ErasedProcessInstruction = fn( - &'static Pubkey, - &'static [KeyedAccount<'static>], - &'static [u8], -) -> Result<(), InstructionError>; - -/// Invocation context passed to loaders -pub trait InvokeContext { - /// Push a program ID on to the invocation stack - fn push(&mut self, key: &Pubkey) -> Result<(), InstructionError>; - /// Pop a program ID off of the invocation stack - fn pop(&mut self); - /// Verify and update PreAccount state based on program execution - fn verify_and_update( - &mut self, - message: &Message, - instruction: &CompiledInstruction, - accounts: &[Rc>], - ) -> Result<(), InstructionError>; - /// Get the program ID of the currently executing program - fn get_caller(&self) -> Result<&Pubkey, InstructionError>; - /// Get a list of built-in programs - fn get_programs(&self) -> &[(Pubkey, ProcessInstruction)]; - /// Get this invocation's logger - fn get_logger(&self) -> Rc>; - /// Are cross program invocations supported - fn is_cross_program_supported(&self) -> bool; - /// Get this invocation's compute budget - fn get_compute_budget(&self) -> ComputeBudget; - /// Get this invocation's compute meter - fn get_compute_meter(&self) -> Rc>; - /// Loaders may need to do work in order to execute a program. Cache - /// the work that can be re-used across executions - fn add_executor(&mut self, pubkey: &Pubkey, executor: Arc); - /// Get the completed loader work that can be re-used across executions - fn get_executor(&mut self, pubkey: &Pubkey) -> Option>; - /// Record invoked instruction - fn record_instruction(&self, instruction: &Instruction); -} - -#[derive(Clone, Copy, Debug)] -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 call - pub log_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 incured by - /// the called program) - pub invoke_units: u64, - /// Maximum cross-program invocation depth allowed including the orignal caller - pub max_invoke_depth: usize, -} -impl Default for ComputeBudget { - fn default() -> Self { - // Tuned for ~1ms - ComputeBudget { - max_units: 200_000, - log_units: 100, - log_64_units: 100, - create_program_address_units: 1500, - invoke_units: 1000, - max_invoke_depth: 2, - } - } -} - -/// Compute meter -pub trait ComputeMeter { - /// Consume compute units - fn consume(&mut self, amount: u64) -> Result<(), InstructionError>; - /// Get the number of remaining compute units - fn get_remaining(&self) -> u64; -} - -/// Log messages -pub trait Logger { - fn log_enabled(&self) -> bool; - /// Log a message - fn log(&mut self, message: &str); -} - -/// Program executor -pub trait Executor: Send + Sync { - /// Execute the program - fn execute( - &self, - program_id: &Pubkey, - keyed_accounts: &[KeyedAccount], - instruction_data: &[u8], - invoke_context: &mut dyn InvokeContext, - ) -> Result<(), InstructionError>; -} -#[cfg(RUSTC_WITH_SPECIALIZATION)] -impl AbiExample for Arc { - fn example() -> Self { - struct ExampleExecutor {} - impl Executor for ExampleExecutor { - fn execute( - &self, - _program_id: &Pubkey, - _keyed_accounts: &[KeyedAccount], - _instruction_data: &[u8], - _invoke_context: &mut dyn InvokeContext, - ) -> std::result::Result<(), InstructionError> { - Ok(()) - } - } - Arc::new(ExampleExecutor {}) - } -}