solana_config_program/
config_processor.rs

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