diff --git a/Cargo.lock b/Cargo.lock
index 5fea18d0..e45f7e42 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -972,6 +972,7 @@ dependencies = [
  "cuprate-blockchain",
  "cuprate-consensus",
  "cuprate-consensus-rules",
+ "cuprate-constants",
  "cuprate-cryptonight",
  "cuprate-dandelion-tower",
  "cuprate-database",
diff --git a/binaries/cuprated/Cargo.toml b/binaries/cuprated/Cargo.toml
index 325406bf..7ec85796 100644
--- a/binaries/cuprated/Cargo.toml
+++ b/binaries/cuprated/Cargo.toml
@@ -12,6 +12,7 @@ repository = "https://github.com/Cuprate/cuprate/tree/main/binaries/cuprated"
 cuprate-consensus        = { path = "../../consensus" }
 cuprate-fast-sync        = { path = "../../consensus/fast-sync" }
 cuprate-consensus-rules  = { path = "../../consensus/rules" }
+cuprate-constants        = { path = "../../constants", features = ["build", "rpc"] }
 cuprate-cryptonight      = { path = "../../cryptonight" }
 cuprate-helper           = { path = "../../helper" }
 cuprate-epee-encoding    = { path = "../../net/epee-encoding" }
diff --git a/binaries/cuprated/src/rpc.rs b/binaries/cuprated/src/rpc.rs
index fe8e5f21..a52fc98c 100644
--- a/binaries/cuprated/src/rpc.rs
+++ b/binaries/cuprated/src/rpc.rs
@@ -7,5 +7,6 @@ mod handler;
 mod json;
 mod other;
 mod request;
+mod helper;
 
 pub use handler::CupratedRpcHandler;
diff --git a/binaries/cuprated/src/rpc/helper.rs b/binaries/cuprated/src/rpc/helper.rs
new file mode 100644
index 00000000..7c95b1b3
--- /dev/null
+++ b/binaries/cuprated/src/rpc/helper.rs
@@ -0,0 +1,168 @@
+//! These are internal helper functions used by the actual RPC handlers.
+//!
+//! Many of the handlers have bodies with only small differences,
+//! the identical code is extracted and reused here in these functions.
+//!
+//! These build on-top of [`crate::rpc::request`] functions.
+
+use std::sync::Arc;
+
+use anyhow::{anyhow, Error};
+use cuprate_rpc_types::misc::{BlockHeader, KeyImageSpentStatus};
+use futures::StreamExt;
+use monero_serai::block::Block;
+use tower::{Service, ServiceExt};
+
+use cuprate_consensus::BlockchainResponse;
+use cuprate_constants::rpc::{RESTRICTED_BLOCK_COUNT, RESTRICTED_BLOCK_HEADER_RANGE};
+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::request::blockchain, rpc::CupratedRpcHandler};
+
+fn into_block_header(
+    height: u64,
+    top_height: u64,
+    fill_pow_hash: bool,
+    block: Block,
+    header: ExtendedBlockHeader,
+) -> BlockHeader {
+    let block_weight = usize_to_u64(header.block_weight);
+    let depth = top_height.saturating_sub(height);
+    let (cumulative_difficulty_top64, cumulative_difficulty) =
+        split_u128_into_low_high_bits(header.cumulative_difficulty);
+
+    BlockHeader {
+        block_size: block_weight,
+        block_weight,
+        cumulative_difficulty_top64,
+        cumulative_difficulty,
+        depth,
+        difficulty_top64: todo!(),
+        difficulty: todo!(),
+        hash: hex::encode(block.hash()),
+        height,
+        long_term_weight: usize_to_u64(header.long_term_weight),
+        major_version: header.version.as_u8(),
+        miner_tx_hash: hex::encode(block.miner_transaction.hash()),
+        minor_version: header.vote,
+        nonce: block.header.nonce,
+        num_txes: usize_to_u64(block.transactions.len()),
+        orphan_status: todo!(),
+        pow_hash: if fill_pow_hash {
+            todo!()
+        } else {
+            String::new()
+        },
+        prev_hash: hex::encode(block.header.previous),
+        reward: todo!(),
+        timestamp: block.header.timestamp,
+        wide_cumulative_difficulty: hex::encode(u128::to_le_bytes(header.cumulative_difficulty)),
+        wide_difficulty: todo!(),
+    }
+}
+
+/// Get a [`VerifiedBlockInformation`] and map it to a [`BlockHeader`].
+pub(super) async fn block_header(
+    state: &mut CupratedRpcHandler,
+    height: u64,
+    fill_pow_hash: bool,
+) -> Result<BlockHeader, Error> {
+    let (top_height, _) = top_height(state).await?;
+    let block = blockchain::block(&mut state.blockchain_read, height).await?;
+    let header = blockchain::block_extended_header(&mut state.blockchain_read, height).await?;
+
+    let block_header = into_block_header(height, top_height, fill_pow_hash, block, header);
+
+    Ok(block_header)
+}
+
+/// Same as [`block_header`] but with the block's hash.
+pub(super) async fn block_header_by_hash(
+    state: &mut CupratedRpcHandler,
+    hash: [u8; 32],
+    fill_pow_hash: bool,
+) -> Result<BlockHeader, Error> {
+    let (top_height, _) = top_height(state).await?;
+    let block = blockchain::block_by_hash(&mut state.blockchain_read, hash).await?;
+    let header: ExtendedBlockHeader = todo!(); //blockchain::block_extended_header_by_hash(state.blockchain_read, hash).await?;
+
+    let block_header = into_block_header(header.height, top_height, fill_pow_hash, block, header);
+
+    Ok(block_header)
+}
+
+/// TODO
+pub(super) async fn top_block_header(
+    state: &mut CupratedRpcHandler,
+    fill_pow_hash: bool,
+) -> Result<BlockHeader, Error> {
+    let block: Block = todo!();
+    let header: ExtendedBlockHeader = todo!();
+
+    let block_header =
+        into_block_header(header.height, header.height, fill_pow_hash, block, header);
+
+    Ok(block_header)
+}
+
+/// 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 CupratedRpcHandler,
+    height: u64,
+) -> Result<u64, Error> {
+    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());
+    };
+
+    let Ok(hash) = bytes.try_into() else {
+        return Err(error());
+    };
+
+    Ok(hash)
+}
+
+/// [`BlockchainResponse::ChainHeight`] minus 1.
+pub(super) async fn top_height(state: &mut CupratedRpcHandler) -> Result<(u64, [u8; 32]), Error> {
+    let (chain_height, hash) = blockchain::chain_height(state).await?;
+    let height = chain_height.saturating_sub(1);
+    Ok((height, hash))
+}
+
+/// TODO
+pub(super) async fn key_image_spent(
+    state: &mut CupratedRpcHandler,
+    key_image: [u8; 32],
+) -> Result<KeyImageSpentStatus, Error> {
+    if blockchain::key_image_spent(state, key_image).await? {
+        Ok(KeyImageSpentStatus::SpentInBlockchain)
+    } else if todo!("key image is spent in tx pool") {
+        Ok(KeyImageSpentStatus::SpentInPool)
+    } else {
+        Ok(KeyImageSpentStatus::Unspent)
+    }
+}
diff --git a/binaries/cuprated/src/rpc/json.rs b/binaries/cuprated/src/rpc/json.rs
index 88b38b92..2ba6796b 100644
--- a/binaries/cuprated/src/rpc/json.rs
+++ b/binaries/cuprated/src/rpc/json.rs
@@ -1,29 +1,43 @@
 use std::sync::Arc;
 
-use anyhow::Error;
-use tower::ServiceExt;
+use anyhow::{anyhow, Error};
+use cuprate_types::HardFork;
+use monero_serai::block::Block;
+use tower::{Service, ServiceExt};
 
-use cuprate_rpc_types::json::{
-    AddAuxPowRequest, AddAuxPowResponse, BannedRequest, BannedResponse, CalcPowRequest,
-    CalcPowResponse, FlushCacheRequest, FlushCacheResponse, FlushTransactionPoolRequest,
-    FlushTransactionPoolResponse, GenerateBlocksRequest, GenerateBlocksResponse,
-    GetAlternateChainsRequest, GetAlternateChainsResponse, GetBansRequest, GetBansResponse,
-    GetBlockCountRequest, GetBlockCountResponse, GetBlockHeaderByHashRequest,
-    GetBlockHeaderByHashResponse, GetBlockHeaderByHeightRequest, GetBlockHeaderByHeightResponse,
-    GetBlockHeadersRangeRequest, GetBlockHeadersRangeResponse, GetBlockRequest, GetBlockResponse,
-    GetCoinbaseTxSumRequest, GetCoinbaseTxSumResponse, GetConnectionsRequest,
-    GetConnectionsResponse, GetFeeEstimateRequest, GetFeeEstimateResponse, GetInfoRequest,
-    GetInfoResponse, GetLastBlockHeaderRequest, GetLastBlockHeaderResponse, GetMinerDataRequest,
-    GetMinerDataResponse, GetOutputHistogramRequest, GetOutputHistogramResponse,
-    GetTransactionPoolBacklogRequest, GetTransactionPoolBacklogResponse, GetTxIdsLooseRequest,
-    GetTxIdsLooseResponse, GetVersionRequest, GetVersionResponse, HardForkInfoRequest,
-    HardForkInfoResponse, JsonRpcRequest, JsonRpcResponse, OnGetBlockHashRequest,
-    OnGetBlockHashResponse, PruneBlockchainRequest, PruneBlockchainResponse, RelayTxRequest,
-    RelayTxResponse, SetBansRequest, SetBansResponse, SubmitBlockRequest, SubmitBlockResponse,
-    SyncInfoRequest, SyncInfoResponse,
+use cuprate_consensus::{BlockchainReadRequest, BlockchainResponse};
+use cuprate_constants::{
+    build::RELEASE,
+    rpc::{BLOCK_SIZE_SANITY_LEEWAY, RESTRICTED_BLOCK_COUNT, RESTRICTED_BLOCK_HEADER_RANGE},
+};
+use cuprate_helper::cast::u64_to_usize;
+use cuprate_rpc_types::{
+    base::{AccessResponseBase, ResponseBase},
+    json::{
+        AddAuxPowRequest, AddAuxPowResponse, BannedRequest, BannedResponse, CalcPowRequest,
+        CalcPowResponse, FlushCacheRequest, FlushCacheResponse, FlushTransactionPoolRequest,
+        FlushTransactionPoolResponse, GenerateBlocksRequest, GenerateBlocksResponse,
+        GetAlternateChainsRequest, GetAlternateChainsResponse, GetBansRequest, GetBansResponse,
+        GetBlockCountRequest, GetBlockCountResponse, GetBlockHeaderByHashRequest,
+        GetBlockHeaderByHashResponse, GetBlockHeaderByHeightRequest,
+        GetBlockHeaderByHeightResponse, GetBlockHeadersRangeRequest, GetBlockHeadersRangeResponse,
+        GetBlockRequest, GetBlockResponse, GetCoinbaseTxSumRequest, GetCoinbaseTxSumResponse,
+        GetConnectionsRequest, GetConnectionsResponse, GetFeeEstimateRequest,
+        GetFeeEstimateResponse, GetInfoRequest, GetInfoResponse, GetLastBlockHeaderRequest,
+        GetLastBlockHeaderResponse, GetMinerDataRequest, GetMinerDataResponse,
+        GetOutputHistogramRequest, GetOutputHistogramResponse, GetTransactionPoolBacklogRequest,
+        GetTransactionPoolBacklogResponse, GetTxIdsLooseRequest, GetTxIdsLooseResponse,
+        GetVersionRequest, GetVersionResponse, HardForkInfoRequest, HardForkInfoResponse,
+        JsonRpcRequest, JsonRpcResponse, OnGetBlockHashRequest, OnGetBlockHashResponse,
+        PruneBlockchainRequest, PruneBlockchainResponse, RelayTxRequest, RelayTxResponse,
+        SetBansRequest, SetBansResponse, SubmitBlockRequest, SubmitBlockResponse, SyncInfoRequest,
+        SyncInfoResponse,
+    },
+    misc::{BlockHeader, Status},
+    CORE_RPC_VERSION,
 };
 
-use crate::rpc::CupratedRpcHandler;
+use crate::rpc::{helper, request::blockchain, CupratedRpcHandler};
 
 /// Map a [`JsonRpcRequest`] to the function that will lead to a [`JsonRpcResponse`].
 pub(super) async fn map_request(
@@ -83,212 +97,518 @@ pub(super) async fn map_request(
     })
 }
 
+/// <https://github.com/monero-project/monero/blob/cc73fe71162d564ffda8e549b79a350bca53c454/src/rpc/core_rpc_server.cpp#L1790-L1804>
 async fn get_block_count(
-    state: CupratedRpcHandler,
+    mut state: CupratedRpcHandler,
     request: GetBlockCountRequest,
 ) -> Result<GetBlockCountResponse, Error> {
-    todo!()
+    Ok(GetBlockCountResponse {
+        base: ResponseBase::ok(),
+        count: helper::top_height(&mut state).await?.0,
+    })
 }
 
+/// <https://github.com/monero-project/monero/blob/cc73fe71162d564ffda8e549b79a350bca53c454/src/rpc/core_rpc_server.cpp#L1806-L1831>
 async fn on_get_block_hash(
-    state: CupratedRpcHandler,
+    mut state: CupratedRpcHandler,
     request: OnGetBlockHashRequest,
 ) -> Result<OnGetBlockHashResponse, Error> {
-    todo!()
+    let [height] = request.block_height;
+    let hash = blockchain::block_hash(&mut state, height).await?;
+    let block_hash = hex::encode(hash);
+
+    Ok(OnGetBlockHashResponse { block_hash })
 }
 
+/// <https://github.com/monero-project/monero/blob/cc73fe71162d564ffda8e549b79a350bca53c454/src/rpc/core_rpc_server.cpp#L2209-L2266>
 async fn submit_block(
-    state: CupratedRpcHandler,
+    mut state: CupratedRpcHandler,
     request: SubmitBlockRequest,
 ) -> Result<SubmitBlockResponse, Error> {
-    todo!()
+    let [blob] = request.block_blob;
+
+    let limit = blockchain::cumulative_block_weight_limit(&mut state).await?;
+
+    if blob.len() > limit + BLOCK_SIZE_SANITY_LEEWAY {
+        return Err(anyhow!("Block size is too big, rejecting block"));
+    }
+
+    let bytes = hex::decode(blob)?;
+    let block = Block::read(&mut bytes.as_slice())?;
+
+    // <https://github.com/monero-project/monero/blob/master/src/cryptonote_core/cryptonote_core.cpp#L1540>
+    let block_id = todo!("submit block to DB");
+    todo!("relay to P2P");
+    todo!("send to txpool");
+
+    Ok(SubmitBlockResponse {
+        base: ResponseBase::ok(),
+        block_id,
+    })
 }
 
+/// <https://github.com/monero-project/monero/blob/cc73fe71162d564ffda8e549b79a350bca53c454/src/rpc/core_rpc_server.cpp#L2268-L2340>
 async fn generate_blocks(
     state: CupratedRpcHandler,
     request: GenerateBlocksRequest,
 ) -> Result<GenerateBlocksResponse, Error> {
-    todo!()
+    Ok(GenerateBlocksResponse {
+        base: ResponseBase::ok(),
+        blocks: todo!(),
+        height: todo!(),
+    })
 }
 
+/// <https://github.com/monero-project/monero/blob/cc73fe71162d564ffda8e549b79a350bca53c454/src/rpc/core_rpc_server.cpp#L2468-L2498>
 async fn get_last_block_header(
-    state: CupratedRpcHandler,
+    mut state: CupratedRpcHandler,
     request: GetLastBlockHeaderRequest,
 ) -> Result<GetLastBlockHeaderResponse, Error> {
-    todo!()
+    let (height, _) = helper::top_height(&mut state).await?;
+    let block_header = helper::block_header(&mut state, height, request.fill_pow_hash).await?;
+
+    Ok(GetLastBlockHeaderResponse {
+        base: AccessResponseBase::ok(),
+        block_header,
+    })
 }
 
+/// <https://github.com/monero-project/monero/blob/cc73fe71162d564ffda8e549b79a350bca53c454/src/rpc/core_rpc_server.cpp#L2468-L2498>
 async fn get_block_header_by_hash(
-    state: CupratedRpcHandler,
+    mut state: CupratedRpcHandler,
     request: GetBlockHeaderByHashRequest,
 ) -> Result<GetBlockHeaderByHashResponse, Error> {
-    todo!()
+    if state.restricted && request.hashes.len() > RESTRICTED_BLOCK_COUNT {
+        return Err(anyhow!(
+            "Too many block headers requested in restricted mode"
+        ));
+    }
+
+    async fn get(
+        state: &mut CupratedRpcHandler,
+        hex: String,
+        fill_pow_hash: bool,
+    ) -> Result<BlockHeader, Error> {
+        let hash = helper::hex_to_hash(hex)?;
+        let block_header = helper::block_header_by_hash(state, hash, fill_pow_hash).await?;
+        Ok(block_header)
+    }
+
+    let block_header = get(&mut state, request.hash, request.fill_pow_hash).await?;
+
+    // FIXME PERF: could make a `Vec` on await on all tasks at the same time.
+    let mut block_headers = Vec::with_capacity(request.hashes.len());
+    for hash in request.hashes {
+        let hash = get(&mut state, hash, request.fill_pow_hash).await?;
+        block_headers.push(hash);
+    }
+
+    Ok(GetBlockHeaderByHashResponse {
+        base: AccessResponseBase::ok(),
+        block_header,
+        block_headers,
+    })
 }
 
+/// <https://github.com/monero-project/monero/blob/cc73fe71162d564ffda8e549b79a350bca53c454/src/rpc/core_rpc_server.cpp#L2629-L2662>
 async fn get_block_header_by_height(
-    state: CupratedRpcHandler,
+    mut state: CupratedRpcHandler,
     request: GetBlockHeaderByHeightRequest,
 ) -> Result<GetBlockHeaderByHeightResponse, Error> {
-    todo!()
+    helper::check_height(&mut state, request.height).await?;
+    let block_header =
+        helper::block_header(&mut state, request.height, request.fill_pow_hash).await?;
+
+    Ok(GetBlockHeaderByHeightResponse {
+        base: AccessResponseBase::ok(),
+        block_header,
+    })
 }
 
+/// <https://github.com/monero-project/monero/blob/cc73fe71162d564ffda8e549b79a350bca53c454/src/rpc/core_rpc_server.cpp#L2569-L2627>
 async fn get_block_headers_range(
-    state: CupratedRpcHandler,
+    mut state: CupratedRpcHandler,
     request: GetBlockHeadersRangeRequest,
 ) -> Result<GetBlockHeadersRangeResponse, Error> {
-    todo!()
+    let (top_height, _) = helper::top_height(&mut state).await?;
+
+    if request.start_height >= top_height
+        || request.end_height >= top_height
+        || request.start_height > request.end_height
+    {
+        return Err(anyhow!("Invalid start/end heights"));
+    }
+
+    if state.restricted
+        && request.end_height.saturating_sub(request.start_height) + 1
+            > RESTRICTED_BLOCK_HEADER_RANGE
+    {
+        return Err(anyhow!("Too many block headers requested."));
+    }
+
+    let block_len = u64_to_usize(request.end_height.saturating_sub(request.start_height));
+    let mut tasks = Vec::with_capacity(block_len);
+    let mut headers = Vec::with_capacity(block_len);
+
+    {
+        let ready = state.blockchain_read.ready().await?;
+        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);
+        }
+    }
+
+    for task in tasks {
+        let BlockchainResponse::Block(header) = task.await?? else {
+            unreachable!();
+        };
+        // headers.push((&header).into());
+        headers.push(todo!());
+    }
+
+    Ok(GetBlockHeadersRangeResponse {
+        base: AccessResponseBase::ok(),
+        headers,
+    })
 }
 
+/// <https://github.com/monero-project/monero/blob/cc73fe71162d564ffda8e549b79a350bca53c454/src/rpc/core_rpc_server.cpp#L2664-L2727>
 async fn get_block(
-    state: CupratedRpcHandler,
+    mut state: CupratedRpcHandler,
     request: GetBlockRequest,
 ) -> Result<GetBlockResponse, Error> {
-    todo!()
+    let block = if request.hash.is_empty() {
+        helper::check_height(&mut state, request.height).await?;
+        blockchain::block(&mut state, request.height).await?
+    } else {
+        let hash = helper::hex_to_hash(request.hash)?;
+        blockchain::block_by_hash(&mut state, hash).await?
+    };
+
+    Ok(todo!())
+
+    // let block_header = (&block).into();
+    // let blob = hex::encode(block.block_blob);
+    // let miner_tx_hash = hex::encode(block.block.miner_transaction.hash());
+    // let tx_hashes = block
+    //     .txs
+    //     .into_iter()
+    //     .map(|tx| hex::encode(tx.tx_hash))
+    //     .collect();
+
+    // Ok(GetBlockResponse {
+    //     base: AccessResponseBase::ok(),
+    //     blob,
+    //     json: todo!(), // TODO: make `JSON` type in `cuprate_rpc_types`
+    //     miner_tx_hash,
+    //     tx_hashes,
+    //     block_header,
+    // })
 }
 
+/// <https://github.com/monero-project/monero/blob/cc73fe71162d564ffda8e549b79a350bca53c454/src/rpc/core_rpc_server.cpp#L2729-L2738>
 async fn get_connections(
     state: CupratedRpcHandler,
     request: GetConnectionsRequest,
 ) -> Result<GetConnectionsResponse, Error> {
-    todo!()
+    Ok(GetConnectionsResponse {
+        base: ResponseBase::ok(),
+        connections: todo!(),
+    })
 }
 
+/// <https://github.com/monero-project/monero/blob/cc73fe71162d564ffda8e549b79a350bca53c454/src/rpc/core_rpc_server.cpp#L501-L582>
 async fn get_info(
     state: CupratedRpcHandler,
     request: GetInfoRequest,
 ) -> Result<GetInfoResponse, Error> {
-    todo!()
+    Ok(GetInfoResponse {
+        base: AccessResponseBase::ok(),
+        adjusted_time: todo!(),
+        alt_blocks_count: todo!(),
+        block_size_limit: todo!(),
+        block_size_median: todo!(),
+        block_weight_limit: todo!(),
+        block_weight_median: todo!(),
+        bootstrap_daemon_address: todo!(),
+        busy_syncing: todo!(),
+        cumulative_difficulty_top64: todo!(),
+        cumulative_difficulty: todo!(),
+        database_size: todo!(),
+        difficulty_top64: todo!(),
+        difficulty: todo!(),
+        free_space: todo!(),
+        grey_peerlist_size: todo!(),
+        height: todo!(),
+        height_without_bootstrap: todo!(),
+        incoming_connections_count: todo!(),
+        mainnet: todo!(),
+        nettype: todo!(),
+        offline: todo!(),
+        outgoing_connections_count: todo!(),
+        restricted: todo!(),
+        rpc_connections_count: todo!(),
+        stagenet: todo!(),
+        start_time: todo!(),
+        synchronized: todo!(),
+        target_height: todo!(),
+        target: todo!(),
+        testnet: todo!(),
+        top_block_hash: todo!(),
+        tx_count: todo!(),
+        tx_pool_size: todo!(),
+        update_available: todo!(),
+        version: todo!(),
+        was_bootstrap_ever_used: todo!(),
+        white_peerlist_size: todo!(),
+        wide_cumulative_difficulty: todo!(),
+        wide_difficulty: todo!(),
+    })
 }
 
+/// <https://github.com/monero-project/monero/blob/cc73fe71162d564ffda8e549b79a350bca53c454/src/rpc/core_rpc_server.cpp#L2751-L2766>
 async fn hard_fork_info(
-    state: CupratedRpcHandler,
+    mut state: CupratedRpcHandler,
     request: HardForkInfoRequest,
 ) -> Result<HardForkInfoResponse, Error> {
-    todo!()
+    let hard_fork = if request.version > 0 {
+        HardFork::from_version(request.version)?
+    } else {
+        blockchain::current_hard_fork(&mut state).await?
+    };
+
+    Ok(HardForkInfoResponse {
+        base: AccessResponseBase::ok(),
+        earliest_height: todo!(),
+        enabled: hard_fork.is_current(),
+        state: todo!(),
+        threshold: todo!(),
+        version: hard_fork.as_u8(),
+        votes: todo!(),
+        voting: todo!(),
+        window: todo!(),
+    })
 }
 
+/// <https://github.com/monero-project/monero/blob/cc73fe71162d564ffda8e549b79a350bca53c454/src/rpc/core_rpc_server.cpp#L2832-L2878>
 async fn set_bans(
     state: CupratedRpcHandler,
     request: SetBansRequest,
 ) -> Result<SetBansResponse, Error> {
-    todo!()
+    todo!();
+
+    Ok(SetBansResponse {
+        base: ResponseBase::ok(),
+    })
 }
 
+/// <https://github.com/monero-project/monero/blob/cc73fe71162d564ffda8e549b79a350bca53c454/src/rpc/core_rpc_server.cpp#L2768-L2801>
 async fn get_bans(
     state: CupratedRpcHandler,
     request: GetBansRequest,
 ) -> Result<GetBansResponse, Error> {
-    todo!()
+    Ok(GetBansResponse {
+        base: ResponseBase::ok(),
+        bans: todo!(),
+    })
 }
 
+/// <https://github.com/monero-project/monero/blob/cc73fe71162d564ffda8e549b79a350bca53c454/src/rpc/core_rpc_server.cpp#L2803-L2830>
 async fn banned(
     state: CupratedRpcHandler,
     request: BannedRequest,
 ) -> Result<BannedResponse, Error> {
-    todo!()
+    Ok(BannedResponse {
+        banned: todo!(),
+        seconds: todo!(),
+        status: todo!(),
+    })
 }
 
+/// <https://github.com/monero-project/monero/blob/cc73fe71162d564ffda8e549b79a350bca53c454/src/rpc/core_rpc_server.cpp#L2880-L2932>
 async fn flush_transaction_pool(
     state: CupratedRpcHandler,
     request: FlushTransactionPoolRequest,
 ) -> Result<FlushTransactionPoolResponse, Error> {
-    todo!()
+    todo!();
+    Ok(FlushTransactionPoolResponse { status: Status::Ok })
 }
 
+/// <https://github.com/monero-project/monero/blob/cc73fe71162d564ffda8e549b79a350bca53c454/src/rpc/core_rpc_server.cpp#L2934-L2979>
 async fn get_output_histogram(
     state: CupratedRpcHandler,
     request: GetOutputHistogramRequest,
 ) -> Result<GetOutputHistogramResponse, Error> {
-    todo!()
+    Ok(GetOutputHistogramResponse {
+        base: AccessResponseBase::ok(),
+        histogram: todo!(),
+    })
 }
 
+/// <https://github.com/monero-project/monero/blob/cc73fe71162d564ffda8e549b79a350bca53c454/src/rpc/core_rpc_server.cpp#L2998-L3013>
 async fn get_coinbase_tx_sum(
     state: CupratedRpcHandler,
     request: GetCoinbaseTxSumRequest,
 ) -> Result<GetCoinbaseTxSumResponse, Error> {
-    todo!()
+    Ok(GetCoinbaseTxSumResponse {
+        base: AccessResponseBase::ok(),
+        emission_amount: todo!(),
+        emission_amount_top64: todo!(),
+        fee_amount: todo!(),
+        fee_amount_top64: todo!(),
+        wide_emission_amount: todo!(),
+        wide_fee_amount: todo!(),
+    })
 }
 
+/// <https://github.com/monero-project/monero/blob/cc73fe71162d564ffda8e549b79a350bca53c454/src/rpc/core_rpc_server.cpp#L2981-L2996>
 async fn get_version(
-    state: CupratedRpcHandler,
+    mut state: CupratedRpcHandler,
     request: GetVersionRequest,
 ) -> Result<GetVersionResponse, Error> {
-    todo!()
+    Ok(GetVersionResponse {
+        base: ResponseBase::ok(),
+        version: CORE_RPC_VERSION,
+        release: RELEASE,
+        current_height: helper::top_height(&mut state).await?.0,
+        target_height: todo!(),
+        hard_forks: todo!(),
+    })
 }
 
+/// <https://github.com/monero-project/monero/blob/cc73fe71162d564ffda8e549b79a350bca53c454/src/rpc/core_rpc_server.cpp#L3015-L3031>
 async fn get_fee_estimate(
     state: CupratedRpcHandler,
     request: GetFeeEstimateRequest,
 ) -> Result<GetFeeEstimateResponse, Error> {
-    todo!()
+    Ok(GetFeeEstimateResponse {
+        base: AccessResponseBase::ok(),
+        fee: todo!(),
+        fees: todo!(),
+        quantization_mask: todo!(),
+    })
 }
 
+/// <https://github.com/monero-project/monero/blob/cc73fe71162d564ffda8e549b79a350bca53c454/src/rpc/core_rpc_server.cpp#L3033-L3064>
 async fn get_alternate_chains(
     state: CupratedRpcHandler,
     request: GetAlternateChainsRequest,
 ) -> Result<GetAlternateChainsResponse, Error> {
-    todo!()
+    Ok(GetAlternateChainsResponse {
+        base: ResponseBase::ok(),
+        chains: todo!(),
+    })
 }
 
+/// <https://github.com/monero-project/monero/blob/cc73fe71162d564ffda8e549b79a350bca53c454/src/rpc/core_rpc_server.cpp#L3254-L3304>
 async fn relay_tx(
     state: CupratedRpcHandler,
     request: RelayTxRequest,
 ) -> Result<RelayTxResponse, Error> {
-    todo!()
+    todo!();
+    Ok(RelayTxResponse { status: Status::Ok })
 }
 
+/// <https://github.com/monero-project/monero/blob/cc73fe71162d564ffda8e549b79a350bca53c454/src/rpc/core_rpc_server.cpp#L3306-L3330>
 async fn sync_info(
     state: CupratedRpcHandler,
     request: SyncInfoRequest,
 ) -> Result<SyncInfoResponse, Error> {
-    todo!()
+    Ok(SyncInfoResponse {
+        base: AccessResponseBase::ok(),
+        height: todo!(),
+        next_needed_pruning_seed: todo!(),
+        overview: todo!(),
+        peers: todo!(),
+        spans: todo!(),
+        target_height: todo!(),
+    })
 }
 
+/// <https://github.com/monero-project/monero/blob/cc73fe71162d564ffda8e549b79a350bca53c454/src/rpc/core_rpc_server.cpp#L3332-L3350>
 async fn get_transaction_pool_backlog(
     state: CupratedRpcHandler,
     request: GetTransactionPoolBacklogRequest,
 ) -> Result<GetTransactionPoolBacklogResponse, Error> {
-    todo!()
+    Ok(GetTransactionPoolBacklogResponse {
+        base: ResponseBase::ok(),
+        backlog: todo!(),
+    })
 }
 
+/// <https://github.com/monero-project/monero/blob/cc73fe71162d564ffda8e549b79a350bca53c454/src/rpc/core_rpc_server.cpp#L1998-L2033>
 async fn get_miner_data(
     state: CupratedRpcHandler,
     request: GetMinerDataRequest,
 ) -> Result<GetMinerDataResponse, Error> {
-    todo!()
+    Ok(GetMinerDataResponse {
+        base: ResponseBase::ok(),
+        major_version: todo!(),
+        height: todo!(),
+        prev_id: todo!(),
+        seed_hash: todo!(),
+        difficulty: todo!(),
+        median_weight: todo!(),
+        already_generated_coins: todo!(),
+        tx_backlog: todo!(),
+    })
 }
 
+/// <https://github.com/monero-project/monero/blob/cc73fe71162d564ffda8e549b79a350bca53c454/src/rpc/core_rpc_server.cpp#L3453-L3476>
 async fn prune_blockchain(
     state: CupratedRpcHandler,
     request: PruneBlockchainRequest,
 ) -> Result<PruneBlockchainResponse, Error> {
-    todo!()
+    Ok(PruneBlockchainResponse {
+        base: ResponseBase::ok(),
+        pruned: todo!(),
+        pruning_seed: todo!(),
+    })
 }
 
+/// <https://github.com/monero-project/monero/blob/cc73fe71162d564ffda8e549b79a350bca53c454/src/rpc/core_rpc_server.cpp#L2035-L2070>
 async fn calc_pow(
     state: CupratedRpcHandler,
     request: CalcPowRequest,
 ) -> Result<CalcPowResponse, Error> {
-    todo!()
+    Ok(CalcPowResponse { pow_hash: todo!() })
 }
 
+/// <https://github.com/monero-project/monero/blob/cc73fe71162d564ffda8e549b79a350bca53c454/src/rpc/core_rpc_server.cpp#L3542-L3551>
 async fn flush_cache(
     state: CupratedRpcHandler,
     request: FlushCacheRequest,
 ) -> Result<FlushCacheResponse, Error> {
-    todo!()
+    todo!();
+
+    Ok(FlushCacheResponse {
+        base: ResponseBase::ok(),
+    })
 }
 
+/// <https://github.com/monero-project/monero/blob/cc73fe71162d564ffda8e549b79a350bca53c454/src/rpc/core_rpc_server.cpp#L2072-L2207>
 async fn add_aux_pow(
     state: CupratedRpcHandler,
     request: AddAuxPowRequest,
 ) -> Result<AddAuxPowResponse, Error> {
-    todo!()
+    Ok(AddAuxPowResponse {
+        base: ResponseBase::ok(),
+        blocktemplate_blob: todo!(),
+        blockhashing_blob: todo!(),
+        merkle_root: todo!(),
+        merkle_tree_depth: todo!(),
+        aux_pow: todo!(),
+    })
 }
 
+/// <https://github.com/monero-project/monero/blob/cc73fe71162d564ffda8e549b79a350bca53c454/src/rpc/core_rpc_server.cpp#L3553-L3627>
 async fn get_tx_ids_loose(
     state: CupratedRpcHandler,
     request: GetTxIdsLooseRequest,
 ) -> Result<GetTxIdsLooseResponse, Error> {
-    todo!()
+    Ok(GetTxIdsLooseResponse {
+        base: ResponseBase::ok(),
+        txids: todo!(),
+    })
 }
diff --git a/binaries/cuprated/src/rpc/request.rs b/binaries/cuprated/src/rpc/request.rs
index 17e12b95..d1d9a21c 100644
--- a/binaries/cuprated/src/rpc/request.rs
+++ b/binaries/cuprated/src/rpc/request.rs
@@ -12,8 +12,8 @@
 //! the [`blockchain`] modules contains methods for the
 //! blockchain database [`tower::Service`] API.
 
-mod address_book;
-mod blockchain;
-mod blockchain_context;
-mod blockchain_manager;
-mod txpool;
+pub(super) mod address_book;
+pub(super) mod blockchain;
+pub(super) mod blockchain_context;
+pub(super) mod blockchain_manager;
+pub(super) mod txpool;
diff --git a/binaries/cuprated/src/rpc/request/blockchain.rs b/binaries/cuprated/src/rpc/request/blockchain.rs
index 8af80e50..7705320f 100644
--- a/binaries/cuprated/src/rpc/request/blockchain.rs
+++ b/binaries/cuprated/src/rpc/request/blockchain.rs
@@ -6,9 +6,10 @@ use std::{
 };
 
 use anyhow::Error;
-use cuprate_blockchain::service::BlockchainReadHandle;
+use monero_serai::block::Block;
 use tower::{Service, ServiceExt};
 
+use cuprate_blockchain::service::BlockchainReadHandle;
 use cuprate_helper::cast::{u64_to_usize, usize_to_u64};
 use cuprate_types::{
     blockchain::{BlockchainReadRequest, BlockchainResponse},
@@ -16,9 +17,45 @@ use cuprate_types::{
     OutputHistogramInput, OutputOnChain,
 };
 
+/// [`BlockchainReadRequest::Block`].
+pub(crate) async fn block(
+    blockchain_read: &mut BlockchainReadHandle,
+    height: u64,
+) -> Result<Block, Error> {
+    let BlockchainResponse::Block(block) = blockchain_read
+        .ready()
+        .await?
+        .call(BlockchainReadRequest::Block {
+            height: u64_to_usize(height),
+        })
+        .await?
+    else {
+        unreachable!();
+    };
+
+    Ok(block)
+}
+
+/// [`BlockchainReadRequest::BlockByHash`].
+pub(crate) async fn block_by_hash(
+    blockchain_read: &mut BlockchainReadHandle,
+    hash: [u8; 32],
+) -> Result<Block, Error> {
+    let BlockchainResponse::Block(block) = blockchain_read
+        .ready()
+        .await?
+        .call(BlockchainReadRequest::BlockByHash(hash))
+        .await?
+    else {
+        unreachable!();
+    };
+
+    Ok(block)
+}
+
 /// [`BlockchainReadRequest::BlockExtendedHeader`].
-pub(super) async fn block_extended_header(
-    mut blockchain_read: BlockchainReadHandle,
+pub(crate) async fn block_extended_header(
+    blockchain_read: &mut BlockchainReadHandle,
     height: u64,
 ) -> Result<ExtendedBlockHeader, Error> {
     let BlockchainResponse::BlockExtendedHeader(header) = blockchain_read
@@ -36,8 +73,8 @@ pub(super) async fn block_extended_header(
 }
 
 /// [`BlockchainReadRequest::BlockHash`].
-pub(super) async fn block_hash(
-    mut blockchain_read: BlockchainReadHandle,
+pub(crate) async fn block_hash(
+    blockchain_read: &mut BlockchainReadHandle,
     height: u64,
     chain: Chain,
 ) -> Result<[u8; 32], Error> {
@@ -57,8 +94,8 @@ pub(super) async fn block_hash(
 }
 
 /// [`BlockchainReadRequest::FindBlock`].
-pub(super) async fn find_block(
-    mut blockchain_read: BlockchainReadHandle,
+pub(crate) async fn find_block(
+    blockchain_read: &mut BlockchainReadHandle,
     block_hash: [u8; 32],
 ) -> Result<Option<(Chain, usize)>, Error> {
     let BlockchainResponse::FindBlock(option) = blockchain_read
@@ -74,8 +111,8 @@ pub(super) async fn find_block(
 }
 
 /// [`BlockchainReadRequest::FilterUnknownHashes`].
-pub(super) async fn filter_unknown_hashes(
-    mut blockchain_read: BlockchainReadHandle,
+pub(crate) async fn filter_unknown_hashes(
+    blockchain_read: &mut BlockchainReadHandle,
     block_hashes: HashSet<[u8; 32]>,
 ) -> Result<HashSet<[u8; 32]>, Error> {
     let BlockchainResponse::FilterUnknownHashes(output) = blockchain_read
@@ -91,8 +128,8 @@ pub(super) async fn filter_unknown_hashes(
 }
 
 /// [`BlockchainReadRequest::BlockExtendedHeaderInRange`]
-pub(super) async fn block_extended_header_in_range(
-    mut blockchain_read: BlockchainReadHandle,
+pub(crate) async fn block_extended_header_in_range(
+    blockchain_read: &mut BlockchainReadHandle,
     range: Range<usize>,
     chain: Chain,
 ) -> Result<Vec<ExtendedBlockHeader>, Error> {
@@ -111,8 +148,8 @@ pub(super) async fn block_extended_header_in_range(
 }
 
 /// [`BlockchainReadRequest::ChainHeight`].
-pub(super) async fn chain_height(
-    mut blockchain_read: BlockchainReadHandle,
+pub(crate) async fn chain_height(
+    blockchain_read: &mut BlockchainReadHandle,
 ) -> Result<(u64, [u8; 32]), Error> {
     let BlockchainResponse::ChainHeight(height, hash) = blockchain_read
         .ready()
@@ -127,8 +164,8 @@ pub(super) async fn chain_height(
 }
 
 /// [`BlockchainReadRequest::GeneratedCoins`].
-pub(super) async fn generated_coins(
-    mut blockchain_read: BlockchainReadHandle,
+pub(crate) async fn generated_coins(
+    blockchain_read: &mut BlockchainReadHandle,
     block_height: u64,
 ) -> Result<u64, Error> {
     let BlockchainResponse::GeneratedCoins(generated_coins) = blockchain_read
@@ -146,8 +183,8 @@ pub(super) async fn generated_coins(
 }
 
 /// [`BlockchainReadRequest::Outputs`]
-pub(super) async fn outputs(
-    mut blockchain_read: BlockchainReadHandle,
+pub(crate) async fn outputs(
+    blockchain_read: &mut BlockchainReadHandle,
     outputs: HashMap<u64, HashSet<u64>>,
 ) -> Result<HashMap<u64, HashMap<u64, OutputOnChain>>, Error> {
     let BlockchainResponse::Outputs(outputs) = blockchain_read
@@ -163,8 +200,8 @@ pub(super) async fn outputs(
 }
 
 /// [`BlockchainReadRequest::NumberOutputsWithAmount`]
-pub(super) async fn number_outputs_with_amount(
-    mut blockchain_read: BlockchainReadHandle,
+pub(crate) async fn number_outputs_with_amount(
+    blockchain_read: &mut BlockchainReadHandle,
     output_amounts: Vec<u64>,
 ) -> Result<HashMap<u64, usize>, Error> {
     let BlockchainResponse::NumberOutputsWithAmount(map) = blockchain_read
@@ -182,8 +219,8 @@ pub(super) async fn number_outputs_with_amount(
 }
 
 /// [`BlockchainReadRequest::KeyImagesSpent`]
-pub(super) async fn key_images_spent(
-    mut blockchain_read: BlockchainReadHandle,
+pub(crate) async fn key_images_spent(
+    blockchain_read: &mut BlockchainReadHandle,
     key_images: HashSet<[u8; 32]>,
 ) -> Result<bool, Error> {
     let BlockchainResponse::KeyImagesSpent(is_spent) = blockchain_read
@@ -199,8 +236,8 @@ pub(super) async fn key_images_spent(
 }
 
 /// [`BlockchainReadRequest::CompactChainHistory`]
-pub(super) async fn compact_chain_history(
-    mut blockchain_read: BlockchainReadHandle,
+pub(crate) async fn compact_chain_history(
+    blockchain_read: &mut BlockchainReadHandle,
 ) -> Result<(Vec<[u8; 32]>, u128), Error> {
     let BlockchainResponse::CompactChainHistory {
         block_ids,
@@ -218,8 +255,8 @@ pub(super) async fn compact_chain_history(
 }
 
 /// [`BlockchainReadRequest::FindFirstUnknown`]
-pub(super) async fn find_first_unknown(
-    mut blockchain_read: BlockchainReadHandle,
+pub(crate) async fn find_first_unknown(
+    blockchain_read: &mut BlockchainReadHandle,
     hashes: Vec<[u8; 32]>,
 ) -> Result<Option<(usize, u64)>, Error> {
     let BlockchainResponse::FindFirstUnknown(resp) = blockchain_read
@@ -235,8 +272,8 @@ pub(super) async fn find_first_unknown(
 }
 
 /// [`BlockchainReadRequest::TotalTxCount`]
-pub(super) async fn total_tx_count(
-    mut blockchain_read: BlockchainReadHandle,
+pub(crate) async fn total_tx_count(
+    blockchain_read: &mut BlockchainReadHandle,
 ) -> Result<u64, Error> {
     let BlockchainResponse::TotalTxCount(tx_count) = blockchain_read
         .ready()
@@ -251,8 +288,8 @@ pub(super) async fn total_tx_count(
 }
 
 /// [`BlockchainReadRequest::DatabaseSize`]
-pub(super) async fn database_size(
-    mut blockchain_read: BlockchainReadHandle,
+pub(crate) async fn database_size(
+    blockchain_read: &mut BlockchainReadHandle,
 ) -> Result<(u64, u64), Error> {
     let BlockchainResponse::DatabaseSize {
         database_size,
@@ -270,8 +307,8 @@ pub(super) async fn database_size(
 }
 
 /// [`BlockchainReadRequest::OutputHistogram`]
-pub(super) async fn output_histogram(
-    mut blockchain_read: BlockchainReadHandle,
+pub(crate) async fn output_histogram(
+    blockchain_read: &mut BlockchainReadHandle,
     input: OutputHistogramInput,
 ) -> Result<Vec<OutputHistogramEntry>, Error> {
     let BlockchainResponse::OutputHistogram(histogram) = blockchain_read
@@ -287,8 +324,8 @@ pub(super) async fn output_histogram(
 }
 
 /// [`BlockchainReadRequest::CoinbaseTxSum`]
-pub(super) async fn coinbase_tx_sum(
-    mut blockchain_read: BlockchainReadHandle,
+pub(crate) async fn coinbase_tx_sum(
+    blockchain_read: &mut BlockchainReadHandle,
     height: u64,
     count: u64,
 ) -> Result<CoinbaseTxSum, Error> {