extend_lookup_table_ix.rs 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266
  1. use {
  2. assert_matches::assert_matches,
  3. common::{
  4. add_lookup_table_account, assert_ix_error, new_address_lookup_table, setup_test_context,
  5. },
  6. solana_address_lookup_table_program::{
  7. instruction::extend_lookup_table,
  8. state::{AddressLookupTable, LookupTableMeta},
  9. },
  10. solana_program_test::*,
  11. solana_sdk::{
  12. account::ReadableAccount,
  13. instruction::{Instruction, InstructionError},
  14. pubkey::{Pubkey, PUBKEY_BYTES},
  15. signature::{Keypair, Signer},
  16. transaction::{Transaction, TransactionError},
  17. },
  18. std::{borrow::Cow, result::Result},
  19. };
  20. mod common;
  21. struct ExpectedTableAccount {
  22. lamports: u64,
  23. data_len: usize,
  24. state: AddressLookupTable<'static>,
  25. }
  26. struct TestCase<'a> {
  27. lookup_table_address: Pubkey,
  28. instruction: Instruction,
  29. extra_signer: Option<&'a Keypair>,
  30. expected_result: Result<ExpectedTableAccount, InstructionError>,
  31. }
  32. async fn run_test_case(context: &mut ProgramTestContext, test_case: TestCase<'_>) {
  33. let client = &mut context.banks_client;
  34. let payer = &context.payer;
  35. let recent_blockhash = context.last_blockhash;
  36. let mut signers = vec![payer];
  37. if let Some(extra_signer) = test_case.extra_signer {
  38. signers.push(extra_signer);
  39. }
  40. let transaction = Transaction::new_signed_with_payer(
  41. &[test_case.instruction],
  42. Some(&payer.pubkey()),
  43. &signers,
  44. recent_blockhash,
  45. );
  46. let process_result = client.process_transaction(transaction).await;
  47. match test_case.expected_result {
  48. Ok(expected_account) => {
  49. assert_matches!(process_result, Ok(()));
  50. let table_account = client
  51. .get_account(test_case.lookup_table_address)
  52. .await
  53. .unwrap()
  54. .unwrap();
  55. let lookup_table = AddressLookupTable::deserialize(&table_account.data).unwrap();
  56. assert_eq!(lookup_table, expected_account.state);
  57. assert_eq!(table_account.lamports(), expected_account.lamports);
  58. assert_eq!(table_account.data().len(), expected_account.data_len);
  59. }
  60. Err(expected_err) => {
  61. assert_eq!(
  62. process_result.unwrap_err().unwrap(),
  63. TransactionError::InstructionError(0, expected_err),
  64. );
  65. }
  66. }
  67. }
  68. #[tokio::test]
  69. async fn test_extend_lookup_table() {
  70. let mut context = setup_test_context().await;
  71. let authority = Keypair::new();
  72. let current_bank_slot = 1;
  73. let rent = context.banks_client.get_rent().await.unwrap();
  74. for extend_same_slot in [true, false] {
  75. for (num_existing_addresses, num_new_addresses, expected_result) in [
  76. (0, 0, Err(InstructionError::InvalidInstructionData)),
  77. (0, 1, Ok(())),
  78. (0, 10, Ok(())),
  79. (1, 1, Ok(())),
  80. (1, 10, Ok(())),
  81. (255, 1, Ok(())),
  82. (255, 2, Err(InstructionError::InvalidInstructionData)),
  83. (246, 10, Ok(())),
  84. (256, 1, Err(InstructionError::InvalidArgument)),
  85. ] {
  86. let mut lookup_table =
  87. new_address_lookup_table(Some(authority.pubkey()), num_existing_addresses);
  88. if extend_same_slot {
  89. lookup_table.meta.last_extended_slot = current_bank_slot;
  90. }
  91. let lookup_table_address = Pubkey::new_unique();
  92. let lookup_table_account =
  93. add_lookup_table_account(&mut context, lookup_table_address, lookup_table.clone())
  94. .await;
  95. let mut new_addresses = Vec::with_capacity(num_new_addresses);
  96. new_addresses.resize_with(num_new_addresses, Pubkey::new_unique);
  97. let instruction = extend_lookup_table(
  98. lookup_table_address,
  99. authority.pubkey(),
  100. context.payer.pubkey(),
  101. new_addresses.clone(),
  102. );
  103. let mut expected_addresses: Vec<Pubkey> = lookup_table.addresses.to_vec();
  104. expected_addresses.extend(new_addresses);
  105. let expected_result = expected_result.map(|_| {
  106. let expected_data_len =
  107. lookup_table_account.data().len() + num_new_addresses * PUBKEY_BYTES;
  108. let expected_lamports = rent.minimum_balance(expected_data_len);
  109. let expected_lookup_table = AddressLookupTable {
  110. meta: LookupTableMeta {
  111. last_extended_slot: current_bank_slot,
  112. last_extended_slot_start_index: if extend_same_slot {
  113. 0u8
  114. } else {
  115. num_existing_addresses as u8
  116. },
  117. deactivation_slot: lookup_table.meta.deactivation_slot,
  118. authority: lookup_table.meta.authority,
  119. _padding: 0u16,
  120. },
  121. addresses: Cow::Owned(expected_addresses),
  122. };
  123. ExpectedTableAccount {
  124. lamports: expected_lamports,
  125. data_len: expected_data_len,
  126. state: expected_lookup_table,
  127. }
  128. });
  129. let test_case = TestCase {
  130. lookup_table_address,
  131. instruction,
  132. extra_signer: Some(&authority),
  133. expected_result,
  134. };
  135. run_test_case(&mut context, test_case).await;
  136. }
  137. }
  138. }
  139. #[tokio::test]
  140. async fn test_extend_lookup_table_with_wrong_authority() {
  141. let mut context = setup_test_context().await;
  142. let authority = Keypair::new();
  143. let wrong_authority = Keypair::new();
  144. let initialized_table = new_address_lookup_table(Some(authority.pubkey()), 0);
  145. let lookup_table_address = Pubkey::new_unique();
  146. add_lookup_table_account(&mut context, lookup_table_address, initialized_table).await;
  147. let new_addresses = vec![Pubkey::new_unique()];
  148. let ix = extend_lookup_table(
  149. lookup_table_address,
  150. wrong_authority.pubkey(),
  151. context.payer.pubkey(),
  152. new_addresses,
  153. );
  154. assert_ix_error(
  155. &mut context,
  156. ix,
  157. Some(&wrong_authority),
  158. InstructionError::IncorrectAuthority,
  159. )
  160. .await;
  161. }
  162. #[tokio::test]
  163. async fn test_extend_lookup_table_without_signing() {
  164. let mut context = setup_test_context().await;
  165. let authority = Keypair::new();
  166. let initialized_table = new_address_lookup_table(Some(authority.pubkey()), 10);
  167. let lookup_table_address = Pubkey::new_unique();
  168. add_lookup_table_account(&mut context, lookup_table_address, initialized_table).await;
  169. let new_addresses = vec![Pubkey::new_unique()];
  170. let mut ix = extend_lookup_table(
  171. lookup_table_address,
  172. authority.pubkey(),
  173. context.payer.pubkey(),
  174. new_addresses,
  175. );
  176. ix.accounts[1].is_signer = false;
  177. assert_ix_error(
  178. &mut context,
  179. ix,
  180. None,
  181. InstructionError::MissingRequiredSignature,
  182. )
  183. .await;
  184. }
  185. #[tokio::test]
  186. async fn test_extend_deactivated_lookup_table() {
  187. let mut context = setup_test_context().await;
  188. let authority = Keypair::new();
  189. let initialized_table = {
  190. let mut table = new_address_lookup_table(Some(authority.pubkey()), 0);
  191. table.meta.deactivation_slot = 0;
  192. table
  193. };
  194. let lookup_table_address = Pubkey::new_unique();
  195. add_lookup_table_account(&mut context, lookup_table_address, initialized_table).await;
  196. let new_addresses = vec![Pubkey::new_unique()];
  197. let ix = extend_lookup_table(
  198. lookup_table_address,
  199. authority.pubkey(),
  200. context.payer.pubkey(),
  201. new_addresses,
  202. );
  203. assert_ix_error(
  204. &mut context,
  205. ix,
  206. Some(&authority),
  207. InstructionError::InvalidArgument,
  208. )
  209. .await;
  210. }
  211. #[tokio::test]
  212. async fn test_extend_immutable_lookup_table() {
  213. let mut context = setup_test_context().await;
  214. let authority = Keypair::new();
  215. let initialized_table = new_address_lookup_table(None, 1);
  216. let lookup_table_address = Pubkey::new_unique();
  217. add_lookup_table_account(&mut context, lookup_table_address, initialized_table).await;
  218. let new_addresses = vec![Pubkey::new_unique()];
  219. let ix = extend_lookup_table(
  220. lookup_table_address,
  221. authority.pubkey(),
  222. context.payer.pubkey(),
  223. new_addresses,
  224. );
  225. assert_ix_error(
  226. &mut context,
  227. ix,
  228. Some(&authority),
  229. InstructionError::Immutable,
  230. )
  231. .await;
  232. }