Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Merged
merged 5 commits into from
Jan 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
8 changes: 3 additions & 5 deletions cli/src/program.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down Expand Up @@ -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]",
),
),
)
Expand Down Expand Up @@ -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(
Expand Down
74 changes: 68 additions & 6 deletions cli/tests/program.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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;
Expand All @@ -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]
Expand Down
20 changes: 14 additions & 6 deletions docs/src/cli/examples/deploy-a-program.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down