mirror of
https://github.com/Cuprate/cuprate.git
synced 2024-12-22 19:49:28 +00:00
rpc: start cuprate-rpc-types
(#147)
* rpc: add `monero-rpc-types`
* lib.rs: add lints
* add base files, deps
* fix macro generation, doc test
* add `strum`, add `misc` module
* document struct generation macro
* add `GetHeight`
* lib.rs: create re-export macro
* macro changes, add few more types
* docs
* `monero-rpc-types` -> `cuprate-rpc-types`
* fix modules
* specify commit in macro, add () type aliases
* macro docs, fixes
* add `Status::Other(String)`
* add TODO for `strum`
* Update rpc/types/Cargo.toml
Co-authored-by: Boog900 <boog900@tutanota.com>
* add `BinaryString`
* add `ResponseBase`
* add `CORE_RPC_*` constants
* fix status; use `CORE_RPC_*` constants
* cargo.toml: add `epee_encoding`
* rpc: add epee_encoding impl for `Status`
* macro: add epee_encoding for every type
* remove `strum`
* add response bases
* add `CORE_RPC_STATUS_UNKNOWN`
* add response/request bases for epee
* create `base` module
* use different type for macro example
* move base / root types around
* docs, status serde test
* status: use `Status::Unknown` for `epee_default_value`
* json: add missing fields to `GetBlockTemplateRequest`
not sure I missed these
cc73fe7116/src/rpc/core_rpc_server_commands_defs.h (L947-L950)
---------
Co-authored-by: Boog900 <boog900@tutanota.com>
This commit is contained in:
parent
5c08d1a0e2
commit
e405786a73
16 changed files with 1035 additions and 5 deletions
7
Cargo.lock
generated
7
Cargo.lock
generated
|
@ -745,6 +745,13 @@ version = "0.0.0"
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cuprate-rpc-types"
|
name = "cuprate-rpc-types"
|
||||||
version = "0.0.0"
|
version = "0.0.0"
|
||||||
|
dependencies = [
|
||||||
|
"cuprate-epee-encoding",
|
||||||
|
"monero-serai",
|
||||||
|
"paste",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cuprate-test-utils"
|
name = "cuprate-test-utils"
|
||||||
|
|
|
@ -23,8 +23,8 @@ members = [
|
||||||
"test-utils",
|
"test-utils",
|
||||||
"types",
|
"types",
|
||||||
"rpc/json-rpc",
|
"rpc/json-rpc",
|
||||||
"rpc/rpc-types",
|
"rpc/types",
|
||||||
"rpc/rpc-interface",
|
"rpc/interface",
|
||||||
]
|
]
|
||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
|
|
|
@ -5,11 +5,18 @@ edition = "2021"
|
||||||
description = "Monero RPC types"
|
description = "Monero RPC types"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
authors = ["hinto-janai"]
|
authors = ["hinto-janai"]
|
||||||
repository = "https://github.com/Cuprate/cuprate/tree/main/rpc/monero-rpc-types"
|
repository = "https://github.com/Cuprate/cuprate/tree/main/rpc/types"
|
||||||
keywords = ["monero", "rpc", "types"]
|
keywords = ["cuprate", "rpc", "types", "monero"]
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
|
default = []
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
cuprate-epee-encoding = { path = "../../net/epee-encoding" }
|
||||||
|
|
||||||
|
monero-serai = { workspace = true }
|
||||||
|
paste = { workspace = true }
|
||||||
|
serde = { workspace = true }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
|
serde_json = { workspace = true }
|
62
rpc/types/README.md
Normal file
62
rpc/types/README.md
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
Monero RPC types.
|
||||||
|
|
||||||
|
# What
|
||||||
|
This crate ports the types used in Monero's RPC interface, including:
|
||||||
|
- JSON types
|
||||||
|
- Binary (epee) types
|
||||||
|
- Mixed types
|
||||||
|
- Other commonly used RPC types
|
||||||
|
|
||||||
|
# Modules
|
||||||
|
This crate's types are split in the following manner:
|
||||||
|
|
||||||
|
This crate has 4 modules:
|
||||||
|
- The root module; `cuprate_rpc_types`
|
||||||
|
- [`json`] module; JSON types from the `/json_rpc` endpoint
|
||||||
|
- [`bin`] module; Binary types from the binary endpoints
|
||||||
|
- [`other`] module; Misc JSON types from other endpoints
|
||||||
|
|
||||||
|
Miscellaneous types are found in the root module, e.g. [`crate::Status`].
|
||||||
|
|
||||||
|
Each type in `{json,bin,other}` come in pairs and have identical names, but are suffixed with either `Request` or `Response`. e.g. [`GetBlockCountRequest`](crate::json::GetBlockCountRequest) & [`GetBlockCountResponse`](crate::json::GetBlockCountResponse).
|
||||||
|
|
||||||
|
# Documentation
|
||||||
|
The documentation for types within `{json,bin,other}` are omitted, as they can be found in [Monero's RPC documentation](https://www.getmonero.org/resources/developer-guides/daemon-rpc.html).
|
||||||
|
|
||||||
|
However, each type will document:
|
||||||
|
- **Definition**: the exact type definition location in `monerod`
|
||||||
|
- **Documentation**: the Monero RPC documentation link
|
||||||
|
- **Request/response**: the other side of this type, either the request or response
|
||||||
|
|
||||||
|
# Naming
|
||||||
|
The naming for types within `{json,bin,other}` follow the following scheme:
|
||||||
|
- Convert the endpoint or method name into `UpperCamelCase`
|
||||||
|
- Remove any suffix extension
|
||||||
|
|
||||||
|
For example:
|
||||||
|
|
||||||
|
| Endpoint/method | Crate location and name |
|
||||||
|
|-----------------|-------------------------|
|
||||||
|
| [`get_block_count`](https://www.getmonero.org/resources/developer-guides/daemon-rpc.html#get_block_count) | [`json::GetBlockCountRequest`] & [`json::GetBlockCountResponse`]
|
||||||
|
| [`/get_blocks.bin`](https://www.getmonero.org/resources/developer-guides/daemon-rpc.html#get_blockbin) | `bin::GetBlocksRequest` & `bin::GetBlocksResponse`
|
||||||
|
| [`/get_height`](https://www.getmonero.org/resources/developer-guides/daemon-rpc.html#get_height) | `other::GetHeightRequest` & `other::GetHeightResponse`
|
||||||
|
|
||||||
|
TODO: fix doc links when types are ready.
|
||||||
|
|
||||||
|
# Mixed types
|
||||||
|
Note that some types within [`other`] mix JSON & binary together, i.e.,
|
||||||
|
the message overall is JSON, however some fields contain binary
|
||||||
|
values inside JSON strings, for example:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"string": "",
|
||||||
|
"float": 30.0,
|
||||||
|
"integer": 30,
|
||||||
|
"binary": "<serialized binary>"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
`binary` here is (de)serialized as a normal [`String`]. In order to be clear on which fields contain binary data, the struct fields that have them will use [`crate::BinaryString`] instead of [`String`].
|
||||||
|
|
||||||
|
TODO: list the specific types.
|
125
rpc/types/src/base.rs
Normal file
125
rpc/types/src/base.rs
Normal file
|
@ -0,0 +1,125 @@
|
||||||
|
//! The base data that appear in many RPC request/responses.
|
||||||
|
//!
|
||||||
|
//! These are the common "headers" or "base" types that are
|
||||||
|
//! [`flattened`](https://serde.rs/field-attrs.html#flatten)
|
||||||
|
//! into many of Monero's RPC types.
|
||||||
|
//!
|
||||||
|
//! The `Access*` structs (e.g. [`AccessResponseBase`]
|
||||||
|
//! are pseudo-deprecated structs for the RPC payment system, see:
|
||||||
|
//!
|
||||||
|
//! - <https://github.com/monero-project/monero/commit/2899379791b7542e4eb920b5d9d58cf232806937>
|
||||||
|
//! - <https://github.com/monero-project/monero/issues/8722>
|
||||||
|
//! - <https://github.com/monero-project/monero/pull/8843>
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------------------------------- Import
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use cuprate_epee_encoding::epee_object;
|
||||||
|
|
||||||
|
use crate::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
|
||||||
|
/// 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.
|
||||||
|
///
|
||||||
|
#[doc = monero_rpc_base_link!(114..=122)]
|
||||||
|
#[derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
|
||||||
|
pub struct AccessRequestBase {
|
||||||
|
/// The RPC payment client.
|
||||||
|
pub client: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
cuprate_epee_encoding::epee_object! {
|
||||||
|
AccessRequestBase,
|
||||||
|
client: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------------------------------- Responses
|
||||||
|
/// An empty response base.
|
||||||
|
///
|
||||||
|
/// 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.
|
||||||
|
///
|
||||||
|
#[doc = monero_rpc_base_link!(101..=112)]
|
||||||
|
#[derive(
|
||||||
|
Copy, Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize,
|
||||||
|
)]
|
||||||
|
pub struct ResponseBase {
|
||||||
|
/// General RPC error code. [`Status::Ok`] means everything looks good.
|
||||||
|
pub status: Status,
|
||||||
|
/// States if the result is obtained using the bootstrap mode,
|
||||||
|
/// and is therefore not trusted (`true`), or when the daemon
|
||||||
|
/// is fully synced and thus handles the RPC locally (`false`).
|
||||||
|
pub untrusted: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
epee_object! {
|
||||||
|
ResponseBase,
|
||||||
|
status: Status,
|
||||||
|
untrusted: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A base for RPC response types that support RPC payment.
|
||||||
|
///
|
||||||
|
#[doc = monero_rpc_base_link!(124..=136)]
|
||||||
|
#[derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
|
||||||
|
pub struct AccessResponseBase {
|
||||||
|
/// A flattened [`ResponseBase`].
|
||||||
|
#[serde(flatten)]
|
||||||
|
pub response_base: ResponseBase,
|
||||||
|
/// If payment for RPC is enabled, the number of credits
|
||||||
|
/// available to the requesting client. Otherwise, `0`.
|
||||||
|
pub credits: u64,
|
||||||
|
/// If payment for RPC is enabled, the hash of the
|
||||||
|
/// highest block in the chain. Otherwise, empty.
|
||||||
|
pub top_hash: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
epee_object! {
|
||||||
|
AccessResponseBase,
|
||||||
|
credits: u64,
|
||||||
|
top_hash: String,
|
||||||
|
!flatten: response_base: ResponseBase,
|
||||||
|
}
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------------------------------- Tests
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
// use super::*;
|
||||||
|
}
|
11
rpc/types/src/bin.rs
Normal file
11
rpc/types/src/bin.rs
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
//! Binary types from [binary](https://www.getmonero.org/resources/developer-guides/daemon-rpc.html#get_blocksbin) endpoints.
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------------------------------- Import
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------------------------------- TODO
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------------------------------- Tests
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
// use super::*;
|
||||||
|
}
|
29
rpc/types/src/binary_string.rs
Normal file
29
rpc/types/src/binary_string.rs
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
//! TODO
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------------------------------- Import
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------------------------------- BinaryString
|
||||||
|
/// TODO
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// use serde::Deserialize;
|
||||||
|
/// use serde_json::from_str;
|
||||||
|
/// use cuprate_rpc_types::BinaryString;
|
||||||
|
///
|
||||||
|
/// #[derive(Deserialize)]
|
||||||
|
/// struct Key {
|
||||||
|
/// key: BinaryString,
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// let binary = r"<22>\b<><62><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>";
|
||||||
|
/// let json = format!("{{\"key\":\"{binary}\"}}");
|
||||||
|
/// let key = from_str::<Key>(&json).unwrap();
|
||||||
|
/// let binary: BinaryString = key.key;
|
||||||
|
/// ```
|
||||||
|
pub type BinaryString = String;
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------------------------------- Tests
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
// use super::*;
|
||||||
|
}
|
65
rpc/types/src/constants.rs
Normal file
65
rpc/types/src/constants.rs
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
//! TODO
|
||||||
|
|
||||||
|
// From: <https://github.com/monero-project/monero/blob/cc73fe71162d564ffda8e549b79a350bca53c454/src/rpc/core_rpc_server_commands_defs.h#L83-L89>
|
||||||
|
//
|
||||||
|
// ```
|
||||||
|
// When making *any* change here, bump minor
|
||||||
|
// If the change is incompatible, then bump major and set minor to 0
|
||||||
|
// This ensures CORE_RPC_VERSION always increases, that every change
|
||||||
|
// has its own version, and that clients can just test major to see
|
||||||
|
// whether they can talk to a given daemon without having to know in
|
||||||
|
// advance which version they will stop working with
|
||||||
|
// Don't go over 32767 for any of these
|
||||||
|
// ```
|
||||||
|
//
|
||||||
|
// What this means for Cuprate: just follow `monerod`.
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------------------------------- Import
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------------------------------- Status
|
||||||
|
// Common RPC status strings:
|
||||||
|
// <https://github.com/monero-project/monero/blob/cc73fe71162d564ffda8e549b79a350bca53c454/src/rpc/core_rpc_server_commands_defs.h#L78-L81>.
|
||||||
|
//
|
||||||
|
// 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/core_rpc_server_commands_defs.h#L78>
|
||||||
|
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>
|
||||||
|
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>
|
||||||
|
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>
|
||||||
|
pub const CORE_RPC_STATUS_PAYMENT_REQUIRED: &str = "PAYMENT REQUIRED";
|
||||||
|
|
||||||
|
/// Custom `CORE_RPC_STATUS` for usage in Cuprate.
|
||||||
|
pub const CORE_RPC_STATUS_UNKNOWN: &str = "UNKNOWN";
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------------------------------- Versions
|
||||||
|
/// 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;
|
||||||
|
|
||||||
|
/// 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;
|
||||||
|
|
||||||
|
/// 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;
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------------------------------- Tests
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
// use super::*;
|
||||||
|
}
|
129
rpc/types/src/json.rs
Normal file
129
rpc/types/src/json.rs
Normal file
|
@ -0,0 +1,129 @@
|
||||||
|
//! JSON types from the [`/json_rpc`](https://www.getmonero.org/resources/developer-guides/daemon-rpc.html#json-rpc-methods) endpoint.
|
||||||
|
//!
|
||||||
|
//! <https://github.com/monero-project/monero/blob/cc73fe71162d564ffda8e549b79a350bca53c454/src/rpc/daemon_messages.h>.
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------------------------------- Import
|
||||||
|
use crate::{
|
||||||
|
base::{EmptyRequestBase, EmptyResponseBase, ResponseBase},
|
||||||
|
macros::define_request_and_response,
|
||||||
|
};
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------------------------------- Struct definitions
|
||||||
|
// This generates 2 structs:
|
||||||
|
//
|
||||||
|
// - `GetBlockTemplateRequest`
|
||||||
|
// - `GetBlockTemplateResponse`
|
||||||
|
//
|
||||||
|
// with some interconnected documentation.
|
||||||
|
define_request_and_response! {
|
||||||
|
// The markdown tag for Monero RPC documentation. Not necessarily the endpoint.
|
||||||
|
get_block_template,
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
cc73fe71162d564ffda8e549b79a350bca53c454 => core_rpc_server_commands_defs.h => 943..=994,
|
||||||
|
|
||||||
|
// The base type name.
|
||||||
|
GetBlockTemplate,
|
||||||
|
|
||||||
|
// The base request type.
|
||||||
|
//
|
||||||
|
// This must be a type found in [`crate::base`].
|
||||||
|
// It acts as a "base" that gets flattened into
|
||||||
|
// the actually 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>.
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
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)
|
||||||
|
// for the struct, they go here, e.g.:
|
||||||
|
// #[derive(Copy)]
|
||||||
|
ResponseBase {
|
||||||
|
// This is using `crate::base::ResponseBase`,
|
||||||
|
// so the type we generate will contain this field:
|
||||||
|
// ```
|
||||||
|
// base: crate::base::ResponseBase,
|
||||||
|
// ```
|
||||||
|
//
|
||||||
|
// This is flattened with serde and epee, so during
|
||||||
|
// (de)serialization, it will act as if there are 2 extra fields here:
|
||||||
|
// ```
|
||||||
|
// status: crate::Status,
|
||||||
|
// 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,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
define_request_and_response! {
|
||||||
|
get_block_count,
|
||||||
|
cc73fe71162d564ffda8e549b79a350bca53c454 =>
|
||||||
|
core_rpc_server_commands_defs.h => 919..=933,
|
||||||
|
GetBlockCount,
|
||||||
|
|
||||||
|
// There is no request type specified,
|
||||||
|
// this will cause the macro to generate a
|
||||||
|
// 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,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------------------------------- Tests
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
// use super::*;
|
||||||
|
}
|
116
rpc/types/src/lib.rs
Normal file
116
rpc/types/src/lib.rs
Normal file
|
@ -0,0 +1,116 @@
|
||||||
|
#![doc = include_str!("../README.md")]
|
||||||
|
//---------------------------------------------------------------------------------------------------- Lints
|
||||||
|
// Forbid lints.
|
||||||
|
// Our code, and code generated (e.g macros) cannot overrule these.
|
||||||
|
#![forbid(
|
||||||
|
// `unsafe` is allowed but it _must_ be
|
||||||
|
// commented with `SAFETY: reason`.
|
||||||
|
clippy::undocumented_unsafe_blocks,
|
||||||
|
|
||||||
|
// Never.
|
||||||
|
unused_unsafe,
|
||||||
|
redundant_semicolons,
|
||||||
|
unused_allocation,
|
||||||
|
coherence_leak_check,
|
||||||
|
while_true,
|
||||||
|
clippy::missing_docs_in_private_items,
|
||||||
|
|
||||||
|
// Maybe can be put into `#[deny]`.
|
||||||
|
unconditional_recursion,
|
||||||
|
for_loops_over_fallibles,
|
||||||
|
unused_braces,
|
||||||
|
unused_labels,
|
||||||
|
keyword_idents,
|
||||||
|
non_ascii_idents,
|
||||||
|
variant_size_differences,
|
||||||
|
single_use_lifetimes,
|
||||||
|
|
||||||
|
// Probably can be put into `#[deny]`.
|
||||||
|
future_incompatible,
|
||||||
|
let_underscore,
|
||||||
|
break_with_label_and_loop,
|
||||||
|
duplicate_macro_attributes,
|
||||||
|
exported_private_dependencies,
|
||||||
|
large_assignments,
|
||||||
|
overlapping_range_endpoints,
|
||||||
|
semicolon_in_expressions_from_macros,
|
||||||
|
noop_method_call,
|
||||||
|
)]
|
||||||
|
// Deny lints.
|
||||||
|
// Some of these are `#[allow]`'ed on a per-case basis.
|
||||||
|
#![deny(
|
||||||
|
clippy::all,
|
||||||
|
clippy::correctness,
|
||||||
|
clippy::suspicious,
|
||||||
|
clippy::style,
|
||||||
|
clippy::complexity,
|
||||||
|
clippy::perf,
|
||||||
|
clippy::pedantic,
|
||||||
|
clippy::nursery,
|
||||||
|
clippy::cargo,
|
||||||
|
unused_doc_comments,
|
||||||
|
unused_mut,
|
||||||
|
missing_docs,
|
||||||
|
deprecated,
|
||||||
|
unused_comparisons,
|
||||||
|
nonstandard_style,
|
||||||
|
unreachable_pub
|
||||||
|
)]
|
||||||
|
#![allow(
|
||||||
|
// FIXME: this lint affects crates outside of
|
||||||
|
// `database/` for some reason, allow for now.
|
||||||
|
clippy::cargo_common_metadata,
|
||||||
|
|
||||||
|
// FIXME: adding `#[must_use]` onto everything
|
||||||
|
// might just be more annoying than useful...
|
||||||
|
// although it is sometimes nice.
|
||||||
|
clippy::must_use_candidate,
|
||||||
|
|
||||||
|
// FIXME: good lint but too many false positives
|
||||||
|
// with our `Env` + `RwLock` setup.
|
||||||
|
clippy::significant_drop_tightening,
|
||||||
|
|
||||||
|
// FIXME: good lint but is less clear in most cases.
|
||||||
|
clippy::items_after_statements,
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
rustdoc::bare_urls,
|
||||||
|
|
||||||
|
clippy::module_name_repetitions,
|
||||||
|
clippy::module_inception,
|
||||||
|
clippy::redundant_pub_crate,
|
||||||
|
clippy::option_if_let_else,
|
||||||
|
)]
|
||||||
|
// Allow some lints when running in debug mode.
|
||||||
|
#![cfg_attr(debug_assertions, allow(clippy::todo, clippy::multiple_crate_versions))]
|
||||||
|
// Allow some lints in tests.
|
||||||
|
#![cfg_attr(
|
||||||
|
test,
|
||||||
|
allow(
|
||||||
|
clippy::cognitive_complexity,
|
||||||
|
clippy::needless_pass_by_value,
|
||||||
|
clippy::cast_possible_truncation,
|
||||||
|
clippy::too_many_lines
|
||||||
|
)
|
||||||
|
)]
|
||||||
|
// TODO: remove me after finishing impl
|
||||||
|
#![allow(dead_code)]
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------------------------------- Use
|
||||||
|
mod binary_string;
|
||||||
|
mod constants;
|
||||||
|
mod macros;
|
||||||
|
mod status;
|
||||||
|
|
||||||
|
pub use binary_string::BinaryString;
|
||||||
|
pub use constants::{
|
||||||
|
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_VERSION_MAJOR, CORE_RPC_VERSION_MINOR,
|
||||||
|
};
|
||||||
|
pub use status::Status;
|
||||||
|
|
||||||
|
pub mod base;
|
||||||
|
pub mod bin;
|
||||||
|
pub mod json;
|
||||||
|
pub mod other;
|
277
rpc/types/src/macros.rs
Normal file
277
rpc/types/src/macros.rs
Normal file
|
@ -0,0 +1,277 @@
|
||||||
|
//! Macros.
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------------------------------- Struct definition
|
||||||
|
/// A template for generating 2 `struct`s with a bunch of information filled out.
|
||||||
|
///
|
||||||
|
/// These are the RPC request and response `struct`s.
|
||||||
|
///
|
||||||
|
/// These `struct`s automatically implement:
|
||||||
|
/// - `Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash`
|
||||||
|
/// - `serde::{Serialize, Deserialize}`
|
||||||
|
/// - `epee_encoding::EpeeObject`
|
||||||
|
///
|
||||||
|
/// It's best to see the output of this macro via the documentation
|
||||||
|
/// of the generated structs via `cargo doc`s to see which parts
|
||||||
|
/// generate which docs.
|
||||||
|
///
|
||||||
|
/// See the [`crate::json`] module for example usage.
|
||||||
|
///
|
||||||
|
/// # Macro internals
|
||||||
|
/// This macro has 2 branches with almost the same output:
|
||||||
|
/// 1. An empty `Request` type
|
||||||
|
/// 2. An `Request` type with fields
|
||||||
|
///
|
||||||
|
/// The first branch is the same as the second with the exception
|
||||||
|
/// that if the caller of this macro provides no fields, it will
|
||||||
|
/// generate:
|
||||||
|
/// ```
|
||||||
|
/// pub type Request = ();
|
||||||
|
/// ```
|
||||||
|
/// instead of:
|
||||||
|
/// ```
|
||||||
|
/// pub struct Request {/* fields */}
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// This is because having a bunch of types that are all empty structs
|
||||||
|
/// means they are not compatible and it makes it cumbersome for end-users.
|
||||||
|
/// Really, they semantically are empty types, so `()` is used.
|
||||||
|
///
|
||||||
|
/// Again, other than this, the 2 branches do (should) not differ.
|
||||||
|
///
|
||||||
|
/// FIXME: there's probably a less painful way to branch here on input
|
||||||
|
/// without having to duplicate 80% of the macro. Sub-macros were attempted
|
||||||
|
/// 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
|
||||||
|
/// the doc generation with [`define_request_and_response_doc`].
|
||||||
|
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.
|
||||||
|
$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 request type (and any doc comments, derives, etc).
|
||||||
|
$( #[$request_type_attr:meta] )*
|
||||||
|
$request_base_type:ty {
|
||||||
|
// And any fields.
|
||||||
|
$(
|
||||||
|
$( #[$request_field_attr:meta] )*
|
||||||
|
$request_field:ident: $request_field_type:ty,
|
||||||
|
)*
|
||||||
|
},
|
||||||
|
|
||||||
|
// 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! {
|
||||||
|
#[allow(dead_code)]
|
||||||
|
#[allow(missing_docs)]
|
||||||
|
#[derive(serde::Serialize, serde::Deserialize)]
|
||||||
|
#[derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
|
$( #[$request_type_attr] )*
|
||||||
|
#[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>],
|
||||||
|
)]
|
||||||
|
pub struct [<$type_name Request>] {
|
||||||
|
#[serde(flatten)]
|
||||||
|
pub base: $request_base_type,
|
||||||
|
|
||||||
|
$(
|
||||||
|
$( #[$request_field_attr] )*
|
||||||
|
pub $request_field: $request_field_type,
|
||||||
|
)*
|
||||||
|
}
|
||||||
|
|
||||||
|
::cuprate_epee_encoding::epee_object! {
|
||||||
|
[<$type_name Request>],
|
||||||
|
$(
|
||||||
|
$request_field: $request_field_type,
|
||||||
|
)*
|
||||||
|
!flatten: base: $request_base_type,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[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,
|
||||||
|
}
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
pub(crate) use define_request_and_response;
|
||||||
|
|
||||||
|
/// Generate documentation for the types generated
|
||||||
|
/// by the [`define_request_and_response`] macro.
|
||||||
|
///
|
||||||
|
/// See it for more info on inputs.
|
||||||
|
macro_rules! define_request_and_response_doc {
|
||||||
|
(
|
||||||
|
// This labels the last `[request]` or `[response]`
|
||||||
|
// hyperlink in documentation. Input is either:
|
||||||
|
// - "request"
|
||||||
|
// - "response"
|
||||||
|
//
|
||||||
|
// Remember this is linking to the _other_ type,
|
||||||
|
// so if defining a `Request` type, input should
|
||||||
|
// be "response".
|
||||||
|
$request_or_response:literal,
|
||||||
|
|
||||||
|
$monero_daemon_rpc_doc_link:ident,
|
||||||
|
$monero_code_commit:ident,
|
||||||
|
$monero_code_filename:ident,
|
||||||
|
$monero_code_filename_extension:ident,
|
||||||
|
$monero_code_line_start:literal,
|
||||||
|
$monero_code_line_end:literal,
|
||||||
|
$type_name:ident,
|
||||||
|
) => {
|
||||||
|
concat!(
|
||||||
|
"",
|
||||||
|
"[Definition](",
|
||||||
|
"https://github.com/monero-project/monero/blob/",
|
||||||
|
stringify!($monero_code_commit),
|
||||||
|
"/src/rpc/",
|
||||||
|
stringify!($monero_code_filename),
|
||||||
|
".",
|
||||||
|
stringify!($monero_code_filename_extension),
|
||||||
|
"#L",
|
||||||
|
stringify!($monero_code_line_start),
|
||||||
|
"-L",
|
||||||
|
stringify!($monero_code_line_end),
|
||||||
|
"), [documentation](",
|
||||||
|
"https://www.getmonero.org/resources/developer-guides/daemon-rpc.html",
|
||||||
|
"#",
|
||||||
|
stringify!($monero_daemon_rpc_doc_link),
|
||||||
|
"), [",
|
||||||
|
$request_or_response,
|
||||||
|
"](",
|
||||||
|
stringify!($type_name),
|
||||||
|
")."
|
||||||
|
)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
pub(crate) use define_request_and_response_doc;
|
21
rpc/types/src/other.rs
Normal file
21
rpc/types/src/other.rs
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
//! JSON types from the [`other`](https://www.getmonero.org/resources/developer-guides/daemon-rpc.html#other-daemon-rpc-calls) endpoints.
|
||||||
|
//!
|
||||||
|
//! <https://github.com/monero-project/monero/blob/cc73fe71162d564ffda8e549b79a350bca53c454/src/rpc/daemon_messages.h>.
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------------------------------- Import
|
||||||
|
use crate::{base::ResponseBase, macros::define_request_and_response};
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------------------------------- TODO
|
||||||
|
define_request_and_response! {
|
||||||
|
save_bc,
|
||||||
|
cc73fe71162d564ffda8e549b79a350bca53c454 =>
|
||||||
|
core_rpc_server_commands_defs.h => 898..=916,
|
||||||
|
SaveBc,
|
||||||
|
ResponseBase {}
|
||||||
|
}
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------------------------------- Tests
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
// use super::*;
|
||||||
|
}
|
182
rpc/types/src/status.rs
Normal file
182
rpc/types/src/status.rs
Normal file
|
@ -0,0 +1,182 @@
|
||||||
|
//! RPC response status type.
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------------------------------- Import
|
||||||
|
use std::fmt::Display;
|
||||||
|
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use cuprate_epee_encoding::{
|
||||||
|
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,
|
||||||
|
};
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------------------------------- Status
|
||||||
|
/// RPC response 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>.
|
||||||
|
///
|
||||||
|
/// ## Serialization and string formatting
|
||||||
|
/// ```rust
|
||||||
|
/// use cuprate_rpc_types::{
|
||||||
|
/// Status,
|
||||||
|
/// CORE_RPC_STATUS_BUSY, CORE_RPC_STATUS_NOT_MINING, CORE_RPC_STATUS_OK,
|
||||||
|
/// CORE_RPC_STATUS_PAYMENT_REQUIRED, CORE_RPC_STATUS_UNKNOWN
|
||||||
|
/// };
|
||||||
|
/// use serde_json::to_string;
|
||||||
|
///
|
||||||
|
/// let unknown = Status::Unknown;
|
||||||
|
///
|
||||||
|
/// assert_eq!(to_string(&Status::Ok).unwrap(), r#""OK""#);
|
||||||
|
/// assert_eq!(to_string(&Status::Busy).unwrap(), r#""BUSY""#);
|
||||||
|
/// assert_eq!(to_string(&Status::NotMining).unwrap(), r#""NOT MINING""#);
|
||||||
|
/// assert_eq!(to_string(&Status::PaymentRequired).unwrap(), r#""PAYMENT REQUIRED""#);
|
||||||
|
/// assert_eq!(to_string(&unknown).unwrap(), r#""UNKNOWN""#);
|
||||||
|
///
|
||||||
|
/// assert_eq!(Status::Ok.as_ref(), CORE_RPC_STATUS_OK);
|
||||||
|
/// assert_eq!(Status::Busy.as_ref(), CORE_RPC_STATUS_BUSY);
|
||||||
|
/// assert_eq!(Status::NotMining.as_ref(), CORE_RPC_STATUS_NOT_MINING);
|
||||||
|
/// assert_eq!(Status::PaymentRequired.as_ref(), CORE_RPC_STATUS_PAYMENT_REQUIRED);
|
||||||
|
/// assert_eq!(unknown.as_ref(), CORE_RPC_STATUS_UNKNOWN);
|
||||||
|
///
|
||||||
|
/// assert_eq!(format!("{}", Status::Ok), CORE_RPC_STATUS_OK);
|
||||||
|
/// assert_eq!(format!("{}", Status::Busy), CORE_RPC_STATUS_BUSY);
|
||||||
|
/// assert_eq!(format!("{}", Status::NotMining), CORE_RPC_STATUS_NOT_MINING);
|
||||||
|
/// assert_eq!(format!("{}", Status::PaymentRequired), CORE_RPC_STATUS_PAYMENT_REQUIRED);
|
||||||
|
/// assert_eq!(format!("{}", unknown), CORE_RPC_STATUS_UNKNOWN);
|
||||||
|
///
|
||||||
|
/// assert_eq!(format!("{:?}", Status::Ok), "Ok");
|
||||||
|
/// assert_eq!(format!("{:?}", Status::Busy), "Busy");
|
||||||
|
/// assert_eq!(format!("{:?}", Status::NotMining), "NotMining");
|
||||||
|
/// assert_eq!(format!("{:?}", Status::PaymentRequired), "PaymentRequired");
|
||||||
|
/// assert_eq!(format!("{:?}", unknown), "Unknown");
|
||||||
|
/// ```
|
||||||
|
#[derive(
|
||||||
|
Copy, Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize,
|
||||||
|
)]
|
||||||
|
pub enum Status {
|
||||||
|
// FIXME:
|
||||||
|
// `#[serde(rename = "")]` only takes raw string literals?
|
||||||
|
// We have to re-type the constants here...
|
||||||
|
/// Successful RPC response, everything is OK; [`CORE_RPC_STATUS_OK`].
|
||||||
|
#[serde(rename = "OK")]
|
||||||
|
#[default]
|
||||||
|
Ok,
|
||||||
|
|
||||||
|
/// The daemon is busy, try later; [`CORE_RPC_STATUS_BUSY`].
|
||||||
|
#[serde(rename = "BUSY")]
|
||||||
|
Busy,
|
||||||
|
|
||||||
|
/// The daemon is not mining; [`CORE_RPC_STATUS_NOT_MINING`].
|
||||||
|
#[serde(rename = "NOT MINING")]
|
||||||
|
NotMining,
|
||||||
|
|
||||||
|
/// Payment is required for RPC; [`CORE_RPC_STATUS_PAYMENT_REQUIRED`].
|
||||||
|
#[serde(rename = "PAYMENT REQUIRED")]
|
||||||
|
PaymentRequired,
|
||||||
|
|
||||||
|
/// Some unknown other string; [`CORE_RPC_STATUS_UNKNOWN`].
|
||||||
|
///
|
||||||
|
/// This exists to act as a catch-all if `monerod` adds
|
||||||
|
/// a string and a Cuprate node hasn't updated yet.
|
||||||
|
///
|
||||||
|
/// The reason this isn't `Unknown(String)` is because that
|
||||||
|
/// disallows [`Status`] to be [`Copy`], and thus other types
|
||||||
|
/// that contain it.
|
||||||
|
#[serde(other)]
|
||||||
|
#[serde(rename = "UNKNOWN")]
|
||||||
|
Unknown,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<String> for Status {
|
||||||
|
fn from(s: String) -> Self {
|
||||||
|
match s.as_str() {
|
||||||
|
CORE_RPC_STATUS_OK => Self::Ok,
|
||||||
|
CORE_RPC_STATUS_BUSY => Self::Busy,
|
||||||
|
CORE_RPC_STATUS_NOT_MINING => Self::NotMining,
|
||||||
|
CORE_RPC_STATUS_PAYMENT_REQUIRED => Self::PaymentRequired,
|
||||||
|
_ => Self::Unknown,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AsRef<str> for Status {
|
||||||
|
fn as_ref(&self) -> &str {
|
||||||
|
match self {
|
||||||
|
Self::Ok => CORE_RPC_STATUS_OK,
|
||||||
|
Self::Busy => CORE_RPC_STATUS_BUSY,
|
||||||
|
Self::NotMining => CORE_RPC_STATUS_NOT_MINING,
|
||||||
|
Self::PaymentRequired => CORE_RPC_STATUS_PAYMENT_REQUIRED,
|
||||||
|
Self::Unknown => CORE_RPC_STATUS_UNKNOWN,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for Status {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
f.write_str(self.as_ref())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// [`Status`] is essentially a [`String`] when it comes to
|
||||||
|
// (de)serialization, except when writing we usually have
|
||||||
|
// access to a `&'static str` and don't need to allocate.
|
||||||
|
//
|
||||||
|
// See below for more impl info:
|
||||||
|
// <https://github.com/Cuprate/cuprate/blob/bef2a2cbd4e1194991751d1fbc96603cba8c7a51/net/epee-encoding/src/value.rs#L366-L392>.
|
||||||
|
impl EpeeValue for Status {
|
||||||
|
const MARKER: Marker = <String as EpeeValue>::MARKER;
|
||||||
|
|
||||||
|
fn read<B: Buf>(r: &mut B, marker: &Marker) -> cuprate_epee_encoding::Result<Self> {
|
||||||
|
let string = <String as EpeeValue>::read(r, marker)?;
|
||||||
|
Ok(Self::from(string))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn should_write(&self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
fn epee_default_value() -> Option<Self> {
|
||||||
|
// <https://github.com/Cuprate/cuprate/pull/147#discussion_r1654992559>
|
||||||
|
Some(Self::Unknown)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write<B: BufMut>(self, w: &mut B) -> cuprate_epee_encoding::Result<()> {
|
||||||
|
cuprate_epee_encoding::write_bytes(self.as_ref(), w)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------------------------------- Tests
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
// Test epee (de)serialization works.
|
||||||
|
#[test]
|
||||||
|
fn epee() {
|
||||||
|
for status in [
|
||||||
|
Status::Ok,
|
||||||
|
Status::Busy,
|
||||||
|
Status::NotMining,
|
||||||
|
Status::PaymentRequired,
|
||||||
|
Status::Unknown,
|
||||||
|
] {
|
||||||
|
let mut buf = vec![];
|
||||||
|
|
||||||
|
<Status as EpeeValue>::write(status, &mut buf).unwrap();
|
||||||
|
let status2 =
|
||||||
|
<Status as EpeeValue>::read(&mut buf.as_slice(), &<Status as EpeeValue>::MARKER)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
assert_eq!(status, status2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue