Upgradeable loader (#13689)
This commit is contained in:
parent
79fb646872
commit
9e90394583
1213
cli/src/cli.rs
1213
cli/src/cli.rs
File diff suppressed because it is too large
Load Diff
|
@ -5,6 +5,7 @@ use solana_core::test_validator::TestValidator;
|
|||
use solana_faucet::faucet::run_local_faucet;
|
||||
use solana_sdk::{
|
||||
bpf_loader,
|
||||
bpf_loader_upgradeable::{self, UpgradeableLoaderState},
|
||||
commitment_config::CommitmentConfig,
|
||||
pubkey::Pubkey,
|
||||
signature::{Keypair, Signer},
|
||||
|
@ -49,11 +50,14 @@ fn test_cli_deploy_program() {
|
|||
config.signers = vec![&keypair];
|
||||
process_command(&config).unwrap();
|
||||
|
||||
config.command = CliCommand::Deploy {
|
||||
config.command = CliCommand::ProgramDeploy {
|
||||
program_location: pathbuf.to_str().unwrap().to_string(),
|
||||
address: None,
|
||||
buffer: None,
|
||||
use_deprecated_loader: false,
|
||||
use_upgradeable_loader: false,
|
||||
allow_excessive_balance: false,
|
||||
upgrade_authority: None,
|
||||
max_len: None,
|
||||
};
|
||||
|
||||
let response = process_command(&config);
|
||||
|
@ -84,11 +88,14 @@ fn test_cli_deploy_program() {
|
|||
// Test custom address
|
||||
let custom_address_keypair = Keypair::new();
|
||||
config.signers = vec![&keypair, &custom_address_keypair];
|
||||
config.command = CliCommand::Deploy {
|
||||
config.command = CliCommand::ProgramDeploy {
|
||||
program_location: pathbuf.to_str().unwrap().to_string(),
|
||||
address: Some(1),
|
||||
buffer: Some(1),
|
||||
use_deprecated_loader: false,
|
||||
use_upgradeable_loader: false,
|
||||
allow_excessive_balance: false,
|
||||
upgrade_authority: None,
|
||||
max_len: None,
|
||||
};
|
||||
process_command(&config).unwrap();
|
||||
let account1 = rpc_client
|
||||
|
@ -116,20 +123,26 @@ fn test_cli_deploy_program() {
|
|||
process_command(&config).unwrap();
|
||||
|
||||
config.signers = vec![&keypair, &custom_address_keypair];
|
||||
config.command = CliCommand::Deploy {
|
||||
config.command = CliCommand::ProgramDeploy {
|
||||
program_location: pathbuf.to_str().unwrap().to_string(),
|
||||
address: Some(1),
|
||||
buffer: Some(1),
|
||||
use_deprecated_loader: false,
|
||||
use_upgradeable_loader: false,
|
||||
allow_excessive_balance: false,
|
||||
upgrade_authority: None,
|
||||
max_len: None,
|
||||
};
|
||||
process_command(&config).unwrap_err();
|
||||
|
||||
// Use forcing parameter to deploy to account with excess balance
|
||||
config.command = CliCommand::Deploy {
|
||||
config.command = CliCommand::ProgramDeploy {
|
||||
program_location: pathbuf.to_str().unwrap().to_string(),
|
||||
address: Some(1),
|
||||
buffer: Some(1),
|
||||
use_deprecated_loader: false,
|
||||
use_upgradeable_loader: false,
|
||||
allow_excessive_balance: true,
|
||||
upgrade_authority: None,
|
||||
max_len: None,
|
||||
};
|
||||
process_command(&config).unwrap();
|
||||
let account2 = rpc_client
|
||||
|
@ -142,3 +155,268 @@ fn test_cli_deploy_program() {
|
|||
assert_eq!(account2.executable, true);
|
||||
assert_eq!(account0.data, account2.data);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_cli_deploy_upgradeable_program() {
|
||||
solana_logger::setup();
|
||||
|
||||
let mut pathbuf = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
|
||||
pathbuf.push("tests");
|
||||
pathbuf.push("fixtures");
|
||||
pathbuf.push("noop");
|
||||
pathbuf.set_extension("so");
|
||||
|
||||
let mint_keypair = Keypair::new();
|
||||
let test_validator = TestValidator::with_no_fees(mint_keypair.pubkey());
|
||||
|
||||
let (sender, receiver) = channel();
|
||||
run_local_faucet(mint_keypair, sender, None);
|
||||
let faucet_addr = receiver.recv().unwrap();
|
||||
|
||||
let rpc_client = RpcClient::new(test_validator.rpc_url());
|
||||
|
||||
let mut file = File::open(pathbuf.to_str().unwrap()).unwrap();
|
||||
let mut program_data = Vec::new();
|
||||
file.read_to_end(&mut program_data).unwrap();
|
||||
let max_len = program_data.len();
|
||||
println!(
|
||||
"max_len {:?} {:?}",
|
||||
max_len,
|
||||
UpgradeableLoaderState::programdata_len(max_len)
|
||||
);
|
||||
let minimum_balance_for_programdata = rpc_client
|
||||
.get_minimum_balance_for_rent_exemption(
|
||||
UpgradeableLoaderState::programdata_len(max_len).unwrap(),
|
||||
)
|
||||
.unwrap();
|
||||
let minimum_balance_for_program = rpc_client
|
||||
.get_minimum_balance_for_rent_exemption(UpgradeableLoaderState::program_len().unwrap())
|
||||
.unwrap();
|
||||
let upgrade_authority = Keypair::new();
|
||||
|
||||
let mut config = CliConfig::recent_for_tests();
|
||||
let keypair = Keypair::new();
|
||||
config.json_rpc_url = test_validator.rpc_url();
|
||||
config.command = CliCommand::Airdrop {
|
||||
faucet_host: None,
|
||||
faucet_port: faucet_addr.port(),
|
||||
pubkey: None,
|
||||
lamports: 100 * minimum_balance_for_programdata + minimum_balance_for_program,
|
||||
};
|
||||
config.signers = vec![&keypair];
|
||||
process_command(&config).unwrap();
|
||||
|
||||
// Deploy and attempt to upgrade a non-upgradeable program
|
||||
config.command = CliCommand::ProgramDeploy {
|
||||
program_location: pathbuf.to_str().unwrap().to_string(),
|
||||
buffer: None,
|
||||
use_deprecated_loader: false,
|
||||
use_upgradeable_loader: true,
|
||||
allow_excessive_balance: false,
|
||||
upgrade_authority: None,
|
||||
max_len: Some(max_len),
|
||||
};
|
||||
let response = process_command(&config);
|
||||
let json: Value = serde_json::from_str(&response.unwrap()).unwrap();
|
||||
let program_id_str = json
|
||||
.as_object()
|
||||
.unwrap()
|
||||
.get("programId")
|
||||
.unwrap()
|
||||
.as_str()
|
||||
.unwrap();
|
||||
let program_id = Pubkey::from_str(&program_id_str).unwrap();
|
||||
|
||||
config.signers = vec![&keypair, &upgrade_authority];
|
||||
config.command = CliCommand::ProgramUpgrade {
|
||||
program_location: pathbuf.to_str().unwrap().to_string(),
|
||||
program: program_id,
|
||||
buffer: None,
|
||||
upgrade_authority: 1,
|
||||
};
|
||||
process_command(&config).unwrap_err();
|
||||
|
||||
// Deploy the upgradeable program
|
||||
config.command = CliCommand::ProgramDeploy {
|
||||
program_location: pathbuf.to_str().unwrap().to_string(),
|
||||
buffer: None,
|
||||
use_deprecated_loader: false,
|
||||
use_upgradeable_loader: true,
|
||||
allow_excessive_balance: false,
|
||||
upgrade_authority: Some(upgrade_authority.pubkey()),
|
||||
max_len: Some(max_len),
|
||||
};
|
||||
let response = process_command(&config);
|
||||
let json: Value = serde_json::from_str(&response.unwrap()).unwrap();
|
||||
let program_id_str = json
|
||||
.as_object()
|
||||
.unwrap()
|
||||
.get("programId")
|
||||
.unwrap()
|
||||
.as_str()
|
||||
.unwrap();
|
||||
let program_id = Pubkey::from_str(&program_id_str).unwrap();
|
||||
let program_account = rpc_client
|
||||
.get_account_with_commitment(&program_id, CommitmentConfig::recent())
|
||||
.unwrap()
|
||||
.value
|
||||
.unwrap();
|
||||
assert_eq!(program_account.lamports, minimum_balance_for_program);
|
||||
assert_eq!(program_account.owner, bpf_loader_upgradeable::id());
|
||||
assert_eq!(program_account.executable, true);
|
||||
let (programdata_pubkey, _) =
|
||||
Pubkey::find_program_address(&[program_id.as_ref()], &bpf_loader_upgradeable::id());
|
||||
let programdata_account = rpc_client
|
||||
.get_account_with_commitment(&programdata_pubkey, CommitmentConfig::recent())
|
||||
.unwrap()
|
||||
.value
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
programdata_account.lamports,
|
||||
minimum_balance_for_programdata
|
||||
);
|
||||
assert_eq!(programdata_account.owner, bpf_loader_upgradeable::id());
|
||||
assert_eq!(programdata_account.executable, false);
|
||||
assert_eq!(
|
||||
programdata_account.data[UpgradeableLoaderState::programdata_data_offset().unwrap()..],
|
||||
program_data[..]
|
||||
);
|
||||
|
||||
// Upgrade the program
|
||||
config.signers = vec![&keypair, &upgrade_authority];
|
||||
config.command = CliCommand::ProgramUpgrade {
|
||||
program_location: pathbuf.to_str().unwrap().to_string(),
|
||||
program: program_id,
|
||||
buffer: None,
|
||||
upgrade_authority: 1,
|
||||
};
|
||||
let response = process_command(&config);
|
||||
let json: Value = serde_json::from_str(&response.unwrap()).unwrap();
|
||||
let program_id_str = json
|
||||
.as_object()
|
||||
.unwrap()
|
||||
.get("programId")
|
||||
.unwrap()
|
||||
.as_str()
|
||||
.unwrap();
|
||||
let program_id = Pubkey::from_str(&program_id_str).unwrap();
|
||||
let program_account = rpc_client
|
||||
.get_account_with_commitment(&program_id, CommitmentConfig::recent())
|
||||
.unwrap()
|
||||
.value
|
||||
.unwrap();
|
||||
assert_eq!(program_account.lamports, minimum_balance_for_program);
|
||||
assert_eq!(program_account.owner, bpf_loader_upgradeable::id());
|
||||
assert_eq!(program_account.executable, true);
|
||||
let (programdata_pubkey, _) =
|
||||
Pubkey::find_program_address(&[program_id.as_ref()], &bpf_loader_upgradeable::id());
|
||||
let programdata_account = rpc_client
|
||||
.get_account_with_commitment(&programdata_pubkey, CommitmentConfig::recent())
|
||||
.unwrap()
|
||||
.value
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
programdata_account.lamports,
|
||||
minimum_balance_for_programdata
|
||||
);
|
||||
assert_eq!(programdata_account.owner, bpf_loader_upgradeable::id());
|
||||
assert_eq!(programdata_account.executable, false);
|
||||
assert_eq!(
|
||||
programdata_account.data[UpgradeableLoaderState::programdata_data_offset().unwrap()..],
|
||||
program_data[..]
|
||||
);
|
||||
|
||||
// Set a new authority
|
||||
let new_upgrade_authority = Keypair::new();
|
||||
config.signers = vec![&keypair, &upgrade_authority];
|
||||
config.command = CliCommand::SetProgramUpgradeAuthority {
|
||||
program: program_id,
|
||||
upgrade_authority: 1,
|
||||
new_upgrade_authority: Some(new_upgrade_authority.pubkey()),
|
||||
};
|
||||
let response = process_command(&config);
|
||||
let json: Value = serde_json::from_str(&response.unwrap()).unwrap();
|
||||
let new_upgrade_authority_str = json
|
||||
.as_object()
|
||||
.unwrap()
|
||||
.get("UpgradeAuthority")
|
||||
.unwrap()
|
||||
.as_str()
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
Pubkey::from_str(&new_upgrade_authority_str).unwrap(),
|
||||
new_upgrade_authority.pubkey()
|
||||
);
|
||||
|
||||
// Upgrade with new authority
|
||||
config.signers = vec![&keypair, &new_upgrade_authority];
|
||||
config.command = CliCommand::ProgramUpgrade {
|
||||
program_location: pathbuf.to_str().unwrap().to_string(),
|
||||
program: program_id,
|
||||
buffer: None,
|
||||
upgrade_authority: 1,
|
||||
};
|
||||
let response = process_command(&config);
|
||||
let json: Value = serde_json::from_str(&response.unwrap()).unwrap();
|
||||
let program_id_str = json
|
||||
.as_object()
|
||||
.unwrap()
|
||||
.get("programId")
|
||||
.unwrap()
|
||||
.as_str()
|
||||
.unwrap();
|
||||
let program_id = Pubkey::from_str(&program_id_str).unwrap();
|
||||
let program_account = rpc_client
|
||||
.get_account_with_commitment(&program_id, CommitmentConfig::recent())
|
||||
.unwrap()
|
||||
.value
|
||||
.unwrap();
|
||||
assert_eq!(program_account.lamports, minimum_balance_for_program);
|
||||
assert_eq!(program_account.owner, bpf_loader_upgradeable::id());
|
||||
assert_eq!(program_account.executable, true);
|
||||
let (programdata_pubkey, _) =
|
||||
Pubkey::find_program_address(&[program_id.as_ref()], &bpf_loader_upgradeable::id());
|
||||
let programdata_account = rpc_client
|
||||
.get_account_with_commitment(&programdata_pubkey, CommitmentConfig::recent())
|
||||
.unwrap()
|
||||
.value
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
programdata_account.lamports,
|
||||
minimum_balance_for_programdata
|
||||
);
|
||||
assert_eq!(programdata_account.owner, bpf_loader_upgradeable::id());
|
||||
assert_eq!(programdata_account.executable, false);
|
||||
assert_eq!(
|
||||
programdata_account.data[UpgradeableLoaderState::programdata_data_offset().unwrap()..],
|
||||
program_data[..]
|
||||
);
|
||||
|
||||
// Set a no authority
|
||||
config.signers = vec![&keypair, &new_upgrade_authority];
|
||||
config.command = CliCommand::SetProgramUpgradeAuthority {
|
||||
program: program_id,
|
||||
upgrade_authority: 1,
|
||||
new_upgrade_authority: None,
|
||||
};
|
||||
let response = process_command(&config);
|
||||
let json: Value = serde_json::from_str(&response.unwrap()).unwrap();
|
||||
let new_upgrade_authority_str = json
|
||||
.as_object()
|
||||
.unwrap()
|
||||
.get("UpgradeAuthority")
|
||||
.unwrap()
|
||||
.as_str()
|
||||
.unwrap();
|
||||
assert_eq!(new_upgrade_authority_str, "None");
|
||||
|
||||
// Upgrade with no authority
|
||||
config.signers = vec![&keypair, &new_upgrade_authority];
|
||||
config.command = CliCommand::ProgramUpgrade {
|
||||
program_location: pathbuf.to_str().unwrap().to_string(),
|
||||
program: program_id,
|
||||
buffer: None,
|
||||
upgrade_authority: 1,
|
||||
};
|
||||
process_command(&config).unwrap_err();
|
||||
}
|
||||
|
|
|
@ -20,6 +20,11 @@ fn genesis_builtins(cluster_type: ClusterType, bpf_jit: bool) -> Vec<Builtin> {
|
|||
} else {
|
||||
to_builtin!(solana_bpf_loader_program!())
|
||||
},
|
||||
if bpf_jit {
|
||||
to_builtin!(solana_bpf_loader_upgradeable_program_with_jit!())
|
||||
} else {
|
||||
to_builtin!(solana_bpf_loader_upgradeable_program!())
|
||||
},
|
||||
]
|
||||
} else {
|
||||
// Remove this `else` block and the `cluster_type` argument to this function once
|
||||
|
@ -30,11 +35,18 @@ fn genesis_builtins(cluster_type: ClusterType, bpf_jit: bool) -> Vec<Builtin> {
|
|||
|
||||
/// Builtin programs activated dynamically by feature
|
||||
fn feature_builtins() -> Vec<(Builtin, Pubkey, ActivationType)> {
|
||||
vec![(
|
||||
vec![
|
||||
(
|
||||
to_builtin!(solana_bpf_loader_program!()),
|
||||
feature_set::bpf_loader2_program::id(),
|
||||
ActivationType::NewProgram,
|
||||
)]
|
||||
),
|
||||
(
|
||||
to_builtin!(solana_bpf_loader_upgradeable_program!()),
|
||||
feature_set::bpf_loader_upgradeable_program::id(),
|
||||
ActivationType::NewProgram,
|
||||
),
|
||||
]
|
||||
}
|
||||
|
||||
pub(crate) fn get(cluster_type: ClusterType, bpf_jit: bool) -> Builtins {
|
||||
|
|
|
@ -2110,6 +2110,20 @@ dependencies = [
|
|||
"solana-program",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "solana-bpf-rust-upgradeable"
|
||||
version = "1.5.0"
|
||||
dependencies = [
|
||||
"solana-program",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "solana-bpf-rust-upgraded"
|
||||
version = "1.5.0"
|
||||
dependencies = [
|
||||
"solana-program",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "solana-config-program"
|
||||
version = "1.5.0"
|
||||
|
|
|
@ -68,6 +68,8 @@ members = [
|
|||
"rust/spoof1",
|
||||
"rust/spoof1_system",
|
||||
"rust/sysval",
|
||||
"rust/upgradeable",
|
||||
"rust/upgraded",
|
||||
]
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
|
|
|
@ -87,6 +87,8 @@ fn main() {
|
|||
"spoof1",
|
||||
"spoof1_system",
|
||||
"sysval",
|
||||
"upgradeable",
|
||||
"upgraded",
|
||||
];
|
||||
for program in rust_programs.iter() {
|
||||
println!(
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
[package]
|
||||
name = "solana-bpf-rust-upgradeable"
|
||||
version = "1.5.0"
|
||||
description = "Solana BPF test program written in Rust"
|
||||
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
||||
repository = "https://github.com/solana-labs/solana"
|
||||
license = "Apache-2.0"
|
||||
homepage = "https://solana.com/"
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
solana-program = { path = "../../../../sdk/program", version = "1.5.0" }
|
||||
|
||||
[lib]
|
||||
name = "solana_bpf_rust_upgradeable"
|
||||
crate-type = ["cdylib"]
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
targets = ["x86_64-unknown-linux-gnu"]
|
|
@ -0,0 +1,2 @@
|
|||
[target.bpfel-unknown-unknown.dependencies.std]
|
||||
features = []
|
|
@ -0,0 +1,16 @@
|
|||
//! @brief Example Rust-based BPF upgradeable program
|
||||
|
||||
extern crate solana_program;
|
||||
use solana_program::{
|
||||
account_info::AccountInfo, entrypoint, entrypoint::ProgramResult, msg, pubkey::Pubkey,
|
||||
};
|
||||
|
||||
entrypoint!(process_instruction);
|
||||
fn process_instruction(
|
||||
_program_id: &Pubkey,
|
||||
_accounts: &[AccountInfo],
|
||||
_instruction_data: &[u8],
|
||||
) -> ProgramResult {
|
||||
msg!("Upgradeable program");
|
||||
Err(42.into())
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
[package]
|
||||
name = "solana-bpf-rust-upgraded"
|
||||
version = "1.5.0"
|
||||
description = "Solana BPF test program written in Rust"
|
||||
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
||||
repository = "https://github.com/solana-labs/solana"
|
||||
license = "Apache-2.0"
|
||||
homepage = "https://solana.com/"
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
solana-program = { path = "../../../../sdk/program", version = "1.5.0" }
|
||||
|
||||
[lib]
|
||||
name = "solana_bpf_rust_upgraded"
|
||||
crate-type = ["cdylib"]
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
targets = ["x86_64-unknown-linux-gnu"]
|
|
@ -0,0 +1,2 @@
|
|||
[target.bpfel-unknown-unknown.dependencies.std]
|
||||
features = []
|
|
@ -0,0 +1,16 @@
|
|||
//! @brief Example Rust-based BPF upgraded program
|
||||
|
||||
extern crate solana_program;
|
||||
use solana_program::{
|
||||
account_info::AccountInfo, entrypoint, entrypoint::ProgramResult, msg, pubkey::Pubkey,
|
||||
};
|
||||
|
||||
entrypoint!(process_instruction);
|
||||
fn process_instruction(
|
||||
_program_id: &Pubkey,
|
||||
_accounts: &[AccountInfo],
|
||||
_instruction_data: &[u8],
|
||||
) -> ProgramResult {
|
||||
msg!("Upgraded program");
|
||||
Err(43.into())
|
||||
}
|
|
@ -14,7 +14,10 @@ use solana_runtime::{
|
|||
bank::Bank,
|
||||
bank_client::BankClient,
|
||||
genesis_utils::{create_genesis_config, GenesisConfigInfo},
|
||||
loader_utils::load_program,
|
||||
loader_utils::{
|
||||
load_buffer_account, load_program, load_upgradeable_program, set_upgrade_authority,
|
||||
upgrade_program,
|
||||
},
|
||||
};
|
||||
use solana_sdk::{
|
||||
account::Account,
|
||||
|
@ -94,6 +97,44 @@ fn write_bpf_program(
|
|||
}
|
||||
}
|
||||
|
||||
fn load_upgradeable_bpf_program(
|
||||
bank_client: &BankClient,
|
||||
payer_keypair: &Keypair,
|
||||
name: &str,
|
||||
) -> (Pubkey, Keypair) {
|
||||
let path = create_bpf_path(name);
|
||||
let mut file = File::open(&path).unwrap_or_else(|err| {
|
||||
panic!("Failed to open {}: {}", path.display(), err);
|
||||
});
|
||||
let mut elf = Vec::new();
|
||||
file.read_to_end(&mut elf).unwrap();
|
||||
load_upgradeable_program(bank_client, payer_keypair, elf)
|
||||
}
|
||||
|
||||
fn upgrade_bpf_program(
|
||||
bank_client: &BankClient,
|
||||
payer_keypair: &Keypair,
|
||||
executable_pubkey: &Pubkey,
|
||||
authority_keypair: &Keypair,
|
||||
name: &str,
|
||||
) {
|
||||
let path = create_bpf_path(name);
|
||||
let mut file = File::open(&path).unwrap_or_else(|err| {
|
||||
panic!("Failed to open {}: {}", path.display(), err);
|
||||
});
|
||||
let mut elf = Vec::new();
|
||||
file.read_to_end(&mut elf).unwrap();
|
||||
let buffer_pubkey = load_buffer_account(bank_client, payer_keypair, &elf);
|
||||
upgrade_program(
|
||||
bank_client,
|
||||
payer_keypair,
|
||||
executable_pubkey,
|
||||
&buffer_pubkey,
|
||||
&authority_keypair,
|
||||
&payer_keypair.pubkey(),
|
||||
)
|
||||
}
|
||||
|
||||
fn run_program(
|
||||
name: &str,
|
||||
program_id: &Pubkey,
|
||||
|
@ -1382,3 +1423,79 @@ fn test_program_bpf_test_use_latest_executor2() {
|
|||
.send_and_confirm_message(&[&mint_keypair], message)
|
||||
.is_ok());
|
||||
}
|
||||
|
||||
#[cfg(feature = "bpf_rust")]
|
||||
#[test]
|
||||
fn test_program_bpf_upgrade() {
|
||||
solana_logger::setup();
|
||||
|
||||
let mut nonce = 0;
|
||||
let GenesisConfigInfo {
|
||||
genesis_config,
|
||||
mint_keypair,
|
||||
..
|
||||
} = create_genesis_config(50);
|
||||
let mut bank = Bank::new(&genesis_config);
|
||||
let (name, id, entrypoint) = solana_bpf_loader_upgradeable_program!();
|
||||
bank.add_builtin(&name, id, entrypoint);
|
||||
let bank_client = BankClient::new(bank);
|
||||
|
||||
// deploy upgrade program
|
||||
let (program_id, authority_keypair) =
|
||||
load_upgradeable_bpf_program(&bank_client, &mint_keypair, "solana_bpf_rust_upgradeable");
|
||||
|
||||
// call upgrade program
|
||||
nonce += 1;
|
||||
let instruction = Instruction::new(program_id, &[nonce], vec![]);
|
||||
let result = bank_client.send_and_confirm_instruction(&mint_keypair, instruction);
|
||||
assert_eq!(
|
||||
result.unwrap_err().unwrap(),
|
||||
TransactionError::InstructionError(0, InstructionError::Custom(42))
|
||||
);
|
||||
|
||||
// upgrade program
|
||||
upgrade_bpf_program(
|
||||
&bank_client,
|
||||
&mint_keypair,
|
||||
&program_id,
|
||||
&authority_keypair,
|
||||
"solana_bpf_rust_upgraded",
|
||||
);
|
||||
|
||||
// call upgraded program
|
||||
nonce += 1;
|
||||
let instruction = Instruction::new(program_id, &[nonce], vec![]);
|
||||
let result = bank_client.send_and_confirm_instruction(&mint_keypair, instruction);
|
||||
assert_eq!(
|
||||
result.unwrap_err().unwrap(),
|
||||
TransactionError::InstructionError(0, InstructionError::Custom(43))
|
||||
);
|
||||
|
||||
// upgrade back to the original program
|
||||
let new_authority_keypair = Keypair::new();
|
||||
set_upgrade_authority(
|
||||
&bank_client,
|
||||
&mint_keypair,
|
||||
&program_id,
|
||||
&authority_keypair,
|
||||
Some(&new_authority_keypair.pubkey()),
|
||||
);
|
||||
|
||||
// upgrade back to the original program
|
||||
upgrade_bpf_program(
|
||||
&bank_client,
|
||||
&mint_keypair,
|
||||
&program_id,
|
||||
&new_authority_keypair,
|
||||
"solana_bpf_rust_upgradeable",
|
||||
);
|
||||
|
||||
// call original program
|
||||
nonce += 1;
|
||||
let instruction = Instruction::new(program_id, &[nonce], vec![]);
|
||||
let result = bank_client.send_and_confirm_instruction(&mint_keypair, instruction);
|
||||
assert_eq!(
|
||||
result.unwrap_err().unwrap(),
|
||||
TransactionError::InstructionError(0, InstructionError::Custom(42))
|
||||
);
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,6 @@
|
|||
solana_sdk::declare_builtin!(
|
||||
solana_sdk::bpf_loader_upgradeable::ID,
|
||||
solana_bpf_loader_upgradeable_program,
|
||||
solana_bpf_loader_program::process_instruction,
|
||||
upgradeable::id
|
||||
);
|
|
@ -0,0 +1,6 @@
|
|||
solana_sdk::declare_builtin!(
|
||||
solana_sdk::bpf_loader_upgradeable::ID,
|
||||
solana_bpf_loader_upgradeable_program_with_jit,
|
||||
solana_bpf_loader_program::process_instruction_jit,
|
||||
upgradeable_with_jit::id
|
||||
);
|
|
@ -16,6 +16,7 @@ use rayon::slice::ParallelSliceMut;
|
|||
use solana_sdk::{
|
||||
account::Account,
|
||||
account_utils::StateMut,
|
||||
bpf_loader_upgradeable::{self, UpgradeableLoaderState},
|
||||
clock::{Epoch, Slot},
|
||||
feature_set::{self, FeatureSet},
|
||||
fee_calculator::{FeeCalculator, FeeConfig},
|
||||
|
@ -236,7 +237,7 @@ impl Accounts {
|
|||
let mut program_id = *program_id;
|
||||
loop {
|
||||
if native_loader::check_id(&program_id) {
|
||||
// at the root of the chain, ready to dispatch
|
||||
// At the root of the chain, ready to dispatch
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -262,8 +263,31 @@ impl Accounts {
|
|||
return Err(TransactionError::InvalidProgramForExecution);
|
||||
}
|
||||
|
||||
// add loader to chain
|
||||
// Add loader to chain
|
||||
let program_owner = program.owner;
|
||||
|
||||
if bpf_loader_upgradeable::check_id(&program_owner) {
|
||||
// The upgradeable loader requires the derived ProgramData account
|
||||
if let Ok(UpgradeableLoaderState::Program {
|
||||
programdata_address,
|
||||
}) = program.state()
|
||||
{
|
||||
if let Some(program) = self
|
||||
.accounts_db
|
||||
.load(ancestors, &programdata_address)
|
||||
.map(|(account, _)| account)
|
||||
{
|
||||
accounts.insert(0, (programdata_address, program));
|
||||
} else {
|
||||
error_counters.account_not_found += 1;
|
||||
return Err(TransactionError::ProgramAccountNotFound);
|
||||
}
|
||||
} else {
|
||||
error_counters.invalid_program_for_execution += 1;
|
||||
return Err(TransactionError::InvalidProgramForExecution);
|
||||
}
|
||||
}
|
||||
|
||||
accounts.insert(0, (program_id, program));
|
||||
program_id = program_owner;
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
use serde::Serialize;
|
||||
use solana_sdk::{
|
||||
bpf_loader_upgradeable::{self, UpgradeableLoaderState},
|
||||
client::Client,
|
||||
instruction::{AccountMeta, Instruction},
|
||||
loader_instruction,
|
||||
|
@ -53,6 +54,132 @@ pub fn load_program<T: Client>(
|
|||
program_pubkey
|
||||
}
|
||||
|
||||
pub fn load_buffer_account<T: Client>(
|
||||
bank_client: &T,
|
||||
from_keypair: &Keypair,
|
||||
program: &[u8],
|
||||
) -> Pubkey {
|
||||
let buffer_keypair = Keypair::new();
|
||||
let buffer_pubkey = buffer_keypair.pubkey();
|
||||
|
||||
bank_client
|
||||
.send_and_confirm_message(
|
||||
&[from_keypair, &buffer_keypair],
|
||||
Message::new(
|
||||
&bpf_loader_upgradeable::create_buffer(
|
||||
&from_keypair.pubkey(),
|
||||
&buffer_pubkey,
|
||||
1.max(
|
||||
bank_client
|
||||
.get_minimum_balance_for_rent_exemption(program.len())
|
||||
.unwrap(),
|
||||
),
|
||||
program.len(),
|
||||
)
|
||||
.unwrap(),
|
||||
Some(&from_keypair.pubkey()),
|
||||
),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let chunk_size = 256; // Size of chunk just needs to fit into tx
|
||||
let mut offset = 0;
|
||||
for chunk in program.chunks(chunk_size) {
|
||||
let message = Message::new(
|
||||
&[bpf_loader_upgradeable::write(
|
||||
&buffer_pubkey,
|
||||
offset,
|
||||
chunk.to_vec(),
|
||||
)],
|
||||
Some(&from_keypair.pubkey()),
|
||||
);
|
||||
bank_client
|
||||
.send_and_confirm_message(&[from_keypair, &buffer_keypair], message)
|
||||
.unwrap();
|
||||
offset += chunk_size as u32;
|
||||
}
|
||||
buffer_keypair.pubkey()
|
||||
}
|
||||
|
||||
pub fn load_upgradeable_program<T: Client>(
|
||||
bank_client: &T,
|
||||
from_keypair: &Keypair,
|
||||
program: Vec<u8>,
|
||||
) -> (Pubkey, Keypair) {
|
||||
let executable_keypair = Keypair::new();
|
||||
let program_pubkey = executable_keypair.pubkey();
|
||||
let authority_keypair = Keypair::new();
|
||||
let authority_pubkey = authority_keypair.pubkey();
|
||||
|
||||
let buffer_pubkey = load_buffer_account(bank_client, &from_keypair, &program);
|
||||
|
||||
let message = Message::new(
|
||||
&bpf_loader_upgradeable::deploy_with_max_program_len(
|
||||
&from_keypair.pubkey(),
|
||||
&program_pubkey,
|
||||
&buffer_pubkey,
|
||||
Some(&authority_pubkey),
|
||||
1.max(
|
||||
bank_client
|
||||
.get_minimum_balance_for_rent_exemption(
|
||||
UpgradeableLoaderState::program_len().unwrap(),
|
||||
)
|
||||
.unwrap(),
|
||||
),
|
||||
program.len() * 2,
|
||||
)
|
||||
.unwrap(),
|
||||
Some(&from_keypair.pubkey()),
|
||||
);
|
||||
bank_client
|
||||
.send_and_confirm_message(&[from_keypair, &executable_keypair], message)
|
||||
.unwrap();
|
||||
|
||||
(executable_keypair.pubkey(), authority_keypair)
|
||||
}
|
||||
|
||||
pub fn upgrade_program<T: Client>(
|
||||
bank_client: &T,
|
||||
from_keypair: &Keypair,
|
||||
program_pubkey: &Pubkey,
|
||||
buffer_pubkey: &Pubkey,
|
||||
authority_keypair: &Keypair,
|
||||
spill_pubkey: &Pubkey,
|
||||
) {
|
||||
let message = Message::new(
|
||||
&[bpf_loader_upgradeable::upgrade(
|
||||
&program_pubkey,
|
||||
&buffer_pubkey,
|
||||
&authority_keypair.pubkey(),
|
||||
&spill_pubkey,
|
||||
)],
|
||||
Some(&from_keypair.pubkey()),
|
||||
);
|
||||
bank_client
|
||||
.send_and_confirm_message(&[from_keypair, &authority_keypair], message)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
pub fn set_upgrade_authority<T: Client>(
|
||||
bank_client: &T,
|
||||
from_keypair: &Keypair,
|
||||
program_pubkey: &Pubkey,
|
||||
current_authority_keypair: &Keypair,
|
||||
new_authority_pubkey: Option<&Pubkey>,
|
||||
) {
|
||||
let message = Message::new(
|
||||
&[bpf_loader_upgradeable::set_authority(
|
||||
program_pubkey,
|
||||
¤t_authority_keypair.pubkey(),
|
||||
new_authority_pubkey,
|
||||
)],
|
||||
Some(&from_keypair.pubkey()),
|
||||
);
|
||||
bank_client
|
||||
.send_and_confirm_message(&[from_keypair, ¤t_authority_keypair], message)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
// Return an Instruction that invokes `program_id` with `data` and required
|
||||
// a signature from `from_pubkey`.
|
||||
pub fn create_invoke_instruction<T: Serialize>(
|
||||
|
|
|
@ -590,6 +590,74 @@ impl MessageProcessor {
|
|||
Ok((message, id, index))
|
||||
}
|
||||
|
||||
/// Entrypoint for a cross-program invocation from a native program
|
||||
pub fn native_invoke(
|
||||
invoke_context: &mut dyn InvokeContext,
|
||||
instruction: Instruction,
|
||||
keyed_accounts: &[&KeyedAccount],
|
||||
signers_seeds: &[&[&[u8]]],
|
||||
) -> Result<(), InstructionError> {
|
||||
let caller_program_id = invoke_context.get_caller()?;
|
||||
|
||||
// Translate and verify caller's data
|
||||
|
||||
let signers = signers_seeds
|
||||
.iter()
|
||||
.map(|seeds| Pubkey::create_program_address(&seeds, caller_program_id))
|
||||
.collect::<Result<Vec<_>, solana_sdk::pubkey::PubkeyError>>()?;
|
||||
let (message, callee_program_id, callee_program_id_index) =
|
||||
Self::create_message(&instruction, &keyed_accounts, &signers)?;
|
||||
let mut accounts = vec![];
|
||||
let mut account_refs = vec![];
|
||||
'root: for account_key in message.account_keys.iter() {
|
||||
for keyed_account in keyed_accounts {
|
||||
if account_key == keyed_account.unsigned_key() {
|
||||
accounts.push(Rc::new(keyed_account.account.clone()));
|
||||
account_refs.push(keyed_account);
|
||||
continue 'root;
|
||||
}
|
||||
}
|
||||
return Err(InstructionError::MissingAccount);
|
||||
}
|
||||
|
||||
// Process instruction
|
||||
|
||||
invoke_context.record_instruction(&instruction);
|
||||
let program_account = (**accounts
|
||||
.get(callee_program_id_index)
|
||||
.ok_or(InstructionError::MissingAccount)?)
|
||||
.clone();
|
||||
if !program_account.borrow().executable {
|
||||
return Err(InstructionError::AccountNotExecutable);
|
||||
}
|
||||
let executable_accounts = vec![(callee_program_id, program_account)];
|
||||
|
||||
MessageProcessor::process_cross_program_instruction(
|
||||
&message,
|
||||
&executable_accounts,
|
||||
&accounts,
|
||||
invoke_context,
|
||||
)?;
|
||||
|
||||
// Copy results back to caller
|
||||
|
||||
for (i, (account, account_ref)) in accounts.iter().zip(account_refs).enumerate() {
|
||||
let account = account.borrow();
|
||||
if message.is_writable(i) && !account.executable {
|
||||
account_ref.try_account_ref_mut()?.lamports = account.lamports;
|
||||
account_ref.try_account_ref_mut()?.owner = account.owner;
|
||||
if account_ref.data_len()? != account.data.len() && account_ref.data_len()? != 0 {
|
||||
// Only support for `CreateAccount` at this time.
|
||||
// Need a way to limit total realloc size across multiple CPI calls
|
||||
return Err(InstructionError::InvalidRealloc);
|
||||
}
|
||||
account_ref.try_account_ref_mut()?.data = account.data.clone();
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Process a cross-program instruction
|
||||
/// This method calls the instruction's program entrypoint function
|
||||
pub fn process_cross_program_instruction(
|
||||
|
|
|
@ -0,0 +1,213 @@
|
|||
//! @brief An Upgradeable Solana BPF loader.
|
||||
//!
|
||||
//! The upgradeable BPF loader is responsible for deploying, upgrading, and
|
||||
//! executing BPF programs. The upgradeable loader allows a program's authority
|
||||
//! to update the program at any time. This ability break's the "code is law"
|
||||
//! contract the usually enforces the policy that once a program is on-chain it
|
||||
//! becomes immutable. Because of this, care should be taken before executing
|
||||
//! upgradeable programs which still have a functioning authority. For more
|
||||
//! information refer to `loader_upgradeable_instruction.rs`
|
||||
|
||||
use crate::{
|
||||
instruction::{AccountMeta, Instruction, InstructionError},
|
||||
loader_upgradeable_instruction::UpgradeableLoaderInstruction,
|
||||
pubkey::Pubkey,
|
||||
system_instruction, sysvar,
|
||||
};
|
||||
use bincode::serialized_size;
|
||||
|
||||
crate::declare_id!("BPFLoaderUpgradeab1e11111111111111111111111");
|
||||
|
||||
/// Upgradeable loader account states
|
||||
#[derive(Debug, Serialize, Deserialize, PartialEq, Clone, Copy, AbiExample)]
|
||||
pub enum UpgradeableLoaderState {
|
||||
/// Account is not initialized.
|
||||
Uninitialized,
|
||||
/// A Buffer account.
|
||||
Buffer,
|
||||
/// An Program account.
|
||||
Program {
|
||||
/// Address of the ProgramData account.
|
||||
programdata_address: Pubkey,
|
||||
},
|
||||
// A ProgramData account.
|
||||
ProgramData {
|
||||
/// Slot that the program was last modified.
|
||||
slot: u64,
|
||||
/// Address of the Program's upgrade authority.
|
||||
upgrade_authority_address: Option<Pubkey>,
|
||||
// The raw program data follows this serialized structure in the
|
||||
// account's data.
|
||||
},
|
||||
}
|
||||
impl UpgradeableLoaderState {
|
||||
/// Length of an buffer account's data.
|
||||
pub fn buffer_len(program_len: usize) -> Result<usize, InstructionError> {
|
||||
Ok(serialized_size(&Self::Buffer)
|
||||
.map(|len| len as usize)
|
||||
.map_err(|_| InstructionError::InvalidInstructionData)?
|
||||
+ program_len)
|
||||
}
|
||||
/// Offset into the ProgramData account's data of the program bits.
|
||||
pub fn buffer_data_offset() -> Result<usize, InstructionError> {
|
||||
Self::buffer_len(0)
|
||||
}
|
||||
/// Length of an executable account's data.
|
||||
pub fn program_len() -> Result<usize, InstructionError> {
|
||||
serialized_size(&Self::Program {
|
||||
programdata_address: Pubkey::default(),
|
||||
})
|
||||
.map(|len| len as usize)
|
||||
.map_err(|_| InstructionError::InvalidInstructionData)
|
||||
}
|
||||
/// Length of a ProgramData account's data.
|
||||
pub fn programdata_len(program_len: usize) -> Result<usize, InstructionError> {
|
||||
Ok(serialized_size(&Self::ProgramData {
|
||||
slot: 0,
|
||||
upgrade_authority_address: Some(Pubkey::default()),
|
||||
})
|
||||
.map(|len| len as usize)
|
||||
.map_err(|_| InstructionError::InvalidInstructionData)?
|
||||
+ program_len)
|
||||
}
|
||||
/// Offset into the ProgramData account's data of the program bits.
|
||||
pub fn programdata_data_offset() -> Result<usize, InstructionError> {
|
||||
Self::programdata_len(0)
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the instructions required to initialize a Buffer account.
|
||||
pub fn create_buffer(
|
||||
payer_address: &Pubkey,
|
||||
buffer_address: &Pubkey,
|
||||
lamports: u64,
|
||||
program_len: usize,
|
||||
) -> Result<Vec<Instruction>, InstructionError> {
|
||||
Ok(vec![
|
||||
system_instruction::create_account(
|
||||
payer_address,
|
||||
buffer_address,
|
||||
lamports,
|
||||
UpgradeableLoaderState::buffer_len(program_len)? as u64,
|
||||
&id(),
|
||||
),
|
||||
Instruction::new(
|
||||
id(),
|
||||
&UpgradeableLoaderInstruction::InitializeBuffer,
|
||||
vec![AccountMeta::new(*buffer_address, false)],
|
||||
),
|
||||
])
|
||||
}
|
||||
|
||||
/// Returns the instructions required to write a chunk of program data to a
|
||||
/// buffer account.
|
||||
pub fn write(buffer_address: &Pubkey, offset: u32, bytes: Vec<u8>) -> Instruction {
|
||||
Instruction::new(
|
||||
id(),
|
||||
&UpgradeableLoaderInstruction::Write { offset, bytes },
|
||||
vec![AccountMeta::new(*buffer_address, true)],
|
||||
)
|
||||
}
|
||||
|
||||
/// Returns the instructions required to deploy a program with a specified
|
||||
/// maximum program length. The maximum length must be large enough to
|
||||
/// accommodate any future upgrades.
|
||||
pub fn deploy_with_max_program_len(
|
||||
payer_address: &Pubkey,
|
||||
program_address: &Pubkey,
|
||||
buffer_address: &Pubkey,
|
||||
upgrade_authority_address: Option<&Pubkey>,
|
||||
program_lamports: u64,
|
||||
max_data_len: usize,
|
||||
) -> Result<Vec<Instruction>, InstructionError> {
|
||||
let (programdata_address, _) = Pubkey::find_program_address(&[program_address.as_ref()], &id());
|
||||
let mut metas = vec![
|
||||
AccountMeta::new(*payer_address, true),
|
||||
AccountMeta::new(programdata_address, false),
|
||||
AccountMeta::new(*program_address, false),
|
||||
AccountMeta::new(*buffer_address, false),
|
||||
AccountMeta::new_readonly(sysvar::rent::id(), false),
|
||||
AccountMeta::new_readonly(sysvar::clock::id(), false),
|
||||
AccountMeta::new_readonly(crate::system_program::id(), false),
|
||||
];
|
||||
if let Some(address) = upgrade_authority_address {
|
||||
metas.push(AccountMeta::new_readonly(*address, false));
|
||||
}
|
||||
Ok(vec![
|
||||
system_instruction::create_account(
|
||||
payer_address,
|
||||
program_address,
|
||||
program_lamports,
|
||||
UpgradeableLoaderState::program_len()? as u64,
|
||||
&id(),
|
||||
),
|
||||
Instruction::new(
|
||||
id(),
|
||||
&UpgradeableLoaderInstruction::DeployWithMaxDataLen { max_data_len },
|
||||
metas,
|
||||
),
|
||||
])
|
||||
}
|
||||
|
||||
/// Returns the instructions required to upgrade a program.
|
||||
pub fn upgrade(
|
||||
program_address: &Pubkey,
|
||||
buffer_address: &Pubkey,
|
||||
authority_address: &Pubkey,
|
||||
spill_address: &Pubkey,
|
||||
) -> Instruction {
|
||||
let (programdata_address, _) = Pubkey::find_program_address(&[program_address.as_ref()], &id());
|
||||
Instruction::new(
|
||||
id(),
|
||||
&UpgradeableLoaderInstruction::Upgrade,
|
||||
vec![
|
||||
AccountMeta::new(programdata_address, false),
|
||||
AccountMeta::new_readonly(*program_address, false),
|
||||
AccountMeta::new(*buffer_address, false),
|
||||
AccountMeta::new(*spill_address, false),
|
||||
AccountMeta::new_readonly(sysvar::rent::id(), false),
|
||||
AccountMeta::new_readonly(sysvar::clock::id(), false),
|
||||
AccountMeta::new_readonly(*authority_address, true),
|
||||
],
|
||||
)
|
||||
}
|
||||
|
||||
/// Returns the instructions required to set a program's authority.
|
||||
pub fn set_authority(
|
||||
program_address: &Pubkey,
|
||||
current_authority_address: &Pubkey,
|
||||
new_authority_address: Option<&Pubkey>,
|
||||
) -> Instruction {
|
||||
let (programdata_address, _) = Pubkey::find_program_address(&[program_address.as_ref()], &id());
|
||||
|
||||
let mut metas = vec![
|
||||
AccountMeta::new(programdata_address, false),
|
||||
AccountMeta::new_readonly(*current_authority_address, true),
|
||||
];
|
||||
if let Some(address) = new_authority_address {
|
||||
metas.push(AccountMeta::new_readonly(*address, false));
|
||||
}
|
||||
Instruction::new(id(), &UpgradeableLoaderInstruction::SetAuthority, metas)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_account_lengths() {
|
||||
assert_eq!(
|
||||
4,
|
||||
serialized_size(&UpgradeableLoaderState::Uninitialized).unwrap()
|
||||
);
|
||||
assert_eq!(36, UpgradeableLoaderState::program_len().unwrap());
|
||||
assert_eq!(
|
||||
45,
|
||||
UpgradeableLoaderState::programdata_data_offset().unwrap()
|
||||
);
|
||||
assert_eq!(
|
||||
45 + 42,
|
||||
UpgradeableLoaderState::programdata_len(42).unwrap()
|
||||
);
|
||||
}
|
||||
}
|
|
@ -7,6 +7,7 @@ extern crate self as solana_program;
|
|||
pub mod account_info;
|
||||
pub mod bpf_loader;
|
||||
pub mod bpf_loader_deprecated;
|
||||
pub mod bpf_loader_upgradeable;
|
||||
pub mod clock;
|
||||
pub mod decode_error;
|
||||
pub mod entrypoint;
|
||||
|
@ -18,6 +19,7 @@ pub mod hash;
|
|||
pub mod incinerator;
|
||||
pub mod instruction;
|
||||
pub mod loader_instruction;
|
||||
pub mod loader_upgradeable_instruction;
|
||||
pub mod log;
|
||||
pub mod message;
|
||||
pub mod native_token;
|
||||
|
|
|
@ -0,0 +1,104 @@
|
|||
//! Upgradeable loader instruction definitions
|
||||
|
||||
#[repr(u8)]
|
||||
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
|
||||
pub enum UpgradeableLoaderInstruction {
|
||||
/// Initialize a Buffer account.
|
||||
///
|
||||
/// A Buffer account is an intermediary that once fully populated is used
|
||||
/// with the `DeployWithMaxDataLen` instruction to populate the program's
|
||||
/// ProgramData account.
|
||||
///
|
||||
/// The `InitializeBuffer` instruction requires no signers and MUST be
|
||||
/// included within the same Transaction as the system program's
|
||||
/// `CreateAccount` instruction that creates the account being initialized.
|
||||
/// Otherwise another party may initialize the account.
|
||||
///
|
||||
/// # Account references
|
||||
/// 0. [writable] source account to initialize.
|
||||
InitializeBuffer,
|
||||
|
||||
/// Write program data into a Buffer account.
|
||||
///
|
||||
/// # Account references
|
||||
/// 0. [writable, signer] Buffer account to write program data to.
|
||||
Write {
|
||||
/// Offset at which to write the given bytes.
|
||||
offset: u32,
|
||||
/// Serialized program data
|
||||
#[serde(with = "serde_bytes")]
|
||||
bytes: Vec<u8>,
|
||||
},
|
||||
|
||||
/// Deploy an executable program.
|
||||
///
|
||||
/// A program consists of a Program and ProgramData account pair.
|
||||
/// - The Program account's address will serve as the program id any
|
||||
/// instructions that execute this program.
|
||||
/// - The ProgramData account will remain mutable by the loader only and
|
||||
/// holds the program data and authority information. The ProgramData
|
||||
/// account's address is derived from the Program account's address and
|
||||
/// created by the DeployWithMaxDataLen instruction.
|
||||
///
|
||||
/// The ProgramData address is derived from the Program account's address as
|
||||
/// follows:
|
||||
///
|
||||
/// `let (program_data_address, _) = Pubkey::find_program_address(
|
||||
/// &[program_address],
|
||||
/// &bpf_loader_upgradeable::id()
|
||||
/// );`
|
||||
///
|
||||
/// The `DeployWithMaxDataLen` instruction does not require the ProgramData
|
||||
/// account be a signer and therefore MUST be included within the same
|
||||
/// Transaction as the system program's `CreateAccount` instruction that
|
||||
/// creates the Program account. Otherwise another party may initialize
|
||||
/// the account.
|
||||
///
|
||||
/// # Account references
|
||||
/// 0. [Signer] The payer account that will pay to create the ProgramData
|
||||
/// account.
|
||||
/// 1. [writable] The uninitialized ProgramData account.
|
||||
/// 2. [writable] The uninitialized Program account.
|
||||
/// 3. [writable] The Buffer account where the program data has been
|
||||
/// written.
|
||||
/// 4. [] Rent sysvar.
|
||||
/// 5. [] Clock sysvar.
|
||||
/// 6. [] System program (`solana_sdk::system_program::id()`).
|
||||
/// 7. [] The program's authority, optional, if omitted then the program
|
||||
/// will no longer upgradeable.
|
||||
DeployWithMaxDataLen {
|
||||
/// Maximum length that the program can be upgraded to.
|
||||
max_data_len: usize,
|
||||
},
|
||||
|
||||
/// Upgrade a program.
|
||||
///
|
||||
/// A program can be updated as long as the program's authority has not been
|
||||
/// set to `None`.
|
||||
///
|
||||
/// The Buffer account must contain sufficient lamports to fund the
|
||||
/// ProgramData account to be rent-exempt, any additional lamports left over
|
||||
/// will be transferred to the spill, account leaving the Buffer account
|
||||
/// balance at zero.
|
||||
///
|
||||
/// # Account references
|
||||
/// 0. [writable] The ProgramData account.
|
||||
/// 1. [] The Program account.
|
||||
/// 2. [Writable] The Buffer account where the program data has been
|
||||
/// written.
|
||||
/// 3. [writable] The spill account.
|
||||
/// 4. [] Rent sysvar.
|
||||
/// 5. [] Clock sysvar.
|
||||
/// 6. [signer] The program's authority.
|
||||
Upgrade,
|
||||
|
||||
/// Set a new authority that is allowed to upgrade the program. To
|
||||
/// permanently disable program updates omit the new authority.
|
||||
///
|
||||
/// # Account references
|
||||
/// 0. `[writable]` The ProgramData account to change the authority of.
|
||||
/// 1. `[signer]` The current authority.
|
||||
/// 2. `[]` The new authority, optional, if omitted then the program will
|
||||
/// not be upgradeable.
|
||||
SetAuthority,
|
||||
}
|
|
@ -102,6 +102,10 @@ pub mod simple_capitalization {
|
|||
solana_sdk::declare_id!("9r69RnnxABmpcPFfj1yhg4n9YFR2MNaLdKJCC6v3Speb");
|
||||
}
|
||||
|
||||
pub mod bpf_loader_upgradeable_program {
|
||||
solana_sdk::declare_id!("FbhK8HN9qvNHvJcoFVHAEUCNkagHvu7DTWzdnLuVQ5u4");
|
||||
}
|
||||
|
||||
lazy_static! {
|
||||
/// Map of feature identifiers to user-visible description
|
||||
pub static ref FEATURE_NAMES: HashMap<Pubkey, &'static str> = [
|
||||
|
@ -129,6 +133,7 @@ lazy_static! {
|
|||
(rewrite_stake::id(), "rewrite stake"),
|
||||
(filter_stake_delegation_accounts::id(), "filter stake_delegation_accounts #14062"),
|
||||
(simple_capitalization::id(), "simple capitalization"),
|
||||
(bpf_loader_upgradeable_program::id(), "upgradeable bpf loader"),
|
||||
/*************** ADD NEW FEATURES HERE ***************/
|
||||
]
|
||||
.iter()
|
||||
|
|
Loading…
Reference in New Issue