浏览代码

Add solana-program-runtime crate (#19438)

Justin Starry 4 年之前
父节点
当前提交
2d7f036afd

+ 22 - 2
Cargo.lock

@@ -4492,6 +4492,7 @@ dependencies = [
  "rustversion",
  "sha3",
  "solana-measure",
+ "solana-program-runtime",
  "solana-runtime",
  "solana-sdk",
  "solana_rbpf",
@@ -4735,6 +4736,7 @@ dependencies = [
  "solana-net-utils",
  "solana-perf",
  "solana-poh",
+ "solana-program-runtime",
  "solana-program-test",
  "solana-rayon-threadlimit",
  "solana-rpc",
@@ -5440,6 +5442,24 @@ dependencies = [
  "thiserror",
 ]
 
+[[package]]
+name = "solana-program-runtime"
+version = "1.8.0"
+dependencies = [
+ "libc",
+ "libloading",
+ "log 0.4.14",
+ "num-derive",
+ "num-traits",
+ "rustc_version 0.4.0",
+ "serde",
+ "solana-frozen-abi 1.8.0",
+ "solana-frozen-abi-macro 1.8.0",
+ "solana-logger 1.8.0",
+ "solana-sdk",
+ "thiserror",
+]
+
 [[package]]
 name = "solana-program-test"
 version = "1.8.0"
@@ -5458,6 +5478,7 @@ dependencies = [
  "solana-banks-server",
  "solana-bpf-loader-program",
  "solana-logger 1.8.0",
+ "solana-program-runtime",
  "solana-runtime",
  "solana-sdk",
  "solana-vote-program",
@@ -5600,8 +5621,6 @@ dependencies = [
  "fnv",
  "itertools 0.10.1",
  "lazy_static",
- "libc",
- "libloading",
  "log 0.4.14",
  "memmap2 0.3.1",
  "num-derive",
@@ -5622,6 +5641,7 @@ dependencies = [
  "solana-measure",
  "solana-metrics",
  "solana-noop-program",
+ "solana-program-runtime",
  "solana-rayon-threadlimit",
  "solana-sdk",
  "solana-secp256k1-program",

+ 1 - 0
core/Cargo.toml

@@ -83,6 +83,7 @@ num_cpus = "1.13.0"
 reqwest = { version = "0.11.4", default-features = false, features = ["blocking", "rustls-tls", "json"] }
 serde_json = "1.0.66"
 serial_test = "0.5.1"
+solana-program-runtime = { path = "../program-runtime", version = "=1.8.0" }
 solana-stake-program = { path = "../programs/stake", version = "=1.8.0" }
 solana-version = { path = "../version", version = "=1.8.0" }
 static_assertions = "1.1.0"

+ 1 - 1
core/src/cost_update_service.rs

@@ -191,7 +191,7 @@ impl CostUpdateService {
 #[cfg(test)]
 mod tests {
     use super::*;
-    use solana_runtime::message_processor::ProgramTiming;
+    use solana_program_runtime::ProgramTiming;
     use solana_sdk::pubkey::Pubkey;
 
     #[test]

+ 33 - 0
program-runtime/Cargo.toml

@@ -0,0 +1,33 @@
+[package]
+name = "solana-program-runtime"
+version = "1.8.0"
+description = "Solana program runtime"
+authors = ["Solana Maintainers <maintainers@solana.foundation>"]
+repository = "https://github.com/solana-labs/solana"
+license = "Apache-2.0"
+homepage = "https://solana.com/"
+documentation = "https://docs.rs/solana-program-runtime"
+edition = "2018"
+
+[dependencies]
+libc = "0.2.101"
+libloading = "0.7.0"
+log = "0.4.14"
+num-derive = { version = "0.3" }
+num-traits = { version = "0.2" }
+serde = { version = "1.0.129", features = ["derive", "rc"] }
+solana-frozen-abi = { path = "../frozen-abi", version = "=1.8.0" }
+solana-frozen-abi-macro = { path = "../frozen-abi/macro", version = "=1.8.0" }
+solana-logger = { path = "../logger", version = "=1.8.0" }
+solana-sdk = { path = "../sdk", version = "=1.8.0" }
+thiserror = "1.0"
+
+[lib]
+crate-type = ["lib"]
+name = "solana_program_runtime"
+
+[package.metadata.docs.rs]
+targets = ["x86_64-unknown-linux-gnu"]
+
+[build-dependencies]
+rustc_version = "0.4"

+ 1 - 1
runtime/benches/message_processor.rs → program-runtime/benches/instruction_processor.rs

@@ -3,7 +3,7 @@
 extern crate test;
 
 use log::*;
-use solana_runtime::message_processor::{ExecuteDetailsTimings, PreAccount};
+use solana_program_runtime::{ExecuteDetailsTimings, PreAccount};
 use solana_sdk::{account::AccountSharedData, pubkey, rent::Rent};
 use test::Bencher;
 

+ 1 - 0
program-runtime/build.rs

@@ -0,0 +1 @@
+../frozen-abi/build.rs

+ 1229 - 0
program-runtime/src/instruction_processor.rs

@@ -0,0 +1,1229 @@
+use crate::native_loader::NativeLoader;
+use serde::{Deserialize, Serialize};
+use solana_sdk::{
+    account::{AccountSharedData, ReadableAccount, WritableAccount},
+    account_utils::StateMut,
+    bpf_loader_upgradeable::{self, UpgradeableLoaderState},
+    ic_logger_msg, ic_msg,
+    instruction::{CompiledInstruction, Instruction, InstructionError},
+    keyed_account::{keyed_account_at_index, KeyedAccount},
+    message::Message,
+    process_instruction::{Executor, InvokeContext, Logger, ProcessInstructionWithContext},
+    pubkey::Pubkey,
+    rent::Rent,
+    system_program,
+};
+use std::{
+    cell::{Ref, RefCell},
+    collections::HashMap,
+    rc::Rc,
+    sync::Arc,
+};
+
+pub struct Executors {
+    pub executors: HashMap<Pubkey, Arc<dyn Executor>>,
+    pub is_dirty: bool,
+}
+impl Default for Executors {
+    fn default() -> Self {
+        Self {
+            executors: HashMap::default(),
+            is_dirty: false,
+        }
+    }
+}
+impl Executors {
+    pub fn insert(&mut self, key: Pubkey, executor: Arc<dyn Executor>) {
+        let _ = self.executors.insert(key, executor);
+        self.is_dirty = true;
+    }
+    pub fn get(&self, key: &Pubkey) -> Option<Arc<dyn Executor>> {
+        self.executors.get(key).cloned()
+    }
+}
+
+#[derive(Default, Debug)]
+pub struct ProgramTiming {
+    pub accumulated_us: u64,
+    pub accumulated_units: u64,
+    pub count: u32,
+}
+
+#[derive(Default, Debug)]
+pub struct ExecuteDetailsTimings {
+    pub serialize_us: u64,
+    pub create_vm_us: u64,
+    pub execute_us: u64,
+    pub deserialize_us: u64,
+    pub changed_account_count: u64,
+    pub total_account_count: u64,
+    pub total_data_size: usize,
+    pub data_size_changed: usize,
+    pub per_program_timings: HashMap<Pubkey, ProgramTiming>,
+}
+impl ExecuteDetailsTimings {
+    pub fn accumulate(&mut self, other: &ExecuteDetailsTimings) {
+        self.serialize_us += other.serialize_us;
+        self.create_vm_us += other.create_vm_us;
+        self.execute_us += other.execute_us;
+        self.deserialize_us += other.deserialize_us;
+        self.changed_account_count += other.changed_account_count;
+        self.total_account_count += other.total_account_count;
+        self.total_data_size += other.total_data_size;
+        self.data_size_changed += other.data_size_changed;
+        for (id, other) in &other.per_program_timings {
+            let program_timing = self.per_program_timings.entry(*id).or_default();
+            program_timing.accumulated_us = program_timing
+                .accumulated_us
+                .saturating_add(other.accumulated_us);
+            program_timing.accumulated_units = program_timing
+                .accumulated_units
+                .saturating_add(other.accumulated_units);
+            program_timing.count = program_timing.count.saturating_add(other.count);
+        }
+    }
+    pub fn accumulate_program(&mut self, program_id: &Pubkey, us: u64, units: u64) {
+        let program_timing = self.per_program_timings.entry(*program_id).or_default();
+        program_timing.accumulated_us = program_timing.accumulated_us.saturating_add(us);
+        program_timing.accumulated_units = program_timing.accumulated_units.saturating_add(units);
+        program_timing.count = program_timing.count.saturating_add(1);
+    }
+}
+
+// The relevant state of an account before an Instruction executes, used
+// to verify account integrity after the Instruction completes
+#[derive(Clone, Debug, Default)]
+pub struct PreAccount {
+    key: Pubkey,
+    account: Rc<RefCell<AccountSharedData>>,
+    changed: bool,
+}
+impl PreAccount {
+    pub fn new(key: &Pubkey, account: &AccountSharedData) -> Self {
+        Self {
+            key: *key,
+            account: Rc::new(RefCell::new(account.clone())),
+            changed: false,
+        }
+    }
+
+    pub fn verify(
+        &self,
+        program_id: &Pubkey,
+        is_writable: bool,
+        rent: &Rent,
+        post: &AccountSharedData,
+        timings: &mut ExecuteDetailsTimings,
+        outermost_call: bool,
+        updated_verify_policy: bool,
+    ) -> Result<(), InstructionError> {
+        let pre = self.account.borrow();
+
+        // Only the owner of the account may change owner and
+        //   only if the account is writable and
+        //   only if the account is not executable and
+        //   only if the data is zero-initialized or empty
+        let owner_changed = pre.owner() != post.owner();
+        if owner_changed
+            && (!is_writable // line coverage used to get branch coverage
+                || pre.executable()
+                || program_id != pre.owner()
+            || !Self::is_zeroed(post.data()))
+        {
+            return Err(InstructionError::ModifiedProgramId);
+        }
+
+        // An account not assigned to the program cannot have its balance decrease.
+        if program_id != pre.owner() // line coverage used to get branch coverage
+         && pre.lamports() > post.lamports()
+        {
+            return Err(InstructionError::ExternalAccountLamportSpend);
+        }
+
+        // The balance of read-only and executable accounts may not change
+        let lamports_changed = pre.lamports() != post.lamports();
+        if lamports_changed {
+            if !is_writable {
+                return Err(InstructionError::ReadonlyLamportChange);
+            }
+            if pre.executable() {
+                return Err(InstructionError::ExecutableLamportChange);
+            }
+        }
+
+        // Only the system program can change the size of the data
+        //  and only if the system program owns the account
+        let data_len_changed = pre.data().len() != post.data().len();
+        if data_len_changed
+            && (!system_program::check_id(program_id) // line coverage used to get branch coverage
+                || !system_program::check_id(pre.owner()))
+        {
+            return Err(InstructionError::AccountDataSizeChanged);
+        }
+
+        // Only the owner may change account data
+        //   and if the account is writable
+        //   and if the account is not executable
+        if !(program_id == pre.owner()
+            && is_writable  // line coverage used to get branch coverage
+            && !pre.executable())
+            && pre.data() != post.data()
+        {
+            if pre.executable() {
+                return Err(InstructionError::ExecutableDataModified);
+            } else if is_writable {
+                return Err(InstructionError::ExternalAccountDataModified);
+            } else {
+                return Err(InstructionError::ReadonlyDataModified);
+            }
+        }
+
+        // executable is one-way (false->true) and only the account owner may set it.
+        let executable_changed = pre.executable() != post.executable();
+        if executable_changed {
+            if !rent.is_exempt(post.lamports(), post.data().len()) {
+                return Err(InstructionError::ExecutableAccountNotRentExempt);
+            }
+            let owner = if updated_verify_policy {
+                post.owner()
+            } else {
+                pre.owner()
+            };
+            if !is_writable // line coverage used to get branch coverage
+                || pre.executable()
+                || program_id != owner
+            {
+                return Err(InstructionError::ExecutableModified);
+            }
+        }
+
+        // No one modifies rent_epoch (yet).
+        let rent_epoch_changed = pre.rent_epoch() != post.rent_epoch();
+        if rent_epoch_changed {
+            return Err(InstructionError::RentEpochModified);
+        }
+
+        if outermost_call {
+            timings.total_account_count += 1;
+            timings.total_data_size += post.data().len();
+            if owner_changed
+                || lamports_changed
+                || data_len_changed
+                || executable_changed
+                || rent_epoch_changed
+                || self.changed
+            {
+                timings.changed_account_count += 1;
+                timings.data_size_changed += post.data().len();
+            }
+        }
+
+        Ok(())
+    }
+
+    pub fn update(&mut self, account: &AccountSharedData) {
+        let mut pre = self.account.borrow_mut();
+        let rent_epoch = pre.rent_epoch();
+        *pre = account.clone();
+        pre.set_rent_epoch(rent_epoch);
+
+        self.changed = true;
+    }
+
+    pub fn key(&self) -> &Pubkey {
+        &self.key
+    }
+
+    pub fn data(&self) -> Ref<[u8]> {
+        Ref::map(self.account.borrow(), |account| account.data())
+    }
+
+    pub fn lamports(&self) -> u64 {
+        self.account.borrow().lamports()
+    }
+
+    pub fn executable(&self) -> bool {
+        self.account.borrow().executable()
+    }
+
+    pub fn is_zeroed(buf: &[u8]) -> bool {
+        const ZEROS_LEN: usize = 1024;
+        static ZEROS: [u8; ZEROS_LEN] = [0; ZEROS_LEN];
+        let mut chunks = buf.chunks_exact(ZEROS_LEN);
+
+        chunks.all(|chunk| chunk == &ZEROS[..])
+            && chunks.remainder() == &ZEROS[..chunks.remainder().len()]
+    }
+}
+
+#[derive(Deserialize, Serialize)]
+pub struct InstructionProcessor {
+    #[serde(skip)]
+    programs: Vec<(Pubkey, ProcessInstructionWithContext)>,
+    #[serde(skip)]
+    native_loader: NativeLoader,
+}
+
+impl std::fmt::Debug for InstructionProcessor {
+    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
+        #[derive(Debug)]
+        struct MessageProcessor<'a> {
+            programs: Vec<String>,
+            native_loader: &'a NativeLoader,
+        }
+
+        // These are just type aliases for work around of Debug-ing above pointers
+        type ErasedProcessInstructionWithContext = fn(
+            &'static Pubkey,
+            &'static [u8],
+            &'static mut dyn InvokeContext,
+        ) -> Result<(), InstructionError>;
+
+        // rustc doesn't compile due to bug without this work around
+        // https://github.com/rust-lang/rust/issues/50280
+        // https://users.rust-lang.org/t/display-function-pointer/17073/2
+        let processor = MessageProcessor {
+            programs: self
+                .programs
+                .iter()
+                .map(|(pubkey, instruction)| {
+                    let erased_instruction: ErasedProcessInstructionWithContext = *instruction;
+                    format!("{}: {:p}", pubkey, erased_instruction)
+                })
+                .collect::<Vec<_>>(),
+            native_loader: &self.native_loader,
+        };
+
+        write!(f, "{:?}", processor)
+    }
+}
+
+impl Default for InstructionProcessor {
+    fn default() -> Self {
+        Self {
+            programs: vec![],
+            native_loader: NativeLoader::default(),
+        }
+    }
+}
+impl Clone for InstructionProcessor {
+    fn clone(&self) -> Self {
+        InstructionProcessor {
+            programs: self.programs.clone(),
+            native_loader: NativeLoader::default(),
+        }
+    }
+}
+
+#[cfg(RUSTC_WITH_SPECIALIZATION)]
+impl ::solana_frozen_abi::abi_example::AbiExample for InstructionProcessor {
+    fn example() -> Self {
+        // MessageProcessor's fields are #[serde(skip)]-ed and not Serialize
+        // so, just rely on Default anyway.
+        InstructionProcessor::default()
+    }
+}
+
+impl InstructionProcessor {
+    pub fn programs(&self) -> &[(Pubkey, ProcessInstructionWithContext)] {
+        &self.programs
+    }
+
+    /// Add a static entrypoint to intercept instructions before the dynamic loader.
+    pub fn add_program(
+        &mut self,
+        program_id: Pubkey,
+        process_instruction: ProcessInstructionWithContext,
+    ) {
+        match self.programs.iter_mut().find(|(key, _)| program_id == *key) {
+            Some((_, processor)) => *processor = process_instruction,
+            None => self.programs.push((program_id, process_instruction)),
+        }
+    }
+
+    /// Create the KeyedAccounts that will be passed to the program
+    pub fn create_keyed_accounts<'a>(
+        message: &'a Message,
+        instruction: &'a CompiledInstruction,
+        executable_accounts: &'a [(Pubkey, Rc<RefCell<AccountSharedData>>)],
+        accounts: &'a [(Pubkey, Rc<RefCell<AccountSharedData>>)],
+    ) -> Vec<(bool, bool, &'a Pubkey, &'a RefCell<AccountSharedData>)> {
+        executable_accounts
+            .iter()
+            .map(|(key, account)| (false, false, key, account as &RefCell<AccountSharedData>))
+            .chain(instruction.accounts.iter().map(|index| {
+                let index = *index as usize;
+                (
+                    message.is_signer(index),
+                    message.is_writable(index),
+                    &accounts[index].0,
+                    &accounts[index].1 as &RefCell<AccountSharedData>,
+                )
+            }))
+            .collect::<Vec<_>>()
+    }
+
+    /// Process an instruction
+    /// This method calls the instruction's program entrypoint method
+    pub fn process_instruction(
+        &self,
+        program_id: &Pubkey,
+        instruction_data: &[u8],
+        invoke_context: &mut dyn InvokeContext,
+    ) -> Result<(), InstructionError> {
+        if let Some(root_account) = invoke_context.get_keyed_accounts()?.iter().next() {
+            let root_id = root_account.unsigned_key();
+            if solana_sdk::native_loader::check_id(&root_account.owner()?) {
+                for (id, process_instruction) in &self.programs {
+                    if id == root_id {
+                        invoke_context.remove_first_keyed_account()?;
+                        // Call the builtin program
+                        return process_instruction(program_id, instruction_data, invoke_context);
+                    }
+                }
+                // Call the program via the native loader
+                return self.native_loader.process_instruction(
+                    &solana_sdk::native_loader::id(),
+                    instruction_data,
+                    invoke_context,
+                );
+            } else {
+                let owner_id = &root_account.owner()?;
+                for (id, process_instruction) in &self.programs {
+                    if id == owner_id {
+                        // Call the program via a builtin loader
+                        return process_instruction(program_id, instruction_data, invoke_context);
+                    }
+                }
+            }
+        }
+        Err(InstructionError::UnsupportedProgramId)
+    }
+
+    pub fn create_message(
+        instruction: &Instruction,
+        keyed_accounts: &[&KeyedAccount],
+        signers: &[Pubkey],
+        invoke_context: &Ref<&mut dyn InvokeContext>,
+    ) -> Result<(Message, Pubkey, usize), InstructionError> {
+        // Check for privilege escalation
+        for account in instruction.accounts.iter() {
+            let keyed_account = keyed_accounts
+                .iter()
+                .find_map(|keyed_account| {
+                    if &account.pubkey == keyed_account.unsigned_key() {
+                        Some(keyed_account)
+                    } else {
+                        None
+                    }
+                })
+                .ok_or_else(|| {
+                    ic_msg!(
+                        invoke_context,
+                        "Instruction references an unknown account {}",
+                        account.pubkey
+                    );
+                    InstructionError::MissingAccount
+                })?;
+            // Readonly account cannot become writable
+            if account.is_writable && !keyed_account.is_writable() {
+                ic_msg!(
+                    invoke_context,
+                    "{}'s writable privilege escalated",
+                    account.pubkey
+                );
+                return Err(InstructionError::PrivilegeEscalation);
+            }
+
+            if account.is_signer && // If message indicates account is signed
+            !( // one of the following needs to be true:
+                keyed_account.signer_key().is_some() // Signed in the parent instruction
+                || signers.contains(&account.pubkey) // Signed by the program
+            ) {
+                ic_msg!(
+                    invoke_context,
+                    "{}'s signer privilege escalated",
+                    account.pubkey
+                );
+                return Err(InstructionError::PrivilegeEscalation);
+            }
+        }
+
+        // validate the caller has access to the program account and that it is executable
+        let program_id = instruction.program_id;
+        match keyed_accounts
+            .iter()
+            .find(|keyed_account| &program_id == keyed_account.unsigned_key())
+        {
+            Some(keyed_account) => {
+                if !keyed_account.executable()? {
+                    ic_msg!(
+                        invoke_context,
+                        "Account {} is not executable",
+                        keyed_account.unsigned_key()
+                    );
+                    return Err(InstructionError::AccountNotExecutable);
+                }
+            }
+            None => {
+                ic_msg!(invoke_context, "Unknown program {}", program_id);
+                return Err(InstructionError::MissingAccount);
+            }
+        }
+
+        let message = Message::new(&[instruction.clone()], None);
+        let program_id_index = message.instructions[0].program_id_index as usize;
+
+        Ok((message, program_id, program_id_index))
+    }
+
+    /// Entrypoint for a cross-program invocation from a native program
+    pub fn native_invoke(
+        invoke_context: &mut dyn InvokeContext,
+        instruction: Instruction,
+        keyed_account_indices: &[usize],
+        signers: &[Pubkey],
+    ) -> Result<(), InstructionError> {
+        let invoke_context = RefCell::new(invoke_context);
+
+        let (
+            message,
+            executable_accounts,
+            accounts,
+            keyed_account_indices_reordered,
+            caller_write_privileges,
+        ) = {
+            let invoke_context = invoke_context.borrow();
+
+            // Translate and verify caller's data
+            let keyed_accounts = invoke_context.get_keyed_accounts()?;
+            let keyed_accounts = keyed_account_indices
+                .iter()
+                .map(|index| keyed_account_at_index(keyed_accounts, *index))
+                .collect::<Result<Vec<&KeyedAccount>, InstructionError>>()?;
+            let (message, callee_program_id, _) =
+                Self::create_message(&instruction, &keyed_accounts, signers, &invoke_context)?;
+            let keyed_accounts = invoke_context.get_keyed_accounts()?;
+            let mut caller_write_privileges = keyed_account_indices
+                .iter()
+                .map(|index| keyed_accounts[*index].is_writable())
+                .collect::<Vec<bool>>();
+            caller_write_privileges.insert(0, false);
+            let mut accounts = vec![];
+            let mut keyed_account_indices_reordered = vec![];
+            let keyed_accounts = invoke_context.get_keyed_accounts()?;
+            'root: for account_key in message.account_keys.iter() {
+                for keyed_account_index in keyed_account_indices {
+                    let keyed_account = &keyed_accounts[*keyed_account_index];
+                    if account_key == keyed_account.unsigned_key() {
+                        accounts.push((*account_key, Rc::new(keyed_account.account.clone())));
+                        keyed_account_indices_reordered.push(*keyed_account_index);
+                        continue 'root;
+                    }
+                }
+                ic_msg!(
+                    invoke_context,
+                    "Instruction references an unknown account {}",
+                    account_key
+                );
+                return Err(InstructionError::MissingAccount);
+            }
+
+            // Process instruction
+
+            invoke_context.record_instruction(&instruction);
+
+            let program_account =
+                invoke_context
+                    .get_account(&callee_program_id)
+                    .ok_or_else(|| {
+                        ic_msg!(invoke_context, "Unknown program {}", callee_program_id);
+                        InstructionError::MissingAccount
+                    })?;
+            if !program_account.borrow().executable() {
+                ic_msg!(
+                    invoke_context,
+                    "Account {} is not executable",
+                    callee_program_id
+                );
+                return Err(InstructionError::AccountNotExecutable);
+            }
+            let programdata = if program_account.borrow().owner() == &bpf_loader_upgradeable::id() {
+                if let UpgradeableLoaderState::Program {
+                    programdata_address,
+                } = program_account.borrow().state()?
+                {
+                    if let Some(account) = invoke_context.get_account(&programdata_address) {
+                        Some((programdata_address, account))
+                    } else {
+                        ic_msg!(
+                            invoke_context,
+                            "Unknown upgradeable programdata account {}",
+                            programdata_address,
+                        );
+                        return Err(InstructionError::MissingAccount);
+                    }
+                } else {
+                    ic_msg!(
+                        invoke_context,
+                        "Upgradeable program account state not valid {}",
+                        callee_program_id,
+                    );
+                    return Err(InstructionError::MissingAccount);
+                }
+            } else {
+                None
+            };
+            let mut executable_accounts = vec![(callee_program_id, program_account)];
+            if let Some(programdata) = programdata {
+                executable_accounts.push(programdata);
+            }
+            (
+                message,
+                executable_accounts,
+                accounts,
+                keyed_account_indices_reordered,
+                caller_write_privileges,
+            )
+        };
+
+        #[allow(clippy::deref_addrof)]
+        InstructionProcessor::process_cross_program_instruction(
+            &message,
+            &executable_accounts,
+            &accounts,
+            &caller_write_privileges,
+            *(&mut *(invoke_context.borrow_mut())),
+        )?;
+
+        // Copy results back to caller
+
+        {
+            let invoke_context = invoke_context.borrow();
+            let keyed_accounts = invoke_context.get_keyed_accounts()?;
+            for (src_keyed_account_index, ((_key, account), dst_keyed_account_index)) in accounts
+                .iter()
+                .zip(keyed_account_indices_reordered)
+                .enumerate()
+            {
+                let dst_keyed_account = &keyed_accounts[dst_keyed_account_index];
+                let src_keyed_account = account.borrow();
+                if message.is_writable(src_keyed_account_index) && !src_keyed_account.executable() {
+                    if dst_keyed_account.data_len()? != src_keyed_account.data().len()
+                        && dst_keyed_account.data_len()? != 0
+                    {
+                        // Only support for `CreateAccount` at this time.
+                        // Need a way to limit total realloc size across multiple CPI calls
+                        ic_msg!(
+                            invoke_context,
+                            "Inner instructions do not support realloc, only SystemProgram::CreateAccount",
+                        );
+                        return Err(InstructionError::InvalidRealloc);
+                    }
+                    dst_keyed_account
+                        .try_account_ref_mut()?
+                        .set_lamports(src_keyed_account.lamports());
+                    dst_keyed_account
+                        .try_account_ref_mut()?
+                        .set_owner(*src_keyed_account.owner());
+                    dst_keyed_account
+                        .try_account_ref_mut()?
+                        .set_data(src_keyed_account.data().to_vec());
+                }
+            }
+        }
+
+        Ok(())
+    }
+
+    /// Process a cross-program instruction
+    /// This method calls the instruction's program entrypoint function
+    pub fn process_cross_program_instruction(
+        message: &Message,
+        executable_accounts: &[(Pubkey, Rc<RefCell<AccountSharedData>>)],
+        accounts: &[(Pubkey, Rc<RefCell<AccountSharedData>>)],
+        caller_write_privileges: &[bool],
+        invoke_context: &mut dyn InvokeContext,
+    ) -> Result<(), InstructionError> {
+        if let Some(instruction) = message.instructions.get(0) {
+            let program_id = instruction.program_id(&message.account_keys);
+
+            // Verify the calling program hasn't misbehaved
+            invoke_context.verify_and_update(instruction, accounts, caller_write_privileges)?;
+
+            // Construct keyed accounts
+            let keyed_accounts =
+                Self::create_keyed_accounts(message, instruction, executable_accounts, accounts);
+
+            // Invoke callee
+            invoke_context.push(program_id, &keyed_accounts)?;
+
+            let mut instruction_processor = InstructionProcessor::default();
+            for (program_id, process_instruction) in invoke_context.get_programs().iter() {
+                instruction_processor.add_program(*program_id, *process_instruction);
+            }
+
+            let mut result = instruction_processor.process_instruction(
+                program_id,
+                &instruction.data,
+                invoke_context,
+            );
+            if result.is_ok() {
+                // Verify the called program has not misbehaved
+                let write_privileges: Vec<bool> = (0..message.account_keys.len())
+                    .map(|i| message.is_writable(i))
+                    .collect();
+                result = invoke_context.verify_and_update(instruction, accounts, &write_privileges);
+            }
+
+            // Restore previous state
+            invoke_context.pop();
+            result
+        } else {
+            // This function is always called with a valid instruction, if that changes return an error
+            Err(InstructionError::GenericError)
+        }
+    }
+
+    /// Record the initial state of the accounts so that they can be compared
+    /// after the instruction is processed
+    pub fn create_pre_accounts(
+        message: &Message,
+        instruction: &CompiledInstruction,
+        accounts: &[(Pubkey, Rc<RefCell<AccountSharedData>>)],
+    ) -> Vec<PreAccount> {
+        let mut pre_accounts = Vec::with_capacity(instruction.accounts.len());
+        {
+            let mut work = |_unique_index: usize, account_index: usize| {
+                if account_index < message.account_keys.len() && account_index < accounts.len() {
+                    let account = accounts[account_index].1.borrow();
+                    pre_accounts.push(PreAccount::new(&accounts[account_index].0, &account));
+                    return Ok(());
+                }
+                Err(InstructionError::MissingAccount)
+            };
+            let _ = instruction.visit_each_account(&mut work);
+        }
+        pre_accounts
+    }
+
+    /// Verify the results of a cross-program instruction
+    #[allow(clippy::too_many_arguments)]
+    pub fn verify_and_update(
+        instruction: &CompiledInstruction,
+        pre_accounts: &mut [PreAccount],
+        accounts: &[(Pubkey, Rc<RefCell<AccountSharedData>>)],
+        program_id: &Pubkey,
+        rent: &Rent,
+        write_privileges: &[bool],
+        timings: &mut ExecuteDetailsTimings,
+        logger: Rc<RefCell<dyn Logger>>,
+        updated_verify_policy: bool,
+    ) -> Result<(), InstructionError> {
+        // Verify the per-account instruction results
+        let (mut pre_sum, mut post_sum) = (0_u128, 0_u128);
+        let mut work = |_unique_index: usize, account_index: usize| {
+            if account_index < write_privileges.len() && account_index < accounts.len() {
+                let (key, account) = &accounts[account_index];
+                let is_writable = write_privileges[account_index];
+                // Find the matching PreAccount
+                for pre_account in pre_accounts.iter_mut() {
+                    if key == pre_account.key() {
+                        {
+                            // Verify account has no outstanding references
+                            let _ = account
+                                .try_borrow_mut()
+                                .map_err(|_| InstructionError::AccountBorrowOutstanding)?;
+                        }
+                        let account = account.borrow();
+                        pre_account
+                            .verify(
+                                program_id,
+                                is_writable,
+                                rent,
+                                &account,
+                                timings,
+                                false,
+                                updated_verify_policy,
+                            )
+                            .map_err(|err| {
+                                ic_logger_msg!(logger, "failed to verify account {}: {}", key, err);
+                                err
+                            })?;
+                        pre_sum += u128::from(pre_account.lamports());
+                        post_sum += u128::from(account.lamports());
+                        if is_writable && !pre_account.executable() {
+                            pre_account.update(&account);
+                        }
+                        return Ok(());
+                    }
+                }
+            }
+            Err(InstructionError::MissingAccount)
+        };
+        instruction.visit_each_account(&mut work)?;
+
+        // Verify that the total sum of all the lamports did not change
+        if pre_sum != post_sum {
+            return Err(InstructionError::UnbalancedInstruction);
+        }
+        Ok(())
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use solana_sdk::{account::Account, instruction::InstructionError};
+
+    #[test]
+    fn test_is_zeroed() {
+        const ZEROS_LEN: usize = 1024;
+        let mut buf = [0; ZEROS_LEN];
+        assert!(PreAccount::is_zeroed(&buf));
+        buf[0] = 1;
+        assert!(!PreAccount::is_zeroed(&buf));
+
+        let mut buf = [0; ZEROS_LEN - 1];
+        assert!(PreAccount::is_zeroed(&buf));
+        buf[0] = 1;
+        assert!(!PreAccount::is_zeroed(&buf));
+
+        let mut buf = [0; ZEROS_LEN + 1];
+        assert!(PreAccount::is_zeroed(&buf));
+        buf[0] = 1;
+        assert!(!PreAccount::is_zeroed(&buf));
+
+        let buf = vec![];
+        assert!(PreAccount::is_zeroed(&buf));
+    }
+
+    struct Change {
+        program_id: Pubkey,
+        is_writable: bool,
+        rent: Rent,
+        pre: PreAccount,
+        post: AccountSharedData,
+    }
+    impl Change {
+        pub fn new(owner: &Pubkey, program_id: &Pubkey) -> Self {
+            Self {
+                program_id: *program_id,
+                rent: Rent::default(),
+                is_writable: true,
+                pre: PreAccount::new(
+                    &solana_sdk::pubkey::new_rand(),
+                    &AccountSharedData::from(Account {
+                        owner: *owner,
+                        lamports: std::u64::MAX,
+                        data: vec![],
+                        ..Account::default()
+                    }),
+                ),
+                post: AccountSharedData::from(Account {
+                    owner: *owner,
+                    lamports: std::u64::MAX,
+                    ..Account::default()
+                }),
+            }
+        }
+        pub fn read_only(mut self) -> Self {
+            self.is_writable = false;
+            self
+        }
+        pub fn executable(mut self, pre: bool, post: bool) -> Self {
+            self.pre.account.borrow_mut().set_executable(pre);
+            self.post.set_executable(post);
+            self
+        }
+        pub fn lamports(mut self, pre: u64, post: u64) -> Self {
+            self.pre.account.borrow_mut().set_lamports(pre);
+            self.post.set_lamports(post);
+            self
+        }
+        pub fn owner(mut self, post: &Pubkey) -> Self {
+            self.post.set_owner(*post);
+            self
+        }
+        pub fn data(mut self, pre: Vec<u8>, post: Vec<u8>) -> Self {
+            self.pre.account.borrow_mut().set_data(pre);
+            self.post.set_data(post);
+            self
+        }
+        pub fn rent_epoch(mut self, pre: u64, post: u64) -> Self {
+            self.pre.account.borrow_mut().set_rent_epoch(pre);
+            self.post.set_rent_epoch(post);
+            self
+        }
+        pub fn verify(&self) -> Result<(), InstructionError> {
+            self.pre.verify(
+                &self.program_id,
+                self.is_writable,
+                &self.rent,
+                &self.post,
+                &mut ExecuteDetailsTimings::default(),
+                false,
+                true,
+            )
+        }
+    }
+
+    #[test]
+    fn test_verify_account_changes_owner() {
+        let system_program_id = system_program::id();
+        let alice_program_id = solana_sdk::pubkey::new_rand();
+        let mallory_program_id = solana_sdk::pubkey::new_rand();
+
+        assert_eq!(
+            Change::new(&system_program_id, &system_program_id)
+                .owner(&alice_program_id)
+                .verify(),
+            Ok(()),
+            "system program should be able to change the account owner"
+        );
+        assert_eq!(
+            Change::new(&system_program_id, &system_program_id)
+                .owner(&alice_program_id)
+                .read_only()
+                .verify(),
+            Err(InstructionError::ModifiedProgramId),
+            "system program should not be able to change the account owner of a read-only account"
+        );
+        assert_eq!(
+            Change::new(&mallory_program_id, &system_program_id)
+                .owner(&alice_program_id)
+                .verify(),
+            Err(InstructionError::ModifiedProgramId),
+            "system program should not be able to change the account owner of a non-system account"
+        );
+        assert_eq!(
+            Change::new(&mallory_program_id, &mallory_program_id)
+                .owner(&alice_program_id)
+                .verify(),
+            Ok(()),
+            "mallory should be able to change the account owner, if she leaves clear data"
+        );
+        assert_eq!(
+            Change::new(&mallory_program_id, &mallory_program_id)
+                .owner(&alice_program_id)
+                .data(vec![42], vec![0])
+                .verify(),
+            Ok(()),
+            "mallory should be able to change the account owner, if she leaves clear data"
+        );
+        assert_eq!(
+            Change::new(&mallory_program_id, &mallory_program_id)
+                .owner(&alice_program_id)
+                .executable(true, true)
+                .data(vec![42], vec![0])
+                .verify(),
+            Err(InstructionError::ModifiedProgramId),
+            "mallory should not be able to change the account owner, if the account executable"
+        );
+        assert_eq!(
+            Change::new(&mallory_program_id, &mallory_program_id)
+                .owner(&alice_program_id)
+                .data(vec![42], vec![42])
+                .verify(),
+            Err(InstructionError::ModifiedProgramId),
+            "mallory should not be able to inject data into the alice program"
+        );
+    }
+
+    #[test]
+    fn test_verify_account_changes_executable() {
+        let owner = solana_sdk::pubkey::new_rand();
+        let mallory_program_id = solana_sdk::pubkey::new_rand();
+        let system_program_id = system_program::id();
+
+        assert_eq!(
+            Change::new(&owner, &system_program_id)
+                .executable(false, true)
+                .verify(),
+            Err(InstructionError::ExecutableModified),
+            "system program can't change executable if system doesn't own the account"
+        );
+        assert_eq!(
+            Change::new(&owner, &system_program_id)
+                .executable(true, true)
+                .data(vec![1], vec![2])
+                .verify(),
+            Err(InstructionError::ExecutableDataModified),
+            "system program can't change executable data if system doesn't own the account"
+        );
+        assert_eq!(
+            Change::new(&owner, &owner).executable(false, true).verify(),
+            Ok(()),
+            "owner should be able to change executable"
+        );
+        assert_eq!(
+            Change::new(&owner, &owner)
+                .executable(false, true)
+                .read_only()
+                .verify(),
+            Err(InstructionError::ExecutableModified),
+            "owner can't modify executable of read-only accounts"
+        );
+        assert_eq!(
+            Change::new(&owner, &owner).executable(true, false).verify(),
+            Err(InstructionError::ExecutableModified),
+            "owner program can't reverse executable"
+        );
+        assert_eq!(
+            Change::new(&owner, &mallory_program_id)
+                .executable(false, true)
+                .verify(),
+            Err(InstructionError::ExecutableModified),
+            "malicious Mallory should not be able to change the account executable"
+        );
+        assert_eq!(
+            Change::new(&owner, &owner)
+                .executable(false, true)
+                .data(vec![1], vec![2])
+                .verify(),
+            Ok(()),
+            "account data can change in the same instruction that sets the bit"
+        );
+        assert_eq!(
+            Change::new(&owner, &owner)
+                .executable(true, true)
+                .data(vec![1], vec![2])
+                .verify(),
+            Err(InstructionError::ExecutableDataModified),
+            "owner should not be able to change an account's data once its marked executable"
+        );
+        assert_eq!(
+            Change::new(&owner, &owner)
+                .executable(true, true)
+                .lamports(1, 2)
+                .verify(),
+            Err(InstructionError::ExecutableLamportChange),
+            "owner should not be able to add lamports once marked executable"
+        );
+        assert_eq!(
+            Change::new(&owner, &owner)
+                .executable(true, true)
+                .lamports(1, 2)
+                .verify(),
+            Err(InstructionError::ExecutableLamportChange),
+            "owner should not be able to add lamports once marked executable"
+        );
+        assert_eq!(
+            Change::new(&owner, &owner)
+                .executable(true, true)
+                .lamports(2, 1)
+                .verify(),
+            Err(InstructionError::ExecutableLamportChange),
+            "owner should not be able to subtract lamports once marked executable"
+        );
+        let data = vec![1; 100];
+        let min_lamports = Rent::default().minimum_balance(data.len());
+        assert_eq!(
+            Change::new(&owner, &owner)
+                .executable(false, true)
+                .lamports(0, min_lamports)
+                .data(data.clone(), data.clone())
+                .verify(),
+            Ok(()),
+        );
+        assert_eq!(
+            Change::new(&owner, &owner)
+                .executable(false, true)
+                .lamports(0, min_lamports - 1)
+                .data(data.clone(), data)
+                .verify(),
+            Err(InstructionError::ExecutableAccountNotRentExempt),
+            "owner should not be able to change an account's data once its marked executable"
+        );
+    }
+
+    #[test]
+    fn test_verify_account_changes_data_len() {
+        let alice_program_id = solana_sdk::pubkey::new_rand();
+
+        assert_eq!(
+            Change::new(&system_program::id(), &system_program::id())
+                .data(vec![0], vec![0, 0])
+                .verify(),
+            Ok(()),
+            "system program should be able to change the data len"
+        );
+        assert_eq!(
+            Change::new(&alice_program_id, &system_program::id())
+            .data(vec![0], vec![0,0])
+            .verify(),
+        Err(InstructionError::AccountDataSizeChanged),
+        "system program should not be able to change the data length of accounts it does not own"
+        );
+    }
+
+    #[test]
+    fn test_verify_account_changes_data() {
+        let alice_program_id = solana_sdk::pubkey::new_rand();
+        let mallory_program_id = solana_sdk::pubkey::new_rand();
+
+        assert_eq!(
+            Change::new(&alice_program_id, &alice_program_id)
+                .data(vec![0], vec![42])
+                .verify(),
+            Ok(()),
+            "alice program should be able to change the data"
+        );
+        assert_eq!(
+            Change::new(&mallory_program_id, &alice_program_id)
+                .data(vec![0], vec![42])
+                .verify(),
+            Err(InstructionError::ExternalAccountDataModified),
+            "non-owner mallory should not be able to change the account data"
+        );
+        assert_eq!(
+            Change::new(&alice_program_id, &alice_program_id)
+                .data(vec![0], vec![42])
+                .read_only()
+                .verify(),
+            Err(InstructionError::ReadonlyDataModified),
+            "alice isn't allowed to touch a CO account"
+        );
+    }
+
+    #[test]
+    fn test_verify_account_changes_rent_epoch() {
+        let alice_program_id = solana_sdk::pubkey::new_rand();
+
+        assert_eq!(
+            Change::new(&alice_program_id, &system_program::id()).verify(),
+            Ok(()),
+            "nothing changed!"
+        );
+        assert_eq!(
+            Change::new(&alice_program_id, &system_program::id())
+                .rent_epoch(0, 1)
+                .verify(),
+            Err(InstructionError::RentEpochModified),
+            "no one touches rent_epoch"
+        );
+    }
+
+    #[test]
+    fn test_verify_account_changes_deduct_lamports_and_reassign_account() {
+        let alice_program_id = solana_sdk::pubkey::new_rand();
+        let bob_program_id = solana_sdk::pubkey::new_rand();
+
+        // positive test of this capability
+        assert_eq!(
+            Change::new(&alice_program_id, &alice_program_id)
+            .owner(&bob_program_id)
+            .lamports(42, 1)
+            .data(vec![42], vec![0])
+            .verify(),
+        Ok(()),
+        "alice should be able to deduct lamports and give the account to bob if the data is zeroed",
+    );
+    }
+
+    #[test]
+    fn test_verify_account_changes_lamports() {
+        let alice_program_id = solana_sdk::pubkey::new_rand();
+
+        assert_eq!(
+            Change::new(&alice_program_id, &system_program::id())
+                .lamports(42, 0)
+                .read_only()
+                .verify(),
+            Err(InstructionError::ExternalAccountLamportSpend),
+            "debit should fail, even if system program"
+        );
+        assert_eq!(
+            Change::new(&alice_program_id, &alice_program_id)
+                .lamports(42, 0)
+                .read_only()
+                .verify(),
+            Err(InstructionError::ReadonlyLamportChange),
+            "debit should fail, even if owning program"
+        );
+        assert_eq!(
+            Change::new(&alice_program_id, &system_program::id())
+                .lamports(42, 0)
+                .owner(&system_program::id())
+                .verify(),
+            Err(InstructionError::ModifiedProgramId),
+            "system program can't debit the account unless it was the pre.owner"
+        );
+        assert_eq!(
+            Change::new(&system_program::id(), &system_program::id())
+                .lamports(42, 0)
+                .owner(&alice_program_id)
+                .verify(),
+            Ok(()),
+            "system can spend (and change owner)"
+        );
+    }
+
+    #[test]
+    fn test_verify_account_changes_data_size_changed() {
+        let alice_program_id = solana_sdk::pubkey::new_rand();
+
+        assert_eq!(
+            Change::new(&alice_program_id, &system_program::id())
+                .data(vec![0], vec![0, 0])
+                .verify(),
+            Err(InstructionError::AccountDataSizeChanged),
+            "system program should not be able to change another program's account data size"
+        );
+        assert_eq!(
+            Change::new(&alice_program_id, &alice_program_id)
+                .data(vec![0], vec![0, 0])
+                .verify(),
+            Err(InstructionError::AccountDataSizeChanged),
+            "non-system programs cannot change their data size"
+        );
+        assert_eq!(
+            Change::new(&system_program::id(), &system_program::id())
+                .data(vec![0], vec![0, 0])
+                .verify(),
+            Ok(()),
+            "system program should be able to change account data size"
+        );
+    }
+
+    #[test]
+    fn test_verify_account_changes_owner_executable() {
+        let alice_program_id = solana_sdk::pubkey::new_rand();
+        let bob_program_id = solana_sdk::pubkey::new_rand();
+
+        assert_eq!(
+            Change::new(&alice_program_id, &alice_program_id)
+                .owner(&bob_program_id)
+                .executable(false, true)
+                .verify(),
+            Err(InstructionError::ExecutableModified),
+            "Program should not be able to change owner and executable at the same time"
+        );
+    }
+
+    #[test]
+    fn test_debug() {
+        let mut instruction_processor = InstructionProcessor::default();
+        #[allow(clippy::unnecessary_wraps)]
+        fn mock_process_instruction(
+            _program_id: &Pubkey,
+            _data: &[u8],
+            _invoke_context: &mut dyn InvokeContext,
+        ) -> Result<(), InstructionError> {
+            Ok(())
+        }
+        #[allow(clippy::unnecessary_wraps)]
+        fn mock_ix_processor(
+            _pubkey: &Pubkey,
+            _data: &[u8],
+            _context: &mut dyn InvokeContext,
+        ) -> Result<(), InstructionError> {
+            Ok(())
+        }
+        let program_id = solana_sdk::pubkey::new_rand();
+        instruction_processor.add_program(program_id, mock_process_instruction);
+        instruction_processor.add_program(program_id, mock_ix_processor);
+
+        assert!(!format!("{:?}", instruction_processor).is_empty());
+    }
+}

+ 8 - 0
program-runtime/src/lib.rs

@@ -0,0 +1,8 @@
+#![cfg_attr(RUSTC_WITH_SPECIALIZATION, feature(min_specialization))]
+#![allow(clippy::integer_arithmetic)]
+
+mod instruction_processor;
+mod native_loader;
+
+pub use instruction_processor::*;
+pub use native_loader::*;

+ 1 - 0
runtime/src/native_loader.rs → program-runtime/src/native_loader.rs

@@ -5,6 +5,7 @@ use libloading::os::unix::*;
 use libloading::os::windows::*;
 use log::*;
 use num_derive::{FromPrimitive, ToPrimitive};
+use serde::Serialize;
 use solana_sdk::{
     account::ReadableAccount,
     decode_error::DecodeError,

+ 1 - 0
program-test/Cargo.toml

@@ -21,6 +21,7 @@ solana-banks-client = { path = "../banks-client", version = "=1.8.0" }
 solana-banks-server = { path = "../banks-server", version = "=1.8.0" }
 solana-bpf-loader-program = { path = "../programs/bpf_loader", version = "=1.8.0" }
 solana-logger = { path = "../logger", version = "=1.8.0" }
+solana-program-runtime = { path = "../program-runtime", version = "=1.8.0" }
 solana-runtime = { path = "../runtime", version = "=1.8.0" }
 solana-sdk = { path = "../sdk", version = "=1.8.0" }
 solana-vote-program = { path = "../programs/vote", version = "=1.8.0" }

+ 2 - 1
program-test/src/lib.rs

@@ -9,6 +9,7 @@ use {
     log::*,
     solana_banks_client::start_client,
     solana_banks_server::banks_server::start_local_server,
+    solana_program_runtime::InstructionProcessor,
     solana_runtime::{
         bank::{Bank, Builtin, ExecuteTimings},
         bank_forks::BankForks,
@@ -324,7 +325,7 @@ impl solana_sdk::program_stubs::SyscallStubs for SyscallStubs {
 
         invoke_context.record_instruction(instruction);
 
-        solana_runtime::message_processor::MessageProcessor::process_cross_program_instruction(
+        InstructionProcessor::process_cross_program_instruction(
             &message,
             &executables,
             &accounts,

+ 21 - 3
programs/bpf/Cargo.lock

@@ -2452,7 +2452,7 @@ dependencies = [
  "regex",
  "sha3",
  "solana-measure",
- "solana-runtime",
+ "solana-program-runtime",
  "solana-sdk",
  "solana_rbpf",
  "thiserror",
@@ -3099,6 +3099,24 @@ dependencies = [
  "thiserror",
 ]
 
+[[package]]
+name = "solana-program-runtime"
+version = "1.8.0"
+dependencies = [
+ "libc",
+ "libloading",
+ "log",
+ "num-derive",
+ "num-traits",
+ "rustc_version 0.4.0",
+ "serde",
+ "solana-frozen-abi 1.8.0",
+ "solana-frozen-abi-macro 1.8.0",
+ "solana-logger 1.8.0",
+ "solana-sdk",
+ "thiserror",
+]
+
 [[package]]
 name = "solana-program-test"
 version = "1.8.0"
@@ -3116,6 +3134,7 @@ dependencies = [
  "solana-banks-server",
  "solana-bpf-loader-program",
  "solana-logger 1.8.0",
+ "solana-program-runtime",
  "solana-runtime",
  "solana-sdk",
  "solana-vote-program",
@@ -3167,8 +3186,6 @@ dependencies = [
  "fnv",
  "itertools 0.10.1",
  "lazy_static",
- "libc",
- "libloading",
  "log",
  "memmap2 0.3.1",
  "num-derive",
@@ -3188,6 +3205,7 @@ dependencies = [
  "solana-logger 1.8.0",
  "solana-measure",
  "solana-metrics",
+ "solana-program-runtime",
  "solana-rayon-threadlimit",
  "solana-sdk",
  "solana-secp256k1-program",

+ 2 - 1
programs/bpf_loader/Cargo.toml

@@ -22,7 +22,7 @@ rand_core = "0.6.3"
 libsecp256k1 = "0.6.0"
 sha3 = "0.9.1"
 solana-measure = { path = "../../measure", version = "=1.8.0" }
-solana-runtime = { path = "../../runtime", version = "=1.8.0" }
+solana-program-runtime = { path = "../../program-runtime", version = "=1.8.0" }
 solana-sdk = { path = "../../sdk", version = "=1.8.0" }
 solana_rbpf = "=0.2.14"
 thiserror = "1.0"
@@ -30,6 +30,7 @@ thiserror = "1.0"
 [dev-dependencies]
 rand = "0.7.3"
 rustversion = "1.0.5"
+solana-runtime = { path = "../../runtime", version = "=1.8.0" }
 
 [lib]
 crate-type = ["lib"]

+ 2 - 2
programs/bpf_loader/src/lib.rs

@@ -14,6 +14,7 @@ use crate::{
 };
 use log::{log_enabled, trace, Level::Trace};
 use solana_measure::measure::Measure;
+use solana_program_runtime::InstructionProcessor;
 use solana_rbpf::{
     aligned_memory::AlignedMemory,
     ebpf::HOST_ALIGN,
@@ -22,7 +23,6 @@ use solana_rbpf::{
     verifier::{self, VerifierError},
     vm::{Config, EbpfVm, Executable, InstructionMeter},
 };
-use solana_runtime::message_processor::MessageProcessor;
 use solana_sdk::{
     account::{ReadableAccount, WritableAccount},
     account_utils::State,
@@ -400,7 +400,7 @@ fn process_loader_upgradeable_instruction(
                 .iter()
                 .map(|seeds| Pubkey::create_program_address(*seeds, caller_program_id))
                 .collect::<Result<Vec<Pubkey>, solana_sdk::pubkey::PubkeyError>>()?;
-            MessageProcessor::native_invoke(
+            InstructionProcessor::native_invoke(
                 invoke_context,
                 instruction,
                 &[0, 1, 6],

+ 3 - 3
programs/bpf_loader/src/syscalls.rs

@@ -1,5 +1,6 @@
 use crate::{alloc, BpfError};
 use alloc::Alloc;
+use solana_program_runtime::InstructionProcessor;
 use solana_rbpf::{
     aligned_memory::AlignedMemory,
     ebpf,
@@ -8,7 +9,6 @@ use solana_rbpf::{
     question_mark,
     vm::{EbpfVm, SyscallObject, SyscallRegistry},
 };
-use solana_runtime::message_processor::MessageProcessor;
 #[allow(deprecated)]
 use solana_sdk::sysvar::fees::Fees;
 use solana_sdk::{
@@ -2332,7 +2332,7 @@ fn call<'a>(
             .iter()
             .collect::<Vec<&KeyedAccount>>();
         let (message, callee_program_id, callee_program_id_index) =
-            MessageProcessor::create_message(
+            InstructionProcessor::create_message(
                 &instruction,
                 &keyed_account_refs,
                 &signers,
@@ -2399,7 +2399,7 @@ fn call<'a>(
     // Process instruction
 
     #[allow(clippy::deref_addrof)]
-    match MessageProcessor::process_cross_program_instruction(
+    match InstructionProcessor::process_cross_program_instruction(
         &message,
         &executables,
         &accounts,

+ 1 - 2
runtime/Cargo.toml

@@ -23,8 +23,6 @@ flate2 = "1.0.20"
 fnv = "1.0.7"
 itertools = "0.10.1"
 lazy_static = "1.4.0"
-libc = "0.2.101"
-libloading = "0.7.0"
 log = "0.4.14"
 memmap2 = "0.3.1"
 num-derive = { version = "0.3" }
@@ -43,6 +41,7 @@ solana-frozen-abi-macro = { path = "../frozen-abi/macro", version = "=1.8.0" }
 solana-logger = { path = "../logger", version = "=1.8.0" }
 solana-measure = { path = "../measure", version = "=1.8.0" }
 solana-metrics = { path = "../metrics", version = "=1.8.0" }
+solana-program-runtime = { path = "../program-runtime", version = "=1.8.0" }
 solana-rayon-threadlimit = { path = "../rayon-threadlimit", version = "=1.8.0" }
 solana-sdk = { path = "../sdk", version = "=1.8.0" }
 solana-secp256k1-program = { path = "../programs/secp256k1", version = "=1.8.0" }

+ 3 - 2
runtime/src/bank.rs

@@ -50,7 +50,7 @@ use crate::{
     inline_spl_token_v2_0,
     instruction_recorder::InstructionRecorder,
     log_collector::LogCollector,
-    message_processor::{ExecuteDetailsTimings, Executors, MessageProcessor},
+    message_processor::MessageProcessor,
     rent_collector::RentCollector,
     stake_weighted_timestamp::{
         calculate_stake_weighted_timestamp, MaxAllowableDrift, MAX_ALLOWABLE_DRIFT_PERCENTAGE,
@@ -68,6 +68,7 @@ use log::*;
 use rayon::ThreadPool;
 use solana_measure::measure::Measure;
 use solana_metrics::{datapoint_debug, inc_new_counter_debug, inc_new_counter_info};
+use solana_program_runtime::{ExecuteDetailsTimings, Executors};
 #[allow(deprecated)]
 use solana_sdk::recent_blockhashes_account;
 use solana_sdk::{
@@ -5766,10 +5767,10 @@ pub(crate) mod tests {
             create_genesis_config_with_leader, create_genesis_config_with_vote_accounts,
             GenesisConfigInfo, ValidatorVoteKeypairs,
         },
-        native_loader::NativeLoaderError,
         status_cache::MAX_CACHE_ENTRIES,
     };
     use crossbeam_channel::{bounded, unbounded};
+    use solana_program_runtime::NativeLoaderError;
     #[allow(deprecated)]
     use solana_sdk::sysvar::fees::Fees;
     use solana_sdk::{

+ 0 - 1
runtime/src/lib.rs

@@ -25,7 +25,6 @@ pub mod instruction_recorder;
 pub mod loader_utils;
 pub mod log_collector;
 pub mod message_processor;
-mod native_loader;
 pub mod neon_evm_program;
 pub mod non_circulating_supply;
 mod pubkey_bins;

文件差异内容过多而无法显示
+ 23 - 1162
runtime/src/message_processor.rs


+ 1 - 1
runtime/src/serde_snapshot.rs

@@ -11,7 +11,6 @@ use {
         blockhash_queue::BlockhashQueue,
         epoch_stakes::EpochStakes,
         hardened_unpack::UnpackedAppendVecMap,
-        message_processor::MessageProcessor,
         rent_collector::RentCollector,
         serde_snapshot::future::SerializableStorage,
         stakes::Stakes,
@@ -21,6 +20,7 @@ use {
     log::*,
     rayon::prelude::*,
     serde::{de::DeserializeOwned, Deserialize, Serialize},
+    solana_program_runtime::InstructionProcessor,
     solana_sdk::{
         clock::{Epoch, Slot, UnixTimestamp},
         epoch_schedule::EpochSchedule,

+ 2 - 2
runtime/src/serde_snapshot/future.rs

@@ -78,7 +78,7 @@ pub(crate) struct DeserializableVersionedBank {
     pub(crate) unused_accounts: UnusedAccounts,
     pub(crate) epoch_stakes: HashMap<Epoch, EpochStakes>,
     pub(crate) is_delta: bool,
-    pub(crate) message_processor: MessageProcessor,
+    pub(crate) message_processor: InstructionProcessor,
 }
 
 impl From<DeserializableVersionedBank> for BankFieldsToDeserialize {
@@ -155,7 +155,7 @@ pub(crate) struct SerializableVersionedBank<'a> {
     pub(crate) unused_accounts: UnusedAccounts,
     pub(crate) epoch_stakes: &'a HashMap<Epoch, EpochStakes>,
     pub(crate) is_delta: bool,
-    pub(crate) message_processor: MessageProcessor,
+    pub(crate) message_processor: InstructionProcessor,
 }
 
 impl<'a> From<crate::bank::BankFieldsToSerialize<'a>> for SerializableVersionedBank<'a> {

+ 1 - 1
runtime/src/serde_snapshot/tests.rs

@@ -308,7 +308,7 @@ mod test_bank_serialize {
 
     // This some what long test harness is required to freeze the ABI of
     // Bank's serialization due to versioned nature
-    #[frozen_abi(digest = "7XCv7DU27QC6iNJ1WYkXY3X4bKu8j6CxAn6morP2u4hu")]
+    #[frozen_abi(digest = "A9KFf8kLJczP3AMbFXRrqzmruoqMjooTPzdvEwZZ4EP7")]
     #[derive(Serialize, AbiExample)]
     pub struct BankAbiTestWrapperFuture {
         #[serde(serialize_with = "wrapper_future")]

部分文件因为文件数量过多而无法显示