From 92800810d91bd59fb463aa4486a1005cf5e2fc3b Mon Sep 17 00:00:00 2001 From: hinto-janai Date: Sun, 8 Sep 2024 10:52:17 -0400 Subject: [PATCH] cuprated: initial RPC module skeleton (#262) * readme * cuprated: add all workspace deps * cuprated: add lints * !! * add state, fn signatures * fixes * error signatures * interface: handle json-rpc concepts * split rpc calls into 3 `Service`s * interface: extract out to `RpcService` * fix merge * remove crate lints * use `BoxFuture` * rpc/interface: impl `thiserror::Error` * split state from main handler struct * cleanup * fix imports * replace `RpcError` with `anyhow::Error` * interface: update error * cuprated: update error type --- Cargo.lock | 78 +++++ Cargo.toml | 3 +- binaries/cuprated/Cargo.toml | 63 ++++ binaries/cuprated/README.md | 2 + binaries/cuprated/src/main.rs | 13 + binaries/cuprated/src/rpc.rs | 7 +- binaries/cuprated/src/rpc/bin.rs | 85 ++++++ binaries/cuprated/src/rpc/handler.rs | 103 +++++++ binaries/cuprated/src/rpc/json.rs | 294 +++++++++++++++++++ binaries/cuprated/src/rpc/other.rs | 260 ++++++++++++++++ binaries/cuprated/src/rpc/request_handler.rs | 1 - rpc/interface/Cargo.toml | 5 +- rpc/interface/README.md | 9 +- rpc/interface/src/lib.rs | 2 - rpc/interface/src/route/bin.rs | 19 +- rpc/interface/src/route/json_rpc.rs | 4 +- rpc/interface/src/route/other.rs | 4 +- rpc/interface/src/rpc_error.rs | 34 --- rpc/interface/src/rpc_handler.rs | 2 +- rpc/interface/src/rpc_handler_dummy.rs | 25 +- rpc/interface/src/rpc_service.rs | 12 +- 21 files changed, 956 insertions(+), 69 deletions(-) create mode 100644 binaries/cuprated/README.md create mode 100644 binaries/cuprated/src/rpc/bin.rs create mode 100644 binaries/cuprated/src/rpc/handler.rs create mode 100644 binaries/cuprated/src/rpc/json.rs create mode 100644 binaries/cuprated/src/rpc/other.rs delete mode 100644 binaries/cuprated/src/rpc/request_handler.rs delete mode 100644 rpc/interface/src/rpc_error.rs diff --git a/Cargo.lock b/Cargo.lock index 950044c..0bb4612 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -56,6 +56,12 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "038dfcf04a5feb68e9c60b21c9625a54c2c0616e79b72b0fd87075a056ae1d1b" +[[package]] +name = "anyhow" +version = "1.0.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10f00e1f6e58a40e807377c75c6a7f97bf9044fab57816f2414e6f5f4499d7b8" + [[package]] name = "async-stream" version = "0.3.5" @@ -799,6 +805,7 @@ dependencies = [ name = "cuprate-rpc-interface" version = "0.0.0" dependencies = [ + "anyhow", "axum", "cuprate-epee-encoding", "cuprate-helper", @@ -908,6 +915,68 @@ dependencies = [ [[package]] name = "cuprated" version = "0.1.0" +dependencies = [ + "anyhow", + "async-trait", + "bitflags 2.5.0", + "borsh", + "bytemuck", + "bytes", + "cfg-if", + "chrono", + "clap", + "crossbeam", + "crypto-bigint", + "cuprate-address-book", + "cuprate-async-buffer", + "cuprate-blockchain", + "cuprate-consensus", + "cuprate-consensus-rules", + "cuprate-cryptonight", + "cuprate-dandelion-tower", + "cuprate-database", + "cuprate-database-service", + "cuprate-epee-encoding", + "cuprate-fast-sync", + "cuprate-fixed-bytes", + "cuprate-helper", + "cuprate-json-rpc", + "cuprate-levin", + "cuprate-p2p", + "cuprate-p2p-core", + "cuprate-pruning", + "cuprate-rpc-interface", + "cuprate-rpc-types", + "cuprate-test-utils", + "cuprate-txpool", + "cuprate-types", + "cuprate-wire", + "curve25519-dalek", + "dashmap", + "dirs", + "futures", + "hex", + "hex-literal", + "indexmap", + "monero-serai", + "paste", + "pin-project", + "rand", + "rand_distr", + "randomx-rs", + "rayon", + "serde", + "serde_bytes", + "serde_json", + "thiserror", + "thread_local", + "tokio", + "tokio-stream", + "tokio-util", + "tower", + "tracing", + "tracing-subscriber", +] [[package]] name = "curve25519-dalek" @@ -2503,6 +2572,15 @@ dependencies = [ "serde_derive", ] +[[package]] +name = "serde_bytes" +version = "0.11.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "387cc504cb06bb40a96c8e04e951fe01854cf6bc921053c954e4a606d9675c6a" +dependencies = [ + "serde", +] + [[package]] name = "serde_derive" version = "1.0.203" diff --git a/Cargo.toml b/Cargo.toml index 0a98eab..2d71893 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -48,6 +48,7 @@ opt-level = 1 opt-level = 3 [workspace.dependencies] +anyhow = { version = "1.0.87", default-features = false } async-trait = { version = "0.1.74", default-features = false } bitflags = { version = "2.4.2", default-features = false } borsh = { version = "1.2.1", default-features = false } @@ -76,7 +77,7 @@ serde_bytes = { version = "0.11.12", default-features = false } serde_json = { version = "1.0.108", default-features = false } serde = { version = "1.0.190", default-features = false } thiserror = { version = "1.0.50", default-features = false } -thread_local = { version = "1.1.7", default-features = false } +thread_local = { version = "1.1.7", default-features = false } tokio-util = { version = "0.7.10", default-features = false } tokio-stream = { version = "0.1.14", default-features = false } tokio = { version = "1.33.0", default-features = false } diff --git a/binaries/cuprated/Cargo.toml b/binaries/cuprated/Cargo.toml index b524390..a886c12 100644 --- a/binaries/cuprated/Cargo.toml +++ b/binaries/cuprated/Cargo.toml @@ -8,6 +8,69 @@ authors = ["Boog900", "hinto-janai", "SyntheticBird45"] repository = "https://github.com/Cuprate/cuprate/tree/main/binaries/cuprated" [dependencies] +# TODO: after v1.0.0, remove unneeded dependencies. +cuprate-consensus = { path = "../../consensus" } +cuprate-fast-sync = { path = "../../consensus/fast-sync" } +cuprate-consensus-rules = { path = "../../consensus/rules" } +cuprate-cryptonight = { path = "../../cryptonight" } +cuprate-helper = { path = "../../helper" } +cuprate-epee-encoding = { path = "../../net/epee-encoding" } +cuprate-fixed-bytes = { path = "../../net/fixed-bytes" } +cuprate-levin = { path = "../../net/levin" } +cuprate-wire = { path = "../../net/wire" } +cuprate-p2p = { path = "../../p2p/p2p" } +cuprate-p2p-core = { path = "../../p2p/p2p-core" } +cuprate-dandelion-tower = { path = "../../p2p/dandelion-tower" } +cuprate-async-buffer = { path = "../../p2p/async-buffer" } +cuprate-address-book = { path = "../../p2p/address-book" } +cuprate-blockchain = { path = "../../storage/blockchain" } +cuprate-database-service = { path = "../../storage/service" } +cuprate-txpool = { path = "../../storage/txpool" } +cuprate-database = { path = "../../storage/database" } +cuprate-pruning = { path = "../../pruning" } +cuprate-test-utils = { path = "../../test-utils" } +cuprate-types = { path = "../../types" } +cuprate-json-rpc = { path = "../../rpc/json-rpc" } +cuprate-rpc-interface = { path = "../../rpc/interface" } +cuprate-rpc-types = { path = "../../rpc/types" } + +# TODO: after v1.0.0, remove unneeded dependencies. +anyhow = { workspace = true } +async-trait = { workspace = true } +bitflags = { workspace = true } +borsh = { workspace = true } +bytemuck = { workspace = true } +bytes = { workspace = true } +cfg-if = { workspace = true } +clap = { workspace = true } +chrono = { workspace = true } +crypto-bigint = { workspace = true } +crossbeam = { workspace = true } +curve25519-dalek = { workspace = true } +dashmap = { workspace = true } +dirs = { workspace = true } +futures = { workspace = true } +hex = { workspace = true } +hex-literal = { workspace = true } +indexmap = { workspace = true } +monero-serai = { workspace = true } +paste = { workspace = true } +pin-project = { workspace = true } +randomx-rs = { workspace = true } +rand = { workspace = true } +rand_distr = { workspace = true } +rayon = { workspace = true } +serde_bytes = { workspace = true } +serde_json = { workspace = true } +serde = { workspace = true } +thiserror = { workspace = true } +thread_local = { workspace = true } +tokio-util = { workspace = true } +tokio-stream = { workspace = true } +tokio = { workspace = true } +tower = { workspace = true } +tracing-subscriber = { workspace = true } +tracing = { workspace = true } [lints] workspace = true diff --git a/binaries/cuprated/README.md b/binaries/cuprated/README.md new file mode 100644 index 0000000..47f0408 --- /dev/null +++ b/binaries/cuprated/README.md @@ -0,0 +1,2 @@ +# `cuprated` +TODO diff --git a/binaries/cuprated/src/main.rs b/binaries/cuprated/src/main.rs index 918429c..76eb85e 100644 --- a/binaries/cuprated/src/main.rs +++ b/binaries/cuprated/src/main.rs @@ -1,3 +1,16 @@ +#![doc = include_str!("../README.md")] +#![cfg_attr(docsrs, feature(doc_cfg))] +#![allow( + unused_imports, + unreachable_pub, + unused_crate_dependencies, + dead_code, + unused_variables, + clippy::needless_pass_by_value, + clippy::unused_async, + reason = "TODO: remove after v1.0.0" +)] + mod blockchain; mod config; mod p2p; diff --git a/binaries/cuprated/src/rpc.rs b/binaries/cuprated/src/rpc.rs index 80b2789..9ebcd1b 100644 --- a/binaries/cuprated/src/rpc.rs +++ b/binaries/cuprated/src/rpc.rs @@ -2,4 +2,9 @@ //! //! Will contain the code to initiate the RPC and a request handler. -mod request_handler; +mod bin; +mod handler; +mod json; +mod other; + +pub use handler::{CupratedRpcHandler, CupratedRpcHandlerState}; diff --git a/binaries/cuprated/src/rpc/bin.rs b/binaries/cuprated/src/rpc/bin.rs new file mode 100644 index 0000000..60d92c1 --- /dev/null +++ b/binaries/cuprated/src/rpc/bin.rs @@ -0,0 +1,85 @@ +use anyhow::Error; + +use cuprate_rpc_types::{ + bin::{ + BinRequest, BinResponse, GetBlocksByHeightRequest, GetBlocksByHeightResponse, + GetBlocksRequest, GetBlocksResponse, GetHashesRequest, GetHashesResponse, + GetOutputIndexesRequest, GetOutputIndexesResponse, GetOutsRequest, GetOutsResponse, + GetTransactionPoolHashesRequest, GetTransactionPoolHashesResponse, + }, + json::{GetOutputDistributionRequest, GetOutputDistributionResponse}, +}; + +use crate::rpc::CupratedRpcHandlerState; + +/// Map a [`BinRequest`] to the function that will lead to a [`BinResponse`]. +pub(super) async fn map_request( + state: CupratedRpcHandlerState, + request: BinRequest, +) -> Result { + use BinRequest as Req; + use BinResponse as Resp; + + Ok(match request { + Req::GetBlocks(r) => Resp::GetBlocks(get_blocks(state, r).await?), + Req::GetBlocksByHeight(r) => Resp::GetBlocksByHeight(get_blocks_by_height(state, r).await?), + Req::GetHashes(r) => Resp::GetHashes(get_hashes(state, r).await?), + Req::GetOutputIndexes(r) => Resp::GetOutputIndexes(get_output_indexes(state, r).await?), + Req::GetOuts(r) => Resp::GetOuts(get_outs(state, r).await?), + Req::GetTransactionPoolHashes(r) => { + Resp::GetTransactionPoolHashes(get_transaction_pool_hashes(state, r).await?) + } + Req::GetOutputDistribution(r) => { + Resp::GetOutputDistribution(get_output_distribution(state, r).await?) + } + }) +} + +async fn get_blocks( + state: CupratedRpcHandlerState, + request: GetBlocksRequest, +) -> Result { + todo!() +} + +async fn get_blocks_by_height( + state: CupratedRpcHandlerState, + request: GetBlocksByHeightRequest, +) -> Result { + todo!() +} + +async fn get_hashes( + state: CupratedRpcHandlerState, + request: GetHashesRequest, +) -> Result { + todo!() +} + +async fn get_output_indexes( + state: CupratedRpcHandlerState, + request: GetOutputIndexesRequest, +) -> Result { + todo!() +} + +async fn get_outs( + state: CupratedRpcHandlerState, + request: GetOutsRequest, +) -> Result { + todo!() +} + +async fn get_transaction_pool_hashes( + state: CupratedRpcHandlerState, + request: GetTransactionPoolHashesRequest, +) -> Result { + todo!() +} + +async fn get_output_distribution( + state: CupratedRpcHandlerState, + request: GetOutputDistributionRequest, +) -> Result { + todo!() +} diff --git a/binaries/cuprated/src/rpc/handler.rs b/binaries/cuprated/src/rpc/handler.rs new file mode 100644 index 0000000..8ba25ea --- /dev/null +++ b/binaries/cuprated/src/rpc/handler.rs @@ -0,0 +1,103 @@ +//! Dummy implementation of [`RpcHandler`]. + +use std::task::{Context, Poll}; + +use anyhow::Error; +use futures::{channel::oneshot::channel, future::BoxFuture}; +use serde::{Deserialize, Serialize}; +use tower::Service; + +use cuprate_blockchain::service::BlockchainReadHandle; +use cuprate_helper::asynch::InfallibleOneshotReceiver; +use cuprate_json_rpc::Id; +use cuprate_rpc_interface::RpcHandler; +use cuprate_rpc_types::{ + bin::{BinRequest, BinResponse}, + json::{JsonRpcRequest, JsonRpcResponse}, + other::{OtherRequest, OtherResponse}, +}; +use cuprate_txpool::service::TxpoolReadHandle; + +use crate::rpc::{bin, json, other}; + +/// TODO +#[derive(Clone)] +pub struct CupratedRpcHandler { + /// Should this RPC server be [restricted](RpcHandler::restricted)? + // + // INVARIANT: + // We don't need to include this in `state` and check for + // `self.is_restricted()` because `cuprate-rpc-interface` handles that. + pub restricted: bool, + + /// State needed for request -> response mapping. + pub state: CupratedRpcHandlerState, +} + +/// TODO +#[derive(Clone)] +pub struct CupratedRpcHandlerState { + /// Read handle to the blockchain database. + pub blockchain: BlockchainReadHandle, + + /// Read handle to the transaction pool database. + pub txpool: TxpoolReadHandle, +} + +impl CupratedRpcHandler { + /// TODO + pub fn init() { + todo!() + } +} + +impl RpcHandler for CupratedRpcHandler { + fn restricted(&self) -> bool { + self.restricted + } +} + +impl Service for CupratedRpcHandler { + type Response = JsonRpcResponse; + type Error = Error; + type Future = BoxFuture<'static, Result>; + + fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll> { + Poll::Ready(Ok(())) + } + + fn call(&mut self, request: JsonRpcRequest) -> Self::Future { + let state = CupratedRpcHandlerState::clone(&self.state); + Box::pin(json::map_request(state, request)) + } +} + +impl Service for CupratedRpcHandler { + type Response = BinResponse; + type Error = Error; + type Future = BoxFuture<'static, Result>; + + fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll> { + Poll::Ready(Ok(())) + } + + fn call(&mut self, request: BinRequest) -> Self::Future { + let state = CupratedRpcHandlerState::clone(&self.state); + Box::pin(bin::map_request(state, request)) + } +} + +impl Service for CupratedRpcHandler { + type Response = OtherResponse; + type Error = Error; + type Future = BoxFuture<'static, Result>; + + fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll> { + Poll::Ready(Ok(())) + } + + fn call(&mut self, request: OtherRequest) -> Self::Future { + let state = CupratedRpcHandlerState::clone(&self.state); + Box::pin(other::map_request(state, request)) + } +} diff --git a/binaries/cuprated/src/rpc/json.rs b/binaries/cuprated/src/rpc/json.rs new file mode 100644 index 0000000..41398d4 --- /dev/null +++ b/binaries/cuprated/src/rpc/json.rs @@ -0,0 +1,294 @@ +use std::sync::Arc; + +use anyhow::Error; +use tower::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 crate::rpc::CupratedRpcHandlerState; + +/// Map a [`JsonRpcRequest`] to the function that will lead to a [`JsonRpcResponse`]. +pub(super) async fn map_request( + state: CupratedRpcHandlerState, + request: JsonRpcRequest, +) -> Result { + use JsonRpcRequest as Req; + use JsonRpcResponse as Resp; + + Ok(match request { + Req::GetBlockCount(r) => Resp::GetBlockCount(get_block_count(state, r).await?), + Req::OnGetBlockHash(r) => Resp::OnGetBlockHash(on_get_block_hash(state, r).await?), + Req::SubmitBlock(r) => Resp::SubmitBlock(submit_block(state, r).await?), + Req::GenerateBlocks(r) => Resp::GenerateBlocks(generate_blocks(state, r).await?), + Req::GetLastBlockHeader(r) => { + Resp::GetLastBlockHeader(get_last_block_header(state, r).await?) + } + Req::GetBlockHeaderByHash(r) => { + Resp::GetBlockHeaderByHash(get_block_header_by_hash(state, r).await?) + } + Req::GetBlockHeaderByHeight(r) => { + Resp::GetBlockHeaderByHeight(get_block_header_by_height(state, r).await?) + } + Req::GetBlockHeadersRange(r) => { + Resp::GetBlockHeadersRange(get_block_headers_range(state, r).await?) + } + Req::GetBlock(r) => Resp::GetBlock(get_block(state, r).await?), + Req::GetConnections(r) => Resp::GetConnections(get_connections(state, r).await?), + Req::GetInfo(r) => Resp::GetInfo(get_info(state, r).await?), + Req::HardForkInfo(r) => Resp::HardForkInfo(hard_fork_info(state, r).await?), + Req::SetBans(r) => Resp::SetBans(set_bans(state, r).await?), + Req::GetBans(r) => Resp::GetBans(get_bans(state, r).await?), + Req::Banned(r) => Resp::Banned(banned(state, r).await?), + Req::FlushTransactionPool(r) => { + Resp::FlushTransactionPool(flush_transaction_pool(state, r).await?) + } + Req::GetOutputHistogram(r) => { + Resp::GetOutputHistogram(get_output_histogram(state, r).await?) + } + Req::GetCoinbaseTxSum(r) => Resp::GetCoinbaseTxSum(get_coinbase_tx_sum(state, r).await?), + Req::GetVersion(r) => Resp::GetVersion(get_version(state, r).await?), + Req::GetFeeEstimate(r) => Resp::GetFeeEstimate(get_fee_estimate(state, r).await?), + Req::GetAlternateChains(r) => { + Resp::GetAlternateChains(get_alternate_chains(state, r).await?) + } + Req::RelayTx(r) => Resp::RelayTx(relay_tx(state, r).await?), + Req::SyncInfo(r) => Resp::SyncInfo(sync_info(state, r).await?), + Req::GetTransactionPoolBacklog(r) => { + Resp::GetTransactionPoolBacklog(get_transaction_pool_backlog(state, r).await?) + } + Req::GetMinerData(r) => Resp::GetMinerData(get_miner_data(state, r).await?), + Req::PruneBlockchain(r) => Resp::PruneBlockchain(prune_blockchain(state, r).await?), + Req::CalcPow(r) => Resp::CalcPow(calc_pow(state, r).await?), + Req::FlushCache(r) => Resp::FlushCache(flush_cache(state, r).await?), + Req::AddAuxPow(r) => Resp::AddAuxPow(add_aux_pow(state, r).await?), + Req::GetTxIdsLoose(r) => Resp::GetTxIdsLoose(get_tx_ids_loose(state, r).await?), + }) +} + +async fn get_block_count( + state: CupratedRpcHandlerState, + request: GetBlockCountRequest, +) -> Result { + todo!() +} + +async fn on_get_block_hash( + state: CupratedRpcHandlerState, + request: OnGetBlockHashRequest, +) -> Result { + todo!() +} + +async fn submit_block( + state: CupratedRpcHandlerState, + request: SubmitBlockRequest, +) -> Result { + todo!() +} + +async fn generate_blocks( + state: CupratedRpcHandlerState, + request: GenerateBlocksRequest, +) -> Result { + todo!() +} + +async fn get_last_block_header( + state: CupratedRpcHandlerState, + request: GetLastBlockHeaderRequest, +) -> Result { + todo!() +} + +async fn get_block_header_by_hash( + state: CupratedRpcHandlerState, + request: GetBlockHeaderByHashRequest, +) -> Result { + todo!() +} + +async fn get_block_header_by_height( + state: CupratedRpcHandlerState, + request: GetBlockHeaderByHeightRequest, +) -> Result { + todo!() +} + +async fn get_block_headers_range( + state: CupratedRpcHandlerState, + request: GetBlockHeadersRangeRequest, +) -> Result { + todo!() +} + +async fn get_block( + state: CupratedRpcHandlerState, + request: GetBlockRequest, +) -> Result { + todo!() +} + +async fn get_connections( + state: CupratedRpcHandlerState, + request: GetConnectionsRequest, +) -> Result { + todo!() +} + +async fn get_info( + state: CupratedRpcHandlerState, + request: GetInfoRequest, +) -> Result { + todo!() +} + +async fn hard_fork_info( + state: CupratedRpcHandlerState, + request: HardForkInfoRequest, +) -> Result { + todo!() +} + +async fn set_bans( + state: CupratedRpcHandlerState, + request: SetBansRequest, +) -> Result { + todo!() +} + +async fn get_bans( + state: CupratedRpcHandlerState, + request: GetBansRequest, +) -> Result { + todo!() +} + +async fn banned( + state: CupratedRpcHandlerState, + request: BannedRequest, +) -> Result { + todo!() +} + +async fn flush_transaction_pool( + state: CupratedRpcHandlerState, + request: FlushTransactionPoolRequest, +) -> Result { + todo!() +} + +async fn get_output_histogram( + state: CupratedRpcHandlerState, + request: GetOutputHistogramRequest, +) -> Result { + todo!() +} + +async fn get_coinbase_tx_sum( + state: CupratedRpcHandlerState, + request: GetCoinbaseTxSumRequest, +) -> Result { + todo!() +} + +async fn get_version( + state: CupratedRpcHandlerState, + request: GetVersionRequest, +) -> Result { + todo!() +} + +async fn get_fee_estimate( + state: CupratedRpcHandlerState, + request: GetFeeEstimateRequest, +) -> Result { + todo!() +} + +async fn get_alternate_chains( + state: CupratedRpcHandlerState, + request: GetAlternateChainsRequest, +) -> Result { + todo!() +} + +async fn relay_tx( + state: CupratedRpcHandlerState, + request: RelayTxRequest, +) -> Result { + todo!() +} + +async fn sync_info( + state: CupratedRpcHandlerState, + request: SyncInfoRequest, +) -> Result { + todo!() +} + +async fn get_transaction_pool_backlog( + state: CupratedRpcHandlerState, + request: GetTransactionPoolBacklogRequest, +) -> Result { + todo!() +} + +async fn get_miner_data( + state: CupratedRpcHandlerState, + request: GetMinerDataRequest, +) -> Result { + todo!() +} + +async fn prune_blockchain( + state: CupratedRpcHandlerState, + request: PruneBlockchainRequest, +) -> Result { + todo!() +} + +async fn calc_pow( + state: CupratedRpcHandlerState, + request: CalcPowRequest, +) -> Result { + todo!() +} + +async fn flush_cache( + state: CupratedRpcHandlerState, + request: FlushCacheRequest, +) -> Result { + todo!() +} + +async fn add_aux_pow( + state: CupratedRpcHandlerState, + request: AddAuxPowRequest, +) -> Result { + todo!() +} + +async fn get_tx_ids_loose( + state: CupratedRpcHandlerState, + request: GetTxIdsLooseRequest, +) -> Result { + todo!() +} diff --git a/binaries/cuprated/src/rpc/other.rs b/binaries/cuprated/src/rpc/other.rs new file mode 100644 index 0000000..c0df399 --- /dev/null +++ b/binaries/cuprated/src/rpc/other.rs @@ -0,0 +1,260 @@ +use anyhow::Error; + +use cuprate_rpc_types::other::{ + GetAltBlocksHashesRequest, GetAltBlocksHashesResponse, GetHeightRequest, GetHeightResponse, + GetLimitRequest, GetLimitResponse, GetNetStatsRequest, GetNetStatsResponse, GetOutsRequest, + GetOutsResponse, GetPeerListRequest, GetPeerListResponse, GetPublicNodesRequest, + GetPublicNodesResponse, GetTransactionPoolHashesRequest, GetTransactionPoolHashesResponse, + GetTransactionPoolRequest, GetTransactionPoolResponse, GetTransactionPoolStatsRequest, + GetTransactionPoolStatsResponse, GetTransactionsRequest, GetTransactionsResponse, + InPeersRequest, InPeersResponse, IsKeyImageSpentRequest, IsKeyImageSpentResponse, + MiningStatusRequest, MiningStatusResponse, OtherRequest, OtherResponse, OutPeersRequest, + OutPeersResponse, PopBlocksRequest, PopBlocksResponse, SaveBcRequest, SaveBcResponse, + SendRawTransactionRequest, SendRawTransactionResponse, SetBootstrapDaemonRequest, + SetBootstrapDaemonResponse, SetLimitRequest, SetLimitResponse, SetLogCategoriesRequest, + SetLogCategoriesResponse, SetLogHashRateRequest, SetLogHashRateResponse, SetLogLevelRequest, + SetLogLevelResponse, StartMiningRequest, StartMiningResponse, StopDaemonRequest, + StopDaemonResponse, StopMiningRequest, StopMiningResponse, UpdateRequest, UpdateResponse, +}; + +use crate::rpc::CupratedRpcHandlerState; + +/// Map a [`OtherRequest`] to the function that will lead to a [`OtherResponse`]. +pub(super) async fn map_request( + state: CupratedRpcHandlerState, + request: OtherRequest, +) -> Result { + use OtherRequest as Req; + use OtherResponse as Resp; + + Ok(match request { + Req::GetHeight(r) => Resp::GetHeight(get_height(state, r).await?), + Req::GetTransactions(r) => Resp::GetTransactions(get_transactions(state, r).await?), + Req::GetAltBlocksHashes(r) => { + Resp::GetAltBlocksHashes(get_alt_blocks_hashes(state, r).await?) + } + Req::IsKeyImageSpent(r) => Resp::IsKeyImageSpent(is_key_image_spent(state, r).await?), + Req::SendRawTransaction(r) => { + Resp::SendRawTransaction(send_raw_transaction(state, r).await?) + } + Req::StartMining(r) => Resp::StartMining(start_mining(state, r).await?), + Req::StopMining(r) => Resp::StopMining(stop_mining(state, r).await?), + Req::MiningStatus(r) => Resp::MiningStatus(mining_status(state, r).await?), + Req::SaveBc(r) => Resp::SaveBc(save_bc(state, r).await?), + Req::GetPeerList(r) => Resp::GetPeerList(get_peer_list(state, r).await?), + Req::SetLogHashRate(r) => Resp::SetLogHashRate(set_log_hash_rate(state, r).await?), + Req::SetLogLevel(r) => Resp::SetLogLevel(set_log_level(state, r).await?), + Req::SetLogCategories(r) => Resp::SetLogCategories(set_log_categories(state, r).await?), + Req::SetBootstrapDaemon(r) => { + Resp::SetBootstrapDaemon(set_bootstrap_daemon(state, r).await?) + } + Req::GetTransactionPool(r) => { + Resp::GetTransactionPool(get_transaction_pool(state, r).await?) + } + Req::GetTransactionPoolStats(r) => { + Resp::GetTransactionPoolStats(get_transaction_pool_stats(state, r).await?) + } + Req::StopDaemon(r) => Resp::StopDaemon(stop_daemon(state, r).await?), + Req::GetLimit(r) => Resp::GetLimit(get_limit(state, r).await?), + Req::SetLimit(r) => Resp::SetLimit(set_limit(state, r).await?), + Req::OutPeers(r) => Resp::OutPeers(out_peers(state, r).await?), + Req::InPeers(r) => Resp::InPeers(in_peers(state, r).await?), + Req::GetNetStats(r) => Resp::GetNetStats(get_net_stats(state, r).await?), + Req::GetOuts(r) => Resp::GetOuts(get_outs(state, r).await?), + Req::Update(r) => Resp::Update(update(state, r).await?), + Req::PopBlocks(r) => Resp::PopBlocks(pop_blocks(state, r).await?), + Req::GetTransactionPoolHashes(r) => { + Resp::GetTransactionPoolHashes(get_transaction_pool_hashes(state, r).await?) + } + Req::GetPublicNodes(r) => Resp::GetPublicNodes(get_public_nodes(state, r).await?), + }) +} + +async fn get_height( + state: CupratedRpcHandlerState, + request: GetHeightRequest, +) -> Result { + todo!() +} + +async fn get_transactions( + state: CupratedRpcHandlerState, + request: GetTransactionsRequest, +) -> Result { + todo!() +} + +async fn get_alt_blocks_hashes( + state: CupratedRpcHandlerState, + request: GetAltBlocksHashesRequest, +) -> Result { + todo!() +} + +async fn is_key_image_spent( + state: CupratedRpcHandlerState, + request: IsKeyImageSpentRequest, +) -> Result { + todo!() +} + +async fn send_raw_transaction( + state: CupratedRpcHandlerState, + request: SendRawTransactionRequest, +) -> Result { + todo!() +} + +async fn start_mining( + state: CupratedRpcHandlerState, + request: StartMiningRequest, +) -> Result { + todo!() +} + +async fn stop_mining( + state: CupratedRpcHandlerState, + request: StopMiningRequest, +) -> Result { + todo!() +} + +async fn mining_status( + state: CupratedRpcHandlerState, + request: MiningStatusRequest, +) -> Result { + todo!() +} + +async fn save_bc( + state: CupratedRpcHandlerState, + request: SaveBcRequest, +) -> Result { + todo!() +} + +async fn get_peer_list( + state: CupratedRpcHandlerState, + request: GetPeerListRequest, +) -> Result { + todo!() +} + +async fn set_log_hash_rate( + state: CupratedRpcHandlerState, + request: SetLogHashRateRequest, +) -> Result { + todo!() +} + +async fn set_log_level( + state: CupratedRpcHandlerState, + request: SetLogLevelRequest, +) -> Result { + todo!() +} + +async fn set_log_categories( + state: CupratedRpcHandlerState, + request: SetLogCategoriesRequest, +) -> Result { + todo!() +} + +async fn set_bootstrap_daemon( + state: CupratedRpcHandlerState, + request: SetBootstrapDaemonRequest, +) -> Result { + todo!() +} + +async fn get_transaction_pool( + state: CupratedRpcHandlerState, + request: GetTransactionPoolRequest, +) -> Result { + todo!() +} + +async fn get_transaction_pool_stats( + state: CupratedRpcHandlerState, + request: GetTransactionPoolStatsRequest, +) -> Result { + todo!() +} + +async fn stop_daemon( + state: CupratedRpcHandlerState, + request: StopDaemonRequest, +) -> Result { + todo!() +} + +async fn get_limit( + state: CupratedRpcHandlerState, + request: GetLimitRequest, +) -> Result { + todo!() +} + +async fn set_limit( + state: CupratedRpcHandlerState, + request: SetLimitRequest, +) -> Result { + todo!() +} + +async fn out_peers( + state: CupratedRpcHandlerState, + request: OutPeersRequest, +) -> Result { + todo!() +} + +async fn in_peers( + state: CupratedRpcHandlerState, + request: InPeersRequest, +) -> Result { + todo!() +} + +async fn get_net_stats( + state: CupratedRpcHandlerState, + request: GetNetStatsRequest, +) -> Result { + todo!() +} + +async fn get_outs( + state: CupratedRpcHandlerState, + request: GetOutsRequest, +) -> Result { + todo!() +} + +async fn update( + state: CupratedRpcHandlerState, + request: UpdateRequest, +) -> Result { + todo!() +} + +async fn pop_blocks( + state: CupratedRpcHandlerState, + request: PopBlocksRequest, +) -> Result { + todo!() +} + +async fn get_transaction_pool_hashes( + state: CupratedRpcHandlerState, + request: GetTransactionPoolHashesRequest, +) -> Result { + todo!() +} + +async fn get_public_nodes( + state: CupratedRpcHandlerState, + request: GetPublicNodesRequest, +) -> Result { + todo!() +} diff --git a/binaries/cuprated/src/rpc/request_handler.rs b/binaries/cuprated/src/rpc/request_handler.rs deleted file mode 100644 index 8b13789..0000000 --- a/binaries/cuprated/src/rpc/request_handler.rs +++ /dev/null @@ -1 +0,0 @@ - diff --git a/rpc/interface/Cargo.toml b/rpc/interface/Cargo.toml index 5f17317..42d1055 100644 --- a/rpc/interface/Cargo.toml +++ b/rpc/interface/Cargo.toml @@ -9,8 +9,8 @@ repository = "https://github.com/Cuprate/cuprate/tree/main/rpc/cuprate-rpc-inte keywords = ["cuprate", "rpc", "interface"] [features] -default = ["dummy", "serde"] -dummy = [] +default = ["dummy", "serde"] +dummy = [] [dependencies] cuprate-epee-encoding = { path = "../../net/epee-encoding", default-features = false } @@ -18,6 +18,7 @@ cuprate-json-rpc = { path = "../json-rpc", default-features = false } cuprate-rpc-types = { path = "../types", features = ["serde", "epee"], default-features = false } cuprate-helper = { path = "../../helper", features = ["asynch"], default-features = false } +anyhow = { workspace = true } axum = { version = "0.7.5", features = ["json"], default-features = false } serde = { workspace = true, optional = true } tower = { workspace = true } diff --git a/rpc/interface/README.md b/rpc/interface/README.md index eb87864..fa5496c 100644 --- a/rpc/interface/README.md +++ b/rpc/interface/README.md @@ -45,15 +45,16 @@ The proper usage of this crate is to: This is your [`tower::Service`] that converts `Request`s into `Response`s, i.e. the "inner handler". -Said concretely, `RpcHandler` is 3 `tower::Service`s where the request/response types are -the 3 endpoint enums from [`cuprate_rpc_types`] and the error type is from this crate: +Said concretely, `RpcHandler` is 3 `tower::Service`s where the +request/response types are the 3 endpoint enums from [`cuprate_rpc_types`]: - [`JsonRpcRequest`](cuprate_rpc_types::json::JsonRpcRequest) & [`JsonRpcResponse`](cuprate_rpc_types::json::JsonRpcResponse) - [`BinRequest`](cuprate_rpc_types::bin::BinRequest) & [`BinResponse`](cuprate_rpc_types::bin::BinRequest) - [`OtherRequest`](cuprate_rpc_types::other::OtherRequest) & [`OtherResponse`](cuprate_rpc_types::other::OtherRequest) -- [`RpcError`] `RpcHandler`'s [`Future`](std::future::Future) is generic, _although_, -it must output `Result<$RESPONSE, RpcError>`. +it must output `Result<$RESPONSE, anyhow::Error>`. + +The error type must always be [`anyhow::Error`]. The `RpcHandler` must also hold some state that is required for RPC server operation. diff --git a/rpc/interface/src/lib.rs b/rpc/interface/src/lib.rs index ebea493..1f84738 100644 --- a/rpc/interface/src/lib.rs +++ b/rpc/interface/src/lib.rs @@ -3,14 +3,12 @@ mod route; mod router_builder; -mod rpc_error; mod rpc_handler; #[cfg(feature = "dummy")] mod rpc_handler_dummy; mod rpc_service; pub use router_builder::RouterBuilder; -pub use rpc_error::RpcError; pub use rpc_handler::RpcHandler; #[cfg(feature = "dummy")] pub use rpc_handler_dummy::RpcHandlerDummy; diff --git a/rpc/interface/src/route/bin.rs b/rpc/interface/src/route/bin.rs index 45447ca..90d06c8 100644 --- a/rpc/interface/src/route/bin.rs +++ b/rpc/interface/src/route/bin.rs @@ -5,7 +5,14 @@ use axum::{body::Bytes, extract::State, http::StatusCode}; use tower::ServiceExt; use cuprate_epee_encoding::from_bytes; -use cuprate_rpc_types::bin::{BinRequest, BinResponse, GetTransactionPoolHashesRequest}; +use cuprate_rpc_types::{ + bin::{ + BinRequest, BinResponse, GetBlocksByHeightRequest, GetBlocksRequest, GetHashesRequest, + GetOutputIndexesRequest, GetOutsRequest, GetTransactionPoolHashesRequest, + }, + json::GetOutputDistributionRequest, + RpcCall, +}; use crate::rpc_handler::RpcHandler; @@ -66,8 +73,16 @@ macro_rules! generate_endpoints_inner { ($variant:ident, $handler:ident, $request:expr) => { paste::paste! { { + // Check if restricted. + if [<$variant Request>]::IS_RESTRICTED && $handler.restricted() { + // TODO: mimic `monerod` behavior. + return Err(StatusCode::FORBIDDEN); + } + // Send request. - let response = $handler.oneshot($request).await?; + let Ok(response) = $handler.oneshot($request).await else { + return Err(StatusCode::INTERNAL_SERVER_ERROR); + }; let BinResponse::$variant(response) = response else { panic!("RPC handler returned incorrect response"); diff --git a/rpc/interface/src/route/json_rpc.rs b/rpc/interface/src/route/json_rpc.rs index bf3d937..7efb851 100644 --- a/rpc/interface/src/route/json_rpc.rs +++ b/rpc/interface/src/route/json_rpc.rs @@ -50,7 +50,9 @@ pub(crate) async fn json_rpc( } // Send request. - let response = handler.oneshot(request.body).await?; + let Ok(response) = handler.oneshot(request.body).await else { + return Err(StatusCode::INTERNAL_SERVER_ERROR); + }; Ok(Json(Response::ok(id, response))) } diff --git a/rpc/interface/src/route/other.rs b/rpc/interface/src/route/other.rs index 129ddd5..3ff8448 100644 --- a/rpc/interface/src/route/other.rs +++ b/rpc/interface/src/route/other.rs @@ -82,7 +82,9 @@ macro_rules! generate_endpoints_inner { // Send request. let request = OtherRequest::$variant($request); - let response = $handler.oneshot(request).await?; + let Ok(response) = $handler.oneshot(request).await else { + return Err(StatusCode::INTERNAL_SERVER_ERROR); + }; let OtherResponse::$variant(response) = response else { panic!("RPC handler returned incorrect response") diff --git a/rpc/interface/src/rpc_error.rs b/rpc/interface/src/rpc_error.rs deleted file mode 100644 index 47563d6..0000000 --- a/rpc/interface/src/rpc_error.rs +++ /dev/null @@ -1,34 +0,0 @@ -//! RPC errors. - -//---------------------------------------------------------------------------------------------------- Import -use axum::http::StatusCode; -#[cfg(feature = "serde")] -use serde::{Deserialize, Serialize}; - -//---------------------------------------------------------------------------------------------------- RpcError -/// Possible errors during RPC operation. -/// -/// These are any errors that can happen _during_ a handler function. -/// I.e. if this error surfaces, it happened _after_ the request was -/// deserialized. -/// -/// This is the `Error` type required to be used in an [`RpcHandler`](crate::RpcHandler). -/// -/// TODO: This is empty as possible errors will be -/// enumerated when the handler functions are created. -#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] -#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))] -pub enum RpcError {} - -impl From for StatusCode { - fn from(_: RpcError) -> Self { - // TODO - Self::INTERNAL_SERVER_ERROR - } -} - -//---------------------------------------------------------------------------------------------------- Tests -#[cfg(test)] -mod test { - // use super::*; -} diff --git a/rpc/interface/src/rpc_handler.rs b/rpc/interface/src/rpc_handler.rs index 1299ec4..1d2676c 100644 --- a/rpc/interface/src/rpc_handler.rs +++ b/rpc/interface/src/rpc_handler.rs @@ -22,7 +22,7 @@ use crate::RpcService; /// In other words, an [`RpcHandler`] is a type that implements [`tower::Service`] 3 times, /// one for each request/response enum type found in [`cuprate_rpc_types`]. /// -/// The error type must always be [`RpcError`](crate::RpcError). +/// The error type must always be [`anyhow::Error`]. /// /// See this crate's `RpcHandlerDummy` for an implementation example of this trait. /// diff --git a/rpc/interface/src/rpc_handler_dummy.rs b/rpc/interface/src/rpc_handler_dummy.rs index 06fa460..0b01835 100644 --- a/rpc/interface/src/rpc_handler_dummy.rs +++ b/rpc/interface/src/rpc_handler_dummy.rs @@ -3,19 +3,20 @@ //---------------------------------------------------------------------------------------------------- Use use std::task::Poll; -use cuprate_rpc_types::{ - bin::{BinRequest, BinResponse}, - json::{JsonRpcRequest, JsonRpcResponse}, - other::{OtherRequest, OtherResponse}, -}; +use anyhow::Error; use futures::channel::oneshot::channel; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; use tower::Service; use cuprate_helper::asynch::InfallibleOneshotReceiver; +use cuprate_rpc_types::{ + bin::{BinRequest, BinResponse}, + json::{JsonRpcRequest, JsonRpcResponse}, + other::{OtherRequest, OtherResponse}, +}; -use crate::{rpc_error::RpcError, rpc_handler::RpcHandler}; +use crate::rpc_handler::RpcHandler; //---------------------------------------------------------------------------------------------------- RpcHandlerDummy /// An [`RpcHandler`] that always returns [`Default::default`]. @@ -45,8 +46,8 @@ impl RpcHandler for RpcHandlerDummy { impl Service for RpcHandlerDummy { type Response = JsonRpcResponse; - type Error = RpcError; - type Future = InfallibleOneshotReceiver>; + type Error = Error; + type Future = InfallibleOneshotReceiver>; fn poll_ready(&mut self, _: &mut std::task::Context<'_>) -> Poll> { Poll::Ready(Ok(())) @@ -100,8 +101,8 @@ impl Service for RpcHandlerDummy { impl Service for RpcHandlerDummy { type Response = BinResponse; - type Error = RpcError; - type Future = InfallibleOneshotReceiver>; + type Error = Error; + type Future = InfallibleOneshotReceiver>; fn poll_ready(&mut self, _: &mut std::task::Context<'_>) -> Poll> { Poll::Ready(Ok(())) @@ -130,8 +131,8 @@ impl Service for RpcHandlerDummy { impl Service for RpcHandlerDummy { type Response = OtherResponse; - type Error = RpcError; - type Future = InfallibleOneshotReceiver>; + type Error = Error; + type Future = InfallibleOneshotReceiver>; fn poll_ready(&mut self, _: &mut std::task::Context<'_>) -> Poll> { Poll::Ready(Ok(())) diff --git a/rpc/interface/src/rpc_service.rs b/rpc/interface/src/rpc_service.rs index db84830..285d60b 100644 --- a/rpc/interface/src/rpc_service.rs +++ b/rpc/interface/src/rpc_service.rs @@ -5,8 +5,6 @@ use std::future::Future; use tower::Service; -use crate::rpc_error::RpcError; - //---------------------------------------------------------------------------------------------------- RpcService /// An RPC [`tower::Service`]. /// @@ -17,7 +15,7 @@ use crate::rpc_error::RpcError; /// The `Request` and `Response` are generic and /// are used in the [`tower::Service`] bounds. /// -/// The error type is always [`RpcError`]. +/// The error type is always [`anyhow::Error`]. /// /// There is a blanket implementation that implements this /// trait on types that implement `tower::Service` correctly. @@ -31,8 +29,8 @@ pub trait RpcService: + Service< Request, Response = Response, - Error = RpcError, - Future: Future> + Send + Sync + 'static, + Error = anyhow::Error, + Future: Future> + Send + 'static, > { } @@ -45,8 +43,8 @@ impl RpcService for T where + Service< Request, Response = Response, - Error = RpcError, - Future: Future> + Send + Sync + 'static, + Error = anyhow::Error, + Future: Future> + Send + 'static, > { }