Skip to content

Commit f939197

Browse files
committed
draft: loop-counter bug
1 parent a7fef8e commit f939197

File tree

2 files changed

+70
-8
lines changed

2 files changed

+70
-8
lines changed

program/src/processor.rs

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,7 @@
33
use {
44
crate::state::ConfigKeys,
55
solana_program::{
6-
account_info::{next_account_info, AccountInfo},
7-
entrypoint::ProgramResult,
8-
msg,
9-
program_error::ProgramError,
6+
account_info::AccountInfo, entrypoint::ProgramResult, msg, program_error::ProgramError,
107
pubkey::Pubkey,
118
},
129
std::collections::BTreeSet,
@@ -28,8 +25,7 @@ where
2825
pub fn process(program_id: &Pubkey, accounts: &[AccountInfo], input: &[u8]) -> ProgramResult {
2926
let key_list: ConfigKeys = limited_deserialize(input)?;
3027

31-
let mut accounts_iter = accounts.iter();
32-
let config_account = next_account_info(&mut accounts_iter)?;
28+
let config_account = accounts.first().ok_or(ProgramError::NotEnoughAccountKeys)?;
3329

3430
if config_account.owner != program_id {
3531
msg!("Config account is not owned by the config program");
@@ -61,7 +57,7 @@ pub fn process(program_id: &Pubkey, accounts: &[AccountInfo], input: &[u8]) -> P
6157
for (signer, _) in key_list.keys.iter().filter(|(_, is_signer)| *is_signer) {
6258
counter = counter.saturating_add(1);
6359
if signer != config_account.key {
64-
let signer_account = next_account_info(&mut accounts_iter).map_err(|_| {
60+
let signer_account = accounts.get(counter).ok_or({
6561
msg!("account {:?} is not in account list", signer);
6662
ProgramError::MissingRequiredSignature
6763
})?;

program/tests/functional.rs

Lines changed: 67 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -314,6 +314,22 @@ fn test_config_updates() {
314314
let updated_config_account = result.get_account(&config).unwrap();
315315

316316
// Attempt update with incomplete signatures.
317+
let keys = vec![
318+
(pubkey, false),
319+
(signer1, true), // Missing signer0.
320+
];
321+
let instruction = config_instruction::store(&config, false, keys, &my_config);
322+
mollusk.process_and_validate_instruction(
323+
&instruction,
324+
&[
325+
(config, updated_config_account.clone()),
326+
// Missing signer0.
327+
(signer1, AccountSharedData::default()),
328+
],
329+
&[Check::err(ProgramError::MissingRequiredSignature)],
330+
);
331+
332+
// Do it again, this time missing signer1.
317333
let keys = vec![
318334
(pubkey, false),
319335
(signer0, true), // Missing signer1.
@@ -323,7 +339,8 @@ fn test_config_updates() {
323339
&instruction,
324340
&[
325341
(config, updated_config_account.clone()),
326-
(signer0, AccountSharedData::default()), // Missing signer1.
342+
(signer0, AccountSharedData::default()),
343+
// Missing signer1.
327344
],
328345
&[Check::err(ProgramError::MissingRequiredSignature)],
329346
);
@@ -346,6 +363,55 @@ fn test_config_updates() {
346363
);
347364
}
348365

366+
#[test]
367+
fn test_config_updates_empty_current_keys() {
368+
let mollusk = setup();
369+
370+
let config = Pubkey::new_unique();
371+
372+
// New keys contains the config account, as well as another signer.
373+
let signer = Pubkey::new_unique();
374+
let new_keys = vec![(config, true), (signer, true)];
375+
let my_config = MyConfig::new(42);
376+
377+
let config_account = {
378+
// Allocate enough space for they `new_keys`, but leave the account
379+
// uninitalized.
380+
let space = get_config_space(new_keys.len());
381+
let lamports = mollusk.sysvars.rent.minimum_balance(space);
382+
AccountSharedData::new(lamports, space, &solana_config_program::id())
383+
};
384+
385+
let mut instruction = config_instruction::store(&config, true, new_keys, &my_config);
386+
mollusk.process_and_validate_instruction(
387+
&instruction,
388+
&[
389+
(config, config_account.clone()),
390+
(signer, AccountSharedData::default()),
391+
],
392+
&[Check::err(ProgramError::MissingRequiredSignature)],
393+
);
394+
395+
// This is kind of strange, since the `store` helper was taken directly
396+
// from the builtin crate, and it's designed to only add the config account
397+
// once, even if it's a signer.
398+
// However, if you include it in the instruction twice, the loop-counter
399+
// mechanism of the processor actually works...
400+
instruction.accounts = vec![
401+
AccountMeta::new(config, true),
402+
AccountMeta::new(config, true),
403+
AccountMeta::new(signer, true),
404+
];
405+
mollusk.process_and_validate_instruction(
406+
&instruction,
407+
&[
408+
(config, config_account),
409+
(signer, AccountSharedData::default()),
410+
],
411+
&[Check::success()],
412+
);
413+
}
414+
349415
#[test]
350416
fn test_config_initialize_contains_duplicates_fails() {
351417
let mollusk = setup();

0 commit comments

Comments
 (0)