diff --git a/programs/address-lookup-table-tests/Cargo.toml b/programs/address-lookup-table-tests/Cargo.toml index 171e56f81a6e1d..c89377eb218857 100644 --- a/programs/address-lookup-table-tests/Cargo.toml +++ b/programs/address-lookup-table-tests/Cargo.toml @@ -8,7 +8,7 @@ authors = ["Solana Maintainers "] repository = "https://github.com/solana-labs/solana" license = "Apache-2.0" homepage = "https://solana.com/" -edition = "2018" +edition = "2021" publish = false [dev-dependencies] diff --git a/programs/address-lookup-table-tests/tests/close_lookup_table_ix.rs b/programs/address-lookup-table-tests/tests/close_lookup_table_ix.rs index c0552c6b96ed26..0ffc88455e28f2 100644 --- a/programs/address-lookup-table-tests/tests/close_lookup_table_ix.rs +++ b/programs/address-lookup-table-tests/tests/close_lookup_table_ix.rs @@ -24,7 +24,7 @@ async fn test_close_lookup_table() { let authority_keypair = Keypair::new(); let initialized_table = new_address_lookup_table(Some(authority_keypair.pubkey()), 0); let lookup_table_address = Pubkey::new_unique(); - add_lookup_table_account(&mut context, lookup_table_address, &initialized_table).await; + add_lookup_table_account(&mut context, lookup_table_address, initialized_table).await; let client = &mut context.banks_client; let payer = &context.payer; @@ -55,7 +55,7 @@ async fn test_close_lookup_table_too_recent() { let authority_keypair = Keypair::new(); let initialized_table = new_address_lookup_table(Some(authority_keypair.pubkey()), 0); let lookup_table_address = Pubkey::new_unique(); - add_lookup_table_account(&mut context, lookup_table_address, &initialized_table).await; + add_lookup_table_account(&mut context, lookup_table_address, initialized_table).await; let ix = close_lookup_table( lookup_table_address, @@ -82,7 +82,7 @@ async fn test_close_immutable_lookup_table() { let initialized_table = new_address_lookup_table(None, 10); let lookup_table_address = Pubkey::new_unique(); - add_lookup_table_account(&mut context, lookup_table_address, &initialized_table).await; + add_lookup_table_account(&mut context, lookup_table_address, initialized_table).await; let authority = Keypair::new(); let ix = close_lookup_table( @@ -108,7 +108,7 @@ async fn test_close_lookup_table_with_wrong_authority() { let wrong_authority = Keypair::new(); let initialized_table = new_address_lookup_table(Some(authority.pubkey()), 10); let lookup_table_address = Pubkey::new_unique(); - add_lookup_table_account(&mut context, lookup_table_address, &initialized_table).await; + add_lookup_table_account(&mut context, lookup_table_address, initialized_table).await; let ix = close_lookup_table( lookup_table_address, @@ -132,7 +132,7 @@ async fn test_close_lookup_table_without_signing() { let authority = Keypair::new(); let initialized_table = new_address_lookup_table(Some(authority.pubkey()), 10); let lookup_table_address = Pubkey::new_unique(); - add_lookup_table_account(&mut context, lookup_table_address, &initialized_table).await; + add_lookup_table_account(&mut context, lookup_table_address, initialized_table).await; let mut ix = close_lookup_table( lookup_table_address, diff --git a/programs/address-lookup-table-tests/tests/common.rs b/programs/address-lookup-table-tests/tests/common.rs index 0688480b2b6af1..a29fd6010f6174 100644 --- a/programs/address-lookup-table-tests/tests/common.rs +++ b/programs/address-lookup-table-tests/tests/common.rs @@ -1,3 +1,4 @@ +#![allow(dead_code)] use { solana_address_lookup_table_program::{ id, @@ -19,13 +20,11 @@ use { std::borrow::Cow, }; -#[allow(dead_code)] pub async fn setup_test_context() -> ProgramTestContext { let program_test = ProgramTest::new("", id(), Some(process_instruction)); program_test.start_with_context().await } -#[allow(dead_code)] pub async fn assert_ix_error( context: &mut ProgramTestContext, ix: Instruction, @@ -58,7 +57,6 @@ pub async fn assert_ix_error( ); } -#[allow(dead_code)] pub fn new_address_lookup_table( authority: Option, num_addresses: usize, @@ -74,14 +72,13 @@ pub fn new_address_lookup_table( } } -#[allow(dead_code)] pub async fn add_lookup_table_account( context: &mut ProgramTestContext, account_address: Pubkey, - address_lookup_table: &AddressLookupTable<'static>, + address_lookup_table: AddressLookupTable<'static>, ) -> AccountSharedData { let mut data = Vec::new(); - address_lookup_table.serialize(&mut data).unwrap(); + address_lookup_table.serialize_for_tests(&mut data).unwrap(); let rent = context.banks_client.get_rent().await.unwrap(); let rent_exempt_balance = rent.minimum_balance(data.len()); @@ -97,7 +94,6 @@ pub async fn add_lookup_table_account( account } -#[allow(dead_code)] pub fn overwrite_slot_hashes_with_slots(context: &mut ProgramTestContext, slots: &[Slot]) { let mut slot_hashes = SlotHashes::default(); for slot in slots { diff --git a/programs/address-lookup-table-tests/tests/extend_lookup_table_ix.rs b/programs/address-lookup-table-tests/tests/extend_lookup_table_ix.rs index 81b9c46af37183..ffed5c619f66a0 100644 --- a/programs/address-lookup-table-tests/tests/extend_lookup_table_ix.rs +++ b/programs/address-lookup-table-tests/tests/extend_lookup_table_ix.rs @@ -103,7 +103,8 @@ async fn test_extend_lookup_table() { let lookup_table_address = Pubkey::new_unique(); let lookup_table_account = - add_lookup_table_account(&mut context, lookup_table_address, &lookup_table).await; + add_lookup_table_account(&mut context, lookup_table_address, lookup_table.clone()) + .await; let mut new_addresses = Vec::with_capacity(num_new_addresses); new_addresses.resize_with(num_new_addresses, Pubkey::new_unique); @@ -131,6 +132,7 @@ async fn test_extend_lookup_table() { }, derivation_slot: lookup_table.meta.derivation_slot, authority: lookup_table.meta.authority, + _padding: 0u16, }, addresses: Cow::Owned(expected_addresses), }; @@ -175,7 +177,8 @@ async fn test_extend_addresses_authority_errors() { ] { let lookup_table = new_address_lookup_table(existing_authority, 0); let lookup_table_address = Pubkey::new_unique(); - let _ = add_lookup_table_account(&mut context, lookup_table_address, &lookup_table).await; + let _ = add_lookup_table_account(&mut context, lookup_table_address, lookup_table.clone()) + .await; let num_new_addresses = 1; let mut new_addresses = Vec::with_capacity(num_new_addresses); diff --git a/programs/address-lookup-table-tests/tests/freeze_lookup_table_ix.rs b/programs/address-lookup-table-tests/tests/freeze_lookup_table_ix.rs index a743a4b55cb66e..acb638a39fb4ab 100644 --- a/programs/address-lookup-table-tests/tests/freeze_lookup_table_ix.rs +++ b/programs/address-lookup-table-tests/tests/freeze_lookup_table_ix.rs @@ -24,7 +24,12 @@ async fn test_freeze_lookup_table() { let authority = Keypair::new(); let mut initialized_table = new_address_lookup_table(Some(authority.pubkey()), 10); let lookup_table_address = Pubkey::new_unique(); - add_lookup_table_account(&mut context, lookup_table_address, &initialized_table).await; + add_lookup_table_account( + &mut context, + lookup_table_address, + initialized_table.clone(), + ) + .await; let client = &mut context.banks_client; let payer = &context.payer; @@ -59,7 +64,7 @@ async fn test_freeze_immutable_lookup_table() { let initialized_table = new_address_lookup_table(None, 10); let lookup_table_address = Pubkey::new_unique(); - add_lookup_table_account(&mut context, lookup_table_address, &initialized_table).await; + add_lookup_table_account(&mut context, lookup_table_address, initialized_table).await; let authority = Keypair::new(); let ix = freeze_lookup_table(lookup_table_address, authority.pubkey()); @@ -81,7 +86,7 @@ async fn test_freeze_lookup_table_with_wrong_authority() { let wrong_authority = Keypair::new(); let initialized_table = new_address_lookup_table(Some(authority.pubkey()), 10); let lookup_table_address = Pubkey::new_unique(); - add_lookup_table_account(&mut context, lookup_table_address, &initialized_table).await; + add_lookup_table_account(&mut context, lookup_table_address, initialized_table).await; let ix = freeze_lookup_table(lookup_table_address, wrong_authority.pubkey()); @@ -101,7 +106,7 @@ async fn test_freeze_lookup_table_without_signing() { let authority = Keypair::new(); let initialized_table = new_address_lookup_table(Some(authority.pubkey()), 10); let lookup_table_address = Pubkey::new_unique(); - add_lookup_table_account(&mut context, lookup_table_address, &initialized_table).await; + add_lookup_table_account(&mut context, lookup_table_address, initialized_table).await; let mut ix = freeze_lookup_table(lookup_table_address, authority.pubkey()); ix.accounts[1].is_signer = false; @@ -122,7 +127,7 @@ async fn test_freeze_empty_lookup_table() { let authority = Keypair::new(); let initialized_table = new_address_lookup_table(Some(authority.pubkey()), 0); let lookup_table_address = Pubkey::new_unique(); - add_lookup_table_account(&mut context, lookup_table_address, &initialized_table).await; + add_lookup_table_account(&mut context, lookup_table_address, initialized_table).await; let ix = freeze_lookup_table(lookup_table_address, authority.pubkey()); diff --git a/programs/address-lookup-table/Cargo.toml b/programs/address-lookup-table/Cargo.toml index dc1198a9484108..8062cb31b874f0 100644 --- a/programs/address-lookup-table/Cargo.toml +++ b/programs/address-lookup-table/Cargo.toml @@ -7,7 +7,7 @@ repository = "https://github.com/solana-labs/solana" license = "Apache-2.0" homepage = "https://solana.com/" documentation = "https://docs.rs/solana-address-loookup-table-program" -edition = "2018" +edition = "2021" [dependencies] bincode = "1.3.3" diff --git a/programs/address-lookup-table/src/instruction.rs b/programs/address-lookup-table/src/instruction.rs index 2c4b10daf867ff..6ba3dfe808bd46 100644 --- a/programs/address-lookup-table/src/instruction.rs +++ b/programs/address-lookup-table/src/instruction.rs @@ -2,7 +2,7 @@ use { crate::id, serde::{Deserialize, Serialize}, solana_sdk::{ - clock::{Epoch, Slot}, + clock::Slot, instruction::{AccountMeta, Instruction}, pubkey::Pubkey, system_program, @@ -59,7 +59,7 @@ pub enum ProgramInstruction { /// Derives the address of an address table account from a wallet address and a recent block's slot. pub fn derive_lookup_table_address( authority_address: &Pubkey, - recent_block_slot: Epoch, + recent_block_slot: Slot, ) -> (Pubkey, u8) { Pubkey::find_program_address( &[authority_address.as_ref(), &recent_block_slot.to_le_bytes()], diff --git a/programs/address-lookup-table/src/processor.rs b/programs/address-lookup-table/src/processor.rs index fa7a0fba51dfba..ce2b9f161af8ae 100644 --- a/programs/address-lookup-table/src/processor.rs +++ b/programs/address-lookup-table/src/processor.rs @@ -196,7 +196,12 @@ impl Processor { drop(lookup_table_account_ref); lookup_table_meta.authority = None; - lookup_table_account.set_state(&ProgramState::LookupTable(lookup_table_meta))?; + AddressLookupTable::overwrite_meta_data( + lookup_table_account + .try_account_ref_mut()? + .data_as_mut_slice(), + lookup_table_meta, + )?; Ok(()) } @@ -277,20 +282,26 @@ impl Processor { })?; } + let lookup_table_meta = lookup_table.meta; + drop(lookup_table_account_ref); + let new_table_data_len = checked_add( LOOKUP_TABLE_META_SIZE, new_table_addresses_len.saturating_mul(PUBKEY_BYTES), )?; - let mut new_table_data = Vec::with_capacity(new_table_data_len); - lookup_table.serialize(&mut new_table_data)?; - for new_address in new_addresses { - new_table_data.extend_from_slice(new_address.as_ref()); - } - drop(lookup_table_account_ref); - lookup_table_account - .try_account_ref_mut()? - .set_data(new_table_data); + { + let mut lookup_table_account_ref_mut = lookup_table_account.try_account_ref_mut()?; + AddressLookupTable::overwrite_meta_data( + lookup_table_account_ref_mut.data_as_mut_slice(), + lookup_table_meta, + )?; + + let table_data = lookup_table_account_ref_mut.data_mut(); + for new_address in new_addresses { + table_data.extend_from_slice(new_address.as_ref()); + } + } let rent: Rent = invoke_context.get_sysvar(&rent::id())?; let required_lamports = rent diff --git a/programs/address-lookup-table/src/state.rs b/programs/address-lookup-table/src/state.rs index 90ac9a72e939cb..906487962bebed 100644 --- a/programs/address-lookup-table/src/state.rs +++ b/programs/address-lookup-table/src/state.rs @@ -37,6 +37,8 @@ pub struct LookupTableMeta { pub last_extended_slot_start_index: u8, /// Authority address which must sign for each modification. pub authority: Option, + // Padding to keep addresses 8-byte aligned + pub _padding: u16, // Raw list of addresses follows this serialized structure in // the account's data, starting from `LOOKUP_TABLE_META_SIZE`. } @@ -46,8 +48,7 @@ impl LookupTableMeta { LookupTableMeta { derivation_slot, authority: Some(authority), - last_extended_slot: 0, - last_extended_slot_start_index: 0, + ..LookupTableMeta::default() } } } @@ -59,15 +60,25 @@ pub struct AddressLookupTable<'a> { } impl<'a> AddressLookupTable<'a> { + /// Serialize an address table's updated meta data and zero + /// any leftover bytes. + pub fn overwrite_meta_data( + data: &mut [u8], + lookup_table_meta: LookupTableMeta, + ) -> Result<(), InstructionError> { + let meta_data = data + .get_mut(0..LOOKUP_TABLE_META_SIZE) + .ok_or(InstructionError::InvalidAccountData)?; + meta_data.fill(0); + bincode::serialize_into(meta_data, &ProgramState::LookupTable(lookup_table_meta)) + .map_err(|_| InstructionError::GenericError)?; + Ok(()) + } + /// Serialize an address table including its addresses - pub fn serialize(&self, mut data: &mut Vec) -> Result<(), InstructionError> { - bincode::serialize_into(&mut data, &(1u32, &self.meta)).map_err(|_| { - // This should only fail if the length of `new_table_data` - // is less than the serialized size of lookup table metadata, - // but this invariant should be tested. - InstructionError::InvalidAccountData - })?; + pub fn serialize_for_tests(self, data: &mut Vec) -> Result<(), InstructionError> { data.resize(LOOKUP_TABLE_META_SIZE, 0); + Self::overwrite_meta_data(data, self.meta)?; self.addresses.iter().for_each(|address| { data.extend_from_slice(address.as_ref()); }); @@ -132,16 +143,16 @@ mod tests { let lookup_table = ProgramState::LookupTable(LookupTableMeta::new_for_tests()); let meta_size = bincode::serialized_size(&lookup_table).unwrap(); assert!(meta_size as usize <= LOOKUP_TABLE_META_SIZE); - assert_eq!(meta_size as usize, 54); + assert_eq!(meta_size as usize, 56); let lookup_table = ProgramState::LookupTable(LookupTableMeta::default()); let meta_size = bincode::serialized_size(&lookup_table).unwrap(); assert!(meta_size as usize <= LOOKUP_TABLE_META_SIZE); - assert_eq!(meta_size as usize, 22); + assert_eq!(meta_size as usize, 24); } #[test] - fn test_serialize() { + fn test_overwrite_meta_data() { let meta = LookupTableMeta::new_for_tests(); let empty_table = ProgramState::LookupTable(meta.clone()); let mut serialized_table_1 = bincode::serialize(&empty_table).unwrap(); @@ -149,7 +160,9 @@ mod tests { let address_table = AddressLookupTable::new_for_tests(meta, 0); let mut serialized_table_2 = Vec::new(); - address_table.serialize(&mut serialized_table_2).unwrap(); + serialized_table_2.resize(LOOKUP_TABLE_META_SIZE, 0); + AddressLookupTable::overwrite_meta_data(&mut serialized_table_2, address_table.meta) + .unwrap(); assert_eq!(serialized_table_1, serialized_table_2); } @@ -170,7 +183,8 @@ mod tests { let lookup_table_meta = LookupTableMeta::new_for_tests(); let address_table = AddressLookupTable::new_for_tests(lookup_table_meta, num_addresses); let mut address_table_data = Vec::new(); - address_table.serialize(&mut address_table_data).unwrap(); + AddressLookupTable::serialize_for_tests(address_table.clone(), &mut address_table_data) + .unwrap(); assert_eq!( AddressLookupTable::deserialize(&address_table_data).unwrap(), address_table, diff --git a/sdk/src/account.rs b/sdk/src/account.rs index ca19f91a857437..2e8e2fc34ad216 100644 --- a/sdk/src/account.rs +++ b/sdk/src/account.rs @@ -103,6 +103,7 @@ pub trait WritableAccount: ReadableAccount { ); Ok(()) } + fn data_mut(&mut self) -> &mut Vec; fn data_as_mut_slice(&mut self) -> &mut [u8]; fn set_owner(&mut self, owner: Pubkey); fn copy_into_owner_from_slice(&mut self, source: &[u8]); @@ -156,6 +157,9 @@ impl WritableAccount for Account { fn set_lamports(&mut self, lamports: u64) { self.lamports = lamports; } + fn data_mut(&mut self) -> &mut Vec { + &mut self.data + } fn data_as_mut_slice(&mut self) -> &mut [u8] { &mut self.data } @@ -192,9 +196,11 @@ impl WritableAccount for AccountSharedData { fn set_lamports(&mut self, lamports: u64) { self.lamports = lamports; } + fn data_mut(&mut self) -> &mut Vec { + Arc::make_mut(&mut self.data) + } fn data_as_mut_slice(&mut self) -> &mut [u8] { - let data = Arc::make_mut(&mut self.data); - &mut data[..] + &mut self.data_mut()[..] } fn set_owner(&mut self, owner: Pubkey) { self.owner = owner;