mirror of
https://github.com/hinto-janai/cuprate.git
synced 2024-12-22 19:49:33 +00:00
add tests/pow
This commit is contained in:
parent
bd2624572a
commit
1d1cba4f63
6 changed files with 328 additions and 1 deletions
18
Cargo.lock
generated
18
Cargo.lock
generated
|
@ -3331,6 +3331,24 @@ dependencies = [
|
|||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tests-pow"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"cuprate-consensus-rules",
|
||||
"cuprate-cryptonight",
|
||||
"function_name",
|
||||
"hex",
|
||||
"monero-serai",
|
||||
"randomx-rs",
|
||||
"rayon",
|
||||
"reqwest",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"thread_local",
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.66"
|
||||
|
|
|
@ -54,6 +54,7 @@ members = [
|
|||
"types",
|
||||
|
||||
# Tests
|
||||
"tests/pow",
|
||||
"tests/monero-serai",
|
||||
]
|
||||
|
||||
|
|
|
@ -56,7 +56,7 @@ async fn main() {
|
|||
count = c;
|
||||
|
||||
println!(
|
||||
"blocks processed ... {c} ({:.2}%)",
|
||||
"blocks processed ... {c}/{top_height} ({:.2}%)",
|
||||
(c as f64 / top_height as f64) * 100.0
|
||||
);
|
||||
|
||||
|
|
22
tests/pow/Cargo.toml
Normal file
22
tests/pow/Cargo.toml
Normal file
|
@ -0,0 +1,22 @@
|
|||
[package]
|
||||
name = "tests-pow"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
cuprate-cryptonight = { workspace = true }
|
||||
cuprate-consensus-rules = { workspace = true }
|
||||
|
||||
function_name = { workspace = true }
|
||||
thread_local = { workspace = true }
|
||||
monero-serai = { workspace = true }
|
||||
hex = { workspace = true, features = ["serde", "std"] }
|
||||
serde = { workspace = true, features = ["derive"] }
|
||||
serde_json = { workspace = true, features = ["std"] }
|
||||
tokio = { workspace = true, features = ["full"] }
|
||||
reqwest = { version = "0.12", features = ["json"] }
|
||||
rayon = { workspace = true }
|
||||
randomx-rs = { workspace = true }
|
||||
|
||||
[lints]
|
||||
workspace = true
|
32
tests/pow/src/main.rs
Normal file
32
tests/pow/src/main.rs
Normal file
|
@ -0,0 +1,32 @@
|
|||
use std::{
|
||||
sync::atomic::{AtomicUsize, Ordering},
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
|
||||
mod rpc;
|
||||
|
||||
pub static TESTED_BLOCK_COUNT: AtomicUsize = AtomicUsize::new(0);
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
let now = Instant::now();
|
||||
|
||||
let rpc_node_url = if let Ok(url) = std::env::var("RPC_NODE_URL") {
|
||||
url
|
||||
} else {
|
||||
"http://127.0.0.1:18081/json_rpc".to_string()
|
||||
};
|
||||
println!("rpc_node_url: {rpc_node_url}");
|
||||
|
||||
let rpc_client = rpc::RpcClient::new(rpc_node_url).await;
|
||||
|
||||
tokio::join!(
|
||||
rpc_client.cryptonight_v0(),
|
||||
rpc_client.cryptonight_v1(),
|
||||
rpc_client.cryptonight_v2(),
|
||||
rpc_client.cryptonight_r(),
|
||||
rpc_client.randomx(),
|
||||
);
|
||||
|
||||
println!("finished all PoW, took: {}s", now.elapsed().as_secs());
|
||||
}
|
254
tests/pow/src/rpc.rs
Normal file
254
tests/pow/src/rpc.rs
Normal file
|
@ -0,0 +1,254 @@
|
|||
use std::{
|
||||
collections::BTreeMap,
|
||||
ops::Range,
|
||||
sync::{atomic::Ordering, Mutex},
|
||||
};
|
||||
|
||||
use function_name::named;
|
||||
use hex::serde::deserialize;
|
||||
use monero_serai::block::Block;
|
||||
use randomx_rs::{RandomXCache, RandomXFlag, RandomXVM};
|
||||
use reqwest::{
|
||||
header::{HeaderMap, HeaderValue},
|
||||
Client, ClientBuilder,
|
||||
};
|
||||
use serde::Deserialize;
|
||||
use serde_json::{json, Value};
|
||||
use thread_local::ThreadLocal;
|
||||
|
||||
use crate::TESTED_BLOCK_COUNT;
|
||||
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
struct JsonRpcResponse {
|
||||
result: GetBlockResponse,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
struct GetBlockResponse {
|
||||
#[serde(deserialize_with = "deserialize")]
|
||||
pub blob: Vec<u8>,
|
||||
pub block_header: BlockHeader,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
struct BlockHeader {
|
||||
#[serde(deserialize_with = "deserialize")]
|
||||
pub pow_hash: Vec<u8>,
|
||||
#[serde(deserialize_with = "deserialize")]
|
||||
pub hash: Vec<u8>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub(crate) struct RpcClient {
|
||||
client: Client,
|
||||
rpc_node_url: String,
|
||||
top_height: usize,
|
||||
}
|
||||
|
||||
impl RpcClient {
|
||||
pub(crate) async fn new(rpc_node_url: String) -> Self {
|
||||
let headers = {
|
||||
let mut h = HeaderMap::new();
|
||||
h.insert("Content-Type", HeaderValue::from_static("application/json"));
|
||||
h
|
||||
};
|
||||
|
||||
let client = ClientBuilder::new()
|
||||
.default_headers(headers)
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
let request = json!({
|
||||
"jsonrpc": "2.0",
|
||||
"id": 0,
|
||||
"method": "get_last_block_header",
|
||||
"params": {}
|
||||
});
|
||||
|
||||
let top_height = client
|
||||
.get(&rpc_node_url)
|
||||
.json(&request)
|
||||
.send()
|
||||
.await
|
||||
.unwrap()
|
||||
.json::<Value>()
|
||||
.await
|
||||
.unwrap()
|
||||
.get("result")
|
||||
.unwrap()
|
||||
.get("block_header")
|
||||
.unwrap()
|
||||
.get("height")
|
||||
.unwrap()
|
||||
.as_u64()
|
||||
.unwrap()
|
||||
.try_into()
|
||||
.unwrap();
|
||||
|
||||
println!("top_height: {top_height}");
|
||||
assert!(top_height > 3301441, "node is behind");
|
||||
|
||||
Self {
|
||||
client,
|
||||
rpc_node_url,
|
||||
top_height,
|
||||
}
|
||||
}
|
||||
|
||||
async fn get_block(&self, height: usize) -> GetBlockResponse {
|
||||
let request = json!({
|
||||
"jsonrpc": "2.0",
|
||||
"id": 0,
|
||||
"method": "get_block",
|
||||
"params": {"height": height, "fill_pow_hash": true}
|
||||
});
|
||||
|
||||
tokio::task::spawn(self.client.get(&self.rpc_node_url).json(&request).send())
|
||||
.await
|
||||
.unwrap()
|
||||
.unwrap()
|
||||
.json::<JsonRpcResponse>()
|
||||
.await
|
||||
.unwrap()
|
||||
.result
|
||||
}
|
||||
|
||||
async fn test<const RANDOMX: bool>(
|
||||
&self,
|
||||
range: Range<usize>,
|
||||
hash: impl Fn(Vec<u8>, u64, u64, [u8; 32]) -> [u8; 32] + Send + Sync + 'static + Copy,
|
||||
name: &'static str,
|
||||
) {
|
||||
let tasks = range.map(|height| {
|
||||
let task = self.get_block(height);
|
||||
(height, task)
|
||||
});
|
||||
|
||||
for (height, task) in tasks {
|
||||
let result = task.await;
|
||||
|
||||
let (seed_height, seed_hash) = if RANDOMX {
|
||||
let seed_height = cuprate_consensus_rules::blocks::randomx_seed_height(height);
|
||||
|
||||
let seed_hash: [u8; 32] = self
|
||||
.get_block(seed_height)
|
||||
.await
|
||||
.block_header
|
||||
.hash
|
||||
.try_into()
|
||||
.unwrap();
|
||||
|
||||
(seed_height, seed_hash)
|
||||
} else {
|
||||
(0, [0; 32])
|
||||
};
|
||||
|
||||
let top_height = self.top_height;
|
||||
|
||||
#[expect(clippy::cast_precision_loss)]
|
||||
rayon::spawn(move || {
|
||||
let GetBlockResponse { blob, block_header } = result;
|
||||
let header = block_header;
|
||||
|
||||
let block = match Block::read(&mut blob.as_slice()) {
|
||||
Ok(b) => b,
|
||||
Err(e) => panic!("{e:?}\nblob: {blob:?}, header: {header:?}"),
|
||||
};
|
||||
|
||||
let pow_hash = hash(
|
||||
block.serialize_pow_hash(),
|
||||
height.try_into().unwrap(),
|
||||
seed_height.try_into().unwrap(),
|
||||
seed_hash,
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
header.pow_hash, pow_hash,
|
||||
"\nheight: {height}\nheader: {header:#?}\nblock: {block:#?}"
|
||||
);
|
||||
|
||||
let count = TESTED_BLOCK_COUNT.fetch_add(1, Ordering::Release);
|
||||
|
||||
let hex_header = hex::encode(header.pow_hash);
|
||||
let hex_hash = hex::encode(pow_hash);
|
||||
let percent = (count as f64 / top_height as f64) * 100.0;
|
||||
|
||||
println!(
|
||||
"progress | {count}/{top_height} ({percent:.2}%)
|
||||
height | {height}
|
||||
algo | {name}
|
||||
header | {hex_header}
|
||||
hash | {hex_hash}\n"
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
#[named]
|
||||
pub(crate) async fn cryptonight_v0(&self) {
|
||||
self.test::<false>(
|
||||
0..1546000,
|
||||
|b, _, _, _| cuprate_cryptonight::cryptonight_hash_v0(&b),
|
||||
function_name!(),
|
||||
)
|
||||
.await;
|
||||
}
|
||||
|
||||
#[named]
|
||||
pub(crate) async fn cryptonight_v1(&self) {
|
||||
self.test::<false>(
|
||||
1546000..1685555,
|
||||
|b, _, _, _| cuprate_cryptonight::cryptonight_hash_v1(&b).unwrap(),
|
||||
function_name!(),
|
||||
)
|
||||
.await;
|
||||
}
|
||||
|
||||
#[named]
|
||||
pub(crate) async fn cryptonight_v2(&self) {
|
||||
self.test::<false>(
|
||||
1685555..1788000,
|
||||
|b, _, _, _| cuprate_cryptonight::cryptonight_hash_v2(&b),
|
||||
function_name!(),
|
||||
)
|
||||
.await;
|
||||
}
|
||||
|
||||
#[named]
|
||||
pub(crate) async fn cryptonight_r(&self) {
|
||||
self.test::<false>(
|
||||
1788000..1978433,
|
||||
|b, h, _, _| cuprate_cryptonight::cryptonight_hash_r(&b, h),
|
||||
function_name!(),
|
||||
)
|
||||
.await;
|
||||
}
|
||||
|
||||
#[named]
|
||||
pub(crate) async fn randomx(&self) {
|
||||
#[expect(clippy::significant_drop_tightening)]
|
||||
let function = move |bytes: Vec<u8>, _, seed_height, seed_hash: [u8; 32]| {
|
||||
static RANDOMX_VM: ThreadLocal<Mutex<BTreeMap<u64, RandomXVM>>> = ThreadLocal::new();
|
||||
|
||||
let mut thread_local = RANDOMX_VM
|
||||
.get_or(|| Mutex::new(BTreeMap::new()))
|
||||
.lock()
|
||||
.unwrap();
|
||||
|
||||
let randomx_vm = thread_local.entry(seed_height).or_insert_with(|| {
|
||||
let flag = RandomXFlag::get_recommended_flags();
|
||||
let cache = RandomXCache::new(flag, &seed_hash).unwrap();
|
||||
RandomXVM::new(flag, Some(cache), None).unwrap()
|
||||
});
|
||||
|
||||
randomx_vm
|
||||
.calculate_hash(&bytes)
|
||||
.unwrap()
|
||||
.try_into()
|
||||
.unwrap()
|
||||
};
|
||||
|
||||
self.test::<true>(1978433..self.top_height, function, function_name!())
|
||||
.await;
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue