Add runtime support for address table lookups (backport #22223) (#22354)

This commit is contained in:
Justin Starry
2022-01-08 07:57:04 +08:00
committed by GitHub
parent 662c6be51e
commit 1f00926874
21 changed files with 663 additions and 159 deletions

View File

@ -6,6 +6,7 @@ use {
instruction::InstructionError,
pubkey::Pubkey,
slot_hashes::{SlotHashes, MAX_ENTRIES},
transaction::AddressLookupError,
},
std::borrow::Cow,
};
@ -133,6 +134,49 @@ impl<'a> AddressLookupTable<'a> {
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
pub fn serialize_for_tests(self, data: &mut Vec<u8>) -> Result<(), InstructionError> {
data.resize(LOOKUP_TABLE_META_SIZE, 0);
@ -322,4 +366,117 @@ mod tests {
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),
);
}
}

View File

@ -453,7 +453,7 @@ mod tests {
invoke_context
.push(
&preparation.message,
&preparation.message.instructions[0],
&preparation.message.instructions()[0],
&program_indices,
&preparation.account_indices,
)

View File

@ -33,7 +33,7 @@ use {
hash::{Hasher, HASH_BYTES},
instruction::{AccountMeta, Instruction, InstructionError},
keccak,
message::Message,
message::{Message, SanitizedMessage},
native_loader,
precompiles::is_precompile,
program::MAX_RETURN_DATA,
@ -2365,10 +2365,11 @@ fn call<'a, 'b: 'a>(
}
// Process instruction
let message = SanitizedMessage::Legacy(message);
invoke_context
.process_instruction(
&message,
&message.instructions[0],
&message.instructions()[0],
&program_indices,
&account_indices,
&caller_write_privileges,
@ -2962,13 +2963,13 @@ mod tests {
let program_id = Pubkey::new_unique();
let program_account = AccountSharedData::new_ref(0, 0, &bpf_loader::id());
let accounts = [(program_id, program_account)];
let message = Message::new(
let message = SanitizedMessage::Legacy(Message::new(
&[Instruction::new_with_bytes(program_id, &[], vec![])],
None,
);
));
let mut invoke_context = InvokeContext::new_mock(&accounts, &[]);
invoke_context
.push(&message, &message.instructions[0], &[0], &[])
.push(&message, &message.instructions()[0], &[0], &[])
.unwrap();
let mut syscall_panic = SyscallPanic {
invoke_context: Rc::new(RefCell::new(&mut invoke_context)),
@ -3039,13 +3040,13 @@ mod tests {
let program_id = Pubkey::new_unique();
let program_account = AccountSharedData::new_ref(0, 0, &bpf_loader::id());
let accounts = [(program_id, program_account)];
let message = Message::new(
let message = SanitizedMessage::Legacy(Message::new(
&[Instruction::new_with_bytes(program_id, &[], vec![])],
None,
);
));
let mut invoke_context = InvokeContext::new_mock(&accounts, &[]);
invoke_context
.push(&message, &message.instructions[0], &[0], &[])
.push(&message, &message.instructions()[0], &[0], &[])
.unwrap();
let mut syscall_sol_log = SyscallLog {
invoke_context: Rc::new(RefCell::new(&mut invoke_context)),
@ -3143,13 +3144,13 @@ mod tests {
let program_id = Pubkey::new_unique();
let program_account = AccountSharedData::new_ref(0, 0, &bpf_loader::id());
let accounts = [(program_id, program_account)];
let message = Message::new(
let message = SanitizedMessage::Legacy(Message::new(
&[Instruction::new_with_bytes(program_id, &[], vec![])],
None,
);
));
let mut invoke_context = InvokeContext::new_mock(&accounts, &[]);
invoke_context
.push(&message, &message.instructions[0], &[0], &[])
.push(&message, &message.instructions()[0], &[0], &[])
.unwrap();
let cost = invoke_context.get_compute_budget().log_64_units;
let mut syscall_sol_log_u64 = SyscallLogU64 {
@ -3185,13 +3186,13 @@ mod tests {
let program_id = Pubkey::new_unique();
let program_account = AccountSharedData::new_ref(0, 0, &bpf_loader::id());
let accounts = [(program_id, program_account)];
let message = Message::new(
let message = SanitizedMessage::Legacy(Message::new(
&[Instruction::new_with_bytes(program_id, &[], vec![])],
None,
);
));
let mut invoke_context = InvokeContext::new_mock(&accounts, &[]);
invoke_context
.push(&message, &message.instructions[0], &[0], &[])
.push(&message, &message.instructions()[0], &[0], &[])
.unwrap();
let cost = invoke_context.get_compute_budget().log_pubkey_units;
let mut syscall_sol_pubkey = SyscallLogPubkey {
@ -3397,10 +3398,10 @@ mod tests {
let program_id = Pubkey::new_unique();
let program_account = AccountSharedData::new_ref(0, 0, &bpf_loader_deprecated::id());
let accounts = [(program_id, program_account)];
let message = Message::new(
let message = SanitizedMessage::Legacy(Message::new(
&[Instruction::new_with_bytes(program_id, &[], vec![])],
None,
);
));
let bytes1 = "Gaggablaghblagh!";
let bytes2 = "flurbos";
@ -3465,7 +3466,7 @@ mod tests {
* 4,
);
invoke_context
.push(&message, &message.instructions[0], &[0], &[])
.push(&message, &message.instructions()[0], &[0], &[])
.unwrap();
let mut syscall = SyscallSha256 {
invoke_context: Rc::new(RefCell::new(&mut invoke_context)),
@ -3526,10 +3527,10 @@ mod tests {
let program_id = Pubkey::new_unique();
let program_account = AccountSharedData::new_ref(0, 0, &bpf_loader::id());
let accounts = [(program_id, program_account)];
let message = Message::new(
let message = SanitizedMessage::Legacy(Message::new(
&[Instruction::new_with_bytes(program_id, &[], vec![])],
None,
);
));
// Test clock sysvar
{
@ -3564,7 +3565,7 @@ mod tests {
let sysvars = [(sysvar::clock::id(), data)];
invoke_context.sysvars = &sysvars;
invoke_context
.push(&message, &message.instructions[0], &[0], &[])
.push(&message, &message.instructions()[0], &[0], &[])
.unwrap();
let mut syscall = SyscallGetClockSysvar {
invoke_context: Rc::new(RefCell::new(&mut invoke_context)),
@ -3609,7 +3610,7 @@ mod tests {
let sysvars = [(sysvar::epoch_schedule::id(), data)];
invoke_context.sysvars = &sysvars;
invoke_context
.push(&message, &message.instructions[0], &[0], &[])
.push(&message, &message.instructions()[0], &[0], &[])
.unwrap();
let mut syscall = SyscallGetEpochScheduleSysvar {
invoke_context: Rc::new(RefCell::new(&mut invoke_context)),
@ -3661,7 +3662,7 @@ mod tests {
let sysvars = [(sysvar::fees::id(), data)];
invoke_context.sysvars = &sysvars;
invoke_context
.push(&message, &message.instructions[0], &[0], &[])
.push(&message, &message.instructions()[0], &[0], &[])
.unwrap();
let mut syscall = SyscallGetFeesSysvar {
invoke_context: Rc::new(RefCell::new(&mut invoke_context)),
@ -3704,7 +3705,7 @@ mod tests {
let sysvars = [(sysvar::rent::id(), data)];
invoke_context.sysvars = &sysvars;
invoke_context
.push(&message, &message.instructions[0], &[0], &[])
.push(&message, &message.instructions()[0], &[0], &[])
.unwrap();
let mut syscall = SyscallGetRentSysvar {
invoke_context: Rc::new(RefCell::new(&mut invoke_context)),
@ -3836,13 +3837,13 @@ mod tests {
let program_id = Pubkey::new_unique();
let program_account = AccountSharedData::new_ref(0, 0, &bpf_loader::id());
let accounts = [(program_id, program_account)];
let message = Message::new(
let message = SanitizedMessage::Legacy(Message::new(
&[Instruction::new_with_bytes(program_id, &[], vec![])],
None,
);
));
let mut invoke_context = InvokeContext::new_mock(&accounts, &[]);
invoke_context
.push(&message, &message.instructions[0], &[0], &[])
.push(&message, &message.instructions()[0], &[0], &[])
.unwrap();
let address = bpf_loader_upgradeable::id();
@ -3952,13 +3953,13 @@ mod tests {
let program_id = Pubkey::new_unique();
let program_account = AccountSharedData::new_ref(0, 0, &bpf_loader::id());
let accounts = [(program_id, program_account)];
let message = Message::new(
let message = SanitizedMessage::Legacy(Message::new(
&[Instruction::new_with_bytes(program_id, &[], vec![])],
None,
);
));
let mut invoke_context = InvokeContext::new_mock(&accounts, &[]);
invoke_context
.push(&message, &message.instructions[0], &[0], &[])
.push(&message, &message.instructions()[0], &[0], &[])
.unwrap();
let cost = invoke_context
.get_compute_budget()

View File

@ -435,7 +435,7 @@ mod tests {
invoke_context.sysvars = &sysvars;
invoke_context.push(
&preparation.message,
&preparation.message.instructions[0],
&preparation.message.instructions()[0],
&program_indices,
&preparation.account_indices,
)?;
@ -1082,7 +1082,7 @@ mod tests {
invoke_context
.push(
&preparation.message,
&preparation.message.instructions[0],
&preparation.message.instructions()[0],
&program_indices,
&preparation.account_indices,
)