Skip to content

Commit

Permalink
cli: Use program length for deployments instead of 2x length (solana-…
Browse files Browse the repository at this point in the history
…labs#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>
  • Loading branch information
joncinque and CriesofCarrots authored Jan 10, 2024
1 parent fb97e93 commit 5cb30cf
Show file tree
Hide file tree
Showing 4 changed files with 88 additions and 17 deletions.
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

0 comments on commit 5cb30cf

Please sign in to comment.