diff --git a/crates/client/Cargo.toml b/crates/client/Cargo.toml index 0d1fef56..fdc7c2b4 100644 --- a/crates/client/Cargo.toml +++ b/crates/client/Cargo.toml @@ -14,6 +14,7 @@ fuzzing = [ "arbitrary/derive", "quinn-proto/arbitrary", ] +poctesting = ["dep:rstest", "dep:serial_test","dep:trdelnik-test"] [build-dependencies] anyhow = { version = "1.0.45", features = ["std"], default-features = false } @@ -22,16 +23,15 @@ anyhow = { version = "1.0.45", features = ["std"], default-features = false } pretty_assertions = "1.1.0" [dependencies] -trdelnik-test = { workspace = true } solana-sdk = { workspace = true } solana-cli-output = { workspace = true } solana-transaction-status = { workspace = true } solana-account-decoder = { workspace = true } anchor-client = { workspace = true } +anchor-lang = {version = "0.28.0"} spl-token = { workspace = true } spl-associated-token-account = { workspace = true } tokio = { workspace = true } -rand = { workspace = true } serde_json = { workspace = true } serde = { workspace = true } bincode = { workspace = true } @@ -40,7 +40,6 @@ futures = { workspace = true } fehler = { workspace = true } thiserror = { workspace = true } ed25519-dalek = { workspace = true } -serial_test = { workspace = true } anyhow = { workspace = true } cargo_metadata = { workspace = true } syn = { workspace = true } @@ -48,12 +47,14 @@ quote = { workspace = true } heck = { workspace = true } toml = { workspace = true } log = { workspace = true } -rstest = { workspace = true } -lazy_static = { workspace = true } + +trdelnik-test = { workspace = true,optional = true } +serial_test = { version = "2.0.0", optional = true } +rstest = { version = "0.18.2", optional = true } honggfuzz = { version = "0.5.55", optional = true } arbitrary = { version = "1.3.0", optional = true } solana-program-test = { version = "1.16.9", optional = true } quinn-proto = { version = "0.9.4", optional = true } shellexpand = { workspace = true } pathdiff = "0.2.1" -indicatif="0.17.7" +indicatif = "0.17.7" diff --git a/crates/client/src/client.rs b/crates/client/src/client.rs index 0ddef422..eb7a562a 100644 --- a/crates/client/src/client.rs +++ b/crates/client/src/client.rs @@ -1,63 +1,47 @@ -use crate::{config::Config, Reader, TempClone}; -use anchor_client::{ - anchor_lang::{ - prelude::System, solana_program::program_pack::Pack, AccountDeserialize, Id, - InstructionData, ToAccountMetas, - }, - solana_client::rpc_config::RpcTransactionConfig, - solana_sdk::{ - account::Account, - bpf_loader, - commitment_config::CommitmentConfig, - instruction::Instruction, - loader_instruction, - pubkey::Pubkey, - signature::read_keypair_file, - signer::{keypair::Keypair, Signer}, - system_instruction, - transaction::Transaction, - }, - Client as AnchorClient, ClientError as Error, Cluster, Program, -}; - -use borsh::BorshDeserialize; +use crate::{constants::*, Config, Reader, TempClone}; + +use anchor_client; +use anchor_client::ClientError as Error; +// TODO maybe can deleted +use borsh; use fehler::{throw, throws}; -use futures::stream::{self, StreamExt}; -use log::debug; -use serde::de::DeserializeOwned; -use solana_account_decoder::parse_token::UiTokenAmount; -use solana_cli_output::display::println_transaction; -use solana_transaction_status::{EncodedConfirmedTransactionWithStatusMeta, UiTransactionEncoding}; -use spl_associated_token_account::get_associated_token_address; -use spl_associated_token_account::instruction::create_associated_token_account; -use std::{mem, rc::Rc}; -use std::{thread::sleep, time::Duration}; +use solana_sdk::program_pack::Pack; +use solana_sdk::signer::Signer; +// TODO maybe can deleted +use futures::{self, StreamExt}; +use log; +// TODO maybe can deleted +use serde; +// TODO maybe can deleted +use solana_account_decoder; +// TODO maybe can deleted +use solana_cli_output; +use solana_transaction_status; +use spl_associated_token_account; // @TODO: Make compatible with the latest Anchor deps. // https://github.com/project-serum/anchor/pull/1307#issuecomment-1022592683 -const RETRY_LOCALNET_EVERY_MILLIS: u64 = 500; -const DEFAULT_KEYPAIR_PATH: &str = "~/.config/solana/id.json"; - -type Payer = Rc; +type Payer = std::rc::Rc; /// `Client` allows you to send typed RPC requests to a Solana cluster. pub struct Client { - payer: Keypair, - anchor_client: AnchorClient, + payer: solana_sdk::signer::keypair::Keypair, + anchor_client: anchor_client::Client, } /// Implement Default trait for Client, which reads keypair from default path for `solana-keygen new` impl Default for Client { fn default() -> Self { - let payer = read_keypair_file(&*shellexpand::tilde(DEFAULT_KEYPAIR_PATH)) - .unwrap_or_else(|_| panic!("Default keypair {DEFAULT_KEYPAIR_PATH} not found.")); + let payer = + solana_sdk::signature::read_keypair_file(&*shellexpand::tilde(DEFAULT_KEYPAIR_PATH)) + .unwrap_or_else(|_| panic!("Default keypair {DEFAULT_KEYPAIR_PATH} not found.")); Self { payer: payer.clone(), - anchor_client: AnchorClient::new_with_options( - Cluster::Localnet, - Rc::new(payer), - CommitmentConfig::confirmed(), + anchor_client: anchor_client::Client::new_with_options( + anchor_client::Cluster::Localnet, + std::rc::Rc::new(payer), + solana_sdk::commitment_config::CommitmentConfig::confirmed(), ), } } @@ -65,30 +49,33 @@ impl Default for Client { impl Client { /// Creates a new `Client` instance. - pub fn new(payer: Keypair) -> Self { + pub fn new(payer: solana_sdk::signer::keypair::Keypair) -> Self { Self { payer: payer.clone(), - anchor_client: AnchorClient::new_with_options( - Cluster::Localnet, - Rc::new(payer), - CommitmentConfig::confirmed(), + anchor_client: anchor_client::Client::new_with_options( + anchor_client::Cluster::Localnet, + std::rc::Rc::new(payer), + solana_sdk::commitment_config::CommitmentConfig::confirmed(), ), } } /// Gets client's payer. - pub fn payer(&self) -> &Keypair { + pub fn payer(&self) -> &solana_sdk::signer::keypair::Keypair { &self.payer } /// Gets the internal Anchor client to call Anchor client's methods directly. - pub fn anchor_client(&self) -> &AnchorClient { + pub fn anchor_client(&self) -> &anchor_client::Client { &self.anchor_client } /// Creates [Program] instance to communicate with the selected program. - pub fn program(&self, program_id: Pubkey) -> Program { - self.anchor_client.program(program_id).unwrap() + pub fn program( + &self, + program_id: &solana_sdk::pubkey::Pubkey, + ) -> anchor_client::Program { + self.anchor_client.program(*program_id).unwrap() } /// Finds out if the Solana localnet is running. @@ -100,7 +87,7 @@ impl Client { let rpc_client = self .anchor_client - .program(System::id()) + .program(solana_sdk::system_program::ID) .unwrap() .async_rpc(); @@ -113,7 +100,9 @@ impl Client { return true; } if retry { - sleep(Duration::from_millis(RETRY_LOCALNET_EVERY_MILLIS)); + std::thread::sleep(std::time::Duration::from_millis( + RETRY_LOCALNET_EVERY_MILLIS, + )); } } false @@ -128,12 +117,15 @@ impl Client { /// - the Solana cluster is not running. /// - deserialization failed. #[throws] - pub async fn account_data(&self, account: Pubkey) -> T + pub async fn account_data(&self, account: &solana_sdk::pubkey::Pubkey) -> T where - T: AccountDeserialize + Send + 'static, + T: anchor_lang::AccountDeserialize + Send + 'static, { - let program = self.anchor_client.program(System::id()).unwrap(); - program.account::(account).await.unwrap() + let program = self + .anchor_client + .program(solana_sdk::system_program::ID) + .unwrap(); + program.account::(*account).await.unwrap() } /// Gets deserialized data from the chosen account serialized with Bincode @@ -145,9 +137,9 @@ impl Client { /// - the Solana cluster is not running. /// - deserialization failed. #[throws] - pub async fn account_data_bincode(&self, account: Pubkey) -> T + pub async fn account_data_bincode(&self, account: &solana_sdk::pubkey::Pubkey) -> T where - T: DeserializeOwned + Send + 'static, + T: serde::de::DeserializeOwned + Send + 'static, { let account = self .get_account(account) @@ -167,9 +159,9 @@ impl Client { /// - the Solana cluster is not running. /// - deserialization failed. #[throws] - pub async fn account_data_borsh(&self, account: Pubkey) -> T + pub async fn account_data_borsh(&self, account: &solana_sdk::pubkey::Pubkey) -> T where - T: BorshDeserialize + Send + 'static, + T: borsh::BorshDeserialize + Send + 'static, { let account = self .get_account(account) @@ -186,8 +178,14 @@ impl Client { /// /// It fails when the Solana cluster is not running. #[throws] - pub async fn get_account(&self, account: Pubkey) -> Option { - let rpc_client = self.anchor_client.program(System::id())?.async_rpc(); + pub async fn get_account( + &self, + account: &solana_sdk::pubkey::Pubkey, + ) -> Option { + let rpc_client = self + .anchor_client + .program(solana_sdk::system_program::ID)? + .async_rpc(); rpc_client .get_account_with_commitment(&account, rpc_client.commitment()) .await @@ -207,7 +205,7 @@ impl Client { /// state: Pubkey, /// user: Pubkey, /// system_program: Pubkey, - /// signers: impl IntoIterator + Send + 'static, + /// signers: impl IntoIterator + Send, /// ) -> Result { /// Ok(client /// .send_instruction( @@ -226,26 +224,29 @@ impl Client { #[throws] pub async fn send_instruction( &self, - program: Pubkey, - instruction: impl InstructionData + Send + 'static, - accounts: impl ToAccountMetas + Send + 'static, - signers: impl IntoIterator + Send + 'static, - ) -> EncodedConfirmedTransactionWithStatusMeta { + program: solana_sdk::pubkey::Pubkey, + instruction: impl anchor_lang::InstructionData + Send, + accounts: impl anchor_lang::ToAccountMetas + Send, + signers: impl IntoIterator + Send, + ) -> solana_transaction_status::EncodedConfirmedTransactionWithStatusMeta { let program = self.anchor_client.program(program).unwrap(); let mut request = program.request().args(instruction).accounts(accounts); let signers = signers.into_iter().collect::>(); - for signer in &signers { + for signer in signers { request = request.signer(signer); } let signature = request.send().await.unwrap(); - let rpc_client = self.anchor_client.program(System::id())?.async_rpc(); + let rpc_client = self + .anchor_client + .program(solana_sdk::system_program::ID)? + .async_rpc(); rpc_client .get_transaction_with_config( &signature, - RpcTransactionConfig { - encoding: Some(UiTransactionEncoding::Binary), - commitment: Some(CommitmentConfig::confirmed()), + anchor_client::solana_client::rpc_config::RpcTransactionConfig { + encoding: Some(solana_transaction_status::UiTransactionEncoding::Binary), + commitment: Some(solana_sdk::commitment_config::CommitmentConfig::confirmed()), max_supported_transaction_version: None, }, ) @@ -282,18 +283,18 @@ impl Client { #[throws] pub async fn send_transaction( &self, - instructions: &[Instruction], - signers: impl IntoIterator + Send, - ) -> EncodedConfirmedTransactionWithStatusMeta { + instructions: &[solana_sdk::instruction::Instruction], + signers: impl IntoIterator + Send, + ) -> solana_transaction_status::EncodedConfirmedTransactionWithStatusMeta { let rpc_client = self .anchor_client - .program(System::id()) + .program(solana_sdk::system_program::ID) .unwrap() .async_rpc(); let mut signers = signers.into_iter().collect::>(); signers.push(self.payer()); - let tx = &Transaction::new_signed_with_payer( + let tx = &solana_sdk::transaction::Transaction::new_signed_with_payer( instructions, Some(&self.payer.pubkey()), &signers, @@ -304,9 +305,9 @@ impl Client { let transaction = rpc_client .get_transaction_with_config( &signature, - RpcTransactionConfig { - encoding: Some(UiTransactionEncoding::Binary), - commitment: Some(CommitmentConfig::confirmed()), + anchor_client::solana_client::rpc_config::RpcTransactionConfig { + encoding: Some(solana_transaction_status::UiTransactionEncoding::Binary), + commitment: Some(solana_sdk::commitment_config::CommitmentConfig::confirmed()), max_supported_transaction_version: None, }, ) @@ -318,10 +319,10 @@ impl Client { /// Airdrops lamports to the chosen account. #[throws] - pub async fn airdrop(&self, address: Pubkey, lamports: u64) { + pub async fn airdrop(&self, address: &solana_sdk::pubkey::Pubkey, lamports: u64) { let rpc_client = self .anchor_client - .program(System::id()) + .program(solana_sdk::system_program::ID) .unwrap() .async_rpc(); @@ -333,11 +334,11 @@ impl Client { let (airdrop_result, error) = loop { match rpc_client.get_signature_status(&signature).await.unwrap() { Some(Ok(_)) => { - debug!("{} lamports airdropped", lamports); + log::debug!("{} lamports airdropped", lamports); break (true, None); } Some(Err(transaction_error)) => break (false, Some(transaction_error)), - None => sleep(Duration::from_millis(500)), + None => std::thread::sleep(std::time::Duration::from_millis(500)), } }; if !airdrop_result { @@ -347,16 +348,25 @@ impl Client { /// Get balance of an account #[throws] - pub async fn get_balance(&mut self, address: &Pubkey) -> u64 { - let rpc_client = self.anchor_client.program(System::id())?.async_rpc(); + pub async fn get_balance(&mut self, address: &solana_sdk::pubkey::Pubkey) -> u64 { + let rpc_client = self + .anchor_client + .program(solana_sdk::system_program::ID)? + .async_rpc(); rpc_client.get_balance(address).await? } /// Get token balance of an token account #[throws] - pub async fn get_token_balance(&mut self, address: Pubkey) -> UiTokenAmount { - let rpc_client = self.anchor_client.program(System::id())?.async_rpc(); - rpc_client.get_token_account_balance(&address).await? + pub async fn get_token_balance( + &mut self, + address: &solana_sdk::pubkey::Pubkey, + ) -> solana_account_decoder::parse_token::UiTokenAmount { + let rpc_client = self + .anchor_client + .program(solana_sdk::system_program::ID)? + .async_rpc(); + rpc_client.get_token_account_balance(address).await? } /// Deploys a program based on it's name. @@ -392,8 +402,12 @@ impl Client { /// client.deploy_program(program_keypair(1), "turnstile"); /// ``` #[throws] - pub async fn deploy_by_name(&self, program_keypair: &Keypair, program_name: &str) { - debug!("reading program data"); + pub async fn deploy_by_name( + &self, + program_keypair: &solana_sdk::signer::keypair::Keypair, + program_name: &str, + ) { + log::debug!("reading program data"); let reader = Reader::new(); @@ -402,17 +416,17 @@ impl Client { .await .expect("reading program data failed"); - debug!("airdropping the minimum balance required to deploy the program"); + log::debug!("airdropping the minimum balance required to deploy the program"); // TODO: This will fail on devnet where airdrops are limited to 1 SOL - self.airdrop(self.payer().pubkey(), 5_000_000_000) + self.airdrop(&self.payer().pubkey(), 5_000_000_000) .await .expect("airdropping for deployment failed"); - debug!("deploying program"); + log::debug!("deploying program"); - self.deploy(program_keypair.clone(), mem::take(&mut program_data)) + self.deploy(program_keypair, std::mem::take(&mut program_data)) .await?; // this will slow down the process because if we call program instruction right after deploy, @@ -420,12 +434,12 @@ impl Client { let deploy_done = loop { match self.anchor_client.program(program_keypair.pubkey()) { Ok(_) => { - debug!("program deployed succefully"); - sleep(Duration::from_millis(1000)); + log::debug!("program deployed succefully"); + std::thread::sleep(std::time::Duration::from_millis(1000)); break true; } Err(_) => { - sleep(Duration::from_millis(500)); + std::thread::sleep(std::time::Duration::from_millis(500)); } } }; @@ -440,98 +454,111 @@ impl Client { /// Deploys the program. #[throws] - async fn deploy(&self, program_keypair: Keypair, program_data: Vec) { + async fn deploy( + &self, + program_keypair: &solana_sdk::signer::keypair::Keypair, + program_data: Vec, + ) { const PROGRAM_DATA_CHUNK_SIZE: usize = 900; let program_data_len = program_data.len(); let rpc_client = self .anchor_client - .program(System::id()) + .program(solana_sdk::system_program::ID) .unwrap() .async_rpc(); - let system_program = self.anchor_client.program(System::id()).unwrap(); + let system_program = self + .anchor_client + .program(solana_sdk::system_program::ID) + .unwrap(); - debug!("program_data_len: {}", program_data_len); + log::debug!("program_data_len: {}", program_data_len); - debug!("create program account"); + log::debug!("create program account"); let min_balance_for_rent_exemption = rpc_client .get_minimum_balance_for_rent_exemption(program_data_len) .await .unwrap(); - let create_account_ix: Instruction = system_instruction::create_account( - &self.payer.pubkey(), - &program_keypair.pubkey(), - min_balance_for_rent_exemption, - program_data_len as u64, - &bpf_loader::id(), - ); + let create_account_ix: solana_sdk::instruction::Instruction = + solana_sdk::system_instruction::create_account( + &self.payer.pubkey(), + &program_keypair.pubkey(), + min_balance_for_rent_exemption, + program_data_len as u64, + &solana_sdk::bpf_loader::id(), + ); system_program .request() .instruction(create_account_ix) - .signer(&program_keypair) + .signer(program_keypair) .send() .await .unwrap(); - debug!("write program data"); + log::debug!("write program data"); let mut offset = 0usize; let mut futures_vec = Vec::new(); for chunk in program_data.chunks(PROGRAM_DATA_CHUNK_SIZE) { - let loader_write_ix = loader_instruction::write( + let loader_write_ix = solana_sdk::loader_instruction::write( &program_keypair.pubkey(), - &bpf_loader::id(), + &solana_sdk::bpf_loader::id(), offset as u32, chunk.to_vec(), ); futures_vec.push(async { - let system_program = self.anchor_client.program(System::id()).unwrap(); + let system_program = self + .anchor_client + .program(solana_sdk::system_program::ID) + .unwrap(); system_program .request() .instruction(loader_write_ix) - .signer(&program_keypair) + .signer(program_keypair) .send() .await .unwrap(); }); offset += chunk.len(); } - stream::iter(futures_vec) + futures::stream::iter(futures_vec) .buffer_unordered(500) .collect::>() .await; - debug!("finalize program"); + log::debug!("finalize program"); - let loader_finalize_ix = - loader_instruction::finalize(&program_keypair.pubkey(), &bpf_loader::id()); + let loader_finalize_ix = solana_sdk::loader_instruction::finalize( + &program_keypair.pubkey(), + &solana_sdk::bpf_loader::id(), + ); system_program .request() .instruction(loader_finalize_ix) - .signer(&program_keypair) + .signer(program_keypair) .send() .await .unwrap(); - debug!("program deployed"); + log::debug!("program deployed"); } /// Creates accounts. #[throws] pub async fn create_account( &self, - keypair: &Keypair, + keypair: &solana_sdk::signer::keypair::Keypair, lamports: u64, space: u64, - owner: &Pubkey, - ) -> EncodedConfirmedTransactionWithStatusMeta { + owner: &solana_sdk::pubkey::Pubkey, + ) -> solana_transaction_status::EncodedConfirmedTransactionWithStatusMeta { self.send_transaction( - &[system_instruction::create_account( + &[solana_sdk::system_instruction::create_account( &self.payer().pubkey(), &keypair.pubkey(), lamports, @@ -547,13 +574,16 @@ impl Client { #[throws] pub async fn create_account_rent_exempt( &mut self, - keypair: &Keypair, + keypair: &solana_sdk::signer::keypair::Keypair, space: u64, - owner: &Pubkey, - ) -> EncodedConfirmedTransactionWithStatusMeta { - let rpc_client = self.anchor_client.program(System::id())?.async_rpc(); + owner: &solana_sdk::pubkey::Pubkey, + ) -> solana_transaction_status::EncodedConfirmedTransactionWithStatusMeta { + let rpc_client = self + .anchor_client + .program(solana_sdk::system_program::ID)? + .async_rpc(); self.send_transaction( - &[system_instruction::create_account( + &[solana_sdk::system_instruction::create_account( &self.payer().pubkey(), &keypair.pubkey(), rpc_client @@ -571,15 +601,18 @@ impl Client { #[throws] pub async fn create_token_mint( &self, - mint: &Keypair, - authority: Pubkey, - freeze_authority: Option, + mint: &solana_sdk::signer::keypair::Keypair, + authority: &solana_sdk::pubkey::Pubkey, + freeze_authority: Option<&solana_sdk::pubkey::Pubkey>, decimals: u8, - ) -> EncodedConfirmedTransactionWithStatusMeta { - let rpc_client = self.anchor_client.program(System::id())?.async_rpc(); + ) -> solana_transaction_status::EncodedConfirmedTransactionWithStatusMeta { + let rpc_client = self + .anchor_client + .program(solana_sdk::system_program::ID)? + .async_rpc(); self.send_transaction( &[ - system_instruction::create_account( + solana_sdk::system_instruction::create_account( &self.payer().pubkey(), &mint.pubkey(), rpc_client @@ -591,8 +624,8 @@ impl Client { spl_token::instruction::initialize_mint( &spl_token::ID, &mint.pubkey(), - &authority, - freeze_authority.as_ref(), + authority, + freeze_authority, decimals, ) .unwrap(), @@ -606,16 +639,16 @@ impl Client { #[throws] pub async fn mint_tokens( &self, - mint: Pubkey, - authority: &Keypair, - account: Pubkey, + mint: &solana_sdk::pubkey::Pubkey, + authority: &solana_sdk::signer::keypair::Keypair, + account: &solana_sdk::pubkey::Pubkey, amount: u64, - ) -> EncodedConfirmedTransactionWithStatusMeta { + ) -> solana_transaction_status::EncodedConfirmedTransactionWithStatusMeta { self.send_transaction( &[spl_token::instruction::mint_to( &spl_token::ID, - &mint, - &account, + mint, + account, &authority.pubkey(), &[], amount, @@ -631,14 +664,17 @@ impl Client { #[throws] pub async fn create_token_account( &self, - account: &Keypair, - mint: &Pubkey, - owner: &Pubkey, - ) -> EncodedConfirmedTransactionWithStatusMeta { - let rpc_client = self.anchor_client.program(System::id())?.async_rpc(); + account: &solana_sdk::signer::keypair::Keypair, + mint: &solana_sdk::pubkey::Pubkey, + owner: &solana_sdk::pubkey::Pubkey, + ) -> solana_transaction_status::EncodedConfirmedTransactionWithStatusMeta { + let rpc_client = self + .anchor_client + .program(solana_sdk::system_program::ID)? + .async_rpc(); self.send_transaction( &[ - system_instruction::create_account( + solana_sdk::system_instruction::create_account( &self.payer().pubkey(), &account.pubkey(), rpc_client @@ -662,36 +698,49 @@ impl Client { /// Executes a transaction constructing the associated token account of the specified mint belonging to the owner. This will fail if the account already exists. #[throws] - pub async fn create_associated_token_account(&self, owner: &Keypair, mint: Pubkey) -> Pubkey { + pub async fn create_associated_token_account( + &self, + owner: &solana_sdk::signer::keypair::Keypair, + mint: &solana_sdk::pubkey::Pubkey, + ) -> solana_sdk::pubkey::Pubkey { self.send_transaction( - &[create_associated_token_account( - &self.payer().pubkey(), - &owner.pubkey(), - &mint, - &spl_token::ID, - )], + &[ + spl_associated_token_account::instruction::create_associated_token_account( + &self.payer().pubkey(), + &owner.pubkey(), + &mint, + &spl_token::ID, + ), + ], &[], ) .await?; - get_associated_token_address(&owner.pubkey(), &mint) + spl_associated_token_account::get_associated_token_address(&owner.pubkey(), &mint) } /// Executes a transaction creating and filling the given account with the given data. /// The account is required to be empty and will be owned by bpf_loader afterwards. #[throws] - pub async fn create_account_with_data(&self, account: &Keypair, data: Vec) { + pub async fn create_account_with_data( + &self, + account: &solana_sdk::signer::keypair::Keypair, + data: Vec, + ) { const DATA_CHUNK_SIZE: usize = 900; - let rpc_client = self.anchor_client.program(System::id())?.async_rpc(); + let rpc_client = self + .anchor_client + .program(solana_sdk::system_program::ID)? + .async_rpc(); self.send_transaction( - &[system_instruction::create_account( + &[solana_sdk::system_instruction::create_account( &self.payer().pubkey(), &account.pubkey(), rpc_client .get_minimum_balance_for_rent_exemption(data.len()) .await?, data.len() as u64, - &bpf_loader::id(), + &solana_sdk::bpf_loader::id(), )], [account], ) @@ -699,11 +748,11 @@ impl Client { let mut offset = 0usize; for chunk in data.chunks(DATA_CHUNK_SIZE) { - debug!("writing bytes {} to {}", offset, offset + chunk.len()); + log::debug!("writing bytes {} to {}", offset, offset + chunk.len()); self.send_transaction( - &[loader_instruction::write( + &[solana_sdk::loader_instruction::write( &account.pubkey(), - &bpf_loader::id(), + &solana_sdk::bpf_loader::id(), offset as u32, chunk.to_vec(), )], @@ -726,13 +775,15 @@ pub trait PrintableTransaction { } } -impl PrintableTransaction for EncodedConfirmedTransactionWithStatusMeta { +impl PrintableTransaction for solana_transaction_status::EncodedConfirmedTransactionWithStatusMeta { fn print_named(&self, name: &str) { let tx = self.transaction.transaction.decode().unwrap(); - debug!("EXECUTE {} (slot {})", name, self.slot); + log::debug!("EXECUTE {} (slot {})", name, self.slot); match self.transaction.meta.clone() { - Some(meta) => println_transaction(&tx, Some(&meta), " ", None, None), - _ => println_transaction(&tx, None, " ", None, None), + Some(meta) => { + solana_cli_output::display::println_transaction(&tx, Some(&meta), " ", None, None) + } + _ => solana_cli_output::display::println_transaction(&tx, None, " ", None, None), } } } diff --git a/crates/client/src/commander.rs b/crates/client/src/commander.rs index 0116b48e..12e4c4ef 100644 --- a/crates/client/src/commander.rs +++ b/crates/client/src/commander.rs @@ -1,30 +1,24 @@ -use crate::config::Config; -use crate::{ - idl::{self, Idl}, - Client, -}; -use cargo_metadata::{MetadataCommand, Package}; +use std::io::Write; +use std::os::unix::process::CommandExt; + +use crate::Client; +use crate::Config; +use crate::{Idl, IdlError}; use fehler::{throw, throws}; -use log::debug; -use solana_sdk::signer::keypair::Keypair; -use std::{io, os::unix::process::CommandExt, path::Path, process::Stdio, string::FromUtf8Error}; +use log; use thiserror::Error; -use tokio::{ - fs, - io::AsyncWriteExt, - process::{Child, Command}, - signal, -}; +// TODO maybe unused +use tokio; // ----- use crate::constants::*; -use indicatif::{ProgressBar, ProgressStyle}; +use indicatif; #[derive(Error, Debug)] pub enum Error { #[error("{0:?}")] - Io(#[from] io::Error), + Io(#[from] std::io::Error), #[error("{0:?}")] - Utf8(#[from] FromUtf8Error), + Utf8(#[from] std::string::FromUtf8Error), #[error("localnet is not running")] LocalnetIsNotRunning, #[error("localnet is still running")] @@ -36,7 +30,7 @@ pub enum Error { #[error("read program code failed: '{0}'")] ReadProgramCodeFailed(String), #[error("{0:?}")] - Idl(#[from] idl::Error), + Idl(#[from] IdlError), #[error("{0:?}")] TomlDeserialize(#[from] toml::de::Error), #[error("parsing Cargo.toml dependencies failed")] @@ -53,7 +47,7 @@ pub enum Error { /// Localnet (the validator process) handle. pub struct LocalnetHandle { - solana_test_validator_process: Child, + solana_test_validator_process: tokio::process::Child, } impl LocalnetHandle { @@ -69,10 +63,13 @@ impl LocalnetHandle { #[throws] pub async fn stop(mut self) { self.solana_test_validator_process.kill().await?; - if Client::new(Keypair::new()).is_localnet_running(false).await { + if Client::new(solana_sdk::signature::Keypair::new()) + .is_localnet_running(false) + .await + { throw!(Error::LocalnetIsStillRunning); } - debug!("localnet stopped"); + log::debug!("localnet stopped"); } /// Stops the localnet and removes the ledger. @@ -88,8 +85,8 @@ impl LocalnetHandle { #[throws] pub async fn stop_and_remove_ledger(self) { self.stop().await?; - fs::remove_dir_all("test-ledger").await?; - debug!("ledger removed"); + tokio::fs::remove_dir_all("test-ledger").await?; + log::debug!("ledger removed"); } } @@ -103,8 +100,8 @@ impl Commander { pub async fn build_programs(arch: &str) { let exit = std::process::Command::new("cargo") .arg(arch) - .stdout(Stdio::inherit()) - .stderr(Stdio::inherit()) + .stdout(std::process::Stdio::inherit()) + .stderr(std::process::Stdio::inherit()) .output() .unwrap(); @@ -113,8 +110,8 @@ impl Commander { } } /// Returns an [Iterator] of program [Package]s read from `Cargo.toml` files. - pub fn program_packages() -> impl Iterator { - let cargo_toml_data = MetadataCommand::new() + pub fn program_packages() -> impl Iterator { + let cargo_toml_data = cargo_metadata::MetadataCommand::new() .no_deps() .exec() .expect("Cargo.toml reading failed"); @@ -131,8 +128,8 @@ impl Commander { }) } #[throws] - pub async fn collect_packages() -> Vec { - let packages: Vec = Commander::program_packages().collect(); + pub async fn collect_packages() -> Vec { + let packages: Vec = Commander::program_packages().collect(); if packages.is_empty() { throw!(Error::NoProgramsFound) } else { @@ -141,7 +138,7 @@ impl Commander { } #[throws] - pub async fn obtain_program_idl(packages: &[Package]) -> Idl { + pub async fn obtain_program_idl(packages: &[cargo_metadata::Package]) -> Idl { let connections = std::sync::Arc::new(std::sync::Mutex::new(Vec::new())); for package in packages.iter() { let is_running = std::sync::Arc::new(std::sync::atomic::AtomicBool::new(true)); @@ -162,7 +159,7 @@ impl Commander { if output.status.success() { let code = String::from_utf8(output.stdout).unwrap(); - let idl_program = idl::parse_to_idl_program(&name, &code).unwrap(); + let idl_program = Idl::parse_to_idl_program(&name, &code).unwrap(); let mut vec = conn.lock().unwrap(); vec.push(idl_program); is_running_for_thread.store(false, std::sync::atomic::Ordering::SeqCst); @@ -174,9 +171,9 @@ impl Commander { } }); - let progress_bar = ProgressBar::new_spinner(); + let progress_bar = indicatif::ProgressBar::new_spinner(); progress_bar.set_style( - ProgressStyle::default_spinner() + indicatif::ProgressStyle::default_spinner() .template("{spinner} {wide_msg}") .unwrap(), ); @@ -205,16 +202,20 @@ impl Commander { #[throws] pub async fn clean_anchor_target() { // INFO perform anchor clean so no keys will be removed - Command::new("anchor").arg("clean").spawn()?.wait().await?; + tokio::process::Command::new("anchor") + .arg("clean") + .spawn()? + .wait() + .await?; } #[throws] - pub async fn clean_hfuzz_target(root: &Path) { + pub async fn clean_hfuzz_target(root: &std::path::Path) { // INFO hfuzz target can be of course located somewhere else // but as we leave it within the root, we also expect it within the root let hfuzz_target_path = root.join(HFUZZ_TARGET); if hfuzz_target_path.exists() { - fs::remove_dir_all(hfuzz_target_path).await?; + tokio::fs::remove_dir_all(hfuzz_target_path).await?; } else { println!("skipping {} directory: not found", HFUZZ_TARGET) } @@ -224,48 +225,57 @@ impl Commander { /// The goal of this method is to find all `use` statements defined by the user in the `.program_client` /// crate. It solves the problem with regenerating the program client and removing imports defined by /// the user. - // TODO is this relevant when program_client should not be changed by user ? #[throws] pub async fn parse_program_client_imports() -> Vec { - let output = std::process::Command::new("cargo") - .arg("+nightly") - .arg("rustc") - .args(["--package", "program_client"]) - .arg("--profile=check") - .arg("--") - .arg("-Zunpretty=expanded") - .output() - .unwrap(); + // TODO + // We have two options here, write that this file is fully generated so + // aditional changes will be rewritten by trdelnik build, or we can + // actually parse the use statements + // let output = std::process::Command::new("cargo") + // .arg("+nightly") + // .arg("rustc") + // .args(["--package", "program_client"]) + // .arg("--profile=check") + // .arg("--") + // .arg("-Zunpretty=expanded") + // .output() + // .unwrap(); + + // if output.status.success() { + // let code = String::from_utf8(output.stdout)?; + // let mut use_modules: Vec = vec![]; + // for item in syn::parse_file(code.as_str()).unwrap().items.into_iter() { + // if let syn::Item::Mod(module) = item { + // let modules = module + // .content + // .ok_or("account mod: empty content") + // .unwrap() + // .1 + // .into_iter(); + // for module in modules { + // if let syn::Item::Use(u) = module { + // use_modules.push(u); + // } + // } + // } + // } + // if use_modules.is_empty() { + // use_modules.push(syn::parse_quote! { use trdelnik_client::program_client::*; }) + // } + // use_modules + // } else { + // let mut use_modules: Vec = vec![]; + // if use_modules.is_empty() { + // use_modules.push(syn::parse_quote! { use trdelnik_client::program_client::*; }) + // } + // use_modules + // } - if output.status.success() { - let code = String::from_utf8(output.stdout)?; - let mut use_modules: Vec = vec![]; - for item in syn::parse_file(code.as_str()).unwrap().items.into_iter() { - if let syn::Item::Mod(module) = item { - let modules = module - .content - .ok_or("account mod: empty content") - .unwrap() - .1 - .into_iter(); - for module in modules { - if let syn::Item::Use(u) = module { - use_modules.push(u); - } - } - } - } - if use_modules.is_empty() { - use_modules.push(syn::parse_quote! { use trdelnik_client::*; }) - } - use_modules - } else { - let mut use_modules: Vec = vec![]; - if use_modules.is_empty() { - use_modules.push(syn::parse_quote! { use trdelnik_client::*; }) - } - use_modules + let mut use_modules: Vec = vec![]; + if use_modules.is_empty() { + use_modules.push(syn::parse_quote! { use trdelnik_client::program_client::*; }) } + use_modules } /// Runs standard Rust tests. @@ -278,7 +288,7 @@ impl Commander { // only poc_tests and not fuzz tests also // FIXME "ok" within the output in terminal is not green // as it should be - let success = Command::new("cargo") + let success = tokio::process::Command::new("cargo") .arg("test") .args(["--package", "poc_tests"]) .arg("--") @@ -303,7 +313,7 @@ impl Commander { let fuzz_args = config.get_fuzz_args(hfuzz_run_args); - let mut child = Command::new("cargo") + let mut child = tokio::process::Command::new("cargo") .env("HFUZZ_RUN_ARGS", fuzz_args) .arg("hfuzz") .arg("run") @@ -318,14 +328,14 @@ impl Commander { }, Err(_) => throw!(Error::FuzzingFailed), }, - _ = signal::ctrl_c() => { + _ = tokio::signal::ctrl_c() => { tokio::time::sleep(tokio::time::Duration::from_millis(100)).await; }, } } #[throws] pub async fn run_fuzzer_with_exit_code(target: String, root: String) { - let root = Path::new(&root); + let root = std::path::Path::new(&root); // obtain config data let config = Config::new(); @@ -350,7 +360,7 @@ impl Commander { // if !cur_dir.try_exists()? { // throw!(Error::NotInitialized); // } - let mut child = Command::new("cargo") + let mut child = tokio::process::Command::new("cargo") .env("HFUZZ_RUN_ARGS", fuzz_args) .arg("hfuzz") .arg("run") @@ -365,7 +375,7 @@ impl Commander { }, Err(_) => throw!(Error::FuzzingFailed), }, - _ = signal::ctrl_c() => { + _ = tokio::signal::ctrl_c() => { tokio::time::sleep(tokio::time::Duration::from_millis(100)).await; }, } @@ -384,7 +394,7 @@ impl Commander { /// Runs fuzzer on the given target. #[throws] pub async fn run_fuzzer_debug(target: String, crash_file_path: String, root: String) { - let root = Path::new(&root); + let root = std::path::Path::new(&root); let cur_dir = root.join(TESTS_WORKSPACE_DIRECTORY); let crash_file = std::env::current_dir()?.join(crash_file_path); @@ -413,20 +423,23 @@ impl Commander { /// Starts the localnet (Solana validator). #[throws] pub async fn start_localnet(root: &String) -> LocalnetHandle { - let mut process = Command::new("solana-test-validator") + let mut process = tokio::process::Command::new("solana-test-validator") .arg("-C") .arg([root, "config.yml"].concat()) .arg("-r") .arg("-q") .spawn()?; - if !Client::new(Keypair::new()).is_localnet_running(true).await { + if !Client::new(solana_sdk::signature::Keypair::new()) + .is_localnet_running(true) + .await + { // The validator might not be running, but the process might be still alive (very slow start, some bug, ...), // therefore we want to kill it if it's still running so ports aren't held. process.kill().await.ok(); throw!(Error::LocalnetIsNotRunning); } - debug!("localnet started"); + log::debug!("localnet started"); LocalnetHandle { solana_test_validator_process: process, } @@ -435,22 +448,21 @@ impl Commander { /// Formats program code. #[throws] pub async fn format_program_code(code: &str) -> String { - let mut rustfmt = Command::new("rustfmt") + let mut rustfmt = std::process::Command::new("rustfmt") .args(["--edition", "2018"]) - .kill_on_drop(true) - .stdin(Stdio::piped()) - .stdout(Stdio::piped()) + .stdin(std::process::Stdio::piped()) + .stdout(std::process::Stdio::piped()) .spawn()?; if let Some(stdio) = &mut rustfmt.stdin { - stdio.write_all(code.as_bytes()).await?; + stdio.write_all(code.as_bytes())?; } - let output = rustfmt.wait_with_output().await?; + let output = rustfmt.wait_with_output()?; String::from_utf8(output.stdout)? } } fn get_crash_dir_and_ext( - root: &Path, + root: &std::path::Path, target: &str, hfuzz_run_args: &str, ) -> (std::path::PathBuf, String) { @@ -464,10 +476,12 @@ fn get_crash_dir_and_ext( .or_else(|| get_cmd_option_value(hfuzz_run_args.clone(), "-W", "--w")); let crash_path = if let Some(dir) = crash_dir { - Path::new(root).join(dir) + std::path::Path::new(root).join(dir) // Path::new(root).join(TESTS_WORKSPACE_DIRECTORY).join(dir) } else { - Path::new(root).join(HFUZZ_WORKSPACE).join(target) + std::path::Path::new(root) + .join(HFUZZ_WORKSPACE) + .join(target) // Path::new(root) // .join(TESTS_WORKSPACE_DIRECTORY) // .join(HFUZZ_WORKSPACE) @@ -620,9 +634,11 @@ mod tests { #[test] fn test_get_crash_dir_and_ext() { - let root = Path::new("/home/fuzz"); + let root = std::path::Path::new("/home/fuzz"); let target = "target"; - let default_crash_path = Path::new(root).join(HFUZZ_WORKSPACE).join(target); + let default_crash_path = std::path::Path::new(root) + .join(HFUZZ_WORKSPACE) + .join(target); let (crash_dir, ext) = get_crash_dir_and_ext(root, target, ""); @@ -642,7 +658,7 @@ mod tests { // test absolute path let (crash_dir, ext) = get_crash_dir_and_ext(root, target, "-Q -W /home/crash -e crash"); - let expected_crash_path = Path::new("/home/crash"); + let expected_crash_path = std::path::Path::new("/home/crash"); assert_eq!(crash_dir, expected_crash_path); assert_eq!(&ext, "crash"); @@ -650,7 +666,7 @@ mod tests { let (crash_dir, ext) = get_crash_dir_and_ext(root, target, "-Q --crash /home/crash -e crash"); - let expected_crash_path = Path::new("/home/crash"); + let expected_crash_path = std::path::Path::new("/home/crash"); assert_eq!(crash_dir, expected_crash_path); assert_eq!(&ext, "crash"); diff --git a/crates/client/src/idl.rs b/crates/client/src/idl.rs index f275d6e2..56806789 100644 --- a/crates/client/src/idl.rs +++ b/crates/client/src/idl.rs @@ -101,7 +101,7 @@ use thiserror::Error; static ACCOUNT_MOD_PREFIX: &str = "__client_accounts_"; #[derive(Error, Debug)] -pub enum Error { +pub enum IdlError { #[error("{0:?}")] RustParsingError(#[from] syn::Error), #[error("missing or invalid program item: '{0}'")] @@ -138,149 +138,150 @@ pub struct IdlAccountGroup { pub accounts: Vec<(String, String)>, } -pub fn parse_to_idl_program(name: &String, code: &str) -> Result { - let mut static_program_id = None::; - let mut mod_private = None::; - let mut mod_instruction = None::; - let mut account_mods = Vec::::new(); +impl Idl { + pub fn parse_to_idl_program(name: &String, code: &str) -> Result { + let mut static_program_id = None::; + let mut mod_private = None::; + let mut mod_instruction = None::; + let mut account_mods = Vec::::new(); - let items = syn::parse_file(code)?.items; + let items = syn::parse_file(code)?.items; - for item in items.iter() { - match item { - syn::Item::Static(item_static) if item_static.ident == "ID" => { - static_program_id = Some(item_static.clone()); + for item in items.iter() { + match item { + syn::Item::Static(item_static) if item_static.ident == "ID" => { + static_program_id = Some(item_static.clone()); + } + syn::Item::Mod(item_mod) => match item_mod.ident.to_string().as_str() { + "__private" => mod_private = Some(item_mod.clone()), + "instruction" => mod_instruction = Some(item_mod.clone()), + _ => set_account_modules(&mut account_mods, item_mod.clone()), + }, + _ => (), } - syn::Item::Mod(item_mod) => match item_mod.ident.to_string().as_str() { - "__private" => mod_private = Some(item_mod.clone()), - "instruction" => mod_instruction = Some(item_mod.clone()), - _ => set_account_modules(&mut account_mods, item_mod.clone()), - }, - _ => (), } - } - let static_program_id = - static_program_id.ok_or(Error::MissingOrInvalidProgramItems("missing static ID"))?; - let mod_private = - mod_private.ok_or(Error::MissingOrInvalidProgramItems("missing mod private"))?; - let mod_instruction = mod_instruction.ok_or(Error::MissingOrInvalidProgramItems( - "missing mod instruction", - ))?; - - // ------ get program id ------ - - // input example: - // ``` - // pub static ID: anchor_lang::solana_program::pubkey::Pubkey = - // anchor_lang::solana_program::pubkey::Pubkey::new_from_array([216u8, 55u8, - // 200u8, 93u8, - // 189u8, 81u8, - // 94u8, 109u8, - // 14u8, 249u8, - // 244u8, 106u8, - // 68u8, 214u8, - // 222u8, 190u8, - // 9u8, 25u8, - // 199u8, 75u8, - // 79u8, 230u8, - // 94u8, 137u8, - // 51u8, 187u8, - // 193u8, 48u8, - // 87u8, 222u8, - // 175u8, - // 163u8]); - // ``` - - let program_id_bytes = { - let new_pubkey_call = match *static_program_id.expr { - syn::Expr::Call(new_pubkey_call) => new_pubkey_call, - _ => { - return Err(Error::MissingOrInvalidProgramItems( - "static ID: new pubkey call not found", - )) + let static_program_id = + static_program_id.ok_or(IdlError::MissingOrInvalidProgramItems("missing static ID"))?; + let mod_private = + mod_private.ok_or(IdlError::MissingOrInvalidProgramItems("missing mod private"))?; + let mod_instruction = mod_instruction.ok_or(IdlError::MissingOrInvalidProgramItems( + "missing mod instruction", + ))?; + + // ------ get program id ------ + + // input example: + // ``` + // pub static ID: anchor_lang::solana_program::pubkey::Pubkey = + // anchor_lang::solana_program::pubkey::Pubkey::new_from_array([216u8, 55u8, + // 200u8, 93u8, + // 189u8, 81u8, + // 94u8, 109u8, + // 14u8, 249u8, + // 244u8, 106u8, + // 68u8, 214u8, + // 222u8, 190u8, + // 9u8, 25u8, + // 199u8, 75u8, + // 79u8, 230u8, + // 94u8, 137u8, + // 51u8, 187u8, + // 193u8, 48u8, + // 87u8, 222u8, + // 175u8, + // 163u8]); + // ``` + + let program_id_bytes = { + let new_pubkey_call = match *static_program_id.expr { + syn::Expr::Call(new_pubkey_call) => new_pubkey_call, + _ => { + return Err(IdlError::MissingOrInvalidProgramItems( + "static ID: new pubkey call not found", + )) + } + }; + match new_pubkey_call.args.into_iter().next() { + Some(syn::Expr::Array(pubkey_bytes)) => pubkey_bytes, + _ => { + return Err(IdlError::MissingOrInvalidProgramItems( + "static ID: pubkey bytes not found", + )) + } } }; - match new_pubkey_call.args.into_iter().next() { - Some(syn::Expr::Array(pubkey_bytes)) => pubkey_bytes, - _ => { - return Err(Error::MissingOrInvalidProgramItems( - "static ID: pubkey bytes not found", - )) - } - } - }; - - // ------ get instruction_item_fns ------ - - // input example: - // ``` - // mod __private { - // pub mod __global { - // use super::*; - // #[inline(never)] - // pub fn initialize(program_id: &Pubkey, accounts: &[AccountInfo], - // ix_data: &[u8]) -> ProgramResult { - // let ix = - // instruction::Initialize::deserialize(&mut &ix_data[..]).map_err(|_| - // anchor_lang::__private::ErrorCode::InstructionDidNotDeserialize)?; - // let instruction::Initialize = ix; - // let mut remaining_accounts: &[AccountInfo] = accounts; - // let mut accounts = - // Initialize::try_accounts(program_id, &mut remaining_accounts, - // ix_data)?; - // turnstile::initialize(Context::new(program_id, &mut accounts, - // remaining_accounts))?; - // accounts.exit(program_id) - // } - // ``` - - let instruction_item_fns = { - let items = mod_private - .content - .map(|(_, items)| items) - .unwrap_or_default(); - let item_mod_global = items - .into_iter() - .find_map(|item| match item { - syn::Item::Mod(item_mod) if item_mod.ident == "__global" => Some(item_mod), - _ => None?, + + // ------ get instruction_item_fns ------ + + // input example: + // ``` + // mod __private { + // pub mod __global { + // use super::*; + // #[inline(never)] + // pub fn initialize(program_id: &Pubkey, accounts: &[AccountInfo], + // ix_data: &[u8]) -> ProgramResult { + // let ix = + // instruction::Initialize::deserialize(&mut &ix_data[..]).map_err(|_| + // anchor_lang::__private::ErrorCode::InstructionDidNotDeserialize)?; + // let instruction::Initialize = ix; + // let mut remaining_accounts: &[AccountInfo] = accounts; + // let mut accounts = + // Initialize::try_accounts(program_id, &mut remaining_accounts, + // ix_data)?; + // turnstile::initialize(Context::new(program_id, &mut accounts, + // remaining_accounts))?; + // accounts.exit(program_id) + // } + // ``` + + let instruction_item_fns = { + let items = mod_private + .content + .map(|(_, items)| items) + .unwrap_or_default(); + let item_mod_global = items + .into_iter() + .find_map(|item| match item { + syn::Item::Mod(item_mod) if item_mod.ident == "__global" => Some(item_mod), + _ => None?, + }) + .ok_or(IdlError::MissingOrInvalidProgramItems( + "mod private: mod global not found", + ))?; + let items = item_mod_global + .content + .map(|(_, items)| items) + .unwrap_or_default(); + items.into_iter().filter_map(|item| match item { + syn::Item::Fn(item_fn) => Some(item_fn), + _ => None, }) - .ok_or(Error::MissingOrInvalidProgramItems( - "mod private: mod global not found", - ))?; - let items = item_mod_global - .content - .map(|(_, items)| items) - .unwrap_or_default(); - items.into_iter().filter_map(|item| match item { - syn::Item::Fn(item_fn) => Some(item_fn), - _ => None, - }) - }; - - // ------ get instruction + account group names ------ - - // input example: - // ``` - // pub fn initialize(program_id: &Pubkey, accounts: &[AccountInfo], - // ix_data: &[u8]) -> ProgramResult { - // let ix = - // instruction::Initialize::deserialize(&mut &ix_data[..]).map_err(|_| - // anchor_lang::__private::ErrorCode::InstructionDidNotDeserialize)?; - // let instruction::Initialize = ix; - // let mut remaining_accounts: &[AccountInfo] = accounts; - // let mut accounts = - // Initialize::try_accounts(program_id, &mut remaining_accounts, - // ix_data)?; - // turnstile::initialize(Context::new(program_id, &mut accounts, - // remaining_accounts))?; - // accounts.exit(program_id) - // } - // ``` - - let mut instruction_account_pairs = Vec::new(); - instruction_item_fns + }; + + // ------ get instruction + account group names ------ + + // input example: + // ``` + // pub fn initialize(program_id: &Pubkey, accounts: &[AccountInfo], + // ix_data: &[u8]) -> ProgramResult { + // let ix = + // instruction::Initialize::deserialize(&mut &ix_data[..]).map_err(|_| + // anchor_lang::__private::ErrorCode::InstructionDidNotDeserialize)?; + // let instruction::Initialize = ix; + // let mut remaining_accounts: &[AccountInfo] = accounts; + // let mut accounts = + // Initialize::try_accounts(program_id, &mut remaining_accounts, + // ix_data)?; + // turnstile::initialize(Context::new(program_id, &mut accounts, + // remaining_accounts))?; + // accounts.exit(program_id) + // } + // ``` + + let mut instruction_account_pairs = Vec::new(); + instruction_item_fns .into_iter() .map(|item_fn| { // stmt example: `let mut accounts = UpdateState::try_accounts(program_id, &mut remaining_accounts, ix_data)?;` @@ -331,143 +332,144 @@ pub fn parse_to_idl_program(name: &String, code: &str) -> Result - { - item_struct - } - _ => None?, - }; - let fields = match instruction_item_struct.fields { - syn::Fields::Named(fields_named) => fields_named.named, - syn::Fields::Unit => syn::punctuated::Punctuated::new(), - syn::Fields::Unnamed(_) => None?, - }; - Some(fields.into_iter()) - }) - .ok_or(Error::MissingOrInvalidProgramItems("instruction struct"))?; - - idl_instruction.parameters = instruction_item_struct_fields - .map(|field| { - let parameter_name = field.ident.unwrap().to_string(); - let parameter_id_type = field.ty.into_token_stream().to_string(); - (parameter_name, parameter_id_type) - }) - .collect(); - } - - // ------ get accounts ------ - - // input example: - // ``` - // pub(crate) mod __client_accounts_initialize { - // use super::*; - // use anchor_lang::prelude::borsh; - // pub struct Initialize { - // pub state: anchor_lang::solana_program::pubkey::Pubkey, - // pub user: anchor_lang::solana_program::pubkey::Pubkey, - // pub system_program: anchor_lang::solana_program::pubkey::Pubkey, - // } - // ``` - - for account_mod_item in account_mods { - let account_struct_name = account_mod_item - .ident - .to_string() - .strip_prefix(ACCOUNT_MOD_PREFIX) - .unwrap() - .to_upper_camel_case(); - - let account_item_struct = account_mod_item + // ------ get instruction parameters ------ + + // input example: + // ``` + // pub mod instruction { + // use super::*; + // pub mod state { + // use super::*; + // } + // // ** + // pub struct Initialize; + // // ** + // pub struct Coin { + // pub dummy_arg: String, + // } + // ``` + + let mut instruction_mod_items = mod_instruction .content - .ok_or(Error::MissingOrInvalidProgramItems( - "account mod: empty content", + .ok_or(IdlError::MissingOrInvalidProgramItems( + "instruction mod: empty content", ))? .1 - .into_iter() - .find_map(|item| match item { - syn::Item::Struct(item_struct) if item_struct.ident == account_struct_name => { - Some(item_struct) - } - _ => None?, - }) - .ok_or(Error::MissingOrInvalidProgramItems( - "account mod: struct not found", - ))?; - - let account_item_struct_fields = match account_item_struct.fields { - syn::Fields::Named(fields_named) => fields_named.named, - syn::Fields::Unit => syn::punctuated::Punctuated::new(), - syn::Fields::Unnamed(_) => { - return Err(Error::MissingOrInvalidProgramItems( - "account struct: unnamed fields not allowed", - )) - } - }; + .into_iter(); + + for (idl_instruction, _) in &mut instruction_account_pairs { + let instruction_struct_name = &idl_instruction.name.upper_camel_case; + + let instruction_item_struct_fields = instruction_mod_items + .find_map(|item| { + let instruction_item_struct = match item { + syn::Item::Struct(item_struct) + if item_struct.ident == instruction_struct_name => + { + item_struct + } + _ => None?, + }; + let fields = match instruction_item_struct.fields { + syn::Fields::Named(fields_named) => fields_named.named, + syn::Fields::Unit => syn::punctuated::Punctuated::new(), + syn::Fields::Unnamed(_) => None?, + }; + Some(fields.into_iter()) + }) + .ok_or(IdlError::MissingOrInvalidProgramItems("instruction struct"))?; + + idl_instruction.parameters = instruction_item_struct_fields + .map(|field| { + let parameter_name = field.ident.unwrap().to_string(); + let parameter_id_type = field.ty.into_token_stream().to_string(); + (parameter_name, parameter_id_type) + }) + .collect(); + } - let accounts = account_item_struct_fields - .into_iter() - .map(|field| { - let account_name = field.ident.unwrap().to_string(); - let account_id_type = field.ty.into_token_stream().to_string(); - (account_name, account_id_type) - }) - .collect::>(); + // ------ get accounts ------ + + // input example: + // ``` + // pub(crate) mod __client_accounts_initialize { + // use super::*; + // use anchor_lang::prelude::borsh; + // pub struct Initialize { + // pub state: anchor_lang::solana_program::pubkey::Pubkey, + // pub user: anchor_lang::solana_program::pubkey::Pubkey, + // pub system_program: anchor_lang::solana_program::pubkey::Pubkey, + // } + // ``` + + for account_mod_item in account_mods { + let account_struct_name = account_mod_item + .ident + .to_string() + .strip_prefix(ACCOUNT_MOD_PREFIX) + .unwrap() + .to_upper_camel_case(); + + let account_item_struct = account_mod_item + .content + .ok_or(IdlError::MissingOrInvalidProgramItems( + "account mod: empty content", + ))? + .1 + .into_iter() + .find_map(|item| match item { + syn::Item::Struct(item_struct) if item_struct.ident == account_struct_name => { + Some(item_struct) + } + _ => None?, + }) + .ok_or(IdlError::MissingOrInvalidProgramItems( + "account mod: struct not found", + ))?; + + let account_item_struct_fields = match account_item_struct.fields { + syn::Fields::Named(fields_named) => fields_named.named, + syn::Fields::Unit => syn::punctuated::Punctuated::new(), + syn::Fields::Unnamed(_) => { + return Err(IdlError::MissingOrInvalidProgramItems( + "account struct: unnamed fields not allowed", + )) + } + }; - for (_, idl_account_group) in &mut instruction_account_pairs { - if idl_account_group.name.upper_camel_case == account_struct_name { - idl_account_group.accounts = accounts.clone(); + let accounts = account_item_struct_fields + .into_iter() + .map(|field| { + let account_name = field.ident.unwrap().to_string(); + let account_id_type = field.ty.into_token_stream().to_string(); + (account_name, account_id_type) + }) + .collect::>(); + + for (_, idl_account_group) in &mut instruction_account_pairs { + if idl_account_group.name.upper_camel_case == account_struct_name { + idl_account_group.accounts = accounts.clone(); + } } } - } - // ------ // ------ + // ------ // ------ - Ok(IdlProgram { - name: IdlName { - upper_camel_case: name.to_upper_camel_case(), - snake_case: name.to_string(), - }, - id: program_id_bytes.into_token_stream().to_string(), - instruction_account_pairs, - }) + Ok(IdlProgram { + name: IdlName { + upper_camel_case: name.to_upper_camel_case(), + snake_case: name.to_string(), + }, + id: program_id_bytes.into_token_stream().to_string(), + instruction_account_pairs, + }) + } } fn set_account_modules(account_modules: &mut Vec, item_module: syn::ItemMod) { @@ -481,7 +483,7 @@ fn set_account_modules(account_modules: &mut Vec, item_module: syn } let modules = item_module .content - .ok_or(Error::MissingOrInvalidProgramItems( + .ok_or(IdlError::MissingOrInvalidProgramItems( "account mod: empty content", )) .unwrap() diff --git a/crates/client/src/keys.rs b/crates/client/src/keys.rs index 0f024f81..998a7297 100644 --- a/crates/client/src/keys.rs +++ b/crates/client/src/keys.rs @@ -1,6 +1,5 @@ use std::error::Error; -use anchor_client::solana_sdk::signer::keypair::Keypair; use heck::ToSnakeCase; use solana_sdk::signer::EncodableKey; @@ -16,7 +15,9 @@ pub enum KeyPairError { } /// Reads program keypair JSON file generated by Anchor based on `program_name`. Returns error if not found. -pub fn anchor_keypair(program_name: &str) -> Result> { +pub fn anchor_keypair( + program_name: &str, +) -> Result> { let root = match Config::discover_root() { Ok(root) => root, Err(_) => return Err(KeyPairError::BadWorkspace.into()), @@ -30,20 +31,20 @@ pub fn anchor_keypair(program_name: &str) -> Result> { return Err(KeyPairError::AnchorKeypairNotFound.into()); } - Keypair::read_from_file(keypair_path) + solana_sdk::signature::Keypair::read_from_file(keypair_path) } /// Generate a random keypair. -pub fn random_keypair() -> Keypair { - Keypair::new() +pub fn random_keypair() -> solana_sdk::signature::Keypair { + solana_sdk::signature::Keypair::new() } /// Returns a recognisable Keypair of your created program. The public key will start with `Pxx`, where /// xx are the three digits of the number. `o` is used instead of `0`, as `0` is not part of the base58 charset. /// You shouldn't call the method with the same `n` twice. /// The `n` must be a number between `0` and `29`. -pub fn program_keypair(n: usize) -> Keypair { - Keypair::from_bytes(&PROGRAM_KEYPAIRS[n]).unwrap() +pub fn program_keypair(n: usize) -> solana_sdk::signature::Keypair { + solana_sdk::signature::Keypair::from_bytes(&PROGRAM_KEYPAIRS[n]).unwrap() } /// Returns a system wallet (wallet which is owned by the system) but it is not required, you can also use the @@ -52,16 +53,16 @@ pub fn program_keypair(n: usize) -> Keypair { /// the base58 charset. Returns a recognisable `Keypair`. This is NOT the same as /// `anchor_lang::system_program::System::id()`! /// The `n` must be a number between `0` and `29`. -pub fn system_keypair(n: usize) -> Keypair { - Keypair::from_bytes(&SYSTEM_KEYPAIRS[n]).unwrap() +pub fn system_keypair(n: usize) -> solana_sdk::signature::Keypair { + solana_sdk::signature::Keypair::from_bytes(&SYSTEM_KEYPAIRS[n]).unwrap() } /// Returns a recognisable `Keypair` / wallet that can be used for the mint account for example. The public key will /// start with `Txxx`, where xxx are the three digits of the number. You shouldn't call the method with the same `n` /// twice. `o` is used instead of `0`, as `0` is not part of the base58 charset. /// The `n` must be a number between `0` and `255`. -pub fn keypair(n: usize) -> Keypair { - Keypair::from_bytes(&KEYPAIRS[n]).unwrap() +pub fn keypair(n: usize) -> solana_sdk::signature::Keypair { + solana_sdk::signature::Keypair::from_bytes(&KEYPAIRS[n]).unwrap() } const PROGRAM_KEYPAIRS: [[u8; 64]; 30] = [ diff --git a/crates/client/src/lib.rs b/crates/client/src/lib.rs index 31c27727..7ce6c478 100644 --- a/crates/client/src/lib.rs +++ b/crates/client/src/lib.rs @@ -3,48 +3,59 @@ //! //! Trdelnik could be useful for writing Rust dApps, too. -pub use anchor_client::{ - self, - anchor_lang::{self, prelude::System, Id, InstructionData, ToAccountMetas}, - solana_sdk::{ - self, - instruction::Instruction, - pubkey::Pubkey, - signature::Signature, - signer::{keypair::Keypair, Signer}, - }, - ClientError, -}; -pub use anyhow::{self, Error}; - #[cfg(feature = "fuzzing")] pub mod fuzzing { - pub use super::{ - anchor_lang, anchor_lang::system_program::ID as SYSTEM_PROGRAM_ID, - solana_sdk::transaction::Transaction, Instruction, Keypair, Pubkey, Signer, - }; - pub use anchor_client::anchor_lang::solana_program::hash::Hash; + pub use anchor_lang; pub use arbitrary; pub use arbitrary::Arbitrary; pub use honggfuzz::fuzz; - pub use solana_program_test::{ - processor, tokio::runtime::Runtime, BanksClient, BanksClientError, ProgramTest, - }; + pub use solana_program_test; + pub use solana_sdk; + pub use solana_sdk::signer::Signer; +} + +pub mod program_client { + pub use crate::client::Client; + pub use anchor_client; + pub use anchor_lang::{InstructionData, ToAccountMetas}; + pub use solana_sdk; + pub use solana_transaction_status; } -pub use futures::{self, FutureExt}; -pub use rstest::*; -pub use serial_test; -pub use solana_transaction_status::EncodedConfirmedTransactionWithStatusMeta; -pub use tokio; +#[cfg(feature = "poctesting")] +pub mod poctesting { + pub use crate::client::Client; + pub use crate::error_reporter::*; + pub use crate::TempClone; + pub use anchor_lang; + pub use anyhow::{Error, Result}; + + pub use fehler::throws; + + pub use futures::FutureExt; + pub use rstest::*; + pub use serial_test; + pub use solana_sdk; + pub use solana_sdk::signer::Signer; + pub use tokio; + pub use trdelnik_test::trdelnik_test; + + pub use crate::keys::*; +} -pub use trdelnik_test::trdelnik_test; +// pub use futures::{self, FutureExt}; +// pub use client::PrintableTransaction; +// pub use trdelnik_test::trdelnik_test; + +// pub use rstest::*; +// pub use serial_test; +// pub use tokio; mod config; +pub use config::Config; mod client; pub use client::Client; -pub use client::PrintableTransaction; mod reader; pub use reader::Reader; @@ -61,10 +72,13 @@ pub use temp_clone::TempClone; mod keys; pub use keys::*; -pub mod idl; -pub mod program_client_generator; +mod idl; +pub use idl::{Idl, IdlError}; + +mod program_client_generator; +pub use program_client_generator::generate_source_code; -pub mod workspace_builder; +mod workspace_builder; pub use workspace_builder::WorkspaceBuilder; pub mod error_reporter; @@ -96,4 +110,7 @@ pub mod constants { pub const GIT_IGNORE: &str = ".gitignore"; pub const CLIENT_TOML_TEMPLATE: &str = "/src/templates/program_client/Cargo.toml.tmpl"; + + pub const RETRY_LOCALNET_EVERY_MILLIS: u64 = 500; + pub const DEFAULT_KEYPAIR_PATH: &str = "~/.config/solana/id.json"; } diff --git a/crates/client/src/program_client_generator.rs b/crates/client/src/program_client_generator.rs index d087ae3f..cbb3a953 100644 --- a/crates/client/src/program_client_generator.rs +++ b/crates/client/src/program_client_generator.rs @@ -55,7 +55,8 @@ pub fn generate_source_code(idl: &Idl, use_modules: &[syn::ItemUse]) -> String { let last_type = &tp.path.segments.last().unwrap().ident.to_string(); if last_type == "Pubkey" { - let t: syn::Type = parse_str(last_type).unwrap(); + let reference = format!("&solana_sdk::pubkey::Pubkey"); + let t: syn::Type = parse_str(&reference).unwrap(); t } else { // we expect only Pubkey, but if it is something different, than return fully qualified type @@ -85,19 +86,21 @@ pub fn generate_source_code(idl: &Idl, use_modules: &[syn::ItemUse]) -> String { .iter() .map(|(name, _)| { let name: syn::Ident = parse_str(name).unwrap(); - let value = format_ident!("a_{name}"); + let value_s = format!("*a_{}", name); + let value: syn::Expr = parse_str(&value_s).unwrap(); let account: syn::FieldValue = parse_quote!(#name: #value); account }) .collect::>(); let instruction: syn::ItemFn = parse_quote! { + #[allow(clippy::too_many_arguments)] pub async fn #instruction_fn_name( client: &Client, #(#parameters,)* #(#accounts,)* - signers: impl IntoIterator + Send + 'static, - ) -> Result { + signers: impl IntoIterator + Send, + ) -> Result { client.send_instruction( PROGRAM_ID, #module_name::instruction::#instruction_struct_name { @@ -112,11 +115,12 @@ pub fn generate_source_code(idl: &Idl, use_modules: &[syn::ItemUse]) -> String { }; let instruction_raw: syn::ItemFn = parse_quote! { + #[allow(clippy::too_many_arguments)] pub fn #instruction_name( #(#parameters,)* #(#accounts,)* - ) -> Instruction { - Instruction{ + ) -> solana_sdk::instruction::Instruction { + solana_sdk::instruction::Instruction{ program_id: PROGRAM_ID, data: #module_name::instruction::#instruction_struct_name { #(#field_parameters,)* @@ -138,7 +142,7 @@ pub fn generate_source_code(idl: &Idl, use_modules: &[syn::ItemUse]) -> String { let program_module: syn::ItemMod = parse_quote! { pub mod #instruction_module_name { #(#use_modules)* - pub static PROGRAM_ID: Pubkey = Pubkey::new_from_array(#pubkey_bytes); + pub static PROGRAM_ID: solana_sdk::pubkey::Pubkey = solana_sdk::pubkey::Pubkey::new_from_array(#pubkey_bytes); #(#instructions)* } }; diff --git a/crates/client/src/temp_clone.rs b/crates/client/src/temp_clone.rs index 8efba3fb..8b79058e 100644 --- a/crates/client/src/temp_clone.rs +++ b/crates/client/src/temp_clone.rs @@ -1,15 +1,16 @@ -use crate::Keypair; - // @TODO remove once `Clone` is implemented for `Keypair` // https://docs.rs/solana-sdk/latest/solana_sdk/signer/keypair/struct.Keypair.html +// probably will not be added into the Keypair +// https://docs.rs/solana-sdk/latest/solana_sdk/signer/keypair/struct.Keypair.html#method.insecure_clone + /// The `TempClone` trait is used as a workaround /// for making non-cloneable foreign types cloneable. pub trait TempClone { fn clone(&self) -> Self; } -impl TempClone for Keypair { +impl TempClone for solana_sdk::signer::keypair::Keypair { fn clone(&self) -> Self { Self::from_bytes(&self.to_bytes()).unwrap() } diff --git a/crates/client/src/templates/trdelnik-tests/Cargo.toml.tmpl b/crates/client/src/templates/trdelnik-tests/Cargo.toml.tmpl deleted file mode 100644 index 367736be..00000000 --- a/crates/client/src/templates/trdelnik-tests/Cargo.toml.tmpl +++ /dev/null @@ -1,26 +0,0 @@ -[package] -name = "trdelnik-tests" -version = "0.1.0" -description = "Created with Trdelnik" -edition = "2021" - -[dependencies] -assert_matches = "1.4.0" - -[dependencies.trdelnik-client] -version = "0.5.0" - -[dependencies.program_client] -path = "../.program_client" - -[dev-dependencies] -fehler = "1.0.0" -rstest = "0.12.0" - -# this may be not optimal to store it here right away -# because we have also option to init without fuzzer -# on the other hand, without the option I found this OK - -[[bin]] -name = "fuzz_target" -path = "fuzz_0/fuzz_target.rs" diff --git a/crates/client/src/templates/trdelnik-tests/Cargo_fuzz.toml.tmpl b/crates/client/src/templates/trdelnik-tests/Cargo_fuzz.toml.tmpl index f2249e77..be0eda33 100644 --- a/crates/client/src/templates/trdelnik-tests/Cargo_fuzz.toml.tmpl +++ b/crates/client/src/templates/trdelnik-tests/Cargo_fuzz.toml.tmpl @@ -13,6 +13,7 @@ assert_matches = "1.4.0" [dependencies.trdelnik-client] version = "0.5.0" +features=["fuzzing"] [dependencies.program_client] path = "../../../.program_client" diff --git a/crates/client/src/templates/trdelnik-tests/Cargo_poc.toml.tmpl b/crates/client/src/templates/trdelnik-tests/Cargo_poc.toml.tmpl index 13b549fc..7fcc9a52 100644 --- a/crates/client/src/templates/trdelnik-tests/Cargo_poc.toml.tmpl +++ b/crates/client/src/templates/trdelnik-tests/Cargo_poc.toml.tmpl @@ -6,12 +6,12 @@ edition = "2018" # Dependencies specific to PoC test # Dev-dependencies are used for compiling tests, [dev-dependencies] -assert_matches = "1.4.0" fehler = "1.0.0" -rstest = "0.12.0" [dev-dependencies.trdelnik-client] version = "0.5.0" +features=["poctesting"] + [dev-dependencies.program_client] path = "../../.program_client" diff --git a/crates/client/src/templates/trdelnik-tests/fuzz_target.rs b/crates/client/src/templates/trdelnik-tests/fuzz_target.rs deleted file mode 100644 index 7b66c7e7..00000000 --- a/crates/client/src/templates/trdelnik-tests/fuzz_target.rs +++ /dev/null @@ -1,71 +0,0 @@ -use assert_matches::*; -use trdelnik_client::fuzzing::*; - -const PROGRAM_NAME: &str = "###PROGRAM_NAME###"; - -#[derive(Arbitrary)] -pub struct FuzzData { - param1: u8, - param2: u8, -} - -fn main() { - loop { - fuzz!(|fuzz_data: FuzzData| { - Runtime::new().unwrap().block_on(async { - let program_test = ProgramTest::new(PROGRAM_NAME, PROGRAM_ID, processor!(entry)); - - let mut ctx = program_test.start_with_context().await; - - // TODO: replace this instruction with one of your generated instructions from trdelnik_client - let init_ix = init_dummy_ix(); - let mut transaction = - Transaction::new_with_payer(&[init_ix], Some(&ctx.payer.pubkey().clone())); - - transaction.sign(&[&ctx.payer], ctx.last_blockhash); - let res = ctx.banks_client.process_transaction(transaction).await; - assert_matches!(res, Ok(())); - - let res = fuzz_ix( - &fuzz_data, - &mut ctx.banks_client, - &ctx.payer, - ctx.last_blockhash, - ) - .await; - assert_matches!(res, Ok(())); - }); - }); - } -} - -async fn fuzz_ix( - fuzz_data: &FuzzData, - banks_client: &mut BanksClient, - payer: &Keypair, - blockhash: Hash, -) -> core::result::Result<(), BanksClientError> { - // TODO: replace this instruction with one of your generated instructions from trdelnik_client - let update_ix = update_dummy_ix(fuzz_data.param1, fuzz_data.param2); - - let mut transaction = Transaction::new_with_payer(&[update_ix], Some(&payer.pubkey())); - transaction.sign(&[payer], blockhash); - - banks_client.process_transaction(transaction).await -} - -fn init_dummy_ix() -> Instruction { - Instruction { - program_id: PROGRAM_ID, - data: vec![], - accounts: vec![], - } -} - -fn update_dummy_ix(param1: u8, param2: u8) -> Instruction { - Instruction { - program_id: PROGRAM_ID, - data: vec![param1, param2], - accounts: vec![], - } -} diff --git a/crates/client/src/templates/trdelnik-tests/fuzz_test.tmpl.rs b/crates/client/src/templates/trdelnik-tests/fuzz_test.tmpl.rs new file mode 100644 index 00000000..45c7cad4 --- /dev/null +++ b/crates/client/src/templates/trdelnik-tests/fuzz_test.tmpl.rs @@ -0,0 +1,80 @@ +use assert_matches::*; +use trdelnik_client::fuzzing::*; + +const PROGRAM_NAME: &str = "###PROGRAM_NAME###"; + +#[derive(Arbitrary)] +pub struct FuzzData { + param1: u8, + param2: u8, +} + +fn main() { + loop { + fuzz!(|fuzz_data: FuzzData| { + solana_program_test::tokio::runtime::Runtime::new() + .unwrap() + .block_on(async { + let program_test = solana_program_test::ProgramTest::new( + PROGRAM_NAME, + PROGRAM_ID, + solana_program_test::processor!(entry), + ); + + let mut ctx = program_test.start_with_context().await; + + // TODO: replace this instruction with one of your generated instructions from trdelnik_client + let init_ix = init_dummy_ix(); + let mut transaction = solana_sdk::transaction::Transaction::new_with_payer( + &[init_ix], + Some(&ctx.payer.pubkey()), + ); + + transaction.sign(&[&ctx.payer], ctx.last_blockhash); + let res = ctx.banks_client.process_transaction(transaction).await; + assert_matches!(res, Ok(())); + + let res = fuzz_ix( + &fuzz_data, + &mut ctx.banks_client, + &ctx.payer, + ctx.last_blockhash, + ) + .await; + assert_matches!(res, Ok(())); + }); + }); + } +} + +async fn fuzz_ix( + fuzz_data: &FuzzData, + banks_client: &mut solana_program_test::BanksClient, + payer: &solana_sdk::signature::Keypair, + blockhash: solana_sdk::hash::Hash, +) -> core::result::Result<(), solana_program_test::BanksClientError> { + // TODO: replace this instruction with one of your generated instructions from trdelnik_client + let update_ix = update_dummy_ix(fuzz_data.param1, fuzz_data.param2); + + let mut transaction = + solana_sdk::transaction::Transaction::new_with_payer(&[update_ix], Some(&payer.pubkey())); + transaction.sign(&[payer], blockhash); + + banks_client.process_transaction(transaction).await +} + +fn init_dummy_ix() -> solana_sdk::instruction::Instruction { + solana_sdk::instruction::Instruction { + program_id: PROGRAM_ID, + data: vec![], + accounts: vec![], + } +} + +fn update_dummy_ix(param1: u8, param2: u8) -> solana_sdk::instruction::Instruction { + solana_sdk::instruction::Instruction { + program_id: PROGRAM_ID, + data: vec![param1, param2], + accounts: vec![], + } +} diff --git a/crates/client/src/templates/trdelnik-tests/test.rs b/crates/client/src/templates/trdelnik-tests/poc_test.tmpl.rs similarity index 79% rename from crates/client/src/templates/trdelnik-tests/test.rs rename to crates/client/src/templates/trdelnik-tests/poc_test.tmpl.rs index a0807c7c..c23205b3 100644 --- a/crates/client/src/templates/trdelnik-tests/test.rs +++ b/crates/client/src/templates/trdelnik-tests/poc_test.tmpl.rs @@ -1,5 +1,4 @@ -use fehler::throws; -use trdelnik_client::{anyhow::Result, *}; +use trdelnik_client::poctesting::*; // @todo: create and deploy your fixture #[throws] @@ -22,8 +21,8 @@ async fn test_happy_path(#[future] init_fixture: Result) { // @todo: design and implement all the logic you need for your fixture(s) struct Fixture { client: Client, - program: Keypair, - state: Keypair, + program: solana_sdk::signer::keypair::Keypair, + state: solana_sdk::signer::keypair::Keypair, } impl Fixture { fn new() -> Self { @@ -37,10 +36,10 @@ impl Fixture { #[throws] async fn deploy(&mut self) { self.client - .airdrop(self.client.payer().pubkey(), 5_000_000_000) + .airdrop(&self.client.payer().pubkey(), 5_000_000_000) .await?; self.client - .deploy_by_name(&self.program.clone(), "###PROGRAM_NAME###") + .deploy_by_name(&self.program, "###PROGRAM_NAME###") .await?; } } diff --git a/crates/client/src/workspace_builder.rs b/crates/client/src/workspace_builder.rs index 63a8c67d..3016c23d 100644 --- a/crates/client/src/workspace_builder.rs +++ b/crates/client/src/workspace_builder.rs @@ -18,8 +18,8 @@ use toml::{ }; use crate::constants::*; -use crate::idl::Idl; -use crate::program_client_generator; +use crate::generate_source_code; +use crate::Idl; #[derive(Error, Debug)] pub enum Error { @@ -166,8 +166,7 @@ impl WorkspaceBuilder { self.add_program_dependencies(&crate_path, "dependencies") .await?; - let program_client = - program_client_generator::generate_source_code(&self.idl, &self.use_tokens); + let program_client = generate_source_code(&self.idl, &self.use_tokens); let program_client = Commander::format_program_code(&program_client).await?; self.create_file(&lib_path, &program_client).await?; @@ -265,7 +264,7 @@ impl WorkspaceBuilder { let fuzz_test_content = include_str!(concat!( env!("CARGO_MANIFEST_DIR"), - "/src/templates/trdelnik-tests/fuzz_target.rs" + "/src/templates/trdelnik-tests/fuzz_test.tmpl.rs" )); let use_entry = format!("use {}::entry;\n", program_name); @@ -286,8 +285,8 @@ impl WorkspaceBuilder { .await?; // add fuzzing feature - self.add_feature_to_dep("trdelnik-client", "fuzzing", &new_fuzz_test_dir) - .await?; + // self.add_feature_to_dep("trdelnik-client", "fuzzing", &new_fuzz_test_dir) + // .await?; } /// - generates new folder and contents for new PoC test Template @@ -339,7 +338,7 @@ impl WorkspaceBuilder { let poc_test_content = include_str!(concat!( env!("CARGO_MANIFEST_DIR"), - "/src/templates/trdelnik-tests/test.rs" + "/src/templates/trdelnik-tests/poc_test.tmpl.rs" )); let test_content = poc_test_content.replace("###PROGRAM_NAME###", program_name); let use_instructions = format!("use program_client::{}_instruction::*;\n", program_name); @@ -417,8 +416,7 @@ impl WorkspaceBuilder { self.add_program_dependencies(&crate_path, "dependencies") .await?; - let program_client = - program_client_generator::generate_source_code(&self.idl, &self.use_tokens); + let program_client = generate_source_code(&self.idl, &self.use_tokens); let program_client = Commander::format_program_code(&program_client).await?; self.update_file(&lib_path, &program_client).await?; @@ -464,6 +462,10 @@ impl WorkspaceBuilder { for line in io::BufReader::new(file).lines().flatten() { if line == ignored_path { // INFO do not add the ignored path again if it is already in the .gitignore file + println!( + "\x1b[93m--> Skipping <--\x1b[0m \x1b[93m{GIT_IGNORE}\x1b[0m, already contains \x1b[93m{ignored_path}\x1b[0m." + ); + return; } } @@ -471,7 +473,7 @@ impl WorkspaceBuilder { if let Ok(mut file) = file { writeln!(file, "{}", ignored_path)?; - println!("\x1b[92mSuccesfully\x1b[0m updated: \x1b[93m{GIT_IGNORE}\x1b[0m."); + println!("\x1b[92mSuccesfully\x1b[0m updated: \x1b[93m{GIT_IGNORE}\x1b[0m with \x1b[93m{ignored_path}\x1b[0m."); } } else { println!("\x1b[93m--> Skipping <--\x1b[0m \x1b[93m{GIT_IGNORE}\x1b[0m, not found.") diff --git a/crates/client/tests/test.rs b/crates/client/tests/test.rs index 6e164300..f10643e6 100644 --- a/crates/client/tests/test.rs +++ b/crates/client/tests/test.rs @@ -22,15 +22,15 @@ pub async fn generate_program_client() { let program_name = String::from("escrow"); let program_idl = - trdelnik_client::idl::parse_to_idl_program(&program_name, expanded_anchor_program)?; + trdelnik_client::Idl::parse_to_idl_program(&program_name, expanded_anchor_program)?; - let idl = trdelnik_client::idl::Idl { + let idl = trdelnik_client::Idl { programs: vec![program_idl], }; - let use_modules: Vec = vec![syn::parse_quote! { use trdelnik_client::*; }]; - let client_code = - trdelnik_client::program_client_generator::generate_source_code(&idl, &use_modules); + let use_modules: Vec = + vec![syn::parse_quote! { use trdelnik_client::program_client::*; }]; + let client_code = trdelnik_client::generate_source_code(&idl, &use_modules); let client_code = trdelnik_client::Commander::format_program_code(&client_code).await?; assert_str_eq!(client_code, expected_client_code); diff --git a/crates/client/tests/test_data/expected_client_code.rs b/crates/client/tests/test_data/expected_client_code.rs index 741b0b16..5b476fd9 100644 --- a/crates/client/tests/test_data/expected_client_code.rs +++ b/crates/client/tests/test_data/expected_client_code.rs @@ -1,23 +1,28 @@ // DO NOT EDIT - automatically generated file (except `use` statements inside the `*_instruction` module pub mod escrow_instruction { - use trdelnik_client::*; - pub static PROGRAM_ID: Pubkey = Pubkey::new_from_array([ - 5u8, 214u8, 204u8, 101u8, 166u8, 163u8, 239u8, 244u8, 13u8, 110u8, 64u8, 106u8, 230u8, - 81u8, 141u8, 186u8, 208u8, 155u8, 78u8, 83u8, 194u8, 215u8, 103u8, 17u8, 94u8, 15u8, 137u8, - 68u8, 170u8, 153u8, 74u8, 59u8, - ]); + use trdelnik_client::program_client::*; + pub static PROGRAM_ID: solana_sdk::pubkey::Pubkey = + solana_sdk::pubkey::Pubkey::new_from_array([ + 5u8, 214u8, 204u8, 101u8, 166u8, 163u8, 239u8, 244u8, 13u8, 110u8, 64u8, 106u8, 230u8, + 81u8, 141u8, 186u8, 208u8, 155u8, 78u8, 83u8, 194u8, 215u8, 103u8, 17u8, 94u8, 15u8, + 137u8, 68u8, 170u8, 153u8, 74u8, 59u8, + ]); + #[allow(clippy::too_many_arguments)] pub async fn initialize_escrow( client: &Client, i_initializer_amount: u64, i_taker_amount: u64, - a_initializer: Pubkey, - a_initializer_deposit_token_account: Pubkey, - a_initializer_receive_token_account: Pubkey, - a_escrow_account: Pubkey, - a_system_program: Pubkey, - a_token_program: Pubkey, - signers: impl IntoIterator + Send + 'static, - ) -> Result { + a_initializer: &solana_sdk::pubkey::Pubkey, + a_initializer_deposit_token_account: &solana_sdk::pubkey::Pubkey, + a_initializer_receive_token_account: &solana_sdk::pubkey::Pubkey, + a_escrow_account: &solana_sdk::pubkey::Pubkey, + a_system_program: &solana_sdk::pubkey::Pubkey, + a_token_program: &solana_sdk::pubkey::Pubkey, + signers: impl IntoIterator + Send, + ) -> Result< + solana_transaction_status::EncodedConfirmedTransactionWithStatusMeta, + anchor_client::ClientError, + > { client .send_instruction( PROGRAM_ID, @@ -26,28 +31,29 @@ pub mod escrow_instruction { taker_amount: i_taker_amount, }, escrow::accounts::InitializeEscrow { - initializer: a_initializer, - initializer_deposit_token_account: a_initializer_deposit_token_account, - initializer_receive_token_account: a_initializer_receive_token_account, - escrow_account: a_escrow_account, - system_program: a_system_program, - token_program: a_token_program, + initializer: *a_initializer, + initializer_deposit_token_account: *a_initializer_deposit_token_account, + initializer_receive_token_account: *a_initializer_receive_token_account, + escrow_account: *a_escrow_account, + system_program: *a_system_program, + token_program: *a_token_program, }, signers, ) .await } + #[allow(clippy::too_many_arguments)] pub fn initialize_escrow_ix( i_initializer_amount: u64, i_taker_amount: u64, - a_initializer: Pubkey, - a_initializer_deposit_token_account: Pubkey, - a_initializer_receive_token_account: Pubkey, - a_escrow_account: Pubkey, - a_system_program: Pubkey, - a_token_program: Pubkey, - ) -> Instruction { - Instruction { + a_initializer: &solana_sdk::pubkey::Pubkey, + a_initializer_deposit_token_account: &solana_sdk::pubkey::Pubkey, + a_initializer_receive_token_account: &solana_sdk::pubkey::Pubkey, + a_escrow_account: &solana_sdk::pubkey::Pubkey, + a_system_program: &solana_sdk::pubkey::Pubkey, + a_token_program: &solana_sdk::pubkey::Pubkey, + ) -> solana_sdk::instruction::Instruction { + solana_sdk::instruction::Instruction { program_id: PROGRAM_ID, data: escrow::instruction::InitializeEscrow { initializer_amount: i_initializer_amount, @@ -55,116 +61,126 @@ pub mod escrow_instruction { } .data(), accounts: escrow::accounts::InitializeEscrow { - initializer: a_initializer, - initializer_deposit_token_account: a_initializer_deposit_token_account, - initializer_receive_token_account: a_initializer_receive_token_account, - escrow_account: a_escrow_account, - system_program: a_system_program, - token_program: a_token_program, + initializer: *a_initializer, + initializer_deposit_token_account: *a_initializer_deposit_token_account, + initializer_receive_token_account: *a_initializer_receive_token_account, + escrow_account: *a_escrow_account, + system_program: *a_system_program, + token_program: *a_token_program, } .to_account_metas(None), } } + #[allow(clippy::too_many_arguments)] pub async fn cancel_escrow( client: &Client, - a_initializer: Pubkey, - a_pda_deposit_token_account: Pubkey, - a_pda_account: Pubkey, - a_escrow_account: Pubkey, - a_token_program: Pubkey, - signers: impl IntoIterator + Send + 'static, - ) -> Result { + a_initializer: &solana_sdk::pubkey::Pubkey, + a_pda_deposit_token_account: &solana_sdk::pubkey::Pubkey, + a_pda_account: &solana_sdk::pubkey::Pubkey, + a_escrow_account: &solana_sdk::pubkey::Pubkey, + a_token_program: &solana_sdk::pubkey::Pubkey, + signers: impl IntoIterator + Send, + ) -> Result< + solana_transaction_status::EncodedConfirmedTransactionWithStatusMeta, + anchor_client::ClientError, + > { client .send_instruction( PROGRAM_ID, escrow::instruction::CancelEscrow {}, escrow::accounts::CancelEscrow { - initializer: a_initializer, - pda_deposit_token_account: a_pda_deposit_token_account, - pda_account: a_pda_account, - escrow_account: a_escrow_account, - token_program: a_token_program, + initializer: *a_initializer, + pda_deposit_token_account: *a_pda_deposit_token_account, + pda_account: *a_pda_account, + escrow_account: *a_escrow_account, + token_program: *a_token_program, }, signers, ) .await } + #[allow(clippy::too_many_arguments)] pub fn cancel_escrow_ix( - a_initializer: Pubkey, - a_pda_deposit_token_account: Pubkey, - a_pda_account: Pubkey, - a_escrow_account: Pubkey, - a_token_program: Pubkey, - ) -> Instruction { - Instruction { + a_initializer: &solana_sdk::pubkey::Pubkey, + a_pda_deposit_token_account: &solana_sdk::pubkey::Pubkey, + a_pda_account: &solana_sdk::pubkey::Pubkey, + a_escrow_account: &solana_sdk::pubkey::Pubkey, + a_token_program: &solana_sdk::pubkey::Pubkey, + ) -> solana_sdk::instruction::Instruction { + solana_sdk::instruction::Instruction { program_id: PROGRAM_ID, data: escrow::instruction::CancelEscrow {}.data(), accounts: escrow::accounts::CancelEscrow { - initializer: a_initializer, - pda_deposit_token_account: a_pda_deposit_token_account, - pda_account: a_pda_account, - escrow_account: a_escrow_account, - token_program: a_token_program, + initializer: *a_initializer, + pda_deposit_token_account: *a_pda_deposit_token_account, + pda_account: *a_pda_account, + escrow_account: *a_escrow_account, + token_program: *a_token_program, } .to_account_metas(None), } } + #[allow(clippy::too_many_arguments)] pub async fn exchange( client: &Client, - a_taker: Pubkey, - a_taker_deposit_token_account: Pubkey, - a_taker_receive_token_account: Pubkey, - a_pda_deposit_token_account: Pubkey, - a_initializer_receive_token_account: Pubkey, - a_initializer_main_account: Pubkey, - a_escrow_account: Pubkey, - a_pda_account: Pubkey, - a_token_program: Pubkey, - signers: impl IntoIterator + Send + 'static, - ) -> Result { + a_taker: &solana_sdk::pubkey::Pubkey, + a_taker_deposit_token_account: &solana_sdk::pubkey::Pubkey, + a_taker_receive_token_account: &solana_sdk::pubkey::Pubkey, + a_pda_deposit_token_account: &solana_sdk::pubkey::Pubkey, + a_initializer_receive_token_account: &solana_sdk::pubkey::Pubkey, + a_initializer_main_account: &solana_sdk::pubkey::Pubkey, + a_escrow_account: &solana_sdk::pubkey::Pubkey, + a_pda_account: &solana_sdk::pubkey::Pubkey, + a_token_program: &solana_sdk::pubkey::Pubkey, + signers: impl IntoIterator + Send, + ) -> Result< + solana_transaction_status::EncodedConfirmedTransactionWithStatusMeta, + anchor_client::ClientError, + > { client .send_instruction( PROGRAM_ID, escrow::instruction::Exchange {}, escrow::accounts::Exchange { - taker: a_taker, - taker_deposit_token_account: a_taker_deposit_token_account, - taker_receive_token_account: a_taker_receive_token_account, - pda_deposit_token_account: a_pda_deposit_token_account, - initializer_receive_token_account: a_initializer_receive_token_account, - initializer_main_account: a_initializer_main_account, - escrow_account: a_escrow_account, - pda_account: a_pda_account, - token_program: a_token_program, + taker: *a_taker, + taker_deposit_token_account: *a_taker_deposit_token_account, + taker_receive_token_account: *a_taker_receive_token_account, + pda_deposit_token_account: *a_pda_deposit_token_account, + initializer_receive_token_account: *a_initializer_receive_token_account, + initializer_main_account: *a_initializer_main_account, + escrow_account: *a_escrow_account, + pda_account: *a_pda_account, + token_program: *a_token_program, }, signers, ) .await } + #[allow(clippy::too_many_arguments)] pub fn exchange_ix( - a_taker: Pubkey, - a_taker_deposit_token_account: Pubkey, - a_taker_receive_token_account: Pubkey, - a_pda_deposit_token_account: Pubkey, - a_initializer_receive_token_account: Pubkey, - a_initializer_main_account: Pubkey, - a_escrow_account: Pubkey, - a_pda_account: Pubkey, - a_token_program: Pubkey, - ) -> Instruction { - Instruction { + a_taker: &solana_sdk::pubkey::Pubkey, + a_taker_deposit_token_account: &solana_sdk::pubkey::Pubkey, + a_taker_receive_token_account: &solana_sdk::pubkey::Pubkey, + a_pda_deposit_token_account: &solana_sdk::pubkey::Pubkey, + a_initializer_receive_token_account: &solana_sdk::pubkey::Pubkey, + a_initializer_main_account: &solana_sdk::pubkey::Pubkey, + a_escrow_account: &solana_sdk::pubkey::Pubkey, + a_pda_account: &solana_sdk::pubkey::Pubkey, + a_token_program: &solana_sdk::pubkey::Pubkey, + ) -> solana_sdk::instruction::Instruction { + solana_sdk::instruction::Instruction { program_id: PROGRAM_ID, data: escrow::instruction::Exchange {}.data(), accounts: escrow::accounts::Exchange { - taker: a_taker, - taker_deposit_token_account: a_taker_deposit_token_account, - taker_receive_token_account: a_taker_receive_token_account, - pda_deposit_token_account: a_pda_deposit_token_account, - initializer_receive_token_account: a_initializer_receive_token_account, - initializer_main_account: a_initializer_main_account, - escrow_account: a_escrow_account, - pda_account: a_pda_account, - token_program: a_token_program, + taker: *a_taker, + taker_deposit_token_account: *a_taker_deposit_token_account, + taker_receive_token_account: *a_taker_receive_token_account, + pda_deposit_token_account: *a_pda_deposit_token_account, + initializer_receive_token_account: *a_initializer_receive_token_account, + initializer_main_account: *a_initializer_main_account, + escrow_account: *a_escrow_account, + pda_account: *a_pda_account, + token_program: *a_token_program, } .to_account_metas(None), } diff --git a/crates/test/src/lib.rs b/crates/test/src/lib.rs index 7e4f9c34..e1fa0be5 100644 --- a/crates/test/src/lib.rs +++ b/crates/test/src/lib.rs @@ -65,15 +65,15 @@ pub fn trdelnik_test(args: TokenStream, input: TokenStream) -> TokenStream { // Note: The line `#(#input_fn_attrs)*` has to be above the line with the code // `#[trdelnik_client::tokio::test...` to make macros like `#[rstest]` work - // see https://github.com/la10736/rstest#inject-test-attribute - #[trdelnik_client::rstest] - #[trdelnik_client::tokio::test(flavor = "multi_thread")] - #[trdelnik_client::serial_test::serial] - async fn #input_fn_name(#input_fn_inputs) -> trdelnik_client::anyhow::Result<()> { + #[trdelnik_client::poctesting::rstest] + #[trdelnik_client::poctesting::tokio::test(flavor = "multi_thread")] + #[trdelnik_client::poctesting::serial_test::serial] + async fn #input_fn_name(#input_fn_inputs) -> Result<()> { let mut tester = trdelnik_client::Tester::with_root(#root); let localnet_handle = tester.before().await?; let test = async { #input_fn_body - Ok::<(), trdelnik_client::anyhow::Error>(()) + Ok::<(), Error>(()) }; let result = std::panic::AssertUnwindSafe(test).catch_unwind().await; tester.after(localnet_handle).await?; diff --git a/crates/test/tests/expand/basic.expanded.rs b/crates/test/tests/expand/basic.expanded.rs index 21b91070..33bcbcc7 100644 --- a/crates/test/tests/expand/basic.expanded.rs +++ b/crates/test/tests/expand/basic.expanded.rs @@ -1,7 +1,7 @@ -#[trdelnik_client::rstest] -#[trdelnik_client::tokio::test(flavor = "multi_thread")] -#[trdelnik_client::serial_test::serial] -async fn test_turnstile() -> trdelnik_client::anyhow::Result<()> { +#[trdelnik_client::poctesting::rstest] +#[trdelnik_client::poctesting::tokio::test(flavor = "multi_thread")] +#[trdelnik_client::poctesting::serial_test::serial] +async fn test_turnstile() -> Result<()> { let mut tester = trdelnik_client::Tester::with_root("../../"); let localnet_handle = tester.before().await?; let test = async { @@ -14,7 +14,7 @@ async fn test_turnstile() -> trdelnik_client::anyhow::Result<()> { turnstile.push_unlocked().await?; turnstile.push_locked().await?; } - Ok::<(), trdelnik_client::anyhow::Error>(()) + Ok::<(), Error>(()) }; let result = std::panic::AssertUnwindSafe(test).catch_unwind().await; tester.after(localnet_handle).await?; diff --git a/crates/test/tests/expand/with_root.expanded.rs b/crates/test/tests/expand/with_root.expanded.rs index 62698e8a..b0b4cc4e 100644 --- a/crates/test/tests/expand/with_root.expanded.rs +++ b/crates/test/tests/expand/with_root.expanded.rs @@ -1,12 +1,12 @@ -#[trdelnik_client::rstest] -#[trdelnik_client::tokio::test(flavor = "multi_thread")] -#[trdelnik_client::serial_test::serial] -async fn test_with_defined_root() -> trdelnik_client::anyhow::Result<()> { +#[trdelnik_client::poctesting::rstest] +#[trdelnik_client::poctesting::tokio::test(flavor = "multi_thread")] +#[trdelnik_client::poctesting::serial_test::serial] +async fn test_with_defined_root() -> Result<()> { let mut tester = trdelnik_client::Tester::with_root("i_am_root"); let localnet_handle = tester.before().await?; let test = async { {} - Ok::<(), trdelnik_client::anyhow::Error>(()) + Ok::<(), Error>(()) }; let result = std::panic::AssertUnwindSafe(test).catch_unwind().await; tester.after(localnet_handle).await?; diff --git a/examples/escrow/.program_client/src/lib.rs b/examples/escrow/.program_client/src/lib.rs index 741b0b16..5b476fd9 100644 --- a/examples/escrow/.program_client/src/lib.rs +++ b/examples/escrow/.program_client/src/lib.rs @@ -1,23 +1,28 @@ // DO NOT EDIT - automatically generated file (except `use` statements inside the `*_instruction` module pub mod escrow_instruction { - use trdelnik_client::*; - pub static PROGRAM_ID: Pubkey = Pubkey::new_from_array([ - 5u8, 214u8, 204u8, 101u8, 166u8, 163u8, 239u8, 244u8, 13u8, 110u8, 64u8, 106u8, 230u8, - 81u8, 141u8, 186u8, 208u8, 155u8, 78u8, 83u8, 194u8, 215u8, 103u8, 17u8, 94u8, 15u8, 137u8, - 68u8, 170u8, 153u8, 74u8, 59u8, - ]); + use trdelnik_client::program_client::*; + pub static PROGRAM_ID: solana_sdk::pubkey::Pubkey = + solana_sdk::pubkey::Pubkey::new_from_array([ + 5u8, 214u8, 204u8, 101u8, 166u8, 163u8, 239u8, 244u8, 13u8, 110u8, 64u8, 106u8, 230u8, + 81u8, 141u8, 186u8, 208u8, 155u8, 78u8, 83u8, 194u8, 215u8, 103u8, 17u8, 94u8, 15u8, + 137u8, 68u8, 170u8, 153u8, 74u8, 59u8, + ]); + #[allow(clippy::too_many_arguments)] pub async fn initialize_escrow( client: &Client, i_initializer_amount: u64, i_taker_amount: u64, - a_initializer: Pubkey, - a_initializer_deposit_token_account: Pubkey, - a_initializer_receive_token_account: Pubkey, - a_escrow_account: Pubkey, - a_system_program: Pubkey, - a_token_program: Pubkey, - signers: impl IntoIterator + Send + 'static, - ) -> Result { + a_initializer: &solana_sdk::pubkey::Pubkey, + a_initializer_deposit_token_account: &solana_sdk::pubkey::Pubkey, + a_initializer_receive_token_account: &solana_sdk::pubkey::Pubkey, + a_escrow_account: &solana_sdk::pubkey::Pubkey, + a_system_program: &solana_sdk::pubkey::Pubkey, + a_token_program: &solana_sdk::pubkey::Pubkey, + signers: impl IntoIterator + Send, + ) -> Result< + solana_transaction_status::EncodedConfirmedTransactionWithStatusMeta, + anchor_client::ClientError, + > { client .send_instruction( PROGRAM_ID, @@ -26,28 +31,29 @@ pub mod escrow_instruction { taker_amount: i_taker_amount, }, escrow::accounts::InitializeEscrow { - initializer: a_initializer, - initializer_deposit_token_account: a_initializer_deposit_token_account, - initializer_receive_token_account: a_initializer_receive_token_account, - escrow_account: a_escrow_account, - system_program: a_system_program, - token_program: a_token_program, + initializer: *a_initializer, + initializer_deposit_token_account: *a_initializer_deposit_token_account, + initializer_receive_token_account: *a_initializer_receive_token_account, + escrow_account: *a_escrow_account, + system_program: *a_system_program, + token_program: *a_token_program, }, signers, ) .await } + #[allow(clippy::too_many_arguments)] pub fn initialize_escrow_ix( i_initializer_amount: u64, i_taker_amount: u64, - a_initializer: Pubkey, - a_initializer_deposit_token_account: Pubkey, - a_initializer_receive_token_account: Pubkey, - a_escrow_account: Pubkey, - a_system_program: Pubkey, - a_token_program: Pubkey, - ) -> Instruction { - Instruction { + a_initializer: &solana_sdk::pubkey::Pubkey, + a_initializer_deposit_token_account: &solana_sdk::pubkey::Pubkey, + a_initializer_receive_token_account: &solana_sdk::pubkey::Pubkey, + a_escrow_account: &solana_sdk::pubkey::Pubkey, + a_system_program: &solana_sdk::pubkey::Pubkey, + a_token_program: &solana_sdk::pubkey::Pubkey, + ) -> solana_sdk::instruction::Instruction { + solana_sdk::instruction::Instruction { program_id: PROGRAM_ID, data: escrow::instruction::InitializeEscrow { initializer_amount: i_initializer_amount, @@ -55,116 +61,126 @@ pub mod escrow_instruction { } .data(), accounts: escrow::accounts::InitializeEscrow { - initializer: a_initializer, - initializer_deposit_token_account: a_initializer_deposit_token_account, - initializer_receive_token_account: a_initializer_receive_token_account, - escrow_account: a_escrow_account, - system_program: a_system_program, - token_program: a_token_program, + initializer: *a_initializer, + initializer_deposit_token_account: *a_initializer_deposit_token_account, + initializer_receive_token_account: *a_initializer_receive_token_account, + escrow_account: *a_escrow_account, + system_program: *a_system_program, + token_program: *a_token_program, } .to_account_metas(None), } } + #[allow(clippy::too_many_arguments)] pub async fn cancel_escrow( client: &Client, - a_initializer: Pubkey, - a_pda_deposit_token_account: Pubkey, - a_pda_account: Pubkey, - a_escrow_account: Pubkey, - a_token_program: Pubkey, - signers: impl IntoIterator + Send + 'static, - ) -> Result { + a_initializer: &solana_sdk::pubkey::Pubkey, + a_pda_deposit_token_account: &solana_sdk::pubkey::Pubkey, + a_pda_account: &solana_sdk::pubkey::Pubkey, + a_escrow_account: &solana_sdk::pubkey::Pubkey, + a_token_program: &solana_sdk::pubkey::Pubkey, + signers: impl IntoIterator + Send, + ) -> Result< + solana_transaction_status::EncodedConfirmedTransactionWithStatusMeta, + anchor_client::ClientError, + > { client .send_instruction( PROGRAM_ID, escrow::instruction::CancelEscrow {}, escrow::accounts::CancelEscrow { - initializer: a_initializer, - pda_deposit_token_account: a_pda_deposit_token_account, - pda_account: a_pda_account, - escrow_account: a_escrow_account, - token_program: a_token_program, + initializer: *a_initializer, + pda_deposit_token_account: *a_pda_deposit_token_account, + pda_account: *a_pda_account, + escrow_account: *a_escrow_account, + token_program: *a_token_program, }, signers, ) .await } + #[allow(clippy::too_many_arguments)] pub fn cancel_escrow_ix( - a_initializer: Pubkey, - a_pda_deposit_token_account: Pubkey, - a_pda_account: Pubkey, - a_escrow_account: Pubkey, - a_token_program: Pubkey, - ) -> Instruction { - Instruction { + a_initializer: &solana_sdk::pubkey::Pubkey, + a_pda_deposit_token_account: &solana_sdk::pubkey::Pubkey, + a_pda_account: &solana_sdk::pubkey::Pubkey, + a_escrow_account: &solana_sdk::pubkey::Pubkey, + a_token_program: &solana_sdk::pubkey::Pubkey, + ) -> solana_sdk::instruction::Instruction { + solana_sdk::instruction::Instruction { program_id: PROGRAM_ID, data: escrow::instruction::CancelEscrow {}.data(), accounts: escrow::accounts::CancelEscrow { - initializer: a_initializer, - pda_deposit_token_account: a_pda_deposit_token_account, - pda_account: a_pda_account, - escrow_account: a_escrow_account, - token_program: a_token_program, + initializer: *a_initializer, + pda_deposit_token_account: *a_pda_deposit_token_account, + pda_account: *a_pda_account, + escrow_account: *a_escrow_account, + token_program: *a_token_program, } .to_account_metas(None), } } + #[allow(clippy::too_many_arguments)] pub async fn exchange( client: &Client, - a_taker: Pubkey, - a_taker_deposit_token_account: Pubkey, - a_taker_receive_token_account: Pubkey, - a_pda_deposit_token_account: Pubkey, - a_initializer_receive_token_account: Pubkey, - a_initializer_main_account: Pubkey, - a_escrow_account: Pubkey, - a_pda_account: Pubkey, - a_token_program: Pubkey, - signers: impl IntoIterator + Send + 'static, - ) -> Result { + a_taker: &solana_sdk::pubkey::Pubkey, + a_taker_deposit_token_account: &solana_sdk::pubkey::Pubkey, + a_taker_receive_token_account: &solana_sdk::pubkey::Pubkey, + a_pda_deposit_token_account: &solana_sdk::pubkey::Pubkey, + a_initializer_receive_token_account: &solana_sdk::pubkey::Pubkey, + a_initializer_main_account: &solana_sdk::pubkey::Pubkey, + a_escrow_account: &solana_sdk::pubkey::Pubkey, + a_pda_account: &solana_sdk::pubkey::Pubkey, + a_token_program: &solana_sdk::pubkey::Pubkey, + signers: impl IntoIterator + Send, + ) -> Result< + solana_transaction_status::EncodedConfirmedTransactionWithStatusMeta, + anchor_client::ClientError, + > { client .send_instruction( PROGRAM_ID, escrow::instruction::Exchange {}, escrow::accounts::Exchange { - taker: a_taker, - taker_deposit_token_account: a_taker_deposit_token_account, - taker_receive_token_account: a_taker_receive_token_account, - pda_deposit_token_account: a_pda_deposit_token_account, - initializer_receive_token_account: a_initializer_receive_token_account, - initializer_main_account: a_initializer_main_account, - escrow_account: a_escrow_account, - pda_account: a_pda_account, - token_program: a_token_program, + taker: *a_taker, + taker_deposit_token_account: *a_taker_deposit_token_account, + taker_receive_token_account: *a_taker_receive_token_account, + pda_deposit_token_account: *a_pda_deposit_token_account, + initializer_receive_token_account: *a_initializer_receive_token_account, + initializer_main_account: *a_initializer_main_account, + escrow_account: *a_escrow_account, + pda_account: *a_pda_account, + token_program: *a_token_program, }, signers, ) .await } + #[allow(clippy::too_many_arguments)] pub fn exchange_ix( - a_taker: Pubkey, - a_taker_deposit_token_account: Pubkey, - a_taker_receive_token_account: Pubkey, - a_pda_deposit_token_account: Pubkey, - a_initializer_receive_token_account: Pubkey, - a_initializer_main_account: Pubkey, - a_escrow_account: Pubkey, - a_pda_account: Pubkey, - a_token_program: Pubkey, - ) -> Instruction { - Instruction { + a_taker: &solana_sdk::pubkey::Pubkey, + a_taker_deposit_token_account: &solana_sdk::pubkey::Pubkey, + a_taker_receive_token_account: &solana_sdk::pubkey::Pubkey, + a_pda_deposit_token_account: &solana_sdk::pubkey::Pubkey, + a_initializer_receive_token_account: &solana_sdk::pubkey::Pubkey, + a_initializer_main_account: &solana_sdk::pubkey::Pubkey, + a_escrow_account: &solana_sdk::pubkey::Pubkey, + a_pda_account: &solana_sdk::pubkey::Pubkey, + a_token_program: &solana_sdk::pubkey::Pubkey, + ) -> solana_sdk::instruction::Instruction { + solana_sdk::instruction::Instruction { program_id: PROGRAM_ID, data: escrow::instruction::Exchange {}.data(), accounts: escrow::accounts::Exchange { - taker: a_taker, - taker_deposit_token_account: a_taker_deposit_token_account, - taker_receive_token_account: a_taker_receive_token_account, - pda_deposit_token_account: a_pda_deposit_token_account, - initializer_receive_token_account: a_initializer_receive_token_account, - initializer_main_account: a_initializer_main_account, - escrow_account: a_escrow_account, - pda_account: a_pda_account, - token_program: a_token_program, + taker: *a_taker, + taker_deposit_token_account: *a_taker_deposit_token_account, + taker_receive_token_account: *a_taker_receive_token_account, + pda_deposit_token_account: *a_pda_deposit_token_account, + initializer_receive_token_account: *a_initializer_receive_token_account, + initializer_main_account: *a_initializer_main_account, + escrow_account: *a_escrow_account, + pda_account: *a_pda_account, + token_program: *a_token_program, } .to_account_metas(None), } diff --git a/examples/escrow/trdelnik-tests/poc_tests/Cargo.toml b/examples/escrow/trdelnik-tests/poc_tests/Cargo.toml index 6083c8da..87ffc4a1 100644 --- a/examples/escrow/trdelnik-tests/poc_tests/Cargo.toml +++ b/examples/escrow/trdelnik-tests/poc_tests/Cargo.toml @@ -4,13 +4,11 @@ version = "0.1.0" edition = "2018" [dev-dependencies] -assert_matches = "1.4.0" fehler = "1.0.0" anchor-spl = "0.28.0" -rstest = "0.12.0" - [dev-dependencies.trdelnik-client] path = "../../../../crates/client" +features = ["poctesting"] [dev-dependencies.program_client] path = "../../.program_client" diff --git a/examples/escrow/trdelnik-tests/poc_tests/tests/test.rs b/examples/escrow/trdelnik-tests/poc_tests/tests/test.rs index c7e3b090..58cd499f 100644 --- a/examples/escrow/trdelnik-tests/poc_tests/tests/test.rs +++ b/examples/escrow/trdelnik-tests/poc_tests/tests/test.rs @@ -1,8 +1,9 @@ +use program_client::escrow_instruction::*; +use trdelnik_client::poctesting::*; + use anchor_spl::token; -use fehler::throws; -use program_client::escrow_instruction; -use trdelnik_client::{anyhow::Result, *}; +// @todo: create and deploy your fixture #[throws] #[fixture] async fn init_fixture() -> Fixture { @@ -10,52 +11,52 @@ async fn init_fixture() -> Fixture { // Deploy fixture.deploy().await?; // Create a PDA authority - fixture.pda = Pubkey::find_program_address(&[b"escrow"], &escrow::id()).0; + fixture.pda = solana_sdk::pubkey::Pubkey::find_program_address(&[b"escrow"], &escrow::id()).0; // Creation of token mint A fixture .client - .create_token_mint(&fixture.mint_a, fixture.mint_authority.pubkey(), None, 0) + .create_token_mint(&fixture.mint_a, &fixture.mint_authority.pubkey(), None, 0) .await?; // Creation of token mint B fixture .client - .create_token_mint(&fixture.mint_b, fixture.mint_authority.pubkey(), None, 0) + .create_token_mint(&fixture.mint_b, &fixture.mint_authority.pubkey(), None, 0) .await?; // Creation of alice's and bob's ATAs for token A fixture.alice_token_a_account = fixture .client - .create_associated_token_account(&fixture.alice_wallet, fixture.mint_a.pubkey()) + .create_associated_token_account(&fixture.alice_wallet, &fixture.mint_a.pubkey()) .await?; fixture.bob_token_a_account = fixture .client - .create_associated_token_account(&fixture.bob_wallet, fixture.mint_a.pubkey()) + .create_associated_token_account(&fixture.bob_wallet, &fixture.mint_a.pubkey()) .await?; // Creation of alice's and bob's ATAs for token B fixture.alice_token_b_account = fixture .client - .create_associated_token_account(&fixture.alice_wallet, fixture.mint_b.pubkey()) + .create_associated_token_account(&fixture.alice_wallet, &fixture.mint_b.pubkey()) .await?; fixture.bob_token_b_account = fixture .client - .create_associated_token_account(&fixture.bob_wallet, fixture.mint_b.pubkey()) + .create_associated_token_account(&fixture.bob_wallet, &fixture.mint_b.pubkey()) .await?; // Mint some tokens fixture .client .mint_tokens( - fixture.mint_a.pubkey(), + &fixture.mint_a.pubkey(), &fixture.mint_authority, - fixture.alice_token_a_account, + &fixture.alice_token_a_account, 500, ) .await?; fixture .client .mint_tokens( - fixture.mint_b.pubkey(), + &fixture.mint_b.pubkey(), &fixture.mint_authority, - fixture.bob_token_b_account, + &fixture.bob_token_b_account, 1000, ) .await?; @@ -68,17 +69,17 @@ async fn test_happy_path1(#[future] init_fixture: Result) { let fixture = init_fixture.await?; // Initialize escrow - escrow_instruction::initialize_escrow( + initialize_escrow( &fixture.client, 500, 1000, - fixture.alice_wallet.pubkey(), - fixture.alice_token_a_account, - fixture.alice_token_b_account, - fixture.escrow_account.pubkey(), - System::id(), - token::ID, - [fixture.alice_wallet.clone(), fixture.escrow_account.clone()], + &fixture.alice_wallet.pubkey(), + &fixture.alice_token_a_account, + &fixture.alice_token_b_account, + &fixture.escrow_account.pubkey(), + &solana_sdk::system_program::id(), + &token::ID, + [&fixture.alice_wallet, &fixture.escrow_account], ) .await?; @@ -101,18 +102,18 @@ async fn test_happy_path1(#[future] init_fixture: Result) { ); // Exchange - escrow_instruction::exchange( + exchange( &fixture.client, - fixture.bob_wallet.pubkey(), - fixture.bob_token_b_account, - fixture.bob_token_a_account, - fixture.alice_token_a_account, - fixture.alice_token_b_account, - fixture.alice_wallet.pubkey(), - fixture.escrow_account.pubkey(), - fixture.pda, - token::ID, - [fixture.bob_wallet.clone()], + &fixture.bob_wallet.pubkey(), + &fixture.bob_token_b_account, + &fixture.bob_token_a_account, + &fixture.alice_token_a_account, + &fixture.alice_token_b_account, + &fixture.alice_wallet.pubkey(), + &fixture.escrow_account.pubkey(), + &fixture.pda, + &token::ID, + [&fixture.bob_wallet], ) .await?; @@ -141,29 +142,29 @@ async fn test_happy_path2(#[future] init_fixture: Result) { let fixture = init_fixture.await?; // Initialize escrow - escrow_instruction::initialize_escrow( + initialize_escrow( &fixture.client, 500, 1000, - fixture.alice_wallet.pubkey(), - fixture.alice_token_a_account, - fixture.alice_token_b_account, - fixture.escrow_account.pubkey(), - System::id(), - token::ID, - [fixture.alice_wallet.clone(), fixture.escrow_account.clone()], + &fixture.alice_wallet.pubkey(), + &fixture.alice_token_a_account, + &fixture.alice_token_b_account, + &fixture.escrow_account.pubkey(), + &solana_sdk::system_program::id(), + &token::ID, + [&fixture.alice_wallet, &fixture.escrow_account], ) .await?; // Cancel - escrow_instruction::cancel_escrow( + cancel_escrow( &fixture.client, - fixture.alice_wallet.pubkey(), - fixture.alice_token_a_account, - fixture.pda, - fixture.escrow_account.pubkey(), - token::ID, - [], + &fixture.alice_wallet.pubkey(), + &fixture.alice_token_a_account, + &fixture.pda, + &fixture.escrow_account.pubkey(), + &token::ID, + &[], ) .await?; @@ -177,23 +178,23 @@ async fn test_happy_path2(#[future] init_fixture: Result) { struct Fixture { client: Client, - program: Keypair, + program: solana_sdk::signer::keypair::Keypair, // Mint stuff - mint_a: Keypair, - mint_b: Keypair, - mint_authority: Keypair, + mint_a: solana_sdk::signer::keypair::Keypair, + mint_b: solana_sdk::signer::keypair::Keypair, + mint_authority: solana_sdk::signer::keypair::Keypair, // Escrow - escrow_account: Keypair, + escrow_account: solana_sdk::signer::keypair::Keypair, // Participants - alice_wallet: Keypair, - bob_wallet: Keypair, + alice_wallet: solana_sdk::signer::keypair::Keypair, + bob_wallet: solana_sdk::signer::keypair::Keypair, // Token accounts - alice_token_a_account: Pubkey, - alice_token_b_account: Pubkey, - bob_token_a_account: Pubkey, - bob_token_b_account: Pubkey, + alice_token_a_account: solana_sdk::pubkey::Pubkey, + alice_token_b_account: solana_sdk::pubkey::Pubkey, + bob_token_a_account: solana_sdk::pubkey::Pubkey, + bob_token_b_account: solana_sdk::pubkey::Pubkey, // PDA authority of escrow - pda: Pubkey, + pda: solana_sdk::pubkey::Pubkey, } impl Fixture { fn new() -> Self { @@ -217,34 +218,34 @@ impl Fixture { alice_wallet: keypair(21), bob_wallet: keypair(22), - alice_token_a_account: Pubkey::default(), - alice_token_b_account: Pubkey::default(), - bob_token_a_account: Pubkey::default(), - bob_token_b_account: Pubkey::default(), + alice_token_a_account: solana_sdk::pubkey::Pubkey::default(), + alice_token_b_account: solana_sdk::pubkey::Pubkey::default(), + bob_token_a_account: solana_sdk::pubkey::Pubkey::default(), + bob_token_b_account: solana_sdk::pubkey::Pubkey::default(), - pda: Pubkey::default(), + pda: solana_sdk::pubkey::Pubkey::default(), } } #[throws] async fn deploy(&mut self) { self.client - .airdrop(self.alice_wallet.pubkey(), 5_000_000_000) - .await?; - self.client - .deploy_by_name(&self.program.clone(), "escrow") + .airdrop(&self.alice_wallet.pubkey(), 5_000_000_000) .await?; + self.client.deploy_by_name(&self.program, "escrow").await?; } #[throws] async fn get_escrow(&self) -> escrow::EscrowAccount { self.client - .account_data::(self.escrow_account.pubkey()) + .account_data::(&self.escrow_account.pubkey()) .await? } #[throws] - async fn get_token_account(&self, key: Pubkey) -> token::TokenAccount { - self.client.account_data::(key).await? + async fn get_token_account(&self, key: solana_sdk::pubkey::Pubkey) -> token::TokenAccount { + self.client + .account_data::(&key) + .await? } } diff --git a/examples/fuzzer/.program_client/src/lib.rs b/examples/fuzzer/.program_client/src/lib.rs index 3c870404..b79ef380 100644 --- a/examples/fuzzer/.program_client/src/lib.rs +++ b/examples/fuzzer/.program_client/src/lib.rs @@ -1,55 +1,65 @@ // DO NOT EDIT - automatically generated file (except `use` statements inside the `*_instruction` module pub mod fuzzer_instruction { - use trdelnik_client::*; - pub static PROGRAM_ID: Pubkey = Pubkey::new_from_array([ - 170u8, 64u8, 48u8, 229u8, 53u8, 121u8, 89u8, 247u8, 36u8, 222u8, 119u8, 168u8, 36u8, 42u8, - 8u8, 162u8, 161u8, 90u8, 85u8, 0u8, 151u8, 100u8, 169u8, 133u8, 216u8, 142u8, 250u8, 145u8, - 26u8, 46u8, 170u8, 146u8, - ]); + use trdelnik_client::program_client::*; + pub static PROGRAM_ID: solana_sdk::pubkey::Pubkey = + solana_sdk::pubkey::Pubkey::new_from_array([ + 170u8, 64u8, 48u8, 229u8, 53u8, 121u8, 89u8, 247u8, 36u8, 222u8, 119u8, 168u8, 36u8, + 42u8, 8u8, 162u8, 161u8, 90u8, 85u8, 0u8, 151u8, 100u8, 169u8, 133u8, 216u8, 142u8, + 250u8, 145u8, 26u8, 46u8, 170u8, 146u8, + ]); + #[allow(clippy::too_many_arguments)] pub async fn initialize( client: &Client, - a_counter: Pubkey, - a_user: Pubkey, - a_system_program: Pubkey, - signers: impl IntoIterator + Send + 'static, - ) -> Result { + a_counter: &solana_sdk::pubkey::Pubkey, + a_user: &solana_sdk::pubkey::Pubkey, + a_system_program: &solana_sdk::pubkey::Pubkey, + signers: impl IntoIterator + Send, + ) -> Result< + solana_transaction_status::EncodedConfirmedTransactionWithStatusMeta, + anchor_client::ClientError, + > { client .send_instruction( PROGRAM_ID, fuzzer::instruction::Initialize {}, fuzzer::accounts::Initialize { - counter: a_counter, - user: a_user, - system_program: a_system_program, + counter: *a_counter, + user: *a_user, + system_program: *a_system_program, }, signers, ) .await } + #[allow(clippy::too_many_arguments)] pub fn initialize_ix( - a_counter: Pubkey, - a_user: Pubkey, - a_system_program: Pubkey, - ) -> Instruction { - Instruction { + a_counter: &solana_sdk::pubkey::Pubkey, + a_user: &solana_sdk::pubkey::Pubkey, + a_system_program: &solana_sdk::pubkey::Pubkey, + ) -> solana_sdk::instruction::Instruction { + solana_sdk::instruction::Instruction { program_id: PROGRAM_ID, data: fuzzer::instruction::Initialize {}.data(), accounts: fuzzer::accounts::Initialize { - counter: a_counter, - user: a_user, - system_program: a_system_program, + counter: *a_counter, + user: *a_user, + system_program: *a_system_program, } .to_account_metas(None), } } + #[allow(clippy::too_many_arguments)] pub async fn update( client: &Client, i_input1: u8, i_input2: u8, - a_counter: Pubkey, - a_authority: Pubkey, - signers: impl IntoIterator + Send + 'static, - ) -> Result { + a_counter: &solana_sdk::pubkey::Pubkey, + a_authority: &solana_sdk::pubkey::Pubkey, + signers: impl IntoIterator + Send, + ) -> Result< + solana_transaction_status::EncodedConfirmedTransactionWithStatusMeta, + anchor_client::ClientError, + > { client .send_instruction( PROGRAM_ID, @@ -58,20 +68,21 @@ pub mod fuzzer_instruction { input2: i_input2, }, fuzzer::accounts::Update { - counter: a_counter, - authority: a_authority, + counter: *a_counter, + authority: *a_authority, }, signers, ) .await } + #[allow(clippy::too_many_arguments)] pub fn update_ix( i_input1: u8, i_input2: u8, - a_counter: Pubkey, - a_authority: Pubkey, - ) -> Instruction { - Instruction { + a_counter: &solana_sdk::pubkey::Pubkey, + a_authority: &solana_sdk::pubkey::Pubkey, + ) -> solana_sdk::instruction::Instruction { + solana_sdk::instruction::Instruction { program_id: PROGRAM_ID, data: fuzzer::instruction::Update { input1: i_input1, @@ -79,8 +90,8 @@ pub mod fuzzer_instruction { } .data(), accounts: fuzzer::accounts::Update { - counter: a_counter, - authority: a_authority, + counter: *a_counter, + authority: *a_authority, } .to_account_metas(None), } diff --git a/examples/fuzzer/trdelnik-tests/fuzz_tests/fuzz_0/test_fuzz.rs b/examples/fuzzer/trdelnik-tests/fuzz_tests/fuzz_0/test_fuzz.rs index 846ffea4..e5c14251 100644 --- a/examples/fuzzer/trdelnik-tests/fuzz_tests/fuzz_0/test_fuzz.rs +++ b/examples/fuzzer/trdelnik-tests/fuzz_tests/fuzz_0/test_fuzz.rs @@ -1,6 +1,6 @@ +use assert_matches::*; use fuzzer::entry; use program_client::fuzzer_instruction::*; -use assert_matches::*; use trdelnik_client::fuzzing::*; const PROGRAM_NAME: &str = "fuzzer"; @@ -14,58 +14,67 @@ pub struct FuzzData { fn main() { loop { fuzz!(|fuzz_data: FuzzData| { - Runtime::new().unwrap().block_on(async { - let program_test = ProgramTest::new(PROGRAM_NAME, PROGRAM_ID, processor!(entry)); + solana_program_test::tokio::runtime::Runtime::new() + .unwrap() + .block_on(async { + let program_test = solana_program_test::ProgramTest::new( + PROGRAM_NAME, + PROGRAM_ID, + solana_program_test::processor!(entry), + ); - let mut ctx = program_test.start_with_context().await; + let mut ctx = program_test.start_with_context().await; - // TODO: replace this instruction with one of your generated instructions from trdelnik_client - let init_ix = init_dummy_ix(); - let mut transaction = - Transaction::new_with_payer(&[init_ix], Some(&ctx.payer.pubkey().clone())); + // TODO: replace this instruction with one of your generated instructions from trdelnik_client + let init_ix = init_dummy_ix(); + let mut transaction = solana_sdk::transaction::Transaction::new_with_payer( + &[init_ix], + Some(&ctx.payer.pubkey()), + ); - transaction.sign(&[&ctx.payer], ctx.last_blockhash); - let res = ctx.banks_client.process_transaction(transaction).await; - assert_matches!(res, Ok(())); + transaction.sign(&[&ctx.payer], ctx.last_blockhash); + let res = ctx.banks_client.process_transaction(transaction).await; + assert_matches!(res, Ok(())); - let res = fuzz_ix( - &fuzz_data, - &mut ctx.banks_client, - &ctx.payer, - ctx.last_blockhash, - ) - .await; - assert_matches!(res, Ok(())); - }); + let res = fuzz_ix( + &fuzz_data, + &mut ctx.banks_client, + &ctx.payer, + ctx.last_blockhash, + ) + .await; + assert_matches!(res, Ok(())); + }); }); } } async fn fuzz_ix( fuzz_data: &FuzzData, - banks_client: &mut BanksClient, - payer: &Keypair, - blockhash: Hash, -) -> core::result::Result<(), BanksClientError> { + banks_client: &mut solana_program_test::BanksClient, + payer: &solana_sdk::signature::Keypair, + blockhash: solana_sdk::hash::Hash, +) -> core::result::Result<(), solana_program_test::BanksClientError> { // TODO: replace this instruction with one of your generated instructions from trdelnik_client let update_ix = update_dummy_ix(fuzz_data.param1, fuzz_data.param2); - let mut transaction = Transaction::new_with_payer(&[update_ix], Some(&payer.pubkey())); + let mut transaction = + solana_sdk::transaction::Transaction::new_with_payer(&[update_ix], Some(&payer.pubkey())); transaction.sign(&[payer], blockhash); banks_client.process_transaction(transaction).await } -fn init_dummy_ix() -> Instruction { - Instruction { +fn init_dummy_ix() -> solana_sdk::instruction::Instruction { + solana_sdk::instruction::Instruction { program_id: PROGRAM_ID, data: vec![], accounts: vec![], } } -fn update_dummy_ix(param1: u8, param2: u8) -> Instruction { - Instruction { +fn update_dummy_ix(param1: u8, param2: u8) -> solana_sdk::instruction::Instruction { + solana_sdk::instruction::Instruction { program_id: PROGRAM_ID, data: vec![param1, param2], accounts: vec![], diff --git a/examples/fuzzer/trdelnik-tests/fuzz_tests/fuzz_1/test_fuzz.rs b/examples/fuzzer/trdelnik-tests/fuzz_tests/fuzz_1/test_fuzz.rs index 48f676e3..75dda0a5 100644 --- a/examples/fuzzer/trdelnik-tests/fuzz_tests/fuzz_1/test_fuzz.rs +++ b/examples/fuzzer/trdelnik-tests/fuzz_tests/fuzz_1/test_fuzz.rs @@ -14,51 +14,63 @@ pub struct FuzzData { fn main() { loop { fuzz!(|fuzz_data: FuzzData| { - Runtime::new().unwrap().block_on(async { - let program_test = ProgramTest::new(PROGRAM_NAME, PROGRAM_ID, processor!(entry)); + solana_program_test::tokio::runtime::Runtime::new() + .unwrap() + .block_on(async { + let program_test = solana_program_test::ProgramTest::new( + PROGRAM_NAME, + PROGRAM_ID, + solana_program_test::processor!(entry), + ); - let mut ctx = program_test.start_with_context().await; + let mut ctx = program_test.start_with_context().await; - let counter = Keypair::new(); + let counter = solana_sdk::signature::Keypair::new(); - let init_ix = - initialize_ix(counter.pubkey(), ctx.payer.pubkey(), SYSTEM_PROGRAM_ID); - let mut transaction = - Transaction::new_with_payer(&[init_ix], Some(&ctx.payer.pubkey().clone())); + let init_ix = initialize_ix( + &counter.pubkey(), + &ctx.payer.pubkey(), + &solana_sdk::system_program::ID, + ); + let mut transaction = solana_sdk::transaction::Transaction::new_with_payer( + &[init_ix], + Some(&ctx.payer.pubkey()), + ); - transaction.sign(&[&ctx.payer, &counter], ctx.last_blockhash); - let res = ctx.banks_client.process_transaction(transaction).await; - assert_matches!(res, Ok(())); + transaction.sign(&[&ctx.payer, &counter], ctx.last_blockhash); + let res = ctx.banks_client.process_transaction(transaction).await; + assert_matches!(res, Ok(())); - let res = fuzz_update_ix( - &fuzz_data, - &mut ctx.banks_client, - &ctx.payer, - &counter, - ctx.last_blockhash, - ) - .await; - assert_matches!(res, Ok(())); - }); + let res = fuzz_update_ix( + &fuzz_data, + &mut ctx.banks_client, + &ctx.payer, + &counter, + ctx.last_blockhash, + ) + .await; + assert_matches!(res, Ok(())); + }); }); } } async fn fuzz_update_ix( fuzz_data: &FuzzData, - banks_client: &mut BanksClient, - payer: &Keypair, - counter: &Keypair, - blockhash: Hash, -) -> core::result::Result<(), BanksClientError> { + banks_client: &mut solana_program_test::BanksClient, + payer: &solana_sdk::signature::Keypair, + counter: &solana_sdk::signature::Keypair, + blockhash: solana_sdk::hash::Hash, +) -> core::result::Result<(), solana_program_test::BanksClientError> { let update_ix = update_ix( fuzz_data.param1, fuzz_data.param2, - counter.pubkey(), - payer.pubkey(), + &counter.pubkey(), + &payer.pubkey(), ); - let mut transaction = Transaction::new_with_payer(&[update_ix], Some(&payer.pubkey())); + let mut transaction = + solana_sdk::transaction::Transaction::new_with_payer(&[update_ix], Some(&payer.pubkey())); transaction.sign(&[payer], blockhash); banks_client.process_transaction(transaction).await diff --git a/examples/turnstile/.program_client/src/lib.rs b/examples/turnstile/.program_client/src/lib.rs index abbc63d1..1f6059f6 100644 --- a/examples/turnstile/.program_client/src/lib.rs +++ b/examples/turnstile/.program_client/src/lib.rs @@ -1,89 +1,112 @@ // DO NOT EDIT - automatically generated file (except `use` statements inside the `*_instruction` module pub mod turnstile_instruction { - use trdelnik_client::*; - pub static PROGRAM_ID: Pubkey = Pubkey::new_from_array([ - 5u8, 214u8, 204u8, 101u8, 166u8, 163u8, 239u8, 244u8, 13u8, 110u8, 64u8, 106u8, 230u8, - 81u8, 141u8, 186u8, 208u8, 155u8, 78u8, 83u8, 194u8, 215u8, 103u8, 17u8, 94u8, 15u8, 137u8, - 68u8, 170u8, 153u8, 74u8, 59u8, - ]); + use trdelnik_client::program_client::*; + pub static PROGRAM_ID: solana_sdk::pubkey::Pubkey = + solana_sdk::pubkey::Pubkey::new_from_array([ + 5u8, 214u8, 204u8, 101u8, 166u8, 163u8, 239u8, 244u8, 13u8, 110u8, 64u8, 106u8, 230u8, + 81u8, 141u8, 186u8, 208u8, 155u8, 78u8, 83u8, 194u8, 215u8, 103u8, 17u8, 94u8, 15u8, + 137u8, 68u8, 170u8, 153u8, 74u8, 59u8, + ]); + #[allow(clippy::too_many_arguments)] pub async fn initialize( client: &Client, - a_state: Pubkey, - a_user: Pubkey, - a_system_program: Pubkey, - signers: impl IntoIterator + Send + 'static, - ) -> Result { + a_state: &solana_sdk::pubkey::Pubkey, + a_user: &solana_sdk::pubkey::Pubkey, + a_system_program: &solana_sdk::pubkey::Pubkey, + signers: impl IntoIterator + Send, + ) -> Result< + solana_transaction_status::EncodedConfirmedTransactionWithStatusMeta, + anchor_client::ClientError, + > { client .send_instruction( PROGRAM_ID, turnstile::instruction::Initialize {}, turnstile::accounts::Initialize { - state: a_state, - user: a_user, - system_program: a_system_program, + state: *a_state, + user: *a_user, + system_program: *a_system_program, }, signers, ) .await } - pub fn initialize_ix(a_state: Pubkey, a_user: Pubkey, a_system_program: Pubkey) -> Instruction { - Instruction { + #[allow(clippy::too_many_arguments)] + pub fn initialize_ix( + a_state: &solana_sdk::pubkey::Pubkey, + a_user: &solana_sdk::pubkey::Pubkey, + a_system_program: &solana_sdk::pubkey::Pubkey, + ) -> solana_sdk::instruction::Instruction { + solana_sdk::instruction::Instruction { program_id: PROGRAM_ID, data: turnstile::instruction::Initialize {}.data(), accounts: turnstile::accounts::Initialize { - state: a_state, - user: a_user, - system_program: a_system_program, + state: *a_state, + user: *a_user, + system_program: *a_system_program, } .to_account_metas(None), } } + #[allow(clippy::too_many_arguments)] pub async fn coin( client: &Client, i_dummy_arg: String, - a_state: Pubkey, - signers: impl IntoIterator + Send + 'static, - ) -> Result { + a_state: &solana_sdk::pubkey::Pubkey, + signers: impl IntoIterator + Send, + ) -> Result< + solana_transaction_status::EncodedConfirmedTransactionWithStatusMeta, + anchor_client::ClientError, + > { client .send_instruction( PROGRAM_ID, turnstile::instruction::Coin { dummy_arg: i_dummy_arg, }, - turnstile::accounts::UpdateState { state: a_state }, + turnstile::accounts::UpdateState { state: *a_state }, signers, ) .await } - pub fn coin_ix(i_dummy_arg: String, a_state: Pubkey) -> Instruction { - Instruction { + #[allow(clippy::too_many_arguments)] + pub fn coin_ix( + i_dummy_arg: String, + a_state: &solana_sdk::pubkey::Pubkey, + ) -> solana_sdk::instruction::Instruction { + solana_sdk::instruction::Instruction { program_id: PROGRAM_ID, data: turnstile::instruction::Coin { dummy_arg: i_dummy_arg, } .data(), - accounts: turnstile::accounts::UpdateState { state: a_state }.to_account_metas(None), + accounts: turnstile::accounts::UpdateState { state: *a_state }.to_account_metas(None), } } + #[allow(clippy::too_many_arguments)] pub async fn push( client: &Client, - a_state: Pubkey, - signers: impl IntoIterator + Send + 'static, - ) -> Result { + a_state: &solana_sdk::pubkey::Pubkey, + signers: impl IntoIterator + Send, + ) -> Result< + solana_transaction_status::EncodedConfirmedTransactionWithStatusMeta, + anchor_client::ClientError, + > { client .send_instruction( PROGRAM_ID, turnstile::instruction::Push {}, - turnstile::accounts::UpdateState { state: a_state }, + turnstile::accounts::UpdateState { state: *a_state }, signers, ) .await } - pub fn push_ix(a_state: Pubkey) -> Instruction { - Instruction { + #[allow(clippy::too_many_arguments)] + pub fn push_ix(a_state: &solana_sdk::pubkey::Pubkey) -> solana_sdk::instruction::Instruction { + solana_sdk::instruction::Instruction { program_id: PROGRAM_ID, data: turnstile::instruction::Push {}.data(), - accounts: turnstile::accounts::UpdateState { state: a_state }.to_account_metas(None), + accounts: turnstile::accounts::UpdateState { state: *a_state }.to_account_metas(None), } } } diff --git a/examples/turnstile/trdelnik-tests/poc_tests/Cargo.toml b/examples/turnstile/trdelnik-tests/poc_tests/Cargo.toml index fba8eae4..2bfb6dc1 100644 --- a/examples/turnstile/trdelnik-tests/poc_tests/Cargo.toml +++ b/examples/turnstile/trdelnik-tests/poc_tests/Cargo.toml @@ -4,12 +4,11 @@ version = "0.1.0" edition = "2018" [dev-dependencies] -assert_matches = "1.4.0" fehler = "1.0.0" -rstest = "0.12.0" [dev-dependencies.trdelnik-client] path = "../../../../crates/client" +features = ["poctesting"] [dev-dependencies.program_client] path = "../../.program_client" diff --git a/examples/turnstile/trdelnik-tests/poc_tests/tests/test.rs b/examples/turnstile/trdelnik-tests/poc_tests/tests/test.rs index 729dd077..0cfbe80d 100644 --- a/examples/turnstile/trdelnik-tests/poc_tests/tests/test.rs +++ b/examples/turnstile/trdelnik-tests/poc_tests/tests/test.rs @@ -1,7 +1,7 @@ -use fehler::throws; use program_client::turnstile_instruction::*; -use trdelnik_client::{anyhow::Result, *}; +use trdelnik_client::poctesting::*; +// @todo: create and deploy your fixture #[throws] #[fixture] async fn init_fixture() -> Fixture { @@ -22,7 +22,7 @@ async fn init_fixture() -> Fixture { }; fixture .client - .airdrop(fixture.user_initializer.pubkey(), 5_000_000_000) + .airdrop(&fixture.user_initializer.pubkey(), 5_000_000_000) .await?; // deploy a tested program fixture @@ -33,10 +33,10 @@ async fn init_fixture() -> Fixture { // init instruction call initialize( &fixture.client, - fixture.state.pubkey(), - fixture.user_initializer.pubkey(), - System::id(), - [fixture.state.clone(), fixture.user_initializer.clone()], + &fixture.state.pubkey(), + &fixture.user_initializer.pubkey(), + &solana_sdk::system_program::id(), + [&fixture.state, &fixture.user_initializer], ) .await?; @@ -51,12 +51,12 @@ async fn test_happy_path(#[future] init_fixture: Result) { coin( &fixture.client, "dummy_string".to_owned(), - fixture.state.pubkey(), + &fixture.state.pubkey(), None, ) .await?; // push instruction call - push(&fixture.client, fixture.state.pubkey(), None).await?; + push(&fixture.client, &fixture.state.pubkey(), None).await?; // check the test result let state = fixture.get_state().await?; @@ -72,7 +72,7 @@ async fn test_unhappy_path(#[future] init_fixture: Result) { let fixture = init_fixture.await?; // pushing without prior coin insertion - push(&fixture.client, fixture.state.pubkey(), None).await?; + push(&fixture.client, &fixture.state.pubkey(), None).await?; // check the test result let state = fixture.get_state().await?; @@ -85,14 +85,14 @@ async fn test_unhappy_path(#[future] init_fixture: Result) { struct Fixture { client: Client, - program: Keypair, - state: Keypair, - user_initializer: Keypair, + program: solana_sdk::signer::keypair::Keypair, + state: solana_sdk::signer::keypair::Keypair, + user_initializer: solana_sdk::signer::keypair::Keypair, } impl Fixture { #[throws] async fn get_state(&self) -> turnstile::State { - self.client.account_data(self.state.pubkey()).await? + self.client.account_data(&self.state.pubkey()).await? } }