2024-04-02 05:26:54 -07:00
|
|
|
use std::{collections::HashSet, ops::Mul, str::FromStr, sync::Arc, time::Duration};
|
|
|
|
|
|
|
|
use clap::Parser;
|
|
|
|
use dashmap::{DashMap, DashSet};
|
|
|
|
use itertools::Itertools;
|
|
|
|
use rand::{
|
|
|
|
distributions::{Alphanumeric, Distribution},
|
|
|
|
SeedableRng,
|
|
|
|
};
|
|
|
|
use solana_lite_rpc_cluster_endpoints::{
|
|
|
|
geyser_grpc_connector::{GrpcConnectionTimeouts, GrpcSourceConfig},
|
|
|
|
grpc_subscription::create_grpc_subscription,
|
|
|
|
json_rpc_leaders_getter::JsonRpcLeaderGetter,
|
|
|
|
json_rpc_subscription::create_json_rpc_polling_subscription,
|
|
|
|
};
|
|
|
|
use solana_lite_rpc_core::{
|
|
|
|
keypair_loader::load_identity_keypair,
|
|
|
|
stores::{
|
|
|
|
block_information_store::{BlockInformation, BlockInformationStore},
|
|
|
|
cluster_info_store::ClusterInfo,
|
|
|
|
data_cache::{DataCache, SlotCache},
|
|
|
|
subscription_store::SubscriptionStore,
|
|
|
|
tx_store::TxStore,
|
|
|
|
},
|
|
|
|
structures::{
|
|
|
|
epoch::EpochCache, identity_stakes::IdentityStakes, leaderschedule::CalculatedSchedule,
|
|
|
|
transaction_sent_info::SentTransactionInfo,
|
|
|
|
},
|
|
|
|
utils::wait_till_block_of_commitment_is_recieved,
|
|
|
|
};
|
|
|
|
use solana_lite_rpc_services::{
|
|
|
|
data_caching_service::DataCachingService,
|
|
|
|
quic_connection_utils::QuicConnectionParameters,
|
|
|
|
tpu_utils::{
|
|
|
|
tpu_connection_path::TpuConnectionPath,
|
|
|
|
tpu_service::{TpuService, TpuServiceConfig},
|
|
|
|
},
|
|
|
|
transaction_replayer::TransactionReplayer,
|
|
|
|
transaction_service::TransactionServiceBuilder,
|
|
|
|
tx_sender::TxSender,
|
|
|
|
};
|
|
|
|
use solana_sdk::{
|
|
|
|
commitment_config::CommitmentConfig,
|
|
|
|
compute_budget,
|
|
|
|
hash::Hash,
|
|
|
|
instruction::Instruction,
|
|
|
|
message::Message,
|
|
|
|
native_token::LAMPORTS_PER_SOL,
|
|
|
|
pubkey::Pubkey,
|
|
|
|
signature::{Keypair, Signature},
|
|
|
|
signer::Signer,
|
|
|
|
system_instruction,
|
|
|
|
transaction::Transaction,
|
|
|
|
};
|
|
|
|
use tokio::sync::{Mutex, RwLock};
|
|
|
|
|
|
|
|
use crate::cli::Args;
|
|
|
|
use solana_rpc_client::nonblocking::rpc_client::RpcClient;
|
|
|
|
|
|
|
|
mod cli;
|
|
|
|
|
|
|
|
const MEMO_PROGRAM_ID: &str = "MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr";
|
|
|
|
|
|
|
|
pub fn create_memo_tx(msg: &[u8], payer: &Keypair, blockhash: Hash, prio_fees: u64) -> Transaction {
|
|
|
|
let memo = Pubkey::from_str(MEMO_PROGRAM_ID).unwrap();
|
|
|
|
|
|
|
|
let cb_1 = compute_budget::ComputeBudgetInstruction::set_compute_unit_limit(7000);
|
|
|
|
let cb_2 = compute_budget::ComputeBudgetInstruction::set_compute_unit_price(prio_fees);
|
|
|
|
let instruction = Instruction::new_with_bytes(memo, msg, vec![]);
|
|
|
|
let message = Message::new(&[cb_1, cb_2, instruction], Some(&payer.pubkey()));
|
|
|
|
Transaction::new(&[payer], message, blockhash)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn generate_random_strings(
|
|
|
|
num_of_txs: usize,
|
|
|
|
random_seed: Option<u64>,
|
|
|
|
n_chars: usize,
|
|
|
|
) -> Vec<Vec<u8>> {
|
|
|
|
let seed = random_seed.map_or(0, |x| x);
|
|
|
|
let mut rng = rand_chacha::ChaCha8Rng::seed_from_u64(seed);
|
|
|
|
(0..num_of_txs)
|
|
|
|
.map(|_| Alphanumeric.sample_iter(&mut rng).take(n_chars).collect())
|
|
|
|
.collect()
|
|
|
|
}
|
|
|
|
|
|
|
|
pub async fn create_signers_from_payer(
|
|
|
|
rpc_client: Arc<RpcClient>,
|
|
|
|
payer: Arc<Keypair>,
|
|
|
|
nb_signers: usize,
|
|
|
|
signer_balance: u64,
|
|
|
|
prio_fees: u64,
|
|
|
|
) -> Vec<Arc<Keypair>> {
|
|
|
|
let signers = (0..nb_signers)
|
|
|
|
.map(|_| Arc::new(Keypair::new()))
|
|
|
|
.collect_vec();
|
|
|
|
let mut signers_to_transfer: HashSet<Pubkey> = signers.iter().map(|kp| kp.pubkey()).collect();
|
|
|
|
|
|
|
|
while !signers_to_transfer.is_empty() {
|
|
|
|
let Ok(blockhash) = rpc_client.get_latest_blockhash().await else {
|
|
|
|
tokio::time::sleep(Duration::from_secs(1)).await;
|
|
|
|
continue;
|
|
|
|
};
|
|
|
|
|
|
|
|
let cb_1 = compute_budget::ComputeBudgetInstruction::set_compute_unit_limit(5000);
|
|
|
|
let cb_2 = compute_budget::ComputeBudgetInstruction::set_compute_unit_price(prio_fees);
|
|
|
|
|
|
|
|
let transactions = signers_to_transfer
|
|
|
|
.iter()
|
|
|
|
.map(|signer| {
|
|
|
|
let instruction =
|
|
|
|
system_instruction::transfer(&payer.pubkey(), signer, signer_balance);
|
|
|
|
let message = Message::new(
|
|
|
|
&[cb_1.clone(), cb_2.clone(), instruction],
|
|
|
|
Some(&payer.pubkey()),
|
|
|
|
);
|
|
|
|
(*signer, Transaction::new(&[&payer], message, blockhash))
|
|
|
|
})
|
|
|
|
.collect_vec();
|
|
|
|
let tasks = transactions
|
|
|
|
.iter()
|
|
|
|
.map(|(signer, tx)| {
|
|
|
|
let rpc_client = rpc_client.clone();
|
|
|
|
let tx = tx.clone();
|
|
|
|
let signer = *signer;
|
|
|
|
tokio::spawn(
|
|
|
|
async move { (signer, rpc_client.send_and_confirm_transaction(&tx).await) },
|
|
|
|
)
|
|
|
|
})
|
|
|
|
.collect_vec();
|
|
|
|
|
|
|
|
let results = futures::future::join_all(tasks).await;
|
|
|
|
for result in results {
|
|
|
|
match result {
|
|
|
|
Ok((signer, Ok(_signature))) => {
|
|
|
|
signers_to_transfer.remove(&signer);
|
|
|
|
}
|
|
|
|
Ok((signer, Err(e))) => {
|
|
|
|
log::error!("Error transfering to {signer:?}, {e:?}");
|
|
|
|
}
|
|
|
|
_ => {
|
|
|
|
// retry
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
signers
|
|
|
|
}
|
|
|
|
|
|
|
|
pub async fn transfer_back_to_payer(
|
|
|
|
rpc_client: Arc<RpcClient>,
|
|
|
|
payer: Arc<Keypair>,
|
|
|
|
signers: Vec<Arc<Keypair>>,
|
|
|
|
prio_fees: u64,
|
|
|
|
) {
|
|
|
|
let mut signers_to_transfer: HashSet<Pubkey> = signers.iter().map(|kp| kp.pubkey()).collect();
|
|
|
|
let payer_pubkey = payer.pubkey();
|
|
|
|
|
|
|
|
while !signers_to_transfer.is_empty() {
|
|
|
|
let Ok(blockhash) = rpc_client.get_latest_blockhash().await else {
|
|
|
|
tokio::time::sleep(Duration::from_secs(1)).await;
|
|
|
|
continue;
|
|
|
|
};
|
|
|
|
|
|
|
|
let transfers = signers
|
|
|
|
.iter()
|
|
|
|
.map(|signer| {
|
|
|
|
let rpc_client = rpc_client.clone();
|
|
|
|
let signer = signer.clone();
|
|
|
|
tokio::spawn(async move {
|
|
|
|
let balance = rpc_client.get_balance(&signer.pubkey()).await.unwrap();
|
|
|
|
|
|
|
|
let cb_1 =
|
|
|
|
compute_budget::ComputeBudgetInstruction::set_compute_unit_limit(5000);
|
|
|
|
let cb_2 =
|
|
|
|
compute_budget::ComputeBudgetInstruction::set_compute_unit_price(prio_fees);
|
|
|
|
let balance_to_transfer = balance
|
|
|
|
.saturating_sub(5000)
|
|
|
|
.saturating_sub(5000 * prio_fees);
|
|
|
|
let instruction = system_instruction::transfer(
|
|
|
|
&signer.pubkey(),
|
|
|
|
&payer_pubkey,
|
|
|
|
balance_to_transfer,
|
|
|
|
);
|
|
|
|
let message = Message::new(
|
|
|
|
&[cb_1.clone(), cb_2.clone(), instruction],
|
|
|
|
Some(&signer.pubkey()),
|
|
|
|
);
|
|
|
|
(
|
|
|
|
signer.pubkey(),
|
|
|
|
rpc_client
|
|
|
|
.send_and_confirm_transaction(&Transaction::new(
|
|
|
|
&[&signer],
|
|
|
|
message,
|
|
|
|
blockhash,
|
|
|
|
))
|
|
|
|
.await,
|
|
|
|
)
|
|
|
|
})
|
|
|
|
})
|
|
|
|
.collect_vec();
|
|
|
|
|
|
|
|
let results = futures::future::join_all(transfers).await;
|
|
|
|
for result in results {
|
|
|
|
match result {
|
|
|
|
Ok((signer, Ok(_signature))) => {
|
|
|
|
signers_to_transfer.remove(&signer);
|
|
|
|
}
|
|
|
|
Ok((signer, Err(e))) => {
|
|
|
|
log::error!("Error transfering to {signer:?}, {e:?}");
|
|
|
|
}
|
|
|
|
_ => {
|
|
|
|
// retry
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[tokio::main]
|
|
|
|
async fn main() -> anyhow::Result<()> {
|
|
|
|
tracing_subscriber::fmt::init();
|
|
|
|
let args = Args::parse();
|
|
|
|
|
|
|
|
let rpc_url = args.rpc_url;
|
|
|
|
let rpc_client = Arc::new(RpcClient::new(rpc_url));
|
|
|
|
|
|
|
|
let leader_schedule = Arc::new(JsonRpcLeaderGetter::new(rpc_client.clone(), 1024, 128));
|
|
|
|
|
|
|
|
let fee_payer = Arc::new(
|
|
|
|
load_identity_keypair(Some(args.fee_payer))
|
|
|
|
.await
|
|
|
|
.expect("Payer should be set or keypair file not found")
|
|
|
|
.unwrap(),
|
|
|
|
);
|
|
|
|
|
|
|
|
let priority_fee = args.priority_fees.unwrap_or_default();
|
|
|
|
let nb_signers = args.additional_signers;
|
|
|
|
let signer_balance = args.signers_transfer_balance.mul(LAMPORTS_PER_SOL as f64) as u64;
|
|
|
|
let signers = create_signers_from_payer(
|
|
|
|
rpc_client.clone(),
|
|
|
|
fee_payer.clone(),
|
|
|
|
nb_signers,
|
|
|
|
signer_balance,
|
|
|
|
priority_fee,
|
|
|
|
)
|
|
|
|
.await;
|
|
|
|
|
|
|
|
println!(
|
|
|
|
"Creating {} users with {} SOL balance",
|
|
|
|
nb_signers, signer_balance
|
|
|
|
);
|
|
|
|
|
|
|
|
let validator_identity = Arc::new(
|
|
|
|
load_identity_keypair(args.staked_identity)
|
|
|
|
.await?
|
|
|
|
.unwrap_or_else(Keypair::new),
|
|
|
|
);
|
|
|
|
|
|
|
|
// START ALL SERVICES REQUIRED BY LITE_RPC
|
|
|
|
// setup endpoint, GRPC/RPC Polling
|
|
|
|
println!("Setting up lite-rpc tpu service");
|
|
|
|
let (endpoints, _handles) = if let Some(grpc_addr) = args.grpc_url {
|
|
|
|
let timeouts = GrpcConnectionTimeouts {
|
|
|
|
connect_timeout: Duration::from_secs(10),
|
|
|
|
request_timeout: Duration::from_secs(10),
|
|
|
|
subscribe_timeout: Duration::from_secs(10),
|
|
|
|
receive_timeout: Duration::from_secs(10),
|
|
|
|
};
|
|
|
|
create_grpc_subscription(
|
|
|
|
rpc_client.clone(),
|
|
|
|
vec![GrpcSourceConfig::new(
|
|
|
|
grpc_addr,
|
|
|
|
args.x_token.clone(),
|
|
|
|
None,
|
|
|
|
timeouts,
|
|
|
|
)],
|
|
|
|
vec![],
|
|
|
|
)?
|
|
|
|
} else {
|
|
|
|
create_json_rpc_polling_subscription(rpc_client.clone(), 100)?
|
|
|
|
};
|
|
|
|
|
|
|
|
let finalized_block_information = wait_till_block_of_commitment_is_recieved(
|
|
|
|
endpoints.blockinfo_notifier.resubscribe(),
|
|
|
|
CommitmentConfig::finalized(),
|
|
|
|
)
|
|
|
|
.await;
|
|
|
|
|
|
|
|
let block_height = rpc_client
|
|
|
|
.get_block_height_with_commitment(CommitmentConfig::finalized())
|
|
|
|
.await?;
|
|
|
|
let (blockhash, _) = rpc_client
|
|
|
|
.get_latest_blockhash_with_commitment(CommitmentConfig::finalized())
|
|
|
|
.await?;
|
|
|
|
let finalize_slot = finalized_block_information.slot;
|
|
|
|
|
|
|
|
println!(
|
|
|
|
"finalized blockheight : {:?}, slot: {}, hash: {}",
|
|
|
|
finalized_block_information.block_height,
|
|
|
|
finalized_block_information.slot,
|
|
|
|
finalized_block_information.blockhash
|
|
|
|
);
|
|
|
|
println!(
|
|
|
|
"From RPC blockheight : {block_height:?}, hash: {}",
|
|
|
|
blockhash
|
|
|
|
);
|
|
|
|
|
|
|
|
let finalized_block_information = BlockInformation {
|
|
|
|
slot: finalized_block_information.slot,
|
|
|
|
block_height,
|
|
|
|
last_valid_blockheight: finalized_block_information.block_height + 300,
|
|
|
|
cleanup_slot: finalized_block_information.slot + 1000000,
|
|
|
|
blockhash: finalized_block_information.blockhash,
|
|
|
|
commitment_config: CommitmentConfig::finalized(),
|
|
|
|
block_time: 0,
|
|
|
|
};
|
|
|
|
|
|
|
|
let block_information_store = BlockInformationStore::new(finalized_block_information);
|
|
|
|
|
|
|
|
let data_cache = DataCache {
|
|
|
|
block_information_store,
|
|
|
|
cluster_info: ClusterInfo::default(),
|
|
|
|
identity_stakes: IdentityStakes::new(validator_identity.pubkey()),
|
|
|
|
slot_cache: SlotCache::new(finalize_slot),
|
|
|
|
tx_subs: SubscriptionStore::default(),
|
|
|
|
txs: TxStore {
|
|
|
|
store: Arc::new(DashMap::new()),
|
|
|
|
},
|
|
|
|
epoch_data: EpochCache::new_for_tests(),
|
|
|
|
leader_schedule: Arc::new(RwLock::new(CalculatedSchedule::default())),
|
|
|
|
};
|
|
|
|
|
|
|
|
let data_cache_service = DataCachingService {
|
|
|
|
data_cache: data_cache.clone(),
|
|
|
|
clean_duration: Duration::from_secs(120),
|
|
|
|
};
|
|
|
|
|
|
|
|
// start listning the cluster data and filling the cache
|
|
|
|
data_cache_service.listen(
|
|
|
|
endpoints.blocks_notifier.resubscribe(),
|
|
|
|
endpoints.blockinfo_notifier,
|
|
|
|
endpoints.slot_notifier.resubscribe(),
|
|
|
|
endpoints.cluster_info_notifier,
|
|
|
|
endpoints.vote_account_notifier,
|
|
|
|
);
|
|
|
|
|
|
|
|
let count = args.transaction_count.unwrap_or(10);
|
|
|
|
let prioritization_heap_size = Some(count * args.number_of_seconds);
|
|
|
|
|
|
|
|
let tpu_config = TpuServiceConfig {
|
|
|
|
fanout_slots: args.fanout_slots.unwrap_or(16),
|
|
|
|
maximum_transaction_in_queue: 2000000,
|
|
|
|
quic_connection_params: QuicConnectionParameters {
|
|
|
|
connection_timeout: Duration::from_secs(60),
|
|
|
|
connection_retry_count: 10,
|
|
|
|
finalize_timeout: Duration::from_millis(10000),
|
|
|
|
max_number_of_connections: 4,
|
|
|
|
unistream_timeout: Duration::from_millis(1000),
|
|
|
|
write_timeout: Duration::from_secs(10),
|
|
|
|
number_of_transactions_per_unistream: 1,
|
|
|
|
unistreams_to_create_new_connection_in_percentage: 5,
|
|
|
|
prioritization_heap_size,
|
|
|
|
},
|
|
|
|
tpu_connection_path: TpuConnectionPath::QuicDirectPath,
|
|
|
|
};
|
|
|
|
|
|
|
|
let tpu_service: TpuService = TpuService::new(
|
|
|
|
tpu_config,
|
|
|
|
validator_identity,
|
|
|
|
leader_schedule,
|
|
|
|
data_cache.clone(),
|
|
|
|
)
|
|
|
|
.await?;
|
|
|
|
let transaction_service_builder = TransactionServiceBuilder::new(
|
|
|
|
TxSender::new(data_cache.clone(), tpu_service.clone()),
|
|
|
|
TransactionReplayer::new(
|
|
|
|
tpu_service.clone(),
|
|
|
|
data_cache.clone(),
|
|
|
|
Duration::from_secs(1),
|
|
|
|
),
|
|
|
|
tpu_service,
|
|
|
|
10000,
|
|
|
|
);
|
|
|
|
let (transaction_service, _) = transaction_service_builder.start(
|
|
|
|
None,
|
|
|
|
data_cache.block_information_store.clone(),
|
|
|
|
10,
|
|
|
|
endpoints.slot_notifier,
|
|
|
|
);
|
|
|
|
|
|
|
|
// CREATE TRANSACTIONS
|
|
|
|
log::info!("Creating memo transactions");
|
|
|
|
|
|
|
|
let memo_msgs = generate_random_strings(count * args.number_of_seconds, None, 5);
|
|
|
|
|
|
|
|
let mut tx_to_confirm = vec![];
|
|
|
|
let mut second = 1;
|
|
|
|
let mut signer_count = 0;
|
|
|
|
let map_of_signature = Arc::new(DashSet::<Signature>::new());
|
|
|
|
let transactions_in_blocks = Arc::new(Mutex::new(Vec::<u64>::new()));
|
|
|
|
let mut block_stream = endpoints.blocks_notifier;
|
|
|
|
|
|
|
|
let block_tps_task = {
|
|
|
|
let map_of_signature = map_of_signature.clone();
|
|
|
|
let transactions_in_blocks = transactions_in_blocks.clone();
|
|
|
|
tokio::spawn(async move {
|
|
|
|
let mut start_tracking = false;
|
|
|
|
while let Ok(block) = block_stream.recv().await {
|
|
|
|
let mut count = 0;
|
|
|
|
for transaction in &block.transactions {
|
|
|
|
if map_of_signature.contains(&transaction.signature) {
|
|
|
|
count += 1;
|
|
|
|
map_of_signature.remove(&transaction.signature);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// start tracking once we have first block with some transactions sent by us
|
|
|
|
if start_tracking || count > 0 {
|
|
|
|
start_tracking = true;
|
|
|
|
let mut lk = transactions_in_blocks.lock().await;
|
|
|
|
lk.push(count);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
})
|
|
|
|
};
|
|
|
|
|
|
|
|
for chunk in memo_msgs.chunks(count) {
|
|
|
|
let instant = tokio::time::Instant::now();
|
|
|
|
let mut current_txs = vec![];
|
|
|
|
println!("Sending memo transactions :{}", second);
|
|
|
|
second += 1;
|
|
|
|
let bh = data_cache
|
|
|
|
.block_information_store
|
|
|
|
.get_latest_blockhash(CommitmentConfig::finalized())
|
|
|
|
.await;
|
|
|
|
let last_valid_block_height =
|
|
|
|
data_cache.block_information_store.get_last_blockheight() + 300;
|
|
|
|
let transactions = chunk
|
|
|
|
.iter()
|
|
|
|
.map(|x| {
|
|
|
|
signer_count += 1;
|
|
|
|
create_memo_tx(x, &signers[signer_count % nb_signers], bh, priority_fee)
|
|
|
|
})
|
|
|
|
.collect_vec();
|
|
|
|
|
|
|
|
for transaction in transactions {
|
|
|
|
let signature = transaction.signatures[0];
|
|
|
|
let raw_tx = bincode::serialize(&transaction).unwrap();
|
|
|
|
let slot = data_cache.slot_cache.get_current_slot();
|
|
|
|
map_of_signature.insert(signature);
|
|
|
|
|
|
|
|
let transaction_info = SentTransactionInfo {
|
|
|
|
signature,
|
|
|
|
last_valid_block_height,
|
|
|
|
slot,
|
|
|
|
transaction: Arc::new(raw_tx),
|
|
|
|
prioritization_fee: priority_fee,
|
|
|
|
};
|
|
|
|
let _ = transaction_service
|
|
|
|
.transaction_channel
|
|
|
|
.send(transaction_info.clone())
|
|
|
|
.await;
|
|
|
|
current_txs.push(signature);
|
|
|
|
}
|
|
|
|
|
|
|
|
tx_to_confirm.push(current_txs);
|
|
|
|
|
|
|
|
let millis = instant.elapsed().as_millis() as u64;
|
|
|
|
if millis < 1000 {
|
|
|
|
tokio::time::sleep(Duration::from_millis(1000 - millis)).await;
|
|
|
|
} else {
|
|
|
|
println!("took {millis:?} millis to send {count:?} transactions");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
println!(
|
|
|
|
"{} memo transactions sent, waiting for a minute to confirm them",
|
|
|
|
count * args.number_of_seconds
|
|
|
|
);
|
|
|
|
|
|
|
|
tokio::time::sleep(Duration::from_secs(120)).await;
|
|
|
|
|
|
|
|
let mut second = 1;
|
|
|
|
for seconds_sigs in tx_to_confirm {
|
|
|
|
let mut tx_confirmed = 0;
|
|
|
|
for sig in seconds_sigs {
|
|
|
|
if data_cache.txs.is_transaction_confirmed(&sig) {
|
|
|
|
tx_confirmed += 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
println!(
|
|
|
|
"{} or {} transactions were confirmed for the {} second",
|
|
|
|
tx_confirmed, count, second
|
|
|
|
);
|
|
|
|
second += 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
block_tps_task.abort();
|
|
|
|
|
|
|
|
let lk = transactions_in_blocks.lock().await;
|
|
|
|
// stop tracking by removing trailling 0s
|
|
|
|
let mut vec = lk.clone();
|
|
|
|
vec.reverse();
|
|
|
|
let mut transaction_blocks = vec.iter().skip_while(|x| **x == 0).cloned().collect_vec();
|
|
|
|
transaction_blocks.reverse();
|
|
|
|
println!(
|
|
|
|
"BLOCKS transactions : {}",
|
|
|
|
transaction_blocks.iter().map(|x| x.to_string()).join(", ")
|
|
|
|
);
|
|
|
|
let sum = transaction_blocks.iter().sum::<u64>();
|
|
|
|
let seconds = (transaction_blocks.len() * 400 / 1000) as u64;
|
|
|
|
let tps = sum / seconds;
|
|
|
|
println!("EFFECTIVE TPS: {tps:?}");
|
|
|
|
|
|
|
|
println!("Transfering remaining lamports to payer");
|
|
|
|
transfer_back_to_payer(rpc_client, fee_payer, signers, priority_fee).await;
|
|
|
|
Ok(())
|
2024-02-26 11:43:02 -08:00
|
|
|
}
|