erc20_payment_lib_test 0.4.1

Payment processor for ERC20 tokens
Documentation
use crate::{
    create_default_config_setup, exclusive_geth_init, setup_random_memory_sqlite_conn,
    test_get_balance,
};
use erc20_payment_lib::config::AdditionalOptions;
use erc20_payment_lib_common::ops::{get_transfer_stats, TransferStatsPart};

use erc20_payment_lib::misc::load_private_keys;
use erc20_payment_lib::runtime::{PaymentRuntime, PaymentRuntimeArgs};
use erc20_payment_lib::signer::PrivateKeySigner;
use erc20_payment_lib_common::error::PaymentError;
use erc20_payment_lib_common::utils::U256ConvExt;
use erc20_payment_lib_common::DriverEvent;
use erc20_payment_lib_common::DriverEventContent::*;
use erc20_payment_lib_extra::{generate_test_payments, GenerateOptions};
use secp256k1::SecretKey;
use std::env;
use std::str::FromStr;
use std::sync::Arc;
use std::time::Duration;
use web3::types::{Address, U256};

#[rustfmt::skip]
pub async fn test_durability2(accounts_count: usize, generate_count: u64, gen_interval_secs: f64, transfers_at_once: usize) -> Result<(), anyhow::Error> {
    // *** TEST SETUP ***
    let geth_container = exclusive_geth_init(Duration::from_secs(6 * 3600)).await;
    let conn = setup_random_memory_sqlite_conn().await;

    let proxy_url_base = format!("http://127.0.0.1:{}", geth_container.web3_proxy_port);
    let proxy_key = "erc20_transfer";

    let (sender, mut receiver) = tokio::sync::mpsc::channel::<DriverEvent>(1);
    let receiver_loop = tokio::spawn(async move {
        let mut transfer_finished_message_count = 0;
        let mut approve_contract_message_count = 0;
        let mut tx_confirmed_message_count = 0;
        let mut fee_paid = U256::from(0_u128);
        let mut fee_paid_approve = U256::from(0_u128);

        while let Some(msg) = receiver.recv().await {
            log::debug!("Received message: {:?}", msg);

            match msg.content {
                TransferFinished(transfer_finished) => {
                    transfer_finished_message_count += 1;
                    fee_paid += U256::from_dec_str(&transfer_finished.token_transfer_dao.fee_paid.expect("fee paid should be set")).expect("fee paid should be a valid U256");
                }
                ApproveFinished(allowance_dao) => {
                    approve_contract_message_count += 1;
                    fee_paid_approve += U256::from_dec_str(&allowance_dao.fee_paid.expect("fee paid should be set")).expect("fee paid should be a valid U256");
                }
                TransactionConfirmed(_tx_dao) => {
                    tx_confirmed_message_count += 1;
                }
                Web3RpcMessage(_) => { }
                StatusChanged(_) => { }
                _ => {
                    //maybe remove this if caused too much hassle to maintain
                    panic!("Unexpected message: {:?}", msg);
                }
            }
        }

        assert!(tx_confirmed_message_count > 0);
        assert_eq!(transfer_finished_message_count, generate_count);
        assert!(approve_contract_message_count > 0 && approve_contract_message_count <= accounts_count);
        (fee_paid, fee_paid_approve)
    });

    let mut config = create_default_config_setup(&proxy_url_base, proxy_key).await;
    let chain_id = config.chain.get_mut("dev").unwrap().chain_id;

    {
        config.chain.get_mut("dev").unwrap().multi_contract.as_mut().unwrap().max_at_once = transfers_at_once;


        let (private_keys, public_keys) = {
            //load private key for account 0xbfb29b133aa51c4b45b49468f9a22958eafea6fa
            let (all_private_keys, all_public_keys) = load_private_keys("0228396638e32d52db01056c00e19bc7bd9bb489e2970a3a7a314d67e55ee963,c2b876dd5ef1bcab6864249c58dfea6018538d67d0237f105ff8b54d32fb98e1,3fa08d05cd8c3ecc61d49d49f482ec8f7ea9a5d7579effb12ea9243f7d7c9591,045fbd511ebae9c0fb94f47ddb0f8e909016e785e730a22e6d620da4c707b258,27086ee0ef84323c3ffea56daa747e3ce7f20fae03b593fa53034027d0fa43c1,20d432cc2c013fbff1d7fa5cc0fdcb7c2b3eb41c2abdc59737063c23714d9ba0,79c2e261b3fc65caadfa9235d38e1a6320d26157990bcc433ae5c0d7973c3808,1817fea7b4c24b53104200fe4e193c7b1f788f583a46e5d3352197a38b7bee7a,089f76e2420baad81208df0834144ae86822ec0a1b70bfc0268ee63e88979a63,3f34bdc61e1ea9037aa06d698fefe6673012fdbc6320983f0ff05a999d3288dc,00787cf04d9ef579cf035c00d4d6b2679fba9ff70b3228b4daf5b6494f7ca6fb,ce9f642585d4d07cf7c8606bb9c6b359d76cc6f1e8c6f394093115ce703d07b2")?;

            let mut private_keys: Vec<SecretKey> = Vec::new();
            let mut public_keys: Vec<Address> = Vec::new();
            if accounts_count > all_private_keys.len() {
                return Err(anyhow::anyhow!("Example only supports up to {} sender accounts", all_private_keys.len()));
            };
            for (private_key, public_key) in all_private_keys.iter().zip(all_public_keys.iter()).take(accounts_count) {
                private_keys.push(*private_key);
                public_keys.push(*public_key);
            }
            (private_keys, public_keys)
        };
        let signer = PrivateKeySigner::new(private_keys.clone());

        let erc20_receiver_pool_size = env::var("ERC20_TEST_RECEIVER_POOL_SIZE").map(|f| usize::from_str(&f).unwrap()).unwrap_or(0);

        let gtp = GenerateOptions {
            chain_name: "dev".to_string(),
            generate_count,
            random_receivers: erc20_receiver_pool_size == 0,
            receivers_ordered_pool: erc20_receiver_pool_size,
            receivers_random_pool: None,
            amounts_pool_size: 100000,
            append_to_db: true,
            file: None,
            separator: ',',
            interval: Some(gen_interval_secs),
            limit_time: None,
            quiet: true,
        };

        let config_ = config.clone();
        let conn_ = conn.clone();
        log::info!("Spawning local task");

        let generate_test_payments_handle = tokio::spawn (
            async move {
                log::info!("Generating test payments");
                generate_test_payments(gtp, &config_, public_keys, Some(conn_)).await?;
                log::info!("Finished generating test payments");
                Ok::<(), PaymentError>(())
            }
        );

        // *** TEST RUN ***
        let conn_ = conn.clone();
        let jh = tokio::spawn(
            async move {
                tokio::time::sleep(Duration::from_secs(1)).await;
                let sp = PaymentRuntime::new(
                    PaymentRuntimeArgs {
                        secret_keys: private_keys,
                        db_filename: Default::default(),
                        config: config.clone(),
                        conn: Some(conn_.clone()),
                        options: Some(AdditionalOptions {
                            keep_running: false,
                            ..Default::default()
                        }),
                        broadcast_sender: None,
                        mspc_sender: Some(sender),

                        extra_testing: None,
                    },
                    Arc::new(Box::new(signer)),
                ).await.unwrap();
                sp.join_tasks().await.unwrap();
            }
        );

        let conn_ = conn.clone();
        let _stats = tokio::spawn(async move {
            loop {
                let stats = match get_transfer_stats(&conn_, chain_id, Some(10000)).await {
                    Ok(stats) => stats,
                    Err(err) => {
                        log::error!("Error from get_transfer_stats {err}");
                        panic!("Error from get_transfer_stats {err}");
                    }
                };

                log::warn!("Stats: {:?}", stats.per_sender.iter().next().map(|(_, val)| &val.all));
                tokio::time::sleep(Duration::from_secs(3)).await;
            }
        });

        let _result = generate_test_payments_handle.await;
        log::info!("Waiting for local task to finish");
        let _r = jh.await;
    }

    {
        // *** RESULT CHECK ***
        let (fee_paid_events, fee_paid_events_approve)  = receiver_loop.await.unwrap();
        log::info!("fee paid from events: {}", fee_paid_events.to_eth().unwrap());

        let transfer_stats = get_transfer_stats(&conn, chain_id, None).await.unwrap();
        let stats_all: Vec<TransferStatsPart> = transfer_stats.per_sender.iter().map(|el|el.1.all.clone())
            .collect();
        let fee_paid_stats = stats_all.iter().map(|el|el.fee_paid).fold(U256::from(0), |acc, x| acc + x);
        log::info!("fee paid from stats: {}", fee_paid_stats.to_eth().unwrap());

        assert_eq!(fee_paid_events, fee_paid_stats);

        log::info!("Number of transfers done: {}", stats_all.iter().map(|el|el.done_count).sum::<u64>());

        assert_eq!(stats_all.iter().map(|el|el.processed_count).sum::<u64>(), 0);
        assert_eq!(stats_all.iter().map(|el|el.done_count).sum::<u64>(), generate_count);

        if accounts_count == 1 {
            let res = test_get_balance(&proxy_url_base, "0xbfb29b133aa51c4b45b49468f9a22958eafea6fa").await?;
            let gas_left = U256::from_dec_str(&res["0xbfb29b133aa51c4b45b49468f9a22958eafea6fa"].gas.clone().unwrap()).unwrap();
            assert_eq!(gas_left + fee_paid_events + fee_paid_events_approve, U256::from(536870912000000000000_u128));
            let mut glm_left = U256::from(1000000000000000000000_u128);
            glm_left -= *stats_all[0].erc20_token_transferred.iter().next().unwrap().1;
            assert_eq!(res["0xbfb29b133aa51c4b45b49468f9a22958eafea6fa"].token, Some(glm_left.to_string()));
        }
        //let transaction_human = list_transactions_human(&proxy_url_base, proxy_key).await;
        //log::info!("transaction list \n {}", transaction_human.join("\n"));
    }

    Ok(())
}