This commit is contained in:
hinto.janai 2024-07-21 20:24:35 -04:00
parent ea8f2e681c
commit 73c11a4cdf
No known key found for this signature in database
GPG key ID: D47CE05FA175A499
15 changed files with 335 additions and 855 deletions

113
Cargo.lock generated
View file

@ -106,6 +106,61 @@ 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"
@ -758,8 +813,12 @@ dependencies = [
name = "cuprate-rpc-interface"
version = "0.0.0"
dependencies = [
"axum",
"cuprate-epee-encoding",
"cuprate-json-rpc",
"cuprate-rpc-types",
"serde",
"tower",
]
[[package]]
@ -1309,6 +1368,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"
@ -1321,6 +1386,7 @@ dependencies = [
"http",
"http-body",
"httparse",
"httpdate",
"itoa",
"pin-project-lite",
"smallvec",
@ -1630,6 +1696,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"
@ -1658,6 +1730,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"
@ -2371,6 +2449,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"
@ -2506,6 +2606,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"
@ -2733,6 +2845,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",

View file

@ -13,7 +13,11 @@ default = []
[dependencies]
cuprate-epee-encoding = { path = "../../net/epee-encoding" }
cuprate-json-rpc = { path = "../json-rpc" }
cuprate-rpc-types = { path = "../types" }
axum = { version = "0.7.5" }
serde = { workspace = true }
tower = { workspace = true }
[dev-dependencies]

View file

@ -1,89 +1,11 @@
Monero RPC types.
# What
This crate ports the types used in Monero's RPC interface, including:
- JSON types
- Binary (epee) types
- Mixed types
- Other commonly used RPC types
# Modules
This crate's types are split in the following manner:
| Module | Purpose |
|--------|---------|
| The root module | Miscellaneous items, e.g. constants.
| [`json`] | Contains JSON request/response (some mixed with binary) that all share the common `/json_rpc` endpoint. |
| [`bin`] | Contains request/response types that are expected to be fully in binary (`cuprate_epee_encoding`) in `monerod` and `cuprated`'s RPC interface. These are called at a custom endpoint instead of `/json_rpc`, e.g. `/get_blocks.bin`. |
| [`other`] | Contains request/response types that are JSON, but aren't called at `/json_rpc` (e.g. [`crate::other::GetHeightRequest`]). |
| [`misc`] | Contains miscellaneous types, e.g. [`crate::misc::Status`]. Many of types here are found and used in request/response types, for example, [`crate::misc::BlockHeader`] is used in [`crate::json::GetLastBlockHeaderResponse`]. |
| [`base`] | Contains base types flattened into many request/response types.
Each type in `{json,bin,other}` come in pairs and have identical names, but are suffixed with either `Request` or `Response`. e.g. [`GetBlockCountRequest`](crate::json::GetBlockCountRequest) & [`GetBlockCountResponse`](crate::json::GetBlockCountResponse).
# Documentation
The documentation for types within `{json,bin,other}` are omitted, as they can be found in [Monero's RPC documentation](https://www.getmonero.org/resources/developer-guides/daemon-rpc.html).
However, each type will document:
- **Definition**: the exact type definition location in `monerod`
- **Documentation**: the Monero RPC documentation link
- **Request/response**: the other side of this type, either the request or response
# Naming
The naming for types within `{json,bin,other}` follow the following scheme:
1. Convert the endpoint or method name into `UpperCamelCase`
1. Remove any suffix extension
1. Add `Request/Response` suffix
For example:
| Endpoint/method | Crate location and name |
|-----------------|-------------------------|
| [`get_block_count`](https://www.getmonero.org/resources/developer-guides/daemon-rpc.html#get_block_count) | [`json::GetBlockCountRequest`] & [`json::GetBlockCountResponse`]
| [`/get_blocks.bin`](https://www.getmonero.org/resources/developer-guides/daemon-rpc.html#get_blockbin) | [`bin::GetBlocksRequest`] & [`bin::GetBlocksResponse`]
| [`/get_height`](https://www.getmonero.org/resources/developer-guides/daemon-rpc.html#get_height) | [`other::GetHeightRequest`] & [`other::GetHeightResponse`]
# Mixed types
Note that some types mix JSON & binary together, i.e., the message overall is JSON,
however some fields contain binary values inside JSON strings, for example:
```json
{
"string": "",
"float": 30.0,
"integer": 30,
"binary": "<serialized binary>"
}
```
`binary` here is (de)serialized as a normal [`String`]. In order to be clear on which fields contain binary data, the struct fields that have them will use [`crate::misc::BinaryString`] instead of [`String`].
These mixed types are:
- [`crate::json::GetTransactionPoolBacklogResponse`]
- [`crate::json::GetOutputDistributionResponse`]
TODO: we need to figure out a type that (de)serializes correctly, `String` errors with `serde_json`
# Fixed byte containers
TODO
<!--
Some fields within requests/responses are containers, but fixed in size.
For example, [`crate::json::GetBlockTemplateResponse::prev_hash`] is always a 32-byte hash.
In these cases, stack allocated types like `cuprate_fixed_bytes::StrArray`
will be used instead of a more typical [`String`] for optimization reasons.
-->
# What
# Feature flags
List of feature flags for `cuprate-rpc-types`.
List of feature flags for `cuprate-rpc-interface`.
All are enabled by default.
| Feature flag | Does what |
|--------------|-----------|
| `serde` | Implements `serde` on all types
| `epee` | Implements `cuprate_epee_encoding` on all types

View file

@ -2,7 +2,9 @@
//---------------------------------------------------------------------------------------------------- Import
//---------------------------------------------------------------------------------------------------- Status
//---------------------------------------------------------------------------------------------------- TODO
/// TODO
pub enum Error {}
//---------------------------------------------------------------------------------------------------- Tests
#[cfg(test)]

View file

@ -1,18 +1,69 @@
//! Free functions.
//---------------------------------------------------------------------------------------------------- Serde
// These are functions used for conditionally (de)serialization.
//---------------------------------------------------------------------------------------------------- Use
use std::{future::Future, marker::PhantomData};
/// Returns `true` if the input `u` is equal to `0`.
#[inline]
#[allow(clippy::trivially_copy_pass_by_ref)] // serde needs `&`
pub(crate) const fn is_zero(u: &u64) -> bool {
*u == 0
}
use axum::{routing::method_routing::get, Router};
use tower::Service;
/// Returns `true` the input `u` is equal to `1`.
#[inline]
#[allow(clippy::trivially_copy_pass_by_ref)] // serde needs `&`
pub(crate) const fn is_one(u: &u64) -> bool {
*u == 1
use crate::{
error::Error, request::Request, response::Response, route::json_rpc, rpc_handler::RpcHandler,
RpcState,
};
//---------------------------------------------------------------------------------------------------- Router
/// TODO
#[allow(clippy::needless_pass_by_value)]
pub fn create_router<H: RpcHandler>() -> Router<H::RpcState> {
// List of `monerod` routes:
// <https://github.com/monero-project/monero/blob/cc73fe71162d564ffda8e549b79a350bca53c454/src/rpc/core_rpc_server.h#L97-L189>
Router::new()
// JSON-RPC route.
.route("/json_rpc", get(json_rpc))
// Other JSON routes.
.route("/get_height", todo!())
.route("/getheight", todo!())
.route("/get_transactions", todo!())
.route("/gettransactions", todo!())
.route("/get_alt_blocks_hashes", todo!())
.route("/is_key_image_spent", todo!())
.route("/send_raw_transaction", todo!())
.route("/sendrawtransaction", todo!())
.route("/start_mining", todo!())
.route("/stop_mining", todo!())
.route("/mining_status", todo!())
.route("/save_bc", todo!())
.route("/get_peer_list", todo!())
.route("/get_public_nodes", todo!())
.route("/set_log_hash_rate", todo!())
.route("/set_log_level", todo!())
.route("/set_log_categories", todo!())
.route("/get_transaction_pool", todo!())
.route("/get_transaction_pool_hashes", todo!())
.route("/get_transaction_pool_stats", todo!())
.route("/set_bootstrap_daemon", todo!())
.route("/stop_daemon", todo!())
.route("/get_info", todo!())
.route("/getinfo", todo!())
.route("/get_net_stats", todo!())
.route("/get_limit", todo!())
.route("/set_limit", todo!())
.route("/out_peers", todo!())
.route("/in_peers", todo!())
.route("/get_outs", todo!())
.route("/update", todo!())
.route("/pop_blocks", todo!())
// Binary routes.
.route("/get_blocks.bin", todo!())
.route("/getblocks.bin", todo!())
.route("/get_blocks_by_height.bin", todo!())
.route("/getblocks_by_height.bin", todo!())
.route("/get_hashes.bin", todo!())
.route("/gethashes.bin", todo!())
.route("/get_o_indexes.bin", todo!())
.route("/get_outs.bin", todo!())
.route("/get_transaction_pool_hashes.bin", todo!())
.route("/get_output_distribution.bin", todo!())
// Unknown route.
.route("/*", todo!())
}

View file

@ -102,14 +102,24 @@
)
)]
// TODO: remove me after finishing impl
#![allow(dead_code)]
#![allow(dead_code, unreachable_code)]
//---------------------------------------------------------------------------------------------------- Mod
mod constants;
mod error;
mod free;
mod macros;
mod method;
mod request;
mod response;
mod route;
mod state;
mod rpc_handler;
mod rpc_state;
pub use error::Error;
pub use free::create_router;
pub use method::Method;
pub use request::Request;
pub use response::Response;
pub use rpc_handler::{ConcreteRpcHandler, RpcHandler};
pub use rpc_state::{ConcreteRpcState, RpcState};

View file

@ -1,379 +1,3 @@
//! Macros.
//---------------------------------------------------------------------------------------------------- define_request_and_response
/// A template for generating the RPC request and response `struct`s.
///
/// These `struct`s automatically implement:
/// - `Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash`
/// - `serde::{Serialize, Deserialize}`
/// - `cuprate_epee_encoding::EpeeObject`
///
/// It's best to see the output of this macro via the documentation
/// of the generated structs via `cargo doc`s to see which parts
/// generate which docs.
///
/// See the [`crate::json`] module for example usage.
///
/// # Macro internals
/// This macro uses:
/// - [`__define_request`]
/// - [`__define_response`]
/// - [`__define_request_and_response_doc`]
///
/// # `__define_request`
/// This macro has 2 branches. If the caller provides
/// `Request {}`, i.e. no fields, it will generate:
/// ```
/// pub type Request = ();
/// ```
/// If they _did_ specify fields, it will generate:
/// ```
/// pub struct Request {/* fields */}
/// ```
/// This is because having a bunch of types that are all empty structs
/// means they are not compatible and it makes it cumbersome for end-users.
/// Really, they semantically are empty types, so `()` is used.
///
/// # `__define_response`
/// This macro has 2 branches. If the caller provides `Response`
/// it will generate a normal struct with no additional fields.
///
/// If the caller provides a base type from [`crate::base`], it will
/// flatten that into the request type automatically.
///
/// E.g. `Response {/*...*/}` and `ResponseBase {/*...*/}`
/// would trigger the different branches.
macro_rules! define_request_and_response {
(
// The markdown tag for Monero daemon RPC documentation. Not necessarily the endpoint.
$monero_daemon_rpc_doc_link:ident,
// The commit hash and `$file.$extension` in which this type is defined in
// the Monero codebase in the `rpc/` directory, followed by the specific lines.
$monero_code_commit:ident =>
$monero_code_filename:ident.
$monero_code_filename_extension:ident =>
$monero_code_line_start:literal..=
$monero_code_line_end:literal,
// The base `struct` name.
// Attributes added here will apply to _both_
// request and response types.
$( #[$type_attr:meta] )*
$type_name:ident,
// The request type (and any doc comments, derives, etc).
$( #[$request_type_attr:meta] )*
Request {
// And any fields.
$(
$( #[$request_field_attr:meta] )* // Field attribute.
$request_field:ident: $request_field_type:ty // field_name: field type
$(as $request_field_type_as:ty)? // (optional) alternative type (de)serialization
$(= $request_field_type_default:expr, $request_field_type_default_string:literal)?, // (optional) default value
)*
},
// The response type (and any doc comments, derives, etc).
$( #[$response_type_attr:meta] )*
$response_base_type:ty {
// And any fields.
$(
$( #[$response_field_attr:meta] )*
$response_field:ident: $response_field_type:ty
$(as $response_field_type_as:ty)?
$(= $response_field_type_default:expr, $response_field_type_default_string:literal)?,
)*
}
) => { paste::paste! {
$crate::macros::__define_request! {
#[doc = $crate::macros::__define_request_and_response_doc!(
"response" => [<$type_name Response>],
$monero_daemon_rpc_doc_link,
$monero_code_commit,
$monero_code_filename,
$monero_code_filename_extension,
$monero_code_line_start,
$monero_code_line_end,
)]
///
$( #[$type_attr] )*
///
$( #[$request_type_attr] )*
[<$type_name Request>] {
$(
$( #[$request_field_attr] )*
$request_field: $request_field_type
$(as $request_field_type_as)?
$(= $request_field_type_default, $request_field_type_default_string)?,
)*
}
}
$crate::macros::__define_response! {
#[allow(dead_code)]
#[allow(missing_docs)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[doc = $crate::macros::__define_request_and_response_doc!(
"request" => [<$type_name Request>],
$monero_daemon_rpc_doc_link,
$monero_code_commit,
$monero_code_filename,
$monero_code_filename_extension,
$monero_code_line_start,
$monero_code_line_end,
)]
///
$( #[$type_attr] )*
///
$( #[$response_type_attr] )*
$response_base_type => [<$type_name Response>] {
$(
$( #[$response_field_attr] )*
$response_field: $response_field_type
$(as $response_field_type_as)?
$(= $response_field_type_default, $response_field_type_default_string)?,
)*
}
}
}};
}
pub(crate) use define_request_and_response;
//---------------------------------------------------------------------------------------------------- define_request
/// Define a request type.
///
/// This is only used in [`define_request_and_response`], see it for docs.
///
/// `__` is used to notate that this shouldn't be called directly.
macro_rules! __define_request {
//------------------------------------------------------------------------------
// This branch will generate a type alias to `()` if only given `{}` as input.
(
// Any doc comments, derives, etc.
$( #[$attr:meta] )*
// The response type.
$t:ident {}
) => {
$( #[$attr] )*
///
/// This request has no inputs.
pub type $t = ();
};
//------------------------------------------------------------------------------
// This branch of the macro expects fields within the `{}`,
// and will generate a `struct`
(
// Any doc comments, derives, etc.
$( #[$attr:meta] )*
// The response type.
$t:ident {
// And any fields.
$(
$( #[$field_attr:meta] )* // field attributes
// field_name: FieldType
$field:ident: $field_type:ty
$(as $field_as:ty)?
$(= $field_default:expr, $field_default_string:literal)?,
// The $field_default is an optional extra token that represents
// a default value to pass to [`cuprate_epee_encoding::epee_object`],
// see it for usage.
)*
}
) => {
#[allow(dead_code, missing_docs)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
$( #[$attr] )*
pub struct $t {
$(
$( #[$field_attr] )*
$(#[cfg_attr(feature = "serde", serde(default = $field_default_string))])?
pub $field: $field_type,
)*
}
#[cfg(feature = "epee")]
::cuprate_epee_encoding::epee_object! {
$t,
$(
$field: $field_type
$(as $field_as)?
$(= $field_default)?,
)*
}
};
}
pub(crate) use __define_request;
//---------------------------------------------------------------------------------------------------- define_response
/// Define a response type.
///
/// This is only used in [`define_request_and_response`], see it for docs.
///
/// `__` is used to notate that this shouldn't be called directly.
macro_rules! __define_response {
//------------------------------------------------------------------------------
// This version of the macro expects the literal ident
// `Response` => $response_type_name.
//
// It will create a `struct` that _doesn't_ use a base from [`crate::base`],
// for example, [`crate::json::BannedResponse`] doesn't use a base, so it
// uses this branch.
(
// Any doc comments, derives, etc.
$( #[$attr:meta] )*
// The response type.
Response => $t:ident {
// And any fields.
// See [`__define_request`] for docs, this does the same thing.
$(
$( #[$field_attr:meta] )*
$field:ident: $field_type:ty
$(as $field_as:ty)?
$(= $field_default:expr, $field_default_string:literal)?,
)*
}
) => {
$( #[$attr] )*
pub struct $t {
$(
$( #[$field_attr] )*
$(#[cfg_attr(feature = "serde", serde(default = $field_default_string))])?
pub $field: $field_type,
)*
}
#[cfg(feature = "epee")]
::cuprate_epee_encoding::epee_object! {
$t,
$(
$field: $field_type
$(as $field_as)?
$(= $field_default)?,
)*
}
};
//------------------------------------------------------------------------------
// This version of the macro expects a `Request` base type from [`crate::bases`].
(
// Any doc comments, derives, etc.
$( #[$attr:meta] )*
// The response base type => actual name of the struct
$base:ty => $t:ident {
// And any fields.
// See [`__define_request`] for docs, this does the same thing.
$(
$( #[$field_attr:meta] )*
$field:ident: $field_type:ty
$(as $field_as:ty)?
$(= $field_default:expr, $field_default_string:literal)?,
)*
}
) => {
$( #[$attr] )*
pub struct $t {
#[cfg_attr(feature = "serde", serde(flatten))]
pub base: $base,
$(
$( #[$field_attr] )*
$(#[cfg_attr(feature = "serde", serde(default = $field_default_string))])?
pub $field: $field_type,
)*
}
#[cfg(feature = "epee")]
::cuprate_epee_encoding::epee_object! {
$t,
$(
$field: $field_type
$(as $field_as)?
$(= $field_default)?,
)*
!flatten: base: $base,
}
};
}
pub(crate) use __define_response;
//---------------------------------------------------------------------------------------------------- define_request_and_response_doc
/// Generate documentation for the types generated
/// by the [`__define_request_and_response`] macro.
///
/// See it for more info on inputs.
///
/// `__` is used to notate that this shouldn't be called directly.
macro_rules! __define_request_and_response_doc {
(
// This labels the last `[request]` or `[response]`
// hyperlink in documentation. Input is either:
// - "request"
// - "response"
//
// Remember this is linking to the _other_ type,
// so if defining a `Request` type, input should
// be "response".
$request_or_response:literal => $request_or_response_type:ident,
$monero_daemon_rpc_doc_link:ident,
$monero_code_commit:ident,
$monero_code_filename:ident,
$monero_code_filename_extension:ident,
$monero_code_line_start:literal,
$monero_code_line_end:literal,
) => {
concat!(
"",
"[Definition](",
"https://github.com/monero-project/monero/blob/",
stringify!($monero_code_commit),
"/src/rpc/",
stringify!($monero_code_filename),
".",
stringify!($monero_code_filename_extension),
"#L",
stringify!($monero_code_line_start),
"-L",
stringify!($monero_code_line_end),
"), [documentation](",
"https://www.getmonero.org/resources/developer-guides/daemon-rpc.html",
"#",
stringify!($monero_daemon_rpc_doc_link),
"), [",
$request_or_response,
"](",
stringify!($request_or_response_type),
")."
)
};
}
pub(crate) use __define_request_and_response_doc;
//---------------------------------------------------------------------------------------------------- Macro
/// Output a string link to `monerod` source code.
macro_rules! monero_definition_link {
(
$commit:ident, // Git commit hash
$file_path:literal, // File path within `monerod`'s `src/`, e.g. `rpc/core_rpc_server_commands_defs.h`
$start:literal$(..=$end:literal)? // File lines, e.g. `0..=123` or `0`
) => {
concat!(
"[Definition](https://github.com/monero-project/monero/blob/",
stringify!($commit),
"/src/",
$file_path,
"#L",
stringify!($start),
$(
"-L",
stringify!($end),
)?
")."
)
};
}
pub(crate) use monero_definition_link;
//---------------------------------------------------------------------------------------------------- TODO

View file

@ -0,0 +1,31 @@
//! TODO
//---------------------------------------------------------------------------------------------------- Import
use serde::{Deserialize, Serialize};
use cuprate_rpc_types::json::GetBlockRequest;
//---------------------------------------------------------------------------------------------------- TODO
/// TODO
#[derive(Deserialize, Serialize)]
#[serde(tag = "method", content = "params")]
#[serde(rename_all = "snake_case")]
pub enum Method {
/// TODO
GetBlock(GetBlockRequest),
}
impl Method {
/// TODO
pub const fn is_restricted(&self) -> bool {
match self {
Self::GetBlock(_) => false,
}
}
}
//---------------------------------------------------------------------------------------------------- Tests
#[cfg(test)]
mod test {
// use super::*;
}

View file

@ -2,7 +2,9 @@
//---------------------------------------------------------------------------------------------------- Import
//---------------------------------------------------------------------------------------------------- Status
//---------------------------------------------------------------------------------------------------- TODO
/// TODO
pub enum Request {}
//---------------------------------------------------------------------------------------------------- Tests
#[cfg(test)]

View file

@ -3,6 +3,8 @@
//---------------------------------------------------------------------------------------------------- Import
//---------------------------------------------------------------------------------------------------- Status
/// TODO
pub enum Response {}
//---------------------------------------------------------------------------------------------------- Tests
#[cfg(test)]

View file

@ -3,8 +3,25 @@
//! All types are originally defined in [`rpc/core_rpc_server_commands_defs.h`](https://github.com/monero-project/monero/blob/cc73fe71162d564ffda8e549b79a350bca53c454/src/rpc/core_rpc_server_commands_defs.h).
//---------------------------------------------------------------------------------------------------- Import
use std::{future::Future, sync::Arc};
use axum::Json;
use tower::Service;
use crate::{
error::Error, method::Method, request::Request, response::Response, rpc_handler::RpcHandler,
};
//---------------------------------------------------------------------------------------------------- Struct definitions
/// TODO
// pub(crate) async fn json_rpc<H: RpcHandler>(
pub(crate) async fn json_rpc(
// handler: Arc<H>,
Json(request): Json<cuprate_json_rpc::Request<Method>>,
) {
todo!()
}
// // This generates 2 structs:
// //
// // - `GetBlockTemplateRequest`

View file

@ -1,5 +1,7 @@
//! TODO
mod bin;
mod json;
mod json_rpc;
mod other;
pub(crate) use json_rpc::json_rpc;

View file

@ -0,0 +1,49 @@
//! TODO
//---------------------------------------------------------------------------------------------------- Use
use std::{future::Future, marker::PhantomData, sync::Arc};
use tower::Service;
use crate::{
error::Error, request::Request, response::Response, rpc_state::ConcreteRpcState, RpcState,
};
//---------------------------------------------------------------------------------------------------- TODO
/// TODO
pub trait RpcHandler: Send + Sync + 'static {
/// TODO
type RpcState: RpcState;
/// TODO
type Handler: Send + Sync + 'static + Service<Request>;
// where
// <Self::Handler as Service<Request>>::Response: Into<Response>,
// <Self::Handler as Service<Request>>::Error: Into<Error>,
// <Self::Handler as Service<Request>>::Future: Future<Output = Result<Response, Error>>;
/// TODO
fn state(&self) -> Self::RpcState;
}
/// TODO
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct ConcreteRpcHandler<Handler> {
state: ConcreteRpcState,
_handler: PhantomData<Handler>,
}
impl<H> RpcHandler for ConcreteRpcHandler<H>
where
H: Send + Sync + 'static + Service<Request>,
<H as Service<Request>>::Response: Into<Response>,
<H as Service<Request>>::Error: Into<Error>,
<H as Service<Request>>::Future: Future<Output = Result<Response, Error>>,
{
type RpcState = ConcreteRpcState;
type Handler = H;
fn state(&self) -> Self::RpcState {
self.state
}
}

View file

@ -0,0 +1,30 @@
//! TODO
//---------------------------------------------------------------------------------------------------- Use
use std::{future::Future, marker::PhantomData, sync::Arc};
use tower::Service;
use crate::{error::Error, request::Request, response::Response};
//---------------------------------------------------------------------------------------------------- TODO
/// TODO
pub trait RpcState
where
Self: Clone + Send + Sync + 'static,
{
/// TODO
fn restricted(&self) -> bool;
}
/// TODO
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct ConcreteRpcState {
restricted: bool,
}
impl RpcState for ConcreteRpcState {
fn restricted(&self) -> bool {
self.restricted
}
}

View file

@ -1,379 +0,0 @@
//! Macros.
//---------------------------------------------------------------------------------------------------- define_request_and_response
/// A template for generating the RPC request and response `struct`s.
///
/// These `struct`s automatically implement:
/// - `Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash`
/// - `serde::{Serialize, Deserialize}`
/// - `cuprate_epee_encoding::EpeeObject`
///
/// It's best to see the output of this macro via the documentation
/// of the generated structs via `cargo doc`s to see which parts
/// generate which docs.
///
/// See the [`crate::json`] module for example usage.
///
/// # Macro internals
/// This macro uses:
/// - [`__define_request`]
/// - [`__define_response`]
/// - [`__define_request_and_response_doc`]
///
/// # `__define_request`
/// This macro has 2 branches. If the caller provides
/// `Request {}`, i.e. no fields, it will generate:
/// ```
/// pub type Request = ();
/// ```
/// If they _did_ specify fields, it will generate:
/// ```
/// pub struct Request {/* fields */}
/// ```
/// This is because having a bunch of types that are all empty structs
/// means they are not compatible and it makes it cumbersome for end-users.
/// Really, they semantically are empty types, so `()` is used.
///
/// # `__define_response`
/// This macro has 2 branches. If the caller provides `Response`
/// it will generate a normal struct with no additional fields.
///
/// If the caller provides a base type from [`crate::base`], it will
/// flatten that into the request type automatically.
///
/// E.g. `Response {/*...*/}` and `ResponseBase {/*...*/}`
/// would trigger the different branches.
macro_rules! define_request_and_response {
(
// The markdown tag for Monero daemon RPC documentation. Not necessarily the endpoint.
$monero_daemon_rpc_doc_link:ident,
// The commit hash and `$file.$extension` in which this type is defined in
// the Monero codebase in the `rpc/` directory, followed by the specific lines.
$monero_code_commit:ident =>
$monero_code_filename:ident.
$monero_code_filename_extension:ident =>
$monero_code_line_start:literal..=
$monero_code_line_end:literal,
// The base `struct` name.
// Attributes added here will apply to _both_
// request and response types.
$( #[$type_attr:meta] )*
$type_name:ident,
// The request type (and any doc comments, derives, etc).
$( #[$request_type_attr:meta] )*
Request {
// And any fields.
$(
$( #[$request_field_attr:meta] )* // Field attribute.
$request_field:ident: $request_field_type:ty // field_name: field type
$(as $request_field_type_as:ty)? // (optional) alternative type (de)serialization
$(= $request_field_type_default:expr, $request_field_type_default_string:literal)?, // (optional) default value
)*
},
// The response type (and any doc comments, derives, etc).
$( #[$response_type_attr:meta] )*
$response_base_type:ty {
// And any fields.
$(
$( #[$response_field_attr:meta] )*
$response_field:ident: $response_field_type:ty
$(as $response_field_type_as:ty)?
$(= $response_field_type_default:expr, $response_field_type_default_string:literal)?,
)*
}
) => { paste::paste! {
$crate::macros::__define_request! {
#[doc = $crate::macros::__define_request_and_response_doc!(
"response" => [<$type_name Response>],
$monero_daemon_rpc_doc_link,
$monero_code_commit,
$monero_code_filename,
$monero_code_filename_extension,
$monero_code_line_start,
$monero_code_line_end,
)]
///
$( #[$type_attr] )*
///
$( #[$request_type_attr] )*
[<$type_name Request>] {
$(
$( #[$request_field_attr] )*
$request_field: $request_field_type
$(as $request_field_type_as)?
$(= $request_field_type_default, $request_field_type_default_string)?,
)*
}
}
$crate::macros::__define_response! {
#[allow(dead_code)]
#[allow(missing_docs)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[doc = $crate::macros::__define_request_and_response_doc!(
"request" => [<$type_name Request>],
$monero_daemon_rpc_doc_link,
$monero_code_commit,
$monero_code_filename,
$monero_code_filename_extension,
$monero_code_line_start,
$monero_code_line_end,
)]
///
$( #[$type_attr] )*
///
$( #[$response_type_attr] )*
$response_base_type => [<$type_name Response>] {
$(
$( #[$response_field_attr] )*
$response_field: $response_field_type
$(as $response_field_type_as)?
$(= $response_field_type_default, $response_field_type_default_string)?,
)*
}
}
}};
}
pub(crate) use define_request_and_response;
//---------------------------------------------------------------------------------------------------- define_request
/// Define a request type.
///
/// This is only used in [`define_request_and_response`], see it for docs.
///
/// `__` is used to notate that this shouldn't be called directly.
macro_rules! __define_request {
//------------------------------------------------------------------------------
// This branch will generate a type alias to `()` if only given `{}` as input.
(
// Any doc comments, derives, etc.
$( #[$attr:meta] )*
// The response type.
$t:ident {}
) => {
$( #[$attr] )*
///
/// This request has no inputs.
pub type $t = ();
};
//------------------------------------------------------------------------------
// This branch of the macro expects fields within the `{}`,
// and will generate a `struct`
(
// Any doc comments, derives, etc.
$( #[$attr:meta] )*
// The response type.
$t:ident {
// And any fields.
$(
$( #[$field_attr:meta] )* // field attributes
// field_name: FieldType
$field:ident: $field_type:ty
$(as $field_as:ty)?
$(= $field_default:expr, $field_default_string:literal)?,
// The $field_default is an optional extra token that represents
// a default value to pass to [`cuprate_epee_encoding::epee_object`],
// see it for usage.
)*
}
) => {
#[allow(dead_code, missing_docs)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
$( #[$attr] )*
pub struct $t {
$(
$( #[$field_attr] )*
$(#[cfg_attr(feature = "serde", serde(default = $field_default_string))])?
pub $field: $field_type,
)*
}
#[cfg(feature = "epee")]
::cuprate_epee_encoding::epee_object! {
$t,
$(
$field: $field_type
$(as $field_as)?
$(= $field_default)?,
)*
}
};
}
pub(crate) use __define_request;
//---------------------------------------------------------------------------------------------------- define_response
/// Define a response type.
///
/// This is only used in [`define_request_and_response`], see it for docs.
///
/// `__` is used to notate that this shouldn't be called directly.
macro_rules! __define_response {
//------------------------------------------------------------------------------
// This version of the macro expects the literal ident
// `Response` => $response_type_name.
//
// It will create a `struct` that _doesn't_ use a base from [`crate::base`],
// for example, [`crate::json::BannedResponse`] doesn't use a base, so it
// uses this branch.
(
// Any doc comments, derives, etc.
$( #[$attr:meta] )*
// The response type.
Response => $t:ident {
// And any fields.
// See [`__define_request`] for docs, this does the same thing.
$(
$( #[$field_attr:meta] )*
$field:ident: $field_type:ty
$(as $field_as:ty)?
$(= $field_default:expr, $field_default_string:literal)?,
)*
}
) => {
$( #[$attr] )*
pub struct $t {
$(
$( #[$field_attr] )*
$(#[cfg_attr(feature = "serde", serde(default = $field_default_string))])?
pub $field: $field_type,
)*
}
#[cfg(feature = "epee")]
::cuprate_epee_encoding::epee_object! {
$t,
$(
$field: $field_type
$(as $field_as)?
$(= $field_default)?,
)*
}
};
//------------------------------------------------------------------------------
// This version of the macro expects a `Request` base type from [`crate::bases`].
(
// Any doc comments, derives, etc.
$( #[$attr:meta] )*
// The response base type => actual name of the struct
$base:ty => $t:ident {
// And any fields.
// See [`__define_request`] for docs, this does the same thing.
$(
$( #[$field_attr:meta] )*
$field:ident: $field_type:ty
$(as $field_as:ty)?
$(= $field_default:expr, $field_default_string:literal)?,
)*
}
) => {
$( #[$attr] )*
pub struct $t {
#[cfg_attr(feature = "serde", serde(flatten))]
pub base: $base,
$(
$( #[$field_attr] )*
$(#[cfg_attr(feature = "serde", serde(default = $field_default_string))])?
pub $field: $field_type,
)*
}
#[cfg(feature = "epee")]
::cuprate_epee_encoding::epee_object! {
$t,
$(
$field: $field_type
$(as $field_as)?
$(= $field_default)?,
)*
!flatten: base: $base,
}
};
}
pub(crate) use __define_response;
//---------------------------------------------------------------------------------------------------- define_request_and_response_doc
/// Generate documentation for the types generated
/// by the [`__define_request_and_response`] macro.
///
/// See it for more info on inputs.
///
/// `__` is used to notate that this shouldn't be called directly.
macro_rules! __define_request_and_response_doc {
(
// This labels the last `[request]` or `[response]`
// hyperlink in documentation. Input is either:
// - "request"
// - "response"
//
// Remember this is linking to the _other_ type,
// so if defining a `Request` type, input should
// be "response".
$request_or_response:literal => $request_or_response_type:ident,
$monero_daemon_rpc_doc_link:ident,
$monero_code_commit:ident,
$monero_code_filename:ident,
$monero_code_filename_extension:ident,
$monero_code_line_start:literal,
$monero_code_line_end:literal,
) => {
concat!(
"",
"[Definition](",
"https://github.com/monero-project/monero/blob/",
stringify!($monero_code_commit),
"/src/rpc/",
stringify!($monero_code_filename),
".",
stringify!($monero_code_filename_extension),
"#L",
stringify!($monero_code_line_start),
"-L",
stringify!($monero_code_line_end),
"), [documentation](",
"https://www.getmonero.org/resources/developer-guides/daemon-rpc.html",
"#",
stringify!($monero_daemon_rpc_doc_link),
"), [",
$request_or_response,
"](",
stringify!($request_or_response_type),
")."
)
};
}
pub(crate) use __define_request_and_response_doc;
//---------------------------------------------------------------------------------------------------- Macro
/// Output a string link to `monerod` source code.
macro_rules! monero_definition_link {
(
$commit:ident, // Git commit hash
$file_path:literal, // File path within `monerod`'s `src/`, e.g. `rpc/core_rpc_server_commands_defs.h`
$start:literal$(..=$end:literal)? // File lines, e.g. `0..=123` or `0`
) => {
concat!(
"[Definition](https://github.com/monero-project/monero/blob/",
stringify!($commit),
"/src/",
$file_path,
"#L",
stringify!($start),
$(
"-L",
stringify!($end),
)?
")."
)
};
}
pub(crate) use monero_definition_link;