diff --git a/cli/src/program.rs b/cli/src/program.rs index 1b3431968c..a58c33518a 100644 --- a/cli/src/program.rs +++ b/cli/src/program.rs @@ -60,6 +60,11 @@ use { }, }; +pub const CLOSE_PROGRAM_WARNING: &str = "WARNING! \ +Closed programs cannot be recreated at the same program id. \ +Once a program is closed, it can never be invoked again. \ +To proceed with closing, rerun the `close` command with the `--bypass-warning` flag"; + #[derive(Debug, PartialEq, Eq)] pub enum ProgramCliCommand { Deploy { @@ -109,6 +114,7 @@ pub enum ProgramCliCommand { recipient_pubkey: Pubkey, authority_index: SignerIndex, use_lamports_unit: bool, + bypass_warning: bool, }, } @@ -386,6 +392,12 @@ impl ProgramSubCommands for App<'_, '_> { .long("lamports") .takes_value(false) .help("Display balance in lamports instead of SOL"), + ) + .arg( + Arg::with_name("bypass_warning") + .long("bypass-warning") + .takes_value(false) + .help("Bypass the permanent program closure warning"), ), ) ) @@ -674,6 +686,7 @@ pub fn parse_program_subcommand( recipient_pubkey, authority_index: signer_info.index_of(authority_pubkey).unwrap(), use_lamports_unit: matches.is_present("lamports"), + bypass_warning: matches.is_present("bypass_warning"), }), signers: signer_info.signers, } @@ -781,6 +794,7 @@ pub fn process_program_subcommand( recipient_pubkey, authority_index, use_lamports_unit, + bypass_warning, } => process_close( &rpc_client, config, @@ -788,6 +802,7 @@ pub fn process_program_subcommand( *recipient_pubkey, *authority_index, *use_lamports_unit, + *bypass_warning, ), } } @@ -1553,6 +1568,7 @@ fn process_close( recipient_pubkey: Pubkey, authority_index: SignerIndex, use_lamports_unit: bool, + bypass_warning: bool, ) -> ProcessResult { let authority_signer = config.signers[authority_index]; @@ -1615,6 +1631,9 @@ fn process_close( ) .into()) } else { + if !bypass_warning { + return Err(String::from(CLOSE_PROGRAM_WARNING).into()); + } close( rpc_client, config, @@ -3010,6 +3029,30 @@ mod tests { recipient_pubkey: default_keypair.pubkey(), authority_index: 0, use_lamports_unit: false, + bypass_warning: false, + }), + signers: vec![read_keypair_file(&keypair_file).unwrap().into()], + } + ); + + // with bypass-warning + write_keypair_file(&authority_keypair, &authority_keypair_file).unwrap(); + let test_command = test_commands.clone().get_matches_from(vec![ + "test", + "program", + "close", + &buffer_pubkey.to_string(), + "--bypass-warning", + ]); + assert_eq!( + parse_command(&test_command, &default_signer, &mut None).unwrap(), + CliCommandInfo { + command: CliCommand::Program(ProgramCliCommand::Close { + account_pubkey: Some(buffer_pubkey), + recipient_pubkey: default_keypair.pubkey(), + authority_index: 0, + use_lamports_unit: false, + bypass_warning: true, }), signers: vec![read_keypair_file(&keypair_file).unwrap().into()], } @@ -3033,6 +3076,7 @@ mod tests { recipient_pubkey: default_keypair.pubkey(), authority_index: 1, use_lamports_unit: false, + bypass_warning: false, }), signers: vec![ read_keypair_file(&keypair_file).unwrap().into(), @@ -3058,6 +3102,7 @@ mod tests { recipient_pubkey, authority_index: 0, use_lamports_unit: false, + bypass_warning: false, }), signers: vec![read_keypair_file(&keypair_file).unwrap().into(),], } @@ -3079,6 +3124,7 @@ mod tests { recipient_pubkey: default_keypair.pubkey(), authority_index: 0, use_lamports_unit: true, + bypass_warning: false, }), signers: vec![read_keypair_file(&keypair_file).unwrap().into(),], } diff --git a/cli/tests/program.rs b/cli/tests/program.rs index 6dbae5cefc..315e9213f0 100644 --- a/cli/tests/program.rs +++ b/cli/tests/program.rs @@ -1,9 +1,10 @@ #![allow(clippy::integer_arithmetic)] + use { serde_json::Value, solana_cli::{ cli::{process_command, CliCommand, CliConfig}, - program::ProgramCliCommand, + program::{ProgramCliCommand, CLOSE_PROGRAM_WARNING}, }, solana_cli_output::OutputFormat, solana_client::rpc_client::RpcClient, @@ -638,13 +639,30 @@ fn test_cli_program_close_program() { let programdata_lamports = close_account.lamports; let recipient_pubkey = Pubkey::new_unique(); config.signers = vec![&keypair, &upgrade_authority]; + + // Close without --bypass-warning flag config.command = CliCommand::Program(ProgramCliCommand::Close { account_pubkey: Some(program_keypair.pubkey()), recipient_pubkey, authority_index: 1, use_lamports_unit: false, + bypass_warning: false, + }); + assert_eq!( + process_command(&config).unwrap_err().to_string(), + CLOSE_PROGRAM_WARNING.to_string() + ); + + // Close with --bypass-warning flag + config.command = CliCommand::Program(ProgramCliCommand::Close { + account_pubkey: Some(program_keypair.pubkey()), + recipient_pubkey, + authority_index: 1, + use_lamports_unit: false, + bypass_warning: true, }); process_command(&config).unwrap(); + rpc_client.get_account(&programdata_pubkey).unwrap_err(); let recipient_account = rpc_client.get_account(&recipient_pubkey).unwrap(); assert_eq!(programdata_lamports, recipient_account.lamports); @@ -902,6 +920,7 @@ fn test_cli_program_write_buffer() { recipient_pubkey, authority_index: 1, use_lamports_unit: false, + bypass_warning: false, }); process_command(&config).unwrap(); rpc_client.get_account(&buffer_pubkey).unwrap_err(); @@ -938,6 +957,7 @@ fn test_cli_program_write_buffer() { recipient_pubkey: keypair.pubkey(), authority_index: 0, use_lamports_unit: false, + bypass_warning: false, }); process_command(&config).unwrap(); rpc_client.get_account(&new_buffer_pubkey).unwrap_err();