mirror of
https://github.com/hinto-janai/cuprate.git
synced 2025-01-08 20:09:41 +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",
|
"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]]
|
[[package]]
|
||||||
name = "thiserror"
|
name = "thiserror"
|
||||||
version = "1.0.66"
|
version = "1.0.66"
|
||||||
|
|
|
@ -54,6 +54,7 @@ members = [
|
||||||
"types",
|
"types",
|
||||||
|
|
||||||
# Tests
|
# Tests
|
||||||
|
"tests/pow",
|
||||||
"tests/monero-serai",
|
"tests/monero-serai",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
|
@ -56,7 +56,7 @@ async fn main() {
|
||||||
count = c;
|
count = c;
|
||||||
|
|
||||||
println!(
|
println!(
|
||||||
"blocks processed ... {c} ({:.2}%)",
|
"blocks processed ... {c}/{top_height} ({:.2}%)",
|
||||||
(c as f64 / top_height as f64) * 100.0
|
(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