gemachain_config_program/
config_processor.rs

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