Skip to content

Commit

Permalink
test(host): renew host tests (#431)
Browse files Browse the repository at this point in the history
* feat(host/tests): rewrite common utils and test cases

* test: add aggregation test

* fix(host/tests): fix No BlockProposed event found for block

* fix(host/test): use SELECTED_BLOCKS to filter duplicated

* fix(host/test): handle duplicate
  • Loading branch information
keroro520 authored Dec 17, 2024
1 parent 7139be0 commit 40274d7
Show file tree
Hide file tree
Showing 18 changed files with 912 additions and 472 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions host/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ reth-provider = { workspace = true }
assert_cmd = { workspace = true }
rstest = { workspace = true }
ethers-core = { workspace = true }
rand = { workspace = true }

[features]
default = []
Expand Down
4 changes: 4 additions & 0 deletions host/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,10 @@ impl ProverState {
// Read the config file.
opts.merge_from_file()?;

Self::init_with_opts(opts)
}

pub fn init_with_opts(opts: Opts) -> HostResult<Self> {
let chain_specs = if let Some(cs_path) = &opts.chain_spec_path {
SupportedChainSpecs::merge_from_file(cs_path.clone()).unwrap_or_default()
} else {
Expand Down
127 changes: 127 additions & 0 deletions host/tests/common/chain.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
use once_cell::sync::Lazy;
use std::cmp::max;
use std::collections::HashSet;
use std::sync::Mutex;

use raiko_lib::consts::{Network, SupportedChainSpecs};
use rand::Rng;
use serde::Deserialize;

#[derive(Debug, Deserialize)]
pub(crate) struct RPCResult<T> {
pub(crate) result: T,
}

pub(crate) type BlockHeightResponse = RPCResult<String>;

#[derive(Debug, Deserialize)]
pub(crate) struct Block {
#[serde(rename = "gasUsed")]
pub(crate) gas_used: String,
}

pub(crate) type BlockResponse = RPCResult<Block>;

// TODO: randomly select block, filter out the block with no BlockProposed event, order by gas used

static SELECTED_BLOCKS: Lazy<Mutex<HashSet<u64>>> = Lazy::new(|| Mutex::new(HashSet::new()));

// NOTE: In order to avoid request collision during multiple tests running in parallel,
// we select a random block number to make the proof request unique.
pub async fn randomly_select_block(network: Network) -> anyhow::Result<u64> {
let supported_chains = SupportedChainSpecs::default();
let client = reqwest::Client::new();
let beacon = supported_chains
.get_chain_spec(&network.to_string())
.unwrap()
.rpc;

println!("[randomly_select_block]: network: {network}, url: {beacon}");

let tip_block_number = get_block_number(network).await?;
let from_block_number = max(1, tip_block_number - 100);
let random_block_number = rand::thread_rng().gen_range(from_block_number..tip_block_number);

let mut min_gas_used = u64::MAX;
let mut min_gas_used_block_number = 0;
for block_number in random_block_number..tip_block_number {
let gas_used = get_block_gas_used(&client, &beacon, block_number).await?;

if SELECTED_BLOCKS.lock().unwrap().contains(&block_number) {
continue;
}

// Avoid the error "No BlockProposed event found for block"
if 200000 < gas_used && gas_used < min_gas_used {
min_gas_used = gas_used;
min_gas_used_block_number = block_number;
}
}

if min_gas_used_block_number == 0 {
return Err(anyhow::anyhow!("No zero gas used block found"));
}

SELECTED_BLOCKS
.lock()
.unwrap()
.insert(min_gas_used_block_number);
Ok(min_gas_used_block_number)
}

// NOTE: In order to avoid request collision during multiple tests running in parallel,
// we select a random block number to make the proof request unique.
pub async fn randomly_select_blocks(network: Network, count: usize) -> anyhow::Result<Vec<u64>> {
let mut blocks = Vec::with_capacity(count);
for _ in 0..count {
blocks.push(randomly_select_block(network).await?);
}
Ok(blocks)
}

async fn get_block_gas_used(
client: &reqwest::Client,
url: &str,
block_number: u64,
) -> anyhow::Result<u64> {
let response = client
.post(url)
.json(&serde_json::json!({
"jsonrpc": "2.0",
"method": "eth_getBlockByNumber",
"params": [format!("0x{block_number:x}"), false],
"id": 1
}))
.send()
.await?
.json::<BlockResponse>()
.await?;

let gas_used = u64::from_str_radix(&response.result.gas_used[2..], 16)?;
Ok(gas_used)
}

pub(crate) async fn get_block_number(network: Network) -> anyhow::Result<u64> {
let supported_chains = SupportedChainSpecs::default();
let client = reqwest::Client::new();
let beacon = supported_chains
.get_chain_spec(&network.to_string())
.unwrap()
.rpc;

let response = client
.post(beacon.clone())
.json(&serde_json::json!({
"jsonrpc": "2.0",
"method": "eth_blockNumber",
"params": [],
"id": 1
}))
.send()
.await?
.json::<BlockHeightResponse>()
.await?;

let block_number = u64::from_str_radix(&response.result[2..], 16)?;
Ok(block_number)
}
113 changes: 32 additions & 81 deletions host/tests/common/client.rs
Original file line number Diff line number Diff line change
@@ -1,106 +1,57 @@
use raiko_core::interfaces::ProofRequestOpt;
use raiko_host::server::api::{v1, v2};
use raiko_tasks::{ProofTaskDescriptor, TaskStatus};

const URL: &str = "http://localhost:8080";

pub struct ProofClient {
use serde::de::DeserializeOwned;
use serde::Serialize;

/// Raiko client.
///
/// Example:
/// ```
/// let client = Client::new("http://localhost:8080");
/// let request = raiko_host::server::api::v1::ProofRequest::default();
/// let response = client.send_request("/v1/proof", &request).await?;
/// ```
pub struct Client {
url: String,
reqwest_client: reqwest::Client,
}

impl ProofClient {
pub fn new() -> Self {
impl Client {
pub fn new(url: String) -> Self {
Self {
url,
reqwest_client: reqwest::Client::new(),
}
}

pub async fn send_proof_v1(
pub async fn post<Request: Serialize, Response: DeserializeOwned + ?Sized>(
&self,
proof_request: ProofRequestOpt,
) -> anyhow::Result<v1::Status> {
path: &str,
request: &Request,
) -> Result<Response, reqwest::Error> {
let response = self
.reqwest_client
.post(&format!("{URL}/v1/proof"))
.json(&proof_request)
.post(self.build_url(path))
.json(&request)
.send()
.await?;

if response.status().is_success() {
let proof_response = response.json::<v1::Status>().await?;
Ok(proof_response)
} else {
Err(anyhow::anyhow!("Failed to send proof request"))
if !response.status().is_success() {
return Err(response.error_for_status().unwrap_err());
}
}

pub async fn send_proof_v2(
&self,
proof_request: ProofRequestOpt,
) -> anyhow::Result<v2::Status> {
let response = self
.reqwest_client
.post(&format!("{URL}/v2/proof"))
.json(&proof_request)
.send()
.await?;

if response.status().is_success() {
let proof_response = response.json::<v2::Status>().await?;
Ok(proof_response)
} else {
Err(anyhow::anyhow!("Failed to send proof request"))
}
response.json().await
}

pub async fn cancel_proof(
&self,
proof_request: ProofRequestOpt,
) -> anyhow::Result<v2::CancelStatus> {
let response = self
.reqwest_client
.post(&format!("{URL}/v2/proof/cancel"))
.json(&proof_request)
.send()
.await?;
pub async fn get(&self, path: &str) -> Result<reqwest::Response, reqwest::Error> {
let response = self.reqwest_client.get(self.build_url(path)).send().await?;

if response.status().is_success() {
let cancel_response = response.json::<v2::CancelStatus>().await?;
Ok(cancel_response)
} else {
Err(anyhow::anyhow!("Failed to send proof request"))
if !response.status().is_success() {
return Err(response.error_for_status().unwrap_err());
}
}

pub async fn prune_proof(&self) -> anyhow::Result<v2::PruneStatus> {
let response = self
.reqwest_client
.post(&format!("{URL}/v2/proof/prune"))
.send()
.await?;

if response.status().is_success() {
let prune_response = response.json::<v2::PruneStatus>().await?;
Ok(prune_response)
} else {
Err(anyhow::anyhow!("Failed to send proof request"))
}
Ok(response)
}

pub async fn report_proof(&self) -> anyhow::Result<Vec<(ProofTaskDescriptor, TaskStatus)>> {
let response = self
.reqwest_client
.get(&format!("{URL}/v2/proof/report"))
.send()
.await?;

if response.status().is_success() {
let report_response = response
.json::<Vec<(ProofTaskDescriptor, TaskStatus)>>()
.await?;
Ok(report_response)
} else {
Err(anyhow::anyhow!("Failed to send proof request"))
}
fn build_url(&self, path: &str) -> String {
format!("{}/{}", self.url, path.trim_start_matches('/'))
}
}
Loading

0 comments on commit 40274d7

Please sign in to comment.