Skip to main content

rialo_s_config_program/
config_processor.rs

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