Exposed a hash-based API for accessing blocks

Also corrects a few panics, which shouldn't have been present, and unnecessary
Cursor uses.
This commit is contained in:
Luke Parker 2023-01-07 04:00:12 -05:00
parent 814a9a8d35
commit 1d6df0099c
No known key found for this signature in database
4 changed files with 45 additions and 26 deletions

View file

@ -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<Block, RpcError> {
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<Block, RpcError> {
#[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<Vec<Transaction>, RpcError> {
let block = self.get_block(height).await?;
pub async fn get_block_by_number(&self, number: usize) -> Result<Block, RpcError> {
self.get_block(self.get_block_hash(number).await?).await
}
pub async fn get_block_transactions(&self, hash: [u8; 32]) -> Result<Vec<Transaction>, 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<Vec<Transaction>, 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<Vec<u64>, 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::<Vec<_>>(),
.map(|out| rpc_hex(&out.txid)?.try_into().map_err(|_| RpcError::InvalidNode))
.collect::<Result<Vec<_>, _>>()?,
)
.await?;

View file

@ -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<PaymentId> {
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

View file

@ -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<O: Clone + Zeroize> Timelocked<O> {
impl Scanner {
/// Scan a transaction to discover the received outputs.
pub fn scan_transaction(&mut self, tx: &Transaction) -> Timelocked<ReceivedOutput> {
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();

View file

@ -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)
}