diff --git a/cli/src/cli.rs b/cli/src/cli.rs index ef18617343..32713572fc 100644 --- a/cli/src/cli.rs +++ b/cli/src/cli.rs @@ -32,7 +32,7 @@ use solana_sdk::{ native_token::lamports_to_sol, pubkey::Pubkey, signature::{Keypair, KeypairUtil, Signature}, - system_instruction::SystemError, + system_instruction::{create_address_with_seed, SystemError, MAX_ADDRESS_SEED_LEN}, system_transaction, transaction::{Transaction, TransactionError}, }; @@ -80,6 +80,11 @@ pub enum CliCommand { node_pubkey: Pubkey, }, ClusterVersion, + CreateAddressWithSeed { + from_pubkey: Option, + seed: String, + program_id: Pubkey, + }, Fees, GetBlockTime { slot: Slot, @@ -312,6 +317,7 @@ pub fn parse_command(matches: &ArgMatches<'_>) -> Result parse_create_address_with_seed(matches), ("fees", Some(_matches)) => Ok(CliCommandInfo { command: CliCommand::Fees, require_keypair: false, @@ -417,8 +423,7 @@ pub fn parse_command(matches: &ArgMatches<'_>) -> Result, +) -> Result { + let from_pubkey = pubkey_of(matches, "from"); + + let require_keypair = from_pubkey.is_none(); + + let program_id = match matches.value_of("program_id").unwrap() { + "STAKE" => solana_stake_program::id(), + "VOTE" => solana_vote_program::id(), + "STORAGE" => solana_storage_program::id(), + "NONCE" => solana_sdk::nonce_program::id(), + _ => pubkey_of(matches, "program_id").unwrap(), + }; + + let seed = matches.value_of("seed").unwrap().to_string(); + + if seed.len() > MAX_ADDRESS_SEED_LEN { + return Err(CliError::BadParameter( + "Address seed must not be longer than 32 bytes".to_string(), + )); + } + + Ok(CliCommandInfo { + command: CliCommand::CreateAddressWithSeed { + from_pubkey, + seed, + program_id, + }, + require_keypair, + }) +} + +fn process_create_address_with_seed( + config: &CliConfig, + from_pubkey: Option<&Pubkey>, + seed: &str, + program_id: &Pubkey, +) -> ProcessResult { + let config_pubkey = config.keypair.pubkey(); + let from_pubkey = from_pubkey.unwrap_or(&config_pubkey); + let address = create_address_with_seed(from_pubkey, seed, program_id)?; + Ok(address.to_string()) +} + fn process_airdrop( rpc_client: &RpcClient, config: &CliConfig, @@ -1039,6 +1089,11 @@ pub fn process_command(config: &CliConfig) -> ProcessResult { // Return software version of solana-cli and cluster entrypoint node CliCommand::Catchup { node_pubkey } => process_catchup(&rpc_client, node_pubkey), CliCommand::ClusterVersion => process_cluster_version(&rpc_client), + CliCommand::CreateAddressWithSeed { + from_pubkey, + seed, + program_id, + } => process_create_address_with_seed(config, from_pubkey.as_ref(), &seed, &program_id), CliCommand::Fees => process_fees(&rpc_client), CliCommand::GetBlockTime { slot } => process_get_block_time(&rpc_client, *slot), CliCommand::GetGenesisHash => process_get_genesis_hash(&rpc_client), @@ -1518,18 +1573,6 @@ pub fn app<'ab, 'v>(name: &str, about: &'ab str, version: &'v str) -> App<'ab, ' .subcommand(SubCommand::with_name("address").about("Get your public key")) .cluster_query_subcommands() .nonce_subcommands() - .subcommand( - SubCommand::with_name("deploy") - .about("Deploy a program") - .arg( - Arg::with_name("program_location") - .index(1) - .value_name("PATH TO BPF PROGRAM") - .takes_value(true) - .required(true) - .help("/path/to/program.o"), - ), - ) .stake_subcommands() .storage_subcommands() .subcommand( @@ -1611,6 +1654,50 @@ pub fn app<'ab, 'v>(name: &str, about: &'ab str, version: &'v str) -> App<'ab, ' .help("The transaction signature to confirm"), ), ) + .subcommand( + SubCommand::with_name("create-address-with-seed") + .about("Generate a dervied account address with a seed") + .arg( + Arg::with_name("seed") + .index(1) + .value_name("SEED_STRING") + .takes_value(true) + .required(true) + .help("The seed. Must not take more than 32 bytes to encode as utf-8"), + ) + .arg( + Arg::with_name("program_id") + .index(2) + .value_name("PROGRAM_ID") + .takes_value(true) + .required(true) + .help( + "The program_id that the address will ultimately be used for, \n\ + or one of STAKE, VOTE, NONCE, and STORAGE keywords", + ), + ) + .arg( + Arg::with_name("from") + .long("from") + .value_name("PUBKEY") + .takes_value(true) + .required(false) + .validator(is_pubkey_or_keypair) + .help("From (base) key, defaults to client keypair."), + ), + ) + .subcommand( + SubCommand::with_name("deploy") + .about("Deploy a program") + .arg( + Arg::with_name("program_location") + .index(1) + .value_name("PATH TO BPF PROGRAM") + .takes_value(true) + .required(true) + .help("/path/to/program.o"), + ), + ) .subcommand( SubCommand::with_name("pay") .about("Send a payment") @@ -1911,6 +1998,53 @@ mod tests { .get_matches_from(vec!["test", "confirm", "deadbeef"]); assert!(parse_command(&test_bad_signature).is_err()); + // Test CreateAddressWithSeed + let from_pubkey = Some(Pubkey::new_rand()); + let from_str = from_pubkey.unwrap().to_string(); + for (name, program_id) in &[ + ("STAKE", solana_stake_program::id()), + ("VOTE", solana_vote_program::id()), + ("NONCE", solana_sdk::nonce_program::id()), + ("STORAGE", solana_storage_program::id()), + ] { + let test_create_address_with_seed = test_commands.clone().get_matches_from(vec![ + "test", + "create-address-with-seed", + "seed", + name, + "--from", + &from_str, + ]); + assert_eq!( + parse_command(&test_create_address_with_seed).unwrap(), + CliCommandInfo { + command: CliCommand::CreateAddressWithSeed { + from_pubkey, + seed: "seed".to_string(), + program_id: *program_id + }, + require_keypair: false + } + ); + } + let test_create_address_with_seed = test_commands.clone().get_matches_from(vec![ + "test", + "create-address-with-seed", + "seed", + "STAKE", + ]); + assert_eq!( + parse_command(&test_create_address_with_seed).unwrap(), + CliCommandInfo { + command: CliCommand::CreateAddressWithSeed { + from_pubkey: None, + seed: "seed".to_string(), + program_id: solana_stake_program::id(), + }, + require_keypair: true + } + ); + // Test Deploy Subcommand let test_deploy = test_commands diff --git a/cli/src/stake.rs b/cli/src/stake.rs index bdb686b0ce..3a3cae416e 100644 --- a/cli/src/stake.rs +++ b/cli/src/stake.rs @@ -482,7 +482,7 @@ pub fn process_create_stake_account( }; println!("{:?}", authorized); - let ixs = stake_instruction::create_stake_account_with_lockup( + let ixs = stake_instruction::create_account( &config.keypair.pubkey(), &stake_account_pubkey, &authorized, diff --git a/ledger/src/staking_utils.rs b/ledger/src/staking_utils.rs index 3a9fbb6379..b4a30c5eb3 100644 --- a/ledger/src/staking_utils.rs +++ b/ledger/src/staking_utils.rs @@ -115,7 +115,7 @@ pub(crate) mod tests { }; use solana_stake_program::{ stake_instruction, - stake_state::{Authorized, Delegation, Stake}, + stake_state::{Authorized, Delegation, Lockup, Stake}, }; use solana_vote_program::{vote_instruction, vote_state::VoteInit}; use std::sync::Arc; @@ -168,11 +168,12 @@ pub(crate) mod tests { process_instructions( bank, &[from_account, &stake_account_keypair], - stake_instruction::create_stake_account_and_delegate_stake( + stake_instruction::create_account_and_delegate_stake( &from_account.pubkey(), &stake_account_pubkey, &vote_pubkey, &Authorized::auto(&stake_account_pubkey), + &Lockup::default(), amount, ), ); diff --git a/local-cluster/src/local_cluster.rs b/local-cluster/src/local_cluster.rs index 5f3c1bd54a..4e7b8962d6 100644 --- a/local-cluster/src/local_cluster.rs +++ b/local-cluster/src/local_cluster.rs @@ -26,7 +26,7 @@ use solana_sdk::{ }; use solana_stake_program::{ config as stake_config, stake_instruction, - stake_state::{Authorized as StakeAuthorized, StakeState}, + stake_state::{Authorized, Lockup, StakeState}, }; use solana_storage_program::{ storage_contract, @@ -522,11 +522,12 @@ impl LocalCluster { let mut transaction = Transaction::new_signed_instructions( &[from_account.as_ref(), &stake_account_keypair], - stake_instruction::create_stake_account_and_delegate_stake( + stake_instruction::create_account_and_delegate_stake( &from_account.pubkey(), &stake_account_pubkey, &vote_account_pubkey, - &StakeAuthorized::auto(&stake_account_pubkey), + &Authorized::auto(&stake_account_pubkey), + &Lockup::default(), amount, ), client diff --git a/programs/bpf/Cargo.lock b/programs/bpf/Cargo.lock index 54e6ac4153..56e181cfee 100644 --- a/programs/bpf/Cargo.lock +++ b/programs/bpf/Cargo.lock @@ -426,14 +426,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "ed25519-dalek" -version = "1.0.0-pre.2" +version = "1.0.0-pre.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "clear_on_drop 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "curve25519-dalek 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_os 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", "sha2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1381,7 +1380,7 @@ dependencies = [ [[package]] name = "reqwest" -version = "0.9.22" +version = "0.9.24" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1740,14 +1739,14 @@ dependencies = [ "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", "cc 1.0.45 (registry+https://github.com/rust-lang/crates.io-index)", "curve25519-dalek 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "ed25519-dalek 1.0.0-pre.2 (registry+https://github.com/rust-lang/crates.io-index)", + "ed25519-dalek 1.0.0-pre.1 (registry+https://github.com/rust-lang/crates.io-index)", "either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)", "failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", "rand_chacha 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "regex-syntax 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", - "reqwest 0.9.22 (registry+https://github.com/rust-lang/crates.io-index)", + "reqwest 0.9.24 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", "syn 0.15.44 (registry+https://github.com/rust-lang/crates.io-index)", "syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1778,7 +1777,7 @@ dependencies = [ "env_logger 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "reqwest 0.9.22 (registry+https://github.com/rust-lang/crates.io-index)", + "reqwest 0.9.24 (registry+https://github.com/rust-lang/crates.io-index)", "solana-sdk 0.22.0", "sys-info 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1832,7 +1831,7 @@ dependencies = [ "bincode 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "bs58 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "ed25519-dalek 1.0.0-pre.2 (registry+https://github.com/rust-lang/crates.io-index)", + "ed25519-dalek 1.0.0-pre.1 (registry+https://github.com/rust-lang/crates.io-index)", "generic-array 0.13.2 (registry+https://github.com/rust-lang/crates.io-index)", "hex 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "hmac 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2651,7 +2650,7 @@ dependencies = [ "checksum digest 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)" = "03b072242a8cbaf9c145665af9d250c59af3b958f83ed6824e13533cf76d5b90" "checksum digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5" "checksum dtoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "ea57b42383d091c85abcc2706240b94ab2a8fa1fc81c10ff23c4de06e2a90b5e" -"checksum ed25519-dalek 1.0.0-pre.2 (registry+https://github.com/rust-lang/crates.io-index)" = "845aaacc16f01178f33349e7c992ecd0cee095aa5e577f0f4dee35971bd36455" +"checksum ed25519-dalek 1.0.0-pre.1 (registry+https://github.com/rust-lang/crates.io-index)" = "81956bcf7ef761fb4e1d88de3fa181358a0d26cbcb9755b587a08f9119824b86" "checksum either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "bb1f6b1ce1c140482ea30ddd3335fc0024ac7ee112895426e0a629a6c20adfe3" "checksum elf 0.0.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4841de15dbe0e49b9b62a417589299e3be0d557e0900d36acb87e6dae47197f5" "checksum elfkit 0.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "02f182eda16a7360c80a2f8638d0726e9d5478173058f1505c42536ca666ecd2" @@ -2759,7 +2758,7 @@ dependencies = [ "checksum regex 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dc220bd33bdce8f093101afe22a037b8eb0e5af33592e6a9caafff0d4cb81cbd" "checksum regex-syntax 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)" = "11a7e20d1cce64ef2fed88b66d347f88bd9babb82845b2b858f3edbf59a4f716" "checksum remove_dir_all 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "4a83fa3702a688b9359eccba92d153ac33fd2e8462f9e0e3fdf155239ea7792e" -"checksum reqwest 0.9.22 (registry+https://github.com/rust-lang/crates.io-index)" = "2c2064233e442ce85c77231ebd67d9eca395207dec2127fe0bbedde4bd29a650" +"checksum reqwest 0.9.24 (registry+https://github.com/rust-lang/crates.io-index)" = "f88643aea3c1343c804950d7bf983bd2067f5ab59db6d613a08e05572f2714ab" "checksum rgb 0.8.14 (registry+https://github.com/rust-lang/crates.io-index)" = "2089e4031214d129e201f8c3c8c2fe97cd7322478a0d1cdf78e7029b0042efdb" "checksum ring 0.16.9 (registry+https://github.com/rust-lang/crates.io-index)" = "6747f8da1f2b1fabbee1aaa4eb8a11abf9adef0bf58a41cee45db5d59cecdfac" "checksum rustc-demangle 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "4c691c0e608126e00913e33f0ccf3727d5fc84573623b8d65b2df340b5201783" diff --git a/programs/stake/src/stake_instruction.rs b/programs/stake/src/stake_instruction.rs index dace9cd65a..cbe14f0592 100644 --- a/programs/stake/src/stake_instruction.rs +++ b/programs/stake/src/stake_instruction.rs @@ -139,7 +139,28 @@ pub fn initialize(stake_pubkey: &Pubkey, authorized: &Authorized, lockup: &Locku ) } -pub fn create_stake_account_with_lockup( +pub fn create_account_with_seed( + from_pubkey: &Pubkey, + stake_pubkey: &Pubkey, + seed: &str, + authorized: &Authorized, + lockup: &Lockup, + lamports: u64, +) -> Vec { + vec![ + system_instruction::create_account_with_seed( + from_pubkey, + stake_pubkey, + seed, + lamports, + std::mem::size_of::() as u64, + &id(), + ), + initialize(stake_pubkey, authorized, lockup), + ] +} + +pub fn create_account( from_pubkey: &Pubkey, stake_pubkey: &Pubkey, authorized: &Authorized, @@ -183,29 +204,15 @@ pub fn split( ] } -pub fn create_stake_account( - from_pubkey: &Pubkey, - stake_pubkey: &Pubkey, - authorized: &Authorized, - lamports: u64, -) -> Vec { - create_stake_account_with_lockup( - from_pubkey, - stake_pubkey, - authorized, - &Lockup::default(), - lamports, - ) -} - -pub fn create_stake_account_and_delegate_stake( +pub fn create_account_and_delegate_stake( from_pubkey: &Pubkey, stake_pubkey: &Pubkey, vote_pubkey: &Pubkey, authorized: &Authorized, + lockup: &Lockup, lamports: u64, ) -> Vec { - let mut instructions = create_stake_account(from_pubkey, stake_pubkey, authorized, lamports); + let mut instructions = create_account(from_pubkey, stake_pubkey, authorized, lockup, lamports); instructions.push(delegate_stake( stake_pubkey, &authorized.staker, diff --git a/programs/vote/src/vote_instruction.rs b/programs/vote/src/vote_instruction.rs index 076609f9cc..dc45a1ecc5 100644 --- a/programs/vote/src/vote_instruction.rs +++ b/programs/vote/src/vote_instruction.rs @@ -87,6 +87,26 @@ pub fn create_account( vec![create_ix, init_ix] } +pub fn create_account_with_seed( + from_pubkey: &Pubkey, + vote_pubkey: &Pubkey, + seed: &str, + vote_init: &VoteInit, + lamports: u64, +) -> Vec { + let space = VoteState::size_of() as u64; + let create_ix = system_instruction::create_account_with_seed( + from_pubkey, + vote_pubkey, + seed, + lamports, + space, + &id(), + ); + let init_ix = initialize_account(vote_pubkey, vote_init); + vec![create_ix, init_ix] +} + pub fn authorize( vote_pubkey: &Pubkey, authorized_pubkey: &Pubkey, // currently authorized diff --git a/runtime/src/bank.rs b/runtime/src/bank.rs index a26b0dcb98..29e4039416 100644 --- a/runtime/src/bank.rs +++ b/runtime/src/bank.rs @@ -3991,11 +3991,12 @@ mod tests { ); let stake_keypair = Keypair::new(); - instructions.extend(stake_instruction::create_stake_account_and_delegate_stake( + instructions.extend(stake_instruction::create_account_and_delegate_stake( &mint_keypair.pubkey(), &stake_keypair.pubkey(), &vote_keypair.pubkey(), &Authorized::auto(&stake_keypair.pubkey()), + &Lockup::default(), 10, )); diff --git a/runtime/tests/stake.rs b/runtime/tests/stake.rs index 89668c457c..3e5c852c1d 100644 --- a/runtime/tests/stake.rs +++ b/runtime/tests/stake.rs @@ -135,11 +135,12 @@ fn test_stake_account_lifetime() { let authorized = stake_state::Authorized::auto(&stake_pubkey); // Create stake account and delegate to vote account - let message = Message::new(stake_instruction::create_stake_account_and_delegate_stake( + let message = Message::new(stake_instruction::create_account_and_delegate_stake( &mint_pubkey, &stake_pubkey, &vote_pubkey, &authorized, + &stake_state::Lockup::default(), 1_000_000, )); bank_client diff --git a/sdk/src/system_instruction.rs b/sdk/src/system_instruction.rs index 7bdd03624b..c0790e3fc1 100644 --- a/sdk/src/system_instruction.rs +++ b/sdk/src/system_instruction.rs @@ -94,6 +94,31 @@ pub fn create_account( ) } +pub fn create_account_with_seed( + from_pubkey: &Pubkey, + to_pubkey: &Pubkey, + seed: &str, + lamports: u64, + space: u64, + program_id: &Pubkey, +) -> Instruction { + let account_metas = vec![ + AccountMeta::new(*from_pubkey, true), + AccountMeta::new(*to_pubkey, false), + ]; + + Instruction::new( + system_program::id(), + &SystemInstruction::CreateAccountWithSeed { + seed: seed.to_string(), + lamports, + space, + program_id: *program_id, + }, + account_metas, + ) +} + pub fn assign(from_pubkey: &Pubkey, program_id: &Pubkey) -> Instruction { let account_metas = vec![AccountMeta::new(*from_pubkey, true)]; Instruction::new(