diff --git a/Cargo.lock b/Cargo.lock index 92a275f..1c99de9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -633,8 +633,10 @@ dependencies = [ "borsh", "bytes", "bzip2", + "cuprate-types", "futures", "hex", + "hex-literal", "monero-p2p", "monero-serai", "monero-wire", diff --git a/test-utils/Cargo.toml b/test-utils/Cargo.toml index 4143a94..349176f 100644 --- a/test-utils/Cargo.toml +++ b/test-utils/Cargo.toml @@ -6,8 +6,9 @@ license = "MIT" authors = ["Boog900"] [dependencies] -monero-wire = {path = "../net/monero-wire"} -monero-p2p = {path = "../p2p/monero-p2p", features = ["borsh"] } +monero-wire = { path = "../net/monero-wire"} +monero-p2p = { path = "../p2p/monero-p2p", features = ["borsh"] } +cuprate-types = { path = "../types" } monero-serai = { workspace = true } futures = { workspace = true, features = ["std"] } @@ -17,6 +18,7 @@ tokio-util = { workspace = true } reqwest = { workspace = true } bytes = { workspace = true, features = ["std"] } tempfile = { workspace = true } +hex-literal = { workspace = true } borsh = { workspace = true, features = ["derive"]} diff --git a/test-utils/README.MD b/test-utils/README.md similarity index 85% rename from test-utils/README.MD rename to test-utils/README.md index e7fd3bc..441f6f3 100644 --- a/test-utils/README.MD +++ b/test-utils/README.md @@ -6,3 +6,4 @@ Cuprate crate, only in tests. It currently contains: - Code to spawn monerod instances and a testing network zone - Real raw and typed Monero data, e.g. `Block, Transaction` +- An RPC client to generate types from `cuprate_types` \ No newline at end of file diff --git a/test-utils/src/data/block/5da0a3d004c352a90cc86b00fab676695d76a4d1de16036c41ba4dd188c4d76f.bin b/test-utils/src/data/block/5da0a3d004c352a90cc86b00fab676695d76a4d1de16036c41ba4dd188c4d76f.bin new file mode 100644 index 0000000..bfe51fb Binary files /dev/null and b/test-utils/src/data/block/5da0a3d004c352a90cc86b00fab676695d76a4d1de16036c41ba4dd188c4d76f.bin differ diff --git a/test-utils/src/data/block/5ecb7e663bbe947c734c8059e7d7d52dc7d6644bb82d81a6ad4057d127ee8eda.bin b/test-utils/src/data/block/5ecb7e663bbe947c734c8059e7d7d52dc7d6644bb82d81a6ad4057d127ee8eda.bin new file mode 100644 index 0000000..a6f2ba3 Binary files /dev/null and b/test-utils/src/data/block/5ecb7e663bbe947c734c8059e7d7d52dc7d6644bb82d81a6ad4057d127ee8eda.bin differ diff --git a/test-utils/src/data/constants.rs b/test-utils/src/data/constants.rs index 77795a6..4f1eca7 100644 --- a/test-utils/src/data/constants.rs +++ b/test-utils/src/data/constants.rs @@ -3,147 +3,223 @@ //---------------------------------------------------------------------------------------------------- Import //---------------------------------------------------------------------------------------------------- Block -/// Block with height `202612` and hash `bbd604d2ba11ba27935e006ed39c9bfdd99b76bf4a50654bc1e1e61217962698`. +/// Generate a `const _: &[u8]` pointing to a block blob. /// -/// ```rust -/// use monero_serai::{block::Block, transaction::Input}; +/// This will deserialize with `Block` to assume the blob is at least deserializable. /// -/// let block = Block::read(&mut -/// cuprate_test_utils::data::BLOCK_BBD604 -/// ).unwrap(); +/// This requires some static block input for testing. /// -/// assert_eq!(block.header.major_version, 1); -/// assert_eq!(block.header.minor_version, 0); -/// assert_eq!(block.header.timestamp, 1409804570); -/// assert_eq!(block.header.nonce, 1073744198); -/// assert!(matches!(block.miner_tx.prefix.inputs[0], Input::Gen(202612))); -/// assert_eq!(block.txs.len(), 513); +/// The actual block blob data on disk is found in `data/block`. /// -/// assert_eq!( -/// hex::encode(block.hash()), -/// "bbd604d2ba11ba27935e006ed39c9bfdd99b76bf4a50654bc1e1e61217962698", -/// ); -/// ``` -pub const BLOCK_BBD604: &[u8] = - include_bytes!("block/bbd604d2ba11ba27935e006ed39c9bfdd99b76bf4a50654bc1e1e61217962698.bin"); +/// See below for actual usage. +macro_rules! const_block_blob { + ( + name: $name:ident, // Name of the `const` created + height: $height:literal, // Block height + hash: $hash:literal, // Block hash + data_path: $data_path:literal, // Path to the block blob + major_version: $major_version:literal, // Block's major version + minor_version: $minor_version:literal, // Block's minor version + timestamp: $timestamp:literal, // Block's timestamp + nonce: $nonce:literal, // Block's nonce + miner_tx_generated: $miner_tx_generated:literal, // Generated Monero in block's miner transaction + tx_len: $tx_len:literal, // How many transactions there are in the block + ) => { + #[doc = concat!("Block with hash `", $hash, "`.")] + /// + #[doc = concat!("Height: `", $height, "`.")] + /// + /// ```rust + #[doc = "# use cuprate_test_utils::data::*;"] + #[doc = "use monero_serai::{block::Block, transaction::Input};"] + #[doc = ""] + #[doc = concat!("let block = Block::read(&mut ", stringify!($name), ").unwrap();")] + #[doc = ""] + #[doc = concat!("assert_eq!(block.header.major_version, ", $major_version, ");")] + #[doc = concat!("assert_eq!(block.header.minor_version, ", $minor_version, ");")] + #[doc = concat!("assert_eq!(block.header.timestamp, ", $timestamp, ");")] + #[doc = concat!("assert_eq!(block.header.nonce, ", $nonce, ");")] + #[doc = concat!("assert!(matches!(block.miner_tx.prefix.inputs[0], Input::Gen(", $miner_tx_generated, ")));")] + #[doc = concat!("assert_eq!(block.txs.len(), ", $tx_len, ");")] + #[doc = concat!("assert_eq!(hex::encode(block.hash()), \"", $hash, "\")")] + /// ``` + pub const $name: &[u8] = include_bytes!($data_path); + }; +} -/// Block with height `2751506` and hash `f910435a5477ca27be1986c080d5476aeab52d0c07cf3d9c72513213350d25d4`. -/// -/// ```rust -/// use monero_serai::{block::Block, transaction::Input}; -/// -/// let block = Block::read(&mut -/// cuprate_test_utils::data::BLOCK_F91043 -/// ).unwrap(); -/// -/// assert_eq!(block.header.major_version, 9); -/// assert_eq!(block.header.minor_version, 9); -/// assert_eq!(block.header.timestamp, 1545423190); -/// assert_eq!(block.header.nonce, 4123173351); -/// assert!(matches!(block.miner_tx.prefix.inputs[0], Input::Gen(1731606))); -/// assert_eq!(block.txs.len(), 3); -/// -/// assert_eq!( -/// hex::encode(block.hash()), -/// "f910435a5477ca27be1986c080d5476aeab52d0c07cf3d9c72513213350d25d4", -/// ); -/// ``` -pub const BLOCK_F91043: &[u8] = - include_bytes!("block/f910435a5477ca27be1986c080d5476aeab52d0c07cf3d9c72513213350d25d4.bin"); +const_block_blob! { + name: BLOCK_BBD604, + height: 202_612, + hash: "bbd604d2ba11ba27935e006ed39c9bfdd99b76bf4a50654bc1e1e61217962698", + data_path: "block/bbd604d2ba11ba27935e006ed39c9bfdd99b76bf4a50654bc1e1e61217962698.bin", + major_version: 1, + minor_version: 0, + timestamp: 1409804570, + nonce: 1073744198, + miner_tx_generated: 202612, + tx_len: 513, +} -/// Block with height `2751506` and hash `43bd1f2b6556dcafa413d8372974af59e4e8f37dbf74dc6b2a9b7212d0577428`. -/// -/// ```rust -/// use monero_serai::{block::Block, transaction::Input}; -/// -/// let block = Block::read(&mut -/// cuprate_test_utils::data::BLOCK_43BD1F -/// ).unwrap(); -/// -/// assert_eq!(block.header.major_version, 16); -/// assert_eq!(block.header.minor_version, 16); -/// assert_eq!(block.header.timestamp, 1667941829); -/// assert_eq!(block.header.nonce, 4110909056); -/// assert!(matches!(block.miner_tx.prefix.inputs[0], Input::Gen(2751506))); -/// assert_eq!(block.txs.len(), 0); -/// -/// assert_eq!( -/// hex::encode(block.hash()), -/// "43bd1f2b6556dcafa413d8372974af59e4e8f37dbf74dc6b2a9b7212d0577428", -/// ); -/// ``` -pub const BLOCK_43BD1F: &[u8] = - include_bytes!("block/43bd1f2b6556dcafa413d8372974af59e4e8f37dbf74dc6b2a9b7212d0577428.bin"); +const_block_blob! { + name: BLOCK_5ECB7E, + height: 202_609, + hash: "5ecb7e663bbe947c734c8059e7d7d52dc7d6644bb82d81a6ad4057d127ee8eda", + data_path: "block/5ecb7e663bbe947c734c8059e7d7d52dc7d6644bb82d81a6ad4057d127ee8eda.bin", + major_version: 1, + minor_version: 0, + timestamp: 1409804315, + nonce: 48426, + miner_tx_generated: 202609, + tx_len: 2, +} + +const_block_blob! { + name: BLOCK_F91043, + height: 2_751_506, + hash: "f910435a5477ca27be1986c080d5476aeab52d0c07cf3d9c72513213350d25d4", + data_path: "block/f910435a5477ca27be1986c080d5476aeab52d0c07cf3d9c72513213350d25d4.bin", + major_version: 9, + minor_version: 9, + timestamp: 1545423190, + nonce: 4123173351, + miner_tx_generated: 1731606, + tx_len: 3, +} + +const_block_blob! { + name: BLOCK_43BD1F, + height: 2_751_506, + hash: "43bd1f2b6556dcafa413d8372974af59e4e8f37dbf74dc6b2a9b7212d0577428", + data_path: "block/43bd1f2b6556dcafa413d8372974af59e4e8f37dbf74dc6b2a9b7212d0577428.bin", + major_version: 16, + minor_version: 16, + timestamp: 1667941829, + nonce: 4110909056, + miner_tx_generated: 2751506, + tx_len: 0, +} //---------------------------------------------------------------------------------------------------- Transaction -/// Transaction with hash `3bc7ff015b227e7313cc2e8668bfbb3f3acbee274a9c201d6211cf681b5f6bb1`. +/// Generate a `const _: &[u8]` pointing to a transaction blob. /// -/// ```rust -/// use monero_serai::transaction::{Transaction, Timelock}; -/// -/// let tx = Transaction::read(&mut -/// cuprate_test_utils::data::TX_3BC7FF -/// ).unwrap(); -/// -/// assert_eq!(tx.prefix.version, 1); -/// assert_eq!(tx.prefix.timelock, Timelock::Block(100_081)); -/// assert_eq!(tx.prefix.inputs.len(), 1); -/// assert_eq!(tx.prefix.outputs.len(), 5); -/// assert_eq!(tx.signatures.len(), 0); -/// -/// assert_eq!( -/// hex::encode(tx.hash()), -/// "3bc7ff015b227e7313cc2e8668bfbb3f3acbee274a9c201d6211cf681b5f6bb1", -/// ); -/// ``` -pub const TX_3BC7FF: &[u8] = - include_bytes!("tx/3bc7ff015b227e7313cc2e8668bfbb3f3acbee274a9c201d6211cf681b5f6bb1.bin"); +/// Same as [`const_block_blob`] but for transactions. +macro_rules! const_tx_blob { + ( + name: $name:ident, // Name of the `const` created + hash: $hash:literal, // Transaction hash + data_path: $data_path:literal, // Path to the transaction blob + version: $version:literal, // Transaction version + timelock: $timelock:expr, // Transaction's timelock (use the real type `Timelock`) + input_len: $input_len:literal, // Amount of inputs + output_len: $output_len:literal, // Amount of outputs + signatures_len: $signatures_len:literal, // Amount of signatures + ) => { + #[doc = concat!("Transaction with hash `", $hash, "`.")] + /// + /// ```rust + #[doc = "# use cuprate_test_utils::data::*;"] + #[doc = "use monero_serai::transaction::{Transaction, Timelock};"] + #[doc = ""] + #[doc = concat!("let tx = Transaction::read(&mut ", stringify!($name), ").unwrap();")] + #[doc = ""] + #[doc = concat!("assert_eq!(tx.prefix.version, ", $version, ");")] + #[doc = concat!("assert_eq!(tx.prefix.timelock, ", stringify!($timelock), ");")] + #[doc = concat!("assert_eq!(tx.prefix.inputs.len(), ", $input_len, ");")] + #[doc = concat!("assert_eq!(tx.prefix.outputs.len(), ", $output_len, ");")] + #[doc = concat!("assert_eq!(tx.signatures.len(), ", $signatures_len, ");")] + #[doc = concat!("assert_eq!(hex::encode(tx.hash()), \"", $hash, "\")")] + /// ``` + pub const $name: &[u8] = include_bytes!($data_path); + }; +} -/// Transaction with hash `9e3f73e66d7c7293af59c59c1ff5d6aae047289f49e5884c66caaf4aea49fb34`. -/// -/// ```rust -/// use monero_serai::transaction::{Transaction, Timelock}; -/// -/// let tx = Transaction::read(&mut -/// cuprate_test_utils::data::TX_9E3F73 -/// ).unwrap(); -/// -/// assert_eq!(tx.prefix.version, 1); -/// assert_eq!(tx.prefix.timelock, Timelock::None); -/// assert_eq!(tx.prefix.inputs.len(), 2); -/// assert_eq!(tx.prefix.outputs.len(), 5); -/// assert_eq!(tx.signatures.len(), 2); -/// -/// assert_eq!( -/// hex::encode(tx.hash()), -/// "9e3f73e66d7c7293af59c59c1ff5d6aae047289f49e5884c66caaf4aea49fb34", -/// ); -/// ``` -pub const TX_9E3F73: &[u8] = - include_bytes!("tx/9e3f73e66d7c7293af59c59c1ff5d6aae047289f49e5884c66caaf4aea49fb34.bin"); +const_tx_blob! { + name: TX_3BC7FF, + hash: "3bc7ff015b227e7313cc2e8668bfbb3f3acbee274a9c201d6211cf681b5f6bb1", + data_path: "tx/3bc7ff015b227e7313cc2e8668bfbb3f3acbee274a9c201d6211cf681b5f6bb1.bin", + version: 1, + timelock: Timelock::Block(100_081), + input_len: 1, + output_len: 5, + signatures_len: 0, +} -/// Transaction with hash `84d48dc11ec91950f8b70a85af9db91fe0c8abef71ef5db08304f7344b99ea66`. -/// -/// ```rust -/// use monero_serai::transaction::{Transaction, Timelock}; -/// -/// let tx = Transaction::read(&mut -/// cuprate_test_utils::data::TX_84D48D -/// ).unwrap(); -/// -/// assert_eq!(tx.prefix.version, 2); -/// assert_eq!(tx.prefix.timelock, Timelock::None); -/// assert_eq!(tx.prefix.inputs.len(), 2); -/// assert_eq!(tx.prefix.outputs.len(), 2); -/// assert_eq!(tx.signatures.len(), 0); -/// -/// assert_eq!( -/// hex::encode(tx.hash()), -/// "84d48dc11ec91950f8b70a85af9db91fe0c8abef71ef5db08304f7344b99ea66", -/// ); -/// ``` -pub const TX_84D48D: &[u8] = - include_bytes!("tx/84d48dc11ec91950f8b70a85af9db91fe0c8abef71ef5db08304f7344b99ea66.bin"); +const_tx_blob! { + name: TX_2180A8, + hash: "2180a87f724702d37af087e22476297e818a73579ef7b7da947da963245202a3", + data_path: "tx/2180a87f724702d37af087e22476297e818a73579ef7b7da947da963245202a3.bin", + version: 1, + timelock: Timelock::None, + input_len: 19, + output_len: 61, + signatures_len: 19, +} + +const_tx_blob! { + name: TX_D7FEBD, + hash: "d7febd16293799d9c6a8e0fe9199b8a0a3e0da5a8a165098937b60f0bbd582df", + data_path: "tx/d7febd16293799d9c6a8e0fe9199b8a0a3e0da5a8a165098937b60f0bbd582df.bin", + version: 1, + timelock: Timelock::None, + input_len: 46, + output_len: 46, + signatures_len: 46, +} + +const_tx_blob! { + name: TX_E2D393, + hash: "e2d39395dd1625b2d707b98af789e7eab9d24c2bd2978ec38ef910961a8cdcee", + data_path: "tx/e2d39395dd1625b2d707b98af789e7eab9d24c2bd2978ec38ef910961a8cdcee.bin", + version: 2, + timelock: Timelock::None, + input_len: 1, + output_len: 2, + signatures_len: 0, +} + +const_tx_blob! { + name: TX_E57440, + hash: "e57440ec66d2f3b2a5fa2081af40128868973e7c021bb3877290db3066317474", + data_path: "tx/e57440ec66d2f3b2a5fa2081af40128868973e7c021bb3877290db3066317474.bin", + version: 2, + timelock: Timelock::None, + input_len: 1, + output_len: 2, + signatures_len: 0, +} + +const_tx_blob! { + name: TX_B6B439, + hash: "b6b4394d4ec5f08ad63267c07962550064caa8d225dd9ad6d739ebf60291c169", + data_path: "tx/b6b4394d4ec5f08ad63267c07962550064caa8d225dd9ad6d739ebf60291c169.bin", + version: 2, + timelock: Timelock::None, + input_len: 2, + output_len: 2, + signatures_len: 0, +} + +const_tx_blob! { + name: TX_9E3F73, + hash: "9e3f73e66d7c7293af59c59c1ff5d6aae047289f49e5884c66caaf4aea49fb34", + data_path: "tx/9e3f73e66d7c7293af59c59c1ff5d6aae047289f49e5884c66caaf4aea49fb34.bin", + version: 1, + timelock: Timelock::None, + input_len: 2, + output_len: 5, + signatures_len: 2, +} + +const_tx_blob! { + name: TX_84D48D, + hash: "84d48dc11ec91950f8b70a85af9db91fe0c8abef71ef5db08304f7344b99ea66", + data_path: "tx/84d48dc11ec91950f8b70a85af9db91fe0c8abef71ef5db08304f7344b99ea66.bin", + version: 2, + timelock: Timelock::None, + input_len: 2, + output_len: 2, + signatures_len: 0, +} //---------------------------------------------------------------------------------------------------- Tests #[cfg(test)] diff --git a/test-utils/src/data/free.rs b/test-utils/src/data/free.rs index e65f267..9a7665d 100644 --- a/test-utils/src/data/free.rs +++ b/test-utils/src/data/free.rs @@ -6,105 +6,260 @@ )] //---------------------------------------------------------------------------------------------------- Import -use std::sync::OnceLock; +use std::sync::{Arc, OnceLock}; +use hex_literal::hex; use monero_serai::{block::Block, transaction::Transaction}; +use cuprate_types::{TransactionVerificationData, VerifiedBlockInformation}; + use crate::data::constants::{ - BLOCK_43BD1F, BLOCK_BBD604, BLOCK_F91043, TX_3BC7FF, TX_84D48D, TX_9E3F73, + BLOCK_43BD1F, BLOCK_5ECB7E, BLOCK_F91043, TX_2180A8, TX_3BC7FF, TX_84D48D, TX_9E3F73, + TX_B6B439, TX_D7FEBD, TX_E2D393, TX_E57440, }; +//---------------------------------------------------------------------------------------------------- Conversion +/// Converts `monero_serai`'s `Block` into a +/// `cuprate_types::VerifiedBlockInformation` (superset). +/// +/// To prevent pulling other code in order to actually calculate things +/// (e.g. `pow_hash`), some information must be provided statically, +/// this struct represents that data that must be provided. +/// +/// Consider using `cuprate_test_utils::rpc` to get this data easily. +struct VerifiedBlockMap<'a> { + block: Block, + pow_hash: [u8; 32], + height: u64, + generated_coins: u64, + weight: usize, + long_term_weight: usize, + cumulative_difficulty: u128, + // Vec of `tx_blob`'s, i.e. the data in `/test-utils/src/data/tx/`. + // This should the actual `tx_blob`'s of the transactions within this block. + txs: Vec<&'a [u8]>, +} + +impl VerifiedBlockMap<'_> { + /// Turn the various static data bits in `self` into a `VerifiedBlockInformation`. + /// + /// Transactions are verified that they at least match the block's, + /// although the correctness of data (whether this block actually existed or not) + /// is not checked. + fn into_verified(self) -> VerifiedBlockInformation { + let Self { + block, + pow_hash, + height, + generated_coins, + weight, + long_term_weight, + cumulative_difficulty, + txs, + } = self; + + let txs: Vec> = txs + .into_iter() + .map(to_tx_verification_data) + .map(Arc::new) + .collect(); + + 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, + txs, + pow_hash, + height, + generated_coins, + weight, + long_term_weight, + cumulative_difficulty, + } + } +} + +// Same as [`VerifiedBlockMap`] but for [`TransactionVerificationData`]. +fn to_tx_verification_data(tx_blob: &[u8]) -> TransactionVerificationData { + let tx_blob = tx_blob.to_vec(); + let tx = Transaction::read(&mut tx_blob.as_slice()).unwrap(); + TransactionVerificationData { + tx_weight: tx.weight(), + fee: tx.rct_signatures.base.fee, + tx_hash: tx.hash(), + tx_blob, + tx, + } +} + //---------------------------------------------------------------------------------------------------- Blocks -/// Return [`BLOCK_BBD604`] as a [`Block`]. +/// Generate a block accessor function with this signature: +/// `fn() -> &'static VerifiedBlockInformation` /// -/// ```rust -/// assert_eq!( -/// &cuprate_test_utils::data::block_v1_tx513().serialize(), -/// cuprate_test_utils::data::BLOCK_BBD604 -/// ); -/// ``` -pub fn block_v1_tx513() -> Block { - /// `OnceLock` holding the data. - static BLOCK: OnceLock = OnceLock::new(); - BLOCK - .get_or_init(|| Block::read(&mut BLOCK_BBD604).unwrap()) - .clone() +/// This will use `VerifiedBlockMap` type above to do various +/// checks on the input data and makes sure it seems correct. +/// +/// This requires some static block/tx input (from data) and some fields. +/// This data can be accessed more easily via: +/// - A block explorer (https://xmrchain.net) +/// - Monero RPC (see cuprate_test_utils::rpc for this) +/// +/// See below for actual usage. +macro_rules! verified_block_information_fn { + ( + fn_name: $fn_name:ident, // Name of the function created + block_blob: $block_blob:ident, // Block blob ([u8], found in `constants.rs`) + tx_blobs: [$($tx_blob:ident),*], // Array of contained transaction blobs + pow_hash: $pow_hash:literal, // PoW hash as a string literal + height: $height:literal, // Block height + generated_coins: $generated_coins:literal, // Generated coins in block (`reward`) + weight: $weight:literal, // Block weight + long_term_weight: $long_term_weight:literal, // Block long term weight + cumulative_difficulty: $cumulative_difficulty:literal, // Block cumulative difficulty + tx_len: $tx_len:literal, // Amount of transactions in this block + ) => { + #[doc = concat!( + "Return [`", + stringify!($block_blob), + "`] as a [`VerifiedBlockInformation`].", + )] + /// + /// Contained transactions: + $( + #[doc = concat!("- [`", stringify!($tx_blob), "`]")] + )* + /// + /// ```rust + #[doc = "# use cuprate_test_utils::data::*;"] + #[doc = "# use hex_literal::hex;"] + #[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.cumulative_difficulty, ", $cumulative_difficulty, ");")] + #[doc = concat!("assert_eq!(block.txs.len(), ", $tx_len, ");")] + /// ``` + pub fn $fn_name() -> &'static VerifiedBlockInformation { + static BLOCK: OnceLock = OnceLock::new(); + BLOCK.get_or_init(|| { + VerifiedBlockMap { + block: Block::read(&mut $block_blob).unwrap(), + pow_hash: hex!($pow_hash), + height: $height, + generated_coins: $generated_coins, + weight: $weight, + long_term_weight: $long_term_weight, + cumulative_difficulty: $cumulative_difficulty, + txs: vec![$($tx_blob),*], + } + .into_verified() + }) + } + }; } -/// Return [`BLOCK_F91043`] as a [`Block`]. -/// -/// ```rust -/// assert_eq!( -/// &cuprate_test_utils::data::block_v9_tx3().serialize(), -/// cuprate_test_utils::data::BLOCK_F91043 -/// ); -/// ``` -pub fn block_v9_tx3() -> Block { - /// `OnceLock` holding the data. - static BLOCK: OnceLock = OnceLock::new(); - BLOCK - .get_or_init(|| Block::read(&mut BLOCK_F91043).unwrap()) - .clone() +verified_block_information_fn! { + fn_name: block_v1_tx2, + block_blob: BLOCK_5ECB7E, + tx_blobs: [TX_2180A8, TX_D7FEBD], + pow_hash: "84f64766475d51837ac9efbef1926486e58563c95a19fef4aec3254f03000000", + height: 202_612, + generated_coins: 13_138_270_468_431, + weight: 55_503, + long_term_weight: 55_503, + cumulative_difficulty: 126_654_460_829_362, + tx_len: 2, } -/// Return [`BLOCK_43BD1F`] as a [`Block`]. -/// -/// ```rust -/// assert_eq!( -/// &cuprate_test_utils::data::block_v16_tx0().serialize(), -/// cuprate_test_utils::data::BLOCK_43BD1F -/// ); -/// ``` -pub fn block_v16_tx0() -> Block { - /// `OnceLock` holding the data. - static BLOCK: OnceLock = OnceLock::new(); - BLOCK - .get_or_init(|| Block::read(&mut BLOCK_43BD1F).unwrap()) - .clone() +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_921_682_163, + weight: 6_597, + long_term_weight: 6_597, + cumulative_difficulty: 23_558_910_234_058_343, + 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: 236_046_001_376_524_168, + tx_len: 0, } //---------------------------------------------------------------------------------------------------- Transactions -/// Return [`TX_3BC7FF`] as a [`Transaction`]. +/// Generate a transaction accessor function with this signature: +/// `fn() -> &'static TransactionVerificationData` /// -/// ```rust -/// assert_eq!( -/// &cuprate_test_utils::data::tx_v1_sig0().serialize(), -/// cuprate_test_utils::data::TX_3BC7FF -/// ); -/// ``` -pub fn tx_v1_sig0() -> Transaction { - /// `OnceLock` holding the data. - static TX: OnceLock = OnceLock::new(); - TX.get_or_init(|| Transaction::read(&mut TX_3BC7FF).unwrap()) - .clone() +/// Same as [`verified_block_information_fn`] but for transactions. +macro_rules! transaction_verification_data_fn { + ( + fn_name: $fn_name:ident, // Name of the function created + tx_blobs: $tx_blob:ident, // Transaction blob ([u8], found in `constants.rs`) + weight: $weight:literal, // Transaction weight + hash: $hash:literal, // Transaction hash as a string literal + ) => { + #[doc = concat!("Return [`", stringify!($tx_blob), "`] as a [`TransactionVerificationData`].")] + /// + /// ```rust + #[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 TransactionVerificationData { + static TX: OnceLock = OnceLock::new(); + TX.get_or_init(|| to_tx_verification_data($tx_blob)) + } + }; } -/// Return [`TX_9E3F73`] as a [`Transaction`]. -/// -/// ```rust -/// assert_eq!( -/// &cuprate_test_utils::data::tx_v1_sig2().serialize(), -/// cuprate_test_utils::data::TX_9E3F73 -/// ); -/// ``` -pub fn tx_v1_sig2() -> Transaction { - /// `OnceLock` holding the data. - static TX: OnceLock = OnceLock::new(); - TX.get_or_init(|| Transaction::read(&mut TX_9E3F73).unwrap()) - .clone() +transaction_verification_data_fn! { + fn_name: tx_v1_sig0, + tx_blobs: TX_3BC7FF, + weight: 248, + hash: "3bc7ff015b227e7313cc2e8668bfbb3f3acbee274a9c201d6211cf681b5f6bb1", } -/// Return [`TX_84D48D`] as a [`Transaction`]. -/// -/// ```rust -/// assert_eq!( -/// &cuprate_test_utils::data::tx_v2_rct3().serialize(), -/// cuprate_test_utils::data::TX_84D48D -/// ); -/// ``` -pub fn tx_v2_rct3() -> Transaction { - /// `OnceLock` holding the data. - static TX: OnceLock = OnceLock::new(); - TX.get_or_init(|| Transaction::read(&mut TX_84D48D).unwrap()) - .clone() +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", } diff --git a/test-utils/src/data/mod.rs b/test-utils/src/data/mod.rs index a721cfb..03c4524 100644 --- a/test-utils/src/data/mod.rs +++ b/test-utils/src/data/mod.rs @@ -1,9 +1,35 @@ -//! Testing data and utilities. +//! Real Monero data. //! -//! Raw data is found in `data/`. +//! This module provides access to _real_ Monero data, +//! either in raw bytes or typed. +//! +//! ## Constants +//! The `const`ants provide byte slices representing block +//! and transaction blobs that can be directly deserialized: +//! +//! ```rust +//! # use cuprate_test_utils::data::*; +//! use monero_serai::{block::Block, transaction::Transaction}; +//! +//! let block: Block = Block::read(&mut BLOCK_43BD1F).unwrap(); +//! let tx: Transaction = Transaction::read(&mut TX_E57440).unwrap(); +//! ``` +//! +//! ## Functions +//! The free functions provide access to typed data found in `cuprate_types`: +//! ```rust +//! # use cuprate_test_utils::data::*; +//! use cuprate_types::{VerifiedBlockInformation, TransactionVerificationData}; +//! +//! let block: VerifiedBlockInformation = block_v16_tx0().clone(); +//! let tx: TransactionVerificationData = tx_v1_sig0().clone(); +//! ``` mod constants; -pub use constants::{BLOCK_43BD1F, BLOCK_BBD604, BLOCK_F91043, TX_3BC7FF, TX_84D48D, TX_9E3F73}; +pub use constants::{ + BLOCK_43BD1F, BLOCK_5ECB7E, BLOCK_BBD604, BLOCK_F91043, TX_2180A8, TX_3BC7FF, TX_84D48D, + TX_9E3F73, TX_B6B439, TX_D7FEBD, TX_E2D393, TX_E57440, +}; mod free; -pub use free::{block_v16_tx0, block_v1_tx513, block_v9_tx3, tx_v1_sig0, tx_v1_sig2, tx_v2_rct3}; +pub use free::{block_v16_tx0, block_v1_tx2, block_v9_tx3, tx_v1_sig0, tx_v1_sig2, tx_v2_rct3}; diff --git a/test-utils/src/data/tx/2180a87f724702d37af087e22476297e818a73579ef7b7da947da963245202a3.bin b/test-utils/src/data/tx/2180a87f724702d37af087e22476297e818a73579ef7b7da947da963245202a3.bin new file mode 100644 index 0000000..e7c506d Binary files /dev/null and b/test-utils/src/data/tx/2180a87f724702d37af087e22476297e818a73579ef7b7da947da963245202a3.bin differ diff --git a/test-utils/src/data/tx/b6b4394d4ec5f08ad63267c07962550064caa8d225dd9ad6d739ebf60291c169.bin b/test-utils/src/data/tx/b6b4394d4ec5f08ad63267c07962550064caa8d225dd9ad6d739ebf60291c169.bin new file mode 100644 index 0000000..b476736 Binary files /dev/null and b/test-utils/src/data/tx/b6b4394d4ec5f08ad63267c07962550064caa8d225dd9ad6d739ebf60291c169.bin differ diff --git a/test-utils/src/data/tx/d7febd16293799d9c6a8e0fe9199b8a0a3e0da5a8a165098937b60f0bbd582df.bin b/test-utils/src/data/tx/d7febd16293799d9c6a8e0fe9199b8a0a3e0da5a8a165098937b60f0bbd582df.bin new file mode 100644 index 0000000..b659a6f Binary files /dev/null and b/test-utils/src/data/tx/d7febd16293799d9c6a8e0fe9199b8a0a3e0da5a8a165098937b60f0bbd582df.bin differ diff --git a/test-utils/src/data/tx/e2d39395dd1625b2d707b98af789e7eab9d24c2bd2978ec38ef910961a8cdcee.bin b/test-utils/src/data/tx/e2d39395dd1625b2d707b98af789e7eab9d24c2bd2978ec38ef910961a8cdcee.bin new file mode 100644 index 0000000..f8667cf Binary files /dev/null and b/test-utils/src/data/tx/e2d39395dd1625b2d707b98af789e7eab9d24c2bd2978ec38ef910961a8cdcee.bin differ diff --git a/test-utils/src/data/tx/e57440ec66d2f3b2a5fa2081af40128868973e7c021bb3877290db3066317474.bin b/test-utils/src/data/tx/e57440ec66d2f3b2a5fa2081af40128868973e7c021bb3877290db3066317474.bin new file mode 100644 index 0000000..5781638 Binary files /dev/null and b/test-utils/src/data/tx/e57440ec66d2f3b2a5fa2081af40128868973e7c021bb3877290db3066317474.bin differ diff --git a/test-utils/src/lib.rs b/test-utils/src/lib.rs index f88b072..75494ec 100644 --- a/test-utils/src/lib.rs +++ b/test-utils/src/lib.rs @@ -1,6 +1,4 @@ -//! Cuprate testing utilities. -//! -//! See the `README.md` for more info. +#![doc = include_str!("../README.md")] pub mod data; pub mod monerod;