From 6dc85c606f530364689be724084e28eeec88aac0 Mon Sep 17 00:00:00 2001 From: "hinto.janai" Date: Wed, 11 Sep 2024 18:42:40 -0400 Subject: [PATCH] bin: `get_blocks_by_height`, `get_hashes` --- binaries/cuprated/src/rpc/bin.rs | 81 +++++++++++++++++++++-- binaries/cuprated/src/rpc/blockchain.rs | 18 ++++++ net/fixed-bytes/src/lib.rs | 27 ++++++++ rpc/types/src/misc/mod.rs | 2 + rpc/types/src/misc/requested_info.rs | 86 +++++++++++++++++++++++++ 5 files changed, 209 insertions(+), 5 deletions(-) create mode 100644 rpc/types/src/misc/requested_info.rs diff --git a/binaries/cuprated/src/rpc/bin.rs b/binaries/cuprated/src/rpc/bin.rs index e1358b05..237d5d38 100644 --- a/binaries/cuprated/src/rpc/bin.rs +++ b/binaries/cuprated/src/rpc/bin.rs @@ -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 { + // Time should be set early: + // + 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 { + 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::, Error>>()?; + Ok(GetBlocksByHeightResponse { base: AccessResponseBase::ok(), - ..todo!() + blocks, }) } /// async fn get_hashes( - state: CupratedRpcHandlerState, + mut state: CupratedRpcHandlerState, request: GetHashesRequest, ) -> Result { + 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, }) } diff --git a/binaries/cuprated/src/rpc/blockchain.rs b/binaries/cuprated/src/rpc/blockchain.rs index f49065e9..da0c7702 100644 --- a/binaries/cuprated/src/rpc/blockchain.rs +++ b/binaries/cuprated/src/rpc/blockchain.rs @@ -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, 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, Chain), diff --git a/net/fixed-bytes/src/lib.rs b/net/fixed-bytes/src/lib.rs index 2e8f1bc5..210813c9 100644 --- a/net/fixed-bytes/src/lib.rs +++ b/net/fixed-bytes/src/lib.rs @@ -154,6 +154,26 @@ impl ByteArrayVec { 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 Index for ByteArrayVec { } } +impl From> for Vec<[u8; N]> { + fn from(value: ByteArrayVec) -> Self { + let len = value.len(); + (0..len).map(|i| value[i]).collect() + } +} + #[cfg(test)] mod tests { use serde_json::{from_str, to_string}; diff --git a/rpc/types/src/misc/mod.rs b/rpc/types/src/misc/mod.rs index c5c1840e..547682d6 100644 --- a/rpc/types/src/misc/mod.rs +++ b/rpc/types/src/misc/mod.rs @@ -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; diff --git a/rpc/types/src/misc/requested_info.rs b/rpc/types/src/misc/requested_info.rs new file mode 100644 index 00000000..e594ced6 --- /dev/null +++ b/rpc/types/src/misc/requested_info.rs @@ -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 { + 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(r: &mut B, marker: &Marker) -> error::Result { + let u = u8::read(r, marker)?; + Self::from_u8(u).ok_or(error::Error::Format("u8 was greater than 2")) + } + + fn write(self, w: &mut B) -> error::Result<()> { + let u = self.to_u8(); + u8::write(u, w)?; + Ok(()) + } +}