Skip to content

Commit 3ae3987

Browse files
authored
fix: CUs discrepancy (#299)
* fix: CUs discrepancy * sync programs mainnet loader --------- Co-authored-by: Aursen <aursen@users.noreply.github.com>
1 parent 4084d2b commit 3ae3987

4 files changed

Lines changed: 104 additions & 19 deletions

File tree

crates/litesvm/src/error.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,4 +30,6 @@ pub enum LiteSVMError {
3030
Instruction(#[from] InstructionError),
3131
#[error("{0}")]
3232
InvalidPath(#[from] std::io::Error),
33+
#[error("{0}")]
34+
InvalidLoader(String),
3335
}

crates/litesvm/src/lib.rs

Lines changed: 85 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -332,6 +332,7 @@ use {
332332
solana_hash::Hash,
333333
solana_keypair::Keypair,
334334
solana_last_restart_slot::LastRestartSlot,
335+
solana_loader_v3_interface::state::UpgradeableLoaderState,
335336
solana_message::{
336337
inner_instruction::InnerInstructionsList, Message, SanitizedMessage, VersionedMessage,
337338
},
@@ -342,7 +343,9 @@ use {
342343
loaded_programs::{LoadProgramMetrics, ProgramCacheEntry},
343344
},
344345
solana_rent::Rent,
345-
solana_sdk_ids::{bpf_loader, native_loader, system_program},
346+
solana_sdk_ids::{
347+
bpf_loader, bpf_loader_deprecated, bpf_loader_upgradeable, native_loader, system_program,
348+
},
346349
solana_signature::Signature,
347350
solana_signer::Signer,
348351
solana_slot_hashes::SlotHashes,
@@ -826,62 +829,129 @@ impl LiteSVM {
826829
Ok(())
827830
}
828831

829-
/// Adds am SBF program to the test environment.
830832
fn add_program_internal<const PREVERIFIED: bool>(
831833
&mut self,
832834
program_id: impl Into<Address>,
833835
program_bytes: &[u8],
836+
loader_id: &Address,
834837
) -> Result<(), LiteSVMError> {
835838
let program_id = program_id.into();
836-
let program_len = program_bytes.len();
837-
let lamports = self.minimum_balance_for_rent_exemption(program_len);
838-
let mut account = AccountSharedData::new(lamports, program_len, &bpf_loader::id());
839-
account.set_executable(true);
840-
account.set_data_from_slice(program_bytes);
841839
let current_slot = self
842840
.accounts
843841
.sysvar_cache
844842
.get_clock()
845843
.unwrap_or_default()
846844
.slot;
845+
846+
let program_size = if bpf_loader_upgradeable::check_id(loader_id) {
847+
let (programdata_address, _bump) =
848+
Address::find_program_address(&[program_id.as_ref()], loader_id);
849+
850+
let programdata_metadata_len = UpgradeableLoaderState::size_of_programdata_metadata();
851+
let programdata_len = programdata_metadata_len + program_bytes.len();
852+
let mut programdata_data = vec![0u8; programdata_len];
853+
854+
bincode::serialize_into(
855+
&mut programdata_data[..programdata_metadata_len],
856+
&UpgradeableLoaderState::ProgramData {
857+
slot: current_slot,
858+
upgrade_authority_address: None,
859+
},
860+
)
861+
.expect("UpgradeableLoaderState::ProgramData serialization should never fail");
862+
programdata_data[programdata_metadata_len..].copy_from_slice(program_bytes);
863+
864+
let programdata_lamports = self.minimum_balance_for_rent_exemption(programdata_len);
865+
let mut programdata_account =
866+
AccountSharedData::new(programdata_lamports, programdata_len, loader_id);
867+
programdata_account.set_data_from_slice(&programdata_data);
868+
869+
let program_account_data = bincode::serialize(&UpgradeableLoaderState::Program {
870+
programdata_address,
871+
})
872+
.expect("UpgradeableLoaderState::Program serialization should never fail");
873+
874+
let program_lamports =
875+
self.minimum_balance_for_rent_exemption(program_account_data.len());
876+
let mut program_account =
877+
AccountSharedData::new(program_lamports, program_account_data.len(), loader_id);
878+
program_account.set_executable(true);
879+
program_account.set_data_from_slice(&program_account_data);
880+
881+
self.accounts
882+
.add_account_no_checks(programdata_address, programdata_account);
883+
self.accounts
884+
.add_account_no_checks(program_id, program_account);
885+
886+
programdata_len
887+
} else if bpf_loader::check_id(loader_id) || bpf_loader_deprecated::check_id(loader_id) {
888+
let program_len = program_bytes.len();
889+
let lamports = self.minimum_balance_for_rent_exemption(program_len);
890+
let mut account = AccountSharedData::new(lamports, program_len, loader_id);
891+
account.set_executable(true);
892+
account.set_data_from_slice(program_bytes);
893+
894+
self.accounts.add_account_no_checks(program_id, account);
895+
896+
program_len
897+
} else {
898+
return Err(LiteSVMError::InvalidLoader(format!(
899+
"Unsupported loader: {loader_id}"
900+
)));
901+
};
902+
847903
let mut loaded_program = solana_bpf_loader_program::load_program_from_bytes(
848904
None,
849905
&mut LoadProgramMetrics::default(),
850-
account.data(),
851-
account.owner(),
852-
account.data().len(),
906+
program_bytes,
907+
loader_id,
908+
program_size,
853909
current_slot,
854910
self.accounts.environments.program_runtime_v1.clone(),
855911
PREVERIFIED,
856912
)
857913
.map_err(LiteSVMError::from)?;
858914
loaded_program.effective_slot = current_slot;
859915

860-
// We already loaded and validated (or explicitly trusted) the executable above.
861-
// Insert the account directly to avoid a second program load.
862-
self.accounts.add_account_no_checks(program_id, account);
863916
self.accounts
864917
.programs_cache
865918
.replenish(program_id, Arc::new(loaded_program));
919+
866920
Ok(())
867921
}
868922

869923
/// Adds an SBF program to the test environment.
924+
///
925+
/// Uses `BPFLoaderUpgradeable` by default for the loader.
870926
pub fn add_program(
871927
&mut self,
872928
program_id: impl Into<Address>,
873929
program_bytes: &[u8],
874930
) -> Result<(), LiteSVMError> {
875-
self.add_program_internal::<false>(program_id, program_bytes)
931+
self.add_program_internal::<false>(program_id, program_bytes, &bpf_loader_upgradeable::id())
932+
}
933+
934+
/// Adds an SBF program with a specific loader to match mainnet CU behavior.
935+
///
936+
/// Use `bpf_loader::id()` for BPFLoader2, `bpf_loader_deprecated::id()` for BPFLoader1,
937+
/// or `bpf_loader_upgradeable::id()` for the upgradeable loader.
938+
pub fn add_program_with_loader(
939+
&mut self,
940+
program_id: impl Into<Address>,
941+
program_bytes: &[u8],
942+
loader_id: Address,
943+
) -> Result<(), LiteSVMError> {
944+
self.add_program_internal::<false>(program_id, program_bytes, &loader_id)
876945
}
877946

878947
/// Adds an SBF program that is known-good and already verified.
879948
pub(crate) fn add_program_preverified(
880949
&mut self,
881950
program_id: impl Into<Address>,
882951
program_bytes: &[u8],
952+
loader_id: &Address,
883953
) -> Result<(), LiteSVMError> {
884-
self.add_program_internal::<true>(program_id, program_bytes)
954+
self.add_program_internal::<true>(program_id, program_bytes, loader_id)
885955
}
886956

887957
fn create_transaction_context(

crates/litesvm/src/programs/mod.rs

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,45 +1,58 @@
11
use {
22
crate::LiteSVM,
33
solana_address::address,
4-
solana_sdk_ids::{address_lookup_table, config},
4+
solana_sdk_ids::{
5+
address_lookup_table, bpf_loader, bpf_loader_deprecated, bpf_loader_upgradeable, config,
6+
},
57
};
68

79
pub fn load_default_programs(svm: &mut LiteSVM) {
810
svm.add_program_preverified(
911
address!("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"),
1012
include_bytes!("elf/spl_token-3.5.0.so"),
13+
&bpf_loader::id(),
1114
)
1215
.unwrap();
1316
svm.add_program_preverified(
1417
address!("TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"),
1518
include_bytes!("elf/spl_token_2022-10.0.0.so"),
19+
&bpf_loader_upgradeable::id(),
1620
)
1721
.unwrap();
1822
svm.add_program_preverified(
1923
address!("Memo1UhkJRfHyvLMcVucJwxXeuD728EqVDDwQDxFMNo"),
2024
include_bytes!("elf/spl_memo-1.0.0.so"),
25+
&bpf_loader_deprecated::id(),
2126
)
2227
.unwrap();
2328
svm.add_program_preverified(
2429
address!("MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr"),
2530
include_bytes!("elf/spl_memo-3.0.0.so"),
31+
&bpf_loader::id(),
2632
)
2733
.unwrap();
2834
svm.add_program_preverified(
2935
address!("ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"),
3036
include_bytes!("elf/spl_associated_token_account-1.1.1.so"),
37+
&bpf_loader::id(),
38+
)
39+
.unwrap();
40+
svm.add_program_preverified(
41+
config::ID,
42+
include_bytes!("elf/config.so"),
43+
&bpf_loader_upgradeable::id(),
3144
)
3245
.unwrap();
33-
svm.add_program_preverified(config::ID, include_bytes!("elf/config.so"))
34-
.unwrap();
3546
svm.add_program_preverified(
3647
address_lookup_table::ID,
3748
include_bytes!("elf/address_lookup_table.so"),
49+
&bpf_loader_upgradeable::id(),
3850
)
3951
.unwrap();
4052
svm.add_program_preverified(
4153
address!("Stake11111111111111111111111111111111111111"),
4254
include_bytes!("elf/core_bpf_stake-1.0.1.so"),
55+
&bpf_loader_upgradeable::id(),
4356
)
4457
.unwrap()
4558
}

crates/loader/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,14 @@ rust-version.workspace = true
1010
[dependencies]
1111
litesvm = { workspace = true }
1212
solana-address.workspace = true
13-
solana-instruction.workspace = true
1413
solana-keypair.workspace = true
1514
solana-loader-v3-interface = { workspace = true, features = ["bincode"] }
1615
solana-signer.workspace = true
1716
solana-transaction.workspace = true
1817

1918
[dev-dependencies]
2019
agave-feature-set.workspace = true
20+
solana-instruction.workspace = true
2121
solana-message.workspace = true
2222
test-log.workspace = true
2323

0 commit comments

Comments
 (0)