mirror of
https://github.com/Cuprate/cuprate.git
synced 2024-12-22 19:49:28 +00:00
test-utils: add rpc
module (#110)
* test-utils: impl `rpc` module * client: use `spawn_blocking` * client: add tests * ignore test for now * add example and `get_transaction_verification_data()` * client: calculate proper `generated_coins` * data: fix `generated_coins/reward` references * data: fix height
This commit is contained in:
parent
9ad7ea3fa0
commit
ee22e81c7e
9 changed files with 354 additions and 16 deletions
2
Cargo.lock
generated
2
Cargo.lock
generated
|
@ -641,6 +641,8 @@ dependencies = [
|
||||||
"monero-serai",
|
"monero-serai",
|
||||||
"monero-wire",
|
"monero-wire",
|
||||||
"reqwest",
|
"reqwest",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
"tar",
|
"tar",
|
||||||
"tempfile",
|
"tempfile",
|
||||||
"tokio",
|
"tokio",
|
||||||
|
|
|
@ -3,22 +3,25 @@ name = "cuprate-test-utils"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
authors = ["Boog900"]
|
authors = ["Boog900", "hinto-janai"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
cuprate-types = { path = "../types" }
|
||||||
monero-wire = { path = "../net/monero-wire" }
|
monero-wire = { path = "../net/monero-wire" }
|
||||||
monero-p2p = { path = "../p2p/monero-p2p", features = ["borsh"] }
|
monero-p2p = { path = "../p2p/monero-p2p", features = ["borsh"] }
|
||||||
cuprate-types = { path = "../types" }
|
|
||||||
|
|
||||||
monero-serai = { workspace = true }
|
hex = { workspace = true }
|
||||||
|
hex-literal = { workspace = true }
|
||||||
|
monero-serai = { workspace = true, features = ["std", "http-rpc"] }
|
||||||
futures = { workspace = true, features = ["std"] }
|
futures = { workspace = true, features = ["std"] }
|
||||||
async-trait = { workspace = true }
|
async-trait = { workspace = true }
|
||||||
tokio = { workspace = true, features = ["full"] }
|
tokio = { workspace = true, features = ["full"] }
|
||||||
tokio-util = { workspace = true }
|
tokio-util = { workspace = true }
|
||||||
reqwest = { workspace = true }
|
reqwest = { workspace = true }
|
||||||
|
serde = { workspace = true }
|
||||||
|
serde_json = { workspace = true }
|
||||||
bytes = { workspace = true, features = ["std"] }
|
bytes = { workspace = true, features = ["std"] }
|
||||||
tempfile = { workspace = true }
|
tempfile = { workspace = true }
|
||||||
hex-literal = { workspace = true }
|
|
||||||
|
|
||||||
borsh = { workspace = true, features = ["derive"]}
|
borsh = { workspace = true, features = ["derive"]}
|
||||||
|
|
||||||
|
|
|
@ -22,7 +22,6 @@ macro_rules! const_block_blob {
|
||||||
minor_version: $minor_version:literal, // Block's minor version
|
minor_version: $minor_version:literal, // Block's minor version
|
||||||
timestamp: $timestamp:literal, // Block's timestamp
|
timestamp: $timestamp:literal, // Block's timestamp
|
||||||
nonce: $nonce:literal, // Block's nonce
|
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
|
tx_len: $tx_len:literal, // How many transactions there are in the block
|
||||||
) => {
|
) => {
|
||||||
#[doc = concat!("Block with hash `", $hash, "`.")]
|
#[doc = concat!("Block with hash `", $hash, "`.")]
|
||||||
|
@ -39,7 +38,7 @@ macro_rules! const_block_blob {
|
||||||
#[doc = concat!("assert_eq!(block.header.minor_version, ", $minor_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.timestamp, ", $timestamp, ");")]
|
||||||
#[doc = concat!("assert_eq!(block.header.nonce, ", $nonce, ");")]
|
#[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!(matches!(block.miner_tx.prefix.inputs[0], Input::Gen(", $height, ")));")]
|
||||||
#[doc = concat!("assert_eq!(block.txs.len(), ", $tx_len, ");")]
|
#[doc = concat!("assert_eq!(block.txs.len(), ", $tx_len, ");")]
|
||||||
#[doc = concat!("assert_eq!(hex::encode(block.hash()), \"", $hash, "\")")]
|
#[doc = concat!("assert_eq!(hex::encode(block.hash()), \"", $hash, "\")")]
|
||||||
/// ```
|
/// ```
|
||||||
|
@ -56,7 +55,6 @@ const_block_blob! {
|
||||||
minor_version: 0,
|
minor_version: 0,
|
||||||
timestamp: 1409804570,
|
timestamp: 1409804570,
|
||||||
nonce: 1073744198,
|
nonce: 1073744198,
|
||||||
miner_tx_generated: 202612,
|
|
||||||
tx_len: 513,
|
tx_len: 513,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -69,20 +67,18 @@ const_block_blob! {
|
||||||
minor_version: 0,
|
minor_version: 0,
|
||||||
timestamp: 1409804315,
|
timestamp: 1409804315,
|
||||||
nonce: 48426,
|
nonce: 48426,
|
||||||
miner_tx_generated: 202609,
|
|
||||||
tx_len: 2,
|
tx_len: 2,
|
||||||
}
|
}
|
||||||
|
|
||||||
const_block_blob! {
|
const_block_blob! {
|
||||||
name: BLOCK_F91043,
|
name: BLOCK_F91043,
|
||||||
height: 2_751_506,
|
height: 1_731_606,
|
||||||
hash: "f910435a5477ca27be1986c080d5476aeab52d0c07cf3d9c72513213350d25d4",
|
hash: "f910435a5477ca27be1986c080d5476aeab52d0c07cf3d9c72513213350d25d4",
|
||||||
data_path: "block/f910435a5477ca27be1986c080d5476aeab52d0c07cf3d9c72513213350d25d4.bin",
|
data_path: "block/f910435a5477ca27be1986c080d5476aeab52d0c07cf3d9c72513213350d25d4.bin",
|
||||||
major_version: 9,
|
major_version: 9,
|
||||||
minor_version: 9,
|
minor_version: 9,
|
||||||
timestamp: 1545423190,
|
timestamp: 1545423190,
|
||||||
nonce: 4123173351,
|
nonce: 4123173351,
|
||||||
miner_tx_generated: 1731606,
|
|
||||||
tx_len: 3,
|
tx_len: 3,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -95,7 +91,6 @@ const_block_blob! {
|
||||||
minor_version: 16,
|
minor_version: 16,
|
||||||
timestamp: 1667941829,
|
timestamp: 1667941829,
|
||||||
nonce: 4110909056,
|
nonce: 4110909056,
|
||||||
miner_tx_generated: 2751506,
|
|
||||||
tx_len: 0,
|
tx_len: 0,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -124,7 +124,7 @@ macro_rules! verified_block_information_fn {
|
||||||
tx_blobs: [$($tx_blob:ident),*], // Array of contained transaction blobs
|
tx_blobs: [$($tx_blob:ident),*], // Array of contained transaction blobs
|
||||||
pow_hash: $pow_hash:literal, // PoW hash as a string literal
|
pow_hash: $pow_hash:literal, // PoW hash as a string literal
|
||||||
height: $height:literal, // Block height
|
height: $height:literal, // Block height
|
||||||
generated_coins: $generated_coins:literal, // Generated coins in block (`reward`)
|
generated_coins: $generated_coins:literal, // Generated coins in block (minus fees)
|
||||||
weight: $weight:literal, // Block weight
|
weight: $weight:literal, // Block weight
|
||||||
long_term_weight: $long_term_weight:literal, // Block long term weight
|
long_term_weight: $long_term_weight:literal, // Block long term weight
|
||||||
cumulative_difficulty: $cumulative_difficulty:literal, // Block cumulative difficulty
|
cumulative_difficulty: $cumulative_difficulty:literal, // Block cumulative difficulty
|
||||||
|
@ -179,7 +179,7 @@ verified_block_information_fn! {
|
||||||
tx_blobs: [TX_2180A8, TX_D7FEBD],
|
tx_blobs: [TX_2180A8, TX_D7FEBD],
|
||||||
pow_hash: "84f64766475d51837ac9efbef1926486e58563c95a19fef4aec3254f03000000",
|
pow_hash: "84f64766475d51837ac9efbef1926486e58563c95a19fef4aec3254f03000000",
|
||||||
height: 202_612,
|
height: 202_612,
|
||||||
generated_coins: 13_138_270_468_431,
|
generated_coins: 13_138_270_467_918,
|
||||||
weight: 55_503,
|
weight: 55_503,
|
||||||
long_term_weight: 55_503,
|
long_term_weight: 55_503,
|
||||||
cumulative_difficulty: 126_654_460_829_362,
|
cumulative_difficulty: 126_654_460_829_362,
|
||||||
|
@ -192,7 +192,7 @@ verified_block_information_fn! {
|
||||||
tx_blobs: [TX_E2D393, TX_E57440, TX_B6B439],
|
tx_blobs: [TX_E2D393, TX_E57440, TX_B6B439],
|
||||||
pow_hash: "7c78b5b67a112a66ea69ea51477492057dba9cfeaa2942ee7372c61800000000",
|
pow_hash: "7c78b5b67a112a66ea69ea51477492057dba9cfeaa2942ee7372c61800000000",
|
||||||
height: 1_731_606,
|
height: 1_731_606,
|
||||||
generated_coins: 3_403_921_682_163,
|
generated_coins: 3_403_774_022_163,
|
||||||
weight: 6_597,
|
weight: 6_597,
|
||||||
long_term_weight: 6_597,
|
long_term_weight: 6_597,
|
||||||
cumulative_difficulty: 23_558_910_234_058_343,
|
cumulative_difficulty: 23_558_910_234_058_343,
|
||||||
|
|
|
@ -2,4 +2,5 @@
|
||||||
|
|
||||||
pub mod data;
|
pub mod data;
|
||||||
pub mod monerod;
|
pub mod monerod;
|
||||||
|
pub mod rpc;
|
||||||
pub mod test_netzone;
|
pub mod test_netzone;
|
||||||
|
|
305
test-utils/src/rpc/client.rs
Normal file
305
test-utils/src/rpc/client.rs
Normal file
|
@ -0,0 +1,305 @@
|
||||||
|
//! HTTP RPC client.
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------------------------------- Use
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use serde::Deserialize;
|
||||||
|
use serde_json::json;
|
||||||
|
use tokio::task::spawn_blocking;
|
||||||
|
|
||||||
|
use monero_serai::{
|
||||||
|
block::Block,
|
||||||
|
rpc::{HttpRpc, Rpc},
|
||||||
|
};
|
||||||
|
|
||||||
|
use cuprate_types::{TransactionVerificationData, VerifiedBlockInformation};
|
||||||
|
|
||||||
|
use crate::rpc::constants::LOCALHOST_RPC_URL;
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------------------------------- HttpRpcClient
|
||||||
|
/// An HTTP RPC client for Monero.
|
||||||
|
pub struct HttpRpcClient {
|
||||||
|
address: String,
|
||||||
|
rpc: Rpc<HttpRpc>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl HttpRpcClient {
|
||||||
|
/// Create an [`HttpRpcClient`].
|
||||||
|
///
|
||||||
|
/// `address` should be an HTTP URL pointing to a `monerod`.
|
||||||
|
///
|
||||||
|
/// If `None` is provided the default is used: [`LOCALHOST_RPC_URL`].
|
||||||
|
///
|
||||||
|
/// Note that for [`Self::get_verified_block_information`] to work, the `monerod`
|
||||||
|
/// must be in unrestricted mode such that some fields (e.g. `pow_hash`) appear
|
||||||
|
/// in the JSON response.
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
/// This panics if the `address` is invalid or a connection could not be made.
|
||||||
|
pub async fn new(address: Option<String>) -> Self {
|
||||||
|
let address = address.unwrap_or_else(|| LOCALHOST_RPC_URL.to_string());
|
||||||
|
|
||||||
|
Self {
|
||||||
|
rpc: HttpRpc::new(address.clone()).await.unwrap(),
|
||||||
|
address,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The address used for this [`HttpRpcClient`].
|
||||||
|
#[allow(dead_code)]
|
||||||
|
const fn address(&self) -> &String {
|
||||||
|
&self.address
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Access to the inner RPC client for other usage.
|
||||||
|
#[allow(dead_code)]
|
||||||
|
const fn rpc(&self) -> &Rpc<HttpRpc> {
|
||||||
|
&self.rpc
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Request data and map the response to a [`VerifiedBlockInformation`].
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
/// This function will panic at any error point, e.g.,
|
||||||
|
/// if the node cannot be connected to, if deserialization fails, etc.
|
||||||
|
pub async fn get_verified_block_information(&self, height: u64) -> VerifiedBlockInformation {
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
struct Result {
|
||||||
|
blob: String,
|
||||||
|
block_header: BlockHeader,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
struct BlockHeader {
|
||||||
|
block_weight: usize,
|
||||||
|
long_term_weight: usize,
|
||||||
|
cumulative_difficulty: u128,
|
||||||
|
hash: String,
|
||||||
|
height: u64,
|
||||||
|
pow_hash: String,
|
||||||
|
reward: u64, // generated_coins + total_tx_fees
|
||||||
|
}
|
||||||
|
|
||||||
|
let result = self
|
||||||
|
.rpc
|
||||||
|
.json_rpc_call::<Result>(
|
||||||
|
"get_block",
|
||||||
|
Some(json!(
|
||||||
|
{
|
||||||
|
"height": height,
|
||||||
|
"fill_pow_hash": true
|
||||||
|
}
|
||||||
|
)),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
// Make sure this is a trusted, `pow_hash` only works there.
|
||||||
|
assert!(
|
||||||
|
!result.block_header.pow_hash.is_empty(),
|
||||||
|
"untrusted node detected, `pow_hash` will not show on these nodes - use a trusted node!"
|
||||||
|
);
|
||||||
|
|
||||||
|
let reward = result.block_header.reward;
|
||||||
|
|
||||||
|
let (block_hash, block) = spawn_blocking(|| {
|
||||||
|
let block = Block::read(&mut hex::decode(result.blob).unwrap().as_slice()).unwrap();
|
||||||
|
(block.hash(), block)
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let txs: Vec<Arc<TransactionVerificationData>> = self
|
||||||
|
.get_transaction_verification_data(&block.txs)
|
||||||
|
.await
|
||||||
|
.map(Arc::new)
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let block_header = result.block_header;
|
||||||
|
let block_hash_2 = <[u8; 32]>::try_from(hex::decode(&block_header.hash).unwrap()).unwrap();
|
||||||
|
let pow_hash = <[u8; 32]>::try_from(hex::decode(&block_header.pow_hash).unwrap()).unwrap();
|
||||||
|
|
||||||
|
// Assert the block hash matches.
|
||||||
|
assert_eq!(block_hash, block_hash_2);
|
||||||
|
|
||||||
|
let total_tx_fees = txs.iter().map(|tx| tx.fee).sum::<u64>();
|
||||||
|
let generated_coins = block
|
||||||
|
.miner_tx
|
||||||
|
.prefix
|
||||||
|
.outputs
|
||||||
|
.iter()
|
||||||
|
.map(|output| output.amount.expect("miner_tx amount was None"))
|
||||||
|
.sum::<u64>()
|
||||||
|
- total_tx_fees;
|
||||||
|
assert_eq!(
|
||||||
|
reward,
|
||||||
|
generated_coins + total_tx_fees,
|
||||||
|
"generated_coins ({generated_coins}) + total_tx_fees ({total_tx_fees}) != reward ({reward})"
|
||||||
|
);
|
||||||
|
|
||||||
|
VerifiedBlockInformation {
|
||||||
|
block,
|
||||||
|
txs,
|
||||||
|
block_hash,
|
||||||
|
pow_hash,
|
||||||
|
generated_coins,
|
||||||
|
height: block_header.height,
|
||||||
|
weight: block_header.block_weight,
|
||||||
|
long_term_weight: block_header.long_term_weight,
|
||||||
|
cumulative_difficulty: block_header.cumulative_difficulty,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Request data and map the response to a [`TransactionVerificationData`].
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
/// This function will panic at any error point, e.g.,
|
||||||
|
/// if the node cannot be connected to, if deserialization fails, etc.
|
||||||
|
pub async fn get_transaction_verification_data<'a>(
|
||||||
|
&self,
|
||||||
|
tx_hashes: &'a [[u8; 32]],
|
||||||
|
) -> impl Iterator<Item = TransactionVerificationData> + 'a {
|
||||||
|
self.rpc
|
||||||
|
.get_transactions(tx_hashes)
|
||||||
|
.await
|
||||||
|
.unwrap()
|
||||||
|
.into_iter()
|
||||||
|
.enumerate()
|
||||||
|
.map(|(i, tx)| {
|
||||||
|
let tx_hash = tx.hash();
|
||||||
|
assert_eq!(tx_hash, tx_hashes[i]);
|
||||||
|
TransactionVerificationData {
|
||||||
|
tx_blob: tx.serialize(),
|
||||||
|
tx_weight: tx.weight(),
|
||||||
|
tx_hash,
|
||||||
|
fee: tx.rct_signatures.base.fee,
|
||||||
|
tx,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------------------------------- TESTS
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use hex_literal::hex;
|
||||||
|
|
||||||
|
/// Assert the default address is localhost.
|
||||||
|
#[tokio::test]
|
||||||
|
async fn localhost() {
|
||||||
|
assert_eq!(HttpRpcClient::new(None).await.address(), LOCALHOST_RPC_URL);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Assert blocks are correctly received/calculated.
|
||||||
|
#[ignore] // FIXME: doesn't work in CI, we need a real unrestricted node
|
||||||
|
#[tokio::test]
|
||||||
|
async fn get() {
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
|
async fn assert_eq(
|
||||||
|
rpc: &HttpRpcClient,
|
||||||
|
height: u64,
|
||||||
|
block_hash: [u8; 32],
|
||||||
|
pow_hash: [u8; 32],
|
||||||
|
generated_coins: u64,
|
||||||
|
weight: usize,
|
||||||
|
long_term_weight: usize,
|
||||||
|
cumulative_difficulty: u128,
|
||||||
|
tx_count: usize,
|
||||||
|
) {
|
||||||
|
let block = rpc.get_verified_block_information(height).await;
|
||||||
|
|
||||||
|
println!("block height: {height}");
|
||||||
|
assert_eq!(block.txs.len(), tx_count);
|
||||||
|
println!("{block:#?}");
|
||||||
|
|
||||||
|
assert_eq!(block.block_hash, block_hash);
|
||||||
|
assert_eq!(block.pow_hash, pow_hash);
|
||||||
|
assert_eq!(block.height, height);
|
||||||
|
assert_eq!(block.generated_coins, generated_coins);
|
||||||
|
assert_eq!(block.weight, weight);
|
||||||
|
assert_eq!(block.long_term_weight, long_term_weight);
|
||||||
|
assert_eq!(block.cumulative_difficulty, cumulative_difficulty);
|
||||||
|
}
|
||||||
|
|
||||||
|
let rpc = HttpRpcClient::new(None).await;
|
||||||
|
|
||||||
|
assert_eq(
|
||||||
|
&rpc,
|
||||||
|
0, // height
|
||||||
|
hex!("418015bb9ae982a1975da7d79277c2705727a56894ba0fb246adaabb1f4632e3"), // block_hash
|
||||||
|
hex!("8a7b1a780e99eec31a9425b7d89c283421b2042a337d5700dfd4a7d6eb7bd774"), // pow_hash
|
||||||
|
17592186044415, // generated_coins
|
||||||
|
80, // weight
|
||||||
|
80, // long_term_weight
|
||||||
|
1, // cumulative_difficulty
|
||||||
|
0, // tx_count (miner_tx excluded)
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
assert_eq(
|
||||||
|
&rpc,
|
||||||
|
1,
|
||||||
|
hex!("771fbcd656ec1464d3a02ead5e18644030007a0fc664c0a964d30922821a8148"),
|
||||||
|
hex!("5aeebb3de73859d92f3f82fdb97286d81264ecb72a42e4b9f1e6d62eb682d7c0"),
|
||||||
|
17592169267200,
|
||||||
|
383,
|
||||||
|
383,
|
||||||
|
2,
|
||||||
|
0,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
assert_eq(
|
||||||
|
&rpc,
|
||||||
|
202612,
|
||||||
|
hex!("bbd604d2ba11ba27935e006ed39c9bfdd99b76bf4a50654bc1e1e61217962698"),
|
||||||
|
hex!("84f64766475d51837ac9efbef1926486e58563c95a19fef4aec3254f03000000"),
|
||||||
|
13138270467918,
|
||||||
|
55503,
|
||||||
|
55503,
|
||||||
|
126654460829362,
|
||||||
|
513,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
assert_eq(
|
||||||
|
&rpc,
|
||||||
|
1731606,
|
||||||
|
hex!("f910435a5477ca27be1986c080d5476aeab52d0c07cf3d9c72513213350d25d4"),
|
||||||
|
hex!("7c78b5b67a112a66ea69ea51477492057dba9cfeaa2942ee7372c61800000000"),
|
||||||
|
3403774022163,
|
||||||
|
6597,
|
||||||
|
6597,
|
||||||
|
23558910234058343,
|
||||||
|
3,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
assert_eq(
|
||||||
|
&rpc,
|
||||||
|
2751506,
|
||||||
|
hex!("43bd1f2b6556dcafa413d8372974af59e4e8f37dbf74dc6b2a9b7212d0577428"),
|
||||||
|
hex!("10b473b5d097d6bfa0656616951840724dfe38c6fb9c4adf8158800300000000"),
|
||||||
|
600000000000,
|
||||||
|
106,
|
||||||
|
176470,
|
||||||
|
236046001376524168,
|
||||||
|
0,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
assert_eq(
|
||||||
|
&rpc,
|
||||||
|
3132285,
|
||||||
|
hex!("a999c6ba4d2993541ba9d81561bb8293baa83b122f8aa9ab65b3c463224397d8"),
|
||||||
|
hex!("4eaa3b3d4dc888644bc14dc4895ca0b008586e30b186fbaa009d330100000000"),
|
||||||
|
600000000000,
|
||||||
|
133498,
|
||||||
|
176470,
|
||||||
|
348189741564698577,
|
||||||
|
57,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
}
|
7
test-utils/src/rpc/constants.rs
Normal file
7
test-utils/src/rpc/constants.rs
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
//! RPC-related Constants.
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------------------------------- Use
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------------------------------- Constants
|
||||||
|
/// The default URL used for Monero RPC connections.
|
||||||
|
pub const LOCALHOST_RPC_URL: &str = "http://127.0.0.1:18081";
|
25
test-utils/src/rpc/mod.rs
Normal file
25
test-utils/src/rpc/mod.rs
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
//! Monero RPC client.
|
||||||
|
//!
|
||||||
|
//! This module is a client for Monero RPC that maps the types
|
||||||
|
//! into the native types used by Cuprate found in `cuprate_types`.
|
||||||
|
//!
|
||||||
|
//! # Usage
|
||||||
|
//! ```rust,ignore
|
||||||
|
//! #[tokio::main]
|
||||||
|
//! async fn main() {
|
||||||
|
//! // Create RPC client.
|
||||||
|
//! let rpc = HttpRpcClient::new(None).await;
|
||||||
|
//!
|
||||||
|
//! // Collect 20 blocks.
|
||||||
|
//! let mut vec: Vec<VerifiedBlockInformation> = vec![];
|
||||||
|
//! for height in (3130269 - 20)..3130269 {
|
||||||
|
//! vec.push(rpc.get_verified_block_information(height).await);
|
||||||
|
//! }
|
||||||
|
//! }
|
||||||
|
//! ```
|
||||||
|
|
||||||
|
mod client;
|
||||||
|
pub use client::HttpRpcClient;
|
||||||
|
|
||||||
|
mod constants;
|
||||||
|
pub use constants::LOCALHOST_RPC_URL;
|
Loading…
Reference in a new issue