bin: get_blocks_by_height, get_hashes

This commit is contained in:
hinto.janai 2024-09-11 18:42:40 -04:00
parent 4f738eef38
commit 6dc85c606f
No known key found for this signature in database
GPG key ID: D47CE05FA175A499
5 changed files with 209 additions and 5 deletions

View file

@ -1,4 +1,4 @@
use anyhow::Error;
use anyhow::{anyhow, Error};
use cuprate_rpc_types::{
base::{AccessResponseBase, ResponseBase},
@ -9,9 +9,13 @@ use cuprate_rpc_types::{
GetTransactionPoolHashesRequest, GetTransactionPoolHashesResponse,
},
json::{GetOutputDistributionRequest, GetOutputDistributionResponse},
misc::RequestedInfo,
};
use cuprate_types::BlockCompleteEntry;
use crate::rpc::CupratedRpcHandlerState;
use crate::rpc::{blockchain, helper, CupratedRpcHandlerState, RESTRICTED_TRANSACTIONS_COUNT};
use super::RESTRICTED_BLOCK_COUNT;
/// Map a [`BinRequest`] to the function that will lead to a [`BinResponse`].
pub(super) async fn map_request(
@ -41,6 +45,38 @@ async fn get_blocks(
state: CupratedRpcHandlerState,
request: GetBlocksRequest,
) -> Result<GetBlocksResponse, Error> {
// Time should be set early:
// <https://github.com/monero-project/monero/blob/cc73fe71162d564ffda8e549b79a350bca53c454/src/rpc/core_rpc_server.cpp#L628-L631>
let daemon_time = cuprate_helper::time::current_unix_timestamp();
let Some(requested_info) = RequestedInfo::from_u8(request.requested_info) else {
return Err(anyhow!("Failed, wrong requested info"));
};
let (get_blocks, get_pool) = match requested_info {
RequestedInfo::BlocksOnly => (true, false),
RequestedInfo::BlocksAndPool => (true, true),
RequestedInfo::PoolOnly => (false, true),
};
if get_pool {
let max_tx_count = if state.restricted {
RESTRICTED_TRANSACTIONS_COUNT
} else {
usize::MAX
};
todo!();
}
if get_blocks {
if !request.block_ids.is_empty() {
todo!();
}
todo!();
}
// Ok(GetBlocksResponse {
// base: ResponseBase::ok(),
// ..todo!()
@ -53,20 +89,55 @@ async fn get_blocks_by_height(
state: CupratedRpcHandlerState,
request: GetBlocksByHeightRequest,
) -> Result<GetBlocksByHeightResponse, Error> {
if state.restricted && request.heights.len() > RESTRICTED_BLOCK_COUNT {
return Err(anyhow!("Too many blocks requested in restricted mode"));
}
let blocks = request
.heights
.into_iter()
.map(|height| Ok(todo!()))
.collect::<Result<Vec<BlockCompleteEntry>, Error>>()?;
Ok(GetBlocksByHeightResponse {
base: AccessResponseBase::ok(),
..todo!()
blocks,
})
}
/// <https://github.com/monero-project/monero/blob/cc73fe71162d564ffda8e549b79a350bca53c454/src/rpc/core_rpc_server.cpp#L859-L880>
async fn get_hashes(
state: CupratedRpcHandlerState,
mut state: CupratedRpcHandlerState,
request: GetHashesRequest,
) -> Result<GetHashesResponse, Error> {
let Some(last) = request.block_ids.last() else {
return Err(anyhow!("block_ids empty"));
};
const GENESIS_BLOCK_HASH: [u8; 32] = [0; 32]; // TODO
if last != GENESIS_BLOCK_HASH {
return Err(anyhow!(
"genesis block mismatch, found: {last:?}, expected: {GENESIS_BLOCK_HASH:?}"
));
}
let mut bytes = request.block_ids;
let hashes: Vec<[u8; 32]> = bytes.clone().into();
let (current_height, _) = helper::top_height(&mut state).await?;
let Some((index, start_height)) = blockchain::find_first_unknown(&mut state, hashes).await?
else {
return Err(anyhow!("Failed"));
};
let m_blocks_ids = bytes.split_off(index);
Ok(GetHashesResponse {
base: AccessResponseBase::ok(),
..todo!()
m_blocks_ids,
start_height,
current_height,
})
}

View file

@ -188,6 +188,24 @@ pub(super) async fn pop_blocks(
Ok(usize_to_u64(height))
}
/// [`BlockchainResponse::FindFirstUnknown`]
pub(super) async fn find_first_unknown(
state: &mut CupratedRpcHandlerState,
hashes: Vec<[u8; 32]>,
) -> Result<Option<(usize, u64)>, Error> {
let BlockchainResponse::FindFirstUnknown(resp) = state
.blockchain_read
.ready()
.await?
.call(BlockchainReadRequest::FindFirstUnknown(hashes))
.await?
else {
unreachable!();
};
Ok(resp.map(|(index, height)| (index, usize_to_u64(height))))
}
// FindBlock([u8; 32]),
// FilterUnknownHashes(HashSet<[u8; 32]>),
// BlockExtendedHeaderInRange(Range<usize>, Chain),

View file

@ -154,6 +154,26 @@ impl<const N: usize> ByteArrayVec<N> {
self.0
}
pub fn first(&self) -> Option<[u8; N]> {
let len = self.len();
if len == 0 {
return None;
}
Some(self[0])
}
pub fn last(&self) -> Option<[u8; N]> {
let len = self.len();
if len == 0 {
return None;
}
Some(self[len - 1])
}
/// Splits the byte array vec into two at the given index.
///
/// Afterwards self contains elements [0, at), and the returned [`ByteArrayVec`] contains elements [at, len).
@ -246,6 +266,13 @@ impl<const N: usize> Index<usize> for ByteArrayVec<N> {
}
}
impl<const N: usize> From<ByteArrayVec<N>> for Vec<[u8; N]> {
fn from(value: ByteArrayVec<N>) -> Self {
let len = value.len();
(0..len).map(|i| value[i]).collect()
}
}
#[cfg(test)]
mod tests {
use serde_json::{from_str, to_string};

View file

@ -18,6 +18,7 @@ mod key_image_spent_status;
#[allow(clippy::module_inception)]
mod misc;
mod pool_info_extent;
mod requested_info;
mod status;
mod tx_entry;
@ -31,5 +32,6 @@ pub use misc::{
SyncInfoPeer, TxBacklogEntry, TxInfo, TxOutputIndices, TxpoolHisto, TxpoolStats,
};
pub use pool_info_extent::PoolInfoExtent;
pub use requested_info::RequestedInfo;
pub use status::Status;
pub use tx_entry::TxEntry;

View file

@ -0,0 +1,86 @@
//! TODO
//---------------------------------------------------------------------------------------------------- Use
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
#[cfg(feature = "epee")]
use cuprate_epee_encoding::{
error,
macros::bytes::{Buf, BufMut},
EpeeValue, Marker,
};
//---------------------------------------------------------------------------------------------------- RequestedInfo
#[doc = crate::macros::monero_definition_link!(
cc73fe71162d564ffda8e549b79a350bca53c454,
"rpc/core_rpc_server_commands_defs.h",
178..=183
)]
/// Used in [`crate::bin::GetBlocksRequest`].
#[derive(Copy, Clone, Default, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[repr(u8)]
pub enum RequestedInfo {
#[default]
BlocksOnly = 0,
BlocksAndPool = 1,
PoolOnly = 2,
}
impl RequestedInfo {
/// Convert [`Self`] to a [`u8`].
///
/// ```rust
/// use cuprate_rpc_types::misc::RequestedInfo as R;
///
/// assert_eq!(R::BlocksOnly.to_u8(), 0);
/// assert_eq!(R::BlocksAndPool.to_u8(), 1);
/// assert_eq!(R::PoolOnly.to_u8(), 2);
/// ```
pub const fn to_u8(self) -> u8 {
match self {
Self::BlocksOnly => 0,
Self::BlocksAndPool => 1,
Self::PoolOnly => 2,
}
}
/// Convert a [`u8`] to a [`Self`].
///
/// # Errors
/// This returns [`None`] if `u > 2`.
///
/// ```rust
/// use cuprate_rpc_types::misc::RequestedInfo as R;
///
/// assert_eq!(R::from_u8(0), Some(R::BlocksOnly));
/// assert_eq!(R::from_u8(1), Some(R::BlocksAndPool));
/// assert_eq!(R::from_u8(2), Some(R::PoolOnly));
/// assert_eq!(R::from_u8(3), None);
/// ```
pub const fn from_u8(u: u8) -> Option<Self> {
Some(match u {
0 => Self::BlocksOnly,
1 => Self::BlocksAndPool,
2 => Self::PoolOnly,
_ => return None,
})
}
}
#[cfg(feature = "epee")]
impl EpeeValue for RequestedInfo {
const MARKER: Marker = u8::MARKER;
fn read<B: Buf>(r: &mut B, marker: &Marker) -> error::Result<Self> {
let u = u8::read(r, marker)?;
Self::from_u8(u).ok_or(error::Error::Format("u8 was greater than 2"))
}
fn write<B: BufMut>(self, w: &mut B) -> error::Result<()> {
let u = self.to_u8();
u8::write(u, w)?;
Ok(())
}
}