SBF test that runs with stable bank and genesis to produce deterministic hash (#30945)
* SBF test that runs with stable bank and genesis to produce deterministic hash * fix warning * ignore the test
This commit is contained in:
parent
db5cdbedae
commit
a2f3a219d8
|
@ -45,10 +45,8 @@ use {
|
|||
entrypoint::MAX_PERMITTED_DATA_INCREASE,
|
||||
feature_set::FeatureSet,
|
||||
fee::FeeStructure,
|
||||
fee_calculator::FeeRateGovernor,
|
||||
loader_instruction,
|
||||
message::{v0::LoadedAddresses, SanitizedMessage},
|
||||
rent::Rent,
|
||||
signature::keypair_from_seed,
|
||||
stake,
|
||||
system_instruction::{self, MAX_PERMITTED_DATA_LENGTH},
|
||||
|
@ -59,7 +57,7 @@ use {
|
|||
ConfirmedTransactionWithStatusMeta, InnerInstructions, TransactionStatusMeta,
|
||||
TransactionWithStatusMeta, VersionedTransactionWithStatusMeta,
|
||||
},
|
||||
std::{collections::HashMap, str::FromStr},
|
||||
std::collections::HashMap,
|
||||
};
|
||||
use {
|
||||
solana_bpf_loader_program::{
|
||||
|
@ -72,21 +70,29 @@ use {
|
|||
solana_runtime::{
|
||||
bank::Bank,
|
||||
bank_client::BankClient,
|
||||
genesis_utils::{create_genesis_config, GenesisConfigInfo},
|
||||
genesis_utils::{
|
||||
bootstrap_validator_stake_lamports, create_genesis_config,
|
||||
create_genesis_config_with_leader_ex, GenesisConfigInfo,
|
||||
},
|
||||
},
|
||||
solana_sdk::{
|
||||
account::AccountSharedData,
|
||||
bpf_loader, bpf_loader_deprecated,
|
||||
client::SyncClient,
|
||||
clock::UnixTimestamp,
|
||||
entrypoint::SUCCESS,
|
||||
fee_calculator::FeeRateGovernor,
|
||||
genesis_config::ClusterType,
|
||||
hash::Hash,
|
||||
instruction::{AccountMeta, Instruction, InstructionError},
|
||||
message::Message,
|
||||
pubkey::Pubkey,
|
||||
rent::Rent,
|
||||
signature::{Keypair, Signer},
|
||||
system_program,
|
||||
transaction::{SanitizedTransaction, Transaction, TransactionError},
|
||||
},
|
||||
std::sync::Arc,
|
||||
std::{str::FromStr, sync::Arc, time::Duration},
|
||||
};
|
||||
|
||||
fn run_program(name: &str) -> u64 {
|
||||
|
@ -1768,6 +1774,221 @@ fn test_program_sbf_upgrade() {
|
|||
);
|
||||
}
|
||||
|
||||
fn get_stable_genesis_config() -> GenesisConfigInfo {
|
||||
let validator_pubkey =
|
||||
Pubkey::from_str("GLh546CXmtZdvpEzL8sxzqhhUf7KPvmGaRpFHB5W1sjV").unwrap();
|
||||
let mint_keypair = Keypair::from_base58_string(
|
||||
"4YTH9JSRgZocmK9ezMZeJCCV2LVeR2NatTBA8AFXkg2x83fqrt8Vwyk91961E7ns4vee9yUBzuDfztb8i9iwTLFd",
|
||||
);
|
||||
let voting_keypair = Keypair::from_base58_string(
|
||||
"4EPWEn72zdNY1JSKkzyZ2vTZcKdPW3jM5WjAgUadnoz83FR5cDFApbo7s5mwBcYXn8afVe2syReJaqBi4fkhG3mH",
|
||||
);
|
||||
let stake_pubkey = Pubkey::from_str("HGq9JF77xFXRgWRJy8VQuhdbdugrT856RvQDzr1KJo6E").unwrap();
|
||||
|
||||
let mut genesis_config = create_genesis_config_with_leader_ex(
|
||||
123,
|
||||
&mint_keypair.pubkey(),
|
||||
&validator_pubkey,
|
||||
&voting_keypair.pubkey(),
|
||||
&stake_pubkey,
|
||||
bootstrap_validator_stake_lamports(),
|
||||
42,
|
||||
FeeRateGovernor::new(0, 0), // most tests can't handle transaction fees
|
||||
Rent::free(), // most tests don't expect rent
|
||||
ClusterType::Development,
|
||||
vec![],
|
||||
);
|
||||
genesis_config.creation_time = Duration::ZERO.as_secs() as UnixTimestamp;
|
||||
|
||||
GenesisConfigInfo {
|
||||
genesis_config,
|
||||
mint_keypair,
|
||||
voting_keypair,
|
||||
validator_pubkey,
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
#[cfg(feature = "sbf_rust")]
|
||||
fn test_program_sbf_invoke_stable_genesis_and_bank() {
|
||||
// The purpose of this test is to exercise various code branches of runtime/VM and
|
||||
// assert that the resulting bank hash matches with the expected value.
|
||||
// The assert check is commented out by default. Please refer to the last few lines
|
||||
// of the test to enable the assertion.
|
||||
solana_logger::setup();
|
||||
|
||||
let GenesisConfigInfo {
|
||||
genesis_config,
|
||||
mint_keypair,
|
||||
..
|
||||
} = get_stable_genesis_config();
|
||||
let mut bank = Bank::new_for_tests(&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);
|
||||
|
||||
// Deploy upgradeable program
|
||||
let buffer_keypair = Keypair::from_base58_string(
|
||||
"4q4UvWxh2oMifTGbChDeWCbdN8eJEUQ1E6cuNnmymJ6AN5CMUT2VW5A1RKnG9dy7ypLczB9inMUAafh5TkpXrtxg",
|
||||
);
|
||||
let program_keypair = Keypair::from_base58_string(
|
||||
"3LQpBxgpaFNJPit5a8t51pJKMkUmNUn5PhSTcuuhuuBxe43cTeqVPhMtKkFNr5VpFzCExf4ihibvuZgGxmjy6t8n",
|
||||
);
|
||||
let program_id = program_keypair.pubkey();
|
||||
let authority_keypair = Keypair::from_base58_string(
|
||||
"285XFW2NTWd6CMvtHzvYYS1kWzmzcGBnyEXbH1v8hq6YJqJsLMTYMPkbEQqeE7m7UqhoMeK5V3HMJLf9DdxwU2Gy",
|
||||
);
|
||||
|
||||
let instruction =
|
||||
Instruction::new_with_bytes(program_id, &[0], vec![AccountMeta::new(clock::id(), false)]);
|
||||
|
||||
// Call program before its deployed
|
||||
let result = bank_client.send_and_confirm_instruction(&mint_keypair, instruction.clone());
|
||||
assert_eq!(
|
||||
result.unwrap_err().unwrap(),
|
||||
TransactionError::ProgramAccountNotFound
|
||||
);
|
||||
|
||||
load_upgradeable_program(
|
||||
&bank_client,
|
||||
&mint_keypair,
|
||||
&buffer_keypair,
|
||||
&program_keypair,
|
||||
&authority_keypair,
|
||||
"solana_sbf_rust_noop",
|
||||
);
|
||||
|
||||
// Deploy indirect invocation program
|
||||
let indirect_program_keypair = Keypair::from_base58_string(
|
||||
"2BgE4gD5wUCwiAVPYbmWd2xzXSsD9W2fWgNjwmVkm8WL7i51vK9XAXNnX1VB6oKQZmjaUPRd5RzE6RggB9DeKbZC",
|
||||
);
|
||||
load_upgradeable_program(
|
||||
&bank_client,
|
||||
&mint_keypair,
|
||||
&buffer_keypair,
|
||||
&indirect_program_keypair,
|
||||
&authority_keypair,
|
||||
"solana_sbf_rust_invoke_and_return",
|
||||
);
|
||||
|
||||
let invoke_instruction =
|
||||
Instruction::new_with_bytes(program_id, &[0], vec![AccountMeta::new(clock::id(), false)]);
|
||||
let indirect_invoke_instruction = Instruction::new_with_bytes(
|
||||
indirect_program_keypair.pubkey(),
|
||||
&[0],
|
||||
vec![
|
||||
AccountMeta::new_readonly(program_id, false),
|
||||
AccountMeta::new_readonly(clock::id(), false),
|
||||
],
|
||||
);
|
||||
|
||||
// Prepare redeployment
|
||||
let buffer_keypair = Keypair::from_base58_string(
|
||||
"5T5L31FiUphXh4N6mxiWhEKPrdLhvMJSbaHo1Ne7zZYkw6YT1fVkqsWdA6pHMtqATiMTc4sfx5yTV9M9AnWDoBkW",
|
||||
);
|
||||
load_upgradeable_buffer(
|
||||
&bank_client,
|
||||
&mint_keypair,
|
||||
&buffer_keypair,
|
||||
&authority_keypair,
|
||||
"solana_sbf_rust_panic",
|
||||
);
|
||||
let redeployment_instruction = bpf_loader_upgradeable::upgrade(
|
||||
&program_id,
|
||||
&buffer_keypair.pubkey(),
|
||||
&authority_keypair.pubkey(),
|
||||
&mint_keypair.pubkey(),
|
||||
);
|
||||
|
||||
// Redeployment causes programs to be unavailable to both top-level-instructions and CPI instructions
|
||||
for invoke_instruction in [invoke_instruction, indirect_invoke_instruction] {
|
||||
// Call upgradeable program
|
||||
let result =
|
||||
bank_client.send_and_confirm_instruction(&mint_keypair, invoke_instruction.clone());
|
||||
assert!(result.is_ok());
|
||||
|
||||
// Upgrade the program and invoke in same tx
|
||||
let message = Message::new(
|
||||
&[redeployment_instruction.clone(), invoke_instruction],
|
||||
Some(&mint_keypair.pubkey()),
|
||||
);
|
||||
let tx = Transaction::new(
|
||||
&[&mint_keypair, &authority_keypair],
|
||||
message.clone(),
|
||||
bank.last_blockhash(),
|
||||
);
|
||||
let (result, _, _) = process_transaction_and_record_inner(&bank, tx);
|
||||
assert_eq!(
|
||||
result.unwrap_err(),
|
||||
TransactionError::InstructionError(1, InstructionError::InvalidAccountData),
|
||||
);
|
||||
}
|
||||
|
||||
// Prepare undeployment
|
||||
let (programdata_address, _) = Pubkey::find_program_address(
|
||||
&[program_keypair.pubkey().as_ref()],
|
||||
&bpf_loader_upgradeable::id(),
|
||||
);
|
||||
let undeployment_instruction = bpf_loader_upgradeable::close_any(
|
||||
&programdata_address,
|
||||
&mint_keypair.pubkey(),
|
||||
Some(&authority_keypair.pubkey()),
|
||||
Some(&program_id),
|
||||
);
|
||||
|
||||
let invoke_instruction =
|
||||
Instruction::new_with_bytes(program_id, &[1], vec![AccountMeta::new(clock::id(), false)]);
|
||||
let indirect_invoke_instruction = Instruction::new_with_bytes(
|
||||
indirect_program_keypair.pubkey(),
|
||||
&[1],
|
||||
vec![
|
||||
AccountMeta::new_readonly(program_id, false),
|
||||
AccountMeta::new_readonly(clock::id(), false),
|
||||
],
|
||||
);
|
||||
|
||||
// Undeployment is visible to both top-level-instructions and CPI instructions
|
||||
for invoke_instruction in [invoke_instruction, indirect_invoke_instruction] {
|
||||
// Call upgradeable program
|
||||
let result =
|
||||
bank_client.send_and_confirm_instruction(&mint_keypair, invoke_instruction.clone());
|
||||
assert!(result.is_ok());
|
||||
|
||||
// Undeploy the program and invoke in same tx
|
||||
let message = Message::new(
|
||||
&[undeployment_instruction.clone(), invoke_instruction],
|
||||
Some(&mint_keypair.pubkey()),
|
||||
);
|
||||
let tx = Transaction::new(
|
||||
&[&mint_keypair, &authority_keypair],
|
||||
message.clone(),
|
||||
bank.last_blockhash(),
|
||||
);
|
||||
let (result, _, _) = process_transaction_and_record_inner(&bank, tx);
|
||||
assert_eq!(
|
||||
result.unwrap_err(),
|
||||
TransactionError::InstructionError(1, InstructionError::InvalidAccountData),
|
||||
);
|
||||
}
|
||||
|
||||
bank.freeze();
|
||||
let expected_hash = Hash::from_str("2A2vqbUKExRbnaAzSnDFXdsBZRZSpCjGZCAA3mFZG2sV")
|
||||
.expect("Failed to generate hash");
|
||||
println!("Stable test produced bank hash: {}", bank.hash());
|
||||
println!("Expected hash: {}", expected_hash);
|
||||
|
||||
// Enable the following code to match the bank hash with the expected bank hash.
|
||||
// Follow these steps.
|
||||
// 1. Run this test on the baseline/master commit, and get the expected bank hash.
|
||||
// 2. Update the `expected_hash` to match the expected bank hash.
|
||||
// 3. Run the test in the PR branch that's being tested.
|
||||
// If the hash doesn't match, the PR likely has runtime changes that can lead to
|
||||
// consensus failure.
|
||||
// assert_eq!(bank.hash(), expected_hash);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "sbf_rust")]
|
||||
fn test_program_sbf_invoke_in_same_tx_as_deployment() {
|
||||
|
|
Loading…
Reference in New Issue