Extract execute_transaction() from the bank

This commit is contained in:
Greg Fitzgerald 2018-11-26 23:25:02 -07:00
parent 08dc169f94
commit a7562c9be1
7 changed files with 63 additions and 52 deletions

View File

@ -22,7 +22,7 @@ use poh_service::NUM_TICKS_PER_SECOND;
use program::ProgramError; use program::ProgramError;
use rayon::prelude::*; use rayon::prelude::*;
use rpc::RpcSignatureStatus; use rpc::RpcSignatureStatus;
use runtime; use runtime::{self, RuntimeError};
use signature::Keypair; use signature::Keypair;
use signature::Signature; use signature::Signature;
use solana_sdk::account::Account; use solana_sdk::account::Account;
@ -707,24 +707,6 @@ impl Bank {
}).collect() }).collect()
} }
/// Execute a function with a subset of accounts as writable references.
/// Since the subset can point to the same references, in any order there is no way
/// for the borrow checker to track them with regards to the original set.
fn with_subset<F, A>(accounts: &mut [Account], ixes: &[u8], func: F) -> A
where
F: FnOnce(&mut [&mut Account]) -> A,
{
let mut subset: Vec<&mut Account> = ixes
.iter()
.map(|ix| {
let ptr = &mut accounts[*ix as usize] as *mut Account;
// lifetime of this unsafe is only within the scope of the closure
// there is no way to reorder them without breaking borrow checker rules
unsafe { &mut *ptr }
}).collect();
func(&mut subset)
}
fn load_executable_accounts(&self, mut program_id: Pubkey) -> Result<Vec<(Pubkey, Account)>> { fn load_executable_accounts(&self, mut program_id: Pubkey) -> Result<Vec<(Pubkey, Account)>> {
if runtime::is_legacy_program(&program_id) { if runtime::is_legacy_program(&program_id) {
return Ok(vec![]); return Ok(vec![]);
@ -769,31 +751,6 @@ impl Bank {
}).collect() }).collect()
} }
/// Execute a transaction.
/// This method calls each instruction in the transaction over the set of loaded Accounts
/// The accounts are committed back to the bank only if every instruction succeeds
fn execute_transaction(
tx: &Transaction,
loaders: &mut [Vec<(Pubkey, Account)>],
tx_accounts: &mut [Account],
tick_height: u64,
) -> Result<()> {
for (instruction_index, instruction) in tx.instructions.iter().enumerate() {
let ref mut executable_accounts = &mut loaders[instruction.program_ids_index as usize];
Self::with_subset(tx_accounts, &instruction.accounts, |program_accounts| {
runtime::execute_instruction(
tx,
instruction_index,
executable_accounts,
program_accounts,
tick_height,
).map_err(|err| BankError::ProgramError(instruction_index as u8, err))?;
Ok(())
})?;
}
Ok(())
}
pub fn store_accounts( pub fn store_accounts(
&self, &self,
txs: &[Transaction], txs: &[Transaction],
@ -903,7 +860,11 @@ impl Bank {
Err(e) => Err(e.clone()), Err(e) => Err(e.clone()),
Ok(ref mut accounts) => { Ok(ref mut accounts) => {
let mut loaders = self.load_loaders(tx)?; let mut loaders = self.load_loaders(tx)?;
Self::execute_transaction(tx, &mut loaders, accounts, tick_height) runtime::execute_transaction(tx, &mut loaders, accounts, tick_height).map_err(
|RuntimeError::ProgramError(index, err)| {
BankError::ProgramError(index, err)
},
)
} }
}).collect(); }).collect();
let execution_elapsed = now.elapsed(); let execution_elapsed = now.elapsed();

View File

@ -139,7 +139,7 @@ pub fn process(
instruction_index: usize, instruction_index: usize,
accounts: &mut [&mut Account], accounts: &mut [&mut Account],
) -> std::result::Result<(), ProgramError> { ) -> std::result::Result<(), ProgramError> {
process_instruction(&tx, instruction_index, accounts).map_err(|_| ProgramError::RuntimeError) process_instruction(&tx, instruction_index, accounts).map_err(|_| ProgramError::GenericError)
} }
//TODO the contract needs to provide a "get_balance" introspection call of the userdata //TODO the contract needs to provide a "get_balance" introspection call of the userdata

View File

@ -7,7 +7,7 @@ pub enum ProgramError {
ResultWithNegativeTokens, ResultWithNegativeTokens,
/// The program returned an error /// The program returned an error
RuntimeError, GenericError,
/// Program's instruction token balance does not equal the balance after the instruction /// Program's instruction token balance does not equal the balance after the instruction
UnbalancedInstruction, UnbalancedInstruction,

View File

@ -8,6 +8,13 @@ use system_program;
use transaction::Transaction; use transaction::Transaction;
use vote_program; use vote_program;
/// Reasons the runtime might have rejected a transaction.
#[derive(Debug, PartialEq, Eq, Clone)]
pub enum RuntimeError {
/// Executing the instruction at the given index produced an error.
ProgramError(u8, ProgramError),
}
pub fn is_legacy_program(program_id: &Pubkey) -> bool { pub fn is_legacy_program(program_id: &Pubkey) -> bool {
system_program::check_id(program_id) system_program::check_id(program_id)
|| budget_program::check_id(program_id) || budget_program::check_id(program_id)
@ -57,7 +64,7 @@ fn process_instruction(
&tx.instructions[instruction_index].userdata, &tx.instructions[instruction_index].userdata,
tick_height, tick_height,
) { ) {
return Err(ProgramError::RuntimeError); return Err(ProgramError::GenericError);
} }
} }
Ok(()) Ok(())
@ -86,7 +93,7 @@ fn verify_instruction(
/// This method calls the instruction's program entrypoint method and verifies that the result of /// This method calls the instruction's program entrypoint method and verifies that the result of
/// the call does not violate the bank's accounting rules. /// the call does not violate the bank's accounting rules.
/// The accounts are committed back to the bank only if this function returns Ok(_). /// The accounts are committed back to the bank only if this function returns Ok(_).
pub fn execute_instruction( fn execute_instruction(
tx: &Transaction, tx: &Transaction,
instruction_index: usize, instruction_index: usize,
executable_accounts: &mut [(Pubkey, Account)], executable_accounts: &mut [(Pubkey, Account)],
@ -122,3 +129,46 @@ pub fn execute_instruction(
} }
Ok(()) Ok(())
} }
/// Execute a function with a subset of accounts as writable references.
/// Since the subset can point to the same references, in any order there is no way
/// for the borrow checker to track them with regards to the original set.
fn with_subset<F, A>(accounts: &mut [Account], ixes: &[u8], func: F) -> A
where
F: FnOnce(&mut [&mut Account]) -> A,
{
let mut subset: Vec<&mut Account> = ixes
.iter()
.map(|ix| {
let ptr = &mut accounts[*ix as usize] as *mut Account;
// lifetime of this unsafe is only within the scope of the closure
// there is no way to reorder them without breaking borrow checker rules
unsafe { &mut *ptr }
}).collect();
func(&mut subset)
}
/// Execute a transaction.
/// This method calls each instruction in the transaction over the set of loaded Accounts
/// The accounts are committed back to the bank only if every instruction succeeds
pub fn execute_transaction(
tx: &Transaction,
loaders: &mut [Vec<(Pubkey, Account)>],
tx_accounts: &mut [Account],
tick_height: u64,
) -> Result<(), RuntimeError> {
for (instruction_index, instruction) in tx.instructions.iter().enumerate() {
let executable_accounts = &mut (&mut loaders[instruction.program_ids_index as usize]);
with_subset(tx_accounts, &instruction.accounts, |program_accounts| {
execute_instruction(
tx,
instruction_index,
executable_accounts,
program_accounts,
tick_height,
).map_err(|err| RuntimeError::ProgramError(instruction_index as u8, err))?;
Ok(())
})?;
}
Ok(())
}

View File

@ -57,7 +57,7 @@ pub fn process(
instruction_index: usize, instruction_index: usize,
accounts: &mut [&mut Account], accounts: &mut [&mut Account],
) -> std::result::Result<(), ProgramError> { ) -> std::result::Result<(), ProgramError> {
process_instruction(&tx, instruction_index, accounts).map_err(|_| ProgramError::RuntimeError) process_instruction(&tx, instruction_index, accounts).map_err(|_| ProgramError::GenericError)
} }
#[cfg(test)] #[cfg(test)]

View File

@ -107,7 +107,7 @@ pub fn process(
) -> std::result::Result<(), ProgramError> { ) -> std::result::Result<(), ProgramError> {
process_instruction(&tx, instruction_index, accounts).map_err(|err| match err { process_instruction(&tx, instruction_index, accounts).map_err(|err| match err {
Error::ResultWithNegativeTokens => ProgramError::ResultWithNegativeTokens, Error::ResultWithNegativeTokens => ProgramError::ResultWithNegativeTokens,
_ => ProgramError::RuntimeError, _ => ProgramError::GenericError,
}) })
} }

View File

@ -122,7 +122,7 @@ pub fn process(
instruction_index: usize, instruction_index: usize,
accounts: &mut [&mut Account], accounts: &mut [&mut Account],
) -> std::result::Result<(), ProgramError> { ) -> std::result::Result<(), ProgramError> {
process_instruction(&tx, instruction_index, accounts).map_err(|_| ProgramError::RuntimeError) process_instruction(&tx, instruction_index, accounts).map_err(|_| ProgramError::GenericError)
} }
pub fn get_max_size() -> usize { pub fn get_max_size() -> usize {