Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

test(host): renew host tests #431

Merged
merged 5 commits into from
Dec 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading