diff --git a/Cargo.lock b/Cargo.lock index 51ebc3649..7a561f5f2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3428,7 +3428,6 @@ dependencies = [ "serde_derive", "serde_json", "solana-account-decoder", - "solana-budget-program", "solana-clap-utils", "solana-cli-config", "solana-client", @@ -3543,7 +3542,6 @@ dependencies = [ "solana-account-decoder", "solana-banks-server", "solana-bpf-loader-program", - "solana-budget-program", "solana-clap-utils", "solana-client", "solana-faucet", diff --git a/cli/Cargo.toml b/cli/Cargo.toml index a3141089e..8bf26c9e0 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -28,7 +28,6 @@ serde = "1.0.112" serde_derive = "1.0.103" serde_json = "1.0.56" solana-account-decoder = { path = "../account-decoder", version = "1.4.0" } -solana-budget-program = { path = "../programs/budget", version = "1.4.0" } solana-clap-utils = { path = "../clap-utils", version = "1.4.0" } solana-cli-config = { path = "../cli-config", version = "1.4.0" } solana-client = { path = "../client", version = "1.4.0" } @@ -49,7 +48,6 @@ url = "2.1.1" [dev-dependencies] solana-core = { path = "../core", version = "1.4.0" } -solana-budget-program = { path = "../programs/budget", version = "1.4.0" } tempfile = "3.1.0" [[bin]] diff --git a/cli/src/cli.rs b/cli/src/cli.rs index 61cb142b4..211923377 100644 --- a/cli/src/cli.rs +++ b/cli/src/cli.rs @@ -10,13 +10,11 @@ use crate::{ validator_info::*, vote::*, }; -use chrono::prelude::*; use clap::{App, AppSettings, Arg, ArgMatches, SubCommand}; use log::*; use num_traits::FromPrimitive; use serde_json::{self, json, Value}; use solana_account_decoder::{UiAccount, UiAccountEncoding}; -use solana_budget_program::budget_instruction::{self, BudgetError}; use solana_clap_utils::{ commitment::commitment_arg_with_default, input_parsers::*, input_validators::*, keypair::signer_from_path, offline::SIGN_ONLY_ARG, ArgConstant, @@ -162,20 +160,6 @@ pub fn nonce_authority_arg<'a, 'b>() -> Arg<'a, 'b> { nonce::nonce_authority_arg().requires(NONCE_ARG.name) } -#[derive(Default, Debug, PartialEq)] -pub struct PayCommand { - pub amount: SpendAmount, - pub to: Pubkey, - pub timestamp: Option>, - pub timestamp_pubkey: Option, - pub witnesses: Option>, - pub cancelable: bool, - pub sign_only: bool, - pub blockhash_query: BlockhashQuery, - pub nonce_account: Option, - pub nonce_authority: SignerIndex, -} - #[derive(Debug, PartialEq)] #[allow(clippy::large_enum_variant)] pub enum CliCommand { @@ -414,17 +398,14 @@ pub enum CliCommand { pubkey: Option, use_lamports_unit: bool, }, - Cancel(Pubkey), Confirm(Signature), DecodeTransaction(Transaction), - Pay(PayCommand), ResolveSigner(Option), ShowAccount { pubkey: Pubkey, output_file: Option, use_lamports_unit: bool, }, - TimeElapsed(Pubkey, Pubkey, DateTime), // TimeElapsed(to, process_id, timestamp) Transfer { amount: SpendAmount, to: Pubkey, @@ -436,7 +417,6 @@ pub enum CliCommand { nonce_authority: SignerIndex, fee_payer: SignerIndex, }, - Witness(Pubkey, Pubkey), // Witness(to, process_id) } #[derive(Debug, PartialEq)] @@ -832,16 +812,6 @@ pub fn parse_command( signers, }) } - ("cancel", Some(matches)) => { - let process_id = value_of(matches, "process_id").unwrap(); - let default_signer = - signer_from_path(matches, default_signer_path, "keypair", wallet_manager)?; - - Ok(CliCommandInfo { - command: CliCommand::Cancel(process_id), - signers: vec![default_signer], - }) - } ("confirm", Some(matches)) => match matches.value_of("signature").unwrap().parse() { Ok(signature) => Ok(CliCommandInfo { command: CliCommand::Confirm(signature), @@ -864,57 +834,6 @@ pub fn parse_command( )) } } - ("pay", Some(matches)) => { - let amount = SpendAmount::new_from_matches(matches, "amount"); - let to = pubkey_of_signer(matches, "to", wallet_manager)?.unwrap(); - let timestamp = if matches.is_present("timestamp") { - // Parse input for serde_json - let date_string = if !matches.value_of("timestamp").unwrap().contains('Z') { - format!("\"{}Z\"", matches.value_of("timestamp").unwrap()) - } else { - format!("\"{}\"", matches.value_of("timestamp").unwrap()) - }; - Some(serde_json::from_str(&date_string)?) - } else { - None - }; - let timestamp_pubkey = value_of(matches, "timestamp_pubkey"); - let witnesses = values_of(matches, "witness"); - let cancelable = matches.is_present("cancelable"); - let sign_only = matches.is_present(SIGN_ONLY_ARG.name); - let blockhash_query = BlockhashQuery::new_from_matches(matches); - let nonce_account = pubkey_of_signer(matches, NONCE_ARG.name, wallet_manager)?; - let (nonce_authority, nonce_authority_pubkey) = - signer_of(matches, NONCE_AUTHORITY_ARG.name, wallet_manager)?; - - let payer_provided = None; - let mut bulk_signers = vec![payer_provided]; - if nonce_account.is_some() { - bulk_signers.push(nonce_authority); - } - let signer_info = generate_unique_signers( - bulk_signers, - matches, - default_signer_path, - wallet_manager, - )?; - - Ok(CliCommandInfo { - command: CliCommand::Pay(PayCommand { - amount, - to, - timestamp, - timestamp_pubkey, - witnesses, - cancelable, - sign_only, - blockhash_query, - nonce_account, - nonce_authority: signer_info.index_of(nonce_authority_pubkey).unwrap(), - }), - signers: signer_info.signers, - }) - } ("account", Some(matches)) => { let account_pubkey = pubkey_of_signer(matches, "account_pubkey", wallet_manager)?.unwrap(); @@ -936,41 +855,7 @@ pub fn parse_command( signers: vec![], }) } - ("send-signature", Some(matches)) => { - let to = value_of(matches, "to").unwrap(); - let process_id = value_of(matches, "process_id").unwrap(); - - let default_signer = - signer_from_path(matches, default_signer_path, "keypair", wallet_manager)?; - - Ok(CliCommandInfo { - command: CliCommand::Witness(to, process_id), - signers: vec![default_signer], - }) - } - ("send-timestamp", Some(matches)) => { - let to = value_of(matches, "to").unwrap(); - let process_id = value_of(matches, "process_id").unwrap(); - let dt = if matches.is_present("datetime") { - // Parse input for serde_json - let date_string = if !matches.value_of("datetime").unwrap().contains('Z') { - format!("\"{}Z\"", matches.value_of("datetime").unwrap()) - } else { - format!("\"{}\"", matches.value_of("datetime").unwrap()) - }; - serde_json::from_str(&date_string)? - } else { - Utc::now() - }; - let default_signer = - signer_from_path(matches, default_signer_path, "keypair", wallet_manager)?; - - Ok(CliCommandInfo { - command: CliCommand::TimeElapsed(to, process_id, dt), - signers: vec![default_signer], - }) - } - ("transfer", Some(matches)) => { + ("pay", Some(matches)) | ("transfer", Some(matches)) => { let amount = SpendAmount::new_from_matches(matches, "amount"); let to = pubkey_of_signer(matches, "to", wallet_manager)?.unwrap(); let sign_only = matches.is_present(SIGN_ONLY_ARG.name); @@ -1457,240 +1342,6 @@ fn process_deploy( .to_string()) } -#[allow(clippy::too_many_arguments)] -fn process_pay( - rpc_client: &RpcClient, - config: &CliConfig, - amount: SpendAmount, - to: &Pubkey, - timestamp: Option>, - timestamp_pubkey: Option, - witnesses: &Option>, - cancelable: bool, - sign_only: bool, - blockhash_query: &BlockhashQuery, - nonce_account: Option, - nonce_authority: SignerIndex, -) -> ProcessResult { - check_unique_pubkeys( - (&config.signers[0].pubkey(), "cli keypair".to_string()), - (to, "to".to_string()), - )?; - - let (blockhash, fee_calculator) = - blockhash_query.get_blockhash_and_fee_calculator(rpc_client, config.commitment)?; - - let cancelable = if cancelable { - Some(config.signers[0].pubkey()) - } else { - None - }; - - if timestamp == None && *witnesses == None { - let nonce_authority = config.signers[nonce_authority]; - let build_message = |lamports| { - let ix = system_instruction::transfer(&config.signers[0].pubkey(), to, lamports); - if let Some(nonce_account) = &nonce_account { - Message::new_with_nonce(vec![ix], None, nonce_account, &nonce_authority.pubkey()) - } else { - Message::new(&[ix], Some(&config.signers[0].pubkey())) - } - }; - - let (message, _) = resolve_spend_tx_and_check_account_balance( - rpc_client, - sign_only, - amount, - &fee_calculator, - &config.signers[0].pubkey(), - build_message, - config.commitment, - )?; - let mut tx = Transaction::new_unsigned(message); - - if sign_only { - tx.try_partial_sign(&config.signers, blockhash)?; - return_signers(&tx, &config) - } else { - if let Some(nonce_account) = &nonce_account { - let nonce_account = nonce::get_account_with_commitment( - rpc_client, - nonce_account, - config.commitment, - )?; - check_nonce_account(&nonce_account, &nonce_authority.pubkey(), &blockhash)?; - } - - tx.try_sign(&config.signers, blockhash)?; - let result = rpc_client.send_and_confirm_transaction_with_spinner_and_config( - &tx, - config.commitment, - config.send_transaction_config, - ); - log_instruction_custom_error::(result, &config) - } - } else if *witnesses == None { - let dt = timestamp.unwrap(); - let dt_pubkey = match timestamp_pubkey { - Some(pubkey) => pubkey, - None => config.signers[0].pubkey(), - }; - - let contract_state = Keypair::new(); - - let build_message = |lamports| { - // Initializing contract - let ixs = budget_instruction::on_date( - &config.signers[0].pubkey(), - to, - &contract_state.pubkey(), - dt, - &dt_pubkey, - cancelable, - lamports, - ); - Message::new(&ixs, Some(&config.signers[0].pubkey())) - }; - let (message, _) = resolve_spend_tx_and_check_account_balance( - rpc_client, - sign_only, - amount, - &fee_calculator, - &config.signers[0].pubkey(), - build_message, - config.commitment, - )?; - let mut tx = Transaction::new_unsigned(message); - if sign_only { - tx.try_partial_sign(&[config.signers[0], &contract_state], blockhash)?; - return_signers(&tx, &config) - } else { - tx.try_sign(&[config.signers[0], &contract_state], blockhash)?; - let result = rpc_client.send_and_confirm_transaction_with_spinner_and_config( - &tx, - config.commitment, - config.send_transaction_config, - ); - let signature = log_instruction_custom_error::(result, &config)?; - Ok(json!({ - "signature": signature, - "processId": format!("{}", contract_state.pubkey()), - }) - .to_string()) - } - } else if timestamp == None { - let witness = if let Some(ref witness_vec) = *witnesses { - witness_vec[0] - } else { - return Err(CliError::BadParameter( - "Could not parse required signature pubkey(s)".to_string(), - ) - .into()); - }; - - let contract_state = Keypair::new(); - - let build_message = |lamports| { - // Initializing contract - let ixs = budget_instruction::when_signed( - &config.signers[0].pubkey(), - to, - &contract_state.pubkey(), - &witness, - cancelable, - lamports, - ); - Message::new(&ixs, Some(&config.signers[0].pubkey())) - }; - let (message, _) = resolve_spend_tx_and_check_account_balance( - rpc_client, - sign_only, - amount, - &fee_calculator, - &config.signers[0].pubkey(), - build_message, - config.commitment, - )?; - let mut tx = Transaction::new_unsigned(message); - if sign_only { - tx.try_partial_sign(&[config.signers[0], &contract_state], blockhash)?; - return_signers(&tx, &config) - } else { - tx.try_sign(&[config.signers[0], &contract_state], blockhash)?; - let result = rpc_client.send_and_confirm_transaction_with_spinner_and_config( - &tx, - config.commitment, - config.send_transaction_config, - ); - let signature = log_instruction_custom_error::(result, &config)?; - Ok(json!({ - "signature": signature, - "processId": format!("{}", contract_state.pubkey()), - }) - .to_string()) - } - } else { - Ok("Combo transactions not yet handled".to_string()) - } -} - -fn process_cancel(rpc_client: &RpcClient, config: &CliConfig, pubkey: &Pubkey) -> ProcessResult { - let (blockhash, fee_calculator, _) = rpc_client - .get_recent_blockhash_with_commitment(config.commitment)? - .value; - let ix = budget_instruction::apply_signature( - &config.signers[0].pubkey(), - pubkey, - &config.signers[0].pubkey(), - ); - let message = Message::new(&[ix], Some(&config.signers[0].pubkey())); - let mut tx = Transaction::new_unsigned(message); - tx.try_sign(&config.signers, blockhash)?; - check_account_for_fee_with_commitment( - rpc_client, - &config.signers[0].pubkey(), - &fee_calculator, - &tx.message, - config.commitment, - )?; - let result = rpc_client.send_and_confirm_transaction_with_spinner_and_config( - &tx, - config.commitment, - config.send_transaction_config, - ); - log_instruction_custom_error::(result, &config) -} - -fn process_time_elapsed( - rpc_client: &RpcClient, - config: &CliConfig, - to: &Pubkey, - pubkey: &Pubkey, - dt: DateTime, -) -> ProcessResult { - let (blockhash, fee_calculator, _) = rpc_client - .get_recent_blockhash_with_commitment(config.commitment)? - .value; - - let ix = budget_instruction::apply_timestamp(&config.signers[0].pubkey(), pubkey, to, dt); - let message = Message::new(&[ix], Some(&config.signers[0].pubkey())); - let mut tx = Transaction::new_unsigned(message); - tx.try_sign(&config.signers, blockhash)?; - check_account_for_fee_with_commitment( - rpc_client, - &config.signers[0].pubkey(), - &fee_calculator, - &tx.message, - config.commitment, - )?; - let result = rpc_client.send_and_confirm_transaction_with_spinner_and_config( - &tx, - config.commitment, - config.send_transaction_config, - ); - log_instruction_custom_error::(result, &config) -} - #[allow(clippy::too_many_arguments)] fn process_transfer( rpc_client: &RpcClient, @@ -1764,35 +1415,6 @@ fn process_transfer( } } -fn process_witness( - rpc_client: &RpcClient, - config: &CliConfig, - to: &Pubkey, - pubkey: &Pubkey, -) -> ProcessResult { - let (blockhash, fee_calculator, _) = rpc_client - .get_recent_blockhash_with_commitment(CommitmentConfig::recent())? - .value; - - let ix = budget_instruction::apply_signature(&config.signers[0].pubkey(), pubkey, to); - let message = Message::new(&[ix], Some(&config.signers[0].pubkey())); - let mut tx = Transaction::new_unsigned(message); - tx.try_sign(&config.signers, blockhash)?; - check_account_for_fee_with_commitment( - rpc_client, - &config.signers[0].pubkey(), - &fee_calculator, - &tx.message, - config.commitment, - )?; - let result = rpc_client.send_and_confirm_transaction_with_spinner_and_config( - &tx, - config.commitment, - config.send_transaction_config, - ); - log_instruction_custom_error::(result, &config) -} - pub fn process_command(config: &CliConfig) -> ProcessResult { if config.verbose && config.output_format == OutputFormat::Display { println_name_value("RPC URL:", &config.json_rpc_url); @@ -2268,37 +1890,9 @@ pub fn process_command(config: &CliConfig) -> ProcessResult { pubkey, use_lamports_unit, } => process_balance(&rpc_client, config, &pubkey, *use_lamports_unit), - // Cancel a contract by contract Pubkey - CliCommand::Cancel(pubkey) => process_cancel(&rpc_client, config, &pubkey), // Confirm the last client transaction by signature CliCommand::Confirm(signature) => process_confirm(&rpc_client, config, signature), CliCommand::DecodeTransaction(transaction) => process_decode_transaction(transaction), - // If client has positive balance, pay lamports to another address - CliCommand::Pay(PayCommand { - amount, - to, - timestamp, - timestamp_pubkey, - ref witnesses, - cancelable, - sign_only, - blockhash_query, - nonce_account, - nonce_authority, - }) => process_pay( - &rpc_client, - config, - *amount, - &to, - *timestamp, - *timestamp_pubkey, - witnesses, - *cancelable, - *sign_only, - blockhash_query, - *nonce_account, - *nonce_authority, - ), CliCommand::ResolveSigner(path) => { if let Some(path) = path { Ok(path.to_string()) @@ -2317,10 +1911,6 @@ pub fn process_command(config: &CliConfig) -> ProcessResult { &output_file, *use_lamports_unit, ), - // Apply time elapsed to contract - CliCommand::TimeElapsed(to, pubkey, dt) => { - process_time_elapsed(&rpc_client, config, &to, &pubkey, *dt) - } CliCommand::Transfer { amount, to, @@ -2344,8 +1934,6 @@ pub fn process_command(config: &CliConfig) -> ProcessResult { *nonce_authority, *fee_payer, ), - // Apply witness signature to contract - CliCommand::Witness(to, pubkey) => process_witness(&rpc_client, config, &to, &pubkey), } } @@ -2535,19 +2123,6 @@ pub fn app<'ab, 'v>(name: &str, about: &'ab str, version: &'v str) -> App<'ab, ' ) .arg(commitment_arg_with_default("max")), ) - .subcommand( - SubCommand::with_name("cancel") - .about("Cancel a transfer") - .arg( - Arg::with_name("process_id") - .index(1) - .value_name("ACCOUNT_ADDRESS") - .takes_value(true) - .required(true) - .validator(is_pubkey) - .help("The account address of the transfer to cancel"), - ), - ) .subcommand( SubCommand::with_name("confirm") .about("Confirm transaction by signature") @@ -2624,7 +2199,7 @@ pub fn app<'ab, 'v>(name: &str, about: &'ab str, version: &'v str) -> App<'ab, ' ) .subcommand( SubCommand::with_name("pay") - .about("Send a payment") + .about("Deprecated alias for the transfer command") .arg( pubkey!(Arg::with_name("to") .index(1) @@ -2641,37 +2216,6 @@ pub fn app<'ab, 'v>(name: &str, about: &'ab str, version: &'v str) -> App<'ab, ' .required(true) .help("The amount to send, in SOL; accepts keyword ALL"), ) - .arg( - Arg::with_name("timestamp") - .long("after") - .value_name("DATETIME") - .takes_value(true) - .help("A timestamp after which transaction will execute"), - ) - .arg( - Arg::with_name("timestamp_pubkey") - .long("require-timestamp-from") - .value_name("PUBKEY") - .takes_value(true) - .requires("timestamp") - .validator(is_pubkey) - .help("Require timestamp from this third party"), - ) - .arg( - Arg::with_name("witness") - .long("require-signature-from") - .value_name("PUBKEY") - .takes_value(true) - .multiple(true) - .use_delimiter(true) - .validator(is_pubkey) - .help("Any third party signatures required to unlock the lamports"), - ) - .arg( - Arg::with_name("cancelable") - .long("cancelable") - .takes_value(false), - ) .offline_args() .arg(nonce_arg()) .arg(nonce_authority_arg()), @@ -2689,51 +2233,6 @@ pub fn app<'ab, 'v>(name: &str, about: &'ab str, version: &'v str) -> App<'ab, ' .help("The signer path to resolve") ) ) - .subcommand( - SubCommand::with_name("send-signature") - .about("Send a signature to authorize a transfer") - .arg( - pubkey!(Arg::with_name("to") - .index(1) - .value_name("RECIPIENT_ADDRESS") - .required(true), - "The account address of recipient. "), - ) - .arg( - Arg::with_name("process_id") - .index(2) - .value_name("ACCOUNT_ADDRESS") - .takes_value(true) - .required(true) - .help("The account address of the transfer to authorize"), - ), - ) - .subcommand( - SubCommand::with_name("send-timestamp") - .about("Send a timestamp to unlock a transfer") - .arg( - pubkey!(Arg::with_name("to") - .index(1) - .value_name("RECIPIENT_ADDRESS") - .required(true), - "The account address of recipient. "), - ) - .arg( - Arg::with_name("process_id") - .index(2) - .value_name("ACCOUNT_ADDRESS") - .takes_value(true) - .required(true) - .help("The account address of the transfer to unlock"), - ) - .arg( - Arg::with_name("datetime") - .long("date") - .value_name("DATETIME") - .takes_value(true) - .help("Optional arbitrary timestamp to apply"), - ), - ) .subcommand( SubCommand::with_name("transfer") .about("Transfer funds between system accounts") @@ -2900,11 +2399,6 @@ mod tests { let pubkey = Pubkey::new_rand(); let pubkey_string = format!("{}", pubkey); - let witness0 = Pubkey::new_rand(); - let witness0_string = format!("{}", witness0); - let witness1 = Pubkey::new_rand(); - let witness1_string = format!("{}", witness1); - let dt = Utc.ymd(2018, 9, 19).and_hms(17, 30, 59); // Test Airdrop Subcommand let test_airdrop = @@ -2975,19 +2469,6 @@ mod tests { } ); - // Test Cancel Subcommand - let test_cancel = - test_commands - .clone() - .get_matches_from(vec!["test", "cancel", &pubkey_string]); - assert_eq!( - parse_command(&test_cancel, &keypair_file, &mut None).unwrap(), - CliCommandInfo { - command: CliCommand::Cancel(pubkey), - signers: vec![read_keypair_file(&keypair_file).unwrap().into()], - } - ); - // Test Confirm Subcommand let signature = Signature::new(&[1; 64]); let signature_string = format!("{:?}", signature); @@ -3116,337 +2597,6 @@ mod tests { signers: vec![], } ); - - // Test Simple Pay Subcommand - let test_pay = - test_commands - .clone() - .get_matches_from(vec!["test", "pay", &pubkey_string, "50"]); - assert_eq!( - parse_command(&test_pay, &keypair_file, &mut None).unwrap(), - CliCommandInfo { - command: CliCommand::Pay(PayCommand { - amount: SpendAmount::Some(50_000_000_000), - to: pubkey, - ..PayCommand::default() - }), - signers: vec![read_keypair_file(&keypair_file).unwrap().into()], - } - ); - - // Test Pay Subcommand w/ Witness - let test_pay_multiple_witnesses = test_commands.clone().get_matches_from(vec![ - "test", - "pay", - &pubkey_string, - "50", - "--require-signature-from", - &witness0_string, - "--require-signature-from", - &witness1_string, - ]); - assert_eq!( - parse_command(&test_pay_multiple_witnesses, &keypair_file, &mut None).unwrap(), - CliCommandInfo { - command: CliCommand::Pay(PayCommand { - amount: SpendAmount::Some(50_000_000_000), - to: pubkey, - witnesses: Some(vec![witness0, witness1]), - ..PayCommand::default() - }), - signers: vec![read_keypair_file(&keypair_file).unwrap().into()], - } - ); - let test_pay_single_witness = test_commands.clone().get_matches_from(vec![ - "test", - "pay", - &pubkey_string, - "50", - "--require-signature-from", - &witness0_string, - ]); - assert_eq!( - parse_command(&test_pay_single_witness, &keypair_file, &mut None).unwrap(), - CliCommandInfo { - command: CliCommand::Pay(PayCommand { - amount: SpendAmount::Some(50_000_000_000), - to: pubkey, - witnesses: Some(vec![witness0]), - ..PayCommand::default() - }), - signers: vec![read_keypair_file(&keypair_file).unwrap().into()], - } - ); - - // Test Pay Subcommand w/ Timestamp - let test_pay_timestamp = test_commands.clone().get_matches_from(vec![ - "test", - "pay", - &pubkey_string, - "50", - "--after", - "2018-09-19T17:30:59", - "--require-timestamp-from", - &witness0_string, - ]); - assert_eq!( - parse_command(&test_pay_timestamp, &keypair_file, &mut None).unwrap(), - CliCommandInfo { - command: CliCommand::Pay(PayCommand { - amount: SpendAmount::Some(50_000_000_000), - to: pubkey, - timestamp: Some(dt), - timestamp_pubkey: Some(witness0), - ..PayCommand::default() - }), - signers: vec![read_keypair_file(&keypair_file).unwrap().into()], - } - ); - - // Test Pay Subcommand w/ sign-only - let blockhash = Hash::default(); - let blockhash_string = format!("{}", blockhash); - let test_pay = test_commands.clone().get_matches_from(vec![ - "test", - "pay", - &pubkey_string, - "50", - "--blockhash", - &blockhash_string, - "--sign-only", - ]); - assert_eq!( - parse_command(&test_pay, &keypair_file, &mut None).unwrap(), - CliCommandInfo { - command: CliCommand::Pay(PayCommand { - amount: SpendAmount::Some(50_000_000_000), - to: pubkey, - blockhash_query: BlockhashQuery::None(blockhash), - sign_only: true, - ..PayCommand::default() - }), - signers: vec![read_keypair_file(&keypair_file).unwrap().into()], - } - ); - - // Test Pay Subcommand w/ Blockhash - let test_pay = test_commands.clone().get_matches_from(vec![ - "test", - "pay", - &pubkey_string, - "50", - "--blockhash", - &blockhash_string, - ]); - assert_eq!( - parse_command(&test_pay, &keypair_file, &mut None).unwrap(), - CliCommandInfo { - command: CliCommand::Pay(PayCommand { - amount: SpendAmount::Some(50_000_000_000), - to: pubkey, - blockhash_query: BlockhashQuery::FeeCalculator( - blockhash_query::Source::Cluster, - blockhash - ), - ..PayCommand::default() - }), - signers: vec![read_keypair_file(&keypair_file).unwrap().into()], - } - ); - - // Test Pay Subcommand w/ Nonce - let blockhash = Hash::default(); - let blockhash_string = format!("{}", blockhash); - let test_pay = test_commands.clone().get_matches_from(vec![ - "test", - "pay", - &pubkey_string, - "50", - "--blockhash", - &blockhash_string, - "--nonce", - &pubkey_string, - ]); - assert_eq!( - parse_command(&test_pay, &keypair_file, &mut None).unwrap(), - CliCommandInfo { - command: CliCommand::Pay(PayCommand { - amount: SpendAmount::Some(50_000_000_000), - to: pubkey, - blockhash_query: BlockhashQuery::FeeCalculator( - blockhash_query::Source::NonceAccount(pubkey), - blockhash - ), - nonce_account: Some(pubkey), - ..PayCommand::default() - }), - signers: vec![read_keypair_file(&keypair_file).unwrap().into()], - } - ); - - // Test Pay Subcommand w/ Nonce and Nonce Authority - let blockhash = Hash::default(); - let blockhash_string = format!("{}", blockhash); - let keypair = read_keypair_file(&keypair_file).unwrap(); - let test_pay = test_commands.clone().get_matches_from(vec![ - "test", - "pay", - &pubkey_string, - "50", - "--blockhash", - &blockhash_string, - "--nonce", - &pubkey_string, - "--nonce-authority", - &keypair_file, - ]); - assert_eq!( - parse_command(&test_pay, &keypair_file, &mut None).unwrap(), - CliCommandInfo { - command: CliCommand::Pay(PayCommand { - amount: SpendAmount::Some(50_000_000_000), - to: pubkey, - blockhash_query: BlockhashQuery::FeeCalculator( - blockhash_query::Source::NonceAccount(pubkey), - blockhash - ), - nonce_account: Some(pubkey), - nonce_authority: 0, - ..PayCommand::default() - }), - signers: vec![keypair.into()], - } - ); - - // Test Pay Subcommand w/ Nonce and Offline Nonce Authority - let keypair = read_keypair_file(&keypair_file).unwrap(); - let authority_pubkey = keypair.pubkey(); - let authority_pubkey_string = format!("{}", authority_pubkey); - let sig = keypair.sign_message(&[0u8]); - let signer_arg = format!("{}={}", authority_pubkey, sig); - let test_pay = test_commands.clone().get_matches_from(vec![ - "test", - "pay", - &pubkey_string, - "50", - "--blockhash", - &blockhash_string, - "--nonce", - &pubkey_string, - "--nonce-authority", - &authority_pubkey_string, - "--signer", - &signer_arg, - ]); - assert_eq!( - parse_command(&test_pay, &keypair_file, &mut None).unwrap(), - CliCommandInfo { - command: CliCommand::Pay(PayCommand { - amount: SpendAmount::Some(50_000_000_000), - to: pubkey, - blockhash_query: BlockhashQuery::FeeCalculator( - blockhash_query::Source::NonceAccount(pubkey), - blockhash - ), - nonce_account: Some(pubkey), - nonce_authority: 0, - ..PayCommand::default() - }), - signers: vec![keypair.into()], - } - ); - - // Test Pay Subcommand w/ Nonce and Offline Nonce Authority - // authority pubkey not in signers fails - let keypair = read_keypair_file(&keypair_file).unwrap(); - let authority_pubkey = keypair.pubkey(); - let authority_pubkey_string = format!("{}", authority_pubkey); - let sig = keypair.sign_message(&[0u8]); - let signer_arg = format!("{}={}", Pubkey::new_rand(), sig); - let test_pay = test_commands.clone().get_matches_from(vec![ - "test", - "pay", - &pubkey_string, - "50", - "--blockhash", - &blockhash_string, - "--nonce", - &pubkey_string, - "--nonce-authority", - &authority_pubkey_string, - "--signer", - &signer_arg, - ]); - assert!(parse_command(&test_pay, &keypair_file, &mut None).is_err()); - - // Test Send-Signature Subcommand - let test_send_signature = test_commands.clone().get_matches_from(vec![ - "test", - "send-signature", - &pubkey_string, - &pubkey_string, - ]); - assert_eq!( - parse_command(&test_send_signature, &keypair_file, &mut None).unwrap(), - CliCommandInfo { - command: CliCommand::Witness(pubkey, pubkey), - signers: vec![read_keypair_file(&keypair_file).unwrap().into()], - } - ); - let test_pay_multiple_witnesses = test_commands.clone().get_matches_from(vec![ - "test", - "pay", - &pubkey_string, - "50", - "--after", - "2018-09-19T17:30:59", - "--require-signature-from", - &witness0_string, - "--require-timestamp-from", - &witness0_string, - "--require-signature-from", - &witness1_string, - ]); - assert_eq!( - parse_command(&test_pay_multiple_witnesses, &keypair_file, &mut None).unwrap(), - CliCommandInfo { - command: CliCommand::Pay(PayCommand { - amount: SpendAmount::Some(50_000_000_000), - to: pubkey, - timestamp: Some(dt), - timestamp_pubkey: Some(witness0), - witnesses: Some(vec![witness0, witness1]), - ..PayCommand::default() - }), - signers: vec![read_keypair_file(&keypair_file).unwrap().into()], - } - ); - - // Test Send-Timestamp Subcommand - let test_send_timestamp = test_commands.clone().get_matches_from(vec![ - "test", - "send-timestamp", - &pubkey_string, - &pubkey_string, - "--date", - "2018-09-19T17:30:59", - ]); - assert_eq!( - parse_command(&test_send_timestamp, &keypair_file, &mut None).unwrap(), - CliCommandInfo { - command: CliCommand::TimeElapsed(pubkey, pubkey, dt), - signers: vec![read_keypair_file(&keypair_file).unwrap().into()], - } - ); - let test_bad_timestamp = test_commands.clone().get_matches_from(vec![ - "test", - "send-timestamp", - &pubkey_string, - &pubkey_string, - "--date", - "20180919T17:30:59", - ]); - assert!(parse_command(&test_bad_timestamp, &keypair_file, &mut None).is_err()); } #[test] @@ -3475,10 +2625,6 @@ mod tests { }; assert_eq!(process_command(&config).unwrap(), "0.00000005 SOL"); - let process_id = Pubkey::new_rand(); - config.command = CliCommand::Cancel(process_id); - assert!(process_command(&config).is_ok()); - let good_signature = Signature::new(&bs58::decode(SIGNATURE).into_vec().unwrap()); config.command = CliCommand::Confirm(good_signature); assert_eq!(process_command(&config).unwrap(), "Confirmed"); @@ -3615,49 +2761,6 @@ mod tests { config.command = CliCommand::GetTransactionCount; assert_eq!(process_command(&config).unwrap(), "1234"); - config.signers = vec![&keypair]; - config.command = CliCommand::Pay(PayCommand { - amount: SpendAmount::Some(10), - to: bob_pubkey, - ..PayCommand::default() - }); - let result = process_command(&config); - assert!(result.is_ok()); - - let date_string = "\"2018-09-19T17:30:59Z\""; - let dt: DateTime = serde_json::from_str(&date_string).unwrap(); - config.command = CliCommand::Pay(PayCommand { - amount: SpendAmount::Some(10), - to: bob_pubkey, - timestamp: Some(dt), - timestamp_pubkey: Some(config.signers[0].pubkey()), - ..PayCommand::default() - }); - let result = process_command(&config); - assert!(result.is_ok()); - - let witness = Pubkey::new_rand(); - config.command = CliCommand::Pay(PayCommand { - amount: SpendAmount::Some(10), - to: bob_pubkey, - witnesses: Some(vec![witness]), - cancelable: true, - ..PayCommand::default() - }); - let result = process_command(&config); - assert!(result.is_ok()); - - let process_id = Pubkey::new_rand(); - config.command = CliCommand::TimeElapsed(bob_pubkey, process_id, dt); - config.signers = vec![&keypair]; - let result = process_command(&config); - assert!(result.is_ok()); - - let witness = Pubkey::new_rand(); - config.command = CliCommand::Witness(bob_pubkey, witness); - let result = process_command(&config); - assert!(result.is_ok()); - // CreateAddressWithSeed let from_pubkey = Pubkey::new_rand(); config.signers = vec![]; @@ -3682,15 +2785,6 @@ mod tests { }; assert!(process_command(&config).is_ok()); - config.command = CliCommand::TimeElapsed(bob_pubkey, process_id, dt); - let result = process_command(&config); - assert!(result.is_ok()); - - let witness = Pubkey::new_rand(); - config.command = CliCommand::Witness(bob_pubkey, witness); - let result = process_command(&config); - assert!(result.is_ok()); - // sig_not_found case config.rpc_client = Some(RpcClient::new_mock("sig_not_found".to_string())); let missing_signature = Signature::new(&bs58::decode("5VERv8NMvzbJMEkV8xnrLkEaWRtSz9CosKDYjCJjBRnbJLgp8uirBgmQpjKhoR4tjF3ZpRzrFmBV6UjKdiSZkQUW").into_vec().unwrap()); @@ -3755,34 +2849,6 @@ mod tests { config.command = CliCommand::GetTransactionCount; assert!(process_command(&config).is_err()); - - config.command = CliCommand::Pay(PayCommand { - amount: SpendAmount::Some(10), - to: bob_pubkey, - ..PayCommand::default() - }); - assert!(process_command(&config).is_err()); - - config.command = CliCommand::Pay(PayCommand { - amount: SpendAmount::Some(10), - to: bob_pubkey, - timestamp: Some(dt), - timestamp_pubkey: Some(config.signers[0].pubkey()), - ..PayCommand::default() - }); - assert!(process_command(&config).is_err()); - - config.command = CliCommand::Pay(PayCommand { - amount: SpendAmount::Some(10), - to: bob_pubkey, - witnesses: Some(vec![witness]), - cancelable: true, - ..PayCommand::default() - }); - assert!(process_command(&config).is_err()); - - config.command = CliCommand::TimeElapsed(bob_pubkey, process_id, dt); - assert!(process_command(&config).is_err()); } #[test] diff --git a/cli/tests/pay.rs b/cli/tests/pay.rs deleted file mode 100644 index 8ba44159d..000000000 --- a/cli/tests/pay.rs +++ /dev/null @@ -1,453 +0,0 @@ -use chrono::prelude::*; -use serde_json::Value; -use solana_cli::{ - cli::{process_command, request_and_confirm_airdrop, CliCommand, CliConfig, PayCommand}, - cli_output::OutputFormat, - nonce, - offline::{ - blockhash_query::{self, BlockhashQuery}, - parse_sign_only_reply_string, - }, - spend_utils::SpendAmount, - test_utils::check_recent_balance, -}; -use solana_client::rpc_client::RpcClient; -use solana_core::validator::TestValidator; -use solana_faucet::faucet::run_local_faucet; -use solana_sdk::{ - commitment_config::CommitmentConfig, - nonce::State as NonceState, - pubkey::Pubkey, - signature::{Keypair, Signer}, -}; -use std::{fs::remove_dir_all, sync::mpsc::channel}; - -#[test] -fn test_cli_timestamp_tx() { - let TestValidator { - server, - leader_data, - alice, - ledger_path, - .. - } = TestValidator::run(); - let bob_pubkey = Pubkey::new_rand(); - - let (sender, receiver) = channel(); - run_local_faucet(alice, sender, None); - let faucet_addr = receiver.recv().unwrap(); - - let rpc_client = RpcClient::new_socket(leader_data.rpc); - let default_signer0 = Keypair::new(); - let default_signer1 = Keypair::new(); - - let mut config_payer = CliConfig::recent_for_tests(); - config_payer.json_rpc_url = - format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port()); - config_payer.signers = vec![&default_signer0]; - - let mut config_witness = CliConfig::recent_for_tests(); - config_witness.json_rpc_url = config_payer.json_rpc_url.clone(); - config_witness.signers = vec![&default_signer1]; - - assert_ne!( - config_payer.signers[0].pubkey(), - config_witness.signers[0].pubkey() - ); - - request_and_confirm_airdrop( - &rpc_client, - &faucet_addr, - &config_payer.signers[0].pubkey(), - 50, - &config_witness, - ) - .unwrap(); - check_recent_balance(50, &rpc_client, &config_payer.signers[0].pubkey()); - - request_and_confirm_airdrop( - &rpc_client, - &faucet_addr, - &config_witness.signers[0].pubkey(), - 1, - &config_witness, - ) - .unwrap(); - - // Make transaction (from config_payer to bob_pubkey) requiring timestamp from config_witness - let date_string = "\"2018-09-19T17:30:59Z\""; - let dt: DateTime = serde_json::from_str(&date_string).unwrap(); - config_payer.command = CliCommand::Pay(PayCommand { - amount: SpendAmount::Some(10), - to: bob_pubkey, - timestamp: Some(dt), - timestamp_pubkey: Some(config_witness.signers[0].pubkey()), - ..PayCommand::default() - }); - let sig_response = process_command(&config_payer); - - let object: Value = serde_json::from_str(&sig_response.unwrap()).unwrap(); - let process_id_str = object.get("processId").unwrap().as_str().unwrap(); - let process_id_vec = bs58::decode(process_id_str) - .into_vec() - .expect("base58-encoded public key"); - let process_id = Pubkey::new(&process_id_vec); - - check_recent_balance(40, &rpc_client, &config_payer.signers[0].pubkey()); // config_payer balance - check_recent_balance(10, &rpc_client, &process_id); // contract balance - check_recent_balance(0, &rpc_client, &bob_pubkey); // recipient balance - - // Sign transaction by config_witness - config_witness.command = CliCommand::TimeElapsed(bob_pubkey, process_id, dt); - process_command(&config_witness).unwrap(); - - check_recent_balance(40, &rpc_client, &config_payer.signers[0].pubkey()); // config_payer balance - check_recent_balance(0, &rpc_client, &process_id); // contract balance - check_recent_balance(10, &rpc_client, &bob_pubkey); // recipient balance - - server.close().unwrap(); - remove_dir_all(ledger_path).unwrap(); -} - -#[test] -fn test_cli_witness_tx() { - let TestValidator { - server, - leader_data, - alice, - ledger_path, - .. - } = TestValidator::run(); - let bob_pubkey = Pubkey::new_rand(); - - let (sender, receiver) = channel(); - run_local_faucet(alice, sender, None); - let faucet_addr = receiver.recv().unwrap(); - - let rpc_client = RpcClient::new_socket(leader_data.rpc); - let default_signer0 = Keypair::new(); - let default_signer1 = Keypair::new(); - - let mut config_payer = CliConfig::recent_for_tests(); - config_payer.json_rpc_url = - format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port()); - config_payer.signers = vec![&default_signer0]; - - let mut config_witness = CliConfig::recent_for_tests(); - config_witness.json_rpc_url = config_payer.json_rpc_url.clone(); - config_witness.signers = vec![&default_signer1]; - - assert_ne!( - config_payer.signers[0].pubkey(), - config_witness.signers[0].pubkey() - ); - - request_and_confirm_airdrop( - &rpc_client, - &faucet_addr, - &config_payer.signers[0].pubkey(), - 50, - &config_witness, - ) - .unwrap(); - request_and_confirm_airdrop( - &rpc_client, - &faucet_addr, - &config_witness.signers[0].pubkey(), - 1, - &config_witness, - ) - .unwrap(); - - // Make transaction (from config_payer to bob_pubkey) requiring witness signature from config_witness - config_payer.command = CliCommand::Pay(PayCommand { - amount: SpendAmount::Some(10), - to: bob_pubkey, - witnesses: Some(vec![config_witness.signers[0].pubkey()]), - ..PayCommand::default() - }); - let sig_response = process_command(&config_payer); - - let object: Value = serde_json::from_str(&sig_response.unwrap()).unwrap(); - let process_id_str = object.get("processId").unwrap().as_str().unwrap(); - let process_id_vec = bs58::decode(process_id_str) - .into_vec() - .expect("base58-encoded public key"); - let process_id = Pubkey::new(&process_id_vec); - - check_recent_balance(40, &rpc_client, &config_payer.signers[0].pubkey()); // config_payer balance - check_recent_balance(10, &rpc_client, &process_id); // contract balance - check_recent_balance(0, &rpc_client, &bob_pubkey); // recipient balance - - // Sign transaction by config_witness - config_witness.command = CliCommand::Witness(bob_pubkey, process_id); - process_command(&config_witness).unwrap(); - - check_recent_balance(40, &rpc_client, &config_payer.signers[0].pubkey()); // config_payer balance - check_recent_balance(0, &rpc_client, &process_id); // contract balance - check_recent_balance(10, &rpc_client, &bob_pubkey); // recipient balance - - server.close().unwrap(); - remove_dir_all(ledger_path).unwrap(); -} - -#[test] -fn test_cli_cancel_tx() { - let TestValidator { - server, - leader_data, - alice, - ledger_path, - .. - } = TestValidator::run(); - let bob_pubkey = Pubkey::new_rand(); - - let (sender, receiver) = channel(); - run_local_faucet(alice, sender, None); - let faucet_addr = receiver.recv().unwrap(); - - let rpc_client = RpcClient::new_socket(leader_data.rpc); - let default_signer0 = Keypair::new(); - let default_signer1 = Keypair::new(); - - let mut config_payer = CliConfig::recent_for_tests(); - config_payer.json_rpc_url = - format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port()); - config_payer.signers = vec![&default_signer0]; - - let mut config_witness = CliConfig::recent_for_tests(); - config_witness.json_rpc_url = config_payer.json_rpc_url.clone(); - config_witness.signers = vec![&default_signer1]; - - assert_ne!( - config_payer.signers[0].pubkey(), - config_witness.signers[0].pubkey() - ); - - request_and_confirm_airdrop( - &rpc_client, - &faucet_addr, - &config_payer.signers[0].pubkey(), - 50, - &config_witness, - ) - .unwrap(); - - // Make transaction (from config_payer to bob_pubkey) requiring witness signature from config_witness - config_payer.command = CliCommand::Pay(PayCommand { - amount: SpendAmount::Some(10), - to: bob_pubkey, - witnesses: Some(vec![config_witness.signers[0].pubkey()]), - cancelable: true, - ..PayCommand::default() - }); - let sig_response = process_command(&config_payer).unwrap(); - - let object: Value = serde_json::from_str(&sig_response).unwrap(); - let process_id_str = object.get("processId").unwrap().as_str().unwrap(); - let process_id_vec = bs58::decode(process_id_str) - .into_vec() - .expect("base58-encoded public key"); - let process_id = Pubkey::new(&process_id_vec); - - check_recent_balance(40, &rpc_client, &config_payer.signers[0].pubkey()); // config_payer balance - check_recent_balance(10, &rpc_client, &process_id); // contract balance - check_recent_balance(0, &rpc_client, &bob_pubkey); // recipient balance - - // Sign transaction by config_witness - config_payer.command = CliCommand::Cancel(process_id); - process_command(&config_payer).unwrap(); - - check_recent_balance(50, &rpc_client, &config_payer.signers[0].pubkey()); // config_payer balance - check_recent_balance(0, &rpc_client, &process_id); // contract balance - check_recent_balance(0, &rpc_client, &bob_pubkey); // recipient balance - - server.close().unwrap(); - remove_dir_all(ledger_path).unwrap(); -} - -#[test] -fn test_offline_pay_tx() { - let TestValidator { - server, - leader_data, - alice, - ledger_path, - .. - } = TestValidator::run(); - let bob_pubkey = Pubkey::new_rand(); - - let (sender, receiver) = channel(); - run_local_faucet(alice, sender, None); - let faucet_addr = receiver.recv().unwrap(); - - let rpc_client = RpcClient::new_socket(leader_data.rpc); - let default_signer = Keypair::new(); - let default_offline_signer = Keypair::new(); - - let mut config_offline = CliConfig::recent_for_tests(); - config_offline.json_rpc_url = - format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port()); - config_offline.signers = vec![&default_offline_signer]; - let mut config_online = CliConfig::recent_for_tests(); - config_online.json_rpc_url = - format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port()); - config_online.signers = vec![&default_signer]; - assert_ne!( - config_offline.signers[0].pubkey(), - config_online.signers[0].pubkey() - ); - - request_and_confirm_airdrop( - &rpc_client, - &faucet_addr, - &config_offline.signers[0].pubkey(), - 50, - &config_offline, - ) - .unwrap(); - - request_and_confirm_airdrop( - &rpc_client, - &faucet_addr, - &config_online.signers[0].pubkey(), - 50, - &config_offline, - ) - .unwrap(); - check_recent_balance(50, &rpc_client, &config_offline.signers[0].pubkey()); - check_recent_balance(50, &rpc_client, &config_online.signers[0].pubkey()); - - let (blockhash, _) = rpc_client.get_recent_blockhash().unwrap(); - config_offline.command = CliCommand::Pay(PayCommand { - amount: SpendAmount::Some(10), - to: bob_pubkey, - blockhash_query: BlockhashQuery::None(blockhash), - sign_only: true, - ..PayCommand::default() - }); - config_offline.output_format = OutputFormat::JsonCompact; - let sig_response = process_command(&config_offline).unwrap(); - - check_recent_balance(50, &rpc_client, &config_offline.signers[0].pubkey()); - check_recent_balance(50, &rpc_client, &config_online.signers[0].pubkey()); - check_recent_balance(0, &rpc_client, &bob_pubkey); - - let sign_only = parse_sign_only_reply_string(&sig_response); - assert!(sign_only.has_all_signers()); - let offline_presigner = sign_only - .presigner_of(&config_offline.signers[0].pubkey()) - .unwrap(); - let online_pubkey = config_online.signers[0].pubkey(); - config_online.signers = vec![&offline_presigner]; - config_online.command = CliCommand::Pay(PayCommand { - amount: SpendAmount::Some(10), - to: bob_pubkey, - blockhash_query: BlockhashQuery::FeeCalculator(blockhash_query::Source::Cluster, blockhash), - ..PayCommand::default() - }); - process_command(&config_online).unwrap(); - - check_recent_balance(40, &rpc_client, &config_offline.signers[0].pubkey()); - check_recent_balance(50, &rpc_client, &online_pubkey); - check_recent_balance(10, &rpc_client, &bob_pubkey); - - server.close().unwrap(); - remove_dir_all(ledger_path).unwrap(); -} - -#[test] -fn test_nonced_pay_tx() { - solana_logger::setup(); - - let TestValidator { - server, - leader_data, - alice, - ledger_path, - .. - } = TestValidator::run(); - let (sender, receiver) = channel(); - run_local_faucet(alice, sender, None); - let faucet_addr = receiver.recv().unwrap(); - - let rpc_client = RpcClient::new_socket(leader_data.rpc); - let default_signer = Keypair::new(); - - let mut config = CliConfig::recent_for_tests(); - config.json_rpc_url = format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port()); - config.signers = vec![&default_signer]; - - let minimum_nonce_balance = rpc_client - .get_minimum_balance_for_rent_exemption(NonceState::size()) - .unwrap(); - - request_and_confirm_airdrop( - &rpc_client, - &faucet_addr, - &config.signers[0].pubkey(), - 50 + minimum_nonce_balance, - &config, - ) - .unwrap(); - check_recent_balance( - 50 + minimum_nonce_balance, - &rpc_client, - &config.signers[0].pubkey(), - ); - - // Create nonce account - let nonce_account = Keypair::new(); - config.command = CliCommand::CreateNonceAccount { - nonce_account: 1, - seed: None, - nonce_authority: Some(config.signers[0].pubkey()), - amount: SpendAmount::Some(minimum_nonce_balance), - }; - config.signers.push(&nonce_account); - process_command(&config).unwrap(); - - check_recent_balance(50, &rpc_client, &config.signers[0].pubkey()); - check_recent_balance(minimum_nonce_balance, &rpc_client, &nonce_account.pubkey()); - - // Fetch nonce hash - let nonce_hash = nonce::get_account_with_commitment( - &rpc_client, - &nonce_account.pubkey(), - CommitmentConfig::recent(), - ) - .and_then(|ref a| nonce::data_from_account(a)) - .unwrap() - .blockhash; - - let bob_pubkey = Pubkey::new_rand(); - config.signers = vec![&default_signer]; - config.command = CliCommand::Pay(PayCommand { - amount: SpendAmount::Some(10), - to: bob_pubkey, - blockhash_query: BlockhashQuery::FeeCalculator( - blockhash_query::Source::NonceAccount(nonce_account.pubkey()), - nonce_hash, - ), - nonce_account: Some(nonce_account.pubkey()), - ..PayCommand::default() - }); - process_command(&config).expect("failed to process pay command"); - - check_recent_balance(40, &rpc_client, &config.signers[0].pubkey()); - check_recent_balance(10, &rpc_client, &bob_pubkey); - - // Verify that nonce has been used - let nonce_hash2 = nonce::get_account_with_commitment( - &rpc_client, - &nonce_account.pubkey(), - CommitmentConfig::recent(), - ) - .and_then(|ref a| nonce::data_from_account(a)) - .unwrap() - .blockhash; - assert_ne!(nonce_hash, nonce_hash2); - - server.close().unwrap(); - remove_dir_all(ledger_path).unwrap(); -} diff --git a/core/Cargo.toml b/core/Cargo.toml index dfca764d6..2821926a8 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -46,7 +46,6 @@ serde_json = "1.0.56" solana-account-decoder = { path = "../account-decoder", version = "1.4.0" } solana-banks-server = { path = "../banks-server", version = "1.4.0" } solana-bpf-loader-program = { path = "../programs/bpf_loader", version = "1.4.0" } -solana-budget-program = { path = "../programs/budget", version = "1.4.0" } solana-clap-utils = { path = "../clap-utils", version = "1.4.0" } solana-client = { path = "../client", version = "1.4.0" } solana-faucet = { path = "../faucet", version = "1.4.0" } diff --git a/core/src/lib.rs b/core/src/lib.rs index 5b353ea24..0cc959420 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -74,9 +74,6 @@ pub mod window_service; #[macro_use] extern crate solana_bpf_loader_program; -#[macro_use] -extern crate solana_budget_program; - #[macro_use] extern crate log; diff --git a/core/src/rpc_pubsub.rs b/core/src/rpc_pubsub.rs index 31dcf30fd..6b0a43dde 100644 --- a/core/src/rpc_pubsub.rs +++ b/core/src/rpc_pubsub.rs @@ -359,7 +359,6 @@ mod tests { use jsonrpc_pubsub::{PubSubHandler, Session}; use serial_test_derive::serial; use solana_account_decoder::{parse_account_data::parse_account_data, UiAccountEncoding}; - use solana_budget_program::{self, budget_instruction}; use solana_runtime::{ bank::Bank, bank_forks::BankForks, @@ -377,6 +376,10 @@ mod tests { system_instruction, system_program, system_transaction, transaction::{self, Transaction}, }; + use solana_stake_program::{ + self, stake_instruction, + stake_state::{Authorized, Lockup, StakeAuthorize, StakeState}, + }; use solana_vote_program::vote_transaction; use std::{ sync::{atomic::AtomicBool, RwLock}, @@ -503,21 +506,16 @@ mod tests { #[serial] fn test_account_subscribe() { let GenesisConfigInfo { - mut genesis_config, + genesis_config, mint_keypair: alice, .. } = create_genesis_config(10_000); - // This test depends on the budget program - genesis_config - .native_instruction_processors - .push(solana_budget_program!()); - - let bob_pubkey = Pubkey::new_rand(); - let witness = Keypair::new(); - let contract_funds = Keypair::new(); - let contract_state = Keypair::new(); - let budget_program_id = solana_budget_program::id(); + let new_stake_authority = Pubkey::new_rand(); + let stake_authority = Keypair::new(); + let from = Keypair::new(); + let stake_account = Keypair::new(); + let stake_program_id = solana_stake_program::id(); let bank = Bank::new(&genesis_config); let blockhash = bank.last_blockhash(); let bank_forks = Arc::new(RwLock::new(BankForks::new(bank))); @@ -540,26 +538,26 @@ mod tests { rpc.account_subscribe( session, subscriber, - contract_state.pubkey().to_string(), + stake_account.pubkey().to_string(), Some(RpcAccountInfoConfig { commitment: Some(CommitmentConfig::recent()), encoding: None, }), ); - let tx = system_transaction::transfer(&alice, &contract_funds.pubkey(), 51, blockhash); + let tx = system_transaction::transfer(&alice, &from.pubkey(), 51, blockhash); process_transaction_and_notify(&bank_forks, &tx, &rpc.subscriptions, 1).unwrap(); - let ixs = budget_instruction::when_signed( - &contract_funds.pubkey(), - &bob_pubkey, - &contract_state.pubkey(), - &witness.pubkey(), - None, + let authorized = Authorized::auto(&stake_authority.pubkey()); + let ixs = stake_instruction::create_account( + &from.pubkey(), + &stake_account.pubkey(), + &authorized, + &Lockup::default(), 51, ); - let message = Message::new(&ixs, Some(&contract_funds.pubkey())); - let tx = Transaction::new(&[&contract_funds, &contract_state], message, blockhash); + let message = Message::new(&ixs, Some(&from.pubkey())); + let tx = Transaction::new(&[&from, &stake_account], message, blockhash); process_transaction_and_notify(&bank_forks, &tx, &rpc.subscriptions, 1).unwrap(); sleep(Duration::from_millis(200)); @@ -569,7 +567,7 @@ mod tests { .unwrap() .get(1) .unwrap() - .get_account(&contract_state.pubkey()) + .get_account(&stake_account.pubkey()) .unwrap() .data; let expected = json!({ @@ -579,7 +577,7 @@ mod tests { "result": { "context": { "slot": 1 }, "value": { - "owner": budget_program_id.to_string(), + "owner": stake_program_id.to_string(), "lamports": 51, "data": bs58::encode(expected_data).into_string(), "executable": false, @@ -593,27 +591,25 @@ mod tests { let (response, _) = robust_poll_or_panic(receiver); assert_eq!(serde_json::to_string(&expected).unwrap(), response); - let tx = system_transaction::transfer(&alice, &witness.pubkey(), 1, blockhash); + let tx = system_transaction::transfer(&alice, &stake_authority.pubkey(), 1, blockhash); process_transaction_and_notify(&bank_forks, &tx, &rpc.subscriptions, 1).unwrap(); sleep(Duration::from_millis(200)); - let ix = budget_instruction::apply_signature( - &witness.pubkey(), - &contract_state.pubkey(), - &bob_pubkey, + let ix = stake_instruction::authorize( + &stake_account.pubkey(), + &stake_authority.pubkey(), + &new_stake_authority, + StakeAuthorize::Staker, ); - let message = Message::new(&[ix], Some(&witness.pubkey())); - let tx = Transaction::new(&[&witness], message, blockhash); + let message = Message::new(&[ix], Some(&stake_authority.pubkey())); + let tx = Transaction::new(&[&stake_authority], message, blockhash); process_transaction_and_notify(&bank_forks, &tx, &rpc.subscriptions, 1).unwrap(); sleep(Duration::from_millis(200)); + let bank = bank_forks.read().unwrap()[1].clone(); + let account = bank.get_account(&stake_account.pubkey()).unwrap(); assert_eq!( - bank_forks - .read() - .unwrap() - .get(1) - .unwrap() - .get_account(&contract_state.pubkey()), - None + StakeState::authorized_from(&account).unwrap().staker, + new_stake_authority ); } diff --git a/core/src/rpc_subscriptions.rs b/core/src/rpc_subscriptions.rs index 35a030c19..499391377 100644 --- a/core/src/rpc_subscriptions.rs +++ b/core/src/rpc_subscriptions.rs @@ -1044,7 +1044,7 @@ pub(crate) mod tests { blockhash, 1, 16, - &solana_budget_program::id(), + &solana_stake_program::id(), ); bank_forks .write() @@ -1067,7 +1067,7 @@ pub(crate) mod tests { "data": "1111111111111111", "executable": false, "lamports": 1, - "owner": "Budget1111111111111111111111111111111111111", + "owner": "Stake11111111111111111111111111111111111111", "rentEpoch": 1, }, }, @@ -1103,7 +1103,7 @@ pub(crate) mod tests { blockhash, 1, 16, - &solana_budget_program::id(), + &solana_stake_program::id(), ); bank_forks .write() @@ -1123,7 +1123,7 @@ pub(crate) mod tests { Arc::new(RwLock::new(BlockCommitmentCache::new_for_tests())), ); subscriptions.add_program_subscription( - solana_budget_program::id(), + solana_stake_program::id(), None, sub_id.clone(), subscriber, @@ -1134,7 +1134,7 @@ pub(crate) mod tests { .program_subscriptions .read() .unwrap() - .contains_key(&solana_budget_program::id())); + .contains_key(&solana_stake_program::id())); subscriptions.notify_subscribers(CommitmentSlots::default()); let (response, _) = robust_poll_or_panic(transport_receiver); @@ -1149,7 +1149,7 @@ pub(crate) mod tests { "data": "1111111111111111", "executable": false, "lamports": 1, - "owner": "Budget1111111111111111111111111111111111111", + "owner": "Stake11111111111111111111111111111111111111", "rentEpoch": 1, }, "pubkey": alice.pubkey().to_string(), @@ -1166,7 +1166,7 @@ pub(crate) mod tests { .program_subscriptions .read() .unwrap() - .contains_key(&solana_budget_program::id())); + .contains_key(&solana_stake_program::id())); } #[test] @@ -1528,7 +1528,7 @@ pub(crate) mod tests { blockhash, 1, 16, - &solana_budget_program::id(), + &solana_stake_program::id(), ); // Add the transaction to the 1st bank and then freeze the bank @@ -1562,7 +1562,7 @@ pub(crate) mod tests { "data": "1111111111111111", "executable": false, "lamports": 1, - "owner": "Budget1111111111111111111111111111111111111", + "owner": "Stake11111111111111111111111111111111111111", "rentEpoch": 1, }, }, @@ -1595,7 +1595,7 @@ pub(crate) mod tests { "data": "1111111111111111", "executable": false, "lamports": 1, - "owner": "Budget1111111111111111111111111111111111111", + "owner": "Stake11111111111111111111111111111111111111", "rentEpoch": 1, }, }, diff --git a/core/src/validator.rs b/core/src/validator.rs index a10abae72..32983d80c 100644 --- a/core/src/validator.rs +++ b/core/src/validator.rs @@ -868,9 +868,6 @@ impl TestValidator { 42, bootstrap_validator_lamports, ); - genesis_config - .native_instruction_processors - .push(solana_budget_program!()); genesis_config .native_instruction_processors .push(solana_bpf_loader_program!()); diff --git a/docs/src/cli/.usage.md.header b/docs/src/cli/.usage.md.header index ead6b6492..b81231c57 100644 --- a/docs/src/cli/.usage.md.header +++ b/docs/src/cli/.usage.md.header @@ -56,118 +56,4 @@ $ solana deploy ``` -### Unconditional Immediate Transfer - -```bash -// Command -$ solana pay 123 - -// Return - -``` - -### Post-Dated Transfer - -```bash -// Command -$ solana pay 123 \ - --after 2018-12-24T23:59:00 --require-timestamp-from - -// Return -{signature: , processId: } -``` - -_`require-timestamp-from` is optional. If not provided, the transaction will expect a timestamp signed by this wallet's private key_ - -### Authorized Transfer - -A third party must send a signature to unlock the lamports. - -```bash -// Command -$ solana pay 123 \ - --require-signature-from - -// Return -{signature: , processId: } -``` - -### Post-Dated and Authorized Transfer - -```bash -// Command -$ solana pay 123 \ - --after 2018-12-24T23:59 --require-timestamp-from \ - --require-signature-from - -// Return -{signature: , processId: } -``` - -### Multiple Witnesses - -```bash -// Command -$ solana pay 123 \ - --require-signature-from \ - --require-signature-from - -// Return -{signature: , processId: } -``` - -### Cancelable Transfer - -```bash -// Command -$ solana pay 123 \ - --require-signature-from \ - --cancelable - -// Return -{signature: , processId: } -``` - -### Cancel Transfer - -```bash -// Command -$ solana cancel - -// Return - -``` - -### Send Signature - -```bash -// Command -$ solana send-signature - -// Return - -``` - -### Indicate Elapsed Time - -Use the current system time: - -```bash -// Command -$ solana send-timestamp - -// Return - -``` - -Or specify some other arbitrary timestamp: - -```bash -// Command -$ solana send-timestamp --date 2018-12-24T23:59:00 - -// Return - -``` - ## Usage diff --git a/web3.js/examples/budget-common.js b/web3.js/examples/budget-common.js deleted file mode 100644 index d55b2fbcf..000000000 --- a/web3.js/examples/budget-common.js +++ /dev/null @@ -1,67 +0,0 @@ -/* eslint-disable import/no-commonjs */ - -/* - Common code for the budget program examples -*/ - -function getTransactionFee(connection) { - return connection.getRecentBlockhash().then(response => { - return response.feeCalculator; - }); -} - -function showBalance(connection, account1, account2, contractState) { - console.log(`\n== Account State`); - return Promise.all([ - connection.getBalance(account1.publicKey), - connection.getBalance(account2.publicKey), - connection.getBalance(contractState.publicKey), - ]).then(([fromBalance, toBalance, contractStateBalance]) => { - console.log( - `Account1: ${account1.publicKey} has a balance of ${fromBalance}`, - ); - console.log( - `Account2: ${account2.publicKey} has a balance of ${toBalance}`, - ); - console.log( - `Contract State: ${contractState.publicKey} has a balance of ${contractStateBalance}`, - ); - }); -} - -function confirmTransaction(connection, signature) { - console.log('Confirming transaction:', signature); - return connection.getSignatureStatus(signature).then(confirmation => { - if (confirmation && 'Ok' in confirmation) { - console.log('Transaction confirmed'); - } else if (confirmation) { - throw new Error( - `Transaction was not confirmed (${JSON.stringify(confirmation.Err)})`, - ); - } else { - throw new Error(`Transaction was not confirmed (${confirmation})`); - } - }); -} - -function airDrop(connection, account, feeCalculator) { - const airdrop = 100 + 5 * feeCalculator.targetLamportsPerSignature; - console.log(`\n== Requesting airdrop of ${airdrop} to ${account.publicKey}`); - return connection - .requestAirdrop(account.publicKey, airdrop) - .then(signature => confirmTransaction(connection, signature)); -} - -function sleep(millis) { - return new Promise(resolve => { - setTimeout(resolve, millis); - }); -} - -module.exports = { - airDrop, - confirmTransaction, - getTransactionFee, - showBalance, - sleep, -}; diff --git a/web3.js/examples/budget-timestamp.js b/web3.js/examples/budget-timestamp.js deleted file mode 100644 index 65c2b2aaa..000000000 --- a/web3.js/examples/budget-timestamp.js +++ /dev/null @@ -1,76 +0,0 @@ -/* eslint-disable import/no-commonjs */ - -/* - Example of using the Budget program to perform a time-lock payment of 50 - lamports from account1 to account2. -*/ - -const common = require('./budget-common'); -const solanaWeb3 = require('..'); -//const solanaWeb3 = require('@solana/web3.js'); - -const account1 = new solanaWeb3.Account(); -const account2 = new solanaWeb3.Account(); -const contractState = new solanaWeb3.Account(); - -let url; -url = 'http://localhost:8899'; -const connection = new solanaWeb3.Connection(url, 'recent'); -const getTransactionFee = () => common.getTransactionFee(connection); -const showBalance = () => - common.showBalance(connection, account1, account2, contractState); -const confirmTransaction = signature => - common.confirmTransaction(connection, signature); -const airDrop = feeCalculator => - common.airDrop(connection, account1, feeCalculator); - -getTransactionFee().then(feeCalculator => { - airDrop(feeCalculator) - .then(showBalance) - .then(() => { - console.log(`\n== Initializing contract`); - const transaction = solanaWeb3.BudgetProgram.pay( - account1.publicKey, - contractState.publicKey, - account2.publicKey, - 50, - solanaWeb3.BudgetProgram.timestampCondition( - account1.publicKey, - new Date('2050'), - ), - ); - return solanaWeb3.sendAndConfirmTransaction( - connection, - transaction, - account1, - contractState, - ); - }) - .then(confirmTransaction) - .then(showBalance) - .then(() => { - console.log(`\n== Witness contract`); - const transaction = solanaWeb3.BudgetProgram.applyTimestamp( - account1.publicKey, - contractState.publicKey, - account2.publicKey, - new Date('2050'), - ); - return solanaWeb3.sendAndConfirmTransaction( - connection, - transaction, - account1, - contractState, - ); - }) - .then(confirmTransaction) - .then(showBalance) - - .then(() => { - console.log('\nDone'); - }) - - .catch(err => { - console.log(err); - }); -}); diff --git a/web3.js/examples/budget-two-approvers.js b/web3.js/examples/budget-two-approvers.js deleted file mode 100644 index 4fa7fe342..000000000 --- a/web3.js/examples/budget-two-approvers.js +++ /dev/null @@ -1,119 +0,0 @@ -/* eslint-disable import/no-commonjs */ - -/* - Example of using the Budget program to perform a payment authorized by two parties -*/ - -const common = require('./budget-common'); -const solanaWeb3 = require('..'); -//const solanaWeb3 = require('@solana/web3.js'); - -const account1 = new solanaWeb3.Account(); -const account2 = new solanaWeb3.Account(); -const contractState = new solanaWeb3.Account(); - -const approver1 = new solanaWeb3.Account(); -const approver2 = new solanaWeb3.Account(); - -let url; -url = 'http://localhost:8899'; -//url = 'http://devnet.solana.com'; -const connection = new solanaWeb3.Connection(url, 'recent'); -const getTransactionFee = () => common.getTransactionFee(connection); -const showBalance = () => - common.showBalance(connection, account1, account2, contractState); -const confirmTransaction = signature => - common.confirmTransaction(connection, signature); -const airDrop = feeCalculator => - common.airDrop(connection, account1, feeCalculator); - -getTransactionFee().then(feeCalculator => { - airDrop(feeCalculator) - .then(() => { - console.log(`\n== Move 1 lamport to approver1`); - const transaction = solanaWeb3.SystemProgram.transfer( - account1.publicKey, - approver1.publicKey, - 1 + feeCalculator.lamportsPerSignature, - ); - return solanaWeb3.sendAndConfirmTransaction( - connection, - transaction, - account1, - ); - }) - .then(confirmTransaction) - .then(getTransactionFee) - .then(() => { - console.log(`\n== Move 1 lamport to approver2`); - const transaction = solanaWeb3.SystemProgram.transfer( - account1.publicKey, - approver2.publicKey, - 1 + feeCalculator.lamportsPerSignature, - ); - return solanaWeb3.sendAndConfirmTransaction( - connection, - transaction, - account1, - ); - }) - .then(confirmTransaction) - .then(showBalance) - .then(() => { - console.log(`\n== Initializing contract`); - const transaction = solanaWeb3.BudgetProgram.payOnBoth( - account1.publicKey, - contractState.publicKey, - account2.publicKey, - 50, - solanaWeb3.BudgetProgram.signatureCondition(approver1.publicKey), - solanaWeb3.BudgetProgram.signatureCondition(approver2.publicKey), - ); - return solanaWeb3.sendAndConfirmTransaction( - connection, - transaction, - account1, - contractState, - ); - }) - .then(confirmTransaction) - .then(showBalance) - .then(() => { - console.log(`\n== Apply approver 1`); - const transaction = solanaWeb3.BudgetProgram.applySignature( - approver1.publicKey, - contractState.publicKey, - account2.publicKey, - ); - return solanaWeb3.sendAndConfirmTransaction( - connection, - transaction, - approver1, - ); - }) - .then(confirmTransaction) - .then(showBalance) - .then(() => { - console.log(`\n== Apply approver 2`); - const transaction = solanaWeb3.BudgetProgram.applySignature( - approver2.publicKey, - contractState.publicKey, - account2.publicKey, - ); - return solanaWeb3.sendAndConfirmTransaction( - connection, - transaction, - approver2, - ); - }) - .then(confirmTransaction) - .then(showBalance) - - .then(() => { - console.log('\nDone'); - }) - - .catch(err => { - console.log(err); - }); -}); diff --git a/web3.js/module.d.ts b/web3.js/module.d.ts index 75fb9ca6c..51c3ca592 100644 --- a/web3.js/module.d.ts +++ b/web3.js/module.d.ts @@ -32,10 +32,6 @@ declare module '@solana/web3.js' { lamportsPerSignature: number; }; - // === src/budget-program.js === - - /* TODO */ - // === src/connection.js === export type Context = { slot: number; diff --git a/web3.js/module.flow.js b/web3.js/module.flow.js index 97220f417..59fe94f0f 100644 --- a/web3.js/module.flow.js +++ b/web3.js/module.flow.js @@ -54,9 +54,6 @@ declare module '@solana/web3.js' { lamportsPerSignature: number, }; - // === src/budget-program.js === - /* TODO */ - // === src/connection.js === declare export type Context = { slot: number, diff --git a/web3.js/src/budget-program.js b/web3.js/src/budget-program.js deleted file mode 100644 index 9275a487f..000000000 --- a/web3.js/src/budget-program.js +++ /dev/null @@ -1,390 +0,0 @@ -// @flow - -import * as BufferLayout from 'buffer-layout'; - -import {Transaction} from './transaction'; -import {PublicKey} from './publickey'; -import {SystemProgram} from './system-program'; - -/** - * Represents a condition that is met by executing a `applySignature()` - * transaction - * - * @typedef {Object} SignatureCondition - * @property {string} type Must equal the string 'timestamp' - * @property {PublicKey} from Public key from which `applySignature()` will be accepted from - */ -export type SignatureCondition = { - type: 'signature', - from: PublicKey, -}; - -/** - * Represents a condition that is met by executing a `applyTimestamp()` - * transaction - * - * @typedef {Object} TimestampCondition - * @property {string} type Must equal the string 'timestamp' - * @property {PublicKey} from Public key from which `applyTimestamp()` will be accepted from - * @property {Date} when The timestamp that was observed - */ -export type TimestampCondition = { - type: 'timestamp', - from: PublicKey, - when: Date, -}; - -/** - * Represents a payment to a given public key - * - * @typedef {Object} Payment - * @property {number} amount Number of lamports - * @property {PublicKey} to Public key of the recipient - */ -export type Payment = { - amount: number, - to: PublicKey, -}; - -/** - * A condition that can unlock a payment - * - * @typedef {SignatureCondition|TimestampCondition} BudgetCondition - */ -export type BudgetCondition = SignatureCondition | TimestampCondition; - -/** - * @private - */ -function serializePayment(payment: Payment): Buffer { - const toData = payment.to.toBuffer(); - const data = Buffer.alloc(8 + toData.length); - data.writeUInt32LE(payment.amount, 0); - toData.copy(data, 8); - return data; -} - -/** - * @private - */ -function serializeDate(when: Date): Buffer { - const data = Buffer.alloc(8 + 20); - data.writeUInt32LE(20, 0); // size of timestamp as u64 - - function iso(date) { - function pad(number) { - if (number < 10) { - return '0' + number; - } - return number; - } - - return ( - date.getUTCFullYear() + - '-' + - pad(date.getUTCMonth() + 1) + - '-' + - pad(date.getUTCDate()) + - 'T' + - pad(date.getUTCHours()) + - ':' + - pad(date.getUTCMinutes()) + - ':' + - pad(date.getUTCSeconds()) + - 'Z' - ); - } - data.write(iso(when), 8); - return data; -} - -/** - * @private - */ -function serializeCondition(condition: BudgetCondition) { - switch (condition.type) { - case 'timestamp': { - const date = serializeDate(condition.when); - const from = condition.from.toBuffer(); - - const data = Buffer.alloc(4 + date.length + from.length); - data.writeUInt32LE(0, 0); // Condition enum = Timestamp - date.copy(data, 4); - from.copy(data, 4 + date.length); - return data; - } - case 'signature': { - const from = condition.from.toBuffer(); - const data = Buffer.alloc(4 + from.length); - data.writeUInt32LE(1, 0); // Condition enum = Signature - from.copy(data, 4); - return data; - } - default: - throw new Error(`Unknown condition type: ${condition.type}`); - } -} - -/** - * Factory class for transactions to interact with the Budget program - */ -export class BudgetProgram { - /** - * Public key that identifies the Budget program - */ - static get programId(): PublicKey { - return new PublicKey('Budget1111111111111111111111111111111111111'); - } - - /** - * The amount of space this program requires - */ - static get space(): number { - return 128; - } - - /** - * Creates a timestamp condition - */ - static timestampCondition(from: PublicKey, when: Date): TimestampCondition { - return { - type: 'timestamp', - from, - when, - }; - } - - /** - * Creates a signature condition - */ - static signatureCondition(from: PublicKey): SignatureCondition { - return { - type: 'signature', - from, - }; - } - - /** - * Generates a transaction that transfers lamports once any of the conditions are met - */ - static pay( - from: PublicKey, - program: PublicKey, - to: PublicKey, - amount: number, - ...conditions: Array - ): Transaction { - const data = Buffer.alloc(1024); - let pos = 0; - data.writeUInt32LE(0, pos); // NewBudget instruction - pos += 4; - - switch (conditions.length) { - case 0: { - data.writeUInt32LE(0, pos); // BudgetExpr enum = Pay - pos += 4; - - { - const payment = serializePayment({amount, to}); - payment.copy(data, pos); - pos += payment.length; - } - const trimmedData = data.slice(0, pos); - - const transaction = SystemProgram.createAccount({ - fromPubkey: from, - newAccountPubkey: program, - lamports: amount, - space: trimmedData.length, - programId: this.programId, - }); - - return transaction.add({ - keys: [ - {pubkey: to, isSigner: false, isWritable: true}, - {pubkey: program, isSigner: false, isWritable: true}, - ], - programId: this.programId, - data: trimmedData, - }); - } - case 1: { - data.writeUInt32LE(1, pos); // BudgetExpr enum = After - pos += 4; - { - const condition = conditions[0]; - - const conditionData = serializeCondition(condition); - conditionData.copy(data, pos); - pos += conditionData.length; - - data.writeUInt32LE(0, pos); // BudgetExpr enum = Pay - pos += 4; - - const paymentData = serializePayment({amount, to}); - paymentData.copy(data, pos); - pos += paymentData.length; - } - const trimmedData = data.slice(0, pos); - - const transaction = SystemProgram.createAccount({ - fromPubkey: from, - newAccountPubkey: program, - lamports: amount, - space: trimmedData.length, - programId: this.programId, - }); - - return transaction.add({ - keys: [{pubkey: program, isSigner: false, isWritable: true}], - programId: this.programId, - data: trimmedData, - }); - } - - case 2: { - data.writeUInt32LE(2, pos); // BudgetExpr enum = Or - pos += 4; - - for (let condition of conditions) { - const conditionData = serializeCondition(condition); - conditionData.copy(data, pos); - pos += conditionData.length; - - data.writeUInt32LE(0, pos); // BudgetExpr enum = Pay - pos += 4; - - const paymentData = serializePayment({amount, to}); - paymentData.copy(data, pos); - pos += paymentData.length; - } - const trimmedData = data.slice(0, pos); - - const transaction = SystemProgram.createAccount({ - fromPubkey: from, - newAccountPubkey: program, - lamports: amount, - space: trimmedData.length, - programId: this.programId, - }); - - return transaction.add({ - keys: [{pubkey: program, isSigner: false, isWritable: true}], - programId: this.programId, - data: trimmedData, - }); - } - - default: - throw new Error( - `A maximum of two conditions are supported: ${conditions.length} provided`, - ); - } - } - - /** - * Generates a transaction that transfers lamports once both conditions are met - */ - static payOnBoth( - from: PublicKey, - program: PublicKey, - to: PublicKey, - amount: number, - condition1: BudgetCondition, - condition2: BudgetCondition, - ): Transaction { - const data = Buffer.alloc(1024); - let pos = 0; - data.writeUInt32LE(0, pos); // NewBudget instruction - pos += 4; - - data.writeUInt32LE(3, pos); // BudgetExpr enum = And - pos += 4; - - for (let condition of [condition1, condition2]) { - const conditionData = serializeCondition(condition); - conditionData.copy(data, pos); - pos += conditionData.length; - } - - data.writeUInt32LE(0, pos); // BudgetExpr enum = Pay - pos += 4; - - const paymentData = serializePayment({amount, to}); - paymentData.copy(data, pos); - pos += paymentData.length; - - const trimmedData = data.slice(0, pos); - - const transaction = SystemProgram.createAccount({ - fromPubkey: from, - newAccountPubkey: program, - lamports: amount, - space: trimmedData.length, - programId: this.programId, - }); - - return transaction.add({ - keys: [{pubkey: program, isSigner: false, isWritable: true}], - programId: this.programId, - data: trimmedData, - }); - } - - /** - * Generates a transaction that applies a timestamp, which could enable a - * pending payment to proceed. - */ - static applyTimestamp( - from: PublicKey, - program: PublicKey, - to: PublicKey, - when: Date, - ): Transaction { - const whenData = serializeDate(when); - const data = Buffer.alloc(4 + whenData.length); - - data.writeUInt32LE(1, 0); // ApplyTimestamp instruction - whenData.copy(data, 4); - - return new Transaction().add({ - keys: [ - {pubkey: from, isSigner: true, isWritable: true}, - {pubkey: program, isSigner: false, isWritable: true}, - {pubkey: to, isSigner: false, isWritable: true}, - ], - programId: this.programId, - data, - }); - } - - /** - * Generates a transaction that applies a signature, which could enable a - * pending payment to proceed. - */ - static applySignature( - from: PublicKey, - program: PublicKey, - to: PublicKey, - ): Transaction { - const dataLayout = BufferLayout.struct([BufferLayout.u32('instruction')]); - - const data = Buffer.alloc(dataLayout.span); - dataLayout.encode( - { - instruction: 2, // ApplySignature instruction - }, - data, - ); - - return new Transaction().add({ - keys: [ - {pubkey: from, isSigner: true, isWritable: true}, - {pubkey: program, isSigner: false, isWritable: true}, - {pubkey: to, isSigner: false, isWritable: true}, - ], - programId: this.programId, - data, - }); - } -} diff --git a/web3.js/src/index.js b/web3.js/src/index.js index 4b8eb56c3..d883b3c4e 100644 --- a/web3.js/src/index.js +++ b/web3.js/src/index.js @@ -1,7 +1,6 @@ // @flow export {Account} from './account'; export {BpfLoader} from './bpf-loader'; -export {BudgetProgram} from './budget-program'; export {Connection} from './connection'; export {Loader} from './loader'; export {Message} from './message'; diff --git a/web3.js/test/budget-program.test.js b/web3.js/test/budget-program.test.js deleted file mode 100644 index b9431db86..000000000 --- a/web3.js/test/budget-program.test.js +++ /dev/null @@ -1,80 +0,0 @@ -// @flow - -import {Account} from '../src/account'; -import {BudgetProgram} from '../src/budget-program'; - -test('pay', () => { - const from = new Account(); - const program = new Account(); - const to = new Account(); - let transaction; - - transaction = BudgetProgram.pay( - from.publicKey, - program.publicKey, - to.publicKey, - 123, - ); - expect(transaction.instructions[0].keys).toHaveLength(2); - expect(transaction.instructions[1].keys).toHaveLength(2); - // TODO: Validate transaction contents more - - transaction = BudgetProgram.pay( - from.publicKey, - program.publicKey, - to.publicKey, - 123, - BudgetProgram.signatureCondition(from.publicKey), - ); - expect(transaction.instructions[0].keys).toHaveLength(2); - expect(transaction.instructions[1].keys).toHaveLength(1); - // TODO: Validate transaction contents more - - transaction = BudgetProgram.pay( - from.publicKey, - program.publicKey, - to.publicKey, - 123, - BudgetProgram.signatureCondition(from.publicKey), - BudgetProgram.timestampCondition(from.publicKey, new Date()), - ); - expect(transaction.instructions[0].keys).toHaveLength(2); - expect(transaction.instructions[1].keys).toHaveLength(1); - // TODO: Validate transaction contents more - - transaction = BudgetProgram.payOnBoth( - from.publicKey, - program.publicKey, - to.publicKey, - 123, - BudgetProgram.signatureCondition(from.publicKey), - BudgetProgram.timestampCondition(from.publicKey, new Date()), - ); - expect(transaction.instructions[0].keys).toHaveLength(2); - expect(transaction.instructions[1].keys).toHaveLength(1); - // TODO: Validate transaction contents more -}); - -test('apply', () => { - const from = new Account(); - const program = new Account(); - const to = new Account(); - let transaction; - - transaction = BudgetProgram.applyTimestamp( - from.publicKey, - program.publicKey, - to.publicKey, - new Date(), - ); - expect(transaction.keys).toHaveLength(3); - // TODO: Validate transaction contents more - - transaction = BudgetProgram.applySignature( - from.publicKey, - program.publicKey, - to.publicKey, - ); - expect(transaction.keys).toHaveLength(3); - // TODO: Validate transaction contents more -}); diff --git a/web3.js/test/system-program.test.js b/web3.js/test/system-program.test.js index 995b322b3..741bd0ff6 100644 --- a/web3.js/test/system-program.test.js +++ b/web3.js/test/system-program.test.js @@ -2,8 +2,8 @@ import { Account, - BudgetProgram, Connection, + StakeProgram, SystemInstruction, SystemProgram, Transaction, @@ -26,8 +26,8 @@ test('createAccount', () => { fromPubkey: new Account().publicKey, newAccountPubkey: new Account().publicKey, lamports: 123, - space: BudgetProgram.space, - programId: BudgetProgram.programId, + space: 0, + programId: SystemProgram.programId, }; const transaction = SystemProgram.createAccount(params); expect(transaction.instructions).toHaveLength(1); @@ -110,8 +110,8 @@ test('createAccountWithSeed', () => { basePubkey: fromPubkey, seed: 'hi there', lamports: 123, - space: BudgetProgram.space, - programId: BudgetProgram.programId, + space: 0, + programId: SystemProgram.programId, }; const transaction = SystemProgram.createAccountWithSeed(params); expect(transaction.instructions).toHaveLength(1); @@ -228,7 +228,6 @@ test('nonceAuthorize', () => { test('non-SystemInstruction error', () => { const from = new Account(); - const program = new Account(); const to = new Account(); const badProgramId = { @@ -236,7 +235,7 @@ test('non-SystemInstruction error', () => { {pubkey: from.publicKey, isSigner: true, isWritable: true}, {pubkey: to.publicKey, isSigner: false, isWritable: true}, ], - programId: BudgetProgram.programId, + programId: StakeProgram.programId, data: Buffer.from([2, 0, 0, 0]), }; expect(() => { @@ -245,16 +244,10 @@ test('non-SystemInstruction error', () => { ); }).toThrow(); - const amount = 123; - const recentBlockhash = 'EETubP5AKHgjPAhzPAFcb8BAY1hMH639CWCFTqi3hq1k'; // Arbitrary known recentBlockhash - const budgetPay = BudgetProgram.pay( - from.publicKey, - program.publicKey, - to.publicKey, - amount, - ); - const transaction = new Transaction({recentBlockhash}).add(budgetPay); - transaction.sign(from); + const stakePubkey = new Account().publicKey; + const authorizedPubkey = new Account().publicKey; + const params = {stakePubkey, authorizedPubkey}; + const transaction = StakeProgram.deactivate(params); expect(() => { SystemInstruction.decodeInstructionType(transaction.instructions[1]);