diff --git a/Cargo.lock b/Cargo.lock index a48cc8653c..6b204465b7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3284,8 +3284,6 @@ dependencies = [ "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.98 (registry+https://github.com/rust-lang/crates.io-index)", "serde_derive 1.0.98 (registry+https://github.com/rust-lang/crates.io-index)", - "solana-logger 0.18.0-pre1", - "solana-runtime 0.18.0-pre1", "solana-sdk 0.18.0-pre1", ] @@ -3299,6 +3297,22 @@ dependencies = [ "solana-sdk 0.18.0-pre1", ] +[[package]] +name = "solana-config-tests" +version = "0.18.0-pre1" +dependencies = [ + "assert_matches 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "bincode 1.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.98 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.98 (registry+https://github.com/rust-lang/crates.io-index)", + "solana-config-api 0.18.0-pre1", + "solana-config-program 0.18.0-pre1", + "solana-logger 0.18.0-pre1", + "solana-runtime 0.18.0-pre1", + "solana-sdk 0.18.0-pre1", +] + [[package]] name = "solana-drone" version = "0.18.0-pre1" diff --git a/Cargo.toml b/Cargo.toml index 54299e11db..f4a7a58ba6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,6 +28,7 @@ members = [ "programs/budget_program", "programs/config_api", "programs/config_program", + "programs/config_tests", "programs/exchange_api", "programs/exchange_program", "programs/failure_program", diff --git a/install/src/command.rs b/install/src/command.rs index ad0aa8325c..43e003b174 100644 --- a/install/src/command.rs +++ b/install/src/command.rs @@ -6,7 +6,7 @@ use console::{style, Emoji}; use indicatif::{ProgressBar, ProgressStyle}; use sha2::{Digest, Sha256}; use solana_client::rpc_client::RpcClient; -use solana_config_api::config_instruction::{self, ConfigKeys}; +use solana_config_api::{config_instruction, get_config_data}; use solana_sdk::message::Message; use solana_sdk::pubkey::Pubkey; use solana_sdk::signature::{read_keypair, Keypair, KeypairUtil, Signable}; @@ -200,7 +200,7 @@ fn new_update_manifest( 1, // lamports vec![], // additional keys ); - let mut transaction = Transaction::new_unsigned_instructions(vec![new_account]); + let mut transaction = Transaction::new_unsigned_instructions(new_account); transaction.sign(&[from_keypair], recent_blockhash); rpc_client.send_and_confirm_transaction(&mut transaction, &[from_keypair])?; @@ -236,13 +236,14 @@ fn get_update_manifest( rpc_client: &RpcClient, update_manifest_pubkey: &Pubkey, ) -> Result { - let mut data = rpc_client + let data = rpc_client .get_account_data(update_manifest_pubkey) .map_err(|err| format!("Unable to fetch update manifest: {}", err))?; - let data = data.split_off(ConfigKeys::serialized_size(vec![])); + let config_data = get_config_data(&data) + .map_err(|err| format!("Unable to get at config_data to update manifest: {}", err))?; let signed_update_manifest = - SignedUpdateManifest::deserialize(update_manifest_pubkey, &data) + SignedUpdateManifest::deserialize(update_manifest_pubkey, config_data) .map_err(|err| format!("Unable to deserialize update manifest: {}", err))?; Ok(signed_update_manifest.manifest) } diff --git a/programs/config_api/Cargo.toml b/programs/config_api/Cargo.toml index e2e5c1a941..4bb5f9f3e3 100644 --- a/programs/config_api/Cargo.toml +++ b/programs/config_api/Cargo.toml @@ -13,11 +13,8 @@ bincode = "1.1.4" log = "0.4.8" serde = "1.0.98" serde_derive = "1.0.98" -solana-logger = { path = "../../logger", version = "0.18.0-pre1" } solana-sdk = { path = "../../sdk", version = "0.18.0-pre1" } -[dev-dependencies] -solana-runtime = { path = "../../runtime", version = "0.18.0-pre1" } [lib] crate-type = ["lib"] diff --git a/programs/config_api/src/config_instruction.rs b/programs/config_api/src/config_instruction.rs index bae5644bf7..f6ac6b5508 100644 --- a/programs/config_api/src/config_instruction.rs +++ b/programs/config_api/src/config_instruction.rs @@ -1,27 +1,15 @@ use crate::id; -use crate::ConfigState; -use bincode::serialize; -use serde_derive::{Deserialize, Serialize}; -use solana_sdk::instruction::{AccountMeta, Instruction}; -use solana_sdk::pubkey::Pubkey; -use solana_sdk::short_vec; -use solana_sdk::system_instruction; +use crate::{ConfigKeys, ConfigState}; +use solana_sdk::{ + instruction::{AccountMeta, Instruction}, + pubkey::Pubkey, + system_instruction, +}; -/// A collection of keys to be stored in Config account data. -#[derive(Debug, Default, Deserialize, Serialize)] -pub struct ConfigKeys { - // Each key tuple comprises a unique `Pubkey` identifier, - // and `bool` whether that key is a signer of the data - #[serde(with = "short_vec")] - pub keys: Vec<(Pubkey, bool)>, -} - -impl ConfigKeys { - pub fn serialized_size(keys: Vec<(Pubkey, bool)>) -> usize { - serialize(&ConfigKeys { keys }) - .unwrap_or_else(|_| vec![]) - .len() - } +fn initialize_account(config_pubkey: &Pubkey) -> Instruction { + let account_metas = vec![AccountMeta::new(*config_pubkey, true)]; + let account_data = (ConfigKeys { keys: vec![] }, T::default()); + Instruction::new(id(), &account_data, account_metas) } /// Create a new, empty configuration account @@ -30,15 +18,18 @@ pub fn create_account( config_account_pubkey: &Pubkey, lamports: u64, keys: Vec<(Pubkey, bool)>, -) -> Instruction { - let space = T::max_space() + ConfigKeys::serialized_size(keys) as u64; - system_instruction::create_account( - from_account_pubkey, - config_account_pubkey, - lamports, - space, - &id(), - ) +) -> Vec { + let space = T::max_space() + ConfigKeys::serialized_size(keys); + vec![ + system_instruction::create_account( + from_account_pubkey, + config_account_pubkey, + lamports, + space, + &id(), + ), + initialize_account::(config_account_pubkey), + ] } /// Store new data in a configuration account diff --git a/programs/config_api/src/config_processor.rs b/programs/config_api/src/config_processor.rs index 04dadb711e..1cadbbe90f 100644 --- a/programs/config_api/src/config_processor.rs +++ b/programs/config_api/src/config_processor.rs @@ -1,6 +1,6 @@ //! Config program -use crate::config_instruction::ConfigKeys; +use crate::ConfigKeys; use bincode::deserialize; use log::*; use solana_sdk::account::KeyedAccount; @@ -100,380 +100,4 @@ pub fn process_instruction( } #[cfg(test)] -mod tests { - use super::*; - use crate::{config_instruction, id, ConfigState}; - use bincode::{deserialize, serialized_size}; - use serde_derive::{Deserialize, Serialize}; - use solana_runtime::bank::Bank; - use solana_runtime::bank_client::BankClient; - use solana_sdk::client::SyncClient; - use solana_sdk::genesis_block::create_genesis_block; - use solana_sdk::message::Message; - use solana_sdk::signature::{Keypair, KeypairUtil}; - use solana_sdk::system_instruction; - - #[derive(Serialize, Deserialize, Default, Debug, PartialEq)] - struct MyConfig { - pub item: u64, - } - impl MyConfig { - pub fn new(item: u64) -> Self { - Self { item } - } - pub fn deserialize(input: &[u8]) -> Option { - deserialize(input).ok() - } - } - - impl ConfigState for MyConfig { - fn max_space() -> u64 { - serialized_size(&Self::default()).unwrap() - } - } - - fn create_bank(lamports: u64) -> (Bank, Keypair) { - let (genesis_block, mint_keypair) = create_genesis_block(lamports); - let mut bank = Bank::new(&genesis_block); - bank.add_instruction_processor(id(), process_instruction); - (bank, mint_keypair) - } - - fn create_config_account( - bank: Bank, - mint_keypair: &Keypair, - keys: Vec<(Pubkey, bool)>, - ) -> (BankClient, Keypair) { - let config_keypair = Keypair::new(); - let config_pubkey = config_keypair.pubkey(); - - let bank_client = BankClient::new(bank); - bank_client - .send_instruction( - mint_keypair, - config_instruction::create_account::( - &mint_keypair.pubkey(), - &config_pubkey, - 1, - keys, - ), - ) - .expect("new_account"); - - (bank_client, config_keypair) - } - - #[test] - fn test_process_create_ok() { - solana_logger::setup(); - let (bank, mint_keypair) = create_bank(10_000); - let (bank_client, config_keypair) = create_config_account(bank, &mint_keypair, vec![]); - let config_account_data = bank_client - .get_account_data(&config_keypair.pubkey()) - .unwrap() - .unwrap(); - assert_eq!( - MyConfig::default(), - MyConfig::deserialize(&config_account_data).unwrap() - ); - } - - #[test] - fn test_process_store_ok() { - solana_logger::setup(); - let (bank, mint_keypair) = create_bank(10_000); - let keys = vec![]; - let (bank_client, config_keypair) = - create_config_account(bank, &mint_keypair, keys.clone()); - let config_pubkey = config_keypair.pubkey(); - - let my_config = MyConfig::new(42); - - let instruction = config_instruction::store(&config_pubkey, true, keys.clone(), &my_config); - let message = Message::new_with_payer(vec![instruction], Some(&mint_keypair.pubkey())); - - bank_client - .send_message(&[&mint_keypair, &config_keypair], message) - .unwrap(); - - let config_account_data = bank_client - .get_account_data(&config_pubkey) - .unwrap() - .unwrap(); - let meta_length = ConfigKeys::serialized_size(keys); - let config_account_data = &config_account_data[meta_length..config_account_data.len()]; - assert_eq!( - my_config, - MyConfig::deserialize(&config_account_data).unwrap() - ); - } - - #[test] - fn test_process_store_fail_instruction_data_too_large() { - solana_logger::setup(); - let (bank, mint_keypair) = create_bank(10_000); - let (bank_client, config_keypair) = create_config_account(bank, &mint_keypair, vec![]); - let config_pubkey = config_keypair.pubkey(); - - let my_config = MyConfig::new(42); - - let mut instruction = config_instruction::store(&config_pubkey, true, vec![], &my_config); - instruction.data = vec![0; 123]; // <-- Replace data with a vector that's too large - let message = Message::new(vec![instruction]); - bank_client - .send_message(&[&config_keypair], message) - .unwrap_err(); - } - - #[test] - fn test_process_store_fail_account0_not_signer() { - solana_logger::setup(); - let (bank, mint_keypair) = create_bank(10_000); - let system_keypair = Keypair::new(); - let system_pubkey = system_keypair.pubkey(); - - bank.transfer(42, &mint_keypair, &system_pubkey).unwrap(); - let (bank_client, config_keypair) = create_config_account(bank, &mint_keypair, vec![]); - let config_pubkey = config_keypair.pubkey(); - - let transfer_instruction = - system_instruction::transfer(&system_pubkey, &Pubkey::new_rand(), 42); - let my_config = MyConfig::new(42); - let mut store_instruction = - config_instruction::store(&config_pubkey, true, vec![], &my_config); - store_instruction.accounts[0].is_signer = false; // <----- not a signer - - let message = Message::new(vec![transfer_instruction, store_instruction]); - bank_client - .send_message(&[&system_keypair], message) - .unwrap_err(); - } - - #[test] - fn test_process_store_with_additional_signers() { - solana_logger::setup(); - let (bank, mint_keypair) = create_bank(10_000); - let pubkey = Pubkey::new_rand(); - let signer0 = Keypair::new(); - let signer1 = Keypair::new(); - let keys = vec![ - (pubkey, false), - (signer0.pubkey(), true), - (signer1.pubkey(), true), - ]; - let (bank_client, config_keypair) = - create_config_account(bank, &mint_keypair, keys.clone()); - let config_pubkey = config_keypair.pubkey(); - - let my_config = MyConfig::new(42); - - let instruction = config_instruction::store(&config_pubkey, true, keys.clone(), &my_config); - let message = Message::new_with_payer(vec![instruction], Some(&mint_keypair.pubkey())); - - bank_client - .send_message( - &[&mint_keypair, &config_keypair, &signer0, &signer1], - message, - ) - .unwrap(); - - let config_account_data = bank_client - .get_account_data(&config_pubkey) - .unwrap() - .unwrap(); - let meta_length = ConfigKeys::serialized_size(keys.clone()); - let meta_data: ConfigKeys = deserialize(&config_account_data[0..meta_length]).unwrap(); - assert_eq!(meta_data.keys, keys); - let config_account_data = &config_account_data[meta_length..config_account_data.len()]; - assert_eq!( - my_config, - MyConfig::deserialize(&config_account_data).unwrap() - ); - } - - #[test] - fn test_process_store_without_config_signer() { - solana_logger::setup(); - let (bank, mint_keypair) = create_bank(10_000); - let pubkey = Pubkey::new_rand(); - let signer0 = Keypair::new(); - let keys = vec![(pubkey, false), (signer0.pubkey(), true)]; - let (bank_client, config_keypair) = - create_config_account(bank, &mint_keypair, keys.clone()); - let config_pubkey = config_keypair.pubkey(); - - let my_config = MyConfig::new(42); - - let instruction = - config_instruction::store(&config_pubkey, false, keys.clone(), &my_config); - let message = Message::new_with_payer(vec![instruction], Some(&mint_keypair.pubkey())); - - bank_client - .send_message(&[&mint_keypair, &signer0], message) - .unwrap_err(); - } - - #[test] - fn test_process_store_with_bad_additional_signer() { - solana_logger::setup(); - let (bank, mint_keypair) = create_bank(10_000); - let signer0 = Keypair::new(); - let signer1 = Keypair::new(); - let keys = vec![(signer0.pubkey(), true)]; - let (bank_client, config_keypair) = - create_config_account(bank, &mint_keypair, keys.clone()); - let config_pubkey = config_keypair.pubkey(); - - let my_config = MyConfig::new(42); - - // Config-data pubkey doesn't match signer - let instruction = config_instruction::store(&config_pubkey, true, keys.clone(), &my_config); - let mut message = - Message::new_with_payer(vec![instruction.clone()], Some(&mint_keypair.pubkey())); - message.account_keys[2] = signer1.pubkey(); - bank_client - .send_message(&[&mint_keypair, &config_keypair, &signer1], message) - .unwrap_err(); - - // Config-data pubkey not a signer - let mut message = Message::new_with_payer(vec![instruction], Some(&mint_keypair.pubkey())); - message.header.num_required_signatures = 2; - bank_client - .send_message(&[&mint_keypair, &config_keypair], message) - .unwrap_err(); - } - - #[test] - fn test_config_updates() { - solana_logger::setup(); - let (bank, mint_keypair) = create_bank(10_000); - let pubkey = Pubkey::new_rand(); - let signer0 = Keypair::new(); - let signer1 = Keypair::new(); - let keys = vec![ - (pubkey, false), - (signer0.pubkey(), true), - (signer1.pubkey(), true), - ]; - let (bank_client, config_keypair) = - create_config_account(bank, &mint_keypair, keys.clone()); - let config_pubkey = config_keypair.pubkey(); - - let my_config = MyConfig::new(42); - - let instruction = config_instruction::store(&config_pubkey, true, keys.clone(), &my_config); - let message = Message::new_with_payer(vec![instruction], Some(&mint_keypair.pubkey())); - - bank_client - .send_message( - &[&mint_keypair, &config_keypair, &signer0, &signer1], - message, - ) - .unwrap(); - - // Update with expected signatures - let new_config = MyConfig::new(84); - let instruction = - config_instruction::store(&config_pubkey, false, keys.clone(), &new_config); - let message = Message::new_with_payer(vec![instruction], Some(&mint_keypair.pubkey())); - bank_client - .send_message(&[&mint_keypair, &signer0, &signer1], message) - .unwrap(); - - let config_account_data = bank_client - .get_account_data(&config_pubkey) - .unwrap() - .unwrap(); - let meta_length = ConfigKeys::serialized_size(keys.clone()); - let meta_data: ConfigKeys = deserialize(&config_account_data[0..meta_length]).unwrap(); - assert_eq!(meta_data.keys, keys); - let config_account_data = &config_account_data[meta_length..config_account_data.len()]; - assert_eq!( - new_config, - MyConfig::deserialize(&config_account_data).unwrap() - ); - - // Attempt update with incomplete signatures - let keys = vec![(pubkey, false), (signer0.pubkey(), true)]; - let instruction = - config_instruction::store(&config_pubkey, false, keys.clone(), &my_config); - let message = Message::new_with_payer(vec![instruction], Some(&mint_keypair.pubkey())); - bank_client - .send_message(&[&mint_keypair, &signer0], message) - .unwrap_err(); - - // Attempt update with incorrect signatures - let signer2 = Keypair::new(); - let keys = vec![ - (pubkey, false), - (signer0.pubkey(), true), - (signer2.pubkey(), true), - ]; - let instruction = - config_instruction::store(&config_pubkey, false, keys.clone(), &my_config); - let message = Message::new_with_payer(vec![instruction], Some(&mint_keypair.pubkey())); - bank_client - .send_message(&[&mint_keypair, &signer0, &signer2], message) - .unwrap_err(); - } - - #[test] - fn test_config_updates_requiring_config() { - solana_logger::setup(); - let (bank, mint_keypair) = create_bank(10_000); - let pubkey = Pubkey::new_rand(); - let signer0 = Keypair::new(); - let keys = vec![ - (pubkey, false), - (signer0.pubkey(), true), - (signer0.pubkey(), true), - ]; // Dummy keys for account sizing - let (bank_client, config_keypair) = - create_config_account(bank, &mint_keypair, keys.clone()); - let config_pubkey = config_keypair.pubkey(); - let keys = vec![ - (pubkey, false), - (signer0.pubkey(), true), - (config_keypair.pubkey(), true), - ]; - - let my_config = MyConfig::new(42); - - let instruction = config_instruction::store(&config_pubkey, true, keys.clone(), &my_config); - let message = Message::new_with_payer(vec![instruction], Some(&mint_keypair.pubkey())); - - bank_client - .send_message(&[&mint_keypair, &config_keypair, &signer0], message) - .unwrap(); - - // Update with expected signatures - let new_config = MyConfig::new(84); - let instruction = - config_instruction::store(&config_pubkey, true, keys.clone(), &new_config); - let message = Message::new_with_payer(vec![instruction], Some(&mint_keypair.pubkey())); - bank_client - .send_message(&[&mint_keypair, &config_keypair, &signer0], message) - .unwrap(); - - let config_account_data = bank_client - .get_account_data(&config_pubkey) - .unwrap() - .unwrap(); - let meta_length = ConfigKeys::serialized_size(keys.clone()); - let meta_data: ConfigKeys = deserialize(&config_account_data[0..meta_length]).unwrap(); - assert_eq!(meta_data.keys, keys); - let config_account_data = &config_account_data[meta_length..config_account_data.len()]; - assert_eq!( - new_config, - MyConfig::deserialize(&config_account_data).unwrap() - ); - - // Attempt update with incomplete signatures - let keys = vec![(pubkey, false), (config_keypair.pubkey(), true)]; - let instruction = config_instruction::store(&config_pubkey, true, keys.clone(), &my_config); - let message = Message::new_with_payer(vec![instruction], Some(&mint_keypair.pubkey())); - bank_client - .send_message(&[&mint_keypair, &config_keypair], message) - .unwrap_err(); - } -} +mod tests {} diff --git a/programs/config_api/src/lib.rs b/programs/config_api/src/lib.rs index dfd1ec9a21..d0e6075b70 100644 --- a/programs/config_api/src/lib.rs +++ b/programs/config_api/src/lib.rs @@ -1,4 +1,6 @@ -use serde::Serialize; +use bincode::{deserialize, serialize, serialized_size}; +use serde_derive::{Deserialize, Serialize}; +use solana_sdk::{account::Account, pubkey::Pubkey, short_vec}; pub mod config_instruction; pub mod config_processor; @@ -13,7 +15,44 @@ solana_sdk::solana_name_id!( "Config1111111111111111111111111111111111111" ); -pub trait ConfigState: Serialize { +pub trait ConfigState: serde::Serialize + Default { /// Maximum space that the serialized representation will require fn max_space() -> u64; } + +/// A collection of keys to be stored in Config account data. +#[derive(Debug, Default, Deserialize, Serialize)] +pub struct ConfigKeys { + // Each key tuple comprises a unique `Pubkey` identifier, + // and `bool` whether that key is a signer of the data + #[serde(with = "short_vec")] + pub keys: Vec<(Pubkey, bool)>, +} + +impl ConfigKeys { + pub fn serialized_size(keys: Vec<(Pubkey, bool)>) -> u64 { + serialized_size(&ConfigKeys { keys }).unwrap() + } +} + +pub fn get_config_data(bytes: &[u8]) -> Result<&[u8], bincode::Error> { + deserialize::(bytes) + .and_then(|keys| serialized_size(&keys)) + .map(|offset| &bytes[offset as usize..]) +} + +// utility for pre-made Accounts +pub fn create_config_account( + keys: Vec<(Pubkey, bool)>, + config_data: &T, + lamports: u64, +) -> Account { + let mut data = serialize(&ConfigKeys { keys }).unwrap(); + data.extend_from_slice(&serialize(config_data).unwrap()); + Account { + lamports, + data, + owner: id(), + ..Account::default() + } +} diff --git a/programs/config_tests/.gitignore b/programs/config_tests/.gitignore new file mode 100644 index 0000000000..5404b132db --- /dev/null +++ b/programs/config_tests/.gitignore @@ -0,0 +1,2 @@ +/target/ +/farf/ diff --git a/programs/config_tests/Cargo.toml b/programs/config_tests/Cargo.toml new file mode 100644 index 0000000000..d2b9e815fb --- /dev/null +++ b/programs/config_tests/Cargo.toml @@ -0,0 +1,24 @@ +[package] +name = "solana-config-tests" +version = "0.18.0-pre1" +description = "Solana config api tests" +authors = ["Solana Maintainers "] +repository = "https://github.com/solana-labs/solana" +license = "Apache-2.0" +homepage = "https://solana.com/" +edition = "2018" + +[dependencies] +bincode = "1.1.4" +log = "0.4.8" +serde = "1.0.98" +serde_derive = "1.0.98" +solana-logger = { path = "../../logger", version = "0.18.0-pre1" } +solana-sdk = { path = "../../sdk", version = "0.18.0-pre1" } +solana-config-api = { path = "../config_api", version = "0.18.0-pre1" } +solana-config-program = { path = "../config_program", version = "0.18.0-pre1" } + + +[dev-dependencies] +solana-runtime = { path = "../../runtime", version = "0.18.0-pre1" } +assert_matches = "1.3.0" diff --git a/programs/config_tests/tests/config_processor.rs b/programs/config_tests/tests/config_processor.rs new file mode 100644 index 0000000000..0a76b1062b --- /dev/null +++ b/programs/config_tests/tests/config_processor.rs @@ -0,0 +1,366 @@ +//! Config program + +use bincode::{deserialize, serialized_size}; +use serde_derive::{Deserialize, Serialize}; +use solana_config_api::{ + config_instruction, config_processor::process_instruction, get_config_data, id, ConfigKeys, + ConfigState, +}; +use solana_runtime::{bank::Bank, bank_client::BankClient}; +use solana_sdk::{ + client::SyncClient, + genesis_block::create_genesis_block, + message::Message, + pubkey::Pubkey, + signature::{Keypair, KeypairUtil}, + system_instruction, +}; + +#[derive(Serialize, Deserialize, Debug, PartialEq)] +struct MyConfig { + pub item: u64, +} +impl Default for MyConfig { + fn default() -> Self { + Self { item: 123456789 } + } +} +impl MyConfig { + pub fn new(item: u64) -> Self { + Self { item } + } + pub fn deserialize(input: &[u8]) -> Option { + deserialize(input).ok() + } +} + +impl ConfigState for MyConfig { + fn max_space() -> u64 { + serialized_size(&Self::default()).unwrap() + } +} + +fn create_bank(lamports: u64) -> (Bank, Keypair) { + let (genesis_block, mint_keypair) = create_genesis_block(lamports); + let mut bank = Bank::new(&genesis_block); + bank.add_instruction_processor(id(), process_instruction); + (bank, mint_keypair) +} + +fn create_config_account( + bank: Bank, + mint_keypair: &Keypair, + keys: Vec<(Pubkey, bool)>, +) -> (BankClient, Keypair) { + let config_keypair = Keypair::new(); + let config_pubkey = config_keypair.pubkey(); + + let bank_client = BankClient::new(bank); + bank_client + .send_message( + &[mint_keypair, &config_keypair], + Message::new(config_instruction::create_account::( + &mint_keypair.pubkey(), + &config_pubkey, + 1, + keys, + )), + ) + .expect("new_account"); + + (bank_client, config_keypair) +} + +#[test] +fn test_process_create_ok() { + solana_logger::setup(); + let (bank, mint_keypair) = create_bank(10_000); + let (bank_client, config_keypair) = create_config_account(bank, &mint_keypair, vec![]); + let config_account_data = bank_client + .get_account_data(&config_keypair.pubkey()) + .unwrap() + .unwrap(); + assert_eq!( + Some(MyConfig::default()), + deserialize(get_config_data(&config_account_data).unwrap()).ok() + ); +} + +#[test] +fn test_process_store_ok() { + solana_logger::setup(); + let (bank, mint_keypair) = create_bank(10_000); + let keys = vec![]; + let (bank_client, config_keypair) = create_config_account(bank, &mint_keypair, keys.clone()); + let config_pubkey = config_keypair.pubkey(); + + let my_config = MyConfig::new(42); + + let instruction = config_instruction::store(&config_pubkey, true, keys.clone(), &my_config); + let message = Message::new_with_payer(vec![instruction], Some(&mint_keypair.pubkey())); + + bank_client + .send_message(&[&mint_keypair, &config_keypair], message) + .unwrap(); + + let config_account_data = bank_client + .get_account_data(&config_pubkey) + .unwrap() + .unwrap(); + assert_eq!( + Some(my_config), + deserialize(get_config_data(&config_account_data).unwrap()).ok() + ); +} + +#[test] +fn test_process_store_fail_instruction_data_too_large() { + solana_logger::setup(); + let (bank, mint_keypair) = create_bank(10_000); + let (bank_client, config_keypair) = create_config_account(bank, &mint_keypair, vec![]); + let config_pubkey = config_keypair.pubkey(); + + let my_config = MyConfig::new(42); + + let mut instruction = config_instruction::store(&config_pubkey, true, vec![], &my_config); + instruction.data = vec![0; 123]; // <-- Replace data with a vector that's too large + let message = Message::new(vec![instruction]); + bank_client + .send_message(&[&config_keypair], message) + .unwrap_err(); +} + +#[test] +fn test_process_store_fail_account0_not_signer() { + solana_logger::setup(); + let (bank, mint_keypair) = create_bank(10_000); + let system_keypair = Keypair::new(); + let system_pubkey = system_keypair.pubkey(); + + bank.transfer(42, &mint_keypair, &system_pubkey).unwrap(); + let (bank_client, config_keypair) = create_config_account(bank, &mint_keypair, vec![]); + let config_pubkey = config_keypair.pubkey(); + + let transfer_instruction = + system_instruction::transfer(&system_pubkey, &Pubkey::new_rand(), 42); + let my_config = MyConfig::new(42); + let mut store_instruction = config_instruction::store(&config_pubkey, true, vec![], &my_config); + store_instruction.accounts[0].is_signer = false; // <----- not a signer + + let message = Message::new(vec![transfer_instruction, store_instruction]); + bank_client + .send_message(&[&system_keypair], message) + .unwrap_err(); +} + +#[test] +fn test_process_store_with_additional_signers() { + solana_logger::setup(); + let (bank, mint_keypair) = create_bank(10_000); + let pubkey = Pubkey::new_rand(); + let signer0 = Keypair::new(); + let signer1 = Keypair::new(); + let keys = vec![ + (pubkey, false), + (signer0.pubkey(), true), + (signer1.pubkey(), true), + ]; + let (bank_client, config_keypair) = create_config_account(bank, &mint_keypair, keys.clone()); + let config_pubkey = config_keypair.pubkey(); + + let my_config = MyConfig::new(42); + + let instruction = config_instruction::store(&config_pubkey, true, keys.clone(), &my_config); + let message = Message::new_with_payer(vec![instruction], Some(&mint_keypair.pubkey())); + + bank_client + .send_message( + &[&mint_keypair, &config_keypair, &signer0, &signer1], + message, + ) + .unwrap(); + + let config_account_data = bank_client + .get_account_data(&config_pubkey) + .unwrap() + .unwrap(); + let meta_data: ConfigKeys = deserialize(&config_account_data).unwrap(); + assert_eq!(meta_data.keys, keys); + assert_eq!( + Some(my_config), + deserialize(get_config_data(&config_account_data).unwrap()).ok() + ); +} + +#[test] +fn test_process_store_without_config_signer() { + solana_logger::setup(); + let (bank, mint_keypair) = create_bank(10_000); + let pubkey = Pubkey::new_rand(); + let signer0 = Keypair::new(); + let keys = vec![(pubkey, false), (signer0.pubkey(), true)]; + let (bank_client, config_keypair) = create_config_account(bank, &mint_keypair, keys.clone()); + let config_pubkey = config_keypair.pubkey(); + + let my_config = MyConfig::new(42); + + let instruction = config_instruction::store(&config_pubkey, false, keys.clone(), &my_config); + let message = Message::new_with_payer(vec![instruction], Some(&mint_keypair.pubkey())); + + bank_client + .send_message(&[&mint_keypair, &signer0], message) + .unwrap_err(); +} + +#[test] +fn test_process_store_with_bad_additional_signer() { + solana_logger::setup(); + let (bank, mint_keypair) = create_bank(10_000); + let signer0 = Keypair::new(); + let signer1 = Keypair::new(); + let keys = vec![(signer0.pubkey(), true)]; + let (bank_client, config_keypair) = create_config_account(bank, &mint_keypair, keys.clone()); + let config_pubkey = config_keypair.pubkey(); + + let my_config = MyConfig::new(42); + + // Config-data pubkey doesn't match signer + let instruction = config_instruction::store(&config_pubkey, true, keys.clone(), &my_config); + let mut message = + Message::new_with_payer(vec![instruction.clone()], Some(&mint_keypair.pubkey())); + message.account_keys[2] = signer1.pubkey(); + bank_client + .send_message(&[&mint_keypair, &config_keypair, &signer1], message) + .unwrap_err(); + + // Config-data pubkey not a signer + let mut message = Message::new_with_payer(vec![instruction], Some(&mint_keypair.pubkey())); + message.header.num_required_signatures = 2; + bank_client + .send_message(&[&mint_keypair, &config_keypair], message) + .unwrap_err(); +} + +#[test] +fn test_config_updates() { + solana_logger::setup(); + let (bank, mint_keypair) = create_bank(10_000); + let pubkey = Pubkey::new_rand(); + let signer0 = Keypair::new(); + let signer1 = Keypair::new(); + let keys = vec![ + (pubkey, false), + (signer0.pubkey(), true), + (signer1.pubkey(), true), + ]; + let (bank_client, config_keypair) = create_config_account(bank, &mint_keypair, keys.clone()); + let config_pubkey = config_keypair.pubkey(); + + let my_config = MyConfig::new(42); + + let instruction = config_instruction::store(&config_pubkey, true, keys.clone(), &my_config); + let message = Message::new_with_payer(vec![instruction], Some(&mint_keypair.pubkey())); + + bank_client + .send_message( + &[&mint_keypair, &config_keypair, &signer0, &signer1], + message, + ) + .unwrap(); + + // Update with expected signatures + let new_config = MyConfig::new(84); + let instruction = config_instruction::store(&config_pubkey, false, keys.clone(), &new_config); + let message = Message::new_with_payer(vec![instruction], Some(&mint_keypair.pubkey())); + bank_client + .send_message(&[&mint_keypair, &signer0, &signer1], message) + .unwrap(); + + let config_account_data = bank_client + .get_account_data(&config_pubkey) + .unwrap() + .unwrap(); + let meta_data: ConfigKeys = deserialize(&config_account_data).unwrap(); + assert_eq!(meta_data.keys, keys); + assert_eq!( + new_config, + MyConfig::deserialize(get_config_data(&config_account_data).unwrap()).unwrap() + ); + + // Attempt update with incomplete signatures + let keys = vec![(pubkey, false), (signer0.pubkey(), true)]; + let instruction = config_instruction::store(&config_pubkey, false, keys.clone(), &my_config); + let message = Message::new_with_payer(vec![instruction], Some(&mint_keypair.pubkey())); + bank_client + .send_message(&[&mint_keypair, &signer0], message) + .unwrap_err(); + + // Attempt update with incorrect signatures + let signer2 = Keypair::new(); + let keys = vec![ + (pubkey, false), + (signer0.pubkey(), true), + (signer2.pubkey(), true), + ]; + let instruction = config_instruction::store(&config_pubkey, false, keys.clone(), &my_config); + let message = Message::new_with_payer(vec![instruction], Some(&mint_keypair.pubkey())); + bank_client + .send_message(&[&mint_keypair, &signer0, &signer2], message) + .unwrap_err(); +} + +#[test] +fn test_config_updates_requiring_config() { + solana_logger::setup(); + let (bank, mint_keypair) = create_bank(10_000); + let pubkey = Pubkey::new_rand(); + let signer0 = Keypair::new(); + let keys = vec![ + (pubkey, false), + (signer0.pubkey(), true), + (signer0.pubkey(), true), + ]; // Dummy keys for account sizing + let (bank_client, config_keypair) = create_config_account(bank, &mint_keypair, keys.clone()); + let config_pubkey = config_keypair.pubkey(); + let keys = vec![ + (pubkey, false), + (signer0.pubkey(), true), + (config_keypair.pubkey(), true), + ]; + + let my_config = MyConfig::new(42); + + let instruction = config_instruction::store(&config_pubkey, true, keys.clone(), &my_config); + let message = Message::new_with_payer(vec![instruction], Some(&mint_keypair.pubkey())); + + bank_client + .send_message(&[&mint_keypair, &config_keypair, &signer0], message) + .unwrap(); + + // Update with expected signatures + let new_config = MyConfig::new(84); + let instruction = config_instruction::store(&config_pubkey, true, keys.clone(), &new_config); + let message = Message::new_with_payer(vec![instruction], Some(&mint_keypair.pubkey())); + bank_client + .send_message(&[&mint_keypair, &config_keypair, &signer0], message) + .unwrap(); + + let config_account_data = bank_client + .get_account_data(&config_pubkey) + .unwrap() + .unwrap(); + let meta_data: ConfigKeys = deserialize(&config_account_data).unwrap(); + assert_eq!(meta_data.keys, keys); + assert_eq!( + new_config, + MyConfig::deserialize(get_config_data(&config_account_data).unwrap()).unwrap() + ); + + // Attempt update with incomplete signatures + let keys = vec![(pubkey, false), (config_keypair.pubkey(), true)]; + let instruction = config_instruction::store(&config_pubkey, true, keys.clone(), &my_config); + let message = Message::new_with_payer(vec![instruction], Some(&mint_keypair.pubkey())); + bank_client + .send_message(&[&mint_keypair, &config_keypair], message) + .unwrap_err(); +} diff --git a/validator-info/src/validator_info.rs b/validator-info/src/validator_info.rs index 866e6b0319..90c803b7dd 100644 --- a/validator-info/src/validator_info.rs +++ b/validator-info/src/validator_info.rs @@ -6,7 +6,7 @@ use reqwest::Client; use serde_derive::{Deserialize, Serialize}; use serde_json::{Map, Value}; use solana_client::rpc_client::RpcClient; -use solana_config_api::{config_instruction, config_instruction::ConfigKeys, ConfigState}; +use solana_config_api::{config_instruction, get_config_data, ConfigKeys, ConfigState}; use solana_sdk::account::Account; use solana_sdk::message::Message; use solana_sdk::pubkey::Pubkey; @@ -31,7 +31,7 @@ solana_sdk::solana_name_id!( "Va1idator1nfo111111111111111111111111111111" ); -#[derive(Debug, Deserialize, Serialize)] +#[derive(Debug, Deserialize, Serialize, Default)] struct ValidatorInfo { info: String, } @@ -152,8 +152,7 @@ fn parse_validator_info( let key_list: ConfigKeys = deserialize(&account_data)?; if !key_list.keys.is_empty() { let (validator_pubkey, _) = key_list.keys[1]; - let meta_length = ConfigKeys::serialized_size(key_list.keys); - let validator_info: String = deserialize(&account_data[meta_length..])?; + let validator_info: String = deserialize(&get_config_data(account_data)?)?; Ok((validator_pubkey, validator_info)) } else { Err(format!( @@ -334,15 +333,18 @@ fn main() -> Result<(), Box> { "Publishing info for Validator {:?}", validator_keypair.pubkey() ); - let instructions = vec![ - config_instruction::create_account::( - &validator_keypair.pubkey(), - &info_keypair.pubkey(), - 1, - keys.clone(), - ), - config_instruction::store(&info_keypair.pubkey(), true, keys, &validator_info), - ]; + let mut instructions = config_instruction::create_account::( + &validator_keypair.pubkey(), + &info_keypair.pubkey(), + 1, + keys.clone(), + ); + instructions.extend_from_slice(&[config_instruction::store( + &info_keypair.pubkey(), + true, + keys, + &validator_info, + )]); let signers = vec![&validator_keypair, &info_keypair]; let message = Message::new(instructions); (message, signers)