diff --git a/.github/workflows/fuzz.yml b/.github/workflows/fuzz.yml index 6b68ebfdb..c80c26fca 100644 --- a/.github/workflows/fuzz.yml +++ b/.github/workflows/fuzz.yml @@ -12,7 +12,7 @@ jobs: env: # The version of `cargo-fuzz` to install and use. - CARGO_FUZZ_VERSION: 0.12.0 + CARGO_FUZZ_VERSION: 0.13.1 # The number of seconds to run the fuzz target. FUZZ_TIME: 60 diff --git a/Cargo.lock b/Cargo.lock index 1c394e108..bcd0d6edd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1449,6 +1449,7 @@ dependencies = [ "getrandom 0.2.17", "hex", "hex-literal 1.1.0", + "monero-oxide", "paste", "pretty_assertions", "serde", diff --git a/binaries/cuprated/src/rpc/handlers/bin.rs b/binaries/cuprated/src/rpc/handlers/bin.rs index 92cb79221..753b356b4 100644 --- a/binaries/cuprated/src/rpc/handlers/bin.rs +++ b/binaries/cuprated/src/rpc/handlers/bin.rs @@ -197,12 +197,12 @@ async fn get_hashes( let hashes: Vec<[u8; 32]> = (&block_ids).into(); - let (m_blocks_ids, _, current_height) = + let (m_block_ids, _, current_height) = blockchain::next_chain_entry(&mut state.blockchain_read, hashes, start_height).await?; Ok(GetHashesResponse { base: helper::access_response_base(false), - m_blocks_ids: m_blocks_ids.into(), + m_block_ids: m_block_ids.into(), current_height: usize_to_u64(current_height), start_height, }) diff --git a/binaries/cuprated/src/rpc/handlers/json_rpc.rs b/binaries/cuprated/src/rpc/handlers/json_rpc.rs index aa83e3015..e12c785d2 100644 --- a/binaries/cuprated/src/rpc/handlers/json_rpc.rs +++ b/binaries/cuprated/src/rpc/handlers/json_rpc.rs @@ -32,8 +32,8 @@ use cuprate_rpc_types::{ base::{AccessResponseBase, ResponseBase}, json::{ AddAuxPowRequest, AddAuxPowResponse, BannedRequest, BannedResponse, CalcPowRequest, - CalcPowResponse, FlushCacheRequest, FlushCacheResponse, FlushTransactionPoolRequest, - FlushTransactionPoolResponse, GenerateBlocksRequest, GenerateBlocksResponse, + CalcPowResponse, FlushCacheRequest, FlushCacheResponse, FlushTxpoolRequest, + FlushTxpoolResponse, GenerateBlocksRequest, GenerateBlocksResponse, GetAlternateChainsRequest, GetAlternateChainsResponse, GetBansRequest, GetBansResponse, GetBlockCountRequest, GetBlockCountResponse, GetBlockHeaderByHashRequest, GetBlockHeaderByHashResponse, GetBlockHeaderByHeightRequest, @@ -44,8 +44,8 @@ use cuprate_rpc_types::{ GetInfoResponse, GetLastBlockHeaderRequest, GetLastBlockHeaderResponse, GetMinerDataRequest, GetMinerDataResponse, GetOutputDistributionRequest, GetOutputDistributionResponse, GetOutputHistogramRequest, GetOutputHistogramResponse, - GetTransactionPoolBacklogRequest, GetTransactionPoolBacklogResponse, GetTxIdsLooseRequest, - GetTxIdsLooseResponse, GetVersionRequest, GetVersionResponse, HardForkInfoRequest, + GetTxIdsLooseRequest, GetTxIdsLooseResponse, GetTxpoolBacklogRequest, + GetTxpoolBacklogResponse, GetVersionRequest, GetVersionResponse, HardForkInfoRequest, HardForkInfoResponse, JsonRpcRequest, JsonRpcResponse, OnGetBlockHashRequest, OnGetBlockHashResponse, PruneBlockchainRequest, PruneBlockchainResponse, RelayTxRequest, RelayTxResponse, SetBansRequest, SetBansResponse, SubmitBlockRequest, SubmitBlockResponse, @@ -104,7 +104,7 @@ pub async fn map_request( Req::SetBans(r) => Resp::SetBans(not_available()?), Req::GetBans(r) => Resp::GetBans(not_available()?), Req::Banned(r) => Resp::Banned(not_available()?), - Req::FlushTransactionPool(r) => Resp::FlushTransactionPool(not_available()?), + Req::FlushTxpool(r) => Resp::FlushTxpool(not_available()?), Req::GetOutputHistogram(r) => Resp::GetOutputHistogram(not_available()?), Req::GetCoinbaseTxSum(r) => Resp::GetCoinbaseTxSum(not_available()?), Req::GetVersion(r) => Resp::GetVersion(not_available()?), @@ -112,14 +112,24 @@ pub async fn map_request( Req::GetAlternateChains(r) => Resp::GetAlternateChains(not_available()?), Req::RelayTx(r) => Resp::RelayTx(not_available()?), Req::SyncInfo(r) => Resp::SyncInfo(not_available()?), - Req::GetTransactionPoolBacklog(r) => Resp::GetTransactionPoolBacklog(not_available()?), + Req::GetTxpoolBacklog(r) => Resp::GetTxpoolBacklog(not_available()?), Req::GetMinerData(r) => Resp::GetMinerData(not_available()?), Req::PruneBlockchain(r) => Resp::PruneBlockchain(not_available()?), Req::CalcPow(r) => Resp::CalcPow(not_available()?), Req::AddAuxPow(r) => Resp::AddAuxPow(not_available()?), + Req::GetOutputDistribution(r) => { + Resp::GetOutputDistribution(get_output_distribution(state, r).await?) + } // Unsupported RPC calls. - Req::GetTxIdsLoose(_) | Req::FlushCache(_) => return Err(anyhow!(UNSUPPORTED_RPC_CALL)), + Req::GetTxIdsLoose(_) + | Req::FlushCache(_) + | Req::RpcAccessInfo(_) + | Req::RpcAccessSubmitNonce(_) + | Req::RpcAccessPay(_) + | Req::RpcAccessTracking(_) + | Req::RpcAccessData(_) + | Req::RpcAccessAccount(_) => return Err(anyhow!(UNSUPPORTED_RPC_CALL)), }) } @@ -729,8 +739,8 @@ async fn banned( /// async fn flush_transaction_pool( mut state: CupratedRpcHandler, - request: FlushTransactionPoolRequest, -) -> Result { + request: FlushTxpoolRequest, +) -> Result { let tx_hashes = request .txids .into_iter() @@ -739,7 +749,7 @@ async fn flush_transaction_pool( txpool::flush(todo!(), tx_hashes).await?; - Ok(FlushTransactionPoolResponse { status: Status::Ok }) + Ok(FlushTxpoolResponse { status: Status::Ok }) } /// @@ -922,8 +932,8 @@ async fn sync_info( /// async fn get_transaction_pool_backlog( mut state: CupratedRpcHandler, - _: GetTransactionPoolBacklogRequest, -) -> Result { + _: GetTxpoolBacklogRequest, +) -> Result { let now = current_unix_timestamp(); let backlog = txpool::backlog(&mut state.txpool_read) @@ -936,7 +946,7 @@ async fn get_transaction_pool_backlog( }) .collect(); - Ok(GetTransactionPoolBacklogResponse { + Ok(GetTxpoolBacklogResponse { base: helper::response_base(false), backlog, }) diff --git a/binaries/cuprated/src/rpc/handlers/other_json.rs b/binaries/cuprated/src/rpc/handlers/other_json.rs index 4793a02bf..7be7fe4ed 100644 --- a/binaries/cuprated/src/rpc/handlers/other_json.rs +++ b/binaries/cuprated/src/rpc/handlers/other_json.rs @@ -95,6 +95,7 @@ pub async fn map_request( Req::PopBlocks(r) => Resp::PopBlocks(not_available()?), Req::GetTransactionPoolHashes(r) => Resp::GetTransactionPoolHashes(not_available()?), Req::GetPublicNodes(r) => Resp::GetPublicNodes(not_available()?), + Req::GetInfo(r) => Resp::GetInfo(not_available()?), // Unsupported requests. Req::SetBootstrapDaemon(_) diff --git a/rpc/interface/src/rpc_handler_dummy.rs b/rpc/interface/src/rpc_handler_dummy.rs index e7bf41a69..84299825c 100644 --- a/rpc/interface/src/rpc_handler_dummy.rs +++ b/rpc/interface/src/rpc_handler_dummy.rs @@ -75,7 +75,7 @@ impl Service for RpcHandlerDummy { Req::SetBans(_) => Resp::SetBans(Default::default()), Req::GetBans(_) => Resp::GetBans(Default::default()), Req::Banned(_) => Resp::Banned(Default::default()), - Req::FlushTransactionPool(_) => Resp::FlushTransactionPool(Default::default()), + Req::FlushTxpool(_) => Resp::FlushTxpool(Default::default()), Req::GetOutputHistogram(_) => Resp::GetOutputHistogram(Default::default()), Req::GetCoinbaseTxSum(_) => Resp::GetCoinbaseTxSum(Default::default()), Req::GetVersion(_) => Resp::GetVersion(Default::default()), @@ -83,15 +83,20 @@ impl Service for RpcHandlerDummy { Req::GetAlternateChains(_) => Resp::GetAlternateChains(Default::default()), Req::RelayTx(_) => Resp::RelayTx(Default::default()), Req::SyncInfo(_) => Resp::SyncInfo(Default::default()), - Req::GetTransactionPoolBacklog(_) => { - Resp::GetTransactionPoolBacklog(Default::default()) - } + Req::GetTxpoolBacklog(_) => Resp::GetTxpoolBacklog(Default::default()), Req::GetMinerData(_) => Resp::GetMinerData(Default::default()), Req::PruneBlockchain(_) => Resp::PruneBlockchain(Default::default()), Req::CalcPow(_) => Resp::CalcPow(Default::default()), Req::FlushCache(_) => Resp::FlushCache(Default::default()), Req::AddAuxPow(_) => Resp::AddAuxPow(Default::default()), Req::GetTxIdsLoose(_) => Resp::GetTxIdsLoose(Default::default()), + Req::GetOutputDistribution(_) => Resp::GetOutputDistribution(Default::default()), + Req::RpcAccessInfo(_) => Resp::RpcAccessInfo(Default::default()), + Req::RpcAccessSubmitNonce(_) => Resp::RpcAccessSubmitNonce(Default::default()), + Req::RpcAccessPay(_) => Resp::RpcAccessPay(Default::default()), + Req::RpcAccessTracking(_) => Resp::RpcAccessTracking(Default::default()), + Req::RpcAccessData(_) => Resp::RpcAccessData(Default::default()), + Req::RpcAccessAccount(_) => Resp::RpcAccessAccount(Default::default()), }; let (tx, rx) = channel(); @@ -172,6 +177,7 @@ impl Service for RpcHandlerDummy { Req::PopBlocks(_) => Resp::PopBlocks(Default::default()), Req::GetTransactionPoolHashes(_) => Resp::GetTransactionPoolHashes(Default::default()), Req::GetPublicNodes(_) => Resp::GetPublicNodes(Default::default()), + Req::GetInfo(_) => Resp::GetInfo(Default::default()), }; let (tx, rx) = channel(); diff --git a/rpc/types/Cargo.toml b/rpc/types/Cargo.toml index 859340538..a5c9c579a 100644 --- a/rpc/types/Cargo.toml +++ b/rpc/types/Cargo.toml @@ -10,8 +10,8 @@ keywords = ["cuprate", "rpc", "types", "monero"] [features] default = ["serde", "epee"] -serde = ["dep:serde", "cuprate-fixed-bytes/serde", "cuprate-types/serde"] -epee = ["dep:cuprate-epee-encoding", "cuprate-types/epee"] +serde = ["dep:serde", "dep:serde_json", "dep:monero-oxide", "cuprate-fixed-bytes/serde", "cuprate-types/serde"] +epee = ["dep:cuprate-epee-encoding", "dep:monero-oxide", "cuprate-types/epee"] from = [ "dep:cuprate-helper", "cuprate-helper/map", @@ -29,9 +29,11 @@ cuprate-types = { workspace = true, default-features = false, features = cuprate-helper = { workspace = true, optional = true, default-features = false } cuprate-p2p-core = { workspace = true, optional = true, default-features = false } -paste = { workspace = true } -serde = { workspace = true, optional = true } -hex = { workspace = true, optional = true } +monero-oxide = { workspace = true, features = ["std"], optional = true } +paste = { workspace = true } +serde = { workspace = true, optional = true } +serde_json = { workspace = true, optional = true, features = ["std"] } +hex = { workspace = true, optional = true } getrandom = { version = "0.2", optional = true, features = ["js"] } diff --git a/rpc/types/README.md b/rpc/types/README.md index ca25c2241..25103c5b3 100644 --- a/rpc/types/README.md +++ b/rpc/types/README.md @@ -61,7 +61,7 @@ however some fields contain binary values inside JSON strings, for example: `binary` here is (de)serialized as a normal [`String`]. In order to be clear on which fields contain binary data, the struct fields that have them will use [`crate::misc::BinaryString`] instead of [`String`]. These mixed types are: -- [`crate::json::GetTransactionPoolBacklogResponse`] +- [`crate::json::GetTxpoolBacklogResponse`] - [`crate::json::GetOutputDistributionResponse`] TODO: we need to figure out a type that (de)serializes correctly, `String` errors with `serde_json` diff --git a/rpc/types/src/bin.rs b/rpc/types/src/bin.rs index 35fd048af..519951e2f 100644 --- a/rpc/types/src/bin.rs +++ b/rpc/types/src/bin.rs @@ -8,9 +8,6 @@ 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_types::{ rpc::{BlockOutputIndices, PoolInfo}, BlockCompleteEntry, @@ -24,7 +21,7 @@ use crate::{ }; #[cfg(any(feature = "epee", feature = "serde"))] -use crate::defaults::default; +use crate::defaults::{default, default_true}; //---------------------------------------------------------------------------------------------------- Definitions define_request_and_response! { @@ -50,13 +47,12 @@ define_request_and_response! { start_height: u64, }, AccessResponseBase { - m_blocks_ids: ByteArrayVec<32> = default::>(), "default", + m_block_ids: ByteArrayVec<32> = default::>(), "default", start_height: u64, current_height: u64, } } -#[cfg(not(feature = "epee"))] define_request_and_response! { get_o_indexesbin, "cc73fe71162d564ffda8e549b79a350bca53c454" => @@ -71,21 +67,6 @@ define_request_and_response! { } } -#[cfg(feature = "epee")] -define_request_and_response! { - get_o_indexesbin, - "cc73fe71162d564ffda8e549b79a350bca53c454" => - core_rpc_server_commands_defs.h => 487..=510, - GetOutputIndexes, - #[derive(Copy)] - Request { - txid: [u8; 32], - }, - AccessResponseBase { - o_indexes: Vec as ContainerAsBlob, - } -} - define_request_and_response! { get_outsbin, "cc73fe71162d564ffda8e549b79a350bca53c454" => @@ -93,7 +74,7 @@ define_request_and_response! { GetOuts, Request { outputs: Vec = default::>(), "default", - get_txid: bool, + get_txid: bool = default_true(), "default_true", }, AccessResponseBase { outs: Vec = default::>(), "default", diff --git a/rpc/types/src/constants.rs b/rpc/types/src/constants.rs index ce601d791..5693a7613 100644 --- a/rpc/types/src/constants.rs +++ b/rpc/types/src/constants.rs @@ -46,7 +46,7 @@ pub const CORE_RPC_VERSION_MAJOR: u32 = 3; #[doc = monero_definition_link!("cc73fe71162d564ffda8e549b79a350bca53c454", "/rpc/core_rpc_server_commands_defs.h", 91)] /// RPC miror version. -pub const CORE_RPC_VERSION_MINOR: u32 = 14; +pub const CORE_RPC_VERSION_MINOR: u32 = 15; #[doc = monero_definition_link!("cc73fe71162d564ffda8e549b79a350bca53c454", "/rpc/core_rpc_server_commands_defs.h", 92..=93)] /// RPC version. diff --git a/rpc/types/src/free.rs b/rpc/types/src/free.rs index a41c853c7..3fe854a87 100644 --- a/rpc/types/src/free.rs +++ b/rpc/types/src/free.rs @@ -6,7 +6,6 @@ /// Returns `true` if the input `u` is equal to `0`. #[inline] #[expect(clippy::trivially_copy_pass_by_ref, reason = "serde signature")] -#[expect(dead_code, reason = "TODO: see if needed after handlers.")] pub(crate) const fn is_zero(u: &u64) -> bool { *u == 0 } diff --git a/rpc/types/src/json.rs b/rpc/types/src/json.rs index ee79cc399..1bdbb4843 100644 --- a/rpc/types/src/json.rs +++ b/rpc/types/src/json.rs @@ -8,7 +8,8 @@ use serde::{Deserialize, Serialize}; use cuprate_hex::{Hex, HexVec}; use cuprate_types::rpc::{ - AuxPow, GetMinerDataTxBacklogEntry, HardForkEntry, HardForkInfo, TxBacklogEntry, + AuxPow, GetMinerDataTxBacklogEntry, HardForkEntry, HardForkInfo, RpcAccessDataEntry, + RpcAccessTrackingEntry, TxBacklogEntry, }; use crate::{ @@ -242,6 +243,7 @@ define_request_and_response! { AccessResponseBase { block_header: BlockHeader, + #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Vec::is_empty"))] block_headers: Vec, } } @@ -307,6 +309,7 @@ define_request_and_response! { /// to create this JSON string in a type-safe manner. json: String, miner_tx_hash: Hex<32>, + #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Vec::is_empty"))] tx_hashes: Vec>, } } @@ -442,7 +445,7 @@ define_request_and_response! { "cc73fe71162d564ffda8e549b79a350bca53c454" => core_rpc_server_commands_defs.h => 2096..=2116, - FlushTransactionPool (restricted), + FlushTxpool (restricted), Request { txids: Vec> = default::>>(), "default", @@ -506,8 +509,9 @@ define_request_and_response! { ResponseBase { version: u32, release: bool, - current_height: u64, - target_height: u64, + current_height: u64 = default::(), "default", + #[cfg_attr(feature = "serde", serde(skip_serializing_if = "crate::free::is_zero"))] + target_height: u64 = default::(), "default", hard_forks: Vec, } } @@ -582,7 +586,7 @@ define_request_and_response! { get_txpool_backlog, "cc73fe71162d564ffda8e549b79a350bca53c454" => core_rpc_server_commands_defs.h => 1637..=1664, - GetTransactionPoolBacklog (empty), + GetTxpoolBacklog (empty), Request {}, ResponseBase { @@ -597,7 +601,7 @@ define_request_and_response! { core_rpc_server_commands_defs.h => 2445..=2520, /// This type is also used in the (undocumented) - /// [`/get_output_distribution.bin`](https://github.com/monero-project/monero/blob/"cc73fe71162d564ffda8e549b79a350bca53c454"/src/rpc/core_rpc_server.h#L138) + /// [`/get_output_distribution.bin`](https://github.com/monero-project/monero/blob/cc73fe71162d564ffda8e549b79a350bca53c454/src/rpc/core_rpc_server.h#L138) /// binary endpoint. GetOutputDistribution, @@ -726,6 +730,109 @@ define_request_and_response! { } } +define_request_and_response! { + UNDOCUMENTED_METHOD, + "cc73fe71162d564ffda8e549b79a350bca53c454" => + core_rpc_server_commands_defs.h => 2522..=2556, + + RpcAccessInfo, + + Request { + client: String, + }, + + AccessResponseBase { + hashing_blob: String, + seed_height: u64, + seed_hash: String, + next_seed_hash: String, + cookie: u32, + diff: u64, + credits_per_hash_found: u64, + height: u64, + } +} + +define_request_and_response! { + UNDOCUMENTED_METHOD, + "cc73fe71162d564ffda8e549b79a350bca53c454" => + core_rpc_server_commands_defs.h => 2558..=2580, + + RpcAccessSubmitNonce, + + Request { + client: String, + nonce: u32, + cookie: u32, + }, + + AccessResponseBase {} +} + +define_request_and_response! { + UNDOCUMENTED_METHOD, + "cc73fe71162d564ffda8e549b79a350bca53c454" => + core_rpc_server_commands_defs.h => 2582..=2604, + + RpcAccessPay, + + Request { + client: String, + paying_for: String, + payment: u64, + }, + + AccessResponseBase {} +} + +define_request_and_response! { + UNDOCUMENTED_METHOD, + "cc73fe71162d564ffda8e549b79a350bca53c454" => + core_rpc_server_commands_defs.h => 2606..=2644, + + RpcAccessTracking (restricted), + + Request { + clear: bool, + }, + + ResponseBase { + data: Vec, + } +} + +define_request_and_response! { + UNDOCUMENTED_METHOD, + "cc73fe71162d564ffda8e549b79a350bca53c454" => + core_rpc_server_commands_defs.h => 2646..=2692, + + RpcAccessData (restricted), + + Request { }, + + ResponseBase { + entries: Vec, + hashrate: u32, + } +} + +define_request_and_response! { + UNDOCUMENTED_METHOD, + "cc73fe71162d564ffda8e549b79a350bca53c454" => + core_rpc_server_commands_defs.h => 2695..=2720, + + RpcAccessAccount (restricted), + + Request { + client: String, + delta_balance: i64 = default::(), "default", + }, + + ResponseBase { + credits: u64, + } +} + //---------------------------------------------------------------------------------------------------- Request /// JSON-RPC requests. /// @@ -735,21 +842,34 @@ define_request_and_response! { /// /// TODO: document and test (de)serialization behavior after figuring out `method/params`. #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] -#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))] +#[cfg_attr(feature = "serde", derive(Serialize))] #[cfg_attr( feature = "serde", serde(rename_all = "snake_case", tag = "method", content = "params") )] pub enum JsonRpcRequest { - GetBlockTemplate(GetBlockTemplateRequest), + #[cfg_attr(feature = "serde", serde(alias = "getblockcount"))] GetBlockCount(GetBlockCountRequest), + #[cfg_attr(feature = "serde", serde(alias = "on_getblockhash"))] OnGetBlockHash(OnGetBlockHashRequest), + #[cfg_attr(feature = "serde", serde(alias = "getblocktemplate"))] + GetBlockTemplate(GetBlockTemplateRequest), + GetMinerData(GetMinerDataRequest), + CalcPow(CalcPowRequest), + AddAuxPow(AddAuxPowRequest), + #[cfg_attr(feature = "serde", serde(alias = "submitblock"))] SubmitBlock(SubmitBlockRequest), - GenerateBlocks(GenerateBlocksRequest), + #[cfg_attr(feature = "serde", serde(alias = "generateblocks"))] + GenerateBlocks(GenerateBlocksRequest), // TODO: this only has 1 endpoint: generateblocks no generate_blocks + #[cfg_attr(feature = "serde", serde(alias = "getlastblockheader"))] GetLastBlockHeader(GetLastBlockHeaderRequest), + #[cfg_attr(feature = "serde", serde(alias = "getblockheaderbyhash"))] GetBlockHeaderByHash(GetBlockHeaderByHashRequest), + #[cfg_attr(feature = "serde", serde(alias = "getblockheaderbyheight"))] GetBlockHeaderByHeight(GetBlockHeaderByHeightRequest), + #[cfg_attr(feature = "serde", serde(alias = "getblockheadersrange"))] GetBlockHeadersRange(GetBlockHeadersRangeRequest), + #[cfg_attr(feature = "serde", serde(alias = "getblock"))] GetBlock(GetBlockRequest), GetConnections(GetConnectionsRequest), GetInfo(GetInfoRequest), @@ -757,21 +877,116 @@ pub enum JsonRpcRequest { SetBans(SetBansRequest), GetBans(GetBansRequest), Banned(BannedRequest), - FlushTransactionPool(FlushTransactionPoolRequest), + FlushTxpool(FlushTxpoolRequest), GetOutputHistogram(GetOutputHistogramRequest), - GetCoinbaseTxSum(GetCoinbaseTxSumRequest), GetVersion(GetVersionRequest), + GetCoinbaseTxSum(GetCoinbaseTxSumRequest), GetFeeEstimate(GetFeeEstimateRequest), GetAlternateChains(GetAlternateChainsRequest), RelayTx(RelayTxRequest), SyncInfo(SyncInfoRequest), - GetTransactionPoolBacklog(GetTransactionPoolBacklogRequest), - GetMinerData(GetMinerDataRequest), + GetTxpoolBacklog(GetTxpoolBacklogRequest), + GetOutputDistribution(GetOutputDistributionRequest), PruneBlockchain(PruneBlockchainRequest), - CalcPow(CalcPowRequest), FlushCache(FlushCacheRequest), - AddAuxPow(AddAuxPowRequest), GetTxIdsLoose(GetTxIdsLooseRequest), + RpcAccessInfo(RpcAccessInfoRequest), + RpcAccessSubmitNonce(RpcAccessSubmitNonceRequest), + RpcAccessPay(RpcAccessPayRequest), + RpcAccessTracking(RpcAccessTrackingRequest), + RpcAccessData(RpcAccessDataRequest), + RpcAccessAccount(RpcAccessAccountRequest), +} + +#[cfg(feature = "serde")] +impl<'de> Deserialize<'de> for JsonRpcRequest { + fn deserialize>(deserializer: D) -> Result { + use serde::de::Error; + + fn default_params() -> serde_json::Value { + serde_json::Value::Object(Default::default()) + } + + #[derive(Deserialize)] + struct Helper { + method: String, + #[serde(default = "default_params")] + params: serde_json::Value, + } + + let Helper { method, mut params } = Helper::deserialize(deserializer)?; + + // monerod's JSON parser silently drops `null` and empty arrays without + // storing them, so we just inject an empty `{}` section. + if params.is_null() || params.as_array().is_some_and(Vec::is_empty) { + params = serde_json::Value::Object(Default::default()); + } + + // Deserialize `params` (a `serde_json::Value`) into the concrete request type. + macro_rules! de { + ($T:ty) => { + serde_json::from_value::<$T>(params).map_err(D::Error::custom)? + }; + } + + Ok(match method.as_str() { + "get_block_count" | "getblockcount" => Self::GetBlockCount(de!(GetBlockCountRequest)), + "on_get_block_hash" | "on_getblockhash" => { + Self::OnGetBlockHash(de!(OnGetBlockHashRequest)) + } + "get_block_template" | "getblocktemplate" => { + Self::GetBlockTemplate(de!(GetBlockTemplateRequest)) + } + "get_miner_data" => Self::GetMinerData(de!(GetMinerDataRequest)), + "calc_pow" => Self::CalcPow(de!(CalcPowRequest)), + "add_aux_pow" => Self::AddAuxPow(de!(AddAuxPowRequest)), + "submit_block" | "submitblock" => Self::SubmitBlock(de!(SubmitBlockRequest)), + "generateblocks" => Self::GenerateBlocks(de!(GenerateBlocksRequest)), + "get_last_block_header" | "getlastblockheader" => { + Self::GetLastBlockHeader(de!(GetLastBlockHeaderRequest)) + } + "get_block_header_by_hash" | "getblockheaderbyhash" => { + Self::GetBlockHeaderByHash(de!(GetBlockHeaderByHashRequest)) + } + "get_block_header_by_height" | "getblockheaderbyheight" => { + Self::GetBlockHeaderByHeight(de!(GetBlockHeaderByHeightRequest)) + } + "get_block_headers_range" | "getblockheadersrange" => { + Self::GetBlockHeadersRange(de!(GetBlockHeadersRangeRequest)) + } + "get_block" | "getblock" => Self::GetBlock(de!(GetBlockRequest)), + "get_connections" => Self::GetConnections(de!(GetConnectionsRequest)), + "get_info" => Self::GetInfo(de!(GetInfoRequest)), + "hard_fork_info" => Self::HardForkInfo(de!(HardForkInfoRequest)), + "set_bans" => Self::SetBans(de!(SetBansRequest)), + "get_bans" => Self::GetBans(de!(GetBansRequest)), + "banned" => Self::Banned(de!(BannedRequest)), + "flush_txpool" => Self::FlushTxpool(de!(FlushTxpoolRequest)), + "get_output_histogram" => Self::GetOutputHistogram(de!(GetOutputHistogramRequest)), + "get_version" => Self::GetVersion(de!(GetVersionRequest)), + "get_coinbase_tx_sum" => Self::GetCoinbaseTxSum(de!(GetCoinbaseTxSumRequest)), + "get_fee_estimate" => Self::GetFeeEstimate(de!(GetFeeEstimateRequest)), + "get_alternate_chains" => Self::GetAlternateChains(de!(GetAlternateChainsRequest)), + "relay_tx" => Self::RelayTx(de!(RelayTxRequest)), + "sync_info" => Self::SyncInfo(de!(SyncInfoRequest)), + "get_txpool_backlog" => Self::GetTxpoolBacklog(de!(GetTxpoolBacklogRequest)), + "get_output_distribution" => { + Self::GetOutputDistribution(de!(GetOutputDistributionRequest)) + } + "prune_blockchain" => Self::PruneBlockchain(de!(PruneBlockchainRequest)), + "flush_cache" => Self::FlushCache(de!(FlushCacheRequest)), + "get_tx_ids_loose" => Self::GetTxIdsLoose(de!(GetTxIdsLooseRequest)), + "rpc_access_info" => Self::RpcAccessInfo(de!(RpcAccessInfoRequest)), + "rpc_access_submit_nonce" => { + Self::RpcAccessSubmitNonce(de!(RpcAccessSubmitNonceRequest)) + } + "rpc_access_pay" => Self::RpcAccessPay(de!(RpcAccessPayRequest)), + "rpc_access_tracking" => Self::RpcAccessTracking(de!(RpcAccessTrackingRequest)), + "rpc_access_data" => Self::RpcAccessData(de!(RpcAccessDataRequest)), + "rpc_access_account" => Self::RpcAccessAccount(de!(RpcAccessAccountRequest)), + other => return Err(D::Error::unknown_variant(other, &[])), + }) + } } impl RpcCallValue for JsonRpcRequest { @@ -791,7 +1006,7 @@ impl RpcCallValue for JsonRpcRequest { Self::GetOutputHistogram(x) => x.is_restricted(), Self::GetVersion(x) => x.is_restricted(), Self::GetFeeEstimate(x) => x.is_restricted(), - Self::GetTransactionPoolBacklog(x) => x.is_restricted(), + Self::GetTxpoolBacklog(x) => x.is_restricted(), Self::GetMinerData(x) => x.is_restricted(), Self::AddAuxPow(x) => x.is_restricted(), Self::GetTxIdsLoose(x) => x.is_restricted(), @@ -800,7 +1015,7 @@ impl RpcCallValue for JsonRpcRequest { Self::SetBans(x) => x.is_restricted(), Self::GetBans(x) => x.is_restricted(), Self::Banned(x) => x.is_restricted(), - Self::FlushTransactionPool(x) => x.is_restricted(), + Self::FlushTxpool(x) => x.is_restricted(), Self::GetCoinbaseTxSum(x) => x.is_restricted(), Self::GetAlternateChains(x) => x.is_restricted(), Self::RelayTx(x) => x.is_restricted(), @@ -808,6 +1023,13 @@ impl RpcCallValue for JsonRpcRequest { Self::PruneBlockchain(x) => x.is_restricted(), Self::CalcPow(x) => x.is_restricted(), Self::FlushCache(x) => x.is_restricted(), + Self::GetOutputDistribution(x) => x.is_restricted(), + Self::RpcAccessInfo(x) => x.is_restricted(), + Self::RpcAccessSubmitNonce(x) => x.is_restricted(), + Self::RpcAccessPay(x) => x.is_restricted(), + Self::RpcAccessTracking(x) => x.is_restricted(), + Self::RpcAccessData(x) => x.is_restricted(), + Self::RpcAccessAccount(x) => x.is_restricted(), } } @@ -827,7 +1049,7 @@ impl RpcCallValue for JsonRpcRequest { Self::GetOutputHistogram(x) => x.is_empty(), Self::GetVersion(x) => x.is_empty(), Self::GetFeeEstimate(x) => x.is_empty(), - Self::GetTransactionPoolBacklog(x) => x.is_empty(), + Self::GetTxpoolBacklog(x) => x.is_empty(), Self::GetMinerData(x) => x.is_empty(), Self::AddAuxPow(x) => x.is_empty(), Self::GetTxIdsLoose(x) => x.is_empty(), @@ -836,7 +1058,7 @@ impl RpcCallValue for JsonRpcRequest { Self::SetBans(x) => x.is_empty(), Self::GetBans(x) => x.is_empty(), Self::Banned(x) => x.is_empty(), - Self::FlushTransactionPool(x) => x.is_empty(), + Self::FlushTxpool(x) => x.is_empty(), Self::GetCoinbaseTxSum(x) => x.is_empty(), Self::GetAlternateChains(x) => x.is_empty(), Self::RelayTx(x) => x.is_empty(), @@ -844,6 +1066,13 @@ impl RpcCallValue for JsonRpcRequest { Self::PruneBlockchain(x) => x.is_empty(), Self::CalcPow(x) => x.is_empty(), Self::FlushCache(x) => x.is_empty(), + Self::GetOutputDistribution(x) => x.is_empty(), + Self::RpcAccessInfo(x) => x.is_empty(), + Self::RpcAccessSubmitNonce(x) => x.is_empty(), + Self::RpcAccessPay(x) => x.is_empty(), + Self::RpcAccessTracking(x) => x.is_empty(), + Self::RpcAccessData(x) => x.is_empty(), + Self::RpcAccessAccount(x) => x.is_empty(), } } } @@ -892,7 +1121,7 @@ pub enum JsonRpcResponse { SetBans(SetBansResponse), GetBans(GetBansResponse), Banned(BannedResponse), - FlushTransactionPool(FlushTransactionPoolResponse), + FlushTxpool(FlushTxpoolResponse), GetOutputHistogram(GetOutputHistogramResponse), GetCoinbaseTxSum(GetCoinbaseTxSumResponse), GetVersion(GetVersionResponse), @@ -900,13 +1129,20 @@ pub enum JsonRpcResponse { GetAlternateChains(GetAlternateChainsResponse), RelayTx(RelayTxResponse), SyncInfo(SyncInfoResponse), - GetTransactionPoolBacklog(GetTransactionPoolBacklogResponse), + GetTxpoolBacklog(GetTxpoolBacklogResponse), + GetOutputDistribution(GetOutputDistributionResponse), GetMinerData(GetMinerDataResponse), PruneBlockchain(PruneBlockchainResponse), CalcPow(CalcPowResponse), FlushCache(FlushCacheResponse), AddAuxPow(AddAuxPowResponse), GetTxIdsLoose(GetTxIdsLooseResponse), + RpcAccessInfo(RpcAccessInfoResponse), + RpcAccessSubmitNonce(RpcAccessSubmitNonceResponse), + RpcAccessPay(RpcAccessPayResponse), + RpcAccessTracking(RpcAccessTrackingResponse), + RpcAccessData(RpcAccessDataResponse), + RpcAccessAccount(RpcAccessAccountResponse), } //---------------------------------------------------------------------------------------------------- Tests @@ -1548,7 +1784,7 @@ mod test { fn flush_transaction_pool_request() { test_json_request( json::FLUSH_TRANSACTION_POOL_REQUEST, - FlushTransactionPoolRequest { + FlushTxpoolRequest { txids: vec![Hex(hex!( "dc16fa8eaffe1484ca9014ea050e13131d3acf23b419f33bb4cc0b32b6c49308" ))], @@ -1560,7 +1796,7 @@ mod test { fn flush_transaction_pool_response() { test_json_response( json::FLUSH_TRANSACTION_POOL_RESPONSE, - FlushTransactionPoolResponse { status: Status::Ok }, + FlushTxpoolResponse { status: Status::Ok }, ); } diff --git a/rpc/types/src/misc/distribution.rs b/rpc/types/src/misc/distribution.rs index ce00de0bb..cb5e59e47 100644 --- a/rpc/types/src/misc/distribution.rs +++ b/rpc/types/src/misc/distribution.rs @@ -1,19 +1,20 @@ //! Output distributions for [`crate::json::GetOutputDistributionResponse`]. //---------------------------------------------------------------------------------------------------- Use +#[cfg(any(feature = "epee", feature = "serde"))] +use monero_oxide::io::VarInt; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; #[cfg(feature = "epee")] use cuprate_epee_encoding::{ + container_as_blob::ContainerAsBlob, epee_object, error, macros::bytes::{Buf, BufMut}, - read_epee_value, write_field, EpeeObject, EpeeObjectBuilder, EpeeValue, + read_epee_value, read_marker, write_field, EpeeObject, EpeeObjectBuilder, EpeeValue, }; //---------------------------------------------------------------------------------------------------- Free -/// TODO: . -/// /// Used for [`Distribution::CompressedBinary::distribution`]. #[doc = crate::macros::monero_definition_link!( "cc73fe71162d564ffda8e549b79a350bca53c454", @@ -21,12 +22,14 @@ use cuprate_epee_encoding::{ 45..=55 )] #[cfg(any(feature = "epee", feature = "serde"))] -fn compress_integer_array(_: &[u64]) -> Vec { - todo!() +fn compress_integer_array(v: &[u64]) -> Vec { + let mut out = Vec::with_capacity(v.len() * 2); + for val in v { + VarInt::write(val, &mut out).expect("Writing to vec should not fail"); + } + out } -/// TODO: . -/// /// Used for [`Distribution::CompressedBinary::distribution`]. #[doc = crate::macros::monero_definition_link!( "cc73fe71162d564ffda8e549b79a350bca53c454", @@ -34,8 +37,12 @@ fn compress_integer_array(_: &[u64]) -> Vec { 57..=72 )] #[cfg(any(feature = "epee", feature = "serde"))] -fn decompress_integer_array(_: &[u8]) -> Vec { - todo!() +fn decompress_integer_array(mut s: &[u8]) -> std::io::Result> { + let mut v = Vec::new(); + while !s.is_empty() { + v.push(VarInt::read(&mut s)?); + } + Ok(v) } //---------------------------------------------------------------------------------------------------- Distribution @@ -77,7 +84,7 @@ impl Default for Distribution { } /// Data within [`Distribution::Uncompressed`]. -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "serde", derive(Deserialize))] #[derive(Clone, Default, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct DistributionUncompressed { pub start_height: u64, @@ -88,14 +95,57 @@ pub struct DistributionUncompressed { pub binary: bool, } +// Manual `Serialize` so the JSON output includes `compress: false`, matching +// monerod. `derive(Serialize)` would only emit the struct fields. +#[cfg(feature = "serde")] +impl Serialize for DistributionUncompressed { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + use serde::ser::SerializeStruct; + let mut s = serializer.serialize_struct("DistributionUncompressed", 6)?; + s.serialize_field("start_height", &self.start_height)?; + s.serialize_field("base", &self.base)?; + s.serialize_field("distribution", &self.distribution)?; + s.serialize_field("amount", &self.amount)?; + s.serialize_field("binary", &self.binary)?; + s.serialize_field("compress", &false)?; + s.end() + } +} + +// `DistributionUncompressed` is never used as a standalone EPEE object. +// Its `EpeeObject` impl exists only so that `Distribution::number_of_fields()` +// can call `s.number_of_fields()`. The write path is handled manually in +// `Distribution::write_fields` to correctly switch between blob and array +// encoding based on the `binary` flag — do NOT add a standalone write impl here. #[cfg(feature = "epee")] -epee_object! { - DistributionUncompressed, - start_height: u64, - base: u64, - distribution: Vec, - amount: u64, - binary: bool, +#[derive(Default)] +pub struct __DistributionUncompressedEpeeBuilder; + +#[cfg(feature = "epee")] +impl EpeeObjectBuilder for __DistributionUncompressedEpeeBuilder { + fn add_field(&mut self, _: &str, _: &mut B) -> error::Result { + unreachable!("DistributionUncompressed is never deserialized as a standalone EPEE object") + } + + fn finish(self) -> error::Result { + unreachable!("DistributionUncompressed is never deserialized as a standalone EPEE object") + } +} + +#[cfg(feature = "epee")] +impl EpeeObject for DistributionUncompressed { + type Builder = __DistributionUncompressedEpeeBuilder; + + fn number_of_fields(&self) -> u64 { + 4 + u64::from(EpeeValue::should_write(&self.distribution)) + } + + fn write_fields(self, _: &mut B) -> error::Result<()> { + unreachable!() // We don't write directly here, we do it in [`Distribution`] + } } /// Data within [`Distribution::CompressedBinary`]. @@ -148,7 +198,8 @@ fn deserialize_compressed_data_as_distribution<'de, D>(d: D) -> Result, where D: serde::Deserializer<'de>, { - Vec::::deserialize(d).map(|v| decompress_integer_array(&v)) + Vec::::deserialize(d) + .and_then(|v| decompress_integer_array(&v).map_err(serde::de::Error::custom)) } //---------------------------------------------------------------------------------------------------- Epee @@ -171,25 +222,25 @@ pub struct __DistributionEpeeBuilder { #[cfg(feature = "epee")] impl EpeeObjectBuilder for __DistributionEpeeBuilder { fn add_field(&mut self, name: &str, r: &mut B) -> error::Result { - 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 + match name { + "start_height" => self.start_height = Some(read_epee_value(r)?), + "base" => self.base = Some(read_epee_value(r)?), + "amount" => self.amount = Some(read_epee_value(r)?), + "binary" => self.binary = Some(read_epee_value(r)?), + "compress" => self.compress = Some(read_epee_value(r)?), + "compressed_data" => self.compressed_data = Some(read_epee_value(r)?), + // `distribution` arrives as a raw LE-u64 blob when `binary=true` + // (monerod uses `KV_SERIALIZE_CONTAINER_POD_AS_BLOB_N`) or as a + // typed EPEE u64 array when `binary=false`. Detect via marker. + "distribution" => { + let marker = read_marker(r)?; + self.distribution = Some(if marker == ContainerAsBlob::::MARKER { + ContainerAsBlob::::read(r, &marker)?.into() + } else { + Vec::::read(r, &marker)? + }); + } + _ => return Ok(false), } Ok(true) @@ -203,23 +254,23 @@ impl EpeeObjectBuilder for __DistributionEpeeBuilder { let amount = self.amount.ok_or(ELSE)?; let distribution = if let Some(compressed_data) = self.compressed_data { - let distribution = decompress_integer_array(&compressed_data); + let distribution = decompress_integer_array(&compressed_data) + .map_err(|_| error::Error::Format("Failed to decompress distribution"))?; Distribution::CompressedBinary(DistributionCompressedBinary { start_height, base, distribution, amount, }) - } else if let Some(distribution) = self.distribution { + } else { + let distribution = self.distribution.unwrap_or_default(); Distribution::Uncompressed(DistributionUncompressed { - binary: self.binary.ok_or(ELSE)?, + binary: self.binary.unwrap_or(true), distribution, start_height, base, amount, }) - } else { - return Err(ELSE); }; Ok(distribution) @@ -241,8 +292,22 @@ impl EpeeObject for Distribution { fn write_fields(self, w: &mut B) -> error::Result<()> { match self { - Self::Uncompressed(s) => { - s.write_fields(w)?; + Self::Uncompressed(DistributionUncompressed { + start_height, + base, + distribution, + amount, + binary, + }) => { + write_field(start_height, "start_height", w)?; + write_field(base, "base", w)?; + if binary { + write_field(ContainerAsBlob::from(distribution), "distribution", w)?; + } else { + write_field(distribution, "distribution", w)?; + } + write_field(amount, "amount", w)?; + write_field(binary, "binary", w)?; write_field(false, "compress", w)?; } @@ -254,11 +319,10 @@ impl EpeeObject for Distribution { }) => { let compressed_data = compress_integer_array(&distribution); - start_height.write(w)?; - base.write(w)?; - compressed_data.write(w)?; - amount.write(w)?; - + write_field(start_height, "start_height", w)?; + write_field(base, "base", w)?; + write_field(compressed_data, "compressed_data", w)?; + write_field(amount, "amount", w)?; write_field(true, "binary", w)?; write_field(true, "compress", w)?; } @@ -267,33 +331,3 @@ impl EpeeObject for Distribution { 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); - // } -} diff --git a/rpc/types/src/misc/status.rs b/rpc/types/src/misc/status.rs index 7e457e7bd..ae38a0a4b 100644 --- a/rpc/types/src/misc/status.rs +++ b/rpc/types/src/misc/status.rs @@ -1,7 +1,7 @@ //! RPC response status type. //---------------------------------------------------------------------------------------------------- Import -use std::fmt::Display; +use std::{borrow::Cow, fmt::Display}; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; @@ -109,7 +109,7 @@ pub enum Status { /// 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), + Other(Cow<'static, str>), } impl From for Status { @@ -120,7 +120,7 @@ impl From for Status { CORE_RPC_STATUS_NOT_MINING => Self::NotMining, CORE_RPC_STATUS_PAYMENT_REQUIRED => Self::PaymentRequired, CORE_RPC_STATUS_FAILED => Self::Failed, - _ => Self::Other(s), + _ => Self::Other(Cow::Owned(s)), } } } @@ -165,7 +165,7 @@ impl EpeeValue for Status { fn epee_default_value() -> Option { // - Some(Self::Other(String::new())) + Some(Self::Other(Cow::Borrowed(""))) } fn write(self, w: &mut B) -> cuprate_epee_encoding::Result<()> { @@ -187,7 +187,7 @@ mod test { Status::Busy, Status::NotMining, Status::PaymentRequired, - Status::Other(String::new()), + Status::Other(Cow::Borrowed("")), ] { let mut buf = vec![]; diff --git a/rpc/types/src/other.rs b/rpc/types/src/other.rs index d0cb2c78c..e67f1a363 100644 --- a/rpc/types/src/other.rs +++ b/rpc/types/src/other.rs @@ -11,6 +11,7 @@ use cuprate_types::rpc::{Peer, PublicNode, TxpoolStats}; use crate::{ base::{AccessResponseBase, ResponseBase}, + json::{GetInfoRequest, GetInfoResponse}, macros::define_request_and_response, misc::{GetOutputsOut, OutKey, SpentKeyImageInfo, Status, TxEntry, TxInfo}, RpcCallValue, @@ -53,7 +54,9 @@ define_request_and_response! { txs_as_hex: Vec, /// `cuprate_rpc_types::json::tx::Transaction` should be used /// to create these JSON strings in a type-safe manner. + #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Vec::is_empty"))] txs_as_json: Vec, + #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Vec::is_empty"))] missed_tx: Vec>, txs: Vec, } @@ -493,6 +496,7 @@ define_request_and_response! { #[cfg_attr(feature = "serde", derive(Deserialize, Serialize))] #[cfg_attr(feature = "serde", serde(untagged))] pub enum OtherRequest { + GetInfo(GetInfoRequest), GetHeight(GetHeightRequest), GetTransactions(GetTransactionsRequest), GetAltBlocksHashes(GetAltBlocksHashesRequest), @@ -525,6 +529,7 @@ pub enum OtherRequest { impl RpcCallValue for OtherRequest { fn is_restricted(&self) -> bool { match self { + Self::GetInfo(x) => x.is_restricted(), Self::GetHeight(x) => x.is_restricted(), Self::GetTransactions(x) => x.is_restricted(), Self::GetAltBlocksHashes(x) => x.is_restricted(), @@ -557,6 +562,7 @@ impl RpcCallValue for OtherRequest { fn is_empty(&self) -> bool { match self { + Self::GetInfo(x) => x.is_empty(), Self::GetHeight(x) => x.is_empty(), Self::GetTransactions(x) => x.is_empty(), Self::GetAltBlocksHashes(x) => x.is_empty(), @@ -610,7 +616,9 @@ impl RpcCallValue for OtherRequest { #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] #[cfg_attr(feature = "serde", derive(Deserialize, Serialize))] #[cfg_attr(feature = "serde", serde(untagged))] +#[expect(clippy::large_enum_variant)] pub enum OtherResponse { + GetInfo(GetInfoResponse), GetHeight(GetHeightResponse), GetTransactions(GetTransactionsResponse), GetAltBlocksHashes(GetAltBlocksHashesResponse), diff --git a/types/types/src/rpc/mod.rs b/types/types/src/rpc/mod.rs index dd998d646..1fab83772 100644 --- a/types/types/src/rpc/mod.rs +++ b/types/types/src/rpc/mod.rs @@ -18,6 +18,7 @@ pub use types::{ FeeEstimate, GetBan, GetMinerDataTxBacklogEntry, GetOutputsOut, HardForkEntry, HardForkInfo, HistogramEntry, MinerData, MinerDataTxBacklogEntry, OutputDistributionData, OutputHistogramEntry, OutputHistogramInput, Peer, PoolInfoFull, PoolInfoIncremental, - PoolTxInfo, PublicNode, SetBan, Span, SpentKeyImageInfo, SyncInfoPeer, TxBacklogEntry, TxInfo, - TxOutputIndices, TxpoolHisto, TxpoolStats, + PoolTxInfo, PublicNode, RpcAccessDataEntry, RpcAccessTrackingEntry, SetBan, Span, + SpentKeyImageInfo, SyncInfoPeer, TxBacklogEntry, TxInfo, TxOutputIndices, TxpoolHisto, + TxpoolStats, }; diff --git a/types/types/src/rpc/types.rs b/types/types/src/rpc/types.rs index 7eb4a6b3c..35e728fae 100644 --- a/types/types/src/rpc/types.rs +++ b/types/types/src/rpc/types.rs @@ -544,6 +544,35 @@ define_struct_and_impl_epee! { added_pool_txs: Vec, remaining_added_pool_txids: ByteArrayVec<32>, } + + #[doc = monero_definition_link!( + "cc73fe71162d564ffda8e549b79a350bca53c454", + "rpc/core_rpc_server_commands_defs.h", + 2606..=2644 + )] + RpcAccessTrackingEntry { + rpc: String, + count: u64, + time: u64, + credits: u64, + } + + #[doc = monero_definition_link!( + "cc73fe71162d564ffda8e549b79a350bca53c454", + "rpc/core_rpc_server_commands_defs.h", + 2646..=2692 + )] + RpcAccessDataEntry { + client: String, + balance: u64, + last_update_time: u64, + credits_total: u64, + credits_used: u64, + nonces_good: u64, + nonces_stale: u64, + nonces_bad: u64, + nonces_dupe: u64, + } } //---------------------------------------------------------------------------------------------------- Tests