diff --git a/Cargo.lock b/Cargo.lock index bb7473a266a052..021075e8425072 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5178,12 +5178,14 @@ version = "1.17.0" dependencies = [ "clap 2.33.3", "crossbeam-channel", + "itertools", "log", "rand 0.7.3", "rayon", "serde_json", "serde_yaml 0.9.21", "serial_test", + "solana-address-lookup-table-program", "solana-clap-utils", "solana-cli-config", "solana-client", diff --git a/bench-tps/Cargo.toml b/bench-tps/Cargo.toml index 803e820a5bd576..b2fe285f4b0654 100644 --- a/bench-tps/Cargo.toml +++ b/bench-tps/Cargo.toml @@ -11,11 +11,13 @@ edition = { workspace = true } [dependencies] clap = { workspace = true } crossbeam-channel = { workspace = true } +itertools = { workspace = true } log = { workspace = true } rand = { workspace = true } rayon = { workspace = true } serde_json = { workspace = true } serde_yaml = { workspace = true } +solana-address-lookup-table-program = { workspace = true } solana-clap-utils = { workspace = true } solana-cli-config = { workspace = true } solana-client = { workspace = true } diff --git a/bench-tps/src/address_table_lookup.rs b/bench-tps/src/address_table_lookup.rs new file mode 100644 index 00000000000000..3b65388b7941a4 --- /dev/null +++ b/bench-tps/src/address_table_lookup.rs @@ -0,0 +1,118 @@ +use { + crate::bench_tps_client::*, + itertools::Itertools, + log::*, + solana_address_lookup_table_program::{ + instruction::{create_lookup_table, extend_lookup_table}, + state::AddressLookupTable, + }, + solana_sdk::{ + commitment_config::CommitmentConfig, + hash::Hash, + pubkey::Pubkey, + signature::{Keypair, Signer}, + slot_history::Slot, + transaction::Transaction, + }, + std::{sync::Arc, thread::sleep, time::Duration}, +}; + +/// To create a lookup-table account via `client`, and extend it with `num_addresses`. +pub fn create_address_lookup_table_account( + client: Arc, + funding_key: &Keypair, + num_addresses: usize, +) -> Result { + let (transaction, lookup_table_address) = build_create_lookup_table_tx( + funding_key, + client.get_slot().unwrap_or(0), + client.get_latest_blockhash().unwrap(), + ); + send_and_confirm_transaction(client.clone(), transaction, &lookup_table_address); + + // Due to legacy transaction's size limits, the number of addresses to be added by one + // `extend_lookup_table` transaction is also limited. If `num_addresses` is greater + // than NUMBER_OF_ADDRESSES_PER_EXTEND, multiple extending are required. + const NUMBER_OF_ADDRESSES_PER_EXTEND: usize = 16; + + let mut i: usize = 0; + while i < num_addresses { + let extend_num_addresses = NUMBER_OF_ADDRESSES_PER_EXTEND.min(num_addresses - i); + i += extend_num_addresses; + + let transaction = build_extend_lookup_table_tx( + &lookup_table_address, + funding_key, + extend_num_addresses, + client.get_latest_blockhash().unwrap(), + ); + send_and_confirm_transaction(client.clone(), transaction, &lookup_table_address); + } + + Ok(lookup_table_address) +} + +fn build_create_lookup_table_tx( + funding_key: &Keypair, + recent_slot: Slot, + recent_blockhash: Hash, +) -> (Transaction, Pubkey) { + let (create_lookup_table_ix, lookup_table_address) = create_lookup_table( + funding_key.pubkey(), // authority + funding_key.pubkey(), // payer + recent_slot, // slot + ); + + ( + Transaction::new_signed_with_payer( + &[create_lookup_table_ix], + Some(&funding_key.pubkey()), + &[funding_key], + recent_blockhash, + ), + lookup_table_address, + ) +} + +fn build_extend_lookup_table_tx( + lookup_table_address: &Pubkey, + funding_key: &Keypair, + num_addresses: usize, + recent_blockhash: Hash, +) -> Transaction { + // generates random addresses to populate lookup table account + let addresses = (0..num_addresses) + .map(|_| Pubkey::new_unique()) + .collect_vec(); + let extend_lookup_table_ix = extend_lookup_table( + *lookup_table_address, + funding_key.pubkey(), // authority + Some(funding_key.pubkey()), // payer + addresses, + ); + + Transaction::new_signed_with_payer( + &[extend_lookup_table_ix], + Some(&funding_key.pubkey()), + &[funding_key], + recent_blockhash, + ) +} + +fn send_and_confirm_transaction( + client: Arc, + transaction: Transaction, + lookup_table_address: &Pubkey, +) { + let _tx_sig = client.send_transaction(transaction).unwrap(); + + // Sleep a few slots to allow transactions to process + sleep(Duration::from_secs(1)); + + // confirm tx + let lookup_table_account = client + .get_account_with_commitment(lookup_table_address, CommitmentConfig::processed()) + .unwrap(); + let lookup_table = AddressLookupTable::deserialize(&lookup_table_account.data).unwrap(); + trace!("lookup table: {:?}", lookup_table); +} diff --git a/bench-tps/src/bench.rs b/bench-tps/src/bench.rs index d6444ebe4b41fb..d0c45ffb7a4314 100644 --- a/bench-tps/src/bench.rs +++ b/bench-tps/src/bench.rs @@ -1,5 +1,6 @@ use { crate::{ + address_table_lookup::create_address_lookup_table_account, bench_tps_client::*, cli::{Config, InstructionPaddingConfig}, perf_utils::{sample_txs, SampleStats}, @@ -8,21 +9,24 @@ use { log::*, rand::distributions::{Distribution, Uniform}, rayon::prelude::*, + solana_address_lookup_table_program::state::AddressLookupTable, solana_client::{nonce_utils, rpc_request::MAX_MULTIPLE_ACCOUNTS}, solana_metrics::{self, datapoint_info}, solana_sdk::{ account::Account, + address_lookup_table_account::AddressLookupTableAccount, clock::{DEFAULT_MS_PER_SLOT, DEFAULT_S_PER_SLOT, MAX_PROCESSING_AGE}, + commitment_config::CommitmentConfig, compute_budget::ComputeBudgetInstruction, hash::Hash, instruction::{AccountMeta, Instruction}, - message::Message, + message::{v0, Message, VersionedMessage}, native_token::Sol, pubkey::Pubkey, signature::{Keypair, Signer}, system_instruction, timing::{duration_as_ms, duration_as_s, duration_as_us, timestamp}, - transaction::Transaction, + transaction::VersionedTransaction, }, spl_instruction_padding::instruction::wrap_instruction, std::{ @@ -48,7 +52,7 @@ const MAX_TX_QUEUE_AGE: u64 = (MAX_PROCESSING_AGE as f64 * DEFAULT_S_PER_SLOT) a // `TRANSFER_TRANSACTION_COMPUTE_UNIT * MAX_COMPUTE_UNIT_PRICE * COMPUTE_UNIT_PRICE_MULTIPLIER / 1_000_000` const MAX_COMPUTE_UNIT_PRICE: u64 = 50; const COMPUTE_UNIT_PRICE_MULTIPLIER: u64 = 1_000; -const TRANSFER_TRANSACTION_COMPUTE_UNIT: u32 = 600; // 1 transfer is plus 3 compute_budget ixs +const TRANSFER_TRANSACTION_COMPUTE_UNIT: u32 = 600 + 10; // 1 transfer is plus 3 compute_budget ixs, plus 10 to use noop ix to load atl /// calculate maximum possible prioritization fee, if `use-randomized-compute-unit-price` is /// enabled, round to nearest lamports. pub fn max_lamports_for_prioritization(use_randomized_compute_unit_price: bool) -> u64 { @@ -70,7 +74,7 @@ pub fn max_lamports_for_prioritization(use_randomized_compute_unit_price: bool) // 32K page size, so it'd cost 0 extra CU. const TRANSFER_TRANSACTION_LOADED_ACCOUNTS_DATA_SIZE: u32 = 30 * 1024; -pub type TimestampedTransaction = (Transaction, Option); +pub type TimestampedTransaction = (VersionedTransaction, Option); pub type SharedTransactions = Arc>>>; /// Keypairs split into source and destination @@ -80,6 +84,12 @@ struct KeypairChunks<'a> { dest: Vec>, } +/// Accounts pubkeys needed to construct instruction that uses ATL +struct AltInstructionAccounts { + lookup_table_account: AddressLookupTableAccount, + alt_instruction_program_id: Pubkey, +} + impl<'a> KeypairChunks<'a> { /// Split input slice of keypairs into two sets of chunks of given size fn new(keypairs: &'a [Keypair], chunk_size: usize) -> Self { @@ -122,6 +132,7 @@ struct TransactionChunkGenerator<'a, 'b, T: ?Sized> { reclaim_lamports_back_to_source_account: bool, use_randomized_compute_unit_price: bool, instruction_padding_config: Option, + alt_instruction_accounts: Option, } impl<'a, 'b, T> TransactionChunkGenerator<'a, 'b, T> @@ -136,6 +147,7 @@ where use_randomized_compute_unit_price: bool, instruction_padding_config: Option, num_conflict_groups: Option, + alt_instruction_accounts: Option, ) -> Self { let account_chunks = if let Some(num_conflict_groups) = num_conflict_groups { KeypairChunks::new_with_conflict_groups(gen_keypairs, chunk_size, num_conflict_groups) @@ -153,6 +165,7 @@ where reclaim_lamports_back_to_source_account: false, use_randomized_compute_unit_price, instruction_padding_config, + alt_instruction_accounts, } } @@ -189,6 +202,7 @@ where blockhash.unwrap(), &self.instruction_padding_config, self.use_randomized_compute_unit_price, + &self.alt_instruction_accounts, ) }; @@ -383,9 +397,38 @@ where use_durable_nonce, instruction_padding_config, num_conflict_groups, + alt_instruction_config, .. } = config; + // if --alt_instruction_load_accounts_count is used, creates Lookup Table account, + // then extend it with requested number of accounts. + // All bench transfer transactions will include a `alt_instruction_program_id` instruction to load + // specified number of accounts from Lookup Table account as writable accounts. + let alt_instruction_accounts = alt_instruction_config.map(|alt_instruction_config| { + // there are 5 accounts already in transaction: signer, 2 for transfer, compute budget and + // sbf program, need to deduct them from alt size. + const MIN_ACCOUNTS_COUNT: usize = 5; + let alt_size = + alt_instruction_config.alt_instruction_load_accounts_count - MIN_ACCOUNTS_COUNT; + let lookup_table_account_address = + create_address_lookup_table_account(client.clone(), &id, alt_size).unwrap(); + let lookup_table_account = client + .get_account_with_commitment( + &lookup_table_account_address, + CommitmentConfig::processed(), + ) + .unwrap(); + let lookup_table = AddressLookupTable::deserialize(&lookup_table_account.data).unwrap(); + AltInstructionAccounts { + lookup_table_account: AddressLookupTableAccount { + key: lookup_table_account_address, + addresses: lookup_table.addresses.to_vec(), + }, + alt_instruction_program_id: alt_instruction_config.alt_instruction_program_id, + } + }); + assert!(gen_keypairs.len() >= 2 * tx_count); let chunk_generator = TransactionChunkGenerator::new( client.clone(), @@ -395,6 +438,7 @@ where use_randomized_compute_unit_price, instruction_padding_config, num_conflict_groups, + alt_instruction_accounts, ); let first_tx_count = loop { @@ -521,6 +565,7 @@ fn generate_system_txs( blockhash: &Hash, instruction_padding_config: &Option, use_randomized_compute_unit_price: bool, + alt_instruction_accounts: &Option, ) -> Vec { let pairs: Vec<_> = if !reclaim { source.iter().zip(dest.iter()).collect() @@ -552,6 +597,7 @@ fn generate_system_txs( *blockhash, instruction_padding_config, Some(**compute_unit_price), + alt_instruction_accounts, ), Some(timestamp()), ) @@ -569,6 +615,7 @@ fn generate_system_txs( *blockhash, instruction_padding_config, None, + alt_instruction_accounts, ), Some(timestamp()), ) @@ -584,7 +631,8 @@ fn transfer_with_compute_unit_price_and_padding( recent_blockhash: Hash, instruction_padding_config: &Option, compute_unit_price: Option, -) -> Transaction { + alt_instruction_accounts: &Option, +) -> VersionedTransaction { let from_pubkey = from_keypair.pubkey(); let transfer_instruction = system_instruction::transfer(&from_pubkey, to, lamports); let instruction = if let Some(instruction_padding_config) = instruction_padding_config { @@ -610,8 +658,41 @@ fn transfer_with_compute_unit_price_and_padding( TRANSFER_TRANSACTION_LOADED_ACCOUNTS_DATA_SIZE, ), ]); - let message = Message::new(&instructions, Some(&from_pubkey)); - Transaction::new(&[from_keypair], message, recent_blockhash) + + let versioned_message = if let Some(alt_instruction_accounts) = alt_instruction_accounts { + let address_lookup_table_account_clone = + alt_instruction_accounts.lookup_table_account.clone(); + let address_lookup_table_accounts = vec![address_lookup_table_account_clone]; + + let account_metas: Vec<_> = alt_instruction_accounts + .lookup_table_account + .addresses + .iter() + .map(|pubkey| AccountMeta::new_readonly(*pubkey, false)) + .collect(); + instructions.extend_from_slice(&[Instruction::new_with_bincode( + alt_instruction_accounts.alt_instruction_program_id, + &(), + account_metas, + )]); + + VersionedMessage::V0( + v0::Message::try_compile( + &from_pubkey, //payer + &instructions, + &address_lookup_table_accounts, + recent_blockhash, + ) + .unwrap(), + ) + } else { + VersionedMessage::Legacy(Message::new_with_blockhash( + &instructions, + Some(&from_pubkey), + &recent_blockhash, + )) + }; + VersionedTransaction::try_new(versioned_message, &[from_keypair]).unwrap() } fn get_nonce_accounts( @@ -680,9 +761,9 @@ fn nonced_transfer_with_padding( lamports: u64, nonce_account: &Pubkey, nonce_authority: &Keypair, - nonce_hash: Hash, + _nonce_hash: Hash, instruction_padding_config: &Option, -) -> Transaction { +) -> VersionedTransaction { let from_pubkey = from_keypair.pubkey(); let transfer_instruction = system_instruction::transfer(&from_pubkey, to, lamports); let instruction = if let Some(instruction_padding_config) = instruction_padding_config { @@ -702,13 +783,16 @@ fn nonced_transfer_with_padding( TRANSFER_TRANSACTION_LOADED_ACCOUNTS_DATA_SIZE, ), ]); - let message = Message::new_with_nonce( - instructions, - Some(&from_pubkey), - nonce_account, - &nonce_authority.pubkey(), - ); - Transaction::new(&[from_keypair, nonce_authority], message, nonce_hash) + VersionedTransaction::try_new( + VersionedMessage::Legacy(Message::new_with_nonce( + instructions, + Some(&from_pubkey), + nonce_account, + &nonce_authority.pubkey(), + )), + &[from_keypair], + ) + .unwrap() } fn generate_nonced_system_txs( @@ -906,7 +990,7 @@ fn do_tx_transfers( ); } - if let Err(error) = client.send_batch(transactions) { + if let Err(error) = client.send_versioned_transaction_batch(transactions) { warn!("send_batch_sync in do_tx_transfers failed: {}", error); } diff --git a/bench-tps/src/bench_tps_client.rs b/bench-tps/src/bench_tps_client.rs index 3ab15bec11f7ee..b1d7aa46b5712c 100644 --- a/bench-tps/src/bench_tps_client.rs +++ b/bench-tps/src/bench_tps_client.rs @@ -1,8 +1,14 @@ use { solana_rpc_client_api::client_error::Error as ClientError, solana_sdk::{ - account::Account, commitment_config::CommitmentConfig, epoch_info::EpochInfo, hash::Hash, - message::Message, pubkey::Pubkey, signature::Signature, transaction::Transaction, + account::Account, + commitment_config::CommitmentConfig, + epoch_info::EpochInfo, + hash::Hash, + message::Message, + pubkey::Pubkey, + signature::Signature, + transaction::{Transaction, VersionedTransaction}, transport::TransportError, }, solana_tpu_client::tpu_client::TpuSenderError, @@ -34,6 +40,12 @@ pub trait BenchTpsClient { /// Send a batch of signed transactions without confirmation. fn send_batch(&self, transactions: Vec) -> Result<()>; + /// Send a batch of signed versioned transactions without confirmation. + fn send_versioned_transaction_batch( + &self, + transactions: Vec, + ) -> Result<()>; + /// Get latest blockhash fn get_latest_blockhash(&self) -> Result; @@ -93,6 +105,8 @@ pub trait BenchTpsClient { ) -> Result; fn get_multiple_accounts(&self, pubkeys: &[Pubkey]) -> Result>>; + + fn get_slot(&self) -> Result; } mod bank_client; diff --git a/bench-tps/src/bench_tps_client/bank_client.rs b/bench-tps/src/bench_tps_client/bank_client.rs index 1aef7284c01ed6..154cfe43dd8343 100644 --- a/bench-tps/src/bench_tps_client/bank_client.rs +++ b/bench-tps/src/bench_tps_client/bank_client.rs @@ -10,7 +10,7 @@ use { message::Message, pubkey::Pubkey, signature::Signature, - transaction::Transaction, + transaction::{Transaction, VersionedTransaction}, }, }; @@ -21,6 +21,13 @@ impl BenchTpsClient for BankClient { fn send_batch(&self, transactions: Vec) -> Result<()> { AsyncClient::async_send_batch(self, transactions).map_err(|err| err.into()) } + fn send_versioned_transaction_batch( + &self, + transactions: Vec, + ) -> Result<()> { + AsyncClient::async_send_versioned_transaction_batch(self, transactions) + .map_err(|err| err.into()) + } fn get_latest_blockhash(&self) -> Result { SyncClient::get_latest_blockhash(self).map_err(|err| err.into()) } @@ -111,4 +118,8 @@ impl BenchTpsClient for BankClient { fn get_multiple_accounts(&self, _pubkeys: &[Pubkey]) -> Result>> { unimplemented!("BankClient doesn't support get_multiple_accounts"); } + + fn get_slot(&self) -> Result { + SyncClient::get_slot(self).map_err(|err| err.into()) + } } diff --git a/bench-tps/src/bench_tps_client/rpc_client.rs b/bench-tps/src/bench_tps_client/rpc_client.rs index 57e97120d0b4af..3f929152b45efd 100644 --- a/bench-tps/src/bench_tps_client/rpc_client.rs +++ b/bench-tps/src/bench_tps_client/rpc_client.rs @@ -2,8 +2,14 @@ use { crate::bench_tps_client::{BenchTpsClient, BenchTpsError, Result}, solana_rpc_client::rpc_client::RpcClient, solana_sdk::{ - account::Account, commitment_config::CommitmentConfig, epoch_info::EpochInfo, hash::Hash, - message::Message, pubkey::Pubkey, signature::Signature, transaction::Transaction, + account::Account, + commitment_config::CommitmentConfig, + epoch_info::EpochInfo, + hash::Hash, + message::Message, + pubkey::Pubkey, + signature::Signature, + transaction::{Transaction, VersionedTransaction}, }, }; @@ -17,6 +23,15 @@ impl BenchTpsClient for RpcClient { } Ok(()) } + fn send_versioned_transaction_batch( + &self, + transactions: Vec, + ) -> Result<()> { + for transaction in transactions { + RpcClient::send_transaction(self, &transaction)?; + } + Ok(()) + } fn get_latest_blockhash(&self) -> Result { RpcClient::get_latest_blockhash(self).map_err(|err| err.into()) } @@ -103,4 +118,8 @@ impl BenchTpsClient for RpcClient { fn get_multiple_accounts(&self, pubkeys: &[Pubkey]) -> Result>> { RpcClient::get_multiple_accounts(self, pubkeys).map_err(|err| err.into()) } + + fn get_slot(&self) -> Result { + RpcClient::get_slot(self).map_err(|err| err.into()) + } } diff --git a/bench-tps/src/bench_tps_client/thin_client.rs b/bench-tps/src/bench_tps_client/thin_client.rs index 6696774d679a8a..e00b7594434010 100644 --- a/bench-tps/src/bench_tps_client/thin_client.rs +++ b/bench-tps/src/bench_tps_client/thin_client.rs @@ -10,7 +10,7 @@ use { message::Message, pubkey::Pubkey, signature::Signature, - transaction::Transaction, + transaction::{Transaction, VersionedTransaction}, }, }; @@ -21,6 +21,13 @@ impl BenchTpsClient for ThinClient { fn send_batch(&self, transactions: Vec) -> Result<()> { AsyncClient::async_send_batch(self, transactions).map_err(|err| err.into()) } + fn send_versioned_transaction_batch( + &self, + transactions: Vec, + ) -> Result<()> { + AsyncClient::async_send_versioned_transaction_batch(self, transactions) + .map_err(|err| err.into()) + } fn get_latest_blockhash(&self) -> Result { SyncClient::get_latest_blockhash(self).map_err(|err| err.into()) } @@ -110,4 +117,8 @@ impl BenchTpsClient for ThinClient { .get_multiple_accounts(pubkeys) .map_err(|err| err.into()) } + + fn get_slot(&self) -> Result { + SyncClient::get_slot(self).map_err(|err| err.into()) + } } diff --git a/bench-tps/src/bench_tps_client/tpu_client.rs b/bench-tps/src/bench_tps_client/tpu_client.rs index ae762e52922ec5..04ed333e1ed6a2 100644 --- a/bench-tps/src/bench_tps_client/tpu_client.rs +++ b/bench-tps/src/bench_tps_client/tpu_client.rs @@ -3,8 +3,14 @@ use { solana_client::tpu_client::TpuClient, solana_connection_cache::connection_cache::{ConnectionManager, ConnectionPool}, solana_sdk::{ - account::Account, commitment_config::CommitmentConfig, epoch_info::EpochInfo, hash::Hash, - message::Message, pubkey::Pubkey, signature::Signature, transaction::Transaction, + account::Account, + commitment_config::CommitmentConfig, + epoch_info::EpochInfo, + hash::Hash, + message::Message, + pubkey::Pubkey, + signature::Signature, + transaction::{Transaction, VersionedTransaction}, }, }; @@ -22,6 +28,14 @@ where self.try_send_transaction_batch(&transactions)?; Ok(()) } + fn send_versioned_transaction_batch( + &self, + transactions: Vec, + ) -> Result<()> { + self.rpc_client() + .send_versioned_transaction_batch(transactions)?; + Ok(()) + } fn get_latest_blockhash(&self) -> Result { self.rpc_client() .get_latest_blockhash() @@ -127,4 +141,8 @@ where .get_multiple_accounts(pubkeys) .map_err(|err| err.into()) } + + fn get_slot(&self) -> Result { + self.rpc_client().get_slot().map_err(|err| err.into()) + } } diff --git a/bench-tps/src/cli.rs b/bench-tps/src/cli.rs index 9acf1e74ce3c70..9de5955194fb4c 100644 --- a/bench-tps/src/cli.rs +++ b/bench-tps/src/cli.rs @@ -2,7 +2,7 @@ use { clap::{crate_description, crate_name, App, Arg, ArgMatches}, solana_clap_utils::{ hidden_unless_forced, - input_validators::{is_keypair, is_url, is_url_or_moniker, is_within_range}, + input_validators::{is_keypair, is_pubkey, is_url, is_url_or_moniker, is_within_range}, }, solana_cli_config::{ConfigInput, CONFIG_FILE}, solana_sdk::{ @@ -43,6 +43,15 @@ pub struct InstructionPaddingConfig { pub data_size: u32, } +#[derive(Eq, PartialEq, Debug)] +pub struct AltInstructionConfig { + // program_id of a sbf program deployed in test cluster, such as `noop`, to + // load accounts from ALT + pub alt_instruction_program_id: Pubkey, + // number of addresses (up to 256) to be added to ATL for program to load + pub alt_instruction_load_accounts_count: usize, +} + /// Holds the configuration for a single run of the benchmark #[derive(PartialEq, Debug)] pub struct Config { @@ -74,6 +83,7 @@ pub struct Config { pub num_conflict_groups: Option, pub bind_address: IpAddr, pub client_node_id: Option, + pub alt_instruction_config: Option, } impl Eq for Config {} @@ -109,6 +119,7 @@ impl Default for Config { num_conflict_groups: None, bind_address: IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), client_node_id: None, + alt_instruction_config: None, } } } @@ -382,6 +393,27 @@ pub fn build_args<'a>(version: &'_ str) -> App<'a, '_> { .validator(is_keypair) .help("File containing the node identity (keypair) of a validator with active stake. This allows communicating with network using staked connection"), ) + .arg( + Arg::with_name("alt_instruction_program_id") + .long("alt-instruction-program-id") + .value_name("ALT_INSTRUCTION_PROGRAM_ID") + .takes_value(true) + .requires("alt_instruction_load_accounts_count") + .validator(is_pubkey) + .help("If program id of deployed SBF progrem, for example `noop`, is provided; bench transfer transactions \ + will include an additional instruction to use the program to load number of accounts from an \ + address lookup table account." + ) + ) + .arg( + Arg::with_name("alt_instruction_load_accounts_count") + .long("alt-instruction-load-accounts-count") + .value_name("ALT_INSTRUCTION_LOAD_ACCOUNTS_COUNT") + .takes_value(true) + .requires("alt_instruction_program_id") + .validator(|n| is_within_range(n, 0..=256)) + .help("The number of accounts are loaded from address lookup table account by program identified as `alt_instruction_program_id`") + ) } /// Parses a clap `ArgMatches` structure into a `Config` @@ -557,6 +589,21 @@ pub fn parse_args(matches: &ArgMatches) -> Result { args.client_node_id = Some(client_node_id); } + if let Some(alt_instruction_program_id) = matches.value_of("alt_instruction_program_id") { + let alt_instruction_load_accounts_count = matches + .value_of("alt_instruction_load_accounts_count") + .unwrap() + .parse() + .map_err(|_| "Can't parse alt_instruction_load_accounts_count")?; + let alt_instruction_program_id = alt_instruction_program_id + .parse::() + .map_err(|_| "Can't parse pubkey alt_instruction_program_id")?; + args.alt_instruction_config = Some(AltInstructionConfig { + alt_instruction_program_id, + alt_instruction_load_accounts_count, + }); + } + Ok(args) } diff --git a/bench-tps/src/lib.rs b/bench-tps/src/lib.rs index 5226b4e56f07d5..7371a07241b770 100644 --- a/bench-tps/src/lib.rs +++ b/bench-tps/src/lib.rs @@ -1,4 +1,5 @@ #![allow(clippy::integer_arithmetic)] +pub mod address_table_lookup; pub mod bench; pub mod bench_tps_client; pub mod cli;