diff --git a/Cargo.lock b/Cargo.lock index 0a57a45..b0cd8db 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5955,6 +5955,7 @@ dependencies = [ "serde_derive", "serde_json", "serde_with 2.3.3", + "sha2 0.10.8", "solana-account-decoder", "solana-client", "solana-sdk", @@ -6758,11 +6759,13 @@ version = "0.1.0" dependencies = [ "anyhow", "autobahn-executor", + "base64 0.12.3", "bincode", "bonfida-test-utils", "env_logger", "log 0.4.21", "router-test-lib", + "sha2 0.10.8", "solana-address-lookup-table-program", "solana-program", "solana-program-test", diff --git a/bin/cli/src/main.rs b/bin/cli/src/main.rs index 4195e0a..e059262 100644 --- a/bin/cli/src/main.rs +++ b/bin/cli/src/main.rs @@ -168,7 +168,10 @@ async fn main() -> Result<(), anyhow::Error> { } &CREATE_REFERRAL_LOG_DISCRIMINANT => { let event = bytemuck::from_bytes::(&decoded[8..]); - println!("CreateReferralLog - referer: {:?}, referee: {:?}, vault: {:?}, mint: {:?}", event.referer, event.referee, event.vault, event.mint); + println!( + "CreateReferralLog - referer: {:?}, referee: {:?}, vault: {:?}, mint: {:?}", + event.referer, event.referee, event.vault, event.mint + ); } _ => panic!("Unknown log discriminant"), } diff --git a/lib/dex-infinity/Cargo.toml b/lib/dex-infinity/Cargo.toml index c2f8afb..8440f33 100644 --- a/lib/dex-infinity/Cargo.toml +++ b/lib/dex-infinity/Cargo.toml @@ -24,7 +24,7 @@ async-trait = "0.1.79" chrono = "0.4.38" sha2 = "0.10.8" tracing = "0.1.40" -spl-associated-token-account = "1.0.5" +spl-associated-token-account = { version = "1.0.5", features = ["no-entrypoint"] } # infinity solana-readonly-account = { version = "1.1.0", features=["solana-sdk"] } diff --git a/lib/dex-infinity/src/infinity.rs b/lib/dex-infinity/src/infinity.rs index fdc6ce6..2917876 100644 --- a/lib/dex-infinity/src/infinity.rs +++ b/lib/dex-infinity/src/infinity.rs @@ -1,5 +1,6 @@ use std::collections::HashMap; use std::collections::HashSet; +use std::str::FromStr; use std::sync::Arc; use jupiter_amm_interface::{Amm, QuoteParams, SwapMode}; @@ -106,6 +107,7 @@ impl DexInterface for InfinityDex { fn program_ids(&self) -> HashSet { [ + Pubkey::from_str("5ocnV1qiCgaQR8Jb8xWnVbApfaygJ8tNoZfgPwsgx9kx").unwrap(), s_controller_lib::program::ID, sanctum_spl_multi_stake_pool_program::ID, sanctum_spl_stake_pool_program::ID, diff --git a/lib/router-lib/Cargo.toml b/lib/router-lib/Cargo.toml index 3e1f870..15a41b3 100644 --- a/lib/router-lib/Cargo.toml +++ b/lib/router-lib/Cargo.toml @@ -32,3 +32,4 @@ lz4 = "1.25.0" async-channel = "1.9.0" lazy_static = "1.5.0" anchor-spl = { version = "0.29.0", features = ["associated_token"] } +sha2 = "0.10.8" diff --git a/lib/router-lib/src/lib.rs b/lib/router-lib/src/lib.rs index 03cc776..499a176 100644 --- a/lib/router-lib/src/lib.rs +++ b/lib/router-lib/src/lib.rs @@ -9,6 +9,6 @@ pub mod test_tools; pub mod utils; pub mod autobahn_executor { - use solana_sdk::declare_id; - declare_id!("AutobNFLMzX1rFCDgwWpwr3ztG5c1oDbSrGq7Jj2LgE"); + use solana_sdk::declare_id; + declare_id!("AutobNFLMzX1rFCDgwWpwr3ztG5c1oDbSrGq7Jj2LgE"); } diff --git a/lib/router-lib/src/test_tools/generate_dex_rpc_dump.rs b/lib/router-lib/src/test_tools/generate_dex_rpc_dump.rs index adc73a0..e3720da 100644 --- a/lib/router-lib/src/test_tools/generate_dex_rpc_dump.rs +++ b/lib/router-lib/src/test_tools/generate_dex_rpc_dump.rs @@ -8,13 +8,14 @@ use itertools::Itertools; use mango_feeds_connector::chain_data::AccountData; use router_feed_lib::router_rpc_client::{RouterRpcClient, RouterRpcClientTrait}; use router_test_lib::{execution_dump, serialize}; +use sha2::{Digest, Sha256}; use solana_sdk::account::ReadableAccount; +use solana_sdk::bpf_loader_upgradeable::UpgradeableLoaderState; use solana_sdk::clock::Clock; use solana_sdk::pubkey::Pubkey; use solana_sdk::signature::Keypair; use solana_sdk::signer::Signer; use solana_sdk::sysvar::SysvarId; -use std::collections::HashSet; use std::sync::Arc; use tracing::{debug, error}; @@ -67,7 +68,7 @@ pub async fn run_dump_mainnet_data_with_custom_amount( let mut skipped = 0; let mut success = 0; - let mut accounts_needed = HashSet::new(); + let mut accounts_needed = dex.program_ids(); for id in edges_identifiers { accounts_needed.insert(id.input_mint()); accounts_needed.insert(id.output_mint()); @@ -123,7 +124,20 @@ pub async fn run_dump_mainnet_data_with_custom_amount( for x in accounts_needed.iter().take(10) { println!("- {} ", x); } - rpc_client.get_multiple_accounts(&accounts_needed).await?; + let accounts = rpc_client.get_multiple_accounts(&accounts_needed).await?; + + for (_, account) in accounts { + // get buffer for upgradable programs + if account.owner == solana_sdk::bpf_loader_upgradeable::ID { + let state = bincode::deserialize::(&account.data).unwrap(); + if let UpgradeableLoaderState::Program { + programdata_address, + } = state + { + rpc_client.get_account(&programdata_address).await?; + } + } + } println!("Error count: {}", errors); println!("Skipped count: {}", skipped); @@ -232,6 +246,25 @@ pub async fn run_dump_swap_ix_with_custom_amount( is_exact_out: false, }); + let chain_data_reader = chain_data.read().unwrap(); + for account in swap_ix.instruction.accounts { + if let Ok(acc) = chain_data_reader.account(&account.pubkey) { + dump.accounts.insert(account.pubkey, acc.account.clone()); + } else { + error!("Missing account (needed for swap) {}", account.pubkey); + } + } + let account = chain_data_reader + .account(&id.input_mint()) + .expect("missing mint"); + dump.accounts + .insert(id.input_mint(), account.account.clone()); + let account = chain_data_reader + .account(&id.input_mint()) + .expect("missing mint"); + dump.accounts + .insert(id.output_mint(), account.account.clone()); + // build exact out tests if dex.supports_exact_out(&id) { let Ok(mut quote_exact_out) = @@ -284,41 +317,8 @@ pub async fn run_dump_swap_ix_with_custom_amount( error!("Missing account (needed for swap) {}", account.pubkey); } } - - let account = chain_data_reader - .account(&id.input_mint()) - .expect("missing mint"); - dump.accounts - .insert(id.input_mint(), account.account.clone()); - - let account = chain_data_reader - .account(&id.input_mint()) - .expect("missing mint"); - dump.accounts - .insert(id.output_mint(), account.account.clone()); } } - - let chain_data_reader = chain_data.read().unwrap(); - for account in swap_ix.instruction.accounts { - if let Ok(acc) = chain_data_reader.account(&account.pubkey) { - dump.accounts.insert(account.pubkey, acc.account.clone()); - } else { - error!("Missing account (needed for swap) {}", account.pubkey); - } - } - - let account = chain_data_reader - .account(&id.input_mint()) - .expect("missing mint"); - dump.accounts - .insert(id.input_mint(), account.account.clone()); - - let account = chain_data_reader - .account(&id.input_mint()) - .expect("missing mint"); - dump.accounts - .insert(id.output_mint(), account.account.clone()); } println!("Error count: {}", errors); @@ -326,6 +326,38 @@ pub async fn run_dump_swap_ix_with_custom_amount( println!("Success count: {}", success); println!("Exactout Success count: {}", exact_out_sucess); + for program in dump.programs.clone() { + let program_account = account_provider.account(&program)?; + + dump.accounts + .insert(program, program_account.account.clone()); + // use downloaded buffers for the upgradable programs + if *program_account.account.owner() == solana_sdk::bpf_loader_upgradeable::ID { + let state = + bincode::deserialize::(program_account.account.data()) + .unwrap(); + if let UpgradeableLoaderState::Program { + programdata_address, + } = state + { + let program_data_account = account_provider.account(&programdata_address)?; + dump.accounts + .insert(programdata_address, program_data_account.account); + } + } + } + + for program in &dump.programs { + debug!("program : {program:?}"); + } + + for (pk, program) in &dump.accounts { + let mut hasher = Sha256::new(); + hasher.update(program.data()); + let result = hasher.finalize(); + let base64 = base64::encode(result); + debug!("account : {pk:?} dump : {base64:?}"); + } serialize::serialize_to_file( &dump, &format!("../../programs/simulator/tests/fixtures/{}", dump_name).to_string(), diff --git a/lib/router-test-lib/src/execution_dump.rs b/lib/router-test-lib/src/execution_dump.rs index 78d0fc7..1632dc8 100644 --- a/lib/router-test-lib/src/execution_dump.rs +++ b/lib/router-test-lib/src/execution_dump.rs @@ -1,4 +1,4 @@ -use std::collections::HashMap; +use std::collections::{HashMap, HashSet}; use serde_derive::{Deserialize, Serialize}; use solana_sdk::account::AccountSharedData; @@ -17,7 +17,7 @@ pub struct ExecutionItem { #[derive(Clone, Serialize, Deserialize)] pub struct ExecutionDump { pub wallet_keypair: String, - pub programs: Vec, + pub programs: HashSet, pub cache: Vec, pub accounts: HashMap, } diff --git a/lib/router-test-lib/src/serialize.rs b/lib/router-test-lib/src/serialize.rs index d3fce08..0309151 100644 --- a/lib/router-test-lib/src/serialize.rs +++ b/lib/router-test-lib/src/serialize.rs @@ -12,6 +12,7 @@ where let mut writer = lz4::EncoderBuilder::new().build(file_writer).unwrap(); writer.write_all(serialized_data.as_slice()).unwrap(); writer.flush().unwrap(); + let _ = writer.finish(); } pub fn deserialize_from_file(path: &String) -> anyhow::Result diff --git a/programs/autobahn-executor/src/lib.rs b/programs/autobahn-executor/src/lib.rs index 20b9219..6b7fc0f 100644 --- a/programs/autobahn-executor/src/lib.rs +++ b/programs/autobahn-executor/src/lib.rs @@ -1,5 +1,5 @@ -mod instructions; pub mod create_pda; +mod instructions; pub mod logs; pub mod swap_ix; pub mod utils; @@ -14,7 +14,6 @@ use solana_program::program_error::ProgramError; use solana_program::program_pack::Pack; use solana_program::{account_info::AccountInfo, pubkey::Pubkey}; - #[cfg(not(feature = "no-entrypoint"))] use {default_env::default_env, solana_program::entrypoint, solana_security_txt::security_txt}; diff --git a/programs/simulator/Cargo.toml b/programs/simulator/Cargo.toml index 4f2309a..ce37c56 100644 --- a/programs/simulator/Cargo.toml +++ b/programs/simulator/Cargo.toml @@ -31,3 +31,8 @@ bonfida-test-utils = "0.4.4" log = "0.4.14" env_logger = "0.9.0" bincode = "1.3.3" +sha2 = "0.10.8" +base64 = "0.12.3" + +[profile.test] +inherits = "release" \ No newline at end of file diff --git a/programs/simulator/tests/cases/test_swap_from_dump.rs b/programs/simulator/tests/cases/test_swap_from_dump.rs index 32c148e..6974809 100644 --- a/programs/simulator/tests/cases/test_swap_from_dump.rs +++ b/programs/simulator/tests/cases/test_swap_from_dump.rs @@ -23,6 +23,8 @@ use spl_token_2022::state::AccountState as AccountState2022; use std::collections::HashMap; use std::process::exit; use std::str::FromStr; +// use sha2::Sha256; +// use sha2::Digest; struct TestLogSyscallStubs; impl SyscallStubs for TestLogSyscallStubs { @@ -121,16 +123,18 @@ async fn run_all_swap_from_dump(dump_name: &str) -> Result, Er continue; } - let mut ctx = setup_test_chain(&data.programs, &clock).await; + let instruction = deserialize_instruction("e.instruction)?; + + let programs = data.programs.iter().copied().collect(); + let mut ctx = setup_test_chain(&programs, &clock, &data).await; create_wallet(&mut ctx, wallet.pubkey()); let initial_in_balance = quote.input_amount * 2; let initial_out_balance = 1_000_000; - let instruction = deserialize_instruction("e.instruction)?; - - initialize_instruction_accounts(&mut ctx, &data, &instruction).await?; + // let slot = ctx.banks_client.get_root_slot().await.unwrap(); + // ctx.warp_to_slot(slot+3).unwrap(); let input_mint_is_2022 = is_2022(&data.accounts, quote.input_mint).await; let output_mint_is_2022 = is_2022(&data.accounts, quote.output_mint).await; @@ -152,6 +156,19 @@ async fn run_all_swap_from_dump(dump_name: &str) -> Result, Er ) .await?; + for meta in &instruction.accounts { + let Ok(Some(account)) = ctx.banks_client.get_account(meta.pubkey).await else { + log::warn!("missing account : {:?}", meta.pubkey); + continue; + }; + // keep code to test hashses + // let mut hasher = Sha256::new(); + // hasher.update(account.data()); + // let result = hasher.finalize(); + // let base64 = base64::encode(result); + // log::debug!("account : {:?} dump : {base64:?} executable : {}", meta.pubkey, account.executable()); + } + if let Some(cus) = simulate_cu_usage(&mut ctx, &wallet, &instruction).await { cus_required.push(cus); } @@ -324,31 +341,23 @@ fn deserialize_instruction(swap_ix: &Vec) -> anyhow::Result { Ok(instruction) } -async fn initialize_instruction_accounts( - ctx: &mut ProgramTestContext, +async fn initialize_accounts( + program_test: &mut ProgramTest, dump: &ExecutionDump, - instruction: &Instruction, ) -> anyhow::Result<()> { - for account_meta in &instruction.accounts { - if dump.programs.contains(&account_meta.pubkey) { - continue; - } - if let Some(account) = dump.accounts.get(&account_meta.pubkey) { - if account.executable() { - continue; - } - debug!("Setting data for {}", account_meta.pubkey); - ctx.set_account(&account_meta.pubkey, account); - } else { - if ctx - .banks_client - .get_account(account_meta.pubkey) - .await? - .is_none() - { - debug!("Missing data for {}", account_meta.pubkey); // Can happen for empty oracle account... - } - } + println!("initializing accounts : {:?}", dump.accounts.len()); + for (pk, account) in &dump.accounts { + println!("Setting data for {}", pk); + program_test.add_account( + *pk, + solana_sdk::account::Account { + lamports: account.lamports(), + owner: *account.owner(), + data: account.data().to_vec(), + rent_epoch: account.rent_epoch(), + executable: account.executable(), + }, + ); } Ok(()) @@ -370,15 +379,18 @@ async fn simulate_cu_usage( match sim { Ok(sim) => { log::debug!("{:?}", sim.result); - if sim.result.is_some() && sim.result.unwrap().is_ok() { - let simulation_details = sim.simulation_details.unwrap(); - let cus = simulation_details.units_consumed; + let simulation_details = sim.simulation_details.unwrap(); + let cus = simulation_details.units_consumed; + if sim.result.is_some() && sim.result.clone().unwrap().is_ok() { log::debug!("units consumed : {}", cus); + Some(cus) + } else if sim.result.is_some() && sim.result.clone().unwrap().is_err() { + log::debug!("simluation failed : {:?}", sim.result.unwrap()); log::debug!("----logs"); for log in simulation_details.logs { log::debug!("{log:?}"); } - Some(cus) + None } else { None } @@ -515,7 +527,11 @@ fn create_wallet(ctx: &mut ProgramTestContext, address: Pubkey) { ); } -async fn setup_test_chain(programs: &Vec, clock: &Clock) -> ProgramTestContext { +async fn setup_test_chain( + programs: &Vec, + clock: &Clock, + dump: &ExecutionDump, +) -> ProgramTestContext { // We need to intercept logs to capture program log output let log_filter = "solana_rbpf=trace,\ solana_runtime::message_processor=debug,\ @@ -531,6 +547,10 @@ async fn setup_test_chain(programs: &Vec, clock: &Clock) -> ProgramTestC let _ = log::set_boxed_logger(Box::new(env_logger)); let mut program_test = ProgramTest::default(); + + initialize_accounts(&mut program_test, dump).await.unwrap(); + + program_test.prefer_bpf(true); for &key in programs { program_test.add_program(key.to_string().as_str(), key, None); } @@ -539,12 +559,14 @@ async fn setup_test_chain(programs: &Vec, clock: &Clock) -> ProgramTestC // TODO: make this dynamic based on routes program_test.set_compute_max_units(1_400_000); - let program_test_context = program_test.start_with_context().await; + let mut program_test_context = program_test.start_with_context().await; // Set clock program_test_context.set_sysvar(clock); info!("Setting clock to: {}", clock.unix_timestamp); + program_test_context.warp_to_slot(40).unwrap(); + program_test_context } diff --git a/programs/simulator/tests/fixtures/675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8.so b/programs/simulator/tests/fixtures/675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8.so index e9428e1..2799004 100644 Binary files a/programs/simulator/tests/fixtures/675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8.so and b/programs/simulator/tests/fixtures/675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8.so differ diff --git a/programs/simulator/tests/fixtures/CPMMoo8L3F4NbTegBCKVNunggL7H1ZpdTHKxQB5qKP1C.so b/programs/simulator/tests/fixtures/CPMMoo8L3F4NbTegBCKVNunggL7H1ZpdTHKxQB5qKP1C.so index 741dd30..8e28bda 100644 Binary files a/programs/simulator/tests/fixtures/CPMMoo8L3F4NbTegBCKVNunggL7H1ZpdTHKxQB5qKP1C.so and b/programs/simulator/tests/fixtures/CPMMoo8L3F4NbTegBCKVNunggL7H1ZpdTHKxQB5qKP1C.so differ diff --git a/programs/simulator/tests/fixtures/download-programs.sh b/programs/simulator/tests/fixtures/download-programs.sh new file mode 100644 index 0000000..9ec22e0 --- /dev/null +++ b/programs/simulator/tests/fixtures/download-programs.sh @@ -0,0 +1,8 @@ +#!/usr/bin/env bash + +for filename in programs/simulator/tests/fixtures/*.so; do + filename=$(basename $filename) + filename="${filename%.*}" + echo $filename + solana program dump -um $filename $SOURCE/$filename.so +done \ No newline at end of file diff --git a/programs/simulator/tests/fixtures/whirLbMiicVdio4qvUfM5KAg6Ct8VwpYzGff3uctyCc.so b/programs/simulator/tests/fixtures/whirLbMiicVdio4qvUfM5KAg6Ct8VwpYzGff3uctyCc.so index 08255fa..d6cd982 100644 Binary files a/programs/simulator/tests/fixtures/whirLbMiicVdio4qvUfM5KAg6Ct8VwpYzGff3uctyCc.so and b/programs/simulator/tests/fixtures/whirLbMiicVdio4qvUfM5KAg6Ct8VwpYzGff3uctyCc.so differ