1#[cfg(not(target_os = "solana"))]
2use crate::{
3 v0::{LoadedAddresses, MessageAddressTableLookup},
4 AddressLookupTableAccount,
5};
6use {
7 crate::MessageHeader, clone_solana_instruction::Instruction, clone_solana_pubkey::Pubkey,
8 clone_solana_sdk_ids::system_program, core::fmt, std::collections::BTreeMap,
9};
10
11#[derive(Default, Debug, Clone, PartialEq, Eq)]
13pub(crate) struct CompiledKeys {
14 payer: Option<Pubkey>,
15 key_meta_map: BTreeMap<Pubkey, CompiledKeyMeta>,
16}
17
18#[cfg_attr(target_os = "solana", allow(dead_code))]
19#[derive(PartialEq, Debug, Eq, Clone)]
20pub enum CompileError {
21 AccountIndexOverflow,
22 AddressTableLookupIndexOverflow,
23 UnknownInstructionKey(Pubkey),
24}
25
26impl std::error::Error for CompileError {}
27
28impl fmt::Display for CompileError {
29 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
30 match self {
31 CompileError::AccountIndexOverflow => {
32 f.write_str("account index overflowed during compilation")
33 }
34 CompileError::AddressTableLookupIndexOverflow => {
35 f.write_str("address lookup table index overflowed during compilation")
36 }
37 CompileError::UnknownInstructionKey(key) => f.write_fmt(format_args!(
38 "encountered unknown account key `{0}` during instruction compilation",
39 key,
40 )),
41 }
42 }
43}
44
45#[derive(Default, Debug, Clone, PartialEq, Eq)]
46struct CompiledKeyMeta {
47 is_signer: bool,
48 is_writable: bool,
49 is_invoked: bool,
50 is_nonce: bool,
51}
52
53impl CompiledKeys {
54 pub(crate) fn compile(instructions: &[Instruction], payer: Option<Pubkey>) -> Self {
57 let mut key_meta_map = BTreeMap::<Pubkey, CompiledKeyMeta>::new();
58 for ix in instructions {
59 let meta = key_meta_map.entry(ix.program_id).or_default();
60 meta.is_invoked = true;
61 for account_meta in &ix.accounts {
62 let meta = key_meta_map.entry(account_meta.pubkey).or_default();
63 meta.is_signer |= account_meta.is_signer;
64 meta.is_writable |= account_meta.is_writable;
65 }
66 }
67 if let Some(nonce_pubkey) = get_nonce_pubkey(instructions) {
68 let meta = key_meta_map.entry(*nonce_pubkey).or_default();
69 meta.is_nonce = true;
70 }
71 if let Some(payer) = &payer {
72 let meta = key_meta_map.entry(*payer).or_default();
73 meta.is_signer = true;
74 meta.is_writable = true;
75 }
76 Self {
77 payer,
78 key_meta_map,
79 }
80 }
81
82 pub(crate) fn try_into_message_components(
83 self,
84 ) -> Result<(MessageHeader, Vec<Pubkey>), CompileError> {
85 let try_into_u8 = |num: usize| -> Result<u8, CompileError> {
86 u8::try_from(num).map_err(|_| CompileError::AccountIndexOverflow)
87 };
88
89 let Self {
90 payer,
91 mut key_meta_map,
92 } = self;
93
94 if let Some(payer) = &payer {
95 key_meta_map.remove_entry(payer);
96 }
97
98 let writable_signer_keys: Vec<Pubkey> = payer
99 .into_iter()
100 .chain(
101 key_meta_map
102 .iter()
103 .filter_map(|(key, meta)| (meta.is_signer && meta.is_writable).then_some(*key)),
104 )
105 .collect();
106 let readonly_signer_keys: Vec<Pubkey> = key_meta_map
107 .iter()
108 .filter_map(|(key, meta)| (meta.is_signer && !meta.is_writable).then_some(*key))
109 .collect();
110 let writable_non_signer_keys: Vec<Pubkey> = key_meta_map
111 .iter()
112 .filter_map(|(key, meta)| (!meta.is_signer && meta.is_writable).then_some(*key))
113 .collect();
114 let readonly_non_signer_keys: Vec<Pubkey> = key_meta_map
115 .iter()
116 .filter_map(|(key, meta)| (!meta.is_signer && !meta.is_writable).then_some(*key))
117 .collect();
118
119 let signers_len = writable_signer_keys
120 .len()
121 .saturating_add(readonly_signer_keys.len());
122
123 let header = MessageHeader {
124 num_required_signatures: try_into_u8(signers_len)?,
125 num_readonly_signed_accounts: try_into_u8(readonly_signer_keys.len())?,
126 num_readonly_unsigned_accounts: try_into_u8(readonly_non_signer_keys.len())?,
127 };
128
129 let static_account_keys = std::iter::empty()
130 .chain(writable_signer_keys)
131 .chain(readonly_signer_keys)
132 .chain(writable_non_signer_keys)
133 .chain(readonly_non_signer_keys)
134 .collect();
135
136 Ok((header, static_account_keys))
137 }
138
139 #[cfg(not(target_os = "solana"))]
140 pub(crate) fn try_extract_table_lookup(
141 &mut self,
142 lookup_table_account: &AddressLookupTableAccount,
143 ) -> Result<Option<(MessageAddressTableLookup, LoadedAddresses)>, CompileError> {
144 let (writable_indexes, drained_writable_keys) = self
145 .try_drain_keys_found_in_lookup_table(&lookup_table_account.addresses, |meta| {
146 !meta.is_signer && !meta.is_invoked && !meta.is_nonce && meta.is_writable
147 })?;
148 let (readonly_indexes, drained_readonly_keys) = self
149 .try_drain_keys_found_in_lookup_table(&lookup_table_account.addresses, |meta| {
150 !meta.is_signer && !meta.is_invoked && !meta.is_nonce && !meta.is_writable
151 })?;
152
153 if writable_indexes.is_empty() && readonly_indexes.is_empty() {
155 return Ok(None);
156 }
157
158 Ok(Some((
159 MessageAddressTableLookup {
160 account_key: lookup_table_account.key,
161 writable_indexes,
162 readonly_indexes,
163 },
164 LoadedAddresses {
165 writable: drained_writable_keys,
166 readonly: drained_readonly_keys,
167 },
168 )))
169 }
170
171 #[cfg(not(target_os = "solana"))]
172 fn try_drain_keys_found_in_lookup_table(
173 &mut self,
174 lookup_table_addresses: &[Pubkey],
175 key_meta_filter: impl Fn(&CompiledKeyMeta) -> bool,
176 ) -> Result<(Vec<u8>, Vec<Pubkey>), CompileError> {
177 let mut lookup_table_indexes = Vec::new();
178 let mut drained_keys = Vec::new();
179
180 for search_key in self
181 .key_meta_map
182 .iter()
183 .filter_map(|(key, meta)| key_meta_filter(meta).then_some(key))
184 {
185 for (key_index, key) in lookup_table_addresses.iter().enumerate() {
186 if key == search_key {
187 let lookup_table_index = u8::try_from(key_index)
188 .map_err(|_| CompileError::AddressTableLookupIndexOverflow)?;
189
190 lookup_table_indexes.push(lookup_table_index);
191 drained_keys.push(*search_key);
192 break;
193 }
194 }
195 }
196
197 for key in &drained_keys {
198 self.key_meta_map.remove_entry(key);
199 }
200
201 Ok((lookup_table_indexes, drained_keys))
202 }
203}
204
205const NONCED_TX_MARKER_IX_INDEX: usize = 0;
207const ADVANCE_NONCE_PREFIX: [u8; 4] = [4, 0, 0, 0];
209
210fn get_nonce_pubkey(instructions: &[Instruction]) -> Option<&Pubkey> {
211 let ix = instructions.get(NONCED_TX_MARKER_IX_INDEX)?;
212 if !system_program::check_id(&ix.program_id) {
213 return None;
214 }
215
216 if ix.data.get(0..4) != Some(&ADVANCE_NONCE_PREFIX[..]) {
217 return None;
218 }
219
220 ix.accounts.first().map(|meta| &meta.pubkey)
221}
222
223#[cfg(test)]
224mod tests {
225 use {
226 super::*,
227 bitflags::bitflags,
228 clone_solana_instruction::AccountMeta,
229 clone_solana_sdk_ids::sysvar::recent_blockhashes,
230 clone_solana_system_interface::instruction::{advance_nonce_account, SystemInstruction},
231 };
232
233 static_assertions::const_assert_eq!(
234 NONCED_TX_MARKER_IX_INDEX,
235 clone_solana_nonce::NONCED_TX_MARKER_IX_INDEX as usize
236 );
237
238 bitflags! {
239 #[derive(Clone, Copy)]
240 pub struct KeyFlags: u8 {
241 const SIGNER = 0b00000001;
242 const WRITABLE = 0b00000010;
243 const INVOKED = 0b00000100;
244 const NONCE = 0b00001000;
245 }
246 }
247
248 impl From<KeyFlags> for CompiledKeyMeta {
249 fn from(flags: KeyFlags) -> Self {
250 Self {
251 is_signer: flags.contains(KeyFlags::SIGNER),
252 is_writable: flags.contains(KeyFlags::WRITABLE),
253 is_invoked: flags.contains(KeyFlags::INVOKED),
254 is_nonce: flags.contains(KeyFlags::NONCE),
255 }
256 }
257 }
258
259 #[test]
260 fn test_advance_nonce_ix_prefix() {
261 let advance_nonce_ix: SystemInstruction = clone_solana_bincode::limited_deserialize(
262 &ADVANCE_NONCE_PREFIX[..],
263 4, )
265 .unwrap();
266 assert_eq!(advance_nonce_ix, SystemInstruction::AdvanceNonceAccount);
267 }
268
269 #[test]
270 fn test_compile_with_dups() {
271 let program_id0 = Pubkey::new_unique();
272 let program_id1 = Pubkey::new_unique();
273 let program_id2 = Pubkey::new_unique();
274 let program_id3 = Pubkey::new_unique();
275 let id0 = Pubkey::new_unique();
276 let id1 = Pubkey::new_unique();
277 let id2 = Pubkey::new_unique();
278 let id3 = Pubkey::new_unique();
279 let compiled_keys = CompiledKeys::compile(
280 &[
281 Instruction::new_with_bincode(
282 program_id0,
283 &0,
284 vec![
285 AccountMeta::new_readonly(id0, false),
286 AccountMeta::new_readonly(id1, true),
287 AccountMeta::new(id2, false),
288 AccountMeta::new(id3, true),
289 AccountMeta::new_readonly(id0, false),
291 AccountMeta::new_readonly(id1, true),
292 AccountMeta::new(id2, false),
293 AccountMeta::new(id3, true),
294 AccountMeta::new_readonly(program_id0, false),
296 AccountMeta::new_readonly(program_id1, true),
297 AccountMeta::new(program_id2, false),
298 AccountMeta::new(program_id3, true),
299 ],
300 ),
301 Instruction::new_with_bincode(program_id1, &0, vec![]),
302 Instruction::new_with_bincode(program_id2, &0, vec![]),
303 Instruction::new_with_bincode(program_id3, &0, vec![]),
304 ],
305 None,
306 );
307
308 assert_eq!(
309 compiled_keys,
310 CompiledKeys {
311 payer: None,
312 key_meta_map: BTreeMap::from([
313 (id0, KeyFlags::empty().into()),
314 (id1, KeyFlags::SIGNER.into()),
315 (id2, KeyFlags::WRITABLE.into()),
316 (id3, (KeyFlags::SIGNER | KeyFlags::WRITABLE).into()),
317 (program_id0, KeyFlags::INVOKED.into()),
318 (program_id1, (KeyFlags::INVOKED | KeyFlags::SIGNER).into()),
319 (program_id2, (KeyFlags::INVOKED | KeyFlags::WRITABLE).into()),
320 (
321 program_id3,
322 (KeyFlags::INVOKED | KeyFlags::WRITABLE | KeyFlags::SIGNER).into()
323 ),
324 ]),
325 }
326 );
327 }
328
329 #[test]
330 fn test_compile_with_dup_payer() {
331 let program_id = Pubkey::new_unique();
332 let payer = Pubkey::new_unique();
333 let compiled_keys = CompiledKeys::compile(
334 &[Instruction::new_with_bincode(
335 program_id,
336 &0,
337 vec![AccountMeta::new_readonly(payer, false)],
338 )],
339 Some(payer),
340 );
341 assert_eq!(
342 compiled_keys,
343 CompiledKeys {
344 payer: Some(payer),
345 key_meta_map: BTreeMap::from([
346 (payer, (KeyFlags::SIGNER | KeyFlags::WRITABLE).into()),
347 (program_id, KeyFlags::INVOKED.into()),
348 ]),
349 }
350 );
351 }
352
353 #[test]
354 fn test_compile_with_dup_signer_mismatch() {
355 let program_id = Pubkey::new_unique();
356 let id0 = Pubkey::new_unique();
357 let compiled_keys = CompiledKeys::compile(
358 &[Instruction::new_with_bincode(
359 program_id,
360 &0,
361 vec![AccountMeta::new(id0, false), AccountMeta::new(id0, true)],
362 )],
363 None,
364 );
365
366 assert_eq!(
368 compiled_keys,
369 CompiledKeys {
370 payer: None,
371 key_meta_map: BTreeMap::from([
372 (id0, (KeyFlags::SIGNER | KeyFlags::WRITABLE).into()),
373 (program_id, KeyFlags::INVOKED.into()),
374 ]),
375 }
376 );
377 }
378
379 #[test]
380 fn test_compile_with_dup_signer_writable_mismatch() {
381 let program_id = Pubkey::new_unique();
382 let id0 = Pubkey::new_unique();
383 let compiled_keys = CompiledKeys::compile(
384 &[Instruction::new_with_bincode(
385 program_id,
386 &0,
387 vec![
388 AccountMeta::new_readonly(id0, true),
389 AccountMeta::new(id0, true),
390 ],
391 )],
392 None,
393 );
394
395 assert_eq!(
397 compiled_keys,
398 CompiledKeys {
399 payer: None,
400 key_meta_map: BTreeMap::from([
401 (id0, (KeyFlags::SIGNER | KeyFlags::WRITABLE).into()),
402 (program_id, KeyFlags::INVOKED.into()),
403 ]),
404 }
405 );
406 }
407
408 #[test]
409 fn test_compile_with_dup_nonsigner_writable_mismatch() {
410 let program_id = Pubkey::new_unique();
411 let id0 = Pubkey::new_unique();
412 let compiled_keys = CompiledKeys::compile(
413 &[
414 Instruction::new_with_bincode(
415 program_id,
416 &0,
417 vec![
418 AccountMeta::new_readonly(id0, false),
419 AccountMeta::new(id0, false),
420 ],
421 ),
422 Instruction::new_with_bincode(program_id, &0, vec![AccountMeta::new(id0, false)]),
423 ],
424 None,
425 );
426
427 assert_eq!(
429 compiled_keys,
430 CompiledKeys {
431 payer: None,
432 key_meta_map: BTreeMap::from([
433 (id0, KeyFlags::WRITABLE.into()),
434 (program_id, KeyFlags::INVOKED.into()),
435 ]),
436 }
437 );
438 }
439
440 #[test]
441 fn test_compile_with_nonce_instruction() {
442 let nonce_pubkey = Pubkey::new_unique();
443 let nonce_authority = Pubkey::new_unique();
444 let compiled_keys = CompiledKeys::compile(
445 &[advance_nonce_account(&nonce_pubkey, &nonce_authority)],
446 Some(nonce_authority),
447 );
448
449 assert_eq!(
450 compiled_keys,
451 CompiledKeys {
452 payer: Some(nonce_authority),
453 key_meta_map: BTreeMap::from([
454 (nonce_pubkey, (KeyFlags::NONCE | KeyFlags::WRITABLE).into()),
455 (
456 nonce_authority,
457 (KeyFlags::SIGNER | KeyFlags::WRITABLE).into()
458 ),
459 (system_program::id(), KeyFlags::INVOKED.into()),
460 (recent_blockhashes::id(), CompiledKeyMeta::default())
461 ]),
462 }
463 );
464 }
465
466 #[test]
467 fn test_try_into_message_components() {
468 let keys = vec![
469 Pubkey::new_unique(),
470 Pubkey::new_unique(),
471 Pubkey::new_unique(),
472 Pubkey::new_unique(),
473 ];
474
475 let compiled_keys = CompiledKeys {
476 payer: None,
477 key_meta_map: BTreeMap::from([
478 (keys[0], (KeyFlags::SIGNER | KeyFlags::WRITABLE).into()),
479 (keys[1], KeyFlags::SIGNER.into()),
480 (keys[2], KeyFlags::WRITABLE.into()),
481 (keys[3], KeyFlags::empty().into()),
482 ]),
483 };
484
485 let result = compiled_keys.try_into_message_components();
486 assert_eq!(result.as_ref().err(), None);
487 let (header, static_keys) = result.unwrap();
488
489 assert_eq!(static_keys, keys);
490 assert_eq!(
491 header,
492 MessageHeader {
493 num_required_signatures: 2,
494 num_readonly_signed_accounts: 1,
495 num_readonly_unsigned_accounts: 1,
496 }
497 );
498 }
499
500 #[test]
501 fn test_try_into_message_components_with_too_many_keys() {
502 const TOO_MANY_KEYS: usize = 257;
503
504 for key_flags in [
505 KeyFlags::WRITABLE | KeyFlags::SIGNER,
506 KeyFlags::SIGNER,
507 KeyFlags::empty(),
509 ] {
510 let test_keys = CompiledKeys {
511 payer: None,
512 key_meta_map: BTreeMap::from_iter(
513 (0..TOO_MANY_KEYS).map(|_| (Pubkey::new_unique(), key_flags.into())),
514 ),
515 };
516
517 assert_eq!(
518 test_keys.try_into_message_components(),
519 Err(CompileError::AccountIndexOverflow)
520 );
521 }
522 }
523
524 #[test]
525 fn test_try_extract_table_lookup() {
526 let keys = vec![
527 Pubkey::new_unique(),
528 Pubkey::new_unique(),
529 Pubkey::new_unique(),
530 Pubkey::new_unique(),
531 Pubkey::new_unique(),
532 Pubkey::new_unique(),
533 ];
534
535 let mut compiled_keys = CompiledKeys {
536 payer: None,
537 key_meta_map: BTreeMap::from([
538 (keys[0], (KeyFlags::SIGNER | KeyFlags::WRITABLE).into()),
539 (keys[1], KeyFlags::SIGNER.into()),
540 (keys[2], KeyFlags::WRITABLE.into()),
541 (keys[3], KeyFlags::empty().into()),
542 (keys[4], (KeyFlags::INVOKED | KeyFlags::WRITABLE).into()),
543 (keys[5], (KeyFlags::INVOKED).into()),
544 ]),
545 };
546
547 let addresses = [keys.clone(), keys.clone()].concat();
549 let lookup_table_account = AddressLookupTableAccount {
550 key: Pubkey::new_unique(),
551 addresses,
552 };
553
554 assert_eq!(
555 compiled_keys.try_extract_table_lookup(&lookup_table_account),
556 Ok(Some((
557 MessageAddressTableLookup {
558 account_key: lookup_table_account.key,
559 writable_indexes: vec![2],
560 readonly_indexes: vec![3],
561 },
562 LoadedAddresses {
563 writable: vec![keys[2]],
564 readonly: vec![keys[3]],
565 },
566 )))
567 );
568
569 assert_eq!(compiled_keys.key_meta_map.len(), 4);
570 assert!(!compiled_keys.key_meta_map.contains_key(&keys[2]));
571 assert!(!compiled_keys.key_meta_map.contains_key(&keys[3]));
572 }
573
574 #[test]
575 fn test_try_extract_table_lookup_returns_none() {
576 let mut compiled_keys = CompiledKeys {
577 payer: None,
578 key_meta_map: BTreeMap::from([
579 (Pubkey::new_unique(), KeyFlags::WRITABLE.into()),
580 (Pubkey::new_unique(), KeyFlags::empty().into()),
581 ]),
582 };
583
584 let lookup_table_account = AddressLookupTableAccount {
585 key: Pubkey::new_unique(),
586 addresses: vec![],
587 };
588
589 let expected_compiled_keys = compiled_keys.clone();
590 assert_eq!(
591 compiled_keys.try_extract_table_lookup(&lookup_table_account),
592 Ok(None)
593 );
594 assert_eq!(compiled_keys, expected_compiled_keys);
595 }
596
597 #[test]
598 fn test_try_extract_table_lookup_for_invalid_table() {
599 let writable_key = Pubkey::new_unique();
600 let mut compiled_keys = CompiledKeys {
601 payer: None,
602 key_meta_map: BTreeMap::from([
603 (writable_key, KeyFlags::WRITABLE.into()),
604 (Pubkey::new_unique(), KeyFlags::empty().into()),
605 ]),
606 };
607
608 const MAX_LENGTH_WITHOUT_OVERFLOW: usize = u8::MAX as usize + 1;
609 let mut addresses = vec![Pubkey::default(); MAX_LENGTH_WITHOUT_OVERFLOW];
610 addresses.push(writable_key);
611
612 let lookup_table_account = AddressLookupTableAccount {
613 key: Pubkey::new_unique(),
614 addresses,
615 };
616
617 let expected_compiled_keys = compiled_keys.clone();
618 assert_eq!(
619 compiled_keys.try_extract_table_lookup(&lookup_table_account),
620 Err(CompileError::AddressTableLookupIndexOverflow),
621 );
622 assert_eq!(compiled_keys, expected_compiled_keys);
623 }
624
625 #[test]
626 fn test_try_drain_keys_found_in_lookup_table() {
627 let orig_keys = [
628 Pubkey::new_unique(),
629 Pubkey::new_unique(),
630 Pubkey::new_unique(),
631 Pubkey::new_unique(),
632 Pubkey::new_unique(),
633 ];
634
635 let mut compiled_keys = CompiledKeys {
636 payer: None,
637 key_meta_map: BTreeMap::from([
638 (orig_keys[0], KeyFlags::empty().into()),
639 (orig_keys[1], KeyFlags::WRITABLE.into()),
640 (orig_keys[2], KeyFlags::WRITABLE.into()),
641 (orig_keys[3], KeyFlags::empty().into()),
642 (orig_keys[4], KeyFlags::empty().into()),
643 ]),
644 };
645
646 let lookup_table_addresses = vec![
647 Pubkey::new_unique(),
648 orig_keys[0],
649 Pubkey::new_unique(),
650 orig_keys[4],
651 Pubkey::new_unique(),
652 orig_keys[2],
653 Pubkey::new_unique(),
654 ];
655
656 let drain_result = compiled_keys
657 .try_drain_keys_found_in_lookup_table(&lookup_table_addresses, |meta| {
658 !meta.is_writable
659 });
660 assert_eq!(drain_result.as_ref().err(), None);
661 let (lookup_table_indexes, drained_keys) = drain_result.unwrap();
662
663 assert_eq!(
664 compiled_keys.key_meta_map.keys().collect::<Vec<&_>>(),
665 vec![&orig_keys[1], &orig_keys[2], &orig_keys[3]]
666 );
667 assert_eq!(drained_keys, vec![orig_keys[0], orig_keys[4]]);
668 assert_eq!(lookup_table_indexes, vec![1, 3]);
669 }
670
671 #[test]
672 fn test_try_drain_keys_found_in_lookup_table_with_empty_keys() {
673 let mut compiled_keys = CompiledKeys::default();
674
675 let lookup_table_addresses = vec![
676 Pubkey::new_unique(),
677 Pubkey::new_unique(),
678 Pubkey::new_unique(),
679 ];
680
681 let drain_result =
682 compiled_keys.try_drain_keys_found_in_lookup_table(&lookup_table_addresses, |_| true);
683 assert_eq!(drain_result.as_ref().err(), None);
684 let (lookup_table_indexes, drained_keys) = drain_result.unwrap();
685
686 assert!(drained_keys.is_empty());
687 assert!(lookup_table_indexes.is_empty());
688 }
689
690 #[test]
691 fn test_try_drain_keys_found_in_lookup_table_with_empty_table() {
692 let original_keys = [
693 Pubkey::new_unique(),
694 Pubkey::new_unique(),
695 Pubkey::new_unique(),
696 ];
697
698 let mut compiled_keys = CompiledKeys {
699 payer: None,
700 key_meta_map: BTreeMap::from_iter(
701 original_keys
702 .iter()
703 .map(|key| (*key, CompiledKeyMeta::default())),
704 ),
705 };
706
707 let lookup_table_addresses = vec![];
708
709 let drain_result =
710 compiled_keys.try_drain_keys_found_in_lookup_table(&lookup_table_addresses, |_| true);
711 assert_eq!(drain_result.as_ref().err(), None);
712 let (lookup_table_indexes, drained_keys) = drain_result.unwrap();
713
714 assert_eq!(compiled_keys.key_meta_map.len(), original_keys.len());
715 assert!(drained_keys.is_empty());
716 assert!(lookup_table_indexes.is_empty());
717 }
718
719 #[test]
720 fn test_try_drain_keys_found_in_lookup_table_with_too_many_addresses() {
721 let key = Pubkey::new_unique();
722 let mut compiled_keys = CompiledKeys {
723 payer: None,
724 key_meta_map: BTreeMap::from([(key, CompiledKeyMeta::default())]),
725 };
726
727 const MAX_LENGTH_WITHOUT_OVERFLOW: usize = u8::MAX as usize + 1;
728 let mut lookup_table_addresses = vec![Pubkey::default(); MAX_LENGTH_WITHOUT_OVERFLOW];
729 lookup_table_addresses.push(key);
730
731 let drain_result =
732 compiled_keys.try_drain_keys_found_in_lookup_table(&lookup_table_addresses, |_| true);
733 assert_eq!(
734 drain_result.err(),
735 Some(CompileError::AddressTableLookupIndexOverflow)
736 );
737 }
738}