cli: Use program length for deployments instead of 2x length (#34730)

* cli: Deploy the appropriate length program

* Extend the extend-program test for new default

* Add CHANGELOG entry

* Update docs, and include `extend`

* Update CHANGELOG.md

Co-authored-by: Tyera <teulberg@gmail.com>

---------

Co-authored-by: Tyera <teulberg@gmail.com>
This commit is contained in:
Jon C 2024-01-10 21:12:29 +01:00 committed by GitHub
parent fb97e93fe3
commit 5cb30cf9cc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 88 additions and 17 deletions

View File

@ -29,6 +29,9 @@ Release channels have their own copy of this changelog:
* Bigtable upload now includes entry summary data for each slot, stored in a
new `entries` table
* Forbid multiple values for the `--signer` CLI flag, forcing users to specify multiple occurrences of `--signer`, one for each signature
* New program deployments default to the exact size of a program, instead of
double the size. Program accounts must be extended with `solana program extend`
before an upgrade if they need to accommodate larger programs.
* 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

@ -219,7 +219,7 @@ impl ProgramSubCommands for App<'_, '_> {
.required(false)
.help(
"Maximum length of the upgradeable program \
[default: twice the length of the original deployed program]",
[default: the length of the original deployed program]",
),
)
.arg(
@ -300,7 +300,7 @@ impl ProgramSubCommands for App<'_, '_> {
.required(false)
.help(
"Maximum length of the upgradeable program \
[default: twice the length of the original deployed program]",
[default: the length of the original deployed program]",
),
),
)
@ -1171,10 +1171,8 @@ fn process_program_deploy(
);
}
len
} else if is_final {
program_len
} else {
program_len * 2
program_len
};
let min_rent_exempt_program_data_balance = rpc_client.get_minimum_balance_for_rent_exemption(

View File

@ -780,6 +780,12 @@ fn test_cli_program_extend_program() {
noop_path.push("noop");
noop_path.set_extension("so");
let mut noop_large_path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
noop_large_path.push("tests");
noop_large_path.push("fixtures");
noop_large_path.push("noop_large");
noop_large_path.set_extension("so");
let mint_keypair = Keypair::new();
let mint_pubkey = mint_keypair.pubkey();
let faucet_addr = run_local_faucet(mint_keypair, None);
@ -826,7 +832,7 @@ fn test_cli_program_extend_program() {
allow_excessive_balance: false,
upgrade_authority_signer_index: 1,
is_final: false,
max_len: Some(max_len),
max_len: None, // Use None to check that it defaults to the max length
skip_fee_check: false,
});
config.output_format = OutputFormat::JsonCompact;
@ -837,22 +843,78 @@ fn test_cli_program_extend_program() {
&bpf_loader_upgradeable::id(),
);
let programdata_account = rpc_client.get_account(&programdata_pubkey).unwrap();
let expected_len = UpgradeableLoaderState::size_of_programdata(max_len);
assert_eq!(expected_len, programdata_account.data.len());
// Wait one slot to avoid "Program was deployed in this block already" error
wait_n_slots(&rpc_client, 1);
// Extend program
let additional_bytes = 100;
// Extend program for larger program, minus 1 required byte
let mut file = File::open(noop_large_path.to_str().unwrap()).unwrap();
let mut new_program_data = Vec::new();
file.read_to_end(&mut new_program_data).unwrap();
let new_max_len = new_program_data.len();
let additional_bytes = (new_max_len - max_len) as u32;
config.signers = vec![&keypair];
config.command = CliCommand::Program(ProgramCliCommand::ExtendProgram {
program_pubkey: program_keypair.pubkey(),
additional_bytes,
additional_bytes: additional_bytes - 1,
});
process_command(&config).unwrap();
let programdata_account = rpc_client.get_account(&programdata_pubkey).unwrap();
let expected_len =
UpgradeableLoaderState::size_of_programdata(max_len + additional_bytes as usize);
let expected_len = UpgradeableLoaderState::size_of_programdata(new_max_len - 1);
assert_eq!(expected_len, programdata_account.data.len());
// Larger program deploy fails because missing 1 byte
config.signers = vec![&keypair, &upgrade_authority];
config.command = CliCommand::Program(ProgramCliCommand::Deploy {
program_location: Some(noop_large_path.to_str().unwrap().to_string()),
fee_payer_signer_index: 0,
program_signer_index: None,
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: None,
skip_fee_check: false,
});
process_command(&config).unwrap_err();
// Wait one slot to avoid "Program was deployed in this block already" error
wait_n_slots(&rpc_client, 1);
// Extend 1 last byte
config.signers = vec![&keypair];
config.command = CliCommand::Program(ProgramCliCommand::ExtendProgram {
program_pubkey: program_keypair.pubkey(),
additional_bytes: 1,
});
process_command(&config).unwrap();
let programdata_account = rpc_client.get_account(&programdata_pubkey).unwrap();
let expected_len = UpgradeableLoaderState::size_of_programdata(new_max_len);
assert_eq!(expected_len, programdata_account.data.len());
// Larger program deploy finally succeeds
config.signers = vec![&keypair, &upgrade_authority];
config.command = CliCommand::Program(ProgramCliCommand::Deploy {
program_location: Some(noop_large_path.to_str().unwrap().to_string()),
fee_payer_signer_index: 0,
program_signer_index: None,
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: None,
skip_fee_check: false,
});
process_command(&config).unwrap();
}
#[test]

View File

@ -102,17 +102,25 @@ The command looks the same as the deployment command:
solana program deploy <PROGRAM_FILEPATH>
```
By default, programs are deployed to accounts that are twice the size of the
original deployment. Doing so leaves room for program growth in future
redeployments. But, if the initially deployed program is very small and then
later grows substantially, the redeployment may fail. To avoid this, specify a
`max_len` that is at least the size (in bytes) that the program is expected to
become (plus some wiggle room).
By default, programs are deployed to accounts that match the size of the
original program file. But, if the redeployed program is larger, the
redeployment will fail. To avoid this, specify a `max_len` that is at least the
size (in bytes) that the program is expected to become (plus some wiggle room).
```bash
solana program deploy --max-len 200000 <PROGRAM_FILEPATH>
```
### Extend a program
If a program has already been deployed, and a redeployment goes beyond the
`max_len` of the account, it's possible to extend the program to fit the larger
redeployment:
```bash
solana program extend <PROGRAM_ID> <ADDITIONAL_BYTES>
```
### Resuming a failed deploy
If program deployment fails, there will be a hanging intermediate buffer account