diff --git a/program/benches/compute_units.md b/program/benches/compute_units.md index b95870b..23e6cd8 100644 --- a/program/benches/compute_units.md +++ b/program/benches/compute_units.md @@ -1,3 +1,44 @@ +#### Compute Units: 2024-11-01 15:31:11.543055 UTC + +| Name | CUs | Delta | +|------|------|-------| +| config_small_init_0_keys | 580 | -4 | +| config_small_init_1_keys | 1211 | -4 | +| config_small_init_5_keys | 2830 | -4 | +| config_small_init_10_keys | 4900 | -4 | +| config_small_init_25_keys | 11742 | -4 | +| config_small_init_37_keys | 16745 | -4 | +| config_small_store_0_keys | 580 | -4 | +| config_small_store_1_keys | 1465 | -4 | +| config_small_store_5_keys | 4000 | -4 | +| config_small_store_10_keys | 7215 | -4 | +| config_small_store_25_keys | 17492 | -4 | +| config_small_store_37_keys | 25243 | -4 | +| config_medium_init_0_keys | 571 | -4 | +| config_medium_init_1_keys | 1158 | -4 | +| config_medium_init_5_keys | 2830 | -4 | +| config_medium_init_10_keys | 4900 | -4 | +| config_medium_init_25_keys | 11742 | -4 | +| config_medium_init_37_keys | 16745 | -4 | +| config_medium_store_0_keys | 571 | -4 | +| config_medium_store_1_keys | 1412 | -4 | +| config_medium_store_5_keys | 4000 | -4 | +| config_medium_store_10_keys | 7215 | -4 | +| config_medium_store_25_keys | 17492 | -4 | +| config_medium_store_37_keys | 25243 | -4 | +| config_large_init_0_keys | 692 | -4 | +| config_large_init_1_keys | 1279 | -4 | +| config_large_init_5_keys | 2951 | -4 | +| config_large_init_10_keys | 5022 | -4 | +| config_large_init_25_keys | 11866 | -4 | +| config_large_init_37_keys | 16870 | -4 | +| config_large_store_0_keys | 692 | -4 | +| config_large_store_1_keys | 1533 | -4 | +| config_large_store_5_keys | 4121 | -4 | +| config_large_store_10_keys | 7337 | -4 | +| config_large_store_25_keys | 17616 | -4 | +| config_large_store_37_keys | 25368 | -4 | + #### Compute Units: 2024-10-31 17:22:37.667457 UTC | Name | CUs | Delta | diff --git a/program/src/processor.rs b/program/src/processor.rs index c25c31a..0ee100f 100644 --- a/program/src/processor.rs +++ b/program/src/processor.rs @@ -3,10 +3,7 @@ use { crate::state::ConfigKeys, solana_program::{ - account_info::{next_account_info, AccountInfo}, - entrypoint::ProgramResult, - msg, - program_error::ProgramError, + account_info::AccountInfo, entrypoint::ProgramResult, msg, program_error::ProgramError, pubkey::Pubkey, }, std::collections::BTreeSet, @@ -56,8 +53,7 @@ fn safe_deserialize_config_keys(input: &[u8]) -> Result ProgramResult { let key_list = safe_deserialize_config_keys(input)?; - let mut accounts_iter = accounts.iter(); - let config_account = next_account_info(&mut accounts_iter)?; + let config_account = accounts.first().ok_or(ProgramError::NotEnoughAccountKeys)?; if config_account.owner != program_id { msg!("Config account is not owned by the config program"); @@ -89,7 +85,7 @@ pub fn process(program_id: &Pubkey, accounts: &[AccountInfo], input: &[u8]) -> P for (signer, _) in key_list.keys.iter().filter(|(_, is_signer)| *is_signer) { counter = counter.saturating_add(1); if signer != config_account.key { - let signer_account = next_account_info(&mut accounts_iter).map_err(|_| { + let signer_account = accounts.get(counter).ok_or_else(|| { msg!("account {:?} is not in account list", signer); ProgramError::MissingRequiredSignature })?; diff --git a/program/tests/functional.rs b/program/tests/functional.rs index 89c89a4..1631bb7 100644 --- a/program/tests/functional.rs +++ b/program/tests/functional.rs @@ -83,7 +83,7 @@ fn test_process_create_ok() { &[(config, config_account)], &[ Check::success(), - Check::compute_units(584), + Check::compute_units(580), Check::account(&config) .data( &bincode::serialize(&(ConfigKeys { keys: vec![] }, MyConfig::default())) @@ -111,7 +111,7 @@ fn test_process_store_ok() { &[(config, config_account)], &[ Check::success(), - Check::compute_units(584), + Check::compute_units(580), Check::account(&config) .data(&bincode::serialize(&(ConfigKeys { keys }, my_config)).unwrap()) .build(), @@ -186,7 +186,7 @@ fn test_process_store_with_additional_signers() { ], &[ Check::success(), - Check::compute_units(3_253), + Check::compute_units(3_234), Check::account(&config) .data(&bincode::serialize(&(ConfigKeys { keys }, my_config)).unwrap()) .build(), @@ -262,6 +262,55 @@ fn test_process_store_with_bad_additional_signer() { ); } +#[test] +fn test_store_requiring_config() { + let mollusk = setup(); + + let config = Pubkey::new_unique(); + + // New keys contains the config account, as well as another signer. + let signer = Pubkey::new_unique(); + let new_keys = vec![(config, true), (signer, true)]; + let my_config = MyConfig::new(42); + + let config_account = { + // Allocate enough space for they `new_keys`, but leave the account + // uninitalized. + let space = get_config_space(new_keys.len()); + let lamports = mollusk.sysvars.rent.minimum_balance(space); + AccountSharedData::new(lamports, space, &solana_config_program::id()) + }; + + let mut instruction = config_instruction::store(&config, true, new_keys, &my_config); + mollusk.process_and_validate_instruction( + &instruction, + &[ + (config, config_account.clone()), + (signer, AccountSharedData::default()), + ], + &[Check::err(ProgramError::MissingRequiredSignature)], + ); + + // This is kind of strange, since the `store` helper was taken directly + // from the builtin crate, and it's designed to only add the config account + // once, even if it's a signer. + // However, if you include it in the instruction twice, the loop-counter + // mechanism of the processor actually works... + instruction.accounts = vec![ + AccountMeta::new(config, true), + AccountMeta::new(config, true), + AccountMeta::new(signer, true), + ]; + mollusk.process_and_validate_instruction( + &instruction, + &[ + (config, config_account), + (signer, AccountSharedData::default()), + ], + &[Check::success()], + ); +} + #[test] fn test_config_updates() { let mollusk = setup(); @@ -285,7 +334,7 @@ fn test_config_updates() { (signer0, AccountSharedData::default()), (signer1, AccountSharedData::default()), ], - &[Check::success(), Check::compute_units(3_253)], + &[Check::success(), Check::compute_units(3_234)], ); // Use this for next invoke. @@ -303,7 +352,7 @@ fn test_config_updates() { ], &[ Check::success(), - Check::compute_units(3_254), + Check::compute_units(3_235), Check::account(&config) .data(&bincode::serialize(&(ConfigKeys { keys }, new_config)).unwrap()) .build(), @@ -314,6 +363,22 @@ fn test_config_updates() { let updated_config_account = result.get_account(&config).unwrap(); // Attempt update with incomplete signatures. + let keys = vec![ + (pubkey, false), + (signer1, true), // Missing signer0. + ]; + let instruction = config_instruction::store(&config, false, keys, &my_config); + mollusk.process_and_validate_instruction( + &instruction, + &[ + (config, updated_config_account.clone()), + // Missing signer0. + (signer1, AccountSharedData::default()), + ], + &[Check::err(ProgramError::MissingRequiredSignature)], + ); + + // Do it again, this time missing signer1. let keys = vec![ (pubkey, false), (signer0, true), // Missing signer1. @@ -323,7 +388,8 @@ fn test_config_updates() { &instruction, &[ (config, updated_config_account.clone()), - (signer0, AccountSharedData::default()), // Missing signer1. + (signer0, AccountSharedData::default()), + // Missing signer1. ], &[Check::err(ProgramError::MissingRequiredSignature)], ); @@ -399,7 +465,7 @@ fn test_config_update_contains_duplicates_fails() { (signer0, AccountSharedData::default()), (signer1, AccountSharedData::default()), ], - &[Check::success(), Check::compute_units(3_253)], + &[Check::success(), Check::compute_units(3_234)], ); // Attempt update with duplicate signer inputs. @@ -443,7 +509,7 @@ fn test_config_updates_requiring_config() { ], &[ Check::success(), - Check::compute_units(3_352), + Check::compute_units(3_330), Check::account(&config) .data(&bincode::serialize(&(ConfigKeys { keys: keys.clone() }, my_config)).unwrap()) .build(), @@ -464,7 +530,7 @@ fn test_config_updates_requiring_config() { ], &[ Check::success(), - Check::compute_units(3_352), + Check::compute_units(3_330), Check::account(&config) .data(&bincode::serialize(&(ConfigKeys { keys }, new_config)).unwrap()) .build(), @@ -558,7 +624,7 @@ fn test_maximum_keys_input() { let result = mollusk.process_and_validate_instruction( &instruction, &[(config, config_account)], - &[Check::success(), Check::compute_units(25_247)], + &[Check::success(), Check::compute_units(25_243)], ); // Use this for next invoke. @@ -571,7 +637,7 @@ fn test_maximum_keys_input() { let result = mollusk.process_and_validate_instruction( &instruction, &[(config, updated_config_account)], - &[Check::success(), Check::compute_units(25_247)], + &[Check::success(), Check::compute_units(25_243)], ); // Use this for next invoke.