Migrating testing to lib svm (#28)
* Using libSVM for simulation * Some more fixes for invalid mint authority * avoiding disabled lst mints * performance improvements, reduced log-levels * adding a todo for token extention program
This commit is contained in:
parent
6324916be7
commit
a4dc9f19fd
|
@ -3865,6 +3865,26 @@ version = "0.4.13"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c"
|
checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "litesvm"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "git+https://github.com/blockworks-foundation/litesvm.git?branch=v0.1.0+solana_1.7#4c884606289e484090be42a08b79dc3bc5f449f7"
|
||||||
|
dependencies = [
|
||||||
|
"bincode",
|
||||||
|
"indexmap 2.2.6",
|
||||||
|
"itertools 0.12.1",
|
||||||
|
"log 0.4.21",
|
||||||
|
"solana-address-lookup-table-program",
|
||||||
|
"solana-bpf-loader-program",
|
||||||
|
"solana-compute-budget-program",
|
||||||
|
"solana-loader-v4-program",
|
||||||
|
"solana-program",
|
||||||
|
"solana-program-runtime",
|
||||||
|
"solana-sdk",
|
||||||
|
"solana-system-program",
|
||||||
|
"thiserror",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lock_api"
|
name = "lock_api"
|
||||||
version = "0.3.4"
|
version = "0.3.4"
|
||||||
|
@ -6763,11 +6783,13 @@ dependencies = [
|
||||||
"bincode",
|
"bincode",
|
||||||
"bonfida-test-utils",
|
"bonfida-test-utils",
|
||||||
"env_logger",
|
"env_logger",
|
||||||
|
"litesvm",
|
||||||
"log 0.4.21",
|
"log 0.4.21",
|
||||||
"router-test-lib",
|
"router-test-lib",
|
||||||
"sha2 0.10.8",
|
"sha2 0.10.8",
|
||||||
"solana-address-lookup-table-program",
|
"solana-address-lookup-table-program",
|
||||||
"solana-program",
|
"solana-program",
|
||||||
|
"solana-program-runtime",
|
||||||
"solana-program-test",
|
"solana-program-test",
|
||||||
"solana-sdk",
|
"solana-sdk",
|
||||||
"spl-associated-token-account 1.1.3",
|
"spl-associated-token-account 1.1.3",
|
||||||
|
@ -6775,6 +6797,8 @@ dependencies = [
|
||||||
"spl-token-2022 1.0.0",
|
"spl-token-2022 1.0.0",
|
||||||
"test-case",
|
"test-case",
|
||||||
"tokio",
|
"tokio",
|
||||||
|
"tracing",
|
||||||
|
"tracing-subscriber",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
|
@ -68,6 +68,14 @@ impl DexInterface for InfinityDex {
|
||||||
let lst_mint = lst_data.sol_val_calc.lst_mint();
|
let lst_mint = lst_data.sol_val_calc.lst_mint();
|
||||||
let account_metas = lst_data.sol_val_calc.ix_accounts();
|
let account_metas = lst_data.sol_val_calc.ix_accounts();
|
||||||
let num_accounts_for_tx = account_metas.len();
|
let num_accounts_for_tx = account_metas.len();
|
||||||
|
let Ok((lst_state, lst_data)) = amm.find_ready_lst(lst_mint) else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
|
||||||
|
if lst_state.is_input_disabled != 0 {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
for pk in lst_data.sol_val_calc.get_accounts_to_update() {
|
for pk in lst_data.sol_val_calc.get_accounts_to_update() {
|
||||||
let edges = vec![
|
let edges = vec![
|
||||||
Arc::new(InfinityEdgeIdentifier {
|
Arc::new(InfinityEdgeIdentifier {
|
||||||
|
|
|
@ -126,7 +126,7 @@ pub async fn run_dump_mainnet_data_with_custom_amount(
|
||||||
}
|
}
|
||||||
let accounts = rpc_client.get_multiple_accounts(&accounts_needed).await?;
|
let accounts = rpc_client.get_multiple_accounts(&accounts_needed).await?;
|
||||||
|
|
||||||
for (_, account) in accounts {
|
for (_pk, account) in accounts {
|
||||||
// get buffer for upgradable programs
|
// get buffer for upgradable programs
|
||||||
if account.owner == solana_sdk::bpf_loader_upgradeable::ID {
|
if account.owner == solana_sdk::bpf_loader_upgradeable::ID {
|
||||||
let state = bincode::deserialize::<UpgradeableLoaderState>(&account.data).unwrap();
|
let state = bincode::deserialize::<UpgradeableLoaderState>(&account.data).unwrap();
|
||||||
|
@ -260,7 +260,7 @@ pub async fn run_dump_swap_ix_with_custom_amount(
|
||||||
dump.accounts
|
dump.accounts
|
||||||
.insert(id.input_mint(), account.account.clone());
|
.insert(id.input_mint(), account.account.clone());
|
||||||
let account = chain_data_reader
|
let account = chain_data_reader
|
||||||
.account(&id.input_mint())
|
.account(&id.output_mint())
|
||||||
.expect("missing mint");
|
.expect("missing mint");
|
||||||
dump.accounts
|
dump.accounts
|
||||||
.insert(id.output_mint(), account.account.clone());
|
.insert(id.output_mint(), account.account.clone());
|
||||||
|
@ -307,9 +307,6 @@ pub async fn run_dump_swap_ix_with_custom_amount(
|
||||||
instruction: bincode::serialize(&swap_exact_out_ix.instruction).unwrap(),
|
instruction: bincode::serialize(&swap_exact_out_ix.instruction).unwrap(),
|
||||||
is_exact_out: true,
|
is_exact_out: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
// add exact out accounts
|
|
||||||
let chain_data_reader = chain_data.read().unwrap();
|
|
||||||
for account in swap_exact_out_ix.instruction.accounts {
|
for account in swap_exact_out_ix.instruction.accounts {
|
||||||
if let Ok(acc) = chain_data_reader.account(&account.pubkey) {
|
if let Ok(acc) = chain_data_reader.account(&account.pubkey) {
|
||||||
dump.accounts.insert(account.pubkey, acc.account.clone());
|
dump.accounts.insert(account.pubkey, acc.account.clone());
|
||||||
|
@ -351,9 +348,9 @@ pub async fn run_dump_swap_ix_with_custom_amount(
|
||||||
debug!("program : {program:?}");
|
debug!("program : {program:?}");
|
||||||
}
|
}
|
||||||
|
|
||||||
for (pk, program) in &dump.accounts {
|
for (pk, account_data) in &dump.accounts {
|
||||||
let mut hasher = Sha256::new();
|
let mut hasher = Sha256::new();
|
||||||
hasher.update(program.data());
|
hasher.update(account_data.data());
|
||||||
let result = hasher.finalize();
|
let result = hasher.finalize();
|
||||||
let base64 = base64::encode(result);
|
let base64 = base64::encode(result);
|
||||||
debug!("account : {pk:?} dump : {base64:?}");
|
debug!("account : {pk:?} dump : {base64:?}");
|
||||||
|
|
|
@ -23,6 +23,7 @@ test-case = "*"
|
||||||
tokio = "1.37.0"
|
tokio = "1.37.0"
|
||||||
solana-address-lookup-table-program = "1.17"
|
solana-address-lookup-table-program = "1.17"
|
||||||
solana-program-test = "1.17"
|
solana-program-test = "1.17"
|
||||||
|
solana-program-runtime = "1.17"
|
||||||
solana-sdk = "1.17"
|
solana-sdk = "1.17"
|
||||||
spl-token = { version = "^3.0.0", features = ["no-entrypoint"] }
|
spl-token = { version = "^3.0.0", features = ["no-entrypoint"] }
|
||||||
spl-token-2022 = { version = "1.0.0", features = ["no-entrypoint"] }
|
spl-token-2022 = { version = "1.0.0", features = ["no-entrypoint"] }
|
||||||
|
@ -33,6 +34,9 @@ env_logger = "0.9.0"
|
||||||
bincode = "1.3.3"
|
bincode = "1.3.3"
|
||||||
sha2 = "0.10.8"
|
sha2 = "0.10.8"
|
||||||
base64 = "0.12.3"
|
base64 = "0.12.3"
|
||||||
|
litesvm = { git = "https://github.com/blockworks-foundation/litesvm.git", branch = "v0.1.0+solana_1.7" }
|
||||||
|
tracing = "0.1.37"
|
||||||
|
tracing-subscriber = "0.3.16"
|
||||||
|
|
||||||
[profile.test]
|
[profile.test]
|
||||||
inherits = "release"
|
inherits = "release"
|
|
@ -1,30 +1,30 @@
|
||||||
use anyhow::{Context, Error};
|
use anyhow::Error;
|
||||||
use bonfida_test_utils::error::TestError;
|
use litesvm::LiteSVM;
|
||||||
use bonfida_test_utils::ProgramTestContextExt;
|
use log::{error, info, warn};
|
||||||
use log::{debug, error, info, warn};
|
|
||||||
use router_test_lib::execution_dump::{ExecutionDump, ExecutionItem};
|
use router_test_lib::execution_dump::{ExecutionDump, ExecutionItem};
|
||||||
use router_test_lib::{execution_dump, serialize};
|
use router_test_lib::{execution_dump, serialize};
|
||||||
use solana_program::clock::{Clock, Epoch};
|
use sha2::Digest;
|
||||||
|
use sha2::Sha256;
|
||||||
|
use solana_program::clock::Clock;
|
||||||
use solana_program::instruction::Instruction;
|
use solana_program::instruction::Instruction;
|
||||||
use solana_program::program_pack::Pack;
|
use solana_program::program_pack::Pack;
|
||||||
use solana_program::program_stubs::{set_syscall_stubs, SyscallStubs};
|
use solana_program::program_stubs::{set_syscall_stubs, SyscallStubs};
|
||||||
use solana_program::pubkey::Pubkey;
|
use solana_program::pubkey::Pubkey;
|
||||||
use solana_program::sysvar::SysvarId;
|
use solana_program::sysvar::SysvarId;
|
||||||
use solana_program_test::BanksClientError;
|
|
||||||
use solana_program_test::{ProgramTest, ProgramTestContext};
|
|
||||||
use solana_sdk::account::{Account, AccountSharedData, ReadableAccount};
|
use solana_sdk::account::{Account, AccountSharedData, ReadableAccount};
|
||||||
use solana_sdk::epoch_info::EpochInfo;
|
use solana_sdk::bpf_loader_upgradeable::UpgradeableLoaderState;
|
||||||
|
use solana_sdk::message::{Message, VersionedMessage};
|
||||||
use solana_sdk::signature::Keypair;
|
use solana_sdk::signature::Keypair;
|
||||||
use solana_sdk::signer::Signer;
|
use solana_sdk::signer::Signer;
|
||||||
use solana_sdk::transaction::Transaction;
|
use solana_sdk::transaction::VersionedTransaction;
|
||||||
use spl_associated_token_account::get_associated_token_address;
|
use spl_associated_token_account::{
|
||||||
|
get_associated_token_address, get_associated_token_address_with_program_id,
|
||||||
|
};
|
||||||
use spl_token::state::AccountState;
|
use spl_token::state::AccountState;
|
||||||
use spl_token_2022::state::AccountState as AccountState2022;
|
use spl_token_2022::state::AccountState as AccountState2022;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::process::exit;
|
use std::path::PathBuf;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
// use sha2::Sha256;
|
|
||||||
// use sha2::Digest;
|
|
||||||
|
|
||||||
struct TestLogSyscallStubs;
|
struct TestLogSyscallStubs;
|
||||||
impl SyscallStubs for TestLogSyscallStubs {
|
impl SyscallStubs for TestLogSyscallStubs {
|
||||||
|
@ -73,13 +73,15 @@ async fn test_quote_match_swap_for_infinity() -> anyhow::Result<()> {
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn run_all_swap_from_dump(dump_name: &str) -> Result<Result<(), Error>, Error> {
|
async fn run_all_swap_from_dump(dump_name: &str) -> Result<Result<(), Error>, Error> {
|
||||||
|
tracing_subscriber::fmt::init();
|
||||||
|
|
||||||
let mut skip_count = option_env!("SKIP_COUNT")
|
let mut skip_count = option_env!("SKIP_COUNT")
|
||||||
.map(|x| u32::from_str(x).unwrap_or(0))
|
.map(|x| u32::from_str(x).unwrap_or(0))
|
||||||
.unwrap_or(0);
|
.unwrap_or(0);
|
||||||
let mut stop_at = u32::MAX;
|
let mut stop_at = u32::MAX;
|
||||||
let skip_ixs_index = vec![];
|
let skip_ixs_index = vec![];
|
||||||
|
|
||||||
let mut run_lot_size = option_env!("RUN_LOT_SIZE")
|
let run_lot_size = option_env!("RUN_LOT_SIZE")
|
||||||
.map(|x| u32::from_str(x).unwrap_or(500))
|
.map(|x| u32::from_str(x).unwrap_or(500))
|
||||||
.unwrap_or(500);
|
.unwrap_or(500);
|
||||||
|
|
||||||
|
@ -126,7 +128,7 @@ async fn run_all_swap_from_dump(dump_name: &str) -> Result<Result<(), Error>, Er
|
||||||
let instruction = deserialize_instruction("e.instruction)?;
|
let instruction = deserialize_instruction("e.instruction)?;
|
||||||
|
|
||||||
let programs = data.programs.iter().copied().collect();
|
let programs = data.programs.iter().copied().collect();
|
||||||
let mut ctx = setup_test_chain(&programs, &clock, &data).await;
|
let mut ctx = setup_test_chain(&programs, &clock, &data, &instruction)?;
|
||||||
|
|
||||||
create_wallet(&mut ctx, wallet.pubkey());
|
create_wallet(&mut ctx, wallet.pubkey());
|
||||||
|
|
||||||
|
@ -145,28 +147,31 @@ async fn run_all_swap_from_dump(dump_name: &str) -> Result<Result<(), Error>, Er
|
||||||
quote.input_mint,
|
quote.input_mint,
|
||||||
initial_in_balance,
|
initial_in_balance,
|
||||||
input_mint_is_2022,
|
input_mint_is_2022,
|
||||||
)
|
)?;
|
||||||
.await?;
|
|
||||||
set_balance(
|
set_balance(
|
||||||
&mut ctx,
|
&mut ctx,
|
||||||
wallet.pubkey(),
|
wallet.pubkey(),
|
||||||
quote.output_mint,
|
quote.output_mint,
|
||||||
initial_out_balance,
|
initial_out_balance,
|
||||||
output_mint_is_2022,
|
output_mint_is_2022,
|
||||||
)
|
)?;
|
||||||
.await?;
|
|
||||||
|
|
||||||
for meta in &instruction.accounts {
|
for meta in &instruction.accounts {
|
||||||
let Ok(Some(account)) = ctx.banks_client.get_account(meta.pubkey).await else {
|
let Some(account) = ctx.get_account(&meta.pubkey) else {
|
||||||
log::warn!("missing account : {:?}", meta.pubkey);
|
log::warn!("missing account : {:?}", meta.pubkey);
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
|
|
||||||
// keep code to test hashses
|
// keep code to test hashses
|
||||||
// let mut hasher = Sha256::new();
|
let mut hasher = Sha256::new();
|
||||||
// hasher.update(account.data());
|
hasher.update(account.data());
|
||||||
// let result = hasher.finalize();
|
let result = hasher.finalize();
|
||||||
// let base64 = base64::encode(result);
|
let base64 = base64::encode(result);
|
||||||
// log::debug!("account : {:?} dump : {base64:?} executable : {}", meta.pubkey, account.executable());
|
log::debug!(
|
||||||
|
"account : {:?} dump : {base64:?} executable : {}",
|
||||||
|
meta.pubkey,
|
||||||
|
account.executable()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(cus) = simulate_cu_usage(&mut ctx, &wallet, &instruction).await {
|
if let Some(cus) = simulate_cu_usage(&mut ctx, &wallet, &instruction).await {
|
||||||
|
@ -278,7 +283,7 @@ async fn debug_print_ix(
|
||||||
success: &mut i32,
|
success: &mut i32,
|
||||||
index: &mut u32,
|
index: &mut u32,
|
||||||
quote: &ExecutionItem,
|
quote: &ExecutionItem,
|
||||||
ctx: &mut ProgramTestContext,
|
ctx: &mut LiteSVM,
|
||||||
instruction: &Instruction,
|
instruction: &Instruction,
|
||||||
input_mint_is_2022: bool,
|
input_mint_is_2022: bool,
|
||||||
output_mint_is_2022: bool,
|
output_mint_is_2022: bool,
|
||||||
|
@ -310,13 +315,8 @@ async fn debug_print_ix(
|
||||||
|
|
||||||
for acc in &instruction.accounts {
|
for acc in &instruction.accounts {
|
||||||
let account = ctx
|
let account = ctx
|
||||||
.banks_client
|
.get_account(&acc.pubkey)
|
||||||
.get_account(acc.pubkey)
|
.map(|x| (x.executable, x.owner.to_string()))
|
||||||
.await
|
|
||||||
.map(|x| {
|
|
||||||
x.map(|y| (y.executable, y.owner.to_string()))
|
|
||||||
.unwrap_or((false, "???".to_string()))
|
|
||||||
})
|
|
||||||
.unwrap_or((false, "???".to_string()));
|
.unwrap_or((false, "???".to_string()));
|
||||||
|
|
||||||
warn!(
|
warn!(
|
||||||
|
@ -341,14 +341,53 @@ fn deserialize_instruction(swap_ix: &Vec<u8>) -> anyhow::Result<Instruction> {
|
||||||
Ok(instruction)
|
Ok(instruction)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn initialize_accounts(
|
fn initialize_accounts(
|
||||||
program_test: &mut ProgramTest,
|
program_test: &mut LiteSVM,
|
||||||
dump: &ExecutionDump,
|
dump: &ExecutionDump,
|
||||||
|
accounts_list: &Vec<Pubkey>,
|
||||||
) -> anyhow::Result<()> {
|
) -> anyhow::Result<()> {
|
||||||
println!("initializing accounts : {:?}", dump.accounts.len());
|
log::debug!("initializing accounts : {:?}", dump.accounts.len());
|
||||||
for (pk, account) in &dump.accounts {
|
for pk in accounts_list {
|
||||||
println!("Setting data for {}", pk);
|
let Some(account) = dump.accounts.get(pk) else {
|
||||||
program_test.add_account(
|
continue;
|
||||||
|
};
|
||||||
|
if *account.owner() == solana_sdk::bpf_loader_upgradeable::ID {
|
||||||
|
log::debug!("{pk:?} has upgradable loader");
|
||||||
|
let state = bincode::deserialize::<UpgradeableLoaderState>(&account.data()).unwrap();
|
||||||
|
if let UpgradeableLoaderState::Program {
|
||||||
|
programdata_address,
|
||||||
|
} = state
|
||||||
|
{
|
||||||
|
// load buffer accounts first
|
||||||
|
match dump.accounts.get(&programdata_address) {
|
||||||
|
Some(program_buffer) => {
|
||||||
|
log::debug!("loading buffer: {programdata_address:?}");
|
||||||
|
program_test.set_account(
|
||||||
|
programdata_address,
|
||||||
|
solana_sdk::account::Account {
|
||||||
|
lamports: program_buffer.lamports(),
|
||||||
|
owner: *program_buffer.owner(),
|
||||||
|
data: program_buffer.data().to_vec(),
|
||||||
|
rent_epoch: program_buffer.rent_epoch(),
|
||||||
|
executable: program_buffer.executable(),
|
||||||
|
},
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
error!("{programdata_address:?} is not there");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
log::debug!(
|
||||||
|
"Setting data for {} with owner {} and is executable {}",
|
||||||
|
pk,
|
||||||
|
account.owner(),
|
||||||
|
account.executable()
|
||||||
|
);
|
||||||
|
|
||||||
|
log::debug!("Setting data for {}", pk);
|
||||||
|
program_test.set_account(
|
||||||
*pk,
|
*pk,
|
||||||
solana_sdk::account::Account {
|
solana_sdk::account::Account {
|
||||||
lamports: account.lamports(),
|
lamports: account.lamports(),
|
||||||
|
@ -357,110 +396,109 @@ async fn initialize_accounts(
|
||||||
rent_epoch: account.rent_epoch(),
|
rent_epoch: account.rent_epoch(),
|
||||||
executable: account.executable(),
|
executable: account.executable(),
|
||||||
},
|
},
|
||||||
);
|
)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn simulate_cu_usage(
|
async fn simulate_cu_usage(
|
||||||
ctx: &mut ProgramTestContext,
|
ctx: &mut LiteSVM,
|
||||||
owner: &Keypair,
|
owner: &Keypair,
|
||||||
instruction: &Instruction,
|
instruction: &Instruction,
|
||||||
) -> Option<u64> {
|
) -> Option<u64> {
|
||||||
let mut transaction =
|
let tx = VersionedTransaction::try_new(
|
||||||
Transaction::new_with_payer(&[instruction.clone()], Some(&ctx.payer.pubkey()));
|
VersionedMessage::Legacy(Message::new(&[instruction.clone()], Some(&owner.pubkey()))),
|
||||||
|
&[owner],
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
transaction.sign(&[&ctx.payer, owner], ctx.last_blockhash);
|
let sim = ctx.simulate_transaction(tx);
|
||||||
let sim = ctx
|
|
||||||
.banks_client
|
|
||||||
.simulate_transaction(transaction.clone())
|
|
||||||
.await;
|
|
||||||
match sim {
|
match sim {
|
||||||
Ok(sim) => {
|
Ok(sim) => {
|
||||||
log::debug!("{:?}", sim.result);
|
let cus = sim.compute_units_consumed;
|
||||||
let simulation_details = sim.simulation_details.unwrap();
|
log::debug!("----logs");
|
||||||
let cus = simulation_details.units_consumed;
|
for log in sim.logs {
|
||||||
if sim.result.is_some() && sim.result.clone().unwrap().is_ok() {
|
log::debug!("{log:?}");
|
||||||
log::debug!("units consumed : {}", cus);
|
}
|
||||||
|
if cus > 0 {
|
||||||
Some(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:?}");
|
|
||||||
}
|
|
||||||
None
|
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
log::warn!("Error simulating : {}", e);
|
log::warn!("Error simulating : {:?}", e);
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn swap(
|
async fn swap(ctx: &mut LiteSVM, owner: &Keypair, instruction: &Instruction) -> anyhow::Result<()> {
|
||||||
ctx: &mut ProgramTestContext,
|
let tx = VersionedTransaction::try_new(
|
||||||
owner: &Keypair,
|
VersionedMessage::Legacy(Message::new(&[instruction.clone()], Some(&owner.pubkey()))),
|
||||||
instruction: &Instruction,
|
&[owner],
|
||||||
) -> anyhow::Result<()> {
|
)
|
||||||
ctx.get_new_latest_blockhash().await?;
|
.unwrap();
|
||||||
|
|
||||||
log::info!("swapping");
|
|
||||||
let result = ctx
|
|
||||||
.sign_send_instructions(&[instruction.clone()], &[&owner])
|
|
||||||
.await;
|
|
||||||
|
|
||||||
|
let result = ctx.send_transaction(tx);
|
||||||
match result {
|
match result {
|
||||||
Ok(()) => Ok(()),
|
Ok(_) => Ok(()),
|
||||||
Err(e) => Err(anyhow::format_err!("Failed to swap {:?}", e)),
|
Err(e) => {
|
||||||
|
log::error!("------------- LOGS ------------------");
|
||||||
|
for log in &e.meta.logs {
|
||||||
|
log::error!("{log:?}");
|
||||||
|
}
|
||||||
|
Err(anyhow::format_err!("Failed to swap {:?}", e.err))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn get_balance(
|
async fn get_balance(
|
||||||
ctx: &mut ProgramTestContext,
|
ctx: &mut LiteSVM,
|
||||||
owner: Pubkey,
|
owner: Pubkey,
|
||||||
mint: Pubkey,
|
mint: Pubkey,
|
||||||
is_2022: bool,
|
is_2022: bool,
|
||||||
) -> anyhow::Result<u64> {
|
) -> anyhow::Result<u64> {
|
||||||
let ata_address = get_associated_token_address(&owner, &mint);
|
let ata_address = get_associated_token_address(&owner, &mint);
|
||||||
|
|
||||||
|
let Some(ata) = ctx.get_account(&ata_address) else {
|
||||||
|
return Ok(0);
|
||||||
|
};
|
||||||
|
|
||||||
if is_2022 {
|
if is_2022 {
|
||||||
let Ok(ata) = ctx.banks_client.get_account(ata_address).await else {
|
|
||||||
return Ok(0);
|
|
||||||
};
|
|
||||||
|
|
||||||
let Some(ata) = ata else {
|
|
||||||
return Ok(0);
|
|
||||||
};
|
|
||||||
|
|
||||||
let ata = spl_token_2022::state::Account::unpack(&ata.data);
|
let ata = spl_token_2022::state::Account::unpack(&ata.data);
|
||||||
if let Ok(ata) = ata {
|
if let Ok(ata) = ata {
|
||||||
return Ok(ata.amount);
|
return Ok(ata.amount);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Ok(ata) = ctx.get_token_account(ata_address).await {
|
if let Ok(ata) = spl_token::state::Account::unpack(&ata.data) {
|
||||||
Ok(ata.amount)
|
Ok(ata.amount)
|
||||||
} else {
|
} else {
|
||||||
Ok(0u64)
|
Ok(0u64)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn set_balance(
|
fn set_balance(
|
||||||
ctx: &mut ProgramTestContext,
|
ctx: &mut LiteSVM,
|
||||||
owner: Pubkey,
|
owner: Pubkey,
|
||||||
mint: Pubkey,
|
mint: Pubkey,
|
||||||
amount: u64,
|
amount: u64,
|
||||||
is_2022: bool,
|
is_2022: bool,
|
||||||
) -> anyhow::Result<()> {
|
) -> anyhow::Result<()> {
|
||||||
let ata_address = get_associated_token_address(&owner, &mint);
|
let token_program_id = if is_2022 {
|
||||||
|
spl_token_2022::ID
|
||||||
|
} else {
|
||||||
|
spl_token::ID
|
||||||
|
};
|
||||||
|
|
||||||
|
let ata_address =
|
||||||
|
get_associated_token_address_with_program_id(&owner, &mint, &token_program_id);
|
||||||
|
let mut data = vec![0u8; 165];
|
||||||
|
|
||||||
if is_2022 {
|
if is_2022 {
|
||||||
let mut data = vec![0u8; 165];
|
// TODO: to properly setup extensions, this is not sufficient
|
||||||
let account = spl_token_2022::state::Account {
|
let account = spl_token_2022::state::Account {
|
||||||
mint,
|
mint,
|
||||||
owner,
|
owner,
|
||||||
|
@ -472,101 +510,83 @@ async fn set_balance(
|
||||||
close_authority: Default::default(),
|
close_authority: Default::default(),
|
||||||
};
|
};
|
||||||
account.pack_into_slice(data.as_mut_slice());
|
account.pack_into_slice(data.as_mut_slice());
|
||||||
|
} else {
|
||||||
ctx.set_account(
|
let account = spl_token::state::Account {
|
||||||
&ata_address,
|
mint,
|
||||||
&AccountSharedData::from(Account {
|
owner,
|
||||||
lamports: 1_000_000_000,
|
amount,
|
||||||
data: data,
|
delegate: Default::default(),
|
||||||
owner: spl_token_2022::ID,
|
state: AccountState::Initialized,
|
||||||
executable: false,
|
is_native: Default::default(),
|
||||||
rent_epoch: 0,
|
delegated_amount: 0,
|
||||||
}),
|
close_authority: Default::default(),
|
||||||
);
|
};
|
||||||
|
account.pack_into_slice(data.as_mut_slice());
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut data = vec![0u8; 165];
|
|
||||||
let account = spl_token::state::Account {
|
|
||||||
mint,
|
|
||||||
owner,
|
|
||||||
amount,
|
|
||||||
delegate: Default::default(),
|
|
||||||
state: AccountState::Initialized,
|
|
||||||
is_native: Default::default(),
|
|
||||||
delegated_amount: 0,
|
|
||||||
close_authority: Default::default(),
|
|
||||||
};
|
};
|
||||||
account.pack_into_slice(data.as_mut_slice());
|
|
||||||
|
|
||||||
ctx.set_account(
|
ctx.set_account(
|
||||||
&ata_address,
|
ata_address,
|
||||||
&AccountSharedData::from(Account {
|
Account {
|
||||||
lamports: 1_000_000_000,
|
lamports: 1_000_000_000,
|
||||||
data: data,
|
data: data,
|
||||||
owner: spl_token::ID,
|
owner: token_program_id,
|
||||||
executable: false,
|
executable: false,
|
||||||
rent_epoch: 0,
|
rent_epoch: u64::MAX,
|
||||||
}),
|
},
|
||||||
);
|
)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_wallet(ctx: &mut ProgramTestContext, address: Pubkey) {
|
fn create_wallet(ctx: &mut LiteSVM, address: Pubkey) {
|
||||||
ctx.set_account(
|
let _ = ctx.airdrop(&address, 1_000_000_000);
|
||||||
&address,
|
|
||||||
&AccountSharedData::from(Account {
|
|
||||||
lamports: 1_000_000_000,
|
|
||||||
data: vec![],
|
|
||||||
owner: address,
|
|
||||||
executable: false,
|
|
||||||
rent_epoch: 0,
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn setup_test_chain(
|
pub fn find_file(filename: &str) -> Option<PathBuf> {
|
||||||
|
for dir in default_shared_object_dirs() {
|
||||||
|
let candidate = dir.join(filename);
|
||||||
|
if candidate.exists() {
|
||||||
|
return Some(candidate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
fn default_shared_object_dirs() -> Vec<PathBuf> {
|
||||||
|
let mut search_path = vec![];
|
||||||
|
if let Ok(bpf_out_dir) = std::env::var("BPF_OUT_DIR") {
|
||||||
|
search_path.push(PathBuf::from(bpf_out_dir));
|
||||||
|
} else if let Ok(bpf_out_dir) = std::env::var("SBF_OUT_DIR") {
|
||||||
|
search_path.push(PathBuf::from(bpf_out_dir));
|
||||||
|
}
|
||||||
|
search_path.push(PathBuf::from("tests/fixtures"));
|
||||||
|
if let Ok(dir) = std::env::current_dir() {
|
||||||
|
search_path.push(dir);
|
||||||
|
}
|
||||||
|
log::trace!("SBF .so search path: {:?}", search_path);
|
||||||
|
search_path
|
||||||
|
}
|
||||||
|
|
||||||
|
fn setup_test_chain(
|
||||||
programs: &Vec<Pubkey>,
|
programs: &Vec<Pubkey>,
|
||||||
clock: &Clock,
|
clock: &Clock,
|
||||||
dump: &ExecutionDump,
|
dump: &ExecutionDump,
|
||||||
) -> ProgramTestContext {
|
instruction: &Instruction,
|
||||||
// We need to intercept logs to capture program log output
|
) -> anyhow::Result<LiteSVM> {
|
||||||
let log_filter = "solana_rbpf=trace,\
|
let mut program_test = LiteSVM::new();
|
||||||
solana_runtime::message_processor=debug,\
|
program_test.set_sysvar(clock);
|
||||||
solana_runtime::system_instruction_processor=trace,\
|
let mut accounts_list = programs.clone();
|
||||||
solana_program_test=info,\
|
accounts_list.extend(instruction.accounts.iter().map(|x| x.pubkey));
|
||||||
solana_metrics::metrics=warn,\
|
|
||||||
tarpc=error,\
|
|
||||||
info";
|
|
||||||
let env_logger =
|
|
||||||
env_logger::Builder::from_env(env_logger::Env::new().default_filter_or(log_filter))
|
|
||||||
.format_timestamp_nanos()
|
|
||||||
.build();
|
|
||||||
let _ = log::set_boxed_logger(Box::new(env_logger));
|
|
||||||
|
|
||||||
let mut program_test = ProgramTest::default();
|
initialize_accounts(&mut program_test, dump, &accounts_list)?;
|
||||||
|
let path = find_file(format!("autobahn_executor.so").as_str()).unwrap();
|
||||||
initialize_accounts(&mut program_test, dump).await.unwrap();
|
log::debug!("Adding program: {:?} at {path:?}", autobahn_executor::ID);
|
||||||
|
program_test.add_program_from_file(autobahn_executor::ID, path)?;
|
||||||
program_test.prefer_bpf(true);
|
|
||||||
for &key in programs {
|
|
||||||
program_test.add_program(key.to_string().as_str(), key, None);
|
|
||||||
}
|
|
||||||
program_test.add_program("autobahn_executor", autobahn_executor::ID, None);
|
|
||||||
|
|
||||||
// TODO: make this dynamic based on routes
|
// TODO: make this dynamic based on routes
|
||||||
program_test.set_compute_max_units(1_400_000);
|
let mut cb = solana_program_runtime::compute_budget::ComputeBudget::default();
|
||||||
|
cb.compute_unit_limit = 1_400_000;
|
||||||
|
program_test.set_compute_budget(cb);
|
||||||
|
|
||||||
let mut program_test_context = program_test.start_with_context().await;
|
Ok(program_test)
|
||||||
|
|
||||||
// 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
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue