diff --git a/coins/monero/src/lib.rs b/coins/monero/src/lib.rs index 85ce2df9..fdda8a9f 100644 --- a/coins/monero/src/lib.rs +++ b/coins/monero/src/lib.rs @@ -22,9 +22,6 @@ pub mod clsag; pub mod rpc; pub mod transaction; -#[cfg(test)] -mod tests; - #[link(name = "wrapper")] extern "C" { pub(crate) fn free(ptr: *const u8); diff --git a/coins/monero/src/rpc.rs b/coins/monero/src/rpc.rs index 1f649711..bbca6960 100644 --- a/coins/monero/src/rpc.rs +++ b/coins/monero/src/rpc.rs @@ -21,9 +21,9 @@ use serde_json::json; use reqwest; #[derive(Deserialize, Debug)] -struct EmptyResponse {} +pub struct EmptyResponse {} #[derive(Deserialize, Debug)] -struct JsonRpcResponse { +pub struct JsonRpcResponse { result: T } @@ -58,7 +58,7 @@ impl Rpc { Rpc(daemon) } - async fn rpc_call< + pub async fn rpc_call< Params: Serialize + Debug, Response: DeserializeOwned + Debug >(&self, method: &str, params: Option) -> Result { @@ -71,7 +71,7 @@ impl Rpc { self.call_tail(method, builder).await } - async fn bin_call< + pub async fn bin_call< Response: DeserializeOwned + Debug >(&self, method: &str, params: Vec) -> Result { let client = reqwest::Client::new(); @@ -236,18 +236,4 @@ impl Rpc { Ok(()) } - - #[cfg(test)] - pub async fn mine_block(&self, address: String) -> Result<(), RpcError> { - let _: EmptyResponse = self.rpc_call("json_rpc", Some(json!({ - "jsonrpc": "2.0", - "id": (), - "method": "generateblocks", - "params": { - "wallet_address": address, - "amount_of_blocks": 10 - }, - }))).await?; - Ok(()) - } } diff --git a/coins/monero/src/transaction/mod.rs b/coins/monero/src/transaction/mod.rs index 99db2e7f..cb103492 100644 --- a/coins/monero/src/transaction/mod.rs +++ b/coins/monero/src/transaction/mod.rs @@ -365,7 +365,7 @@ pub async fn send( payments: &[(Address, u64)], change: Address, fee_per_byte: u64 -) -> Result { +) -> Result { let (_, mask_sum, mut tx) = prepare_outputs( &mut Preparation::Leader(rng), inputs, @@ -386,7 +386,5 @@ pub async fn send( prunable.Clsags = clsags.iter().map(|clsag| clsag.0.clone()).collect(); prunable.pseudo_outs = clsags.iter().map(|clsag| Key { key: clsag.1.compress().to_bytes() }).collect(); tx.rct_signatures.p = Some(prunable); - - rpc.publish_transaction(&tx).await.map_err(|e| TransactionError::InvalidTransaction(e))?; - Ok(tx.hash()) + Ok(tx) } diff --git a/coins/monero/tests/rpc.rs b/coins/monero/tests/rpc.rs new file mode 100644 index 00000000..2ad468b7 --- /dev/null +++ b/coins/monero/tests/rpc.rs @@ -0,0 +1,13 @@ +use serde_json::json; + +use monero_serai::rpc::{EmptyResponse, RpcError, Rpc}; + +pub async fn mine_block(rpc: &Rpc, address: String) -> Result { + rpc.rpc_call("json_rpc", Some(json!({ + "method": "generateblocks", + "params": { + "wallet_address": address, + "amount_of_blocks": 10 + }, + }))).await +} diff --git a/coins/monero/tests/send.rs b/coins/monero/tests/send.rs new file mode 100644 index 00000000..6fd4beb4 --- /dev/null +++ b/coins/monero/tests/send.rs @@ -0,0 +1,56 @@ +use rand::rngs::OsRng; + +use curve25519_dalek::constants::ED25519_BASEPOINT_TABLE; + +use monero::{ + network::Network, + util::{key::PublicKey, address::Address} +}; + +use monero_serai::{ + random_scalar, + transaction, + rpc::Rpc +}; + +mod rpc; +use crate::rpc::mine_block; + +#[tokio::test] +pub async fn send() { + let rpc = Rpc::new("http://127.0.0.1:18081".to_string()); + + // Generate an address + let view = random_scalar(&mut OsRng); + let spend = random_scalar(&mut OsRng); + let spend_pub = &spend * &ED25519_BASEPOINT_TABLE; + + let addr = Address::standard( + Network::Mainnet, + PublicKey { point: spend_pub.compress() }, + PublicKey { point: (&view * &ED25519_BASEPOINT_TABLE).compress() } + ); + + let fee_per_byte = 50000000; + let fee = fee_per_byte * 2000; + + let mut tx; + let mut output; + let mut amount; + for i in 0 .. 2 { + let start = rpc.get_height().await.unwrap(); + for _ in 0 .. 7 { + mine_block(&rpc, addr.to_string()).await.unwrap(); + } + + // Test both a miner output and a normal output + tx = rpc.get_block_transactions(start).await.unwrap().swap_remove(i); + output = transaction::scan(&tx, view, spend_pub).swap_remove(0); + // Test creating a zero change output and a non-zero change output + amount = output.commitment.amount - fee - u64::try_from(i).unwrap(); + let tx = transaction::send( + &mut OsRng, &rpc, &spend, &vec![output], &vec![(addr, amount)], addr, fee_per_byte + ).await.unwrap(); + rpc.publish_transaction(&tx).await.unwrap(); + } +}