This commit is contained in:
@ -48,7 +48,13 @@ Create TYPE "TransactionErrorCode" AS ENUM (
|
|||||||
'WouldExceedMaxBlockCostLimit',
|
'WouldExceedMaxBlockCostLimit',
|
||||||
'UnsupportedVersion',
|
'UnsupportedVersion',
|
||||||
'InvalidWritableAccount',
|
'InvalidWritableAccount',
|
||||||
'WouldExceedMaxAccountDataCostLimit'
|
'WouldExceedMaxAccountDataCostLimit',
|
||||||
|
'TooManyAccountLocks',
|
||||||
|
'AddressLookupError'
|
||||||
|
'AddressLookupTableNotFound',
|
||||||
|
'InvalidAddressLookupTableOwner',
|
||||||
|
'InvalidAddressLookupTableData',
|
||||||
|
'InvalidAddressLookupTableIndex'
|
||||||
);
|
);
|
||||||
|
|
||||||
CREATE TYPE "TransactionError" AS (
|
CREATE TYPE "TransactionError" AS (
|
||||||
|
@ -332,6 +332,10 @@ pub enum DbTransactionErrorCode {
|
|||||||
InvalidWritableAccount,
|
InvalidWritableAccount,
|
||||||
WouldExceedMaxAccountDataCostLimit,
|
WouldExceedMaxAccountDataCostLimit,
|
||||||
TooManyAccountLocks,
|
TooManyAccountLocks,
|
||||||
|
AddressLookupTableNotFound,
|
||||||
|
InvalidAddressLookupTableOwner,
|
||||||
|
InvalidAddressLookupTableData,
|
||||||
|
InvalidAddressLookupTableIndex,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<&TransactionError> for DbTransactionErrorCode {
|
impl From<&TransactionError> for DbTransactionErrorCode {
|
||||||
@ -364,6 +368,14 @@ impl From<&TransactionError> for DbTransactionErrorCode {
|
|||||||
Self::WouldExceedMaxAccountDataCostLimit
|
Self::WouldExceedMaxAccountDataCostLimit
|
||||||
}
|
}
|
||||||
TransactionError::TooManyAccountLocks => Self::TooManyAccountLocks,
|
TransactionError::TooManyAccountLocks => Self::TooManyAccountLocks,
|
||||||
|
TransactionError::AddressLookupTableNotFound => Self::AddressLookupTableNotFound,
|
||||||
|
TransactionError::InvalidAddressLookupTableOwner => {
|
||||||
|
Self::InvalidAddressLookupTableOwner
|
||||||
|
}
|
||||||
|
TransactionError::InvalidAddressLookupTableData => Self::InvalidAddressLookupTableData,
|
||||||
|
TransactionError::InvalidAddressLookupTableIndex => {
|
||||||
|
Self::InvalidAddressLookupTableIndex
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -34,7 +34,10 @@ use {
|
|||||||
MAX_TRANSACTION_FORWARDING_DELAY_GPU,
|
MAX_TRANSACTION_FORWARDING_DELAY_GPU,
|
||||||
},
|
},
|
||||||
feature_set,
|
feature_set,
|
||||||
message::Message,
|
message::{
|
||||||
|
v0::{LoadedAddresses, MessageAddressTableLookup},
|
||||||
|
Message,
|
||||||
|
},
|
||||||
pubkey::Pubkey,
|
pubkey::Pubkey,
|
||||||
short_vec::decode_shortu16_len,
|
short_vec::decode_shortu16_len,
|
||||||
signature::Signature,
|
signature::Signature,
|
||||||
@ -1121,6 +1124,7 @@ impl BankingStage {
|
|||||||
transaction_indexes: &[usize],
|
transaction_indexes: &[usize],
|
||||||
feature_set: &Arc<feature_set::FeatureSet>,
|
feature_set: &Arc<feature_set::FeatureSet>,
|
||||||
votes_only: bool,
|
votes_only: bool,
|
||||||
|
address_loader: impl Fn(&[MessageAddressTableLookup]) -> transaction::Result<LoadedAddresses>,
|
||||||
) -> (Vec<SanitizedTransaction>, Vec<usize>) {
|
) -> (Vec<SanitizedTransaction>, Vec<usize>) {
|
||||||
transaction_indexes
|
transaction_indexes
|
||||||
.iter()
|
.iter()
|
||||||
@ -1137,7 +1141,7 @@ impl BankingStage {
|
|||||||
tx,
|
tx,
|
||||||
message_hash,
|
message_hash,
|
||||||
Some(p.meta.is_simple_vote_tx()),
|
Some(p.meta.is_simple_vote_tx()),
|
||||||
|_| Err(TransactionError::UnsupportedVersion),
|
&address_loader,
|
||||||
)
|
)
|
||||||
.ok()?;
|
.ok()?;
|
||||||
tx.verify_precompiles(feature_set).ok()?;
|
tx.verify_precompiles(feature_set).ok()?;
|
||||||
@ -1203,6 +1207,7 @@ impl BankingStage {
|
|||||||
&packet_indexes,
|
&packet_indexes,
|
||||||
&bank.feature_set,
|
&bank.feature_set,
|
||||||
bank.vote_only_bank(),
|
bank.vote_only_bank(),
|
||||||
|
|lookup| bank.load_lookup_table_addresses(lookup),
|
||||||
);
|
);
|
||||||
packet_conversion_time.stop();
|
packet_conversion_time.stop();
|
||||||
inc_new_counter_info!("banking_stage-packet_conversion", 1);
|
inc_new_counter_info!("banking_stage-packet_conversion", 1);
|
||||||
@ -1277,6 +1282,7 @@ impl BankingStage {
|
|||||||
transaction_indexes,
|
transaction_indexes,
|
||||||
&bank.feature_set,
|
&bank.feature_set,
|
||||||
bank.vote_only_bank(),
|
bank.vote_only_bank(),
|
||||||
|
|lookup| bank.load_lookup_table_addresses(lookup),
|
||||||
);
|
);
|
||||||
unprocessed_packet_conversion_time.stop();
|
unprocessed_packet_conversion_time.stop();
|
||||||
|
|
||||||
@ -3226,6 +3232,7 @@ mod tests {
|
|||||||
&packet_indexes,
|
&packet_indexes,
|
||||||
&Arc::new(FeatureSet::default()),
|
&Arc::new(FeatureSet::default()),
|
||||||
votes_only,
|
votes_only,
|
||||||
|
|_| Err(TransactionError::UnsupportedVersion),
|
||||||
);
|
);
|
||||||
assert_eq!(2, txs.len());
|
assert_eq!(2, txs.len());
|
||||||
assert_eq!(vec![0, 1], tx_packet_index);
|
assert_eq!(vec![0, 1], tx_packet_index);
|
||||||
@ -3236,6 +3243,7 @@ mod tests {
|
|||||||
&packet_indexes,
|
&packet_indexes,
|
||||||
&Arc::new(FeatureSet::default()),
|
&Arc::new(FeatureSet::default()),
|
||||||
votes_only,
|
votes_only,
|
||||||
|
|_| Err(TransactionError::UnsupportedVersion),
|
||||||
);
|
);
|
||||||
assert_eq!(0, txs.len());
|
assert_eq!(0, txs.len());
|
||||||
assert_eq!(0, tx_packet_index.len());
|
assert_eq!(0, tx_packet_index.len());
|
||||||
@ -3255,6 +3263,7 @@ mod tests {
|
|||||||
&packet_indexes,
|
&packet_indexes,
|
||||||
&Arc::new(FeatureSet::default()),
|
&Arc::new(FeatureSet::default()),
|
||||||
votes_only,
|
votes_only,
|
||||||
|
|_| Err(TransactionError::UnsupportedVersion),
|
||||||
);
|
);
|
||||||
assert_eq!(3, txs.len());
|
assert_eq!(3, txs.len());
|
||||||
assert_eq!(vec![0, 1, 2], tx_packet_index);
|
assert_eq!(vec![0, 1, 2], tx_packet_index);
|
||||||
@ -3265,6 +3274,7 @@ mod tests {
|
|||||||
&packet_indexes,
|
&packet_indexes,
|
||||||
&Arc::new(FeatureSet::default()),
|
&Arc::new(FeatureSet::default()),
|
||||||
votes_only,
|
votes_only,
|
||||||
|
|_| Err(TransactionError::UnsupportedVersion),
|
||||||
);
|
);
|
||||||
assert_eq!(2, txs.len());
|
assert_eq!(2, txs.len());
|
||||||
assert_eq!(vec![0, 2], tx_packet_index);
|
assert_eq!(vec![0, 2], tx_packet_index);
|
||||||
@ -3284,6 +3294,7 @@ mod tests {
|
|||||||
&packet_indexes,
|
&packet_indexes,
|
||||||
&Arc::new(FeatureSet::default()),
|
&Arc::new(FeatureSet::default()),
|
||||||
votes_only,
|
votes_only,
|
||||||
|
|_| Err(TransactionError::UnsupportedVersion),
|
||||||
);
|
);
|
||||||
assert_eq!(3, txs.len());
|
assert_eq!(3, txs.len());
|
||||||
assert_eq!(vec![0, 1, 2], tx_packet_index);
|
assert_eq!(vec![0, 1, 2], tx_packet_index);
|
||||||
@ -3294,6 +3305,7 @@ mod tests {
|
|||||||
&packet_indexes,
|
&packet_indexes,
|
||||||
&Arc::new(FeatureSet::default()),
|
&Arc::new(FeatureSet::default()),
|
||||||
votes_only,
|
votes_only,
|
||||||
|
|_| Err(TransactionError::UnsupportedVersion),
|
||||||
);
|
);
|
||||||
assert_eq!(3, txs.len());
|
assert_eq!(3, txs.len());
|
||||||
assert_eq!(vec![0, 1, 2], tx_packet_index);
|
assert_eq!(vec![0, 1, 2], tx_packet_index);
|
||||||
|
@ -335,8 +335,8 @@ impl Blockstore {
|
|||||||
if let Some(&signature) = transaction.signatures.get(0) {
|
if let Some(&signature) = transaction.signatures.get(0) {
|
||||||
batch.delete::<cf::TransactionStatus>((0, signature, slot))?;
|
batch.delete::<cf::TransactionStatus>((0, signature, slot))?;
|
||||||
batch.delete::<cf::TransactionStatus>((1, signature, slot))?;
|
batch.delete::<cf::TransactionStatus>((1, signature, slot))?;
|
||||||
// TODO: support purging mapped addresses from versioned transactions
|
// TODO: support purging dynamically loaded addresses from versioned transactions
|
||||||
for pubkey in transaction.message.unmapped_keys() {
|
for pubkey in transaction.message.into_static_account_keys() {
|
||||||
batch.delete::<cf::AddressSignatures>((0, pubkey, slot, signature))?;
|
batch.delete::<cf::AddressSignatures>((0, pubkey, slot, signature))?;
|
||||||
batch.delete::<cf::AddressSignatures>((1, pubkey, slot, signature))?;
|
batch.delete::<cf::AddressSignatures>((1, pubkey, slot, signature))?;
|
||||||
}
|
}
|
||||||
|
@ -22,7 +22,7 @@ use {
|
|||||||
hash::Hash,
|
hash::Hash,
|
||||||
instruction::{AccountMeta, CompiledInstruction, Instruction, InstructionError},
|
instruction::{AccountMeta, CompiledInstruction, Instruction, InstructionError},
|
||||||
keyed_account::{create_keyed_accounts_unified, keyed_account_at_index, KeyedAccount},
|
keyed_account::{create_keyed_accounts_unified, keyed_account_at_index, KeyedAccount},
|
||||||
message::Message,
|
message::{Message, SanitizedMessage},
|
||||||
pubkey::Pubkey,
|
pubkey::Pubkey,
|
||||||
rent::Rent,
|
rent::Rent,
|
||||||
saturating_add_assign,
|
saturating_add_assign,
|
||||||
@ -268,7 +268,7 @@ impl<'a> InvokeContext<'a> {
|
|||||||
/// Push a stack frame onto the invocation stack
|
/// Push a stack frame onto the invocation stack
|
||||||
pub fn push(
|
pub fn push(
|
||||||
&mut self,
|
&mut self,
|
||||||
message: &Message,
|
message: &SanitizedMessage,
|
||||||
instruction: &CompiledInstruction,
|
instruction: &CompiledInstruction,
|
||||||
program_indices: &[usize],
|
program_indices: &[usize],
|
||||||
account_indices: &[usize],
|
account_indices: &[usize],
|
||||||
@ -383,11 +383,13 @@ impl<'a> InvokeContext<'a> {
|
|||||||
/// Verify the results of an instruction
|
/// Verify the results of an instruction
|
||||||
fn verify(
|
fn verify(
|
||||||
&mut self,
|
&mut self,
|
||||||
message: &Message,
|
message: &SanitizedMessage,
|
||||||
instruction: &CompiledInstruction,
|
instruction: &CompiledInstruction,
|
||||||
program_indices: &[usize],
|
program_indices: &[usize],
|
||||||
) -> Result<(), InstructionError> {
|
) -> Result<(), InstructionError> {
|
||||||
let program_id = instruction.program_id(&message.account_keys);
|
let program_id = message
|
||||||
|
.get_account_key(instruction.program_id_index as usize)
|
||||||
|
.expect("invalid program id index");
|
||||||
let do_support_realloc = self.feature_set.is_active(&do_support_realloc::id());
|
let do_support_realloc = self.feature_set.is_active(&do_support_realloc::id());
|
||||||
let cap_accounts_data_len = self.feature_set.is_active(&cap_accounts_data_len::id());
|
let cap_accounts_data_len = self.feature_set.is_active(&cap_accounts_data_len::id());
|
||||||
|
|
||||||
@ -567,9 +569,11 @@ impl<'a> InvokeContext<'a> {
|
|||||||
if let Some(instruction_recorder) = &self.instruction_recorder {
|
if let Some(instruction_recorder) = &self.instruction_recorder {
|
||||||
instruction_recorder.record_instruction(instruction);
|
instruction_recorder.record_instruction(instruction);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let message = SanitizedMessage::Legacy(message);
|
||||||
self.process_instruction(
|
self.process_instruction(
|
||||||
&message,
|
&message,
|
||||||
&message.instructions[0],
|
&message.instructions()[0],
|
||||||
&program_indices,
|
&program_indices,
|
||||||
&account_indices,
|
&account_indices,
|
||||||
&caller_write_privileges,
|
&caller_write_privileges,
|
||||||
@ -711,7 +715,7 @@ impl<'a> InvokeContext<'a> {
|
|||||||
/// Processes a cross-program instruction and returns how many compute units were used
|
/// Processes a cross-program instruction and returns how many compute units were used
|
||||||
pub fn process_instruction(
|
pub fn process_instruction(
|
||||||
&mut self,
|
&mut self,
|
||||||
message: &Message,
|
message: &SanitizedMessage,
|
||||||
instruction: &CompiledInstruction,
|
instruction: &CompiledInstruction,
|
||||||
program_indices: &[usize],
|
program_indices: &[usize],
|
||||||
account_indices: &[usize],
|
account_indices: &[usize],
|
||||||
@ -746,7 +750,10 @@ impl<'a> InvokeContext<'a> {
|
|||||||
.and_then(|_| {
|
.and_then(|_| {
|
||||||
let mut process_executable_chain_time =
|
let mut process_executable_chain_time =
|
||||||
Measure::start("process_executable_chain_time");
|
Measure::start("process_executable_chain_time");
|
||||||
self.return_data = (*instruction.program_id(&message.account_keys), Vec::new());
|
let program_id = message
|
||||||
|
.get_account_key(instruction.program_id_index as usize)
|
||||||
|
.expect("invalid program id index");
|
||||||
|
self.return_data = (*program_id, Vec::new());
|
||||||
let pre_remaining_units = self.compute_meter.borrow().get_remaining();
|
let pre_remaining_units = self.compute_meter.borrow().get_remaining();
|
||||||
let execution_result = self.process_executable_chain(&instruction.data);
|
let execution_result = self.process_executable_chain(&instruction.data);
|
||||||
let post_remaining_units = self.compute_meter.borrow().get_remaining();
|
let post_remaining_units = self.compute_meter.borrow().get_remaining();
|
||||||
@ -759,7 +766,7 @@ impl<'a> InvokeContext<'a> {
|
|||||||
if is_lowest_invocation_level {
|
if is_lowest_invocation_level {
|
||||||
self.verify(message, instruction, program_indices)
|
self.verify(message, instruction, program_indices)
|
||||||
} else {
|
} else {
|
||||||
let write_privileges: Vec<bool> = (0..message.account_keys.len())
|
let write_privileges: Vec<bool> = (0..message.account_keys_len())
|
||||||
.map(|i| message.is_writable(i))
|
.map(|i| message.is_writable(i))
|
||||||
.collect();
|
.collect();
|
||||||
self.verify_and_update(instruction, account_indices, &write_privileges)
|
self.verify_and_update(instruction, account_indices, &write_privileges)
|
||||||
@ -963,7 +970,7 @@ impl<'a> InvokeContext<'a> {
|
|||||||
|
|
||||||
pub struct MockInvokeContextPreparation {
|
pub struct MockInvokeContextPreparation {
|
||||||
pub accounts: TransactionAccountRefCells,
|
pub accounts: TransactionAccountRefCells,
|
||||||
pub message: Message,
|
pub message: SanitizedMessage,
|
||||||
pub account_indices: Vec<usize>,
|
pub account_indices: Vec<usize>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -994,17 +1001,16 @@ pub fn prepare_mock_invoke_context(
|
|||||||
for program_index in program_indices.iter().rev() {
|
for program_index in program_indices.iter().rev() {
|
||||||
metas.remove(*program_index);
|
metas.remove(*program_index);
|
||||||
}
|
}
|
||||||
let message = Message::new(
|
let message = SanitizedMessage::Legacy(Message::new(
|
||||||
&[Instruction::new_with_bytes(
|
&[Instruction::new_with_bytes(
|
||||||
program_id,
|
program_id,
|
||||||
instruction_data,
|
instruction_data,
|
||||||
metas,
|
metas,
|
||||||
)],
|
)],
|
||||||
None,
|
None,
|
||||||
);
|
));
|
||||||
let account_indices: Vec<usize> = message
|
let account_indices: Vec<usize> = message
|
||||||
.account_keys
|
.account_keys_iter()
|
||||||
.iter()
|
|
||||||
.map(|search_key| {
|
.map(|search_key| {
|
||||||
accounts
|
accounts
|
||||||
.iter()
|
.iter()
|
||||||
@ -1050,7 +1056,7 @@ pub fn with_mock_invoke_context<R, F: FnMut(&mut InvokeContext) -> R>(
|
|||||||
invoke_context
|
invoke_context
|
||||||
.push(
|
.push(
|
||||||
&preparation.message,
|
&preparation.message,
|
||||||
&preparation.message.instructions[0],
|
&preparation.message.instructions()[0],
|
||||||
&program_indices,
|
&program_indices,
|
||||||
&preparation.account_indices,
|
&preparation.account_indices,
|
||||||
)
|
)
|
||||||
@ -1075,7 +1081,7 @@ pub fn mock_process_instruction_with_sysvars(
|
|||||||
invoke_context.sysvars = sysvars;
|
invoke_context.sysvars = sysvars;
|
||||||
invoke_context.push(
|
invoke_context.push(
|
||||||
&preparation.message,
|
&preparation.message,
|
||||||
&preparation.message.instructions[0],
|
&preparation.message.instructions()[0],
|
||||||
&program_indices,
|
&program_indices,
|
||||||
&preparation.account_indices,
|
&preparation.account_indices,
|
||||||
)?;
|
)?;
|
||||||
@ -1250,10 +1256,10 @@ mod tests {
|
|||||||
}
|
}
|
||||||
let account_indices = (0..accounts.len()).collect::<Vec<usize>>();
|
let account_indices = (0..accounts.len()).collect::<Vec<usize>>();
|
||||||
|
|
||||||
let message = Message::new(
|
let message = SanitizedMessage::Legacy(Message::new(
|
||||||
&[Instruction::new_with_bytes(invoke_stack[0], &[0], metas)],
|
&[Instruction::new_with_bytes(invoke_stack[0], &[0], metas)],
|
||||||
None,
|
None,
|
||||||
);
|
));
|
||||||
let mut invoke_context = InvokeContext::new_mock(&accounts, &[]);
|
let mut invoke_context = InvokeContext::new_mock(&accounts, &[]);
|
||||||
|
|
||||||
// Check call depth increases and has a limit
|
// Check call depth increases and has a limit
|
||||||
@ -1262,7 +1268,7 @@ mod tests {
|
|||||||
if Err(InstructionError::CallDepth)
|
if Err(InstructionError::CallDepth)
|
||||||
== invoke_context.push(
|
== invoke_context.push(
|
||||||
&message,
|
&message,
|
||||||
&message.instructions[0],
|
&message.instructions()[0],
|
||||||
&[MAX_DEPTH + depth_reached],
|
&[MAX_DEPTH + depth_reached],
|
||||||
&[],
|
&[],
|
||||||
)
|
)
|
||||||
@ -1333,25 +1339,25 @@ mod tests {
|
|||||||
solana_sdk::pubkey::new_rand(),
|
solana_sdk::pubkey::new_rand(),
|
||||||
Rc::new(RefCell::new(AccountSharedData::default())),
|
Rc::new(RefCell::new(AccountSharedData::default())),
|
||||||
)];
|
)];
|
||||||
let message = Message::new(
|
let message = SanitizedMessage::Legacy(Message::new(
|
||||||
&[Instruction::new_with_bincode(
|
&[Instruction::new_with_bincode(
|
||||||
accounts[0].0,
|
accounts[0].0,
|
||||||
&MockInstruction::NoopSuccess,
|
&MockInstruction::NoopSuccess,
|
||||||
vec![AccountMeta::new_readonly(accounts[0].0, false)],
|
vec![AccountMeta::new_readonly(accounts[0].0, false)],
|
||||||
)],
|
)],
|
||||||
None,
|
None,
|
||||||
);
|
));
|
||||||
let mut invoke_context = InvokeContext::new_mock(&accounts, &[]);
|
let mut invoke_context = InvokeContext::new_mock(&accounts, &[]);
|
||||||
invoke_context
|
invoke_context
|
||||||
.push(&message, &message.instructions[0], &[0], &[])
|
.push(&message, &message.instructions()[0], &[0], &[])
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert!(invoke_context
|
assert!(invoke_context
|
||||||
.verify(&message, &message.instructions[0], &[0])
|
.verify(&message, &message.instructions()[0], &[0])
|
||||||
.is_ok());
|
.is_ok());
|
||||||
|
|
||||||
let mut _borrowed = accounts[0].1.borrow();
|
let mut _borrowed = accounts[0].1.borrow();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
invoke_context.verify(&message, &message.instructions[0], &[0]),
|
invoke_context.verify(&message, &message.instructions()[0], &[0]),
|
||||||
Err(InstructionError::AccountBorrowOutstanding)
|
Err(InstructionError::AccountBorrowOutstanding)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -1400,7 +1406,7 @@ mod tests {
|
|||||||
&MockInstruction::NoopSuccess,
|
&MockInstruction::NoopSuccess,
|
||||||
metas.clone(),
|
metas.clone(),
|
||||||
);
|
);
|
||||||
let message = Message::new(&[callee_instruction], None);
|
let message = SanitizedMessage::Legacy(Message::new(&[callee_instruction], None));
|
||||||
|
|
||||||
let builtin_programs = &[BuiltinProgram {
|
let builtin_programs = &[BuiltinProgram {
|
||||||
program_id: callee_program_id,
|
program_id: callee_program_id,
|
||||||
@ -1413,8 +1419,7 @@ mod tests {
|
|||||||
|
|
||||||
// not owned account modified by the caller (before the invoke)
|
// not owned account modified by the caller (before the invoke)
|
||||||
let caller_write_privileges = message
|
let caller_write_privileges = message
|
||||||
.account_keys
|
.account_keys_iter()
|
||||||
.iter()
|
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.map(|(i, _)| message.is_writable(i))
|
.map(|(i, _)| message.is_writable(i))
|
||||||
.collect::<Vec<bool>>();
|
.collect::<Vec<bool>>();
|
||||||
@ -1423,7 +1428,7 @@ mod tests {
|
|||||||
invoke_context
|
invoke_context
|
||||||
.process_instruction(
|
.process_instruction(
|
||||||
&message,
|
&message,
|
||||||
&message.instructions[0],
|
&message.instructions()[0],
|
||||||
&program_indices[1..],
|
&program_indices[1..],
|
||||||
&account_indices,
|
&account_indices,
|
||||||
&caller_write_privileges,
|
&caller_write_privileges,
|
||||||
@ -1440,7 +1445,7 @@ mod tests {
|
|||||||
invoke_context
|
invoke_context
|
||||||
.process_instruction(
|
.process_instruction(
|
||||||
&message,
|
&message,
|
||||||
&message.instructions[0],
|
&message.instructions()[0],
|
||||||
&program_indices[1..],
|
&program_indices[1..],
|
||||||
&account_indices,
|
&account_indices,
|
||||||
&caller_write_privileges,
|
&caller_write_privileges,
|
||||||
@ -1486,20 +1491,19 @@ mod tests {
|
|||||||
for case in cases {
|
for case in cases {
|
||||||
let callee_instruction =
|
let callee_instruction =
|
||||||
Instruction::new_with_bincode(callee_program_id, &case.0, metas.clone());
|
Instruction::new_with_bincode(callee_program_id, &case.0, metas.clone());
|
||||||
let message = Message::new(&[callee_instruction], None);
|
let message = SanitizedMessage::Legacy(Message::new(&[callee_instruction], None));
|
||||||
invoke_context
|
invoke_context
|
||||||
.push(&message, &caller_instruction, &program_indices[..1], &[])
|
.push(&message, &caller_instruction, &program_indices[..1], &[])
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let caller_write_privileges = message
|
let caller_write_privileges = message
|
||||||
.account_keys
|
.account_keys_iter()
|
||||||
.iter()
|
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.map(|(i, _)| message.is_writable(i))
|
.map(|(i, _)| message.is_writable(i))
|
||||||
.collect::<Vec<bool>>();
|
.collect::<Vec<bool>>();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
invoke_context.process_instruction(
|
invoke_context.process_instruction(
|
||||||
&message,
|
&message,
|
||||||
&message.instructions[0],
|
&message.instructions()[0],
|
||||||
&program_indices[1..],
|
&program_indices[1..],
|
||||||
&account_indices,
|
&account_indices,
|
||||||
&caller_write_privileges,
|
&caller_write_privileges,
|
||||||
@ -1553,7 +1557,7 @@ mod tests {
|
|||||||
&MockInstruction::NoopSuccess,
|
&MockInstruction::NoopSuccess,
|
||||||
metas.clone(),
|
metas.clone(),
|
||||||
);
|
);
|
||||||
let message = Message::new(&[callee_instruction.clone()], None);
|
let message = SanitizedMessage::Legacy(Message::new(&[callee_instruction.clone()], None));
|
||||||
|
|
||||||
let builtin_programs = &[BuiltinProgram {
|
let builtin_programs = &[BuiltinProgram {
|
||||||
program_id: callee_program_id,
|
program_id: callee_program_id,
|
||||||
@ -1602,7 +1606,8 @@ mod tests {
|
|||||||
for case in cases {
|
for case in cases {
|
||||||
let callee_instruction =
|
let callee_instruction =
|
||||||
Instruction::new_with_bincode(callee_program_id, &case.0, metas.clone());
|
Instruction::new_with_bincode(callee_program_id, &case.0, metas.clone());
|
||||||
let message = Message::new(&[callee_instruction.clone()], None);
|
let message =
|
||||||
|
SanitizedMessage::Legacy(Message::new(&[callee_instruction.clone()], None));
|
||||||
invoke_context
|
invoke_context
|
||||||
.push(&message, &caller_instruction, &program_indices, &[])
|
.push(&message, &caller_instruction, &program_indices, &[])
|
||||||
.unwrap();
|
.unwrap();
|
||||||
@ -1627,22 +1632,22 @@ mod tests {
|
|||||||
),
|
),
|
||||||
];
|
];
|
||||||
|
|
||||||
let noop_message = Message::new(
|
let noop_message = SanitizedMessage::Legacy(Message::new(
|
||||||
&[Instruction::new_with_bincode(
|
&[Instruction::new_with_bincode(
|
||||||
accounts[0].0,
|
accounts[0].0,
|
||||||
&MockInstruction::NoopSuccess,
|
&MockInstruction::NoopSuccess,
|
||||||
vec![AccountMeta::new_readonly(accounts[0].0, false)],
|
vec![AccountMeta::new_readonly(accounts[0].0, false)],
|
||||||
)],
|
)],
|
||||||
None,
|
None,
|
||||||
);
|
));
|
||||||
let neon_message = Message::new(
|
let neon_message = SanitizedMessage::Legacy(Message::new(
|
||||||
&[Instruction::new_with_bincode(
|
&[Instruction::new_with_bincode(
|
||||||
crate::neon_evm_program::id(),
|
crate::neon_evm_program::id(),
|
||||||
&MockInstruction::NoopSuccess,
|
&MockInstruction::NoopSuccess,
|
||||||
vec![AccountMeta::new_readonly(accounts[0].0, false)],
|
vec![AccountMeta::new_readonly(accounts[0].0, false)],
|
||||||
)],
|
)],
|
||||||
None,
|
None,
|
||||||
);
|
));
|
||||||
|
|
||||||
let mut feature_set = FeatureSet::all_enabled();
|
let mut feature_set = FeatureSet::all_enabled();
|
||||||
feature_set.deactivate(&tx_wide_compute_cap::id());
|
feature_set.deactivate(&tx_wide_compute_cap::id());
|
||||||
@ -1651,7 +1656,7 @@ mod tests {
|
|||||||
invoke_context.feature_set = Arc::new(feature_set);
|
invoke_context.feature_set = Arc::new(feature_set);
|
||||||
|
|
||||||
invoke_context
|
invoke_context
|
||||||
.push(&noop_message, &noop_message.instructions[0], &[0], &[])
|
.push(&noop_message, &noop_message.instructions()[0], &[0], &[])
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
*invoke_context.get_compute_budget(),
|
*invoke_context.get_compute_budget(),
|
||||||
@ -1660,7 +1665,7 @@ mod tests {
|
|||||||
invoke_context.pop();
|
invoke_context.pop();
|
||||||
|
|
||||||
invoke_context
|
invoke_context
|
||||||
.push(&neon_message, &neon_message.instructions[0], &[1], &[])
|
.push(&neon_message, &neon_message.instructions()[0], &[1], &[])
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let expected_compute_budget = ComputeBudget {
|
let expected_compute_budget = ComputeBudget {
|
||||||
max_units: 500_000,
|
max_units: 500_000,
|
||||||
@ -1674,7 +1679,7 @@ mod tests {
|
|||||||
invoke_context.pop();
|
invoke_context.pop();
|
||||||
|
|
||||||
invoke_context
|
invoke_context
|
||||||
.push(&noop_message, &noop_message.instructions[0], &[0], &[])
|
.push(&noop_message, &noop_message.instructions()[0], &[0], &[])
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
*invoke_context.get_compute_budget(),
|
*invoke_context.get_compute_budget(),
|
||||||
@ -1739,19 +1744,19 @@ mod tests {
|
|||||||
},
|
},
|
||||||
metas.clone(),
|
metas.clone(),
|
||||||
);
|
);
|
||||||
let message = Message::new(&[callee_instruction.clone()], None);
|
let message =
|
||||||
|
SanitizedMessage::Legacy(Message::new(&[callee_instruction.clone()], None));
|
||||||
invoke_context
|
invoke_context
|
||||||
.push(&message, &caller_instruction, &program_indices[..1], &[])
|
.push(&message, &caller_instruction, &program_indices[..1], &[])
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let caller_write_privileges = message
|
let caller_write_privileges = message
|
||||||
.account_keys
|
.account_keys_iter()
|
||||||
.iter()
|
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.map(|(i, _)| message.is_writable(i))
|
.map(|(i, _)| message.is_writable(i))
|
||||||
.collect::<Vec<bool>>();
|
.collect::<Vec<bool>>();
|
||||||
let result = invoke_context.process_instruction(
|
let result = invoke_context.process_instruction(
|
||||||
&message,
|
&message,
|
||||||
&message.instructions[0],
|
&message.instructions()[0],
|
||||||
&program_indices[1..],
|
&program_indices[1..],
|
||||||
&account_indices,
|
&account_indices,
|
||||||
&caller_write_privileges,
|
&caller_write_privileges,
|
||||||
@ -1815,10 +1820,9 @@ mod tests {
|
|||||||
&MockInstruction::Resize { new_len },
|
&MockInstruction::Resize { new_len },
|
||||||
metas.clone(),
|
metas.clone(),
|
||||||
);
|
);
|
||||||
let message = Message::new(&[instruction], None);
|
let message = SanitizedMessage::Legacy(Message::new(&[instruction], None));
|
||||||
let caller_write_privileges = message
|
let caller_write_privileges = message
|
||||||
.account_keys
|
.account_keys_iter()
|
||||||
.iter()
|
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.map(|(i, _)| message.is_writable(i))
|
.map(|(i, _)| message.is_writable(i))
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
@ -1826,7 +1830,7 @@ mod tests {
|
|||||||
let result = invoke_context
|
let result = invoke_context
|
||||||
.process_instruction(
|
.process_instruction(
|
||||||
&message,
|
&message,
|
||||||
&message.instructions[0],
|
&message.instructions()[0],
|
||||||
&program_indices,
|
&program_indices,
|
||||||
&account_indices,
|
&account_indices,
|
||||||
&caller_write_privileges,
|
&caller_write_privileges,
|
||||||
@ -1846,10 +1850,9 @@ mod tests {
|
|||||||
&MockInstruction::Resize { new_len },
|
&MockInstruction::Resize { new_len },
|
||||||
metas.clone(),
|
metas.clone(),
|
||||||
);
|
);
|
||||||
let message = Message::new(&[instruction], None);
|
let message = SanitizedMessage::Legacy(Message::new(&[instruction], None));
|
||||||
let caller_write_privileges = message
|
let caller_write_privileges = message
|
||||||
.account_keys
|
.account_keys_iter()
|
||||||
.iter()
|
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.map(|(i, _)| message.is_writable(i))
|
.map(|(i, _)| message.is_writable(i))
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
@ -1857,7 +1860,7 @@ mod tests {
|
|||||||
let result = invoke_context
|
let result = invoke_context
|
||||||
.process_instruction(
|
.process_instruction(
|
||||||
&message,
|
&message,
|
||||||
&message.instructions[0],
|
&message.instructions()[0],
|
||||||
&program_indices,
|
&program_indices,
|
||||||
&account_indices,
|
&account_indices,
|
||||||
&caller_write_privileges,
|
&caller_write_privileges,
|
||||||
@ -1877,10 +1880,9 @@ mod tests {
|
|||||||
&MockInstruction::Resize { new_len },
|
&MockInstruction::Resize { new_len },
|
||||||
metas,
|
metas,
|
||||||
);
|
);
|
||||||
let message = Message::new(&[instruction], None);
|
let message = SanitizedMessage::Legacy(Message::new(&[instruction], None));
|
||||||
let caller_write_privileges = message
|
let caller_write_privileges = message
|
||||||
.account_keys
|
.account_keys_iter()
|
||||||
.iter()
|
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.map(|(i, _)| message.is_writable(i))
|
.map(|(i, _)| message.is_writable(i))
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
@ -1888,7 +1890,7 @@ mod tests {
|
|||||||
let result = invoke_context
|
let result = invoke_context
|
||||||
.process_instruction(
|
.process_instruction(
|
||||||
&message,
|
&message,
|
||||||
&message.instructions[0],
|
&message.instructions()[0],
|
||||||
&program_indices,
|
&program_indices,
|
||||||
&account_indices,
|
&account_indices,
|
||||||
&caller_write_privileges,
|
&caller_write_privileges,
|
||||||
|
@ -32,7 +32,7 @@ use {
|
|||||||
genesis_config::{ClusterType, GenesisConfig},
|
genesis_config::{ClusterType, GenesisConfig},
|
||||||
hash::Hash,
|
hash::Hash,
|
||||||
instruction::{Instruction, InstructionError},
|
instruction::{Instruction, InstructionError},
|
||||||
message::Message,
|
message::{Message, SanitizedMessage},
|
||||||
native_token::sol_to_lamports,
|
native_token::sol_to_lamports,
|
||||||
poh_config::PohConfig,
|
poh_config::PohConfig,
|
||||||
program_error::{ProgramError, ACCOUNT_BORROW_FAILED, UNSUPPORTED_SYSVAR},
|
program_error::{ProgramError, ACCOUNT_BORROW_FAILED, UNSUPPORTED_SYSVAR},
|
||||||
@ -316,10 +316,11 @@ impl solana_sdk::program_stubs::SyscallStubs for SyscallStubs {
|
|||||||
instruction_recorder.record_instruction(instruction.clone());
|
instruction_recorder.record_instruction(instruction.clone());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let message = SanitizedMessage::Legacy(message);
|
||||||
invoke_context
|
invoke_context
|
||||||
.process_instruction(
|
.process_instruction(
|
||||||
&message,
|
&message,
|
||||||
&message.instructions[0],
|
&message.instructions()[0],
|
||||||
&program_indices,
|
&program_indices,
|
||||||
&account_indices,
|
&account_indices,
|
||||||
&caller_privileges,
|
&caller_privileges,
|
||||||
|
@ -6,6 +6,7 @@ use {
|
|||||||
instruction::InstructionError,
|
instruction::InstructionError,
|
||||||
pubkey::Pubkey,
|
pubkey::Pubkey,
|
||||||
slot_hashes::{SlotHashes, MAX_ENTRIES},
|
slot_hashes::{SlotHashes, MAX_ENTRIES},
|
||||||
|
transaction::AddressLookupError,
|
||||||
},
|
},
|
||||||
std::borrow::Cow,
|
std::borrow::Cow,
|
||||||
};
|
};
|
||||||
@ -133,6 +134,49 @@ impl<'a> AddressLookupTable<'a> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the length of addresses that are active for lookups
|
||||||
|
pub fn get_active_addresses_len(
|
||||||
|
&self,
|
||||||
|
current_slot: Slot,
|
||||||
|
slot_hashes: &SlotHashes,
|
||||||
|
) -> Result<usize, AddressLookupError> {
|
||||||
|
if !self.meta.is_active(current_slot, slot_hashes) {
|
||||||
|
// Once a lookup table is no longer active, it can be closed
|
||||||
|
// at any point, so returning a specific error for deactivated
|
||||||
|
// lookup tables could result in a race condition.
|
||||||
|
return Err(AddressLookupError::LookupTableAccountNotFound);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the address table was extended in the same slot in which it is used
|
||||||
|
// to lookup addresses for another transaction, the recently extended
|
||||||
|
// addresses are not considered active and won't be accessible.
|
||||||
|
let active_addresses_len = if current_slot > self.meta.last_extended_slot {
|
||||||
|
self.addresses.len()
|
||||||
|
} else {
|
||||||
|
self.meta.last_extended_slot_start_index as usize
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(active_addresses_len)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Lookup addresses for provided table indexes. Since lookups are performed on
|
||||||
|
/// tables which are not read-locked, this implementation needs to be careful
|
||||||
|
/// about resolving addresses consistently.
|
||||||
|
pub fn lookup(
|
||||||
|
&self,
|
||||||
|
current_slot: Slot,
|
||||||
|
indexes: &[u8],
|
||||||
|
slot_hashes: &SlotHashes,
|
||||||
|
) -> Result<Vec<Pubkey>, AddressLookupError> {
|
||||||
|
let active_addresses_len = self.get_active_addresses_len(current_slot, slot_hashes)?;
|
||||||
|
let active_addresses = &self.addresses[0..active_addresses_len];
|
||||||
|
indexes
|
||||||
|
.iter()
|
||||||
|
.map(|idx| active_addresses.get(*idx as usize).cloned())
|
||||||
|
.collect::<Option<_>>()
|
||||||
|
.ok_or(AddressLookupError::InvalidLookupIndex)
|
||||||
|
}
|
||||||
|
|
||||||
/// Serialize an address table including its addresses
|
/// Serialize an address table including its addresses
|
||||||
pub fn serialize_for_tests(self, data: &mut Vec<u8>) -> Result<(), InstructionError> {
|
pub fn serialize_for_tests(self, data: &mut Vec<u8>) -> Result<(), InstructionError> {
|
||||||
data.resize(LOOKUP_TABLE_META_SIZE, 0);
|
data.resize(LOOKUP_TABLE_META_SIZE, 0);
|
||||||
@ -322,4 +366,117 @@ mod tests {
|
|||||||
test_case(case);
|
test_case(case);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_lookup_from_empty_table() {
|
||||||
|
let lookup_table = AddressLookupTable {
|
||||||
|
meta: LookupTableMeta::default(),
|
||||||
|
addresses: Cow::Owned(vec![]),
|
||||||
|
};
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
lookup_table.lookup(0, &[], &SlotHashes::default()),
|
||||||
|
Ok(vec![])
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
lookup_table.lookup(0, &[0], &SlotHashes::default()),
|
||||||
|
Err(AddressLookupError::InvalidLookupIndex)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_lookup_from_deactivating_table() {
|
||||||
|
let current_slot = 1;
|
||||||
|
let slot_hashes = SlotHashes::default();
|
||||||
|
let addresses = vec![Pubkey::new_unique()];
|
||||||
|
let lookup_table = AddressLookupTable {
|
||||||
|
meta: LookupTableMeta {
|
||||||
|
deactivation_slot: current_slot,
|
||||||
|
last_extended_slot: current_slot - 1,
|
||||||
|
..LookupTableMeta::default()
|
||||||
|
},
|
||||||
|
addresses: Cow::Owned(addresses.clone()),
|
||||||
|
};
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
lookup_table.meta.status(current_slot, &slot_hashes),
|
||||||
|
LookupTableStatus::Deactivating {
|
||||||
|
remaining_blocks: MAX_ENTRIES + 1
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
lookup_table.lookup(current_slot, &[0], &slot_hashes),
|
||||||
|
Ok(vec![addresses[0]]),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_lookup_from_deactivated_table() {
|
||||||
|
let current_slot = 1;
|
||||||
|
let slot_hashes = SlotHashes::default();
|
||||||
|
let lookup_table = AddressLookupTable {
|
||||||
|
meta: LookupTableMeta {
|
||||||
|
deactivation_slot: current_slot - 1,
|
||||||
|
last_extended_slot: current_slot - 1,
|
||||||
|
..LookupTableMeta::default()
|
||||||
|
},
|
||||||
|
addresses: Cow::Owned(vec![]),
|
||||||
|
};
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
lookup_table.meta.status(current_slot, &slot_hashes),
|
||||||
|
LookupTableStatus::Deactivated
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
lookup_table.lookup(current_slot, &[0], &slot_hashes),
|
||||||
|
Err(AddressLookupError::LookupTableAccountNotFound)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_lookup_from_table_extended_in_current_slot() {
|
||||||
|
let current_slot = 0;
|
||||||
|
let addresses: Vec<_> = (0..2).map(|_| Pubkey::new_unique()).collect();
|
||||||
|
let lookup_table = AddressLookupTable {
|
||||||
|
meta: LookupTableMeta {
|
||||||
|
last_extended_slot: current_slot,
|
||||||
|
last_extended_slot_start_index: 1,
|
||||||
|
..LookupTableMeta::default()
|
||||||
|
},
|
||||||
|
addresses: Cow::Owned(addresses.clone()),
|
||||||
|
};
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
lookup_table.lookup(current_slot, &[0], &SlotHashes::default()),
|
||||||
|
Ok(vec![addresses[0]])
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
lookup_table.lookup(current_slot, &[1], &SlotHashes::default()),
|
||||||
|
Err(AddressLookupError::InvalidLookupIndex),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_lookup_from_table_extended_in_previous_slot() {
|
||||||
|
let current_slot = 1;
|
||||||
|
let addresses: Vec<_> = (0..10).map(|_| Pubkey::new_unique()).collect();
|
||||||
|
let lookup_table = AddressLookupTable {
|
||||||
|
meta: LookupTableMeta {
|
||||||
|
last_extended_slot: current_slot - 1,
|
||||||
|
last_extended_slot_start_index: 1,
|
||||||
|
..LookupTableMeta::default()
|
||||||
|
},
|
||||||
|
addresses: Cow::Owned(addresses.clone()),
|
||||||
|
};
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
lookup_table.lookup(current_slot, &[0, 3, 1, 5], &SlotHashes::default()),
|
||||||
|
Ok(vec![addresses[0], addresses[3], addresses[1], addresses[5]])
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
lookup_table.lookup(current_slot, &[10], &SlotHashes::default()),
|
||||||
|
Err(AddressLookupError::InvalidLookupIndex),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -453,7 +453,7 @@ mod tests {
|
|||||||
invoke_context
|
invoke_context
|
||||||
.push(
|
.push(
|
||||||
&preparation.message,
|
&preparation.message,
|
||||||
&preparation.message.instructions[0],
|
&preparation.message.instructions()[0],
|
||||||
&program_indices,
|
&program_indices,
|
||||||
&preparation.account_indices,
|
&preparation.account_indices,
|
||||||
)
|
)
|
||||||
|
@ -33,7 +33,7 @@ use {
|
|||||||
hash::{Hasher, HASH_BYTES},
|
hash::{Hasher, HASH_BYTES},
|
||||||
instruction::{AccountMeta, Instruction, InstructionError},
|
instruction::{AccountMeta, Instruction, InstructionError},
|
||||||
keccak,
|
keccak,
|
||||||
message::Message,
|
message::{Message, SanitizedMessage},
|
||||||
native_loader,
|
native_loader,
|
||||||
precompiles::is_precompile,
|
precompiles::is_precompile,
|
||||||
program::MAX_RETURN_DATA,
|
program::MAX_RETURN_DATA,
|
||||||
@ -2365,10 +2365,11 @@ fn call<'a, 'b: 'a>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Process instruction
|
// Process instruction
|
||||||
|
let message = SanitizedMessage::Legacy(message);
|
||||||
invoke_context
|
invoke_context
|
||||||
.process_instruction(
|
.process_instruction(
|
||||||
&message,
|
&message,
|
||||||
&message.instructions[0],
|
&message.instructions()[0],
|
||||||
&program_indices,
|
&program_indices,
|
||||||
&account_indices,
|
&account_indices,
|
||||||
&caller_write_privileges,
|
&caller_write_privileges,
|
||||||
@ -2962,13 +2963,13 @@ mod tests {
|
|||||||
let program_id = Pubkey::new_unique();
|
let program_id = Pubkey::new_unique();
|
||||||
let program_account = AccountSharedData::new_ref(0, 0, &bpf_loader::id());
|
let program_account = AccountSharedData::new_ref(0, 0, &bpf_loader::id());
|
||||||
let accounts = [(program_id, program_account)];
|
let accounts = [(program_id, program_account)];
|
||||||
let message = Message::new(
|
let message = SanitizedMessage::Legacy(Message::new(
|
||||||
&[Instruction::new_with_bytes(program_id, &[], vec![])],
|
&[Instruction::new_with_bytes(program_id, &[], vec![])],
|
||||||
None,
|
None,
|
||||||
);
|
));
|
||||||
let mut invoke_context = InvokeContext::new_mock(&accounts, &[]);
|
let mut invoke_context = InvokeContext::new_mock(&accounts, &[]);
|
||||||
invoke_context
|
invoke_context
|
||||||
.push(&message, &message.instructions[0], &[0], &[])
|
.push(&message, &message.instructions()[0], &[0], &[])
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let mut syscall_panic = SyscallPanic {
|
let mut syscall_panic = SyscallPanic {
|
||||||
invoke_context: Rc::new(RefCell::new(&mut invoke_context)),
|
invoke_context: Rc::new(RefCell::new(&mut invoke_context)),
|
||||||
@ -3039,13 +3040,13 @@ mod tests {
|
|||||||
let program_id = Pubkey::new_unique();
|
let program_id = Pubkey::new_unique();
|
||||||
let program_account = AccountSharedData::new_ref(0, 0, &bpf_loader::id());
|
let program_account = AccountSharedData::new_ref(0, 0, &bpf_loader::id());
|
||||||
let accounts = [(program_id, program_account)];
|
let accounts = [(program_id, program_account)];
|
||||||
let message = Message::new(
|
let message = SanitizedMessage::Legacy(Message::new(
|
||||||
&[Instruction::new_with_bytes(program_id, &[], vec![])],
|
&[Instruction::new_with_bytes(program_id, &[], vec![])],
|
||||||
None,
|
None,
|
||||||
);
|
));
|
||||||
let mut invoke_context = InvokeContext::new_mock(&accounts, &[]);
|
let mut invoke_context = InvokeContext::new_mock(&accounts, &[]);
|
||||||
invoke_context
|
invoke_context
|
||||||
.push(&message, &message.instructions[0], &[0], &[])
|
.push(&message, &message.instructions()[0], &[0], &[])
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let mut syscall_sol_log = SyscallLog {
|
let mut syscall_sol_log = SyscallLog {
|
||||||
invoke_context: Rc::new(RefCell::new(&mut invoke_context)),
|
invoke_context: Rc::new(RefCell::new(&mut invoke_context)),
|
||||||
@ -3143,13 +3144,13 @@ mod tests {
|
|||||||
let program_id = Pubkey::new_unique();
|
let program_id = Pubkey::new_unique();
|
||||||
let program_account = AccountSharedData::new_ref(0, 0, &bpf_loader::id());
|
let program_account = AccountSharedData::new_ref(0, 0, &bpf_loader::id());
|
||||||
let accounts = [(program_id, program_account)];
|
let accounts = [(program_id, program_account)];
|
||||||
let message = Message::new(
|
let message = SanitizedMessage::Legacy(Message::new(
|
||||||
&[Instruction::new_with_bytes(program_id, &[], vec![])],
|
&[Instruction::new_with_bytes(program_id, &[], vec![])],
|
||||||
None,
|
None,
|
||||||
);
|
));
|
||||||
let mut invoke_context = InvokeContext::new_mock(&accounts, &[]);
|
let mut invoke_context = InvokeContext::new_mock(&accounts, &[]);
|
||||||
invoke_context
|
invoke_context
|
||||||
.push(&message, &message.instructions[0], &[0], &[])
|
.push(&message, &message.instructions()[0], &[0], &[])
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let cost = invoke_context.get_compute_budget().log_64_units;
|
let cost = invoke_context.get_compute_budget().log_64_units;
|
||||||
let mut syscall_sol_log_u64 = SyscallLogU64 {
|
let mut syscall_sol_log_u64 = SyscallLogU64 {
|
||||||
@ -3185,13 +3186,13 @@ mod tests {
|
|||||||
let program_id = Pubkey::new_unique();
|
let program_id = Pubkey::new_unique();
|
||||||
let program_account = AccountSharedData::new_ref(0, 0, &bpf_loader::id());
|
let program_account = AccountSharedData::new_ref(0, 0, &bpf_loader::id());
|
||||||
let accounts = [(program_id, program_account)];
|
let accounts = [(program_id, program_account)];
|
||||||
let message = Message::new(
|
let message = SanitizedMessage::Legacy(Message::new(
|
||||||
&[Instruction::new_with_bytes(program_id, &[], vec![])],
|
&[Instruction::new_with_bytes(program_id, &[], vec![])],
|
||||||
None,
|
None,
|
||||||
);
|
));
|
||||||
let mut invoke_context = InvokeContext::new_mock(&accounts, &[]);
|
let mut invoke_context = InvokeContext::new_mock(&accounts, &[]);
|
||||||
invoke_context
|
invoke_context
|
||||||
.push(&message, &message.instructions[0], &[0], &[])
|
.push(&message, &message.instructions()[0], &[0], &[])
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let cost = invoke_context.get_compute_budget().log_pubkey_units;
|
let cost = invoke_context.get_compute_budget().log_pubkey_units;
|
||||||
let mut syscall_sol_pubkey = SyscallLogPubkey {
|
let mut syscall_sol_pubkey = SyscallLogPubkey {
|
||||||
@ -3397,10 +3398,10 @@ mod tests {
|
|||||||
let program_id = Pubkey::new_unique();
|
let program_id = Pubkey::new_unique();
|
||||||
let program_account = AccountSharedData::new_ref(0, 0, &bpf_loader_deprecated::id());
|
let program_account = AccountSharedData::new_ref(0, 0, &bpf_loader_deprecated::id());
|
||||||
let accounts = [(program_id, program_account)];
|
let accounts = [(program_id, program_account)];
|
||||||
let message = Message::new(
|
let message = SanitizedMessage::Legacy(Message::new(
|
||||||
&[Instruction::new_with_bytes(program_id, &[], vec![])],
|
&[Instruction::new_with_bytes(program_id, &[], vec![])],
|
||||||
None,
|
None,
|
||||||
);
|
));
|
||||||
|
|
||||||
let bytes1 = "Gaggablaghblagh!";
|
let bytes1 = "Gaggablaghblagh!";
|
||||||
let bytes2 = "flurbos";
|
let bytes2 = "flurbos";
|
||||||
@ -3465,7 +3466,7 @@ mod tests {
|
|||||||
* 4,
|
* 4,
|
||||||
);
|
);
|
||||||
invoke_context
|
invoke_context
|
||||||
.push(&message, &message.instructions[0], &[0], &[])
|
.push(&message, &message.instructions()[0], &[0], &[])
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let mut syscall = SyscallSha256 {
|
let mut syscall = SyscallSha256 {
|
||||||
invoke_context: Rc::new(RefCell::new(&mut invoke_context)),
|
invoke_context: Rc::new(RefCell::new(&mut invoke_context)),
|
||||||
@ -3526,10 +3527,10 @@ mod tests {
|
|||||||
let program_id = Pubkey::new_unique();
|
let program_id = Pubkey::new_unique();
|
||||||
let program_account = AccountSharedData::new_ref(0, 0, &bpf_loader::id());
|
let program_account = AccountSharedData::new_ref(0, 0, &bpf_loader::id());
|
||||||
let accounts = [(program_id, program_account)];
|
let accounts = [(program_id, program_account)];
|
||||||
let message = Message::new(
|
let message = SanitizedMessage::Legacy(Message::new(
|
||||||
&[Instruction::new_with_bytes(program_id, &[], vec![])],
|
&[Instruction::new_with_bytes(program_id, &[], vec![])],
|
||||||
None,
|
None,
|
||||||
);
|
));
|
||||||
|
|
||||||
// Test clock sysvar
|
// Test clock sysvar
|
||||||
{
|
{
|
||||||
@ -3564,7 +3565,7 @@ mod tests {
|
|||||||
let sysvars = [(sysvar::clock::id(), data)];
|
let sysvars = [(sysvar::clock::id(), data)];
|
||||||
invoke_context.sysvars = &sysvars;
|
invoke_context.sysvars = &sysvars;
|
||||||
invoke_context
|
invoke_context
|
||||||
.push(&message, &message.instructions[0], &[0], &[])
|
.push(&message, &message.instructions()[0], &[0], &[])
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let mut syscall = SyscallGetClockSysvar {
|
let mut syscall = SyscallGetClockSysvar {
|
||||||
invoke_context: Rc::new(RefCell::new(&mut invoke_context)),
|
invoke_context: Rc::new(RefCell::new(&mut invoke_context)),
|
||||||
@ -3609,7 +3610,7 @@ mod tests {
|
|||||||
let sysvars = [(sysvar::epoch_schedule::id(), data)];
|
let sysvars = [(sysvar::epoch_schedule::id(), data)];
|
||||||
invoke_context.sysvars = &sysvars;
|
invoke_context.sysvars = &sysvars;
|
||||||
invoke_context
|
invoke_context
|
||||||
.push(&message, &message.instructions[0], &[0], &[])
|
.push(&message, &message.instructions()[0], &[0], &[])
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let mut syscall = SyscallGetEpochScheduleSysvar {
|
let mut syscall = SyscallGetEpochScheduleSysvar {
|
||||||
invoke_context: Rc::new(RefCell::new(&mut invoke_context)),
|
invoke_context: Rc::new(RefCell::new(&mut invoke_context)),
|
||||||
@ -3661,7 +3662,7 @@ mod tests {
|
|||||||
let sysvars = [(sysvar::fees::id(), data)];
|
let sysvars = [(sysvar::fees::id(), data)];
|
||||||
invoke_context.sysvars = &sysvars;
|
invoke_context.sysvars = &sysvars;
|
||||||
invoke_context
|
invoke_context
|
||||||
.push(&message, &message.instructions[0], &[0], &[])
|
.push(&message, &message.instructions()[0], &[0], &[])
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let mut syscall = SyscallGetFeesSysvar {
|
let mut syscall = SyscallGetFeesSysvar {
|
||||||
invoke_context: Rc::new(RefCell::new(&mut invoke_context)),
|
invoke_context: Rc::new(RefCell::new(&mut invoke_context)),
|
||||||
@ -3704,7 +3705,7 @@ mod tests {
|
|||||||
let sysvars = [(sysvar::rent::id(), data)];
|
let sysvars = [(sysvar::rent::id(), data)];
|
||||||
invoke_context.sysvars = &sysvars;
|
invoke_context.sysvars = &sysvars;
|
||||||
invoke_context
|
invoke_context
|
||||||
.push(&message, &message.instructions[0], &[0], &[])
|
.push(&message, &message.instructions()[0], &[0], &[])
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let mut syscall = SyscallGetRentSysvar {
|
let mut syscall = SyscallGetRentSysvar {
|
||||||
invoke_context: Rc::new(RefCell::new(&mut invoke_context)),
|
invoke_context: Rc::new(RefCell::new(&mut invoke_context)),
|
||||||
@ -3836,13 +3837,13 @@ mod tests {
|
|||||||
let program_id = Pubkey::new_unique();
|
let program_id = Pubkey::new_unique();
|
||||||
let program_account = AccountSharedData::new_ref(0, 0, &bpf_loader::id());
|
let program_account = AccountSharedData::new_ref(0, 0, &bpf_loader::id());
|
||||||
let accounts = [(program_id, program_account)];
|
let accounts = [(program_id, program_account)];
|
||||||
let message = Message::new(
|
let message = SanitizedMessage::Legacy(Message::new(
|
||||||
&[Instruction::new_with_bytes(program_id, &[], vec![])],
|
&[Instruction::new_with_bytes(program_id, &[], vec![])],
|
||||||
None,
|
None,
|
||||||
);
|
));
|
||||||
let mut invoke_context = InvokeContext::new_mock(&accounts, &[]);
|
let mut invoke_context = InvokeContext::new_mock(&accounts, &[]);
|
||||||
invoke_context
|
invoke_context
|
||||||
.push(&message, &message.instructions[0], &[0], &[])
|
.push(&message, &message.instructions()[0], &[0], &[])
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let address = bpf_loader_upgradeable::id();
|
let address = bpf_loader_upgradeable::id();
|
||||||
|
|
||||||
@ -3952,13 +3953,13 @@ mod tests {
|
|||||||
let program_id = Pubkey::new_unique();
|
let program_id = Pubkey::new_unique();
|
||||||
let program_account = AccountSharedData::new_ref(0, 0, &bpf_loader::id());
|
let program_account = AccountSharedData::new_ref(0, 0, &bpf_loader::id());
|
||||||
let accounts = [(program_id, program_account)];
|
let accounts = [(program_id, program_account)];
|
||||||
let message = Message::new(
|
let message = SanitizedMessage::Legacy(Message::new(
|
||||||
&[Instruction::new_with_bytes(program_id, &[], vec![])],
|
&[Instruction::new_with_bytes(program_id, &[], vec![])],
|
||||||
None,
|
None,
|
||||||
);
|
));
|
||||||
let mut invoke_context = InvokeContext::new_mock(&accounts, &[]);
|
let mut invoke_context = InvokeContext::new_mock(&accounts, &[]);
|
||||||
invoke_context
|
invoke_context
|
||||||
.push(&message, &message.instructions[0], &[0], &[])
|
.push(&message, &message.instructions()[0], &[0], &[])
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let cost = invoke_context
|
let cost = invoke_context
|
||||||
.get_compute_budget()
|
.get_compute_budget()
|
||||||
|
@ -435,7 +435,7 @@ mod tests {
|
|||||||
invoke_context.sysvars = &sysvars;
|
invoke_context.sysvars = &sysvars;
|
||||||
invoke_context.push(
|
invoke_context.push(
|
||||||
&preparation.message,
|
&preparation.message,
|
||||||
&preparation.message.instructions[0],
|
&preparation.message.instructions()[0],
|
||||||
&program_indices,
|
&program_indices,
|
||||||
&preparation.account_indices,
|
&preparation.account_indices,
|
||||||
)?;
|
)?;
|
||||||
@ -1082,7 +1082,7 @@ mod tests {
|
|||||||
invoke_context
|
invoke_context
|
||||||
.push(
|
.push(
|
||||||
&preparation.message,
|
&preparation.message,
|
||||||
&preparation.message.instructions[0],
|
&preparation.message.instructions()[0],
|
||||||
&program_indices,
|
&program_indices,
|
||||||
&preparation.account_indices,
|
&preparation.account_indices,
|
||||||
)
|
)
|
||||||
|
@ -211,7 +211,7 @@ native machine code before execting it in the virtual machine.",
|
|||||||
invoke_context
|
invoke_context
|
||||||
.push(
|
.push(
|
||||||
&preparation.message,
|
&preparation.message,
|
||||||
&preparation.message.instructions[0],
|
&preparation.message.instructions()[0],
|
||||||
&program_indices,
|
&program_indices,
|
||||||
&preparation.account_indices,
|
&preparation.account_indices,
|
||||||
)
|
)
|
||||||
|
@ -22,6 +22,7 @@ use {
|
|||||||
},
|
},
|
||||||
log::*,
|
log::*,
|
||||||
rand::{thread_rng, Rng},
|
rand::{thread_rng, Rng},
|
||||||
|
solana_address_lookup_table_program::state::AddressLookupTable,
|
||||||
solana_sdk::{
|
solana_sdk::{
|
||||||
account::{Account, AccountSharedData, ReadableAccount, WritableAccount},
|
account::{Account, AccountSharedData, ReadableAccount, WritableAccount},
|
||||||
account_utils::StateMut,
|
account_utils::StateMut,
|
||||||
@ -30,13 +31,19 @@ use {
|
|||||||
feature_set::{self, FeatureSet},
|
feature_set::{self, FeatureSet},
|
||||||
genesis_config::ClusterType,
|
genesis_config::ClusterType,
|
||||||
hash::Hash,
|
hash::Hash,
|
||||||
message::SanitizedMessage,
|
message::{
|
||||||
|
v0::{LoadedAddresses, MessageAddressTableLookup},
|
||||||
|
SanitizedMessage,
|
||||||
|
},
|
||||||
native_loader,
|
native_loader,
|
||||||
nonce::{state::Versions as NonceVersions, State as NonceState},
|
nonce::{state::Versions as NonceVersions, State as NonceState},
|
||||||
pubkey::Pubkey,
|
pubkey::Pubkey,
|
||||||
system_program,
|
system_program,
|
||||||
sysvar::{self, instructions::construct_instructions_data},
|
sysvar::{self, instructions::construct_instructions_data, slot_hashes::SlotHashes},
|
||||||
transaction::{Result, SanitizedTransaction, TransactionAccountLocks, TransactionError},
|
transaction::{
|
||||||
|
AddressLookupError, Result, SanitizedTransaction, TransactionAccountLocks,
|
||||||
|
TransactionError,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
std::{
|
std::{
|
||||||
cmp::Reverse,
|
cmp::Reverse,
|
||||||
@ -223,6 +230,40 @@ impl Accounts {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn load_lookup_table_addresses(
|
||||||
|
&self,
|
||||||
|
ancestors: &Ancestors,
|
||||||
|
address_table_lookup: &MessageAddressTableLookup,
|
||||||
|
slot_hashes: &SlotHashes,
|
||||||
|
) -> std::result::Result<LoadedAddresses, AddressLookupError> {
|
||||||
|
let table_account = self
|
||||||
|
.accounts_db
|
||||||
|
.load_with_fixed_root(ancestors, &address_table_lookup.account_key)
|
||||||
|
.map(|(account, _rent)| account)
|
||||||
|
.ok_or(AddressLookupError::LookupTableAccountNotFound)?;
|
||||||
|
|
||||||
|
if table_account.owner() == &solana_address_lookup_table_program::id() {
|
||||||
|
let current_slot = ancestors.max_slot();
|
||||||
|
let lookup_table = AddressLookupTable::deserialize(table_account.data())
|
||||||
|
.map_err(|_ix_err| AddressLookupError::InvalidAccountData)?;
|
||||||
|
|
||||||
|
Ok(LoadedAddresses {
|
||||||
|
writable: lookup_table.lookup(
|
||||||
|
current_slot,
|
||||||
|
&address_table_lookup.writable_indexes,
|
||||||
|
slot_hashes,
|
||||||
|
)?,
|
||||||
|
readonly: lookup_table.lookup(
|
||||||
|
current_slot,
|
||||||
|
&address_table_lookup.readonly_indexes,
|
||||||
|
slot_hashes,
|
||||||
|
)?,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
Err(AddressLookupError::InvalidAccountOwner)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn load_transaction(
|
fn load_transaction(
|
||||||
&self,
|
&self,
|
||||||
ancestors: &Ancestors,
|
ancestors: &Ancestors,
|
||||||
@ -1242,6 +1283,7 @@ mod tests {
|
|||||||
bank::{DurableNonceFee, TransactionExecutionDetails},
|
bank::{DurableNonceFee, TransactionExecutionDetails},
|
||||||
rent_collector::RentCollector,
|
rent_collector::RentCollector,
|
||||||
},
|
},
|
||||||
|
solana_address_lookup_table_program::state::LookupTableMeta,
|
||||||
solana_sdk::{
|
solana_sdk::{
|
||||||
account::{AccountSharedData, WritableAccount},
|
account::{AccountSharedData, WritableAccount},
|
||||||
epoch_schedule::EpochSchedule,
|
epoch_schedule::EpochSchedule,
|
||||||
@ -1256,6 +1298,7 @@ mod tests {
|
|||||||
transaction::{Transaction, MAX_TX_ACCOUNT_LOCKS},
|
transaction::{Transaction, MAX_TX_ACCOUNT_LOCKS},
|
||||||
},
|
},
|
||||||
std::{
|
std::{
|
||||||
|
borrow::Cow,
|
||||||
convert::TryFrom,
|
convert::TryFrom,
|
||||||
sync::atomic::{AtomicBool, AtomicU64, Ordering},
|
sync::atomic::{AtomicBool, AtomicU64, Ordering},
|
||||||
thread, time,
|
thread, time,
|
||||||
@ -1836,6 +1879,149 @@ mod tests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_load_lookup_table_addresses_account_not_found() {
|
||||||
|
let ancestors = vec![(0, 0)].into_iter().collect();
|
||||||
|
let accounts = Accounts::new_with_config_for_tests(
|
||||||
|
Vec::new(),
|
||||||
|
&ClusterType::Development,
|
||||||
|
AccountSecondaryIndexes::default(),
|
||||||
|
false,
|
||||||
|
AccountShrinkThreshold::default(),
|
||||||
|
);
|
||||||
|
|
||||||
|
let invalid_table_key = Pubkey::new_unique();
|
||||||
|
let address_table_lookup = MessageAddressTableLookup {
|
||||||
|
account_key: invalid_table_key,
|
||||||
|
writable_indexes: vec![],
|
||||||
|
readonly_indexes: vec![],
|
||||||
|
};
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
accounts.load_lookup_table_addresses(
|
||||||
|
&ancestors,
|
||||||
|
&address_table_lookup,
|
||||||
|
&SlotHashes::default(),
|
||||||
|
),
|
||||||
|
Err(AddressLookupError::LookupTableAccountNotFound),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_load_lookup_table_addresses_invalid_account_owner() {
|
||||||
|
let ancestors = vec![(0, 0)].into_iter().collect();
|
||||||
|
let accounts = Accounts::new_with_config_for_tests(
|
||||||
|
Vec::new(),
|
||||||
|
&ClusterType::Development,
|
||||||
|
AccountSecondaryIndexes::default(),
|
||||||
|
false,
|
||||||
|
AccountShrinkThreshold::default(),
|
||||||
|
);
|
||||||
|
|
||||||
|
let invalid_table_key = Pubkey::new_unique();
|
||||||
|
let invalid_table_account = AccountSharedData::default();
|
||||||
|
accounts.store_slow_uncached(0, &invalid_table_key, &invalid_table_account);
|
||||||
|
|
||||||
|
let address_table_lookup = MessageAddressTableLookup {
|
||||||
|
account_key: invalid_table_key,
|
||||||
|
writable_indexes: vec![],
|
||||||
|
readonly_indexes: vec![],
|
||||||
|
};
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
accounts.load_lookup_table_addresses(
|
||||||
|
&ancestors,
|
||||||
|
&address_table_lookup,
|
||||||
|
&SlotHashes::default(),
|
||||||
|
),
|
||||||
|
Err(AddressLookupError::InvalidAccountOwner),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_load_lookup_table_addresses_invalid_account_data() {
|
||||||
|
let ancestors = vec![(0, 0)].into_iter().collect();
|
||||||
|
let accounts = Accounts::new_with_config_for_tests(
|
||||||
|
Vec::new(),
|
||||||
|
&ClusterType::Development,
|
||||||
|
AccountSecondaryIndexes::default(),
|
||||||
|
false,
|
||||||
|
AccountShrinkThreshold::default(),
|
||||||
|
);
|
||||||
|
|
||||||
|
let invalid_table_key = Pubkey::new_unique();
|
||||||
|
let invalid_table_account =
|
||||||
|
AccountSharedData::new(1, 0, &solana_address_lookup_table_program::id());
|
||||||
|
accounts.store_slow_uncached(0, &invalid_table_key, &invalid_table_account);
|
||||||
|
|
||||||
|
let address_table_lookup = MessageAddressTableLookup {
|
||||||
|
account_key: invalid_table_key,
|
||||||
|
writable_indexes: vec![],
|
||||||
|
readonly_indexes: vec![],
|
||||||
|
};
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
accounts.load_lookup_table_addresses(
|
||||||
|
&ancestors,
|
||||||
|
&address_table_lookup,
|
||||||
|
&SlotHashes::default(),
|
||||||
|
),
|
||||||
|
Err(AddressLookupError::InvalidAccountData),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_load_lookup_table_addresses() {
|
||||||
|
let ancestors = vec![(1, 1), (0, 0)].into_iter().collect();
|
||||||
|
let accounts = Accounts::new_with_config_for_tests(
|
||||||
|
Vec::new(),
|
||||||
|
&ClusterType::Development,
|
||||||
|
AccountSecondaryIndexes::default(),
|
||||||
|
false,
|
||||||
|
AccountShrinkThreshold::default(),
|
||||||
|
);
|
||||||
|
|
||||||
|
let table_key = Pubkey::new_unique();
|
||||||
|
let table_addresses = vec![Pubkey::new_unique(), Pubkey::new_unique()];
|
||||||
|
let table_account = {
|
||||||
|
let table_state = AddressLookupTable {
|
||||||
|
meta: LookupTableMeta::default(),
|
||||||
|
addresses: Cow::Owned(table_addresses.clone()),
|
||||||
|
};
|
||||||
|
let table_data = {
|
||||||
|
let mut data = vec![];
|
||||||
|
table_state.serialize_for_tests(&mut data).unwrap();
|
||||||
|
data
|
||||||
|
};
|
||||||
|
AccountSharedData::create(
|
||||||
|
1,
|
||||||
|
table_data,
|
||||||
|
solana_address_lookup_table_program::id(),
|
||||||
|
false,
|
||||||
|
0,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
accounts.store_slow_uncached(0, &table_key, &table_account);
|
||||||
|
|
||||||
|
let address_table_lookup = MessageAddressTableLookup {
|
||||||
|
account_key: table_key,
|
||||||
|
writable_indexes: vec![0],
|
||||||
|
readonly_indexes: vec![1],
|
||||||
|
};
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
accounts.load_lookup_table_addresses(
|
||||||
|
&ancestors,
|
||||||
|
&address_table_lookup,
|
||||||
|
&SlotHashes::default(),
|
||||||
|
),
|
||||||
|
Ok(LoadedAddresses {
|
||||||
|
writable: vec![table_addresses[0]],
|
||||||
|
readonly: vec![table_addresses[1]],
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_load_by_program_slot() {
|
fn test_load_by_program_slot() {
|
||||||
let accounts = Accounts::new_with_config_for_tests(
|
let accounts = Accounts::new_with_config_for_tests(
|
||||||
|
@ -111,7 +111,10 @@ use {
|
|||||||
inflation::Inflation,
|
inflation::Inflation,
|
||||||
instruction::CompiledInstruction,
|
instruction::CompiledInstruction,
|
||||||
lamports::LamportsError,
|
lamports::LamportsError,
|
||||||
message::SanitizedMessage,
|
message::{
|
||||||
|
v0::{LoadedAddresses, MessageAddressTableLookup},
|
||||||
|
SanitizedMessage,
|
||||||
|
},
|
||||||
native_loader,
|
native_loader,
|
||||||
native_token::sol_to_lamports,
|
native_token::sol_to_lamports,
|
||||||
nonce, nonce_account,
|
nonce, nonce_account,
|
||||||
@ -127,7 +130,7 @@ use {
|
|||||||
sysvar::{self, Sysvar, SysvarId},
|
sysvar::{self, Sysvar, SysvarId},
|
||||||
timing::years_as_slots,
|
timing::years_as_slots,
|
||||||
transaction::{
|
transaction::{
|
||||||
Result, SanitizedTransaction, Transaction, TransactionError,
|
AddressLookupError, Result, SanitizedTransaction, Transaction, TransactionError,
|
||||||
TransactionVerificationMode, VersionedTransaction,
|
TransactionVerificationMode, VersionedTransaction,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -213,7 +216,7 @@ impl RentDebits {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type BankStatusCache = StatusCache<Result<()>>;
|
type BankStatusCache = StatusCache<Result<()>>;
|
||||||
#[frozen_abi(digest = "6XG6H1FChrDdY39K62KFWj5XfDao4dd24WZgcJkdMu1E")]
|
#[frozen_abi(digest = "2r36f5cfgP7ABq7D3kRkRfQZWdggGFUnnhwTrVEWhoTC")]
|
||||||
pub type BankSlotDelta = SlotDelta<Result<()>>;
|
pub type BankSlotDelta = SlotDelta<Result<()>>;
|
||||||
|
|
||||||
// Eager rent collection repeats in cyclic manner.
|
// Eager rent collection repeats in cyclic manner.
|
||||||
@ -3522,6 +3525,44 @@ impl Bank {
|
|||||||
Arc::make_mut(&mut cache).remove(pubkey);
|
Arc::make_mut(&mut cache).remove(pubkey);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the value of a cached sysvar by its id
|
||||||
|
pub fn get_cached_sysvar<T: Sysvar>(&self, id: &Pubkey) -> Option<T> {
|
||||||
|
self.sysvar_cache
|
||||||
|
.read()
|
||||||
|
.unwrap()
|
||||||
|
.iter()
|
||||||
|
.find_map(|(key, data)| {
|
||||||
|
if id == key {
|
||||||
|
bincode::deserialize(data).ok()
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn load_lookup_table_addresses(
|
||||||
|
&self,
|
||||||
|
address_table_lookups: &[MessageAddressTableLookup],
|
||||||
|
) -> Result<LoadedAddresses> {
|
||||||
|
if !self.versioned_tx_message_enabled() {
|
||||||
|
return Err(TransactionError::UnsupportedVersion);
|
||||||
|
}
|
||||||
|
|
||||||
|
let slot_hashes: SlotHashes = self
|
||||||
|
.get_cached_sysvar(&sysvar::slot_hashes::id())
|
||||||
|
.ok_or(TransactionError::AccountNotFound)?;
|
||||||
|
Ok(address_table_lookups
|
||||||
|
.iter()
|
||||||
|
.map(|address_table_lookup| {
|
||||||
|
self.rc.accounts.load_lookup_table_addresses(
|
||||||
|
&self.ancestors,
|
||||||
|
address_table_lookup,
|
||||||
|
&slot_hashes,
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.collect::<std::result::Result<_, AddressLookupError>>()?)
|
||||||
|
}
|
||||||
|
|
||||||
/// Execute a transaction using the provided loaded accounts and update
|
/// Execute a transaction using the provided loaded accounts and update
|
||||||
/// the executors cache if the transaction was successful.
|
/// the executors cache if the transaction was successful.
|
||||||
fn execute_loaded_transaction(
|
fn execute_loaded_transaction(
|
||||||
@ -3535,16 +3576,6 @@ impl Bank {
|
|||||||
timings: &mut ExecuteTimings,
|
timings: &mut ExecuteTimings,
|
||||||
error_counters: &mut ErrorCounters,
|
error_counters: &mut ErrorCounters,
|
||||||
) -> TransactionExecutionResult {
|
) -> TransactionExecutionResult {
|
||||||
let legacy_message = match tx.message().legacy_message() {
|
|
||||||
Some(message) => message,
|
|
||||||
None => {
|
|
||||||
// TODO: support versioned messages
|
|
||||||
return TransactionExecutionResult::NotExecuted(
|
|
||||||
TransactionError::UnsupportedVersion,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut get_executors_time = Measure::start("get_executors_time");
|
let mut get_executors_time = Measure::start("get_executors_time");
|
||||||
let executors = self.get_executors(
|
let executors = self.get_executors(
|
||||||
tx.message(),
|
tx.message(),
|
||||||
@ -3579,7 +3610,7 @@ impl Bank {
|
|||||||
let mut process_message_time = Measure::start("process_message_time");
|
let mut process_message_time = Measure::start("process_message_time");
|
||||||
let process_result = MessageProcessor::process_message(
|
let process_result = MessageProcessor::process_message(
|
||||||
&self.builtin_programs.vec,
|
&self.builtin_programs.vec,
|
||||||
legacy_message,
|
tx.message(),
|
||||||
&loaded_transaction.program_indices,
|
&loaded_transaction.program_indices,
|
||||||
&account_refcells,
|
&account_refcells,
|
||||||
self.rent_collector.rent,
|
self.rent_collector.rent,
|
||||||
@ -5336,8 +5367,8 @@ impl Bank {
|
|||||||
tx.message.hash()
|
tx.message.hash()
|
||||||
};
|
};
|
||||||
|
|
||||||
SanitizedTransaction::try_create(tx, message_hash, None, |_| {
|
SanitizedTransaction::try_create(tx, message_hash, None, |lookups| {
|
||||||
Err(TransactionError::UnsupportedVersion)
|
self.load_lookup_table_addresses(lookups)
|
||||||
})
|
})
|
||||||
}?;
|
}?;
|
||||||
|
|
||||||
|
@ -15,7 +15,7 @@ use {
|
|||||||
compute_budget::ComputeBudget,
|
compute_budget::ComputeBudget,
|
||||||
feature_set::{prevent_calling_precompiles_as_programs, FeatureSet},
|
feature_set::{prevent_calling_precompiles_as_programs, FeatureSet},
|
||||||
hash::Hash,
|
hash::Hash,
|
||||||
message::Message,
|
message::SanitizedMessage,
|
||||||
precompiles::is_precompile,
|
precompiles::is_precompile,
|
||||||
pubkey::Pubkey,
|
pubkey::Pubkey,
|
||||||
rent::Rent,
|
rent::Rent,
|
||||||
@ -54,7 +54,7 @@ impl MessageProcessor {
|
|||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
pub fn process_message(
|
pub fn process_message(
|
||||||
builtin_programs: &[BuiltinProgram],
|
builtin_programs: &[BuiltinProgram],
|
||||||
message: &Message,
|
message: &SanitizedMessage,
|
||||||
program_indices: &[Vec<usize>],
|
program_indices: &[Vec<usize>],
|
||||||
accounts: &[TransactionAccountRefCell],
|
accounts: &[TransactionAccountRefCell],
|
||||||
rent: Rent,
|
rent: Rent,
|
||||||
@ -83,14 +83,12 @@ impl MessageProcessor {
|
|||||||
current_accounts_data_len,
|
current_accounts_data_len,
|
||||||
);
|
);
|
||||||
|
|
||||||
debug_assert_eq!(program_indices.len(), message.instructions.len());
|
debug_assert_eq!(program_indices.len(), message.instructions().len());
|
||||||
for (instruction_index, (instruction, program_indices)) in message
|
for (instruction_index, ((program_id, instruction), program_indices)) in message
|
||||||
.instructions
|
.program_instructions_iter()
|
||||||
.iter()
|
|
||||||
.zip(program_indices.iter())
|
.zip(program_indices.iter())
|
||||||
.enumerate()
|
.enumerate()
|
||||||
{
|
{
|
||||||
let program_id = instruction.program_id(&message.account_keys);
|
|
||||||
if invoke_context
|
if invoke_context
|
||||||
.feature_set
|
.feature_set
|
||||||
.is_active(&prevent_calling_precompiles_as_programs::id())
|
.is_active(&prevent_calling_precompiles_as_programs::id())
|
||||||
@ -102,7 +100,7 @@ impl MessageProcessor {
|
|||||||
|
|
||||||
// Fixup the special instructions key if present
|
// Fixup the special instructions key if present
|
||||||
// before the account pre-values are taken care of
|
// before the account pre-values are taken care of
|
||||||
for (pubkey, account) in accounts.iter().take(message.account_keys.len()) {
|
for (pubkey, account) in accounts.iter().take(message.account_keys_len()) {
|
||||||
if instructions::check_id(pubkey) {
|
if instructions::check_id(pubkey) {
|
||||||
let mut mut_account_ref = account.borrow_mut();
|
let mut mut_account_ref = account.borrow_mut();
|
||||||
instructions::store_current_index(
|
instructions::store_current_index(
|
||||||
@ -131,7 +129,7 @@ impl MessageProcessor {
|
|||||||
);
|
);
|
||||||
time.stop();
|
time.stop();
|
||||||
timings.details.accumulate_program(
|
timings.details.accumulate_program(
|
||||||
instruction.program_id(&message.account_keys),
|
program_id,
|
||||||
time.as_us(),
|
time.as_us(),
|
||||||
compute_units_consumed,
|
compute_units_consumed,
|
||||||
result.is_err(),
|
result.is_err(),
|
||||||
@ -247,14 +245,14 @@ mod tests {
|
|||||||
AccountMeta::new(accounts[0].0, true),
|
AccountMeta::new(accounts[0].0, true),
|
||||||
AccountMeta::new_readonly(accounts[1].0, false),
|
AccountMeta::new_readonly(accounts[1].0, false),
|
||||||
];
|
];
|
||||||
let message = Message::new(
|
let message = SanitizedMessage::Legacy(Message::new(
|
||||||
&[Instruction::new_with_bincode(
|
&[Instruction::new_with_bincode(
|
||||||
mock_system_program_id,
|
mock_system_program_id,
|
||||||
&MockSystemInstruction::Correct,
|
&MockSystemInstruction::Correct,
|
||||||
account_metas.clone(),
|
account_metas.clone(),
|
||||||
)],
|
)],
|
||||||
Some(&accounts[0].0),
|
Some(&accounts[0].0),
|
||||||
);
|
));
|
||||||
|
|
||||||
let result = MessageProcessor::process_message(
|
let result = MessageProcessor::process_message(
|
||||||
builtin_programs,
|
builtin_programs,
|
||||||
@ -277,14 +275,14 @@ mod tests {
|
|||||||
assert_eq!(accounts[0].1.borrow().lamports(), 100);
|
assert_eq!(accounts[0].1.borrow().lamports(), 100);
|
||||||
assert_eq!(accounts[1].1.borrow().lamports(), 0);
|
assert_eq!(accounts[1].1.borrow().lamports(), 0);
|
||||||
|
|
||||||
let message = Message::new(
|
let message = SanitizedMessage::Legacy(Message::new(
|
||||||
&[Instruction::new_with_bincode(
|
&[Instruction::new_with_bincode(
|
||||||
mock_system_program_id,
|
mock_system_program_id,
|
||||||
&MockSystemInstruction::AttemptCredit { lamports: 50 },
|
&MockSystemInstruction::AttemptCredit { lamports: 50 },
|
||||||
account_metas.clone(),
|
account_metas.clone(),
|
||||||
)],
|
)],
|
||||||
Some(&accounts[0].0),
|
Some(&accounts[0].0),
|
||||||
);
|
));
|
||||||
|
|
||||||
let result = MessageProcessor::process_message(
|
let result = MessageProcessor::process_message(
|
||||||
builtin_programs,
|
builtin_programs,
|
||||||
@ -311,14 +309,14 @@ mod tests {
|
|||||||
))
|
))
|
||||||
);
|
);
|
||||||
|
|
||||||
let message = Message::new(
|
let message = SanitizedMessage::Legacy(Message::new(
|
||||||
&[Instruction::new_with_bincode(
|
&[Instruction::new_with_bincode(
|
||||||
mock_system_program_id,
|
mock_system_program_id,
|
||||||
&MockSystemInstruction::AttemptDataChange { data: 50 },
|
&MockSystemInstruction::AttemptDataChange { data: 50 },
|
||||||
account_metas,
|
account_metas,
|
||||||
)],
|
)],
|
||||||
Some(&accounts[0].0),
|
Some(&accounts[0].0),
|
||||||
);
|
));
|
||||||
|
|
||||||
let result = MessageProcessor::process_message(
|
let result = MessageProcessor::process_message(
|
||||||
builtin_programs,
|
builtin_programs,
|
||||||
@ -457,14 +455,14 @@ mod tests {
|
|||||||
];
|
];
|
||||||
|
|
||||||
// Try to borrow mut the same account
|
// Try to borrow mut the same account
|
||||||
let message = Message::new(
|
let message = SanitizedMessage::Legacy(Message::new(
|
||||||
&[Instruction::new_with_bincode(
|
&[Instruction::new_with_bincode(
|
||||||
mock_program_id,
|
mock_program_id,
|
||||||
&MockSystemInstruction::BorrowFail,
|
&MockSystemInstruction::BorrowFail,
|
||||||
account_metas.clone(),
|
account_metas.clone(),
|
||||||
)],
|
)],
|
||||||
Some(&accounts[0].0),
|
Some(&accounts[0].0),
|
||||||
);
|
));
|
||||||
let result = MessageProcessor::process_message(
|
let result = MessageProcessor::process_message(
|
||||||
builtin_programs,
|
builtin_programs,
|
||||||
&message,
|
&message,
|
||||||
@ -491,14 +489,14 @@ mod tests {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Try to borrow mut the same account in a safe way
|
// Try to borrow mut the same account in a safe way
|
||||||
let message = Message::new(
|
let message = SanitizedMessage::Legacy(Message::new(
|
||||||
&[Instruction::new_with_bincode(
|
&[Instruction::new_with_bincode(
|
||||||
mock_program_id,
|
mock_program_id,
|
||||||
&MockSystemInstruction::MultiBorrowMut,
|
&MockSystemInstruction::MultiBorrowMut,
|
||||||
account_metas.clone(),
|
account_metas.clone(),
|
||||||
)],
|
)],
|
||||||
Some(&accounts[0].0),
|
Some(&accounts[0].0),
|
||||||
);
|
));
|
||||||
let result = MessageProcessor::process_message(
|
let result = MessageProcessor::process_message(
|
||||||
builtin_programs,
|
builtin_programs,
|
||||||
&message,
|
&message,
|
||||||
@ -519,7 +517,7 @@ mod tests {
|
|||||||
assert!(result.is_ok());
|
assert!(result.is_ok());
|
||||||
|
|
||||||
// Do work on the same account but at different location in keyed_accounts[]
|
// Do work on the same account but at different location in keyed_accounts[]
|
||||||
let message = Message::new(
|
let message = SanitizedMessage::Legacy(Message::new(
|
||||||
&[Instruction::new_with_bincode(
|
&[Instruction::new_with_bincode(
|
||||||
mock_program_id,
|
mock_program_id,
|
||||||
&MockSystemInstruction::DoWork {
|
&MockSystemInstruction::DoWork {
|
||||||
@ -529,7 +527,7 @@ mod tests {
|
|||||||
account_metas,
|
account_metas,
|
||||||
)],
|
)],
|
||||||
Some(&accounts[0].0),
|
Some(&accounts[0].0),
|
||||||
);
|
));
|
||||||
let result = MessageProcessor::process_message(
|
let result = MessageProcessor::process_message(
|
||||||
builtin_programs,
|
builtin_programs,
|
||||||
&message,
|
&message,
|
||||||
@ -577,7 +575,7 @@ mod tests {
|
|||||||
(mock_program_id, mock_program_account),
|
(mock_program_id, mock_program_account),
|
||||||
];
|
];
|
||||||
|
|
||||||
let message = Message::new(
|
let message = SanitizedMessage::Legacy(Message::new(
|
||||||
&[
|
&[
|
||||||
new_secp256k1_instruction(
|
new_secp256k1_instruction(
|
||||||
&libsecp256k1::SecretKey::random(&mut rand::thread_rng()),
|
&libsecp256k1::SecretKey::random(&mut rand::thread_rng()),
|
||||||
@ -586,7 +584,7 @@ mod tests {
|
|||||||
Instruction::new_with_bytes(mock_program_id, &[], vec![]),
|
Instruction::new_with_bytes(mock_program_id, &[], vec![]),
|
||||||
],
|
],
|
||||||
None,
|
None,
|
||||||
);
|
));
|
||||||
|
|
||||||
let result = MessageProcessor::process_message(
|
let result = MessageProcessor::process_message(
|
||||||
builtin_programs,
|
builtin_programs,
|
||||||
|
@ -43,21 +43,28 @@ impl VersionedMessage {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn unmapped_keys(self) -> Vec<Pubkey> {
|
pub fn static_account_keys(&self) -> &[Pubkey] {
|
||||||
|
match self {
|
||||||
|
Self::Legacy(message) => &message.account_keys,
|
||||||
|
Self::V0(message) => &message.account_keys,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn into_static_account_keys(self) -> Vec<Pubkey> {
|
||||||
match self {
|
match self {
|
||||||
Self::Legacy(message) => message.account_keys,
|
Self::Legacy(message) => message.account_keys,
|
||||||
Self::V0(message) => message.account_keys,
|
Self::V0(message) => message.account_keys,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn unmapped_keys_iter(&self) -> impl Iterator<Item = &Pubkey> {
|
pub fn static_account_keys_iter(&self) -> impl Iterator<Item = &Pubkey> {
|
||||||
match self {
|
match self {
|
||||||
Self::Legacy(message) => message.account_keys.iter(),
|
Self::Legacy(message) => message.account_keys.iter(),
|
||||||
Self::V0(message) => message.account_keys.iter(),
|
Self::V0(message) => message.account_keys.iter(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn unmapped_keys_len(&self) -> usize {
|
pub fn static_account_keys_len(&self) -> usize {
|
||||||
match self {
|
match self {
|
||||||
Self::Legacy(message) => message.account_keys.len(),
|
Self::Legacy(message) => message.account_keys.len(),
|
||||||
Self::V0(message) => message.account_keys.len(),
|
Self::V0(message) => message.account_keys.len(),
|
||||||
|
@ -5,7 +5,7 @@ use {
|
|||||||
pubkey::Pubkey,
|
pubkey::Pubkey,
|
||||||
sysvar,
|
sysvar,
|
||||||
},
|
},
|
||||||
std::{collections::HashSet, ops::Deref, convert::TryFrom},
|
std::{collections::HashSet, ops::Deref},
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Combination of a version #0 message and its loaded addresses
|
/// Combination of a version #0 message and its loaded addresses
|
||||||
@ -34,6 +34,19 @@ pub struct LoadedAddresses {
|
|||||||
pub readonly: Vec<Pubkey>,
|
pub readonly: Vec<Pubkey>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl FromIterator<LoadedAddresses> for LoadedAddresses {
|
||||||
|
fn from_iter<T: IntoIterator<Item = LoadedAddresses>>(iter: T) -> Self {
|
||||||
|
let (writable, readonly): (Vec<Vec<Pubkey>>, Vec<Vec<Pubkey>>) = iter
|
||||||
|
.into_iter()
|
||||||
|
.map(|addresses| (addresses.writable, addresses.readonly))
|
||||||
|
.unzip();
|
||||||
|
LoadedAddresses {
|
||||||
|
writable: writable.into_iter().flatten().collect(),
|
||||||
|
readonly: readonly.into_iter().flatten().collect(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl LoadedMessage {
|
impl LoadedMessage {
|
||||||
/// Returns an iterator of account key segments. The ordering of segments
|
/// Returns an iterator of account key segments. The ordering of segments
|
||||||
/// affects how account indexes from compiled instructions are resolved and
|
/// affects how account indexes from compiled instructions are resolved and
|
||||||
@ -68,8 +81,9 @@ impl LoadedMessage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the address of the account at the specified index of the list of
|
/// Returns the address of the account at the specified index of the list of
|
||||||
/// message account keys constructed from unmapped keys, followed by mapped
|
/// message account keys constructed from static keys, followed by dynamically
|
||||||
/// writable addresses, and lastly the list of mapped readonly addresses.
|
/// loaded writable addresses, and lastly the list of dynamically loaded
|
||||||
|
/// readonly addresses.
|
||||||
pub fn get_account_key(&self, mut index: usize) -> Option<&Pubkey> {
|
pub fn get_account_key(&self, mut index: usize) -> Option<&Pubkey> {
|
||||||
for key_segment in self.account_keys_segment_iter() {
|
for key_segment in self.account_keys_segment_iter() {
|
||||||
if index < key_segment.len() {
|
if index < key_segment.len() {
|
||||||
@ -88,8 +102,8 @@ impl LoadedMessage {
|
|||||||
let num_account_keys = self.message.account_keys.len();
|
let num_account_keys = self.message.account_keys.len();
|
||||||
let num_signed_accounts = usize::from(header.num_required_signatures);
|
let num_signed_accounts = usize::from(header.num_required_signatures);
|
||||||
if key_index >= num_account_keys {
|
if key_index >= num_account_keys {
|
||||||
let mapped_addresses_index = key_index.saturating_sub(num_account_keys);
|
let loaded_addresses_index = key_index.saturating_sub(num_account_keys);
|
||||||
mapped_addresses_index < self.loaded_addresses.writable.len()
|
loaded_addresses_index < self.loaded_addresses.writable.len()
|
||||||
} else if key_index >= num_signed_accounts {
|
} else if key_index >= num_signed_accounts {
|
||||||
let num_unsigned_accounts = num_account_keys.saturating_sub(num_signed_accounts);
|
let num_unsigned_accounts = num_account_keys.saturating_sub(num_signed_accounts);
|
||||||
let num_writable_unsigned_accounts = num_unsigned_accounts
|
let num_writable_unsigned_accounts = num_unsigned_accounts
|
||||||
|
@ -109,6 +109,22 @@ pub enum TransactionError {
|
|||||||
/// Transaction locked too many accounts
|
/// Transaction locked too many accounts
|
||||||
#[error("Transaction locked too many accounts")]
|
#[error("Transaction locked too many accounts")]
|
||||||
TooManyAccountLocks,
|
TooManyAccountLocks,
|
||||||
|
|
||||||
|
/// Address lookup table not found
|
||||||
|
#[error("Transaction loads an address table account that doesn't exist")]
|
||||||
|
AddressLookupTableNotFound,
|
||||||
|
|
||||||
|
/// Attempted to lookup addresses from an account owned by the wrong program
|
||||||
|
#[error("Transaction loads an address table account with an invalid owner")]
|
||||||
|
InvalidAddressLookupTableOwner,
|
||||||
|
|
||||||
|
/// Attempted to lookup addresses from an invalid account
|
||||||
|
#[error("Transaction loads an address table account with invalid data")]
|
||||||
|
InvalidAddressLookupTableData,
|
||||||
|
|
||||||
|
/// Address table lookup uses an invalid index
|
||||||
|
#[error("Transaction address table lookup uses an invalid index")]
|
||||||
|
InvalidAddressLookupTableIndex,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<SanitizeError> for TransactionError {
|
impl From<SanitizeError> for TransactionError {
|
||||||
@ -122,3 +138,33 @@ impl From<SanitizeMessageError> for TransactionError {
|
|||||||
Self::SanitizeFailure
|
Self::SanitizeFailure
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Error, PartialEq, Eq, Clone)]
|
||||||
|
pub enum AddressLookupError {
|
||||||
|
/// Attempted to lookup addresses from a table that does not exist
|
||||||
|
#[error("Attempted to lookup addresses from a table that does not exist")]
|
||||||
|
LookupTableAccountNotFound,
|
||||||
|
|
||||||
|
/// Attempted to lookup addresses from an account owned by the wrong program
|
||||||
|
#[error("Attempted to lookup addresses from an account owned by the wrong program")]
|
||||||
|
InvalidAccountOwner,
|
||||||
|
|
||||||
|
/// Attempted to lookup addresses from an invalid account
|
||||||
|
#[error("Attempted to lookup addresses from an invalid account")]
|
||||||
|
InvalidAccountData,
|
||||||
|
|
||||||
|
/// Address lookup contains an invalid index
|
||||||
|
#[error("Address lookup contains an invalid index")]
|
||||||
|
InvalidLookupIndex,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<AddressLookupError> for TransactionError {
|
||||||
|
fn from(err: AddressLookupError) -> Self {
|
||||||
|
match err {
|
||||||
|
AddressLookupError::LookupTableAccountNotFound => Self::AddressLookupTableNotFound,
|
||||||
|
AddressLookupError::InvalidAccountOwner => Self::InvalidAddressLookupTableOwner,
|
||||||
|
AddressLookupError::InvalidAccountData => Self::InvalidAddressLookupTableData,
|
||||||
|
AddressLookupError::InvalidLookupIndex => Self::InvalidAddressLookupTableIndex,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,10 +1,9 @@
|
|||||||
#![cfg(feature = "full")]
|
#![cfg(feature = "full")]
|
||||||
|
|
||||||
use {
|
use {
|
||||||
crate::{
|
crate::{
|
||||||
hash::Hash,
|
hash::Hash,
|
||||||
message::{
|
message::{
|
||||||
v0::{self, LoadedAddresses},
|
v0::{self, LoadedAddresses, MessageAddressTableLookup},
|
||||||
SanitizedMessage, VersionedMessage,
|
SanitizedMessage, VersionedMessage,
|
||||||
},
|
},
|
||||||
nonce::NONCED_TX_MARKER_IX_INDEX,
|
nonce::NONCED_TX_MARKER_IX_INDEX,
|
||||||
@ -51,7 +50,7 @@ impl SanitizedTransaction {
|
|||||||
tx: VersionedTransaction,
|
tx: VersionedTransaction,
|
||||||
message_hash: Hash,
|
message_hash: Hash,
|
||||||
is_simple_vote_tx: Option<bool>,
|
is_simple_vote_tx: Option<bool>,
|
||||||
address_loader: impl Fn(&v0::Message) -> Result<LoadedAddresses>,
|
address_loader: impl Fn(&[MessageAddressTableLookup]) -> Result<LoadedAddresses>,
|
||||||
) -> Result<Self> {
|
) -> Result<Self> {
|
||||||
tx.sanitize()?;
|
tx.sanitize()?;
|
||||||
|
|
||||||
@ -59,7 +58,7 @@ impl SanitizedTransaction {
|
|||||||
let message = match tx.message {
|
let message = match tx.message {
|
||||||
VersionedMessage::Legacy(message) => SanitizedMessage::Legacy(message),
|
VersionedMessage::Legacy(message) => SanitizedMessage::Legacy(message),
|
||||||
VersionedMessage::V0(message) => SanitizedMessage::V0(v0::LoadedMessage {
|
VersionedMessage::V0(message) => SanitizedMessage::V0(v0::LoadedMessage {
|
||||||
loaded_addresses: address_loader(&message)?,
|
loaded_addresses: address_loader(&message.address_table_lookups)?,
|
||||||
message,
|
message,
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
|
@ -35,9 +35,9 @@ impl Sanitize for VersionedTransaction {
|
|||||||
return Err(SanitizeError::IndexOutOfBounds);
|
return Err(SanitizeError::IndexOutOfBounds);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Signatures are verified before message keys are mapped so all signers
|
// Signatures are verified before message keys are loaded so all signers
|
||||||
// must correspond to unmapped keys.
|
// must correspond to static account keys.
|
||||||
if self.signatures.len() > self.message.unmapped_keys_len() {
|
if self.signatures.len() > self.message.static_account_keys_len() {
|
||||||
return Err(SanitizeError::IndexOutOfBounds);
|
return Err(SanitizeError::IndexOutOfBounds);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -69,16 +69,28 @@ impl VersionedTransaction {
|
|||||||
/// Verify the transaction and hash its message
|
/// Verify the transaction and hash its message
|
||||||
pub fn verify_and_hash_message(&self) -> Result<Hash> {
|
pub fn verify_and_hash_message(&self) -> Result<Hash> {
|
||||||
let message_bytes = self.message.serialize();
|
let message_bytes = self.message.serialize();
|
||||||
if self
|
if !self
|
||||||
.signatures
|
._verify_with_results(&message_bytes)
|
||||||
.iter()
|
.iter()
|
||||||
.zip(self.message.unmapped_keys_iter())
|
.all(|verify_result| *verify_result)
|
||||||
.map(|(signature, pubkey)| signature.verify(pubkey.as_ref(), &message_bytes))
|
|
||||||
.any(|verified| !verified)
|
|
||||||
{
|
{
|
||||||
Err(TransactionError::SignatureFailure)
|
Err(TransactionError::SignatureFailure)
|
||||||
} else {
|
} else {
|
||||||
Ok(VersionedMessage::hash_raw_message(&message_bytes))
|
Ok(VersionedMessage::hash_raw_message(&message_bytes))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Verify the transaction and return a list of verification results
|
||||||
|
pub fn verify_with_results(&self) -> Vec<bool> {
|
||||||
|
let message_bytes = self.message.serialize();
|
||||||
|
self._verify_with_results(&message_bytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn _verify_with_results(&self, message_bytes: &[u8]) -> Vec<bool> {
|
||||||
|
self.signatures
|
||||||
|
.iter()
|
||||||
|
.zip(self.message.static_account_keys_iter())
|
||||||
|
.map(|(signature, pubkey)| signature.verify(pubkey.as_ref(), message_bytes))
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -47,6 +47,10 @@ enum TransactionErrorType {
|
|||||||
WOULD_EXCEED_MAX_ACCOUNT_COST_LIMIT = 20;
|
WOULD_EXCEED_MAX_ACCOUNT_COST_LIMIT = 20;
|
||||||
WOULD_EXCEED_MAX_ACCOUNT_DATA_COST_LIMIT = 21;
|
WOULD_EXCEED_MAX_ACCOUNT_DATA_COST_LIMIT = 21;
|
||||||
TOO_MANY_ACCOUNT_LOCKS = 22;
|
TOO_MANY_ACCOUNT_LOCKS = 22;
|
||||||
|
ADDRESS_LOOKUP_TABLE_NOT_FOUND = 23;
|
||||||
|
INVALID_ADDRESS_LOOKUP_TABLE_OWNER = 24;
|
||||||
|
INVALID_ADDRESS_LOOKUP_TABLE_DATA = 25;
|
||||||
|
INVALID_ADDRESS_LOOKUP_TABLE_INDEX = 26;
|
||||||
}
|
}
|
||||||
|
|
||||||
message InstructionError {
|
message InstructionError {
|
||||||
|
@ -570,6 +570,10 @@ impl TryFrom<tx_by_addr::TransactionError> for TransactionError {
|
|||||||
20 => TransactionError::WouldExceedMaxAccountCostLimit,
|
20 => TransactionError::WouldExceedMaxAccountCostLimit,
|
||||||
21 => TransactionError::WouldExceedMaxAccountDataCostLimit,
|
21 => TransactionError::WouldExceedMaxAccountDataCostLimit,
|
||||||
22 => TransactionError::TooManyAccountLocks,
|
22 => TransactionError::TooManyAccountLocks,
|
||||||
|
23 => TransactionError::AddressLookupTableNotFound,
|
||||||
|
24 => TransactionError::InvalidAddressLookupTableOwner,
|
||||||
|
25 => TransactionError::InvalidAddressLookupTableData,
|
||||||
|
26 => TransactionError::InvalidAddressLookupTableIndex,
|
||||||
_ => return Err("Invalid TransactionError"),
|
_ => return Err("Invalid TransactionError"),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -646,6 +650,18 @@ impl From<TransactionError> for tx_by_addr::TransactionError {
|
|||||||
TransactionError::TooManyAccountLocks => {
|
TransactionError::TooManyAccountLocks => {
|
||||||
tx_by_addr::TransactionErrorType::TooManyAccountLocks
|
tx_by_addr::TransactionErrorType::TooManyAccountLocks
|
||||||
}
|
}
|
||||||
|
TransactionError::AddressLookupTableNotFound => {
|
||||||
|
tx_by_addr::TransactionErrorType::AddressLookupTableNotFound
|
||||||
|
}
|
||||||
|
TransactionError::InvalidAddressLookupTableOwner => {
|
||||||
|
tx_by_addr::TransactionErrorType::InvalidAddressLookupTableOwner
|
||||||
|
}
|
||||||
|
TransactionError::InvalidAddressLookupTableData => {
|
||||||
|
tx_by_addr::TransactionErrorType::InvalidAddressLookupTableData
|
||||||
|
}
|
||||||
|
TransactionError::InvalidAddressLookupTableIndex => {
|
||||||
|
tx_by_addr::TransactionErrorType::InvalidAddressLookupTableIndex
|
||||||
|
}
|
||||||
} as i32,
|
} as i32,
|
||||||
instruction_error: match transaction_error {
|
instruction_error: match transaction_error {
|
||||||
TransactionError::InstructionError(index, ref instruction_error) => {
|
TransactionError::InstructionError(index, ref instruction_error) => {
|
||||||
|
Reference in New Issue
Block a user