Decouple program-v4 command processors from CliConfig (#33554)

This commit is contained in:
Pankaj Garg 2023-10-06 03:53:55 -07:00 committed by GitHub
parent 64b36135cb
commit ef47c977dc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 105 additions and 116 deletions

View File

@ -1682,7 +1682,7 @@ pub fn request_and_confirm_airdrop(
Ok(signature)
}
fn common_error_adapter<E>(ix_error: &InstructionError) -> Option<E>
pub fn common_error_adapter<E>(ix_error: &InstructionError) -> Option<E>
where
E: 'static + std::error::Error + DecodeError<E> + FromPrimitive,
{
@ -1700,12 +1700,12 @@ pub fn log_instruction_custom_error<E>(
where
E: 'static + std::error::Error + DecodeError<E> + FromPrimitive,
{
log_instruction_custom_error_ex::<E, _>(result, config, common_error_adapter)
log_instruction_custom_error_ex::<E, _>(result, &config.output_format, common_error_adapter)
}
pub fn log_instruction_custom_error_ex<E, F>(
result: ClientResult<Signature>,
config: &CliConfig,
output_format: &OutputFormat,
error_adapter: F,
) -> ProcessResult
where
@ -1726,7 +1726,7 @@ where
let signature = CliSignature {
signature: sig.clone().to_string(),
};
Ok(config.output_format.formatted_string(&signature))
Ok(output_format.formatted_string(&signature))
}
}
}

View File

@ -2,8 +2,8 @@ use {
crate::{
checks::*,
cli::{
log_instruction_custom_error, CliCommand, CliCommandInfo, CliConfig, CliError,
ProcessResult,
common_error_adapter, log_instruction_custom_error_ex, CliCommand, CliCommandInfo,
CliConfig, CliError, ProcessResult,
},
program::calculate_max_chunk_size,
},
@ -14,7 +14,7 @@ use {
input_validators::is_valid_signer,
keypair::{DefaultSigner, SignerIndex},
},
solana_cli_output::CliProgramId,
solana_cli_output::{CliProgramId, OutputFormat},
solana_client::{
connection_cache::ConnectionCache,
send_and_confirm_transactions_in_parallel::{
@ -29,6 +29,7 @@ use {
solana_rpc_client_api::config::RpcSendTransactionConfig,
solana_sdk::{
account::Account,
commitment_config::CommitmentConfig,
hash::Hash,
instruction::Instruction,
loader_v4::{
@ -334,6 +335,28 @@ fn read_and_verify_elf(program_location: &str) -> Result<Vec<u8>, Box<dyn std::e
Ok(program_data)
}
pub struct ProgramV4CommandConfig<'a> {
pub websocket_url: &'a str,
pub commitment: CommitmentConfig,
pub payer: &'a dyn Signer,
pub authority: &'a dyn Signer,
pub output_format: &'a OutputFormat,
pub use_quic: bool,
}
impl<'a> ProgramV4CommandConfig<'a> {
fn new_from_cli_config(config: &'a CliConfig, auth_signer_index: &SignerIndex) -> Self {
ProgramV4CommandConfig {
websocket_url: &config.websocket_url,
commitment: config.commitment,
payer: config.signers[0],
authority: config.signers[*auth_signer_index],
output_format: &config.output_format,
use_quic: config.use_quic,
}
}
}
pub fn process_program_v4_subcommand(
rpc_client: Arc<RpcClient>,
config: &CliConfig,
@ -350,12 +373,11 @@ pub fn process_program_v4_subcommand(
process_deploy_program(
rpc_client,
config,
&ProgramV4CommandConfig::new_from_cli_config(config, authority_signer_index),
&program_data,
program_len,
&config.signers[*program_signer_index].pubkey(),
Some(config.signers[*program_signer_index]),
config.signers[*authority_signer_index],
)
}
ProgramV4CliCommand::Redeploy {
@ -370,12 +392,11 @@ pub fn process_program_v4_subcommand(
process_deploy_program(
rpc_client,
config,
&ProgramV4CommandConfig::new_from_cli_config(config, authority_signer_index),
&program_data,
program_len,
program_address,
buffer_signer,
config.signers[*authority_signer_index],
)
}
ProgramV4CliCommand::Undeploy {
@ -383,18 +404,16 @@ pub fn process_program_v4_subcommand(
authority_signer_index,
} => process_undeploy_program(
rpc_client,
config,
&ProgramV4CommandConfig::new_from_cli_config(config, authority_signer_index),
program_address,
config.signers[*authority_signer_index],
),
ProgramV4CliCommand::Finalize {
program_address,
authority_signer_index,
} => process_finalize_program(
rpc_client,
config,
&ProgramV4CommandConfig::new_from_cli_config(config, authority_signer_index),
program_address,
config.signers[*authority_signer_index],
),
}
}
@ -410,15 +429,14 @@ pub fn process_program_v4_subcommand(
// (program_address must contain program ID and must NOT be same as buffer_signer.pubkey())
fn process_deploy_program(
rpc_client: Arc<RpcClient>,
config: &CliConfig,
config: &ProgramV4CommandConfig,
program_data: &[u8],
program_data_len: u32,
program_address: &Pubkey,
buffer_signer: Option<&dyn Signer>,
authority_signer: &dyn Signer,
) -> ProcessResult {
let blockhash = rpc_client.get_latest_blockhash()?;
let payer_pubkey = config.signers[0].pubkey();
let payer_pubkey = config.payer.pubkey();
let (initial_messages, balance_needed, buffer_address) =
if let Some(buffer_signer) = buffer_signer {
@ -428,8 +446,6 @@ fn process_deploy_program(
config,
program_address,
&buffer_address,
&payer_pubkey,
&authority_signer.pubkey(),
program_data_len,
&blockhash,
)?;
@ -445,7 +461,6 @@ fn process_deploy_program(
config,
program_data_len,
program_address,
authority_signer,
)
.map(|(messages, balance_needed)| (messages, balance_needed, *program_address))?
};
@ -453,7 +468,7 @@ fn process_deploy_program(
// Create and add write messages
let create_msg = |offset: u32, bytes: Vec<u8>| {
let instruction =
loader_v4::write(&buffer_address, &authority_signer.pubkey(), offset, bytes);
loader_v4::write(&buffer_address, &config.authority.pubkey(), offset, bytes);
Message::new_with_blockhash(&[instruction], Some(&payer_pubkey), &blockhash)
};
@ -469,14 +484,13 @@ fn process_deploy_program(
config,
program_address,
&buffer_address,
authority_signer,
)?
} else {
// Create and add deploy message
vec![Message::new_with_blockhash(
&[loader_v4::deploy(
program_address,
&authority_signer.pubkey(),
&config.authority.pubkey(),
)],
Some(&payer_pubkey),
&blockhash,
@ -499,7 +513,6 @@ fn process_deploy_program(
&write_messages,
&final_messages,
buffer_signer,
authority_signer,
)?;
let program_id = CliProgramId {
@ -510,12 +523,11 @@ fn process_deploy_program(
fn process_undeploy_program(
rpc_client: Arc<RpcClient>,
config: &CliConfig,
config: &ProgramV4CommandConfig,
program_address: &Pubkey,
authority_signer: &dyn Signer,
) -> ProcessResult {
let blockhash = rpc_client.get_latest_blockhash()?;
let payer_pubkey = config.signers[0].pubkey();
let payer_pubkey = config.payer.pubkey();
let Some(program_account) = rpc_client
.get_account_with_commitment(program_address, config.commitment)?
@ -527,7 +539,7 @@ fn process_undeploy_program(
let retract_instruction = build_retract_instruction(
&program_account,
program_address,
&authority_signer.pubkey(),
&config.authority.pubkey(),
)?;
let mut initial_messages = if let Some(instruction) = retract_instruction {
@ -542,7 +554,7 @@ fn process_undeploy_program(
let truncate_instruction = loader_v4::truncate(
program_address,
&authority_signer.pubkey(),
&config.authority.pubkey(),
0,
&payer_pubkey,
);
@ -555,15 +567,7 @@ fn process_undeploy_program(
check_payer(&rpc_client, config, 0, &initial_messages, &[], &[])?;
send_messages(
rpc_client,
config,
&initial_messages,
&[],
&[],
None,
authority_signer,
)?;
send_messages(rpc_client, config, &initial_messages, &[], &[], None)?;
let program_id = CliProgramId {
program_id: program_address.to_string(),
@ -573,33 +577,23 @@ fn process_undeploy_program(
fn process_finalize_program(
rpc_client: Arc<RpcClient>,
config: &CliConfig,
config: &ProgramV4CommandConfig,
program_address: &Pubkey,
authority_signer: &dyn Signer,
) -> ProcessResult {
let blockhash = rpc_client.get_latest_blockhash()?;
let payer_pubkey = config.signers[0].pubkey();
let message = [Message::new_with_blockhash(
&[loader_v4::transfer_authority(
program_address,
&authority_signer.pubkey(),
&config.authority.pubkey(),
None,
)],
Some(&payer_pubkey),
Some(&config.payer.pubkey()),
&blockhash,
)];
check_payer(&rpc_client, config, 0, &message, &[], &[])?;
send_messages(
rpc_client,
config,
&message,
&[],
&[],
None,
authority_signer,
)?;
send_messages(rpc_client, config, &message, &[], &[], None)?;
let program_id = CliProgramId {
program_id: program_address.to_string(),
@ -609,7 +603,7 @@ fn process_finalize_program(
fn check_payer(
rpc_client: &RpcClient,
config: &CliConfig,
config: &ProgramV4CommandConfig,
balance_needed: u64,
initial_messages: &[Message],
write_messages: &[Message],
@ -630,7 +624,7 @@ fn check_payer(
}
check_account_for_spend_and_fee_with_commitment(
rpc_client,
&config.signers[0].pubkey(),
&config.payer.pubkey(),
balance_needed,
fee,
config.commitment,
@ -640,15 +634,12 @@ fn check_payer(
fn send_messages(
rpc_client: Arc<RpcClient>,
config: &CliConfig,
config: &ProgramV4CommandConfig,
initial_messages: &[Message],
write_messages: &[Message],
final_messages: &[Message],
program_signer: Option<&dyn Signer>,
authority_signer: &dyn Signer,
) -> Result<(), Box<dyn std::error::Error>> {
let payer_signer = config.signers[0];
for message in initial_messages {
if message.header.num_required_signatures == 3 {
// The initial message that creates the account and truncates it to the required size requires
@ -658,11 +649,15 @@ fn send_messages(
let mut initial_transaction = Transaction::new_unsigned(message.clone());
initial_transaction
.try_sign(&[payer_signer, initial_signer, authority_signer], blockhash)?;
.try_sign(&[config.payer, initial_signer, config.authority], blockhash)?;
let result =
rpc_client.send_and_confirm_transaction_with_spinner(&initial_transaction);
log_instruction_custom_error::<SystemError>(result, config)
.map_err(|err| format!("Account allocation failed: {err}"))?;
log_instruction_custom_error_ex::<SystemError, _>(
result,
config.output_format,
common_error_adapter,
)
.map_err(|err| format!("Account allocation failed: {err}"))?;
} else {
return Err("Buffer account not created yet, must provide a key pair".into());
}
@ -671,10 +666,14 @@ fn send_messages(
let blockhash = rpc_client.get_latest_blockhash()?;
let mut initial_transaction = Transaction::new_unsigned(message.clone());
initial_transaction.try_sign(&[payer_signer, authority_signer], blockhash)?;
initial_transaction.try_sign(&[config.payer, config.authority], blockhash)?;
let result = rpc_client.send_and_confirm_transaction_with_spinner(&initial_transaction);
log_instruction_custom_error::<SystemError>(result, config)
.map_err(|err| format!("Failed to send initial message: {err}"))?;
log_instruction_custom_error_ex::<SystemError, _>(
result,
config.output_format,
common_error_adapter,
)
.map_err(|err| format!("Failed to send initial message: {err}"))?;
} else {
return Err("Initial message requires incorrect number of signatures".into());
}
@ -690,19 +689,19 @@ fn send_messages(
let transaction_errors = match connection_cache {
ConnectionCache::Udp(cache) => TpuClient::new_with_connection_cache(
rpc_client.clone(),
&config.websocket_url,
config.websocket_url,
TpuClientConfig::default(),
cache,
)?
.send_and_confirm_messages_with_spinner(
write_messages,
&[payer_signer, authority_signer],
&[config.payer, config.authority],
),
ConnectionCache::Quic(cache) => {
let tpu_client_fut =
solana_client::nonblocking::tpu_client::TpuClient::new_with_connection_cache(
rpc_client.get_inner_client().clone(),
config.websocket_url.as_str(),
config.websocket_url,
solana_client::tpu_client::TpuClientConfig::default(),
cache,
);
@ -715,7 +714,7 @@ fn send_messages(
rpc_client.clone(),
Some(tpu_client),
write_messages,
&[payer_signer, authority_signer],
&[config.payer, config.authority],
SendAndConfirmConfig {
resign_txs_count: Some(5),
with_spinner: true,
@ -739,7 +738,7 @@ fn send_messages(
for message in final_messages {
let blockhash = rpc_client.get_latest_blockhash()?;
let mut final_tx = Transaction::new_unsigned(message.clone());
final_tx.try_sign(&[payer_signer, authority_signer], blockhash)?;
final_tx.try_sign(&[config.payer, config.authority], blockhash)?;
rpc_client
.send_and_confirm_transaction_with_spinner_and_config(
&final_tx,
@ -758,11 +757,9 @@ fn send_messages(
fn build_create_buffer_message(
rpc_client: Arc<RpcClient>,
config: &CliConfig,
config: &ProgramV4CommandConfig,
program_address: &Pubkey,
buffer_address: &Pubkey,
payer_address: &Pubkey,
authority: &Pubkey,
program_data_length: u32,
blockhash: &Hash,
) -> Result<(Option<Message>, u64), Box<dyn std::error::Error>> {
@ -786,17 +783,16 @@ fn build_create_buffer_message(
let (truncate_instructions, balance_needed) = build_truncate_instructions(
rpc_client.clone(),
payer_address,
config,
&account,
buffer_address,
authority,
program_data_length,
)?;
if !truncate_instructions.is_empty() {
Ok((
Some(Message::new_with_blockhash(
&truncate_instructions,
Some(payer_address),
Some(&config.payer.pubkey()),
blockhash,
)),
balance_needed,
@ -811,14 +807,14 @@ fn build_create_buffer_message(
Ok((
Some(Message::new_with_blockhash(
&loader_v4::create_buffer(
payer_address,
&config.payer.pubkey(),
buffer_address,
lamports_required,
authority,
&config.authority.pubkey(),
program_data_length,
payer_address,
&config.payer.pubkey(),
),
Some(payer_address),
Some(&config.payer.pubkey()),
blockhash,
)),
lamports_required,
@ -828,12 +824,10 @@ fn build_create_buffer_message(
fn build_retract_and_truncate_messages(
rpc_client: Arc<RpcClient>,
config: &CliConfig,
config: &ProgramV4CommandConfig,
program_data_len: u32,
program_address: &Pubkey,
authority_signer: &dyn Signer,
) -> Result<(Vec<Message>, u64), Box<dyn std::error::Error>> {
let payer_pubkey = config.signers[0].pubkey();
let blockhash = rpc_client.get_latest_blockhash()?;
let Some(program_account) = rpc_client
.get_account_with_commitment(program_address, config.commitment)?
@ -845,13 +839,13 @@ fn build_retract_and_truncate_messages(
let retract_instruction = build_retract_instruction(
&program_account,
program_address,
&authority_signer.pubkey(),
&config.authority.pubkey(),
)?;
let mut messages = if let Some(instruction) = retract_instruction {
vec![Message::new_with_blockhash(
&[instruction],
Some(&payer_pubkey),
Some(&config.payer.pubkey()),
&blockhash,
)]
} else {
@ -860,17 +854,16 @@ fn build_retract_and_truncate_messages(
let (truncate_instructions, balance_needed) = build_truncate_instructions(
rpc_client.clone(),
&payer_pubkey,
config,
&program_account,
program_address,
&authority_signer.pubkey(),
program_data_len,
)?;
if !truncate_instructions.is_empty() {
messages.push(Message::new_with_blockhash(
&truncate_instructions,
Some(&payer_pubkey),
Some(&config.payer.pubkey()),
&blockhash,
));
}
@ -880,13 +873,11 @@ fn build_retract_and_truncate_messages(
fn build_retract_and_deploy_messages(
rpc_client: Arc<RpcClient>,
config: &CliConfig,
config: &ProgramV4CommandConfig,
program_address: &Pubkey,
buffer_address: &Pubkey,
authority_signer: &dyn Signer,
) -> Result<Vec<Message>, Box<dyn std::error::Error>> {
let blockhash = rpc_client.get_latest_blockhash()?;
let payer_pubkey = config.signers[0].pubkey();
let Some(program_account) = rpc_client
.get_account_with_commitment(program_address, config.commitment)?
@ -898,13 +889,13 @@ fn build_retract_and_deploy_messages(
let retract_instruction = build_retract_instruction(
&program_account,
program_address,
&authority_signer.pubkey(),
&config.authority.pubkey(),
)?;
let mut messages = if let Some(instruction) = retract_instruction {
vec![Message::new_with_blockhash(
&[instruction],
Some(&payer_pubkey),
Some(&config.payer.pubkey()),
&blockhash,
)]
} else {
@ -915,10 +906,10 @@ fn build_retract_and_deploy_messages(
messages.push(Message::new_with_blockhash(
&[loader_v4::deploy_from_source(
program_address,
&authority_signer.pubkey(),
&config.authority.pubkey(),
buffer_address,
)],
Some(&payer_pubkey),
Some(&config.payer.pubkey()),
&blockhash,
));
Ok(messages)
@ -957,16 +948,18 @@ fn build_retract_instruction(
fn build_truncate_instructions(
rpc_client: Arc<RpcClient>,
payer: &Pubkey,
config: &ProgramV4CommandConfig,
account: &Account,
buffer_address: &Pubkey,
authority: &Pubkey,
program_data_length: u32,
) -> Result<(Vec<Instruction>, u64), Box<dyn std::error::Error>> {
if !loader_v4::check_id(&account.owner) {
return Err("Buffer account passed is already in use by another program".into());
}
let payer = &config.payer.pubkey();
let authority = &config.authority.pubkey();
let truncate_instruction = if account.data.is_empty() {
loader_v4::truncate_uninitialized(buffer_address, authority, program_data_length, payer)
} else {
@ -1125,6 +1118,9 @@ mod tests {
let authority_signer = program_authority();
config.signers.push(&payer);
config.signers.push(&authority_signer);
let config = ProgramV4CommandConfig::new_from_cli_config(&config, &1);
assert!(process_deploy_program(
Arc::new(rpc_client_no_existing_program()),
@ -1133,7 +1129,6 @@ mod tests {
data.len() as u32,
&program_signer.pubkey(),
Some(&program_signer),
&authority_signer,
)
.is_ok());
@ -1144,7 +1139,6 @@ mod tests {
data.len() as u32,
&program_signer.pubkey(),
Some(&program_signer),
&authority_signer,
)
.is_err());
@ -1155,7 +1149,6 @@ mod tests {
data.len() as u32,
&program_signer.pubkey(),
Some(&program_signer),
&authority_signer,
)
.is_err());
}
@ -1170,6 +1163,9 @@ mod tests {
let authority_signer = program_authority();
config.signers.push(&payer);
config.signers.push(&authority_signer);
let config = ProgramV4CommandConfig::new_from_cli_config(&config, &1);
// Redeploying a non-existent program should fail
assert!(process_deploy_program(
@ -1179,7 +1175,6 @@ mod tests {
data.len() as u32,
&program_address,
None,
&authority_signer,
)
.is_err());
@ -1190,7 +1185,6 @@ mod tests {
data.len() as u32,
&program_address,
None,
&authority_signer,
)
.is_ok());
@ -1201,7 +1195,6 @@ mod tests {
data.len() as u32,
&program_address,
None,
&authority_signer,
)
.is_ok());
@ -1212,7 +1205,6 @@ mod tests {
data.len() as u32,
&program_address,
None,
&authority_signer,
)
.is_err());
@ -1223,7 +1215,6 @@ mod tests {
data.len() as u32,
&program_address,
None,
&authority_signer,
)
.is_err());
@ -1234,7 +1225,6 @@ mod tests {
data.len() as u32,
&program_address,
None,
&authority_signer,
)
.is_err());
}
@ -1250,6 +1240,9 @@ mod tests {
let authority_signer = program_authority();
config.signers.push(&payer);
config.signers.push(&authority_signer);
let config = ProgramV4CommandConfig::new_from_cli_config(&config, &1);
// Redeploying a non-existent program should fail
assert!(process_deploy_program(
@ -1259,7 +1252,6 @@ mod tests {
data.len() as u32,
&program_address,
Some(&buffer_signer),
&authority_signer,
)
.is_err());
@ -1270,7 +1262,6 @@ mod tests {
data.len() as u32,
&program_address,
Some(&buffer_signer),
&authority_signer,
)
.is_err());
@ -1281,7 +1272,6 @@ mod tests {
data.len() as u32,
&program_address,
Some(&buffer_signer),
&authority_signer,
)
.is_err());
}
@ -1295,12 +1285,14 @@ mod tests {
let authority_signer = program_authority();
config.signers.push(&payer);
config.signers.push(&authority_signer);
let config = ProgramV4CommandConfig::new_from_cli_config(&config, &1);
assert!(process_undeploy_program(
Arc::new(rpc_client_no_existing_program()),
&config,
&program_signer.pubkey(),
&authority_signer,
)
.is_err());
@ -1308,7 +1300,6 @@ mod tests {
Arc::new(rpc_client_with_program_retracted()),
&config,
&program_signer.pubkey(),
&authority_signer,
)
.is_ok());
@ -1316,7 +1307,6 @@ mod tests {
Arc::new(rpc_client_with_program_deployed()),
&config,
&program_signer.pubkey(),
&authority_signer,
)
.is_ok());
@ -1324,7 +1314,6 @@ mod tests {
Arc::new(rpc_client_with_program_finalized()),
&config,
&program_signer.pubkey(),
&authority_signer,
)
.is_err());
@ -1332,7 +1321,6 @@ mod tests {
Arc::new(rpc_client_wrong_account_owner()),
&config,
&program_signer.pubkey(),
&authority_signer,
)
.is_err());
@ -1340,7 +1328,6 @@ mod tests {
Arc::new(rpc_client_wrong_authority()),
&config,
&program_signer.pubkey(),
&authority_signer,
)
.is_err());
}
@ -1354,12 +1341,14 @@ mod tests {
let authority_signer = program_authority();
config.signers.push(&payer);
config.signers.push(&authority_signer);
let config = ProgramV4CommandConfig::new_from_cli_config(&config, &1);
assert!(process_finalize_program(
Arc::new(rpc_client_with_program_deployed()),
&config,
&program_signer.pubkey(),
&authority_signer,
)
.is_ok());
}