cli: Customize max sign attempts for deploy and write-buffer (#526)

* cli: Customize max sign attempts for deploy and write-buffer

* Update changelog

* Improve help message

* Fixup line break
This commit is contained in:
Jon C 2024-04-02 15:29:38 +02:00 committed by GitHub
parent 798cb561e1
commit 2643ae85c3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 182 additions and 13 deletions

View File

@ -18,6 +18,7 @@ Release channels have their own copy of this changelog:
* `central-scheduler` as default option for `--block-production-method` (#34891)
* `solana-rpc-client-api`: `RpcFilterError` depends on `base64` version 0.22, so users may need to upgrade to `base64` version 0.22
* Changed default value for `--health-check-slot-distance` from 150 to 128
* CLI: Can specify `--with-compute-unit-price` and `--max-sign-attempts` during program deployment
## [1.18.0]
* Changes
@ -39,6 +40,7 @@ Release channels have their own copy of this changelog:
double the size. Program accounts must be extended with `solana program extend`
before an upgrade if they need to accommodate larger programs.
* Interface for `gossip_service::get_client()` has changed. `gossip_service::get_multi_client()` has been removed.
* CLI: Can specify `--with-compute-unit-price` and `--max-sign-attempts` during program deployment
* Upgrade Notes
* `solana-program` and `solana-sdk` default to support for Borsh v1, with
limited backward compatibility for v0.10 and v0.9. Please upgrade to Borsh v1.

View File

@ -98,6 +98,7 @@ pub enum ProgramCliCommand {
allow_excessive_balance: bool,
skip_fee_check: bool,
compute_unit_price: Option<u64>,
max_sign_attempts: usize,
},
Upgrade {
fee_payer_signer_index: SignerIndex,
@ -117,6 +118,7 @@ pub enum ProgramCliCommand {
max_len: Option<usize>,
skip_fee_check: bool,
compute_unit_price: Option<u64>,
max_sign_attempts: usize,
},
SetBufferAuthority {
buffer_pubkey: Pubkey,
@ -246,6 +248,26 @@ impl ProgramSubCommands for App<'_, '_> {
holds a large balance of SOL",
),
)
.arg(
Arg::with_name("max_sign_attempts")
.long("max-sign-attempts")
.takes_value(true)
.validator(is_parsable::<u64>)
.default_value("5")
.help(
"Maximum number of attempts to sign or resign transactions \
after blockhash expiration. \
If any transactions sent during the program deploy are still \
unconfirmed after the initially chosen recent blockhash \
expires, those transactions will be resigned with a new \
recent blockhash and resent. Use this setting to adjust \
the maximum number of transaction signing iterations. Each \
blockhash is valid for about 60 seconds, which means using \
the default value of 5 will lead to sending transactions \
for at least 5 minutes or until all transactions are confirmed,\
whichever comes first.",
),
)
.arg(compute_unit_price_arg()),
)
.subcommand(
@ -319,6 +341,26 @@ impl ProgramSubCommands for App<'_, '_> {
[default: the length of the original deployed program]",
),
)
.arg(
Arg::with_name("max_sign_attempts")
.long("max-sign-attempts")
.takes_value(true)
.validator(is_parsable::<u64>)
.default_value("5")
.help(
"Maximum number of attempts to sign or resign transactions \
after blockhash expiration. \
If any transactions sent during the program deploy are still \
unconfirmed after the initially chosen recent blockhash \
expires, those transactions will be resigned with a new \
recent blockhash and resent. Use this setting to adjust \
the maximum number of transaction signing iterations. Each \
blockhash is valid for about 60 seconds, which means using \
the default value of 5 will lead to sending transactions \
for at least 5 minutes or until all transactions are confirmed,\
whichever comes first.",
),
)
.arg(compute_unit_price_arg()),
)
.subcommand(
@ -613,6 +655,7 @@ pub fn parse_program_subcommand(
default_signer.generate_unique_signers(bulk_signers, matches, wallet_manager)?;
let compute_unit_price = value_of(matches, "compute_unit_price");
let max_sign_attempts = value_of(matches, "max_sign_attempts").unwrap();
CliCommandInfo {
command: CliCommand::Program(ProgramCliCommand::Deploy {
@ -630,6 +673,7 @@ pub fn parse_program_subcommand(
allow_excessive_balance: matches.is_present("allow_excessive_balance"),
skip_fee_check,
compute_unit_price,
max_sign_attempts,
}),
signers: signer_info.signers,
}
@ -702,6 +746,7 @@ pub fn parse_program_subcommand(
default_signer.generate_unique_signers(bulk_signers, matches, wallet_manager)?;
let compute_unit_price = value_of(matches, "compute_unit_price");
let max_sign_attempts = value_of(matches, "max_sign_attempts").unwrap();
CliCommandInfo {
command: CliCommand::Program(ProgramCliCommand::WriteBuffer {
@ -715,6 +760,7 @@ pub fn parse_program_subcommand(
max_len,
skip_fee_check,
compute_unit_price,
max_sign_attempts,
}),
signers: signer_info.signers,
}
@ -917,6 +963,7 @@ pub fn process_program_subcommand(
allow_excessive_balance,
skip_fee_check,
compute_unit_price,
max_sign_attempts,
} => process_program_deploy(
rpc_client,
config,
@ -932,6 +979,7 @@ pub fn process_program_subcommand(
*allow_excessive_balance,
*skip_fee_check,
*compute_unit_price,
*max_sign_attempts,
),
ProgramCliCommand::Upgrade {
fee_payer_signer_index,
@ -961,6 +1009,7 @@ pub fn process_program_subcommand(
max_len,
skip_fee_check,
compute_unit_price,
max_sign_attempts,
} => process_write_buffer(
rpc_client,
config,
@ -972,6 +1021,7 @@ pub fn process_program_subcommand(
*max_len,
*skip_fee_check,
*compute_unit_price,
*max_sign_attempts,
),
ProgramCliCommand::SetBufferAuthority {
buffer_pubkey,
@ -1104,6 +1154,7 @@ fn process_program_deploy(
allow_excessive_balance: bool,
skip_fee_check: bool,
compute_unit_price: Option<u64>,
max_sign_attempts: usize,
) -> ProcessResult {
let fee_payer_signer = config.signers[fee_payer_signer_index];
let upgrade_authority_signer = config.signers[upgrade_authority_signer_index];
@ -1244,6 +1295,7 @@ fn process_program_deploy(
allow_excessive_balance,
skip_fee_check,
compute_unit_price,
max_sign_attempts,
)
} else {
do_process_program_upgrade(
@ -1259,6 +1311,7 @@ fn process_program_deploy(
buffer_signer,
skip_fee_check,
compute_unit_price,
max_sign_attempts,
)
};
if result.is_ok() && is_final {
@ -1408,6 +1461,7 @@ fn process_write_buffer(
max_len: Option<usize>,
skip_fee_check: bool,
compute_unit_price: Option<u64>,
max_sign_attempts: usize,
) -> ProcessResult {
let fee_payer_signer = config.signers[fee_payer_signer_index];
let buffer_authority = config.signers[buffer_authority_signer_index];
@ -1474,6 +1528,7 @@ fn process_write_buffer(
true,
skip_fee_check,
compute_unit_price,
max_sign_attempts,
);
if result.is_err() && buffer_signer_index.is_none() && buffer_signer.is_some() {
report_ephemeral_mnemonic(words, mnemonic);
@ -2228,6 +2283,7 @@ fn do_process_program_write_and_deploy(
allow_excessive_balance: bool,
skip_fee_check: bool,
compute_unit_price: Option<u64>,
max_sign_attempts: usize,
) -> ProcessResult {
let blockhash = rpc_client.get_latest_blockhash()?;
@ -2366,6 +2422,7 @@ fn do_process_program_write_and_deploy(
buffer_signer,
Some(buffer_authority_signer),
program_signers,
max_sign_attempts,
)?;
if let Some(program_signers) = program_signers {
@ -2396,6 +2453,7 @@ fn do_process_program_upgrade(
buffer_signer: Option<&dyn Signer>,
skip_fee_check: bool,
compute_unit_price: Option<u64>,
max_sign_attempts: usize,
) -> ProcessResult {
let blockhash = rpc_client.get_latest_blockhash()?;
@ -2513,6 +2571,7 @@ fn do_process_program_upgrade(
buffer_signer,
Some(upgrade_authority),
Some(&[upgrade_authority]),
max_sign_attempts,
)?;
let program_id = CliProgramId {
@ -2696,6 +2755,7 @@ fn simulate_and_update_compute_unit_limit(
))
}
#[allow(clippy::too_many_arguments)]
fn send_deploy_messages(
rpc_client: Arc<RpcClient>,
config: &CliConfig,
@ -2706,6 +2766,7 @@ fn send_deploy_messages(
initial_signer: Option<&dyn Signer>,
write_signer: Option<&dyn Signer>,
final_signers: Option<&[&dyn Signer]>,
max_sign_attempts: usize,
) -> Result<Option<Signature>, Box<dyn std::error::Error>> {
if let Some(message) = initial_message {
if let Some(initial_signer) = initial_signer {
@ -2793,7 +2854,7 @@ fn send_deploy_messages(
&write_messages,
&[fee_payer_signer, write_signer],
SendAndConfirmConfig {
resign_txs_count: Some(5),
resign_txs_count: Some(max_sign_attempts),
with_spinner: true,
},
)
@ -2942,7 +3003,8 @@ mod tests {
max_len: None,
allow_excessive_balance: false,
skip_fee_check: false,
compute_unit_price: None
compute_unit_price: None,
max_sign_attempts: 5,
}),
signers: vec![Box::new(read_keypair_file(&keypair_file).unwrap())],
}
@ -2971,7 +3033,8 @@ mod tests {
max_len: Some(42),
allow_excessive_balance: false,
skip_fee_check: false,
compute_unit_price: None
compute_unit_price: None,
max_sign_attempts: 5,
}),
signers: vec![Box::new(read_keypair_file(&keypair_file).unwrap())],
}
@ -3002,7 +3065,8 @@ mod tests {
max_len: None,
allow_excessive_balance: false,
skip_fee_check: false,
compute_unit_price: None
compute_unit_price: None,
max_sign_attempts: 5,
}),
signers: vec![
Box::new(read_keypair_file(&keypair_file).unwrap()),
@ -3035,7 +3099,8 @@ mod tests {
max_len: None,
allow_excessive_balance: false,
skip_fee_check: false,
compute_unit_price: None
compute_unit_price: None,
max_sign_attempts: 5,
}),
signers: vec![Box::new(read_keypair_file(&keypair_file).unwrap())],
}
@ -3067,7 +3132,8 @@ mod tests {
max_len: None,
allow_excessive_balance: false,
skip_fee_check: false,
compute_unit_price: None
compute_unit_price: None,
max_sign_attempts: 5,
}),
signers: vec![
Box::new(read_keypair_file(&keypair_file).unwrap()),
@ -3102,7 +3168,8 @@ mod tests {
max_len: None,
allow_excessive_balance: false,
skip_fee_check: false,
compute_unit_price: None
compute_unit_price: None,
max_sign_attempts: 5,
}),
signers: vec![
Box::new(read_keypair_file(&keypair_file).unwrap()),
@ -3133,7 +3200,38 @@ mod tests {
max_len: None,
skip_fee_check: false,
allow_excessive_balance: false,
compute_unit_price: None
compute_unit_price: None,
max_sign_attempts: 5,
}),
signers: vec![Box::new(read_keypair_file(&keypair_file).unwrap())],
}
);
let test_command = test_commands.clone().get_matches_from(vec![
"test",
"program",
"deploy",
"/Users/test/program.so",
"--max-sign-attempts",
"1",
]);
assert_eq!(
parse_command(&test_command, &default_signer, &mut None).unwrap(),
CliCommandInfo {
command: CliCommand::Program(ProgramCliCommand::Deploy {
program_location: Some("/Users/test/program.so".to_string()),
fee_payer_signer_index: 0,
buffer_signer_index: None,
buffer_pubkey: None,
program_signer_index: None,
program_pubkey: None,
upgrade_authority_signer_index: 0,
is_final: false,
max_len: None,
allow_excessive_balance: false,
skip_fee_check: false,
compute_unit_price: None,
max_sign_attempts: 1,
}),
signers: vec![Box::new(read_keypair_file(&keypair_file).unwrap())],
}
@ -3168,7 +3266,8 @@ mod tests {
buffer_authority_signer_index: 0,
max_len: None,
skip_fee_check: false,
compute_unit_price: None
compute_unit_price: None,
max_sign_attempts: 5,
}),
signers: vec![Box::new(read_keypair_file(&keypair_file).unwrap())],
}
@ -3194,7 +3293,8 @@ mod tests {
buffer_authority_signer_index: 0,
max_len: Some(42),
skip_fee_check: false,
compute_unit_price: None
compute_unit_price: None,
max_sign_attempts: 5,
}),
signers: vec![Box::new(read_keypair_file(&keypair_file).unwrap())],
}
@ -3223,7 +3323,8 @@ mod tests {
buffer_authority_signer_index: 0,
max_len: None,
skip_fee_check: false,
compute_unit_price: None
compute_unit_price: None,
max_sign_attempts: 5,
}),
signers: vec![
Box::new(read_keypair_file(&keypair_file).unwrap()),
@ -3255,7 +3356,8 @@ mod tests {
buffer_authority_signer_index: 1,
max_len: None,
skip_fee_check: false,
compute_unit_price: None
compute_unit_price: None,
max_sign_attempts: 5,
}),
signers: vec![
Box::new(read_keypair_file(&keypair_file).unwrap()),
@ -3292,7 +3394,8 @@ mod tests {
buffer_authority_signer_index: 2,
max_len: None,
skip_fee_check: false,
compute_unit_price: None
compute_unit_price: None,
max_sign_attempts: 5,
}),
signers: vec![
Box::new(read_keypair_file(&keypair_file).unwrap()),
@ -3301,6 +3404,33 @@ mod tests {
],
}
);
// specify max sign attempts
let test_command = test_commands.clone().get_matches_from(vec![
"test",
"program",
"write-buffer",
"/Users/test/program.so",
"--max-sign-attempts",
"10",
]);
assert_eq!(
parse_command(&test_command, &default_signer, &mut None).unwrap(),
CliCommandInfo {
command: CliCommand::Program(ProgramCliCommand::WriteBuffer {
program_location: "/Users/test/program.so".to_string(),
fee_payer_signer_index: 0,
buffer_signer_index: None,
buffer_pubkey: None,
buffer_authority_signer_index: 0,
max_len: None,
skip_fee_check: false,
compute_unit_price: None,
max_sign_attempts: 10,
}),
signers: vec![Box::new(read_keypair_file(&keypair_file).unwrap())],
}
);
}
#[test]
@ -3852,6 +3982,7 @@ mod tests {
allow_excessive_balance: false,
skip_fee_check: false,
compute_unit_price: None,
max_sign_attempts: 5,
}),
signers: vec![&default_keypair],
output_format: OutputFormat::JsonCompact,

View File

@ -98,6 +98,7 @@ fn test_cli_program_deploy_non_upgradeable() {
max_len: None,
skip_fee_check: false,
compute_unit_price: None,
max_sign_attempts: 5,
});
config.output_format = OutputFormat::JsonCompact;
let response = process_command(&config);
@ -145,6 +146,7 @@ fn test_cli_program_deploy_non_upgradeable() {
max_len: None,
skip_fee_check: false,
compute_unit_price: None,
max_sign_attempts: 5,
});
process_command(&config).unwrap();
let account1 = rpc_client
@ -201,6 +203,7 @@ fn test_cli_program_deploy_non_upgradeable() {
max_len: None,
skip_fee_check: false,
compute_unit_price: None,
max_sign_attempts: 5,
});
let err = process_command(&config).unwrap_err();
assert_eq!(
@ -225,6 +228,7 @@ fn test_cli_program_deploy_non_upgradeable() {
max_len: None,
skip_fee_check: false,
compute_unit_price: None,
max_sign_attempts: 5,
});
process_command(&config).unwrap_err();
}
@ -287,6 +291,7 @@ fn test_cli_program_deploy_no_authority() {
max_len: None,
skip_fee_check: false,
compute_unit_price: None,
max_sign_attempts: 5,
});
config.output_format = OutputFormat::JsonCompact;
let response = process_command(&config);
@ -315,6 +320,7 @@ fn test_cli_program_deploy_no_authority() {
max_len: None,
skip_fee_check: false,
compute_unit_price: None,
max_sign_attempts: 5,
});
process_command(&config).unwrap_err();
}
@ -378,6 +384,7 @@ fn test_cli_program_deploy_with_authority() {
max_len: Some(max_len),
skip_fee_check: false,
compute_unit_price: None,
max_sign_attempts: 5,
});
config.output_format = OutputFormat::JsonCompact;
let response = process_command(&config);
@ -428,6 +435,7 @@ fn test_cli_program_deploy_with_authority() {
max_len: Some(max_len),
skip_fee_check: false,
compute_unit_price: None,
max_sign_attempts: 5,
});
let response = process_command(&config);
let json: Value = serde_json::from_str(&response.unwrap()).unwrap();
@ -472,6 +480,7 @@ fn test_cli_program_deploy_with_authority() {
max_len: Some(max_len),
skip_fee_check: false,
compute_unit_price: None,
max_sign_attempts: 5,
});
process_command(&config).unwrap();
let program_account = rpc_client.get_account(&program_pubkey).unwrap();
@ -548,6 +557,7 @@ fn test_cli_program_deploy_with_authority() {
max_len: None,
skip_fee_check: false,
compute_unit_price: None,
max_sign_attempts: 5,
});
process_command(&config).unwrap();
let program_account = rpc_client.get_account(&program_pubkey).unwrap();
@ -628,6 +638,7 @@ fn test_cli_program_deploy_with_authority() {
max_len: None,
skip_fee_check: false,
compute_unit_price: None,
max_sign_attempts: 5,
});
process_command(&config).unwrap_err();
@ -646,6 +657,7 @@ fn test_cli_program_deploy_with_authority() {
max_len: None,
skip_fee_check: false,
compute_unit_price: None,
max_sign_attempts: 5,
});
let response = process_command(&config);
let json: Value = serde_json::from_str(&response.unwrap()).unwrap();
@ -751,6 +763,7 @@ fn test_cli_program_close_program() {
max_len: Some(max_len),
skip_fee_check: false,
compute_unit_price: None,
max_sign_attempts: 5,
});
config.output_format = OutputFormat::JsonCompact;
process_command(&config).unwrap();
@ -862,6 +875,7 @@ fn test_cli_program_extend_program() {
max_len: None, // Use None to check that it defaults to the max length
skip_fee_check: false,
compute_unit_price: None,
max_sign_attempts: 5,
});
config.output_format = OutputFormat::JsonCompact;
process_command(&config).unwrap();
@ -910,6 +924,7 @@ fn test_cli_program_extend_program() {
max_len: None,
skip_fee_check: false,
compute_unit_price: None,
max_sign_attempts: 5,
});
process_command(&config).unwrap_err();
@ -943,6 +958,7 @@ fn test_cli_program_extend_program() {
max_len: None,
skip_fee_check: false,
compute_unit_price: None,
max_sign_attempts: 5,
});
process_command(&config).unwrap();
}
@ -1008,6 +1024,7 @@ fn test_cli_program_write_buffer() {
max_len: None,
skip_fee_check: false,
compute_unit_price: None,
max_sign_attempts: 5,
});
config.output_format = OutputFormat::JsonCompact;
let response = process_command(&config);
@ -1045,6 +1062,7 @@ fn test_cli_program_write_buffer() {
max_len: Some(max_len),
skip_fee_check: false,
compute_unit_price: None,
max_sign_attempts: 5,
});
let response = process_command(&config);
let json: Value = serde_json::from_str(&response.unwrap()).unwrap();
@ -1109,6 +1127,7 @@ fn test_cli_program_write_buffer() {
max_len: None,
skip_fee_check: false,
compute_unit_price: None,
max_sign_attempts: 5,
});
let response = process_command(&config);
let json: Value = serde_json::from_str(&response.unwrap()).unwrap();
@ -1149,6 +1168,7 @@ fn test_cli_program_write_buffer() {
max_len: None,
skip_fee_check: false,
compute_unit_price: None,
max_sign_attempts: 5,
});
let response = process_command(&config);
let json: Value = serde_json::from_str(&response.unwrap()).unwrap();
@ -1225,6 +1245,7 @@ fn test_cli_program_write_buffer() {
max_len: None,
skip_fee_check: false,
compute_unit_price: None,
max_sign_attempts: 5,
});
config.output_format = OutputFormat::JsonCompact;
let response = process_command(&config);
@ -1268,6 +1289,7 @@ fn test_cli_program_write_buffer() {
max_len: None, //Some(max_len),
skip_fee_check: false,
compute_unit_price: None,
max_sign_attempts: 5,
});
process_command(&config).unwrap();
config.signers = vec![&keypair, &buffer_keypair];
@ -1284,6 +1306,7 @@ fn test_cli_program_write_buffer() {
max_len: None,
skip_fee_check: false,
compute_unit_price: None,
max_sign_attempts: 5,
});
config.output_format = OutputFormat::JsonCompact;
let error = process_command(&config).unwrap_err();
@ -1344,6 +1367,7 @@ fn test_cli_program_set_buffer_authority() {
max_len: None,
skip_fee_check: false,
compute_unit_price: None,
max_sign_attempts: 5,
});
process_command(&config).unwrap();
let buffer_account = rpc_client.get_account(&buffer_keypair.pubkey()).unwrap();
@ -1397,6 +1421,7 @@ fn test_cli_program_set_buffer_authority() {
max_len: None,
skip_fee_check: false,
compute_unit_price: None,
max_sign_attempts: 5,
});
config.output_format = OutputFormat::JsonCompact;
process_command(&config).unwrap_err();
@ -1443,6 +1468,7 @@ fn test_cli_program_set_buffer_authority() {
max_len: None,
skip_fee_check: false,
compute_unit_price: None,
max_sign_attempts: 5,
});
config.output_format = OutputFormat::JsonCompact;
process_command(&config).unwrap();
@ -1500,6 +1526,7 @@ fn test_cli_program_mismatch_buffer_authority() {
max_len: None,
skip_fee_check: false,
compute_unit_price: None,
max_sign_attempts: 5,
});
process_command(&config).unwrap();
let buffer_account = rpc_client.get_account(&buffer_keypair.pubkey()).unwrap();
@ -1525,6 +1552,7 @@ fn test_cli_program_mismatch_buffer_authority() {
max_len: None,
skip_fee_check: false,
compute_unit_price: None,
max_sign_attempts: 5,
});
process_command(&config).unwrap_err();
@ -1543,6 +1571,7 @@ fn test_cli_program_mismatch_buffer_authority() {
max_len: None,
skip_fee_check: false,
compute_unit_price: None,
max_sign_attempts: 5,
});
process_command(&config).unwrap();
}
@ -1627,6 +1656,7 @@ fn test_cli_program_deploy_with_offline_signing(use_offline_signer_as_fee_payer:
max_len: Some(max_program_data_len), // allows for larger program size with future upgrades
skip_fee_check: false,
compute_unit_price: None,
max_sign_attempts: 5,
});
config.output_format = OutputFormat::JsonCompact;
process_command(&config).unwrap();
@ -1795,6 +1825,7 @@ fn test_cli_program_show() {
max_len: None,
skip_fee_check: false,
compute_unit_price: None,
max_sign_attempts: 5,
});
process_command(&config).unwrap();
@ -1857,6 +1888,7 @@ fn test_cli_program_show() {
max_len: Some(max_len),
skip_fee_check: false,
compute_unit_price: None,
max_sign_attempts: 5,
});
config.output_format = OutputFormat::JsonCompact;
let min_slot = rpc_client.get_slot().unwrap();
@ -1986,6 +2018,7 @@ fn test_cli_program_dump() {
max_len: None,
skip_fee_check: false,
compute_unit_price: None,
max_sign_attempts: 5,
});
process_command(&config).unwrap();
@ -2030,6 +2063,7 @@ fn create_buffer_with_offline_authority<'a>(
max_len: None,
skip_fee_check: false,
compute_unit_price: None,
max_sign_attempts: 5,
});
process_command(config).unwrap();
let buffer_account = rpc_client.get_account(&buffer_signer.pubkey()).unwrap();
@ -2125,6 +2159,7 @@ fn cli_program_deploy_with_args(compute_unit_price: Option<u64>) {
max_len: Some(max_len),
skip_fee_check: false,
compute_unit_price,
max_sign_attempts: 5,
});
config.output_format = OutputFormat::JsonCompact;
let response = process_command(&config);

View File

@ -248,6 +248,7 @@ fn run_transactions_dos(
is_final: true,
max_len: None,
compute_unit_price: None,
max_sign_attempts: 5,
skip_fee_check: true, // skip_fee_check
});