clone_solana_config_program/
config_processor.rs

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