#![allow(
const_item_mutation, clippy::missing_panics_doc, )]
use std::sync::OnceLock;
use hex_literal::hex;
use monero_serai::{block::Block, transaction::Transaction};
use cuprate_helper::map::combine_low_high_bits_to_u128;
use cuprate_types::{VerifiedBlockInformation, VerifiedTransactionInformation};
use crate::data::constants::{
BLOCK_43BD1F, BLOCK_5ECB7E, BLOCK_F91043, TX_2180A8, TX_3BC7FF, TX_84D48D, TX_9E3F73,
TX_B6B439, TX_D7FEBD, TX_E2D393, TX_E57440,
};
struct VerifiedBlockMap {
block_blob: &'static [u8],
pow_hash: [u8; 32],
height: u64,
generated_coins: u64,
weight: usize,
long_term_weight: usize,
cumulative_difficulty_low: u64,
cumulative_difficulty_high: u64,
txs: &'static [&'static [u8]],
}
impl VerifiedBlockMap {
fn into_verified(self) -> VerifiedBlockInformation {
let Self {
block_blob,
pow_hash,
height,
generated_coins,
weight,
long_term_weight,
cumulative_difficulty_low,
cumulative_difficulty_high,
txs,
} = self;
let block_blob = block_blob.to_vec();
let block = Block::read(&mut block_blob.as_slice()).unwrap();
let txs = txs.iter().map(to_tx_verification_data).collect::<Vec<_>>();
assert_eq!(
txs.len(),
block.txs.len(),
"(deserialized txs).len() != (txs hashes in block).len()"
);
for (tx, tx_hash_in_block) in txs.iter().zip(&block.txs) {
assert_eq!(
&tx.tx_hash, tx_hash_in_block,
"deserialized tx hash is not the same as the one in the parent block"
);
}
VerifiedBlockInformation {
block_hash: block.hash(),
block_blob,
block,
txs,
pow_hash,
height,
generated_coins,
weight,
long_term_weight,
cumulative_difficulty: combine_low_high_bits_to_u128(
cumulative_difficulty_low,
cumulative_difficulty_high,
),
}
}
}
fn to_tx_verification_data(tx_blob: impl AsRef<[u8]>) -> VerifiedTransactionInformation {
let tx_blob = tx_blob.as_ref().to_vec();
let tx = Transaction::read(&mut tx_blob.as_slice()).unwrap();
VerifiedTransactionInformation {
tx_weight: tx.weight(),
fee: tx.rct_signatures.base.fee,
tx_hash: tx.hash(),
tx_blob,
tx,
}
}
macro_rules! verified_block_information_fn {
(
fn_name: $fn_name:ident, block_blob: $block_blob:ident, tx_blobs: [$($tx_blob:ident),*], pow_hash: $pow_hash:literal, height: $height:literal, generated_coins: $generated_coins:literal, weight: $weight:literal, long_term_weight: $long_term_weight:literal, cumulative_difficulty_low: $cumulative_difficulty_low:literal, cumulative_difficulty_high: $cumulative_difficulty_high:literal, tx_len: $tx_len:literal, ) => {
#[doc = concat!(
"Return [`",
stringify!($block_blob),
"`] as a [`VerifiedBlockInformation`].",
)]
$(
#[doc = concat!("- [`", stringify!($tx_blob), "`]")]
)*
#[doc = "# use cuprate_test_utils::data::*;"]
#[doc = "# use hex_literal::hex;"]
#[doc = "use cuprate_helper::map::combine_low_high_bits_to_u128;"]
#[doc = ""]
#[doc = concat!("let block = ", stringify!($fn_name), "();")]
#[doc = concat!("assert_eq!(&block.block.serialize(), ", stringify!($block_blob), ");")]
#[doc = concat!("assert_eq!(block.pow_hash, hex!(\"", $pow_hash, "\"));")]
#[doc = concat!("assert_eq!(block.height, ", $height, ");")]
#[doc = concat!("assert_eq!(block.generated_coins, ", $generated_coins, ");")]
#[doc = concat!("assert_eq!(block.weight, ", $weight, ");")]
#[doc = concat!("assert_eq!(block.long_term_weight, ", $long_term_weight, ");")]
#[doc = concat!("assert_eq!(block.txs.len(), ", $tx_len, ");")]
#[doc = ""]
#[doc = concat!(
"assert_eq!(block.cumulative_difficulty, ",
"combine_low_high_bits_to_u128(",
stringify!($cumulative_difficulty_low),
", ",
stringify!($cumulative_difficulty_high),
"));"
)]
pub fn $fn_name() -> &'static VerifiedBlockInformation {
static BLOCK: OnceLock<VerifiedBlockInformation> = OnceLock::new();
BLOCK.get_or_init(|| {
VerifiedBlockMap {
block_blob: $block_blob,
pow_hash: hex!($pow_hash),
height: $height,
generated_coins: $generated_coins,
weight: $weight,
long_term_weight: $long_term_weight,
cumulative_difficulty_low: $cumulative_difficulty_low,
cumulative_difficulty_high: $cumulative_difficulty_high,
txs: &[$($tx_blob),*],
}
.into_verified()
})
}
};
}
verified_block_information_fn! {
fn_name: block_v1_tx2,
block_blob: BLOCK_5ECB7E,
tx_blobs: [TX_2180A8, TX_D7FEBD],
pow_hash: "c960d540000459480560b7816de968c7470083e5874e10040bdd4cc501000000",
height: 202_609,
generated_coins: 14_535_350_982_449,
weight: 21_905,
long_term_weight: 21_905,
cumulative_difficulty_low: 126_650_740_038_710,
cumulative_difficulty_high: 0,
tx_len: 2,
}
verified_block_information_fn! {
fn_name: block_v9_tx3,
block_blob: BLOCK_F91043,
tx_blobs: [TX_E2D393, TX_E57440, TX_B6B439],
pow_hash: "7c78b5b67a112a66ea69ea51477492057dba9cfeaa2942ee7372c61800000000",
height: 1_731_606,
generated_coins: 3_403_774_022_163,
weight: 6_597,
long_term_weight: 6_597,
cumulative_difficulty_low: 23_558_910_234_058_343,
cumulative_difficulty_high: 0,
tx_len: 3,
}
verified_block_information_fn! {
fn_name: block_v16_tx0,
block_blob: BLOCK_43BD1F,
tx_blobs: [],
pow_hash: "10b473b5d097d6bfa0656616951840724dfe38c6fb9c4adf8158800300000000",
height: 2_751_506,
generated_coins: 600_000_000_000,
weight: 106,
long_term_weight: 176_470,
cumulative_difficulty_low: 236_046_001_376_524_168,
cumulative_difficulty_high: 0,
tx_len: 0,
}
macro_rules! transaction_verification_data_fn {
(
fn_name: $fn_name:ident, tx_blobs: $tx_blob:ident, weight: $weight:literal, hash: $hash:literal, ) => {
#[doc = concat!("Return [`", stringify!($tx_blob), "`] as a [`VerifiedTransactionInformation`].")]
#[doc = "# use cuprate_test_utils::data::*;"]
#[doc = "# use hex_literal::hex;"]
#[doc = concat!("let tx = ", stringify!($fn_name), "();")]
#[doc = concat!("assert_eq!(&tx.tx.serialize(), ", stringify!($tx_blob), ");")]
#[doc = concat!("assert_eq!(tx.tx_blob, ", stringify!($tx_blob), ");")]
#[doc = concat!("assert_eq!(tx.tx_weight, ", $weight, ");")]
#[doc = concat!("assert_eq!(tx.tx_hash, hex!(\"", $hash, "\"));")]
#[doc = "assert_eq!(tx.fee, tx.tx.rct_signatures.base.fee);"]
pub fn $fn_name() -> &'static VerifiedTransactionInformation {
static TX: OnceLock<VerifiedTransactionInformation> = OnceLock::new();
TX.get_or_init(|| to_tx_verification_data($tx_blob))
}
};
}
transaction_verification_data_fn! {
fn_name: tx_v1_sig0,
tx_blobs: TX_3BC7FF,
weight: 248,
hash: "3bc7ff015b227e7313cc2e8668bfbb3f3acbee274a9c201d6211cf681b5f6bb1",
}
transaction_verification_data_fn! {
fn_name: tx_v1_sig2,
tx_blobs: TX_9E3F73,
weight: 448,
hash: "9e3f73e66d7c7293af59c59c1ff5d6aae047289f49e5884c66caaf4aea49fb34",
}
transaction_verification_data_fn! {
fn_name: tx_v2_rct3,
tx_blobs: TX_84D48D,
weight: 2743,
hash: "84d48dc11ec91950f8b70a85af9db91fe0c8abef71ef5db08304f7344b99ea66",
}
#[cfg(test)]
mod tests {
use super::*;
use pretty_assertions::assert_eq;
use crate::rpc::HttpRpcClient;
#[ignore] #[tokio::test]
async fn block_same_as_rpc() {
let rpc = HttpRpcClient::new(None).await;
for block in [block_v1_tx2(), block_v9_tx3(), block_v16_tx0()] {
println!("block_height: {}", block.height);
let block_rpc = rpc.get_verified_block_information(block.height).await;
assert_eq!(block, &block_rpc);
}
}
#[ignore] #[tokio::test]
async fn tx_same_as_rpc() {
let rpc = HttpRpcClient::new(None).await;
let mut txs = [block_v1_tx2(), block_v9_tx3(), block_v16_tx0()]
.into_iter()
.flat_map(|block| block.txs.iter().cloned())
.collect::<Vec<VerifiedTransactionInformation>>();
txs.extend([
tx_v1_sig0().clone(),
tx_v1_sig2().clone(),
tx_v2_rct3().clone(),
]);
for tx in txs {
println!("tx_hash: {:?}", tx.tx_hash);
let tx_rpc = rpc
.get_transaction_verification_data(&[tx.tx_hash])
.await
.collect::<Vec<VerifiedTransactionInformation>>()
.pop()
.unwrap();
assert_eq!(tx, tx_rpc);
}
}
}