rpc: impl cuprate-rpc-interface (#233)
Some checks are pending
Audit / audit (push) Waiting to run
CI / fmt (push) Waiting to run
CI / typo (push) Waiting to run
CI / ci (macos-latest, stable, bash) (push) Waiting to run
CI / ci (ubuntu-latest, stable, bash) (push) Waiting to run
CI / ci (windows-latest, stable-x86_64-pc-windows-gnu, msys2 {0}) (push) Waiting to run
Deny / audit (push) Waiting to run
Doc / build (push) Waiting to run
Doc / deploy (push) Blocked by required conditions

* fixed-bytes: add `serde`, document feature flags

* fixed-bytes: add derives

* rpc: add `as _` syntax to macro

* rpc: use `ByteArrayVec` and `ContainerAsBlob` for binary types

* fixed-bytes: re-add derives

* rpc-types: dedup default value within macro

* readme: fixed bytes section

* types: custom epee - `BlockCompleteEntry`

* types: custom epee - `KeyImageSpentStatus`

* types: custom epee - `PoolInfoExtent`

* types: add `Status::Other(String)` variant

* types: custom epee - `TxEntry`, add `read_epee_field` macro

* bin: custom epee - `GetBlocks`

* types: add `serde.rs`

* misc: make `TxEntry` an `enum`, impl serde

* misc: `unimplemented!()` for `TxEntry`'s epee

* types: add `BlockCompleteEntry`

* rpc: replace `BlockCompleteEntry` with `cuprate-types`

* types: document `BlockCompleteEntry`

* bin: fix `number_of_fields` for `GetBlocksResponse`

* misc: add `Distribution`

* distribution: add todo

* misc fixes

* readme: add `(De)serialization invariants`

* distribution: compress variants

* types: add `block_complete_entry.rs`

* net: fix imports

* p2p: fix imports

* turn off default-features

* p2p: fix imports

* misc fixes

* Update net/wire/Cargo.toml

Co-authored-by: Boog900 <boog900@tutanota.com>

* distribution: module doc

* wire: re-export types

* test-utils: add `crate::rpc::types` module

* test-utils: conditional json doc-tests

* bin: use enum for `GetBlocksResponse`

* misc: use lowercase for stringify

* json: add test data, fix macro doc tests

* json: add all data

* other: add all data

* bin: add skeleton

* docs

* move type to correct file

* remove duplicated fields for custom epee

* rpc: `client/{client,constants}.rs` -> `client.rs`

* lib.rs: remove `clippy::module_inception`

* macros: add json doc test macro

* json: add some tests

* json: add doc-test for all types

* add all other JSON doc-tests

* move doc-test macros to files

* base: add doc-tests

* rpc: add `cuprate-rpc-interface` skeleton files

* traits

* json_rpc_method: add `.is_restricted()`

* add route fn signatures

* types: add rpc enums

* interface: routes, types

* interface: simplify routes

* rewrite interface fns

* types: remove `()` type alias, add `(restricted)`

* types: add `other::InPeers`

* interface: routes

* types: fix `is_restricted()`

* interface: reorder short-circuit bool

* clean up traits/bounds

* types: remove `axum` feature

* interface: cleanup unused imports

* interface: call handler in routes

* json: TODO distribution test

* interface: readme intro

* combine `RpcHandler` + `RpcService`, add `RpcDummyHandler`

* interface: readme docs + test

* `IsRestricted` -> `RpcCall`

* fix no input route problem

* interface: `RpcHandlerDummy` docs

* interface: crate docs

* replace `create_router` with `RouterBuilder`

* types: docs

* types: doc `JsonRpc{Request,Response}`

* types: readme docs

* interface: doc `route/`

* interface: fix `todo!()`

* interface: allow customizing HTTP method on route functions

* interface: fix tests

* fix derives

* Update rpc/interface/README.md

Co-authored-by: Boog900 <boog900@tutanota.com>

* Update rpc/interface/README.md

Co-authored-by: Boog900 <boog900@tutanota.com>

* interface: make `RpcHandler`'s `Future` generic

* interface: add JSON-RPC notification todo

* formatting

* interface: use associated type bound for `RpcHandler`'s `Future`

---------

Co-authored-by: Boog900 <boog900@tutanota.com>
This commit is contained in:
hinto-janai 2024-08-05 19:50:38 -04:00 committed by GitHub
parent 1a178381dd
commit 27767690ca
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
15 changed files with 1334 additions and 0 deletions

196
Cargo.lock generated
View file

@ -94,12 +94,73 @@ dependencies = [
"syn 2.0.66",
]
[[package]]
name = "atomic-waker"
version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0"
[[package]]
name = "autocfg"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0"
[[package]]
name = "axum"
version = "0.7.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3a6c9af12842a67734c9a2e355436e5d03b22383ed60cf13cd0c18fbfe3dcbcf"
dependencies = [
"async-trait",
"axum-core",
"bytes",
"futures-util",
"http",
"http-body",
"http-body-util",
"hyper",
"hyper-util",
"itoa",
"matchit",
"memchr",
"mime",
"percent-encoding",
"pin-project-lite",
"rustversion",
"serde",
"serde_json",
"serde_path_to_error",
"serde_urlencoded",
"sync_wrapper 1.0.1",
"tokio",
"tower",
"tower-layer",
"tower-service",
"tracing",
]
[[package]]
name = "axum-core"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a15c63fd72d41492dc4f497196f5da1fb04fb7529e631d73630d1b491e47a2e3"
dependencies = [
"async-trait",
"bytes",
"futures-util",
"http",
"http-body",
"http-body-util",
"mime",
"pin-project-lite",
"rustversion",
"sync_wrapper 0.1.2",
"tower-layer",
"tower-service",
"tracing",
]
[[package]]
name = "backtrace"
version = "0.3.73"
@ -374,6 +435,15 @@ dependencies = [
"libc",
]
[[package]]
name = "crc32fast"
version = "1.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3"
dependencies = [
"cfg-if",
]
[[package]]
name = "crossbeam"
version = "0.8.4"
@ -764,6 +834,20 @@ dependencies = [
[[package]]
name = "cuprate-rpc-interface"
version = "0.0.0"
dependencies = [
"axum",
"cuprate-epee-encoding",
"cuprate-helper",
"cuprate-json-rpc",
"cuprate-rpc-types",
"futures",
"paste",
"serde",
"serde_json",
"tokio",
"tower",
"ureq",
]
[[package]]
name = "cuprate-rpc-types"
@ -1030,6 +1114,16 @@ version = "0.2.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d"
[[package]]
name = "flate2"
version = "1.0.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f54427cfd1c7829e2a139fcefea601bf088ebca651d2bf53ebc600eac295dae"
dependencies = [
"crc32fast",
"miniz_oxide",
]
[[package]]
name = "flexible-transcript"
version = "0.3.2"
@ -1179,6 +1273,25 @@ dependencies = [
"subtle",
]
[[package]]
name = "h2"
version = "0.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fa82e28a107a8cc405f0839610bdc9b15f1e25ec7d696aa5cf173edbcb1486ab"
dependencies = [
"atomic-waker",
"bytes",
"fnv",
"futures-core",
"futures-sink",
"http",
"indexmap 2.2.6",
"slab",
"tokio",
"tokio-util",
"tracing",
]
[[package]]
name = "hashbrown"
version = "0.14.5"
@ -1299,6 +1412,12 @@ version = "1.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d0e7a4dd27b9476dc40cb050d3632d3bba3a70ddbff012285f7f8559a1e7e545"
[[package]]
name = "httpdate"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9"
[[package]]
name = "hyper"
version = "1.3.1"
@ -1308,9 +1427,11 @@ dependencies = [
"bytes",
"futures-channel",
"futures-util",
"h2",
"http",
"http-body",
"httparse",
"httpdate",
"itoa",
"pin-project-lite",
"smallvec",
@ -1610,6 +1731,12 @@ version = "0.4.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c"
[[package]]
name = "matchit"
version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94"
[[package]]
name = "md-5"
version = "0.10.6"
@ -1638,6 +1765,12 @@ dependencies = [
"zeroize",
]
[[package]]
name = "mime"
version = "0.3.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
[[package]]
name = "miniz_oxide"
version = "0.7.3"
@ -2204,6 +2337,7 @@ version = "0.23.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "05cff451f60db80f490f3c182b77c35260baace73209e9cdbbe526bfe3a4d402"
dependencies = [
"log",
"once_cell",
"ring",
"rustls-pki-types",
@ -2351,6 +2485,28 @@ dependencies = [
"serde",
]
[[package]]
name = "serde_path_to_error"
version = "0.1.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af99884400da37c88f5e9146b7f1fd0fbcae8f6eec4e9da38b67d05486f814a6"
dependencies = [
"itoa",
"serde",
]
[[package]]
name = "serde_urlencoded"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd"
dependencies = [
"form_urlencoded",
"itoa",
"ryu",
"serde",
]
[[package]]
name = "sha2"
version = "0.10.8"
@ -2486,6 +2642,18 @@ dependencies = [
"syn 2.0.66",
]
[[package]]
name = "sync_wrapper"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160"
[[package]]
name = "sync_wrapper"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a7065abeca94b6a8a577f9bd45aa0867a2238b74e8eb67cf10d492bc39351394"
[[package]]
name = "synchronoise"
version = "1.0.1"
@ -2706,6 +2874,7 @@ version = "0.1.40"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef"
dependencies = [
"log",
"pin-project-lite",
"tracing-attributes",
"tracing-core",
@ -2770,6 +2939,24 @@ version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
[[package]]
name = "ureq"
version = "2.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72139d247e5f97a3eff96229a7ae85ead5328a39efe76f8bf5a06313d505b6ea"
dependencies = [
"base64",
"flate2",
"log",
"once_cell",
"rustls",
"rustls-pki-types",
"serde",
"serde_json",
"url",
"webpki-roots",
]
[[package]]
name = "url"
version = "2.5.1"
@ -2877,6 +3064,15 @@ version = "0.2.92"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96"
[[package]]
name = "webpki-roots"
version = "0.26.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bd7c23921eeb1713a4e851530e9b9756e4fb0e89978582942612524cf09f01cd"
dependencies = [
"rustls-pki-types",
]
[[package]]
name = "winapi"
version = "0.3.9"

View file

@ -9,7 +9,24 @@ repository = "https://github.com/Cuprate/cuprate/tree/main/rpc/cuprate-rpc-inte
keywords = ["cuprate", "rpc", "interface"]
[features]
default = ["dummy", "serde"]
dummy = []
[dependencies]
cuprate-epee-encoding = { path = "../../net/epee-encoding", default-features = false }
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 }
axum = { version = "0.7.5", features = ["json"], default-features = false }
serde = { workspace = true, optional = true }
serde_json = { workspace = true, features = ["std"] }
tower = { workspace = true }
paste = { workspace = true }
futures = { workspace = true }
[dev-dependencies]
axum = { version = "0.7.5", features = ["json", "tokio", "http2"] }
serde_json = { workspace = true, features = ["std"] }
tokio = { workspace = true, features = ["full"] }
ureq = { version = "2.10.0", features = ["json"] }

161
rpc/interface/README.md Normal file
View file

@ -0,0 +1,161 @@
# `cuprate-rpc-interface`
This crate provides Cuprate's RPC _interface_.
This crate is _not_ a standalone RPC server, it is just the interface.
```text
cuprate-rpc-interface provides these parts
│ │
┌───────────────────────────┤ ├───────────────────┐
▼ ▼ ▼ ▼
CLIENT ─► ROUTE ─► REQUEST ─► HANDLER ─► RESPONSE ─► CLIENT
▲ ▲
└───┬───┘
You provide this part
```
Everything coming _in_ from a client is handled by this crate.
This is where your [`RpcHandler`] turns this [`RpcRequest`] into a [`RpcResponse`].
You hand this `Response` back to `cuprate-rpc-interface` and it will take care of sending it back to the client.
The main handler used by Cuprate is implemented in the `cuprate-rpc-handler` crate;
it implements the standard RPC handlers modeled after `monerod`.
# Purpose
`cuprate-rpc-interface` is built on-top of [`axum`],
which is the crate _actually_ handling everything.
This crate simply handles:
- Registering endpoint routes (e.g. `/get_block.bin`)
- Defining handler function signatures
- (De)serialization of requests/responses (JSON-RPC, binary, JSON)
The actual server details are all handled by the [`axum`] and [`tower`] ecosystem.
The proper usage of this crate is to:
1. Implement a [`RpcHandler`]
2. Use it with [`RouterBuilder`] to generate an
[`axum::Router`] with all Monero RPC routes set
3. Do whatever with it
# The [`RpcHandler`]
This is your [`tower::Service`] that converts [`RpcRequest`]s into [`RpcResponse`]s,
i.e. the "inner handler".
Said concretely, `RpcHandler` is a `tower::Service` where the associated types are from this crate:
- [`RpcRequest`]
- [`RpcResponse`]
- [`RpcError`]
`RpcHandler`'s [`Future`](std::future::Future) is generic, _although_,
it must output `Result<RpcResponse, RpcError>`.
The `RpcHandler` must also hold some state that is required
for RPC server operation.
The only state currently needed is [`RpcHandler::restricted`], which determines if an RPC
server is restricted or not, and thus, if some endpoints/methods are allowed or not.
# Unknown endpoint behavior
TODO: decide what this crate should return (per different endpoint)
when a request is received to an unknown endpoint, including HTTP stuff, e.g. status code.
# Unknown JSON-RPC method behavior
TODO: decide what this crate returns when a `/json_rpc`
request is received with an unknown method, including HTTP stuff, e.g. status code.
# Example
Example usage of this crate + starting an RPC server.
This uses `RpcHandlerDummy` as the handler; it always responds with the
correct response type, but set to a default value regardless of the request.
```rust
use std::sync::Arc;
use tokio::{net::TcpListener, sync::Barrier};
use cuprate_json_rpc::{Request, Response, Id};
use cuprate_rpc_types::{
json::{JsonRpcRequest, JsonRpcResponse, GetBlockCountResponse},
other::{OtherRequest, OtherResponse},
};
use cuprate_rpc_interface::{RouterBuilder, RpcHandlerDummy, RpcRequest};
// Send a `/get_height` request. This endpoint has no inputs.
async fn get_height(port: u16) -> OtherResponse {
let url = format!("http://127.0.0.1:{port}/get_height");
ureq::get(&url)
.set("Content-Type", "application/json")
.call()
.unwrap()
.into_json()
.unwrap()
}
// Send a JSON-RPC request with the `get_block_count` method.
//
// The returned [`String`] is JSON.
async fn get_block_count(port: u16) -> String {
let url = format!("http://127.0.0.1:{port}/json_rpc");
let method = JsonRpcRequest::GetBlockCount(Default::default());
let request = Request::new(method);
ureq::get(&url)
.set("Content-Type", "application/json")
.send_json(request)
.unwrap()
.into_string()
.unwrap()
}
#[tokio::main]
async fn main() {
// Start a local RPC server.
let port = {
// Create the router.
let state = RpcHandlerDummy { restricted: false };
let router = RouterBuilder::new().all().build().with_state(state);
// Start a server.
let listener = TcpListener::bind("127.0.0.1:0")
.await
.unwrap();
let port = listener.local_addr().unwrap().port();
// Run the server with `axum`.
tokio::task::spawn(async move {
axum::serve(listener, router).await.unwrap();
});
port
};
// Assert the response is the default.
let response = get_height(port).await;
let expected = OtherResponse::GetHeight(Default::default());
assert_eq!(response, expected);
// Assert the response JSON is correct.
let response = get_block_count(port).await;
let expected = r#"{"jsonrpc":"2.0","id":null,"result":{"status":"OK","untrusted":false,"count":0}}"#;
assert_eq!(response, expected);
// Assert that (de)serialization works.
let expected = Response::ok(Id::Null, Default::default());
let response: Response<GetBlockCountResponse> = serde_json::from_str(&response).unwrap();
assert_eq!(response, expected);
}
```
# Feature flags
List of feature flags for `cuprate-rpc-interface`.
All are enabled by default.
| Feature flag | Does what |
|--------------|-----------|
| `serde` | Enables serde on applicable types
| `dummy` | Enables the `RpcHandlerDummy` type

View file

@ -1 +1,123 @@
#![doc = include_str!("../README.md")]
#![cfg_attr(docsrs, feature(doc_cfg))]
//---------------------------------------------------------------------------------------------------- Lints
// Forbid lints.
// Our code, and code generated (e.g macros) cannot overrule these.
#![forbid(
// `unsafe` is allowed but it _must_ be
// commented with `SAFETY: reason`.
clippy::undocumented_unsafe_blocks,
// Never.
unused_unsafe,
redundant_semicolons,
unused_allocation,
coherence_leak_check,
while_true,
// Maybe can be put into `#[deny]`.
unconditional_recursion,
for_loops_over_fallibles,
unused_braces,
unused_labels,
keyword_idents,
non_ascii_idents,
variant_size_differences,
single_use_lifetimes,
// Probably can be put into `#[deny]`.
future_incompatible,
let_underscore,
break_with_label_and_loop,
duplicate_macro_attributes,
exported_private_dependencies,
large_assignments,
overlapping_range_endpoints,
semicolon_in_expressions_from_macros,
noop_method_call,
)]
// Deny lints.
// Some of these are `#[allow]`'ed on a per-case basis.
#![deny(
clippy::all,
clippy::correctness,
clippy::suspicious,
clippy::style,
clippy::complexity,
clippy::perf,
clippy::pedantic,
clippy::nursery,
clippy::cargo,
unused_doc_comments,
unused_mut,
missing_docs,
deprecated,
unused_comparisons,
nonstandard_style,
unreachable_pub
)]
#![allow(
// FIXME: this lint affects crates outside of
// `database/` for some reason, allow for now.
clippy::cargo_common_metadata,
// FIXME: adding `#[must_use]` onto everything
// might just be more annoying than useful...
// although it is sometimes nice.
clippy::must_use_candidate,
// FIXME: good lint but too many false positives
// with our `Env` + `RwLock` setup.
clippy::significant_drop_tightening,
// FIXME: good lint but is less clear in most cases.
clippy::items_after_statements,
// TODO
rustdoc::bare_urls,
clippy::module_name_repetitions,
clippy::module_inception,
clippy::redundant_pub_crate,
clippy::option_if_let_else,
)]
// Allow some lints when running in debug mode.
#![cfg_attr(
debug_assertions,
allow(
clippy::todo,
clippy::multiple_crate_versions,
unused_imports,
unused_variables
)
)]
// Allow some lints in tests.
#![cfg_attr(
test,
allow(
clippy::cognitive_complexity,
clippy::needless_pass_by_value,
clippy::cast_possible_truncation,
clippy::too_many_lines
)
)]
// TODO: remove me after finishing impl
#![allow(dead_code, unreachable_code, clippy::diverging_sub_expression)]
//---------------------------------------------------------------------------------------------------- Mod
mod route;
mod router_builder;
mod rpc_error;
mod rpc_handler;
#[cfg(feature = "dummy")]
mod rpc_handler_dummy;
mod rpc_request;
mod rpc_response;
pub use router_builder::RouterBuilder;
pub use rpc_error::RpcError;
pub use rpc_handler::RpcHandler;
#[cfg(feature = "dummy")]
pub use rpc_handler_dummy::RpcHandlerDummy;
pub use rpc_request::RpcRequest;
pub use rpc_response::RpcResponse;

View file

@ -0,0 +1,108 @@
//! Binary route functions.
//---------------------------------------------------------------------------------------------------- Import
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 crate::{rpc_handler::RpcHandler, rpc_request::RpcRequest, rpc_response::RpcResponse};
//---------------------------------------------------------------------------------------------------- Routes
/// This macro generates route functions that expect input.
///
/// See below for usage.
macro_rules! generate_endpoints_with_input {
($(
// Syntax:
// Function name => Expected input type
$endpoint:ident => $variant:ident
),*) => { paste::paste! {
$(
/// TODO
#[allow(unused_mut)]
pub(crate) async fn $endpoint<H: RpcHandler>(
State(handler): State<H>,
mut request: Bytes,
) -> Result<Bytes, StatusCode> {
// Serialize into the request type.
let request = BinRequest::$variant(
from_bytes(&mut request).map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?
);
generate_endpoints_inner!($variant, handler, request)
}
)*
}};
}
/// This macro generates route functions that expect _no_ input.
///
/// See below for usage.
macro_rules! generate_endpoints_with_no_input {
($(
// Syntax:
// Function name => Expected input type (that is empty)
$endpoint:ident => $variant:ident
),*) => { paste::paste! {
$(
/// TODO
#[allow(unused_mut)]
pub(crate) async fn $endpoint<H: RpcHandler>(
State(handler): State<H>,
) -> Result<Bytes, StatusCode> {
const REQUEST: BinRequest = BinRequest::$variant([<$variant Request>] {});
generate_endpoints_inner!($variant, handler, REQUEST)
}
)*
}};
}
/// De-duplicated inner function body for:
/// - [`generate_endpoints_with_input`]
/// - [`generate_endpoints_with_no_input`]
macro_rules! generate_endpoints_inner {
($variant:ident, $handler:ident, $request:expr) => {
paste::paste! {
{
// Send request.
let request = RpcRequest::Binary($request);
let channel = $handler.oneshot(request).await?;
// Assert the response from the inner handler is correct.
let RpcResponse::Binary(response) = channel else {
panic!("RPC handler did not return a binary response");
};
let BinResponse::$variant(response) = response else {
panic!("RPC handler returned incorrect response");
};
// Serialize to bytes and respond.
match cuprate_epee_encoding::to_bytes(response) {
Ok(bytes) => Ok(bytes.freeze()),
Err(e) => Err(StatusCode::INTERNAL_SERVER_ERROR),
}
}
}
};
}
generate_endpoints_with_input! {
get_blocks => GetBlocks,
get_blocks_by_height => GetBlocksByHeight,
get_hashes => GetHashes,
get_o_indexes => GetOutputIndexes,
get_outs => GetOuts,
get_output_distribution => GetOutputDistribution
}
generate_endpoints_with_no_input! {
get_transaction_pool_hashes => GetTransactionPoolHashes
}
//---------------------------------------------------------------------------------------------------- Tests
#[cfg(test)]
mod test {
// use super::*;
}

View file

@ -0,0 +1,18 @@
//! Fallback route functions.
//---------------------------------------------------------------------------------------------------- Import
use axum::http::StatusCode;
//---------------------------------------------------------------------------------------------------- Routes
/// Fallback route function.
///
/// This is used as the fallback endpoint in [`crate::RouterBuilder`].
pub(crate) async fn fallback() -> StatusCode {
StatusCode::NOT_FOUND
}
//---------------------------------------------------------------------------------------------------- Tests
#[cfg(test)]
mod test {
// use super::*;
}

View file

@ -0,0 +1,68 @@
//! JSON-RPC 2.0 endpoint route functions.
//---------------------------------------------------------------------------------------------------- Import
use std::borrow::Cow;
use axum::{extract::State, http::StatusCode, Json};
use tower::ServiceExt;
use cuprate_json_rpc::{
error::{ErrorCode, ErrorObject},
Id,
};
use cuprate_rpc_types::{
json::{JsonRpcRequest, JsonRpcResponse},
RpcCallValue,
};
use crate::{rpc_handler::RpcHandler, rpc_request::RpcRequest, rpc_response::RpcResponse};
//---------------------------------------------------------------------------------------------------- Routes
/// The `/json_rpc` route function used in [`crate::RouterBuilder`].
pub(crate) async fn json_rpc<H: RpcHandler>(
State(handler): State<H>,
Json(request): Json<cuprate_json_rpc::Request<JsonRpcRequest>>,
) -> Result<Json<cuprate_json_rpc::Response<JsonRpcResponse>>, StatusCode> {
// TODO: <https://www.jsonrpc.org/specification#notification>
//
// JSON-RPC notifications (requests without `id`)
// must not be responded too, although, the request's side-effects
// must remain. How to do this considering this function will
// always return and cause `axum` to respond?
// Return early if this RPC server is restricted and
// the requested method is only for non-restricted RPC.
if request.body.is_restricted() && handler.restricted() {
let error_object = ErrorObject {
code: ErrorCode::ServerError(-1 /* TODO */),
message: Cow::Borrowed("Restricted. TODO: mimic monerod message"),
data: None,
};
// JSON-RPC 2.0 rule:
// If there was an error in detecting the `Request`'s ID,
// the `Response` must contain an `Id::Null`
let id = request.id.unwrap_or(Id::Null);
let response = cuprate_json_rpc::Response::err(id, error_object);
return Ok(Json(response));
}
// Send request.
let request = RpcRequest::JsonRpc(request);
let channel = handler.oneshot(request).await?;
// Assert the response from the inner handler is correct.
let RpcResponse::JsonRpc(response) = channel else {
panic!("RPC handler returned incorrect response");
};
Ok(Json(response))
}
//---------------------------------------------------------------------------------------------------- Tests
#[cfg(test)]
mod test {
// use super::*;
}

View file

@ -0,0 +1,9 @@
//! Routing functions.
//!
//! These are the function signatures passed to
//! [`crate::RouterBuilder`] when registering routes.
pub(crate) mod bin;
pub(crate) mod fallback;
pub(crate) mod json_rpc;
pub(crate) mod other;

View file

@ -0,0 +1,138 @@
//! Other JSON endpoint route functions.
//---------------------------------------------------------------------------------------------------- Import
use axum::{extract::State, http::StatusCode, Json};
use tower::ServiceExt;
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,
},
RpcCall,
};
use crate::{rpc_handler::RpcHandler, rpc_request::RpcRequest, rpc_response::RpcResponse};
//---------------------------------------------------------------------------------------------------- Routes
/// This macro generates route functions that expect input.
///
/// See below for usage.
macro_rules! generate_endpoints_with_input {
($(
// Syntax:
// Function name => Expected input type
$endpoint:ident => $variant:ident
),*) => { paste::paste! {
$(
pub(crate) async fn $endpoint<H: RpcHandler>(
State(handler): State<H>,
Json(request): Json<[<$variant Request>]>,
) -> Result<Json<[<$variant Response>]>, StatusCode> {
generate_endpoints_inner!($variant, handler, request)
}
)*
}};
}
/// This macro generates route functions that expect _no_ input.
///
/// See below for usage.
macro_rules! generate_endpoints_with_no_input {
($(
// Syntax:
// Function name => Expected input type (that is empty)
$endpoint:ident => $variant:ident
),*) => { paste::paste! {
$(
pub(crate) async fn $endpoint<H: RpcHandler>(
State(handler): State<H>,
) -> Result<Json<[<$variant Response>]>, StatusCode> {
generate_endpoints_inner!($variant, handler, [<$variant Request>] {})
}
)*
}};
}
/// De-duplicated inner function body for:
/// - [`generate_endpoints_with_input`]
/// - [`generate_endpoints_with_no_input`]
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 request = RpcRequest::Other(OtherRequest::$variant($request));
let channel = $handler.oneshot(request).await?;
// Assert the response from the inner handler is correct.
let RpcResponse::Other(response) = channel else {
panic!("RPC handler did not return a binary response");
};
let OtherResponse::$variant(response) = response else {
panic!("RPC handler returned incorrect response")
};
Ok(Json(response))
}
}
};
}
generate_endpoints_with_input! {
get_transactions => GetTransactions,
is_key_image_spent => IsKeyImageSpent,
send_raw_transaction => SendRawTransaction,
start_mining => StartMining,
get_peer_list => GetPeerList,
set_log_hash_rate => SetLogHashRate,
set_log_level => SetLogLevel,
set_log_categories => SetLogCategories,
set_bootstrap_daemon => SetBootstrapDaemon,
set_limit => SetLimit,
out_peers => OutPeers,
in_peers => InPeers,
get_outs => GetOuts,
update => Update,
pop_blocks => PopBlocks,
get_public_nodes => GetPublicNodes
}
generate_endpoints_with_no_input! {
get_height => GetHeight,
get_alt_blocks_hashes => GetAltBlocksHashes,
stop_mining => StopMining,
mining_status => MiningStatus,
save_bc => SaveBc,
get_transaction_pool => GetTransactionPool,
get_transaction_pool_stats => GetTransactionPoolStats,
stop_daemon => StopDaemon,
get_limit => GetLimit,
get_net_stats => GetNetStats,
get_transaction_pool_hashes => GetTransactionPoolHashes
}
//---------------------------------------------------------------------------------------------------- Tests
#[cfg(test)]
mod test {
// use super::*;
}

View file

@ -0,0 +1,198 @@
//! Free functions.
use std::marker::PhantomData;
//---------------------------------------------------------------------------------------------------- Use
use axum::{
routing::{method_routing::get, post},
Router,
};
use crate::{
route::{bin, fallback, json_rpc, other},
rpc_handler::RpcHandler,
};
//---------------------------------------------------------------------------------------------------- RouterBuilder
/// Generate the `RouterBuilder` struct.
macro_rules! generate_router_builder {
($(
// Syntax:
// $BUILDER_FUNCTION_NAME =>
// $ACTUAL_ENDPOINT_STRING =>
// $ENDPOINT_FUNCTION_MODULE::$ENDPOINT_FUNCTION =>
// ($HTTP_METHOD(s))
$endpoint_ident:ident =>
$endpoint_string:literal =>
$endpoint_module:ident::$endpoint_fn:ident =>
($($http_method:ident),*)
),* $(,)?) => {
/// Builder for creating the RPC router.
///
/// This builder allows you to selectively enable endpoints for the router,
/// and a [`fallback`](RouterBuilder::fallback) route.
///
/// The [`default`](RouterBuilder::default) is to enable [`all`](RouterBuilder::all) routes.
///
/// # Routes
/// Functions that enable routes are separated into 3 groups:
/// - `json_rpc` (enables all of JSON RPC 2.0)
/// - `other_` (e.g. [`other_get_height`](RouterBuilder::other_get_height))
/// - `bin_` (e.g. [`bin_get_blocks`](RouterBuilder::bin_get_blocks))
///
/// For a list of all `monerod` routes, see
/// [here](https://github.com/monero-project/monero/blob/cc73fe71162d564ffda8e549b79a350bca53c454/src/rpc/core_rpc_server.h#L97-L189),
/// or the source file of this type.
///
/// # Aliases
/// Some routes have aliases, such as [`/get_height`](RouterBuilder::other_get_height)
/// and [`/getheight`](RouterBuilder::other_getheight).
///
/// These both route to the same handler function, but they do not enable each other.
///
/// If desired, you can enable `/get_height` but not `/getheight`.
///
/// # Example
/// ```rust
/// use cuprate_rpc_interface::{RouterBuilder, RpcHandlerDummy};
///
/// // Create a router with _only_ `/json_rpc` enabled.
/// let only_json_rpc = RouterBuilder::<RpcHandlerDummy>::new()
/// .json_rpc()
/// .build();
///
/// // Create a router with:
/// // - `/get_outs.bin` enabled
/// // - A fallback enabled
/// let get_outs_bin_and_fallback = RouterBuilder::<RpcHandlerDummy>::new()
/// .bin_get_outs()
/// .fallback()
/// .build();
///
/// // Create a router with all endpoints enabled.
/// let all = RouterBuilder::<RpcHandlerDummy>::new()
/// .all()
/// .build();
/// ```
#[allow(clippy::struct_excessive_bools)]
#[derive(Clone)]
pub struct RouterBuilder<H: RpcHandler> {
router: Router<H>,
}
impl<H: RpcHandler> RouterBuilder<H> {
/// Create a new [`Self`].
#[must_use]
pub fn new() -> Self {
Self {
router: Router::new(),
}
}
/// Build [`Self`] into a [`Router`].
///
/// All endpoints enabled in [`RouterBuilder`]
/// will be enabled in this [`Router`].
pub fn build(self) -> Router<H> {
self.router
}
/// Enable all endpoints, including [`Self::fallback`].
#[must_use]
pub fn all(mut self) -> Self {
$(
self = self.$endpoint_ident();
)*
self.fallback()
}
/// Enable the catch-all fallback route.
///
/// Any unknown or disabled route will route here, e.g.:
/// - `get_info`
/// - `getinfo`
/// - `asdf`
#[must_use]
pub fn fallback(self) -> Self {
Self {
router: self.router.fallback(fallback::fallback),
}
}
$(
#[doc = concat!(
"Enable the `",
$endpoint_string,
"` endpoint.",
)]
#[must_use]
pub fn $endpoint_ident(self) -> Self {
Self {
router: self.router.route(
$endpoint_string,
::axum::routing::method_routing::MethodRouter::new()
$(.$http_method($endpoint_module::$endpoint_fn::<H>))*
),
}
}
)*
}
};
}
generate_router_builder! {
// JSON-RPC 2.0 route.
json_rpc => "/json_rpc" => json_rpc::json_rpc => (get, post),
// Other JSON routes.
other_get_height => "/get_height" => other::get_height => (get, post),
other_getheight => "/getheight" => other::get_height => (get, post),
other_get_transactions => "/get_transactions" => other::get_transactions => (get, post),
other_gettransactions => "/gettransactions" => other::get_transactions => (get, post),
other_get_alt_blocks_hashes => "/get_alt_blocks_hashes" => other::get_alt_blocks_hashes => (get, post),
other_is_key_image_spent => "/is_key_image_spent" => other::is_key_image_spent => (get, post),
other_send_raw_transaction => "/send_raw_transaction" => other::send_raw_transaction => (get, post),
other_sendrawtransaction => "/sendrawtransaction" => other::send_raw_transaction => (get, post),
other_start_mining => "/start_mining" => other::start_mining => (get, post),
other_stop_mining => "/stop_mining" => other::stop_mining => (get, post),
other_mining_status => "/mining_status" => other::mining_status => (get, post),
other_save_bc => "/save_bc" => other::save_bc => (get, post),
other_get_peer_list => "/get_peer_list" => other::get_peer_list => (get, post),
other_get_public_nodes => "/get_public_nodes" => other::get_public_nodes => (get, post),
other_set_log_hash_rate => "/set_log_hash_rate" => other::set_log_hash_rate => (get, post),
other_set_log_level => "/set_log_level" => other::set_log_level => (get, post),
other_set_log_categories => "/set_log_categories" => other::set_log_categories => (get, post),
other_get_transaction_pool => "/get_transaction_pool" => other::get_transaction_pool => (get, post),
other_get_transaction_pool_hashes => "/get_transaction_pool_hashes" => other::get_transaction_pool_hashes => (get, post),
other_get_transaction_pool_stats => "/get_transaction_pool_stats" => other::get_transaction_pool_stats => (get, post),
other_set_bootstrap_daemon => "/set_bootstrap_daemon" => other::set_bootstrap_daemon => (get, post),
other_stop_daemon => "/stop_daemon" => other::stop_daemon => (get, post),
other_get_net_stats => "/get_net_stats" => other::get_net_stats => (get, post),
other_get_limit => "/get_limit" => other::get_limit => (get, post),
other_set_limit => "/set_limit" => other::set_limit => (get, post),
other_out_peers => "/out_peers" => other::out_peers => (get, post),
other_in_peers => "/in_peers" => other::in_peers => (get, post),
other_get_outs => "/get_outs" => other::get_outs => (get, post),
other_update => "/update" => other::update => (get, post),
other_pop_blocks => "/pop_blocks" => other::pop_blocks => (get, post),
// Binary routes.
bin_get_blocks => "/get_blocks.bin" => bin::get_blocks => (get, post),
bin_getblocks => "/getblocks.bin" => bin::get_blocks => (get, post),
bin_get_blocks_by_height => "/get_blocks_by_height.bin" => bin::get_blocks_by_height => (get, post),
bin_getblocks_by_height => "/getblocks_by_height.bin" => bin::get_blocks_by_height => (get, post),
bin_get_hashes => "/get_hashes.bin" => bin::get_hashes => (get, post),
bin_gethashes => "/gethashes.bin" => bin::get_hashes => (get, post),
bin_get_o_indexes => "/get_o_indexes.bin" => bin::get_o_indexes => (get, post),
bin_get_outs => "/get_outs.bin" => bin::get_outs => (get, post),
bin_get_transaction_pool_hashes => "/get_transaction_pool_hashes.bin" => bin::get_transaction_pool_hashes => (get, post),
bin_get_output_distribution => "/get_output_distribution.bin" => bin::get_output_distribution => (get, post),
}
impl<H: RpcHandler> Default for RouterBuilder<H> {
/// Uses [`Self::all`].
fn default() -> Self {
Self::new().all()
}
}

View file

@ -0,0 +1,34 @@
//! 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<RpcError> for StatusCode {
fn from(value: RpcError) -> Self {
// TODO
Self::INTERNAL_SERVER_ERROR
}
}
//---------------------------------------------------------------------------------------------------- Tests
#[cfg(test)]
mod test {
// use super::*;
}

View file

@ -0,0 +1,57 @@
//! RPC handler trait.
//---------------------------------------------------------------------------------------------------- Use
use std::{future::Future, task::Poll};
use axum::{http::StatusCode, response::IntoResponse};
use futures::{channel::oneshot::channel, FutureExt};
use tower::Service;
use cuprate_helper::asynch::InfallibleOneshotReceiver;
use cuprate_json_rpc::Id;
use cuprate_rpc_types::json::JsonRpcRequest;
use crate::{rpc_error::RpcError, rpc_request::RpcRequest, rpc_response::RpcResponse};
//---------------------------------------------------------------------------------------------------- RpcHandler
/// An RPC handler.
///
/// This trait represents a type that can turn [`RpcRequest`]s into [`RpcResponse`]s.
///
/// Implementors of this trait must be [`tower::Service`]s that use:
/// - [`RpcRequest`] as the generic `Request` type
/// - [`RpcResponse`] as the associated `Response` type
/// - [`RpcError`] as the associated `Error` type
/// - A generic [`Future`] that outputs `Result<RpcResponse, RpcError>`
///
/// See this crate's `RpcHandlerDummy` for an implementation example of this trait.
///
/// # Panics
/// Your [`RpcHandler`] must reply to [`RpcRequest`]s with the correct
/// [`RpcResponse`] or else this crate will panic during routing functions.
///
/// For example, upon a [`RpcRequest::Binary`] must be replied with
/// [`RpcRequest::Binary`]. If an [`RpcRequest::Other`] were returned instead,
/// this crate would panic.
pub trait RpcHandler:
Clone
+ Send
+ Sync
+ 'static
+ Service<
RpcRequest,
Response = RpcResponse,
Error = RpcError,
Future: Future<Output = Result<RpcResponse, RpcError>> + Send + Sync + 'static,
>
{
/// Is this [`RpcHandler`] restricted?
///
/// If this returns `true`, restricted methods and endpoints such as:
/// - `/json_rpc`'s `relay_tx` method
/// - The `/pop_blocks` endpoint
///
/// will automatically be denied access when using the
/// [`axum::Router`] provided by [`RouterBuilder`](crate::RouterBuilder).
fn restricted(&self) -> bool;
}

View file

@ -0,0 +1,142 @@
//! Dummy implementation of [`RpcHandler`].
//---------------------------------------------------------------------------------------------------- Use
use std::task::Poll;
use futures::{channel::oneshot::channel, FutureExt};
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
use tower::Service;
use cuprate_helper::asynch::InfallibleOneshotReceiver;
use cuprate_json_rpc::Id;
use cuprate_rpc_types::json::JsonRpcRequest;
use crate::{
rpc_error::RpcError, rpc_handler::RpcHandler, rpc_request::RpcRequest,
rpc_response::RpcResponse,
};
//---------------------------------------------------------------------------------------------------- RpcHandlerDummy
/// An [`RpcHandler`] that always returns [`Default::default`].
///
/// This `struct` implements [`RpcHandler`], and always responds
/// with the response `struct` set to [`Default::default`].
///
/// See the [`crate`] documentation for example usage.
///
/// This is mostly used for testing purposes and can
/// be disabled by disable the `dummy` feature flag.
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
pub struct RpcHandlerDummy {
/// Should this RPC server be [restricted](RpcHandler::restricted)?
///
/// The dummy will honor this [`bool`]
/// on restricted methods/endpoints.
pub restricted: bool,
}
impl RpcHandler for RpcHandlerDummy {
fn restricted(&self) -> bool {
self.restricted
}
}
impl Service<RpcRequest> for RpcHandlerDummy {
type Response = RpcResponse;
type Error = RpcError;
type Future = InfallibleOneshotReceiver<Result<RpcResponse, RpcError>>;
fn poll_ready(&mut self, cx: &mut std::task::Context<'_>) -> Poll<Result<(), Self::Error>> {
Poll::Ready(Ok(()))
}
fn call(&mut self, req: RpcRequest) -> Self::Future {
use cuprate_rpc_types::bin::BinRequest as BReq;
use cuprate_rpc_types::bin::BinResponse as BResp;
use cuprate_rpc_types::json::JsonRpcRequest as JReq;
use cuprate_rpc_types::json::JsonRpcResponse as JResp;
use cuprate_rpc_types::other::OtherRequest as OReq;
use cuprate_rpc_types::other::OtherResponse as OResp;
#[rustfmt::skip]
#[allow(clippy::default_trait_access)]
let resp = match req {
RpcRequest::JsonRpc(j) => RpcResponse::JsonRpc(cuprate_json_rpc::Response::ok(Id::Null, match j.body {
JReq::GetBlockCount(_) => JResp::GetBlockCount(Default::default()),
JReq::OnGetBlockHash(_) => JResp::OnGetBlockHash(Default::default()),
JReq::SubmitBlock(_) => JResp::SubmitBlock(Default::default()),
JReq::GenerateBlocks(_) => JResp::GenerateBlocks(Default::default()),
JReq::GetLastBlockHeader(_) => JResp::GetLastBlockHeader(Default::default()),
JReq::GetBlockHeaderByHash(_) => JResp::GetBlockHeaderByHash(Default::default()),
JReq::GetBlockHeaderByHeight(_) => JResp::GetBlockHeaderByHeight(Default::default()),
JReq::GetBlockHeadersRange(_) => JResp::GetBlockHeadersRange(Default::default()),
JReq::GetBlock(_) => JResp::GetBlock(Default::default()),
JReq::GetConnections(_) => JResp::GetConnections(Default::default()),
JReq::GetInfo(_) => JResp::GetInfo(Default::default()),
JReq::HardForkInfo(_) => JResp::HardForkInfo(Default::default()),
JReq::SetBans(_) => JResp::SetBans(Default::default()),
JReq::GetBans(_) => JResp::GetBans(Default::default()),
JReq::Banned(_) => JResp::Banned(Default::default()),
JReq::FlushTransactionPool(_) => JResp::FlushTransactionPool(Default::default()),
JReq::GetOutputHistogram(_) => JResp::GetOutputHistogram(Default::default()),
JReq::GetCoinbaseTxSum(_) => JResp::GetCoinbaseTxSum(Default::default()),
JReq::GetVersion(_) => JResp::GetVersion(Default::default()),
JReq::GetFeeEstimate(_) => JResp::GetFeeEstimate(Default::default()),
JReq::GetAlternateChains(_) => JResp::GetAlternateChains(Default::default()),
JReq::RelayTx(_) => JResp::RelayTx(Default::default()),
JReq::SyncInfo(_) => JResp::SyncInfo(Default::default()),
JReq::GetTransactionPoolBacklog(_) => JResp::GetTransactionPoolBacklog(Default::default()),
JReq::GetMinerData(_) => JResp::GetMinerData(Default::default()),
JReq::PruneBlockchain(_) => JResp::PruneBlockchain(Default::default()),
JReq::CalcPow(_) => JResp::CalcPow(Default::default()),
JReq::FlushCache(_) => JResp::FlushCache(Default::default()),
JReq::AddAuxPow(_) => JResp::AddAuxPow(Default::default()),
JReq::GetTxIdsLoose(_) => JResp::GetTxIdsLoose(Default::default()),
})),
RpcRequest::Binary(b) => RpcResponse::Binary(match b {
BReq::GetBlocks(_) => BResp::GetBlocks(Default::default()),
BReq::GetBlocksByHeight(_) => BResp::GetBlocksByHeight(Default::default()),
BReq::GetHashes(_) => BResp::GetHashes(Default::default()),
BReq::GetOutputIndexes(_) => BResp::GetOutputIndexes(Default::default()),
BReq::GetOuts(_) => BResp::GetOuts(Default::default()),
BReq::GetTransactionPoolHashes(_) => BResp::GetTransactionPoolHashes(Default::default()),
BReq::GetOutputDistribution(_) => BResp::GetOutputDistribution(Default::default()),
}),
RpcRequest::Other(o) => RpcResponse::Other(match o {
OReq::GetHeight(_) => OResp::GetHeight(Default::default()),
OReq::GetTransactions(_) => OResp::GetTransactions(Default::default()),
OReq::GetAltBlocksHashes(_) => OResp::GetAltBlocksHashes(Default::default()),
OReq::IsKeyImageSpent(_) => OResp::IsKeyImageSpent(Default::default()),
OReq::SendRawTransaction(_) => OResp::SendRawTransaction(Default::default()),
OReq::StartMining(_) => OResp::StartMining(Default::default()),
OReq::StopMining(_) => OResp::StopMining(Default::default()),
OReq::MiningStatus(_) => OResp::MiningStatus(Default::default()),
OReq::SaveBc(_) => OResp::SaveBc(Default::default()),
OReq::GetPeerList(_) => OResp::GetPeerList(Default::default()),
OReq::SetLogHashRate(_) => OResp::SetLogHashRate(Default::default()),
OReq::SetLogLevel(_) => OResp::SetLogLevel(Default::default()),
OReq::SetLogCategories(_) => OResp::SetLogCategories(Default::default()),
OReq::SetBootstrapDaemon(_) => OResp::SetBootstrapDaemon(Default::default()),
OReq::GetTransactionPool(_) => OResp::GetTransactionPool(Default::default()),
OReq::GetTransactionPoolStats(_) => OResp::GetTransactionPoolStats(Default::default()),
OReq::StopDaemon(_) => OResp::StopDaemon(Default::default()),
OReq::GetLimit(_) => OResp::GetLimit(Default::default()),
OReq::SetLimit(_) => OResp::SetLimit(Default::default()),
OReq::OutPeers(_) => OResp::OutPeers(Default::default()),
OReq::InPeers(_) => OResp::InPeers(Default::default()),
OReq::GetNetStats(_) => OResp::GetNetStats(Default::default()),
OReq::GetOuts(_) => OResp::GetOuts(Default::default()),
OReq::Update(_) => OResp::Update(Default::default()),
OReq::PopBlocks(_) => OResp::PopBlocks(Default::default()),
OReq::GetTransactionPoolHashes(_) => OResp::GetTransactionPoolHashes(Default::default()),
OReq::GetPublicNodes(_) => OResp::GetPublicNodes(Default::default()),
})
};
let (tx, rx) = channel();
drop(tx.send(Ok(resp)));
InfallibleOneshotReceiver::from(rx)
}
}

View file

@ -0,0 +1,33 @@
//! RPC requests.
//---------------------------------------------------------------------------------------------------- Import
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
use cuprate_rpc_types::{bin::BinRequest, json::JsonRpcRequest, other::OtherRequest};
//---------------------------------------------------------------------------------------------------- RpcRequest
/// All possible RPC requests.
///
/// This enum encapsulates all possible RPC requests:
/// - JSON RPC 2.0 requests
/// - Binary requests
/// - Other JSON requests
///
/// It is the `Request` type required to be used in an [`RpcHandler`](crate::RpcHandler).
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
pub enum RpcRequest {
/// JSON-RPC 2.0 requests.
JsonRpc(cuprate_json_rpc::Request<JsonRpcRequest>),
/// Binary requests.
Binary(BinRequest),
/// Other JSON requests.
Other(OtherRequest),
}
//---------------------------------------------------------------------------------------------------- Tests
#[cfg(test)]
mod test {
// use super::*;
}

View file

@ -0,0 +1,33 @@
//! RPC responses.
//---------------------------------------------------------------------------------------------------- Import
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
use cuprate_rpc_types::{bin::BinResponse, json::JsonRpcResponse, other::OtherResponse};
//---------------------------------------------------------------------------------------------------- RpcResponse
/// All possible RPC responses.
///
/// This enum encapsulates all possible RPC responses:
/// - JSON RPC 2.0 responses
/// - Binary responses
/// - Other JSON responses
///
/// It is the `Response` type required to be used in an [`RpcHandler`](crate::RpcHandler).
#[derive(Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
pub enum RpcResponse {
/// JSON RPC 2.0 responses.
JsonRpc(cuprate_json_rpc::Response<JsonRpcResponse>),
/// Binary responses.
Binary(BinResponse),
/// Other JSON responses.
Other(OtherResponse),
}
//---------------------------------------------------------------------------------------------------- Tests
#[cfg(test)]
mod test {
// use super::*;
}