From 2ff4e67d4a3318eb496a8dcdfb683fff109ce391 Mon Sep 17 00:00:00 2001 From: Nick Guo <1387955+nickguo@users.noreply.github.com> Date: Fri, 29 Sep 2023 13:21:03 -0700 Subject: [PATCH] Support --upgradeable-program in anchor test Resolves #2641 --- CHANGELOG.md | 5 +++-- cli/src/config.rs | 7 +++++++ cli/src/lib.rs | 31 +++++++++++++++++++++++++------ docs/src/pages/docs/manifest.md | 14 ++++++++++++++ 4 files changed, 49 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 93aabdfc6b..3ee334400a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,6 +28,7 @@ The minor version will be incremented upon a breaking change and the patch versi - ts: Add strong type support for `Program.addEventListener` method ([#2627](https://github.com/coral-xyz/anchor/pull/2627)). - syn: Add `IdlBuild` trait to implement IDL support for custom types ([#2629](https://github.com/coral-xyz/anchor/pull/2629)). - spl: Add `idl-build` feature. IDL build method will not work without enabling this feature when using `anchor-spl` ([#2629](https://github.com/coral-xyz/anchor/pull/2629)). +- cli: Add `test.upgradeable`, `test.genesis.upgradeable` setting in anchor.toml to support testing upgradeable programs ([#2641](https://github.com/coral-xyz/anchor/pull/2642)). ### Fixes @@ -311,9 +312,9 @@ The minor version will be incremented upon a breaking change and the patch versi - lang: Require doc comments when using AccountInfo or UncheckedAccount types ([#1452](https://github.com/coral-xyz/anchor/pull/1452)). - lang: add [`error!`](https://docs.rs/anchor-lang/latest/anchor_lang/prelude/macro.error.html) and [`err!`](https://docs.rs/anchor-lang/latest/anchor_lang/prelude/macro.err.html) macro and `Result` type ([#1462](https://github.com/coral-xyz/anchor/pull/1462)). This change will break most programs. Do the following to upgrade: - _ change all `ProgramResult`'s to `Result<()>` + _change all `ProgramResult`'s to `Result<()>` _ change `#[error]` to `#[error_code]` - _ change all `Err(MyError::SomeError.into())` to `Err(error!(MyError::SomeError))` and all `Err(ProgramError::SomeProgramError)` to `Err(ProgramError::SomeProgramError.into())` or `Err(Error::from(ProgramError::SomeProgramError).with_source(source!()))` to provide file and line source of the error (`with_source` is most useful with `ProgramError`s. `error!` already adds source information for custom and anchor internal errors). + _change all `Err(MyError::SomeError.into())` to `Err(error!(MyError::SomeError))` and all `Err(ProgramError::SomeProgramError)` to `Err(ProgramError::SomeProgramError.into())` or `Err(Error::from(ProgramError::SomeProgramError).with_source(source!()))` to provide file and line source of the error (`with_source` is most useful with `ProgramError`s. `error!` already adds source information for custom and anchor internal errors). _ change all `solana_program::program::invoke()` to `solana_program::program::invoke().map_err(Into::into)` and `solana_program::program::invoke_signed()` to `solana_program::program::invoke_signed().map_err(Into::into)` ## [0.21.0] - 2022-02-07 diff --git a/cli/src/config.rs b/cli/src/config.rs index e5277d9d16..16137f2846 100644 --- a/cli/src/config.rs +++ b/cli/src/config.rs @@ -706,6 +706,7 @@ pub struct TestValidator { pub validator: Option, pub startup_wait: i32, pub shutdown_wait: i32, + pub upgradeable: bool, } #[derive(Default, Debug, Clone, Serialize, Deserialize)] @@ -718,6 +719,8 @@ pub struct _TestValidator { pub startup_wait: Option, #[serde(skip_serializing_if = "Option::is_none")] pub shutdown_wait: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub upgradeable: Option, } pub const STARTUP_WAIT: i32 = 5000; @@ -730,6 +733,7 @@ impl From<_TestValidator> for TestValidator { startup_wait: _test_validator.startup_wait.unwrap_or(STARTUP_WAIT), genesis: _test_validator.genesis, validator: _test_validator.validator.map(Into::into), + upgradeable: _test_validator.upgradeable.unwrap_or(false), } } } @@ -741,6 +745,7 @@ impl From for _TestValidator { startup_wait: Some(test_validator.startup_wait), genesis: test_validator.genesis, validator: test_validator.validator.map(Into::into), + upgradeable: Some(test_validator.upgradeable), } } } @@ -949,6 +954,8 @@ pub struct GenesisEntry { pub address: String, // Filepath to the compiled program to embed into the genesis. pub program: String, + // Whether the genesis program is upgradeable. + pub upgradeable: bool, } #[derive(Debug, Clone, Serialize, Deserialize)] diff --git a/cli/src/lib.rs b/cli/src/lib.rs index f698cafa49..50a2b698f1 100644 --- a/cli/src/lib.rs +++ b/cli/src/lib.rs @@ -3025,6 +3025,11 @@ fn validator_flags( ) -> Result> { let programs = cfg.programs.get(&Cluster::Localnet); + let test_upgradeable_program = test_validator + .as_ref() + .map(|test_validator| test_validator.upgradeable) + .unwrap_or(false); + let mut flags = Vec::new(); for mut program in cfg.read_all_programs()? { let binary_path = program.binary_path().display().to_string(); @@ -3036,9 +3041,16 @@ fn validator_flags( .map(|deployment| Ok(deployment.address.to_string())) .unwrap_or_else(|| program.pubkey().map(|p| p.to_string()))?; - flags.push("--bpf-program".to_string()); - flags.push(address.clone()); - flags.push(binary_path); + if test_upgradeable_program { + flags.push("--upgradeable-program".to_string()); + flags.push(address.clone()); + flags.push(binary_path); + flags.push(cfg.wallet_kp()?.pubkey().to_string()); + } else { + flags.push("--bpf-program".to_string()); + flags.push(address.clone()); + flags.push(binary_path); + } if let Some(idl) = program.idl.as_mut() { // Add program address to the IDL. @@ -3062,9 +3074,16 @@ fn validator_flags( program_path.display() )); } - flags.push("--bpf-program".to_string()); - flags.push(entry.address.clone()); - flags.push(entry.program.clone()); + if entry.upgradeable { + flags.push("--upgradeable-program".to_string()); + flags.push(entry.address.clone()); + flags.push(entry.program.clone()); + flags.push(cfg.wallet_kp()?.pubkey().to_string()); + } else { + flags.push("--bpf-program".to_string()); + flags.push(entry.address.clone()); + flags.push(entry.program.clone()); + } } } if let Some(validator) = &test.validator { diff --git a/docs/src/pages/docs/manifest.md b/docs/src/pages/docs/manifest.md index 7cc2547275..f64567bf50 100644 --- a/docs/src/pages/docs/manifest.md +++ b/docs/src/pages/docs/manifest.md @@ -122,6 +122,20 @@ program = "dex.so" [[test.genesis]] address = "22Y43yTVxuUkoRKdm9thyRhQ3SdgQS7c7kB6UNCiaczD" program = "swap.so" +upgradeable = true +``` + +#### upgradeable + +Deploys the program-to-test using `--upgradeable-program`. This makes it possible to test that certain instructions can only be executed by the program's upgrade authority. The initial upgrade authority will be set to `provider.wallet`. + +If unspecified or explicitly set to false, then the test program will be deployed with `--bpf-program`, disabling upgrades to it. + +Example: + +```toml +[test] +upgradeable = true ``` ## test.validator