solana/runtime/src/message_processor.rs

596 lines
21 KiB
Rust
Raw Normal View History

use serde::{Deserialize, Serialize};
2021-06-04 07:04:31 -07:00
use solana_measure::measure::Measure;
use solana_program_runtime::{
instruction_recorder::InstructionRecorder,
invoke_context::{BuiltinProgram, ComputeMeter, Executors, InvokeContext},
log_collector::LogCollector,
timings::ExecuteDetailsTimings,
};
2020-01-28 17:03:20 -08:00
use solana_sdk::{
account::{AccountSharedData, WritableAccount},
2021-07-22 10:18:51 -07:00
compute_budget::ComputeBudget,
2021-11-11 14:09:28 -08:00
feature_set::{prevent_calling_precompiles_as_programs, FeatureSet},
hash::Hash,
2020-01-28 17:03:20 -08:00
message::Message,
precompiles::is_precompile,
2020-01-28 17:03:20 -08:00
pubkey::Pubkey,
2020-04-28 14:33:56 -07:00
rent::Rent,
sysvar::instructions,
2020-01-28 17:03:20 -08:00
transaction::TransactionError,
};
use std::{cell::RefCell, rc::Rc, sync::Arc};
#[derive(Debug, Default, Clone, Deserialize, Serialize)]
pub struct MessageProcessor {}
#[cfg(RUSTC_WITH_SPECIALIZATION)]
impl ::solana_frozen_abi::abi_example::AbiExample for MessageProcessor {
fn example() -> Self {
// MessageProcessor's fields are #[serde(skip)]-ed and not Serialize
// so, just rely on Default anyway.
MessageProcessor::default()
}
}
2019-04-02 08:35:38 -07:00
impl MessageProcessor {
/// Process a message.
/// This method calls each instruction in the message over the set of loaded accounts.
/// For each instruction it calls the program entrypoint method and verifies that the result of
/// the call does not violate the bank's accounting rules.
/// The accounts are committed back to the bank only if every instruction succeeds.
#[allow(clippy::too_many_arguments)]
pub fn process_message(
builtin_programs: &[BuiltinProgram],
2019-03-29 09:05:06 -07:00
message: &Message,
program_indices: &[Vec<usize>],
accounts: &[(Pubkey, Rc<RefCell<AccountSharedData>>)],
rent: Rent,
log_collector: Option<Rc<RefCell<LogCollector>>>,
executors: Rc<RefCell<Executors>>,
instruction_recorders: Option<&[InstructionRecorder]>,
feature_set: Arc<FeatureSet>,
2021-07-22 10:18:51 -07:00
compute_budget: ComputeBudget,
compute_meter: Rc<RefCell<ComputeMeter>>,
2021-03-03 15:07:45 -08:00
timings: &mut ExecuteDetailsTimings,
sysvars: &[(Pubkey, Vec<u8>)],
blockhash: Hash,
lamports_per_signature: u64,
) -> Result<(), TransactionError> {
let mut invoke_context = InvokeContext::new(
rent,
accounts,
builtin_programs,
sysvars,
log_collector,
2021-07-22 10:18:51 -07:00
compute_budget,
2021-07-16 00:31:22 -07:00
compute_meter,
executors,
instruction_recorders,
feature_set,
blockhash,
lamports_per_signature,
);
debug_assert_eq!(program_indices.len(), message.instructions.len());
for (instruction_index, (instruction, program_indices)) in message
.instructions
.iter()
.zip(program_indices.iter())
.enumerate()
{
let program_id = instruction.program_id(&message.account_keys);
if invoke_context.is_feature_active(&prevent_calling_precompiles_as_programs::id())
&& is_precompile(program_id, |id| invoke_context.is_feature_active(id))
{
// Precompiled programs don't have an instruction processor
continue;
}
// Fixup the special instructions key if present
// before the account pre-values are taken care of
for (pubkey, accont) in accounts.iter().take(message.account_keys.len()) {
if instructions::check_id(pubkey) {
let mut mut_account_ref = accont.borrow_mut();
instructions::store_current_index(
mut_account_ref.data_as_mut_slice(),
instruction_index as u16,
);
break;
}
}
invoke_context.set_instruction_index(instruction_index);
let result = invoke_context
.push(message, instruction, program_indices, None)
.and_then(|_| {
let pre_remaining_units =
invoke_context.get_compute_meter().borrow().get_remaining();
let mut time = Measure::start("execute_instruction");
invoke_context.process_instruction(&instruction.data)?;
invoke_context.verify(message, instruction, program_indices)?;
time.stop();
let post_remaining_units =
invoke_context.get_compute_meter().borrow().get_remaining();
timings.accumulate_program(
instruction.program_id(&message.account_keys),
time.as_us(),
pre_remaining_units - post_remaining_units,
);
timings.accumulate(&invoke_context.timings);
Ok(())
})
2021-06-04 07:04:31 -07:00
.map_err(|err| TransactionError::InstructionError(instruction_index as u8, err));
invoke_context.pop();
result?;
2019-03-11 15:35:25 -07:00
}
Ok(())
}
2019-03-11 15:35:25 -07:00
}
#[cfg(test)]
mod tests {
use super::*;
use crate::rent_collector::RentCollector;
use solana_program_runtime::invoke_context::ComputeMeter;
2020-01-28 17:03:20 -08:00
use solana_sdk::{
account::ReadableAccount,
2020-01-28 17:03:20 -08:00
instruction::{AccountMeta, Instruction, InstructionError},
keyed_account::keyed_account_at_index,
2020-01-28 17:03:20 -08:00
message::Message,
native_loader::{self, create_loadable_account_for_test},
secp256k1_instruction::new_secp256k1_instruction,
secp256k1_program,
2020-01-28 17:03:20 -08:00
};
#[derive(Debug, Serialize, Deserialize)]
enum MockInstruction {
NoopSuccess,
NoopFail,
ModifyOwned,
ModifyNotOwned,
ModifyReadonly,
}
#[test]
fn test_process_message_readonly_handling() {
#[derive(Serialize, Deserialize)]
enum MockSystemInstruction {
Correct,
AttemptCredit { lamports: u64 },
AttemptDataChange { data: u8 },
}
fn mock_system_process_instruction(
first_instruction_account: usize,
data: &[u8],
invoke_context: &mut InvokeContext,
) -> Result<(), InstructionError> {
let keyed_accounts = invoke_context.get_keyed_accounts()?;
if let Ok(instruction) = bincode::deserialize(data) {
match instruction {
MockSystemInstruction::Correct => Ok(()),
MockSystemInstruction::AttemptCredit { lamports } => {
keyed_account_at_index(keyed_accounts, first_instruction_account)?
.account
.borrow_mut()
.checked_sub_lamports(lamports)?;
keyed_account_at_index(keyed_accounts, first_instruction_account + 1)?
2021-04-27 07:56:18 -07:00
.account
.borrow_mut()
.checked_add_lamports(lamports)?;
Ok(())
}
// Change data in a read-only account
MockSystemInstruction::AttemptDataChange { data } => {
keyed_account_at_index(keyed_accounts, first_instruction_account + 1)?
.account
.borrow_mut()
.set_data(vec![data]);
Ok(())
}
}
} else {
Err(InstructionError::InvalidInstructionData)
}
}
let mock_system_program_id = Pubkey::new(&[2u8; 32]);
let rent_collector = RentCollector::default();
let builtin_programs = &[BuiltinProgram {
program_id: mock_system_program_id,
process_instruction: mock_system_process_instruction,
}];
let program_account = Rc::new(RefCell::new(create_loadable_account_for_test(
"mock_system_program",
)));
let accounts = vec![
(
solana_sdk::pubkey::new_rand(),
AccountSharedData::new_ref(100, 1, &mock_system_program_id),
),
(
solana_sdk::pubkey::new_rand(),
AccountSharedData::new_ref(0, 1, &mock_system_program_id),
),
(mock_system_program_id, program_account),
];
let program_indices = vec![vec![2]];
let executors = Rc::new(RefCell::new(Executors::default()));
let account_metas = vec![
AccountMeta::new(accounts[0].0, true),
AccountMeta::new_readonly(accounts[1].0, false),
];
let message = Message::new(
2021-03-03 21:46:48 -08:00
&[Instruction::new_with_bincode(
mock_system_program_id,
&MockSystemInstruction::Correct,
account_metas.clone(),
)],
Some(&accounts[0].0),
);
let result = MessageProcessor::process_message(
builtin_programs,
&message,
&program_indices,
&accounts,
rent_collector.rent,
None,
executors.clone(),
None,
Arc::new(FeatureSet::all_enabled()),
2021-07-22 10:18:51 -07:00
ComputeBudget::new(),
ComputeMeter::new_ref(std::i64::MAX as u64),
2021-03-03 15:07:45 -08:00
&mut ExecuteDetailsTimings::default(),
&[],
Hash::default(),
0,
);
assert_eq!(result, Ok(()));
assert_eq!(accounts[0].1.borrow().lamports(), 100);
assert_eq!(accounts[1].1.borrow().lamports(), 0);
let message = Message::new(
2021-03-03 21:46:48 -08:00
&[Instruction::new_with_bincode(
mock_system_program_id,
&MockSystemInstruction::AttemptCredit { lamports: 50 },
account_metas.clone(),
)],
Some(&accounts[0].0),
);
let result = MessageProcessor::process_message(
builtin_programs,
&message,
&program_indices,
&accounts,
rent_collector.rent,
None,
executors.clone(),
None,
Arc::new(FeatureSet::all_enabled()),
2021-07-22 10:18:51 -07:00
ComputeBudget::new(),
ComputeMeter::new_ref(std::i64::MAX as u64),
2021-03-03 15:07:45 -08:00
&mut ExecuteDetailsTimings::default(),
&[],
Hash::default(),
0,
);
assert_eq!(
result,
Err(TransactionError::InstructionError(
0,
InstructionError::ReadonlyLamportChange
))
);
let message = Message::new(
2021-03-03 21:46:48 -08:00
&[Instruction::new_with_bincode(
mock_system_program_id,
&MockSystemInstruction::AttemptDataChange { data: 50 },
account_metas,
)],
Some(&accounts[0].0),
);
let result = MessageProcessor::process_message(
builtin_programs,
&message,
&program_indices,
&accounts,
rent_collector.rent,
None,
executors,
None,
Arc::new(FeatureSet::all_enabled()),
2021-07-22 10:18:51 -07:00
ComputeBudget::new(),
ComputeMeter::new_ref(std::i64::MAX as u64),
2021-03-03 15:07:45 -08:00
&mut ExecuteDetailsTimings::default(),
&[],
Hash::default(),
0,
);
assert_eq!(
result,
Err(TransactionError::InstructionError(
0,
InstructionError::ReadonlyDataModified
))
);
2019-02-22 12:08:54 -08:00
}
#[test]
fn test_process_message_duplicate_accounts() {
#[derive(Serialize, Deserialize)]
enum MockSystemInstruction {
BorrowFail,
MultiBorrowMut,
DoWork { lamports: u64, data: u8 },
}
fn mock_system_process_instruction(
first_instruction_account: usize,
data: &[u8],
invoke_context: &mut InvokeContext,
) -> Result<(), InstructionError> {
let keyed_accounts = invoke_context.get_keyed_accounts()?;
if let Ok(instruction) = bincode::deserialize(data) {
match instruction {
MockSystemInstruction::BorrowFail => {
let from_account =
keyed_account_at_index(keyed_accounts, first_instruction_account)?
.try_account_ref_mut()?;
let dup_account =
keyed_account_at_index(keyed_accounts, first_instruction_account + 2)?
.try_account_ref_mut()?;
if from_account.lamports() != dup_account.lamports() {
return Err(InstructionError::InvalidArgument);
}
Ok(())
}
MockSystemInstruction::MultiBorrowMut => {
let from_lamports = {
let from_account =
keyed_account_at_index(keyed_accounts, first_instruction_account)?
.try_account_ref_mut()?;
2021-05-03 08:45:54 -07:00
from_account.lamports()
};
let dup_lamports = {
let dup_account = keyed_account_at_index(
keyed_accounts,
first_instruction_account + 2,
)?
.try_account_ref_mut()?;
2021-05-03 08:45:54 -07:00
dup_account.lamports()
};
if from_lamports != dup_lamports {
return Err(InstructionError::InvalidArgument);
}
Ok(())
}
MockSystemInstruction::DoWork { lamports, data } => {
{
let mut to_account = keyed_account_at_index(
keyed_accounts,
first_instruction_account + 1,
)?
.try_account_ref_mut()?;
let mut dup_account = keyed_account_at_index(
keyed_accounts,
first_instruction_account + 2,
)?
.try_account_ref_mut()?;
dup_account.checked_sub_lamports(lamports)?;
2021-04-27 07:56:18 -07:00
to_account.checked_add_lamports(lamports)?;
dup_account.set_data(vec![data]);
}
keyed_account_at_index(keyed_accounts, first_instruction_account)?
.try_account_ref_mut()?
.checked_sub_lamports(lamports)?;
keyed_account_at_index(keyed_accounts, first_instruction_account + 1)?
2021-04-27 07:56:18 -07:00
.try_account_ref_mut()?
.checked_add_lamports(lamports)?;
Ok(())
}
}
} else {
Err(InstructionError::InvalidInstructionData)
}
}
let mock_program_id = Pubkey::new(&[2u8; 32]);
let rent_collector = RentCollector::default();
let builtin_programs = &[BuiltinProgram {
program_id: mock_program_id,
process_instruction: mock_system_process_instruction,
}];
let program_account = Rc::new(RefCell::new(create_loadable_account_for_test(
"mock_system_program",
)));
let accounts = vec![
(
solana_sdk::pubkey::new_rand(),
AccountSharedData::new_ref(100, 1, &mock_program_id),
),
(
solana_sdk::pubkey::new_rand(),
AccountSharedData::new_ref(0, 1, &mock_program_id),
),
(mock_program_id, program_account),
];
let program_indices = vec![vec![2]];
let executors = Rc::new(RefCell::new(Executors::default()));
let account_metas = vec![
AccountMeta::new(accounts[0].0, true),
AccountMeta::new(accounts[1].0, false),
AccountMeta::new(accounts[0].0, false),
];
// Try to borrow mut the same account
let message = Message::new(
2021-03-03 21:46:48 -08:00
&[Instruction::new_with_bincode(
mock_program_id,
&MockSystemInstruction::BorrowFail,
account_metas.clone(),
)],
Some(&accounts[0].0),
);
let result = MessageProcessor::process_message(
builtin_programs,
&message,
&program_indices,
&accounts,
rent_collector.rent,
None,
executors.clone(),
None,
Arc::new(FeatureSet::all_enabled()),
2021-07-22 10:18:51 -07:00
ComputeBudget::new(),
ComputeMeter::new_ref(std::i64::MAX as u64),
2021-03-03 15:07:45 -08:00
&mut ExecuteDetailsTimings::default(),
&[],
Hash::default(),
0,
);
assert_eq!(
result,
Err(TransactionError::InstructionError(
0,
InstructionError::AccountBorrowFailed
))
);
// Try to borrow mut the same account in a safe way
let message = Message::new(
2021-03-03 21:46:48 -08:00
&[Instruction::new_with_bincode(
mock_program_id,
&MockSystemInstruction::MultiBorrowMut,
account_metas.clone(),
)],
Some(&accounts[0].0),
);
let result = MessageProcessor::process_message(
builtin_programs,
&message,
&program_indices,
&accounts,
rent_collector.rent,
None,
executors.clone(),
None,
Arc::new(FeatureSet::all_enabled()),
2021-07-22 10:18:51 -07:00
ComputeBudget::new(),
ComputeMeter::new_ref(std::i64::MAX as u64),
2021-03-03 15:07:45 -08:00
&mut ExecuteDetailsTimings::default(),
&[],
Hash::default(),
0,
);
assert_eq!(result, Ok(()));
// Do work on the same account but at different location in keyed_accounts[]
let message = Message::new(
2021-03-03 21:46:48 -08:00
&[Instruction::new_with_bincode(
mock_program_id,
&MockSystemInstruction::DoWork {
lamports: 10,
data: 42,
},
account_metas,
)],
Some(&accounts[0].0),
);
let result = MessageProcessor::process_message(
builtin_programs,
&message,
&program_indices,
&accounts,
rent_collector.rent,
None,
executors,
None,
Arc::new(FeatureSet::all_enabled()),
2021-07-22 10:18:51 -07:00
ComputeBudget::new(),
ComputeMeter::new_ref(std::i64::MAX as u64),
2021-03-03 15:07:45 -08:00
&mut ExecuteDetailsTimings::default(),
&[],
Hash::default(),
0,
);
assert_eq!(result, Ok(()));
assert_eq!(accounts[0].1.borrow().lamports(), 80);
assert_eq!(accounts[1].1.borrow().lamports(), 20);
assert_eq!(accounts[0].1.borrow().data(), &vec![42]);
}
2020-04-28 14:33:56 -07:00
#[test]
fn test_precompile() {
let mock_program_id = Pubkey::new_unique();
fn mock_process_instruction(
_first_instruction_account: usize,
_data: &[u8],
_invoke_context: &mut InvokeContext,
) -> Result<(), InstructionError> {
Err(InstructionError::Custom(0xbabb1e))
}
let builtin_programs = &[BuiltinProgram {
program_id: mock_program_id,
process_instruction: mock_process_instruction,
}];
let secp256k1_account = AccountSharedData::new_ref(1, 0, &native_loader::id());
secp256k1_account.borrow_mut().set_executable(true);
let mock_program_account = AccountSharedData::new_ref(1, 0, &native_loader::id());
mock_program_account.borrow_mut().set_executable(true);
let accounts = vec![
(secp256k1_program::id(), secp256k1_account),
(mock_program_id, mock_program_account),
];
let message = Message::new(
&[
new_secp256k1_instruction(
&libsecp256k1::SecretKey::random(&mut rand::thread_rng()),
b"hello",
),
Instruction::new_with_bytes(mock_program_id, &[], vec![]),
],
None,
);
let result = MessageProcessor::process_message(
builtin_programs,
&message,
&[vec![0], vec![1]],
&accounts,
RentCollector::default().rent,
None,
Rc::new(RefCell::new(Executors::default())),
None,
Arc::new(FeatureSet::all_enabled()),
ComputeBudget::new(),
ComputeMeter::new_ref(std::i64::MAX as u64),
&mut ExecuteDetailsTimings::default(),
&[],
Hash::default(),
0,
);
assert_eq!(
result,
Err(TransactionError::InstructionError(
1,
InstructionError::Custom(0xbabb1e)
))
);
}
}