mirror of
https://github.com/Cuprate/cuprate.git
synced 2025-04-22 22:08:13 +00:00
cuprated: RPC handlers (#355)
* 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:
parent
8292da4e06
commit
d3b7ca3e65
101 changed files with 8096 additions and 3538 deletions
Cargo.lockCargo.toml
binaries/cuprated
Cargo.toml
src
books/architecture/src/appendix
consensus
helper
net/epee-encoding
p2p
rpc
storage
blockchain/src
txpool
types
fixed-bytes
hex
src
types
zmq/types
25
Cargo.lock
generated
25
Cargo.lock
generated
|
@ -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",
|
||||
|
|
15
Cargo.toml
15
Cargo.toml
|
@ -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 }
|
||||
|
|
|
@ -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"] }
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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!()
|
||||
}
|
|
@ -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.";
|
||||
|
|
18
binaries/cuprated/src/rpc/handlers.rs
Normal file
18
binaries/cuprated/src/rpc/handlers.rs
Normal 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;
|
253
binaries/cuprated/src/rpc/handlers/bin.rs
Normal file
253
binaries/cuprated/src/rpc/handlers/bin.rs
Normal 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
|
||||
}
|
191
binaries/cuprated/src/rpc/handlers/helper.rs
Normal file
191
binaries/cuprated/src/rpc/handlers/helper.rs
Normal 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
|
||||
}
|
||||
}
|
1277
binaries/cuprated/src/rpc/handlers/json_rpc.rs
Normal file
1277
binaries/cuprated/src/rpc/handlers/json_rpc.rs
Normal file
File diff suppressed because it is too large
Load diff
790
binaries/cuprated/src/rpc/handlers/other_json.rs
Normal file
790
binaries/cuprated/src/rpc/handlers/other_json.rs
Normal 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!();
|
||||
}
|
130
binaries/cuprated/src/rpc/handlers/shared.rs
Normal file
130
binaries/cuprated/src/rpc/handlers/shared.rs
Normal 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>"
|
||||
),
|
||||
})
|
||||
}
|
|
@ -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!()
|
||||
}
|
|
@ -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!()
|
||||
}
|
|
@ -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(())
|
||||
}
|
|
@ -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))
|
||||
}
|
||||
}
|
|
@ -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;
|
|
@ -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))
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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(())
|
||||
}
|
244
binaries/cuprated/src/rpc/service/txpool.rs
Normal file
244
binaries/cuprated/src/rpc/service/txpool.rs
Normal 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!())
|
||||
}
|
|
@ -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
|
|
@ -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;
|
||||
|
|
|
@ -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!();
|
||||
|
|
|
@ -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,
|
||||
}),)+)+
|
||||
];
|
||||
|
||||
|
|
|
@ -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"] }
|
||||
|
|
|
@ -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
44
helper/src/fmt.rs
Normal 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 {}
|
|
@ -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;
|
||||
|
|
|
@ -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`].
|
||||
///
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 },
|
||||
|
||||
|
|
|
@ -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>>,
|
||||
}
|
||||
|
|
|
@ -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 }
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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),
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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()),
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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))]
|
||||
|
|
|
@ -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",
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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
190
rpc/types/src/from.rs
Normal 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
|
@ -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,
|
||||
};
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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
|
||||
)]
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
|
100
rpc/types/src/misc/requested_info.rs
Normal file
100
rpc/types/src/misc/requested_info.rs
Normal 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(())
|
||||
}
|
||||
}
|
|
@ -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,
|
||||
|
|
|
@ -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
331
rpc/types/src/misc/types.rs
Normal 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
|
@ -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;
|
||||
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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!()))
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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"] }
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
22
types/hex/Cargo.toml
Normal 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
2
types/hex/README.md
Normal file
|
@ -0,0 +1,2 @@
|
|||
# `cuprate-hex`
|
||||
Cuprate's hexadecimal data types.
|
154
types/hex/src/array.rs
Normal file
154
types/hex/src/array.rs
Normal 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
9
types/hex/src/lib.rs
Normal 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
156
types/hex/src/vec.rs
Normal 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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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 }
|
|
@ -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
|
|
@ -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>
|
|
@ -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.
|
||||
///
|
|
@ -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(())
|
||||
}
|
||||
}
|
|
@ -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"
|
||||
)),
|
||||
],
|
|
@ -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;
|
|
@ -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
|
@ -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
|
|
@ -89,6 +89,7 @@ impl OutputCache {
|
|||
time_lock: tx.prefix().additional_timelock,
|
||||
key: out.key,
|
||||
commitment: get_output_commitment(tx, i),
|
||||
txid: None,
|
||||
},
|
||||
);
|
||||
}
|
|
@ -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;
|
23
types/types/src/rpc/mod.rs
Normal file
23
types/types/src/rpc/mod.rs
Normal 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,
|
||||
};
|
|
@ -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`].
|
|
@ -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));
|
|
@ -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::*;
|
||||
}
|
|
@ -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 {
|
|
@ -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
Loading…
Reference in a new issue