rpc: feature flags, macro changes, misc setup (#218)

* `serde/epee` feature flags

* modify type generator macros

* add `defaults.rs`

* add `free.rs`

* add `misc` module

* modify `base.rs`, `contants.rs`

* remove `binary_string.rs`, `status.rs`

* fix macro usage

* base: re-add `AccessRequestBase`

* fix default functions

* tx_entry: fix link
This commit is contained in:
hinto-janai 2024-07-09 17:58:02 -04:00 committed by GitHub
parent a82c08cc80
commit 136abf7edd
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
18 changed files with 1259 additions and 364 deletions

View file

@ -9,14 +9,16 @@ repository = "https://github.com/Cuprate/cuprate/tree/main/rpc/types"
keywords = ["cuprate", "rpc", "types", "monero"] keywords = ["cuprate", "rpc", "types", "monero"]
[features] [features]
default = [] default = ["serde", "epee"]
serde = ["dep:serde"]
epee = ["dep:cuprate-epee-encoding"]
[dependencies] [dependencies]
cuprate-epee-encoding = { path = "../../net/epee-encoding" } cuprate-epee-encoding = { path = "../../net/epee-encoding", optional = true }
monero-serai = { workspace = true } monero-serai = { workspace = true }
paste = { workspace = true } paste = { workspace = true }
serde = { workspace = true } serde = { workspace = true, optional = true }
[dev-dependencies] [dev-dependencies]
serde_json = { workspace = true } serde_json = { workspace = true }

View file

@ -10,13 +10,14 @@ This crate ports the types used in Monero's RPC interface, including:
# Modules # Modules
This crate's types are split in the following manner: This crate's types are split in the following manner:
This crate has 4 modules: | Module | Purpose |
- The root module; `cuprate_rpc_types` |--------|---------|
- [`json`] module; JSON types from the `/json_rpc` endpoint | The root module | Miscellaneous items, e.g. constants.
- [`bin`] module; Binary types from the binary endpoints | [`json`] | Contains JSON request/response (some mixed with binary) that all share the common `/json_rpc` endpoint. |
- [`other`] module; Misc JSON types from other endpoints | [`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`]). |
Miscellaneous types are found in the root module, e.g. [`crate::Status`]. | [`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). 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).
@ -30,23 +31,21 @@ However, each type will document:
# Naming # Naming
The naming for types within `{json,bin,other}` follow the following scheme: The naming for types within `{json,bin,other}` follow the following scheme:
- Convert the endpoint or method name into `UpperCamelCase` 1. Convert the endpoint or method name into `UpperCamelCase`
- Remove any suffix extension 1. Remove any suffix extension
1. Add `Request/Response` suffix
For example: For example:
| Endpoint/method | Crate location and name | | 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_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_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` | [`/get_height`](https://www.getmonero.org/resources/developer-guides/daemon-rpc.html#get_height) | [`other::GetHeightRequest`] & [`other::GetHeightResponse`]
TODO: fix doc links when types are ready.
# Mixed types # Mixed types
Note that some types within [`other`] mix JSON & binary together, i.e., Note that some types mix JSON & binary together, i.e., the message overall is JSON,
the message overall is JSON, however some fields contain binary however some fields contain binary values inside JSON strings, for example:
values inside JSON strings, for example:
```json ```json
{ {
@ -57,6 +56,20 @@ values inside JSON strings, for example:
} }
``` ```
`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::BinaryString`] instead of [`String`]. `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`].
TODO: list the specific types. 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`
# Feature flags
List of feature flags for `cuprate-rpc-types`.
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

@ -10,76 +10,44 @@
//! - <https://github.com/monero-project/monero/commit/2899379791b7542e4eb920b5d9d58cf232806937> //! - <https://github.com/monero-project/monero/commit/2899379791b7542e4eb920b5d9d58cf232806937>
//! - <https://github.com/monero-project/monero/issues/8722> //! - <https://github.com/monero-project/monero/issues/8722>
//! - <https://github.com/monero-project/monero/pull/8843> //! - <https://github.com/monero-project/monero/pull/8843>
//!
//! Note that this library doesn't use [`AccessRequestBase`](https://github.com/monero-project/monero/blob/cc73fe71162d564ffda8e549b79a350bca53c454/src/rpc/core_rpc_server_commands_defs.h#L114-L122) found in `monerod`
//! as the type is practically deprecated.
//!
//! Although, [`AccessResponseBase`] still exists as to allow
//! outputting the same JSON fields as `monerod` (even if deprecated).
//---------------------------------------------------------------------------------------------------- Import //---------------------------------------------------------------------------------------------------- Import
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
#[cfg(feature = "epee")]
use cuprate_epee_encoding::epee_object; use cuprate_epee_encoding::epee_object;
use crate::Status; use crate::{macros::monero_definition_link, misc::Status};
//---------------------------------------------------------------------------------------------------- Macro
/// Link the original `monerod` definition for RPC base types.
macro_rules! monero_rpc_base_link {
($start:literal..=$end:literal) => {
concat!(
"[Definition](https://github.com/monero-project/monero/blob/cc73fe71162d564ffda8e549b79a350bca53c454/src/rpc/core_rpc_server_commands_defs.h#L",
stringify!($start),
"-L",
stringify!($end),
")."
)
};
}
//---------------------------------------------------------------------------------------------------- Requests //---------------------------------------------------------------------------------------------------- Requests
/// The most common base for responses (nothing).
///
#[doc = monero_rpc_base_link!(95..=99)]
#[derive(
Copy, Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize,
)]
pub struct EmptyRequestBase;
cuprate_epee_encoding::epee_object! {
EmptyRequestBase,
}
/// A base for RPC request types that support RPC payment. /// A base for RPC request types that support RPC payment.
/// ///
#[doc = monero_rpc_base_link!(114..=122)] #[doc = monero_definition_link!(cc73fe71162d564ffda8e549b79a350bca53c454, "rpc/core_rpc_server_commands_defs.h", 114..=122)]
#[derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)] #[derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct AccessRequestBase { pub struct AccessRequestBase {
/// The RPC payment client. /// The RPC payment client.
pub client: String, pub client: String,
} }
cuprate_epee_encoding::epee_object! { #[cfg(feature = "epee")]
epee_object! {
AccessRequestBase, AccessRequestBase,
client: String, client: String,
} }
//---------------------------------------------------------------------------------------------------- Responses //---------------------------------------------------------------------------------------------------- Responses
/// An empty response base. #[doc = monero_definition_link!(cc73fe71162d564ffda8e549b79a350bca53c454, "rpc/core_rpc_server_commands_defs.h", 101..=112)]
///
/// This is for response types that do not contain
/// any extra fields, e.g. TODO.
// [`CalcPowResponse`](crate::json::CalcPowResponse).
#[derive(
Copy, Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize,
)]
pub struct EmptyResponseBase;
cuprate_epee_encoding::epee_object! {
EmptyResponseBase,
}
/// The most common base for responses. /// The most common base for responses.
/// #[derive(Copy, Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[doc = monero_rpc_base_link!(101..=112)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(
Copy, Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize,
)]
pub struct ResponseBase { pub struct ResponseBase {
/// General RPC error code. [`Status::Ok`] means everything looks good. /// General RPC error code. [`Status::Ok`] means everything looks good.
pub status: Status, pub status: Status,
@ -89,19 +57,20 @@ pub struct ResponseBase {
pub untrusted: bool, pub untrusted: bool,
} }
#[cfg(feature = "epee")]
epee_object! { epee_object! {
ResponseBase, ResponseBase,
status: Status, status: Status,
untrusted: bool, untrusted: bool,
} }
#[doc = monero_definition_link!(cc73fe71162d564ffda8e549b79a350bca53c454, "rpc/core_rpc_server_commands_defs.h", 124..=136)]
/// A base for RPC response types that support RPC payment. /// A base for RPC response types that support RPC payment.
/// #[derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[doc = monero_rpc_base_link!(124..=136)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
pub struct AccessResponseBase { pub struct AccessResponseBase {
/// A flattened [`ResponseBase`]. /// A flattened [`ResponseBase`].
#[serde(flatten)] #[cfg_attr(feature = "serde", serde(flatten))]
pub response_base: ResponseBase, pub response_base: ResponseBase,
/// If payment for RPC is enabled, the number of credits /// If payment for RPC is enabled, the number of credits
/// available to the requesting client. Otherwise, `0`. /// available to the requesting client. Otherwise, `0`.
@ -111,6 +80,7 @@ pub struct AccessResponseBase {
pub top_hash: String, pub top_hash: String,
} }
#[cfg(feature = "epee")]
epee_object! { epee_object! {
AccessResponseBase, AccessResponseBase,
credits: u64, credits: u64,

View file

@ -15,6 +15,7 @@
// What this means for Cuprate: just follow `monerod`. // What this means for Cuprate: just follow `monerod`.
//---------------------------------------------------------------------------------------------------- Import //---------------------------------------------------------------------------------------------------- Import
use crate::macros::monero_definition_link;
//---------------------------------------------------------------------------------------------------- Status //---------------------------------------------------------------------------------------------------- Status
// Common RPC status strings: // Common RPC status strings:
@ -23,39 +24,32 @@
// Note that these are _distinct_ from the ones in ZMQ: // Note that these are _distinct_ from the ones in ZMQ:
// <https://github.com/monero-project/monero/blob/cc73fe71162d564ffda8e549b79a350bca53c454/src/rpc/message.cpp#L40-L44>. // <https://github.com/monero-project/monero/blob/cc73fe71162d564ffda8e549b79a350bca53c454/src/rpc/message.cpp#L40-L44>.
/// <https://github.com/monero-project/monero/blob/cc73fe71162d564ffda8e549b79a350bca53c454/src/rpc/core_rpc_server_commands_defs.h#L78> #[doc = monero_definition_link!(cc73fe71162d564ffda8e549b79a350bca53c454, "/rpc/core_rpc_server_commands_defs.h", 78)]
pub const CORE_RPC_STATUS_OK: &str = "OK"; pub const CORE_RPC_STATUS_OK: &str = "OK";
/// <https://github.com/monero-project/monero/blob/cc73fe71162d564ffda8e549b79a350bca53c454/src/rpc/core_rpc_server_commands_defs.h#L79> #[doc = monero_definition_link!(cc73fe71162d564ffda8e549b79a350bca53c454, "/rpc/core_rpc_server_commands_defs.h", 79)]
pub const CORE_RPC_STATUS_BUSY: &str = "BUSY"; pub const CORE_RPC_STATUS_BUSY: &str = "BUSY";
/// <https://github.com/monero-project/monero/blob/cc73fe71162d564ffda8e549b79a350bca53c454/src/rpc/core_rpc_server_commands_defs.h#L80> #[doc = monero_definition_link!(cc73fe71162d564ffda8e549b79a350bca53c454, "/rpc/core_rpc_server_commands_defs.h", 80)]
pub const CORE_RPC_STATUS_NOT_MINING: &str = "NOT MINING"; pub const CORE_RPC_STATUS_NOT_MINING: &str = "NOT MINING";
/// <https://github.com/monero-project/monero/blob/cc73fe71162d564ffda8e549b79a350bca53c454/src/rpc/core_rpc_server_commands_defs.h#L81> #[doc = monero_definition_link!(cc73fe71162d564ffda8e549b79a350bca53c454, "/rpc/core_rpc_server_commands_defs.h", 81)]
pub const CORE_RPC_STATUS_PAYMENT_REQUIRED: &str = "PAYMENT REQUIRED"; pub const CORE_RPC_STATUS_PAYMENT_REQUIRED: &str = "PAYMENT REQUIRED";
/// Custom `CORE_RPC_STATUS` for usage in Cuprate. /// Custom `CORE_RPC_STATUS` for usage in Cuprate.
pub const CORE_RPC_STATUS_UNKNOWN: &str = "UNKNOWN"; pub const CORE_RPC_STATUS_UNKNOWN: &str = "UNKNOWN";
//---------------------------------------------------------------------------------------------------- Versions //---------------------------------------------------------------------------------------------------- Versions
#[doc = monero_definition_link!(cc73fe71162d564ffda8e549b79a350bca53c454, "/rpc/core_rpc_server_commands_defs.h", 90)]
/// RPC major version. /// RPC major version.
///
/// See: <https://github.com/monero-project/monero/blob/cc73fe71162d564ffda8e549b79a350bca53c454/src/rpc/core_rpc_server_commands_defs.h#L90>.
pub const CORE_RPC_VERSION_MAJOR: u32 = 3; pub const CORE_RPC_VERSION_MAJOR: u32 = 3;
#[doc = monero_definition_link!(cc73fe71162d564ffda8e549b79a350bca53c454, "/rpc/core_rpc_server_commands_defs.h", 91)]
/// RPC miror version. /// RPC miror version.
///
/// See: <https://github.com/monero-project/monero/blob/cc73fe71162d564ffda8e549b79a350bca53c454/src/rpc/core_rpc_server_commands_defs.h#L91>.
pub const CORE_RPC_VERSION_MINOR: u32 = 14; pub const CORE_RPC_VERSION_MINOR: u32 = 14;
#[doc = monero_definition_link!(cc73fe71162d564ffda8e549b79a350bca53c454, "/rpc/core_rpc_server_commands_defs.h", 92..=93)]
/// RPC version. /// RPC version.
///
/// See: <https://github.com/monero-project/monero/blob/cc73fe71162d564ffda8e549b79a350bca53c454/src/rpc/core_rpc_server_commands_defs.h#L92-L93>.
///
/// ```rust
/// assert_eq!(cuprate_rpc_types::CORE_RPC_VERSION, 196_622);
/// ```
pub const CORE_RPC_VERSION: u32 = (CORE_RPC_VERSION_MAJOR << 16) | CORE_RPC_VERSION_MINOR; pub const CORE_RPC_VERSION: u32 = (CORE_RPC_VERSION_MAJOR << 16) | CORE_RPC_VERSION_MINOR;
//---------------------------------------------------------------------------------------------------- Tests //---------------------------------------------------------------------------------------------------- Tests

70
rpc/types/src/defaults.rs Normal file
View file

@ -0,0 +1,70 @@
//! These functions define the default values
//! of optional fields in request/response types.
//!
//! For example, [`crate::json::GetBlockRequest`]
//! has a [`crate::json::GetBlockRequest::height`]
//! field and a [`crate::json::GetBlockRequest::hash`]
//! field, when the RPC interface reads JSON without
//! `height`, it will use [`default_height`] to fill that in.
//---------------------------------------------------------------------------------------------------- Import
use std::borrow::Cow;
//---------------------------------------------------------------------------------------------------- TODO
/// Default [`bool`] type used in request/response types, `false`.
#[inline]
pub(crate) const fn default_false() -> bool {
false
}
/// Default [`bool`] type used in _some_ request/response types, `true`.
#[inline]
pub(crate) const fn default_true() -> bool {
true
}
/// Default `Cow<'static, str` type used in request/response types.
#[inline]
pub(crate) const fn default_cow_str() -> Cow<'static, str> {
Cow::Borrowed("")
}
/// Default [`String`] type used in request/response types.
#[inline]
pub(crate) const fn default_string() -> String {
String::new()
}
/// Default block height used in request/response types.
#[inline]
pub(crate) const fn default_height() -> u64 {
0
}
/// Default [`Vec`] used in request/response types.
#[inline]
pub(crate) const fn default_vec<T>() -> Vec<T> {
Vec::new()
}
/// Default `0` value used in request/response types.
#[inline]
pub(crate) fn default_zero<T: From<u8>>() -> T {
T::from(0)
}
//---------------------------------------------------------------------------------------------------- Tests
#[cfg(test)]
mod test {
use super::*;
/// Tests that [`default_zero`] returns `0` on all unsigned numbers.
#[test]
fn zero() {
assert_eq!(default_zero::<usize>(), 0);
assert_eq!(default_zero::<u64>(), 0);
assert_eq!(default_zero::<u32>(), 0);
assert_eq!(default_zero::<u16>(), 0);
assert_eq!(default_zero::<u8>(), 0);
}
}

18
rpc/types/src/free.rs Normal file
View file

@ -0,0 +1,18 @@
//! Free functions.
//---------------------------------------------------------------------------------------------------- Serde
// These are functions used for conditionally (de)serialization.
/// 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
}
/// 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
}

View file

@ -3,10 +3,7 @@
//! <https://github.com/monero-project/monero/blob/cc73fe71162d564ffda8e549b79a350bca53c454/src/rpc/daemon_messages.h>. //! <https://github.com/monero-project/monero/blob/cc73fe71162d564ffda8e549b79a350bca53c454/src/rpc/daemon_messages.h>.
//---------------------------------------------------------------------------------------------------- Import //---------------------------------------------------------------------------------------------------- Import
use crate::{ use crate::{base::ResponseBase, macros::define_request_and_response};
base::{EmptyRequestBase, EmptyResponseBase, ResponseBase},
macros::define_request_and_response,
};
//---------------------------------------------------------------------------------------------------- Struct definitions //---------------------------------------------------------------------------------------------------- Struct definitions
// This generates 2 structs: // This generates 2 structs:
@ -26,38 +23,43 @@ define_request_and_response! {
// The base type name. // The base type name.
GetBlockTemplate, GetBlockTemplate,
// The base request type. // The request type.
// //
// This must be a type found in [`crate::base`]. // If `Request {/* fields */}` is provided, a struct is generate as-is.
// It acts as a "base" that gets flattened into
// the actually request type.
// //
// "Flatten" means the field(s) of a struct gets inlined // If `Request {}` is specified here, it will create a `pub type YOUR_REQUEST_TYPE = ()`
// directly into the struct during (de)serialization, see:
// <https://serde.rs/field-attrs.html#flatten>.
//
// For example here, we're using [`crate::base::EmptyRequestBase`],
// which means that there is no extra fields flattened.
//
// If a request is not specified here, it will create a `type alias YOUR_REQUEST_TYPE = ()`
// instead of a `struct`, see below in other macro definitions for an example. // instead of a `struct`, see below in other macro definitions for an example.
EmptyRequestBase {
reserve_size: u64,
wallet_address: String,
prev_block: String,
extra_nonce: String,
},
// The base response type.
//
// This is the same as the request base type,
// it must be a type found in [`crate::base`].
// //
// If there are any additional attributes (`/// docs` or `#[derive]`s) // If there are any additional attributes (`/// docs` or `#[derive]`s)
// for the struct, they go here, e.g.: // for the struct, they go here, e.g.:
// #[derive(Copy)] // #[derive(Copy)]
Request {
// Within the `{}` is an infinite matching pattern of:
// ```
// $ATTRIBUTES
// $FIELD_NAME: $FIELD_TYPE,
// ```
// The struct generated and all fields are `pub`.
extra_nonce: String,
prev_block: String,
reserve_size: u64,
wallet_address: String,
},
// The response type.
//
// If `Response {/* fields */}` is used,
// this will generate a struct as-is.
//
// If a type found in [`crate::base`] is used,
// It acts as a "base" that gets flattened into
// the actual request type.
//
// "Flatten" means the field(s) of a struct gets inlined
// directly into the struct during (de)serialization, see:
// <https://serde.rs/field-attrs.html#flatten>.
ResponseBase { ResponseBase {
// This is using `crate::base::ResponseBase`, // This is using [`crate::base::ResponseBase`],
// so the type we generate will contain this field: // so the type we generate will contain this field:
// ``` // ```
// base: crate::base::ResponseBase, // base: crate::base::ResponseBase,
@ -69,56 +71,18 @@ define_request_and_response! {
// status: crate::Status, // status: crate::Status,
// untrusted: bool, // untrusted: bool,
// ``` // ```
// Within the `{}` is an infinite matching pattern of:
// ```
// $ATTRIBUTES
// $FIELD_NAME: $FIELD_TYPE,
// ```
// The struct generated and all fields are `pub`.
difficulty: u64,
wide_difficulty: String,
difficulty_top64: u64,
height: u64,
reserved_offset: u64,
expected_reward: u64,
prev_hash: String,
seed_height: u64,
seed_hash: String,
next_seed_hash: String,
blocktemplate_blob: String,
blockhashing_blob: String, blockhashing_blob: String,
} blocktemplate_blob: String,
} difficulty_top64: u64,
difficulty: u64,
define_request_and_response! { expected_reward: u64,
get_block_count, height: u64,
cc73fe71162d564ffda8e549b79a350bca53c454 => next_seed_hash: String,
core_rpc_server_commands_defs.h => 919..=933, prev_hash: String,
GetBlockCount, reserved_offset: u64,
seed_hash: String,
// There is no request type specified, seed_height: u64,
// this will cause the macro to generate a wide_difficulty: String,
// type alias to `()` instead of a `struct`.
ResponseBase {
count: u64,
}
}
define_request_and_response! {
on_get_block_hash,
cc73fe71162d564ffda8e549b79a350bca53c454 =>
core_rpc_server_commands_defs.h => 935..=939,
OnGetBlockHash,
#[derive(Copy)]
EmptyRequestBase {
#[serde(flatten)]
block_height: u64,
},
EmptyResponseBase {
#[serde(flatten)]
block_hash: String,
} }
} }

View file

@ -1,4 +1,5 @@
#![doc = include_str!("../README.md")] #![doc = include_str!("../README.md")]
#![cfg_attr(docsrs, feature(doc_cfg))]
//---------------------------------------------------------------------------------------------------- Lints //---------------------------------------------------------------------------------------------------- Lints
// Forbid lints. // Forbid lints.
// Our code, and code generated (e.g macros) cannot overrule these. // Our code, and code generated (e.g macros) cannot overrule these.
@ -13,7 +14,6 @@
unused_allocation, unused_allocation,
coherence_leak_check, coherence_leak_check,
while_true, while_true,
clippy::missing_docs_in_private_items,
// Maybe can be put into `#[deny]`. // Maybe can be put into `#[deny]`.
unconditional_recursion, unconditional_recursion,
@ -82,7 +82,15 @@
clippy::option_if_let_else, clippy::option_if_let_else,
)] )]
// Allow some lints when running in debug mode. // Allow some lints when running in debug mode.
#![cfg_attr(debug_assertions, allow(clippy::todo, clippy::multiple_crate_versions))] #![cfg_attr(
debug_assertions,
allow(
clippy::todo,
clippy::multiple_crate_versions,
unused_imports,
unused_variables
)
)]
// Allow some lints in tests. // Allow some lints in tests.
#![cfg_attr( #![cfg_attr(
test, test,
@ -94,23 +102,25 @@
) )
)] )]
// TODO: remove me after finishing impl // TODO: remove me after finishing impl
#![allow(dead_code)] #![allow(
dead_code,
rustdoc::broken_intra_doc_links // TODO: remove after `{bin,json,other}.rs` gets merged
)]
//---------------------------------------------------------------------------------------------------- Use //---------------------------------------------------------------------------------------------------- Mod
mod binary_string;
mod constants; mod constants;
mod defaults;
mod free;
mod macros; mod macros;
mod status;
pub use binary_string::BinaryString; pub mod base;
pub mod bin;
pub mod json;
pub mod misc;
pub mod other;
pub use constants::{ pub use constants::{
CORE_RPC_STATUS_BUSY, CORE_RPC_STATUS_NOT_MINING, CORE_RPC_STATUS_OK, CORE_RPC_STATUS_BUSY, CORE_RPC_STATUS_NOT_MINING, CORE_RPC_STATUS_OK,
CORE_RPC_STATUS_PAYMENT_REQUIRED, CORE_RPC_STATUS_UNKNOWN, CORE_RPC_VERSION, CORE_RPC_STATUS_PAYMENT_REQUIRED, CORE_RPC_STATUS_UNKNOWN, CORE_RPC_VERSION,
CORE_RPC_VERSION_MAJOR, CORE_RPC_VERSION_MINOR, CORE_RPC_VERSION_MAJOR, CORE_RPC_VERSION_MINOR,
}; };
pub use status::Status;
pub mod base;
pub mod bin;
pub mod json;
pub mod other;

View file

@ -1,14 +1,12 @@
//! Macros. //! Macros.
//---------------------------------------------------------------------------------------------------- Struct definition //---------------------------------------------------------------------------------------------------- define_request_and_response
/// A template for generating 2 `struct`s with a bunch of information filled out. /// A template for generating the RPC request and response `struct`s.
///
/// These are the RPC request and response `struct`s.
/// ///
/// These `struct`s automatically implement: /// These `struct`s automatically implement:
/// - `Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash` /// - `Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash`
/// - `serde::{Serialize, Deserialize}` /// - `serde::{Serialize, Deserialize}`
/// - `epee_encoding::EpeeObject` /// - `cuprate_epee_encoding::EpeeObject`
/// ///
/// It's best to see the output of this macro via the documentation /// 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 /// of the generated structs via `cargo doc`s to see which parts
@ -17,110 +15,35 @@
/// See the [`crate::json`] module for example usage. /// See the [`crate::json`] module for example usage.
/// ///
/// # Macro internals /// # Macro internals
/// This macro has 2 branches with almost the same output: /// This macro uses:
/// 1. An empty `Request` type /// - [`__define_request`]
/// 2. An `Request` type with fields /// - [`__define_response`]
/// - [`__define_request_and_response_doc`]
/// ///
/// The first branch is the same as the second with the exception /// # `__define_request`
/// that if the caller of this macro provides no fields, it will /// This macro has 2 branches. If the caller provides
/// generate: /// `Request {}`, i.e. no fields, it will generate:
/// ``` /// ```
/// pub type Request = (); /// pub type Request = ();
/// ``` /// ```
/// instead of: /// If they _did_ specify fields, it will generate:
/// ``` /// ```
/// pub struct Request {/* fields */} /// pub struct Request {/* fields */}
/// ``` /// ```
///
/// This is because having a bunch of types that are all empty structs /// 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. /// means they are not compatible and it makes it cumbersome for end-users.
/// Really, they semantically are empty types, so `()` is used. /// Really, they semantically are empty types, so `()` is used.
/// ///
/// Again, other than this, the 2 branches do (should) not differ. /// # `__define_response`
/// This macro has 2 branches. If the caller provides `Response`
/// it will generate a normal struct with no additional fields.
/// ///
/// FIXME: there's probably a less painful way to branch here on input /// If the caller provides a base type from [`crate::base`], it will
/// without having to duplicate 80% of the macro. Sub-macros were attempted /// flatten that into the request type automatically.
/// but they ended up unreadable. So for now, make sure to fix the other ///
/// branch as well when making changes. The only de-duplicated part is /// E.g. `Response {/*...*/}` and `ResponseBase {/*...*/}`
/// the doc generation with [`define_request_and_response_doc`]. /// would trigger the different branches.
macro_rules! define_request_and_response { macro_rules! define_request_and_response {
//------------------------------------------------------------------------------
// This version of the macro expects a `Request` type with no fields, i.e. `Request {}`.
(
// The markdown tag for Monero 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.
$type_name:ident,
// 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,
)*
}
) => { paste::paste! {
#[doc = $crate::macros::define_request_and_response_doc!(
"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_name Request>],
)]
///
/// This request has no inputs.
pub type [<$type_name Request>] = ();
#[allow(dead_code)]
#[allow(missing_docs)]
#[derive(serde::Serialize, serde::Deserialize)]
#[derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
$( #[$response_type_attr] )*
#[doc = $crate::macros::define_request_and_response_doc!(
"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_name Response>],
)]
pub struct [<$type_name Response>] {
#[serde(flatten)]
pub base: $response_base_type,
$(
$( #[$response_field_attr] )*
pub $response_field: $response_field_type,
)*
}
::cuprate_epee_encoding::epee_object! {
[<$type_name Response>],
$(
$response_field: $response_field_type,
)*
!flatten: base: $response_base_type,
}
}};
//------------------------------------------------------------------------------
// This version of the macro expects a `Request` type with fields.
( (
// The markdown tag for Monero RPC documentation. Not necessarily the endpoint. // The markdown tag for Monero RPC documentation. Not necessarily the endpoint.
$monero_daemon_rpc_doc_link:ident, $monero_daemon_rpc_doc_link:ident,
@ -134,15 +57,18 @@ macro_rules! define_request_and_response {
$monero_code_line_end:literal, $monero_code_line_end:literal,
// The base `struct` name. // The base `struct` name.
// Attributes added here will apply to _both_
// request and response types.
$( #[$type_attr:meta] )*
$type_name:ident, $type_name:ident,
// The request type (and any doc comments, derives, etc). // The request type (and any doc comments, derives, etc).
$( #[$request_type_attr:meta] )* $( #[$request_type_attr:meta] )*
$request_base_type:ty { Request {
// And any fields. // And any fields.
$( $(
$( #[$request_field_attr:meta] )* $( #[$request_field_attr:meta] )*
$request_field:ident: $request_field_type:ty, $request_field:ident: $request_field_type:ty $(= $request_field_type_default:expr)?,
)* )*
}, },
@ -152,84 +78,213 @@ macro_rules! define_request_and_response {
// And any fields. // And any fields.
$( $(
$( #[$response_field_attr:meta] )* $( #[$response_field_attr:meta] )*
$response_field:ident: $response_field_type:ty, $response_field:ident: $response_field_type:ty $(= $response_field_type_default:expr)?,
)* )*
} }
) => { paste::paste! { ) => { paste::paste! {
#[allow(dead_code)] $crate::macros::__define_request! {
#[allow(missing_docs)] #[doc = $crate::macros::__define_request_and_response_doc!(
#[derive(serde::Serialize, serde::Deserialize)] "response" => [<$type_name Response>],
#[derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] $monero_daemon_rpc_doc_link,
$( #[$request_type_attr] )* $monero_code_commit,
#[doc = $crate::macros::define_request_and_response_doc!( $monero_code_filename,
"response", $monero_code_filename_extension,
$monero_daemon_rpc_doc_link, $monero_code_line_start,
$monero_code_commit, $monero_code_line_end,
$monero_code_filename, )]
$monero_code_filename_extension, ///
$monero_code_line_start, $( #[$type_attr] )*
$monero_code_line_end, ///
[<$type_name Request>], $( #[$request_type_attr] )*
)] [<$type_name Request>] {
pub struct [<$type_name Request>] { $(
#[serde(flatten)] $( #[$request_field_attr] )*
pub base: $request_base_type, $request_field: $request_field_type $(= $request_field_type_default)?,
)*
$( }
$( #[$request_field_attr] )*
pub $request_field: $request_field_type,
)*
} }
::cuprate_epee_encoding::epee_object! { $crate::macros::__define_response! {
[<$type_name Request>], #[allow(dead_code)]
$( #[allow(missing_docs)]
$request_field: $request_field_type, #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
)* #[derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
!flatten: base: $request_base_type, #[doc = $crate::macros::__define_request_and_response_doc!(
} "request" => [<$type_name Request>],
$monero_daemon_rpc_doc_link,
#[allow(dead_code)] $monero_code_commit,
#[allow(missing_docs)] $monero_code_filename,
#[derive(serde::Serialize, serde::Deserialize)] $monero_code_filename_extension,
#[derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] $monero_code_line_start,
$( #[$response_type_attr] )* $monero_code_line_end,
#[doc = $crate::macros::define_request_and_response_doc!( )]
"request", ///
$monero_daemon_rpc_doc_link, $( #[$type_attr] )*
$monero_code_commit, ///
$monero_code_filename, $( #[$response_type_attr] )*
$monero_code_filename_extension, $response_base_type => [<$type_name Response>] {
$monero_code_line_start, $(
$monero_code_line_end, $( #[$response_field_attr] )*
[<$type_name Response>], $response_field: $response_field_type $(= $response_field_type_default)?,
)] )*
pub struct [<$type_name Response>] { }
#[serde(flatten)]
pub base: $response_base_type,
$(
$( #[$response_field_attr] )*
pub $response_field: $response_field_type,
)*
}
::cuprate_epee_encoding::epee_object! {
[<$type_name Response>],
$(
$response_field: $response_field_type,
)*
!flatten: base: $response_base_type,
} }
}}; }};
} }
pub(crate) use define_request_and_response; 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 $(= $field_default:expr)?,
// 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] )*
pub $field: $field_type,
)*
}
#[cfg(feature = "epee")]
::cuprate_epee_encoding::epee_object! {
$t,
$(
$field: $field_type $(= $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 $(= $field_default:expr)?,
)*
}
) => {
$( #[$attr] )*
pub struct $t {
$(
$( #[$field_attr] )*
pub $field: $field_type,
)*
}
#[cfg(feature = "epee")]
::cuprate_epee_encoding::epee_object! {
$t,
$(
$field: $field_type $($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 $(= $field_default:expr)?,
)*
}
) => {
$( #[$attr] )*
pub struct $t {
#[cfg_attr(feature = "serde", serde(flatten))]
pub base: $base,
$(
$( #[$field_attr] )*
pub $field: $field_type,
)*
}
#[cfg(feature = "epee")]
::cuprate_epee_encoding::epee_object! {
$t,
$(
$field: $field_type $(= $field_default)?,
)*
!flatten: base: $base,
}
};
}
pub(crate) use __define_response;
//---------------------------------------------------------------------------------------------------- define_request_and_response_doc
/// Generate documentation for the types generated /// Generate documentation for the types generated
/// by the [`define_request_and_response`] macro. /// by the [`__define_request_and_response`] macro.
/// ///
/// See it for more info on inputs. /// See it for more info on inputs.
macro_rules! define_request_and_response_doc { ///
/// `__` 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]` // This labels the last `[request]` or `[response]`
// hyperlink in documentation. Input is either: // hyperlink in documentation. Input is either:
@ -239,7 +294,7 @@ macro_rules! define_request_and_response_doc {
// Remember this is linking to the _other_ type, // Remember this is linking to the _other_ type,
// so if defining a `Request` type, input should // so if defining a `Request` type, input should
// be "response". // be "response".
$request_or_response:literal, $request_or_response:literal => $request_or_response_type:ident,
$monero_daemon_rpc_doc_link:ident, $monero_daemon_rpc_doc_link:ident,
$monero_code_commit:ident, $monero_code_commit:ident,
@ -247,7 +302,6 @@ macro_rules! define_request_and_response_doc {
$monero_code_filename_extension:ident, $monero_code_filename_extension:ident,
$monero_code_line_start:literal, $monero_code_line_start:literal,
$monero_code_line_end:literal, $monero_code_line_end:literal,
$type_name:ident,
) => { ) => {
concat!( concat!(
"", "",
@ -269,9 +323,34 @@ macro_rules! define_request_and_response_doc {
"), [", "), [",
$request_or_response, $request_or_response,
"](", "](",
stringify!($type_name), stringify!($request_or_response_type),
")." ")."
) )
}; };
} }
pub(crate) use define_request_and_response_doc; 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;

View file

@ -1,14 +1,14 @@
//! TODO //! JSON string containing binary data.
//---------------------------------------------------------------------------------------------------- Import //---------------------------------------------------------------------------------------------------- Import
//---------------------------------------------------------------------------------------------------- BinaryString //---------------------------------------------------------------------------------------------------- BinaryString
/// TODO /// TODO: we need to figure out a type that (de)serializes correctly, `String` errors with `serde_json`
/// ///
/// ```rust /// ```rust
/// use serde::Deserialize; /// use serde::Deserialize;
/// use serde_json::from_str; /// use serde_json::from_str;
/// use cuprate_rpc_types::BinaryString; /// use cuprate_rpc_types::misc::BinaryString;
/// ///
/// #[derive(Deserialize)] /// #[derive(Deserialize)]
/// struct Key { /// struct Key {

View file

@ -0,0 +1,37 @@
//! TODO
//---------------------------------------------------------------------------------------------------- Use
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
#[cfg(feature = "epee")]
use cuprate_epee_encoding::epee_object;
use crate::misc::TxBlobEntry;
//---------------------------------------------------------------------------------------------------- BlockCompleteEntry
#[doc = crate::macros::monero_definition_link!(
cc73fe71162d564ffda8e549b79a350bca53c454,
"rpc/core_rpc_server_commands_defs.h",
210..=221
)]
/// Used in [`crate::bin::GetBlocksResponse`].
#[derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct BlockCompleteEntry {
pub pruned: bool,
pub block: String,
pub block_weight: u64,
pub txs: Vec<TxBlobEntry>,
}
// TODO: custom epee
// <https://github.com/monero-project/monero/blob/cc73fe71162d564ffda8e549b79a350bca53c454/src/cryptonote_protocol/cryptonote_protocol_defs.h#L138-L163>
#[cfg(feature = "epee")]
epee_object! {
BlockCompleteEntry,
pruned: bool,
block: String,
block_weight: u64,
txs: Vec<TxBlobEntry>,
}

View file

@ -0,0 +1,48 @@
//! TODO
//---------------------------------------------------------------------------------------------------- Use
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
#[cfg(feature = "epee")]
use cuprate_epee_encoding::{
macros::bytes::{Buf, BufMut},
EpeeValue, Marker,
};
//---------------------------------------------------------------------------------------------------- KeyImageSpentStatus
#[doc = crate::macros::monero_definition_link!(
cc73fe71162d564ffda8e549b79a350bca53c454,
"rpc/core_rpc_server_commands_defs.h",
456..=460
)]
/// Used in [`crate::other::IsKeyImageSpentResponse`].
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[repr(u8)]
pub enum KeyImageSpentStatus {
Unspent = 0,
SpentInBlockchain = 1,
SpentInPool = 2,
}
#[cfg(feature = "epee")]
impl EpeeValue for KeyImageSpentStatus {
const MARKER: Marker = <String as EpeeValue>::MARKER;
fn read<B: Buf>(r: &mut B, marker: &Marker) -> cuprate_epee_encoding::Result<Self> {
todo!()
}
fn should_write(&self) -> bool {
todo!()
}
fn epee_default_value() -> Option<Self> {
todo!()
}
fn write<B: BufMut>(self, w: &mut B) -> cuprate_epee_encoding::Result<()> {
todo!()
}
}

539
rpc/types/src/misc/misc.rs Normal file
View file

@ -0,0 +1,539 @@
//! Miscellaneous types.
//!
//! These are `struct`s that appear in request/response types.
//! For example, [`crate::json::GetConnectionsResponse`] contains
//! the [`crate::misc::ConnectionInfo`] struct defined here.
//---------------------------------------------------------------------------------------------------- Import
use std::fmt::Display;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
#[cfg(feature = "epee")]
use cuprate_epee_encoding::{
epee_object,
macros::bytes::{Buf, BufMut},
EpeeValue, Marker,
};
use crate::{
constants::{
CORE_RPC_STATUS_BUSY, CORE_RPC_STATUS_NOT_MINING, CORE_RPC_STATUS_OK,
CORE_RPC_STATUS_PAYMENT_REQUIRED, CORE_RPC_STATUS_UNKNOWN,
},
defaults::default_zero,
macros::monero_definition_link,
};
//---------------------------------------------------------------------------------------------------- Macros
/// This macro (local to this file) defines all the misc types.
///
/// This macro:
/// 1. Defines a `pub struct` with all `pub` fields
/// 2. Implements `serde` on the struct
/// 3. Implements `epee` on the struct
///
/// When using, consider documenting:
/// - The original Monero definition site with [`monero_definition_link`]
/// - The request/responses where the `struct` is used
macro_rules! define_struct_and_impl_epee {
(
// Optional `struct` attributes.
$( #[$struct_attr:meta] )*
// The `struct`'s name.
$struct_name:ident {
// And any fields.
$(
$( #[$field_attr:meta] )* // Field attributes
// Field name => the type => optional `epee_object` default value.
$field_name:ident: $field_type:ty $(= $field_default:expr)?,
)*
}
) => {
$( #[$struct_attr] )*
#[derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct $struct_name {
$(
$( #[$field_attr] )*
pub $field_name: $field_type,
)*
}
#[cfg(feature = "epee")]
epee_object! {
$struct_name,
$(
$field_name: $field_type $(= $field_default)?,
)*
}
};
}
//---------------------------------------------------------------------------------------------------- Type Definitions
define_struct_and_impl_epee! {
#[doc = monero_definition_link!(
cc73fe71162d564ffda8e549b79a350bca53c454,
"rpc/core_rpc_server_commands_defs.h",
1163..=1212
)]
///
/// Used in:
/// - [`crate::json::GetLastBlockHeaderResponse`]
/// - [`crate::json::GetBlockHeaderByHashResponse`]
/// - [`crate::json::GetBlockHeaderByHeightResponse`]
/// - [`crate::json::GetBlockHeadersRangeResponse`]
/// - [`crate::json::GetBlockResponse`]
BlockHeader {
block_size: u64,
block_weight: u64,
cumulative_difficulty_top64: u64,
cumulative_difficulty: u64,
depth: u64,
difficulty_top64: u64,
difficulty: u64,
hash: String,
height: u64,
long_term_weight: u64,
major_version: u8,
miner_tx_hash: String,
minor_version: u8,
nonce: u32,
num_txes: u64,
orphan_status: bool,
pow_hash: String,
prev_hash: String,
reward: u64,
timestamp: u64,
wide_cumulative_difficulty: String,
wide_difficulty: String,
}
}
define_struct_and_impl_epee! {
#[doc = monero_definition_link!(
cc73fe71162d564ffda8e549b79a350bca53c454,
"cryptonote_protocol/cryptonote_protocol_defs.h",
47..=116
)]
/// Used in [`crate::json::GetConnectionsResponse`].
ConnectionInfo {
address: String,
address_type: u8,
avg_download: u64,
avg_upload: u64,
connection_id: String,
current_download: u64,
current_upload: u64,
height: u64,
host: String,
incoming: bool,
ip: String,
live_time: u64,
localhost: bool,
local_ip: bool,
peer_id: String,
port: String,
pruning_seed: u32,
recv_count: u64,
recv_idle_time: u64,
rpc_credits_per_hash: u32,
rpc_port: u16,
send_count: u64,
send_idle_time: u64,
ssl: bool,
state: String,
support_flags: u32,
}
}
define_struct_and_impl_epee! {
#[doc = monero_definition_link!(
cc73fe71162d564ffda8e549b79a350bca53c454,
"rpc/core_rpc_server_commands_defs.h",
2034..=2047
)]
/// Used in [`crate::json::SetBansRequest`].
SetBan {
host: String,
ip: u32,
ban: bool,
seconds: u32,
}
}
define_struct_and_impl_epee! {
#[doc = monero_definition_link!(
cc73fe71162d564ffda8e549b79a350bca53c454,
"rpc/core_rpc_server_commands_defs.h",
1999..=2010
)]
/// Used in [`crate::json::GetBansResponse`].
GetBan {
host: String,
ip: u32,
seconds: u32,
}
}
define_struct_and_impl_epee! {
#[doc = monero_definition_link!(
cc73fe71162d564ffda8e549b79a350bca53c454,
"rpc/core_rpc_server_commands_defs.h",
2139..=2156
)]
#[derive(Copy)]
/// Used in [`crate::json::GetOutputHistogramResponse`].
HistogramEntry {
amount: u64,
total_instances: u64,
unlocked_instances: u64,
recent_instances: u64,
}
}
define_struct_and_impl_epee! {
#[doc = monero_definition_link!(
cc73fe71162d564ffda8e549b79a350bca53c454,
"rpc/core_rpc_server_commands_defs.h",
2180..=2191
)]
#[derive(Copy)]
/// Used in [`crate::json::GetVersionResponse`].
HardforkEntry {
height: u64,
hf_version: u8,
}
}
define_struct_and_impl_epee! {
#[doc = monero_definition_link!(
cc73fe71162d564ffda8e549b79a350bca53c454,
"rpc/core_rpc_server_commands_defs.h",
2289..=2310
)]
/// Used in [`crate::json::GetAlternateChainsResponse`].
ChainInfo {
block_hash: String,
block_hashes: Vec<String>,
difficulty: u64,
difficulty_top64: u64,
height: u64,
length: u64,
main_chain_parent_block: String,
wide_difficulty: String,
}
}
define_struct_and_impl_epee! {
#[doc = monero_definition_link!(
cc73fe71162d564ffda8e549b79a350bca53c454,
"rpc/core_rpc_server_commands_defs.h",
2393..=2400
)]
/// Used in [`crate::json::SyncInfoResponse`].
SyncInfoPeer {
info: ConnectionInfo,
}
}
define_struct_and_impl_epee! {
#[doc = monero_definition_link!(
cc73fe71162d564ffda8e549b79a350bca53c454,
"rpc/core_rpc_server_commands_defs.h",
2402..=2421
)]
/// Used in [`crate::json::SyncInfoResponse`].
Span {
connection_id: String,
nblocks: u64,
rate: u32,
remote_address: String,
size: u64,
speed: u32,
start_block_height: u64,
}
}
define_struct_and_impl_epee! {
#[doc = monero_definition_link!(
cc73fe71162d564ffda8e549b79a350bca53c454,
"rpc/core_rpc_server_commands_defs.h",
1637..=1642
)]
#[derive(Copy)]
/// Used in [`crate::json::GetTransactionPoolBacklogResponse`].
TxBacklogEntry {
weight: u64,
fee: u64,
time_in_pool: u64,
}
}
define_struct_and_impl_epee! {
#[doc = monero_definition_link!(
cc73fe71162d564ffda8e549b79a350bca53c454,
"rpc/rpc_handler.h",
45..=50
)]
/// Used in [`crate::json::GetOutputDistributionResponse`].
OutputDistributionData {
distribution: Vec<u64>,
start_height: u64,
base: u64,
}
}
define_struct_and_impl_epee! {
#[doc = monero_definition_link!(
cc73fe71162d564ffda8e549b79a350bca53c454,
"rpc/core_rpc_server_commands_defs.h",
1016..=1027
)]
/// Used in [`crate::json::GetMinerDataResponse`].
///
/// Note that this is different than [`crate::misc::TxBacklogEntry`].
GetMinerDataTxBacklogEntry {
id: String,
weight: u64,
fee: u64,
}
}
define_struct_and_impl_epee! {
#[doc = monero_definition_link!(
cc73fe71162d564ffda8e549b79a350bca53c454,
"rpc/core_rpc_server_commands_defs.h",
1070..=1079
)]
/// Used in [`crate::json::AddAuxPowRequest`].
AuxPow {
id: String,
hash: String,
}
}
define_struct_and_impl_epee! {
#[doc = monero_definition_link!(
cc73fe71162d564ffda8e549b79a350bca53c454,
"rpc/core_rpc_server_commands_defs.h",
192..=199
)]
/// Used in [`crate::bin::GetBlocksResponse`].
TxOutputIndices {
indices: Vec<u64>,
}
}
define_struct_and_impl_epee! {
#[doc = monero_definition_link!(
cc73fe71162d564ffda8e549b79a350bca53c454,
"rpc/core_rpc_server_commands_defs.h",
201..=208
)]
/// Used in [`crate::bin::GetBlocksResponse`].
BlockOutputIndices {
indices: Vec<TxOutputIndices>,
}
}
define_struct_and_impl_epee! {
#[doc = monero_definition_link!(
cc73fe71162d564ffda8e549b79a350bca53c454,
"rpc/core_rpc_server_commands_defs.h",
210..=221
)]
/// Used in [`crate::bin::GetBlocksResponse`].
PoolTxInfo {
tx_hash: [u8; 32],
tx_blob: String,
double_spend_seen: bool,
}
}
define_struct_and_impl_epee! {
#[doc = monero_definition_link!(
cc73fe71162d564ffda8e549b79a350bca53c454,
"cryptonote_protocol/cryptonote_protocol_defs.h",
121..=131
)]
/// Used in [`crate::bin::GetBlocksResponse`].
TxBlobEntry {
blob: String,
prunable_hash: [u8; 32],
}
}
define_struct_and_impl_epee! {
#[doc = monero_definition_link!(
cc73fe71162d564ffda8e549b79a350bca53c454,
"rpc/core_rpc_server_commands_defs.h",
512..=521
)]
#[derive(Copy)]
///
/// Used in:
/// - [`crate::bin::GetOutsRequest`]
/// - [`crate::other::GetOutsRequest`]
GetOutputsOut {
amount: u64,
index: u64,
}
}
define_struct_and_impl_epee! {
#[doc = monero_definition_link!(
cc73fe71162d564ffda8e549b79a350bca53c454,
"rpc/core_rpc_server_commands_defs.h",
538..=553
)]
#[derive(Copy)]
/// Used in [`crate::bin::GetOutsRequest`].
OutKeyBin {
key: [u8; 32],
mask: [u8; 32],
unlocked: bool,
height: u64,
txid: [u8; 32],
}
}
define_struct_and_impl_epee! {
#[doc = monero_definition_link!(
cc73fe71162d564ffda8e549b79a350bca53c454,
"rpc/core_rpc_server_commands_defs.h",
1335..=1367
)]
/// Used in [`crate::other::GetPeerListResponse`].
Peer {
id: u64,
host: String,
ip: u32,
port: u16,
#[cfg_attr(feature = "serde", serde(default = "default_zero"))]
rpc_port: u16 = default_zero::<u16>(),
#[cfg_attr(feature = "serde", serde(default = "default_zero"))]
rpc_credits_per_hash: u32 = default_zero::<u32>(),
last_seen: u64,
#[cfg_attr(feature = "serde", serde(default = "default_zero"))]
pruning_seed: u32 = default_zero::<u32>(),
}
}
define_struct_and_impl_epee! {
#[doc = monero_definition_link!(
cc73fe71162d564ffda8e549b79a350bca53c454,
"rpc/core_rpc_server_commands_defs.h",
1398..=1417
)]
///
/// Used in:
/// - [`crate::other::GetPeerListResponse`]
/// - [`crate::other::GetPublicNodesResponse`]
PublicNode {
host: String,
last_seen: u64,
rpc_port: u16,
rpc_credits_per_hash: u32,
}
}
define_struct_and_impl_epee! {
#[doc = monero_definition_link!(
cc73fe71162d564ffda8e549b79a350bca53c454,
"rpc/core_rpc_server_commands_defs.h",
1519..=1556
)]
/// Used in [`crate::other::GetTransactionPoolResponse`].
TxInfo {
blob_size: u64,
do_not_relay: bool,
double_spend_seen: bool,
fee: u64,
id_hash: String,
kept_by_block: bool,
last_failed_height: u64,
last_failed_id_hash: String,
last_relayed_time: u64,
max_used_block_height: u64,
max_used_block_id_hash: String,
receive_time: u64,
relayed: bool,
tx_blob: String,
tx_json: String, // TODO: this should be another struct
#[cfg_attr(feature = "serde", serde(default = "default_zero"))]
weight: u64 = default_zero::<u64>(),
}
}
define_struct_and_impl_epee! {
#[doc = monero_definition_link!(
cc73fe71162d564ffda8e549b79a350bca53c454,
"rpc/core_rpc_server_commands_defs.h",
1558..=1567
)]
/// Used in [`crate::other::GetTransactionPoolResponse`].
SpentKeyImageInfo {
id_hash: String,
txs_hashes: Vec<String>,
}
}
define_struct_and_impl_epee! {
#[doc = monero_definition_link!(
cc73fe71162d564ffda8e549b79a350bca53c454,
"rpc/core_rpc_server_commands_defs.h",
1666..=1675
)]
#[derive(Copy)]
/// Used in [`crate::other::GetTransactionPoolStatsResponse`].
TxpoolHisto {
txs: u32,
bytes: u64,
}
}
define_struct_and_impl_epee! {
#[doc = monero_definition_link!(
cc73fe71162d564ffda8e549b79a350bca53c454,
"rpc/core_rpc_server_commands_defs.h",
1677..=1710
)]
/// Used in [`crate::other::GetTransactionPoolStatsResponse`].
TxpoolStats {
bytes_max: u32,
bytes_med: u32,
bytes_min: u32,
bytes_total: u64,
fee_total: u64,
histo_98pc: u64,
histo: Vec<TxpoolHisto>,
num_10m: u32,
num_double_spends: u32,
num_failing: u32,
num_not_relayed: u32,
oldest: u64,
txs_total: u32,
}
}
define_struct_and_impl_epee! {
#[doc = monero_definition_link!(
cc73fe71162d564ffda8e549b79a350bca53c454,
"rpc/core_rpc_server_commands_defs.h",
582..=597
)]
/// Used in [`crate::other::GetOutsResponse`].
OutKey {
key: String,
mask: String,
unlocked: bool,
height: u64,
txid: String,
}
}
//---------------------------------------------------------------------------------------------------- Tests
#[cfg(test)]
mod test {}

34
rpc/types/src/misc/mod.rs Normal file
View file

@ -0,0 +1,34 @@
//! Miscellaneous types.
//!
//! These are data types that appear in request/response types.
//!
//! For example, [`crate::json::GetConnectionsResponse`] contains
//! the [`crate::misc::ConnectionInfo`] struct defined here.
//---------------------------------------------------------------------------------------------------- Lints
#![allow(
missing_docs, // Docs are at: <https://www.getmonero.org/resources/developer-guides/daemon-rpc.html>
clippy::struct_excessive_bools, // hey man, tell that to the people who wrote `monerod`
)]
//---------------------------------------------------------------------------------------------------- Mod
mod binary_string;
mod block_complete_entry;
mod key_image_spent_status;
mod misc;
mod pool_info_extent;
mod status;
mod tx_entry;
pub use binary_string::BinaryString;
pub use block_complete_entry::BlockCompleteEntry;
pub use key_image_spent_status::KeyImageSpentStatus;
pub use misc::{
AuxPow, BlockHeader, BlockOutputIndices, ChainInfo, ConnectionInfo, GetBan,
GetMinerDataTxBacklogEntry, GetOutputsOut, HardforkEntry, HistogramEntry, OutKey, OutKeyBin,
OutputDistributionData, Peer, PoolTxInfo, PublicNode, SetBan, Span, SpentKeyImageInfo,
SyncInfoPeer, TxBacklogEntry, TxBlobEntry, TxInfo, TxOutputIndices, TxpoolHisto, TxpoolStats,
};
pub use pool_info_extent::PoolInfoExtent;
pub use status::Status;
pub use tx_entry::TxEntry;

View file

@ -0,0 +1,49 @@
//! TODO
//---------------------------------------------------------------------------------------------------- Use
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
#[cfg(feature = "epee")]
use cuprate_epee_encoding::{
macros::bytes::{Buf, BufMut},
EpeeValue, Marker,
};
//---------------------------------------------------------------------------------------------------- PoolInfoExtent
#[doc = crate::macros::monero_definition_link!(
cc73fe71162d564ffda8e549b79a350bca53c454,
"rpc/core_rpc_server_commands_defs.h",
223..=228
)]
/// Used in [`crate::bin::GetBlocksResponse`].
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[repr(u8)]
pub enum PoolInfoExtent {
None = 0,
Incremental = 1,
Full = 2,
}
// <https://github.com/monero-project/monero/blob/cc73fe71162d564ffda8e549b79a350bca53c454/src/cryptonote_protocol/cryptonote_protocol_defs.h#L138-L163>
#[cfg(feature = "epee")]
impl EpeeValue for PoolInfoExtent {
const MARKER: Marker = <String as EpeeValue>::MARKER;
fn read<B: Buf>(r: &mut B, marker: &Marker) -> cuprate_epee_encoding::Result<Self> {
todo!()
}
fn should_write(&self) -> bool {
todo!()
}
fn epee_default_value() -> Option<Self> {
todo!()
}
fn write<B: BufMut>(self, w: &mut B) -> cuprate_epee_encoding::Result<()> {
todo!()
}
}

View file

@ -3,8 +3,10 @@
//---------------------------------------------------------------------------------------------------- Import //---------------------------------------------------------------------------------------------------- Import
use std::fmt::Display; use std::fmt::Display;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
#[cfg(feature = "epee")]
use cuprate_epee_encoding::{ use cuprate_epee_encoding::{
macros::bytes::{Buf, BufMut}, macros::bytes::{Buf, BufMut},
EpeeValue, Marker, EpeeValue, Marker,
@ -16,18 +18,20 @@ use crate::constants::{
}; };
//---------------------------------------------------------------------------------------------------- Status //---------------------------------------------------------------------------------------------------- Status
// TODO: this type needs to expand more.
// There are a lot of RPC calls that will return a random
// string inside, which isn't compatible with [`Status`].
/// RPC response status. /// RPC response status.
/// ///
/// This type represents `monerod`'s frequently appearing string field, `status`. /// This type represents `monerod`'s frequently appearing string field, `status`.
/// ///
/// This field appears within RPC [JSON response](crate::json) types.
///
/// Reference: <https://github.com/monero-project/monero/blob/cc73fe71162d564ffda8e549b79a350bca53c454/src/rpc/core_rpc_server_commands_defs.h#L78-L81>. /// Reference: <https://github.com/monero-project/monero/blob/cc73fe71162d564ffda8e549b79a350bca53c454/src/rpc/core_rpc_server_commands_defs.h#L78-L81>.
/// ///
/// ## Serialization and string formatting /// ## Serialization and string formatting
/// ```rust /// ```rust
/// use cuprate_rpc_types::{ /// use cuprate_rpc_types::{
/// Status, /// misc::Status,
/// CORE_RPC_STATUS_BUSY, CORE_RPC_STATUS_NOT_MINING, CORE_RPC_STATUS_OK, /// CORE_RPC_STATUS_BUSY, CORE_RPC_STATUS_NOT_MINING, CORE_RPC_STATUS_OK,
/// CORE_RPC_STATUS_PAYMENT_REQUIRED, CORE_RPC_STATUS_UNKNOWN /// CORE_RPC_STATUS_PAYMENT_REQUIRED, CORE_RPC_STATUS_UNKNOWN
/// }; /// };
@ -59,28 +63,27 @@ use crate::constants::{
/// assert_eq!(format!("{:?}", Status::PaymentRequired), "PaymentRequired"); /// assert_eq!(format!("{:?}", Status::PaymentRequired), "PaymentRequired");
/// assert_eq!(format!("{:?}", unknown), "Unknown"); /// assert_eq!(format!("{:?}", unknown), "Unknown");
/// ``` /// ```
#[derive( #[derive(Copy, Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
Copy, Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize, #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
)]
pub enum Status { pub enum Status {
// FIXME: // FIXME:
// `#[serde(rename = "")]` only takes raw string literals? // `#[serde(rename = "")]` only takes raw string literals?
// We have to re-type the constants here... // We have to re-type the constants here...
/// Successful RPC response, everything is OK; [`CORE_RPC_STATUS_OK`]. /// Successful RPC response, everything is OK; [`CORE_RPC_STATUS_OK`].
#[serde(rename = "OK")] #[cfg_attr(feature = "serde", serde(rename = "OK"))]
#[default] #[default]
Ok, Ok,
/// The daemon is busy, try later; [`CORE_RPC_STATUS_BUSY`]. /// The daemon is busy, try later; [`CORE_RPC_STATUS_BUSY`].
#[serde(rename = "BUSY")] #[cfg_attr(feature = "serde", serde(rename = "BUSY"))]
Busy, Busy,
/// The daemon is not mining; [`CORE_RPC_STATUS_NOT_MINING`]. /// The daemon is not mining; [`CORE_RPC_STATUS_NOT_MINING`].
#[serde(rename = "NOT MINING")] #[cfg_attr(feature = "serde", serde(rename = "NOT MINING"))]
NotMining, NotMining,
/// Payment is required for RPC; [`CORE_RPC_STATUS_PAYMENT_REQUIRED`]. /// Payment is required for RPC; [`CORE_RPC_STATUS_PAYMENT_REQUIRED`].
#[serde(rename = "PAYMENT REQUIRED")] #[cfg_attr(feature = "serde", serde(rename = "PAYMENT REQUIRED"))]
PaymentRequired, PaymentRequired,
/// Some unknown other string; [`CORE_RPC_STATUS_UNKNOWN`]. /// Some unknown other string; [`CORE_RPC_STATUS_UNKNOWN`].
@ -91,8 +94,8 @@ pub enum Status {
/// The reason this isn't `Unknown(String)` is because that /// The reason this isn't `Unknown(String)` is because that
/// disallows [`Status`] to be [`Copy`], and thus other types /// disallows [`Status`] to be [`Copy`], and thus other types
/// that contain it. /// that contain it.
#[serde(other)] #[cfg_attr(feature = "serde", serde(other))]
#[serde(rename = "UNKNOWN")] #[cfg_attr(feature = "serde", serde(rename = "UNKNOWN"))]
Unknown, Unknown,
} }
@ -132,6 +135,7 @@ impl Display for Status {
// //
// See below for more impl info: // See below for more impl info:
// <https://github.com/Cuprate/cuprate/blob/bef2a2cbd4e1194991751d1fbc96603cba8c7a51/net/epee-encoding/src/value.rs#L366-L392>. // <https://github.com/Cuprate/cuprate/blob/bef2a2cbd4e1194991751d1fbc96603cba8c7a51/net/epee-encoding/src/value.rs#L366-L392>.
#[cfg(feature = "epee")]
impl EpeeValue for Status { impl EpeeValue for Status {
const MARKER: Marker = <String as EpeeValue>::MARKER; const MARKER: Marker = <String as EpeeValue>::MARKER;
@ -161,6 +165,7 @@ mod test {
// Test epee (de)serialization works. // Test epee (de)serialization works.
#[test] #[test]
#[cfg(feature = "epee")]
fn epee() { fn epee() {
for status in [ for status in [
Status::Ok, Status::Ok,

View file

@ -0,0 +1,59 @@
//! TODO
//---------------------------------------------------------------------------------------------------- Use
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
#[cfg(feature = "epee")]
use cuprate_epee_encoding::{
epee_object,
macros::bytes::{Buf, BufMut},
EpeeValue, Marker,
};
//---------------------------------------------------------------------------------------------------- TxEntry
#[doc = crate::macros::monero_definition_link!(
cc73fe71162d564ffda8e549b79a350bca53c454,
"rpc/core_rpc_server_commands_defs.h",
389..=428
)]
/// Used in [`crate::other::GetTransactionsResponse`].
#[derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct TxEntry {
pub as_hex: String,
pub as_json: String,
pub block_height: u64,
pub block_timestamp: u64,
pub confirmations: u64,
pub double_spend_seen: bool,
pub in_pool: bool,
pub output_indices: Vec<u64>,
pub prunable_as_hex: String,
pub prunable_hash: String,
pub pruned_as_hex: String,
pub received_timestamp: u64,
pub relayed: bool,
pub tx_hash: String,
}
// TODO: custom epee
// <https://github.com/monero-project/monero/blob/cc73fe71162d564ffda8e549b79a350bca53c454/src/rpc/core_rpc_server_commands_defs.h#L406-L427>
#[cfg(feature = "epee")]
epee_object! {
TxEntry,
as_hex: String,
as_json: String, // TODO: should be its own struct
block_height: u64,
block_timestamp: u64,
confirmations: u64,
double_spend_seen: bool,
in_pool: bool,
output_indices: Vec<u64>,
prunable_as_hex: String,
prunable_hash: String,
pruned_as_hex: String,
received_timestamp: u64,
relayed: bool,
tx_hash: String,
}

View file

@ -7,11 +7,15 @@ use crate::{base::ResponseBase, macros::define_request_and_response};
//---------------------------------------------------------------------------------------------------- TODO //---------------------------------------------------------------------------------------------------- TODO
define_request_and_response! { define_request_and_response! {
save_bc, get_height,
cc73fe71162d564ffda8e549b79a350bca53c454 => cc73fe71162d564ffda8e549b79a350bca53c454 =>
core_rpc_server_commands_defs.h => 898..=916, core_rpc_server_commands_defs.h => 138..=160,
SaveBc, GetHeight,
ResponseBase {} Request {},
ResponseBase {
hash: String,
height: u64,
}
} }
//---------------------------------------------------------------------------------------------------- Tests //---------------------------------------------------------------------------------------------------- Tests