Prevent the invoke and upgrade of programs in the same tx batch (#14653)
* Prevent the invoke and upgrade of programs in the same tx batch * Pass program address as writable in the upgrade instruction
This commit is contained in:
1242
programs/bpf/Cargo.lock
generated
1242
programs/bpf/Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@ -22,14 +22,18 @@ walkdir = "2"
|
|||||||
bincode = "1.1.4"
|
bincode = "1.1.4"
|
||||||
byteorder = "1.3.2"
|
byteorder = "1.3.2"
|
||||||
elf = "0.0.10"
|
elf = "0.0.10"
|
||||||
|
itertools = "0.10.0"
|
||||||
miow = "0.2.2"
|
miow = "0.2.2"
|
||||||
net2 = "0.2.37"
|
net2 = "0.2.37"
|
||||||
solana-bpf-loader-program = { path = "../bpf_loader", version = "1.6.0" }
|
solana-bpf-loader-program = { path = "../bpf_loader", version = "1.6.0" }
|
||||||
|
solana-cli-output = { path = "../../cli-output", version = "1.6.0" }
|
||||||
solana-logger = { path = "../../logger", version = "1.6.0" }
|
solana-logger = { path = "../../logger", version = "1.6.0" }
|
||||||
solana-measure = { path = "../../measure", version = "1.6.0" }
|
solana-measure = { path = "../../measure", version = "1.6.0" }
|
||||||
|
solana_rbpf = "=0.2.3"
|
||||||
solana-runtime = { path = "../../runtime", version = "1.6.0" }
|
solana-runtime = { path = "../../runtime", version = "1.6.0" }
|
||||||
solana-sdk = { path = "../../sdk", version = "1.6.0" }
|
solana-sdk = { path = "../../sdk", version = "1.6.0" }
|
||||||
solana_rbpf = "=0.2.3"
|
solana-transaction-status = { path = "../../transaction-status", version = "1.6.0" }
|
||||||
|
|
||||||
|
|
||||||
[[bench]]
|
[[bench]]
|
||||||
name = "bpf_loader"
|
name = "bpf_loader"
|
||||||
|
@ -3,15 +3,17 @@
|
|||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate solana_bpf_loader_program;
|
extern crate solana_bpf_loader_program;
|
||||||
|
|
||||||
|
use itertools::izip;
|
||||||
use solana_bpf_loader_program::{
|
use solana_bpf_loader_program::{
|
||||||
create_vm,
|
create_vm,
|
||||||
serialization::{deserialize_parameters, serialize_parameters},
|
serialization::{deserialize_parameters, serialize_parameters},
|
||||||
syscalls::register_syscalls,
|
syscalls::register_syscalls,
|
||||||
ThisInstructionMeter,
|
ThisInstructionMeter,
|
||||||
};
|
};
|
||||||
|
use solana_cli_output::display::println_transaction;
|
||||||
use solana_rbpf::vm::{Config, Executable, Tracer};
|
use solana_rbpf::vm::{Config, Executable, Tracer};
|
||||||
use solana_runtime::{
|
use solana_runtime::{
|
||||||
bank::{Bank, ExecuteTimings},
|
bank::{Bank, ExecuteTimings, NonceRollbackInfo, TransactionBalancesSet, TransactionResults},
|
||||||
bank_client::BankClient,
|
bank_client::BankClient,
|
||||||
genesis_utils::{create_genesis_config, GenesisConfigInfo},
|
genesis_utils::{create_genesis_config, GenesisConfigInfo},
|
||||||
loader_utils::{
|
loader_utils::{
|
||||||
@ -30,11 +32,16 @@ use solana_sdk::{
|
|||||||
message::Message,
|
message::Message,
|
||||||
process_instruction::{InvokeContext, MockInvokeContext},
|
process_instruction::{InvokeContext, MockInvokeContext},
|
||||||
pubkey::Pubkey,
|
pubkey::Pubkey,
|
||||||
signature::{Keypair, Signer},
|
signature::{keypair_from_seed, Keypair, Signer},
|
||||||
|
system_instruction,
|
||||||
sysvar::{clock, fees, rent, slot_hashes, stake_history},
|
sysvar::{clock, fees, rent, slot_hashes, stake_history},
|
||||||
transaction::{Transaction, TransactionError},
|
transaction::{Transaction, TransactionError},
|
||||||
};
|
};
|
||||||
use std::{cell::RefCell, env, fs::File, io::Read, path::PathBuf, sync::Arc};
|
use solana_transaction_status::{
|
||||||
|
token_balances::collect_token_balances, ConfirmedTransaction, InnerInstructions,
|
||||||
|
TransactionStatusMeta, TransactionWithStatusMeta, UiTransactionEncoding,
|
||||||
|
};
|
||||||
|
use std::{cell::RefCell, collections::HashMap, env, fs::File, io::Read, path::PathBuf, sync::Arc};
|
||||||
|
|
||||||
/// BPF program file extension
|
/// BPF program file extension
|
||||||
const PLATFORM_FILE_EXTENSION_BPF: &str = "so";
|
const PLATFORM_FILE_EXTENSION_BPF: &str = "so";
|
||||||
@ -100,20 +107,31 @@ fn write_bpf_program(
|
|||||||
fn load_upgradeable_bpf_program(
|
fn load_upgradeable_bpf_program(
|
||||||
bank_client: &BankClient,
|
bank_client: &BankClient,
|
||||||
payer_keypair: &Keypair,
|
payer_keypair: &Keypair,
|
||||||
|
buffer_keypair: &Keypair,
|
||||||
|
executable_keypair: &Keypair,
|
||||||
|
authority_keypair: &Keypair,
|
||||||
name: &str,
|
name: &str,
|
||||||
) -> (Pubkey, Keypair) {
|
) {
|
||||||
let path = create_bpf_path(name);
|
let path = create_bpf_path(name);
|
||||||
let mut file = File::open(&path).unwrap_or_else(|err| {
|
let mut file = File::open(&path).unwrap_or_else(|err| {
|
||||||
panic!("Failed to open {}: {}", path.display(), err);
|
panic!("Failed to open {}: {}", path.display(), err);
|
||||||
});
|
});
|
||||||
let mut elf = Vec::new();
|
let mut elf = Vec::new();
|
||||||
file.read_to_end(&mut elf).unwrap();
|
file.read_to_end(&mut elf).unwrap();
|
||||||
load_upgradeable_program(bank_client, payer_keypair, elf)
|
load_upgradeable_program(
|
||||||
|
bank_client,
|
||||||
|
payer_keypair,
|
||||||
|
buffer_keypair,
|
||||||
|
executable_keypair,
|
||||||
|
authority_keypair,
|
||||||
|
elf,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn upgrade_bpf_program(
|
fn upgrade_bpf_program(
|
||||||
bank_client: &BankClient,
|
bank_client: &BankClient,
|
||||||
payer_keypair: &Keypair,
|
payer_keypair: &Keypair,
|
||||||
|
buffer_keypair: &Keypair,
|
||||||
executable_pubkey: &Pubkey,
|
executable_pubkey: &Pubkey,
|
||||||
authority_keypair: &Keypair,
|
authority_keypair: &Keypair,
|
||||||
name: &str,
|
name: &str,
|
||||||
@ -124,15 +142,15 @@ fn upgrade_bpf_program(
|
|||||||
});
|
});
|
||||||
let mut elf = Vec::new();
|
let mut elf = Vec::new();
|
||||||
file.read_to_end(&mut elf).unwrap();
|
file.read_to_end(&mut elf).unwrap();
|
||||||
let buffer_pubkey = load_buffer_account(bank_client, payer_keypair, &elf);
|
load_buffer_account(bank_client, payer_keypair, &buffer_keypair, &elf);
|
||||||
upgrade_program(
|
upgrade_program(
|
||||||
bank_client,
|
bank_client,
|
||||||
payer_keypair,
|
payer_keypair,
|
||||||
executable_pubkey,
|
executable_pubkey,
|
||||||
&buffer_pubkey,
|
&buffer_keypair.pubkey(),
|
||||||
&authority_keypair,
|
&authority_keypair,
|
||||||
&payer_keypair.pubkey(),
|
&payer_keypair.pubkey(),
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_program(
|
fn run_program(
|
||||||
@ -242,6 +260,108 @@ fn process_transaction_and_record_inner(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn execute_transactions(bank: &Bank, txs: &[Transaction]) -> Vec<ConfirmedTransaction> {
|
||||||
|
let batch = bank.prepare_batch(txs, None);
|
||||||
|
let mut timings = ExecuteTimings::default();
|
||||||
|
let mut mint_decimals = HashMap::new();
|
||||||
|
let tx_pre_token_balances = collect_token_balances(&bank, &batch, &mut mint_decimals);
|
||||||
|
let (
|
||||||
|
TransactionResults {
|
||||||
|
execution_results, ..
|
||||||
|
},
|
||||||
|
TransactionBalancesSet {
|
||||||
|
pre_balances,
|
||||||
|
post_balances,
|
||||||
|
..
|
||||||
|
},
|
||||||
|
mut inner_instructions,
|
||||||
|
mut transaction_logs,
|
||||||
|
) = bank.load_execute_and_commit_transactions(
|
||||||
|
&batch,
|
||||||
|
std::usize::MAX,
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
&mut timings,
|
||||||
|
);
|
||||||
|
let tx_post_token_balances = collect_token_balances(&bank, &batch, &mut mint_decimals);
|
||||||
|
|
||||||
|
for _ in 0..(txs.len() - transaction_logs.len()) {
|
||||||
|
transaction_logs.push(vec![]);
|
||||||
|
}
|
||||||
|
for _ in 0..(txs.len() - inner_instructions.len()) {
|
||||||
|
inner_instructions.push(None);
|
||||||
|
}
|
||||||
|
|
||||||
|
izip!(
|
||||||
|
txs.iter(),
|
||||||
|
execution_results.into_iter(),
|
||||||
|
inner_instructions.into_iter(),
|
||||||
|
pre_balances.into_iter(),
|
||||||
|
post_balances.into_iter(),
|
||||||
|
tx_pre_token_balances.into_iter(),
|
||||||
|
tx_post_token_balances.into_iter(),
|
||||||
|
transaction_logs.into_iter(),
|
||||||
|
)
|
||||||
|
.map(
|
||||||
|
|(
|
||||||
|
tx,
|
||||||
|
(execute_result, nonce_rollback),
|
||||||
|
inner_instructions,
|
||||||
|
pre_balances,
|
||||||
|
post_balances,
|
||||||
|
pre_token_balances,
|
||||||
|
post_token_balances,
|
||||||
|
log_messages,
|
||||||
|
)| {
|
||||||
|
let fee_calculator = nonce_rollback
|
||||||
|
.map(|nonce_rollback| nonce_rollback.fee_calculator())
|
||||||
|
.unwrap_or_else(|| bank.get_fee_calculator(&tx.message().recent_blockhash))
|
||||||
|
.expect("FeeCalculator must exist");
|
||||||
|
let fee = fee_calculator.calculate_fee(tx.message());
|
||||||
|
|
||||||
|
let inner_instructions = inner_instructions.map(|inner_instructions| {
|
||||||
|
inner_instructions
|
||||||
|
.into_iter()
|
||||||
|
.enumerate()
|
||||||
|
.map(|(index, instructions)| InnerInstructions {
|
||||||
|
index: index as u8,
|
||||||
|
instructions,
|
||||||
|
})
|
||||||
|
.filter(|i| !i.instructions.is_empty())
|
||||||
|
.collect()
|
||||||
|
});
|
||||||
|
|
||||||
|
let tx_status_meta = TransactionStatusMeta {
|
||||||
|
status: execute_result,
|
||||||
|
fee,
|
||||||
|
pre_balances,
|
||||||
|
post_balances,
|
||||||
|
pre_token_balances: Some(pre_token_balances),
|
||||||
|
post_token_balances: Some(post_token_balances),
|
||||||
|
inner_instructions,
|
||||||
|
log_messages: Some(log_messages),
|
||||||
|
};
|
||||||
|
|
||||||
|
ConfirmedTransaction {
|
||||||
|
slot: bank.slot(),
|
||||||
|
transaction: TransactionWithStatusMeta {
|
||||||
|
transaction: tx.clone(),
|
||||||
|
meta: Some(tx_status_meta),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn print_confirmed_tx(name: &str, confirmed_tx: ConfirmedTransaction) {
|
||||||
|
let tx = confirmed_tx.transaction.transaction.clone();
|
||||||
|
let encoded = confirmed_tx.encode(UiTransactionEncoding::JsonParsed);
|
||||||
|
println!("EXECUTE {} (slot {})", name, encoded.slot);
|
||||||
|
println_transaction(&tx, &encoded.transaction.meta, " ");
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(any(feature = "bpf_c", feature = "bpf_rust"))]
|
#[cfg(any(feature = "bpf_c", feature = "bpf_rust"))]
|
||||||
fn test_program_bpf_sanity() {
|
fn test_program_bpf_sanity() {
|
||||||
@ -1572,8 +1692,18 @@ fn test_program_bpf_upgrade() {
|
|||||||
let bank_client = BankClient::new(bank);
|
let bank_client = BankClient::new(bank);
|
||||||
|
|
||||||
// Deploy upgrade program
|
// Deploy upgrade program
|
||||||
let (program_id, authority_keypair) =
|
let buffer_keypair = Keypair::new();
|
||||||
load_upgradeable_bpf_program(&bank_client, &mint_keypair, "solana_bpf_rust_upgradeable");
|
let program_keypair = Keypair::new();
|
||||||
|
let program_id = program_keypair.pubkey();
|
||||||
|
let authority_keypair = Keypair::new();
|
||||||
|
load_upgradeable_bpf_program(
|
||||||
|
&bank_client,
|
||||||
|
&mint_keypair,
|
||||||
|
&buffer_keypair,
|
||||||
|
&program_keypair,
|
||||||
|
&authority_keypair,
|
||||||
|
"solana_bpf_rust_upgradeable",
|
||||||
|
);
|
||||||
|
|
||||||
let mut instruction = Instruction::new(
|
let mut instruction = Instruction::new(
|
||||||
program_id,
|
program_id,
|
||||||
@ -1593,9 +1723,11 @@ fn test_program_bpf_upgrade() {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Upgrade program
|
// Upgrade program
|
||||||
|
let buffer_keypair = Keypair::new();
|
||||||
upgrade_bpf_program(
|
upgrade_bpf_program(
|
||||||
&bank_client,
|
&bank_client,
|
||||||
&mint_keypair,
|
&mint_keypair,
|
||||||
|
&buffer_keypair,
|
||||||
&program_id,
|
&program_id,
|
||||||
&authority_keypair,
|
&authority_keypair,
|
||||||
"solana_bpf_rust_upgraded",
|
"solana_bpf_rust_upgraded",
|
||||||
@ -1620,9 +1752,11 @@ fn test_program_bpf_upgrade() {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Upgrade back to the original program
|
// Upgrade back to the original program
|
||||||
|
let buffer_keypair = Keypair::new();
|
||||||
upgrade_bpf_program(
|
upgrade_bpf_program(
|
||||||
&bank_client,
|
&bank_client,
|
||||||
&mint_keypair,
|
&mint_keypair,
|
||||||
|
&buffer_keypair,
|
||||||
&program_id,
|
&program_id,
|
||||||
&new_authority_keypair,
|
&new_authority_keypair,
|
||||||
"solana_bpf_rust_upgradeable",
|
"solana_bpf_rust_upgradeable",
|
||||||
@ -1661,8 +1795,18 @@ fn test_program_bpf_invoke_upgradeable_via_cpi() {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Deploy upgradeable program
|
// Deploy upgradeable program
|
||||||
let (program_id, authority_keypair) =
|
let buffer_keypair = Keypair::new();
|
||||||
load_upgradeable_bpf_program(&bank_client, &mint_keypair, "solana_bpf_rust_upgradeable");
|
let program_keypair = Keypair::new();
|
||||||
|
let program_id = program_keypair.pubkey();
|
||||||
|
let authority_keypair = Keypair::new();
|
||||||
|
load_upgradeable_bpf_program(
|
||||||
|
&bank_client,
|
||||||
|
&mint_keypair,
|
||||||
|
&buffer_keypair,
|
||||||
|
&program_keypair,
|
||||||
|
&authority_keypair,
|
||||||
|
"solana_bpf_rust_upgradeable",
|
||||||
|
);
|
||||||
|
|
||||||
let mut instruction = Instruction::new(
|
let mut instruction = Instruction::new(
|
||||||
invoke_and_return,
|
invoke_and_return,
|
||||||
@ -1684,9 +1828,11 @@ fn test_program_bpf_invoke_upgradeable_via_cpi() {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Upgrade program
|
// Upgrade program
|
||||||
|
let buffer_keypair = Keypair::new();
|
||||||
upgrade_bpf_program(
|
upgrade_bpf_program(
|
||||||
&bank_client,
|
&bank_client,
|
||||||
&mint_keypair,
|
&mint_keypair,
|
||||||
|
&buffer_keypair,
|
||||||
&program_id,
|
&program_id,
|
||||||
&authority_keypair,
|
&authority_keypair,
|
||||||
"solana_bpf_rust_upgraded",
|
"solana_bpf_rust_upgraded",
|
||||||
@ -1711,9 +1857,11 @@ fn test_program_bpf_invoke_upgradeable_via_cpi() {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Upgrade back to the original program
|
// Upgrade back to the original program
|
||||||
|
let buffer_keypair = Keypair::new();
|
||||||
upgrade_bpf_program(
|
upgrade_bpf_program(
|
||||||
&bank_client,
|
&bank_client,
|
||||||
&mint_keypair,
|
&mint_keypair,
|
||||||
|
&buffer_keypair,
|
||||||
&program_id,
|
&program_id,
|
||||||
&new_authority_keypair,
|
&new_authority_keypair,
|
||||||
"solana_bpf_rust_upgradeable",
|
"solana_bpf_rust_upgradeable",
|
||||||
@ -1794,8 +1942,18 @@ fn test_program_bpf_upgrade_via_cpi() {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Deploy upgradeable program
|
// Deploy upgradeable program
|
||||||
let (program_id, authority_keypair) =
|
let buffer_keypair = Keypair::new();
|
||||||
load_upgradeable_bpf_program(&bank_client, &mint_keypair, "solana_bpf_rust_upgradeable");
|
let program_keypair = Keypair::new();
|
||||||
|
let program_id = program_keypair.pubkey();
|
||||||
|
let authority_keypair = Keypair::new();
|
||||||
|
load_upgradeable_bpf_program(
|
||||||
|
&bank_client,
|
||||||
|
&mint_keypair,
|
||||||
|
&buffer_keypair,
|
||||||
|
&program_keypair,
|
||||||
|
&authority_keypair,
|
||||||
|
"solana_bpf_rust_upgradeable",
|
||||||
|
);
|
||||||
|
|
||||||
let mut instruction = Instruction::new(
|
let mut instruction = Instruction::new(
|
||||||
invoke_and_return,
|
invoke_and_return,
|
||||||
@ -1823,12 +1981,13 @@ fn test_program_bpf_upgrade_via_cpi() {
|
|||||||
});
|
});
|
||||||
let mut elf = Vec::new();
|
let mut elf = Vec::new();
|
||||||
file.read_to_end(&mut elf).unwrap();
|
file.read_to_end(&mut elf).unwrap();
|
||||||
let buffer_pubkey = load_buffer_account(&bank_client, &mint_keypair, &elf);
|
let buffer_keypair = Keypair::new();
|
||||||
|
load_buffer_account(&bank_client, &mint_keypair, &buffer_keypair, &elf);
|
||||||
|
|
||||||
// Upgrade program via CPI
|
// Upgrade program via CPI
|
||||||
let mut upgrade_instruction = bpf_loader_upgradeable::upgrade(
|
let mut upgrade_instruction = bpf_loader_upgradeable::upgrade(
|
||||||
&program_id,
|
&program_id,
|
||||||
&buffer_pubkey,
|
&buffer_keypair.pubkey(),
|
||||||
&authority_keypair.pubkey(),
|
&authority_keypair.pubkey(),
|
||||||
&mint_keypair.pubkey(),
|
&mint_keypair.pubkey(),
|
||||||
);
|
);
|
||||||
@ -1849,3 +2008,137 @@ fn test_program_bpf_upgrade_via_cpi() {
|
|||||||
TransactionError::InstructionError(0, InstructionError::Custom(43))
|
TransactionError::InstructionError(0, InstructionError::Custom(43))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "bpf_rust")]
|
||||||
|
#[test]
|
||||||
|
fn test_program_upgradeable_locks() {
|
||||||
|
fn setup_program_upgradeable_locks(
|
||||||
|
payer_keypair: &Keypair,
|
||||||
|
buffer_keypair: &Keypair,
|
||||||
|
program_keypair: &Keypair,
|
||||||
|
) -> (Arc<Bank>, Transaction, Transaction) {
|
||||||
|
solana_logger::setup();
|
||||||
|
|
||||||
|
let GenesisConfigInfo {
|
||||||
|
genesis_config,
|
||||||
|
mint_keypair,
|
||||||
|
..
|
||||||
|
} = create_genesis_config(2_000_000_000);
|
||||||
|
let mut bank = Bank::new(&genesis_config);
|
||||||
|
let (name, id, entrypoint) = solana_bpf_loader_upgradeable_program!();
|
||||||
|
bank.add_builtin(&name, id, entrypoint);
|
||||||
|
let bank = Arc::new(bank);
|
||||||
|
let bank_client = BankClient::new_shared(&bank);
|
||||||
|
|
||||||
|
load_upgradeable_bpf_program(
|
||||||
|
&bank_client,
|
||||||
|
&mint_keypair,
|
||||||
|
buffer_keypair,
|
||||||
|
program_keypair,
|
||||||
|
payer_keypair,
|
||||||
|
"solana_bpf_rust_panic",
|
||||||
|
);
|
||||||
|
|
||||||
|
// Load the buffer account
|
||||||
|
let path = create_bpf_path("solana_bpf_rust_noop");
|
||||||
|
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_buffer_account(&bank_client, &mint_keypair, buffer_keypair, &elf);
|
||||||
|
|
||||||
|
bank_client
|
||||||
|
.send_and_confirm_instruction(
|
||||||
|
&mint_keypair,
|
||||||
|
system_instruction::transfer(
|
||||||
|
&mint_keypair.pubkey(),
|
||||||
|
&payer_keypair.pubkey(),
|
||||||
|
1_000_000_000,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let invoke_tx = Transaction::new(
|
||||||
|
&[payer_keypair],
|
||||||
|
Message::new(
|
||||||
|
&[Instruction::new(
|
||||||
|
program_keypair.pubkey(),
|
||||||
|
&[0u8; 0],
|
||||||
|
vec![],
|
||||||
|
)],
|
||||||
|
Some(&payer_keypair.pubkey()),
|
||||||
|
),
|
||||||
|
bank.last_blockhash(),
|
||||||
|
);
|
||||||
|
let upgrade_tx = Transaction::new(
|
||||||
|
&[payer_keypair],
|
||||||
|
Message::new(
|
||||||
|
&[bpf_loader_upgradeable::upgrade(
|
||||||
|
&program_keypair.pubkey(),
|
||||||
|
&buffer_keypair.pubkey(),
|
||||||
|
&payer_keypair.pubkey(),
|
||||||
|
&payer_keypair.pubkey(),
|
||||||
|
)],
|
||||||
|
Some(&payer_keypair.pubkey()),
|
||||||
|
),
|
||||||
|
bank.last_blockhash(),
|
||||||
|
);
|
||||||
|
|
||||||
|
(bank, invoke_tx, upgrade_tx)
|
||||||
|
}
|
||||||
|
|
||||||
|
let payer_keypair = keypair_from_seed(&[56u8; 32]).unwrap();
|
||||||
|
let buffer_keypair = keypair_from_seed(&[11; 32]).unwrap();
|
||||||
|
let program_keypair = keypair_from_seed(&[77u8; 32]).unwrap();
|
||||||
|
|
||||||
|
let results1 = {
|
||||||
|
let (bank, invoke_tx, upgrade_tx) =
|
||||||
|
setup_program_upgradeable_locks(&payer_keypair, &buffer_keypair, &program_keypair);
|
||||||
|
execute_transactions(&bank, &[upgrade_tx, invoke_tx])
|
||||||
|
};
|
||||||
|
|
||||||
|
let results2 = {
|
||||||
|
let (bank, invoke_tx, upgrade_tx) =
|
||||||
|
setup_program_upgradeable_locks(&payer_keypair, &buffer_keypair, &program_keypair);
|
||||||
|
execute_transactions(&bank, &[invoke_tx, upgrade_tx])
|
||||||
|
};
|
||||||
|
|
||||||
|
if false {
|
||||||
|
println!("upgrade and invoke");
|
||||||
|
for result in &results1 {
|
||||||
|
print_confirmed_tx("result", result.clone());
|
||||||
|
}
|
||||||
|
println!("invoke and upgrade");
|
||||||
|
for result in &results2 {
|
||||||
|
print_confirmed_tx("result", result.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(ref meta) = results1[0].transaction.meta {
|
||||||
|
assert_eq!(meta.status, Ok(()));
|
||||||
|
} else {
|
||||||
|
panic!("no meta");
|
||||||
|
}
|
||||||
|
if let Some(ref meta) = results1[1].transaction.meta {
|
||||||
|
assert_eq!(meta.status, Err(TransactionError::AccountInUse));
|
||||||
|
} else {
|
||||||
|
panic!("no meta");
|
||||||
|
}
|
||||||
|
if let Some(ref meta) = results2[0].transaction.meta {
|
||||||
|
assert_eq!(
|
||||||
|
meta.status,
|
||||||
|
Err(TransactionError::InstructionError(
|
||||||
|
0,
|
||||||
|
InstructionError::ProgramFailedToComplete
|
||||||
|
))
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
panic!("no meta");
|
||||||
|
}
|
||||||
|
if let Some(ref meta) = results2[1].transaction.meta {
|
||||||
|
assert_eq!(meta.status, Err(TransactionError::AccountInUse));
|
||||||
|
} else {
|
||||||
|
panic!("no meta");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -26,7 +26,7 @@ use solana_sdk::{
|
|||||||
bpf_loader_upgradeable::{self, UpgradeableLoaderState},
|
bpf_loader_upgradeable::{self, UpgradeableLoaderState},
|
||||||
clock::Clock,
|
clock::Clock,
|
||||||
entrypoint::SUCCESS,
|
entrypoint::SUCCESS,
|
||||||
feature_set::bpf_compute_budget_balancing,
|
feature_set::{bpf_compute_budget_balancing, prevent_upgrade_and_invoke},
|
||||||
instruction::InstructionError,
|
instruction::InstructionError,
|
||||||
keyed_account::{from_keyed_account, next_keyed_account, KeyedAccount},
|
keyed_account::{from_keyed_account, next_keyed_account, KeyedAccount},
|
||||||
loader_instruction::LoaderInstruction,
|
loader_instruction::LoaderInstruction,
|
||||||
@ -470,6 +470,12 @@ fn process_loader_upgradeable_instruction(
|
|||||||
log!(logger, "Program account not executable");
|
log!(logger, "Program account not executable");
|
||||||
return Err(InstructionError::AccountNotExecutable);
|
return Err(InstructionError::AccountNotExecutable);
|
||||||
}
|
}
|
||||||
|
if !program.is_writable()
|
||||||
|
&& invoke_context.is_feature_active(&prevent_upgrade_and_invoke::id())
|
||||||
|
{
|
||||||
|
log!(logger, "Program account not writeable");
|
||||||
|
return Err(InstructionError::InvalidArgument);
|
||||||
|
}
|
||||||
if &program.owner()? != program_id {
|
if &program.owner()? != program_id {
|
||||||
log!(logger, "Program account not owned by loader");
|
log!(logger, "Program account not owned by loader");
|
||||||
return Err(InstructionError::IncorrectProgramId);
|
return Err(InstructionError::IncorrectProgramId);
|
||||||
@ -2000,7 +2006,7 @@ mod tests {
|
|||||||
&bpf_loader_upgradeable::id(),
|
&bpf_loader_upgradeable::id(),
|
||||||
&[
|
&[
|
||||||
KeyedAccount::new(&programdata_address, false, &programdata_account),
|
KeyedAccount::new(&programdata_address, false, &programdata_account),
|
||||||
KeyedAccount::new_readonly(&program_address, false, &program_account),
|
KeyedAccount::new(&program_address, false, &program_account),
|
||||||
KeyedAccount::new(&buffer_address, false, &buffer_account),
|
KeyedAccount::new(&buffer_address, false, &buffer_account),
|
||||||
KeyedAccount::new(&spill_address, false, &spill_account),
|
KeyedAccount::new(&spill_address, false, &spill_account),
|
||||||
KeyedAccount::new_readonly(&sysvar::rent::id(), false, &rent_account),
|
KeyedAccount::new_readonly(&sysvar::rent::id(), false, &rent_account),
|
||||||
@ -2062,7 +2068,7 @@ mod tests {
|
|||||||
&bpf_loader_upgradeable::id(),
|
&bpf_loader_upgradeable::id(),
|
||||||
&[
|
&[
|
||||||
KeyedAccount::new(&programdata_address, false, &programdata_account),
|
KeyedAccount::new(&programdata_address, false, &programdata_account),
|
||||||
KeyedAccount::new_readonly(&program_address, false, &program_account),
|
KeyedAccount::new(&program_address, false, &program_account),
|
||||||
KeyedAccount::new(&buffer_address, false, &buffer_account),
|
KeyedAccount::new(&buffer_address, false, &buffer_account),
|
||||||
KeyedAccount::new(&spill_address, false, &spill_account),
|
KeyedAccount::new(&spill_address, false, &spill_account),
|
||||||
KeyedAccount::new_readonly(&sysvar::rent::id(), false, &rent_account),
|
KeyedAccount::new_readonly(&sysvar::rent::id(), false, &rent_account),
|
||||||
@ -2095,7 +2101,7 @@ mod tests {
|
|||||||
&bpf_loader_upgradeable::id(),
|
&bpf_loader_upgradeable::id(),
|
||||||
&[
|
&[
|
||||||
KeyedAccount::new(&programdata_address, false, &programdata_account),
|
KeyedAccount::new(&programdata_address, false, &programdata_account),
|
||||||
KeyedAccount::new_readonly(&program_address, false, &program_account),
|
KeyedAccount::new(&program_address, false, &program_account),
|
||||||
KeyedAccount::new(&buffer_address, false, &buffer_account),
|
KeyedAccount::new(&buffer_address, false, &buffer_account),
|
||||||
KeyedAccount::new(&spill_address, false, &spill_account),
|
KeyedAccount::new(&spill_address, false, &spill_account),
|
||||||
KeyedAccount::new_readonly(&sysvar::rent::id(), false, &rent_account),
|
KeyedAccount::new_readonly(&sysvar::rent::id(), false, &rent_account),
|
||||||
@ -2128,7 +2134,7 @@ mod tests {
|
|||||||
&bpf_loader_upgradeable::id(),
|
&bpf_loader_upgradeable::id(),
|
||||||
&[
|
&[
|
||||||
KeyedAccount::new(&programdata_address, false, &programdata_account),
|
KeyedAccount::new(&programdata_address, false, &programdata_account),
|
||||||
KeyedAccount::new_readonly(&program_address, false, &program_account),
|
KeyedAccount::new(&program_address, false, &program_account),
|
||||||
KeyedAccount::new(&buffer_address, false, &buffer_account),
|
KeyedAccount::new(&buffer_address, false, &buffer_account),
|
||||||
KeyedAccount::new(&spill_address, false, &spill_account),
|
KeyedAccount::new(&spill_address, false, &spill_account),
|
||||||
KeyedAccount::new_readonly(&sysvar::rent::id(), false, &rent_account),
|
KeyedAccount::new_readonly(&sysvar::rent::id(), false, &rent_account),
|
||||||
@ -2162,7 +2168,7 @@ mod tests {
|
|||||||
&bpf_loader_upgradeable::id(),
|
&bpf_loader_upgradeable::id(),
|
||||||
&[
|
&[
|
||||||
KeyedAccount::new(&programdata_address, false, &programdata_account),
|
KeyedAccount::new(&programdata_address, false, &programdata_account),
|
||||||
KeyedAccount::new_readonly(&program_address, false, &program_account),
|
KeyedAccount::new(&program_address, false, &program_account),
|
||||||
KeyedAccount::new(&buffer_address, false, &buffer_account),
|
KeyedAccount::new(&buffer_address, false, &buffer_account),
|
||||||
KeyedAccount::new(&spill_address, false, &spill_account),
|
KeyedAccount::new(&spill_address, false, &spill_account),
|
||||||
KeyedAccount::new_readonly(&sysvar::rent::id(), false, &rent_account),
|
KeyedAccount::new_readonly(&sysvar::rent::id(), false, &rent_account),
|
||||||
@ -2192,6 +2198,39 @@ mod tests {
|
|||||||
program_account.borrow_mut().owner = Pubkey::new_unique();
|
program_account.borrow_mut().owner = Pubkey::new_unique();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
Err(InstructionError::IncorrectProgramId),
|
Err(InstructionError::IncorrectProgramId),
|
||||||
|
process_instruction(
|
||||||
|
&bpf_loader_upgradeable::id(),
|
||||||
|
&[
|
||||||
|
KeyedAccount::new(&programdata_address, false, &programdata_account),
|
||||||
|
KeyedAccount::new(&program_address, false, &program_account),
|
||||||
|
KeyedAccount::new(&buffer_address, false, &buffer_account),
|
||||||
|
KeyedAccount::new(&spill_address, false, &spill_account),
|
||||||
|
KeyedAccount::new_readonly(&sysvar::rent::id(), false, &rent_account),
|
||||||
|
KeyedAccount::new_readonly(&sysvar::clock::id(), false, &clock_account),
|
||||||
|
KeyedAccount::new_readonly(
|
||||||
|
&upgrade_authority_address,
|
||||||
|
true,
|
||||||
|
&upgrade_authority_account
|
||||||
|
)
|
||||||
|
],
|
||||||
|
&instruction,
|
||||||
|
&mut MockInvokeContext::default()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Case: Program account not writable
|
||||||
|
let (buffer_account, program_account, programdata_account, spill_account) = get_accounts(
|
||||||
|
&buffer_address,
|
||||||
|
&programdata_address,
|
||||||
|
&upgrade_authority_address,
|
||||||
|
slot,
|
||||||
|
&elf_orig,
|
||||||
|
&elf_new,
|
||||||
|
min_program_balance,
|
||||||
|
min_programdata_balance,
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
Err(InstructionError::InvalidArgument),
|
||||||
process_instruction(
|
process_instruction(
|
||||||
&bpf_loader_upgradeable::id(),
|
&bpf_loader_upgradeable::id(),
|
||||||
&[
|
&[
|
||||||
@ -2233,7 +2272,7 @@ mod tests {
|
|||||||
&bpf_loader_upgradeable::id(),
|
&bpf_loader_upgradeable::id(),
|
||||||
&[
|
&[
|
||||||
KeyedAccount::new(&programdata_address, false, &programdata_account),
|
KeyedAccount::new(&programdata_address, false, &programdata_account),
|
||||||
KeyedAccount::new_readonly(&program_address, false, &program_account),
|
KeyedAccount::new(&program_address, false, &program_account),
|
||||||
KeyedAccount::new(&buffer_address, false, &buffer_account),
|
KeyedAccount::new(&buffer_address, false, &buffer_account),
|
||||||
KeyedAccount::new(&spill_address, false, &spill_account),
|
KeyedAccount::new(&spill_address, false, &spill_account),
|
||||||
KeyedAccount::new_readonly(&sysvar::rent::id(), false, &rent_account),
|
KeyedAccount::new_readonly(&sysvar::rent::id(), false, &rent_account),
|
||||||
@ -2270,7 +2309,7 @@ mod tests {
|
|||||||
&bpf_loader_upgradeable::id(),
|
&bpf_loader_upgradeable::id(),
|
||||||
&[
|
&[
|
||||||
KeyedAccount::new(&programdata_address, false, &programdata_account),
|
KeyedAccount::new(&programdata_address, false, &programdata_account),
|
||||||
KeyedAccount::new_readonly(&program_address, false, &program_account),
|
KeyedAccount::new(&program_address, false, &program_account),
|
||||||
KeyedAccount::new(&buffer_address, false, &buffer_account),
|
KeyedAccount::new(&buffer_address, false, &buffer_account),
|
||||||
KeyedAccount::new(&spill_address, false, &spill_account),
|
KeyedAccount::new(&spill_address, false, &spill_account),
|
||||||
KeyedAccount::new_readonly(&sysvar::rent::id(), false, &rent_account),
|
KeyedAccount::new_readonly(&sysvar::rent::id(), false, &rent_account),
|
||||||
@ -2303,7 +2342,7 @@ mod tests {
|
|||||||
&bpf_loader_upgradeable::id(),
|
&bpf_loader_upgradeable::id(),
|
||||||
&[
|
&[
|
||||||
KeyedAccount::new(&Pubkey::new_unique(), false, &programdata_account),
|
KeyedAccount::new(&Pubkey::new_unique(), false, &programdata_account),
|
||||||
KeyedAccount::new_readonly(&program_address, false, &program_account),
|
KeyedAccount::new(&program_address, false, &program_account),
|
||||||
KeyedAccount::new(&buffer_address, false, &buffer_account),
|
KeyedAccount::new(&buffer_address, false, &buffer_account),
|
||||||
KeyedAccount::new(&spill_address, false, &spill_account),
|
KeyedAccount::new(&spill_address, false, &spill_account),
|
||||||
KeyedAccount::new_readonly(&sysvar::rent::id(), false, &rent_account),
|
KeyedAccount::new_readonly(&sysvar::rent::id(), false, &rent_account),
|
||||||
@ -2340,7 +2379,7 @@ mod tests {
|
|||||||
&bpf_loader_upgradeable::id(),
|
&bpf_loader_upgradeable::id(),
|
||||||
&[
|
&[
|
||||||
KeyedAccount::new(&programdata_address, false, &programdata_account),
|
KeyedAccount::new(&programdata_address, false, &programdata_account),
|
||||||
KeyedAccount::new_readonly(&program_address, false, &program_account),
|
KeyedAccount::new(&program_address, false, &program_account),
|
||||||
KeyedAccount::new(&buffer_address, false, &buffer_account),
|
KeyedAccount::new(&buffer_address, false, &buffer_account),
|
||||||
KeyedAccount::new(&spill_address, false, &spill_account),
|
KeyedAccount::new(&spill_address, false, &spill_account),
|
||||||
KeyedAccount::new_readonly(&sysvar::rent::id(), false, &rent_account),
|
KeyedAccount::new_readonly(&sysvar::rent::id(), false, &rent_account),
|
||||||
@ -2384,7 +2423,7 @@ mod tests {
|
|||||||
&bpf_loader_upgradeable::id(),
|
&bpf_loader_upgradeable::id(),
|
||||||
&[
|
&[
|
||||||
KeyedAccount::new(&programdata_address, false, &programdata_account),
|
KeyedAccount::new(&programdata_address, false, &programdata_account),
|
||||||
KeyedAccount::new_readonly(&program_address, false, &program_account),
|
KeyedAccount::new(&program_address, false, &program_account),
|
||||||
KeyedAccount::new(&buffer_address, false, &buffer_account),
|
KeyedAccount::new(&buffer_address, false, &buffer_account),
|
||||||
KeyedAccount::new(&spill_address, false, &spill_account),
|
KeyedAccount::new(&spill_address, false, &spill_account),
|
||||||
KeyedAccount::new_readonly(&sysvar::rent::id(), false, &rent_account),
|
KeyedAccount::new_readonly(&sysvar::rent::id(), false, &rent_account),
|
||||||
@ -2419,7 +2458,7 @@ mod tests {
|
|||||||
&bpf_loader_upgradeable::id(),
|
&bpf_loader_upgradeable::id(),
|
||||||
&[
|
&[
|
||||||
KeyedAccount::new(&programdata_address, false, &programdata_account),
|
KeyedAccount::new(&programdata_address, false, &programdata_account),
|
||||||
KeyedAccount::new_readonly(&program_address, false, &program_account),
|
KeyedAccount::new(&program_address, false, &program_account),
|
||||||
KeyedAccount::new(&buffer_address, false, &buffer_account),
|
KeyedAccount::new(&buffer_address, false, &buffer_account),
|
||||||
KeyedAccount::new(&spill_address, false, &spill_account),
|
KeyedAccount::new(&spill_address, false, &spill_account),
|
||||||
KeyedAccount::new_readonly(&sysvar::rent::id(), false, &rent_account),
|
KeyedAccount::new_readonly(&sysvar::rent::id(), false, &rent_account),
|
||||||
|
@ -57,9 +57,9 @@ pub fn load_program<T: Client>(
|
|||||||
pub fn load_buffer_account<T: Client>(
|
pub fn load_buffer_account<T: Client>(
|
||||||
bank_client: &T,
|
bank_client: &T,
|
||||||
from_keypair: &Keypair,
|
from_keypair: &Keypair,
|
||||||
|
buffer_keypair: &Keypair,
|
||||||
program: &[u8],
|
program: &[u8],
|
||||||
) -> Pubkey {
|
) {
|
||||||
let buffer_keypair = Keypair::new();
|
|
||||||
let buffer_pubkey = buffer_keypair.pubkey();
|
let buffer_pubkey = buffer_keypair.pubkey();
|
||||||
|
|
||||||
bank_client
|
bank_client
|
||||||
@ -100,26 +100,26 @@ pub fn load_buffer_account<T: Client>(
|
|||||||
.unwrap();
|
.unwrap();
|
||||||
offset += chunk_size as u32;
|
offset += chunk_size as u32;
|
||||||
}
|
}
|
||||||
buffer_keypair.pubkey()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn load_upgradeable_program<T: Client>(
|
pub fn load_upgradeable_program<T: Client>(
|
||||||
bank_client: &T,
|
bank_client: &T,
|
||||||
from_keypair: &Keypair,
|
from_keypair: &Keypair,
|
||||||
|
buffer_keypair: &Keypair,
|
||||||
|
executable_keypair: &Keypair,
|
||||||
|
authority_keypair: &Keypair,
|
||||||
program: Vec<u8>,
|
program: Vec<u8>,
|
||||||
) -> (Pubkey, Keypair) {
|
) {
|
||||||
let executable_keypair = Keypair::new();
|
|
||||||
let program_pubkey = executable_keypair.pubkey();
|
let program_pubkey = executable_keypair.pubkey();
|
||||||
let authority_keypair = Keypair::new();
|
|
||||||
let authority_pubkey = authority_keypair.pubkey();
|
let authority_pubkey = authority_keypair.pubkey();
|
||||||
|
|
||||||
let buffer_pubkey = load_buffer_account(bank_client, &from_keypair, &program);
|
load_buffer_account(bank_client, &from_keypair, buffer_keypair, &program);
|
||||||
|
|
||||||
let message = Message::new(
|
let message = Message::new(
|
||||||
&bpf_loader_upgradeable::deploy_with_max_program_len(
|
&bpf_loader_upgradeable::deploy_with_max_program_len(
|
||||||
&from_keypair.pubkey(),
|
&from_keypair.pubkey(),
|
||||||
&program_pubkey,
|
&program_pubkey,
|
||||||
&buffer_pubkey,
|
&buffer_keypair.pubkey(),
|
||||||
Some(&authority_pubkey),
|
Some(&authority_pubkey),
|
||||||
1.max(
|
1.max(
|
||||||
bank_client
|
bank_client
|
||||||
@ -136,8 +136,6 @@ pub fn load_upgradeable_program<T: Client>(
|
|||||||
bank_client
|
bank_client
|
||||||
.send_and_confirm_message(&[from_keypair, &executable_keypair], message)
|
.send_and_confirm_message(&[from_keypair, &executable_keypair], message)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
(executable_keypair.pubkey(), authority_keypair)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn upgrade_program<T: Client>(
|
pub fn upgrade_program<T: Client>(
|
||||||
|
@ -182,7 +182,7 @@ pub fn upgrade(
|
|||||||
&UpgradeableLoaderInstruction::Upgrade,
|
&UpgradeableLoaderInstruction::Upgrade,
|
||||||
vec![
|
vec![
|
||||||
AccountMeta::new(programdata_address, false),
|
AccountMeta::new(programdata_address, false),
|
||||||
AccountMeta::new_readonly(*program_address, false),
|
AccountMeta::new(*program_address, false),
|
||||||
AccountMeta::new(*buffer_address, false),
|
AccountMeta::new(*buffer_address, false),
|
||||||
AccountMeta::new(*spill_address, false),
|
AccountMeta::new(*spill_address, false),
|
||||||
AccountMeta::new_readonly(sysvar::rent::id(), false),
|
AccountMeta::new_readonly(sysvar::rent::id(), false),
|
||||||
@ -250,4 +250,45 @@ mod tests {
|
|||||||
UpgradeableLoaderState::programdata_len(42).unwrap()
|
UpgradeableLoaderState::programdata_len(42).unwrap()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_is_upgrade_instruction() {
|
||||||
|
assert_eq!(
|
||||||
|
false,
|
||||||
|
is_upgrade_instruction(
|
||||||
|
&bincode::serialize(&UpgradeableLoaderInstruction::InitializeBuffer).unwrap()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
false,
|
||||||
|
is_upgrade_instruction(
|
||||||
|
&bincode::serialize(&UpgradeableLoaderInstruction::Write {
|
||||||
|
offset: 0,
|
||||||
|
bytes: vec![],
|
||||||
|
})
|
||||||
|
.unwrap()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
false,
|
||||||
|
is_upgrade_instruction(
|
||||||
|
&bincode::serialize(&UpgradeableLoaderInstruction::DeployWithMaxDataLen {
|
||||||
|
max_data_len: 0,
|
||||||
|
})
|
||||||
|
.unwrap()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
true,
|
||||||
|
is_upgrade_instruction(
|
||||||
|
&bincode::serialize(&UpgradeableLoaderInstruction::Upgrade).unwrap()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
false,
|
||||||
|
is_upgrade_instruction(
|
||||||
|
&bincode::serialize(&UpgradeableLoaderInstruction::SetAuthority).unwrap()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -86,7 +86,7 @@ pub enum UpgradeableLoaderInstruction {
|
|||||||
///
|
///
|
||||||
/// # Account references
|
/// # Account references
|
||||||
/// 0. [writable] The ProgramData account.
|
/// 0. [writable] The ProgramData account.
|
||||||
/// 1. [] The Program account.
|
/// 1. [writable] The Program account.
|
||||||
/// 2. [Writable] The Buffer account where the program data has been
|
/// 2. [Writable] The Buffer account where the program data has been
|
||||||
/// written.
|
/// written.
|
||||||
/// 3. [writable] The spill account.
|
/// 3. [writable] The spill account.
|
||||||
|
@ -142,6 +142,10 @@ pub mod turbine_retransmit_peers_patch {
|
|||||||
solana_sdk::declare_id!("5Lu3JnWSFwRYpXzwDMkanWSk6XqSuF2i5fpnVhzB5CTc");
|
solana_sdk::declare_id!("5Lu3JnWSFwRYpXzwDMkanWSk6XqSuF2i5fpnVhzB5CTc");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub mod prevent_upgrade_and_invoke {
|
||||||
|
solana_sdk::declare_id!("BiNjYd8jCYDgAwMqP91uwZs6skWpuHtKrZbckuKESs8N");
|
||||||
|
}
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
/// Map of feature identifiers to user-visible description
|
/// Map of feature identifiers to user-visible description
|
||||||
pub static ref FEATURE_NAMES: HashMap<Pubkey, &'static str> = [
|
pub static ref FEATURE_NAMES: HashMap<Pubkey, &'static str> = [
|
||||||
@ -179,6 +183,7 @@ lazy_static! {
|
|||||||
(abort_on_all_cpi_failures::id(), "Abort on all CPI failures"),
|
(abort_on_all_cpi_failures::id(), "Abort on all CPI failures"),
|
||||||
(use_loaded_executables::id(), "Use loaded executable accounts"),
|
(use_loaded_executables::id(), "Use loaded executable accounts"),
|
||||||
(turbine_retransmit_peers_patch::id(), "turbine retransmit peers patch #14631"),
|
(turbine_retransmit_peers_patch::id(), "turbine retransmit peers patch #14631"),
|
||||||
|
(prevent_upgrade_and_invoke::id(), "Prevent upgrade and invoke in same tx batch"),
|
||||||
/*************** ADD NEW FEATURES HERE ***************/
|
/*************** ADD NEW FEATURES HERE ***************/
|
||||||
]
|
]
|
||||||
.iter()
|
.iter()
|
||||||
|
@ -359,7 +359,7 @@ pub struct EncodedConfirmedBlock {
|
|||||||
pub block_time: Option<UnixTimestamp>,
|
pub block_time: Option<UnixTimestamp>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct ConfirmedTransaction {
|
pub struct ConfirmedTransaction {
|
||||||
pub slot: Slot,
|
pub slot: Slot,
|
||||||
|
Reference in New Issue
Block a user