From 3557ee63bf90c6a2a2cdfd337dbdf79d25796e77 Mon Sep 17 00:00:00 2001
From: "hinto.janai" <hinto.janai@protonmail.com>
Date: Sun, 7 Jul 2024 16:29:24 -0400
Subject: [PATCH] add `misc` module, start `bin` and `other`

---
 rpc/types/README.md       |  21 ++-
 rpc/types/src/base.rs     |   2 +-
 rpc/types/src/bin.rs      |  63 ++++++++
 rpc/types/src/defaults.rs |   6 +
 rpc/types/src/json.rs     |   3 +-
 rpc/types/src/lib.rs      |  19 +--
 rpc/types/src/misc.rs     | 301 +++++++++++++++++++++++++++++++++++++-
 rpc/types/src/other.rs    |  15 +-
 8 files changed, 401 insertions(+), 29 deletions(-)

diff --git a/rpc/types/README.md b/rpc/types/README.md
index 0b067436..ad1e0e80 100644
--- a/rpc/types/README.md
+++ b/rpc/types/README.md
@@ -10,17 +10,17 @@ This crate ports the types used in Monero's RPC interface, including:
 # Modules
 This crate's types are split in the following manner:
 
-This crate has 5 modules:
-- The root module - miscellaneous items
-- [`json`] - JSON types from the `/json_rpc` endpoint
-- [`bin`] - Binary types from the binary endpoints
-- [`other`] - Misc JSON types from other endpoints
-- [`base`] - Base response types
+| Module | Purpose |
+|--------|---------|
+| The root module | Miscellaneous items, e.g. constants.
+| [`json`] | Contains JSON request/response (some mixed with binary) that all share the common `/json_rpc` endpoint. |
+| [`bin`] | Contains request/response types that are expected to be fully in binary (`cuprate_epee_encoding`) in `monerod` and `cuprated`'s RPC interface. These are called at a custom endpoint instead of `/json_rpc`, e.g. `/get_blocks.bin`. |
+| [`other`] | Contains request/response types that are JSON, but aren't called at `/json_rpc` (e.g. [`crate::other::GetHeightRequest`]). |
+| [`misc`] | Contains miscellaneous types, e.g. [`crate::misc::Status`]. Many of types here are found and used in request/response types, for example, [`crate::misc::BlockHeader`] is used in [`crate::json::GetLastBlockHeaderResponse`]. |
+| [`base`] | Contains base types flattened into many request/response types.
 
 Each type in `{json,bin,other}` come in pairs and have identical names, but are suffixed with either `Request` or `Response`. e.g. [`GetBlockCountRequest`](crate::json::GetBlockCountRequest) & [`GetBlockCountResponse`](crate::json::GetBlockCountResponse).
 
-Miscellaneous types are found in the root module, e.g. [`crate::Status`]. Many of types here are found and used in request/response types, for example, [`crate::BlockHeader`] is used in [`crate::json::GetLastBlockHeaderResponse`].
-
 # Documentation
 The documentation for types within `{json,bin,other}` are omitted, as they can be found in [Monero's RPC documentation](https://www.getmonero.org/resources/developer-guides/daemon-rpc.html).
 
@@ -45,9 +45,8 @@ For example:
 TODO: fix doc links when types are ready.
 
 # Mixed types
-Note that some types within [`other`] mix JSON & binary together, i.e.,
-the message overall is JSON, however some fields contain binary
-values inside JSON strings, for example:
+Note that some types mix JSON & binary together, i.e., the message overall is JSON,
+however some fields contain binary values inside JSON strings, for example:
 
 ```json
 {
diff --git a/rpc/types/src/base.rs b/rpc/types/src/base.rs
index 38f857d1..d55c0ef1 100644
--- a/rpc/types/src/base.rs
+++ b/rpc/types/src/base.rs
@@ -24,7 +24,7 @@ use serde::{Deserialize, Serialize};
 #[cfg(feature = "epee")]
 use cuprate_epee_encoding::epee_object;
 
-use crate::{macros::monero_definition_link, Status};
+use crate::{macros::monero_definition_link, misc::Status};
 
 //---------------------------------------------------------------------------------------------------- Requests
 /* no types here... yet */
diff --git a/rpc/types/src/bin.rs b/rpc/types/src/bin.rs
index f327847f..66e02a03 100644
--- a/rpc/types/src/bin.rs
+++ b/rpc/types/src/bin.rs
@@ -1,8 +1,71 @@
 //! Binary types from [binary](https://www.getmonero.org/resources/developer-guides/daemon-rpc.html#get_blocksbin) endpoints.
+//!
+//! Most (if not all) of these types are defined here:
+//! - <https://github.com/monero-project/monero/blob/cc73fe71162d564ffda8e549b79a350bca53c454/src/rpc/core_rpc_server_commands_defs.h>
 
 //---------------------------------------------------------------------------------------------------- Import
+use crate::{
+    base::{AccessResponseBase, ResponseBase},
+    defaults::{
+        default_bool, default_height, default_string, default_u64, default_u8, default_vec,
+    },
+    free::{is_one, is_zero},
+    macros::define_request_and_response,
+    misc::{
+        AuxPow, BlockCompleteEntry, BlockHeader, BlockOutputIndices, ChainInfo, ConnectionInfo,
+        GetBan, HardforkEntry, HistogramEntry, OutputDistributionData, Peer, PoolTxInfo, SetBan,
+        Span, Status, TxBacklogEntry,
+    },
+};
 
 //---------------------------------------------------------------------------------------------------- TODO
+define_request_and_response! {
+    get_blocks_bin,
+    cc73fe71162d564ffda8e549b79a350bca53c454 =>
+    core_rpc_server_commands_defs.h => 162..=262,
+    GetBlocksBin,
+    Request {
+        #[cfg_attr(feature = "serde", serde(default = "default_u8"))]
+        requested_info: u8 = default_u8(),
+        // TODO: This is a `std::list` in `monerod` because...?
+        block_ids: Vec<[u8; 32]>,
+        start_height: u64,
+        prune: bool,
+        #[cfg_attr(feature = "serde", serde(default = "default_bool"))]
+        no_miner_tx: bool = default_bool(),
+        #[cfg_attr(feature = "serde", serde(default = "default_u64"))]
+        pool_info_since: u64 = default_u64(),
+    },
+    ResponseBase {
+      blocks: Vec<BlockCompleteEntry>,
+      start_height: u64,
+      current_height: u64,
+      output_indices: Vec<BlockOutputIndices>,
+      daemon_time: u64,
+      pool_info_extent: u8,
+      added_pool_txs: Vec<PoolTxInfo>,
+      remaining_added_pool_txids: Vec<[u8; 32]>,
+      removed_pool_txids: Vec<[u8; 32]>,
+    }
+}
+
+define_request_and_response! {
+    add_aux_pow,
+    cc73fe71162d564ffda8e549b79a350bca53c454 =>
+    core_rpc_server_commands_defs.h => 1068..=1112,
+    AddAuxPow,
+    Request {
+        blocktemplate_blob: String,
+        aux_pow: Vec<AuxPow>,
+    },
+    ResponseBase {
+        blocktemplate_blob: String,
+        blockhashing_blob: String,
+        merkle_root: String,
+        merkle_tree_depth: u64,
+        aux_pow: Vec<AuxPow>,
+    }
+}
 
 //---------------------------------------------------------------------------------------------------- Tests
 #[cfg(test)]
diff --git a/rpc/types/src/defaults.rs b/rpc/types/src/defaults.rs
index 833f33ef..27aac90a 100644
--- a/rpc/types/src/defaults.rs
+++ b/rpc/types/src/defaults.rs
@@ -47,6 +47,12 @@ pub(crate) const fn default_u64() -> u64 {
     0
 }
 
+/// Default [`u8`] used in request/response types.
+#[inline]
+pub(crate) const fn default_u8() -> u8 {
+    0
+}
+
 //---------------------------------------------------------------------------------------------------- Tests
 #[cfg(test)]
 mod test {
diff --git a/rpc/types/src/json.rs b/rpc/types/src/json.rs
index b5ae493c..dd2d9f16 100644
--- a/rpc/types/src/json.rs
+++ b/rpc/types/src/json.rs
@@ -11,9 +11,8 @@ use crate::{
     macros::define_request_and_response,
     misc::{
         AuxPow, BlockHeader, ChainInfo, ConnectionInfo, GetBan, HardforkEntry, HistogramEntry,
-        Peer, SetBan, Span, TxBacklogEntry,
+        OutputDistributionData, Peer, SetBan, Span, Status, TxBacklogEntry,
     },
-    OutputDistributionData, Status,
 };
 
 //---------------------------------------------------------------------------------------------------- Struct definitions
diff --git a/rpc/types/src/lib.rs b/rpc/types/src/lib.rs
index 42c6b551..268e3875 100644
--- a/rpc/types/src/lib.rs
+++ b/rpc/types/src/lib.rs
@@ -82,7 +82,15 @@
 	clippy::option_if_let_else,
 )]
 // Allow some lints when running in debug mode.
-#![cfg_attr(debug_assertions, allow(clippy::todo, clippy::multiple_crate_versions))]
+#![cfg_attr(
+    debug_assertions,
+    allow(
+        clippy::todo,
+        clippy::multiple_crate_versions,
+        unused_imports,
+        unused_variables
+    )
+)]
 // Allow some lints in tests.
 #![cfg_attr(
     test,
@@ -102,7 +110,6 @@ mod constants;
 mod defaults;
 mod free;
 mod macros;
-mod status;
 
 pub use binary_string::BinaryString;
 pub use constants::{
@@ -110,7 +117,6 @@ pub use constants::{
     CORE_RPC_STATUS_PAYMENT_REQUIRED, CORE_RPC_STATUS_UNKNOWN, CORE_RPC_VERSION,
     CORE_RPC_VERSION_MAJOR, CORE_RPC_VERSION_MINOR,
 };
-pub use status::Status;
 
 pub mod base;
 #[cfg(feature = "bin")]
@@ -119,12 +125,7 @@ pub mod bin;
 #[cfg(feature = "json")]
 #[cfg_attr(docsrs, doc(cfg(feature = "json")))]
 pub mod json;
+pub mod misc;
 #[cfg(feature = "other")]
 #[cfg_attr(docsrs, doc(cfg(feature = "other")))]
 pub mod other;
-
-mod misc;
-pub use misc::{
-    AuxPow, BlockHeader, ChainInfo, ConnectionInfo, GetBan, GetMinerDataTxBacklogEntry,
-    HardforkEntry, HistogramEntry, OutputDistributionData, Peer, SetBan, Span, TxBacklogEntry,
-};
diff --git a/rpc/types/src/misc.rs b/rpc/types/src/misc.rs
index 2c95d74b..5974aaaf 100644
--- a/rpc/types/src/misc.rs
+++ b/rpc/types/src/misc.rs
@@ -3,8 +3,6 @@
 //! These are `struct`s that appear in request/response types.
 //! For example, [`crate::json::GetConnectionsResponse`] contains
 //! the [`crate::misc::ConnectionInfo`] struct defined here.
-//!
-//! These types are re-exported to the root module.
 
 //---------------------------------------------------------------------------------------------------- Lints
 #![allow(
@@ -13,13 +11,25 @@
 )]
 
 //---------------------------------------------------------------------------------------------------- Import
+use std::fmt::Display;
+
 #[cfg(feature = "serde")]
 use serde::{Deserialize, Serialize};
 
 #[cfg(feature = "epee")]
-use cuprate_epee_encoding::epee_object;
+use cuprate_epee_encoding::{
+    epee_object,
+    macros::bytes::{Buf, BufMut},
+    EpeeValue, Marker,
+};
 
-use crate::macros::monero_definition_link;
+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,
+    },
+    macros::monero_definition_link,
+};
 
 //---------------------------------------------------------------------------------------------------- Macros
 /// This macro (local to this file) defines all the misc types.
@@ -328,8 +338,289 @@ define_struct_and_impl_epee! {
     }
 }
 
+define_struct_and_impl_epee! {
+    #[doc = monero_definition_link!(
+        cc73fe71162d564ffda8e549b79a350bca53c454,
+        "rpc/core_rpc_server_commands_defs.h",
+        192..=199
+    )]
+    /// Used in [`crate::bin::GetBlocksBinResponse`].
+    #[derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
+    #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
+    TxOutputIndices {
+        indices: Vec<u64>,
+    }
+}
+
+define_struct_and_impl_epee! {
+    #[doc = monero_definition_link!(
+        cc73fe71162d564ffda8e549b79a350bca53c454,
+        "rpc/core_rpc_server_commands_defs.h",
+        201..=208
+    )]
+    /// Used in [`crate::bin::GetBlocksBinResponse`].
+    #[derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
+    #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
+    BlockOutputIndices {
+        indices: Vec<TxOutputIndices>,
+    }
+}
+
+define_struct_and_impl_epee! {
+    #[doc = monero_definition_link!(
+        cc73fe71162d564ffda8e549b79a350bca53c454,
+        "rpc/core_rpc_server_commands_defs.h",
+        210..=221
+    )]
+    /// Used in [`crate::bin::GetBlocksBinResponse`].
+    #[derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
+    #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
+    PoolTxInfo {
+        tx_hash: [u8; 32],
+        tx_blob: String,
+        double_spend_seen: bool,
+    }
+}
+
+define_struct_and_impl_epee! {
+    #[doc = monero_definition_link!(
+        cc73fe71162d564ffda8e549b79a350bca53c454,
+        "cryptonote_protocol/cryptonote_protocol_defs.h",
+        121..=131
+    )]
+    /// Used in [`crate::bin::GetBlocksBinResponse`].
+    #[derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
+    #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
+    TxBlobEntry {
+        blob: String,
+        prunable_hash: [u8; 32],
+    }
+}
+
+//---------------------------------------------------------------------------------------------------- TODO
+// TODO - weird types.
+
+#[doc = monero_definition_link!(
+    cc73fe71162d564ffda8e549b79a350bca53c454,
+    "rpc/core_rpc_server_commands_defs.h",
+    210..=221
+)]
+/// Used in [`crate::bin::GetBlocksBinResponse`].
+#[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>,
+}
+
+/// Used in [`crate::bin::GetBlocksBinResponse`].
+#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
+#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
+#[repr(u8)]
+pub enum PoolInfoExtent {
+    None = 0,
+    Incremental = 1,
+    Full = 2,
+}
+
+// <https://github.com/monero-project/monero/blob/cc73fe71162d564ffda8e549b79a350bca53c454/src/cryptonote_protocol/cryptonote_protocol_defs.h#L138-L163>
+#[cfg(feature = "epee")]
+impl EpeeValue for PoolInfoExtent {
+    const MARKER: Marker = <String as EpeeValue>::MARKER;
+
+    fn read<B: Buf>(r: &mut B, marker: &Marker) -> cuprate_epee_encoding::Result<Self> {
+        todo!()
+    }
+
+    fn should_write(&self) -> bool {
+        todo!()
+    }
+
+    fn epee_default_value() -> Option<Self> {
+        todo!()
+    }
+
+    fn write<B: BufMut>(self, w: &mut B) -> cuprate_epee_encoding::Result<()> {
+        todo!()
+    }
+}
+
+//---------------------------------------------------------------------------------------------------- Status
+/// RPC response status.
+///
+/// This type represents `monerod`'s frequently appearing string field, `status`.
+///
+/// This field appears within RPC [JSON response](crate::json) types.
+///
+/// Reference: <https://github.com/monero-project/monero/blob/cc73fe71162d564ffda8e549b79a350bca53c454/src/rpc/core_rpc_server_commands_defs.h#L78-L81>.
+///
+/// ## Serialization and string formatting
+/// ```rust
+/// use cuprate_rpc_types::{
+///     misc::Status,
+///     CORE_RPC_STATUS_BUSY, CORE_RPC_STATUS_NOT_MINING, CORE_RPC_STATUS_OK,
+///     CORE_RPC_STATUS_PAYMENT_REQUIRED, CORE_RPC_STATUS_UNKNOWN
+/// };
+/// use serde_json::to_string;
+///
+/// let unknown = Status::Unknown;
+///
+/// assert_eq!(to_string(&Status::Ok).unwrap(),              r#""OK""#);
+/// assert_eq!(to_string(&Status::Busy).unwrap(),            r#""BUSY""#);
+/// assert_eq!(to_string(&Status::NotMining).unwrap(),       r#""NOT MINING""#);
+/// assert_eq!(to_string(&Status::PaymentRequired).unwrap(), r#""PAYMENT REQUIRED""#);
+/// assert_eq!(to_string(&unknown).unwrap(),                 r#""UNKNOWN""#);
+///
+/// assert_eq!(Status::Ok.as_ref(),              CORE_RPC_STATUS_OK);
+/// assert_eq!(Status::Busy.as_ref(),            CORE_RPC_STATUS_BUSY);
+/// assert_eq!(Status::NotMining.as_ref(),       CORE_RPC_STATUS_NOT_MINING);
+/// assert_eq!(Status::PaymentRequired.as_ref(), CORE_RPC_STATUS_PAYMENT_REQUIRED);
+/// assert_eq!(unknown.as_ref(),                 CORE_RPC_STATUS_UNKNOWN);
+///
+/// assert_eq!(format!("{}", Status::Ok),              CORE_RPC_STATUS_OK);
+/// assert_eq!(format!("{}", Status::Busy),            CORE_RPC_STATUS_BUSY);
+/// assert_eq!(format!("{}", Status::NotMining),       CORE_RPC_STATUS_NOT_MINING);
+/// assert_eq!(format!("{}", Status::PaymentRequired), CORE_RPC_STATUS_PAYMENT_REQUIRED);
+/// assert_eq!(format!("{}", unknown),                 CORE_RPC_STATUS_UNKNOWN);
+///
+/// assert_eq!(format!("{:?}", Status::Ok),              "Ok");
+/// assert_eq!(format!("{:?}", Status::Busy),            "Busy");
+/// assert_eq!(format!("{:?}", Status::NotMining),       "NotMining");
+/// assert_eq!(format!("{:?}", Status::PaymentRequired), "PaymentRequired");
+/// assert_eq!(format!("{:?}", unknown),                 "Unknown");
+/// ```
+#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
+#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
+pub enum Status {
+    // FIXME:
+    // `#[serde(rename = "")]` only takes raw string literals?
+    // We have to re-type the constants here...
+    /// Successful RPC response, everything is OK; [`CORE_RPC_STATUS_OK`].
+    #[cfg_attr(feature = "serde", serde(rename = "OK"))]
+    #[default]
+    Ok,
+
+    /// The daemon is busy, try later; [`CORE_RPC_STATUS_BUSY`].
+    #[cfg_attr(feature = "serde", serde(rename = "BUSY"))]
+    Busy,
+
+    /// The daemon is not mining; [`CORE_RPC_STATUS_NOT_MINING`].
+    #[cfg_attr(feature = "serde", serde(rename = "NOT MINING"))]
+    NotMining,
+
+    /// Payment is required for RPC; [`CORE_RPC_STATUS_PAYMENT_REQUIRED`].
+    #[cfg_attr(feature = "serde", serde(rename = "PAYMENT REQUIRED"))]
+    PaymentRequired,
+
+    /// Some unknown other string; [`CORE_RPC_STATUS_UNKNOWN`].
+    ///
+    /// This exists to act as a catch-all if `monerod` adds
+    /// a string and a Cuprate node hasn't updated yet.
+    ///
+    /// The reason this isn't `Unknown(String)` is because that
+    /// disallows [`Status`] to be [`Copy`], and thus other types
+    /// that contain it.
+    #[cfg_attr(feature = "serde", serde(other))]
+    #[cfg_attr(feature = "serde", serde(rename = "UNKNOWN"))]
+    Unknown,
+}
+
+impl From<String> for Status {
+    fn from(s: String) -> Self {
+        match s.as_str() {
+            CORE_RPC_STATUS_OK => Self::Ok,
+            CORE_RPC_STATUS_BUSY => Self::Busy,
+            CORE_RPC_STATUS_NOT_MINING => Self::NotMining,
+            CORE_RPC_STATUS_PAYMENT_REQUIRED => Self::PaymentRequired,
+            _ => Self::Unknown,
+        }
+    }
+}
+
+impl AsRef<str> for Status {
+    fn as_ref(&self) -> &str {
+        match self {
+            Self::Ok => CORE_RPC_STATUS_OK,
+            Self::Busy => CORE_RPC_STATUS_BUSY,
+            Self::NotMining => CORE_RPC_STATUS_NOT_MINING,
+            Self::PaymentRequired => CORE_RPC_STATUS_PAYMENT_REQUIRED,
+            Self::Unknown => CORE_RPC_STATUS_UNKNOWN,
+        }
+    }
+}
+
+impl Display for Status {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        f.write_str(self.as_ref())
+    }
+}
+
+// [`Status`] is essentially a [`String`] when it comes to
+// (de)serialization, except when writing we usually have
+// access to a `&'static str` and don't need to allocate.
+//
+// See below for more impl info:
+// <https://github.com/Cuprate/cuprate/blob/bef2a2cbd4e1194991751d1fbc96603cba8c7a51/net/epee-encoding/src/value.rs#L366-L392>.
+#[cfg(feature = "epee")]
+impl EpeeValue for Status {
+    const MARKER: Marker = <String as EpeeValue>::MARKER;
+
+    fn read<B: Buf>(r: &mut B, marker: &Marker) -> cuprate_epee_encoding::Result<Self> {
+        let string = <String as EpeeValue>::read(r, marker)?;
+        Ok(Self::from(string))
+    }
+
+    fn should_write(&self) -> bool {
+        true
+    }
+
+    fn epee_default_value() -> Option<Self> {
+        // <https://github.com/Cuprate/cuprate/pull/147#discussion_r1654992559>
+        Some(Self::Unknown)
+    }
+
+    fn write<B: BufMut>(self, w: &mut B) -> cuprate_epee_encoding::Result<()> {
+        cuprate_epee_encoding::write_bytes(self.as_ref(), w)
+    }
+}
+
 //---------------------------------------------------------------------------------------------------- Tests
 #[cfg(test)]
 mod test {
-    // use super::*;
+    use super::*;
+
+    // Test epee (de)serialization works.
+    #[test]
+    #[cfg(feature = "epee")]
+    fn epee() {
+        for status in [
+            Status::Ok,
+            Status::Busy,
+            Status::NotMining,
+            Status::PaymentRequired,
+            Status::Unknown,
+        ] {
+            let mut buf = vec![];
+
+            <Status as EpeeValue>::write(status, &mut buf).unwrap();
+            let status2 =
+                <Status as EpeeValue>::read(&mut buf.as_slice(), &<Status as EpeeValue>::MARKER)
+                    .unwrap();
+
+            assert_eq!(status, status2);
+        }
+    }
 }
diff --git a/rpc/types/src/other.rs b/rpc/types/src/other.rs
index ac55e1de..c8bc6208 100644
--- a/rpc/types/src/other.rs
+++ b/rpc/types/src/other.rs
@@ -1,11 +1,24 @@
 //! JSON types from the [`other`](https://www.getmonero.org/resources/developer-guides/daemon-rpc.html#other-daemon-rpc-calls) endpoints.
 //!
-//! <https://github.com/monero-project/monero/blob/cc73fe71162d564ffda8e549b79a350bca53c454/src/rpc/daemon_messages.h>.
+//! Most (if not all) of these types are defined here:
+//! - <https://github.com/monero-project/monero/blob/cc73fe71162d564ffda8e549b79a350bca53c454/src/rpc/core_rpc_server_commands_defs.h>
 
 //---------------------------------------------------------------------------------------------------- Import
 use crate::{base::ResponseBase, macros::define_request_and_response};
 
 //---------------------------------------------------------------------------------------------------- TODO
+define_request_and_response! {
+    get_height,
+    cc73fe71162d564ffda8e549b79a350bca53c454 =>
+    core_rpc_server_commands_defs.h => 138..=160,
+    GetHeight,
+    Request {},
+    ResponseBase {
+        hash: String,
+        height: u64,
+    }
+}
+
 define_request_and_response! {
     save_bc,
     cc73fe71162d564ffda8e549b79a350bca53c454 =>