programs/config: Disallow duplicate signers
This commit is contained in:
@ -11,6 +11,7 @@ use solana_sdk::{
|
|||||||
program_utils::limited_deserialize,
|
program_utils::limited_deserialize,
|
||||||
pubkey::Pubkey,
|
pubkey::Pubkey,
|
||||||
};
|
};
|
||||||
|
use std::collections::BTreeSet;
|
||||||
|
|
||||||
pub fn process_instruction(
|
pub fn process_instruction(
|
||||||
_program_id: &Pubkey,
|
_program_id: &Pubkey,
|
||||||
@ -101,6 +102,15 @@ pub fn process_instruction(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if invoke_context.is_feature_active(&feature_set::dedupe_config_program_signers::id()) {
|
||||||
|
let total_new_keys = key_list.keys.len();
|
||||||
|
let unique_new_keys = key_list.keys.into_iter().collect::<BTreeSet<_>>();
|
||||||
|
if unique_new_keys.len() != total_new_keys {
|
||||||
|
ic_msg!(invoke_context, "new config contains duplicate keys");
|
||||||
|
return Err(InstructionError::InvalidArgument);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Check for Config data signers not present in incoming account update
|
// Check for Config data signers not present in incoming account update
|
||||||
if current_signer_keys.len() > counter {
|
if current_signer_keys.len() > counter {
|
||||||
ic_msg!(
|
ic_msg!(
|
||||||
@ -505,6 +515,96 @@ mod tests {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_config_initialize_contains_duplicates_fails() {
|
||||||
|
solana_logger::setup();
|
||||||
|
let config_address = Pubkey::new_unique();
|
||||||
|
let signer0_pubkey = Pubkey::new_unique();
|
||||||
|
let signer0_account = RefCell::new(AccountSharedData::default());
|
||||||
|
let keys = vec![
|
||||||
|
(config_address, false),
|
||||||
|
(signer0_pubkey, true),
|
||||||
|
(signer0_pubkey, true),
|
||||||
|
];
|
||||||
|
let (config_keypair, config_account) = create_config_account(keys.clone());
|
||||||
|
let config_pubkey = config_keypair.pubkey();
|
||||||
|
let my_config = MyConfig::new(42);
|
||||||
|
|
||||||
|
// Attempt initialization with duplicate signer inputs
|
||||||
|
let instruction = config_instruction::store(&config_pubkey, true, keys, &my_config);
|
||||||
|
let accounts = vec![
|
||||||
|
(true, false, &config_pubkey, &config_account),
|
||||||
|
(true, false, &signer0_pubkey, &signer0_account),
|
||||||
|
(true, false, &signer0_pubkey, &signer0_account),
|
||||||
|
];
|
||||||
|
let keyed_accounts = create_keyed_accounts_unified(&accounts);
|
||||||
|
assert_eq!(
|
||||||
|
process_instruction(
|
||||||
|
&id(),
|
||||||
|
&instruction.data,
|
||||||
|
&mut MockInvokeContext::new(keyed_accounts)
|
||||||
|
),
|
||||||
|
Err(InstructionError::InvalidArgument),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_config_update_contains_duplicates_fails() {
|
||||||
|
solana_logger::setup();
|
||||||
|
let config_address = Pubkey::new_unique();
|
||||||
|
let signer0_pubkey = Pubkey::new_unique();
|
||||||
|
let signer1_pubkey = Pubkey::new_unique();
|
||||||
|
let signer0_account = RefCell::new(AccountSharedData::default());
|
||||||
|
let signer1_account = RefCell::new(AccountSharedData::default());
|
||||||
|
let keys = vec![
|
||||||
|
(config_address, false),
|
||||||
|
(signer0_pubkey, true),
|
||||||
|
(signer1_pubkey, true),
|
||||||
|
];
|
||||||
|
let (config_keypair, config_account) = create_config_account(keys.clone());
|
||||||
|
let config_pubkey = config_keypair.pubkey();
|
||||||
|
let my_config = MyConfig::new(42);
|
||||||
|
|
||||||
|
let instruction = config_instruction::store(&config_pubkey, true, keys, &my_config);
|
||||||
|
let accounts = vec![
|
||||||
|
(true, false, &config_pubkey, &config_account),
|
||||||
|
(true, false, &signer0_pubkey, &signer0_account),
|
||||||
|
(true, false, &signer1_pubkey, &signer1_account),
|
||||||
|
];
|
||||||
|
let keyed_accounts = create_keyed_accounts_unified(&accounts);
|
||||||
|
assert_eq!(
|
||||||
|
process_instruction(
|
||||||
|
&id(),
|
||||||
|
&instruction.data,
|
||||||
|
&mut MockInvokeContext::new(keyed_accounts)
|
||||||
|
),
|
||||||
|
Ok(()),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Attempt update with duplicate signer inputs
|
||||||
|
let new_config = MyConfig::new(84);
|
||||||
|
let dupe_keys = vec![
|
||||||
|
(config_address, false),
|
||||||
|
(signer0_pubkey, true),
|
||||||
|
(signer0_pubkey, true),
|
||||||
|
];
|
||||||
|
let instruction = config_instruction::store(&config_pubkey, false, dupe_keys, &new_config);
|
||||||
|
let accounts = vec![
|
||||||
|
(false, false, &config_pubkey, &config_account),
|
||||||
|
(true, false, &signer0_pubkey, &signer0_account),
|
||||||
|
(true, false, &signer0_pubkey, &signer0_account),
|
||||||
|
];
|
||||||
|
let keyed_accounts = create_keyed_accounts_unified(&accounts);
|
||||||
|
assert_eq!(
|
||||||
|
process_instruction(
|
||||||
|
&id(),
|
||||||
|
&instruction.data,
|
||||||
|
&mut MockInvokeContext::new(keyed_accounts)
|
||||||
|
),
|
||||||
|
Err(InstructionError::InvalidArgument),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_config_updates_requiring_config() {
|
fn test_config_updates_requiring_config() {
|
||||||
solana_logger::setup();
|
solana_logger::setup();
|
||||||
|
@ -150,6 +150,10 @@ pub mod memory_ops_syscalls {
|
|||||||
solana_sdk::declare_id!("ENQi37wsVhTvFz2gUiZAAbqFEWGN2jwFsqdEDTE8A4MU");
|
solana_sdk::declare_id!("ENQi37wsVhTvFz2gUiZAAbqFEWGN2jwFsqdEDTE8A4MU");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub mod dedupe_config_program_signers {
|
||||||
|
solana_sdk::declare_id!("8kEuAshXLsgkUEdcFVLqrjCGGHVWFW99ZZpxvAzzMtBp");
|
||||||
|
}
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
/// Map of feature identifiers to user-visible description
|
/// Map of feature identifiers to user-visible description
|
||||||
pub static ref FEATURE_NAMES: HashMap<Pubkey, &'static str> = [
|
pub static ref FEATURE_NAMES: HashMap<Pubkey, &'static str> = [
|
||||||
@ -186,6 +190,7 @@ lazy_static! {
|
|||||||
(stake_program_v4::id(), "solana_stake_program v4"),
|
(stake_program_v4::id(), "solana_stake_program v4"),
|
||||||
(system_transfer_zero_check::id(), "perform all checks for transfers of 0 lamports"),
|
(system_transfer_zero_check::id(), "perform all checks for transfers of 0 lamports"),
|
||||||
(memory_ops_syscalls::id(), "add syscalls for memory operations"),
|
(memory_ops_syscalls::id(), "add syscalls for memory operations"),
|
||||||
|
(dedupe_config_program_signers::id(), "dedupe config program signers"),
|
||||||
/*************** ADD NEW FEATURES HERE ***************/
|
/*************** ADD NEW FEATURES HERE ***************/
|
||||||
]
|
]
|
||||||
.iter()
|
.iter()
|
||||||
|
Reference in New Issue
Block a user