-
Notifications
You must be signed in to change notification settings - Fork 99
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* 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
Showing
18 changed files
with
912 additions
and
472 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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('/')) | ||
} | ||
} |
Oops, something went wrong.