From 1d6df0099ce62a5f80c00791668e8b7a46e43ffd Mon Sep 17 00:00:00 2001 From: Luke Parker Date: Sat, 7 Jan 2023 04:00:12 -0500 Subject: [PATCH] Exposed a hash-based API for accessing blocks Also corrects a few panics, which shouldn't have been present, and unnecessary Cursor uses. --- coins/monero/src/rpc.rs | 59 ++++++++++++++++++++++---------- coins/monero/src/wallet/extra.rs | 4 +-- coins/monero/src/wallet/scan.rs | 4 +-- coins/monero/tests/runner.rs | 4 +-- 4 files changed, 45 insertions(+), 26 deletions(-) diff --git a/coins/monero/src/rpc.rs b/coins/monero/src/rpc.rs index 94bf84ff..ae4b6a80 100644 --- a/coins/monero/src/rpc.rs +++ b/coins/monero/src/rpc.rs @@ -249,9 +249,10 @@ impl Rpc { .txs .iter() .map(|res| { - let tx = Transaction::deserialize(&mut std::io::Cursor::new(rpc_hex( - if !res.as_hex.is_empty() { &res.as_hex } else { &res.pruned_as_hex }, - )?)) + let tx = Transaction::deserialize::<&[u8]>( + &mut rpc_hex(if !res.as_hex.is_empty() { &res.as_hex } else { &res.pruned_as_hex })? + .as_ref(), + ) .map_err(|_| match hash_hex(&res.tx_hash) { Ok(hash) => RpcError::InvalidTransaction(hash), Err(err) => err, @@ -287,27 +288,52 @@ impl Rpc { Ok(txs.txs[0].block_height) } - pub async fn get_block(&self, height: usize) -> Result { + pub async fn get_block_hash(&self, number: usize) -> Result<[u8; 32], RpcError> { + #[derive(Deserialize, Debug)] + struct BlockHeaderResponse { + hash: String, + } + #[derive(Deserialize, Debug)] + struct BlockHeaderByHeightResponse { + block_header: BlockHeaderResponse, + } + + let header: BlockHeaderByHeightResponse = + self.json_rpc_call("get_block_header_by_height", Some(json!({ "height": number }))).await?; + rpc_hex(&header.block_header.hash)?.try_into().map_err(|_| RpcError::InvalidNode) + } + + pub async fn get_block(&self, hash: [u8; 32]) -> Result { #[derive(Deserialize, Debug)] struct BlockResponse { blob: String, } - let block: BlockResponse = - self.json_rpc_call("get_block", Some(json!({ "height": height }))).await?; - Ok( - Block::deserialize(&mut std::io::Cursor::new(rpc_hex(&block.blob)?)) - .expect("Monero returned a block we couldn't deserialize"), - ) + let res: BlockResponse = + self.json_rpc_call("get_block", Some(json!({ "hash": hex::encode(hash) }))).await?; + + Block::deserialize::<&[u8]>(&mut rpc_hex(&res.blob)?.as_ref()) + .map_err(|_| RpcError::InvalidNode) } - pub async fn get_block_transactions(&self, height: usize) -> Result, RpcError> { - let block = self.get_block(height).await?; + pub async fn get_block_by_number(&self, number: usize) -> Result { + self.get_block(self.get_block_hash(number).await?).await + } + + pub async fn get_block_transactions(&self, hash: [u8; 32]) -> Result, RpcError> { + let block = self.get_block(hash).await?; let mut res = vec![block.miner_tx]; res.extend(self.get_transactions(&block.txs).await?); Ok(res) } + pub async fn get_block_transactions_by_number( + &self, + number: usize, + ) -> Result, RpcError> { + self.get_block_transactions(self.get_block_hash(number).await?).await + } + /// Get the output indexes of the specified transaction. pub async fn get_o_indexes(&self, hash: [u8; 32]) -> Result, RpcError> { #[derive(Serialize, Debug)] @@ -407,13 +433,8 @@ impl Rpc { &outs .outs .iter() - .map(|out| { - rpc_hex(&out.txid) - .expect("Monero returned an invalidly encoded hash") - .try_into() - .expect("Monero returned an invalid sized hash") - }) - .collect::>(), + .map(|out| rpc_hex(&out.txid)?.try_into().map_err(|_| RpcError::InvalidNode)) + .collect::, _>>()?, ) .await?; diff --git a/coins/monero/src/wallet/extra.rs b/coins/monero/src/wallet/extra.rs index ac8924a4..dda72f53 100644 --- a/coins/monero/src/wallet/extra.rs +++ b/coins/monero/src/wallet/extra.rs @@ -1,5 +1,5 @@ use core::ops::BitXor; -use std::io::{self, Read, Write, Cursor}; +use std::io::{self, Read, Write}; use zeroize::Zeroize; @@ -127,7 +127,7 @@ impl Extra { pub(crate) fn payment_id(&self) -> Option { for field in &self.0 { if let ExtraField::Nonce(data) = field { - return PaymentId::deserialize(&mut Cursor::new(data)).ok(); + return PaymentId::deserialize::<&[u8]>(&mut data.as_ref()).ok(); } } None diff --git a/coins/monero/src/wallet/scan.rs b/coins/monero/src/wallet/scan.rs index cefa3d1e..29dfdd07 100644 --- a/coins/monero/src/wallet/scan.rs +++ b/coins/monero/src/wallet/scan.rs @@ -1,5 +1,3 @@ -use std::io::Cursor; - use zeroize::{Zeroize, ZeroizeOnDrop}; use curve25519_dalek::{constants::ED25519_BASEPOINT_TABLE, scalar::Scalar, edwards::EdwardsPoint}; @@ -232,7 +230,7 @@ impl Timelocked { impl Scanner { /// Scan a transaction to discover the received outputs. pub fn scan_transaction(&mut self, tx: &Transaction) -> Timelocked { - let extra = Extra::deserialize(&mut Cursor::new(&tx.prefix.extra)); + let extra = Extra::deserialize::<&[u8]>(&mut tx.prefix.extra.as_ref()); let keys; let extra = if let Ok(extra) = extra { keys = extra.keys(); diff --git a/coins/monero/tests/runner.rs b/coins/monero/tests/runner.rs index 8c202422..6861889a 100644 --- a/coins/monero/tests/runner.rs +++ b/coins/monero/tests/runner.rs @@ -43,7 +43,7 @@ pub async fn mine_until_unlocked(rpc: &Rpc, addr: &str, tx_hash: [u8; 32]) { let mut height = rpc.get_height().await.unwrap(); let mut found = false; while !found { - let block = rpc.get_block(height - 1).await.unwrap(); + let block = rpc.get_block_by_number(height - 1).await.unwrap(); found = match block.txs.iter().find(|&&x| x == tx_hash) { Some(_) => true, None => { @@ -69,7 +69,7 @@ pub async fn get_miner_tx_output(rpc: &Rpc, view: &ViewPair) -> SpendableOutput .await .unwrap(); - let block = rpc.get_block(start).await.unwrap(); + let block = rpc.get_block_by_number(start).await.unwrap(); scanner.scan(rpc, &block).await.unwrap().swap_remove(0).ignore_timelock().swap_remove(0) }