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 = [
|
||||
"cuprate-epee-encoding",
|
||||
"cuprate-fixed-bytes",
|
||||
"cuprate-types",
|
||||
"monero-serai",
|
||||
"paste",
|
||||
"pretty_assertions",
|
||||
"serde",
|
||||
"serde_json",
|
||||
]
|
||||
|
|
|
@ -16,6 +16,7 @@ epee = ["dep:cuprate-epee-encoding"]
|
|||
[dependencies]
|
||||
cuprate-epee-encoding = { path = "../../net/epee-encoding", optional = true }
|
||||
cuprate-fixed-bytes = { path = "../../net/fixed-bytes" }
|
||||
cuprate-types = { path = "../../types" }
|
||||
|
||||
monero-serai = { workspace = true }
|
||||
paste = { workspace = true }
|
||||
|
@ -23,3 +24,4 @@ serde = { workspace = true, optional = true }
|
|||
|
||||
[dev-dependencies]
|
||||
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
|
||||
List of feature flags for `cuprate-rpc-types`.
|
||||
|
||||
|
|
|
@ -46,7 +46,7 @@ epee_object! {
|
|||
//---------------------------------------------------------------------------------------------------- Responses
|
||||
#[doc = monero_definition_link!(cc73fe71162d564ffda8e549b79a350bca53c454, "rpc/core_rpc_server_commands_defs.h", 101..=112)]
|
||||
/// 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))]
|
||||
pub struct ResponseBase {
|
||||
/// General RPC error code. [`Status::Ok`] means everything looks good.
|
||||
|
|
|
@ -5,51 +5,32 @@
|
|||
//---------------------------------------------------------------------------------------------------- Import
|
||||
use cuprate_fixed_bytes::ByteArrayVec;
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[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::{
|
||||
base::{AccessResponseBase, ResponseBase},
|
||||
defaults::{default_false, default_height, default_string, default_vec, default_zero},
|
||||
free::{is_one, is_zero},
|
||||
macros::define_request_and_response,
|
||||
macros::{define_request, define_request_and_response, define_request_and_response_doc},
|
||||
misc::{
|
||||
AuxPow, BlockCompleteEntry, BlockHeader, BlockOutputIndices, ChainInfo, ConnectionInfo,
|
||||
GetBan, GetOutputsOut, HardforkEntry, HistogramEntry, OutKeyBin, OutputDistributionData,
|
||||
Peer, PoolTxInfo, SetBan, Span, Status, TxBacklogEntry,
|
||||
AuxPow, BlockHeader, BlockOutputIndices, ChainInfo, ConnectionInfo, GetBan, GetOutputsOut,
|
||||
HardforkEntry, HistogramEntry, OutKeyBin, OutputDistributionData, Peer, PoolInfoExtent,
|
||||
PoolTxInfo, SetBan, Span, Status, TxBacklogEntry,
|
||||
},
|
||||
};
|
||||
|
||||
//---------------------------------------------------------------------------------------------------- TODO
|
||||
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]>,
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------------------------------- Definitions
|
||||
define_request_and_response! {
|
||||
get_blocks_by_heightbin,
|
||||
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
|
||||
#[cfg(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)]
|
||||
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
|
||||
#[doc = monero_definition_link!(cc73fe71162d564ffda8e549b79a350bca53c454, "/rpc/core_rpc_server_commands_defs.h", 90)]
|
||||
/// RPC major version.
|
||||
|
|
|
@ -9,12 +9,12 @@ use crate::{
|
|||
free::{is_one, is_zero},
|
||||
macros::define_request_and_response,
|
||||
misc::{
|
||||
AuxPow, BlockHeader, ChainInfo, ConnectionInfo, GetBan, HardforkEntry, HistogramEntry,
|
||||
OutputDistributionData, SetBan, Span, Status, SyncInfoPeer, TxBacklogEntry,
|
||||
AuxPow, BlockHeader, ChainInfo, ConnectionInfo, Distribution, GetBan, HardforkEntry,
|
||||
HistogramEntry, OutputDistributionData, SetBan, Span, Status, SyncInfoPeer, TxBacklogEntry,
|
||||
},
|
||||
};
|
||||
|
||||
//---------------------------------------------------------------------------------------------------- Struct definitions
|
||||
//---------------------------------------------------------------------------------------------------- Definitions
|
||||
// This generates 2 structs:
|
||||
//
|
||||
// - `GetBlockTemplateRequest`
|
||||
|
@ -131,7 +131,6 @@ define_request_and_response! {
|
|||
// type alias to `()` instead of a `struct`.
|
||||
Request {},
|
||||
|
||||
#[derive(Copy)]
|
||||
ResponseBase {
|
||||
count: u64,
|
||||
}
|
||||
|
@ -292,7 +291,7 @@ define_request_and_response! {
|
|||
AccessResponseBase {
|
||||
blob: String,
|
||||
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,
|
||||
tx_hashes: Vec<String>,
|
||||
}
|
||||
|
@ -409,7 +408,6 @@ define_request_and_response! {
|
|||
Request {
|
||||
address: String,
|
||||
},
|
||||
#[derive(Copy)]
|
||||
Response {
|
||||
banned: bool,
|
||||
seconds: u32,
|
||||
|
@ -425,7 +423,6 @@ define_request_and_response! {
|
|||
Request {
|
||||
txids: Vec<String> = default_vec::<String>(), "default_vec",
|
||||
},
|
||||
#[derive(Copy)]
|
||||
#[cfg_attr(feature = "serde", serde(transparent))]
|
||||
#[repr(transparent)]
|
||||
Response {
|
||||
|
@ -479,9 +476,9 @@ define_request_and_response! {
|
|||
version: u32,
|
||||
release: bool,
|
||||
#[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")]
|
||||
target_height: u64 = default_zero(), "default_zero",
|
||||
target_height: u64 = default_zero::<u64>(), "default_zero",
|
||||
#[serde(skip_serializing_if = "Vec::is_empty")]
|
||||
hard_forks: Vec<HardforkEntry> = default_vec(), "default_vec",
|
||||
}
|
||||
|
@ -520,7 +517,6 @@ define_request_and_response! {
|
|||
Request {
|
||||
txids: Vec<String>,
|
||||
},
|
||||
#[derive(Copy)]
|
||||
#[cfg_attr(feature = "serde", serde(transparent))]
|
||||
#[repr(transparent)]
|
||||
Response {
|
||||
|
@ -574,10 +570,8 @@ define_request_and_response! {
|
|||
from_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 {
|
||||
distributions: Vec<OutputDistributionData>,
|
||||
distributions: Vec<Distribution>,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -607,7 +601,6 @@ define_request_and_response! {
|
|||
Request {
|
||||
check: bool = default_false(), "default_false",
|
||||
},
|
||||
#[derive(Copy)]
|
||||
ResponseBase {
|
||||
pruned: bool,
|
||||
pruning_seed: u32,
|
||||
|
|
|
@ -113,6 +113,9 @@ mod defaults;
|
|||
mod free;
|
||||
mod macros;
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
mod serde;
|
||||
|
||||
pub mod base;
|
||||
pub mod bin;
|
||||
pub mod json;
|
||||
|
@ -121,6 +124,6 @@ pub mod other;
|
|||
|
||||
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,
|
||||
CORE_RPC_STATUS_PAYMENT_REQUIRED, CORE_RPC_VERSION, CORE_RPC_VERSION_MAJOR,
|
||||
CORE_RPC_VERSION_MINOR,
|
||||
};
|
||||
|
|
|
@ -16,11 +16,11 @@
|
|||
///
|
||||
/// # Macro internals
|
||||
/// This macro uses:
|
||||
/// - [`__define_request`]
|
||||
/// - [`__define_response`]
|
||||
/// - [`__define_request_and_response_doc`]
|
||||
/// - [`define_request`]
|
||||
/// - [`define_response`]
|
||||
/// - [`define_request_and_response_doc`]
|
||||
///
|
||||
/// # `__define_request`
|
||||
/// # `define_request`
|
||||
/// This macro has 2 branches. If the caller provides
|
||||
/// `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.
|
||||
/// Really, they semantically are empty types, so `()` is used.
|
||||
///
|
||||
/// # `__define_response`
|
||||
/// # `define_response`
|
||||
/// This macro has 2 branches. If the caller provides `Response`
|
||||
/// it will generate a normal struct with no additional fields.
|
||||
///
|
||||
|
@ -86,8 +86,8 @@ macro_rules! define_request_and_response {
|
|||
)*
|
||||
}
|
||||
) => { paste::paste! {
|
||||
$crate::macros::__define_request! {
|
||||
#[doc = $crate::macros::__define_request_and_response_doc!(
|
||||
$crate::macros::define_request! {
|
||||
#[doc = $crate::macros::define_request_and_response_doc!(
|
||||
"response" => [<$type_name Response>],
|
||||
$monero_daemon_rpc_doc_link,
|
||||
$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(missing_docs)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
#[doc = $crate::macros::__define_request_and_response_doc!(
|
||||
#[doc = $crate::macros::define_request_and_response_doc!(
|
||||
"request" => [<$type_name Request>],
|
||||
$monero_daemon_rpc_doc_link,
|
||||
$monero_code_commit,
|
||||
|
@ -145,9 +145,7 @@ pub(crate) use define_request_and_response;
|
|||
/// Define a request type.
|
||||
///
|
||||
/// This is only used in [`define_request_and_response`], see it for docs.
|
||||
///
|
||||
/// `__` is used to notate that this shouldn't be called directly.
|
||||
macro_rules! __define_request {
|
||||
macro_rules! define_request {
|
||||
//------------------------------------------------------------------------------
|
||||
// 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 a response type.
|
||||
///
|
||||
/// This is only used in [`define_request_and_response`], see it for docs.
|
||||
///
|
||||
/// `__` is used to notate that this shouldn't be called directly.
|
||||
macro_rules! __define_response {
|
||||
/// This is used in [`define_request_and_response`], see it for docs.
|
||||
macro_rules! define_response {
|
||||
//------------------------------------------------------------------------------
|
||||
// This version of the macro expects the literal ident
|
||||
// `Response` => $response_type_name.
|
||||
|
@ -228,7 +224,7 @@ macro_rules! __define_response {
|
|||
// The response type.
|
||||
Response => $t:ident {
|
||||
// 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:ident: $field_type:ty
|
||||
|
@ -265,7 +261,7 @@ macro_rules! __define_response {
|
|||
// The response base type => actual name of the struct
|
||||
$base:ty => $t:ident {
|
||||
// And any fields.
|
||||
// See [`__define_request`] for docs, this does the same thing.
|
||||
// See [`define_request`] for docs, this does the same thing.
|
||||
$(
|
||||
$( #[$field_attr:meta] )*
|
||||
$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
|
||||
/// 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.
|
||||
///
|
||||
/// `__` is used to notate that this shouldn't be called directly.
|
||||
macro_rules! __define_request_and_response_doc {
|
||||
macro_rules! define_request_and_response_doc {
|
||||
(
|
||||
// This labels the last `[request]` or `[response]`
|
||||
// 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
|
||||
/// 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")]
|
||||
use cuprate_epee_encoding::{
|
||||
error,
|
||||
macros::bytes::{Buf, BufMut},
|
||||
EpeeValue, Marker,
|
||||
};
|
||||
|
@ -17,7 +18,7 @@ use cuprate_epee_encoding::{
|
|||
456..=460
|
||||
)]
|
||||
/// 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))]
|
||||
#[repr(u8)]
|
||||
pub enum KeyImageSpentStatus {
|
||||
|
@ -26,23 +27,59 @@ pub enum KeyImageSpentStatus {
|
|||
SpentInPool = 2,
|
||||
}
|
||||
|
||||
#[cfg(feature = "epee")]
|
||||
impl EpeeValue for KeyImageSpentStatus {
|
||||
const MARKER: Marker = <String as EpeeValue>::MARKER;
|
||||
|
||||
fn read<B: Buf>(r: &mut B, marker: &Marker) -> cuprate_epee_encoding::Result<Self> {
|
||||
todo!()
|
||||
impl KeyImageSpentStatus {
|
||||
/// Convert [`Self`] to a [`u8`].
|
||||
///
|
||||
/// ```rust
|
||||
/// use cuprate_rpc_types::misc::KeyImageSpentStatus as K;
|
||||
///
|
||||
/// 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 {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn epee_default_value() -> Option<Self> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn write<B: BufMut>(self, w: &mut B) -> cuprate_epee_encoding::Result<()> {
|
||||
todo!()
|
||||
/// Convert a [`u8`] to a [`Self`].
|
||||
///
|
||||
/// # Errors
|
||||
/// This returns [`None`] if `u > 2`.
|
||||
///
|
||||
/// ```rust
|
||||
/// use cuprate_rpc_types::misc::KeyImageSpentStatus as K;
|
||||
///
|
||||
/// assert_eq!(K::from_u8(0), Some(K::Unspent));
|
||||
/// 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::{
|
||||
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_STATUS_PAYMENT_REQUIRED,
|
||||
},
|
||||
defaults::default_zero,
|
||||
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! {
|
||||
#[doc = monero_definition_link!(
|
||||
cc73fe71162d564ffda8e549b79a350bca53c454,
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
|
||||
//---------------------------------------------------------------------------------------------------- Mod
|
||||
mod binary_string;
|
||||
mod block_complete_entry;
|
||||
mod distribution;
|
||||
mod key_image_spent_status;
|
||||
mod misc;
|
||||
mod pool_info_extent;
|
||||
|
@ -21,13 +21,13 @@ mod status;
|
|||
mod tx_entry;
|
||||
|
||||
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 misc::{
|
||||
AuxPow, BlockHeader, BlockOutputIndices, ChainInfo, ConnectionInfo, GetBan,
|
||||
GetMinerDataTxBacklogEntry, GetOutputsOut, HardforkEntry, HistogramEntry, OutKey, OutKeyBin,
|
||||
OutputDistributionData, Peer, PoolTxInfo, PublicNode, SetBan, Span, SpentKeyImageInfo,
|
||||
SyncInfoPeer, TxBacklogEntry, TxBlobEntry, TxInfo, TxOutputIndices, TxpoolHisto, TxpoolStats,
|
||||
SyncInfoPeer, TxBacklogEntry, TxInfo, TxOutputIndices, TxpoolHisto, TxpoolStats,
|
||||
};
|
||||
pub use pool_info_extent::PoolInfoExtent;
|
||||
pub use status::Status;
|
||||
|
|
|
@ -6,6 +6,7 @@ use serde::{Deserialize, Serialize};
|
|||
|
||||
#[cfg(feature = "epee")]
|
||||
use cuprate_epee_encoding::{
|
||||
error,
|
||||
macros::bytes::{Buf, BufMut},
|
||||
EpeeValue, Marker,
|
||||
};
|
||||
|
@ -17,33 +18,69 @@ use cuprate_epee_encoding::{
|
|||
223..=228
|
||||
)]
|
||||
/// 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))]
|
||||
#[repr(u8)]
|
||||
pub enum PoolInfoExtent {
|
||||
#[default]
|
||||
None = 0,
|
||||
Incremental = 1,
|
||||
Full = 2,
|
||||
}
|
||||
|
||||
// <https://github.com/monero-project/monero/blob/cc73fe71162d564ffda8e549b79a350bca53c454/src/cryptonote_protocol/cryptonote_protocol_defs.h#L138-L163>
|
||||
#[cfg(feature = "epee")]
|
||||
impl EpeeValue for PoolInfoExtent {
|
||||
const MARKER: Marker = <String as EpeeValue>::MARKER;
|
||||
|
||||
fn read<B: Buf>(r: &mut B, marker: &Marker) -> cuprate_epee_encoding::Result<Self> {
|
||||
todo!()
|
||||
impl PoolInfoExtent {
|
||||
/// Convert [`Self`] to a [`u8`].
|
||||
///
|
||||
/// ```rust
|
||||
/// use cuprate_rpc_types::misc::PoolInfoExtent as P;
|
||||
///
|
||||
/// 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 {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn epee_default_value() -> Option<Self> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn write<B: BufMut>(self, w: &mut B) -> cuprate_epee_encoding::Result<()> {
|
||||
todo!()
|
||||
/// Convert a [`u8`] to a [`Self`].
|
||||
///
|
||||
/// # Errors
|
||||
/// This returns [`None`] if `u > 2`.
|
||||
///
|
||||
/// ```rust
|
||||
/// use cuprate_rpc_types::misc::PoolInfoExtent as P;
|
||||
///
|
||||
/// assert_eq!(P::from_u8(0), Some(P::None));
|
||||
/// 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::{
|
||||
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
|
||||
|
@ -33,37 +33,37 @@ use crate::constants::{
|
|||
/// use cuprate_rpc_types::{
|
||||
/// misc::Status,
|
||||
/// 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;
|
||||
///
|
||||
/// let unknown = Status::Unknown;
|
||||
/// let other = Status::Other("OTHER".into());
|
||||
///
|
||||
/// 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!(to_string(&other).unwrap(), r#""OTHER""#);
|
||||
///
|
||||
/// 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!(other.as_ref(), "OTHER");
|
||||
///
|
||||
/// 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!("{}", other), "OTHER");
|
||||
///
|
||||
/// 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");
|
||||
/// 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))]
|
||||
pub enum Status {
|
||||
// FIXME:
|
||||
|
@ -86,17 +86,12 @@ pub enum Status {
|
|||
#[cfg_attr(feature = "serde", serde(rename = "PAYMENT REQUIRED"))]
|
||||
PaymentRequired,
|
||||
|
||||
/// Some unknown other string; [`CORE_RPC_STATUS_UNKNOWN`].
|
||||
/// Some unknown other string.
|
||||
///
|
||||
/// 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.
|
||||
#[cfg_attr(feature = "serde", serde(other))]
|
||||
#[cfg_attr(feature = "serde", serde(rename = "UNKNOWN"))]
|
||||
Unknown,
|
||||
/// This exists to act as a catch-all for all of
|
||||
/// `monerod`'s other strings it puts in the `status` field.
|
||||
#[cfg_attr(feature = "serde", serde(rename = "OTHER"), serde(untagged))]
|
||||
Other(String),
|
||||
}
|
||||
|
||||
impl From<String> for Status {
|
||||
|
@ -106,7 +101,7 @@ impl From<String> for Status {
|
|||
CORE_RPC_STATUS_BUSY => Self::Busy,
|
||||
CORE_RPC_STATUS_NOT_MINING => Self::NotMining,
|
||||
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::NotMining => CORE_RPC_STATUS_NOT_MINING,
|
||||
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> {
|
||||
// <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<()> {
|
||||
|
@ -172,11 +167,11 @@ mod test {
|
|||
Status::Busy,
|
||||
Status::NotMining,
|
||||
Status::PaymentRequired,
|
||||
Status::Unknown,
|
||||
Status::Other(String::new()),
|
||||
] {
|
||||
let mut buf = vec![];
|
||||
|
||||
<Status as EpeeValue>::write(status, &mut buf).unwrap();
|
||||
<Status as EpeeValue>::write(status.clone(), &mut buf).unwrap();
|
||||
let status2 =
|
||||
<Status as EpeeValue>::read(&mut buf.as_slice(), &<Status as EpeeValue>::MARKER)
|
||||
.unwrap();
|
||||
|
|
|
@ -2,13 +2,15 @@
|
|||
|
||||
//---------------------------------------------------------------------------------------------------- Use
|
||||
#[cfg(feature = "serde")]
|
||||
use crate::serde::{serde_false, serde_true};
|
||||
#[cfg(feature = "serde")]
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[cfg(feature = "epee")]
|
||||
use cuprate_epee_encoding::{
|
||||
epee_object,
|
||||
epee_object, error,
|
||||
macros::bytes::{Buf, BufMut},
|
||||
EpeeValue, Marker,
|
||||
read_epee_value, write_field, EpeeObject, EpeeObjectBuilder, EpeeValue, Marker,
|
||||
};
|
||||
|
||||
//---------------------------------------------------------------------------------------------------- TxEntry
|
||||
|
@ -18,42 +20,127 @@ use cuprate_epee_encoding::{
|
|||
389..=428
|
||||
)]
|
||||
/// 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))]
|
||||
pub struct TxEntry {
|
||||
pub as_hex: String,
|
||||
pub as_json: String,
|
||||
pub block_height: u64,
|
||||
pub block_timestamp: u64,
|
||||
pub confirmations: u64,
|
||||
pub double_spend_seen: bool,
|
||||
pub in_pool: bool,
|
||||
pub output_indices: Vec<u64>,
|
||||
pub prunable_as_hex: String,
|
||||
pub prunable_hash: String,
|
||||
pub pruned_as_hex: String,
|
||||
pub received_timestamp: u64,
|
||||
pub relayed: bool,
|
||||
pub tx_hash: String,
|
||||
}
|
||||
|
||||
// TODO: custom epee
|
||||
// <https://github.com/monero-project/monero/blob/cc73fe71162d564ffda8e549b79a350bca53c454/src/rpc/core_rpc_server_commands_defs.h#L406-L427>
|
||||
#[cfg(feature = "epee")]
|
||||
epee_object! {
|
||||
TxEntry,
|
||||
#[cfg_attr(feature = "serde", serde(untagged))]
|
||||
pub enum TxEntry {
|
||||
/// This entry exists in the transaction pool.
|
||||
InPool {
|
||||
as_hex: String,
|
||||
as_json: String, // TODO: should be its own struct
|
||||
as_json: String,
|
||||
block_height: u64,
|
||||
block_timestamp: u64,
|
||||
confirmations: u64,
|
||||
double_spend_seen: bool,
|
||||
in_pool: bool,
|
||||
output_indices: Vec<u64>,
|
||||
prunable_as_hex: String,
|
||||
prunable_hash: String,
|
||||
pruned_as_hex: String,
|
||||
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,
|
||||
relayed: bool,
|
||||
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},
|
||||
macros::define_request_and_response,
|
||||
misc::{
|
||||
GetOutputsOut, OutKey, Peer, PublicNode, SpentKeyImageInfo, Status, TxEntry, TxInfo,
|
||||
TxpoolStats,
|
||||
GetOutputsOut, KeyImageSpentStatus, OutKey, Peer, PublicNode, SpentKeyImageInfo, Status,
|
||||
TxEntry, TxInfo, TxpoolStats,
|
||||
},
|
||||
};
|
||||
|
||||
//---------------------------------------------------------------------------------------------------- TODO
|
||||
//---------------------------------------------------------------------------------------------------- Definitions
|
||||
define_request_and_response! {
|
||||
get_height,
|
||||
cc73fe71162d564ffda8e549b79a350bca53c454 =>
|
||||
|
@ -68,7 +68,8 @@ define_request_and_response! {
|
|||
key_images: Vec<String>,
|
||||
},
|
||||
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,
|
||||
proxy: String,
|
||||
},
|
||||
#[derive(Copy)]
|
||||
Response {
|
||||
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