mirror of
https://github.com/Cuprate/cuprate.git
synced 2024-12-22 19:49:28 +00:00
rpc: custom epee for misc/bin types (#229)
* fixed-bytes: add `serde`, document feature flags * fixed-bytes: add derives * rpc: add `as _` syntax to macro * rpc: use `ByteArrayVec` and `ContainerAsBlob` for binary types * fixed-bytes: re-add derives * rpc-types: dedup default value within macro * readme: fixed bytes section * types: custom epee - `BlockCompleteEntry` * types: custom epee - `KeyImageSpentStatus` * types: custom epee - `PoolInfoExtent` * types: add `Status::Other(String)` variant * types: custom epee - `TxEntry`, add `read_epee_field` macro * bin: custom epee - `GetBlocks` * types: add `serde.rs` * misc: make `TxEntry` an `enum`, impl serde * misc: `unimplemented!()` for `TxEntry`'s epee * types: add `BlockCompleteEntry` * rpc: replace `BlockCompleteEntry` with `cuprate-types` * types: document `BlockCompleteEntry` * bin: fix `number_of_fields` for `GetBlocksResponse` * misc: add `Distribution` * distribution: add todo * misc fixes * readme: add `(De)serialization invariants` * distribution: compress variants * types: add `block_complete_entry.rs` * net: fix imports * p2p: fix imports * turn off default-features * p2p: fix imports * misc fixes * Update net/wire/Cargo.toml Co-authored-by: Boog900 <boog900@tutanota.com> * distribution: module doc * wire: re-export types * bin: use enum for `GetBlocksResponse` * misc: use lowercase for stringify * remove duplicated fields for custom epee * types: remove `should_write()` for custom epee * bin: split `GetBlocksResponse` variant fields into structs * misc: split `Distribution` variant fields into structs * small fixes * put all fields in `read_epee_field!` * distribution: (de)compress during epee/serde (de)serialization * distribution: leave (de)compression functions as `todo!()` --------- Co-authored-by: Boog900 <boog900@tutanota.com>
This commit is contained in:
parent
aa718e224f
commit
929d19c450
20 changed files with 949 additions and 236 deletions
2
Cargo.lock
generated
2
Cargo.lock
generated
|
@ -764,8 +764,10 @@ version = "0.0.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cuprate-epee-encoding",
|
"cuprate-epee-encoding",
|
||||||
"cuprate-fixed-bytes",
|
"cuprate-fixed-bytes",
|
||||||
|
"cuprate-types",
|
||||||
"monero-serai",
|
"monero-serai",
|
||||||
"paste",
|
"paste",
|
||||||
|
"pretty_assertions",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
]
|
]
|
||||||
|
|
|
@ -16,6 +16,7 @@ epee = ["dep:cuprate-epee-encoding"]
|
||||||
[dependencies]
|
[dependencies]
|
||||||
cuprate-epee-encoding = { path = "../../net/epee-encoding", optional = true }
|
cuprate-epee-encoding = { path = "../../net/epee-encoding", optional = true }
|
||||||
cuprate-fixed-bytes = { path = "../../net/fixed-bytes" }
|
cuprate-fixed-bytes = { path = "../../net/fixed-bytes" }
|
||||||
|
cuprate-types = { path = "../../types" }
|
||||||
|
|
||||||
monero-serai = { workspace = true }
|
monero-serai = { workspace = true }
|
||||||
paste = { workspace = true }
|
paste = { workspace = true }
|
||||||
|
@ -23,3 +24,4 @@ serde = { workspace = true, optional = true }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
serde_json = { workspace = true }
|
serde_json = { workspace = true }
|
||||||
|
pretty_assertions = { workspace = true }
|
||||||
|
|
|
@ -78,6 +78,22 @@ will be used instead of a more typical [`String`] for optimization reasons.
|
||||||
|
|
||||||
-->
|
-->
|
||||||
|
|
||||||
|
# (De)serialization invariants
|
||||||
|
Due to how types are defined in this library internally (all through a single macro),
|
||||||
|
most types implement both `serde` and `epee`.
|
||||||
|
|
||||||
|
However, some of the types will panic with [`unimplemented`]
|
||||||
|
or will otherwise have undefined implementation in the incorrect context.
|
||||||
|
|
||||||
|
In other words:
|
||||||
|
- The epee (de)serialization of [`json`] & [`other`] types should **not** be relied upon
|
||||||
|
- The JSON (de)serialization of [`bin`] types should **not** be relied upon
|
||||||
|
|
||||||
|
The invariants that can be relied upon:
|
||||||
|
- Types in [`json`] & [`other`] will implement `serde` correctly
|
||||||
|
- Types in [`bin`] will implement `epee` correctly
|
||||||
|
- Misc types will implement `serde/epee` correctly as needed
|
||||||
|
|
||||||
# Feature flags
|
# Feature flags
|
||||||
List of feature flags for `cuprate-rpc-types`.
|
List of feature flags for `cuprate-rpc-types`.
|
||||||
|
|
||||||
|
|
|
@ -46,7 +46,7 @@ epee_object! {
|
||||||
//---------------------------------------------------------------------------------------------------- Responses
|
//---------------------------------------------------------------------------------------------------- Responses
|
||||||
#[doc = monero_definition_link!(cc73fe71162d564ffda8e549b79a350bca53c454, "rpc/core_rpc_server_commands_defs.h", 101..=112)]
|
#[doc = monero_definition_link!(cc73fe71162d564ffda8e549b79a350bca53c454, "rpc/core_rpc_server_commands_defs.h", 101..=112)]
|
||||||
/// The most common base for responses.
|
/// The most common base for responses.
|
||||||
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
#[derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
#[cfg_attr(feature = "serde", derive(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.
|
||||||
|
|
|
@ -5,51 +5,32 @@
|
||||||
//---------------------------------------------------------------------------------------------------- Import
|
//---------------------------------------------------------------------------------------------------- Import
|
||||||
use cuprate_fixed_bytes::ByteArrayVec;
|
use cuprate_fixed_bytes::ByteArrayVec;
|
||||||
|
|
||||||
|
#[cfg(feature = "serde")]
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
#[cfg(feature = "epee")]
|
#[cfg(feature = "epee")]
|
||||||
use cuprate_epee_encoding::container_as_blob::ContainerAsBlob;
|
use cuprate_epee_encoding::{
|
||||||
|
container_as_blob::ContainerAsBlob,
|
||||||
|
epee_object, error,
|
||||||
|
macros::bytes::{Buf, BufMut},
|
||||||
|
read_epee_value, write_field, EpeeObject, EpeeObjectBuilder, EpeeValue,
|
||||||
|
};
|
||||||
|
|
||||||
|
use cuprate_types::BlockCompleteEntry;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
base::{AccessResponseBase, ResponseBase},
|
base::{AccessResponseBase, ResponseBase},
|
||||||
defaults::{default_false, default_height, default_string, default_vec, default_zero},
|
defaults::{default_false, default_height, default_string, default_vec, default_zero},
|
||||||
free::{is_one, is_zero},
|
free::{is_one, is_zero},
|
||||||
macros::define_request_and_response,
|
macros::{define_request, define_request_and_response, define_request_and_response_doc},
|
||||||
misc::{
|
misc::{
|
||||||
AuxPow, BlockCompleteEntry, BlockHeader, BlockOutputIndices, ChainInfo, ConnectionInfo,
|
AuxPow, BlockHeader, BlockOutputIndices, ChainInfo, ConnectionInfo, GetBan, GetOutputsOut,
|
||||||
GetBan, GetOutputsOut, HardforkEntry, HistogramEntry, OutKeyBin, OutputDistributionData,
|
HardforkEntry, HistogramEntry, OutKeyBin, OutputDistributionData, Peer, PoolInfoExtent,
|
||||||
Peer, PoolTxInfo, SetBan, Span, Status, TxBacklogEntry,
|
PoolTxInfo, SetBan, Span, Status, TxBacklogEntry,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
//---------------------------------------------------------------------------------------------------- TODO
|
//---------------------------------------------------------------------------------------------------- Definitions
|
||||||
define_request_and_response! {
|
|
||||||
get_blocksbin,
|
|
||||||
cc73fe71162d564ffda8e549b79a350bca53c454 =>
|
|
||||||
core_rpc_server_commands_defs.h => 162..=262,
|
|
||||||
GetBlocks,
|
|
||||||
Request {
|
|
||||||
requested_info: u8 = default_zero(), "default_zero",
|
|
||||||
// FIXME: This is a `std::list` in `monerod` because...?
|
|
||||||
block_ids: ByteArrayVec<32>,
|
|
||||||
start_height: u64,
|
|
||||||
prune: bool,
|
|
||||||
no_miner_tx: bool = default_false(), "default_false",
|
|
||||||
pool_info_since: u64 = default_zero(), "default_zero",
|
|
||||||
},
|
|
||||||
// TODO: this has custom epee (de)serialization.
|
|
||||||
// <https://github.com/monero-project/monero/blob/cc73fe71162d564ffda8e549b79a350bca53c454/src/rpc/core_rpc_server_commands_defs.h#L242-L259>
|
|
||||||
ResponseBase {
|
|
||||||
blocks: Vec<BlockCompleteEntry>,
|
|
||||||
start_height: u64,
|
|
||||||
current_height: u64,
|
|
||||||
output_indices: Vec<BlockOutputIndices>,
|
|
||||||
daemon_time: u64,
|
|
||||||
pool_info_extent: u8,
|
|
||||||
added_pool_txs: Vec<PoolTxInfo>,
|
|
||||||
remaining_added_pool_txids: Vec<[u8; 32]>,
|
|
||||||
removed_pool_txids: Vec<[u8; 32]>,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
define_request_and_response! {
|
define_request_and_response! {
|
||||||
get_blocks_by_heightbin,
|
get_blocks_by_heightbin,
|
||||||
cc73fe71162d564ffda8e549b79a350bca53c454 =>
|
cc73fe71162d564ffda8e549b79a350bca53c454 =>
|
||||||
|
@ -134,6 +115,284 @@ define_request_and_response! {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------------------------------- GetBlocks
|
||||||
|
define_request! {
|
||||||
|
#[doc = define_request_and_response_doc!(
|
||||||
|
"response" => GetBlocksResponse,
|
||||||
|
get_blocksbin,
|
||||||
|
cc73fe71162d564ffda8e549b79a350bca53c454,
|
||||||
|
core_rpc_server_commands_defs, h, 162, 262,
|
||||||
|
)]
|
||||||
|
GetBlocksRequest {
|
||||||
|
requested_info: u8 = default_zero::<u8>(), "default_zero",
|
||||||
|
// FIXME: This is a `std::list` in `monerod` because...?
|
||||||
|
block_ids: ByteArrayVec<32>,
|
||||||
|
start_height: u64,
|
||||||
|
prune: bool,
|
||||||
|
no_miner_tx: bool = default_false(), "default_false",
|
||||||
|
pool_info_since: u64 = default_zero::<u64>(), "default_zero",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[doc = define_request_and_response_doc!(
|
||||||
|
"request" => GetBlocksRequest,
|
||||||
|
get_blocksbin,
|
||||||
|
cc73fe71162d564ffda8e549b79a350bca53c454,
|
||||||
|
core_rpc_server_commands_defs, h, 162, 262,
|
||||||
|
)]
|
||||||
|
///
|
||||||
|
/// This response's variant depends upon [`PoolInfoExtent`].
|
||||||
|
#[allow(dead_code, missing_docs)]
|
||||||
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
|
pub enum GetBlocksResponse {
|
||||||
|
/// Will always serialize a [`PoolInfoExtent::None`] field.
|
||||||
|
PoolInfoNone(GetBlocksResponsePoolInfoNone),
|
||||||
|
/// Will always serialize a [`PoolInfoExtent::Incremental`] field.
|
||||||
|
PoolInfoIncremental(GetBlocksResponsePoolInfoIncremental),
|
||||||
|
/// Will always serialize a [`PoolInfoExtent::Full`] field.
|
||||||
|
PoolInfoFull(GetBlocksResponsePoolInfoFull),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for GetBlocksResponse {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::PoolInfoNone(GetBlocksResponsePoolInfoNone::default())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Data within [`GetBlocksResponse::PoolInfoNone`].
|
||||||
|
#[allow(dead_code, missing_docs)]
|
||||||
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
|
#[derive(Clone, Default, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
|
pub struct GetBlocksResponsePoolInfoNone {
|
||||||
|
pub status: Status,
|
||||||
|
pub untrusted: bool,
|
||||||
|
pub blocks: Vec<BlockCompleteEntry>,
|
||||||
|
pub start_height: u64,
|
||||||
|
pub current_height: u64,
|
||||||
|
pub output_indices: Vec<BlockOutputIndices>,
|
||||||
|
pub daemon_time: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "epee")]
|
||||||
|
epee_object! {
|
||||||
|
GetBlocksResponsePoolInfoNone,
|
||||||
|
status: Status,
|
||||||
|
untrusted: bool,
|
||||||
|
blocks: Vec<BlockCompleteEntry>,
|
||||||
|
start_height: u64,
|
||||||
|
current_height: u64,
|
||||||
|
output_indices: Vec<BlockOutputIndices>,
|
||||||
|
daemon_time: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Data within [`GetBlocksResponse::PoolInfoIncremental`].
|
||||||
|
#[allow(dead_code, missing_docs)]
|
||||||
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
|
#[derive(Clone, Default, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
|
pub struct GetBlocksResponsePoolInfoIncremental {
|
||||||
|
pub status: Status,
|
||||||
|
pub untrusted: bool,
|
||||||
|
pub blocks: Vec<BlockCompleteEntry>,
|
||||||
|
pub start_height: u64,
|
||||||
|
pub current_height: u64,
|
||||||
|
pub output_indices: Vec<BlockOutputIndices>,
|
||||||
|
pub daemon_time: u64,
|
||||||
|
pub added_pool_txs: Vec<PoolTxInfo>,
|
||||||
|
pub remaining_added_pool_txids: ByteArrayVec<32>,
|
||||||
|
pub removed_pool_txids: ByteArrayVec<32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "epee")]
|
||||||
|
epee_object! {
|
||||||
|
GetBlocksResponsePoolInfoIncremental,
|
||||||
|
status: Status,
|
||||||
|
untrusted: bool,
|
||||||
|
blocks: Vec<BlockCompleteEntry>,
|
||||||
|
start_height: u64,
|
||||||
|
current_height: u64,
|
||||||
|
output_indices: Vec<BlockOutputIndices>,
|
||||||
|
daemon_time: u64,
|
||||||
|
added_pool_txs: Vec<PoolTxInfo>,
|
||||||
|
remaining_added_pool_txids: ByteArrayVec<32>,
|
||||||
|
removed_pool_txids: ByteArrayVec<32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Data within [`GetBlocksResponse::PoolInfoFull`].
|
||||||
|
#[allow(dead_code, missing_docs)]
|
||||||
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
|
#[derive(Clone, Default, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
|
pub struct GetBlocksResponsePoolInfoFull {
|
||||||
|
pub status: Status,
|
||||||
|
pub untrusted: bool,
|
||||||
|
pub blocks: Vec<BlockCompleteEntry>,
|
||||||
|
pub start_height: u64,
|
||||||
|
pub current_height: u64,
|
||||||
|
pub output_indices: Vec<BlockOutputIndices>,
|
||||||
|
pub daemon_time: u64,
|
||||||
|
pub added_pool_txs: Vec<PoolTxInfo>,
|
||||||
|
pub remaining_added_pool_txids: ByteArrayVec<32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "epee")]
|
||||||
|
epee_object! {
|
||||||
|
GetBlocksResponsePoolInfoFull,
|
||||||
|
status: Status,
|
||||||
|
untrusted: bool,
|
||||||
|
blocks: Vec<BlockCompleteEntry>,
|
||||||
|
start_height: u64,
|
||||||
|
current_height: u64,
|
||||||
|
output_indices: Vec<BlockOutputIndices>,
|
||||||
|
daemon_time: u64,
|
||||||
|
added_pool_txs: Vec<PoolTxInfo>,
|
||||||
|
remaining_added_pool_txids: ByteArrayVec<32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "epee")]
|
||||||
|
/// [`EpeeObjectBuilder`] for [`GetBlocksResponse`].
|
||||||
|
///
|
||||||
|
/// Not for public usage.
|
||||||
|
#[allow(dead_code, missing_docs)]
|
||||||
|
#[derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
|
pub struct __GetBlocksResponseEpeeBuilder {
|
||||||
|
pub status: Option<Status>,
|
||||||
|
pub untrusted: Option<bool>,
|
||||||
|
pub blocks: Option<Vec<BlockCompleteEntry>>,
|
||||||
|
pub start_height: Option<u64>,
|
||||||
|
pub current_height: Option<u64>,
|
||||||
|
pub output_indices: Option<Vec<BlockOutputIndices>>,
|
||||||
|
pub daemon_time: Option<u64>,
|
||||||
|
pub pool_info_extent: Option<PoolInfoExtent>,
|
||||||
|
pub added_pool_txs: Option<Vec<PoolTxInfo>>,
|
||||||
|
pub remaining_added_pool_txids: Option<ByteArrayVec<32>>,
|
||||||
|
pub removed_pool_txids: Option<ByteArrayVec<32>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "epee")]
|
||||||
|
impl EpeeObjectBuilder<GetBlocksResponse> for __GetBlocksResponseEpeeBuilder {
|
||||||
|
fn add_field<B: Buf>(&mut self, name: &str, r: &mut B) -> error::Result<bool> {
|
||||||
|
macro_rules! read_epee_field {
|
||||||
|
($($field:ident),*) => {
|
||||||
|
match name {
|
||||||
|
$(
|
||||||
|
stringify!($field) => { self.$field = Some(read_epee_value(r)?); },
|
||||||
|
)*
|
||||||
|
_ => return Ok(false),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
read_epee_field! {
|
||||||
|
status,
|
||||||
|
untrusted,
|
||||||
|
blocks,
|
||||||
|
start_height,
|
||||||
|
current_height,
|
||||||
|
output_indices,
|
||||||
|
daemon_time,
|
||||||
|
pool_info_extent,
|
||||||
|
added_pool_txs,
|
||||||
|
remaining_added_pool_txids,
|
||||||
|
removed_pool_txids
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn finish(self) -> error::Result<GetBlocksResponse> {
|
||||||
|
const ELSE: error::Error = error::Error::Format("Required field was not found!");
|
||||||
|
|
||||||
|
let status = self.status.ok_or(ELSE)?;
|
||||||
|
let untrusted = self.untrusted.ok_or(ELSE)?;
|
||||||
|
let blocks = self.blocks.ok_or(ELSE)?;
|
||||||
|
let start_height = self.start_height.ok_or(ELSE)?;
|
||||||
|
let current_height = self.current_height.ok_or(ELSE)?;
|
||||||
|
let output_indices = self.output_indices.ok_or(ELSE)?;
|
||||||
|
let daemon_time = self.daemon_time.ok_or(ELSE)?;
|
||||||
|
let pool_info_extent = self.pool_info_extent.ok_or(ELSE)?;
|
||||||
|
|
||||||
|
let this = match pool_info_extent {
|
||||||
|
PoolInfoExtent::None => {
|
||||||
|
GetBlocksResponse::PoolInfoNone(GetBlocksResponsePoolInfoNone {
|
||||||
|
status,
|
||||||
|
untrusted,
|
||||||
|
blocks,
|
||||||
|
start_height,
|
||||||
|
current_height,
|
||||||
|
output_indices,
|
||||||
|
daemon_time,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
PoolInfoExtent::Incremental => {
|
||||||
|
GetBlocksResponse::PoolInfoIncremental(GetBlocksResponsePoolInfoIncremental {
|
||||||
|
status,
|
||||||
|
untrusted,
|
||||||
|
blocks,
|
||||||
|
start_height,
|
||||||
|
current_height,
|
||||||
|
output_indices,
|
||||||
|
daemon_time,
|
||||||
|
added_pool_txs: self.added_pool_txs.ok_or(ELSE)?,
|
||||||
|
remaining_added_pool_txids: self.remaining_added_pool_txids.ok_or(ELSE)?,
|
||||||
|
removed_pool_txids: self.removed_pool_txids.ok_or(ELSE)?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
PoolInfoExtent::Full => {
|
||||||
|
GetBlocksResponse::PoolInfoFull(GetBlocksResponsePoolInfoFull {
|
||||||
|
status,
|
||||||
|
untrusted,
|
||||||
|
blocks,
|
||||||
|
start_height,
|
||||||
|
current_height,
|
||||||
|
output_indices,
|
||||||
|
daemon_time,
|
||||||
|
added_pool_txs: self.added_pool_txs.ok_or(ELSE)?,
|
||||||
|
remaining_added_pool_txids: self.remaining_added_pool_txids.ok_or(ELSE)?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(this)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "epee")]
|
||||||
|
#[allow(clippy::cognitive_complexity)]
|
||||||
|
impl EpeeObject for GetBlocksResponse {
|
||||||
|
type Builder = __GetBlocksResponseEpeeBuilder;
|
||||||
|
|
||||||
|
fn number_of_fields(&self) -> u64 {
|
||||||
|
// [`PoolInfoExtent`] + inner struct fields.
|
||||||
|
let inner_fields = match self {
|
||||||
|
Self::PoolInfoNone(s) => s.number_of_fields(),
|
||||||
|
Self::PoolInfoIncremental(s) => s.number_of_fields(),
|
||||||
|
Self::PoolInfoFull(s) => s.number_of_fields(),
|
||||||
|
};
|
||||||
|
|
||||||
|
1 + inner_fields
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_fields<B: BufMut>(self, w: &mut B) -> error::Result<()> {
|
||||||
|
match self {
|
||||||
|
Self::PoolInfoNone(s) => {
|
||||||
|
s.write_fields(w)?;
|
||||||
|
write_field(PoolInfoExtent::None.to_u8(), "pool_info_extent", w)?;
|
||||||
|
}
|
||||||
|
Self::PoolInfoIncremental(s) => {
|
||||||
|
s.write_fields(w)?;
|
||||||
|
write_field(PoolInfoExtent::Incremental.to_u8(), "pool_info_extent", w)?;
|
||||||
|
}
|
||||||
|
Self::PoolInfoFull(s) => {
|
||||||
|
s.write_fields(w)?;
|
||||||
|
write_field(PoolInfoExtent::Full.to_u8(), "pool_info_extent", w)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//---------------------------------------------------------------------------------------------------- Tests
|
//---------------------------------------------------------------------------------------------------- Tests
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
|
|
|
@ -36,9 +36,6 @@ pub const CORE_RPC_STATUS_NOT_MINING: &str = "NOT MINING";
|
||||||
#[doc = monero_definition_link!(cc73fe71162d564ffda8e549b79a350bca53c454, "/rpc/core_rpc_server_commands_defs.h", 81)]
|
#[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.
|
|
||||||
pub const CORE_RPC_STATUS_UNKNOWN: &str = "UNKNOWN";
|
|
||||||
|
|
||||||
//---------------------------------------------------------------------------------------------------- Versions
|
//---------------------------------------------------------------------------------------------------- Versions
|
||||||
#[doc = monero_definition_link!(cc73fe71162d564ffda8e549b79a350bca53c454, "/rpc/core_rpc_server_commands_defs.h", 90)]
|
#[doc = monero_definition_link!(cc73fe71162d564ffda8e549b79a350bca53c454, "/rpc/core_rpc_server_commands_defs.h", 90)]
|
||||||
/// RPC major version.
|
/// RPC major version.
|
||||||
|
|
|
@ -9,12 +9,12 @@ use crate::{
|
||||||
free::{is_one, is_zero},
|
free::{is_one, is_zero},
|
||||||
macros::define_request_and_response,
|
macros::define_request_and_response,
|
||||||
misc::{
|
misc::{
|
||||||
AuxPow, BlockHeader, ChainInfo, ConnectionInfo, GetBan, HardforkEntry, HistogramEntry,
|
AuxPow, BlockHeader, ChainInfo, ConnectionInfo, Distribution, GetBan, HardforkEntry,
|
||||||
OutputDistributionData, SetBan, Span, Status, SyncInfoPeer, TxBacklogEntry,
|
HistogramEntry, OutputDistributionData, SetBan, Span, Status, SyncInfoPeer, TxBacklogEntry,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
//---------------------------------------------------------------------------------------------------- Struct definitions
|
//---------------------------------------------------------------------------------------------------- Definitions
|
||||||
// This generates 2 structs:
|
// This generates 2 structs:
|
||||||
//
|
//
|
||||||
// - `GetBlockTemplateRequest`
|
// - `GetBlockTemplateRequest`
|
||||||
|
@ -131,7 +131,6 @@ define_request_and_response! {
|
||||||
// type alias to `()` instead of a `struct`.
|
// type alias to `()` instead of a `struct`.
|
||||||
Request {},
|
Request {},
|
||||||
|
|
||||||
#[derive(Copy)]
|
|
||||||
ResponseBase {
|
ResponseBase {
|
||||||
count: u64,
|
count: u64,
|
||||||
}
|
}
|
||||||
|
@ -292,7 +291,7 @@ define_request_and_response! {
|
||||||
AccessResponseBase {
|
AccessResponseBase {
|
||||||
blob: String,
|
blob: String,
|
||||||
block_header: BlockHeader,
|
block_header: BlockHeader,
|
||||||
json: String, // TODO: this should be defined in a struct, it has many fields.
|
json: String, // FIXME: this should be defined in a struct, it has many fields.
|
||||||
miner_tx_hash: String,
|
miner_tx_hash: String,
|
||||||
tx_hashes: Vec<String>,
|
tx_hashes: Vec<String>,
|
||||||
}
|
}
|
||||||
|
@ -409,7 +408,6 @@ define_request_and_response! {
|
||||||
Request {
|
Request {
|
||||||
address: String,
|
address: String,
|
||||||
},
|
},
|
||||||
#[derive(Copy)]
|
|
||||||
Response {
|
Response {
|
||||||
banned: bool,
|
banned: bool,
|
||||||
seconds: u32,
|
seconds: u32,
|
||||||
|
@ -425,7 +423,6 @@ define_request_and_response! {
|
||||||
Request {
|
Request {
|
||||||
txids: Vec<String> = default_vec::<String>(), "default_vec",
|
txids: Vec<String> = default_vec::<String>(), "default_vec",
|
||||||
},
|
},
|
||||||
#[derive(Copy)]
|
|
||||||
#[cfg_attr(feature = "serde", serde(transparent))]
|
#[cfg_attr(feature = "serde", serde(transparent))]
|
||||||
#[repr(transparent)]
|
#[repr(transparent)]
|
||||||
Response {
|
Response {
|
||||||
|
@ -479,9 +476,9 @@ define_request_and_response! {
|
||||||
version: u32,
|
version: u32,
|
||||||
release: bool,
|
release: bool,
|
||||||
#[serde(skip_serializing_if = "is_zero")]
|
#[serde(skip_serializing_if = "is_zero")]
|
||||||
current_height: u64 = default_zero(), "default_zero",
|
current_height: u64 = default_zero::<u64>(), "default_zero",
|
||||||
#[serde(skip_serializing_if = "is_zero")]
|
#[serde(skip_serializing_if = "is_zero")]
|
||||||
target_height: u64 = default_zero(), "default_zero",
|
target_height: u64 = default_zero::<u64>(), "default_zero",
|
||||||
#[serde(skip_serializing_if = "Vec::is_empty")]
|
#[serde(skip_serializing_if = "Vec::is_empty")]
|
||||||
hard_forks: Vec<HardforkEntry> = default_vec(), "default_vec",
|
hard_forks: Vec<HardforkEntry> = default_vec(), "default_vec",
|
||||||
}
|
}
|
||||||
|
@ -520,7 +517,6 @@ define_request_and_response! {
|
||||||
Request {
|
Request {
|
||||||
txids: Vec<String>,
|
txids: Vec<String>,
|
||||||
},
|
},
|
||||||
#[derive(Copy)]
|
|
||||||
#[cfg_attr(feature = "serde", serde(transparent))]
|
#[cfg_attr(feature = "serde", serde(transparent))]
|
||||||
#[repr(transparent)]
|
#[repr(transparent)]
|
||||||
Response {
|
Response {
|
||||||
|
@ -574,10 +570,8 @@ define_request_and_response! {
|
||||||
from_height: u64,
|
from_height: u64,
|
||||||
to_height: u64,
|
to_height: u64,
|
||||||
},
|
},
|
||||||
/// TODO: this request has custom serde:
|
|
||||||
/// <https://github.com/monero-project/monero/blob/cc73fe71162d564ffda8e549b79a350bca53c454/src/rpc/core_rpc_server_commands_defs.h#L2468-L2508>
|
|
||||||
AccessResponseBase {
|
AccessResponseBase {
|
||||||
distributions: Vec<OutputDistributionData>,
|
distributions: Vec<Distribution>,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -607,7 +601,6 @@ define_request_and_response! {
|
||||||
Request {
|
Request {
|
||||||
check: bool = default_false(), "default_false",
|
check: bool = default_false(), "default_false",
|
||||||
},
|
},
|
||||||
#[derive(Copy)]
|
|
||||||
ResponseBase {
|
ResponseBase {
|
||||||
pruned: bool,
|
pruned: bool,
|
||||||
pruning_seed: u32,
|
pruning_seed: u32,
|
||||||
|
|
|
@ -113,6 +113,9 @@ mod defaults;
|
||||||
mod free;
|
mod free;
|
||||||
mod macros;
|
mod macros;
|
||||||
|
|
||||||
|
#[cfg(feature = "serde")]
|
||||||
|
mod serde;
|
||||||
|
|
||||||
pub mod base;
|
pub mod base;
|
||||||
pub mod bin;
|
pub mod bin;
|
||||||
pub mod json;
|
pub mod json;
|
||||||
|
@ -121,6 +124,6 @@ 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_VERSION, CORE_RPC_VERSION_MAJOR,
|
||||||
CORE_RPC_VERSION_MAJOR, CORE_RPC_VERSION_MINOR,
|
CORE_RPC_VERSION_MINOR,
|
||||||
};
|
};
|
||||||
|
|
|
@ -16,11 +16,11 @@
|
||||||
///
|
///
|
||||||
/// # Macro internals
|
/// # Macro internals
|
||||||
/// This macro uses:
|
/// This macro uses:
|
||||||
/// - [`__define_request`]
|
/// - [`define_request`]
|
||||||
/// - [`__define_response`]
|
/// - [`define_response`]
|
||||||
/// - [`__define_request_and_response_doc`]
|
/// - [`define_request_and_response_doc`]
|
||||||
///
|
///
|
||||||
/// # `__define_request`
|
/// # `define_request`
|
||||||
/// This macro has 2 branches. If the caller provides
|
/// This macro has 2 branches. If the caller provides
|
||||||
/// `Request {}`, i.e. no fields, it will generate:
|
/// `Request {}`, i.e. no fields, it will generate:
|
||||||
/// ```
|
/// ```
|
||||||
|
@ -34,7 +34,7 @@
|
||||||
/// 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.
|
||||||
///
|
///
|
||||||
/// # `__define_response`
|
/// # `define_response`
|
||||||
/// This macro has 2 branches. If the caller provides `Response`
|
/// This macro has 2 branches. If the caller provides `Response`
|
||||||
/// it will generate a normal struct with no additional fields.
|
/// it will generate a normal struct with no additional fields.
|
||||||
///
|
///
|
||||||
|
@ -86,8 +86,8 @@ macro_rules! define_request_and_response {
|
||||||
)*
|
)*
|
||||||
}
|
}
|
||||||
) => { paste::paste! {
|
) => { paste::paste! {
|
||||||
$crate::macros::__define_request! {
|
$crate::macros::define_request! {
|
||||||
#[doc = $crate::macros::__define_request_and_response_doc!(
|
#[doc = $crate::macros::define_request_and_response_doc!(
|
||||||
"response" => [<$type_name Response>],
|
"response" => [<$type_name Response>],
|
||||||
$monero_daemon_rpc_doc_link,
|
$monero_daemon_rpc_doc_link,
|
||||||
$monero_code_commit,
|
$monero_code_commit,
|
||||||
|
@ -110,12 +110,12 @@ macro_rules! define_request_and_response {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$crate::macros::__define_response! {
|
$crate::macros::define_response! {
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
#[allow(missing_docs)]
|
#[allow(missing_docs)]
|
||||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||||
#[derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
#[derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
#[doc = $crate::macros::__define_request_and_response_doc!(
|
#[doc = $crate::macros::define_request_and_response_doc!(
|
||||||
"request" => [<$type_name Request>],
|
"request" => [<$type_name Request>],
|
||||||
$monero_daemon_rpc_doc_link,
|
$monero_daemon_rpc_doc_link,
|
||||||
$monero_code_commit,
|
$monero_code_commit,
|
||||||
|
@ -145,9 +145,7 @@ pub(crate) use define_request_and_response;
|
||||||
/// Define a request type.
|
/// Define a request type.
|
||||||
///
|
///
|
||||||
/// This is only used in [`define_request_and_response`], see it for docs.
|
/// This is only used in [`define_request_and_response`], see it for docs.
|
||||||
///
|
macro_rules! define_request {
|
||||||
/// `__` 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.
|
// This branch will generate a type alias to `()` if only given `{}` as input.
|
||||||
(
|
(
|
||||||
|
@ -206,15 +204,13 @@ macro_rules! __define_request {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
pub(crate) use __define_request;
|
pub(crate) use define_request;
|
||||||
|
|
||||||
//---------------------------------------------------------------------------------------------------- define_response
|
//---------------------------------------------------------------------------------------------------- define_response
|
||||||
/// Define a response type.
|
/// Define a response type.
|
||||||
///
|
///
|
||||||
/// This is only used in [`define_request_and_response`], see it for docs.
|
/// This is used in [`define_request_and_response`], see it for docs.
|
||||||
///
|
macro_rules! define_response {
|
||||||
/// `__` is used to notate that this shouldn't be called directly.
|
|
||||||
macro_rules! __define_response {
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
// This version of the macro expects the literal ident
|
// This version of the macro expects the literal ident
|
||||||
// `Response` => $response_type_name.
|
// `Response` => $response_type_name.
|
||||||
|
@ -228,7 +224,7 @@ macro_rules! __define_response {
|
||||||
// The response type.
|
// The response type.
|
||||||
Response => $t:ident {
|
Response => $t:ident {
|
||||||
// And any fields.
|
// And any fields.
|
||||||
// See [`__define_request`] for docs, this does the same thing.
|
// See [`define_request`] for docs, this does the same thing.
|
||||||
$(
|
$(
|
||||||
$( #[$field_attr:meta] )*
|
$( #[$field_attr:meta] )*
|
||||||
$field:ident: $field_type:ty
|
$field:ident: $field_type:ty
|
||||||
|
@ -265,7 +261,7 @@ macro_rules! __define_response {
|
||||||
// The response base type => actual name of the struct
|
// The response base type => actual name of the struct
|
||||||
$base:ty => $t:ident {
|
$base:ty => $t:ident {
|
||||||
// And any fields.
|
// And any fields.
|
||||||
// See [`__define_request`] for docs, this does the same thing.
|
// See [`define_request`] for docs, this does the same thing.
|
||||||
$(
|
$(
|
||||||
$( #[$field_attr:meta] )*
|
$( #[$field_attr:meta] )*
|
||||||
$field:ident: $field_type:ty
|
$field:ident: $field_type:ty
|
||||||
|
@ -298,16 +294,14 @@ macro_rules! __define_response {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
pub(crate) use __define_response;
|
pub(crate) use define_response;
|
||||||
|
|
||||||
//---------------------------------------------------------------------------------------------------- define_request_and_response_doc
|
//---------------------------------------------------------------------------------------------------- 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:
|
||||||
|
@ -351,7 +345,7 @@ macro_rules! __define_request_and_response_doc {
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
pub(crate) use __define_request_and_response_doc;
|
pub(crate) use define_request_and_response_doc;
|
||||||
|
|
||||||
//---------------------------------------------------------------------------------------------------- Macro
|
//---------------------------------------------------------------------------------------------------- Macro
|
||||||
/// Output a string link to `monerod` source code.
|
/// Output a string link to `monerod` source code.
|
||||||
|
|
|
@ -1,37 +0,0 @@
|
||||||
//! 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>,
|
|
||||||
}
|
|
309
rpc/types/src/misc/distribution.rs
Normal file
309
rpc/types/src/misc/distribution.rs
Normal file
|
@ -0,0 +1,309 @@
|
||||||
|
//! Output distributions for [`crate::json::GetOutputDistributionResponse`].
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------------------------------- Use
|
||||||
|
use std::mem::size_of;
|
||||||
|
|
||||||
|
#[cfg(feature = "serde")]
|
||||||
|
use serde::{ser::SerializeStruct, Deserialize, Serialize};
|
||||||
|
|
||||||
|
#[cfg(feature = "epee")]
|
||||||
|
use cuprate_epee_encoding::{
|
||||||
|
epee_object, error,
|
||||||
|
macros::bytes::{Buf, BufMut},
|
||||||
|
read_epee_value, read_varint, write_field, write_varint, EpeeObject, EpeeObjectBuilder,
|
||||||
|
EpeeValue, Marker,
|
||||||
|
};
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------------------------------- Free
|
||||||
|
/// TODO: <https://github.com/Cuprate/cuprate/pull/229#discussion_r1690531904>.
|
||||||
|
///
|
||||||
|
/// Used for [`Distribution::CompressedBinary::distribution`].
|
||||||
|
#[doc = crate::macros::monero_definition_link!(
|
||||||
|
cc73fe71162d564ffda8e549b79a350bca53c454,
|
||||||
|
"rpc/core_rpc_server_commands_defs.h",
|
||||||
|
45..=55
|
||||||
|
)]
|
||||||
|
#[cfg(feature = "epee")]
|
||||||
|
fn compress_integer_array(array: &[u64]) -> error::Result<Vec<u8>> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// TODO: <https://github.com/Cuprate/cuprate/pull/229#discussion_r1690531904>.
|
||||||
|
///
|
||||||
|
/// Used for [`Distribution::CompressedBinary::distribution`].
|
||||||
|
#[doc = crate::macros::monero_definition_link!(
|
||||||
|
cc73fe71162d564ffda8e549b79a350bca53c454,
|
||||||
|
"rpc/core_rpc_server_commands_defs.h",
|
||||||
|
57..=72
|
||||||
|
)]
|
||||||
|
fn decompress_integer_array(array: &[u8]) -> Vec<u64> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------------------------------- Distribution
|
||||||
|
#[doc = crate::macros::monero_definition_link!(
|
||||||
|
cc73fe71162d564ffda8e549b79a350bca53c454,
|
||||||
|
"rpc/core_rpc_server_commands_defs.h",
|
||||||
|
2468..=2508
|
||||||
|
)]
|
||||||
|
/// Used in [`crate::json::GetOutputDistributionResponse`].
|
||||||
|
///
|
||||||
|
/// # Internals
|
||||||
|
/// This type's (de)serialization depends on `monerod`'s (de)serialization.
|
||||||
|
///
|
||||||
|
/// During serialization:
|
||||||
|
/// [`Self::Uncompressed`] will emit:
|
||||||
|
/// - `compress: false`
|
||||||
|
///
|
||||||
|
/// [`Self::CompressedBinary`] will emit:
|
||||||
|
/// - `binary: true`
|
||||||
|
/// - `compress: true`
|
||||||
|
///
|
||||||
|
/// Upon deserialization, the presence of a `compressed_data`
|
||||||
|
/// field signifies that the [`Self::CompressedBinary`] should
|
||||||
|
/// be selected.
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
|
#[cfg_attr(feature = "serde", serde(untagged))]
|
||||||
|
pub enum Distribution {
|
||||||
|
/// Distribution data will be (de)serialized as either JSON or binary (uncompressed).
|
||||||
|
Uncompressed(DistributionUncompressed),
|
||||||
|
/// Distribution data will be (de)serialized as compressed binary.
|
||||||
|
CompressedBinary(DistributionCompressedBinary),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Distribution {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::Uncompressed(DistributionUncompressed::default())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Data within [`Distribution::Uncompressed`].
|
||||||
|
#[allow(dead_code, missing_docs)]
|
||||||
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
|
#[derive(Clone, Default, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
|
pub struct DistributionUncompressed {
|
||||||
|
pub start_height: u64,
|
||||||
|
pub base: u64,
|
||||||
|
/// TODO: this is a binary JSON string if `binary == true`.
|
||||||
|
pub distribution: Vec<u64>,
|
||||||
|
pub amount: u64,
|
||||||
|
pub binary: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "epee")]
|
||||||
|
epee_object! {
|
||||||
|
DistributionUncompressed,
|
||||||
|
start_height: u64,
|
||||||
|
base: u64,
|
||||||
|
distribution: Vec<u64>,
|
||||||
|
amount: u64,
|
||||||
|
binary: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Data within [`Distribution::CompressedBinary`].
|
||||||
|
#[allow(dead_code, missing_docs)]
|
||||||
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
|
#[derive(Clone, Default, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
|
pub struct DistributionCompressedBinary {
|
||||||
|
pub start_height: u64,
|
||||||
|
pub base: u64,
|
||||||
|
#[cfg_attr(
|
||||||
|
feature = "serde",
|
||||||
|
serde(serialize_with = "serialize_distribution_as_compressed_data")
|
||||||
|
)]
|
||||||
|
#[cfg_attr(
|
||||||
|
feature = "serde",
|
||||||
|
serde(deserialize_with = "deserialize_compressed_data_as_distribution")
|
||||||
|
)]
|
||||||
|
#[cfg_attr(feature = "serde", serde(rename = "compressed_data"))]
|
||||||
|
pub distribution: Vec<u64>,
|
||||||
|
pub amount: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "epee")]
|
||||||
|
epee_object! {
|
||||||
|
DistributionCompressedBinary,
|
||||||
|
start_height: u64,
|
||||||
|
base: u64,
|
||||||
|
distribution: Vec<u64>,
|
||||||
|
amount: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Serializer function for [`DistributionCompressedBinary::distribution`].
|
||||||
|
///
|
||||||
|
/// 1. Compresses the distribution array
|
||||||
|
/// 2. Serializes the compressed data
|
||||||
|
#[cfg(feature = "serde")]
|
||||||
|
#[allow(clippy::ptr_arg)]
|
||||||
|
fn serialize_distribution_as_compressed_data<S>(v: &Vec<u64>, s: S) -> Result<S::Ok, S::Error>
|
||||||
|
where
|
||||||
|
S: serde::Serializer,
|
||||||
|
{
|
||||||
|
match compress_integer_array(v) {
|
||||||
|
Ok(compressed_data) => compressed_data.serialize(s),
|
||||||
|
Err(_) => Err(serde::ser::Error::custom(
|
||||||
|
"error compressing distribution array",
|
||||||
|
)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Deserializer function for [`DistributionCompressedBinary::distribution`].
|
||||||
|
///
|
||||||
|
/// 1. Deserializes as `compressed_data` field.
|
||||||
|
/// 2. Decompresses and returns the data
|
||||||
|
#[cfg(feature = "serde")]
|
||||||
|
fn deserialize_compressed_data_as_distribution<'de, D>(d: D) -> Result<Vec<u64>, D::Error>
|
||||||
|
where
|
||||||
|
D: serde::Deserializer<'de>,
|
||||||
|
{
|
||||||
|
Vec::<u8>::deserialize(d).map(|v| decompress_integer_array(&v))
|
||||||
|
}
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------------------------------- Epee
|
||||||
|
#[cfg(feature = "epee")]
|
||||||
|
/// [`EpeeObjectBuilder`] for [`Distribution`].
|
||||||
|
///
|
||||||
|
/// Not for public usage.
|
||||||
|
#[allow(dead_code, missing_docs)]
|
||||||
|
#[derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
|
pub struct __DistributionEpeeBuilder {
|
||||||
|
pub start_height: Option<u64>,
|
||||||
|
pub base: Option<u64>,
|
||||||
|
pub distribution: Option<Vec<u64>>,
|
||||||
|
pub amount: Option<u64>,
|
||||||
|
pub compressed_data: Option<Vec<u8>>,
|
||||||
|
pub binary: Option<bool>,
|
||||||
|
pub compress: Option<bool>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "epee")]
|
||||||
|
impl EpeeObjectBuilder<Distribution> for __DistributionEpeeBuilder {
|
||||||
|
fn add_field<B: Buf>(&mut self, name: &str, r: &mut B) -> error::Result<bool> {
|
||||||
|
macro_rules! read_epee_field {
|
||||||
|
($($field:ident),*) => {
|
||||||
|
match name {
|
||||||
|
$(
|
||||||
|
stringify!($field) => { self.$field = Some(read_epee_value(r)?); },
|
||||||
|
)*
|
||||||
|
_ => return Ok(false),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
read_epee_field! {
|
||||||
|
start_height,
|
||||||
|
base,
|
||||||
|
amount,
|
||||||
|
binary,
|
||||||
|
compress,
|
||||||
|
compressed_data,
|
||||||
|
distribution
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn finish(self) -> error::Result<Distribution> {
|
||||||
|
const ELSE: error::Error = error::Error::Format("Required field was not found!");
|
||||||
|
|
||||||
|
let start_height = self.start_height.ok_or(ELSE)?;
|
||||||
|
let base = self.base.ok_or(ELSE)?;
|
||||||
|
let amount = self.amount.ok_or(ELSE)?;
|
||||||
|
|
||||||
|
let distribution = if let Some(compressed_data) = self.compressed_data {
|
||||||
|
let distribution = decompress_integer_array(&compressed_data);
|
||||||
|
Distribution::CompressedBinary(DistributionCompressedBinary {
|
||||||
|
start_height,
|
||||||
|
base,
|
||||||
|
distribution,
|
||||||
|
amount,
|
||||||
|
})
|
||||||
|
} else if let Some(distribution) = self.distribution {
|
||||||
|
Distribution::Uncompressed(DistributionUncompressed {
|
||||||
|
binary: self.binary.ok_or(ELSE)?,
|
||||||
|
distribution,
|
||||||
|
start_height,
|
||||||
|
base,
|
||||||
|
amount,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
return Err(ELSE);
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(distribution)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "epee")]
|
||||||
|
impl EpeeObject for Distribution {
|
||||||
|
type Builder = __DistributionEpeeBuilder;
|
||||||
|
|
||||||
|
fn number_of_fields(&self) -> u64 {
|
||||||
|
match self {
|
||||||
|
// Inner struct fields + `compress`.
|
||||||
|
Self::Uncompressed(s) => s.number_of_fields() + 1,
|
||||||
|
// Inner struct fields + `compress` + `binary`.
|
||||||
|
Self::CompressedBinary(s) => s.number_of_fields() + 2,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_fields<B: BufMut>(self, w: &mut B) -> error::Result<()> {
|
||||||
|
match self {
|
||||||
|
Self::Uncompressed(s) => {
|
||||||
|
s.write_fields(w)?;
|
||||||
|
write_field(false, "compress", w)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Self::CompressedBinary(DistributionCompressedBinary {
|
||||||
|
start_height,
|
||||||
|
base,
|
||||||
|
distribution,
|
||||||
|
amount,
|
||||||
|
}) => {
|
||||||
|
let compressed_data = compress_integer_array(&distribution)?;
|
||||||
|
|
||||||
|
start_height.write(w)?;
|
||||||
|
base.write(w)?;
|
||||||
|
compressed_data.write(w)?;
|
||||||
|
amount.write(w)?;
|
||||||
|
|
||||||
|
write_field(true, "binary", w)?;
|
||||||
|
write_field(true, "compress", w)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------------------------------- Tests
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use pretty_assertions::assert_eq;
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
// TODO: re-enable tests after (de)compression functions are implemented.
|
||||||
|
|
||||||
|
// /// Tests that [`compress_integer_array`] outputs as expected.
|
||||||
|
// #[test]
|
||||||
|
// fn compress() {
|
||||||
|
// let varints = &[16_384, 16_383, 16_382, 16_381];
|
||||||
|
// let bytes = compress_integer_array(varints).unwrap();
|
||||||
|
|
||||||
|
// let expected = [2, 0, 1, 0, 253, 255, 249, 255, 245, 255];
|
||||||
|
// assert_eq!(expected, *bytes);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// /// Tests that [`decompress_integer_array`] outputs as expected.
|
||||||
|
// #[test]
|
||||||
|
// fn decompress() {
|
||||||
|
// let bytes = &[2, 0, 1, 0, 253, 255, 249, 255, 245, 255];
|
||||||
|
// let varints = decompress_integer_array(bytes);
|
||||||
|
|
||||||
|
// let expected = vec![16_384, 16_383, 16_382, 16_381];
|
||||||
|
// assert_eq!(expected, varints);
|
||||||
|
// }
|
||||||
|
}
|
|
@ -6,6 +6,7 @@ use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
#[cfg(feature = "epee")]
|
#[cfg(feature = "epee")]
|
||||||
use cuprate_epee_encoding::{
|
use cuprate_epee_encoding::{
|
||||||
|
error,
|
||||||
macros::bytes::{Buf, BufMut},
|
macros::bytes::{Buf, BufMut},
|
||||||
EpeeValue, Marker,
|
EpeeValue, Marker,
|
||||||
};
|
};
|
||||||
|
@ -17,7 +18,7 @@ use cuprate_epee_encoding::{
|
||||||
456..=460
|
456..=460
|
||||||
)]
|
)]
|
||||||
/// Used in [`crate::other::IsKeyImageSpentResponse`].
|
/// Used in [`crate::other::IsKeyImageSpentResponse`].
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
#[repr(u8)]
|
#[repr(u8)]
|
||||||
pub enum KeyImageSpentStatus {
|
pub enum KeyImageSpentStatus {
|
||||||
|
@ -26,23 +27,59 @@ pub enum KeyImageSpentStatus {
|
||||||
SpentInPool = 2,
|
SpentInPool = 2,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "epee")]
|
impl KeyImageSpentStatus {
|
||||||
impl EpeeValue for KeyImageSpentStatus {
|
/// Convert [`Self`] to a [`u8`].
|
||||||
const MARKER: Marker = <String as EpeeValue>::MARKER;
|
///
|
||||||
|
/// ```rust
|
||||||
fn read<B: Buf>(r: &mut B, marker: &Marker) -> cuprate_epee_encoding::Result<Self> {
|
/// use cuprate_rpc_types::misc::KeyImageSpentStatus as K;
|
||||||
todo!()
|
///
|
||||||
|
/// assert_eq!(K::Unspent.to_u8(), 0);
|
||||||
|
/// assert_eq!(K::SpentInBlockchain.to_u8(), 1);
|
||||||
|
/// assert_eq!(K::SpentInPool.to_u8(), 2);
|
||||||
|
/// ```
|
||||||
|
pub const fn to_u8(self) -> u8 {
|
||||||
|
match self {
|
||||||
|
Self::Unspent => 0,
|
||||||
|
Self::SpentInBlockchain => 1,
|
||||||
|
Self::SpentInPool => 2,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn should_write(&self) -> bool {
|
/// Convert a [`u8`] to a [`Self`].
|
||||||
todo!()
|
///
|
||||||
}
|
/// # Errors
|
||||||
|
/// This returns [`None`] if `u > 2`.
|
||||||
fn epee_default_value() -> Option<Self> {
|
///
|
||||||
todo!()
|
/// ```rust
|
||||||
}
|
/// use cuprate_rpc_types::misc::KeyImageSpentStatus as K;
|
||||||
|
///
|
||||||
fn write<B: BufMut>(self, w: &mut B) -> cuprate_epee_encoding::Result<()> {
|
/// assert_eq!(K::from_u8(0), Some(K::Unspent));
|
||||||
todo!()
|
/// assert_eq!(K::from_u8(1), Some(K::SpentInBlockchain));
|
||||||
|
/// assert_eq!(K::from_u8(2), Some(K::SpentInPool));
|
||||||
|
/// assert_eq!(K::from_u8(3), None);
|
||||||
|
/// ```
|
||||||
|
pub const fn from_u8(u: u8) -> Option<Self> {
|
||||||
|
Some(match u {
|
||||||
|
0 => Self::Unspent,
|
||||||
|
1 => Self::SpentInBlockchain,
|
||||||
|
2 => Self::SpentInPool,
|
||||||
|
_ => return None,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "epee")]
|
||||||
|
impl EpeeValue for KeyImageSpentStatus {
|
||||||
|
const MARKER: Marker = u8::MARKER;
|
||||||
|
|
||||||
|
fn read<B: Buf>(r: &mut B, marker: &Marker) -> error::Result<Self> {
|
||||||
|
let u = u8::read(r, marker)?;
|
||||||
|
Self::from_u8(u).ok_or(error::Error::Format("u8 was greater than 2"))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write<B: BufMut>(self, w: &mut B) -> error::Result<()> {
|
||||||
|
let u = self.to_u8();
|
||||||
|
u8::write(u, w)?;
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,7 +20,7 @@ use cuprate_epee_encoding::{
|
||||||
use crate::{
|
use crate::{
|
||||||
constants::{
|
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_STATUS_PAYMENT_REQUIRED,
|
||||||
},
|
},
|
||||||
defaults::default_zero,
|
defaults::default_zero,
|
||||||
macros::monero_definition_link,
|
macros::monero_definition_link,
|
||||||
|
@ -352,19 +352,6 @@ define_struct_and_impl_epee! {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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! {
|
define_struct_and_impl_epee! {
|
||||||
#[doc = monero_definition_link!(
|
#[doc = monero_definition_link!(
|
||||||
cc73fe71162d564ffda8e549b79a350bca53c454,
|
cc73fe71162d564ffda8e549b79a350bca53c454,
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
|
|
||||||
//---------------------------------------------------------------------------------------------------- Mod
|
//---------------------------------------------------------------------------------------------------- Mod
|
||||||
mod binary_string;
|
mod binary_string;
|
||||||
mod block_complete_entry;
|
mod distribution;
|
||||||
mod key_image_spent_status;
|
mod key_image_spent_status;
|
||||||
mod misc;
|
mod misc;
|
||||||
mod pool_info_extent;
|
mod pool_info_extent;
|
||||||
|
@ -21,13 +21,13 @@ mod status;
|
||||||
mod tx_entry;
|
mod tx_entry;
|
||||||
|
|
||||||
pub use binary_string::BinaryString;
|
pub use binary_string::BinaryString;
|
||||||
pub use block_complete_entry::BlockCompleteEntry;
|
pub use distribution::{Distribution, DistributionCompressedBinary, DistributionUncompressed};
|
||||||
pub use key_image_spent_status::KeyImageSpentStatus;
|
pub use key_image_spent_status::KeyImageSpentStatus;
|
||||||
pub use misc::{
|
pub use misc::{
|
||||||
AuxPow, BlockHeader, BlockOutputIndices, ChainInfo, ConnectionInfo, GetBan,
|
AuxPow, BlockHeader, BlockOutputIndices, ChainInfo, ConnectionInfo, GetBan,
|
||||||
GetMinerDataTxBacklogEntry, GetOutputsOut, HardforkEntry, HistogramEntry, OutKey, OutKeyBin,
|
GetMinerDataTxBacklogEntry, GetOutputsOut, HardforkEntry, HistogramEntry, OutKey, OutKeyBin,
|
||||||
OutputDistributionData, Peer, PoolTxInfo, PublicNode, SetBan, Span, SpentKeyImageInfo,
|
OutputDistributionData, Peer, PoolTxInfo, PublicNode, SetBan, Span, SpentKeyImageInfo,
|
||||||
SyncInfoPeer, TxBacklogEntry, TxBlobEntry, TxInfo, TxOutputIndices, TxpoolHisto, TxpoolStats,
|
SyncInfoPeer, TxBacklogEntry, TxInfo, TxOutputIndices, TxpoolHisto, TxpoolStats,
|
||||||
};
|
};
|
||||||
pub use pool_info_extent::PoolInfoExtent;
|
pub use pool_info_extent::PoolInfoExtent;
|
||||||
pub use status::Status;
|
pub use status::Status;
|
||||||
|
|
|
@ -6,6 +6,7 @@ use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
#[cfg(feature = "epee")]
|
#[cfg(feature = "epee")]
|
||||||
use cuprate_epee_encoding::{
|
use cuprate_epee_encoding::{
|
||||||
|
error,
|
||||||
macros::bytes::{Buf, BufMut},
|
macros::bytes::{Buf, BufMut},
|
||||||
EpeeValue, Marker,
|
EpeeValue, Marker,
|
||||||
};
|
};
|
||||||
|
@ -17,33 +18,69 @@ use cuprate_epee_encoding::{
|
||||||
223..=228
|
223..=228
|
||||||
)]
|
)]
|
||||||
/// Used in [`crate::bin::GetBlocksResponse`].
|
/// Used in [`crate::bin::GetBlocksResponse`].
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
#[derive(Copy, Clone, Default, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
#[repr(u8)]
|
#[repr(u8)]
|
||||||
pub enum PoolInfoExtent {
|
pub enum PoolInfoExtent {
|
||||||
|
#[default]
|
||||||
None = 0,
|
None = 0,
|
||||||
Incremental = 1,
|
Incremental = 1,
|
||||||
Full = 2,
|
Full = 2,
|
||||||
}
|
}
|
||||||
|
|
||||||
// <https://github.com/monero-project/monero/blob/cc73fe71162d564ffda8e549b79a350bca53c454/src/cryptonote_protocol/cryptonote_protocol_defs.h#L138-L163>
|
impl PoolInfoExtent {
|
||||||
#[cfg(feature = "epee")]
|
/// Convert [`Self`] to a [`u8`].
|
||||||
impl EpeeValue for PoolInfoExtent {
|
///
|
||||||
const MARKER: Marker = <String as EpeeValue>::MARKER;
|
/// ```rust
|
||||||
|
/// use cuprate_rpc_types::misc::PoolInfoExtent as P;
|
||||||
fn read<B: Buf>(r: &mut B, marker: &Marker) -> cuprate_epee_encoding::Result<Self> {
|
///
|
||||||
todo!()
|
/// assert_eq!(P::None.to_u8(), 0);
|
||||||
|
/// assert_eq!(P::Incremental.to_u8(), 1);
|
||||||
|
/// assert_eq!(P::Full.to_u8(), 2);
|
||||||
|
/// ```
|
||||||
|
pub const fn to_u8(self) -> u8 {
|
||||||
|
match self {
|
||||||
|
Self::None => 0,
|
||||||
|
Self::Incremental => 1,
|
||||||
|
Self::Full => 2,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn should_write(&self) -> bool {
|
/// Convert a [`u8`] to a [`Self`].
|
||||||
todo!()
|
///
|
||||||
}
|
/// # Errors
|
||||||
|
/// This returns [`None`] if `u > 2`.
|
||||||
fn epee_default_value() -> Option<Self> {
|
///
|
||||||
todo!()
|
/// ```rust
|
||||||
}
|
/// use cuprate_rpc_types::misc::PoolInfoExtent as P;
|
||||||
|
///
|
||||||
fn write<B: BufMut>(self, w: &mut B) -> cuprate_epee_encoding::Result<()> {
|
/// assert_eq!(P::from_u8(0), Some(P::None));
|
||||||
todo!()
|
/// assert_eq!(P::from_u8(1), Some(P::Incremental));
|
||||||
|
/// assert_eq!(P::from_u8(2), Some(P::Full));
|
||||||
|
/// assert_eq!(P::from_u8(3), None);
|
||||||
|
/// ```
|
||||||
|
pub const fn from_u8(u: u8) -> Option<Self> {
|
||||||
|
Some(match u {
|
||||||
|
0 => Self::None,
|
||||||
|
1 => Self::Incremental,
|
||||||
|
2 => Self::Full,
|
||||||
|
_ => return None,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "epee")]
|
||||||
|
impl EpeeValue for PoolInfoExtent {
|
||||||
|
const MARKER: Marker = <u8 as EpeeValue>::MARKER;
|
||||||
|
|
||||||
|
fn read<B: Buf>(r: &mut B, marker: &Marker) -> error::Result<Self> {
|
||||||
|
let u = u8::read(r, marker)?;
|
||||||
|
Self::from_u8(u).ok_or(error::Error::Format("u8 was greater than 2"))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write<B: BufMut>(self, w: &mut B) -> error::Result<()> {
|
||||||
|
let u = self.to_u8();
|
||||||
|
u8::write(u, w)?;
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,7 @@ use cuprate_epee_encoding::{
|
||||||
|
|
||||||
use crate::constants::{
|
use crate::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_STATUS_PAYMENT_REQUIRED,
|
||||||
};
|
};
|
||||||
|
|
||||||
//---------------------------------------------------------------------------------------------------- Status
|
//---------------------------------------------------------------------------------------------------- Status
|
||||||
|
@ -33,37 +33,37 @@ use crate::constants::{
|
||||||
/// use cuprate_rpc_types::{
|
/// use cuprate_rpc_types::{
|
||||||
/// misc::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
|
||||||
/// };
|
/// };
|
||||||
/// use serde_json::to_string;
|
/// use serde_json::to_string;
|
||||||
///
|
///
|
||||||
/// let unknown = Status::Unknown;
|
/// let other = Status::Other("OTHER".into());
|
||||||
///
|
///
|
||||||
/// assert_eq!(to_string(&Status::Ok).unwrap(), r#""OK""#);
|
/// assert_eq!(to_string(&Status::Ok).unwrap(), r#""OK""#);
|
||||||
/// assert_eq!(to_string(&Status::Busy).unwrap(), r#""BUSY""#);
|
/// assert_eq!(to_string(&Status::Busy).unwrap(), r#""BUSY""#);
|
||||||
/// assert_eq!(to_string(&Status::NotMining).unwrap(), r#""NOT MINING""#);
|
/// assert_eq!(to_string(&Status::NotMining).unwrap(), r#""NOT MINING""#);
|
||||||
/// assert_eq!(to_string(&Status::PaymentRequired).unwrap(), r#""PAYMENT REQUIRED""#);
|
/// assert_eq!(to_string(&Status::PaymentRequired).unwrap(), r#""PAYMENT REQUIRED""#);
|
||||||
/// assert_eq!(to_string(&unknown).unwrap(), r#""UNKNOWN""#);
|
/// assert_eq!(to_string(&other).unwrap(), r#""OTHER""#);
|
||||||
///
|
///
|
||||||
/// assert_eq!(Status::Ok.as_ref(), CORE_RPC_STATUS_OK);
|
/// assert_eq!(Status::Ok.as_ref(), CORE_RPC_STATUS_OK);
|
||||||
/// assert_eq!(Status::Busy.as_ref(), CORE_RPC_STATUS_BUSY);
|
/// assert_eq!(Status::Busy.as_ref(), CORE_RPC_STATUS_BUSY);
|
||||||
/// assert_eq!(Status::NotMining.as_ref(), CORE_RPC_STATUS_NOT_MINING);
|
/// assert_eq!(Status::NotMining.as_ref(), CORE_RPC_STATUS_NOT_MINING);
|
||||||
/// assert_eq!(Status::PaymentRequired.as_ref(), CORE_RPC_STATUS_PAYMENT_REQUIRED);
|
/// assert_eq!(Status::PaymentRequired.as_ref(), CORE_RPC_STATUS_PAYMENT_REQUIRED);
|
||||||
/// assert_eq!(unknown.as_ref(), CORE_RPC_STATUS_UNKNOWN);
|
/// assert_eq!(other.as_ref(), "OTHER");
|
||||||
///
|
///
|
||||||
/// assert_eq!(format!("{}", Status::Ok), CORE_RPC_STATUS_OK);
|
/// assert_eq!(format!("{}", Status::Ok), CORE_RPC_STATUS_OK);
|
||||||
/// assert_eq!(format!("{}", Status::Busy), CORE_RPC_STATUS_BUSY);
|
/// assert_eq!(format!("{}", Status::Busy), CORE_RPC_STATUS_BUSY);
|
||||||
/// assert_eq!(format!("{}", Status::NotMining), CORE_RPC_STATUS_NOT_MINING);
|
/// assert_eq!(format!("{}", Status::NotMining), CORE_RPC_STATUS_NOT_MINING);
|
||||||
/// assert_eq!(format!("{}", Status::PaymentRequired), CORE_RPC_STATUS_PAYMENT_REQUIRED);
|
/// assert_eq!(format!("{}", Status::PaymentRequired), CORE_RPC_STATUS_PAYMENT_REQUIRED);
|
||||||
/// assert_eq!(format!("{}", unknown), CORE_RPC_STATUS_UNKNOWN);
|
/// assert_eq!(format!("{}", other), "OTHER");
|
||||||
///
|
///
|
||||||
/// assert_eq!(format!("{:?}", Status::Ok), "Ok");
|
/// assert_eq!(format!("{:?}", Status::Ok), "Ok");
|
||||||
/// assert_eq!(format!("{:?}", Status::Busy), "Busy");
|
/// assert_eq!(format!("{:?}", Status::Busy), "Busy");
|
||||||
/// assert_eq!(format!("{:?}", Status::NotMining), "NotMining");
|
/// assert_eq!(format!("{:?}", Status::NotMining), "NotMining");
|
||||||
/// assert_eq!(format!("{:?}", Status::PaymentRequired), "PaymentRequired");
|
/// assert_eq!(format!("{:?}", Status::PaymentRequired), "PaymentRequired");
|
||||||
/// assert_eq!(format!("{:?}", unknown), "Unknown");
|
/// assert_eq!(format!("{:?}", other), r#"Other("OTHER")"#);
|
||||||
/// ```
|
/// ```
|
||||||
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
#[derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
pub enum Status {
|
pub enum Status {
|
||||||
// FIXME:
|
// FIXME:
|
||||||
|
@ -86,17 +86,12 @@ pub enum Status {
|
||||||
#[cfg_attr(feature = "serde", 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.
|
||||||
///
|
///
|
||||||
/// This exists to act as a catch-all if `monerod` adds
|
/// This exists to act as a catch-all for all of
|
||||||
/// a string and a Cuprate node hasn't updated yet.
|
/// `monerod`'s other strings it puts in the `status` field.
|
||||||
///
|
#[cfg_attr(feature = "serde", serde(rename = "OTHER"), serde(untagged))]
|
||||||
/// The reason this isn't `Unknown(String)` is because that
|
Other(String),
|
||||||
/// disallows [`Status`] to be [`Copy`], and thus other types
|
|
||||||
/// that contain it.
|
|
||||||
#[cfg_attr(feature = "serde", serde(other))]
|
|
||||||
#[cfg_attr(feature = "serde", serde(rename = "UNKNOWN"))]
|
|
||||||
Unknown,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<String> for Status {
|
impl From<String> for Status {
|
||||||
|
@ -106,7 +101,7 @@ impl From<String> for Status {
|
||||||
CORE_RPC_STATUS_BUSY => Self::Busy,
|
CORE_RPC_STATUS_BUSY => Self::Busy,
|
||||||
CORE_RPC_STATUS_NOT_MINING => Self::NotMining,
|
CORE_RPC_STATUS_NOT_MINING => Self::NotMining,
|
||||||
CORE_RPC_STATUS_PAYMENT_REQUIRED => Self::PaymentRequired,
|
CORE_RPC_STATUS_PAYMENT_REQUIRED => Self::PaymentRequired,
|
||||||
_ => Self::Unknown,
|
_ => Self::Other(s),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -118,7 +113,7 @@ impl AsRef<str> for Status {
|
||||||
Self::Busy => CORE_RPC_STATUS_BUSY,
|
Self::Busy => CORE_RPC_STATUS_BUSY,
|
||||||
Self::NotMining => CORE_RPC_STATUS_NOT_MINING,
|
Self::NotMining => CORE_RPC_STATUS_NOT_MINING,
|
||||||
Self::PaymentRequired => CORE_RPC_STATUS_PAYMENT_REQUIRED,
|
Self::PaymentRequired => CORE_RPC_STATUS_PAYMENT_REQUIRED,
|
||||||
Self::Unknown => CORE_RPC_STATUS_UNKNOWN,
|
Self::Other(s) => s,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -150,7 +145,7 @@ impl EpeeValue for Status {
|
||||||
|
|
||||||
fn epee_default_value() -> Option<Self> {
|
fn epee_default_value() -> Option<Self> {
|
||||||
// <https://github.com/Cuprate/cuprate/pull/147#discussion_r1654992559>
|
// <https://github.com/Cuprate/cuprate/pull/147#discussion_r1654992559>
|
||||||
Some(Self::Unknown)
|
Some(Self::Other(String::new()))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write<B: BufMut>(self, w: &mut B) -> cuprate_epee_encoding::Result<()> {
|
fn write<B: BufMut>(self, w: &mut B) -> cuprate_epee_encoding::Result<()> {
|
||||||
|
@ -172,11 +167,11 @@ mod test {
|
||||||
Status::Busy,
|
Status::Busy,
|
||||||
Status::NotMining,
|
Status::NotMining,
|
||||||
Status::PaymentRequired,
|
Status::PaymentRequired,
|
||||||
Status::Unknown,
|
Status::Other(String::new()),
|
||||||
] {
|
] {
|
||||||
let mut buf = vec![];
|
let mut buf = vec![];
|
||||||
|
|
||||||
<Status as EpeeValue>::write(status, &mut buf).unwrap();
|
<Status as EpeeValue>::write(status.clone(), &mut buf).unwrap();
|
||||||
let status2 =
|
let status2 =
|
||||||
<Status as EpeeValue>::read(&mut buf.as_slice(), &<Status as EpeeValue>::MARKER)
|
<Status as EpeeValue>::read(&mut buf.as_slice(), &<Status as EpeeValue>::MARKER)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
|
@ -2,13 +2,15 @@
|
||||||
|
|
||||||
//---------------------------------------------------------------------------------------------------- Use
|
//---------------------------------------------------------------------------------------------------- Use
|
||||||
#[cfg(feature = "serde")]
|
#[cfg(feature = "serde")]
|
||||||
|
use crate::serde::{serde_false, serde_true};
|
||||||
|
#[cfg(feature = "serde")]
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
#[cfg(feature = "epee")]
|
#[cfg(feature = "epee")]
|
||||||
use cuprate_epee_encoding::{
|
use cuprate_epee_encoding::{
|
||||||
epee_object,
|
epee_object, error,
|
||||||
macros::bytes::{Buf, BufMut},
|
macros::bytes::{Buf, BufMut},
|
||||||
EpeeValue, Marker,
|
read_epee_value, write_field, EpeeObject, EpeeObjectBuilder, EpeeValue, Marker,
|
||||||
};
|
};
|
||||||
|
|
||||||
//---------------------------------------------------------------------------------------------------- TxEntry
|
//---------------------------------------------------------------------------------------------------- TxEntry
|
||||||
|
@ -18,42 +20,127 @@ use cuprate_epee_encoding::{
|
||||||
389..=428
|
389..=428
|
||||||
)]
|
)]
|
||||||
/// Used in [`crate::other::GetTransactionsResponse`].
|
/// Used in [`crate::other::GetTransactionsResponse`].
|
||||||
#[derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
///
|
||||||
|
/// # Epee
|
||||||
|
/// This type is only used in a JSON endpoint, so the
|
||||||
|
/// epee implementation on this type only panics.
|
||||||
|
///
|
||||||
|
/// It is only implemented to satisfy the RPC type generator
|
||||||
|
/// macro, which requires all objects to be serde + epee.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
/// ```rust
|
||||||
|
/// use cuprate_rpc_types::misc::*;
|
||||||
|
/// use serde_json::{json, from_value};
|
||||||
|
///
|
||||||
|
/// let json = json!({
|
||||||
|
/// "as_hex": String::default(),
|
||||||
|
/// "as_json": String::default(),
|
||||||
|
/// "block_height": u64::default(),
|
||||||
|
/// "block_timestamp": u64::default(),
|
||||||
|
/// "confirmations": u64::default(),
|
||||||
|
/// "double_spend_seen": bool::default(),
|
||||||
|
/// "output_indices": Vec::<u64>::default(),
|
||||||
|
/// "prunable_as_hex": String::default(),
|
||||||
|
/// "prunable_hash": String::default(),
|
||||||
|
/// "pruned_as_hex": String::default(),
|
||||||
|
/// "tx_hash": String::default(),
|
||||||
|
/// "in_pool": bool::default(),
|
||||||
|
/// });
|
||||||
|
/// let tx_entry = from_value::<TxEntry>(json).unwrap();
|
||||||
|
/// assert!(matches!(tx_entry, TxEntry::InPool { .. }));
|
||||||
|
///
|
||||||
|
/// let json = json!({
|
||||||
|
/// "as_hex": String::default(),
|
||||||
|
/// "as_json": String::default(),
|
||||||
|
/// "double_spend_seen": bool::default(),
|
||||||
|
/// "prunable_as_hex": String::default(),
|
||||||
|
/// "prunable_hash": String::default(),
|
||||||
|
/// "pruned_as_hex": String::default(),
|
||||||
|
/// "received_timestamp": u64::default(),
|
||||||
|
/// "relayed": bool::default(),
|
||||||
|
/// "tx_hash": String::default(),
|
||||||
|
/// "in_pool": bool::default(),
|
||||||
|
/// });
|
||||||
|
/// let tx_entry = from_value::<TxEntry>(json).unwrap();
|
||||||
|
/// assert!(matches!(tx_entry, TxEntry::NotInPool { .. }));
|
||||||
|
/// ```
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
pub struct TxEntry {
|
#[cfg_attr(feature = "serde", serde(untagged))]
|
||||||
pub as_hex: String,
|
pub enum TxEntry {
|
||||||
pub as_json: String,
|
/// This entry exists in the transaction pool.
|
||||||
pub block_height: u64,
|
InPool {
|
||||||
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_hex: String,
|
||||||
as_json: String, // TODO: should be its own struct
|
as_json: String,
|
||||||
block_height: u64,
|
block_height: u64,
|
||||||
block_timestamp: u64,
|
block_timestamp: u64,
|
||||||
confirmations: u64,
|
confirmations: u64,
|
||||||
double_spend_seen: bool,
|
double_spend_seen: bool,
|
||||||
in_pool: bool,
|
|
||||||
output_indices: Vec<u64>,
|
output_indices: Vec<u64>,
|
||||||
prunable_as_hex: String,
|
prunable_as_hex: String,
|
||||||
prunable_hash: String,
|
prunable_hash: String,
|
||||||
pruned_as_hex: String,
|
pruned_as_hex: String,
|
||||||
|
tx_hash: String,
|
||||||
|
#[cfg_attr(feature = "serde", serde(serialize_with = "serde_true"))]
|
||||||
|
/// Will always be serialized as `true`.
|
||||||
|
in_pool: bool,
|
||||||
|
},
|
||||||
|
/// This entry _does not_ exist in the transaction pool.
|
||||||
|
NotInPool {
|
||||||
|
as_hex: String,
|
||||||
|
as_json: String,
|
||||||
|
double_spend_seen: bool,
|
||||||
|
prunable_as_hex: String,
|
||||||
|
prunable_hash: String,
|
||||||
|
pruned_as_hex: String,
|
||||||
received_timestamp: u64,
|
received_timestamp: u64,
|
||||||
relayed: bool,
|
relayed: bool,
|
||||||
tx_hash: String,
|
tx_hash: String,
|
||||||
|
#[cfg_attr(feature = "serde", serde(serialize_with = "serde_false"))]
|
||||||
|
/// Will always be serialized as `false`.
|
||||||
|
in_pool: bool,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for TxEntry {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::NotInPool {
|
||||||
|
as_hex: String::default(),
|
||||||
|
as_json: String::default(),
|
||||||
|
double_spend_seen: bool::default(),
|
||||||
|
prunable_as_hex: String::default(),
|
||||||
|
prunable_hash: String::default(),
|
||||||
|
pruned_as_hex: String::default(),
|
||||||
|
received_timestamp: u64::default(),
|
||||||
|
relayed: bool::default(),
|
||||||
|
tx_hash: String::default(),
|
||||||
|
in_pool: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------------------------------- Epee
|
||||||
|
#[cfg(feature = "epee")]
|
||||||
|
impl EpeeObjectBuilder<TxEntry> for () {
|
||||||
|
fn add_field<B: Buf>(&mut self, name: &str, r: &mut B) -> error::Result<bool> {
|
||||||
|
unreachable!()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn finish(self) -> error::Result<TxEntry> {
|
||||||
|
unreachable!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "epee")]
|
||||||
|
impl EpeeObject for TxEntry {
|
||||||
|
type Builder = ();
|
||||||
|
|
||||||
|
fn number_of_fields(&self) -> u64 {
|
||||||
|
unreachable!()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_fields<B: BufMut>(self, w: &mut B) -> error::Result<()> {
|
||||||
|
unreachable!()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,12 +8,12 @@ use crate::{
|
||||||
defaults::{default_false, default_string, default_true},
|
defaults::{default_false, default_string, default_true},
|
||||||
macros::define_request_and_response,
|
macros::define_request_and_response,
|
||||||
misc::{
|
misc::{
|
||||||
GetOutputsOut, OutKey, Peer, PublicNode, SpentKeyImageInfo, Status, TxEntry, TxInfo,
|
GetOutputsOut, KeyImageSpentStatus, OutKey, Peer, PublicNode, SpentKeyImageInfo, Status,
|
||||||
TxpoolStats,
|
TxEntry, TxInfo, TxpoolStats,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
//---------------------------------------------------------------------------------------------------- TODO
|
//---------------------------------------------------------------------------------------------------- Definitions
|
||||||
define_request_and_response! {
|
define_request_and_response! {
|
||||||
get_height,
|
get_height,
|
||||||
cc73fe71162d564ffda8e549b79a350bca53c454 =>
|
cc73fe71162d564ffda8e549b79a350bca53c454 =>
|
||||||
|
@ -68,7 +68,8 @@ define_request_and_response! {
|
||||||
key_images: Vec<String>,
|
key_images: Vec<String>,
|
||||||
},
|
},
|
||||||
AccessResponseBase {
|
AccessResponseBase {
|
||||||
spent_status: Vec<u8>, // TODO: should be `KeyImageSpentStatus`.
|
/// FIXME: These are [`KeyImageSpentStatus`] in [`u8`] form.
|
||||||
|
spent_status: Vec<u8>,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -219,7 +220,6 @@ define_request_and_response! {
|
||||||
password: String,
|
password: String,
|
||||||
proxy: String,
|
proxy: String,
|
||||||
},
|
},
|
||||||
#[derive(Copy)]
|
|
||||||
Response {
|
Response {
|
||||||
status: Status,
|
status: Status,
|
||||||
}
|
}
|
||||||
|
|
32
rpc/types/src/serde.rs
Normal file
32
rpc/types/src/serde.rs
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
//! Custom (de)serialization functions for serde.
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------------------------------- Lints
|
||||||
|
#![allow(clippy::trivially_copy_pass_by_ref)] // serde fn signature
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------------------------------- Import
|
||||||
|
use serde::Serializer;
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------------------------------- Free functions
|
||||||
|
/// Always serializes `true`.
|
||||||
|
#[inline]
|
||||||
|
pub(crate) fn serde_true<S>(_: &bool, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
|
where
|
||||||
|
S: Serializer,
|
||||||
|
{
|
||||||
|
serializer.serialize_bool(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Always serializes `false`.
|
||||||
|
#[inline]
|
||||||
|
pub(crate) fn serde_false<S>(_: &bool, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
|
where
|
||||||
|
S: Serializer,
|
||||||
|
{
|
||||||
|
serializer.serialize_bool(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------------------------------------- Tests
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use super::*;
|
||||||
|
}
|
Loading…
Reference in a new issue