2023-02-23 02:02:57 -08:00
|
|
|
use {
|
2023-03-14 05:39:19 -07:00
|
|
|
log::info,
|
2023-03-13 14:14:17 -07:00
|
|
|
mango_simulation::{
|
2023-02-23 02:02:57 -08:00
|
|
|
cli,
|
2023-06-23 06:30:02 -07:00
|
|
|
confirmation_strategies::confirmation_by_lite_rpc_notification_stream,
|
2023-04-05 10:06:58 -07:00
|
|
|
crank::{self, KeeperConfig},
|
2023-02-23 02:02:57 -08:00
|
|
|
helpers::{
|
|
|
|
get_latest_blockhash, get_mango_market_perps_cache, start_blockhash_polling_service,
|
2023-03-14 05:39:19 -07:00
|
|
|
to_sdk_pk,
|
2023-02-23 02:02:57 -08:00
|
|
|
},
|
|
|
|
keeper::start_keepers,
|
|
|
|
mango::{AccountKeys, MangoConfig},
|
2023-04-15 03:17:15 -07:00
|
|
|
market_markers::{clean_market_makers, start_market_making_threads},
|
2023-03-14 05:39:19 -07:00
|
|
|
result_writer::initialize_result_writers,
|
|
|
|
states::PerpMarketCache,
|
|
|
|
stats::MangoSimulationStats,
|
2023-03-15 08:13:25 -07:00
|
|
|
tpu_manager::TpuManager,
|
2023-02-15 08:08:55 -08:00
|
|
|
},
|
2023-06-15 01:41:58 -07:00
|
|
|
solana_client::nonblocking::rpc_client::RpcClient as NbRpcClient,
|
2023-06-23 06:30:02 -07:00
|
|
|
solana_lite_rpc_core::{
|
|
|
|
block_store::BlockStore,
|
|
|
|
notifications::NotificationMsg,
|
|
|
|
quic_connection_utils::QuicConnectionParameters,
|
|
|
|
tx_store::{empty_tx_store, TxStore},
|
|
|
|
},
|
|
|
|
solana_lite_rpc_services::{
|
|
|
|
block_listenser::BlockListener,
|
|
|
|
tpu_utils::tpu_service::{TpuService, TpuServiceConfig},
|
|
|
|
transaction_replayer::TransactionReplayer,
|
|
|
|
transaction_service::{TransactionService, TransactionServiceBuilder},
|
|
|
|
tx_sender::TxSender,
|
|
|
|
},
|
2023-02-24 06:12:14 -08:00
|
|
|
solana_program::pubkey::Pubkey,
|
2023-03-15 08:13:25 -07:00
|
|
|
solana_sdk::{commitment_config::CommitmentConfig, signer::keypair::Keypair},
|
2023-02-23 02:02:57 -08:00
|
|
|
std::{
|
|
|
|
fs,
|
2023-02-24 06:12:14 -08:00
|
|
|
str::FromStr,
|
2023-03-14 05:39:19 -07:00
|
|
|
sync::atomic::{AtomicBool, AtomicU64, Ordering},
|
|
|
|
sync::Arc,
|
2023-03-09 07:20:49 -08:00
|
|
|
time::Duration,
|
|
|
|
},
|
2023-06-23 06:30:02 -07:00
|
|
|
tokio::sync::mpsc::{unbounded_channel, UnboundedSender},
|
2023-03-14 05:39:19 -07:00
|
|
|
tokio::{sync::RwLock, task::JoinHandle},
|
2022-09-02 07:26:44 -07:00
|
|
|
};
|
|
|
|
|
2023-03-31 10:16:24 -07:00
|
|
|
const METRICS_NAME: &str = "mango-bencher";
|
|
|
|
|
2023-06-23 06:30:02 -07:00
|
|
|
async fn configure_transaction_service(
|
|
|
|
rpc_client: Arc<NbRpcClient>,
|
|
|
|
identity: Keypair,
|
|
|
|
block_store: BlockStore,
|
|
|
|
tx_store: TxStore,
|
|
|
|
notifier: UnboundedSender<NotificationMsg>,
|
|
|
|
) -> (TransactionService, JoinHandle<anyhow::Result<()>>) {
|
2023-06-15 01:41:58 -07:00
|
|
|
let slot = rpc_client.get_slot().await.expect("GetSlot should work");
|
2023-06-23 06:29:28 -07:00
|
|
|
let tpu_config = TpuServiceConfig {
|
2023-06-23 06:30:02 -07:00
|
|
|
fanout_slots: 12,
|
|
|
|
number_of_leaders_to_cache: 1024,
|
|
|
|
clusterinfo_refresh_time: Duration::from_secs(60 * 60),
|
|
|
|
leader_schedule_update_frequency: Duration::from_secs(10),
|
|
|
|
maximum_transaction_in_queue: 200_000,
|
|
|
|
maximum_number_of_errors: 10,
|
|
|
|
quic_connection_params: QuicConnectionParameters {
|
|
|
|
connection_timeout: Duration::from_secs(1),
|
|
|
|
connection_retry_count: 10,
|
|
|
|
finalize_timeout: Duration::from_millis(200),
|
|
|
|
max_number_of_connections: 10,
|
|
|
|
unistream_timeout: Duration::from_millis(500),
|
|
|
|
write_timeout: Duration::from_secs(1),
|
|
|
|
number_of_transactions_per_unistream: 10,
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
2023-06-23 06:29:28 -07:00
|
|
|
let tpu_service = TpuService::new(
|
|
|
|
tpu_config,
|
|
|
|
Arc::new(identity),
|
|
|
|
slot,
|
|
|
|
rpc_client.clone(),
|
|
|
|
tx_store.clone(),
|
|
|
|
)
|
2023-06-23 06:30:02 -07:00
|
|
|
.await
|
|
|
|
.expect("Should be able to create TPU");
|
|
|
|
|
2023-06-15 01:41:58 -07:00
|
|
|
let tx_sender = TxSender::new(tx_store.clone(), tpu_service.clone());
|
2023-06-23 06:30:02 -07:00
|
|
|
let block_listenser =
|
|
|
|
BlockListener::new(rpc_client.clone(), tx_store.clone(), block_store.clone());
|
|
|
|
let replayer = TransactionReplayer::new(
|
|
|
|
tpu_service.clone(),
|
|
|
|
tx_store.clone(),
|
|
|
|
Duration::from_secs(2),
|
|
|
|
);
|
|
|
|
let builder = TransactionServiceBuilder::new(
|
|
|
|
tx_sender,
|
|
|
|
replayer,
|
|
|
|
block_listenser,
|
|
|
|
tpu_service,
|
|
|
|
1_000_000,
|
|
|
|
);
|
2023-06-23 06:29:28 -07:00
|
|
|
builder.start(Some(notifier), block_store, 10, Duration::from_secs(90))
|
2023-06-15 01:41:58 -07:00
|
|
|
}
|
|
|
|
|
2023-03-14 05:39:19 -07:00
|
|
|
#[tokio::main(flavor = "multi_thread", worker_threads = 10)]
|
|
|
|
pub async fn main() -> anyhow::Result<()> {
|
2023-03-31 10:16:24 -07:00
|
|
|
solana_logger::setup_with_default("info");
|
2022-09-02 07:26:44 -07:00
|
|
|
solana_metrics::set_panic_hook("bench-mango", /*version:*/ None);
|
|
|
|
|
2023-07-06 23:12:57 -07:00
|
|
|
let version = solana_version::version!();
|
|
|
|
let matches = cli::build_args(version).get_matches();
|
2022-09-02 07:26:44 -07:00
|
|
|
let cli_config = cli::extract_args(&matches);
|
|
|
|
|
|
|
|
let cli::Config {
|
|
|
|
json_rpc_url,
|
|
|
|
websocket_url,
|
2023-06-15 01:41:58 -07:00
|
|
|
identity,
|
2022-09-02 07:26:44 -07:00
|
|
|
account_keys,
|
|
|
|
mango_keys,
|
|
|
|
duration,
|
|
|
|
quotes_per_second,
|
|
|
|
transaction_save_file,
|
2022-09-14 06:48:33 -07:00
|
|
|
block_data_save_file,
|
2022-10-01 05:51:46 -07:00
|
|
|
mango_cluster,
|
2023-02-21 02:04:02 -08:00
|
|
|
priority_fees_proba,
|
2023-02-23 02:02:57 -08:00
|
|
|
keeper_authority,
|
2023-02-24 06:48:08 -08:00
|
|
|
number_of_markers_per_mm,
|
2023-04-15 03:39:26 -07:00
|
|
|
keeper_prioritization,
|
2022-09-02 07:26:44 -07:00
|
|
|
..
|
|
|
|
} = &cli_config;
|
2023-02-24 06:48:08 -08:00
|
|
|
let number_of_markers_per_mm = *number_of_markers_per_mm;
|
2023-04-15 03:39:26 -07:00
|
|
|
let keeper_prioritization = *keeper_prioritization;
|
2022-09-02 07:26:44 -07:00
|
|
|
|
2022-09-08 12:52:18 -07:00
|
|
|
let transaction_save_file = transaction_save_file.clone();
|
2022-09-14 06:48:33 -07:00
|
|
|
let block_data_save_file = block_data_save_file.clone();
|
2022-09-08 12:52:18 -07:00
|
|
|
|
2023-02-21 00:49:20 -08:00
|
|
|
info!(
|
|
|
|
"Connecting to the cluster {}, {}",
|
|
|
|
json_rpc_url, websocket_url
|
|
|
|
);
|
2022-09-02 07:26:44 -07:00
|
|
|
|
|
|
|
let account_keys_json = fs::read_to_string(account_keys).expect("unable to read accounts file");
|
|
|
|
let account_keys_parsed: Vec<AccountKeys> =
|
|
|
|
serde_json::from_str(&account_keys_json).expect("accounts JSON was not well-formatted");
|
|
|
|
|
|
|
|
let mango_keys_json = fs::read_to_string(mango_keys).expect("unable to read mango keys file");
|
|
|
|
let mango_keys_parsed: MangoConfig =
|
|
|
|
serde_json::from_str(&mango_keys_json).expect("mango JSON was not well-formatted");
|
|
|
|
|
2022-10-01 05:51:46 -07:00
|
|
|
let mango_group_id = mango_cluster;
|
2022-09-02 07:26:44 -07:00
|
|
|
let mango_group_config = mango_keys_parsed
|
|
|
|
.groups
|
|
|
|
.iter()
|
2022-10-01 05:51:46 -07:00
|
|
|
.find(|g| g.name == *mango_group_id)
|
2023-06-15 01:41:58 -07:00
|
|
|
.expect("Mango group config should exist");
|
2022-09-02 07:26:44 -07:00
|
|
|
|
2023-03-14 05:39:19 -07:00
|
|
|
let nb_rpc_client = Arc::new(NbRpcClient::new_with_commitment(
|
|
|
|
json_rpc_url.to_string(),
|
2023-06-27 01:44:59 -07:00
|
|
|
CommitmentConfig::finalized(),
|
2023-03-14 05:39:19 -07:00
|
|
|
));
|
|
|
|
|
2023-06-15 01:41:58 -07:00
|
|
|
let tx_store = empty_tx_store();
|
2023-06-23 06:30:02 -07:00
|
|
|
let block_store = BlockStore::new(&nb_rpc_client)
|
|
|
|
.await
|
|
|
|
.expect("Blockstore should be created");
|
2023-06-27 01:44:59 -07:00
|
|
|
|
2023-06-15 01:41:58 -07:00
|
|
|
let (notif_sx, notif_rx) = unbounded_channel();
|
2023-06-23 06:30:02 -07:00
|
|
|
let (transaction_service, tx_service_jh) = configure_transaction_service(
|
|
|
|
nb_rpc_client.clone(),
|
|
|
|
Keypair::from_bytes(identity.to_bytes().as_slice()).unwrap(),
|
|
|
|
block_store,
|
|
|
|
tx_store,
|
|
|
|
notif_sx,
|
|
|
|
)
|
|
|
|
.await;
|
2023-06-15 01:41:58 -07:00
|
|
|
|
2023-03-15 08:13:25 -07:00
|
|
|
let nb_users = account_keys_parsed.len();
|
2023-02-17 04:01:18 -08:00
|
|
|
|
2023-03-31 10:16:24 -07:00
|
|
|
let mut mango_sim_stats = MangoSimulationStats::new(
|
2023-03-15 08:13:25 -07:00
|
|
|
nb_users,
|
|
|
|
*quotes_per_second as usize,
|
|
|
|
number_of_markers_per_mm as usize,
|
|
|
|
duration.as_secs() as usize,
|
2023-02-21 00:49:20 -08:00
|
|
|
);
|
2022-09-02 07:26:44 -07:00
|
|
|
|
2023-03-15 08:13:25 -07:00
|
|
|
let (tx_record_sx, tx_record_rx) = tokio::sync::mpsc::unbounded_channel();
|
|
|
|
|
2023-06-15 01:41:58 -07:00
|
|
|
// continuosly fetch blockhash
|
|
|
|
let exit_signal = Arc::new(AtomicBool::new(false));
|
|
|
|
let latest_blockhash = get_latest_blockhash(&nb_rpc_client.clone()).await;
|
|
|
|
let blockhash = Arc::new(RwLock::new(latest_blockhash));
|
|
|
|
let current_slot = Arc::new(AtomicU64::new(0));
|
|
|
|
let blockhash_thread = start_blockhash_polling_service(
|
|
|
|
exit_signal.clone(),
|
|
|
|
blockhash.clone(),
|
|
|
|
current_slot.clone(),
|
2023-03-15 08:13:25 -07:00
|
|
|
nb_rpc_client.clone(),
|
2023-06-15 01:41:58 -07:00
|
|
|
);
|
|
|
|
|
|
|
|
let tpu_manager = TpuManager::new(
|
|
|
|
transaction_service,
|
2023-03-15 08:13:25 -07:00
|
|
|
mango_sim_stats.clone(),
|
|
|
|
tx_record_sx.clone(),
|
|
|
|
)
|
2023-06-15 01:41:58 -07:00
|
|
|
.await?;
|
2023-05-12 02:18:07 -07:00
|
|
|
|
2022-09-02 07:26:44 -07:00
|
|
|
info!(
|
|
|
|
"accounts:{:?} markets:{:?} quotes_per_second:{:?} expected_tps:{:?} duration:{:?}",
|
|
|
|
account_keys_parsed.len(),
|
2023-04-15 03:39:26 -07:00
|
|
|
number_of_markers_per_mm,
|
2022-09-02 07:26:44 -07:00
|
|
|
quotes_per_second,
|
2023-07-07 00:26:04 -07:00
|
|
|
account_keys_parsed.len() * number_of_markers_per_mm as usize * *quotes_per_second as usize,
|
2022-09-02 07:26:44 -07:00
|
|
|
duration
|
|
|
|
);
|
|
|
|
|
2023-06-23 06:30:02 -07:00
|
|
|
let mango_program_pk = Pubkey::from_str(mango_group_config.mango_program_id.as_str())
|
|
|
|
.expect("Mango program should be able to convert into pubkey");
|
2023-02-15 08:08:55 -08:00
|
|
|
let perp_market_caches: Vec<PerpMarketCache> =
|
2023-06-15 01:41:58 -07:00
|
|
|
get_mango_market_perps_cache(nb_rpc_client.clone(), mango_group_config, &mango_program_pk)
|
|
|
|
.await;
|
2023-02-15 08:08:55 -08:00
|
|
|
|
2023-02-24 06:48:08 -08:00
|
|
|
let quote_root_bank =
|
2023-06-23 06:30:02 -07:00
|
|
|
Pubkey::from_str(mango_group_config.tokens.last().unwrap().root_key.as_str())
|
|
|
|
.expect("Quote root bank should be able to convert into pubkey");
|
2023-02-24 06:48:08 -08:00
|
|
|
let quote_node_banks = mango_group_config
|
|
|
|
.tokens
|
|
|
|
.last()
|
|
|
|
.unwrap()
|
2023-02-24 06:12:14 -08:00
|
|
|
.node_keys
|
|
|
|
.iter()
|
2023-06-23 06:30:02 -07:00
|
|
|
.map(|x| {
|
|
|
|
Pubkey::from_str(x.as_str()).expect("Token mint should be able to convert into pubkey")
|
|
|
|
})
|
2023-02-24 06:12:14 -08:00
|
|
|
.collect();
|
2023-04-15 03:17:15 -07:00
|
|
|
|
|
|
|
clean_market_makers(
|
|
|
|
nb_rpc_client.clone(),
|
|
|
|
&account_keys_parsed,
|
|
|
|
&perp_market_caches,
|
|
|
|
blockhash.clone(),
|
|
|
|
)
|
|
|
|
.await;
|
|
|
|
|
2023-02-23 02:02:57 -08:00
|
|
|
// start keeper if keeper authority is present
|
|
|
|
let keepers_jl = if let Some(keeper_authority) = keeper_authority {
|
|
|
|
let jl = start_keepers(
|
|
|
|
exit_signal.clone(),
|
2023-03-15 08:13:25 -07:00
|
|
|
tpu_manager.clone(),
|
2023-02-23 02:02:57 -08:00
|
|
|
perp_market_caches.clone(),
|
|
|
|
blockhash.clone(),
|
2023-03-15 08:13:25 -07:00
|
|
|
current_slot.clone(),
|
2023-02-23 02:02:57 -08:00
|
|
|
keeper_authority,
|
2023-02-24 06:12:14 -08:00
|
|
|
quote_root_bank,
|
|
|
|
quote_node_banks,
|
2023-04-15 03:39:26 -07:00
|
|
|
keeper_prioritization,
|
2023-02-23 02:02:57 -08:00
|
|
|
);
|
|
|
|
Some(jl)
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
};
|
2023-06-15 01:41:58 -07:00
|
|
|
|
2023-03-10 04:38:11 -08:00
|
|
|
let keeper_config = KeeperConfig {
|
|
|
|
program_id: to_sdk_pk(&mango_program_pk),
|
|
|
|
rpc_url: json_rpc_url.clone(),
|
|
|
|
websocket_url: websocket_url.clone(),
|
|
|
|
};
|
2023-02-15 08:08:55 -08:00
|
|
|
|
2023-03-06 00:37:42 -08:00
|
|
|
crank::start(
|
2023-03-10 04:38:11 -08:00
|
|
|
keeper_config,
|
2023-03-06 00:37:42 -08:00
|
|
|
exit_signal.clone(),
|
|
|
|
blockhash.clone(),
|
2023-03-15 08:13:25 -07:00
|
|
|
current_slot.clone(),
|
|
|
|
tpu_manager.clone(),
|
2023-03-06 00:37:42 -08:00
|
|
|
mango_group_config,
|
2023-06-15 01:41:58 -07:00
|
|
|
identity,
|
2023-04-15 03:39:26 -07:00
|
|
|
keeper_prioritization,
|
2023-03-06 00:37:42 -08:00
|
|
|
);
|
|
|
|
|
2023-04-13 10:48:25 -07:00
|
|
|
let warmup_duration = Duration::from_secs(20);
|
2023-03-14 05:39:19 -07:00
|
|
|
info!("waiting for keepers to warmup for {warmup_duration:?}");
|
|
|
|
tokio::time::sleep(warmup_duration).await;
|
|
|
|
|
2023-03-15 08:13:25 -07:00
|
|
|
let mm_tasks: Vec<JoinHandle<()>> = start_market_making_threads(
|
2023-02-15 08:08:55 -08:00
|
|
|
account_keys_parsed.clone(),
|
|
|
|
perp_market_caches.clone(),
|
|
|
|
exit_signal.clone(),
|
|
|
|
blockhash.clone(),
|
|
|
|
current_slot.clone(),
|
2023-06-15 01:41:58 -07:00
|
|
|
tpu_manager.clone(),
|
2023-07-06 23:12:57 -07:00
|
|
|
duration,
|
2023-02-15 08:08:55 -08:00
|
|
|
*quotes_per_second,
|
2023-02-21 02:04:02 -08:00
|
|
|
*priority_fees_proba,
|
2023-02-24 06:48:08 -08:00
|
|
|
number_of_markers_per_mm,
|
2023-02-15 08:08:55 -08:00
|
|
|
);
|
2023-03-06 00:37:42 -08:00
|
|
|
|
2023-03-15 08:13:25 -07:00
|
|
|
info!("Number of MM threads {}", mm_tasks.len());
|
2023-03-14 05:39:19 -07:00
|
|
|
drop(tx_record_sx);
|
2023-06-15 01:41:58 -07:00
|
|
|
let mut tasks = vec![blockhash_thread];
|
2022-09-02 07:26:44 -07:00
|
|
|
|
2023-04-15 03:39:26 -07:00
|
|
|
let (tx_status_sx, tx_status_rx) = tokio::sync::broadcast::channel(1000000);
|
|
|
|
let (block_status_sx, block_status_rx) = tokio::sync::broadcast::channel(1000000);
|
|
|
|
|
|
|
|
let stats_handle = mango_sim_stats.update_from_tx_status_stream(tx_status_rx);
|
|
|
|
tasks.push(stats_handle);
|
2022-09-02 07:26:44 -07:00
|
|
|
|
2023-03-14 05:39:19 -07:00
|
|
|
let mut writers_jh = initialize_result_writers(
|
|
|
|
transaction_save_file,
|
|
|
|
block_data_save_file,
|
2023-04-15 03:39:26 -07:00
|
|
|
tx_status_sx.subscribe(),
|
2023-03-16 08:25:26 -07:00
|
|
|
block_status_rx,
|
2023-03-14 05:39:19 -07:00
|
|
|
);
|
|
|
|
tasks.append(&mut writers_jh);
|
|
|
|
|
2023-06-15 01:41:58 -07:00
|
|
|
let mut confirmation_threads = confirmation_by_lite_rpc_notification_stream(
|
2023-03-14 05:39:19 -07:00
|
|
|
tx_record_rx,
|
2023-06-15 01:41:58 -07:00
|
|
|
notif_rx,
|
2023-03-14 05:39:19 -07:00
|
|
|
tx_status_sx,
|
|
|
|
block_status_sx,
|
2023-06-23 06:30:02 -07:00
|
|
|
exit_signal.clone(),
|
2023-03-14 05:39:19 -07:00
|
|
|
);
|
|
|
|
tasks.append(&mut confirmation_threads);
|
2023-02-23 02:02:57 -08:00
|
|
|
|
2023-03-15 08:13:25 -07:00
|
|
|
if let Some(keepers_jl) = keepers_jl {
|
|
|
|
tasks.push(keepers_jl);
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
let exit_signal = exit_signal.clone();
|
2023-03-31 10:16:24 -07:00
|
|
|
let mut mango_sim_stats = mango_sim_stats.clone();
|
2023-03-15 08:13:25 -07:00
|
|
|
let reporting_thread = tokio::spawn(async move {
|
|
|
|
loop {
|
|
|
|
if exit_signal.load(Ordering::Relaxed) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
tokio::time::sleep(Duration::from_secs(60)).await;
|
2023-04-15 03:39:26 -07:00
|
|
|
mango_sim_stats.report(false, METRICS_NAME).await;
|
2023-03-15 08:13:25 -07:00
|
|
|
}
|
|
|
|
});
|
|
|
|
tasks.push(reporting_thread);
|
2023-02-23 02:02:57 -08:00
|
|
|
}
|
2023-03-15 08:13:25 -07:00
|
|
|
|
|
|
|
// when all market makers tasks are joined that means we are ready to exit
|
|
|
|
// we start stopping all other process
|
|
|
|
// some processes like confirmation of transactions will take some time and will get additional 2 minutes
|
|
|
|
// to confirm remaining transactions
|
2023-06-15 01:41:58 -07:00
|
|
|
let market_makers_wait_task = {
|
|
|
|
let exit_signal = exit_signal.clone();
|
|
|
|
tokio::spawn(async move {
|
|
|
|
futures::future::join_all(mm_tasks).await;
|
|
|
|
info!("finished market making, joining all other services");
|
|
|
|
exit_signal.store(true, Ordering::Relaxed);
|
|
|
|
})
|
|
|
|
};
|
|
|
|
|
2023-06-23 06:29:28 -07:00
|
|
|
tasks.push(market_makers_wait_task);
|
2023-06-15 01:41:58 -07:00
|
|
|
|
|
|
|
let transaction_service = tokio::spawn(async move {
|
|
|
|
let _ = tx_service_jh.await;
|
|
|
|
info!("Transaction service joined");
|
|
|
|
});
|
2023-03-15 08:13:25 -07:00
|
|
|
|
2023-06-23 06:30:02 -07:00
|
|
|
tokio::select! {
|
2023-06-23 06:29:28 -07:00
|
|
|
_ = futures::future::join_all(tasks) => {},
|
|
|
|
_ = transaction_service => {},
|
|
|
|
};
|
2023-06-23 06:30:02 -07:00
|
|
|
|
2023-04-15 03:39:26 -07:00
|
|
|
mango_sim_stats.report(true, METRICS_NAME).await;
|
2023-03-14 05:39:19 -07:00
|
|
|
Ok(())
|
2022-09-02 07:26:44 -07:00
|
|
|
}
|