solana_message/
compiled_keys.rs

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