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