1use 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 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 !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 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]; 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; 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 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 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 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 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 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 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 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 ]; 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 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 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}