diff --git a/cli-output/src/cli_output.rs b/cli-output/src/cli_output.rs index 8fccda2e96..dfdda27be3 100644 --- a/cli-output/src/cli_output.rs +++ b/cli-output/src/cli_output.rs @@ -1557,7 +1557,7 @@ impl fmt::Display for CliUpgradeableProgram { pub struct CliUpgradeableBuffer { pub address: String, pub authority: String, - pub program_len: usize, + pub data_len: usize, } impl QuietDisplay for CliUpgradeableBuffer {} impl VerboseDisplay for CliUpgradeableBuffer {} @@ -1568,8 +1568,8 @@ impl fmt::Display for CliUpgradeableBuffer { writeln_name_value(f, "Authority:", &self.authority)?; writeln_name_value( f, - "Program Length:", - &format!("{:?} ({:#x?}) bytes", self.program_len, self.program_len), + "Data Length:", + &format!("{:?} ({:#x?}) bytes", self.data_len, self.data_len), )?; Ok(()) } diff --git a/cli/src/program.rs b/cli/src/program.rs index c025e79948..9906d149f6 100644 --- a/cli/src/program.rs +++ b/cli/src/program.rs @@ -963,7 +963,7 @@ fn process_show( authority: authority_address .map(|pubkey| pubkey.to_string()) .unwrap_or_else(|| "none".to_string()), - program_len: account.data.len() + data_len: account.data.len() - UpgradeableLoaderState::buffer_data_offset()?, })) } else { diff --git a/cli/tests/program.rs b/cli/tests/program.rs index aaae4d8f04..0bbb5d5139 100644 --- a/cli/tests/program.rs +++ b/cli/tests/program.rs @@ -3,6 +3,7 @@ use solana_cli::{ cli::{process_command, CliCommand, CliConfig}, program::ProgramCliCommand, }; +use solana_cli_output::OutputFormat; use solana_client::rpc_client::RpcClient; use solana_core::test_validator::TestValidator; use solana_faucet::faucet::run_local_faucet; @@ -14,7 +15,7 @@ use solana_sdk::{ pubkey::Pubkey, signature::{Keypair, Signer}, }; -use std::{fs::File, io::Read, path::PathBuf, str::FromStr}; +use std::{env, fs::File, io::Read, path::PathBuf, str::FromStr}; #[test] fn test_cli_program_deploy_non_upgradeable() { @@ -274,6 +275,7 @@ fn test_cli_program_deploy_with_authority() { is_final: false, max_len: Some(max_len), }); + config.output_format = OutputFormat::JsonCompact; let response = process_command(&config); let json: Value = serde_json::from_str(&response.unwrap()).unwrap(); let program_pubkey_str = json @@ -434,6 +436,25 @@ fn test_cli_program_deploy_with_authority() { program_data[..] ); + // Get upgrade authority + config.signers = vec![&keypair]; + config.command = CliCommand::Program(ProgramCliCommand::Show { + account_pubkey: Some(program_pubkey), + }); + let response = process_command(&config); + let json: Value = serde_json::from_str(&response.unwrap()).unwrap(); + let authority_pubkey_str = json + .as_object() + .unwrap() + .get("authority") + .unwrap() + .as_str() + .unwrap(); + assert_eq!( + new_upgrade_authority.pubkey(), + Pubkey::from_str(&authority_pubkey_str).unwrap() + ); + // Set no authority config.signers = vec![&keypair, &new_upgrade_authority]; config.command = CliCommand::Program(ProgramCliCommand::SetUpgradeAuthority { @@ -502,6 +523,22 @@ fn test_cli_program_deploy_with_authority() { } else { panic!("not a buffer account"); } + + // Get buffer authority + config.signers = vec![&keypair]; + config.command = CliCommand::Program(ProgramCliCommand::Show { + account_pubkey: Some(program_pubkey), + }); + let response = process_command(&config); + let json: Value = serde_json::from_str(&response.unwrap()).unwrap(); + let authority_pubkey_str = json + .as_object() + .unwrap() + .get("authority") + .unwrap() + .as_str() + .unwrap(); + assert_eq!("none", authority_pubkey_str); } #[test] @@ -557,6 +594,7 @@ fn test_cli_program_write_buffer() { buffer_authority_signer_index: None, max_len: None, }); + config.output_format = OutputFormat::JsonCompact; let response = process_command(&config); let json: Value = serde_json::from_str(&response.unwrap()).unwrap(); let buffer_pubkey_str = json @@ -616,6 +654,25 @@ fn test_cli_program_write_buffer() { program_data[..] ); + // Get buffer authority + config.signers = vec![&keypair]; + config.command = CliCommand::Program(ProgramCliCommand::Show { + account_pubkey: Some(buffer_keypair.pubkey()), + }); + let response = process_command(&config); + let json: Value = serde_json::from_str(&response.unwrap()).unwrap(); + let authority_pubkey_str = json + .as_object() + .unwrap() + .get("authority") + .unwrap() + .as_str() + .unwrap(); + assert_eq!( + keypair.pubkey(), + Pubkey::from_str(&authority_pubkey_str).unwrap() + ); + // Specify buffer authority let buffer_keypair = Keypair::new(); let authority_keypair = Keypair::new(); @@ -686,6 +743,25 @@ fn test_cli_program_write_buffer() { buffer_account.data[UpgradeableLoaderState::buffer_data_offset().unwrap()..], program_data[..] ); + + // Get buffer authority + config.signers = vec![&keypair]; + config.command = CliCommand::Program(ProgramCliCommand::Show { + account_pubkey: Some(buffer_pubkey), + }); + let response = process_command(&config); + let json: Value = serde_json::from_str(&response.unwrap()).unwrap(); + let authority_pubkey_str = json + .as_object() + .unwrap() + .get("authority") + .unwrap() + .as_str() + .unwrap(); + assert_eq!( + authority_keypair.pubkey(), + Pubkey::from_str(&authority_pubkey_str).unwrap() + ); } #[test] @@ -890,3 +966,253 @@ fn test_cli_program_mismatch_buffer_authority() { }); process_command(&config).unwrap(); } + +#[test] +fn test_cli_program_show() { + solana_logger::setup(); + + let mut pathbuf = PathBuf::from(env!("CARGO_MANIFEST_DIR")); + pathbuf.push("tests"); + pathbuf.push("fixtures"); + pathbuf.push("noop"); + pathbuf.set_extension("so"); + + let mint_keypair = Keypair::new(); + let test_validator = TestValidator::with_no_fees(mint_keypair.pubkey()); + let faucet_addr = run_local_faucet(mint_keypair, None); + + let rpc_client = + RpcClient::new_with_commitment(test_validator.rpc_url(), CommitmentConfig::processed()); + + let mut file = File::open(pathbuf.to_str().unwrap()).unwrap(); + let mut program_data = Vec::new(); + file.read_to_end(&mut program_data).unwrap(); + let max_len = program_data.len(); + let minimum_balance_for_buffer = rpc_client + .get_minimum_balance_for_rent_exemption( + UpgradeableLoaderState::programdata_len(max_len).unwrap(), + ) + .unwrap(); + + let mut config = CliConfig::recent_for_tests(); + let keypair = Keypair::new(); + config.json_rpc_url = test_validator.rpc_url(); + config.output_format = OutputFormat::Json; + + // Airdrop + config.signers = vec![&keypair]; + config.command = CliCommand::Airdrop { + faucet_host: None, + faucet_port: faucet_addr.port(), + pubkey: None, + lamports: 100 * minimum_balance_for_buffer, + }; + process_command(&config).unwrap(); + + // Write a buffer + let buffer_keypair = Keypair::new(); + let authority_keypair = Keypair::new(); + config.signers = vec![&keypair, &buffer_keypair, &authority_keypair]; + config.command = CliCommand::Program(ProgramCliCommand::WriteBuffer { + program_location: pathbuf.to_str().unwrap().to_string(), + buffer_signer_index: Some(1), + buffer_pubkey: Some(buffer_keypair.pubkey()), + buffer_authority_signer_index: Some(2), + max_len: None, + }); + process_command(&config).unwrap(); + + // Verify show + config.signers = vec![&keypair]; + config.command = CliCommand::Program(ProgramCliCommand::Show { + account_pubkey: Some(buffer_keypair.pubkey()), + }); + let response = process_command(&config); + let json: Value = serde_json::from_str(&response.unwrap()).unwrap(); + let address_str = json + .as_object() + .unwrap() + .get("address") + .unwrap() + .as_str() + .unwrap(); + assert_eq!( + buffer_keypair.pubkey(), + Pubkey::from_str(&address_str).unwrap() + ); + let authority_str = json + .as_object() + .unwrap() + .get("authority") + .unwrap() + .as_str() + .unwrap(); + assert_eq!( + authority_keypair.pubkey(), + Pubkey::from_str(&authority_str).unwrap() + ); + let data_len = json + .as_object() + .unwrap() + .get("dataLen") + .unwrap() + .as_u64() + .unwrap(); + assert_eq!(max_len, data_len as usize); + + // Deploy + let program_keypair = Keypair::new(); + config.signers = vec![&keypair, &authority_keypair, &program_keypair]; + config.command = CliCommand::Program(ProgramCliCommand::Deploy { + program_location: Some(pathbuf.to_str().unwrap().to_string()), + program_signer_index: Some(2), + program_pubkey: Some(program_keypair.pubkey()), + buffer_signer_index: None, + buffer_pubkey: None, + allow_excessive_balance: false, + upgrade_authority_signer_index: 1, + is_final: false, + max_len: Some(max_len), + }); + config.output_format = OutputFormat::JsonCompact; + process_command(&config).unwrap(); + let slot = rpc_client.get_slot().unwrap(); + + // Verify show + config.signers = vec![&keypair]; + config.command = CliCommand::Program(ProgramCliCommand::Show { + account_pubkey: Some(program_keypair.pubkey()), + }); + let response = process_command(&config); + let json: Value = serde_json::from_str(&response.unwrap()).unwrap(); + let address_str = json + .as_object() + .unwrap() + .get("programId") + .unwrap() + .as_str() + .unwrap(); + assert_eq!( + program_keypair.pubkey(), + Pubkey::from_str(&address_str).unwrap() + ); + let programdata_address_str = json + .as_object() + .unwrap() + .get("programdataAddress") + .unwrap() + .as_str() + .unwrap(); + let (programdata_pubkey, _) = Pubkey::find_program_address( + &[program_keypair.pubkey().as_ref()], + &bpf_loader_upgradeable::id(), + ); + assert_eq!( + programdata_pubkey, + Pubkey::from_str(&programdata_address_str).unwrap() + ); + let authority_str = json + .as_object() + .unwrap() + .get("authority") + .unwrap() + .as_str() + .unwrap(); + assert_eq!( + authority_keypair.pubkey(), + Pubkey::from_str(&authority_str).unwrap() + ); + let deployed_slot = json + .as_object() + .unwrap() + .get("lastDeploySlot") + .unwrap() + .as_u64() + .unwrap(); + assert_eq!(slot, deployed_slot); + let data_len = json + .as_object() + .unwrap() + .get("dataLen") + .unwrap() + .as_u64() + .unwrap(); + assert_eq!(max_len, data_len as usize); +} + +#[test] +fn test_cli_program_dump() { + solana_logger::setup(); + + let mut pathbuf = PathBuf::from(env!("CARGO_MANIFEST_DIR")); + pathbuf.push("tests"); + pathbuf.push("fixtures"); + pathbuf.push("noop"); + pathbuf.set_extension("so"); + + let mint_keypair = Keypair::new(); + let test_validator = TestValidator::with_no_fees(mint_keypair.pubkey()); + let faucet_addr = run_local_faucet(mint_keypair, None); + + let rpc_client = + RpcClient::new_with_commitment(test_validator.rpc_url(), CommitmentConfig::processed()); + + let mut file = File::open(pathbuf.to_str().unwrap()).unwrap(); + let mut program_data = Vec::new(); + file.read_to_end(&mut program_data).unwrap(); + let max_len = program_data.len(); + let minimum_balance_for_buffer = rpc_client + .get_minimum_balance_for_rent_exemption( + UpgradeableLoaderState::programdata_len(max_len).unwrap(), + ) + .unwrap(); + + let mut config = CliConfig::recent_for_tests(); + let keypair = Keypair::new(); + config.json_rpc_url = test_validator.rpc_url(); + config.output_format = OutputFormat::Json; + + // Airdrop + config.signers = vec![&keypair]; + config.command = CliCommand::Airdrop { + faucet_host: None, + faucet_port: faucet_addr.port(), + pubkey: None, + lamports: 100 * minimum_balance_for_buffer, + }; + process_command(&config).unwrap(); + + // Write a buffer + let buffer_keypair = Keypair::new(); + let authority_keypair = Keypair::new(); + config.signers = vec![&keypair, &buffer_keypair, &authority_keypair]; + config.command = CliCommand::Program(ProgramCliCommand::WriteBuffer { + program_location: pathbuf.to_str().unwrap().to_string(), + buffer_signer_index: Some(1), + buffer_pubkey: Some(buffer_keypair.pubkey()), + buffer_authority_signer_index: Some(2), + max_len: None, + }); + process_command(&config).unwrap(); + + // Verify dump + let mut out_file = { + let current_exe = env::current_exe().unwrap(); + PathBuf::from(current_exe.parent().unwrap().parent().unwrap()) + }; + out_file.set_file_name("out.txt"); + config.signers = vec![&keypair]; + config.command = CliCommand::Program(ProgramCliCommand::Dump { + account_pubkey: Some(buffer_keypair.pubkey()), + output_location: out_file.clone().into_os_string().into_string().unwrap(), + }); + process_command(&config).unwrap(); + + let mut file = File::open(out_file).unwrap(); + let mut out_data = Vec::new(); + file.read_to_end(&mut out_data).unwrap(); + assert_eq!(program_data.len(), out_data.len()); + for i in 0..program_data.len() { + assert_eq!(program_data[i], out_data[i]); + } +}