solana_config_program/
config_processor.rs

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