cuprated: RPC handlers ()

* import diffs

* small fixes, hardfork changes

* lints

* hard_fork

* apply diffs

* review fixes

* binaries/cuprated/src/rpc/request: `pub(super)` -> `pub(crate)`

* add `BlockChainContextService`, `on_get_block_hash`

* map `tower::BoxError` to `anyhow::Error`

* get_block

* connection_info

* hard_fork_info

* set_bans

* get_bans

* banned

* flush_transaction_pool

* get_output_histogram

* get_coinbase_tx_sum

* get_version

* get_fee_estimate

* get_alternate_chains

* relay_tx

* response_base: `fn` -> `const`

* get_transaction_pool_backlog

* prune

* calc_pow

* add_aux_pow

* get_tx_ids_loose

* generate_blocks

* get_info

* sync_info

* get_miner_data

* `BlockchainManagerRequest` docs

* docs, `ConnectionInfo`, `AddressType`

* sig docs, remove `HardForks` request

* clean imports

* fix `on_get_block_hash`, `generate_blocks`, `get_block_headers_range`

* fix `get_info`, `banned`

* fix `sync_info`

* fix `get_miner_data`

* initial `add_aux_pow` impl

* fix `calculate_pow`

* add_aux_pow

* `get_output_distribution`

* checkup

* `find_nonce()` + `add_aux_pow` async wrapper

* fixes

* `helper::block_header`

* review fixes

* fixes

* doc fix

* p2p: remove tmp `AddressBookRequest::NextNeededPruningSeed`

* lint/todo fixes

* fix bans

* merge diffs from https://github.com/Cuprate/cuprate/pull/272

* `cuprate_types::rpc`, `from` module for `cuprate_rpc_types`

* `rpc-types` -> `types` pt. 2

* type fixes, move fn to `-helper`

* clippy fix

* rpc: move json-rpc types away from macros

* !!

* move types, fix orphan impl + cyclic dependency

* architecture book

* fix json-rpc handlers

* remove `::<N>`

* fix clippy

* fix type defaults, use `Hex`

* return defaults, hex test

* json_rpc: get_block_template

* `/get_transactions`

* `/is_key_image_spent`

* !!

* `/get_transactions` hex

* most of `/send_raw_transaction`

* `/send_raw_transaction`, `/save_bc`, response_base

* `/peerlist`

* `/get_transaction_pool`

* `/get_transaction_pool_stats`

* finish other draft

* get_blocks_by_height, shared::get_outs

* `/get_o_indexes.bin`

* `/get_output_distribution.bin`

* clippy

* `/get_blocks.bin`

* rpc-interface: add restricted invariant comments

* restricted json-rpc error

* get_output_distribution

* module cleanup

* txpool: all_hashes

* `HexVec`

* fix `get_txid` for `/get_outs`

miner transaction was not accounted for

* fix doc tests

* fix conflict

* json-rpc fixes

* `get_transaction_pool_hashes` fix

* rpc/interface: fix cargo hack

* review fixes

* cargo hack fix

* use `monero_address`

* Update binaries/cuprated/src/rpc/handlers/json_rpc.rs

Co-authored-by: Boog900 <boog900@tutanota.com>

* Update binaries/cuprated/src/rpc/handlers/json_rpc.rs

Co-authored-by: Boog900 <boog900@tutanota.com>

* review fixes

* fix `get_hashes`

* fix `is_key_image_spent`

* fix key image types

* fixes

* fix book

* output timelock fix + `blockchain_context()`

* fix

* fix

* fix

* fix getblocks.bin

* `cuprate_types` doc

* output fix

* fixme

* rct output fix

* fix cast

* clippy

---------

Co-authored-by: Boog900 <boog900@tutanota.com>
This commit is contained in:
hinto-janai 2025-04-08 12:09:43 -04:00 committed by GitHub
parent 8292da4e06
commit d3b7ca3e65
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
101 changed files with 8096 additions and 3538 deletions

25
Cargo.lock generated
View file

@ -766,6 +766,7 @@ version = "0.5.0"
dependencies = [
"bytes",
"cuprate-fixed-bytes",
"cuprate-hex",
"hex",
"paste",
"ref-cast",
@ -821,6 +822,15 @@ dependencies = [
"windows",
]
[[package]]
name = "cuprate-hex"
version = "0.0.0"
dependencies = [
"hex",
"serde",
"serde_json",
]
[[package]]
name = "cuprate-json-rpc"
version = "0.0.0"
@ -948,9 +958,15 @@ version = "0.0.0"
dependencies = [
"cuprate-epee-encoding",
"cuprate-fixed-bytes",
"cuprate-helper",
"cuprate-hex",
"cuprate-p2p-core",
"cuprate-test-utils",
"cuprate-types",
"hex",
"hex-literal",
"paste",
"pretty_assertions",
"serde",
"serde_json",
]
@ -1007,12 +1023,14 @@ dependencies = [
name = "cuprate-types"
version = "0.0.0"
dependencies = [
"bitflags 2.6.0",
"bytes",
"cfg-if",
"cuprate-epee-encoding",
"cuprate-fixed-bytes",
"cuprate-helper",
"cuprate-hex",
"curve25519-dalek",
"hex",
"hex-literal",
"indexmap",
"monero-serai",
@ -1045,7 +1063,7 @@ name = "cuprate-zmq-types"
version = "0.1.0"
dependencies = [
"assert-json-diff",
"cuprate-types",
"cuprate-hex",
"hex",
"serde",
"serde_json",
@ -1082,6 +1100,7 @@ dependencies = [
"cuprate-fast-sync",
"cuprate-fixed-bytes",
"cuprate-helper",
"cuprate-hex",
"cuprate-json-rpc",
"cuprate-levin",
"cuprate-p2p",
@ -1100,6 +1119,7 @@ dependencies = [
"hex",
"hex-literal",
"indexmap",
"monero-address",
"monero-serai",
"nu-ansi-term",
"paste",
@ -1111,6 +1131,7 @@ dependencies = [
"serde",
"serde_bytes",
"serde_json",
"strum",
"tempfile",
"thiserror",
"thread_local",

View file

@ -12,7 +12,6 @@ members = [
# Net
"net/epee-encoding",
"net/fixed-bytes",
"net/levin",
"net/wire",
@ -30,6 +29,11 @@ members = [
"storage/txpool",
"storage/database",
# Types
"types/types",
"types/hex",
"types/fixed-bytes",
# RPC
"rpc/json-rpc",
"rpc/types",
@ -44,7 +48,6 @@ members = [
"helper",
"pruning",
"test-utils",
"types",
]
[profile.release]
@ -77,14 +80,13 @@ cuprate-consensus-context = { path = "consensus/context", default-featur
cuprate-cryptonight = { path = "cryptonight", default-features = false }
cuprate-helper = { path = "helper", default-features = false }
cuprate-epee-encoding = { path = "net/epee-encoding", default-features = false }
cuprate-fixed-bytes = { path = "net/fixed-bytes", default-features = false }
cuprate-levin = { path = "net/levin", default-features = false }
cuprate-wire = { path = "net/wire", default-features = false }
cuprate-async-buffer = { path = "p2p/async-buffer", default-features = false }
cuprate-p2p = { path = "p2p/p2p", default-features = false }
cuprate-p2p-core = { path = "p2p/p2p-core", default-features = false }
cuprate-p2p-bucket = { path = "p2p/p2p-bucket", default-features = false }
cuprate-dandelion-tower = { path = "p2p/dandelion-tower", default-features = false }
cuprate-async-buffer = { path = "p2p/async-buffer", default-features = false }
cuprate-address-book = { path = "p2p/address-book", default-features = false }
cuprate-blockchain = { path = "storage/blockchain", default-features = false }
cuprate-database = { path = "storage/database", default-features = false }
@ -92,7 +94,9 @@ cuprate-database-service = { path = "storage/service", default-featur
cuprate-txpool = { path = "storage/txpool", default-features = false }
cuprate-pruning = { path = "pruning", default-features = false }
cuprate-test-utils = { path = "test-utils", default-features = false }
cuprate-types = { path = "types", default-features = false }
cuprate-types = { path = "types/types", default-features = false }
cuprate-hex = { path = "types/hex", default-features = false }
cuprate-fixed-bytes = { path = "types/fixed-bytes", default-features = false }
cuprate-json-rpc = { path = "rpc/json-rpc", default-features = false }
cuprate-rpc-types = { path = "rpc/types", default-features = false }
cuprate-rpc-interface = { path = "rpc/interface", default-features = false }
@ -121,6 +125,7 @@ futures = { version = "0.3", default-features = false }
hex = { version = "0.4", default-features = false }
hex-literal = { version = "0.4", default-features = false }
indexmap = { version = "2", default-features = false }
monero-address = { git = "https://github.com/Cuprate/serai.git", rev = "e6ae8c2", default-features = false }
monero-serai = { git = "https://github.com/Cuprate/serai.git", rev = "e6ae8c2", default-features = false }
nu-ansi-term = { version = "0.46", default-features = false }
paste = { version = "1", default-features = false }

View file

@ -9,32 +9,34 @@ repository = "https://github.com/Cuprate/cuprate/tree/main/binaries/cuprated"
[dependencies]
# TODO: after v1.0.0, remove unneeded dependencies.
cuprate-constants = { workspace = true, features = ["build"] }
cuprate-consensus = { workspace = true }
cuprate-fast-sync = { workspace = true }
cuprate-address-book = { workspace = true }
cuprate-async-buffer = { workspace = true }
cuprate-blockchain = { workspace = true }
cuprate-consensus-context = { workspace = true }
cuprate-consensus-rules = { workspace = true }
cuprate-consensus = { workspace = true }
cuprate-constants = { workspace = true, features = ["build", "rpc"] }
cuprate-cryptonight = { workspace = true }
cuprate-helper = { workspace = true, features = ["std", "serde", "time"] }
cuprate-epee-encoding = { workspace = true }
cuprate-fixed-bytes = { workspace = true }
cuprate-levin = { workspace = true }
cuprate-wire = { workspace = true }
cuprate-p2p = { workspace = true }
cuprate-p2p-core = { workspace = true }
cuprate-dandelion-tower = { workspace = true, features = ["txpool"] }
cuprate-async-buffer = { workspace = true }
cuprate-address-book = { workspace = true }
cuprate-blockchain = { workspace = true }
cuprate-database-service = { workspace = true, features = ["serde"] }
cuprate-txpool = { workspace = true }
cuprate-database = { workspace = true, features = ["serde"] }
cuprate-pruning = { workspace = true }
cuprate-test-utils = { workspace = true }
cuprate-types = { workspace = true }
cuprate-epee-encoding = { workspace = true }
cuprate-fast-sync = { workspace = true }
cuprate-fixed-bytes = { workspace = true }
cuprate-helper = { workspace = true, features = ["std", "serde", "time"] }
cuprate-hex = { workspace = true }
cuprate-json-rpc = { workspace = true }
cuprate-levin = { workspace = true }
cuprate-p2p-core = { workspace = true }
cuprate-p2p = { workspace = true }
cuprate-pruning = { workspace = true }
cuprate-rpc-interface = { workspace = true }
cuprate-rpc-types = { workspace = true }
cuprate-rpc-types = { workspace = true, features = ["from"] }
cuprate-test-utils = { workspace = true }
cuprate-txpool = { workspace = true }
cuprate-types = { workspace = true, features = ["json"] }
cuprate-wire = { workspace = true }
# TODO: after v1.0.0, remove unneeded dependencies.
anyhow = { workspace = true }
@ -56,6 +58,7 @@ futures = { workspace = true }
hex = { workspace = true }
hex-literal = { workspace = true }
indexmap = { workspace = true }
monero-address = { workspace = true }
monero-serai = { workspace = true }
nu-ansi-term = { workspace = true }
paste = { workspace = true }
@ -67,6 +70,7 @@ rayon = { workspace = true }
serde_bytes = { workspace = true }
serde_json = { workspace = true }
serde = { workspace = true }
strum = { workspace = true }
thiserror = { workspace = true }
thread_local = { workspace = true }
tokio-util = { workspace = true, features = ["rt"] }

View file

@ -2,11 +2,9 @@
//!
//! Will contain the code to initiate the RPC and a request handler.
mod bin;
mod constants;
mod handler;
mod json;
mod other;
mod request;
mod handlers;
mod rpc_handler;
mod service;
pub use handler::CupratedRpcHandler;
pub use rpc_handler::CupratedRpcHandler;

View file

@ -1,85 +0,0 @@
use anyhow::Error;
use cuprate_rpc_types::{
bin::{
BinRequest, BinResponse, GetBlocksByHeightRequest, GetBlocksByHeightResponse,
GetBlocksRequest, GetBlocksResponse, GetHashesRequest, GetHashesResponse,
GetOutputIndexesRequest, GetOutputIndexesResponse, GetOutsRequest, GetOutsResponse,
GetTransactionPoolHashesRequest, GetTransactionPoolHashesResponse,
},
json::{GetOutputDistributionRequest, GetOutputDistributionResponse},
};
use crate::rpc::CupratedRpcHandler;
/// Map a [`BinRequest`] to the function that will lead to a [`BinResponse`].
pub(super) async fn map_request(
state: CupratedRpcHandler,
request: BinRequest,
) -> Result<BinResponse, Error> {
use BinRequest as Req;
use BinResponse as Resp;
Ok(match request {
Req::GetBlocks(r) => Resp::GetBlocks(get_blocks(state, r).await?),
Req::GetBlocksByHeight(r) => Resp::GetBlocksByHeight(get_blocks_by_height(state, r).await?),
Req::GetHashes(r) => Resp::GetHashes(get_hashes(state, r).await?),
Req::GetOutputIndexes(r) => Resp::GetOutputIndexes(get_output_indexes(state, r).await?),
Req::GetOuts(r) => Resp::GetOuts(get_outs(state, r).await?),
Req::GetTransactionPoolHashes(r) => {
Resp::GetTransactionPoolHashes(get_transaction_pool_hashes(state, r).await?)
}
Req::GetOutputDistribution(r) => {
Resp::GetOutputDistribution(get_output_distribution(state, r).await?)
}
})
}
async fn get_blocks(
state: CupratedRpcHandler,
request: GetBlocksRequest,
) -> Result<GetBlocksResponse, Error> {
todo!()
}
async fn get_blocks_by_height(
state: CupratedRpcHandler,
request: GetBlocksByHeightRequest,
) -> Result<GetBlocksByHeightResponse, Error> {
todo!()
}
async fn get_hashes(
state: CupratedRpcHandler,
request: GetHashesRequest,
) -> Result<GetHashesResponse, Error> {
todo!()
}
async fn get_output_indexes(
state: CupratedRpcHandler,
request: GetOutputIndexesRequest,
) -> Result<GetOutputIndexesResponse, Error> {
todo!()
}
async fn get_outs(
state: CupratedRpcHandler,
request: GetOutsRequest,
) -> Result<GetOutsResponse, Error> {
todo!()
}
async fn get_transaction_pool_hashes(
state: CupratedRpcHandler,
request: GetTransactionPoolHashesRequest,
) -> Result<GetTransactionPoolHashesResponse, Error> {
todo!()
}
async fn get_output_distribution(
state: CupratedRpcHandler,
request: GetOutputDistributionRequest,
) -> Result<GetOutputDistributionResponse, Error> {
todo!()
}

View file

@ -3,3 +3,6 @@
/// The string message used in RPC response fields for when
/// `cuprated` does not support a field that `monerod` has.
pub(super) const FIELD_NOT_SUPPORTED: &str = "`cuprated` does not support this field.";
/// The error message returned when an unsupported RPC call is requested.
pub(super) const UNSUPPORTED_RPC_CALL: &str = "This RPC call is not supported by Cuprate.";

View file

@ -0,0 +1,18 @@
//! RPC handler functions.
//!
//! These are the glue (async) functions that connect all the
//! internal `cuprated` functions and fulfill the request.
//!
//! - JSON-RPC handlers are in [`json_rpc`]
//! - Other JSON endpoint handlers are in [`other_json`]
//! - Other binary endpoint handlers are in [`bin`]
//!
//! - [`helper`] contains helper functions used by many handlers
//! - [`shared`] contains shared functions used by multiple handlers
pub(super) mod bin;
pub(super) mod json_rpc;
pub(super) mod other_json;
mod helper;
mod shared;

View file

@ -0,0 +1,253 @@
//! RPC request handler functions (binary endpoints).
//!
//! TODO:
//! Some handlers have `todo!()`s for other Cuprate internals that must be completed, see:
//! <https://github.com/Cuprate/cuprate/pull/355>
use std::num::NonZero;
use anyhow::{anyhow, Error};
use bytes::Bytes;
use cuprate_constants::rpc::{RESTRICTED_BLOCK_COUNT, RESTRICTED_TRANSACTIONS_COUNT};
use cuprate_fixed_bytes::ByteArrayVec;
use cuprate_helper::cast::{u64_to_usize, usize_to_u64};
use cuprate_rpc_interface::RpcHandler;
use cuprate_rpc_types::{
base::{AccessResponseBase, ResponseBase},
bin::{
BinRequest, BinResponse, GetBlocksByHeightRequest, GetBlocksByHeightResponse,
GetBlocksRequest, GetBlocksResponse, GetHashesRequest, GetHashesResponse,
GetOutputIndexesRequest, GetOutputIndexesResponse, GetOutsRequest, GetOutsResponse,
GetTransactionPoolHashesRequest, GetTransactionPoolHashesResponse,
},
json::{GetOutputDistributionRequest, GetOutputDistributionResponse},
misc::RequestedInfo,
};
use cuprate_types::{
rpc::{PoolInfo, PoolInfoExtent},
BlockCompleteEntry,
};
use crate::rpc::{
handlers::{helper, shared},
service::{blockchain, txpool},
CupratedRpcHandler,
};
/// Map a [`BinRequest`] to the function that will lead to a [`BinResponse`].
pub async fn map_request(
state: CupratedRpcHandler,
request: BinRequest,
) -> Result<BinResponse, Error> {
use BinRequest as Req;
use BinResponse as Resp;
Ok(match request {
Req::GetBlocks(r) => Resp::GetBlocks(get_blocks(state, r).await?),
Req::GetBlocksByHeight(r) => Resp::GetBlocksByHeight(get_blocks_by_height(state, r).await?),
Req::GetHashes(r) => Resp::GetHashes(get_hashes(state, r).await?),
Req::GetOutputIndexes(r) => Resp::GetOutputIndexes(get_output_indexes(state, r).await?),
Req::GetOuts(r) => Resp::GetOuts(get_outs(state, r).await?),
Req::GetTransactionPoolHashes(r) => {
Resp::GetTransactionPoolHashes(get_transaction_pool_hashes(state, r).await?)
}
Req::GetOutputDistribution(r) => {
Resp::GetOutputDistribution(get_output_distribution(state, r).await?)
}
})
}
/// <https://github.com/monero-project/monero/blob/cc73fe71162d564ffda8e549b79a350bca53c454/src/rpc/core_rpc_server.cpp#L611-L789>
async fn get_blocks(
mut state: CupratedRpcHandler,
request: GetBlocksRequest,
) -> Result<GetBlocksResponse, Error> {
// Time should be set early:
// <https://github.com/monero-project/monero/blob/cc73fe71162d564ffda8e549b79a350bca53c454/src/rpc/core_rpc_server.cpp#L628-L631>
let daemon_time = cuprate_helper::time::current_unix_timestamp();
let GetBlocksRequest {
requested_info,
block_ids,
start_height,
prune,
no_miner_tx,
pool_info_since,
} = request;
let block_hashes: Vec<[u8; 32]> = (&block_ids).into();
drop(block_ids);
let Some(requested_info) = RequestedInfo::from_u8(request.requested_info) else {
return Err(anyhow!("Wrong requested info"));
};
let (get_blocks, get_pool) = match requested_info {
RequestedInfo::BlocksOnly => (true, false),
RequestedInfo::BlocksAndPool => (true, true),
RequestedInfo::PoolOnly => (false, true),
};
let pool_info_extent = PoolInfoExtent::None;
let pool_info = if get_pool {
let is_restricted = state.is_restricted();
let include_sensitive_txs = !is_restricted;
let max_tx_count = if is_restricted {
RESTRICTED_TRANSACTIONS_COUNT
} else {
usize::MAX
};
txpool::pool_info(
&mut state.txpool_read,
include_sensitive_txs,
max_tx_count,
NonZero::new(u64_to_usize(request.pool_info_since)),
)
.await?
} else {
PoolInfo::None
};
let resp = GetBlocksResponse {
base: helper::access_response_base(false),
blocks: vec![],
start_height: 0,
current_height: 0,
output_indices: vec![],
daemon_time,
pool_info,
};
if !get_blocks {
return Ok(resp);
}
if let Some(block_id) = block_hashes.first() {
let (height, hash) = helper::top_height(&mut state).await?;
if hash == *block_id {
return Ok(GetBlocksResponse {
current_height: height + 1,
..resp
});
}
}
let (block_hashes, start_height, _) =
blockchain::next_chain_entry(&mut state.blockchain_read, block_hashes, start_height)
.await?;
if start_height.is_none() {
return Err(anyhow!("Block IDs were not sorted properly"));
}
let (blocks, missing_hashes, height) =
blockchain::block_complete_entries(&mut state.blockchain_read, block_hashes).await?;
if !missing_hashes.is_empty() {
return Err(anyhow!("Missing blocks"));
}
Ok(GetBlocksResponse {
blocks,
current_height: usize_to_u64(height),
..resp
})
}
/// <https://github.com/monero-project/monero/blob/cc73fe71162d564ffda8e549b79a350bca53c454/src/rpc/core_rpc_server.cpp#L817-L857>
async fn get_blocks_by_height(
mut state: CupratedRpcHandler,
request: GetBlocksByHeightRequest,
) -> Result<GetBlocksByHeightResponse, Error> {
if state.is_restricted() && request.heights.len() > RESTRICTED_BLOCK_COUNT {
return Err(anyhow!("Too many blocks requested in restricted mode"));
}
let blocks =
blockchain::block_complete_entries_by_height(&mut state.blockchain_read, request.heights)
.await?;
Ok(GetBlocksByHeightResponse {
base: helper::access_response_base(false),
blocks,
})
}
/// <https://github.com/monero-project/monero/blob/cc73fe71162d564ffda8e549b79a350bca53c454/src/rpc/core_rpc_server.cpp#L859-L880>
async fn get_hashes(
mut state: CupratedRpcHandler,
request: GetHashesRequest,
) -> Result<GetHashesResponse, Error> {
let GetHashesRequest {
start_height,
block_ids,
} = request;
// FIXME: impl `last()`
let last = {
let len = block_ids.len();
if len == 0 {
return Err(anyhow!("block_ids empty"));
}
block_ids[len - 1]
};
let hashes: Vec<[u8; 32]> = (&block_ids).into();
let (m_blocks_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(),
current_height: usize_to_u64(current_height),
start_height,
})
}
/// <https://github.com/monero-project/monero/blob/cc73fe71162d564ffda8e549b79a350bca53c454/src/rpc/core_rpc_server.cpp#L959-L977>
async fn get_output_indexes(
mut state: CupratedRpcHandler,
request: GetOutputIndexesRequest,
) -> Result<GetOutputIndexesResponse, Error> {
Ok(GetOutputIndexesResponse {
base: helper::access_response_base(false),
o_indexes: blockchain::tx_output_indexes(&mut state.blockchain_read, request.txid).await?,
})
}
/// <https://github.com/monero-project/monero/blob/cc73fe71162d564ffda8e549b79a350bca53c454/src/rpc/core_rpc_server.cpp#L882-L910>
async fn get_outs(
state: CupratedRpcHandler,
request: GetOutsRequest,
) -> Result<GetOutsResponse, Error> {
shared::get_outs(state, request).await
}
/// <https://github.com/monero-project/monero/blob/cc73fe71162d564ffda8e549b79a350bca53c454/src/rpc/core_rpc_server.cpp#L1689-L1711>
async fn get_transaction_pool_hashes(
mut state: CupratedRpcHandler,
_: GetTransactionPoolHashesRequest,
) -> Result<GetTransactionPoolHashesResponse, Error> {
Ok(GetTransactionPoolHashesResponse {
base: helper::access_response_base(false),
tx_hashes: shared::get_transaction_pool_hashes(state)
.await
.map(ByteArrayVec::from)?,
})
}
/// <https://github.com/monero-project/monero/blob/cc73fe71162d564ffda8e549b79a350bca53c454/src/rpc/core_rpc_server.cpp#L3352-L3398>
async fn get_output_distribution(
state: CupratedRpcHandler,
request: GetOutputDistributionRequest,
) -> Result<GetOutputDistributionResponse, Error> {
shared::get_output_distribution(state, request).await
}

View file

@ -0,0 +1,191 @@
//! These are internal helper functions used by the actual RPC handlers.
//!
//! Many of the handlers have bodies with only small differences,
//! the identical code is extracted and reused here in these functions.
//!
//! These build on-top of [`crate::rpc::service`] functions.
use anyhow::{anyhow, Error};
use cuprate_helper::{
cast::{u64_to_usize, usize_to_u64},
map::split_u128_into_low_high_bits,
};
use cuprate_rpc_types::{
base::{AccessResponseBase, ResponseBase},
misc::BlockHeader,
};
use cuprate_types::HardFork;
use monero_serai::transaction::Timelock;
use crate::rpc::{
service::{blockchain, blockchain_context},
CupratedRpcHandler,
};
/// Map some data into a [`BlockHeader`].
///
/// Sort of equivalent to:
/// <https://github.com/monero-project/monero/blob/893916ad091a92e765ce3241b94e706ad012b62a/src/rpc/core_rpc_server.cpp#L2361>.
pub(super) async fn block_header(
state: &mut CupratedRpcHandler,
height: u64,
fill_pow_hash: bool,
) -> Result<BlockHeader, Error> {
let block = blockchain::block(&mut state.blockchain_read, height).await?;
let header = blockchain::block_extended_header(&mut state.blockchain_read, height).await?;
let hardfork = HardFork::from_vote(header.vote);
let (top_height, _) = top_height(state).await?;
// TODO: if the request block is not on the main chain,
// we must get the alt block and this variable will be `true`.
let orphan_status = false;
// TODO: impl cheaper way to get this.
// <https://github.com/Cuprate/cuprate/pull/355#discussion_r1904508934>
let difficulty = blockchain_context::batch_get_difficulties(
&mut state.blockchain_context,
vec![(height, hardfork)],
)
.await?
.first()
.copied()
.ok_or_else(|| anyhow!("Failed to get block difficulty"))?;
let pow_hash = if fill_pow_hash {
let seed_height =
cuprate_consensus_rules::blocks::randomx_seed_height(u64_to_usize(height));
let seed_hash = blockchain::block_hash(
&mut state.blockchain_read,
height,
todo!("access to `cuprated`'s Chain"),
)
.await?;
Some(
blockchain_context::calculate_pow(
&mut state.blockchain_context,
hardfork,
block,
seed_hash,
)
.await?,
)
} else {
None
};
let block_weight = usize_to_u64(header.block_weight);
let depth = top_height.saturating_sub(height);
let (cumulative_difficulty_top64, cumulative_difficulty) =
split_u128_into_low_high_bits(header.cumulative_difficulty);
let (difficulty_top64, difficulty) = split_u128_into_low_high_bits(difficulty);
let reward = block
.miner_transaction
.prefix()
.outputs
.iter()
.map(|o| o.amount.expect("coinbase is transparent"))
.sum::<u64>();
Ok(cuprate_types::rpc::BlockHeader {
block_weight,
cumulative_difficulty_top64,
cumulative_difficulty,
depth,
difficulty_top64,
difficulty,
hash: block.hash(),
height,
long_term_weight: usize_to_u64(header.long_term_weight),
major_version: header.version,
miner_tx_hash: block.miner_transaction.hash(),
minor_version: header.vote,
nonce: block.header.nonce,
num_txes: usize_to_u64(block.transactions.len()),
orphan_status,
pow_hash,
prev_hash: block.header.previous,
reward,
timestamp: block.header.timestamp,
}
.into())
}
/// Same as [`block_header`] but with the block's hash.
pub(super) async fn block_header_by_hash(
state: &mut CupratedRpcHandler,
hash: [u8; 32],
fill_pow_hash: bool,
) -> Result<BlockHeader, Error> {
let (_, height) = blockchain::find_block(&mut state.blockchain_read, hash)
.await?
.ok_or_else(|| anyhow!("Block did not exist."))?;
let block_header = block_header(state, usize_to_u64(height), fill_pow_hash).await?;
Ok(block_header)
}
/// Check if `height` is greater than the [`top_height`].
///
/// # Errors
/// This returns the [`top_height`] on [`Ok`] and
/// returns [`Error`] if `height` is greater than [`top_height`].
pub(super) async fn check_height(
state: &mut CupratedRpcHandler,
height: u64,
) -> Result<u64, Error> {
let (top_height, _) = top_height(state).await?;
if height > top_height {
return Err(anyhow!(
"Requested block height: {height} greater than top block height: {top_height}",
));
}
Ok(top_height)
}
/// Parse a hexadecimal [`String`] as a 32-byte hash.
#[expect(clippy::needless_pass_by_value)]
pub(super) fn hex_to_hash(hex: String) -> Result<[u8; 32], Error> {
let error = || anyhow!("Failed to parse hex representation of hash. Hex = {hex}.");
let Ok(bytes) = hex::decode(&hex) else {
return Err(error());
};
let Ok(hash) = bytes.try_into() else {
return Err(error());
};
Ok(hash)
}
/// [`cuprate_types::blockchain::BlockchainResponse::ChainHeight`] minus 1.
pub(super) async fn top_height(state: &mut CupratedRpcHandler) -> Result<(u64, [u8; 32]), Error> {
let (chain_height, hash) = blockchain::chain_height(&mut state.blockchain_read).await?;
let height = chain_height.checked_sub(1).unwrap();
Ok((height, hash))
}
/// TODO: impl bootstrap
pub const fn response_base(is_bootstrap: bool) -> ResponseBase {
if is_bootstrap {
ResponseBase::OK_UNTRUSTED
} else {
ResponseBase::OK
}
}
/// TODO: impl bootstrap
pub const fn access_response_base(is_bootstrap: bool) -> AccessResponseBase {
if is_bootstrap {
AccessResponseBase::OK_UNTRUSTED
} else {
AccessResponseBase::OK
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,790 @@
//! RPC request handler functions (other JSON endpoints).
//!
//! TODO:
//! Some handlers have `todo!()`s for other Cuprate internals that must be completed, see:
//! <https://github.com/Cuprate/cuprate/pull/355>
use std::{
borrow::Cow,
collections::{BTreeSet, HashMap, HashSet},
};
use anyhow::{anyhow, Error};
use monero_serai::transaction::{Input, Timelock, Transaction};
use cuprate_constants::rpc::{
MAX_RESTRICTED_GLOBAL_FAKE_OUTS_COUNT, RESTRICTED_SPENT_KEY_IMAGES_COUNT,
RESTRICTED_TRANSACTIONS_COUNT,
};
use cuprate_helper::cast::usize_to_u64;
use cuprate_hex::{Hex, HexVec};
use cuprate_p2p_core::{client::handshaker::builder::DummyAddressBook, ClearNet};
use cuprate_rpc_interface::RpcHandler;
use cuprate_rpc_types::{
base::{AccessResponseBase, ResponseBase},
misc::{Status, TxEntry, TxEntryType},
other::{
GetAltBlocksHashesRequest, GetAltBlocksHashesResponse, GetHeightRequest, GetHeightResponse,
GetLimitRequest, GetLimitResponse, GetNetStatsRequest, GetNetStatsResponse, GetOutsRequest,
GetOutsResponse, GetPeerListRequest, GetPeerListResponse, GetPublicNodesRequest,
GetPublicNodesResponse, GetTransactionPoolHashesRequest, GetTransactionPoolHashesResponse,
GetTransactionPoolRequest, GetTransactionPoolResponse, GetTransactionPoolStatsRequest,
GetTransactionPoolStatsResponse, GetTransactionsRequest, GetTransactionsResponse,
InPeersRequest, InPeersResponse, IsKeyImageSpentRequest, IsKeyImageSpentResponse,
MiningStatusRequest, MiningStatusResponse, OtherRequest, OtherResponse, OutPeersRequest,
OutPeersResponse, PopBlocksRequest, PopBlocksResponse, SaveBcRequest, SaveBcResponse,
SendRawTransactionRequest, SendRawTransactionResponse, SetBootstrapDaemonRequest,
SetBootstrapDaemonResponse, SetLimitRequest, SetLimitResponse, SetLogCategoriesRequest,
SetLogCategoriesResponse, SetLogHashRateRequest, SetLogHashRateResponse,
SetLogLevelRequest, SetLogLevelResponse, StartMiningRequest, StartMiningResponse,
StopDaemonRequest, StopDaemonResponse, StopMiningRequest, StopMiningResponse,
UpdateRequest, UpdateResponse,
},
};
use cuprate_types::{
rpc::{KeyImageSpentStatus, PoolInfo, PoolTxInfo, PublicNode},
TxInPool, TxRelayChecks,
};
use crate::{
rpc::{
constants::UNSUPPORTED_RPC_CALL,
handlers::{helper, shared},
service::{address_book, blockchain, blockchain_context, blockchain_manager, txpool},
CupratedRpcHandler,
},
statics::START_INSTANT_UNIX,
};
/// Map a [`OtherRequest`] to the function that will lead to a [`OtherResponse`].
pub async fn map_request(
state: CupratedRpcHandler,
request: OtherRequest,
) -> Result<OtherResponse, Error> {
use OtherRequest as Req;
use OtherResponse as Resp;
Ok(match request {
Req::GetHeight(r) => Resp::GetHeight(get_height(state, r).await?),
Req::GetTransactions(r) => Resp::GetTransactions(get_transactions(state, r).await?),
Req::GetAltBlocksHashes(r) => {
Resp::GetAltBlocksHashes(get_alt_blocks_hashes(state, r).await?)
}
Req::IsKeyImageSpent(r) => Resp::IsKeyImageSpent(is_key_image_spent(state, r).await?),
Req::SendRawTransaction(r) => {
Resp::SendRawTransaction(send_raw_transaction(state, r).await?)
}
Req::SaveBc(r) => Resp::SaveBc(save_bc(state, r).await?),
Req::GetPeerList(r) => Resp::GetPeerList(get_peer_list(state, r).await?),
Req::SetLogLevel(r) => Resp::SetLogLevel(set_log_level(state, r).await?),
Req::SetLogCategories(r) => Resp::SetLogCategories(set_log_categories(state, r).await?),
Req::GetTransactionPool(r) => {
Resp::GetTransactionPool(get_transaction_pool(state, r).await?)
}
Req::GetTransactionPoolStats(r) => {
Resp::GetTransactionPoolStats(get_transaction_pool_stats(state, r).await?)
}
Req::StopDaemon(r) => Resp::StopDaemon(stop_daemon(state, r).await?),
Req::GetLimit(r) => Resp::GetLimit(get_limit(state, r).await?),
Req::SetLimit(r) => Resp::SetLimit(set_limit(state, r).await?),
Req::OutPeers(r) => Resp::OutPeers(out_peers(state, r).await?),
Req::InPeers(r) => Resp::InPeers(in_peers(state, r).await?),
Req::GetNetStats(r) => Resp::GetNetStats(get_net_stats(state, r).await?),
Req::GetOuts(r) => Resp::GetOuts(get_outs(state, r).await?),
Req::PopBlocks(r) => Resp::PopBlocks(pop_blocks(state, r).await?),
Req::GetTransactionPoolHashes(r) => {
Resp::GetTransactionPoolHashes(get_transaction_pool_hashes(state, r).await?)
}
Req::GetPublicNodes(r) => Resp::GetPublicNodes(get_public_nodes(state, r).await?),
// Unsupported requests.
Req::SetBootstrapDaemon(_)
| Req::Update(_)
| Req::StartMining(_)
| Req::StopMining(_)
| Req::MiningStatus(_)
| Req::SetLogHashRate(_) => return Err(anyhow!(UNSUPPORTED_RPC_CALL)),
})
}
/// <https://github.com/monero-project/monero/blob/cc73fe71162d564ffda8e549b79a350bca53c454/src/rpc/core_rpc_server.cpp#L486-L499>
async fn get_height(
mut state: CupratedRpcHandler,
_: GetHeightRequest,
) -> Result<GetHeightResponse, Error> {
let (height, hash) = helper::top_height(&mut state).await?;
let hash = Hex(hash);
Ok(GetHeightResponse {
base: helper::response_base(false),
height,
hash,
})
}
/// <https://github.com/monero-project/monero/blob/cc73fe71162d564ffda8e549b79a350bca53c454/src/rpc/core_rpc_server.cpp#L979-L1227>
async fn get_transactions(
mut state: CupratedRpcHandler,
request: GetTransactionsRequest,
) -> Result<GetTransactionsResponse, Error> {
if state.is_restricted() && request.txs_hashes.len() > RESTRICTED_TRANSACTIONS_COUNT {
return Err(anyhow!(
"Too many transactions requested in restricted mode"
));
}
let (txs_in_blockchain, missed_txs) = {
let requested_txs = request.txs_hashes.into_iter().map(|tx| tx.0).collect();
blockchain::transactions(&mut state.blockchain_read, requested_txs).await?
};
let missed_tx = missed_txs.clone().into_iter().map(Hex).collect();
// Check the txpool for missed transactions.
let txs_in_pool = if missed_txs.is_empty() {
vec![]
} else {
let include_sensitive_txs = !state.is_restricted();
txpool::txs_by_hash(&mut state.txpool_read, missed_txs, include_sensitive_txs).await?
};
let (txs, txs_as_hex, txs_as_json) = {
// Prepare the final JSON output.
let len = txs_in_blockchain.len() + txs_in_pool.len();
let mut txs = Vec::with_capacity(len);
let mut txs_as_hex = Vec::with_capacity(len);
let mut txs_as_json = Vec::with_capacity(if request.decode_as_json { len } else { 0 });
// Map all blockchain transactions.
for tx in txs_in_blockchain {
let tx_hash = Hex(tx.tx_hash);
let prunable_hash = Hex(tx.prunable_hash);
let (pruned_as_hex, prunable_as_hex) = if tx.pruned_blob.is_empty() {
(HexVec::new(), HexVec::new())
} else {
(HexVec(tx.pruned_blob), HexVec(tx.prunable_blob))
};
let as_hex = if pruned_as_hex.is_empty() {
// `monerod` will insert a `""` into the `txs_as_hex` array for pruned transactions.
// curl http://127.0.0.1:18081/get_transactions -d '{"txs_hashes":["4c8b98753d1577d225a497a50f453827cff3aa023a4add60ec4ce4f923f75de8"]}' -H 'Content-Type: application/json'
HexVec::new()
} else {
HexVec(tx.tx_blob)
};
txs_as_hex.push(as_hex.clone());
let as_json = if request.decode_as_json {
let tx = Transaction::read(&mut as_hex.as_slice())?;
let json_type = cuprate_types::json::tx::Transaction::from(tx);
let json = serde_json::to_string(&json_type).unwrap();
txs_as_json.push(json.clone());
json
} else {
String::new()
};
let tx_entry_type = TxEntryType::Blockchain {
block_height: tx.block_height,
block_timestamp: tx.block_timestamp,
confirmations: tx.confirmations,
output_indices: tx.output_indices,
in_pool: false,
};
let tx = TxEntry {
as_hex,
as_json,
double_spend_seen: false,
tx_hash,
prunable_as_hex,
prunable_hash,
pruned_as_hex,
tx_entry_type,
};
txs.push(tx);
}
// Map all txpool transactions.
for tx_in_pool in txs_in_pool {
let TxInPool {
tx_blob,
tx_hash,
double_spend_seen,
received_timestamp,
relayed,
} = tx_in_pool;
let tx_hash = Hex(tx_hash);
let tx = Transaction::read(&mut tx_blob.as_slice())?;
let pruned_as_hex = HexVec::new();
let prunable_as_hex = HexVec::new();
let prunable_hash = Hex([0; 32]);
let as_hex = HexVec(tx_blob);
txs_as_hex.push(as_hex.clone());
let as_json = if request.decode_as_json {
let json_type = cuprate_types::json::tx::Transaction::from(tx);
let json = serde_json::to_string(&json_type).unwrap();
txs_as_json.push(json.clone());
json
} else {
String::new()
};
let tx_entry_type = TxEntryType::Pool {
relayed,
received_timestamp,
in_pool: true,
};
let tx = TxEntry {
as_hex,
as_json,
double_spend_seen,
tx_hash,
prunable_as_hex,
prunable_hash,
pruned_as_hex,
tx_entry_type,
};
txs.push(tx);
}
(txs, txs_as_hex, txs_as_json)
};
Ok(GetTransactionsResponse {
base: helper::access_response_base(false),
txs_as_hex,
txs_as_json,
missed_tx,
txs,
})
}
/// <https://github.com/monero-project/monero/blob/cc73fe71162d564ffda8e549b79a350bca53c454/src/rpc/core_rpc_server.cpp#L790-L815>
async fn get_alt_blocks_hashes(
mut state: CupratedRpcHandler,
_: GetAltBlocksHashesRequest,
) -> Result<GetAltBlocksHashesResponse, Error> {
let blks_hashes = blockchain::alt_chains(&mut state.blockchain_read)
.await?
.into_iter()
.map(|info| Hex(info.block_hash))
.collect();
Ok(GetAltBlocksHashesResponse {
base: helper::access_response_base(false),
blks_hashes,
})
}
/// <https://github.com/monero-project/monero/blob/cc73fe71162d564ffda8e549b79a350bca53c454/src/rpc/core_rpc_server.cpp#L1229-L1305>
async fn is_key_image_spent(
mut state: CupratedRpcHandler,
request: IsKeyImageSpentRequest,
) -> Result<IsKeyImageSpentResponse, Error> {
let restricted = state.is_restricted();
if restricted && request.key_images.len() > RESTRICTED_SPENT_KEY_IMAGES_COUNT {
return Err(anyhow!("Too many key images queried in restricted mode"));
}
let key_images = request
.key_images
.into_iter()
.map(|k| k.0)
.collect::<Vec<[u8; 32]>>();
let mut spent_status = Vec::with_capacity(key_images.len());
// Check the blockchain for key image spend status.
blockchain::key_images_spent_vec(&mut state.blockchain_read, key_images.clone())
.await?
.into_iter()
.for_each(|ki| {
if ki {
spent_status.push(KeyImageSpentStatus::SpentInBlockchain);
} else {
spent_status.push(KeyImageSpentStatus::Unspent);
}
});
assert_eq!(spent_status.len(), key_images.len(), "key_images_spent() should be returning a Vec with an equal length to the input, the below zip() relies on this.");
// Filter the remaining unspent key images out from the vector.
let key_images = key_images
.into_iter()
.zip(&spent_status)
.filter_map(|(ki, status)| match status {
KeyImageSpentStatus::Unspent => Some(ki),
KeyImageSpentStatus::SpentInBlockchain => None,
KeyImageSpentStatus::SpentInPool => unreachable!(),
})
.collect::<Vec<[u8; 32]>>();
// Check if the remaining unspent key images exist in the transaction pool.
if !key_images.is_empty() {
txpool::key_images_spent_vec(&mut state.txpool_read, key_images, !restricted)
.await?
.into_iter()
.for_each(|ki| {
if ki {
spent_status.push(KeyImageSpentStatus::SpentInPool);
} else {
spent_status.push(KeyImageSpentStatus::Unspent);
}
});
}
let spent_status = spent_status
.into_iter()
.map(KeyImageSpentStatus::to_u8)
.collect();
Ok(IsKeyImageSpentResponse {
base: helper::access_response_base(false),
spent_status,
})
}
/// <https://github.com/monero-project/monero/blob/cc73fe71162d564ffda8e549b79a350bca53c454/src/rpc/core_rpc_server.cpp#L1307-L1411>
async fn send_raw_transaction(
mut state: CupratedRpcHandler,
request: SendRawTransactionRequest,
) -> Result<SendRawTransactionResponse, Error> {
let mut resp = SendRawTransactionResponse {
base: helper::access_response_base(false),
double_spend: false,
fee_too_low: false,
invalid_input: false,
invalid_output: false,
low_mixin: false,
nonzero_unlock_time: false,
not_relayed: request.do_not_relay,
overspend: false,
reason: String::new(),
sanity_check_failed: false,
too_big: false,
too_few_outputs: false,
tx_extra_too_big: false,
};
let tx = Transaction::read(&mut request.tx_as_hex.as_slice())?;
if request.do_sanity_checks {
/// FIXME: these checks could be defined elsewhere.
///
/// <https://github.com/monero-project/monero/blob/893916ad091a92e765ce3241b94e706ad012b62a/src/cryptonote_core/tx_sanity_check.cpp#L42>
fn tx_sanity_check(tx: &Transaction, rct_outs_available: u64) -> Result<(), String> {
let Some(input) = tx.prefix().inputs.first() else {
return Err("No inputs".to_string());
};
let mut rct_indices = vec![];
let mut n_indices: usize = 0;
for input in &tx.prefix().inputs {
match input {
Input::Gen(_) => return Err("Transaction is coinbase".to_string()),
Input::ToKey {
amount,
key_offsets,
key_image,
} => {
let Some(amount) = amount else {
continue;
};
/// <https://github.com/monero-project/monero/blob/893916ad091a92e765ce3241b94e706ad012b62a/src/cryptonote_basic/cryptonote_format_utils.cpp#L1526>
fn relative_output_offsets_to_absolute(mut offsets: Vec<u64>) -> Vec<u64> {
assert!(!offsets.is_empty());
for i in 1..offsets.len() {
offsets[i] += offsets[i - 1];
}
offsets
}
n_indices += key_offsets.len();
let absolute = relative_output_offsets_to_absolute(key_offsets.clone());
rct_indices.extend(absolute);
}
}
}
if n_indices <= 10 {
return Ok(());
}
if rct_outs_available < 10_000 {
return Ok(());
}
let rct_indices_len = rct_indices.len();
if rct_indices_len < n_indices * 8 / 10 {
return Err(format!("amount of unique indices is too low (amount of rct indices is {rct_indices_len} out of total {n_indices} indices."));
}
let median = cuprate_helper::num::median(rct_indices);
if median < rct_outs_available * 6 / 10 {
return Err(format!("median offset index is too low (median is {median} out of total {rct_outs_available} offsets). Transactions should contain a higher fraction of recent outputs."));
}
Ok(())
}
let rct_outs_available = blockchain::total_rct_outputs(&mut state.blockchain_read).await?;
if let Err(e) = tx_sanity_check(&tx, rct_outs_available) {
resp.base.response_base.status = Status::Failed;
resp.reason.push_str(&format!("Sanity check failed: {e}"));
resp.sanity_check_failed = true;
return Ok(resp);
}
}
let tx_relay_checks =
txpool::check_maybe_relay_local(&mut state.txpool_manager, tx, !request.do_not_relay)
.await?;
if tx_relay_checks.is_empty() {
return Ok(resp);
}
// <https://github.com/monero-project/monero/blob/cc73fe71162d564ffda8e549b79a350bca53c454/src/rpc/core_rpc_server.cpp#L124>
fn add_reason(reasons: &mut String, reason: &'static str) {
if !reasons.is_empty() {
reasons.push_str(", ");
}
reasons.push_str(reason);
}
let mut reasons = String::new();
#[rustfmt::skip]
let array = [
(&mut resp.double_spend, TxRelayChecks::DOUBLE_SPEND, "double spend"),
(&mut resp.fee_too_low, TxRelayChecks::FEE_TOO_LOW, "fee too low"),
(&mut resp.invalid_input, TxRelayChecks::INVALID_INPUT, "invalid input"),
(&mut resp.invalid_output, TxRelayChecks::INVALID_OUTPUT, "invalid output"),
(&mut resp.low_mixin, TxRelayChecks::LOW_MIXIN, "bad ring size"),
(&mut resp.nonzero_unlock_time, TxRelayChecks::NONZERO_UNLOCK_TIME, "tx unlock time is not zero"),
(&mut resp.overspend, TxRelayChecks::OVERSPEND, "overspend"),
(&mut resp.too_big, TxRelayChecks::TOO_BIG, "too big"),
(&mut resp.too_few_outputs, TxRelayChecks::TOO_FEW_OUTPUTS, "too few outputs"),
(&mut resp.tx_extra_too_big, TxRelayChecks::TX_EXTRA_TOO_BIG, "tx-extra too big"),
];
for (field, flag, reason) in array {
if tx_relay_checks.contains(flag) {
*field = true;
add_reason(&mut reasons, reason);
}
}
Ok(resp)
}
/// <https://github.com/monero-project/monero/blob/cc73fe71162d564ffda8e549b79a350bca53c454/src/rpc/core_rpc_server.cpp#L1525-L1535>
async fn save_bc(mut state: CupratedRpcHandler, _: SaveBcRequest) -> Result<SaveBcResponse, Error> {
blockchain_manager::sync(&mut state.blockchain_manager).await?;
Ok(SaveBcResponse {
base: ResponseBase::OK,
})
}
/// <https://github.com/monero-project/monero/blob/cc73fe71162d564ffda8e549b79a350bca53c454/src/rpc/core_rpc_server.cpp#L1537-L1582>
async fn get_peer_list(
mut state: CupratedRpcHandler,
request: GetPeerListRequest,
) -> Result<GetPeerListResponse, Error> {
let (white_list, gray_list) = address_book::peerlist::<ClearNet>(&mut DummyAddressBook).await?;
Ok(GetPeerListResponse {
base: helper::response_base(false),
white_list,
gray_list,
})
}
/// <https://github.com/monero-project/monero/blob/cc73fe71162d564ffda8e549b79a350bca53c454/src/rpc/core_rpc_server.cpp#L1663-L1687>
async fn get_transaction_pool(
mut state: CupratedRpcHandler,
_: GetTransactionPoolRequest,
) -> Result<GetTransactionPoolResponse, Error> {
let include_sensitive_txs = !state.is_restricted();
let (transactions, spent_key_images) =
txpool::pool(&mut state.txpool_read, include_sensitive_txs).await?;
Ok(GetTransactionPoolResponse {
base: helper::access_response_base(false),
transactions,
spent_key_images,
})
}
/// <https://github.com/monero-project/monero/blob/cc73fe71162d564ffda8e549b79a350bca53c454/src/rpc/core_rpc_server.cpp#L1741-L1756>
async fn get_transaction_pool_stats(
mut state: CupratedRpcHandler,
_: GetTransactionPoolStatsRequest,
) -> Result<GetTransactionPoolStatsResponse, Error> {
let include_sensitive_txs = !state.is_restricted();
let pool_stats = txpool::pool_stats(&mut state.txpool_read, include_sensitive_txs).await?;
Ok(GetTransactionPoolStatsResponse {
base: helper::access_response_base(false),
pool_stats,
})
}
/// <https://github.com/monero-project/monero/blob/cc73fe71162d564ffda8e549b79a350bca53c454/src/rpc/core_rpc_server.cpp#L1780-L1788>
async fn stop_daemon(
mut state: CupratedRpcHandler,
_: StopDaemonRequest,
) -> Result<StopDaemonResponse, Error> {
blockchain_manager::stop(&mut state.blockchain_manager).await?;
Ok(StopDaemonResponse { status: Status::Ok })
}
/// <https://github.com/monero-project/monero/blob/cc73fe71162d564ffda8e549b79a350bca53c454/src/rpc/core_rpc_server.cpp#L3066-L3077>
async fn get_limit(
mut state: CupratedRpcHandler,
_: GetLimitRequest,
) -> Result<GetLimitResponse, Error> {
todo!("waiting on p2p service");
Ok(GetLimitResponse {
base: helper::response_base(false),
limit_down: todo!(),
limit_up: todo!(),
})
}
/// <https://github.com/monero-project/monero/blob/cc73fe71162d564ffda8e549b79a350bca53c454/src/rpc/core_rpc_server.cpp#L3079-L3117>
async fn set_limit(
mut state: CupratedRpcHandler,
request: SetLimitRequest,
) -> Result<SetLimitResponse, Error> {
todo!("waiting on p2p service");
Ok(SetLimitResponse {
base: helper::response_base(false),
limit_down: todo!(),
limit_up: todo!(),
})
}
/// <https://github.com/monero-project/monero/blob/cc73fe71162d564ffda8e549b79a350bca53c454/src/rpc/core_rpc_server.cpp#L3119-L3127>
async fn out_peers(
mut state: CupratedRpcHandler,
request: OutPeersRequest,
) -> Result<OutPeersResponse, Error> {
todo!("waiting on p2p service");
Ok(OutPeersResponse {
base: helper::response_base(false),
out_peers: todo!(),
})
}
/// <https://github.com/monero-project/monero/blob/cc73fe71162d564ffda8e549b79a350bca53c454/src/rpc/core_rpc_server.cpp#L3129-L3137>
async fn in_peers(
mut state: CupratedRpcHandler,
request: InPeersRequest,
) -> Result<InPeersResponse, Error> {
todo!("waiting on p2p service");
Ok(InPeersResponse {
base: helper::response_base(false),
in_peers: todo!(),
})
}
/// <https://github.com/monero-project/monero/blob/cc73fe71162d564ffda8e549b79a350bca53c454/src/rpc/core_rpc_server.cpp#L584-L599>
async fn get_net_stats(
mut state: CupratedRpcHandler,
_: GetNetStatsRequest,
) -> Result<GetNetStatsResponse, Error> {
todo!("waiting on p2p service");
Ok(GetNetStatsResponse {
base: helper::response_base(false),
start_time: *START_INSTANT_UNIX,
total_packets_in: todo!(),
total_bytes_in: todo!(),
total_packets_out: todo!(),
total_bytes_out: todo!(),
})
}
/// <https://github.com/monero-project/monero/blob/cc73fe71162d564ffda8e549b79a350bca53c454/src/rpc/core_rpc_server.cpp#L912-L957>
async fn get_outs(
state: CupratedRpcHandler,
request: GetOutsRequest,
) -> Result<GetOutsResponse, Error> {
let outs = shared::get_outs(
state,
cuprate_rpc_types::bin::GetOutsRequest {
outputs: request.outputs,
get_txid: request.get_txid,
},
)
.await?
.outs
.into_iter()
.map(Into::into)
.collect();
Ok(GetOutsResponse {
base: helper::response_base(false),
outs,
})
}
/// <https://github.com/monero-project/monero/blob/cc73fe71162d564ffda8e549b79a350bca53c454/src/rpc/core_rpc_server.cpp#L3242-L3252>
async fn pop_blocks(
mut state: CupratedRpcHandler,
request: PopBlocksRequest,
) -> Result<PopBlocksResponse, Error> {
let height =
blockchain_manager::pop_blocks(&mut state.blockchain_manager, request.nblocks).await?;
Ok(PopBlocksResponse {
base: helper::response_base(false),
height,
})
}
/// <https://github.com/monero-project/monero/blob/cc73fe71162d564ffda8e549b79a350bca53c454/src/rpc/core_rpc_server.cpp#L1713-L1739>
async fn get_transaction_pool_hashes(
mut state: CupratedRpcHandler,
_: GetTransactionPoolHashesRequest,
) -> Result<GetTransactionPoolHashesResponse, Error> {
Ok(GetTransactionPoolHashesResponse {
base: helper::response_base(false),
tx_hashes: shared::get_transaction_pool_hashes(state)
.await?
.into_iter()
.map(Hex)
.collect(),
})
}
/// <https://github.com/monero-project/monero/blob/cc73fe71162d564ffda8e549b79a350bca53c454/src/rpc/core_rpc_server.cpp#L193-L225>
async fn get_public_nodes(
mut state: CupratedRpcHandler,
request: GetPublicNodesRequest,
) -> Result<GetPublicNodesResponse, Error> {
let (white, gray) = address_book::peerlist::<ClearNet>(&mut DummyAddressBook).await?;
fn map(peers: Vec<cuprate_types::rpc::Peer>) -> Vec<PublicNode> {
peers
.into_iter()
.map(|peer| {
let cuprate_types::rpc::Peer {
host,
rpc_port,
rpc_credits_per_hash,
last_seen,
..
} = peer;
PublicNode {
host,
rpc_port,
rpc_credits_per_hash,
last_seen,
}
})
.collect()
}
let white = map(white);
let gray = map(gray);
Ok(GetPublicNodesResponse {
base: helper::response_base(false),
white,
gray,
})
}
//---------------------------------------------------------------------------------------------------- Unsupported RPC calls (for now)
/// <https://github.com/monero-project/monero/blob/cc73fe71162d564ffda8e549b79a350bca53c454/src/rpc/core_rpc_server.cpp#L1758-L1778>
async fn set_bootstrap_daemon(
state: CupratedRpcHandler,
request: SetBootstrapDaemonRequest,
) -> Result<SetBootstrapDaemonResponse, Error> {
todo!();
}
/// <https://github.com/monero-project/monero/blob/cc73fe71162d564ffda8e549b79a350bca53c454/src/rpc/core_rpc_server.cpp#L3139-L3240>
async fn update(
state: CupratedRpcHandler,
request: UpdateRequest,
) -> Result<UpdateResponse, Error> {
todo!();
}
/// <https://github.com/monero-project/monero/blob/cc73fe71162d564ffda8e549b79a350bca53c454/src/rpc/core_rpc_server.cpp#L1641-L1652>
async fn set_log_level(
state: CupratedRpcHandler,
request: SetLogLevelRequest,
) -> Result<SetLogLevelResponse, Error> {
todo!()
}
/// <https://github.com/monero-project/monero/blob/cc73fe71162d564ffda8e549b79a350bca53c454/src/rpc/core_rpc_server.cpp#L1654-L1661>
async fn set_log_categories(
state: CupratedRpcHandler,
request: SetLogCategoriesRequest,
) -> Result<SetLogCategoriesResponse, Error> {
todo!()
}
//---------------------------------------------------------------------------------------------------- Unsupported RPC calls (forever)
/// <https://github.com/monero-project/monero/blob/cc73fe71162d564ffda8e549b79a350bca53c454/src/rpc/core_rpc_server.cpp#L1413-L1462>
async fn start_mining(
state: CupratedRpcHandler,
request: StartMiningRequest,
) -> Result<StartMiningResponse, Error> {
unreachable!()
}
/// <https://github.com/monero-project/monero/blob/cc73fe71162d564ffda8e549b79a350bca53c454/src/rpc/core_rpc_server.cpp#L1464-L1482>
async fn stop_mining(
state: CupratedRpcHandler,
request: StopMiningRequest,
) -> Result<StopMiningResponse, Error> {
unreachable!();
}
/// <https://github.com/monero-project/monero/blob/cc73fe71162d564ffda8e549b79a350bca53c454/src/rpc/core_rpc_server.cpp#L1484-L1523>
async fn mining_status(
state: CupratedRpcHandler,
request: MiningStatusRequest,
) -> Result<MiningStatusResponse, Error> {
unreachable!();
}
/// <https://github.com/monero-project/monero/blob/cc73fe71162d564ffda8e549b79a350bca53c454/src/rpc/core_rpc_server.cpp#L1626-L1639>
async fn set_log_hash_rate(
state: CupratedRpcHandler,
request: SetLogHashRateRequest,
) -> Result<SetLogHashRateResponse, Error> {
unreachable!();
}

View file

@ -0,0 +1,130 @@
//! RPC handler functions that are shared between different endpoint/methods.
//!
//! TODO:
//! Some handlers have `todo!()`s for other Cuprate internals that must be completed, see:
//! <https://github.com/Cuprate/cuprate/pull/355>
use std::{
collections::{HashMap, HashSet},
num::NonZero,
};
use anyhow::{anyhow, Error};
use cuprate_types::OutputDistributionInput;
use monero_serai::transaction::Timelock;
use cuprate_constants::rpc::MAX_RESTRICTED_GLOBAL_FAKE_OUTS_COUNT;
use cuprate_helper::cast::usize_to_u64;
use cuprate_hex::Hex;
use cuprate_rpc_interface::RpcHandler;
use cuprate_rpc_types::{
bin::{
GetOutsRequest, GetOutsResponse, GetTransactionPoolHashesRequest,
GetTransactionPoolHashesResponse,
},
json::{GetOutputDistributionRequest, GetOutputDistributionResponse},
misc::{Distribution, OutKeyBin},
};
use crate::rpc::{
handlers::helper,
service::{blockchain, blockchain_context, txpool},
CupratedRpcHandler,
};
/// <https://github.com/monero-project/monero/blob/cc73fe71162d564ffda8e549b79a350bca53c454/src/rpc/core_rpc_server.cpp#L912-L957>
///
/// Shared between:
/// - Other JSON's `/get_outs`
/// - Binary's `/get_outs.bin`
pub(super) async fn get_outs(
mut state: CupratedRpcHandler,
request: GetOutsRequest,
) -> Result<GetOutsResponse, Error> {
if state.is_restricted() && request.outputs.len() > MAX_RESTRICTED_GLOBAL_FAKE_OUTS_COUNT {
return Err(anyhow!("Too many outs requested"));
}
let outputs = blockchain::outputs_vec(
&mut state.blockchain_read,
request.outputs,
request.get_txid,
)
.await?;
let mut outs = Vec::<OutKeyBin>::with_capacity(outputs.len());
let blockchain_ctx = state.blockchain_context.blockchain_context();
for (_, index_vec) in outputs {
for (_, out) in index_vec {
let out_key = OutKeyBin {
key: out.key.0,
mask: out.commitment.0,
unlocked: cuprate_consensus_rules::transactions::output_unlocked(
&out.time_lock,
blockchain_ctx.chain_height,
blockchain_ctx.current_adjusted_timestamp_for_time_lock(),
blockchain_ctx.current_hf,
),
height: usize_to_u64(out.height),
txid: out.txid.unwrap_or_default(),
};
outs.push(out_key);
}
}
Ok(GetOutsResponse {
base: helper::access_response_base(false),
outs,
})
}
/// <https://github.com/monero-project/monero/blob/cc73fe71162d564ffda8e549b79a350bca53c454/src/rpc/core_rpc_server.cpp#L1713-L1739>
///
/// Shared between:
/// - Other JSON's `/get_transaction_pool_hashes`
/// - Binary's `/get_transaction_pool_hashes.bin`
///
/// Returns transaction hashes.
pub(super) async fn get_transaction_pool_hashes(
mut state: CupratedRpcHandler,
) -> Result<Vec<[u8; 32]>, Error> {
let include_sensitive_txs = !state.is_restricted();
txpool::all_hashes(&mut state.txpool_read, include_sensitive_txs).await
}
/// <https://github.com/monero-project/monero/blob/cc73fe71162d564ffda8e549b79a350bca53c454/src/rpc/core_rpc_server.cpp#L3352-L3398>
///
/// Shared between:
/// - Other JSON's `/get_output_distribution`
/// - Binary's `/get_output_distribution.bin`
///
/// Returns transaction hashes.
pub(super) async fn get_output_distribution(
mut state: CupratedRpcHandler,
request: GetOutputDistributionRequest,
) -> Result<GetOutputDistributionResponse, Error> {
if state.is_restricted() && request.amounts != [0] {
return Err(anyhow!(
"Restricted RPC can only get output distribution for RCT outputs. Use your own node."
));
}
let input = OutputDistributionInput {
amounts: request.amounts,
cumulative: request.cumulative,
from_height: request.from_height,
// 0 / `None` is placeholder for the whole chain
to_height: NonZero::new(request.to_height),
};
let distributions = blockchain::output_distribution(&mut state.blockchain_read, input).await?;
Ok(GetOutputDistributionResponse {
base: helper::access_response_base(false),
distributions: todo!(
"This type contains binary strings: <https://github.com/monero-project/monero/issues/9422>"
),
})
}

View file

@ -1,294 +0,0 @@
use std::sync::Arc;
use anyhow::Error;
use tower::ServiceExt;
use cuprate_rpc_types::json::{
AddAuxPowRequest, AddAuxPowResponse, BannedRequest, BannedResponse, CalcPowRequest,
CalcPowResponse, FlushCacheRequest, FlushCacheResponse, FlushTransactionPoolRequest,
FlushTransactionPoolResponse, GenerateBlocksRequest, GenerateBlocksResponse,
GetAlternateChainsRequest, GetAlternateChainsResponse, GetBansRequest, GetBansResponse,
GetBlockCountRequest, GetBlockCountResponse, GetBlockHeaderByHashRequest,
GetBlockHeaderByHashResponse, GetBlockHeaderByHeightRequest, GetBlockHeaderByHeightResponse,
GetBlockHeadersRangeRequest, GetBlockHeadersRangeResponse, GetBlockRequest, GetBlockResponse,
GetCoinbaseTxSumRequest, GetCoinbaseTxSumResponse, GetConnectionsRequest,
GetConnectionsResponse, GetFeeEstimateRequest, GetFeeEstimateResponse, GetInfoRequest,
GetInfoResponse, GetLastBlockHeaderRequest, GetLastBlockHeaderResponse, GetMinerDataRequest,
GetMinerDataResponse, GetOutputHistogramRequest, GetOutputHistogramResponse,
GetTransactionPoolBacklogRequest, GetTransactionPoolBacklogResponse, GetTxIdsLooseRequest,
GetTxIdsLooseResponse, GetVersionRequest, GetVersionResponse, HardForkInfoRequest,
HardForkInfoResponse, JsonRpcRequest, JsonRpcResponse, OnGetBlockHashRequest,
OnGetBlockHashResponse, PruneBlockchainRequest, PruneBlockchainResponse, RelayTxRequest,
RelayTxResponse, SetBansRequest, SetBansResponse, SubmitBlockRequest, SubmitBlockResponse,
SyncInfoRequest, SyncInfoResponse,
};
use crate::rpc::CupratedRpcHandler;
/// Map a [`JsonRpcRequest`] to the function that will lead to a [`JsonRpcResponse`].
pub(super) async fn map_request(
state: CupratedRpcHandler,
request: JsonRpcRequest,
) -> Result<JsonRpcResponse, Error> {
use JsonRpcRequest as Req;
use JsonRpcResponse as Resp;
Ok(match request {
Req::GetBlockCount(r) => Resp::GetBlockCount(get_block_count(state, r).await?),
Req::OnGetBlockHash(r) => Resp::OnGetBlockHash(on_get_block_hash(state, r).await?),
Req::SubmitBlock(r) => Resp::SubmitBlock(submit_block(state, r).await?),
Req::GenerateBlocks(r) => Resp::GenerateBlocks(generate_blocks(state, r).await?),
Req::GetLastBlockHeader(r) => {
Resp::GetLastBlockHeader(get_last_block_header(state, r).await?)
}
Req::GetBlockHeaderByHash(r) => {
Resp::GetBlockHeaderByHash(get_block_header_by_hash(state, r).await?)
}
Req::GetBlockHeaderByHeight(r) => {
Resp::GetBlockHeaderByHeight(get_block_header_by_height(state, r).await?)
}
Req::GetBlockHeadersRange(r) => {
Resp::GetBlockHeadersRange(get_block_headers_range(state, r).await?)
}
Req::GetBlock(r) => Resp::GetBlock(get_block(state, r).await?),
Req::GetConnections(r) => Resp::GetConnections(get_connections(state, r).await?),
Req::GetInfo(r) => Resp::GetInfo(get_info(state, r).await?),
Req::HardForkInfo(r) => Resp::HardForkInfo(hard_fork_info(state, r).await?),
Req::SetBans(r) => Resp::SetBans(set_bans(state, r).await?),
Req::GetBans(r) => Resp::GetBans(get_bans(state, r).await?),
Req::Banned(r) => Resp::Banned(banned(state, r).await?),
Req::FlushTransactionPool(r) => {
Resp::FlushTransactionPool(flush_transaction_pool(state, r).await?)
}
Req::GetOutputHistogram(r) => {
Resp::GetOutputHistogram(get_output_histogram(state, r).await?)
}
Req::GetCoinbaseTxSum(r) => Resp::GetCoinbaseTxSum(get_coinbase_tx_sum(state, r).await?),
Req::GetVersion(r) => Resp::GetVersion(get_version(state, r).await?),
Req::GetFeeEstimate(r) => Resp::GetFeeEstimate(get_fee_estimate(state, r).await?),
Req::GetAlternateChains(r) => {
Resp::GetAlternateChains(get_alternate_chains(state, r).await?)
}
Req::RelayTx(r) => Resp::RelayTx(relay_tx(state, r).await?),
Req::SyncInfo(r) => Resp::SyncInfo(sync_info(state, r).await?),
Req::GetTransactionPoolBacklog(r) => {
Resp::GetTransactionPoolBacklog(get_transaction_pool_backlog(state, r).await?)
}
Req::GetMinerData(r) => Resp::GetMinerData(get_miner_data(state, r).await?),
Req::PruneBlockchain(r) => Resp::PruneBlockchain(prune_blockchain(state, r).await?),
Req::CalcPow(r) => Resp::CalcPow(calc_pow(state, r).await?),
Req::FlushCache(r) => Resp::FlushCache(flush_cache(state, r).await?),
Req::AddAuxPow(r) => Resp::AddAuxPow(add_aux_pow(state, r).await?),
Req::GetTxIdsLoose(r) => Resp::GetTxIdsLoose(get_tx_ids_loose(state, r).await?),
})
}
async fn get_block_count(
state: CupratedRpcHandler,
request: GetBlockCountRequest,
) -> Result<GetBlockCountResponse, Error> {
todo!()
}
async fn on_get_block_hash(
state: CupratedRpcHandler,
request: OnGetBlockHashRequest,
) -> Result<OnGetBlockHashResponse, Error> {
todo!()
}
async fn submit_block(
state: CupratedRpcHandler,
request: SubmitBlockRequest,
) -> Result<SubmitBlockResponse, Error> {
todo!()
}
async fn generate_blocks(
state: CupratedRpcHandler,
request: GenerateBlocksRequest,
) -> Result<GenerateBlocksResponse, Error> {
todo!()
}
async fn get_last_block_header(
state: CupratedRpcHandler,
request: GetLastBlockHeaderRequest,
) -> Result<GetLastBlockHeaderResponse, Error> {
todo!()
}
async fn get_block_header_by_hash(
state: CupratedRpcHandler,
request: GetBlockHeaderByHashRequest,
) -> Result<GetBlockHeaderByHashResponse, Error> {
todo!()
}
async fn get_block_header_by_height(
state: CupratedRpcHandler,
request: GetBlockHeaderByHeightRequest,
) -> Result<GetBlockHeaderByHeightResponse, Error> {
todo!()
}
async fn get_block_headers_range(
state: CupratedRpcHandler,
request: GetBlockHeadersRangeRequest,
) -> Result<GetBlockHeadersRangeResponse, Error> {
todo!()
}
async fn get_block(
state: CupratedRpcHandler,
request: GetBlockRequest,
) -> Result<GetBlockResponse, Error> {
todo!()
}
async fn get_connections(
state: CupratedRpcHandler,
request: GetConnectionsRequest,
) -> Result<GetConnectionsResponse, Error> {
todo!()
}
async fn get_info(
state: CupratedRpcHandler,
request: GetInfoRequest,
) -> Result<GetInfoResponse, Error> {
todo!()
}
async fn hard_fork_info(
state: CupratedRpcHandler,
request: HardForkInfoRequest,
) -> Result<HardForkInfoResponse, Error> {
todo!()
}
async fn set_bans(
state: CupratedRpcHandler,
request: SetBansRequest,
) -> Result<SetBansResponse, Error> {
todo!()
}
async fn get_bans(
state: CupratedRpcHandler,
request: GetBansRequest,
) -> Result<GetBansResponse, Error> {
todo!()
}
async fn banned(
state: CupratedRpcHandler,
request: BannedRequest,
) -> Result<BannedResponse, Error> {
todo!()
}
async fn flush_transaction_pool(
state: CupratedRpcHandler,
request: FlushTransactionPoolRequest,
) -> Result<FlushTransactionPoolResponse, Error> {
todo!()
}
async fn get_output_histogram(
state: CupratedRpcHandler,
request: GetOutputHistogramRequest,
) -> Result<GetOutputHistogramResponse, Error> {
todo!()
}
async fn get_coinbase_tx_sum(
state: CupratedRpcHandler,
request: GetCoinbaseTxSumRequest,
) -> Result<GetCoinbaseTxSumResponse, Error> {
todo!()
}
async fn get_version(
state: CupratedRpcHandler,
request: GetVersionRequest,
) -> Result<GetVersionResponse, Error> {
todo!()
}
async fn get_fee_estimate(
state: CupratedRpcHandler,
request: GetFeeEstimateRequest,
) -> Result<GetFeeEstimateResponse, Error> {
todo!()
}
async fn get_alternate_chains(
state: CupratedRpcHandler,
request: GetAlternateChainsRequest,
) -> Result<GetAlternateChainsResponse, Error> {
todo!()
}
async fn relay_tx(
state: CupratedRpcHandler,
request: RelayTxRequest,
) -> Result<RelayTxResponse, Error> {
todo!()
}
async fn sync_info(
state: CupratedRpcHandler,
request: SyncInfoRequest,
) -> Result<SyncInfoResponse, Error> {
todo!()
}
async fn get_transaction_pool_backlog(
state: CupratedRpcHandler,
request: GetTransactionPoolBacklogRequest,
) -> Result<GetTransactionPoolBacklogResponse, Error> {
todo!()
}
async fn get_miner_data(
state: CupratedRpcHandler,
request: GetMinerDataRequest,
) -> Result<GetMinerDataResponse, Error> {
todo!()
}
async fn prune_blockchain(
state: CupratedRpcHandler,
request: PruneBlockchainRequest,
) -> Result<PruneBlockchainResponse, Error> {
todo!()
}
async fn calc_pow(
state: CupratedRpcHandler,
request: CalcPowRequest,
) -> Result<CalcPowResponse, Error> {
todo!()
}
async fn flush_cache(
state: CupratedRpcHandler,
request: FlushCacheRequest,
) -> Result<FlushCacheResponse, Error> {
todo!()
}
async fn add_aux_pow(
state: CupratedRpcHandler,
request: AddAuxPowRequest,
) -> Result<AddAuxPowResponse, Error> {
todo!()
}
async fn get_tx_ids_loose(
state: CupratedRpcHandler,
request: GetTxIdsLooseRequest,
) -> Result<GetTxIdsLooseResponse, Error> {
todo!()
}

View file

@ -1,260 +0,0 @@
use anyhow::Error;
use cuprate_rpc_types::other::{
GetAltBlocksHashesRequest, GetAltBlocksHashesResponse, GetHeightRequest, GetHeightResponse,
GetLimitRequest, GetLimitResponse, GetNetStatsRequest, GetNetStatsResponse, GetOutsRequest,
GetOutsResponse, GetPeerListRequest, GetPeerListResponse, GetPublicNodesRequest,
GetPublicNodesResponse, GetTransactionPoolHashesRequest, GetTransactionPoolHashesResponse,
GetTransactionPoolRequest, GetTransactionPoolResponse, GetTransactionPoolStatsRequest,
GetTransactionPoolStatsResponse, GetTransactionsRequest, GetTransactionsResponse,
InPeersRequest, InPeersResponse, IsKeyImageSpentRequest, IsKeyImageSpentResponse,
MiningStatusRequest, MiningStatusResponse, OtherRequest, OtherResponse, OutPeersRequest,
OutPeersResponse, PopBlocksRequest, PopBlocksResponse, SaveBcRequest, SaveBcResponse,
SendRawTransactionRequest, SendRawTransactionResponse, SetBootstrapDaemonRequest,
SetBootstrapDaemonResponse, SetLimitRequest, SetLimitResponse, SetLogCategoriesRequest,
SetLogCategoriesResponse, SetLogHashRateRequest, SetLogHashRateResponse, SetLogLevelRequest,
SetLogLevelResponse, StartMiningRequest, StartMiningResponse, StopDaemonRequest,
StopDaemonResponse, StopMiningRequest, StopMiningResponse, UpdateRequest, UpdateResponse,
};
use crate::rpc::CupratedRpcHandler;
/// Map a [`OtherRequest`] to the function that will lead to a [`OtherResponse`].
pub(super) async fn map_request(
state: CupratedRpcHandler,
request: OtherRequest,
) -> Result<OtherResponse, Error> {
use OtherRequest as Req;
use OtherResponse as Resp;
Ok(match request {
Req::GetHeight(r) => Resp::GetHeight(get_height(state, r).await?),
Req::GetTransactions(r) => Resp::GetTransactions(get_transactions(state, r).await?),
Req::GetAltBlocksHashes(r) => {
Resp::GetAltBlocksHashes(get_alt_blocks_hashes(state, r).await?)
}
Req::IsKeyImageSpent(r) => Resp::IsKeyImageSpent(is_key_image_spent(state, r).await?),
Req::SendRawTransaction(r) => {
Resp::SendRawTransaction(send_raw_transaction(state, r).await?)
}
Req::StartMining(r) => Resp::StartMining(start_mining(state, r).await?),
Req::StopMining(r) => Resp::StopMining(stop_mining(state, r).await?),
Req::MiningStatus(r) => Resp::MiningStatus(mining_status(state, r).await?),
Req::SaveBc(r) => Resp::SaveBc(save_bc(state, r).await?),
Req::GetPeerList(r) => Resp::GetPeerList(get_peer_list(state, r).await?),
Req::SetLogHashRate(r) => Resp::SetLogHashRate(set_log_hash_rate(state, r).await?),
Req::SetLogLevel(r) => Resp::SetLogLevel(set_log_level(state, r).await?),
Req::SetLogCategories(r) => Resp::SetLogCategories(set_log_categories(state, r).await?),
Req::SetBootstrapDaemon(r) => {
Resp::SetBootstrapDaemon(set_bootstrap_daemon(state, r).await?)
}
Req::GetTransactionPool(r) => {
Resp::GetTransactionPool(get_transaction_pool(state, r).await?)
}
Req::GetTransactionPoolStats(r) => {
Resp::GetTransactionPoolStats(get_transaction_pool_stats(state, r).await?)
}
Req::StopDaemon(r) => Resp::StopDaemon(stop_daemon(state, r).await?),
Req::GetLimit(r) => Resp::GetLimit(get_limit(state, r).await?),
Req::SetLimit(r) => Resp::SetLimit(set_limit(state, r).await?),
Req::OutPeers(r) => Resp::OutPeers(out_peers(state, r).await?),
Req::InPeers(r) => Resp::InPeers(in_peers(state, r).await?),
Req::GetNetStats(r) => Resp::GetNetStats(get_net_stats(state, r).await?),
Req::GetOuts(r) => Resp::GetOuts(get_outs(state, r).await?),
Req::Update(r) => Resp::Update(update(state, r).await?),
Req::PopBlocks(r) => Resp::PopBlocks(pop_blocks(state, r).await?),
Req::GetTransactionPoolHashes(r) => {
Resp::GetTransactionPoolHashes(get_transaction_pool_hashes(state, r).await?)
}
Req::GetPublicNodes(r) => Resp::GetPublicNodes(get_public_nodes(state, r).await?),
})
}
async fn get_height(
state: CupratedRpcHandler,
request: GetHeightRequest,
) -> Result<GetHeightResponse, Error> {
todo!()
}
async fn get_transactions(
state: CupratedRpcHandler,
request: GetTransactionsRequest,
) -> Result<GetTransactionsResponse, Error> {
todo!()
}
async fn get_alt_blocks_hashes(
state: CupratedRpcHandler,
request: GetAltBlocksHashesRequest,
) -> Result<GetAltBlocksHashesResponse, Error> {
todo!()
}
async fn is_key_image_spent(
state: CupratedRpcHandler,
request: IsKeyImageSpentRequest,
) -> Result<IsKeyImageSpentResponse, Error> {
todo!()
}
async fn send_raw_transaction(
state: CupratedRpcHandler,
request: SendRawTransactionRequest,
) -> Result<SendRawTransactionResponse, Error> {
todo!()
}
async fn start_mining(
state: CupratedRpcHandler,
request: StartMiningRequest,
) -> Result<StartMiningResponse, Error> {
todo!()
}
async fn stop_mining(
state: CupratedRpcHandler,
request: StopMiningRequest,
) -> Result<StopMiningResponse, Error> {
todo!()
}
async fn mining_status(
state: CupratedRpcHandler,
request: MiningStatusRequest,
) -> Result<MiningStatusResponse, Error> {
todo!()
}
async fn save_bc(
state: CupratedRpcHandler,
request: SaveBcRequest,
) -> Result<SaveBcResponse, Error> {
todo!()
}
async fn get_peer_list(
state: CupratedRpcHandler,
request: GetPeerListRequest,
) -> Result<GetPeerListResponse, Error> {
todo!()
}
async fn set_log_hash_rate(
state: CupratedRpcHandler,
request: SetLogHashRateRequest,
) -> Result<SetLogHashRateResponse, Error> {
todo!()
}
async fn set_log_level(
state: CupratedRpcHandler,
request: SetLogLevelRequest,
) -> Result<SetLogLevelResponse, Error> {
todo!()
}
async fn set_log_categories(
state: CupratedRpcHandler,
request: SetLogCategoriesRequest,
) -> Result<SetLogCategoriesResponse, Error> {
todo!()
}
async fn set_bootstrap_daemon(
state: CupratedRpcHandler,
request: SetBootstrapDaemonRequest,
) -> Result<SetBootstrapDaemonResponse, Error> {
todo!()
}
async fn get_transaction_pool(
state: CupratedRpcHandler,
request: GetTransactionPoolRequest,
) -> Result<GetTransactionPoolResponse, Error> {
todo!()
}
async fn get_transaction_pool_stats(
state: CupratedRpcHandler,
request: GetTransactionPoolStatsRequest,
) -> Result<GetTransactionPoolStatsResponse, Error> {
todo!()
}
async fn stop_daemon(
state: CupratedRpcHandler,
request: StopDaemonRequest,
) -> Result<StopDaemonResponse, Error> {
todo!()
}
async fn get_limit(
state: CupratedRpcHandler,
request: GetLimitRequest,
) -> Result<GetLimitResponse, Error> {
todo!()
}
async fn set_limit(
state: CupratedRpcHandler,
request: SetLimitRequest,
) -> Result<SetLimitResponse, Error> {
todo!()
}
async fn out_peers(
state: CupratedRpcHandler,
request: OutPeersRequest,
) -> Result<OutPeersResponse, Error> {
todo!()
}
async fn in_peers(
state: CupratedRpcHandler,
request: InPeersRequest,
) -> Result<InPeersResponse, Error> {
todo!()
}
async fn get_net_stats(
state: CupratedRpcHandler,
request: GetNetStatsRequest,
) -> Result<GetNetStatsResponse, Error> {
todo!()
}
async fn get_outs(
state: CupratedRpcHandler,
request: GetOutsRequest,
) -> Result<GetOutsResponse, Error> {
todo!()
}
async fn update(
state: CupratedRpcHandler,
request: UpdateRequest,
) -> Result<UpdateResponse, Error> {
todo!()
}
async fn pop_blocks(
state: CupratedRpcHandler,
request: PopBlocksRequest,
) -> Result<PopBlocksResponse, Error> {
todo!()
}
async fn get_transaction_pool_hashes(
state: CupratedRpcHandler,
request: GetTransactionPoolHashesRequest,
) -> Result<GetTransactionPoolHashesResponse, Error> {
todo!()
}
async fn get_public_nodes(
state: CupratedRpcHandler,
request: GetPublicNodesRequest,
) -> Result<GetPublicNodesResponse, Error> {
todo!()
}

View file

@ -1,72 +0,0 @@
//! Functions for [`TxpoolReadRequest`].
use std::convert::Infallible;
use anyhow::{anyhow, Error};
use tower::{Service, ServiceExt};
use cuprate_helper::cast::usize_to_u64;
use cuprate_txpool::{
service::{
interface::{TxpoolReadRequest, TxpoolReadResponse},
TxpoolReadHandle,
},
TxEntry,
};
// FIXME: use `anyhow::Error` over `tower::BoxError` in txpool.
/// [`TxpoolReadRequest::Backlog`]
pub(crate) async fn backlog(txpool_read: &mut TxpoolReadHandle) -> Result<Vec<TxEntry>, Error> {
let TxpoolReadResponse::Backlog(tx_entries) = txpool_read
.ready()
.await
.map_err(|e| anyhow!(e))?
.call(TxpoolReadRequest::Backlog)
.await
.map_err(|e| anyhow!(e))?
else {
unreachable!();
};
Ok(tx_entries)
}
/// [`TxpoolReadRequest::Size`]
pub(crate) async fn size(
txpool_read: &mut TxpoolReadHandle,
include_sensitive_txs: bool,
) -> Result<u64, Error> {
let TxpoolReadResponse::Size(size) = txpool_read
.ready()
.await
.map_err(|e| anyhow!(e))?
.call(TxpoolReadRequest::Size {
include_sensitive_txs,
})
.await
.map_err(|e| anyhow!(e))?
else {
unreachable!();
};
Ok(usize_to_u64(size))
}
/// TODO
pub(crate) async fn flush(
txpool_manager: &mut Infallible,
tx_hashes: Vec<[u8; 32]>,
) -> Result<(), Error> {
todo!();
Ok(())
}
/// TODO
pub(crate) async fn relay(
txpool_manager: &mut Infallible,
tx_hashes: Vec<[u8; 32]>,
) -> Result<(), Error> {
todo!();
Ok(())
}

View file

@ -1,4 +1,4 @@
//! Dummy implementation of [`RpcHandler`].
//! `cuprated`'s implementation of [`RpcHandler`].
use std::task::{Context, Poll};
@ -16,14 +16,13 @@ use cuprate_rpc_types::{
json::{JsonRpcRequest, JsonRpcResponse},
other::{OtherRequest, OtherResponse},
};
use cuprate_txpool::service::{TxpoolReadHandle, TxpoolWriteHandle};
use cuprate_types::{AddAuxPow, AuxPow, HardFork};
use cuprate_txpool::service::TxpoolReadHandle;
use cuprate_types::BlockTemplate;
use crate::rpc::{bin, json, other};
use crate::rpc::handlers;
/// TODO: use real type when public.
#[derive(Clone)]
#[expect(clippy::large_enum_variant)]
pub enum BlockchainManagerRequest {
/// Pop blocks off the top of the blockchain.
///
@ -37,7 +36,13 @@ pub enum BlockchainManagerRequest {
Pruned,
/// Relay a block to the network.
RelayBlock(Block),
RelayBlock(
/// This is [`Box`]ed due to `clippy::large_enum_variant`.
Box<Block>,
),
/// Sync/flush the blockchain database to disk.
Sync,
/// Is the blockchain in the middle of syncing?
///
@ -65,7 +70,7 @@ pub enum BlockchainManagerRequest {
/// Number of the blocks to be generated.
amount_of_blocks: u64,
/// The previous block's hash.
prev_block: [u8; 32],
prev_block: Option<[u8; 32]>,
/// The starting value for the nonce.
starting_nonce: u32,
/// The address that will receive the coinbase reward.
@ -83,6 +88,16 @@ pub enum BlockchainManagerRequest {
//
/// Get the next [`PruningSeed`] needed for a pruned sync.
NextNeededPruningSeed,
/// Create a block template.
CreateBlockTemplate {
prev_block: [u8; 32],
account_public_address: String,
extra_nonce: Vec<u8>,
},
/// Safely shutdown `cuprated`.
Stop,
}
/// TODO: use real type when public.
@ -93,6 +108,7 @@ pub enum BlockchainManagerResponse {
/// Response to:
/// - [`BlockchainManagerRequest::Prune`]
/// - [`BlockchainManagerRequest::RelayBlock`]
/// - [`BlockchainManagerRequest::Sync`]
Ok,
/// Response to [`BlockchainManagerRequest::PopBlocks`]
@ -124,10 +140,11 @@ pub enum BlockchainManagerResponse {
height: usize,
},
// /// Response to [`BlockchainManagerRequest::Spans`].
// Spans(Vec<Span<Z::Addr>>),
/// Response to [`BlockchainManagerRequest::NextNeededPruningSeed`].
NextNeededPruningSeed(PruningSeed),
/// Response to [`BlockchainManagerRequest::CreateBlockTemplate`].
CreateBlockTemplate(Box<BlockTemplate>),
}
/// TODO: use real type when public.
@ -139,7 +156,7 @@ pub type BlockchainManagerHandle = cuprate_database_service::DatabaseReadService
/// TODO
#[derive(Clone)]
pub struct CupratedRpcHandler {
/// Should this RPC server be [restricted](RpcHandler::restricted)?
/// Should this RPC server be [restricted](RpcHandler::is_restricted)?
///
/// This is not `pub` on purpose, as it should not be mutated after [`Self::new`].
restricted: bool,
@ -182,7 +199,7 @@ impl CupratedRpcHandler {
}
impl RpcHandler for CupratedRpcHandler {
fn restricted(&self) -> bool {
fn is_restricted(&self) -> bool {
self.restricted
}
}
@ -198,7 +215,7 @@ impl Service<JsonRpcRequest> for CupratedRpcHandler {
fn call(&mut self, request: JsonRpcRequest) -> Self::Future {
let state = self.clone();
Box::pin(json::map_request(state, request))
Box::pin(handlers::json_rpc::map_request(state, request))
}
}
@ -213,7 +230,7 @@ impl Service<BinRequest> for CupratedRpcHandler {
fn call(&mut self, request: BinRequest) -> Self::Future {
let state = self.clone();
Box::pin(bin::map_request(state, request))
Box::pin(handlers::bin::map_request(state, request))
}
}
@ -228,6 +245,6 @@ impl Service<OtherRequest> for CupratedRpcHandler {
fn call(&mut self, request: OtherRequest) -> Self::Future {
let state = self.clone();
Box::pin(other::map_request(state, request))
Box::pin(handlers::other_json::map_request(state, request))
}
}

View file

@ -1,4 +1,4 @@
//! Convenience functions for requests/responses.
//! Convenience functions for Cuprate's various [`tower::Service`] requests/responses.
//!
//! This module implements many methods for
//! [`CupratedRpcHandler`](crate::rpc::CupratedRpcHandler)
@ -12,8 +12,8 @@
//! the [`blockchain`] modules contains methods for the
//! blockchain database [`tower::Service`] API.
mod address_book;
mod blockchain;
mod blockchain_context;
mod blockchain_manager;
mod txpool;
pub(super) mod address_book;
pub(super) mod blockchain;
pub(super) mod blockchain_context;
pub(super) mod blockchain_manager;
pub(super) mod txpool;

View file

@ -1,25 +1,23 @@
//! Functions for TODO: doc enum message.
//! Functions to send [`AddressBookRequest`]s.
use std::convert::Infallible;
use std::net::SocketAddrV4;
use anyhow::{anyhow, Error};
use tower::ServiceExt;
use cuprate_helper::cast::usize_to_u64;
use cuprate_helper::{cast::usize_to_u64, map::u32_from_ipv4};
use cuprate_p2p_core::{
services::{AddressBookRequest, AddressBookResponse},
services::{AddressBookRequest, AddressBookResponse, ZoneSpecificPeerListEntryBase},
types::{BanState, ConnectionId},
AddressBook, NetworkZone,
};
use cuprate_pruning::PruningSeed;
use cuprate_rpc_types::misc::{ConnectionInfo, Span};
use crate::rpc::constants::FIELD_NOT_SUPPORTED;
use cuprate_rpc_types::misc::ConnectionInfo;
use cuprate_types::rpc::Peer;
// FIXME: use `anyhow::Error` over `tower::BoxError` in address book.
/// [`AddressBookRequest::PeerlistSize`]
pub(crate) async fn peerlist_size<Z: NetworkZone>(
pub async fn peerlist_size<Z: NetworkZone>(
address_book: &mut impl AddressBook<Z>,
) -> Result<(u64, u64), Error> {
let AddressBookResponse::PeerlistSize { white, grey } = address_book
@ -37,7 +35,7 @@ pub(crate) async fn peerlist_size<Z: NetworkZone>(
}
/// [`AddressBookRequest::ConnectionInfo`]
pub(crate) async fn connection_info<Z: NetworkZone>(
pub async fn connection_info<Z: NetworkZone>(
address_book: &mut impl AddressBook<Z>,
) -> Result<Vec<ConnectionInfo>, Error> {
let AddressBookResponse::ConnectionInfo(vec) = address_book
@ -94,7 +92,7 @@ pub(crate) async fn connection_info<Z: NetworkZone>(
}
/// [`AddressBookRequest::ConnectionCount`]
pub(crate) async fn connection_count<Z: NetworkZone>(
pub async fn connection_count<Z: NetworkZone>(
address_book: &mut impl AddressBook<Z>,
) -> Result<(u64, u64), Error> {
let AddressBookResponse::ConnectionCount { incoming, outgoing } = address_book
@ -112,7 +110,7 @@ pub(crate) async fn connection_count<Z: NetworkZone>(
}
/// [`AddressBookRequest::SetBan`]
pub(crate) async fn set_ban<Z: NetworkZone>(
pub async fn set_ban<Z: NetworkZone>(
address_book: &mut impl AddressBook<Z>,
set_ban: cuprate_p2p_core::types::SetBan<Z::Addr>,
) -> Result<(), Error> {
@ -131,7 +129,7 @@ pub(crate) async fn set_ban<Z: NetworkZone>(
}
/// [`AddressBookRequest::GetBan`]
pub(crate) async fn get_ban<Z: NetworkZone>(
pub async fn get_ban<Z: NetworkZone>(
address_book: &mut impl AddressBook<Z>,
peer: Z::Addr,
) -> Result<Option<std::time::Instant>, Error> {
@ -150,7 +148,7 @@ pub(crate) async fn get_ban<Z: NetworkZone>(
}
/// [`AddressBookRequest::GetBans`]
pub(crate) async fn get_bans<Z: NetworkZone>(
pub async fn get_bans<Z: NetworkZone>(
address_book: &mut impl AddressBook<Z>,
) -> Result<Vec<BanState<Z::Addr>>, Error> {
let AddressBookResponse::GetBans(bans) = address_book
@ -166,3 +164,62 @@ pub(crate) async fn get_bans<Z: NetworkZone>(
Ok(bans)
}
/// [`AddressBookRequest::Peerlist`]
pub async fn peerlist<Z: NetworkZone>(
address_book: &mut impl AddressBook<Z>,
) -> Result<(Vec<Peer>, Vec<Peer>), Error> {
let AddressBookResponse::Peerlist(peerlist) = address_book
.ready()
.await
.map_err(|e| anyhow!(e))?
.call(AddressBookRequest::Peerlist)
.await
.map_err(|e| anyhow!(e))?
else {
unreachable!();
};
fn map<Z: NetworkZone>(peers: Vec<ZoneSpecificPeerListEntryBase<Z::Addr>>) -> Vec<Peer> {
peers
.into_iter()
.map(|peer| {
let ZoneSpecificPeerListEntryBase {
adr,
id,
last_seen,
pruning_seed,
rpc_port,
rpc_credits_per_hash,
} = peer;
let host = adr.to_string();
let (ip, port) = if let Ok(socket_addr) = host.parse::<SocketAddrV4>() {
(u32_from_ipv4(*socket_addr.ip()), socket_addr.port())
} else {
(0, 0)
};
let last_seen = last_seen.try_into().unwrap_or(0);
let pruning_seed = pruning_seed.compress();
Peer {
id,
host,
ip,
port,
rpc_port,
rpc_credits_per_hash,
last_seen,
pruning_seed,
}
})
.collect()
}
let white = map::<Z>(peerlist.white);
let grey = map::<Z>(peerlist.grey);
Ok((white, grey))
}

View file

@ -1,7 +1,7 @@
//! Functions for [`BlockchainReadRequest`].
//! Functions to send [`BlockchainReadRequest`]s.
use std::{
collections::{BTreeMap, HashMap, HashSet},
collections::{BTreeSet, HashMap, HashSet},
ops::Range,
};
@ -10,17 +10,22 @@ use indexmap::{IndexMap, IndexSet};
use monero_serai::block::Block;
use tower::{Service, ServiceExt};
use cuprate_blockchain::{service::BlockchainReadHandle, types::AltChainInfo};
use cuprate_blockchain::service::BlockchainReadHandle;
use cuprate_helper::cast::{u64_to_usize, usize_to_u64};
use cuprate_rpc_types::misc::GetOutputsOut;
use cuprate_types::{
blockchain::{BlockchainReadRequest, BlockchainResponse},
output_cache::OutputCache,
Chain, ChainInfo, CoinbaseTxSum, ExtendedBlockHeader, HardFork, MinerData,
OutputHistogramEntry, OutputHistogramInput, OutputOnChain,
rpc::{
ChainInfo, CoinbaseTxSum, KeyImageSpentStatus, OutputDistributionData,
OutputHistogramEntry, OutputHistogramInput,
},
BlockCompleteEntry, Chain, ExtendedBlockHeader, OutputDistributionInput, OutputOnChain,
TxInBlockchain,
};
/// [`BlockchainReadRequest::Block`].
pub(crate) async fn block(
pub async fn block(
blockchain_read: &mut BlockchainReadHandle,
height: u64,
) -> Result<Block, Error> {
@ -39,7 +44,7 @@ pub(crate) async fn block(
}
/// [`BlockchainReadRequest::BlockByHash`].
pub(crate) async fn block_by_hash(
pub async fn block_by_hash(
blockchain_read: &mut BlockchainReadHandle,
hash: [u8; 32],
) -> Result<Block, Error> {
@ -56,7 +61,7 @@ pub(crate) async fn block_by_hash(
}
/// [`BlockchainReadRequest::BlockExtendedHeader`].
pub(crate) async fn block_extended_header(
pub async fn block_extended_header(
blockchain_read: &mut BlockchainReadHandle,
height: u64,
) -> Result<ExtendedBlockHeader, Error> {
@ -75,7 +80,7 @@ pub(crate) async fn block_extended_header(
}
/// [`BlockchainReadRequest::BlockHash`].
pub(crate) async fn block_hash(
pub async fn block_hash(
blockchain_read: &mut BlockchainReadHandle,
height: u64,
chain: Chain,
@ -96,7 +101,7 @@ pub(crate) async fn block_hash(
}
/// [`BlockchainReadRequest::FindBlock`].
pub(crate) async fn find_block(
pub async fn find_block(
blockchain_read: &mut BlockchainReadHandle,
block_hash: [u8; 32],
) -> Result<Option<(Chain, usize)>, Error> {
@ -112,8 +117,39 @@ pub(crate) async fn find_block(
Ok(option)
}
/// [`BlockchainReadRequest::NextChainEntry`].
///
/// Returns only the:
/// - block IDs
/// - start height
/// - current chain height
pub async fn next_chain_entry(
blockchain_read: &mut BlockchainReadHandle,
block_hashes: Vec<[u8; 32]>,
start_height: u64,
) -> Result<(Vec<[u8; 32]>, Option<usize>, usize), Error> {
let BlockchainResponse::NextChainEntry {
block_ids,
start_height,
chain_height,
..
} = blockchain_read
.ready()
.await?
.call(BlockchainReadRequest::NextChainEntry(
block_hashes,
u64_to_usize(start_height),
))
.await?
else {
unreachable!();
};
Ok((block_ids, start_height, chain_height))
}
/// [`BlockchainReadRequest::FilterUnknownHashes`].
pub(crate) async fn filter_unknown_hashes(
pub async fn filter_unknown_hashes(
blockchain_read: &mut BlockchainReadHandle,
block_hashes: HashSet<[u8; 32]>,
) -> Result<HashSet<[u8; 32]>, Error> {
@ -130,7 +166,7 @@ pub(crate) async fn filter_unknown_hashes(
}
/// [`BlockchainReadRequest::BlockExtendedHeaderInRange`]
pub(crate) async fn block_extended_header_in_range(
pub async fn block_extended_header_in_range(
blockchain_read: &mut BlockchainReadHandle,
range: Range<usize>,
chain: Chain,
@ -150,7 +186,7 @@ pub(crate) async fn block_extended_header_in_range(
}
/// [`BlockchainReadRequest::ChainHeight`].
pub(crate) async fn chain_height(
pub async fn chain_height(
blockchain_read: &mut BlockchainReadHandle,
) -> Result<(u64, [u8; 32]), Error> {
let BlockchainResponse::ChainHeight(height, hash) = blockchain_read
@ -166,7 +202,7 @@ pub(crate) async fn chain_height(
}
/// [`BlockchainReadRequest::GeneratedCoins`].
pub(crate) async fn generated_coins(
pub async fn generated_coins(
blockchain_read: &mut BlockchainReadHandle,
block_height: u64,
) -> Result<u64, Error> {
@ -185,14 +221,38 @@ pub(crate) async fn generated_coins(
}
/// [`BlockchainReadRequest::Outputs`]
pub(crate) async fn outputs(
pub async fn outputs(
blockchain_read: &mut BlockchainReadHandle,
outputs: IndexMap<u64, IndexSet<u64>>,
get_txid: bool,
) -> Result<OutputCache, Error> {
let BlockchainResponse::Outputs(outputs) = blockchain_read
.ready()
.await?
.call(BlockchainReadRequest::Outputs(outputs))
.call(BlockchainReadRequest::Outputs { outputs, get_txid })
.await?
else {
unreachable!();
};
Ok(outputs)
}
/// [`BlockchainReadRequest::OutputsVec`]
pub async fn outputs_vec(
blockchain_read: &mut BlockchainReadHandle,
outputs: Vec<GetOutputsOut>,
get_txid: bool,
) -> Result<Vec<(u64, Vec<(u64, OutputOnChain)>)>, Error> {
let outputs = outputs
.into_iter()
.map(|output| (output.amount, output.index))
.collect();
let BlockchainResponse::OutputsVec(outputs) = blockchain_read
.ready()
.await?
.call(BlockchainReadRequest::OutputsVec { outputs, get_txid })
.await?
else {
unreachable!();
@ -202,7 +262,7 @@ pub(crate) async fn outputs(
}
/// [`BlockchainReadRequest::NumberOutputsWithAmount`]
pub(crate) async fn number_outputs_with_amount(
pub async fn number_outputs_with_amount(
blockchain_read: &mut BlockchainReadHandle,
output_amounts: Vec<u64>,
) -> Result<HashMap<u64, usize>, Error> {
@ -221,11 +281,11 @@ pub(crate) async fn number_outputs_with_amount(
}
/// [`BlockchainReadRequest::KeyImagesSpent`]
pub(crate) async fn key_images_spent(
pub async fn key_images_spent(
blockchain_read: &mut BlockchainReadHandle,
key_images: HashSet<[u8; 32]>,
) -> Result<bool, Error> {
let BlockchainResponse::KeyImagesSpent(is_spent) = blockchain_read
let BlockchainResponse::KeyImagesSpent(status) = blockchain_read
.ready()
.await?
.call(BlockchainReadRequest::KeyImagesSpent(key_images))
@ -234,11 +294,28 @@ pub(crate) async fn key_images_spent(
unreachable!();
};
Ok(is_spent)
Ok(status)
}
/// [`BlockchainReadRequest::KeyImagesSpentVec`]
pub async fn key_images_spent_vec(
blockchain_read: &mut BlockchainReadHandle,
key_images: Vec<[u8; 32]>,
) -> Result<Vec<bool>, Error> {
let BlockchainResponse::KeyImagesSpentVec(status) = blockchain_read
.ready()
.await?
.call(BlockchainReadRequest::KeyImagesSpentVec(key_images))
.await?
else {
unreachable!();
};
Ok(status)
}
/// [`BlockchainReadRequest::CompactChainHistory`]
pub(crate) async fn compact_chain_history(
pub async fn compact_chain_history(
blockchain_read: &mut BlockchainReadHandle,
) -> Result<(Vec<[u8; 32]>, u128), Error> {
let BlockchainResponse::CompactChainHistory {
@ -257,7 +334,7 @@ pub(crate) async fn compact_chain_history(
}
/// [`BlockchainReadRequest::FindFirstUnknown`]
pub(crate) async fn find_first_unknown(
pub async fn find_first_unknown(
blockchain_read: &mut BlockchainReadHandle,
hashes: Vec<[u8; 32]>,
) -> Result<Option<(usize, u64)>, Error> {
@ -274,9 +351,7 @@ pub(crate) async fn find_first_unknown(
}
/// [`BlockchainReadRequest::TotalTxCount`]
pub(crate) async fn total_tx_count(
blockchain_read: &mut BlockchainReadHandle,
) -> Result<u64, Error> {
pub async fn total_tx_count(blockchain_read: &mut BlockchainReadHandle) -> Result<u64, Error> {
let BlockchainResponse::TotalTxCount(tx_count) = blockchain_read
.ready()
.await?
@ -290,7 +365,7 @@ pub(crate) async fn total_tx_count(
}
/// [`BlockchainReadRequest::DatabaseSize`]
pub(crate) async fn database_size(
pub async fn database_size(
blockchain_read: &mut BlockchainReadHandle,
) -> Result<(u64, u64), Error> {
let BlockchainResponse::DatabaseSize {
@ -308,8 +383,25 @@ pub(crate) async fn database_size(
Ok((database_size, free_space))
}
/// [`BlockchainReadRequest::OutputDistribution`]
pub async fn output_distribution(
blockchain_read: &mut BlockchainReadHandle,
input: OutputDistributionInput,
) -> Result<Vec<OutputDistributionData>, Error> {
let BlockchainResponse::OutputDistribution(data) = blockchain_read
.ready()
.await?
.call(BlockchainReadRequest::OutputDistribution(input))
.await?
else {
unreachable!();
};
Ok(data)
}
/// [`BlockchainReadRequest::OutputHistogram`]
pub(crate) async fn output_histogram(
pub async fn output_histogram(
blockchain_read: &mut BlockchainReadHandle,
input: OutputHistogramInput,
) -> Result<Vec<OutputHistogramEntry>, Error> {
@ -326,7 +418,7 @@ pub(crate) async fn output_histogram(
}
/// [`BlockchainReadRequest::CoinbaseTxSum`]
pub(crate) async fn coinbase_tx_sum(
pub async fn coinbase_tx_sum(
blockchain_read: &mut BlockchainReadHandle,
height: u64,
count: u64,
@ -347,7 +439,7 @@ pub(crate) async fn coinbase_tx_sum(
}
/// [`BlockchainReadRequest::AltChains`]
pub(crate) async fn alt_chains(
pub async fn alt_chains(
blockchain_read: &mut BlockchainReadHandle,
) -> Result<Vec<ChainInfo>, Error> {
let BlockchainResponse::AltChains(vec) = blockchain_read
@ -363,9 +455,7 @@ pub(crate) async fn alt_chains(
}
/// [`BlockchainReadRequest::AltChainCount`]
pub(crate) async fn alt_chain_count(
blockchain_read: &mut BlockchainReadHandle,
) -> Result<u64, Error> {
pub async fn alt_chain_count(blockchain_read: &mut BlockchainReadHandle) -> Result<u64, Error> {
let BlockchainResponse::AltChainCount(count) = blockchain_read
.ready()
.await?
@ -377,3 +467,91 @@ pub(crate) async fn alt_chain_count(
Ok(usize_to_u64(count))
}
/// [`BlockchainReadRequest::Transactions`].
pub async fn transactions(
blockchain_read: &mut BlockchainReadHandle,
tx_hashes: HashSet<[u8; 32]>,
) -> Result<(Vec<TxInBlockchain>, Vec<[u8; 32]>), Error> {
let BlockchainResponse::Transactions { txs, missed_txs } = blockchain_read
.ready()
.await?
.call(BlockchainReadRequest::Transactions { tx_hashes })
.await?
else {
unreachable!();
};
Ok((txs, missed_txs))
}
/// [`BlockchainReadRequest::TotalRctOutputs`].
pub async fn total_rct_outputs(blockchain_read: &mut BlockchainReadHandle) -> Result<u64, Error> {
let BlockchainResponse::TotalRctOutputs(n) = blockchain_read
.ready()
.await?
.call(BlockchainReadRequest::TotalRctOutputs)
.await?
else {
unreachable!();
};
Ok(n)
}
/// [`BlockchainReadRequest::BlockCompleteEntries`].
pub async fn block_complete_entries(
blockchain_read: &mut BlockchainReadHandle,
block_hashes: Vec<[u8; 32]>,
) -> Result<(Vec<BlockCompleteEntry>, Vec<[u8; 32]>, usize), Error> {
let BlockchainResponse::BlockCompleteEntries {
blocks,
missing_hashes,
blockchain_height,
} = blockchain_read
.ready()
.await?
.call(BlockchainReadRequest::BlockCompleteEntries(block_hashes))
.await?
else {
unreachable!();
};
Ok((blocks, missing_hashes, blockchain_height))
}
/// [`BlockchainReadRequest::BlockCompleteEntriesByHeight`].
pub async fn block_complete_entries_by_height(
blockchain_read: &mut BlockchainReadHandle,
block_heights: Vec<u64>,
) -> Result<Vec<BlockCompleteEntry>, Error> {
let BlockchainResponse::BlockCompleteEntriesByHeight(blocks) = blockchain_read
.ready()
.await?
.call(BlockchainReadRequest::BlockCompleteEntriesByHeight(
block_heights.into_iter().map(u64_to_usize).collect(),
))
.await?
else {
unreachable!();
};
Ok(blocks)
}
/// [`BlockchainReadRequest::TxOutputIndexes`].
pub async fn tx_output_indexes(
blockchain_read: &mut BlockchainReadHandle,
tx_hash: [u8; 32],
) -> Result<Vec<u64>, Error> {
let BlockchainResponse::TxOutputIndexes(o_indexes) = blockchain_read
.ready()
.await?
.call(BlockchainReadRequest::TxOutputIndexes { tx_hash })
.await?
else {
unreachable!();
};
Ok(o_indexes)
}

View file

@ -1,6 +1,4 @@
//! Functions for [`BlockChainContextRequest`] and [`BlockChainContextResponse`].
use std::convert::Infallible;
//! Functions to send [`BlockChainContextRequest`]s.
use anyhow::{anyhow, Error};
use monero_serai::block::Block;
@ -10,20 +8,13 @@ use cuprate_consensus_context::{
BlockChainContextRequest, BlockChainContextResponse, BlockchainContext,
BlockchainContextService,
};
use cuprate_helper::cast::u64_to_usize;
use cuprate_types::{FeeEstimate, HardFork, HardForkInfo};
use cuprate_types::{
rpc::{FeeEstimate, HardForkInfo},
HardFork,
};
// FIXME: use `anyhow::Error` over `tower::BoxError` in blockchain context.
pub(crate) async fn context(
blockchain_context: &mut BlockchainContextService,
) -> Result<BlockchainContext, Error> {
// TODO: Remove this whole function just call directly in all usages.
let context = blockchain_context.blockchain_context().clone();
Ok(context)
}
/// [`BlockChainContextRequest::HardForkInfo`].
pub(crate) async fn hard_fork_info(
blockchain_context: &mut BlockchainContextService,
@ -66,17 +57,22 @@ pub(crate) async fn fee_estimate(
pub(crate) async fn calculate_pow(
blockchain_context: &mut BlockchainContextService,
hardfork: HardFork,
height: u64,
block: Box<Block>,
block: Block,
seed_hash: [u8; 32],
) -> Result<[u8; 32], Error> {
let Some(height) = block.number() else {
return Err(anyhow!("Block is missing height"));
};
let block = Box::new(block);
let BlockChainContextResponse::CalculatePow(hash) = blockchain_context
.ready()
.await
.map_err(|e| anyhow!(e))?
.call(BlockChainContextRequest::CalculatePow {
hardfork,
height: u64_to_usize(height),
height,
block,
seed_hash,
})
@ -88,3 +84,22 @@ pub(crate) async fn calculate_pow(
Ok(hash)
}
/// [`BlockChainContextRequest::BatchGetDifficulties`]
pub async fn batch_get_difficulties(
blockchain_context: &mut BlockchainContextService,
difficulties: Vec<(u64, HardFork)>,
) -> Result<Vec<u128>, Error> {
let BlockChainContextResponse::BatchDifficulties(resp) = blockchain_context
.ready()
.await
.map_err(|e| anyhow!(e))?
.call(BlockChainContextRequest::BatchGetDifficulties(difficulties))
.await
.map_err(|e| anyhow!(e))?
else {
unreachable!();
};
Ok(resp)
}

View file

@ -1,4 +1,4 @@
//! Functions for [`BlockchainManagerRequest`] & [`BlockchainManagerResponse`].
//! Functions to send [`BlockchainManagerRequest`]s.
use anyhow::Error;
use monero_serai::block::Block;
@ -8,15 +8,14 @@ use cuprate_helper::cast::{u64_to_usize, usize_to_u64};
use cuprate_p2p_core::{types::ConnectionId, NetworkZone};
use cuprate_pruning::PruningSeed;
use cuprate_rpc_types::misc::Span;
use cuprate_types::{AddAuxPow, AuxPow, HardFork};
use cuprate_types::BlockTemplate;
use crate::rpc::{
constants::FIELD_NOT_SUPPORTED,
handler::{BlockchainManagerHandle, BlockchainManagerRequest, BlockchainManagerResponse},
use crate::rpc::rpc_handler::{
BlockchainManagerHandle, BlockchainManagerRequest, BlockchainManagerResponse,
};
/// [`BlockchainManagerRequest::PopBlocks`]
pub(crate) async fn pop_blocks(
pub async fn pop_blocks(
blockchain_manager: &mut BlockchainManagerHandle,
amount: u64,
) -> Result<u64, Error> {
@ -35,9 +34,7 @@ pub(crate) async fn pop_blocks(
}
/// [`BlockchainManagerRequest::Prune`]
pub(crate) async fn prune(
blockchain_manager: &mut BlockchainManagerHandle,
) -> Result<PruningSeed, Error> {
pub async fn prune(blockchain_manager: &mut BlockchainManagerHandle) -> Result<PruningSeed, Error> {
let BlockchainManagerResponse::Prune(seed) = blockchain_manager
.ready()
.await?
@ -51,9 +48,7 @@ pub(crate) async fn prune(
}
/// [`BlockchainManagerRequest::Pruned`]
pub(crate) async fn pruned(
blockchain_manager: &mut BlockchainManagerHandle,
) -> Result<bool, Error> {
pub async fn pruned(blockchain_manager: &mut BlockchainManagerHandle) -> Result<bool, Error> {
let BlockchainManagerResponse::Pruned(pruned) = blockchain_manager
.ready()
.await?
@ -67,9 +62,9 @@ pub(crate) async fn pruned(
}
/// [`BlockchainManagerRequest::RelayBlock`]
pub(crate) async fn relay_block(
pub async fn relay_block(
blockchain_manager: &mut BlockchainManagerHandle,
block: Block,
block: Box<Block>,
) -> Result<(), Error> {
let BlockchainManagerResponse::Ok = blockchain_manager
.ready()
@ -84,9 +79,7 @@ pub(crate) async fn relay_block(
}
/// [`BlockchainManagerRequest::Syncing`]
pub(crate) async fn syncing(
blockchain_manager: &mut BlockchainManagerHandle,
) -> Result<bool, Error> {
pub async fn syncing(blockchain_manager: &mut BlockchainManagerHandle) -> Result<bool, Error> {
let BlockchainManagerResponse::Syncing(syncing) = blockchain_manager
.ready()
.await?
@ -100,9 +93,7 @@ pub(crate) async fn syncing(
}
/// [`BlockchainManagerRequest::Synced`]
pub(crate) async fn synced(
blockchain_manager: &mut BlockchainManagerHandle,
) -> Result<bool, Error> {
pub async fn synced(blockchain_manager: &mut BlockchainManagerHandle) -> Result<bool, Error> {
let BlockchainManagerResponse::Synced(syncing) = blockchain_manager
.ready()
.await?
@ -116,7 +107,7 @@ pub(crate) async fn synced(
}
/// [`BlockchainManagerRequest::Target`]
pub(crate) async fn target(
pub async fn target(
blockchain_manager: &mut BlockchainManagerHandle,
) -> Result<std::time::Duration, Error> {
let BlockchainManagerResponse::Target(target) = blockchain_manager
@ -132,9 +123,7 @@ pub(crate) async fn target(
}
/// [`BlockchainManagerRequest::TargetHeight`]
pub(crate) async fn target_height(
blockchain_manager: &mut BlockchainManagerHandle,
) -> Result<u64, Error> {
pub async fn target_height(blockchain_manager: &mut BlockchainManagerHandle) -> Result<u64, Error> {
let BlockchainManagerResponse::TargetHeight { height } = blockchain_manager
.ready()
.await?
@ -148,10 +137,10 @@ pub(crate) async fn target_height(
}
/// [`BlockchainManagerRequest::GenerateBlocks`]
pub(crate) async fn generate_blocks(
pub async fn generate_blocks(
blockchain_manager: &mut BlockchainManagerHandle,
amount_of_blocks: u64,
prev_block: [u8; 32],
prev_block: Option<[u8; 32]>,
starting_nonce: u32,
wallet_address: String,
) -> Result<(Vec<[u8; 32]>, u64), Error> {
@ -173,7 +162,7 @@ pub(crate) async fn generate_blocks(
}
// [`BlockchainManagerRequest::Spans`]
pub(crate) async fn spans<Z: NetworkZone>(
pub async fn spans<Z: NetworkZone>(
blockchain_manager: &mut BlockchainManagerHandle,
) -> Result<Vec<Span>, Error> {
// let BlockchainManagerResponse::Spans(vec) = blockchain_manager
@ -185,7 +174,8 @@ pub(crate) async fn spans<Z: NetworkZone>(
// unreachable!();
// };
let vec: Vec<cuprate_p2p_core::types::Span<Z::Addr>> = todo!();
let vec: Vec<cuprate_p2p_core::types::Span<Z::Addr>> =
todo!("waiting on blockchain downloader/syncer: <https://github.com/Cuprate/cuprate/pull/320#discussion_r1811089758>");
// FIXME: impl this map somewhere instead of inline.
let vec = vec
@ -205,7 +195,7 @@ pub(crate) async fn spans<Z: NetworkZone>(
}
/// [`BlockchainManagerRequest::NextNeededPruningSeed`]
pub(crate) async fn next_needed_pruning_seed(
pub async fn next_needed_pruning_seed(
blockchain_manager: &mut BlockchainManagerHandle,
) -> Result<PruningSeed, Error> {
let BlockchainManagerResponse::NextNeededPruningSeed(seed) = blockchain_manager
@ -219,3 +209,54 @@ pub(crate) async fn next_needed_pruning_seed(
Ok(seed)
}
/// [`BlockchainManagerRequest::CreateBlockTemplate`]
pub async fn create_block_template(
blockchain_manager: &mut BlockchainManagerHandle,
prev_block: [u8; 32],
account_public_address: String,
extra_nonce: Vec<u8>,
) -> Result<Box<BlockTemplate>, Error> {
let BlockchainManagerResponse::CreateBlockTemplate(block_template) = blockchain_manager
.ready()
.await?
.call(BlockchainManagerRequest::CreateBlockTemplate {
prev_block,
account_public_address,
extra_nonce,
})
.await?
else {
unreachable!();
};
Ok(block_template)
}
/// [`BlockchainManagerRequest::Sync`]
pub async fn sync(blockchain_manager: &mut BlockchainManagerHandle) -> Result<(), Error> {
let BlockchainManagerResponse::Ok = blockchain_manager
.ready()
.await?
.call(BlockchainManagerRequest::Sync)
.await?
else {
unreachable!();
};
Ok(())
}
/// [`BlockchainManagerRequest::Stop`]
pub async fn stop(blockchain_manager: &mut BlockchainManagerHandle) -> Result<(), Error> {
let BlockchainManagerResponse::Ok = blockchain_manager
.ready()
.await?
.call(BlockchainManagerRequest::Stop)
.await?
else {
unreachable!();
};
Ok(())
}

View file

@ -0,0 +1,244 @@
//! Functions to send [`TxpoolReadRequest`]s.
use std::{collections::HashSet, convert::Infallible, num::NonZero};
use anyhow::{anyhow, Error};
use monero_serai::transaction::Transaction;
use tower::{Service, ServiceExt};
use cuprate_helper::cast::usize_to_u64;
use cuprate_rpc_types::misc::{SpentKeyImageInfo, TxInfo};
use cuprate_txpool::{
service::{
interface::{TxpoolReadRequest, TxpoolReadResponse},
TxpoolReadHandle,
},
TxEntry,
};
use cuprate_types::{
rpc::{PoolInfo, PoolInfoFull, PoolInfoIncremental, PoolTxInfo, TxpoolStats},
TxInPool, TxRelayChecks,
};
// FIXME: use `anyhow::Error` over `tower::BoxError` in txpool.
/// [`TxpoolReadRequest::Backlog`]
pub async fn backlog(txpool_read: &mut TxpoolReadHandle) -> Result<Vec<TxEntry>, Error> {
let TxpoolReadResponse::Backlog(tx_entries) = txpool_read
.ready()
.await
.map_err(|e| anyhow!(e))?
.call(TxpoolReadRequest::Backlog)
.await
.map_err(|e| anyhow!(e))?
else {
unreachable!();
};
Ok(tx_entries)
}
/// [`TxpoolReadRequest::Size`]
pub async fn size(
txpool_read: &mut TxpoolReadHandle,
include_sensitive_txs: bool,
) -> Result<u64, Error> {
let TxpoolReadResponse::Size(size) = txpool_read
.ready()
.await
.map_err(|e| anyhow!(e))?
.call(TxpoolReadRequest::Size {
include_sensitive_txs,
})
.await
.map_err(|e| anyhow!(e))?
else {
unreachable!();
};
Ok(usize_to_u64(size))
}
/// [`TxpoolReadRequest::PoolInfo`]
pub async fn pool_info(
txpool_read: &mut TxpoolReadHandle,
include_sensitive_txs: bool,
max_tx_count: usize,
start_time: Option<NonZero<usize>>,
) -> Result<PoolInfo, Error> {
let TxpoolReadResponse::PoolInfo(pool_info) = txpool_read
.ready()
.await
.map_err(|e| anyhow!(e))?
.call(TxpoolReadRequest::PoolInfo {
include_sensitive_txs,
max_tx_count,
start_time,
})
.await
.map_err(|e| anyhow!(e))?
else {
unreachable!();
};
Ok(pool_info)
}
/// [`TxpoolReadRequest::TxsByHash`]
pub async fn txs_by_hash(
txpool_read: &mut TxpoolReadHandle,
tx_hashes: Vec<[u8; 32]>,
include_sensitive_txs: bool,
) -> Result<Vec<TxInPool>, Error> {
let TxpoolReadResponse::TxsByHash(txs_in_pool) = txpool_read
.ready()
.await
.map_err(|e| anyhow!(e))?
.call(TxpoolReadRequest::TxsByHash {
tx_hashes,
include_sensitive_txs,
})
.await
.map_err(|e| anyhow!(e))?
else {
unreachable!();
};
Ok(txs_in_pool)
}
/// [`TxpoolReadRequest::KeyImagesSpent`]
pub async fn key_images_spent(
txpool_read: &mut TxpoolReadHandle,
key_images: HashSet<[u8; 32]>,
include_sensitive_txs: bool,
) -> Result<bool, Error> {
let TxpoolReadResponse::KeyImagesSpent(status) = txpool_read
.ready()
.await
.map_err(|e| anyhow!(e))?
.call(TxpoolReadRequest::KeyImagesSpent {
key_images,
include_sensitive_txs,
})
.await
.map_err(|e| anyhow!(e))?
else {
unreachable!();
};
Ok(status)
}
/// [`TxpoolReadRequest::KeyImagesSpentVec`]
pub async fn key_images_spent_vec(
txpool_read: &mut TxpoolReadHandle,
key_images: Vec<[u8; 32]>,
include_sensitive_txs: bool,
) -> Result<Vec<bool>, Error> {
let TxpoolReadResponse::KeyImagesSpentVec(status) = txpool_read
.ready()
.await
.map_err(|e| anyhow!(e))?
.call(TxpoolReadRequest::KeyImagesSpentVec {
key_images,
include_sensitive_txs,
})
.await
.map_err(|e| anyhow!(e))?
else {
unreachable!();
};
Ok(status)
}
/// [`TxpoolReadRequest::Pool`]
pub async fn pool(
txpool_read: &mut TxpoolReadHandle,
include_sensitive_txs: bool,
) -> Result<(Vec<TxInfo>, Vec<SpentKeyImageInfo>), Error> {
let TxpoolReadResponse::Pool {
txs,
spent_key_images,
} = txpool_read
.ready()
.await
.map_err(|e| anyhow!(e))?
.call(TxpoolReadRequest::Pool {
include_sensitive_txs,
})
.await
.map_err(|e| anyhow!(e))?
else {
unreachable!();
};
let txs = txs.into_iter().map(Into::into).collect();
let spent_key_images = spent_key_images.into_iter().map(Into::into).collect();
Ok((txs, spent_key_images))
}
/// [`TxpoolReadRequest::PoolStats`]
pub async fn pool_stats(
txpool_read: &mut TxpoolReadHandle,
include_sensitive_txs: bool,
) -> Result<TxpoolStats, Error> {
let TxpoolReadResponse::PoolStats(txpool_stats) = txpool_read
.ready()
.await
.map_err(|e| anyhow!(e))?
.call(TxpoolReadRequest::PoolStats {
include_sensitive_txs,
})
.await
.map_err(|e| anyhow!(e))?
else {
unreachable!();
};
Ok(txpool_stats)
}
/// [`TxpoolReadRequest::AllHashes`]
pub async fn all_hashes(
txpool_read: &mut TxpoolReadHandle,
include_sensitive_txs: bool,
) -> Result<Vec<[u8; 32]>, Error> {
let TxpoolReadResponse::AllHashes(hashes) = txpool_read
.ready()
.await
.map_err(|e| anyhow!(e))?
.call(TxpoolReadRequest::AllHashes {
include_sensitive_txs,
})
.await
.map_err(|e| anyhow!(e))?
else {
unreachable!();
};
Ok(hashes)
}
/// TODO: impl txpool manager.
pub async fn flush(txpool_manager: &mut Infallible, tx_hashes: Vec<[u8; 32]>) -> Result<(), Error> {
todo!();
Ok(())
}
/// TODO: impl txpool manager.
pub async fn relay(txpool_manager: &mut Infallible, tx_hashes: Vec<[u8; 32]>) -> Result<(), Error> {
todo!();
Ok(())
}
/// TODO: impl txpool manager.
pub async fn check_maybe_relay_local(
txpool_manager: &mut Infallible,
tx: Transaction,
relay: bool,
) -> Result<TxRelayChecks, Error> {
Ok(todo!())
}

View file

@ -24,7 +24,6 @@ cargo doc --open --package cuprate-blockchain
| Crate | In-tree path | Purpose |
|-------|--------------|---------|
| [`cuprate-epee-encoding`](https://doc.cuprate.org/cuprate_epee_encoding) | [`net/epee-encoding/`](https://github.com/Cuprate/cuprate/tree/main/net/epee-encoding) | Epee (de)serialization
| [`cuprate-fixed-bytes`](https://doc.cuprate.org/cuprate_fixed_bytes) | [`net/fixed-bytes/`](https://github.com/Cuprate/cuprate/tree/main/net/fixed-bytes) | Fixed byte containers backed by `byte::Byte`
| [`cuprate-levin`](https://doc.cuprate.org/cuprate_levin) | [`net/levin/`](https://github.com/Cuprate/cuprate/tree/main/net/levin) | Levin bucket protocol implementation
| [`cuprate-wire`](https://doc.cuprate.org/cuprate_wire) | [`net/wire/`](https://github.com/Cuprate/cuprate/tree/main/net/wire) | TODO
@ -46,6 +45,13 @@ cargo doc --open --package cuprate-blockchain
| [`cuprate-database-service`](https://doc.cuprate.org/cuprate_database_service) | [`storage/database-service/`](https://github.com/Cuprate/cuprate/tree/main/storage/database-service) | `tower::Service` + thread-pool abstraction built on-top of `cuprate-database`
| [`cuprate-txpool`](https://doc.cuprate.org/cuprate_txpool) | [`storage/txpool/`](https://github.com/Cuprate/cuprate/tree/main/storage/txpool) | Transaction pool database built on-top of `cuprate-database` & `cuprate-database-service`
## Types
| Crate | In-tree path | Purpose |
|-------|--------------|---------|
| [`cuprate-types`](https://doc.cuprate.org/cuprate_types) | [`types/types/`](https://github.com/Cuprate/cuprate/tree/main/types/types) | General types used throughout Cuprate |
| [`cuprate-hex`](https://doc.cuprate.org/cuprate_hex) | [`types/hex/`](https://github.com/Cuprate/cuprate/tree/main/types/hex) | Hexadecimal data types |
| [`cuprate-fixed-bytes`](https://doc.cuprate.org/cuprate_fixed_bytes) | [`types/fixed-bytes/`](https://github.com/Cuprate/cuprate/tree/main/net/fixed-bytes) | Fixed byte containers backed by `byte::Byte`
## RPC
| Crate | In-tree path | Purpose |
|-------|--------------|---------|
@ -66,5 +72,4 @@ cargo doc --open --package cuprate-blockchain
| [`cuprate-cryptonight`](https://doc.cuprate.org/cuprate_cryptonight) | [`cryptonight/`](https://github.com/Cuprate/cuprate/tree/main/cryptonight) | CryptoNight hash functions
| [`cuprate-pruning`](https://doc.cuprate.org/cuprate_pruning) | [`pruning/`](https://github.com/Cuprate/cuprate/tree/main/pruning) | Monero pruning logic/types
| [`cuprate-helper`](https://doc.cuprate.org/cuprate_helper) | [`helper/`](https://github.com/Cuprate/cuprate/tree/main/helper) | Kitchen-sink helper crate for Cuprate
| [`cuprate-test-utils`](https://doc.cuprate.org/cuprate_test_utils) | [`test-utils/`](https://github.com/Cuprate/cuprate/tree/main/test-utils) | Testing utilities for Cuprate
| [`cuprate-types`](https://doc.cuprate.org/cuprate_types) | [`types/`](https://github.com/Cuprate/cuprate/tree/main/types) | Shared types across Cuprate
| [`cuprate-test-utils`](https://doc.cuprate.org/cuprate_test_utils) | [`test-utils/`](https://github.com/Cuprate/cuprate/tree/main/test-utils) | Testing utilities for Cuprate

View file

@ -36,7 +36,10 @@ pub mod weight;
mod alt_chains;
mod task;
use cuprate_types::{Chain, ChainInfo, FeeEstimate, HardForkInfo};
use cuprate_types::{
rpc::{ChainInfo, FeeEstimate, HardForkInfo},
Chain,
};
use difficulty::DifficultyCache;
use rx_vms::RandomXVm;
use weight::BlockWeightsCache;

View file

@ -131,17 +131,20 @@ pub async fn get_output_cache<D: Database>(
txs_verification_data: impl Iterator<Item = &TransactionVerificationData>,
mut database: D,
) -> Result<OutputCache, ExtendedConsensusError> {
let mut output_ids = IndexMap::new();
let mut outputs = IndexMap::new();
for tx_v_data in txs_verification_data {
insert_ring_member_ids(&tx_v_data.tx.prefix().inputs, &mut output_ids)
insert_ring_member_ids(&tx_v_data.tx.prefix().inputs, &mut outputs)
.map_err(ConsensusError::Transaction)?;
}
let BlockchainResponse::Outputs(outputs) = database
.ready()
.await?
.call(BlockchainReadRequest::Outputs(output_ids))
.call(BlockchainReadRequest::Outputs {
outputs,
get_txid: false,
})
.await?
else {
unreachable!();
@ -160,10 +163,10 @@ pub async fn batch_get_ring_member_info<D: Database>(
mut database: D,
cache: Option<&OutputCache>,
) -> Result<Vec<TxRingMembersInfo>, ExtendedConsensusError> {
let mut output_ids = IndexMap::new();
let mut outputs = IndexMap::new();
for tx_v_data in txs_verification_data.clone() {
insert_ring_member_ids(&tx_v_data.tx.prefix().inputs, &mut output_ids)
insert_ring_member_ids(&tx_v_data.tx.prefix().inputs, &mut outputs)
.map_err(ConsensusError::Transaction)?;
}
@ -173,7 +176,10 @@ pub async fn batch_get_ring_member_info<D: Database>(
let BlockchainResponse::Outputs(outputs) = database
.ready()
.await?
.call(BlockchainReadRequest::Outputs(output_ids))
.call(BlockchainReadRequest::Outputs {
outputs,
get_txid: false,
})
.await?
else {
unreachable!();

View file

@ -31,7 +31,7 @@ fn dummy_database(outputs: BTreeMap<u64, OutputOnChain>) -> impl Database + Clon
BlockchainReadRequest::NumberOutputsWithAmount(_) => {
BlockchainResponse::NumberOutputsWithAmount(HashMap::new())
}
BlockchainReadRequest::Outputs(outs) => {
BlockchainReadRequest::Outputs { outputs: outs, .. } => {
let idxs = &outs[&0];
let mut ret = IndexMap::new();
@ -73,6 +73,7 @@ macro_rules! test_verify_valid_v2_tx {
time_lock: Timelock::None,
commitment: CompressedEdwardsY(hex_literal::hex!($commitment)),
key: CompressedEdwardsY(hex_literal::hex!($ring_member)),
txid: None,
}),)+)+
];
@ -100,6 +101,7 @@ macro_rules! test_verify_valid_v2_tx {
time_lock: Timelock::None,
commitment: ED25519_BASEPOINT_COMPRESSED,
key: CompressedEdwardsY(hex_literal::hex!($ring_member)),
txid: None,
}),)+)+
];

View file

@ -23,6 +23,7 @@ map = ["cast", "dep:monero-serai", "dep:cuprate-constants"]
time = ["dep:chrono", "std"]
thread = ["std", "dep:target_os_lib"]
tx = ["dep:monero-serai"]
fmt = ["map", "std"]
[dependencies]
cuprate-constants = { workspace = true, optional = true, features = ["block"] }

View file

@ -3,6 +3,18 @@
//! This modules provides utilities for casting between types.
//!
//! `#[no_std]` compatible.
//!
//! # 64-bit invariant
//! This module is available on 32-bit arches although panics
//! will occur between lossy casts, e.g. [`u64_to_usize`] where
//! the input is larger than [`u32::MAX`].
//!
//! On 64-bit arches, all functions are lossless.
// TODO:
// These casting functions are heavily used throughout the codebase
// yet it is not enforced that all usages are correct in 32-bit cases.
// Panicking may be a short-term solution - find a better fix for 32-bit arches.
#![allow(clippy::cast_possible_truncation)]
@ -10,44 +22,78 @@
//============================ SAFETY: DO NOT REMOVE ===========================//
// //
// //
// Only allow building 64-bit targets. //
// This allows us to assume 64-bit invariants in this file. //
#[cfg(not(target_pointer_width = "64"))]
compile_error!("Cuprate is only compatible with 64-bit CPUs");
// Only allow building {32,64}-bit targets. //
// This allows us to assume {32,64}-bit invariants in this file. //
#[cfg(not(any(target_pointer_width = "64", target_pointer_width = "32")))]
compile_error!("This module is only compatible with {32,64}-bit CPUs");
// //
// //
//============================ SAFETY: DO NOT REMOVE ===========================//
/// Cast [`u64`] to [`usize`].
#[inline(always)]
pub const fn u64_to_usize(u: u64) -> usize {
u as usize
#[cfg(target_pointer_width = "64")]
mod functions {
/// Cast [`u64`] to [`usize`].
#[inline(always)]
pub const fn u64_to_usize(u: u64) -> usize {
u as usize
}
/// Cast [`i64`] to [`isize`].
#[inline(always)]
pub const fn i64_to_isize(i: i64) -> isize {
i as isize
}
}
#[cfg(target_pointer_width = "32")]
mod functions {
/// Cast [`u64`] to [`usize`].
///
/// # Panics
/// This panics on 32-bit arches if `u` is larger than [`u32::MAX`].
#[inline(always)]
pub const fn u64_to_usize(u: u64) -> usize {
if u > u32::MAX as u64 {
panic!()
} else {
u as usize
}
}
/// Cast [`i64`] to [`isize`].
///
/// # Panics
/// This panics on 32-bit arches if `i` is lesser than [`i32::MIN`] or greater [`i32::MAX`].
#[inline(always)]
pub const fn i64_to_isize(i: i64) -> isize {
if i < i32::MIN as i64 || i > i32::MAX as i64 {
panic!()
} else {
i as isize
}
}
}
pub use functions::{i64_to_isize, u64_to_usize};
/// Cast [`u32`] to [`usize`].
#[inline(always)]
pub const fn u32_to_usize(u: u32) -> usize {
u as usize
}
/// Cast [`usize`] to [`u64`].
#[inline(always)]
pub const fn usize_to_u64(u: usize) -> u64 {
u as u64
}
/// Cast [`i64`] to [`isize`].
#[inline(always)]
pub const fn i64_to_isize(i: i64) -> isize {
i as isize
}
/// Cast [`i32`] to [`isize`].
#[inline(always)]
pub const fn i32_to_isize(i: i32) -> isize {
i as isize
}
/// Cast [`usize`] to [`u64`].
#[inline(always)]
pub const fn usize_to_u64(u: usize) -> u64 {
u as u64
}
/// Cast [`isize`] to [`i64`].
#[inline(always)]
pub const fn isize_to_i64(i: isize) -> i64 {
@ -60,7 +106,8 @@ mod test {
use super::*;
#[test]
fn max_unsigned() {
#[cfg(target_pointer_width = "64")]
fn max_64bit() {
assert_eq!(u32_to_usize(u32::MAX), usize::try_from(u32::MAX).unwrap());
assert_eq!(usize_to_u64(u32_to_usize(u32::MAX)), u64::from(u32::MAX));
@ -69,10 +116,7 @@ mod test {
assert_eq!(usize_to_u64(usize::MAX), u64::MAX);
assert_eq!(u64_to_usize(usize_to_u64(usize::MAX)), usize::MAX);
}
#[test]
fn max_signed() {
assert_eq!(i32_to_isize(i32::MAX), isize::try_from(i32::MAX).unwrap());
assert_eq!(isize_to_i64(i32_to_isize(i32::MAX)), i64::from(i32::MAX));
@ -82,4 +126,25 @@ mod test {
assert_eq!(isize_to_i64(isize::MAX), i64::MAX);
assert_eq!(i64_to_isize(isize_to_i64(isize::MAX)), isize::MAX);
}
#[test]
#[cfg(target_pointer_width = "32")]
#[should_panic]
fn panic_u64_32bit() {
u64_to_usize(u64::from(u32::MAX + 1));
}
#[test]
#[cfg(target_pointer_width = "32")]
#[should_panic]
fn panic_i64_lesser_32bit() {
i64_to_usize(i64::from(i32::MIN - 1));
}
#[test]
#[cfg(target_pointer_width = "32")]
#[should_panic]
fn panic_i64_greater_32bit() {
i64_to_usize(i64::from(i32::MAX + 1));
}
}

44
helper/src/fmt.rs Normal file
View file

@ -0,0 +1,44 @@
//! String formatting.
/// A type that can be represented in hexadecimal (with a `0x` prefix).
pub trait HexPrefix {
/// Turn `self` into a hexadecimal string prefixed with `0x`.
fn hex_prefix(self) -> String;
}
macro_rules! impl_hex_prefix {
($(
$t:ty
),*) => {
$(
impl HexPrefix for $t {
fn hex_prefix(self) -> String {
format!("{:#x}", self)
}
}
)*
};
}
impl_hex_prefix!(u8, u16, u32, u64, u128, i8, i16, i32, i64, i128, usize, isize);
impl HexPrefix for (u64, u64) {
/// Combine the low and high bits of a [`u128`] as a lower-case hexadecimal string prefixed with `0x`.
///
/// ```rust
/// # use cuprate_helper::fmt::HexPrefix;
/// assert_eq!((0, 0).hex_prefix(), "0x0");
/// assert_eq!((0, u64::MAX).hex_prefix(), "0xffffffffffffffff0000000000000000");
/// assert_eq!((u64::MAX, 0).hex_prefix(), "0xffffffffffffffff");
/// assert_eq!((u64::MAX, u64::MAX).hex_prefix(), "0xffffffffffffffffffffffffffffffff");
/// ```
fn hex_prefix(self) -> String {
format!(
"{:#x}",
crate::map::combine_low_high_bits_to_u128(self.0, self.1)
)
}
}
#[cfg(test)]
mod tests {}

View file

@ -11,7 +11,7 @@ pub mod atomic;
#[cfg(feature = "cast")]
pub mod cast;
#[cfg(all(feature = "fs", feature = "std"))]
#[cfg(feature = "fs")]
pub mod fs;
pub mod network;
@ -33,6 +33,6 @@ pub mod tx;
#[cfg(feature = "crypto")]
pub mod crypto;
//---------------------------------------------------------------------------------------------------- Private Usage
//----------------------------------------------------------------------------------------------------
#[cfg(feature = "fmt")]
pub mod fmt;

View file

@ -5,6 +5,8 @@
//! `#[no_std]` compatible.
//---------------------------------------------------------------------------------------------------- Use
use core::net::Ipv4Addr;
use monero_serai::transaction::Timelock;
use cuprate_constants::block::MAX_BLOCK_HEIGHT;
@ -28,6 +30,7 @@ use crate::cast::{u64_to_usize, usize_to_u64};
/// let high = u64::MAX;
///
/// assert_eq!(split_u128_into_low_high_bits(value), (low, high));
/// assert_eq!(split_u128_into_low_high_bits(0), (0, 0));
/// ```
#[inline]
pub const fn split_u128_into_low_high_bits(value: u128) -> (u64, u64) {
@ -52,6 +55,7 @@ pub const fn split_u128_into_low_high_bits(value: u128) -> (u64, u64) {
/// let high = u64::MAX;
///
/// assert_eq!(combine_low_high_bits_to_u128(low, high), value);
/// assert_eq!(combine_low_high_bits_to_u128(0, 0), 0);
/// ```
#[inline]
pub const fn combine_low_high_bits_to_u128(low_bits: u64, high_bits: u64) -> u128 {
@ -59,6 +63,24 @@ pub const fn combine_low_high_bits_to_u128(low_bits: u64, high_bits: u64) -> u12
res | (low_bits as u128)
}
//---------------------------------------------------------------------------------------------------- IPv4
/// Convert an [`Ipv4Addr`] to a [`u32`].
///
/// For why this exists, see: <https://architecture.cuprate.org/oddities/le-ipv4.html>.
#[inline]
pub const fn ipv4_from_u32(ip: u32) -> Ipv4Addr {
let [a, b, c, d] = ip.to_le_bytes();
Ipv4Addr::new(a, b, c, d)
}
/// Convert a [`u32`] to an [`Ipv4Addr`].
///
/// For why this exists, see: <https://architecture.cuprate.org/oddities/le-ipv4.html>.
#[inline]
pub const fn u32_from_ipv4(ip: Ipv4Addr) -> u32 {
u32::from_le_bytes(ip.octets())
}
//---------------------------------------------------------------------------------------------------- Timelock
/// Map a [`u64`] to a [`Timelock`].
///

View file

@ -16,6 +16,7 @@ std = ["dep:thiserror", "bytes/std", "cuprate-fixed-bytes/std"]
[dependencies]
cuprate-fixed-bytes = { workspace = true, default-features = false }
cuprate-hex = { workspace = true, default-features = false }
paste = "1.0.15"
ref-cast = "1.0.23"

View file

@ -7,6 +7,7 @@ use core::fmt::Debug;
use bytes::{Buf, BufMut, Bytes, BytesMut};
use cuprate_fixed_bytes::{ByteArray, ByteArrayVec};
use cuprate_hex::{Hex, HexVec};
use crate::{
io::{checked_read_primitive, checked_write_primitive},
@ -392,6 +393,53 @@ impl<const N: usize> EpeeValue for Vec<[u8; N]> {
}
}
impl<const N: usize> EpeeValue for Hex<N> {
const MARKER: Marker = <[u8; N] as EpeeValue>::MARKER;
fn read<B: Buf>(r: &mut B, marker: &Marker) -> Result<Self> {
Ok(Self(<[u8; N] as EpeeValue>::read(r, marker)?))
}
fn write<B: BufMut>(self, w: &mut B) -> Result<()> {
<[u8; N] as EpeeValue>::write(self.0, w)
}
}
impl<const N: usize> EpeeValue for Vec<Hex<N>> {
const MARKER: Marker = Vec::<[u8; N]>::MARKER;
fn read<B: Buf>(r: &mut B, marker: &Marker) -> Result<Self> {
Ok(Vec::<[u8; N]>::read(r, marker)?
.into_iter()
.map(Hex)
.collect())
}
fn should_write(&self) -> bool {
!self.is_empty()
}
fn epee_default_value() -> Option<Self> {
Some(Self::new())
}
fn write<B: BufMut>(self, w: &mut B) -> Result<()> {
write_iterator(self.into_iter(), w)
}
}
impl EpeeValue for HexVec {
const MARKER: Marker = <Vec<u8> as EpeeValue>::MARKER;
fn read<B: Buf>(r: &mut B, marker: &Marker) -> Result<Self> {
Ok(Self(<Vec<u8> as EpeeValue>::read(r, marker)?))
}
fn write<B: BufMut>(self, w: &mut B) -> Result<()> {
<Vec<u8> as EpeeValue>::write(self.0, w)
}
}
macro_rules! epee_seq {
($val:ty) => {
impl EpeeValue for Vec<$val> {
@ -458,6 +506,7 @@ epee_seq!(u16);
epee_seq!(f64);
epee_seq!(bool);
epee_seq!(Vec<u8>);
epee_seq!(HexVec);
epee_seq!(String);
epee_seq!(Bytes);
epee_seq!(BytesMut);

View file

@ -417,7 +417,8 @@ impl<Z: BorshNetworkZone> Service<AddressBookRequest<Z>> for AddressBook<Z> {
AddressBookRequest::GetBan(addr) => Ok(AddressBookResponse::GetBan {
unban_instant: self.peer_unban_instant(&addr).map(Instant::into_std),
}),
AddressBookRequest::PeerlistSize
AddressBookRequest::Peerlist
| AddressBookRequest::PeerlistSize
| AddressBookRequest::ConnectionCount
| AddressBookRequest::SetBan(_)
| AddressBookRequest::GetBans

View file

@ -108,7 +108,8 @@ impl<N: NetworkZone> Service<AddressBookRequest<N>> for DummyAddressBook {
AddressBookRequest::GetBan(_) => AddressBookResponse::GetBan {
unban_instant: None,
},
AddressBookRequest::PeerlistSize
AddressBookRequest::Peerlist
| AddressBookRequest::PeerlistSize
| AddressBookRequest::ConnectionCount
| AddressBookRequest::SetBan(_)
| AddressBookRequest::GetBans

View file

@ -6,7 +6,7 @@ use cuprate_wire::{CoreSyncData, PeerListEntryBase};
use crate::{
client::InternalPeerID,
handles::ConnectionHandle,
types::{BanState, ConnectionInfo, SetBan},
types::{BanState, ConnectionInfo, Peerlist, SetBan},
NetZoneAddress, NetworkAddressIncorrectZone, NetworkZone,
};
@ -115,6 +115,9 @@ pub enum AddressBookRequest<Z: NetworkZone> {
/// Gets the specified number of white peers, or less if we don't have enough.
GetWhitePeers(usize),
/// Get info on all peers, white & grey.
Peerlist,
/// Get the amount of white & grey peers.
PeerlistSize,
@ -152,6 +155,9 @@ pub enum AddressBookResponse<Z: NetworkZone> {
/// Response to [`AddressBookRequest::GetWhitePeers`].
Peers(Vec<ZoneSpecificPeerListEntryBase<Z::Addr>>),
/// Response to [`AddressBookRequest::Peerlist`].
Peerlist(Peerlist<Z::Addr>),
/// Response to [`AddressBookRequest::PeerlistSize`].
PeerlistSize { white: usize, grey: usize },

View file

@ -5,7 +5,7 @@ use std::time::{Duration, Instant};
use cuprate_pruning::PruningSeed;
use cuprate_types::{AddressType, ConnectionState};
use crate::NetZoneAddress;
use crate::{NetZoneAddress, ZoneSpecificPeerListEntryBase};
/// Data within [`crate::services::AddressBookRequest::SetBan`].
pub struct SetBan<A: NetZoneAddress> {
@ -94,3 +94,10 @@ pub struct Span<A: NetZoneAddress> {
pub speed: u32,
pub start_block_height: u64,
}
/// Used in RPC's `/get_peer_list`.
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct Peerlist<A: NetZoneAddress> {
pub white: Vec<ZoneSpecificPeerListEntryBase<A>>,
pub grey: Vec<ZoneSpecificPeerListEntryBase<A>>,
}

View file

@ -15,7 +15,7 @@ dummy = ["dep:cuprate-helper", "dep:futures"]
[dependencies]
cuprate-epee-encoding = { workspace = true, default-features = false }
cuprate-json-rpc = { workspace = true, default-features = false }
cuprate-rpc-types = { workspace = true, features = ["serde", "epee"], default-features = false }
cuprate-rpc-types = { workspace = true, features = ["serde", "epee", "from"], default-features = false }
cuprate-helper = { workspace = true, features = ["asynch"], default-features = false, optional = true }
anyhow = { workspace = true }

View file

@ -59,7 +59,7 @@ The error type must always be [`anyhow::Error`].
The `RpcHandler` must also hold some state that is required
for RPC server operation.
The only state currently needed is [`RpcHandler::restricted`], which determines if an RPC
The only state currently needed is [`RpcHandler::is_restricted`], which determines if an RPC
server is restricted or not, and thus, if some endpoints/methods are allowed or not.
# Unknown endpoint behavior

View file

@ -72,7 +72,13 @@ macro_rules! generate_endpoints_inner {
paste::paste! {
{
// Check if restricted.
if [<$variant Request>]::IS_RESTRICTED && $handler.restricted() {
//
// INVARIANT:
// The RPC handler functions in `cuprated` depend on this line existing,
// the functions themselves do not check if they are being called
// from an (un)restricted context. This line must be here or all
// methods will be allowed to be called freely.
if [<$variant Request>]::IS_RESTRICTED && $handler.is_restricted() {
// TODO: mimic `monerod` behavior.
return Err(StatusCode::FORBIDDEN);
}

View file

@ -1,15 +1,10 @@
//! JSON-RPC 2.0 endpoint route functions.
//---------------------------------------------------------------------------------------------------- Import
use std::borrow::Cow;
use axum::{extract::State, http::StatusCode, Json};
use tower::ServiceExt;
use cuprate_json_rpc::{
error::{ErrorCode, ErrorObject},
Id, Response,
};
use cuprate_json_rpc::{Id, Response};
use cuprate_rpc_types::{
json::{JsonRpcRequest, JsonRpcResponse},
RpcCallValue,
@ -37,16 +32,18 @@ pub(crate) async fn json_rpc<H: RpcHandler>(
// Return early if this RPC server is restricted and
// the requested method is only for non-restricted RPC.
if request.body.is_restricted() && handler.restricted() {
let error_object = ErrorObject {
code: ErrorCode::ServerError(-1 /* TODO */),
message: Cow::Borrowed("Restricted. TODO: mimic monerod message"),
data: None,
};
let response = Response::err(id, error_object);
return Ok(Json(response));
//
// INVARIANT:
// The RPC handler functions in `cuprated` depend on this line existing,
// the functions themselves do not check if they are being called
// from an (un)restricted context. This line must be here or all
// methods will be allowed to be called freely.
if request.body.is_restricted() && handler.is_restricted() {
// The error when a restricted JSON-RPC method is called as per:
//
// - <https://github.com/monero-project/monero/blob/893916ad091a92e765ce3241b94e706ad012b62a/contrib/epee/include/net/http_server_handlers_map2.h#L244-L252>
// - <https://github.com/monero-project/monero/blob/cc73fe71162d564ffda8e549b79a350bca53c454/src/rpc/core_rpc_server.h#L188>
return Ok(Json(Response::method_not_found(id)));
}
// Send request.

View file

@ -6,4 +6,4 @@
pub(crate) mod bin;
pub(crate) mod fallback;
pub(crate) mod json_rpc;
pub(crate) mod other;
pub(crate) mod other_json;

View file

@ -75,7 +75,13 @@ macro_rules! generate_endpoints_inner {
paste::paste! {
{
// Check if restricted.
if [<$variant Request>]::IS_RESTRICTED && $handler.restricted() {
//
// INVARIANT:
// The RPC handler functions in `cuprated` depend on this line existing,
// the functions themselves do not check if they are being called
// from an (un)restricted context. This line must be here or all
// methods will be allowed to be called freely.
if [<$variant Request>]::IS_RESTRICTED && $handler.is_restricted() {
// TODO: mimic `monerod` behavior.
return Err(StatusCode::FORBIDDEN);
}

View file

@ -4,7 +4,7 @@
use axum::Router;
use crate::{
route::{bin, fallback, json_rpc, other},
route::{bin, fallback, json_rpc, other_json},
rpc_handler::RpcHandler,
};
@ -140,36 +140,36 @@ generate_router_builder! {
json_rpc => "/json_rpc" => json_rpc::json_rpc => (get, post),
// Other JSON routes.
other_get_height => "/get_height" => other::get_height => (get, post),
other_getheight => "/getheight" => other::get_height => (get, post),
other_get_transactions => "/get_transactions" => other::get_transactions => (get, post),
other_gettransactions => "/gettransactions" => other::get_transactions => (get, post),
other_get_alt_blocks_hashes => "/get_alt_blocks_hashes" => other::get_alt_blocks_hashes => (get, post),
other_is_key_image_spent => "/is_key_image_spent" => other::is_key_image_spent => (get, post),
other_send_raw_transaction => "/send_raw_transaction" => other::send_raw_transaction => (get, post),
other_sendrawtransaction => "/sendrawtransaction" => other::send_raw_transaction => (get, post),
other_start_mining => "/start_mining" => other::start_mining => (get, post),
other_stop_mining => "/stop_mining" => other::stop_mining => (get, post),
other_mining_status => "/mining_status" => other::mining_status => (get, post),
other_save_bc => "/save_bc" => other::save_bc => (get, post),
other_get_peer_list => "/get_peer_list" => other::get_peer_list => (get, post),
other_get_public_nodes => "/get_public_nodes" => other::get_public_nodes => (get, post),
other_set_log_hash_rate => "/set_log_hash_rate" => other::set_log_hash_rate => (get, post),
other_set_log_level => "/set_log_level" => other::set_log_level => (get, post),
other_set_log_categories => "/set_log_categories" => other::set_log_categories => (get, post),
other_get_transaction_pool => "/get_transaction_pool" => other::get_transaction_pool => (get, post),
other_get_transaction_pool_hashes => "/get_transaction_pool_hashes" => other::get_transaction_pool_hashes => (get, post),
other_get_transaction_pool_stats => "/get_transaction_pool_stats" => other::get_transaction_pool_stats => (get, post),
other_set_bootstrap_daemon => "/set_bootstrap_daemon" => other::set_bootstrap_daemon => (get, post),
other_stop_daemon => "/stop_daemon" => other::stop_daemon => (get, post),
other_get_net_stats => "/get_net_stats" => other::get_net_stats => (get, post),
other_get_limit => "/get_limit" => other::get_limit => (get, post),
other_set_limit => "/set_limit" => other::set_limit => (get, post),
other_out_peers => "/out_peers" => other::out_peers => (get, post),
other_in_peers => "/in_peers" => other::in_peers => (get, post),
other_get_outs => "/get_outs" => other::get_outs => (get, post),
other_update => "/update" => other::update => (get, post),
other_pop_blocks => "/pop_blocks" => other::pop_blocks => (get, post),
other_get_height => "/get_height" => other_json::get_height => (get, post),
other_getheight => "/getheight" => other_json::get_height => (get, post),
other_get_transactions => "/get_transactions" => other_json::get_transactions => (get, post),
other_gettransactions => "/gettransactions" => other_json::get_transactions => (get, post),
other_get_alt_blocks_hashes => "/get_alt_blocks_hashes" => other_json::get_alt_blocks_hashes => (get, post),
other_is_key_image_spent => "/is_key_image_spent" => other_json::is_key_image_spent => (get, post),
other_send_raw_transaction => "/send_raw_transaction" => other_json::send_raw_transaction => (get, post),
other_sendrawtransaction => "/sendrawtransaction" => other_json::send_raw_transaction => (get, post),
other_start_mining => "/start_mining" => other_json::start_mining => (get, post),
other_stop_mining => "/stop_mining" => other_json::stop_mining => (get, post),
other_mining_status => "/mining_status" => other_json::mining_status => (get, post),
other_save_bc => "/save_bc" => other_json::save_bc => (get, post),
other_get_peer_list => "/get_peer_list" => other_json::get_peer_list => (get, post),
other_get_public_nodes => "/get_public_nodes" => other_json::get_public_nodes => (get, post),
other_set_log_hash_rate => "/set_log_hash_rate" => other_json::set_log_hash_rate => (get, post),
other_set_log_level => "/set_log_level" => other_json::set_log_level => (get, post),
other_set_log_categories => "/set_log_categories" => other_json::set_log_categories => (get, post),
other_get_transaction_pool => "/get_transaction_pool" => other_json::get_transaction_pool => (get, post),
other_get_transaction_pool_hashes => "/get_transaction_pool_hashes" => other_json::get_transaction_pool_hashes => (get, post),
other_get_transaction_pool_stats => "/get_transaction_pool_stats" => other_json::get_transaction_pool_stats => (get, post),
other_set_bootstrap_daemon => "/set_bootstrap_daemon" => other_json::set_bootstrap_daemon => (get, post),
other_stop_daemon => "/stop_daemon" => other_json::stop_daemon => (get, post),
other_get_net_stats => "/get_net_stats" => other_json::get_net_stats => (get, post),
other_get_limit => "/get_limit" => other_json::get_limit => (get, post),
other_set_limit => "/set_limit" => other_json::set_limit => (get, post),
other_out_peers => "/out_peers" => other_json::out_peers => (get, post),
other_in_peers => "/in_peers" => other_json::in_peers => (get, post),
other_get_outs => "/get_outs" => other_json::get_outs => (get, post),
other_update => "/update" => other_json::update => (get, post),
other_pop_blocks => "/pop_blocks" => other_json::pop_blocks => (get, post),
// Binary routes.
bin_get_blocks => "/get_blocks.bin" => bin::get_blocks => (get, post),

View file

@ -46,5 +46,5 @@ pub trait RpcHandler:
///
/// will automatically be denied access when using the
/// [`axum::Router`] provided by [`RouterBuilder`](crate::RouterBuilder).
fn restricted(&self) -> bool;
fn is_restricted(&self) -> bool;
}

View file

@ -31,7 +31,7 @@ use crate::rpc_handler::RpcHandler;
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
pub struct RpcHandlerDummy {
/// Should this RPC server be [restricted](RpcHandler::restricted)?
/// Should this RPC server be [restricted](RpcHandler::is_restricted)?
///
/// The dummy will honor this [`bool`]
/// on restricted methods/endpoints.
@ -39,7 +39,7 @@ pub struct RpcHandlerDummy {
}
impl RpcHandler for RpcHandlerDummy {
fn restricted(&self) -> bool {
fn is_restricted(&self) -> bool {
self.restricted
}
}
@ -59,6 +59,7 @@ impl Service<JsonRpcRequest> for RpcHandlerDummy {
#[expect(clippy::default_trait_access)]
let resp = match req {
Req::GetBlockTemplate(_) => Resp::GetBlockTemplate(Default::default()),
Req::GetBlockCount(_) => Resp::GetBlockCount(Default::default()),
Req::OnGetBlockHash(_) => Resp::OnGetBlockHash(Default::default()),
Req::SubmitBlock(_) => Resp::SubmitBlock(Default::default()),

View file

@ -12,20 +12,33 @@ keywords = ["cuprate", "rpc", "types", "monero"]
default = ["serde", "epee"]
serde = ["dep:serde", "cuprate-fixed-bytes/serde", "cuprate-types/serde"]
epee = ["dep:cuprate-epee-encoding", "cuprate-types/epee"]
from = [
"dep:cuprate-helper",
"cuprate-helper/map",
"cuprate-helper/fmt",
"dep:cuprate-p2p-core",
"dep:hex"
]
[dependencies]
cuprate-epee-encoding = { workspace = true, optional = true }
cuprate-fixed-bytes = { workspace = true }
cuprate-types = { workspace = true, default-features = false }
cuprate-hex = { workspace = true }
cuprate-epee-encoding = { workspace = true, optional = true }
cuprate-types = { workspace = true, default-features = false, features = ["rpc", "json"] }
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 }
[dev-dependencies]
cuprate-test-utils = { workspace = true }
serde = { workspace = true }
serde_json = { workspace = true }
hex-literal = { workspace = true }
pretty_assertions = { workspace = true }
serde = { workspace = true }
serde_json = { workspace = true }
[lints]
workspace = true

View file

@ -105,9 +105,10 @@ For `enum`s that encapsulate all request/response types, see:
# Feature flags
List of feature flags for `cuprate-rpc-types`.
All are enabled by default.
Enabled by default: `serde`, `epee`.
| Feature flag | Does what |
|--------------|-----------|
| `serde` | Implements `serde` on all types
| `epee` | Implements `cuprate_epee_encoding` on all types
| `epee` | Implements `cuprate_epee_encoding` on all types
| `from` | Implements [`From`] on multiple duplicate types throughout the Cuprate codebase

View file

@ -29,7 +29,7 @@ use crate::{macros::monero_definition_link, misc::Status};
//---------------------------------------------------------------------------------------------------- Requests
/// A base for RPC request types that support RPC payment.
///
#[doc = monero_definition_link!(cc73fe71162d564ffda8e549b79a350bca53c454, "rpc/core_rpc_server_commands_defs.h", 114..=122)]
#[doc = monero_definition_link!("cc73fe71162d564ffda8e549b79a350bca53c454", "rpc/core_rpc_server_commands_defs.h", 114..=122)]
#[derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct AccessRequestBase {
@ -44,7 +44,7 @@ epee_object! {
}
//---------------------------------------------------------------------------------------------------- Responses
#[doc = monero_definition_link!(cc73fe71162d564ffda8e549b79a350bca53c454, "rpc/core_rpc_server_commands_defs.h", 101..=112)]
#[doc = monero_definition_link!("cc73fe71162d564ffda8e549b79a350bca53c454", "rpc/core_rpc_server_commands_defs.h", 101..=112)]
/// The most common base for responses.
#[derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
@ -98,7 +98,7 @@ epee_object! {
untrusted: bool,
}
#[doc = monero_definition_link!(cc73fe71162d564ffda8e549b79a350bca53c454, "rpc/core_rpc_server_commands_defs.h", 124..=136)]
#[doc = monero_definition_link!("cc73fe71162d564ffda8e549b79a350bca53c454", "rpc/core_rpc_server_commands_defs.h", 124..=136)]
/// A base for RPC response types that support RPC payment.
#[derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]

View file

@ -1,6 +1,6 @@
//! Binary types from [`.bin` endpoints](https://www.getmonero.org/resources/developer-guides/daemon-rpc.html#get_blocksbin).
//!
//! All types are originally defined in [`rpc/core_rpc_server_commands_defs.h`](https://github.com/monero-project/monero/blob/cc73fe71162d564ffda8e549b79a350bca53c454/src/rpc/core_rpc_server_commands_defs.h).
//! All types are originally defined in [`rpc/core_rpc_server_commands_defs.h`](https://github.com/monero-project/monero/blob/"cc73fe71162d564ffda8e549b79a350bca53c454"/src/rpc/core_rpc_server_commands_defs.h).
//---------------------------------------------------------------------------------------------------- Import
use cuprate_fixed_bytes::ByteArrayVec;
@ -11,43 +11,46 @@ use serde::{Deserialize, Serialize};
#[cfg(feature = "epee")]
use cuprate_epee_encoding::container_as_blob::ContainerAsBlob;
use cuprate_types::BlockCompleteEntry;
use cuprate_types::{
rpc::{BlockOutputIndices, PoolInfo},
BlockCompleteEntry,
};
use crate::{
base::AccessResponseBase,
macros::define_request_and_response,
misc::{BlockOutputIndices, GetOutputsOut, OutKeyBin, PoolInfo},
misc::{GetOutputsOut, OutKeyBin},
rpc_call::RpcCallValue,
};
#[cfg(any(feature = "epee", feature = "serde"))]
use crate::defaults::{default_false, default_zero};
use crate::defaults::default;
//---------------------------------------------------------------------------------------------------- Definitions
define_request_and_response! {
get_blocks_by_heightbin,
cc73fe71162d564ffda8e549b79a350bca53c454 =>
"cc73fe71162d564ffda8e549b79a350bca53c454" =>
core_rpc_server_commands_defs.h => 264..=286,
GetBlocksByHeight,
Request {
heights: Vec<u64>,
},
AccessResponseBase {
blocks: Vec<BlockCompleteEntry>,
blocks: Vec<BlockCompleteEntry> = default::<Vec<BlockCompleteEntry>>(), "default",
}
}
define_request_and_response! {
get_hashesbin,
cc73fe71162d564ffda8e549b79a350bca53c454 =>
"cc73fe71162d564ffda8e549b79a350bca53c454" =>
core_rpc_server_commands_defs.h => 309..=338,
GetHashes,
Request {
block_ids: ByteArrayVec<32>,
block_ids: ByteArrayVec<32> = default::<ByteArrayVec<32>>(), "default",
start_height: u64,
},
AccessResponseBase {
m_blocks_ids: ByteArrayVec<32>,
m_blocks_ids: ByteArrayVec<32> = default::<ByteArrayVec<32>>(), "default",
start_height: u64,
current_height: u64,
}
@ -56,7 +59,7 @@ define_request_and_response! {
#[cfg(not(feature = "epee"))]
define_request_and_response! {
get_o_indexesbin,
cc73fe71162d564ffda8e549b79a350bca53c454 =>
"cc73fe71162d564ffda8e549b79a350bca53c454" =>
core_rpc_server_commands_defs.h => 487..=510,
GetOutputIndexes,
#[derive(Copy)]
@ -64,14 +67,14 @@ define_request_and_response! {
txid: [u8; 32],
},
AccessResponseBase {
o_indexes: Vec<u64>,
o_indexes: Vec<u64> = default::<Vec<u64>>(), "default",
}
}
#[cfg(feature = "epee")]
define_request_and_response! {
get_o_indexesbin,
cc73fe71162d564ffda8e549b79a350bca53c454 =>
"cc73fe71162d564ffda8e549b79a350bca53c454" =>
core_rpc_server_commands_defs.h => 487..=510,
GetOutputIndexes,
#[derive(Copy)]
@ -85,56 +88,54 @@ define_request_and_response! {
define_request_and_response! {
get_outsbin,
cc73fe71162d564ffda8e549b79a350bca53c454 =>
"cc73fe71162d564ffda8e549b79a350bca53c454" =>
core_rpc_server_commands_defs.h => 512..=565,
GetOuts,
Request {
outputs: Vec<GetOutputsOut>,
get_txid: bool = default_false(), "default_false",
outputs: Vec<GetOutputsOut> = default::<Vec<GetOutputsOut>>(), "default",
get_txid: bool,
},
AccessResponseBase {
outs: Vec<OutKeyBin>,
outs: Vec<OutKeyBin> = default::<Vec<OutKeyBin>>(), "default",
}
}
define_request_and_response! {
get_transaction_pool_hashesbin,
cc73fe71162d564ffda8e549b79a350bca53c454 =>
"cc73fe71162d564ffda8e549b79a350bca53c454" =>
core_rpc_server_commands_defs.h => 1593..=1613,
GetTransactionPoolHashes,
Request {},
AccessResponseBase {
tx_hashes: ByteArrayVec<32>,
tx_hashes: ByteArrayVec<32> = default::<ByteArrayVec<32>>(), "default",
}
}
define_request_and_response! {
get_blocksbin,
cc73fe71162d564ffda8e549b79a350bca53c454 =>
"cc73fe71162d564ffda8e549b79a350bca53c454" =>
core_rpc_server_commands_defs.h => 162..=262,
GetBlocks,
Request {
requested_info: u8 = default_zero::<u8>(), "default_zero",
// FIXME: This is a `std::list` in `monerod` because...?
block_ids: ByteArrayVec<32>,
requested_info: u8 = default::<u8>(), "default",
block_ids: ByteArrayVec<32> = default::<ByteArrayVec<32>>(), "default",
start_height: u64,
prune: bool,
no_miner_tx: bool = default_false(), "default_false",
pool_info_since: u64 = default_zero::<u64>(), "default_zero",
no_miner_tx: bool,
pool_info_since: u64 = default::<u64>(), "default",
},
// TODO: add `top_block_hash` field
// <https://github.com/monero-project/monero/blame/893916ad091a92e765ce3241b94e706ad012b62a/src/rpc/core_rpc_server_commands_defs.h#L263>
AccessResponseBase {
blocks: Vec<BlockCompleteEntry>,
blocks: Vec<BlockCompleteEntry> = default::<Vec<BlockCompleteEntry>>(), "default",
start_height: u64,
current_height: u64,
output_indices: Vec<BlockOutputIndices>,
daemon_time: u64 = default_zero::<u64>(), "default_zero",
// FIXME: use `default()` after <https://github.com/Cuprate/cuprate/pull/355>
pool_info: PoolInfo = PoolInfo::None, "PoolInfo::default",
output_indices: Vec<BlockOutputIndices> = default::<Vec<BlockOutputIndices>>(), "default",
daemon_time: u64 = default::<u64>(), "default",
pool_info: PoolInfo = default::<PoolInfo>(), "default",
}
}

View file

@ -24,28 +24,31 @@ use crate::macros::monero_definition_link;
// Note that these are _distinct_ from the ones in ZMQ:
// <https://github.com/monero-project/monero/blob/cc73fe71162d564ffda8e549b79a350bca53c454/src/rpc/message.cpp#L40-L44>.
#[doc = monero_definition_link!(cc73fe71162d564ffda8e549b79a350bca53c454, "/rpc/core_rpc_server_commands_defs.h", 78)]
#[doc = monero_definition_link!("cc73fe71162d564ffda8e549b79a350bca53c454", "/rpc/core_rpc_server_commands_defs.h", 78)]
pub const CORE_RPC_STATUS_OK: &str = "OK";
#[doc = monero_definition_link!(cc73fe71162d564ffda8e549b79a350bca53c454, "/rpc/core_rpc_server_commands_defs.h", 79)]
#[doc = monero_definition_link!("cc73fe71162d564ffda8e549b79a350bca53c454", "/rpc/core_rpc_server_commands_defs.h", 79)]
pub const CORE_RPC_STATUS_BUSY: &str = "BUSY";
#[doc = monero_definition_link!(cc73fe71162d564ffda8e549b79a350bca53c454, "/rpc/core_rpc_server_commands_defs.h", 80)]
#[doc = monero_definition_link!("cc73fe71162d564ffda8e549b79a350bca53c454", "/rpc/core_rpc_server_commands_defs.h", 80)]
pub const CORE_RPC_STATUS_NOT_MINING: &str = "NOT MINING";
#[doc = monero_definition_link!(cc73fe71162d564ffda8e549b79a350bca53c454, "/rpc/core_rpc_server_commands_defs.h", 81)]
#[doc = monero_definition_link!("cc73fe71162d564ffda8e549b79a350bca53c454", "/rpc/core_rpc_server_commands_defs.h", 81)]
pub const CORE_RPC_STATUS_PAYMENT_REQUIRED: &str = "PAYMENT REQUIRED";
/// Not defined in `monerod` although used frequently.
pub const CORE_RPC_STATUS_FAILED: &str = "Failed";
//---------------------------------------------------------------------------------------------------- Versions
#[doc = monero_definition_link!(cc73fe71162d564ffda8e549b79a350bca53c454, "/rpc/core_rpc_server_commands_defs.h", 90)]
#[doc = monero_definition_link!("cc73fe71162d564ffda8e549b79a350bca53c454", "/rpc/core_rpc_server_commands_defs.h", 90)]
/// RPC major version.
pub const CORE_RPC_VERSION_MAJOR: u32 = 3;
#[doc = monero_definition_link!(cc73fe71162d564ffda8e549b79a350bca53c454, "/rpc/core_rpc_server_commands_defs.h", 91)]
#[doc = monero_definition_link!("cc73fe71162d564ffda8e549b79a350bca53c454", "/rpc/core_rpc_server_commands_defs.h", 91)]
/// RPC miror version.
pub const CORE_RPC_VERSION_MINOR: u32 = 14;
#[doc = monero_definition_link!(cc73fe71162d564ffda8e549b79a350bca53c454, "/rpc/core_rpc_server_commands_defs.h", 92..=93)]
#[doc = monero_definition_link!("cc73fe71162d564ffda8e549b79a350bca53c454", "/rpc/core_rpc_server_commands_defs.h", 92..=93)]
/// RPC version.
pub const CORE_RPC_VERSION: u32 = (CORE_RPC_VERSION_MAJOR << 16) | CORE_RPC_VERSION_MINOR;

View file

@ -5,65 +5,29 @@
//! has a [`crate::json::GetBlockRequest::height`]
//! field and a [`crate::json::GetBlockRequest::hash`]
//! field, when the RPC interface reads JSON without
//! `height`, it will use [`default_height`] to fill that in.
//! `height`, it will use [`default`] to fill that in.
//---------------------------------------------------------------------------------------------------- Import
//---------------------------------------------------------------------------------------------------- TODO
/// Default [`bool`] type used in request/response types, `false`.
#[inline]
pub(crate) const fn default_false() -> bool {
false
}
/// Default [`bool`] type used in _some_ request/response types, `true`.
#[inline]
pub(crate) const fn default_true() -> bool {
true
}
/// Default [`String`] type used in request/response types.
#[inline]
pub(crate) const fn default_string() -> String {
String::new()
}
/// Default block height used in request/response types.
#[inline]
pub(crate) const fn default_height() -> u64 {
0
}
/// Default [`Vec`] used in request/response types.
#[inline]
pub(crate) const fn default_vec<T>() -> Vec<T> {
Vec::new()
}
/// Default `0` value used in request/response types.
#[inline]
pub(crate) fn default_zero<T: From<u8>>() -> T {
T::from(0)
}
/// Default `1` value used in request/response types.
#[inline]
pub(crate) fn default_one<T: From<u8>>() -> T {
T::from(1)
}
/// Generate a default `T` to be used in request/response types.
#[inline]
pub(crate) fn default<T: Default>() -> T {
T::default()
}
//---------------------------------------------------------------------------------------------------- Tests
#[cfg(test)]
mod test {
use super::*;
/// Tests that [`default_zero`] returns `0` on all unsigned numbers.
#[test]
fn zero() {
assert_eq!(default_zero::<usize>(), 0);
assert_eq!(default_zero::<u64>(), 0);
assert_eq!(default_zero::<u32>(), 0);
assert_eq!(default_zero::<u16>(), 0);
assert_eq!(default_zero::<u8>(), 0);
}
}
mod test {}

190
rpc/types/src/from.rs Normal file
View file

@ -0,0 +1,190 @@
//! [`From`] implementations from other crate's types into [`crate`] types.
//!
//! Only non-crate types are imported, all crate types use `crate::`.
use std::{
net::{SocketAddr, SocketAddrV4},
time::Duration,
};
use cuprate_helper::{fmt::HexPrefix, map::ipv4_from_u32};
use cuprate_hex::{Hex, HexVec};
use cuprate_p2p_core::{
types::{ConnectionId, ConnectionInfo, SetBan, Span},
NetZoneAddress,
};
use cuprate_types::rpc::{BlockHeader, ChainInfo, HistogramEntry, SpentKeyImageInfo, TxInfo};
impl From<BlockHeader> for crate::misc::BlockHeader {
fn from(x: BlockHeader) -> Self {
Self {
block_size: x.block_weight,
block_weight: x.block_weight,
cumulative_difficulty_top64: x.cumulative_difficulty_top64,
cumulative_difficulty: x.cumulative_difficulty,
depth: x.depth,
difficulty_top64: x.difficulty_top64,
difficulty: x.difficulty,
hash: Hex(x.hash),
height: x.height,
long_term_weight: x.long_term_weight,
major_version: x.major_version,
miner_tx_hash: Hex(x.miner_tx_hash),
minor_version: x.minor_version,
nonce: x.nonce,
num_txes: x.num_txes,
orphan_status: x.orphan_status,
pow_hash: x.pow_hash.map_or_else(HexVec::new, |a| HexVec(a.into())),
prev_hash: Hex(x.prev_hash),
reward: x.reward,
timestamp: x.timestamp,
// FIXME: if we made a type that automatically did `hex_prefix_u128`,
// we wouldn't need `crate::misc::BlockHeader`.
wide_cumulative_difficulty: (x.cumulative_difficulty, x.cumulative_difficulty_top64)
.hex_prefix(),
wide_difficulty: (x.difficulty, x.difficulty_top64).hex_prefix(),
}
}
}
impl<A: NetZoneAddress> From<ConnectionInfo<A>> for crate::misc::ConnectionInfo {
fn from(x: ConnectionInfo<A>) -> Self {
let (ip, port) = match x.socket_addr {
Some(socket) => (socket.ip().to_string(), socket.port().to_string()),
None => (String::new(), String::new()),
};
Self {
address: x.address.to_string(),
address_type: x.address_type,
avg_download: x.avg_download,
avg_upload: x.avg_upload,
connection_id: String::from(ConnectionId::DEFAULT_STR),
current_download: x.current_download,
current_upload: x.current_upload,
height: x.height,
host: x.host,
incoming: x.incoming,
ip,
live_time: x.live_time,
localhost: x.localhost,
local_ip: x.local_ip,
peer_id: hex::encode(x.peer_id.to_ne_bytes()),
port,
pruning_seed: x.pruning_seed.compress(),
recv_count: x.recv_count,
recv_idle_time: x.recv_idle_time,
rpc_credits_per_hash: x.rpc_credits_per_hash,
rpc_port: x.rpc_port,
send_count: x.send_count,
send_idle_time: x.send_idle_time,
state: x.state,
support_flags: x.support_flags,
}
}
}
// TODO: support non-clearnet addresses.
impl From<crate::misc::SetBan> for SetBan<SocketAddr> {
fn from(x: crate::misc::SetBan) -> Self {
let address = SocketAddr::V4(SocketAddrV4::new(ipv4_from_u32(x.ip), 0));
let ban = if x.ban {
Some(Duration::from_secs(x.seconds.into()))
} else {
None
};
Self { address, ban }
}
}
// TODO: do we need this type?
impl From<HistogramEntry> for crate::misc::HistogramEntry {
fn from(x: HistogramEntry) -> Self {
Self {
amount: x.amount,
total_instances: x.total_instances,
unlocked_instances: x.unlocked_instances,
recent_instances: x.recent_instances,
}
}
}
impl From<ChainInfo> for crate::misc::ChainInfo {
fn from(x: ChainInfo) -> Self {
Self {
block_hash: Hex(x.block_hash),
block_hashes: x.block_hashes.into_iter().map(Hex).collect(),
difficulty_top64: x.difficulty_top64,
difficulty: x.difficulty,
height: x.height,
length: x.length,
main_chain_parent_block: Hex(x.main_chain_parent_block),
wide_difficulty: (x.difficulty, x.difficulty_top64).hex_prefix(),
}
}
}
// TODO: support non-clearnet addresses.
impl From<Span<SocketAddr>> for crate::misc::Span {
fn from(x: Span<SocketAddr>) -> Self {
Self {
connection_id: String::from(ConnectionId::DEFAULT_STR),
nblocks: x.nblocks,
rate: x.rate,
remote_address: x.remote_address.to_string(),
size: x.size,
speed: x.speed,
start_block_height: x.start_block_height,
}
}
}
impl From<TxInfo> for crate::misc::TxInfo {
fn from(x: TxInfo) -> Self {
Self {
blob_size: x.blob_size,
do_not_relay: x.do_not_relay,
double_spend_seen: x.double_spend_seen,
fee: x.fee,
id_hash: Hex(x.id_hash),
kept_by_block: x.kept_by_block,
last_failed_height: x.last_failed_height,
last_failed_id_hash: Hex(x.last_failed_id_hash),
last_relayed_time: x.last_relayed_time,
max_used_block_height: x.max_used_block_height,
max_used_block_id_hash: Hex(x.max_used_block_id_hash),
receive_time: x.receive_time,
relayed: x.relayed,
tx_blob: HexVec(x.tx_blob),
tx_json: x.tx_json,
weight: x.weight,
}
}
}
impl From<SpentKeyImageInfo> for crate::misc::SpentKeyImageInfo {
fn from(x: SpentKeyImageInfo) -> Self {
Self {
id_hash: Hex(x.id_hash),
txs_hashes: x.txs_hashes.into_iter().map(Hex).collect(),
}
}
}
impl From<crate::misc::OutKeyBin> for crate::misc::OutKey {
fn from(x: crate::misc::OutKeyBin) -> Self {
Self {
key: Hex(x.key),
mask: Hex(x.mask),
unlocked: x.unlocked,
height: x.height,
txid: if x.txid == [0; 32] {
HexVec::new()
} else {
HexVec::from(x.txid)
},
}
}
}

File diff suppressed because it is too large Load diff

View file

@ -9,6 +9,8 @@ mod constants;
#[cfg(any(feature = "serde", feature = "epee"))]
mod defaults;
mod free;
#[cfg(feature = "from")]
mod from;
mod macros;
mod rpc_call;
@ -22,7 +24,7 @@ pub mod misc;
pub mod other;
pub use constants::{
CORE_RPC_STATUS_BUSY, CORE_RPC_STATUS_NOT_MINING, CORE_RPC_STATUS_OK,
CORE_RPC_STATUS_BUSY, CORE_RPC_STATUS_FAILED, CORE_RPC_STATUS_NOT_MINING, CORE_RPC_STATUS_OK,
CORE_RPC_STATUS_PAYMENT_REQUIRED, CORE_RPC_VERSION, CORE_RPC_VERSION_MAJOR,
CORE_RPC_VERSION_MINOR,
};

View file

@ -50,7 +50,7 @@ macro_rules! define_request_and_response {
// The commit hash and `$file.$extension` in which this type is defined in
// the Monero codebase in the `rpc/` directory, followed by the specific lines.
$monero_code_commit:ident =>
$monero_code_commit:literal =>
$monero_code_filename:ident.
$monero_code_filename_extension:ident =>
$monero_code_line_start:literal..=
@ -291,6 +291,7 @@ macro_rules! define_response {
}
) => {
$( #[$attr] )*
#[cfg_attr(feature = "serde", serde(default))] // TODO: link epee field not serializing oddity
pub struct $t {
$(
$( #[$field_attr] )*
@ -328,6 +329,7 @@ macro_rules! define_response {
}
) => {
$( #[$attr] )*
#[cfg_attr(feature = "serde", serde(default))] // TODO: link epee field not serializing oddity
pub struct $t {
#[cfg_attr(feature = "serde", serde(flatten))]
pub base: $base,
@ -371,7 +373,7 @@ macro_rules! define_request_and_response_doc {
$request_or_response:literal => $request_or_response_type:ident,
$monero_daemon_rpc_doc_link:ident,
$monero_code_commit:ident,
$monero_code_commit:literal,
$monero_code_filename:ident,
$monero_code_filename_extension:ident,
$monero_code_line_start:literal,
@ -381,7 +383,7 @@ macro_rules! define_request_and_response_doc {
"",
"[Definition](",
"https://github.com/monero-project/monero/blob/",
stringify!($monero_code_commit),
$monero_code_commit,
"/src/rpc/",
stringify!($monero_code_filename),
".",
@ -408,13 +410,13 @@ pub(crate) use define_request_and_response_doc;
/// Output a string link to `monerod` source code.
macro_rules! monero_definition_link {
(
$commit:ident, // Git commit hash
$commit:literal, // Git commit hash
$file_path:literal, // File path within `monerod`'s `src/`, e.g. `rpc/core_rpc_server_commands_defs.h`
$start:literal$(..=$end:literal)? // File lines, e.g. `0..=123` or `0`
) => {
concat!(
"[Definition](https://github.com/monero-project/monero/blob/",
stringify!($commit),
$commit,
"/src/",
$file_path,
"#L",

View file

@ -16,7 +16,7 @@ use cuprate_epee_encoding::{
///
/// Used for [`Distribution::CompressedBinary::distribution`].
#[doc = crate::macros::monero_definition_link!(
cc73fe71162d564ffda8e549b79a350bca53c454,
"cc73fe71162d564ffda8e549b79a350bca53c454",
"rpc/core_rpc_server_commands_defs.h",
45..=55
)]
@ -29,7 +29,7 @@ fn compress_integer_array(_: &[u64]) -> Vec<u8> {
///
/// Used for [`Distribution::CompressedBinary::distribution`].
#[doc = crate::macros::monero_definition_link!(
cc73fe71162d564ffda8e549b79a350bca53c454,
"cc73fe71162d564ffda8e549b79a350bca53c454",
"rpc/core_rpc_server_commands_defs.h",
57..=72
)]
@ -40,7 +40,7 @@ fn decompress_integer_array(_: &[u8]) -> Vec<u64> {
//---------------------------------------------------------------------------------------------------- Distribution
#[doc = crate::macros::monero_definition_link!(
cc73fe71162d564ffda8e549b79a350bca53c454,
"cc73fe71162d564ffda8e549b79a350bca53c454",
"rpc/core_rpc_server_commands_defs.h",
2468..=2508
)]

View file

@ -14,24 +14,17 @@
//---------------------------------------------------------------------------------------------------- Mod
mod binary_string;
mod distribution;
mod key_image_spent_status;
#[expect(clippy::module_inception)]
mod misc;
mod pool_info;
mod pool_info_extent;
mod requested_info;
mod status;
mod tx_entry;
mod types;
pub use binary_string::BinaryString;
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, TxInfo, TxOutputIndices, TxpoolHisto, TxpoolStats,
};
pub use pool_info::PoolInfo;
pub use pool_info_extent::PoolInfoExtent;
pub use requested_info::RequestedInfo;
pub use status::Status;
pub use tx_entry::TxEntry;
pub use tx_entry::{TxEntry, TxEntryType};
pub use types::{
BlockHeader, ChainInfo, ConnectionInfo, GetBan, GetOutputsOut, HistogramEntry, OutKey,
OutKeyBin, SetBan, Span, SpentKeyImageInfo, SyncInfoPeer, TxInfo,
};

View file

@ -0,0 +1,100 @@
//! [`RequestedInfo`]
//---------------------------------------------------------------------------------------------------- Use
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
#[cfg(feature = "epee")]
use cuprate_epee_encoding::{
error,
macros::bytes::{Buf, BufMut},
EpeeValue, Marker,
};
//---------------------------------------------------------------------------------------------------- RequestedInfo
#[doc = crate::macros::monero_definition_link!(
"cc73fe71162d564ffda8e549b79a350bca53c454",
"rpc/core_rpc_server_commands_defs.h",
178..=183
)]
/// Used in [`crate::bin::GetBlocksRequest`].
#[derive(Copy, Clone, Default, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(try_from = "u8", into = "u8"))]
#[repr(u8)]
pub enum RequestedInfo {
#[default]
BlocksOnly = 0,
BlocksAndPool = 1,
PoolOnly = 2,
}
impl RequestedInfo {
/// Convert [`Self`] to a [`u8`].
///
/// ```rust
/// use cuprate_rpc_types::misc::RequestedInfo as R;
///
/// assert_eq!(R::BlocksOnly.to_u8(), 0);
/// assert_eq!(R::BlocksAndPool.to_u8(), 1);
/// assert_eq!(R::PoolOnly.to_u8(), 2);
/// ```
pub const fn to_u8(self) -> u8 {
match self {
Self::BlocksOnly => 0,
Self::BlocksAndPool => 1,
Self::PoolOnly => 2,
}
}
/// Convert a [`u8`] to a [`Self`].
///
/// # Errors
/// This returns [`None`] if `u > 2`.
///
/// ```rust
/// use cuprate_rpc_types::misc::RequestedInfo as R;
///
/// assert_eq!(R::from_u8(0), Some(R::BlocksOnly));
/// assert_eq!(R::from_u8(1), Some(R::BlocksAndPool));
/// assert_eq!(R::from_u8(2), Some(R::PoolOnly));
/// assert_eq!(R::from_u8(3), None);
/// ```
pub const fn from_u8(u: u8) -> Option<Self> {
Some(match u {
0 => Self::BlocksOnly,
1 => Self::BlocksAndPool,
2 => Self::PoolOnly,
_ => return None,
})
}
}
impl From<RequestedInfo> for u8 {
fn from(value: RequestedInfo) -> Self {
value.to_u8()
}
}
impl TryFrom<u8> for RequestedInfo {
type Error = u8;
fn try_from(value: u8) -> Result<Self, Self::Error> {
Self::from_u8(value).ok_or(value)
}
}
#[cfg(feature = "epee")]
impl EpeeValue for RequestedInfo {
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(())
}
}

View file

@ -13,7 +13,7 @@ use cuprate_epee_encoding::{
};
use crate::constants::{
CORE_RPC_STATUS_BUSY, CORE_RPC_STATUS_NOT_MINING, CORE_RPC_STATUS_OK,
CORE_RPC_STATUS_BUSY, CORE_RPC_STATUS_FAILED, CORE_RPC_STATUS_NOT_MINING, CORE_RPC_STATUS_OK,
CORE_RPC_STATUS_PAYMENT_REQUIRED,
};
@ -33,31 +33,45 @@ 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_PAYMENT_REQUIRED, CORE_RPC_STATUS_FAILED
/// };
/// use serde_json::to_string;
/// use serde_json::{to_string, from_str};
///
/// let other = Status::Other("OTHER".into());
///
/// assert_eq!(to_string(&Status::Ok).unwrap(), r#""OK""#);
/// assert_eq!(to_string(&Status::Failed).unwrap(), r#""Failed""#);
/// 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(&other).unwrap(), r#""OTHER""#);
///
/// assert_eq!(from_str::<Status>(r#""Ok""#).unwrap(), Status::Ok);
/// assert_eq!(from_str::<Status>(r#""OK""#).unwrap(), Status::Ok);
/// assert_eq!(from_str::<Status>(r#""Failed""#).unwrap(), Status::Failed);
/// assert_eq!(from_str::<Status>(r#""FAILED""#).unwrap(), Status::Failed);
/// assert_eq!(from_str::<Status>(r#""Busy""#).unwrap(), Status::Busy);
/// assert_eq!(from_str::<Status>(r#""BUSY""#).unwrap(), Status::Busy);
/// assert_eq!(from_str::<Status>(r#""NOT MINING""#).unwrap(), Status::NotMining);
/// assert_eq!(from_str::<Status>(r#""PAYMENT REQUIRED""#).unwrap(), Status::PaymentRequired);
/// assert_eq!(from_str::<Status>(r#""OTHER""#).unwrap(), other);
///
/// assert_eq!(Status::Ok.as_ref(), CORE_RPC_STATUS_OK);
/// assert_eq!(Status::Failed.as_ref(), CORE_RPC_STATUS_FAILED);
/// 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!(other.as_ref(), "OTHER");
///
/// assert_eq!(format!("{}", Status::Ok), CORE_RPC_STATUS_OK);
/// assert_eq!(format!("{}", Status::Failed), CORE_RPC_STATUS_FAILED);
/// 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!("{}", other), "OTHER");
///
/// assert_eq!(format!("{:?}", Status::Ok), "Ok");
/// assert_eq!(format!("{:?}", Status::Failed), "Failed");
/// assert_eq!(format!("{:?}", Status::Busy), "Busy");
/// assert_eq!(format!("{:?}", Status::NotMining), "NotMining");
/// assert_eq!(format!("{:?}", Status::PaymentRequired), "PaymentRequired");
@ -70,12 +84,16 @@ pub enum Status {
// `#[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"))]
#[cfg_attr(feature = "serde", serde(rename = "OK", alias = "Ok"))]
#[default]
Ok,
/// Generic request failure.
#[cfg_attr(feature = "serde", serde(alias = "FAILED"))]
Failed,
/// The daemon is busy, try later; [`CORE_RPC_STATUS_BUSY`].
#[cfg_attr(feature = "serde", serde(rename = "BUSY"))]
#[cfg_attr(feature = "serde", serde(rename = "BUSY", alias = "Busy"))]
Busy,
/// The daemon is not mining; [`CORE_RPC_STATUS_NOT_MINING`].
@ -101,6 +119,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,
CORE_RPC_STATUS_FAILED => Self::Failed,
_ => Self::Other(s),
}
}
@ -110,6 +129,7 @@ impl AsRef<str> for Status {
fn as_ref(&self) -> &str {
match self {
Self::Ok => CORE_RPC_STATUS_OK,
Self::Failed => CORE_RPC_STATUS_FAILED,
Self::Busy => CORE_RPC_STATUS_BUSY,
Self::NotMining => CORE_RPC_STATUS_NOT_MINING,
Self::PaymentRequired => CORE_RPC_STATUS_PAYMENT_REQUIRED,

View file

@ -11,12 +11,14 @@ use cuprate_epee_encoding::{
EpeeObject, EpeeObjectBuilder,
};
use cuprate_hex::{Hex, HexVec};
#[cfg(feature = "serde")]
use crate::serde::{serde_false, serde_true};
//---------------------------------------------------------------------------------------------------- TxEntry
#[doc = crate::macros::monero_definition_link!(
cc73fe71162d564ffda8e549b79a350bca53c454,
"cc73fe71162d564ffda8e549b79a350bca53c454",
"rpc/core_rpc_server_commands_defs.h",
389..=428
)]
@ -28,100 +30,63 @@ use crate::serde::{serde_false, serde_true};
///
/// 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, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct TxEntry {
/// `cuprate_types::json::tx::Transaction` should be used
/// to create this JSON string in a type-safe manner.
pub as_json: String,
pub as_hex: HexVec,
pub double_spend_seen: bool,
pub tx_hash: Hex<32>,
pub prunable_as_hex: HexVec,
pub prunable_hash: Hex<32>,
pub pruned_as_hex: HexVec,
#[cfg_attr(feature = "serde", serde(flatten))]
pub tx_entry_type: TxEntryType,
}
/// Different fields in [`TxEntry`] variants.
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(untagged))]
pub enum TxEntry {
/// This entry exists in the transaction pool.
InPool {
/// This field is [flattened](https://serde.rs/field-attrs.html#flatten).
#[cfg_attr(feature = "serde", serde(flatten))]
prefix: TxEntryPrefix,
pub enum TxEntryType {
/// This transaction exists in the blockchain.
Blockchain {
block_height: u64,
block_timestamp: u64,
confirmations: u64,
output_indices: Vec<u64>,
#[cfg_attr(feature = "serde", serde(serialize_with = "serde_true"))]
/// Will always be serialized as `true`.
/// Will always be serialized as `false`.
#[cfg_attr(feature = "serde", serde(serialize_with = "serde_false"))]
in_pool: bool,
},
/// This entry _does not_ exist in the transaction pool.
NotInPool {
/// This field is [flattened](https://serde.rs/field-attrs.html#flatten).
#[cfg_attr(feature = "serde", serde(flatten))]
prefix: TxEntryPrefix,
/// This transaction exists in the transaction pool.
Pool {
received_timestamp: u64,
relayed: bool,
#[cfg_attr(feature = "serde", serde(serialize_with = "serde_false"))]
/// Will always be serialized as `false`.
/// Will always be serialized as `true`.
#[cfg_attr(feature = "serde", serde(serialize_with = "serde_true"))]
in_pool: bool,
},
}
impl Default for TxEntry {
impl Default for TxEntryType {
fn default() -> Self {
Self::NotInPool {
prefix: Default::default(),
received_timestamp: u64::default(),
relayed: bool::default(),
in_pool: false,
Self::Blockchain {
block_height: Default::default(),
block_timestamp: Default::default(),
confirmations: Default::default(),
output_indices: Default::default(),
in_pool: Default::default(),
}
}
}
/// Common fields in all [`TxEntry`] variants.
#[derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct TxEntryPrefix {
as_hex: String,
/// `cuprate_rpc_types::json::tx::Transaction` should be used
/// to create this JSON string in a type-safe manner.
as_json: String,
double_spend_seen: bool,
tx_hash: String,
prunable_as_hex: String,
prunable_hash: String,
pruned_as_hex: String,
}
//---------------------------------------------------------------------------------------------------- Epee
#[cfg(feature = "epee")]
impl EpeeObjectBuilder<TxEntry> for () {

331
rpc/types/src/misc/types.rs Normal file
View file

@ -0,0 +1,331 @@
//! Miscellaneous types.
//!
//! These are `struct`s that appear in request/response types.
//! For example, [`crate::json::GetConnectionsResponse`] contains
//! the [`crate::misc::ConnectionInfo`] struct defined here.
//---------------------------------------------------------------------------------------------------- Import
use cuprate_hex::{Hex, HexVec};
use cuprate_types::HardFork;
#[cfg(any(feature = "epee", feature = "serde"))]
use crate::defaults::default;
use crate::macros::monero_definition_link;
//---------------------------------------------------------------------------------------------------- Macros
/// This macro (local to this file) defines all the misc types.
///
/// This macro:
/// 1. Defines a `pub struct` with all `pub` fields
/// 2. Implements `serde` on the struct
/// 3. Implements `epee` on the struct
///
/// When using, consider documenting:
/// - The original Monero definition site with [`monero_definition_link`]
/// - The request/responses where the `struct` is used
macro_rules! define_struct_and_impl_epee {
($(
// Optional `struct` attributes.
$( #[$struct_attr:meta] )*
// The `struct`'s name.
$struct_name:ident {
// And any fields.
$(
$( #[$field_attr:meta] )* // Field attributes
// Field name => the type => optional `epee_object` default value.
$field_name:ident: $field_type:ty $(= $field_default:expr_2021)?,
)*
}
)*) => {
$(
#[derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serde", serde(default))]
$( #[$struct_attr] )*
pub struct $struct_name {
$(
$( #[$field_attr] )*
pub $field_name: $field_type,
)*
}
#[cfg(feature = "epee")]
cuprate_epee_encoding::epee_object! {
$struct_name,
$(
$field_name: $field_type $(= $field_default)?,
)*
}
)*
};
}
//---------------------------------------------------------------------------------------------------- Type Definitions
define_struct_and_impl_epee! {
#[doc = monero_definition_link!(
"cc73fe71162d564ffda8e549b79a350bca53c454",
"rpc/core_rpc_server_commands_defs.h",
1163..=1212
)]
///
/// Used in:
/// - [`crate::json::GetLastBlockHeaderResponse`]
/// - [`crate::json::GetBlockHeaderByHashResponse`]
/// - [`crate::json::GetBlockHeaderByHeightResponse`]
/// - [`crate::json::GetBlockHeadersRangeResponse`]
/// - [`crate::json::GetBlockResponse`]
BlockHeader {
block_size: u64,
block_weight: u64,
cumulative_difficulty_top64: u64,
cumulative_difficulty: u64,
depth: u64,
difficulty_top64: u64,
difficulty: u64,
hash: Hex<32>,
height: u64,
long_term_weight: u64,
major_version: HardFork,
miner_tx_hash: Hex<32>,
minor_version: u8,
nonce: u32,
num_txes: u64,
orphan_status: bool,
/// This is a [`Hex<32>`] that is sometimes empty.
pow_hash: HexVec,
prev_hash: Hex<32>,
reward: u64,
timestamp: u64,
wide_cumulative_difficulty: String,
wide_difficulty: String,
}
}
define_struct_and_impl_epee! {
#[doc = monero_definition_link!(
"cc73fe71162d564ffda8e549b79a350bca53c454",
"cryptonote_protocol/cryptonote_protocol_defs.h",
47..=116
)]
/// Used in [`crate::json::GetConnectionsResponse`].
ConnectionInfo {
address: String,
address_type: cuprate_types::AddressType,
avg_download: u64,
avg_upload: u64,
connection_id: String,
current_download: u64,
current_upload: u64,
height: u64,
host: String,
incoming: bool,
ip: String,
live_time: u64,
localhost: bool,
local_ip: bool,
peer_id: String,
port: String,
pruning_seed: u32,
recv_count: u64,
recv_idle_time: u64,
rpc_credits_per_hash: u32,
rpc_port: u16,
send_count: u64,
send_idle_time: u64,
// Exists in the original definition, but isn't
// used or (de)serialized for RPC purposes.
// ssl: bool,
state: cuprate_types::ConnectionState,
support_flags: u32,
}
}
define_struct_and_impl_epee! {
#[doc = monero_definition_link!(
"cc73fe71162d564ffda8e549b79a350bca53c454",
"rpc/core_rpc_server_commands_defs.h",
2034..=2047
)]
/// Used in [`crate::json::SetBansRequest`].
SetBan {
host: String,
ip: u32,
ban: bool,
seconds: u32,
}
}
define_struct_and_impl_epee! {
#[doc = monero_definition_link!(
"cc73fe71162d564ffda8e549b79a350bca53c454",
"rpc/core_rpc_server_commands_defs.h",
1999..=2010
)]
/// Used in [`crate::json::GetBansResponse`].
GetBan {
host: String,
ip: u32,
seconds: u32,
}
}
define_struct_and_impl_epee! {
#[doc = monero_definition_link!(
"cc73fe71162d564ffda8e549b79a350bca53c454",
"rpc/core_rpc_server_commands_defs.h",
2139..=2156
)]
#[derive(Copy)]
/// Used in [`crate::json::GetOutputHistogramResponse`].
HistogramEntry {
amount: u64,
total_instances: u64,
unlocked_instances: u64,
recent_instances: u64,
}
}
define_struct_and_impl_epee! {
#[doc = monero_definition_link!(
"cc73fe71162d564ffda8e549b79a350bca53c454",
"rpc/core_rpc_server_commands_defs.h",
2289..=2310
)]
/// Used in [`crate::json::GetAlternateChainsResponse`].
ChainInfo {
block_hash: Hex<32>,
block_hashes: Vec<Hex<32>>,
difficulty: u64,
difficulty_top64: u64,
height: u64,
length: u64,
main_chain_parent_block: Hex<32>,
wide_difficulty: String,
}
}
define_struct_and_impl_epee! {
#[doc = monero_definition_link!(
"cc73fe71162d564ffda8e549b79a350bca53c454",
"rpc/core_rpc_server_commands_defs.h",
2393..=2400
)]
/// Used in [`crate::json::SyncInfoResponse`].
SyncInfoPeer {
info: ConnectionInfo,
}
}
define_struct_and_impl_epee! {
#[doc = monero_definition_link!(
"cc73fe71162d564ffda8e549b79a350bca53c454",
"rpc/core_rpc_server_commands_defs.h",
2402..=2421
)]
/// Used in [`crate::json::SyncInfoResponse`].
Span {
connection_id: String,
nblocks: u64,
rate: u32,
remote_address: String,
size: u64,
speed: u32,
start_block_height: u64,
}
}
define_struct_and_impl_epee! {
#[doc = monero_definition_link!(
"cc73fe71162d564ffda8e549b79a350bca53c454",
"rpc/core_rpc_server_commands_defs.h",
512..=521
)]
#[derive(Copy)]
///
/// Used in:
/// - [`crate::bin::GetOutsRequest`]
/// - [`crate::other::GetOutsRequest`]
GetOutputsOut {
amount: u64,
index: u64,
}
}
define_struct_and_impl_epee! {
#[doc = monero_definition_link!(
"cc73fe71162d564ffda8e549b79a350bca53c454",
"rpc/core_rpc_server_commands_defs.h",
582..=597
)]
/// Used in [`crate::other::GetOutsRequest`].
OutKey {
key: Hex<32>,
mask: Hex<32>,
unlocked: bool,
height: u64,
/// This is a [`Hex<32>`] that is sometimes empty.
txid: HexVec,
}
}
define_struct_and_impl_epee! {
#[doc = monero_definition_link!(
"cc73fe71162d564ffda8e549b79a350bca53c454",
"rpc/core_rpc_server_commands_defs.h",
538..=553
)]
#[derive(Copy)]
/// Used in [`crate::bin::GetOutsRequest`].
OutKeyBin {
key: [u8; 32],
mask: [u8; 32],
unlocked: bool,
height: u64,
txid: [u8; 32],
}
}
define_struct_and_impl_epee! {
#[doc = monero_definition_link!(
"cc73fe71162d564ffda8e549b79a350bca53c454",
"rpc/core_rpc_server_commands_defs.h",
1519..=1556
)]
/// Used in [`crate::other::GetTransactionPoolResponse`].
TxInfo {
blob_size: u64,
do_not_relay: bool,
double_spend_seen: bool,
fee: u64,
id_hash: Hex<32>,
kept_by_block: bool,
last_failed_height: u64,
last_failed_id_hash: Hex<32>,
last_relayed_time: u64,
max_used_block_height: u64,
max_used_block_id_hash: Hex<32>,
receive_time: u64,
relayed: bool,
tx_blob: HexVec,
tx_json: cuprate_types::json::tx::Transaction,
weight: u64 = default::<u64>(),
}
}
define_struct_and_impl_epee! {
#[doc = monero_definition_link!(
"cc73fe71162d564ffda8e549b79a350bca53c454",
"rpc/core_rpc_server_commands_defs.h",
1558..=1567
)]
/// Used in [`crate::other::GetTransactionPoolResponse`].
SpentKeyImageInfo {
id_hash: Hex<32>,
txs_hashes: Vec<Hex<32>>,
}
}
//---------------------------------------------------------------------------------------------------- Tests
#[cfg(test)]
mod test {}

File diff suppressed because one or more lines are too long

View file

@ -254,7 +254,7 @@ pub fn get_block_blob_with_tx_indexes(
Ok((block, miner_tx_idx, numb_txs))
}
//---------------------------------------------------------------------------------------------------- `get_block_extended_header_*`
//---------------------------------------------------------------------------------------------------- `get_block_complete_entry_*`
/// Retrieve a [`BlockCompleteEntry`] from the database.
///
#[doc = doc_error!()]
@ -263,8 +263,18 @@ pub fn get_block_complete_entry(
tables: &impl TablesIter,
) -> Result<BlockCompleteEntry, RuntimeError> {
let block_height = tables.block_heights().get(block_hash)?;
get_block_complete_entry_from_height(&block_height, tables)
}
/// Retrieve a [`BlockCompleteEntry`] from the database.
///
#[doc = doc_error!()]
pub fn get_block_complete_entry_from_height(
block_height: &BlockHeight,
tables: &impl TablesIter,
) -> Result<BlockCompleteEntry, RuntimeError> {
let (block_blob, miner_tx_idx, numb_non_miner_txs) =
get_block_blob_with_tx_indexes(&block_height, tables)?;
get_block_blob_with_tx_indexes(block_height, tables)?;
let first_tx_idx = miner_tx_idx + 1;

View file

@ -2,18 +2,20 @@
//---------------------------------------------------------------------------------------------------- Import
use curve25519_dalek::edwards::CompressedEdwardsY;
use monero_serai::transaction::Timelock;
use monero_serai::transaction::{Timelock, Transaction};
use cuprate_database::{
DbResult, RuntimeError, {DatabaseRo, DatabaseRw},
};
use cuprate_helper::crypto::compute_zero_commitment;
use cuprate_helper::map::u64_to_timelock;
use cuprate_helper::{cast::u32_to_usize, crypto::compute_zero_commitment};
use cuprate_helper::{cast::u64_to_usize, map::u64_to_timelock};
use cuprate_types::OutputOnChain;
use crate::{
ops::macros::{doc_add_block_inner_invariant, doc_error},
tables::{Outputs, RctOutputs, Tables, TablesMut, TxUnlockTime},
tables::{
BlockInfos, BlockTxsHashes, Outputs, RctOutputs, Tables, TablesMut, TxBlobs, TxUnlockTime,
},
types::{Amount, AmountIndex, Output, OutputFlags, PreRctOutputId, RctOutput},
};
@ -152,7 +154,11 @@ pub fn get_rct_num_outputs(table_rct_outputs: &impl DatabaseRo<RctOutputs>) -> D
pub fn output_to_output_on_chain(
output: &Output,
amount: Amount,
get_txid: bool,
table_tx_unlock_time: &impl DatabaseRo<TxUnlockTime>,
table_block_txs_hashes: &impl DatabaseRo<BlockTxsHashes>,
table_block_infos: &impl DatabaseRo<BlockInfos>,
table_tx_blobs: &impl DatabaseRo<TxBlobs>,
) -> DbResult<OutputOnChain> {
let commitment = compute_zero_commitment(amount);
@ -167,11 +173,27 @@ pub fn output_to_output_on_chain(
let key = CompressedEdwardsY(output.key);
let txid = if get_txid {
let height = u32_to_usize(output.height);
let tx_idx = u64_to_usize(output.tx_idx);
let txid = if let Some(hash) = table_block_txs_hashes.get(&height)?.get(tx_idx) {
*hash
} else {
let miner_tx_id = table_block_infos.get(&height)?.mining_tx_index;
let tx_blob = table_tx_blobs.get(&miner_tx_id)?;
Transaction::read(&mut tx_blob.0.as_slice())?.hash()
};
Some(txid)
} else {
None
};
Ok(OutputOnChain {
height: output.height as usize,
height: u32_to_usize(output.height),
time_lock,
key,
commitment,
txid,
})
}
@ -186,7 +208,11 @@ pub fn output_to_output_on_chain(
#[doc = doc_error!()]
pub fn rct_output_to_output_on_chain(
rct_output: &RctOutput,
get_txid: bool,
table_tx_unlock_time: &impl DatabaseRo<TxUnlockTime>,
table_block_txs_hashes: &impl DatabaseRo<BlockTxsHashes>,
table_block_infos: &impl DatabaseRo<BlockInfos>,
table_tx_blobs: &impl DatabaseRo<TxBlobs>,
) -> DbResult<OutputOnChain> {
// INVARIANT: Commitments stored are valid when stored by the database.
let commitment = CompressedEdwardsY(rct_output.commitment);
@ -202,11 +228,30 @@ pub fn rct_output_to_output_on_chain(
let key = CompressedEdwardsY(rct_output.key);
let txid = if get_txid {
let height = u32_to_usize(rct_output.height);
let miner_tx_id = table_block_infos.get(&height)?.mining_tx_index;
let txid = if miner_tx_id == rct_output.tx_idx {
let tx_blob = table_tx_blobs.get(&miner_tx_id)?;
Transaction::read(&mut tx_blob.0.as_slice())?.hash()
} else {
let idx = u64_to_usize(rct_output.tx_idx - miner_tx_id - 1);
table_block_txs_hashes.get(&height)?[idx]
};
Some(txid)
} else {
None
};
Ok(OutputOnChain {
height: rct_output.height as usize,
height: u32_to_usize(rct_output.height),
time_lock,
key,
commitment,
txid,
})
}
@ -214,18 +259,36 @@ pub fn rct_output_to_output_on_chain(
///
/// Note that this still support RCT outputs, in that case, [`PreRctOutputId::amount`] should be `0`.
#[doc = doc_error!()]
pub fn id_to_output_on_chain(id: &PreRctOutputId, tables: &impl Tables) -> DbResult<OutputOnChain> {
pub fn id_to_output_on_chain(
id: &PreRctOutputId,
get_txid: bool,
tables: &impl Tables,
) -> DbResult<OutputOnChain> {
// v2 transactions.
if id.amount == 0 {
let rct_output = get_rct_output(&id.amount_index, tables.rct_outputs())?;
let output_on_chain = rct_output_to_output_on_chain(&rct_output, tables.tx_unlock_time())?;
let output_on_chain = rct_output_to_output_on_chain(
&rct_output,
get_txid,
tables.tx_unlock_time(),
tables.block_txs_hashes(),
tables.block_infos(),
tables.tx_blobs(),
)?;
Ok(output_on_chain)
} else {
// v1 transactions.
let output = get_output(id, tables.outputs())?;
let output_on_chain =
output_to_output_on_chain(&output, id.amount, tables.tx_unlock_time())?;
let output_on_chain = output_to_output_on_chain(
&output,
id.amount,
get_txid,
tables.tx_unlock_time(),
tables.block_txs_hashes(),
tables.block_infos(),
tables.tx_blobs(),
)?;
Ok(output_on_chain)
}

View file

@ -30,7 +30,8 @@ use cuprate_helper::map::combine_low_high_bits_to_u128;
use cuprate_types::{
blockchain::{BlockchainReadRequest, BlockchainResponse},
output_cache::OutputCache,
Chain, ChainId, ExtendedBlockHeader, OutputHistogramInput, TxsInBlock,
rpc::OutputHistogramInput,
Chain, ChainId, ExtendedBlockHeader, OutputDistributionInput, TxsInBlock,
};
use crate::{
@ -41,7 +42,8 @@ use crate::{
},
block::{
block_exists, get_block_blob_with_tx_indexes, get_block_complete_entry,
get_block_extended_header_from_height, get_block_height, get_block_info,
get_block_complete_entry_from_height, get_block_extended_header_from_height,
get_block_height, get_block_info,
},
blockchain::{cumulative_generated_coins, find_split_point, top_block_height},
key_image::key_image_exists,
@ -51,7 +53,10 @@ use crate::{
free::{compact_history_genesis_not_included, compact_history_index_to_height_offset},
types::{BlockchainReadHandle, ResponseResult},
},
tables::{AltBlockHeights, BlockHeights, BlockInfos, OpenTables, Tables, TablesIter},
tables::{
AltBlockHeights, BlockHeights, BlockInfos, OpenTables, RctOutputs, Tables, TablesIter,
TxIds, TxOutputs,
},
types::{
AltBlockHeight, Amount, AmountIndex, BlockHash, BlockHeight, KeyImage, PreRctOutputId,
},
@ -106,6 +111,7 @@ fn map_request(
match request {
R::BlockCompleteEntries(block_hashes) => block_complete_entries(env, block_hashes),
R::BlockCompleteEntriesByHeight(heights) => block_complete_entries_by_height(env, heights),
R::BlockExtendedHeader(block) => block_extended_header(env, block),
R::BlockHash(block, chain) => block_hash(env, block, chain),
R::BlockHashInRange(blocks, chain) => block_hash_in_range(env, blocks, chain),
@ -116,9 +122,14 @@ fn map_request(
}
R::ChainHeight => chain_height(env),
R::GeneratedCoins(height) => generated_coins(env, height),
R::Outputs(map) => outputs(env, map),
R::Outputs {
outputs: map,
get_txid,
} => outputs(env, map, get_txid),
R::OutputsVec { outputs, get_txid } => outputs_vec(env, outputs, get_txid),
R::NumberOutputsWithAmount(vec) => number_outputs_with_amount(env, vec),
R::KeyImagesSpent(set) => key_images_spent(env, set),
R::KeyImagesSpentVec(set) => key_images_spent_vec(env, set),
R::CompactChainHistory => compact_chain_history(env),
R::NextChainEntry(block_hashes, amount) => next_chain_entry(env, &block_hashes, amount),
R::FindFirstUnknown(block_ids) => find_first_unknown(env, &block_ids),
@ -135,6 +146,10 @@ fn map_request(
R::CoinbaseTxSum { height, count } => coinbase_tx_sum(env, height, count),
R::AltChains => alt_chains(env),
R::AltChainCount => alt_chain_count(env),
R::Transactions { tx_hashes } => transactions(env, tx_hashes),
R::TotalRctOutputs => total_rct_outputs(env),
R::TxOutputIndexes { tx_hash } => tx_output_indexes(env, &tx_hash),
R::OutputDistribution(input) => output_distribution(env, input),
}
/* SOMEDAY: post-request handling, run some code for each request? */
@ -242,6 +257,31 @@ fn block_complete_entries(env: &ConcreteEnv, block_hashes: Vec<BlockHash>) -> Re
})
}
/// [`BlockchainReadRequest::BlockCompleteEntriesByHeight`].
fn block_complete_entries_by_height(
env: &ConcreteEnv,
block_heights: Vec<BlockHeight>,
) -> ResponseResult {
// Prepare tx/tables in `ThreadLocal`.
let env_inner = env.env_inner();
let tx_ro = thread_local(env);
let tables = thread_local(env);
let blocks = block_heights
.into_par_iter()
.map(|height| {
let tx_ro = tx_ro.get_or_try(|| env_inner.tx_ro())?;
let tables = get_tables!(env_inner, tx_ro, tables)?.as_ref();
get_block_complete_entry_from_height(&height, tables)
})
.collect::<DbResult<_>>()?;
let tx_ro = tx_ro.get_or_try(|| env_inner.tx_ro())?;
let tables = get_tables!(env_inner, tx_ro, tables)?.as_ref();
Ok(BlockchainResponse::BlockCompleteEntriesByHeight(blocks))
}
/// [`BlockchainReadRequest::BlockExtendedHeader`].
#[inline]
fn block_extended_header(env: &ConcreteEnv, block_height: BlockHeight) -> ResponseResult {
@ -445,7 +485,11 @@ fn generated_coins(env: &ConcreteEnv, height: usize) -> ResponseResult {
/// [`BlockchainReadRequest::Outputs`].
#[inline]
fn outputs(env: &ConcreteEnv, outputs: IndexMap<Amount, IndexSet<AmountIndex>>) -> ResponseResult {
fn outputs(
env: &ConcreteEnv,
outputs: IndexMap<Amount, IndexSet<AmountIndex>>,
get_txid: bool,
) -> ResponseResult {
// Prepare tx/tables in `ThreadLocal`.
let env_inner = env.env_inner();
let tx_ro = thread_local(env);
@ -483,7 +527,7 @@ fn outputs(env: &ConcreteEnv, outputs: IndexMap<Amount, IndexSet<AmountIndex>>)
amount_index,
};
let output_on_chain = match id_to_output_on_chain(&id, tables) {
let output_on_chain = match id_to_output_on_chain(&id, get_txid, tables) {
Ok(output) => output,
Err(RuntimeError::KeyNotFound) => return Ok(Either::Right(amount_index)),
Err(e) => return Err(e),
@ -510,6 +554,16 @@ fn outputs(env: &ConcreteEnv, outputs: IndexMap<Amount, IndexSet<AmountIndex>>)
Ok(BlockchainResponse::Outputs(cache))
}
/// [`BlockchainReadRequest::OutputsVec`].
#[inline]
fn outputs_vec(
env: &ConcreteEnv,
outputs: Vec<(Amount, AmountIndex)>,
get_txid: bool,
) -> ResponseResult {
Ok(BlockchainResponse::OutputsVec(todo!()))
}
/// [`BlockchainReadRequest::NumberOutputsWithAmount`].
#[inline]
fn number_outputs_with_amount(env: &ConcreteEnv, amounts: Vec<Amount>) -> ResponseResult {
@ -596,6 +650,29 @@ fn key_images_spent(env: &ConcreteEnv, key_images: HashSet<KeyImage>) -> Respons
}
}
/// [`BlockchainReadRequest::KeyImagesSpentVec`].
fn key_images_spent_vec(env: &ConcreteEnv, key_images: Vec<KeyImage>) -> ResponseResult {
// Prepare tx/tables in `ThreadLocal`.
let env_inner = env.env_inner();
let tx_ro = thread_local(env);
let tables = thread_local(env);
// Key image check function.
let key_image_exists = |key_image| {
let tx_ro = tx_ro.get_or_try(|| env_inner.tx_ro())?;
let tables = get_tables!(env_inner, tx_ro, tables)?.as_ref();
key_image_exists(&key_image, tables.key_images())
};
// Collect results using `rayon`.
Ok(BlockchainResponse::KeyImagesSpentVec(
key_images
.into_par_iter()
.map(key_image_exists)
.collect::<DbResult<_>>()?,
))
}
/// [`BlockchainReadRequest::CompactChainHistory`]
fn compact_chain_history(env: &ConcreteEnv) -> ResponseResult {
let env_inner = env.env_inner();
@ -851,3 +928,37 @@ fn alt_chains(env: &ConcreteEnv) -> ResponseResult {
fn alt_chain_count(env: &ConcreteEnv) -> ResponseResult {
Ok(BlockchainResponse::AltChainCount(todo!()))
}
/// [`BlockchainReadRequest::Transactions`]
fn transactions(env: &ConcreteEnv, tx_hashes: HashSet<[u8; 32]>) -> ResponseResult {
Ok(BlockchainResponse::Transactions {
txs: todo!(),
missed_txs: todo!(),
})
}
/// [`BlockchainReadRequest::TotalRctOutputs`]
fn total_rct_outputs(env: &ConcreteEnv) -> ResponseResult {
// Single-threaded, no `ThreadLocal` required.
let env_inner = env.env_inner();
let tx_ro = env_inner.tx_ro()?;
let len = env_inner.open_db_ro::<RctOutputs>(&tx_ro)?.len()?;
Ok(BlockchainResponse::TotalRctOutputs(len))
}
/// [`BlockchainReadRequest::TxOutputIndexes`]
fn tx_output_indexes(env: &ConcreteEnv, tx_hash: &[u8; 32]) -> ResponseResult {
// Single-threaded, no `ThreadLocal` required.
let env_inner = env.env_inner();
let tx_ro = env_inner.tx_ro()?;
let tx_id = env_inner.open_db_ro::<TxIds>(&tx_ro)?.get(tx_hash)?;
let o_indexes = env_inner.open_db_ro::<TxOutputs>(&tx_ro)?.get(&tx_id)?;
Ok(BlockchainResponse::TxOutputIndexes(o_indexes.0))
}
/// [`BlockchainReadRequest::OutputDistribution`]
fn output_distribution(env: &ConcreteEnv, input: OutputDistributionInput) -> ResponseResult {
Ok(BlockchainResponse::OutputDistribution(todo!()))
}

View file

@ -6,10 +6,7 @@
#![allow(clippy::await_holding_lock, clippy::too_many_lines)]
//---------------------------------------------------------------------------------------------------- Use
use std::{
collections::{HashMap, HashSet},
sync::Arc,
};
use std::{collections::HashMap, sync::Arc};
use indexmap::{IndexMap, IndexSet};
use pretty_assertions::assert_eq;
@ -180,8 +177,8 @@ async fn test_template(
));
// Contains a fake non-spent key-image.
let ki_req = HashSet::from([[0; 32]]);
let ki_resp = Ok(BlockchainResponse::KeyImagesSpent(false));
let ki_req = vec![[0; 32]];
let ki_resp = Ok(BlockchainResponse::KeyImagesSpentVec(vec![false]));
//----------------------------------------------------------------------- Assert expected response
// Assert read requests lead to the expected responses.
@ -219,7 +216,7 @@ async fn test_template(
BlockchainReadRequest::NumberOutputsWithAmount(num_req),
num_resp,
),
(BlockchainReadRequest::KeyImagesSpent(ki_req), ki_resp),
(BlockchainReadRequest::KeyImagesSpentVec(ki_req), ki_resp),
] {
let response = reader.clone().oneshot(request).await;
println!("response: {response:#?}, expected_response: {expected_response:#?}");
@ -233,10 +230,13 @@ async fn test_template(
// Assert each key image we inserted comes back as "spent".
for key_image in tables.key_images_iter().keys().unwrap() {
let key_image = key_image.unwrap();
let request = BlockchainReadRequest::KeyImagesSpent(HashSet::from([key_image]));
let request = BlockchainReadRequest::KeyImagesSpentVec(vec![key_image]);
let response = reader.clone().oneshot(request).await;
println!("response: {response:#?}, key_image: {key_image:#?}");
assert_eq!(response.unwrap(), BlockchainResponse::KeyImagesSpent(true));
assert_eq!(
response.unwrap(),
BlockchainResponse::KeyImagesSpentVec(vec![true])
);
}
//----------------------------------------------------------------------- Output checks
@ -287,13 +287,16 @@ async fn test_template(
amount: *amount,
amount_index: *amount_index,
};
id_to_output_on_chain(&id, &tables).unwrap()
id_to_output_on_chain(&id, false, &tables).unwrap()
})
})
.collect::<Vec<OutputOnChain>>();
// Send a request for every output we inserted before.
let request = BlockchainReadRequest::Outputs(map.clone());
let request = BlockchainReadRequest::Outputs {
outputs: map.clone(),
get_txid: false,
};
let response = reader.clone().oneshot(request).await;
println!("Response::Outputs response: {response:#?}");
let Ok(BlockchainResponse::Outputs(response)) = response else {

View file

@ -20,7 +20,7 @@ serde = ["dep:serde", "cuprate-database/serde", "cuprate-database-service/
[dependencies]
cuprate-database = { workspace = true, features = ["heed"] }
cuprate-database-service = { workspace = true }
cuprate-types = { workspace = true }
cuprate-types = { workspace = true, features = ["rpc"] }
cuprate-helper = { workspace = true, default-features = false, features = ["constants"] }
monero-serai = { workspace = true, features = ["std"] }

View file

@ -1,9 +1,16 @@
//! Tx-pool [`service`](super) interface.
//!
//! This module contains `cuprate_txpool`'s [`tower::Service`] request and response enums.
use std::collections::{HashMap, HashSet};
use cuprate_types::TransactionVerificationData;
use std::{
collections::{HashMap, HashSet},
num::NonZero,
};
use cuprate_types::{
rpc::{PoolInfo, SpentKeyImageInfo, TxInfo, TxpoolStats},
TransactionVerificationData, TxInPool,
};
use crate::{
tx::TxEntry,
@ -12,6 +19,10 @@ use crate::{
//---------------------------------------------------------------------------------------------------- TxpoolReadRequest
/// The transaction pool [`tower::Service`] read request type.
///
/// ### `include_sensitive_txs`
/// This field exists in many requests.
/// If this is [`true`], the request will include private (local) transactions in the response.
#[derive(Clone)]
pub enum TxpoolReadRequest {
/// Get the blob (raw bytes) of a transaction with the given hash.
@ -32,11 +43,47 @@ pub enum TxpoolReadRequest {
Backlog,
/// Get the number of transactions in the pool.
Size {
/// If this is [`true`], the size returned will
/// include private transactions in the pool.
Size { include_sensitive_txs: bool },
/// Get general information on the txpool.
PoolInfo {
include_sensitive_txs: bool,
/// The maximum amount of transactions to retrieve.
max_tx_count: usize,
/// Fetch transactions that start from this time.
///
/// [`None`] means all transactions.
start_time: Option<NonZero<usize>>,
},
/// Get transactions by their hashes.
TxsByHash {
tx_hashes: Vec<[u8; 32]>,
include_sensitive_txs: bool,
},
/// Check if any individual key images of a set exist in the txpool.
KeyImagesSpent {
key_images: HashSet<[u8; 32]>,
include_sensitive_txs: bool,
},
/// Same as [`TxpoolReadRequest::KeyImagesSpent`] but with a [`Vec`].
///
/// The response will be in the same order as the request.
KeyImagesSpentVec {
key_images: Vec<[u8; 32]>,
include_sensitive_txs: bool,
},
/// Get txpool info.
Pool { include_sensitive_txs: bool },
/// Get txpool stats.
PoolStats { include_sensitive_txs: bool },
/// Get the hashes of all transaction in the pool.
AllHashes { include_sensitive_txs: bool },
}
//---------------------------------------------------------------------------------------------------- TxpoolReadResponse
@ -76,6 +123,35 @@ pub enum TxpoolReadResponse {
/// The inner value is the amount of
/// transactions currently in the pool.
Size(usize),
/// Response to [`TxpoolReadRequest::PoolInfo`].
PoolInfo(PoolInfo),
/// Response to [`TxpoolReadRequest::TxsByHash`].
TxsByHash(Vec<TxInPool>),
/// Response to [`TxpoolReadRequest::KeyImagesSpent`].
KeyImagesSpent(bool),
/// Response to [`TxpoolReadRequest::KeyImagesSpentVec`].
///
/// Inner value is a `Vec` the same length as the input.
///
/// The index of each entry corresponds with the request.
/// `true` means that the key image was spent.
KeyImagesSpentVec(Vec<bool>),
/// Response to [`TxpoolReadRequest::Pool`].
Pool {
txs: Vec<TxInfo>,
spent_key_images: Vec<SpentKeyImageInfo>,
},
/// Response to [`TxpoolReadRequest::PoolStats`].
PoolStats(TxpoolStats),
/// Response to [`TxpoolReadRequest::AllHashes`].
AllHashes(Vec<[u8; 32]>),
}
//---------------------------------------------------------------------------------------------------- TxpoolWriteRequest

View file

@ -2,10 +2,12 @@
unreachable_code,
unused_variables,
clippy::unnecessary_wraps,
clippy::needless_pass_by_value,
reason = "TODO: finish implementing the signatures from <https://github.com/Cuprate/cuprate/pull/297>"
)]
use std::{
collections::{HashMap, HashSet},
num::NonZero,
sync::Arc,
};
@ -77,6 +79,32 @@ fn map_request(
TxpoolReadRequest::Size {
include_sensitive_txs,
} => size(env, include_sensitive_txs),
TxpoolReadRequest::PoolInfo {
include_sensitive_txs,
max_tx_count,
start_time,
} => pool_info(env, include_sensitive_txs, max_tx_count, start_time),
TxpoolReadRequest::TxsByHash {
tx_hashes,
include_sensitive_txs,
} => txs_by_hash(env, tx_hashes, include_sensitive_txs),
TxpoolReadRequest::KeyImagesSpent {
key_images,
include_sensitive_txs,
} => key_images_spent(env, key_images, include_sensitive_txs),
TxpoolReadRequest::KeyImagesSpentVec {
key_images,
include_sensitive_txs,
} => key_images_spent_vec(env, key_images, include_sensitive_txs),
TxpoolReadRequest::Pool {
include_sensitive_txs,
} => pool(env, include_sensitive_txs),
TxpoolReadRequest::PoolStats {
include_sensitive_txs,
} => pool_stats(env, include_sensitive_txs),
TxpoolReadRequest::AllHashes {
include_sensitive_txs,
} => all_hashes(env, include_sensitive_txs),
}
}
@ -209,3 +237,58 @@ fn backlog(env: &ConcreteEnv) -> ReadResponseResult {
fn size(env: &ConcreteEnv, include_sensitive_txs: bool) -> ReadResponseResult {
Ok(TxpoolReadResponse::Size(todo!()))
}
/// [`TxpoolReadRequest::PoolInfo`].
fn pool_info(
env: &ConcreteEnv,
include_sensitive_txs: bool,
max_tx_count: usize,
start_time: Option<NonZero<usize>>,
) -> ReadResponseResult {
Ok(TxpoolReadResponse::PoolInfo(todo!()))
}
/// [`TxpoolReadRequest::TxsByHash`].
fn txs_by_hash(
env: &ConcreteEnv,
tx_hashes: Vec<[u8; 32]>,
include_sensitive_txs: bool,
) -> ReadResponseResult {
Ok(TxpoolReadResponse::TxsByHash(todo!()))
}
/// [`TxpoolReadRequest::KeyImagesSpent`].
fn key_images_spent(
env: &ConcreteEnv,
key_images: HashSet<[u8; 32]>,
include_sensitive_txs: bool,
) -> ReadResponseResult {
Ok(TxpoolReadResponse::KeyImagesSpent(todo!()))
}
/// [`TxpoolReadRequest::KeyImagesSpentVec`].
fn key_images_spent_vec(
env: &ConcreteEnv,
key_images: Vec<[u8; 32]>,
include_sensitive_txs: bool,
) -> ReadResponseResult {
Ok(TxpoolReadResponse::KeyImagesSpent(todo!()))
}
/// [`TxpoolReadRequest::Pool`].
fn pool(env: &ConcreteEnv, include_sensitive_txs: bool) -> ReadResponseResult {
Ok(TxpoolReadResponse::Pool {
txs: todo!(),
spent_key_images: todo!(),
})
}
/// [`TxpoolReadRequest::PoolStats`].
fn pool_stats(env: &ConcreteEnv, include_sensitive_txs: bool) -> ReadResponseResult {
Ok(TxpoolReadResponse::PoolStats(todo!()))
}
/// [`TxpoolReadRequest::AllHashes`].
fn all_hashes(env: &ConcreteEnv, include_sensitive_txs: bool) -> ReadResponseResult {
Ok(TxpoolReadResponse::AllHashes(todo!()))
}

22
types/hex/Cargo.toml Normal file
View file

@ -0,0 +1,22 @@
[package]
name = "cuprate-hex"
version = "0.0.0"
edition = "2021"
description = "Cuprate's hexadecimal data types"
license = "MIT"
authors = ["hinto-janai"]
repository = "https://github.com/Cuprate/cuprate/tree/main/types"
keywords = ["cuprate", "hex"]
[features]
default = []
[dependencies]
hex = { workspace = true, features = ["alloc", "serde"] }
serde = { workspace = true, features = ["std", "derive"] }
[dev-dependencies]
serde_json = { workspace = true, features = ["std"] }
[lints]
workspace = true

2
types/hex/README.md Normal file
View file

@ -0,0 +1,2 @@
# `cuprate-hex`
Cuprate's hexadecimal data types.

154
types/hex/src/array.rs Normal file
View file

@ -0,0 +1,154 @@
//! Hexadecimal serde wrappers for arrays.
//!
//! This module provides transparent wrapper types for
//! arrays that (de)serialize from hexadecimal input/output.
use std::{
borrow::Borrow,
ops::{Deref, DerefMut},
};
use hex::{FromHex, FromHexError};
use serde::{Deserialize, Deserializer, Serialize};
/// Wrapper type for a byte array that (de)serializes from/to hexadecimal strings.
///
/// ```rust
/// # use cuprate_hex::Hex;
/// let hash = [1; 32];
/// let hex_bytes = Hex::<32>(hash);
/// let expected_json = r#""0101010101010101010101010101010101010101010101010101010101010101""#;
///
/// let to_string = serde_json::to_string(&hex_bytes).unwrap();
/// assert_eq!(to_string, expected_json);
///
/// let from_str = serde_json::from_str::<Hex<32>>(expected_json).unwrap();
/// assert_eq!(hex_bytes, from_str);
///
/// //------
///
/// let vec = vec![hex_bytes; 2];
/// let expected_json = r#"["0101010101010101010101010101010101010101010101010101010101010101","0101010101010101010101010101010101010101010101010101010101010101"]"#;
///
/// let to_string = serde_json::to_string(&vec).unwrap();
/// assert_eq!(to_string, expected_json);
///
/// let from_str = serde_json::from_str::<Vec<Hex<32>>>(expected_json).unwrap();
/// assert_eq!(vec, from_str);
/// ```
///
/// # Deserialization
/// This struct has a custom deserialization that only applies to certain
/// `N` lengths because [`FromHex`] does not implement for a generic `N`:
/// <https://docs.rs/hex/0.4.3/src/hex/lib.rs.html#220-230>
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize)]
#[serde(transparent)]
#[repr(transparent)]
pub struct Hex<const N: usize>(#[serde(with = "hex::serde")] pub [u8; N]);
impl<const N: usize> Hex<N> {
/// Returns `true` if the inner array is zeroed.
///
/// ```rust
/// # use cuprate_hex::Hex;
/// assert!(Hex([0; 32]).is_zeroed());
/// assert!(!Hex([1; 32]).is_zeroed());
/// ```
pub fn is_zeroed(&self) -> bool {
*self == Self([0; N])
}
}
impl<'de, const N: usize> Deserialize<'de> for Hex<N>
where
[u8; N]: FromHex,
<[u8; N] as FromHex>::Error: std::fmt::Display,
{
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
Ok(Self(hex::serde::deserialize(deserializer)?))
}
}
// Default is not implemented for arrays >32, so we must do it manually.
impl<const N: usize> Default for Hex<N> {
fn default() -> Self {
Self([0; N])
}
}
impl<const N: usize> Deref for Hex<N> {
type Target = [u8; N];
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl<const N: usize> DerefMut for Hex<N> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
impl<const N: usize> Borrow<[u8; N]> for Hex<N> {
fn borrow(&self) -> &[u8; N] {
&self.0
}
}
impl<const N: usize> AsRef<[u8; N]> for Hex<N> {
fn as_ref(&self) -> &[u8; N] {
&self.0
}
}
impl<const N: usize> From<Hex<N>> for [u8; N] {
fn from(hex: Hex<N>) -> Self {
hex.0
}
}
impl<const N: usize> From<[u8; N]> for Hex<N> {
fn from(value: [u8; N]) -> Self {
Self(value)
}
}
impl<const N: usize> TryFrom<String> for Hex<N> {
type Error = FromHexError;
fn try_from(value: String) -> Result<Self, Self::Error> {
let vec = hex::decode(value)?;
match <[u8; N]>::try_from(vec) {
Ok(s) => Ok(Self(s)),
Err(_) => Err(FromHexError::InvalidStringLength),
}
}
}
impl<const N: usize> TryFrom<&str> for Hex<N> {
type Error = FromHexError;
fn try_from(value: &str) -> Result<Self, Self::Error> {
let mut bytes = [0; N];
hex::decode_to_slice(value, &mut bytes).map(|()| Self(bytes))
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn asdf() {
let hash = [0; 32];
let hex_bytes = Hex::<32>(hash);
let expected_json = r#""0000000000000000000000000000000000000000000000000000000000000000""#;
let to_string = serde_json::to_string(&hex_bytes).unwrap();
assert_eq!(to_string, expected_json);
let from_str = serde_json::from_str::<Hex<32>>(expected_json).unwrap();
assert_eq!(hex_bytes, from_str);
}
}

9
types/hex/src/lib.rs Normal file
View file

@ -0,0 +1,9 @@
#![doc = include_str!("../README.md")]
// Allow some lints when running in debug mode.
#![cfg_attr(debug_assertions, allow(clippy::todo, clippy::multiple_crate_versions))]
mod array;
mod vec;
pub use array::Hex;
pub use vec::HexVec;

156
types/hex/src/vec.rs Normal file
View file

@ -0,0 +1,156 @@
//! Hexadecimal serde wrappers for [`Vec<u8>`].
//!
//! This module provides transparent wrapper types for
//! arrays that (de)serialize from hexadecimal input/output.
use std::{
borrow::Borrow,
ops::{Deref, DerefMut},
};
use hex::FromHexError;
use serde::{Deserialize, Deserializer, Serialize};
/// Wrapper type for a byte [`Vec`] that (de)serializes from/to hexadecimal strings.
///
/// ```rust
/// # use cuprate_hex::HexVec;
/// let hash = [1; 32];
/// let hex_vec = HexVec::from(hash);
/// let expected_json = r#""0101010101010101010101010101010101010101010101010101010101010101""#;
///
/// let to_string = serde_json::to_string(&hex_vec).unwrap();
/// assert_eq!(to_string, expected_json);
///
/// let from_str = serde_json::from_str::<HexVec>(expected_json).unwrap();
/// assert_eq!(hex_vec, from_str);
///
/// //------
///
/// let vec = vec![hex_vec; 2];
/// let expected_json = r#"["0101010101010101010101010101010101010101010101010101010101010101","0101010101010101010101010101010101010101010101010101010101010101"]"#;
///
/// let to_string = serde_json::to_string(&vec).unwrap();
/// assert_eq!(to_string, expected_json);
///
/// let from_str = serde_json::from_str::<Vec<HexVec>>(expected_json).unwrap();
/// assert_eq!(vec, from_str);
/// ```
#[derive(Clone, Default, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize)]
#[serde(transparent)]
#[repr(transparent)]
pub struct HexVec(#[serde(with = "hex::serde")] pub Vec<u8>);
impl HexVec {
/// [`Vec::new`].
pub const fn new() -> Self {
Self(Vec::new())
}
/// Returns an empty [`Self`] if `array` is all `0`s.
///
/// ```rust
/// # use cuprate_hex::HexVec;
/// assert_eq!(HexVec::empty_if_zeroed([1; 32]).0, [1; 32]);
/// assert_eq!(HexVec::empty_if_zeroed([0; 32]), HexVec(vec![]));
/// assert!(HexVec::empty_if_zeroed([0; 32]).is_empty());
/// ```
pub fn empty_if_zeroed<const N: usize>(array: [u8; N]) -> Self {
if array == [0; N] {
Self(Vec::new())
} else {
Self(array.to_vec())
}
}
}
impl<'de> Deserialize<'de> for HexVec {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
Ok(Self(hex::serde::deserialize(deserializer)?))
}
}
impl Deref for HexVec {
type Target = Vec<u8>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl DerefMut for HexVec {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
impl Borrow<Vec<u8>> for HexVec {
fn borrow(&self) -> &Vec<u8> {
&self.0
}
}
impl AsRef<Vec<u8>> for HexVec {
fn as_ref(&self) -> &Vec<u8> {
&self.0
}
}
impl From<HexVec> for Vec<u8> {
fn from(hex: HexVec) -> Self {
hex.0
}
}
impl From<Vec<u8>> for HexVec {
fn from(value: Vec<u8>) -> Self {
Self(value)
}
}
impl<const N: usize> From<[u8; N]> for HexVec {
fn from(value: [u8; N]) -> Self {
Self(value.to_vec())
}
}
impl TryFrom<String> for HexVec {
type Error = FromHexError;
fn try_from(value: String) -> Result<Self, Self::Error> {
hex::decode(value).map(Self)
}
}
impl TryFrom<&str> for HexVec {
type Error = FromHexError;
fn try_from(value: &str) -> Result<Self, Self::Error> {
hex::decode(value).map(Self)
}
}
impl<const N: usize> TryFrom<HexVec> for [u8; N] {
type Error = FromHexError;
fn try_from(value: HexVec) -> Result<Self, Self::Error> {
Self::try_from(value.0).map_err(|_| FromHexError::InvalidStringLength)
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn asdf() {
let hash = vec![0; 32];
let hex_vec = HexVec(hash);
let expected_json = r#""0000000000000000000000000000000000000000000000000000000000000000""#;
let to_string = serde_json::to_string(&hex_vec).unwrap();
assert_eq!(to_string, expected_json);
let from_str = serde_json::from_str::<HexVec>(expected_json).unwrap();
assert_eq!(hex_vec, from_str);
}
}

View file

@ -1,75 +0,0 @@
//! Hexadecimal serde wrappers for arrays.
//!
//! This module provides transparent wrapper types for
//! arrays that (de)serialize from hexadecimal input/output.
#[cfg(feature = "epee")]
use cuprate_epee_encoding::{error, macros::bytes, EpeeValue, Marker};
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
/// Wrapper type for a byte array that (de)serializes from/to hexadecimal strings.
///
/// # Deserialization
/// This struct has a custom deserialization that only applies to certain
/// `N` lengths because [`hex::FromHex`] does not implement for a generic `N`:
/// <https://docs.rs/hex/0.4.3/src/hex/lib.rs.html#220-230>
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize))]
#[cfg_attr(feature = "serde", serde(transparent))]
#[repr(transparent)]
pub struct HexBytes<const N: usize>(
#[cfg_attr(feature = "serde", serde(with = "hex::serde"))] pub [u8; N],
);
#[cfg(feature = "serde")]
impl<'de, const N: usize> Deserialize<'de> for HexBytes<N>
where
[u8; N]: hex::FromHex,
<[u8; N] as hex::FromHex>::Error: std::fmt::Display,
{
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
Ok(Self(hex::serde::deserialize(deserializer)?))
}
}
#[cfg(feature = "epee")]
impl<const N: usize> EpeeValue for HexBytes<N> {
const MARKER: Marker = <[u8; N] as EpeeValue>::MARKER;
fn read<B: bytes::Buf>(r: &mut B, marker: &Marker) -> error::Result<Self> {
Ok(Self(<[u8; N] as EpeeValue>::read(r, marker)?))
}
fn write<B: bytes::BufMut>(self, w: &mut B) -> error::Result<()> {
<[u8; N] as EpeeValue>::write(self.0, w)
}
}
// Default is not implemented for arrays >32, so we must do it manually.
impl<const N: usize> Default for HexBytes<N> {
fn default() -> Self {
Self([0; N])
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn hex_bytes_32() {
let hash = [1; 32];
let hex_bytes = HexBytes::<32>(hash);
let expected_json = r#""0101010101010101010101010101010101010101010101010101010101010101""#;
let to_string = serde_json::to_string(&hex_bytes).unwrap();
assert_eq!(to_string, expected_json);
let from_str = serde_json::from_str::<HexBytes<32>>(expected_json).unwrap();
assert_eq!(hex_bytes, from_str);
}
}

View file

@ -9,25 +9,25 @@ repository = "https://github.com/Cuprate/cuprate/tree/main/types"
keywords = ["cuprate", "types"]
[features]
default = ["blockchain", "epee", "serde", "json", "hex"]
blockchain = ["dep:indexmap", "dep:cuprate-helper", "cuprate-helper/crypto"]
default = ["blockchain", "epee", "serde", "json"]
blockchain = ["dep:indexmap", "dep:cuprate-helper", "cuprate-helper/crypto", "rpc"]
epee = ["dep:cuprate-epee-encoding"]
serde = ["dep:serde", "hex"]
serde = ["dep:serde"]
proptest = ["dep:proptest", "dep:proptest-derive"]
json = ["hex", "dep:cuprate-helper"]
# We sadly have no choice but to enable serde here as otherwise we will get warnings from the `hex` dep being unused.
# This isn't too bad as `HexBytes` only makes sense with serde anyway.
hex = ["serde", "dep:hex"]
json = ["dep:cuprate-hex", "dep:cuprate-helper"]
rpc = ["dep:cuprate-hex", "json"]
[dependencies]
cuprate-epee-encoding = { workspace = true, optional = true, features = ["std"] }
cuprate-helper = { workspace = true, optional = true, features = ["cast"] }
cuprate-fixed-bytes = { workspace = true, features = ["std", "serde"] }
cuprate-epee-encoding = { workspace = true, optional = true, features = ["std"] }
cuprate-helper = { workspace = true, optional = true, features = ["cast"] }
cuprate-fixed-bytes = { workspace = true, features = ["std", "serde"] }
cuprate-hex = { workspace = true, optional = true }
bitflags = { workspace = true }
bytes = { workspace = true }
cfg-if = { workspace = true }
curve25519-dalek = { workspace = true }
monero-serai = { workspace = true }
hex = { workspace = true, features = ["serde", "alloc"], optional = true }
serde = { workspace = true, features = ["std", "derive"], optional = true }
strum = { workspace = true, features = ["derive"] }
thiserror = { workspace = true }

View file

@ -11,4 +11,3 @@ This crate is a kitchen-sink for data types that are shared across Cuprate.
| `epee` | Enables `cuprate-epee-encoding` on types where applicable
| `proptest` | Enables `proptest::arbitrary::Arbitrary` on some types
| `json` | Enables the `json` module, containing JSON representations of common Monero types
| `hex` | Enables the `hex` module, containing the `HexBytes` type

View file

@ -16,7 +16,7 @@ use strum::{
/// An enumeration of address types.
///
/// Used in `cuprate_p2p` and `cuprate_types`
/// Used in `cuprate-p2p`, `cuprate-rpc-types`.
///
/// Original definition:
/// - <https://github.com/monero-project/monero/blob/cc73fe71162d564ffda8e549b79a350bca53c454/src/epee/include/net/enums.h/#L49>

View file

@ -13,9 +13,12 @@ use monero_serai::block::Block;
use crate::{
output_cache::OutputCache,
types::{Chain, ExtendedBlockHeader, TxsInBlock, VerifiedBlockInformation},
AltBlockInformation, BlockCompleteEntry, ChainId, ChainInfo, CoinbaseTxSum,
OutputHistogramEntry, OutputHistogramInput,
rpc::{
ChainInfo, CoinbaseTxSum, OutputDistributionData, OutputHistogramEntry,
OutputHistogramInput,
},
types::{Chain, ExtendedBlockHeader, OutputOnChain, TxsInBlock, VerifiedBlockInformation},
AltBlockInformation, BlockCompleteEntry, ChainId, OutputDistributionInput, TxInBlockchain,
};
//---------------------------------------------------------------------------------------------------- ReadRequest
@ -34,6 +37,11 @@ pub enum BlockchainReadRequest {
/// The input is the block hashes.
BlockCompleteEntries(Vec<[u8; 32]>),
/// Request [`BlockCompleteEntry`]s.
///
/// The input is the block heights.
BlockCompleteEntriesByHeight(Vec<usize>),
/// Request a block's extended header.
///
/// The input is the block's height.
@ -93,18 +101,36 @@ pub enum BlockchainReadRequest {
/// For RCT outputs, the amounts would be `0` and
/// the amount indices would represent the global
/// RCT output indices.
Outputs(IndexMap<u64, IndexSet<u64>>),
Outputs {
outputs: IndexMap<u64, IndexSet<u64>>,
get_txid: bool,
},
/// This is the same as [`BlockchainReadRequest::Outputs`] but with a [`Vec`] container.
///
/// The input [`Vec`] values are `(amount, amount_index)`.
///
/// The response will be in the same order as the request.
OutputsVec {
outputs: Vec<(u64, u64)>,
get_txid: bool,
},
/// Request the amount of outputs with a certain amount.
///
/// The input is a list of output amounts.
NumberOutputsWithAmount(Vec<u64>),
/// Check that all key images within a set are not spent.
/// Check the spend status of key images.
///
/// Input is a set of key images.
KeyImagesSpent(HashSet<[u8; 32]>),
/// Same as [`BlockchainReadRequest::KeyImagesSpent`] but with a [`Vec`].
///
/// The response will be in the same order as the request.
KeyImagesSpentVec(Vec<[u8; 32]>),
/// A request for the compact chain history.
CompactChainHistory,
@ -156,6 +182,12 @@ pub enum BlockchainReadRequest {
/// TODO: document fields after impl.
OutputHistogram(OutputHistogramInput),
/// Get the distribution for an output amount.
///
/// - TODO: document fields after impl.
/// - TODO: <https://github.com/monero-project/monero/blob/893916ad091a92e765ce3241b94e706ad012b62a/src/rpc/rpc_handler.cpp#L29>
OutputDistribution(OutputDistributionInput),
/// Get the coinbase amount and the fees amount for
/// `N` last blocks starting at particular height.
///
@ -167,6 +199,15 @@ pub enum BlockchainReadRequest {
/// Get the amount of alternative chains that exist.
AltChainCount,
/// Get transaction blobs by their hashes.
Transactions { tx_hashes: HashSet<[u8; 32]> },
/// Get the total amount of RCT outputs in the blockchain.
TotalRctOutputs,
/// Get the output indexes of a transaction.
TxOutputIndexes { tx_hash: [u8; 32] },
}
//---------------------------------------------------------------------------------------------------- WriteRequest
@ -229,6 +270,9 @@ pub enum BlockchainResponse {
blockchain_height: usize,
},
/// Response to [`BlockchainReadRequest::BlockCompleteEntriesByHeight`].
BlockCompleteEntriesByHeight(Vec<BlockCompleteEntry>),
/// Response to [`BlockchainReadRequest::BlockExtendedHeader`].
///
/// Inner value is the extended headed of the requested block.
@ -275,6 +319,9 @@ pub enum BlockchainResponse {
/// in the cache until the cache is updated with the block containing those outputs.
Outputs(OutputCache),
/// Response to [`BlockchainReadRequest::OutputsVec`].
OutputsVec(Vec<(u64, Vec<(u64, OutputOnChain)>)>),
/// Response to [`BlockchainReadRequest::NumberOutputsWithAmount`].
///
/// Inner value is a `HashMap` of all the outputs requested where:
@ -290,6 +337,14 @@ pub enum BlockchainResponse {
/// The inner value is `false` if _none_ of the key images were spent.
KeyImagesSpent(bool),
/// Response to [`BlockchainReadRequest::KeyImagesSpentVec`].
///
/// Inner value is a `Vec` the same length as the input.
///
/// The index of each entry corresponds with the request.
/// `true` means that the key image was spent.
KeyImagesSpentVec(Vec<bool>),
/// Response to [`BlockchainReadRequest::CompactChainHistory`].
CompactChainHistory {
/// A list of blocks IDs in our chain, starting with the most recent block, all the way to the genesis block.
@ -352,6 +407,9 @@ pub enum BlockchainResponse {
free_space: u64,
},
/// Response to [`BlockchainReadRequest::OutputDistribution`].
OutputDistribution(Vec<OutputDistributionData>),
/// Response to [`BlockchainReadRequest::OutputHistogram`].
OutputHistogram(Vec<OutputHistogramEntry>),
@ -364,6 +422,20 @@ pub enum BlockchainResponse {
/// Response to [`BlockchainReadRequest::AltChainCount`].
AltChainCount(usize),
/// Response to [`BlockchainReadRequest::Transactions`].
Transactions {
/// The transaction blobs found.
txs: Vec<TxInBlockchain>,
/// The hashes of any transactions that could not be found.
missed_txs: Vec<[u8; 32]>,
},
/// Response to [`BlockchainReadRequest::TotalRctOutputs`].
TotalRctOutputs(u64),
/// Response to [`BlockchainReadRequest::TxOutputIndexes`].
TxOutputIndexes(Vec<u64>),
//------------------------------------------------------ Writes
/// A generic Ok response to indicate a request was successfully handled.
///

View file

@ -7,6 +7,13 @@ use strum::{
use monero_serai::block::BlockHeader;
#[cfg(feature = "epee")]
use cuprate_epee_encoding::{
error,
macros::bytes::{Buf, BufMut},
EpeeValue, Marker,
};
/// Target block time for hf 1.
///
/// ref: <https://monero-book.cuprate.org/consensus_rules/blocks/difficulty.html#target-seconds>
@ -51,6 +58,8 @@ pub enum HardForkError {
VariantArray,
)]
#[cfg_attr(any(feature = "proptest"), derive(proptest_derive::Arbitrary))]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serde", serde(try_from = "u8", into = "u8"))]
#[repr(u8)]
pub enum HardFork {
#[default]
@ -194,3 +203,32 @@ impl HardFork {
matches!(self, Self::LATEST)
}
}
impl TryFrom<u8> for HardFork {
type Error = HardForkError;
fn try_from(version: u8) -> Result<Self, Self::Error> {
Self::from_version(version)
}
}
impl From<HardFork> for u8 {
fn from(value: HardFork) -> Self {
value.as_u8()
}
}
#[cfg(feature = "epee")]
impl EpeeValue for HardFork {
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_repr(u).ok_or(error::Error::Format("unknown hardfork"))
}
fn write<B: BufMut>(self, w: &mut B) -> error::Result<()> {
let u = self.as_u8();
u8::write(u, w)?;
Ok(())
}
}

View file

@ -6,11 +6,9 @@ use serde::{Deserialize, Serialize};
use monero_serai::{block, transaction};
use cuprate_helper::cast::usize_to_u64;
use cuprate_hex::Hex;
use crate::{
hex::HexBytes,
json::output::{Output, TaggedKey, Target},
};
use crate::json::output::{Output, TaggedKey, Target};
/// JSON representation of a block.
///
@ -22,10 +20,10 @@ pub struct Block {
pub major_version: u8,
pub minor_version: u8,
pub timestamp: u64,
pub prev_id: HexBytes<32>,
pub prev_id: Hex<32>,
pub nonce: u32,
pub miner_tx: MinerTransaction,
pub tx_hashes: Vec<HexBytes<32>>,
pub tx_hashes: Vec<Hex<32>>,
}
impl From<block::Block> for Block {
@ -34,13 +32,13 @@ impl From<block::Block> for Block {
unreachable!("input is a miner tx, this should never fail");
};
let tx_hashes = b.transactions.into_iter().map(HexBytes::<32>).collect();
let tx_hashes = b.transactions.into_iter().map(Hex).collect();
Self {
major_version: b.header.hardfork_version,
minor_version: b.header.hardfork_signal,
timestamp: b.header.timestamp,
prev_id: HexBytes::<32>(b.header.previous),
prev_id: Hex(b.header.previous),
nonce: b.header.nonce,
miner_tx,
tx_hashes,
@ -101,15 +99,13 @@ impl TryFrom<transaction::Transaction> for MinerTransaction {
let target = match o.view_tag {
Some(view_tag) => {
let tagged_key = TaggedKey {
key: HexBytes::<32>(o.key.0),
view_tag: HexBytes::<1>([view_tag]),
key: Hex(o.key.0),
view_tag: Hex([view_tag]),
};
Target::TaggedKey { tagged_key }
}
None => Target::Key {
key: HexBytes::<32>(o.key.0),
},
None => Target::Key { key: Hex(o.key.0) },
};
Output { amount, target }
@ -222,7 +218,7 @@ mod test {
major_version: 1,
minor_version: 0,
timestamp: 1415690591,
prev_id: HexBytes::<32>(hex!(
prev_id: Hex(hex!(
"e97a0ab6307de9b9f9a9872263ef3e957976fb227eb9422c6854e989e5d5d34c"
)),
nonce: 2147484616,
@ -237,25 +233,25 @@ mod test {
Output {
amount: 47019296802,
target: Target::Key {
key: HexBytes::<32>(hex!("3c1dcbf5b485987ecef4596bb700e32cbc7bd05964e3888ffc05f8a46bf5fc33")),
key: Hex(hex!("3c1dcbf5b485987ecef4596bb700e32cbc7bd05964e3888ffc05f8a46bf5fc33")),
}
},
Output {
amount: 200000000000,
target: Target::Key {
key: HexBytes::<32>(hex!("5810afc7a1b01a1c913eb6aab15d4a851cbc4a8cf0adf90bb80ac1a7ca9928aa")),
key: Hex(hex!("5810afc7a1b01a1c913eb6aab15d4a851cbc4a8cf0adf90bb80ac1a7ca9928aa")),
}
},
Output {
amount: 3000000000000,
target: Target::Key {
key: HexBytes::<32>(hex!("520f49c5f2ce8456dc1a565f35ed3a5ccfff3a1210b340870a57d2749a81a2df")),
key: Hex(hex!("520f49c5f2ce8456dc1a565f35ed3a5ccfff3a1210b340870a57d2749a81a2df")),
}
},
Output {
amount: 10000000000000,
target: Target::Key {
key: HexBytes::<32>(hex!("44d7705e62c76c2e349a474df6724aa1d9932092002b03a94f9c19d9d12b9427")),
key: Hex(hex!("44d7705e62c76c2e349a474df6724aa1d9932092002b03a94f9c19d9d12b9427")),
}
}
],
@ -281,7 +277,7 @@ mod test {
major_version: 16,
minor_version: 16,
timestamp: 1727293028,
prev_id: HexBytes::<32>(hex!(
prev_id: Hex(hex!(
"41b56c273d69def3294e56179de71c61808042d54c1e085078d21dbe99e81b6f"
)),
nonce: 311,
@ -296,10 +292,10 @@ mod test {
amount: 601012280000,
target: Target::TaggedKey {
tagged_key: TaggedKey {
key: HexBytes::<32>(hex!(
key: Hex(hex!(
"8c0b16c6df02b9944b49f375d96a958a0fc5431c048879bb5bf25f64a1163b9e"
)),
view_tag: HexBytes::<1>(hex!("88")),
view_tag: Hex(hex!("88")),
},
},
}],
@ -312,43 +308,43 @@ mod test {
rct_signatures: MinerTransactionRctSignatures { r#type: 0 },
},
tx_hashes: vec![
HexBytes::<32>(hex!(
Hex(hex!(
"eab76986a0cbcae690d8499f0f616f783fd2c89c6f611417f18011950dbdab2e"
)),
HexBytes::<32>(hex!(
Hex(hex!(
"57b19aa8c2cdbb6836cf13dd1e321a67860965c12e4418f3c30f58c8899a851e"
)),
HexBytes::<32>(hex!(
Hex(hex!(
"5340185432ab6b74fb21379f7e8d8f0e37f0882b2a7121fd7c08736f079e2edc"
)),
HexBytes::<32>(hex!(
Hex(hex!(
"01dc6d31db56d68116f5294c1b4f80b33b048b5cdfefcd904f23e6c0de3daff5"
)),
HexBytes::<32>(hex!(
Hex(hex!(
"c9fb6a2730678203948fef2a49fa155b63f35a3649f3d32ed405a6806f3bbd56"
)),
HexBytes::<32>(hex!(
Hex(hex!(
"af965cdd2a2315baf1d4a3d242f44fe07b1fd606d5f4853c9ff546ca6c12a5af"
)),
HexBytes::<32>(hex!(
Hex(hex!(
"97bc9e047d25fae8c14ce6ec882224e7b722f5e79b62a2602a6bacebdac8547b"
)),
HexBytes::<32>(hex!(
Hex(hex!(
"28c46992eaf10dc0cceb313c30572d023432b7bd26e85e679bc8fe419533a7bf"
)),
HexBytes::<32>(hex!(
Hex(hex!(
"c32e3acde2ff2885c9cc87253b40d6827d167dfcc3022c72f27084fd98788062"
)),
HexBytes::<32>(hex!(
Hex(hex!(
"19e66a47f075c7cccde8a7b52803119e089e33e3a4847cace0bd1d17b0d22bab"
)),
HexBytes::<32>(hex!(
Hex(hex!(
"8e8ac560e77a1ee72e82a5eb6887adbe5979a10cd29cb2c2a3720ce87db43a70"
)),
HexBytes::<32>(hex!(
Hex(hex!(
"b7ff5141524b5cca24de6780a5dbfdf71e7de1e062fd85f557fb3b43b8e285dc"
)),
HexBytes::<32>(hex!(
Hex(hex!(
"f09df0f113763ef9b9a2752ac293b478102f7cab03ef803a3d9db7585aea8912"
)),
],

View file

@ -9,6 +9,12 @@
//! of types sometimes differs, thus, the modules hold the types
//! that match the specific schema, for example [`block::Input`]
//! is different than [`tx::Input`].
//!
//! # (De)serialization invariants
//! These types are only meant for JSON (de)serialization.
//!
//! Some types implement `epee` although will panic with [`unimplemented`].
//! See `cuprate_rpc_types` for more information.
pub mod block;
pub mod output;

View file

@ -7,10 +7,10 @@
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
use crate::hex::HexBytes;
use cuprate_hex::Hex;
/// JSON representation of an output.
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[derive(Clone, Default, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Output {
pub amount: u64,
@ -22,7 +22,7 @@ pub struct Output {
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(untagged))]
pub enum Target {
Key { key: HexBytes<32> },
Key { key: Hex<32> },
TaggedKey { tagged_key: TaggedKey },
}
@ -38,6 +38,6 @@ impl Default for Target {
#[derive(Clone, Default, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct TaggedKey {
pub key: HexBytes<32>,
pub view_tag: HexBytes<1>,
pub key: Hex<32>,
pub view_tag: Hex<1>,
}

File diff suppressed because it is too large Load diff

View file

@ -24,9 +24,8 @@ pub use transaction_verification_data::{
CachedVerificationState, TransactionVerificationData, TxVersion,
};
pub use types::{
AddAuxPow, AltBlockInformation, AuxPow, Chain, ChainId, ChainInfo, CoinbaseTxSum,
ExtendedBlockHeader, FeeEstimate, HardForkInfo, MinerData, MinerDataTxBacklogEntry,
OutputHistogramEntry, OutputHistogramInput, OutputOnChain, TxsInBlock,
AltBlockInformation, BlockTemplate, Chain, ChainId, ExtendedBlockHeader,
OutputDistributionInput, OutputOnChain, TxInBlockchain, TxInPool, TxRelayChecks, TxsInBlock,
VerifiedBlockInformation, VerifiedTransactionInformation,
};
@ -40,6 +39,10 @@ pub mod output_cache;
#[cfg(feature = "json")]
pub mod json;
#[cfg(feature = "hex")]
pub mod hex;
cfg_if::cfg_if! {
if #[cfg(feature = "rpc")] {
pub mod rpc;
}
}
//---------------------------------------------------------------------------------------------------- Private

View file

@ -89,6 +89,7 @@ impl OutputCache {
time_lock: tx.prefix().additional_timelock,
key: out.key,
commitment: get_output_commitment(tx, i),
txid: None,
},
);
}

View file

@ -12,14 +12,10 @@ use cuprate_epee_encoding::{
};
//---------------------------------------------------------------------------------------------------- KeyImageSpentStatus
#[doc = crate::macros::monero_definition_link!(
cc73fe71162d564ffda8e549b79a350bca53c454,
"rpc/core_rpc_server_commands_defs.h",
456..=460
)]
/// Used in [`crate::other::IsKeyImageSpentResponse`].
/// Used in RPC's `/is_key_image_spent`.
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(try_from = "u8", into = "u8"))]
#[repr(u8)]
pub enum KeyImageSpentStatus {
Unspent = 0,
@ -31,7 +27,7 @@ impl KeyImageSpentStatus {
/// Convert [`Self`] to a [`u8`].
///
/// ```rust
/// use cuprate_rpc_types::misc::KeyImageSpentStatus as K;
/// use cuprate_types::rpc::KeyImageSpentStatus as K;
///
/// assert_eq!(K::Unspent.to_u8(), 0);
/// assert_eq!(K::SpentInBlockchain.to_u8(), 1);
@ -51,7 +47,7 @@ impl KeyImageSpentStatus {
/// This returns [`None`] if `u > 2`.
///
/// ```rust
/// use cuprate_rpc_types::misc::KeyImageSpentStatus as K;
/// use cuprate_types::rpc::KeyImageSpentStatus as K;
///
/// assert_eq!(K::from_u8(0), Some(K::Unspent));
/// assert_eq!(K::from_u8(1), Some(K::SpentInBlockchain));
@ -68,6 +64,19 @@ impl KeyImageSpentStatus {
}
}
impl From<KeyImageSpentStatus> for u8 {
fn from(value: KeyImageSpentStatus) -> Self {
value.to_u8()
}
}
impl TryFrom<u8> for KeyImageSpentStatus {
type Error = u8;
fn try_from(value: u8) -> Result<Self, Self::Error> {
Self::from_u8(value).ok_or(value)
}
}
#[cfg(feature = "epee")]
impl EpeeValue for KeyImageSpentStatus {
const MARKER: Marker = u8::MARKER;

View file

@ -0,0 +1,23 @@
//! Various types (in)directly used in RPC.
//!
//! These types map very closely to types within `cuprate-rpc-types`,
//! however they use more canonical types when appropriate, for example,
//! instead of `hash: String`, this module's types would use something like
//! `hash: [u8; 32]`.
mod key_image_spent_status;
mod pool_info;
mod pool_info_extent;
mod types;
pub use key_image_spent_status::KeyImageSpentStatus;
pub use pool_info::PoolInfo;
pub use pool_info_extent::PoolInfoExtent;
pub use types::{
AddAuxPow, AuxPow, BlockHeader, BlockOutputIndices, ChainInfo, CoinbaseTxSum, ConnectionInfo,
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,
};

View file

@ -2,25 +2,20 @@
use serde::{Deserialize, Serialize};
#[cfg(feature = "epee")]
use crate::misc::PoolInfoExtent;
use crate::rpc::PoolInfoExtent;
#[cfg(feature = "epee")]
use cuprate_epee_encoding::{
epee_object, error,
error,
macros::bytes::{Buf, BufMut},
read_epee_value, write_field, EpeeObject, EpeeObjectBuilder,
};
use cuprate_fixed_bytes::ByteArrayVec;
use crate::misc::PoolTxInfo;
use crate::rpc::{PoolInfoFull, PoolInfoIncremental, PoolTxInfo};
//---------------------------------------------------------------------------------------------------- PoolInfo
#[doc = crate::macros::monero_definition_link!(
cc73fe71162d564ffda8e549b79a350bca53c454,
"rpc/core_rpc_server_commands_defs.h",
223..=228
)]
/// Used in [`crate::bin::GetBlocksResponse`].
/// Used in RPC's `get_blocks.bin`.
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(u8)]
@ -31,39 +26,6 @@ pub enum PoolInfo {
Full(PoolInfoFull),
}
//---------------------------------------------------------------------------------------------------- Internal data
/// Data within [`PoolInfo::Incremental`].
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Clone, Default, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct PoolInfoIncremental {
pub added_pool_txs: Vec<PoolTxInfo>,
pub remaining_added_pool_txids: ByteArrayVec<32>,
pub removed_pool_txids: ByteArrayVec<32>,
}
#[cfg(feature = "epee")]
epee_object! {
PoolInfoIncremental,
added_pool_txs: Vec<PoolTxInfo>,
remaining_added_pool_txids: ByteArrayVec<32>,
removed_pool_txids: ByteArrayVec<32>,
}
/// Data within [`PoolInfo::Full`].
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Clone, Default, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct PoolInfoFull {
pub added_pool_txs: Vec<PoolTxInfo>,
pub remaining_added_pool_txids: ByteArrayVec<32>,
}
#[cfg(feature = "epee")]
epee_object! {
PoolInfoFull,
added_pool_txs: Vec<PoolTxInfo>,
remaining_added_pool_txids: ByteArrayVec<32>,
}
//---------------------------------------------------------------------------------------------------- PoolInfo epee impl
#[cfg(feature = "epee")]
/// [`EpeeObjectBuilder`] for [`GetBlocksResponse`].

View file

@ -12,12 +12,7 @@ use cuprate_epee_encoding::{
};
//---------------------------------------------------------------------------------------------------- PoolInfoExtent
#[doc = crate::macros::monero_definition_link!(
cc73fe71162d564ffda8e549b79a350bca53c454,
"rpc/core_rpc_server_commands_defs.h",
223..=228
)]
/// Used in [`crate::bin::GetBlocksResponse`].
/// Used in RPC's `get_blocks.bin`.
#[derive(Copy, Clone, Default, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[repr(u8)]
@ -32,7 +27,7 @@ impl PoolInfoExtent {
/// Convert [`Self`] to a [`u8`].
///
/// ```rust
/// use cuprate_rpc_types::misc::PoolInfoExtent as P;
/// use cuprate_types::rpc::PoolInfoExtent as P;
///
/// assert_eq!(P::None.to_u8(), 0);
/// assert_eq!(P::Incremental.to_u8(), 1);
@ -52,7 +47,7 @@ impl PoolInfoExtent {
/// This returns [`None`] if `u > 2`.
///
/// ```rust
/// use cuprate_rpc_types::misc::PoolInfoExtent as P;
/// use cuprate_types::rpc::PoolInfoExtent as P;
///
/// assert_eq!(P::from_u8(0), Some(P::None));
/// assert_eq!(P::from_u8(1), Some(P::Incremental));

View file

@ -1,26 +1,45 @@
//! Miscellaneous types.
//!
//! These are `struct`s that appear in request/response types.
//! For example, [`crate::json::GetConnectionsResponse`] contains
//! the [`crate::misc::ConnectionInfo`] struct defined here.
//! Various types (in)directly used in RPC.
//---------------------------------------------------------------------------------------------------- Import
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
use cuprate_fixed_bytes::ByteArrayVec;
use cuprate_hex::Hex;
#[cfg(feature = "epee")]
use cuprate_epee_encoding::epee_object;
use crate::{AddressType, ConnectionState, HardFork};
#[cfg(any(feature = "epee", feature = "serde"))]
use crate::defaults::default_zero;
const fn default_string() -> String {
String::new()
}
use crate::macros::monero_definition_link;
fn default_zero<T: From<u8>>() -> T {
T::from(0)
}
/// Output a string link to `monerod` source code.
macro_rules! monero_definition_link {
(
$commit:literal, // Git commit hash
$file_path:literal, // File path within `monerod`'s `src/`, e.g. `rpc/core_rpc_server_commands_defs.h`
$start:literal$(..=$end:literal)? // File lines, e.g. `0..=123` or `0`
) => {
concat!(
"[Definition](https://github.com/monero-project/monero/blob/",
$commit,
"/src/",
$file_path,
"#L",
stringify!($start),
$(
"-L",
stringify!($end),
)?
")."
)
};
}
//---------------------------------------------------------------------------------------------------- Macros
/// This macro (local to this file) defines all the misc types.
///
/// This macro:
/// 1. Defines a `pub struct` with all `pub` fields
/// 1. Defines a `struct` with all `pub` fields
/// 2. Implements `serde` on the struct
/// 3. Implements `epee` on the struct
///
@ -28,7 +47,7 @@ use crate::macros::monero_definition_link;
/// - The original Monero definition site with [`monero_definition_link`]
/// - The request/responses where the `struct` is used
macro_rules! define_struct_and_impl_epee {
(
($(
// Optional `struct` attributes.
$( #[$struct_attr:meta] )*
// The `struct`'s name.
@ -40,77 +59,66 @@ macro_rules! define_struct_and_impl_epee {
$field_name:ident: $field_type:ty $(= $field_default:expr_2021)?,
)*
}
) => {
#[derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
$( #[$struct_attr] )*
pub struct $struct_name {
$(
$( #[$field_attr] )*
pub $field_name: $field_type,
)*
}
)*) => {
$(
#[derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
$( #[$struct_attr] )*
pub struct $struct_name {
$(
$( #[$field_attr] )*
pub $field_name: $field_type,
)*
}
#[cfg(feature = "epee")]
epee_object! {
$struct_name,
$(
$field_name: $field_type $(= $field_default)?,
)*
}
#[cfg(feature = "epee")]
cuprate_epee_encoding::epee_object! {
$struct_name,
$(
$field_name: $field_type $(= $field_default)?,
)*
}
)*
};
}
//---------------------------------------------------------------------------------------------------- Type Definitions
define_struct_and_impl_epee! {
#[doc = monero_definition_link!(
cc73fe71162d564ffda8e549b79a350bca53c454,
"cc73fe71162d564ffda8e549b79a350bca53c454",
"rpc/core_rpc_server_commands_defs.h",
1163..=1212
)]
///
/// Used in:
/// - [`crate::json::GetLastBlockHeaderResponse`]
/// - [`crate::json::GetBlockHeaderByHashResponse`]
/// - [`crate::json::GetBlockHeaderByHeightResponse`]
/// - [`crate::json::GetBlockHeadersRangeResponse`]
/// - [`crate::json::GetBlockResponse`]
BlockHeader {
block_size: u64,
block_weight: u64,
cumulative_difficulty_top64: u64,
cumulative_difficulty: u64,
depth: u64,
difficulty_top64: u64,
difficulty: u64,
hash: String,
hash: [u8; 32],
height: u64,
long_term_weight: u64,
major_version: u8,
miner_tx_hash: String,
major_version: HardFork,
miner_tx_hash: [u8; 32],
minor_version: u8,
nonce: u32,
num_txes: u64,
orphan_status: bool,
pow_hash: String,
prev_hash: String,
/// This is [`None`] if the `fill_pow_hash` param is `false`.
pow_hash: Option<[u8; 32]>,
prev_hash: [u8; 32],
reward: u64,
timestamp: u64,
wide_cumulative_difficulty: String,
wide_difficulty: String,
}
}
define_struct_and_impl_epee! {
#[doc = monero_definition_link!(
cc73fe71162d564ffda8e549b79a350bca53c454,
"cc73fe71162d564ffda8e549b79a350bca53c454",
"cryptonote_protocol/cryptonote_protocol_defs.h",
47..=116
)]
/// Used in [`crate::json::GetConnectionsResponse`].
ConnectionInfo {
address: String,
address_type: cuprate_types::AddressType,
address_type: AddressType,
avg_download: u64,
avg_upload: u64,
connection_id: String,
@ -135,110 +143,88 @@ define_struct_and_impl_epee! {
// Exists in the original definition, but isn't
// used or (de)serialized for RPC purposes.
// ssl: bool,
state: cuprate_types::ConnectionState,
state: ConnectionState,
support_flags: u32,
}
}
define_struct_and_impl_epee! {
#[doc = monero_definition_link!(
cc73fe71162d564ffda8e549b79a350bca53c454,
"cc73fe71162d564ffda8e549b79a350bca53c454",
"rpc/core_rpc_server_commands_defs.h",
2034..=2047
)]
/// Used in [`crate::json::SetBansRequest`].
SetBan {
#[cfg_attr(feature = "serde", serde(default = "crate::defaults::default_string"))]
#[cfg_attr(feature = "serde", serde(default = "default_string"))]
host: String,
#[cfg_attr(feature = "serde", serde(default = "default_zero"))]
ip: u32,
ban: bool,
seconds: u32,
}
}
define_struct_and_impl_epee! {
#[doc = monero_definition_link!(
cc73fe71162d564ffda8e549b79a350bca53c454,
"cc73fe71162d564ffda8e549b79a350bca53c454",
"rpc/core_rpc_server_commands_defs.h",
1999..=2010
)]
/// Used in [`crate::json::GetBansResponse`].
GetBan {
host: String,
ip: u32,
seconds: u32,
}
}
define_struct_and_impl_epee! {
#[doc = monero_definition_link!(
cc73fe71162d564ffda8e549b79a350bca53c454,
"cc73fe71162d564ffda8e549b79a350bca53c454",
"rpc/core_rpc_server_commands_defs.h",
2139..=2156
)]
#[derive(Copy)]
/// Used in [`crate::json::GetOutputHistogramResponse`].
HistogramEntry {
amount: u64,
total_instances: u64,
unlocked_instances: u64,
recent_instances: u64,
}
}
define_struct_and_impl_epee! {
#[doc = monero_definition_link!(
cc73fe71162d564ffda8e549b79a350bca53c454,
"cc73fe71162d564ffda8e549b79a350bca53c454",
"rpc/core_rpc_server_commands_defs.h",
2180..=2191
)]
#[derive(Copy)]
/// Used in [`crate::json::GetVersionResponse`].
HardforkEntry {
HardForkEntry {
height: u64,
hf_version: u8,
hf_version: HardFork,
}
}
define_struct_and_impl_epee! {
#[doc = monero_definition_link!(
cc73fe71162d564ffda8e549b79a350bca53c454,
"cc73fe71162d564ffda8e549b79a350bca53c454",
"rpc/core_rpc_server_commands_defs.h",
2289..=2310
)]
/// Used in [`crate::json::GetAlternateChainsResponse`].
ChainInfo {
block_hash: String,
block_hashes: Vec<String>,
difficulty: u64,
block_hash: [u8; 32],
block_hashes: Vec<[u8; 32]>,
difficulty_top64: u64,
difficulty: u64,
height: u64,
length: u64,
main_chain_parent_block: String,
wide_difficulty: String,
main_chain_parent_block: [u8; 32],
}
}
define_struct_and_impl_epee! {
#[doc = monero_definition_link!(
cc73fe71162d564ffda8e549b79a350bca53c454,
"cc73fe71162d564ffda8e549b79a350bca53c454",
"rpc/core_rpc_server_commands_defs.h",
2393..=2400
)]
/// Used in [`crate::json::SyncInfoResponse`].
SyncInfoPeer {
info: ConnectionInfo,
}
}
define_struct_and_impl_epee! {
#[doc = monero_definition_link!(
cc73fe71162d564ffda8e549b79a350bca53c454,
"cc73fe71162d564ffda8e549b79a350bca53c454",
"rpc/core_rpc_server_commands_defs.h",
2402..=2421
)]
/// Used in [`crate::json::SyncInfoResponse`].
Span {
connection_id: String,
nblocks: u64,
@ -248,145 +234,86 @@ define_struct_and_impl_epee! {
speed: u32,
start_block_height: u64,
}
}
define_struct_and_impl_epee! {
#[doc = monero_definition_link!(
cc73fe71162d564ffda8e549b79a350bca53c454,
"cc73fe71162d564ffda8e549b79a350bca53c454",
"rpc/core_rpc_server_commands_defs.h",
1637..=1642
)]
#[derive(Copy)]
/// Used in [`crate::json::GetTransactionPoolBacklogResponse`].
TxBacklogEntry {
weight: u64,
fee: u64,
time_in_pool: u64,
}
}
define_struct_and_impl_epee! {
#[doc = monero_definition_link!(
cc73fe71162d564ffda8e549b79a350bca53c454,
"cc73fe71162d564ffda8e549b79a350bca53c454",
"rpc/rpc_handler.h",
45..=50
)]
/// Used in [`crate::json::GetOutputDistributionResponse`].
OutputDistributionData {
amount: u64,
distribution: Vec<u64>,
start_height: u64,
base: u64,
}
}
define_struct_and_impl_epee! {
#[doc = monero_definition_link!(
cc73fe71162d564ffda8e549b79a350bca53c454,
"cc73fe71162d564ffda8e549b79a350bca53c454",
"rpc/core_rpc_server_commands_defs.h",
1016..=1027
)]
/// Used in [`crate::json::GetMinerDataResponse`].
///
/// Note that this is different than [`crate::misc::TxBacklogEntry`].
GetMinerDataTxBacklogEntry {
id: String,
id: Hex<32>,
weight: u64,
fee: u64,
}
}
define_struct_and_impl_epee! {
#[doc = monero_definition_link!(
cc73fe71162d564ffda8e549b79a350bca53c454,
"cc73fe71162d564ffda8e549b79a350bca53c454",
"rpc/core_rpc_server_commands_defs.h",
1070..=1079
)]
/// Used in [`crate::json::AddAuxPowRequest`].
AuxPow {
id: String,
hash: String,
id: Hex<32>,
hash: Hex<32>,
}
}
define_struct_and_impl_epee! {
#[doc = monero_definition_link!(
cc73fe71162d564ffda8e549b79a350bca53c454,
"cc73fe71162d564ffda8e549b79a350bca53c454",
"rpc/core_rpc_server_commands_defs.h",
192..=199
)]
/// Used in [`crate::bin::GetBlocksResponse`].
TxOutputIndices {
indices: Vec<u64>,
}
}
define_struct_and_impl_epee! {
#[doc = monero_definition_link!(
cc73fe71162d564ffda8e549b79a350bca53c454,
"cc73fe71162d564ffda8e549b79a350bca53c454",
"rpc/core_rpc_server_commands_defs.h",
201..=208
)]
/// Used in [`crate::bin::GetBlocksResponse`].
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::GetBlocksResponse`].
PoolTxInfo {
tx_hash: [u8; 32],
tx_blob: String,
double_spend_seen: bool,
}
}
define_struct_and_impl_epee! {
#[doc = monero_definition_link!(
cc73fe71162d564ffda8e549b79a350bca53c454,
"cc73fe71162d564ffda8e549b79a350bca53c454",
"rpc/core_rpc_server_commands_defs.h",
512..=521
)]
#[derive(Copy)]
///
/// Used in:
/// - [`crate::bin::GetOutsRequest`]
/// - [`crate::other::GetOutsRequest`]
GetOutputsOut {
amount: u64,
index: u64,
}
}
define_struct_and_impl_epee! {
#[doc = monero_definition_link!(
cc73fe71162d564ffda8e549b79a350bca53c454,
"rpc/core_rpc_server_commands_defs.h",
538..=553
)]
#[derive(Copy)]
/// Used in [`crate::bin::GetOutsRequest`].
OutKeyBin {
key: [u8; 32],
mask: [u8; 32],
unlocked: bool,
height: u64,
txid: [u8; 32],
}
}
define_struct_and_impl_epee! {
#[doc = monero_definition_link!(
cc73fe71162d564ffda8e549b79a350bca53c454,
"cc73fe71162d564ffda8e549b79a350bca53c454",
"rpc/core_rpc_server_commands_defs.h",
1335..=1367
)]
/// Used in [`crate::other::GetPeerListResponse`].
Peer {
id: u64,
host: String,
@ -400,88 +327,70 @@ define_struct_and_impl_epee! {
#[cfg_attr(feature = "serde", serde(default = "default_zero"))]
pruning_seed: u32 = default_zero::<u32>(),
}
}
define_struct_and_impl_epee! {
#[doc = monero_definition_link!(
cc73fe71162d564ffda8e549b79a350bca53c454,
"cc73fe71162d564ffda8e549b79a350bca53c454",
"rpc/core_rpc_server_commands_defs.h",
1398..=1417
)]
///
/// Used in:
/// - [`crate::other::GetPeerListResponse`]
/// - [`crate::other::GetPublicNodesResponse`]
PublicNode {
host: String,
last_seen: u64,
rpc_port: u16,
rpc_credits_per_hash: u32,
}
}
define_struct_and_impl_epee! {
#[doc = monero_definition_link!(
cc73fe71162d564ffda8e549b79a350bca53c454,
"cc73fe71162d564ffda8e549b79a350bca53c454",
"rpc/core_rpc_server_commands_defs.h",
1519..=1556
)]
/// Used in [`crate::other::GetTransactionPoolResponse`].
TxInfo {
blob_size: u64,
do_not_relay: bool,
double_spend_seen: bool,
fee: u64,
id_hash: String,
id_hash: [u8; 32],
kept_by_block: bool,
last_failed_height: u64,
last_failed_id_hash: String,
last_failed_id_hash: [u8; 32],
last_relayed_time: u64,
max_used_block_height: u64,
max_used_block_id_hash: String,
max_used_block_id_hash: [u8; 32],
receive_time: u64,
relayed: bool,
tx_blob: String,
tx_json: String, // TODO: this should be another struct
tx_blob: Vec<u8>,
tx_json: crate::json::tx::Transaction,
#[cfg_attr(feature = "serde", serde(default = "default_zero"))]
weight: u64 = default_zero::<u64>(),
}
}
define_struct_and_impl_epee! {
#[doc = monero_definition_link!(
cc73fe71162d564ffda8e549b79a350bca53c454,
"cc73fe71162d564ffda8e549b79a350bca53c454",
"rpc/core_rpc_server_commands_defs.h",
1558..=1567
)]
/// Used in [`crate::other::GetTransactionPoolResponse`].
SpentKeyImageInfo {
id_hash: String,
txs_hashes: Vec<String>,
id_hash: [u8; 32],
txs_hashes: Vec<[u8; 32]>,
}
}
define_struct_and_impl_epee! {
#[doc = monero_definition_link!(
cc73fe71162d564ffda8e549b79a350bca53c454,
"cc73fe71162d564ffda8e549b79a350bca53c454",
"rpc/core_rpc_server_commands_defs.h",
1666..=1675
)]
#[derive(Copy)]
/// Used in [`crate::other::GetTransactionPoolStatsResponse`].
TxpoolHisto {
txs: u32,
bytes: u64,
}
}
define_struct_and_impl_epee! {
#[doc = monero_definition_link!(
cc73fe71162d564ffda8e549b79a350bca53c454,
"cc73fe71162d564ffda8e549b79a350bca53c454",
"rpc/core_rpc_server_commands_defs.h",
1677..=1710
)]
/// Used in [`crate::other::GetTransactionPoolStatsResponse`].
TxpoolStats {
bytes_max: u32,
bytes_med: u32,
@ -497,24 +406,147 @@ define_struct_and_impl_epee! {
oldest: u64,
txs_total: u32,
}
}
define_struct_and_impl_epee! {
#[doc = monero_definition_link!(
cc73fe71162d564ffda8e549b79a350bca53c454,
"rpc/core_rpc_server_commands_defs.h",
582..=597
"893916ad091a92e765ce3241b94e706ad012b62a",
"blockchain_db/lmdb/db_lmdb.cpp",
4222
)]
/// Used in [`crate::other::GetOutsResponse`].
OutKey {
key: String,
mask: String,
OutputHistogramInput {
amounts: Vec<u64>,
min_count: u64,
max_count: u64,
unlocked: bool,
recent_cutoff: u64,
}
#[doc = monero_definition_link!(
"893916ad091a92e765ce3241b94e706ad012b62a",
"rpc/core_rpc_server_commands_defs.h",
2139..=2156
)]
OutputHistogramEntry {
amount: u64,
total_instances: u64,
unlocked_instances: u64,
recent_instances: u64,
}
#[doc = monero_definition_link!(
"893916ad091a92e765ce3241b94e706ad012b62a",
"rpc/core_rpc_server_commands_defs.h",
2228..=2247
)]
CoinbaseTxSum {
emission_amount_top64: u64,
emission_amount: u64,
fee_amount_top64: u64,
fee_amount: u64,
}
#[doc = monero_definition_link!(
"893916ad091a92e765ce3241b94e706ad012b62a",
"rpc/core_rpc_server_commands_defs.h",
1027..=1033
)]
MinerData {
major_version: u8,
height: u64,
txid: String,
prev_id: [u8; 32],
seed_hash: [u8; 32],
difficulty_top64: u64,
difficulty: u64,
median_weight: u64,
already_generated_coins: u64,
tx_backlog: Vec<MinerDataTxBacklogEntry>,
}
#[doc = monero_definition_link!(
"893916ad091a92e765ce3241b94e706ad012b62a",
"rpc/core_rpc_server_commands_defs.h",
1037..=1039
)]
MinerDataTxBacklogEntry {
id: [u8; 32],
weight: u64,
fee: u64,
}
#[doc = monero_definition_link!(
"893916ad091a92e765ce3241b94e706ad012b62a",
"rpc/core_rpc_server_commands_defs.h",
1973..=1980
)]
HardForkInfo {
earliest_height: u64,
enabled: bool,
state: u32,
threshold: u32,
version: u8,
votes: u32,
voting: u8,
window: u32,
}
#[doc = monero_definition_link!(
"893916ad091a92e765ce3241b94e706ad012b62a",
"rpc/core_rpc_server_commands_defs.h",
2264..=2267
)]
FeeEstimate {
fee: u64,
fees: Vec<u64>,
quantization_mask: u64,
}
#[doc = monero_definition_link!(
"893916ad091a92e765ce3241b94e706ad012b62a",
"rpc/core_rpc_server_commands_defs.h",
1115..=1119
)]
AddAuxPow {
blocktemplate_blob: Vec<u8>,
blockhashing_blob: Vec<u8>,
merkle_root: [u8; 32],
merkle_tree_depth: u64,
aux_pow: Vec<AuxPow>,
}
#[doc = monero_definition_link!(
"893916ad091a92e765ce3241b94e706ad012b62a",
"rpc/core_rpc_server_commands_defs.h",
227..=229
)]
PoolTxInfo {
tx_hash: [u8; 32],
tx_blob: Vec<u8>,
double_spend_seen: bool,
}
#[doc = monero_definition_link!(
"893916ad091a92e765ce3241b94e706ad012b62a",
"rpc/core_rpc_server_commands_defs.h",
254..=256
)]
PoolInfoIncremental {
added_pool_txs: Vec<PoolTxInfo>,
remaining_added_pool_txids: ByteArrayVec<32>,
removed_pool_txids: ByteArrayVec<32>,
}
#[doc = monero_definition_link!(
"893916ad091a92e765ce3241b94e706ad012b62a",
"rpc/core_rpc_server_commands_defs.h",
254..=256
)]
PoolInfoFull {
added_pool_txs: Vec<PoolTxInfo>,
remaining_added_pool_txids: ByteArrayVec<32>,
}
}
//---------------------------------------------------------------------------------------------------- Tests
#[cfg(test)]
mod test {}
mod test {
// use super::*;
}

View file

@ -145,118 +145,9 @@ pub struct OutputOnChain {
pub key: CompressedEdwardsY,
/// The output's commitment.
pub commitment: CompressedEdwardsY,
}
/// Input required to generate an output histogram.
///
/// Used in RPC's `get_output_histogram`.
#[derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct OutputHistogramInput {
pub amounts: Vec<u64>,
pub min_count: u64,
pub max_count: u64,
pub unlocked: bool,
pub recent_cutoff: u64,
}
/// A single entry in an output histogram.
///
/// Used in RPC's `get_output_histogram`.
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct OutputHistogramEntry {
pub amount: u64,
pub total_instances: u64,
pub unlocked_instances: u64,
pub recent_instances: u64,
}
/// Data of summed coinbase transactions.
///
/// Used in RPC's `get_coinbase_tx_sum`.
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct CoinbaseTxSum {
pub emission_amount: u128,
pub fee_amount: u128,
}
/// Data to create a custom block template.
///
/// Used in RPC's `get_miner_data`.
#[derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct MinerData {
pub major_version: u8,
pub height: u64,
pub prev_id: [u8; 32],
pub seed_hash: [u8; 32],
pub difficulty: u128,
pub median_weight: u64,
pub already_generated_coins: u64,
pub tx_backlog: Vec<MinerDataTxBacklogEntry>,
}
/// A transaction in the txpool.
///
/// Used in [`MinerData::tx_backlog`].
#[derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct MinerDataTxBacklogEntry {
pub id: [u8; 32],
pub weight: u64,
pub fee: u64,
}
/// Information on a [`HardFork`].
///
/// Used in RPC's `hard_fork_info`.
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct HardForkInfo {
pub earliest_height: u64,
pub enabled: bool,
pub state: u32,
pub threshold: u32,
pub version: u8,
pub votes: u32,
pub voting: u8,
pub window: u32,
}
/// Estimated fee data.
///
/// Used in RPC's `get_fee_estimate`.
#[derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct FeeEstimate {
pub fee: u64,
pub fees: Vec<u64>,
pub quantization_mask: u64,
}
/// Information on a (maybe alternate) chain.
///
/// Used in RPC's `get_alternate_chains`.
#[derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct ChainInfo {
pub block_hash: [u8; 32],
pub block_hashes: Vec<[u8; 32]>,
pub difficulty: u128,
pub height: u64,
pub length: u64,
pub main_chain_parent_block: [u8; 32],
}
/// Used in RPC's `add_aux_pow`.
#[derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct AuxPow {
pub id: [u8; 32],
pub hash: [u8; 32],
}
/// Used in RPC's `add_aux_pow`.
#[derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct AddAuxPow {
pub blocktemplate_blob: Vec<u8>,
pub blockhashing_blob: Vec<u8>,
pub merkle_root: [u8; 32],
pub merkle_tree_depth: u64,
pub aux_pow: Vec<AuxPow>,
/// The hash of the transaction hash this output belongs to.
/// This is [`None`] when this field is not requested for in relevant functions.
pub txid: Option<[u8; 32]>,
}
/// The inner response for a request for txs in a block.
@ -266,6 +157,69 @@ pub struct TxsInBlock {
pub txs: Vec<Vec<u8>>,
}
/// A block template, used in RPC's `get_block_template`.
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct BlockTemplate {
pub block: Block,
pub reserved_offset: u64,
pub difficulty: u128,
pub height: u64,
pub expected_reward: u64,
pub seed_height: u64,
pub seed_hash: [u8; 32],
pub next_seed_hash: [u8; 32],
}
/// TODO
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct TxInBlockchain {
pub block_height: u64,
pub block_timestamp: u64,
pub confirmations: u64,
pub output_indices: Vec<u64>,
pub tx_hash: [u8; 32],
pub tx_blob: Vec<u8>,
pub pruned_blob: Vec<u8>,
pub prunable_blob: Vec<u8>,
pub prunable_hash: [u8; 32],
}
/// TODO
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct TxInPool {
pub tx_blob: Vec<u8>,
pub tx_hash: [u8; 32],
pub double_spend_seen: bool,
pub received_timestamp: u64,
pub relayed: bool,
}
bitflags::bitflags! {
pub struct TxRelayChecks: u16 {
const DOUBLE_SPEND = 1;
const FEE_TOO_LOW = 1 << 1;
const INVALID_INPUT = 1 << 2;
const INVALID_OUTPUT = 1 << 3;
const LOW_MIXIN = 1 << 4;
const NONZERO_UNLOCK_TIME = 1 << 5;
const OVERSPEND = 1 << 6;
const TOO_BIG = 1 << 7;
const TOO_FEW_OUTPUTS = 1 << 8;
const TX_EXTRA_TOO_BIG = 1 << 9;
}
}
/// Used in RPC's `get_output_distribution`.
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct OutputDistributionInput {
pub amounts: Vec<u64>,
pub cumulative: bool,
pub from_height: u64,
/// [`None`] means the entire blockchain.
pub to_height: Option<NonZero<u64>>,
}
//---------------------------------------------------------------------------------------------------- Tests
#[cfg(test)]
mod test {

View file

@ -10,7 +10,7 @@ repository = "https://github.com/Cuprate/cuprate/tree/main/zmq/types"
[dependencies]
serde = { workspace = true, features = ["derive"] }
hex = { workspace = true, features = ["std", "serde"] }
cuprate-types = { workspace = true, features = ["hex"] }
cuprate-hex = { workspace = true }
[dev-dependencies]
serde_json = { workspace = true, features = ["std"] }

Some files were not shown because too many files have changed in this diff Show more