solana_config_program/
config_processor.rs

1//! Config program
2
3use {
4    crate::ConfigKeys,
5    bincode::deserialize,
6    solana_program_runtime::{ic_msg, invoke_context::InvokeContext},
7    solana_sdk::{
8        feature_set, instruction::InstructionError, program_utils::limited_deserialize,
9        pubkey::Pubkey,
10    },
11    std::collections::BTreeSet,
12};
13
14pub fn process_instruction(
15    _first_instruction_account: usize,
16    invoke_context: &mut InvokeContext,
17) -> Result<(), InstructionError> {
18    let transaction_context = &invoke_context.transaction_context;
19    let instruction_context = transaction_context.get_current_instruction_context()?;
20    let data = instruction_context.get_instruction_data();
21
22    let key_list: ConfigKeys = limited_deserialize(data)?;
23    let config_account_key = transaction_context.get_key_of_account_at_index(
24        instruction_context.get_index_of_instruction_account_in_transaction(0)?,
25    )?;
26    let config_account =
27        instruction_context.try_borrow_instruction_account(transaction_context, 0)?;
28    let is_config_account_signer = config_account.is_signer();
29    let current_data: ConfigKeys = {
30        if config_account.get_owner() != &crate::id() {
31            return Err(InstructionError::InvalidAccountOwner);
32        }
33
34        deserialize(config_account.get_data()).map_err(|err| {
35            ic_msg!(
36                invoke_context,
37                "Unable to deserialize config account: {}",
38                err
39            );
40            InstructionError::InvalidAccountData
41        })?
42    };
43    drop(config_account);
44
45    let current_signer_keys: Vec<Pubkey> = current_data
46        .keys
47        .iter()
48        .filter(|(_, is_signer)| *is_signer)
49        .map(|(pubkey, _)| *pubkey)
50        .collect();
51    if current_signer_keys.is_empty() {
52        // Config account keypair must be a signer on account initialization,
53        // or when no signers specified in Config data
54        if !is_config_account_signer {
55            return Err(InstructionError::MissingRequiredSignature);
56        }
57    }
58
59    let mut counter = 0;
60    for (signer, _) in key_list.keys.iter().filter(|(_, is_signer)| *is_signer) {
61        counter += 1;
62        if signer != config_account_key {
63            let signer_account = instruction_context
64                .try_borrow_instruction_account(transaction_context, counter)
65                .map_err(|_| {
66                    ic_msg!(
67                        invoke_context,
68                        "account {:?} is not in account list",
69                        signer,
70                    );
71                    InstructionError::MissingRequiredSignature
72                })?;
73            if !signer_account.is_signer() {
74                ic_msg!(
75                    invoke_context,
76                    "account {:?} signer_key().is_none()",
77                    signer
78                );
79                return Err(InstructionError::MissingRequiredSignature);
80            }
81            if signer_account.get_key() != signer {
82                ic_msg!(
83                    invoke_context,
84                    "account[{:?}].signer_key() does not match Config data)",
85                    counter + 1
86                );
87                return Err(InstructionError::MissingRequiredSignature);
88            }
89            // If Config account is already initialized, update signatures must match Config data
90            if !current_data.keys.is_empty()
91                && !current_signer_keys.iter().any(|pubkey| pubkey == signer)
92            {
93                ic_msg!(
94                    invoke_context,
95                    "account {:?} is not in stored signer list",
96                    signer
97                );
98                return Err(InstructionError::MissingRequiredSignature);
99            }
100        } else if !is_config_account_signer {
101            ic_msg!(invoke_context, "account[0].signer_key().is_none()");
102            return Err(InstructionError::MissingRequiredSignature);
103        }
104    }
105
106    if invoke_context
107        .feature_set
108        .is_active(&feature_set::dedupe_config_program_signers::id())
109    {
110        let total_new_keys = key_list.keys.len();
111        let unique_new_keys = key_list.keys.into_iter().collect::<BTreeSet<_>>();
112        if unique_new_keys.len() != total_new_keys {
113            ic_msg!(invoke_context, "new config contains duplicate keys");
114            return Err(InstructionError::InvalidArgument);
115        }
116    }
117
118    // Check for Config data signers not present in incoming account update
119    if current_signer_keys.len() > counter {
120        ic_msg!(
121            invoke_context,
122            "too few signers: {:?}; expected: {:?}",
123            counter,
124            current_signer_keys.len()
125        );
126        return Err(InstructionError::MissingRequiredSignature);
127    }
128
129    let mut config_account =
130        instruction_context.try_borrow_instruction_account(transaction_context, 0)?;
131    if config_account.get_data().len() < data.len() {
132        ic_msg!(invoke_context, "instruction data too large");
133        return Err(InstructionError::InvalidInstructionData);
134    }
135    config_account.get_data_mut()?[..data.len()].copy_from_slice(data);
136    Ok(())
137}
138
139#[cfg(test)]
140mod tests {
141    use {
142        super::*,
143        crate::{config_instruction, get_config_data, id, ConfigKeys, ConfigState},
144        bincode::serialized_size,
145        serde_derive::{Deserialize, Serialize},
146        solana_program_runtime::invoke_context::mock_process_instruction,
147        solana_sdk::{
148            account::{AccountSharedData, ReadableAccount},
149            instruction::AccountMeta,
150            pubkey::Pubkey,
151            signature::{Keypair, Signer},
152            system_instruction::SystemInstruction,
153        },
154    };
155
156    fn process_instruction(
157        instruction_data: &[u8],
158        transaction_accounts: Vec<(Pubkey, AccountSharedData)>,
159        instruction_accounts: Vec<AccountMeta>,
160        expected_result: Result<(), InstructionError>,
161    ) -> Vec<AccountSharedData> {
162        mock_process_instruction(
163            &id(),
164            Vec::new(),
165            instruction_data,
166            transaction_accounts,
167            instruction_accounts,
168            None,
169            None,
170            expected_result,
171            super::process_instruction,
172        )
173    }
174
175    #[derive(Serialize, Deserialize, Debug, PartialEq, Eq)]
176    struct MyConfig {
177        pub item: u64,
178    }
179    impl Default for MyConfig {
180        fn default() -> Self {
181            Self { item: 123_456_789 }
182        }
183    }
184    impl MyConfig {
185        pub fn new(item: u64) -> Self {
186            Self { item }
187        }
188        pub fn deserialize(input: &[u8]) -> Option<Self> {
189            deserialize(input).ok()
190        }
191    }
192
193    impl ConfigState for MyConfig {
194        fn max_space() -> u64 {
195            serialized_size(&Self::default()).unwrap()
196        }
197    }
198
199    fn create_config_account(keys: Vec<(Pubkey, bool)>) -> (Keypair, AccountSharedData) {
200        let from_pubkey = Pubkey::new_unique();
201        let config_keypair = Keypair::new();
202        let config_pubkey = config_keypair.pubkey();
203        let instructions =
204            config_instruction::create_account::<MyConfig>(&from_pubkey, &config_pubkey, 1, keys);
205        let system_instruction = limited_deserialize(&instructions[0].data).unwrap();
206        let space = match system_instruction {
207            SystemInstruction::CreateAccount {
208                lamports: _,
209                space,
210                owner: _,
211            } => space,
212            _ => panic!("Not a CreateAccount system instruction"),
213        };
214        let config_account = AccountSharedData::new(0, space as usize, &id());
215        let accounts = process_instruction(
216            &instructions[1].data,
217            vec![(config_pubkey, config_account)],
218            vec![AccountMeta {
219                pubkey: config_pubkey,
220                is_signer: true,
221                is_writable: true,
222            }],
223            Ok(()),
224        );
225        (config_keypair, accounts[0].clone())
226    }
227
228    #[test]
229    fn test_process_create_ok() {
230        solana_logger::setup();
231        let (_, config_account) = create_config_account(vec![]);
232        assert_eq!(
233            Some(MyConfig::default()),
234            deserialize(get_config_data(config_account.data()).unwrap()).ok()
235        );
236    }
237
238    #[test]
239    fn test_process_store_ok() {
240        solana_logger::setup();
241        let keys = vec![];
242        let (config_keypair, config_account) = create_config_account(keys.clone());
243        let config_pubkey = config_keypair.pubkey();
244        let my_config = MyConfig::new(42);
245
246        let instruction = config_instruction::store(&config_pubkey, true, keys, &my_config);
247        let accounts = process_instruction(
248            &instruction.data,
249            vec![(config_pubkey, config_account)],
250            vec![AccountMeta {
251                pubkey: config_pubkey,
252                is_signer: true,
253                is_writable: true,
254            }],
255            Ok(()),
256        );
257        assert_eq!(
258            Some(my_config),
259            deserialize(get_config_data(accounts[0].data()).unwrap()).ok()
260        );
261    }
262
263    #[test]
264    fn test_process_store_fail_instruction_data_too_large() {
265        solana_logger::setup();
266        let keys = vec![];
267        let (config_keypair, config_account) = create_config_account(keys.clone());
268        let config_pubkey = config_keypair.pubkey();
269        let my_config = MyConfig::new(42);
270
271        let mut instruction = config_instruction::store(&config_pubkey, true, keys, &my_config);
272        instruction.data = vec![0; 123]; // <-- Replace data with a vector that's too large
273        process_instruction(
274            &instruction.data,
275            vec![(config_pubkey, config_account)],
276            vec![AccountMeta {
277                pubkey: config_pubkey,
278                is_signer: true,
279                is_writable: true,
280            }],
281            Err(InstructionError::InvalidInstructionData),
282        );
283    }
284
285    #[test]
286    fn test_process_store_fail_account0_not_signer() {
287        solana_logger::setup();
288        let keys = vec![];
289        let (config_keypair, config_account) = create_config_account(keys);
290        let config_pubkey = config_keypair.pubkey();
291        let my_config = MyConfig::new(42);
292
293        let mut instruction = config_instruction::store(&config_pubkey, true, vec![], &my_config);
294        instruction.accounts[0].is_signer = false; // <----- not a signer
295        process_instruction(
296            &instruction.data,
297            vec![(config_pubkey, config_account)],
298            vec![AccountMeta {
299                pubkey: config_pubkey,
300                is_signer: false,
301                is_writable: true,
302            }],
303            Err(InstructionError::MissingRequiredSignature),
304        );
305    }
306
307    #[test]
308    fn test_process_store_with_additional_signers() {
309        solana_logger::setup();
310        let pubkey = Pubkey::new_unique();
311        let signer0_pubkey = Pubkey::new_unique();
312        let signer1_pubkey = Pubkey::new_unique();
313        let keys = vec![
314            (pubkey, false),
315            (signer0_pubkey, true),
316            (signer1_pubkey, true),
317        ];
318        let (config_keypair, config_account) = create_config_account(keys.clone());
319        let config_pubkey = config_keypair.pubkey();
320        let my_config = MyConfig::new(42);
321        let signer0_account = AccountSharedData::new(0, 0, &Pubkey::new_unique());
322        let signer1_account = AccountSharedData::new(0, 0, &Pubkey::new_unique());
323
324        let instruction = config_instruction::store(&config_pubkey, true, keys.clone(), &my_config);
325        let accounts = process_instruction(
326            &instruction.data,
327            vec![
328                (config_pubkey, config_account),
329                (signer0_pubkey, signer0_account),
330                (signer1_pubkey, signer1_account),
331            ],
332            vec![
333                AccountMeta {
334                    pubkey: config_pubkey,
335                    is_signer: true,
336                    is_writable: true,
337                },
338                AccountMeta {
339                    pubkey: signer0_pubkey,
340                    is_signer: true,
341                    is_writable: false,
342                },
343                AccountMeta {
344                    pubkey: signer1_pubkey,
345                    is_signer: true,
346                    is_writable: false,
347                },
348            ],
349            Ok(()),
350        );
351        let meta_data: ConfigKeys = deserialize(accounts[0].data()).unwrap();
352        assert_eq!(meta_data.keys, keys);
353        assert_eq!(
354            Some(my_config),
355            deserialize(get_config_data(accounts[0].data()).unwrap()).ok()
356        );
357    }
358
359    #[test]
360    fn test_process_store_without_config_signer() {
361        solana_logger::setup();
362        let pubkey = Pubkey::new_unique();
363        let signer0_pubkey = Pubkey::new_unique();
364        let keys = vec![(pubkey, false), (signer0_pubkey, true)];
365        let (config_keypair, _) = create_config_account(keys.clone());
366        let config_pubkey = config_keypair.pubkey();
367        let my_config = MyConfig::new(42);
368        let signer0_account = AccountSharedData::new(0, 0, &id());
369
370        let instruction = config_instruction::store(&config_pubkey, false, keys, &my_config);
371        process_instruction(
372            &instruction.data,
373            vec![(signer0_pubkey, signer0_account)],
374            vec![AccountMeta {
375                pubkey: signer0_pubkey,
376                is_signer: true,
377                is_writable: false,
378            }],
379            Err(InstructionError::InvalidAccountData),
380        );
381    }
382
383    #[test]
384    fn test_process_store_with_bad_additional_signer() {
385        solana_logger::setup();
386        let signer0_pubkey = Pubkey::new_unique();
387        let signer1_pubkey = Pubkey::new_unique();
388        let signer0_account = AccountSharedData::new(0, 0, &Pubkey::new_unique());
389        let signer1_account = AccountSharedData::new(0, 0, &Pubkey::new_unique());
390        let keys = vec![(signer0_pubkey, true)];
391        let (config_keypair, config_account) = create_config_account(keys.clone());
392        let config_pubkey = config_keypair.pubkey();
393        let my_config = MyConfig::new(42);
394
395        // Config-data pubkey doesn't match signer
396        let instruction = config_instruction::store(&config_pubkey, true, keys, &my_config);
397        process_instruction(
398            &instruction.data,
399            vec![
400                (config_pubkey, config_account.clone()),
401                (signer1_pubkey, signer1_account),
402            ],
403            vec![
404                AccountMeta {
405                    pubkey: config_pubkey,
406                    is_signer: true,
407                    is_writable: true,
408                },
409                AccountMeta {
410                    pubkey: signer1_pubkey,
411                    is_signer: true,
412                    is_writable: false,
413                },
414            ],
415            Err(InstructionError::MissingRequiredSignature),
416        );
417
418        // Config-data pubkey not a signer
419        process_instruction(
420            &instruction.data,
421            vec![
422                (config_pubkey, config_account),
423                (signer0_pubkey, signer0_account),
424            ],
425            vec![
426                AccountMeta {
427                    pubkey: config_pubkey,
428                    is_signer: true,
429                    is_writable: true,
430                },
431                AccountMeta {
432                    pubkey: signer0_pubkey,
433                    is_signer: false,
434                    is_writable: false,
435                },
436            ],
437            Err(InstructionError::MissingRequiredSignature),
438        );
439    }
440
441    #[test]
442    fn test_config_updates() {
443        solana_logger::setup();
444        let pubkey = Pubkey::new_unique();
445        let signer0_pubkey = Pubkey::new_unique();
446        let signer1_pubkey = Pubkey::new_unique();
447        let signer2_pubkey = Pubkey::new_unique();
448        let signer0_account = AccountSharedData::new(0, 0, &Pubkey::new_unique());
449        let signer1_account = AccountSharedData::new(0, 0, &Pubkey::new_unique());
450        let signer2_account = AccountSharedData::new(0, 0, &Pubkey::new_unique());
451        let keys = vec![
452            (pubkey, false),
453            (signer0_pubkey, true),
454            (signer1_pubkey, true),
455        ];
456        let (config_keypair, config_account) = create_config_account(keys.clone());
457        let config_pubkey = config_keypair.pubkey();
458        let my_config = MyConfig::new(42);
459
460        let instruction = config_instruction::store(&config_pubkey, true, keys.clone(), &my_config);
461        let accounts = process_instruction(
462            &instruction.data,
463            vec![
464                (config_pubkey, config_account),
465                (signer0_pubkey, signer0_account.clone()),
466                (signer1_pubkey, signer1_account.clone()),
467            ],
468            vec![
469                AccountMeta {
470                    pubkey: config_pubkey,
471                    is_signer: true,
472                    is_writable: true,
473                },
474                AccountMeta {
475                    pubkey: signer0_pubkey,
476                    is_signer: true,
477                    is_writable: false,
478                },
479                AccountMeta {
480                    pubkey: signer1_pubkey,
481                    is_signer: true,
482                    is_writable: false,
483                },
484            ],
485            Ok(()),
486        );
487
488        // Update with expected signatures
489        let new_config = MyConfig::new(84);
490        let instruction =
491            config_instruction::store(&config_pubkey, false, keys.clone(), &new_config);
492        let accounts = process_instruction(
493            &instruction.data,
494            vec![
495                (config_pubkey, accounts[0].clone()),
496                (signer0_pubkey, signer0_account.clone()),
497                (signer1_pubkey, signer1_account.clone()),
498            ],
499            vec![
500                AccountMeta {
501                    pubkey: config_pubkey,
502                    is_signer: false,
503                    is_writable: true,
504                },
505                AccountMeta {
506                    pubkey: signer0_pubkey,
507                    is_signer: true,
508                    is_writable: false,
509                },
510                AccountMeta {
511                    pubkey: signer1_pubkey,
512                    is_signer: true,
513                    is_writable: false,
514                },
515            ],
516            Ok(()),
517        );
518        let meta_data: ConfigKeys = deserialize(accounts[0].data()).unwrap();
519        assert_eq!(meta_data.keys, keys);
520        assert_eq!(
521            new_config,
522            MyConfig::deserialize(get_config_data(accounts[0].data()).unwrap()).unwrap()
523        );
524
525        // Attempt update with incomplete signatures
526        let keys = vec![(pubkey, false), (signer0_pubkey, true)];
527        let instruction = config_instruction::store(&config_pubkey, false, keys, &my_config);
528        process_instruction(
529            &instruction.data,
530            vec![
531                (config_pubkey, accounts[0].clone()),
532                (signer0_pubkey, signer0_account.clone()),
533                (signer1_pubkey, signer1_account),
534            ],
535            vec![
536                AccountMeta {
537                    pubkey: config_pubkey,
538                    is_signer: false,
539                    is_writable: true,
540                },
541                AccountMeta {
542                    pubkey: signer0_pubkey,
543                    is_signer: true,
544                    is_writable: false,
545                },
546                AccountMeta {
547                    pubkey: signer1_pubkey,
548                    is_signer: false,
549                    is_writable: false,
550                },
551            ],
552            Err(InstructionError::MissingRequiredSignature),
553        );
554
555        // Attempt update with incorrect signatures
556        let keys = vec![
557            (pubkey, false),
558            (signer0_pubkey, true),
559            (signer2_pubkey, true),
560        ];
561        let instruction = config_instruction::store(&config_pubkey, false, keys, &my_config);
562        process_instruction(
563            &instruction.data,
564            vec![
565                (config_pubkey, accounts[0].clone()),
566                (signer0_pubkey, signer0_account),
567                (signer2_pubkey, signer2_account),
568            ],
569            vec![
570                AccountMeta {
571                    pubkey: config_pubkey,
572                    is_signer: false,
573                    is_writable: true,
574                },
575                AccountMeta {
576                    pubkey: signer0_pubkey,
577                    is_signer: true,
578                    is_writable: false,
579                },
580                AccountMeta {
581                    pubkey: signer2_pubkey,
582                    is_signer: true,
583                    is_writable: false,
584                },
585            ],
586            Err(InstructionError::MissingRequiredSignature),
587        );
588    }
589
590    #[test]
591    fn test_config_initialize_contains_duplicates_fails() {
592        solana_logger::setup();
593        let config_address = Pubkey::new_unique();
594        let signer0_pubkey = Pubkey::new_unique();
595        let signer0_account = AccountSharedData::new(0, 0, &Pubkey::new_unique());
596        let keys = vec![
597            (config_address, false),
598            (signer0_pubkey, true),
599            (signer0_pubkey, true),
600        ];
601        let (config_keypair, config_account) = create_config_account(keys.clone());
602        let config_pubkey = config_keypair.pubkey();
603        let my_config = MyConfig::new(42);
604
605        // Attempt initialization with duplicate signer inputs
606        let instruction = config_instruction::store(&config_pubkey, true, keys, &my_config);
607        process_instruction(
608            &instruction.data,
609            vec![
610                (config_pubkey, config_account),
611                (signer0_pubkey, signer0_account),
612            ],
613            vec![
614                AccountMeta {
615                    pubkey: config_pubkey,
616                    is_signer: true,
617                    is_writable: true,
618                },
619                AccountMeta {
620                    pubkey: signer0_pubkey,
621                    is_signer: true,
622                    is_writable: false,
623                },
624                AccountMeta {
625                    pubkey: signer0_pubkey,
626                    is_signer: true,
627                    is_writable: false,
628                },
629            ],
630            Err(InstructionError::InvalidArgument),
631        );
632    }
633
634    #[test]
635    fn test_config_update_contains_duplicates_fails() {
636        solana_logger::setup();
637        let config_address = Pubkey::new_unique();
638        let signer0_pubkey = Pubkey::new_unique();
639        let signer1_pubkey = Pubkey::new_unique();
640        let signer0_account = AccountSharedData::new(0, 0, &Pubkey::new_unique());
641        let signer1_account = AccountSharedData::new(0, 0, &Pubkey::new_unique());
642        let keys = vec![
643            (config_address, false),
644            (signer0_pubkey, true),
645            (signer1_pubkey, true),
646        ];
647        let (config_keypair, config_account) = create_config_account(keys.clone());
648        let config_pubkey = config_keypair.pubkey();
649        let my_config = MyConfig::new(42);
650
651        let instruction = config_instruction::store(&config_pubkey, true, keys, &my_config);
652        let accounts = process_instruction(
653            &instruction.data,
654            vec![
655                (config_pubkey, config_account),
656                (signer0_pubkey, signer0_account.clone()),
657                (signer1_pubkey, signer1_account),
658            ],
659            vec![
660                AccountMeta {
661                    pubkey: config_pubkey,
662                    is_signer: true,
663                    is_writable: true,
664                },
665                AccountMeta {
666                    pubkey: signer0_pubkey,
667                    is_signer: true,
668                    is_writable: false,
669                },
670                AccountMeta {
671                    pubkey: signer1_pubkey,
672                    is_signer: true,
673                    is_writable: false,
674                },
675            ],
676            Ok(()),
677        );
678
679        // Attempt update with duplicate signer inputs
680        let new_config = MyConfig::new(84);
681        let dupe_keys = vec![
682            (config_address, false),
683            (signer0_pubkey, true),
684            (signer0_pubkey, true),
685        ];
686        let instruction = config_instruction::store(&config_pubkey, false, dupe_keys, &new_config);
687        process_instruction(
688            &instruction.data,
689            vec![
690                (config_pubkey, accounts[0].clone()),
691                (signer0_pubkey, signer0_account),
692            ],
693            vec![
694                AccountMeta {
695                    pubkey: config_pubkey,
696                    is_signer: true,
697                    is_writable: true,
698                },
699                AccountMeta {
700                    pubkey: signer0_pubkey,
701                    is_signer: true,
702                    is_writable: false,
703                },
704                AccountMeta {
705                    pubkey: signer0_pubkey,
706                    is_signer: true,
707                    is_writable: false,
708                },
709            ],
710            Err(InstructionError::InvalidArgument),
711        );
712    }
713
714    #[test]
715    fn test_config_updates_requiring_config() {
716        solana_logger::setup();
717        let pubkey = Pubkey::new_unique();
718        let signer0_pubkey = Pubkey::new_unique();
719        let signer0_account = AccountSharedData::new(0, 0, &Pubkey::new_unique());
720        let keys = vec![
721            (pubkey, false),
722            (signer0_pubkey, true),
723            (signer0_pubkey, true),
724        ]; // Dummy keys for account sizing
725        let (config_keypair, config_account) = create_config_account(keys);
726        let config_pubkey = config_keypair.pubkey();
727        let my_config = MyConfig::new(42);
728        let keys = vec![
729            (pubkey, false),
730            (signer0_pubkey, true),
731            (config_keypair.pubkey(), true),
732        ];
733
734        let instruction = config_instruction::store(&config_pubkey, true, keys.clone(), &my_config);
735        let accounts = process_instruction(
736            &instruction.data,
737            vec![
738                (config_pubkey, config_account),
739                (signer0_pubkey, signer0_account.clone()),
740            ],
741            vec![
742                AccountMeta {
743                    pubkey: config_pubkey,
744                    is_signer: true,
745                    is_writable: true,
746                },
747                AccountMeta {
748                    pubkey: signer0_pubkey,
749                    is_signer: true,
750                    is_writable: false,
751                },
752            ],
753            Ok(()),
754        );
755
756        // Update with expected signatures
757        let new_config = MyConfig::new(84);
758        let instruction =
759            config_instruction::store(&config_pubkey, true, keys.clone(), &new_config);
760        let accounts = process_instruction(
761            &instruction.data,
762            vec![
763                (config_pubkey, accounts[0].clone()),
764                (signer0_pubkey, signer0_account),
765            ],
766            vec![
767                AccountMeta {
768                    pubkey: config_pubkey,
769                    is_signer: true,
770                    is_writable: true,
771                },
772                AccountMeta {
773                    pubkey: signer0_pubkey,
774                    is_signer: true,
775                    is_writable: false,
776                },
777            ],
778            Ok(()),
779        );
780        let meta_data: ConfigKeys = deserialize(accounts[0].data()).unwrap();
781        assert_eq!(meta_data.keys, keys);
782        assert_eq!(
783            new_config,
784            MyConfig::deserialize(get_config_data(accounts[0].data()).unwrap()).unwrap()
785        );
786
787        // Attempt update with incomplete signatures
788        let keys = vec![(pubkey, false), (config_keypair.pubkey(), true)];
789        let instruction = config_instruction::store(&config_pubkey, true, keys, &my_config);
790        process_instruction(
791            &instruction.data,
792            vec![(config_pubkey, accounts[0].clone())],
793            vec![AccountMeta {
794                pubkey: config_pubkey,
795                is_signer: true,
796                is_writable: true,
797            }],
798            Err(InstructionError::MissingRequiredSignature),
799        );
800    }
801
802    #[test]
803    fn test_config_initialize_no_panic() {
804        let from_pubkey = Pubkey::new_unique();
805        let config_pubkey = Pubkey::new_unique();
806        let (_, _config_account) = create_config_account(vec![]);
807        let instructions =
808            config_instruction::create_account::<MyConfig>(&from_pubkey, &config_pubkey, 1, vec![]);
809        process_instruction(
810            &instructions[1].data,
811            Vec::new(),
812            Vec::new(),
813            Err(InstructionError::NotEnoughAccountKeys),
814        );
815    }
816
817    #[test]
818    fn test_config_bad_owner() {
819        let from_pubkey = Pubkey::new_unique();
820        let config_pubkey = Pubkey::new_unique();
821        let new_config = MyConfig::new(84);
822        let signer0_pubkey = Pubkey::new_unique();
823        let signer0_account = AccountSharedData::new(0, 0, &Pubkey::new_unique());
824        let config_account = AccountSharedData::new(0, 0, &Pubkey::new_unique());
825        let (_, _config_account) = create_config_account(vec![]);
826        let keys = vec![
827            (from_pubkey, false),
828            (signer0_pubkey, true),
829            (config_pubkey, true),
830        ];
831
832        let instruction = config_instruction::store(&config_pubkey, true, keys, &new_config);
833        process_instruction(
834            &instruction.data,
835            vec![
836                (config_pubkey, config_account),
837                (signer0_pubkey, signer0_account),
838            ],
839            vec![
840                AccountMeta {
841                    pubkey: config_pubkey,
842                    is_signer: true,
843                    is_writable: true,
844                },
845                AccountMeta {
846                    pubkey: signer0_pubkey,
847                    is_signer: true,
848                    is_writable: false,
849                },
850            ],
851            Err(InstructionError::InvalidAccountOwner),
852        );
853    }
854}