Skip to main content

solana_message/
compiled_keys.rs

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