From 136e9b70a644fbd948b692c3ba73bd3a3a29aadc Mon Sep 17 00:00:00 2001 From: "hinto.janai" Date: Sun, 8 Sep 2024 20:48:31 -0400 Subject: [PATCH] split `blockchain/helper` functions --- binaries/cuprated/src/rpc.rs | 1 + binaries/cuprated/src/rpc/blockchain.rs | 142 ++++++++++++++++++++ binaries/cuprated/src/rpc/helper.rs | 167 ++++++------------------ binaries/cuprated/src/rpc/json.rs | 82 ++++-------- 4 files changed, 203 insertions(+), 189 deletions(-) create mode 100644 binaries/cuprated/src/rpc/blockchain.rs diff --git a/binaries/cuprated/src/rpc.rs b/binaries/cuprated/src/rpc.rs index 233883ed..7df05b39 100644 --- a/binaries/cuprated/src/rpc.rs +++ b/binaries/cuprated/src/rpc.rs @@ -3,6 +3,7 @@ //! Will contain the code to initiate the RPC and a request handler. mod bin; +mod blockchain; mod constants; mod handler; mod helper; diff --git a/binaries/cuprated/src/rpc/blockchain.rs b/binaries/cuprated/src/rpc/blockchain.rs new file mode 100644 index 00000000..b96c5618 --- /dev/null +++ b/binaries/cuprated/src/rpc/blockchain.rs @@ -0,0 +1,142 @@ +//! These are convenience functions that make +//! sending [`BlockchainReadRequest`] less verbose. + +use std::sync::Arc; + +use anyhow::{anyhow, Error}; +use futures::StreamExt; +use tower::{Service, ServiceExt}; + +use cuprate_consensus::BlockchainResponse; +use cuprate_helper::{ + cast::{u64_to_usize, usize_to_u64}, + map::split_u128_into_low_high_bits, +}; +use cuprate_types::{ + blockchain::BlockchainReadRequest, Chain, ExtendedBlockHeader, VerifiedBlockInformation, +}; + +use crate::rpc::{CupratedRpcHandlerState, RESTRICTED_BLOCK_COUNT, RESTRICTED_BLOCK_HEADER_RANGE}; + +/// [`BlockchainResponse::ChainHeight`]. +pub(super) async fn chain_height( + state: &mut CupratedRpcHandlerState, +) -> Result<(u64, [u8; 32]), Error> { + let BlockchainResponse::ChainHeight(height, hash) = state + .blockchain + .ready() + .await? + .call(BlockchainReadRequest::ChainHeight) + .await? + else { + unreachable!(); + }; + + Ok((usize_to_u64(height), hash)) +} + +/// [`BlockchainResponse::Block`]. +pub(super) async fn block( + state: &mut CupratedRpcHandlerState, + height: u64, +) -> Result { + let BlockchainResponse::Block(block) = state + .blockchain + .ready() + .await? + .call(BlockchainReadRequest::Block(u64_to_usize(height))) + .await? + else { + unreachable!(); + }; + + Ok(block) +} + +/// [`BlockchainResponse::BlockByHash`]. +pub(super) async fn block_by_hash( + state: &mut CupratedRpcHandlerState, + hash: [u8; 32], +) -> Result { + let BlockchainResponse::BlockByHash(block) = state + .blockchain + .ready() + .await? + .call(BlockchainReadRequest::BlockByHash(hash)) + .await? + else { + unreachable!(); + }; + + Ok(block) +} + +/// [`BlockchainResponse::BlockExtendedHeader`]. +pub(super) async fn block_extended_header( + state: &mut CupratedRpcHandlerState, + height: u64, +) -> Result { + let BlockchainResponse::BlockExtendedHeader(header) = state + .blockchain + .ready() + .await? + .call(BlockchainReadRequest::BlockExtendedHeader(u64_to_usize( + height, + ))) + .await? + else { + unreachable!(); + }; + + Ok(header) +} + +/// [`BlockchainResponse::BlockExtendedHeaderByHash`]. +pub(super) async fn block_extended_header_by_hash( + state: &mut CupratedRpcHandlerState, + hash: [u8; 32], +) -> Result { + let BlockchainResponse::BlockExtendedHeaderByHash(header) = state + .blockchain + .ready() + .await? + .call(BlockchainReadRequest::BlockExtendedHeaderByHash(hash)) + .await? + else { + unreachable!(); + }; + + Ok(header) +} + +/// [`BlockchainResponse::BlockHash`] with [`Chain::Main`]. +pub(super) async fn block_hash( + state: &mut CupratedRpcHandlerState, + height: u64, +) -> Result<[u8; 32], Error> { + let BlockchainResponse::BlockHash(hash) = state + .blockchain + .ready() + .await? + .call(BlockchainReadRequest::BlockHash( + u64_to_usize(height), + Chain::Main, + )) + .await? + else { + unreachable!(); + }; + + Ok(hash) +} + +// FindBlock([u8; 32]), +// FilterUnknownHashes(HashSet<[u8; 32]>), +// BlockExtendedHeaderInRange(Range, Chain), +// ChainHeight, +// GeneratedCoins(usize), +// Outputs(HashMap>), +// NumberOutputsWithAmount(Vec), +// KeyImagesSpent(HashSet<[u8; 32]>), +// CompactChainHistory, +// FindFirstUnknown(Vec<[u8; 32]>), diff --git a/binaries/cuprated/src/rpc/helper.rs b/binaries/cuprated/src/rpc/helper.rs index 96166ff9..5c2e2424 100644 --- a/binaries/cuprated/src/rpc/helper.rs +++ b/binaries/cuprated/src/rpc/helper.rs @@ -3,7 +3,7 @@ //! Many of the handlers have bodies with only small differences, //! the identical code is extracted and reused here in these functions. //! -//! They typically map to database requests. +//! These build on-top of [`crate::rpc::blockchain`] functions. use std::sync::Arc; @@ -20,144 +20,51 @@ use cuprate_types::{ blockchain::BlockchainReadRequest, Chain, ExtendedBlockHeader, VerifiedBlockInformation, }; -use crate::rpc::{CupratedRpcHandlerState, RESTRICTED_BLOCK_COUNT, RESTRICTED_BLOCK_HEADER_RANGE}; +use crate::{ + rpc::helper, + rpc::{CupratedRpcHandlerState, RESTRICTED_BLOCK_COUNT, RESTRICTED_BLOCK_HEADER_RANGE}, +}; -/// [`BlockchainResponse::ChainHeight`]. -pub(super) async fn chain_height( +/// Check if `height` is greater than the [`top_height`]. +/// +/// # Errors +/// This returns the [`top_height`] on [`Ok`] and +/// returns [`Error`] if `height` is greater than [`top_height`]. +pub(super) async fn check_height( state: &mut CupratedRpcHandlerState, -) -> Result<(u64, [u8; 32]), Error> { - let BlockchainResponse::ChainHeight(height, hash) = state - .blockchain - .ready() - .await? - .call(BlockchainReadRequest::ChainHeight) - .await? - else { - unreachable!(); + height: u64, +) -> Result { + let (top_height, _) = top_height(state).await?; + + if height > top_height { + return Err(anyhow!( + "Requested block height: {height} greater than current top block height: {top_height}", + )); + } + + Ok(top_height) +} + +/// Parse a hexadecimal [`String`] as a 32-byte hash. +pub(super) fn hex_to_hash(hex: String) -> Result<[u8; 32], Error> { + let error = || anyhow!("Failed to parse hex representation of hash. Hex = {hex}."); + + let Ok(bytes) = hex::decode(&hex) else { + return Err(error()); }; - Ok((usize_to_u64(height), hash)) + let Ok(hash) = bytes.try_into() else { + return Err(error()); + }; + + Ok(hash) } /// [`BlockchainResponse::ChainHeight`] minus 1. pub(super) async fn top_height( state: &mut CupratedRpcHandlerState, ) -> Result<(u64, [u8; 32]), Error> { - let BlockchainResponse::ChainHeight(height, hash) = state - .blockchain - .ready() - .await? - .call(BlockchainReadRequest::ChainHeight) - .await? - else { - unreachable!(); - }; - + let (chain_height, hash) = helper::chain_height().await?; + let height = chain_height.saturating_sub(1); Ok((usize_to_u64(height), hash)) } - -/// [`BlockchainResponse::Block`]. -pub(super) async fn block( - state: &mut CupratedRpcHandlerState, - height: u64, -) -> Result { - let BlockchainResponse::Block(block) = state - .blockchain - .ready() - .await? - .call(BlockchainReadRequest::Block(u64_to_usize(height))) - .await? - else { - unreachable!(); - }; - - Ok(block) -} - -/// [`BlockchainResponse::BlockByHash`]. -pub(super) async fn block_by_hash( - state: &mut CupratedRpcHandlerState, - hash: [u8; 32], -) -> Result { - let BlockchainResponse::BlockByHash(block) = state - .blockchain - .ready() - .await? - .call(BlockchainReadRequest::BlockByHash(hash)) - .await? - else { - unreachable!(); - }; - - Ok(block) -} - -/// [`BlockchainResponse::BlockExtendedHeader`]. -pub(super) async fn block_extended_header( - state: &mut CupratedRpcHandlerState, - height: u64, -) -> Result { - let BlockchainResponse::BlockExtendedHeader(header) = state - .blockchain - .ready() - .await? - .call(BlockchainReadRequest::BlockExtendedHeader(u64_to_usize( - height, - ))) - .await? - else { - unreachable!(); - }; - - Ok(header) -} - -/// [`BlockchainResponse::BlockExtendedHeaderByHash`]. -pub(super) async fn block_extended_header_by_hash( - state: &mut CupratedRpcHandlerState, - hash: [u8; 32], -) -> Result { - let BlockchainResponse::BlockExtendedHeaderByHash(header) = state - .blockchain - .ready() - .await? - .call(BlockchainReadRequest::BlockExtendedHeaderByHash(hash)) - .await? - else { - unreachable!(); - }; - - Ok(header) -} - -/// [`BlockchainResponse::BlockHash`] with [`Chain::Main`]. -pub(super) async fn block_hash( - state: &mut CupratedRpcHandlerState, - height: u64, -) -> Result<[u8; 32], Error> { - let BlockchainResponse::BlockHash(hash) = state - .blockchain - .ready() - .await? - .call(BlockchainReadRequest::BlockHash( - u64_to_usize(height), - Chain::Main, - )) - .await? - else { - unreachable!(); - }; - - Ok(hash) -} - -// FindBlock([u8; 32]), -// FilterUnknownHashes(HashSet<[u8; 32]>), -// BlockExtendedHeaderInRange(Range, Chain), -// ChainHeight, -// GeneratedCoins(usize), -// Outputs(HashMap>), -// NumberOutputsWithAmount(Vec), -// KeyImagesSpent(HashSet<[u8; 32]>), -// CompactChainHistory, -// FindFirstUnknown(Vec<[u8; 32]>), diff --git a/binaries/cuprated/src/rpc/json.rs b/binaries/cuprated/src/rpc/json.rs index e7319676..e830d206 100644 --- a/binaries/cuprated/src/rpc/json.rs +++ b/binaries/cuprated/src/rpc/json.rs @@ -36,7 +36,7 @@ use cuprate_rpc_types::{ use cuprate_types::{blockchain::BlockchainReadRequest, Chain}; use crate::{ - rpc::helper, + rpc::{blockchain, helper}, rpc::{CupratedRpcHandlerState, RESTRICTED_BLOCK_COUNT, RESTRICTED_BLOCK_HEADER_RANGE}, }; @@ -104,7 +104,7 @@ async fn get_block_count( ) -> Result { Ok(GetBlockCountResponse { base: ResponseBase::ok(), - count: helper::top_height(&mut state).await?, + count: helper::top_height(&mut state).await?.0, }) } @@ -113,7 +113,7 @@ async fn on_get_block_hash( request: OnGetBlockHashRequest, ) -> Result { let [height] = request.block_height; - let hash = helper::block_hash(&mut state, height).await?; + let hash = blockchain::block_hash(&mut state, height).await?; let block_hash = hex::encode(hash); Ok(OnGetBlockHashResponse { block_hash }) @@ -145,7 +145,7 @@ async fn get_last_block_header( request: GetLastBlockHeaderRequest, ) -> Result { Ok(GetLastBlockHeaderResponse { - base: ResponseBase::ok(), + base: AccessResponseBase::ok(), block_header: todo!(), }) } @@ -165,17 +165,8 @@ async fn get_block_header_by_hash( hex: String, fill_pow_hash: bool, ) -> Result { - let Ok(bytes) = hex::decode(&hex) else { - return Err(anyhow!( - "Failed to parse hex representation of block hash. Hex = {hex}." - )); - }; - - let Ok(hash) = bytes.try_into() else { - return Err(anyhow!("TODO")); - }; - - let block = helper::block_by_hash(state, hash).await?; + let hash = helper::hex_to_hash(hex)?; + let block = blockchain::block_by_hash(state, hash).await?; let block_header = BlockHeader::from(&block); Ok(block_header) @@ -200,16 +191,8 @@ async fn get_block_header_by_height( mut state: CupratedRpcHandlerState, request: GetBlockHeaderByHeightRequest, ) -> Result { - let (height, _) = helper::top_height(&mut state).await?; - - if request.height > height { - return Err(anyhow!( - "Requested block height: {} greater than current top block height: {height}", - request.height - )); - } - - let block = helper::block(&mut state, height).await?; + helper::check_height(&mut state, request.height).await?; + let block = blockchain::block(&mut state, request.height).await?; Ok(GetBlockHeaderByHeightResponse { base: AccessResponseBase::ok(), @@ -221,10 +204,10 @@ async fn get_block_headers_range( mut state: CupratedRpcHandlerState, request: GetBlockHeadersRangeRequest, ) -> Result { - let (height, _) = helper::top_height(&mut state).await?; + let (top_height, _) = helper::top_height(&mut state).await?; - if request.start_height >= height - || request.end_height >= height + if request.start_height >= top_height + || request.end_height >= top_height || request.start_height > request.end_height { return Err(anyhow!("Invalid start/end heights")); @@ -243,8 +226,8 @@ async fn get_block_headers_range( { let ready = state.blockchain.ready().await?; - let height = u64_to_usize(height); - for block in request.start_height..=request.end_height { + for height in request.start_height..=request.end_height { + let height = u64_to_usize(height); let task = tokio::task::spawn(ready.call(BlockchainReadRequest::Block(height))); tasks.push(task); } @@ -267,31 +250,12 @@ async fn get_block( mut state: CupratedRpcHandlerState, request: GetBlockRequest, ) -> Result { - let block = if !request.hash.is_empty() { - let hex = request.hash; - - let Ok(bytes) = hex::decode(&hex) else { - return Err(anyhow!( - "Failed to parse hex representation of block hash. Hex = {hex}." - )); - }; - - let Ok(hash) = bytes.try_into() else { - return Err(anyhow!("TODO")); - }; - - helper::block_by_hash(&mut state, hash).await? + let block = if request.hash.is_empty() { + helper::check_height(&mut state, request.height).await?; + blockchain::block(&mut state, request.height).await? } else { - let (height, _) = helper::top_height(&mut state).await?; - - if request.height > height { - return Err(anyhow!( - "Requested block height: {} greater than current top block height: {height}", - request.height - )); - } - - helper::block(&mut state, height).await? + let hash = helper::hex_to_hash(request.hash)?; + blockchain::block_by_hash(&mut state, hash).await? }; let block_header = (&block).into(); @@ -458,7 +422,7 @@ async fn get_version( request: GetVersionRequest, ) -> Result { Ok(GetVersionResponse { - base: AccessResponseBase::ok(), + base: ResponseBase::ok(), version: todo!(), release: todo!(), current_height: todo!(), @@ -484,7 +448,7 @@ async fn get_alternate_chains( request: GetAlternateChainsRequest, ) -> Result { Ok(GetAlternateChainsResponse { - base: AccessResponseBase::ok(), + base: ResponseBase::ok(), chains: todo!(), }) } @@ -516,7 +480,7 @@ async fn get_transaction_pool_backlog( request: GetTransactionPoolBacklogRequest, ) -> Result { Ok(GetTransactionPoolBacklogResponse { - base: AccessResponseBase::ok(), + base: ResponseBase::ok(), backlog: todo!(), }) } @@ -526,7 +490,7 @@ async fn get_miner_data( request: GetMinerDataRequest, ) -> Result { Ok(GetMinerDataResponse { - base: AccessResponseBase::ok(), + base: ResponseBase::ok(), major_version: todo!(), height: todo!(), prev_id: todo!(), @@ -543,7 +507,7 @@ async fn prune_blockchain( request: PruneBlockchainRequest, ) -> Result { Ok(PruneBlockchainResponse { - base: AccessResponseBase::ok(), + base: ResponseBase::ok(), pruned: todo!(), pruning_seed: todo!(), })