Skip to content

Commit

Permalink
add test_contract_deployment
Browse files Browse the repository at this point in the history
  • Loading branch information
LePremierHomme committed Jan 19, 2025
1 parent 14f948c commit 184ea13
Show file tree
Hide file tree
Showing 3 changed files with 154 additions and 33 deletions.
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
// Copyright 2022-2024 Protocol Labs
// SPDX-License-Identifier: Apache-2.0, MIT

use anyhow::anyhow;
use std::fmt::{Display, Formatter};

#[derive(Debug)]
#[derive(Debug, Default)]
pub struct Metrics {
pub mean: f64,
pub median: f64,
Expand All @@ -29,9 +28,9 @@ impl Metrics {
}
}

pub fn calc_metrics(data: Vec<f64>) -> anyhow::Result<Metrics> {
pub fn calc_metrics(data: Vec<f64>) -> Metrics {
if data.is_empty() {
return Err(anyhow!("empty data"));
return Metrics::default();
}

let mut sorted_data = data.clone();
Expand All @@ -52,13 +51,13 @@ pub fn calc_metrics(data: Vec<f64>) -> anyhow::Result<Metrics> {
let percentile_90_index = ((count as f64) * 0.9).ceil() as usize - 1;
let percentile_90 = sorted_data[percentile_90_index];

Ok(Metrics {
Metrics {
mean,
median,
max,
min,
percentile_90,
})
}
}

#[cfg(test)]
Expand All @@ -76,7 +75,7 @@ mod tests {
let expected_min = 1.0;
let expected_percentile_90 = 9.0;

let metrics = calc_metrics(data).unwrap();
let metrics = calc_metrics(data);

assert!((metrics.mean - expected_mean).abs() < FLOAT_TOLERANCE);
assert!((metrics.median - expected_median).abs() < FLOAT_TOLERANCE);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,10 @@ impl StepSummary {
let mut errs = Vec::new();

for res in results {
if let Some(err) = res.err {
errs.push(err);
}

let Some(bencher) = res.bencher else { continue };

for (key, duration) in bencher.latencies.clone() {
Expand All @@ -162,18 +166,14 @@ impl StepSummary {
.or_default()
.push(duration.as_secs_f64());
}

if let Some(err) = res.err {
errs.push(err);
}
}

let latencies = latencies
.into_iter()
.map(|(key, dataset)| (key, calc_metrics(dataset).unwrap()))
.map(|(key, dataset)| (key, calc_metrics(dataset)))
.collect();

let tps = calc_metrics(calc_tps(blocks)).unwrap();
let tps = calc_metrics(calc_tps(blocks));

Self {
cfg,
Expand Down
162 changes: 142 additions & 20 deletions fendermint/testing/materializer/tests/docker_tests/benches.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
// Copyright 2022-2024 Protocol Labs
// SPDX-License-Identifier: Apache-2.0, MIT

use std::sync::Arc;
use std::time::Duration;

use crate::make_testnet;
use anyhow::{bail, Context};
use anyhow::{anyhow, bail, Context};
use ethers::abi::Abi;
use ethers::contract::{abigen, ContractFactory};
use ethers::core::types::Bytes;
use ethers::prelude::{Block, H256};
use ethers::types::U256;
use ethers::{
Expand All @@ -26,7 +26,8 @@ use fendermint_materializer::{
HasEthApi,
};
use futures::FutureExt;
use tokio::time::sleep;
use std::sync::Arc;
use std::time::Duration;

const MANIFEST: &str = "benches.yaml";

Expand All @@ -47,16 +48,13 @@ where
Ok(SignerMiddleware::new(provider, wallet))
}

const BLOCK_GAS_LIMIT: u64 = 10_000_000_000;
const MAX_TX_GAS_LIMIT: u64 = 3_000_000;

#[serial_test::serial]
#[tokio::test]
async fn test_native_coin_transfer() -> Result<(), anyhow::Error> {
let (testnet, cleanup) = make_testnet(MANIFEST, |_| {}).await?;

let block_gas_limit = U256::from(BLOCK_GAS_LIMIT);
let max_tx_gas_limit = U256::from(MAX_TX_GAS_LIMIT);
let block_gas_limit = U256::from(10_000_000_000u64);
let max_tx_gas_limit = U256::from(3_000_000u64);

let pangea = testnet.node(&testnet.root().node("pangea"))?;
let provider = pangea
Expand Down Expand Up @@ -141,18 +139,142 @@ async fn test_native_coin_transfer() -> Result<(), anyhow::Error> {
}
input.bencher.mempool();

loop {
if let Ok(Some(tx)) = middleware.get_transaction_receipt(tx_hash).await {
println!(
"tx included in block {:?} (test_id={})",
tx.block_number, input.test_id
);
let block_number = tx.block_number.unwrap().as_u64();
input.bencher.block_inclusion(block_number);
break;
let receipt = pending
.interval(Duration::from_millis(50))
.confirmations(1)
.await?
.ok_or(anyhow!("fd"))?;
println!(
"tx included in block {:?} (test_id={})",
receipt.block_number, input.test_id
);
let block_number = receipt.block_number.unwrap().as_u64();
input.bencher.block_inclusion(block_number);

Ok(TestOutput {
bencher: input.bencher,
tx_hash,
})
};
test.boxed()
})
.await;

cancel.send();
let blocks = blocks_collector.await??;
let summary = ExecutionSummary::new(cfg.clone(), blocks, results);
summary.print();

let res = summary.to_result();
let Ok(testnet) = Arc::try_unwrap(testnet) else {
bail!("Arc::try_unwrap(testnet)");
};
cleanup(res.is_err(), testnet).await;
res
}

abigen!(SimpleCoin, "../../testing/contracts/SimpleCoin.abi");
const SIMPLECOIN_HEX: &str = include_str!("../../../../testing/contracts/SimpleCoin.bin");

#[serial_test::serial]
#[tokio::test]
async fn test_contract_deployment() -> Result<(), anyhow::Error> {
let (testnet, cleanup) = make_testnet(MANIFEST, |_| {}).await?;

let block_gas_limit = U256::from(10_000_000_000u64);
let max_tx_gas_limit = U256::from(50_000_000u64);

let pangea = testnet.node(&testnet.root().node("pangea"))?;
let provider = pangea
.ethapi_http_provider()?
.expect("ethapi should be enabled");
let chain_id = provider
.get_chainid()
.await
.context("failed to get chain ID")?;

let cancel = Arc::new(Signal::new());

// Set up background blocks collector.
let blocks_collector = {
let cancel = cancel.clone();
let provider = provider.clone();
let assert = move |block: &Block<H256>| {
// Make sure block gas limit isn't the bottleneck.
let unused_gas_limit = block_gas_limit - block.gas_limit;
assert!(unused_gas_limit >= max_tx_gas_limit);
};
tokio::spawn(collect_blocks(cancel, provider, assert))
};

// Drive concurrency.
let cfg = Execution::new()
.add_step(1, 5)
.add_step(10, 5)
.add_step(100, 5)
.add_step(150, 5);
let testnet = Arc::new(testnet);
let testnet_clone = testnet.clone();
let nonce_manager = Arc::new(NonceManager::new());

let results = concurrency::execute(cfg.clone(), move |mut input| {
let testnet = testnet_clone.clone();
let nonce_manager = nonce_manager.clone();
let provider = provider.clone();

let test = async move {
let sender = testnet.account_mod_nth(input.test_id);
println!("running (test_id={})", input.test_id);

let middleware = make_middleware(provider, sender, chain_id)
.await
.context("make_middleware")?;
let middleware = Arc::new(middleware);

let sender: H160 = sender.eth_addr().into();
let nonce = nonce_manager.get_and_increment(sender).await;

let bytecode =
Bytes::from(hex::decode(SIMPLECOIN_HEX).context("failed to decode contract hex")?);
let abi: Abi = SIMPLECOIN_ABI.clone();
let factory = ContractFactory::new(abi, bytecode.clone(), middleware.clone());
let mut deployer = factory.deploy(())?;
deployer.tx.set_nonce(nonce);

let gas_estimation = middleware.estimate_gas(&deployer.tx, None).await.unwrap();
deployer.tx.set_gas(gas_estimation);
assert!(gas_estimation <= max_tx_gas_limit);

input.bencher.start();

let pending: PendingTransaction<_> = middleware
.send_transaction(deployer.tx, None)
.await
.context("failed to send txn")?;
let tx_hash = pending.tx_hash();
println!("sent tx {:?} (test_id={})", tx_hash, input.test_id);

// We expect that the transaction is pending, however it should not return an error.
match middleware.get_transaction(tx_hash).await {
Ok(Some(_)) => {}
Ok(None) => bail!("pending transaction not found by eth hash"),
Err(e) => {
bail!("failed to get pending transaction: {e}")
}
sleep(Duration::from_millis(100)).await;
}
input.bencher.mempool();

let receipt = pending
.interval(Duration::from_millis(50))
.confirmations(1)
.await?
.ok_or(anyhow!("fd"))?;
println!(
"tx included in block {:?}, contract address: {:?} (test_id={})",
receipt.block_number, receipt.contract_address, input.test_id
);
let block_number = receipt.block_number.unwrap().as_u64();
input.bencher.block_inclusion(block_number);

Ok(TestOutput {
bencher: input.bencher,
Expand Down

0 comments on commit 184ea13

Please sign in to comment.