Skip to main content

cuprate_test_utils/data/
statics.rs

1//! `static LazyLock`s to access data.
2
3#![allow(
4    const_item_mutation, // `R: Read` needs `&mut self`
5    clippy::missing_panics_doc, // These functions shouldn't panic
6)]
7
8//---------------------------------------------------------------------------------------------------- Import
9use std::sync::LazyLock;
10
11use hex_literal::hex;
12use monero_oxide::{block::Block, transaction::Transaction};
13
14use cuprate_helper::{map::combine_low_high_bits_to_u128, tx::tx_fee};
15use cuprate_types::{VerifiedBlockInformation, VerifiedTransactionInformation};
16
17use crate::data::constants::{
18    BLOCK_43BD1F, BLOCK_5ECB7E, BLOCK_F91043, TX_2180A8, TX_3BC7FF, TX_84D48D, TX_9E3F73,
19    TX_B6B439, TX_D7FEBD, TX_E2D393, TX_E57440,
20};
21
22//---------------------------------------------------------------------------------------------------- Conversion
23/// Converts [`monero_oxide::Block`] into a
24/// [`VerifiedBlockInformation`] (superset).
25///
26/// To prevent pulling other code in order to actually calculate things
27/// (e.g. `pow_hash`), some information must be provided statically,
28/// this struct represents that data that must be provided.
29///
30/// Consider using [`cuprate_test_utils::rpc`] to get this data easily.
31struct VerifiedBlockMap {
32    block_blob: &'static [u8],
33    pow_hash: [u8; 32],
34    height: usize,
35    generated_coins: u64,
36    weight: usize,
37    long_term_weight: usize,
38    cumulative_difficulty_low: u64,
39    cumulative_difficulty_high: u64,
40    // Vec of `tx_blob`'s, i.e. the data in `/test-utils/src/data/tx/`.
41    // This should the actual `tx_blob`'s of the transactions within this block.
42    txs: &'static [&'static [u8]],
43}
44
45impl VerifiedBlockMap {
46    /// Turn the various static data bits in `self` into a [`VerifiedBlockInformation`].
47    ///
48    /// Transactions are verified that they at least match the block's,
49    /// although the correctness of data (whether this block actually existed or not)
50    /// is not checked.
51    fn into_verified(self) -> VerifiedBlockInformation {
52        let Self {
53            block_blob,
54            pow_hash,
55            height,
56            generated_coins,
57            weight,
58            long_term_weight,
59            cumulative_difficulty_low,
60            cumulative_difficulty_high,
61            txs,
62        } = self;
63
64        let block_blob = block_blob.to_vec();
65        let block = Block::read(&mut block_blob.as_slice()).unwrap();
66
67        let txs = txs.iter().map(to_tx_verification_data).collect::<Vec<_>>();
68
69        assert_eq!(
70            txs.len(),
71            block.transactions.len(),
72            "(deserialized txs).len() != (txs hashes in block).len()"
73        );
74
75        for (tx, tx_hash_in_block) in txs.iter().zip(&block.transactions) {
76            assert_eq!(
77                &tx.tx_hash, tx_hash_in_block,
78                "deserialized tx hash is not the same as the one in the parent block"
79            );
80        }
81
82        VerifiedBlockInformation {
83            block_hash: block.hash(),
84            block_blob,
85            block,
86            txs,
87            pow_hash,
88            height,
89            generated_coins,
90            weight,
91            long_term_weight,
92            cumulative_difficulty: combine_low_high_bits_to_u128(
93                cumulative_difficulty_low,
94                cumulative_difficulty_high,
95            ),
96        }
97    }
98}
99
100// Same as [`VerifiedBlockMap`] but for [`VerifiedTransactionInformation`].
101fn to_tx_verification_data(tx_blob: impl AsRef<[u8]>) -> VerifiedTransactionInformation {
102    let tx = Transaction::read(&mut tx_blob.as_ref()).unwrap();
103    let tx_weight = tx.weight();
104    let fee = tx_fee(&tx);
105    let tx_hash = tx.hash();
106
107    let (tx, tx_prunable_blob) = tx.pruned_with_prunable();
108    VerifiedTransactionInformation {
109        tx_weight,
110        fee,
111        tx_hash,
112        tx_prunable_blob,
113        tx_pruned: tx.serialize(),
114        tx,
115    }
116}
117
118//---------------------------------------------------------------------------------------------------- Blocks
119/// Generate a `static LazyLock<VerifiedBlockInformation>`.
120///
121/// This will use `VerifiedBlockMap` type above to do various
122/// checks on the input data and makes sure it seems correct.
123///
124/// This requires some static block/tx input (from data) and some fields.
125/// This data can be accessed more easily via:
126/// - A block explorer (<https://xmrchain.net>)
127/// - Monero RPC (see `cuprate_test_utils::rpc` for this)
128///
129/// See below for actual usage.
130macro_rules! verified_block_information {
131    (
132        name: $name:ident, // Name of the `LazyLock` created
133        block_blob: $block_blob:ident, // Block blob ([u8], found in `constants.rs`)
134        tx_blobs: [$($tx_blob:ident),*], // Array of contained transaction blobs
135        pow_hash: $pow_hash:literal, // PoW hash as a string literal
136        height: $height:literal, // Block height
137        generated_coins: $generated_coins:literal, // Generated coins in block (minus fees)
138        weight: $weight:literal, // Block weight
139        long_term_weight: $long_term_weight:literal, // Block long term weight
140        cumulative_difficulty_low: $cumulative_difficulty_low:literal, // Least significant 64-bits of block cumulative difficulty
141        cumulative_difficulty_high: $cumulative_difficulty_high:literal, // Most significant 64-bits of block cumulative difficulty
142        tx_len: $tx_len:literal, // Amount of transactions in this block
143    ) => {
144        #[doc = concat!(
145            "Return [`",
146            stringify!($block_blob),
147            "`] as a [`VerifiedBlockInformation`].",
148        )]
149        ///
150        /// Contained transactions:
151        $(
152            #[doc = concat!("- [`", stringify!($tx_blob), "`]")]
153        )*
154        ///
155        /// ```rust
156        #[doc = "# use cuprate_test_utils::data::*;"]
157        #[doc = "# use hex_literal::hex;"]
158        #[doc = "use cuprate_helper::map::combine_low_high_bits_to_u128;"]
159        #[doc = ""]
160        #[doc = concat!("let block = &*", stringify!($name), ";")]
161        #[doc = concat!("assert_eq!(&block.block.serialize(), ", stringify!($block_blob), ");")]
162        #[doc = concat!("assert_eq!(block.pow_hash, hex!(\"", $pow_hash, "\"));")]
163        #[doc = concat!("assert_eq!(block.height, ", $height, ");")]
164        #[doc = concat!("assert_eq!(block.generated_coins, ", $generated_coins, ");")]
165        #[doc = concat!("assert_eq!(block.weight, ", $weight, ");")]
166        #[doc = concat!("assert_eq!(block.long_term_weight, ", $long_term_weight, ");")]
167        #[doc = concat!("assert_eq!(block.txs.len(), ", $tx_len, ");")]
168        #[doc = ""]
169        #[doc = concat!(
170            "assert_eq!(block.cumulative_difficulty, ",
171            "combine_low_high_bits_to_u128(",
172            stringify!($cumulative_difficulty_low),
173            ", ",
174            stringify!($cumulative_difficulty_high),
175            "));"
176        )]
177        /// ```
178        pub static $name: LazyLock<VerifiedBlockInformation> = LazyLock::new(|| {
179            VerifiedBlockMap {
180                block_blob: $block_blob,
181                pow_hash: hex!($pow_hash),
182                height: $height,
183                generated_coins: $generated_coins,
184                weight: $weight,
185                long_term_weight: $long_term_weight,
186                cumulative_difficulty_low: $cumulative_difficulty_low,
187                cumulative_difficulty_high: $cumulative_difficulty_high,
188                txs: &[$($tx_blob),*],
189            }
190            .into_verified()
191        });
192    };
193}
194
195verified_block_information! {
196    name: BLOCK_V1_TX2,
197    block_blob: BLOCK_5ECB7E,
198    tx_blobs: [TX_2180A8, TX_D7FEBD],
199    pow_hash: "c960d540000459480560b7816de968c7470083e5874e10040bdd4cc501000000",
200    height: 202_609,
201    generated_coins: 14_535_350_982_449,
202    weight: 21_905,
203    long_term_weight: 21_905,
204    cumulative_difficulty_low: 126_650_740_038_710,
205    cumulative_difficulty_high: 0,
206    tx_len: 2,
207}
208
209verified_block_information! {
210    name: BLOCK_V9_TX3,
211    block_blob: BLOCK_F91043,
212    tx_blobs: [TX_E2D393, TX_E57440, TX_B6B439],
213    pow_hash: "7c78b5b67a112a66ea69ea51477492057dba9cfeaa2942ee7372c61800000000",
214    height: 1_731_606,
215    generated_coins: 3_403_774_022_163,
216    weight: 6_597,
217    long_term_weight: 6_597,
218    cumulative_difficulty_low: 23_558_910_234_058_343,
219    cumulative_difficulty_high: 0,
220    tx_len: 3,
221}
222
223verified_block_information! {
224    name: BLOCK_V16_TX0,
225    block_blob: BLOCK_43BD1F,
226    tx_blobs: [],
227    pow_hash: "10b473b5d097d6bfa0656616951840724dfe38c6fb9c4adf8158800300000000",
228    height: 2_751_506,
229    generated_coins: 600_000_000_000,
230    weight: 106,
231    long_term_weight: 176_470,
232    cumulative_difficulty_low: 236_046_001_376_524_168,
233    cumulative_difficulty_high: 0,
234    tx_len: 0,
235}
236
237//---------------------------------------------------------------------------------------------------- Transactions
238/// Generate a `const LazyLock<VerifiedTransactionInformation>`.
239///
240/// Same as [`verified_block_information`] but for transactions.
241macro_rules! transaction_verification_data {
242    (
243        name: $name:ident, // Name of the `LazyLock` created
244        tx_blobs: $tx_blob:ident, // Transaction blob ([u8], found in `constants.rs`)
245        weight: $weight:literal, // Transaction weight
246        hash: $hash:literal, // Transaction hash as a string literal
247    ) => {
248        #[doc = concat!("Return [`", stringify!($tx_blob), "`] as a [`VerifiedTransactionInformation`].")]
249        ///
250        /// ```rust
251        #[doc = "# use cuprate_test_utils::data::*;"]
252        #[doc = "# use hex_literal::hex;"]
253        #[doc = concat!("let tx = &*", stringify!($name), ";")]
254        #[doc = concat!("assert_eq!([tx.tx_pruned.as_slice(), tx.tx_prunable_blob.as_slice()].concat(), ", stringify!($tx_blob), ");")]
255        #[doc = concat!("assert_eq!(tx.tx_weight, ", $weight, ");")]
256        #[doc = concat!("assert_eq!(tx.tx_hash, hex!(\"", $hash, "\"));")]
257        /// ```
258        pub static $name: LazyLock<VerifiedTransactionInformation> = LazyLock::new(|| {
259            to_tx_verification_data($tx_blob)
260        });
261    };
262}
263
264transaction_verification_data! {
265    name: TX_V1_SIG0,
266    tx_blobs: TX_3BC7FF,
267    weight: 248,
268    hash: "3bc7ff015b227e7313cc2e8668bfbb3f3acbee274a9c201d6211cf681b5f6bb1",
269}
270
271transaction_verification_data! {
272    name: TX_V1_SIG2,
273    tx_blobs: TX_9E3F73,
274    weight: 448,
275    hash: "9e3f73e66d7c7293af59c59c1ff5d6aae047289f49e5884c66caaf4aea49fb34",
276}
277
278transaction_verification_data! {
279    name: TX_V2_RCT3,
280    tx_blobs: TX_84D48D,
281    weight: 2743,
282    hash: "84d48dc11ec91950f8b70a85af9db91fe0c8abef71ef5db08304f7344b99ea66",
283}
284
285//---------------------------------------------------------------------------------------------------- TESTS
286#[cfg(test)]
287mod tests {
288    use pretty_assertions::assert_eq;
289
290    use crate::rpc::client::HttpRpcClient;
291
292    use super::*;
293
294    /// Assert the defined blocks are the same compared to ones received from a local RPC call.
295    #[ignore] // FIXME: doesn't work in CI, we need a real unrestricted node
296    #[tokio::test]
297    async fn block_same_as_rpc() {
298        let rpc = HttpRpcClient::new(None).await;
299        for block in [&*BLOCK_V1_TX2, &*BLOCK_V9_TX3, &*BLOCK_V16_TX0] {
300            println!("block_height: {}", block.height);
301            let block_rpc = rpc.get_verified_block_information(block.height).await;
302            assert_eq!(block, &block_rpc);
303        }
304    }
305
306    /// Same as `block_same_as_rpc` but for transactions.
307    /// This also tests all the transactions within the defined blocks.
308    #[ignore] // FIXME: doesn't work in CI, we need a real unrestricted node
309    #[tokio::test]
310    async fn tx_same_as_rpc() {
311        let rpc = HttpRpcClient::new(None).await;
312
313        let mut txs = [&*BLOCK_V1_TX2, &*BLOCK_V9_TX3, &*BLOCK_V16_TX0]
314            .into_iter()
315            .flat_map(|block| block.txs.iter().cloned())
316            .collect::<Vec<VerifiedTransactionInformation>>();
317
318        txs.extend([TX_V1_SIG0.clone(), TX_V1_SIG2.clone(), TX_V2_RCT3.clone()]);
319
320        for tx in txs {
321            println!("tx_hash: {:?}", tx.tx_hash);
322            let tx_rpc = rpc
323                .get_transaction_verification_data(&[tx.tx_hash])
324                .await
325                .collect::<Vec<VerifiedTransactionInformation>>()
326                .pop()
327                .unwrap();
328            assert_eq!(tx, tx_rpc);
329        }
330    }
331}