From bd375eae40acfad7c8d0205bb10afd0b78e424d2 Mon Sep 17 00:00:00 2001 From: hinto-janai Date: Thu, 1 Aug 2024 17:04:22 -0400 Subject: [PATCH] rpc-types: add traits and `enum` requests/responses (#241) apply diff --- rpc/types/README.md | 8 ++ rpc/types/src/bin.rs | 67 ++++++++++ rpc/types/src/json.rs | 257 ++++++++++++++++++++++++++++++++++---- rpc/types/src/lib.rs | 2 + rpc/types/src/macros.rs | 93 +++++++++++--- rpc/types/src/other.rs | 238 +++++++++++++++++++++++++++++++---- rpc/types/src/rpc_call.rs | 96 ++++++++++++++ 7 files changed, 700 insertions(+), 61 deletions(-) create mode 100644 rpc/types/src/rpc_call.rs diff --git a/rpc/types/README.md b/rpc/types/README.md index 566cca7e..b5a4f65f 100644 --- a/rpc/types/README.md +++ b/rpc/types/README.md @@ -7,6 +7,8 @@ This crate ports the types used in Monero's RPC interface, including: - Mixed types - Other commonly used RPC types +It also includes some traits for these types. + # Modules This crate's types are split in the following manner: @@ -94,6 +96,12 @@ The invariants that can be relied upon: - Types in [`bin`] will implement `epee` correctly - Misc types will implement `serde/epee` correctly as needed +# Requests and responses +For `enum`s that encapsulate all request/response types, see: +- [`crate::json::JsonRpcRequest`] & [`crate::json::JsonRpcResponse`] +- [`crate::bin::BinRequest`] & [`crate::bin::BinResponse`] +- [`crate::other::OtherRequest`] & [`crate::other::OtherResponse`] + # Feature flags List of feature flags for `cuprate-rpc-types`. diff --git a/rpc/types/src/bin.rs b/rpc/types/src/bin.rs index c801c69e..278e5352 100644 --- a/rpc/types/src/bin.rs +++ b/rpc/types/src/bin.rs @@ -28,6 +28,7 @@ use crate::{ HardforkEntry, HistogramEntry, OutKeyBin, OutputDistributionData, Peer, PoolInfoExtent, PoolTxInfo, SetBan, Span, Status, TxBacklogEntry, }, + rpc_call::{RpcCall, RpcCallValue}, }; //---------------------------------------------------------------------------------------------------- Definitions @@ -393,6 +394,72 @@ impl EpeeObject for GetBlocksResponse { } } +//---------------------------------------------------------------------------------------------------- Request +/// Binary requests. +/// +/// This enum contains all [`crate::bin`] requests. +/// +/// See also: [`BinResponse`]. +#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))] +#[cfg_attr(feature = "serde", serde(untagged))] +#[allow(missing_docs)] +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub enum BinRequest { + GetBlocks(GetBlocksRequest), + GetBlocksByHeight(GetBlocksByHeightRequest), + GetHashes(GetHashesRequest), + GetOutputIndexes(GetOutputIndexesRequest), + GetOuts(GetOutsRequest), + GetTransactionPoolHashes(GetTransactionPoolHashesRequest), + GetOutputDistribution(crate::json::GetOutputDistributionRequest), +} + +impl RpcCallValue for BinRequest { + fn is_restricted(&self) -> bool { + match self { + Self::GetBlocks(x) => x.is_restricted(), + Self::GetBlocksByHeight(x) => x.is_restricted(), + Self::GetHashes(x) => x.is_restricted(), + Self::GetOutputIndexes(x) => x.is_restricted(), + Self::GetOuts(x) => x.is_restricted(), + Self::GetTransactionPoolHashes(x) => x.is_restricted(), + Self::GetOutputDistribution(x) => x.is_restricted(), + } + } + + fn is_empty(&self) -> bool { + match self { + Self::GetBlocks(x) => x.is_empty(), + Self::GetBlocksByHeight(x) => x.is_empty(), + Self::GetHashes(x) => x.is_empty(), + Self::GetOutputIndexes(x) => x.is_empty(), + Self::GetOuts(x) => x.is_empty(), + Self::GetTransactionPoolHashes(x) => x.is_empty(), + Self::GetOutputDistribution(x) => x.is_empty(), + } + } +} + +//---------------------------------------------------------------------------------------------------- Response +/// Binary responses. +/// +/// This enum contains all [`crate::bin`] responses. +/// +/// See also: [`BinRequest`]. +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))] +#[cfg_attr(feature = "serde", serde(untagged))] +#[allow(missing_docs)] +pub enum BinResponse { + GetBlocks(GetBlocksResponse), + GetBlocksByHeight(GetBlocksByHeightResponse), + GetHashes(GetHashesResponse), + GetOutputIndexes(GetOutputIndexesResponse), + GetOuts(GetOutsResponse), + GetTransactionPoolHashes(GetTransactionPoolHashesResponse), + GetOutputDistribution(crate::json::GetOutputDistributionResponse), +} + //---------------------------------------------------------------------------------------------------- Tests #[cfg(test)] mod test { diff --git a/rpc/types/src/json.rs b/rpc/types/src/json.rs index dd2e6483..49710613 100644 --- a/rpc/types/src/json.rs +++ b/rpc/types/src/json.rs @@ -3,6 +3,9 @@ //! 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 +#[cfg(feature = "serde")] +use serde::{Deserialize, Serialize}; + use crate::{ base::{AccessResponseBase, ResponseBase}, defaults::{ @@ -16,6 +19,7 @@ use crate::{ GetMinerDataTxBacklogEntry, HardforkEntry, HistogramEntry, OutputDistributionData, SetBan, Span, Status, SyncInfoPeer, TxBacklogEntry, }, + rpc_call::RpcCallValue, }; //---------------------------------------------------------------------------------------------------- Macro @@ -93,7 +97,17 @@ define_request_and_response! { cc73fe71162d564ffda8e549b79a350bca53c454 => core_rpc_server_commands_defs.h => 943..=994, // The base type name. - GetBlockTemplate, + // + // After the type name, 2 optional idents are allowed: + // - `restricted` + // - `empty` + // + // These have to be within `()` and will affect the + // [`crate::RpcCall`] implementation on the request type. + // + // This type is not either restricted or empty so nothing is + // here, but the correct syntax is shown in a comment below: + GetBlockTemplate /* (restricted, empty) */, // The request type. // @@ -218,7 +232,7 @@ define_request_and_response! { get_block_count, cc73fe71162d564ffda8e549b79a350bca53c454 => core_rpc_server_commands_defs.h => 919..=933, - GetBlockCount, + GetBlockCount (empty), // There are no request fields specified, // this will cause the macro to generate a @@ -300,7 +314,7 @@ define_request_and_response! { cc73fe71162d564ffda8e549b79a350bca53c454 => core_rpc_server_commands_defs.h => 1130..=1161, - GenerateBlocks, + GenerateBlocks (restricted), #[doc = serde_doc_test!( GENERATE_BLOCKS_REQUEST => GenerateBlocksRequest { @@ -633,7 +647,7 @@ define_request_and_response! { cc73fe71162d564ffda8e549b79a350bca53c454 => core_rpc_server_commands_defs.h => 1734..=1754, - GetConnections, + GetConnections (restricted, empty), Request {}, @@ -708,7 +722,7 @@ define_request_and_response! { get_info, cc73fe71162d564ffda8e549b79a350bca53c454 => core_rpc_server_commands_defs.h => 693..=789, - GetInfo, + GetInfo (empty), Request {}, #[doc = serde_doc_test!( @@ -802,7 +816,7 @@ define_request_and_response! { hard_fork_info, cc73fe71162d564ffda8e549b79a350bca53c454 => core_rpc_server_commands_defs.h => 1958..=1995, - HardForkInfo, + HardForkInfo (empty), Request {}, #[doc = serde_doc_test!( @@ -834,7 +848,8 @@ define_request_and_response! { set_bans, cc73fe71162d564ffda8e549b79a350bca53c454 => core_rpc_server_commands_defs.h => 2032..=2067, - SetBans, + + SetBans (restricted), #[doc = serde_doc_test!( SET_BANS_REQUEST => SetBansRequest { @@ -862,7 +877,7 @@ define_request_and_response! { get_bans, cc73fe71162d564ffda8e549b79a350bca53c454 => core_rpc_server_commands_defs.h => 1997..=2030, - GetBans, + GetBans (restricted, empty), Request {}, #[doc = serde_doc_test!( @@ -891,7 +906,8 @@ define_request_and_response! { banned, cc73fe71162d564ffda8e549b79a350bca53c454 => core_rpc_server_commands_defs.h => 2069..=2094, - Banned, + + Banned (restricted), #[doc = serde_doc_test!( BANNED_REQUEST => BannedRequest { @@ -920,7 +936,8 @@ define_request_and_response! { flush_txpool, cc73fe71162d564ffda8e549b79a350bca53c454 => core_rpc_server_commands_defs.h => 2096..=2116, - FlushTransactionPool, + + FlushTransactionPool (restricted), #[doc = serde_doc_test!( FLUSH_TRANSACTION_POOL_REQUEST => FlushTransactionPoolRequest { @@ -986,7 +1003,7 @@ define_request_and_response! { cc73fe71162d564ffda8e549b79a350bca53c454 => core_rpc_server_commands_defs.h => 2213..=2248, - GetCoinbaseTxSum, + GetCoinbaseTxSum (restricted), #[doc = serde_doc_test!( GET_COINBASE_TX_SUM_REQUEST => GetCoinbaseTxSumRequest { @@ -1025,7 +1042,7 @@ define_request_and_response! { cc73fe71162d564ffda8e549b79a350bca53c454 => core_rpc_server_commands_defs.h => 2170..=2211, - GetVersion, + GetVersion (empty), Request {}, #[doc = serde_doc_test!( @@ -1116,7 +1133,7 @@ define_request_and_response! { get_fee_estimate, cc73fe71162d564ffda8e549b79a350bca53c454 => core_rpc_server_commands_defs.h => 2250..=2277, - GetFeeEstimate, + GetFeeEstimate (empty), Request {}, #[doc = serde_doc_test!( @@ -1138,7 +1155,7 @@ define_request_and_response! { get_alternate_chains, cc73fe71162d564ffda8e549b79a350bca53c454 => core_rpc_server_commands_defs.h => 2279..=2310, - GetAlternateChains, + GetAlternateChains (restricted, empty), Request {}, #[doc = serde_doc_test!( @@ -1178,7 +1195,7 @@ define_request_and_response! { cc73fe71162d564ffda8e549b79a350bca53c454 => core_rpc_server_commands_defs.h => 2361..=2381, - RelayTx, + RelayTx (restricted), #[doc = serde_doc_test!( RELAY_TX_REQUEST => RelayTxRequest { @@ -1205,7 +1222,8 @@ define_request_and_response! { cc73fe71162d564ffda8e549b79a350bca53c454 => core_rpc_server_commands_defs.h => 2383..=2443, - SyncInfo, + SyncInfo (restricted, empty), + Request {}, #[doc = serde_doc_test!( @@ -1294,7 +1312,7 @@ define_request_and_response! { get_txpool_backlog, cc73fe71162d564ffda8e549b79a350bca53c454 => core_rpc_server_commands_defs.h => 1637..=1664, - GetTransactionPoolBacklog, + GetTransactionPoolBacklog (empty), Request {}, // TODO: enable test after binary string impl. @@ -1361,7 +1379,7 @@ define_request_and_response! { get_miner_data, cc73fe71162d564ffda8e549b79a350bca53c454 => core_rpc_server_commands_defs.h => 996..=1044, - GetMinerData, + GetMinerData (empty), Request {}, #[doc = serde_doc_test!( @@ -1405,7 +1423,7 @@ define_request_and_response! { cc73fe71162d564ffda8e549b79a350bca53c454 => core_rpc_server_commands_defs.h => 2747..=2772, - PruneBlockchain, + PruneBlockchain (restricted), #[derive(Copy)] #[doc = serde_doc_test!( @@ -1435,7 +1453,7 @@ define_request_and_response! { cc73fe71162d564ffda8e549b79a350bca53c454 => core_rpc_server_commands_defs.h => 1046..=1066, - CalcPow, + CalcPow (restricted), #[doc = serde_doc_test!( CALC_POW_REQUEST => CalcPowRequest { @@ -1469,7 +1487,7 @@ define_request_and_response! { cc73fe71162d564ffda8e549b79a350bca53c454 => core_rpc_server_commands_defs.h => 2774..=2796, - FlushCache, + FlushCache (restricted), #[derive(Copy)] #[doc = serde_doc_test!( @@ -1534,6 +1552,203 @@ define_request_and_response! { } } +define_request_and_response! { + UNDOCUMENTED_METHOD, + cc73fe71162d564ffda8e549b79a350bca53c454 => + core_rpc_server_commands_defs.h => 2798..=2823, + + GetTxIdsLoose, + + Request { + txid_template: String, + num_matching_bits: u32, + }, + ResponseBase { + txids: Vec, + } +} + +//---------------------------------------------------------------------------------------------------- Request +/// JSON-RPC requests. +/// +/// This enum contains all [`crate::json`] requests. +/// +/// See also: [`JsonRpcResponse`]. +/// +/// TODO: document and test (de)serialization behavior after figuring out `method/params`. +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))] +#[cfg_attr( + feature = "serde", + serde(rename_all = "snake_case", tag = "method", content = "params") +)] +#[allow(missing_docs)] +pub enum JsonRpcRequest { + GetBlockCount(GetBlockCountRequest), + OnGetBlockHash(OnGetBlockHashRequest), + SubmitBlock(SubmitBlockRequest), + GenerateBlocks(GenerateBlocksRequest), + GetLastBlockHeader(GetLastBlockHeaderRequest), + GetBlockHeaderByHash(GetBlockHeaderByHashRequest), + GetBlockHeaderByHeight(GetBlockHeaderByHeightRequest), + GetBlockHeadersRange(GetBlockHeadersRangeRequest), + GetBlock(GetBlockRequest), + GetConnections(GetConnectionsRequest), + GetInfo(GetInfoRequest), + HardForkInfo(HardForkInfoRequest), + SetBans(SetBansRequest), + GetBans(GetBansRequest), + Banned(BannedRequest), + FlushTransactionPool(FlushTransactionPoolRequest), + GetOutputHistogram(GetOutputHistogramRequest), + GetCoinbaseTxSum(GetCoinbaseTxSumRequest), + GetVersion(GetVersionRequest), + GetFeeEstimate(GetFeeEstimateRequest), + GetAlternateChains(GetAlternateChainsRequest), + RelayTx(RelayTxRequest), + SyncInfo(SyncInfoRequest), + GetTransactionPoolBacklog(GetTransactionPoolBacklogRequest), + GetMinerData(GetMinerDataRequest), + PruneBlockchain(PruneBlockchainRequest), + CalcPow(CalcPowRequest), + FlushCache(FlushCacheRequest), + AddAuxPow(AddAuxPowRequest), + GetTxIdsLoose(GetTxIdsLooseRequest), +} + +impl RpcCallValue for JsonRpcRequest { + fn is_restricted(&self) -> bool { + match self { + Self::GetBlockCount(x) => x.is_restricted(), + Self::OnGetBlockHash(x) => x.is_restricted(), + Self::SubmitBlock(x) => x.is_restricted(), + Self::GetLastBlockHeader(x) => x.is_restricted(), + Self::GetBlockHeaderByHash(x) => x.is_restricted(), + Self::GetBlockHeaderByHeight(x) => x.is_restricted(), + Self::GetBlockHeadersRange(x) => x.is_restricted(), + Self::GetBlock(x) => x.is_restricted(), + Self::GetInfo(x) => x.is_restricted(), + Self::HardForkInfo(x) => x.is_restricted(), + Self::GetOutputHistogram(x) => x.is_restricted(), + Self::GetVersion(x) => x.is_restricted(), + Self::GetFeeEstimate(x) => x.is_restricted(), + Self::GetTransactionPoolBacklog(x) => x.is_restricted(), + Self::GetMinerData(x) => x.is_restricted(), + Self::AddAuxPow(x) => x.is_restricted(), + Self::GetTxIdsLoose(x) => x.is_restricted(), + Self::GenerateBlocks(x) => x.is_restricted(), + Self::GetConnections(x) => x.is_restricted(), + Self::SetBans(x) => x.is_restricted(), + Self::GetBans(x) => x.is_restricted(), + Self::Banned(x) => x.is_restricted(), + Self::FlushTransactionPool(x) => x.is_restricted(), + Self::GetCoinbaseTxSum(x) => x.is_restricted(), + Self::GetAlternateChains(x) => x.is_restricted(), + Self::RelayTx(x) => x.is_restricted(), + Self::SyncInfo(x) => x.is_restricted(), + Self::PruneBlockchain(x) => x.is_restricted(), + Self::CalcPow(x) => x.is_restricted(), + Self::FlushCache(x) => x.is_restricted(), + } + } + + fn is_empty(&self) -> bool { + match self { + Self::GetBlockCount(x) => x.is_empty(), + Self::OnGetBlockHash(x) => x.is_empty(), + Self::SubmitBlock(x) => x.is_empty(), + Self::GetLastBlockHeader(x) => x.is_empty(), + Self::GetBlockHeaderByHash(x) => x.is_empty(), + Self::GetBlockHeaderByHeight(x) => x.is_empty(), + Self::GetBlockHeadersRange(x) => x.is_empty(), + Self::GetBlock(x) => x.is_empty(), + Self::GetInfo(x) => x.is_empty(), + Self::HardForkInfo(x) => x.is_empty(), + Self::GetOutputHistogram(x) => x.is_empty(), + Self::GetVersion(x) => x.is_empty(), + Self::GetFeeEstimate(x) => x.is_empty(), + Self::GetTransactionPoolBacklog(x) => x.is_empty(), + Self::GetMinerData(x) => x.is_empty(), + Self::AddAuxPow(x) => x.is_empty(), + Self::GetTxIdsLoose(x) => x.is_empty(), + Self::GenerateBlocks(x) => x.is_empty(), + Self::GetConnections(x) => x.is_empty(), + Self::SetBans(x) => x.is_empty(), + Self::GetBans(x) => x.is_empty(), + Self::Banned(x) => x.is_empty(), + Self::FlushTransactionPool(x) => x.is_empty(), + Self::GetCoinbaseTxSum(x) => x.is_empty(), + Self::GetAlternateChains(x) => x.is_empty(), + Self::RelayTx(x) => x.is_empty(), + Self::SyncInfo(x) => x.is_empty(), + Self::PruneBlockchain(x) => x.is_empty(), + Self::CalcPow(x) => x.is_empty(), + Self::FlushCache(x) => x.is_empty(), + } + } +} + +//---------------------------------------------------------------------------------------------------- Response +/// JSON-RPC responses. +/// +/// This enum contains all [`crate::json`] responses. +/// +/// See also: [`JsonRpcRequest`]. +/// +/// # (De)serialization +/// The `serde` implementation will (de)serialize from +/// the inner variant itself, e.g. [`JsonRpcRequest::Banned`] +/// has the same (de)serialization as [`BannedResponse`]. +/// +/// ```rust +/// use cuprate_rpc_types::{misc::*, json::*}; +/// +/// let response = JsonRpcResponse::Banned(BannedResponse { +/// banned: true, +/// seconds: 123, +/// status: Status::Ok, +/// }); +/// let json = serde_json::to_string(&response).unwrap(); +/// assert_eq!(json, r#"{"banned":true,"seconds":123,"status":"OK"}"#); +/// let response: JsonRpcResponse = serde_json::from_str(&json).unwrap(); +/// ``` +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))] +#[cfg_attr(feature = "serde", serde(untagged, rename_all = "snake_case"))] +#[allow(missing_docs)] +pub enum JsonRpcResponse { + GetBlockCount(GetBlockCountResponse), + OnGetBlockHash(OnGetBlockHashResponse), + SubmitBlock(SubmitBlockResponse), + GenerateBlocks(GenerateBlocksResponse), + GetLastBlockHeader(GetLastBlockHeaderResponse), + GetBlockHeaderByHash(GetBlockHeaderByHashResponse), + GetBlockHeaderByHeight(GetBlockHeaderByHeightResponse), + GetBlockHeadersRange(GetBlockHeadersRangeResponse), + GetBlock(GetBlockResponse), + GetConnections(GetConnectionsResponse), + GetInfo(GetInfoResponse), + HardForkInfo(HardForkInfoResponse), + SetBans(SetBansResponse), + GetBans(GetBansResponse), + Banned(BannedResponse), + FlushTransactionPool(FlushTransactionPoolResponse), + GetOutputHistogram(GetOutputHistogramResponse), + GetCoinbaseTxSum(GetCoinbaseTxSumResponse), + GetVersion(GetVersionResponse), + GetFeeEstimate(GetFeeEstimateResponse), + GetAlternateChains(GetAlternateChainsResponse), + RelayTx(RelayTxResponse), + SyncInfo(SyncInfoResponse), + GetTransactionPoolBacklog(GetTransactionPoolBacklogResponse), + GetMinerData(GetMinerDataResponse), + PruneBlockchain(PruneBlockchainResponse), + CalcPow(CalcPowResponse), + FlushCache(FlushCacheResponse), + AddAuxPow(AddAuxPowResponse), + GetTxIdsLoose(GetTxIdsLooseResponse), +} + //---------------------------------------------------------------------------------------------------- Tests #[cfg(test)] mod test { diff --git a/rpc/types/src/lib.rs b/rpc/types/src/lib.rs index d0d1e00d..b48f22ed 100644 --- a/rpc/types/src/lib.rs +++ b/rpc/types/src/lib.rs @@ -112,6 +112,7 @@ mod constants; mod defaults; mod free; mod macros; +mod rpc_call; #[cfg(feature = "serde")] mod serde; @@ -127,3 +128,4 @@ pub use constants::{ CORE_RPC_STATUS_PAYMENT_REQUIRED, CORE_RPC_VERSION, CORE_RPC_VERSION_MAJOR, CORE_RPC_VERSION_MINOR, }; +pub use rpc_call::{RpcCall, RpcCallValue}; diff --git a/rpc/types/src/macros.rs b/rpc/types/src/macros.rs index fa0d5188..60ffa90a 100644 --- a/rpc/types/src/macros.rs +++ b/rpc/types/src/macros.rs @@ -60,7 +60,14 @@ macro_rules! define_request_and_response { // Attributes added here will apply to _both_ // request and response types. $( #[$type_attr:meta] )* - $type_name:ident, + // After the type name, 2 optional idents are allowed: + // + // - `restricted` + // - `empty` + // + // These have to be within `()` and will affect the + // [`crate::RpcCall`] implementation on the request type. + $type_name:ident $(($restricted:ident $(, $empty:ident)?))?, // The request type (and any doc comments, derives, etc). $( #[$request_type_attr:meta] )* @@ -100,7 +107,7 @@ macro_rules! define_request_and_response { $( #[$type_attr] )* /// $( #[$request_type_attr] )* - [<$type_name Request>] { + [<$type_name Request>] $(($restricted $(, $empty)?))? { $( $( #[$request_field_attr] )* $request_field: $request_field_type @@ -141,6 +148,69 @@ macro_rules! define_request_and_response { } pub(crate) use define_request_and_response; +//---------------------------------------------------------------------------------------------------- impl_rpc_call +/// Implement [`crate::RpcCall`] and [`crate::RpcCallValue`] on request types. +/// +/// Input for this is: +/// `$REQUEST_TYPE restricted empty` +/// where `restricted` and `empty` are the idents themselves. +/// The implementation for [`crate::RpcCall`] will change +/// depending if they exist or not. +macro_rules! impl_rpc_call { + // Restricted and empty RPC calls. + ($t:ident, restricted, empty) => { + impl $crate::RpcCall for $t { + const IS_RESTRICTED: bool = true; + const IS_EMPTY: bool = true; + } + + impl From<()> for $t { + fn from(_: ()) -> Self { + Self {} + } + } + + impl From<$t> for () { + fn from(_: $t) -> Self {} + } + }; + + // Empty RPC calls. + ($t:ident, empty) => { + impl $crate::RpcCall for $t { + const IS_RESTRICTED: bool = false; + const IS_EMPTY: bool = true; + } + + impl From<()> for $t { + fn from(_: ()) -> Self { + Self {} + } + } + + impl From<$t> for () { + fn from(_: $t) -> Self {} + } + }; + + // Restricted RPC calls. + ($t:ident, restricted) => { + impl $crate::RpcCall for $t { + const IS_RESTRICTED: bool = true; + const IS_EMPTY: bool = false; + } + }; + + // Not restrict or empty RPC calls. + ($t:ident) => { + impl $crate::RpcCall for $t { + const IS_RESTRICTED: bool = false; + const IS_EMPTY: bool = false; + } + }; +} +pub(crate) use impl_rpc_call; + //---------------------------------------------------------------------------------------------------- define_request /// Define a request type. /// @@ -152,22 +222,7 @@ macro_rules! define_request { // 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 { + $t:ident $(($restricted:ident $(, $empty:ident)?))? { // And any fields. $( $( #[$field_attr:meta] )* // field attributes @@ -193,6 +248,8 @@ macro_rules! define_request { )* } + $crate::macros::impl_rpc_call!($t $(, $restricted $(, $empty)?)?); + #[cfg(feature = "epee")] ::cuprate_epee_encoding::epee_object! { $t, diff --git a/rpc/types/src/other.rs b/rpc/types/src/other.rs index c1407778..9457250f 100644 --- a/rpc/types/src/other.rs +++ b/rpc/types/src/other.rs @@ -3,6 +3,9 @@ //! 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 +#[cfg(feature = "serde")] +use serde::{Deserialize, Serialize}; + use crate::{ base::{AccessResponseBase, ResponseBase}, defaults::{default_false, default_string, default_true, default_vec, default_zero}, @@ -11,6 +14,8 @@ use crate::{ GetOutputsOut, KeyImageSpentStatus, OutKey, Peer, PublicNode, SpentKeyImageInfo, Status, TxEntry, TxInfo, TxpoolStats, }, + rpc_call::RpcCall, + RpcCallValue, }; //---------------------------------------------------------------------------------------------------- Macro @@ -93,7 +98,7 @@ define_request_and_response! { get_height, cc73fe71162d564ffda8e549b79a350bca53c454 => core_rpc_server_commands_defs.h => 138..=160, - GetHeight, + GetHeight (empty), Request {}, #[doc = serde_doc_test!( @@ -146,7 +151,7 @@ define_request_and_response! { get_alt_blocks_hashes, cc73fe71162d564ffda8e549b79a350bca53c454 => core_rpc_server_commands_defs.h => 288..=308, - GetAltBlocksHashes, + GetAltBlocksHashes (empty), Request {}, #[doc = serde_doc_test!( @@ -258,7 +263,7 @@ define_request_and_response! { cc73fe71162d564ffda8e549b79a350bca53c454 => core_rpc_server_commands_defs.h => 665..=691, - StartMining, + StartMining (restricted), #[doc = serde_doc_test!( START_MINING_REQUEST => StartMiningRequest { @@ -287,7 +292,7 @@ define_request_and_response! { stop_mining, cc73fe71162d564ffda8e549b79a350bca53c454 => core_rpc_server_commands_defs.h => 825..=843, - StopMining, + StopMining (restricted, empty), Request {}, #[doc = serde_doc_test!( @@ -302,7 +307,7 @@ define_request_and_response! { mining_status, cc73fe71162d564ffda8e549b79a350bca53c454 => core_rpc_server_commands_defs.h => 846..=895, - MiningStatus, + MiningStatus (restricted), Request {}, #[doc = serde_doc_test!( @@ -348,7 +353,7 @@ define_request_and_response! { save_bc, cc73fe71162d564ffda8e549b79a350bca53c454 => core_rpc_server_commands_defs.h => 898..=916, - SaveBc, + SaveBc (restricted), Request {}, #[doc = serde_doc_test!( @@ -364,7 +369,7 @@ define_request_and_response! { cc73fe71162d564ffda8e549b79a350bca53c454 => core_rpc_server_commands_defs.h => 1369..=1417, - GetPeerList, + GetPeerList (restricted), #[doc = serde_doc_test!( GET_PEER_LIST_REQUEST => GetPeerListRequest { @@ -446,7 +451,8 @@ define_request_and_response! { set_log_hash_rate, cc73fe71162d564ffda8e549b79a350bca53c454 => core_rpc_server_commands_defs.h => 1450..=1470, - SetLogHashRate, + + SetLogHashRate (restricted), #[derive(Copy)] #[doc = serde_doc_test!( @@ -471,7 +477,7 @@ define_request_and_response! { cc73fe71162d564ffda8e549b79a350bca53c454 => core_rpc_server_commands_defs.h => 1450..=1470, - SetLogLevel, + SetLogLevel (restricted), #[derive(Copy)] #[doc = serde_doc_test!( @@ -496,7 +502,7 @@ define_request_and_response! { cc73fe71162d564ffda8e549b79a350bca53c454 => core_rpc_server_commands_defs.h => 1494..=1517, - SetLogCategories, + SetLogCategories (restricted), #[doc = serde_doc_test!( SET_LOG_CATEGORIES_REQUEST => SetLogCategoriesRequest { @@ -523,7 +529,7 @@ define_request_and_response! { cc73fe71162d564ffda8e549b79a350bca53c454 => core_rpc_server_commands_defs.h => 1785..=1812, - SetBootstrapDaemon, + SetBootstrapDaemon (restricted), #[doc = serde_doc_test!( SET_BOOTSTRAP_DAEMON_REQUEST => SetBootstrapDaemonRequest { @@ -555,7 +561,7 @@ define_request_and_response! { cc73fe71162d564ffda8e549b79a350bca53c454 => core_rpc_server_commands_defs.h => 1569..=1591, - GetTransactionPool, + GetTransactionPool (empty), Request {}, #[doc = serde_doc_test!(GET_TRANSACTION_POOL_RESPONSE)] @@ -570,7 +576,7 @@ define_request_and_response! { cc73fe71162d564ffda8e549b79a350bca53c454 => core_rpc_server_commands_defs.h => 1712..=1732, - GetTransactionPoolStats, + GetTransactionPoolStats (empty), Request {}, #[doc = serde_doc_test!( @@ -614,7 +620,7 @@ define_request_and_response! { cc73fe71162d564ffda8e549b79a350bca53c454 => core_rpc_server_commands_defs.h => 1814..=1831, - StopDaemon, + StopDaemon (restricted, empty), Request {}, #[doc = serde_doc_test!( @@ -632,7 +638,7 @@ define_request_and_response! { cc73fe71162d564ffda8e549b79a350bca53c454 => core_rpc_server_commands_defs.h => 1852..=1874, - GetLimit, + GetLimit (empty), Request {}, #[doc = serde_doc_test!( @@ -653,7 +659,8 @@ define_request_and_response! { cc73fe71162d564ffda8e549b79a350bca53c454 => core_rpc_server_commands_defs.h => 1876..=1903, - SetLimit, + SetLimit (restricted), + #[doc = serde_doc_test!( SET_LIMIT_REQUEST => SetLimitRequest { limit_down: 1024, @@ -684,7 +691,7 @@ define_request_and_response! { cc73fe71162d564ffda8e549b79a350bca53c454 => core_rpc_server_commands_defs.h => 1876..=1903, - OutPeers, + OutPeers (restricted), #[doc = serde_doc_test!( OUT_PEERS_REQUEST => OutPeersRequest { @@ -708,12 +715,26 @@ define_request_and_response! { } } +define_request_and_response! { + in_peers, + cc73fe71162d564ffda8e549b79a350bca53c454 => + core_rpc_server_commands_defs.h => 1932..=1956, + InPeers (restricted), + Request { + set: bool = default_true(), "default_true", + in_peers: u32, + }, + ResponseBase { + in_peers: u32, + } +} + define_request_and_response! { get_net_stats, cc73fe71162d564ffda8e549b79a350bca53c454 => core_rpc_server_commands_defs.h => 793..=822, - GetNetStats, + GetNetStats (restricted, empty), Request {}, #[doc = serde_doc_test!( @@ -786,7 +807,7 @@ define_request_and_response! { cc73fe71162d564ffda8e549b79a350bca53c454 => core_rpc_server_commands_defs.h => 2324..=2359, - Update, + Update (restricted), #[doc = serde_doc_test!( UPDATE_REQUEST => UpdateRequest { @@ -825,7 +846,7 @@ define_request_and_response! { cc73fe71162d564ffda8e549b79a350bca53c454 => core_rpc_server_commands_defs.h => 2722..=2745, - PopBlocks, + PopBlocks (restricted), #[doc = serde_doc_test!( POP_BLOCKS_REQUEST => PopBlocksRequest { @@ -852,7 +873,7 @@ define_request_and_response! { cc73fe71162d564ffda8e549b79a350bca53c454 => core_rpc_server_commands_defs.h => 1615..=1635, - GetTransactionPoolHashes, + GetTransactionPoolHashes (empty), Request {}, #[doc = serde_doc_test!( @@ -889,7 +910,8 @@ define_request_and_response! { UNDOCUMENTED_ENDPOINT, cc73fe71162d564ffda8e549b79a350bca53c454 => core_rpc_server_commands_defs.h => 1419..=1448, - GetPublicNodes, + + GetPublicNodes (restricted), #[doc = serde_doc_test!( GET_PUBLIC_NODES_REQUEST => GetPublicNodesRequest { @@ -930,6 +952,178 @@ define_request_and_response! { } } +//---------------------------------------------------------------------------------------------------- Request +/// Other JSON requests. +/// +/// This enum contains all [`crate::other`] requests. +/// +/// See also: [`OtherResponse`]. +/// +/// # (De)serialization +/// The `serde` implementation will (de)serialize from +/// the inner variant itself, e.g. [`OtherRequest::SetLogLevel`] +/// has the same (de)serialization as [`SetLogLevelRequest`]. +/// +/// ```rust +/// use cuprate_rpc_types::other::*; +/// +/// let request = OtherRequest::SetLogLevel(Default::default()); +/// let json = serde_json::to_string(&request).unwrap(); +/// assert_eq!(json, r#"{"level":0}"#); +/// ``` +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))] +#[cfg_attr(feature = "serde", serde(untagged))] +#[allow(missing_docs)] +pub enum OtherRequest { + GetHeight(GetHeightRequest), + GetTransactions(GetTransactionsRequest), + GetAltBlocksHashes(GetAltBlocksHashesRequest), + IsKeyImageSpent(IsKeyImageSpentRequest), + SendRawTransaction(SendRawTransactionRequest), + StartMining(StartMiningRequest), + StopMining(StopMiningRequest), + MiningStatus(MiningStatusRequest), + SaveBc(SaveBcRequest), + GetPeerList(GetPeerListRequest), + SetLogHashRate(SetLogHashRateRequest), + SetLogLevel(SetLogLevelRequest), + SetLogCategories(SetLogCategoriesRequest), + SetBootstrapDaemon(SetBootstrapDaemonRequest), + GetTransactionPool(GetTransactionPoolRequest), + GetTransactionPoolStats(GetTransactionPoolStatsRequest), + StopDaemon(StopDaemonRequest), + GetLimit(GetLimitRequest), + SetLimit(SetLimitRequest), + OutPeers(OutPeersRequest), + InPeers(InPeersRequest), + GetNetStats(GetNetStatsRequest), + GetOuts(GetOutsRequest), + Update(UpdateRequest), + PopBlocks(PopBlocksRequest), + GetTransactionPoolHashes(GetTransactionPoolHashesRequest), + GetPublicNodes(GetPublicNodesRequest), +} + +impl RpcCallValue for OtherRequest { + fn is_restricted(&self) -> bool { + match self { + Self::GetHeight(x) => x.is_restricted(), + Self::GetTransactions(x) => x.is_restricted(), + Self::GetAltBlocksHashes(x) => x.is_restricted(), + Self::IsKeyImageSpent(x) => x.is_restricted(), + Self::SendRawTransaction(x) => x.is_restricted(), + Self::StartMining(x) => x.is_restricted(), + Self::StopMining(x) => x.is_restricted(), + Self::MiningStatus(x) => x.is_restricted(), + Self::SaveBc(x) => x.is_restricted(), + Self::GetPeerList(x) => x.is_restricted(), + Self::SetLogHashRate(x) => x.is_restricted(), + Self::SetLogLevel(x) => x.is_restricted(), + Self::SetLogCategories(x) => x.is_restricted(), + Self::SetBootstrapDaemon(x) => x.is_restricted(), + Self::GetTransactionPool(x) => x.is_restricted(), + Self::GetTransactionPoolStats(x) => x.is_restricted(), + Self::StopDaemon(x) => x.is_restricted(), + Self::GetLimit(x) => x.is_restricted(), + Self::SetLimit(x) => x.is_restricted(), + Self::OutPeers(x) => x.is_restricted(), + Self::InPeers(x) => x.is_restricted(), + Self::GetNetStats(x) => x.is_restricted(), + Self::GetOuts(x) => x.is_restricted(), + Self::Update(x) => x.is_restricted(), + Self::PopBlocks(x) => x.is_restricted(), + Self::GetTransactionPoolHashes(x) => x.is_restricted(), + Self::GetPublicNodes(x) => x.is_restricted(), + } + } + + fn is_empty(&self) -> bool { + match self { + Self::GetHeight(x) => x.is_empty(), + Self::GetTransactions(x) => x.is_empty(), + Self::GetAltBlocksHashes(x) => x.is_empty(), + Self::IsKeyImageSpent(x) => x.is_empty(), + Self::SendRawTransaction(x) => x.is_empty(), + Self::StartMining(x) => x.is_empty(), + Self::StopMining(x) => x.is_empty(), + Self::MiningStatus(x) => x.is_empty(), + Self::SaveBc(x) => x.is_empty(), + Self::GetPeerList(x) => x.is_empty(), + Self::SetLogHashRate(x) => x.is_empty(), + Self::SetLogLevel(x) => x.is_empty(), + Self::SetLogCategories(x) => x.is_empty(), + Self::SetBootstrapDaemon(x) => x.is_empty(), + Self::GetTransactionPool(x) => x.is_empty(), + Self::GetTransactionPoolStats(x) => x.is_empty(), + Self::StopDaemon(x) => x.is_empty(), + Self::GetLimit(x) => x.is_empty(), + Self::SetLimit(x) => x.is_empty(), + Self::OutPeers(x) => x.is_empty(), + Self::InPeers(x) => x.is_empty(), + Self::GetNetStats(x) => x.is_empty(), + Self::GetOuts(x) => x.is_empty(), + Self::Update(x) => x.is_empty(), + Self::PopBlocks(x) => x.is_empty(), + Self::GetTransactionPoolHashes(x) => x.is_empty(), + Self::GetPublicNodes(x) => x.is_empty(), + } + } +} + +//---------------------------------------------------------------------------------------------------- Response +/// Other JSON responses. +/// +/// This enum contains all [`crate::other`] responses. +/// +/// See also: [`OtherRequest`]. +/// +/// # (De)serialization +/// The `serde` implementation will (de)serialize from +/// the inner variant itself, e.g. [`OtherRequest::SetBootstrapDaemon`] +/// has the same (de)serialization as [`SetBootstrapDaemonResponse`]. +/// +/// ```rust +/// use cuprate_rpc_types::other::*; +/// +/// let response = OtherResponse::SetBootstrapDaemon(Default::default()); +/// let json = serde_json::to_string(&response).unwrap(); +/// assert_eq!(json, r#"{"status":"OK"}"#); +/// ``` +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))] +#[cfg_attr(feature = "serde", serde(untagged))] +#[allow(missing_docs)] +pub enum OtherResponse { + GetHeight(GetHeightResponse), + GetTransactions(GetTransactionsResponse), + GetAltBlocksHashes(GetAltBlocksHashesResponse), + IsKeyImageSpent(IsKeyImageSpentResponse), + SendRawTransaction(SendRawTransactionResponse), + StartMining(StartMiningResponse), + StopMining(StopMiningResponse), + MiningStatus(MiningStatusResponse), + SaveBc(SaveBcResponse), + GetPeerList(GetPeerListResponse), + SetLogHashRate(SetLogHashRateResponse), + SetLogLevel(SetLogLevelResponse), + SetLogCategories(SetLogCategoriesResponse), + SetBootstrapDaemon(SetBootstrapDaemonResponse), + GetTransactionPool(GetTransactionPoolResponse), + GetTransactionPoolStats(GetTransactionPoolStatsResponse), + StopDaemon(StopDaemonResponse), + GetLimit(GetLimitResponse), + SetLimit(SetLimitResponse), + OutPeers(OutPeersResponse), + InPeers(InPeersResponse), + GetNetStats(GetNetStatsResponse), + GetOuts(GetOutsResponse), + Update(UpdateResponse), + PopBlocks(PopBlocksResponse), + GetTransactionPoolHashes(GetTransactionPoolHashesResponse), + GetPublicNodes(GetPublicNodesResponse), +} + //---------------------------------------------------------------------------------------------------- Tests #[cfg(test)] mod test { diff --git a/rpc/types/src/rpc_call.rs b/rpc/types/src/rpc_call.rs new file mode 100644 index 00000000..5fb742e0 --- /dev/null +++ b/rpc/types/src/rpc_call.rs @@ -0,0 +1,96 @@ +//! RPC call metadata. + +//---------------------------------------------------------------------------------------------------- Import + +//---------------------------------------------------------------------------------------------------- RpcCall +/// Metadata about an RPC call. +/// +/// This trait describes some metadata about RPC requests. +/// +/// It is implemented on all request types within: +/// - [`crate::json`] +/// - [`crate::other`] +/// - [`crate::bin`] +/// +/// See also [`RpcCallValue`] for a dynamic by-value version of this trait. +pub trait RpcCall { + /// Is `true` if this RPC method should + /// only be allowed on local servers. + /// + /// If this is `false`, it should be + /// okay to execute the method even on restricted + /// RPC servers. + /// + /// ```rust + /// use cuprate_rpc_types::{RpcCall, json::*}; + /// + /// // Allowed method, even on restricted RPC servers (18089). + /// assert!(!GetBlockCountRequest::IS_RESTRICTED); + /// + /// // Restricted methods, only allowed + /// // for unrestricted RPC servers (18081). + /// assert!(GetConnectionsRequest::IS_RESTRICTED); + /// ``` + const IS_RESTRICTED: bool; + + /// Is `true` if this RPC method has no inputs, i.e. it is a `struct` with no fields. + /// + /// ```rust + /// use cuprate_rpc_types::{RpcCall, json::*}; + /// + /// assert!(GetBlockCountRequest::IS_EMPTY); + /// assert!(!OnGetBlockHashRequest::IS_EMPTY); + /// ``` + const IS_EMPTY: bool; +} + +//---------------------------------------------------------------------------------------------------- RpcCallValue +/// By-value version of [`RpcCall`]. +/// +/// This trait is a mirror of [`RpcCall`], +/// except it takes `self` by value instead +/// of being a `const` property. +/// +/// This exists for `enum`s where requests must be dynamically +/// `match`ed like [`JsonRpcRequest`](crate::json::JsonRpcRequest). +/// +/// All types that implement [`RpcCall`] automatically implement [`RpcCallValue`]. +pub trait RpcCallValue { + /// Same as [`RpcCall::IS_RESTRICTED`]. + /// + /// ```rust + /// use cuprate_rpc_types::{RpcCallValue, json::*}; + /// + /// assert!(!GetBlockCountRequest::default().is_restricted()); + /// assert!(GetConnectionsRequest::default().is_restricted()); + /// ``` + fn is_restricted(&self) -> bool; + + /// Same as [`RpcCall::IS_EMPTY`]. + /// + /// ```rust + /// use cuprate_rpc_types::{RpcCallValue, json::*}; + /// + /// assert!(GetBlockCountRequest::default().is_empty()); + /// assert!(!OnGetBlockHashRequest::default().is_empty()); + /// ``` + fn is_empty(&self) -> bool; +} + +impl RpcCallValue for T { + #[inline] + fn is_restricted(&self) -> bool { + Self::IS_RESTRICTED + } + + #[inline] + fn is_empty(&self) -> bool { + Self::IS_EMPTY + } +} + +//---------------------------------------------------------------------------------------------------- Tests +#[cfg(test)] +mod test { + // use super::*; +}