From b2b7e93e4cfbb9f5794913aa811cbaf96ca04fcc Mon Sep 17 00:00:00 2001 From: "hinto.janai" Date: Thu, 19 Dec 2024 20:41:50 -0500 Subject: [PATCH] json-rpc fixes --- Cargo.lock | 25 +++- .../cuprated/src/rpc/handlers/json_rpc.rs | 121 ++++++++++-------- binaries/cuprated/src/rpc/service/txpool.rs | 18 +-- rpc/types/src/json.rs | 33 +++-- 4 files changed, 115 insertions(+), 82 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index fda0fce..ab21d3d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -885,6 +885,7 @@ dependencies = [ "bytes", "cuprate-fixed-bytes", "cuprate-helper", + "cuprate-hex", "hex", "paste", "ref-cast", @@ -939,6 +940,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" @@ -1066,9 +1076,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", ] @@ -1125,12 +1141,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", "monero-serai", "pretty_assertions", @@ -1162,7 +1180,7 @@ name = "cuprate-zmq-types" version = "0.1.0" dependencies = [ "assert-json-diff", - "cuprate-types", + "cuprate-hex", "hex", "serde", "serde_json", @@ -1190,6 +1208,7 @@ dependencies = [ "cuprate-consensus", "cuprate-consensus-context", "cuprate-consensus-rules", + "cuprate-constants", "cuprate-cryptonight", "cuprate-dandelion-tower", "cuprate-database", @@ -1198,6 +1217,7 @@ dependencies = [ "cuprate-fast-sync", "cuprate-fixed-bytes", "cuprate-helper", + "cuprate-hex", "cuprate-json-rpc", "cuprate-levin", "cuprate-p2p", @@ -1226,6 +1246,7 @@ dependencies = [ "serde", "serde_bytes", "serde_json", + "strum", "thiserror", "thread_local", "tokio", diff --git a/binaries/cuprated/src/rpc/handlers/json_rpc.rs b/binaries/cuprated/src/rpc/handlers/json_rpc.rs index 6c42ec8..a93cadb 100644 --- a/binaries/cuprated/src/rpc/handlers/json_rpc.rs +++ b/binaries/cuprated/src/rpc/handlers/json_rpc.rs @@ -54,7 +54,7 @@ use cuprate_rpc_types::{ }; use cuprate_types::{ rpc::{AuxPow, CoinbaseTxSum, GetMinerDataTxBacklogEntry, HardForkEntry, TxBacklogEntry}, - BlockTemplate, HardFork, + BlockTemplate, Chain, HardFork, }; use crate::{ @@ -228,12 +228,7 @@ async fn on_get_block_hash( request: OnGetBlockHashRequest, ) -> Result { let [height] = request.block_height; - let hash = blockchain::block_hash( - &mut state.blockchain_read, - height, - todo!("access to `cuprated`'s Chain"), - ) - .await?; + let hash = blockchain::block_hash(&mut state.blockchain_read, height, Chain::Main).await?; Ok(OnGetBlockHashResponse { block_hash: Hex(hash), @@ -428,7 +423,7 @@ async fn get_block( let blob = HexVec(block.serialize()); let miner_tx_hash = Hex(block.miner_transaction.hash()); - let tx_hashes = block.transactions.iter().map(|a| Hex(*a)).collect(); + let tx_hashes = block.transactions.iter().copied().map(Hex).collect(); let json = { let block = cuprate_types::json::block::Block::from(block); serde_json::to_string_pretty(&block)? @@ -467,8 +462,7 @@ async fn get_info( let c = context.unchecked_blockchain_context(); let cumulative_difficulty = c.cumulative_difficulty; - let adjusted_time = c.current_adjusted_timestamp_for_time_lock(); // TODO: is this correct? - + let adjusted_time = c.current_adjusted_timestamp_for_time_lock(); let c = &c.context_to_verify_block; let alt_blocks_count = if restricted { @@ -476,76 +470,98 @@ async fn get_info( } else { blockchain::alt_chain_count(&mut state.blockchain_read).await? }; - let block_weight_limit = usize_to_u64(c.effective_median_weight); // TODO: is this correct? - let block_weight_median = usize_to_u64(c.median_weight_for_block_reward); // TODO: is this correct? + + let block_weight_limit = usize_to_u64(c.effective_median_weight); + let block_weight_median = usize_to_u64(c.median_weight_for_block_reward); let block_size_limit = block_weight_limit; let block_size_median = block_weight_median; + + #[expect(clippy::if_same_then_else, reason = "TODO: support bootstrap")] let (bootstrap_daemon_address, was_bootstrap_ever_used) = if restricted { (String::new(), false) } else { - todo!("support bootstrap daemon") + (String::new(), false) }; + let busy_syncing = blockchain_manager::syncing(&mut state.blockchain_manager).await?; + let (cumulative_difficulty, cumulative_difficulty_top64) = split_u128_into_low_high_bits(cumulative_difficulty); + let (database_size, free_space) = blockchain::database_size(&mut state.blockchain_read).await?; let (database_size, free_space) = if restricted { // const fn round_up(value: u64, quantum: u64) -> u64 { value.div_ceil(quantum) } + let database_size = round_up(database_size, 5 * 1024 * 1024 * 1024); (database_size, u64::MAX) } else { (database_size, free_space) }; + let (difficulty, difficulty_top64) = split_u128_into_low_high_bits(c.next_difficulty); + let height = usize_to_u64(c.chain_height); let height_without_bootstrap = if restricted { 0 } else { height }; + let (incoming_connections_count, outgoing_connections_count) = if restricted { (0, 0) } else { address_book::connection_count::(&mut DummyAddressBook).await? }; - let (mainnet, testnet, stagenet) = match todo!("access to `cuprated`'s `Network`") { + + // TODO: This should be `cuprated`'s active network. + let network = Network::Mainnet; + + let (mainnet, testnet, stagenet) = match network { Network::Mainnet => (true, false, false), Network::Testnet => (false, true, false), Network::Stagenet => (false, false, true), }; - // TODO: make sure this is: - // - the same case as `monerod` - // - untagged (no `Network::`) - let nettype = todo!("access to `cuprated`'s `Network`").to_string(); - let offline = todo!("access to CLI/config's `--offline`"); - let rpc_connections_count = if restricted { - 0 - } else { - todo!("implement a connection counter in axum/RPC") - }; + + let nettype = network.to_string(); + // TODO: access to CLI/config's `--offline` + let offline = false; + + #[expect( + clippy::if_same_then_else, + reason = "TODO: implement a connection counter in axum/RPC" + )] + let rpc_connections_count = if restricted { 0 } else { 0 }; + let start_time = if restricted { 0 } else { *START_INSTANT_UNIX }; let synchronized = blockchain_manager::synced(&mut state.blockchain_manager).await?; + let target_height = blockchain_manager::target_height(&mut state.blockchain_manager).await?; let target = blockchain_manager::target(&mut state.blockchain_manager) .await? .as_secs(); let top_block_hash = Hex(c.top_hash); + let tx_count = blockchain::total_tx_count(&mut state.blockchain_read).await?; let tx_pool_size = txpool::size(&mut state.txpool_read, !restricted).await?; - let update_available = if restricted { - false - } else { - todo!("implement an update checker for `cuprated`") - }; + + #[expect( + clippy::if_same_then_else, + clippy::needless_bool, + reason = "TODO: implement an update checker for `cuprated`?" + )] + let update_available = if restricted { false } else { false }; + let version = if restricted { String::new() } else { VERSION_BUILD.to_string() }; + let (white_peerlist_size, grey_peerlist_size) = if restricted { (0, 0) } else { address_book::peerlist_size::(&mut DummyAddressBook).await? }; + let wide_cumulative_difficulty = format!("{cumulative_difficulty:#x}"); let wide_difficulty = format!("{:#x}", c.next_difficulty); @@ -607,18 +623,12 @@ async fn hard_fork_info( .current_hf }; - let info = blockchain_context::hard_fork_info(&mut state.blockchain_context, hard_fork).await?; + let hard_fork_info = + blockchain_context::hard_fork_info(&mut state.blockchain_context, hard_fork).await?; Ok(HardForkInfoResponse { base: helper::access_response_base(false), - earliest_height: info.earliest_height, - enabled: info.enabled, - state: info.state, - threshold: info.threshold, - version: info.version, - votes: info.votes, - voting: info.voting, - window: info.window, + hard_fork_info, }) } @@ -1036,6 +1046,7 @@ async fn calc_pow( // } // TODO: will `CalculatePow` do the above checks? + let pow_hash = blockchain_context::calculate_pow( &mut state.blockchain_context, hardfork, @@ -1049,23 +1060,16 @@ async fn calc_pow( }) } -/// -async fn flush_cache( - state: CupratedRpcHandler, - request: FlushCacheRequest, -) -> Result { - // TODO: cuprated doesn't need this call; decide behavior. - - Ok(FlushCacheResponse { - base: helper::response_base(false), - }) -} - /// An async-friendly wrapper for [`add_aux_pow_inner`]. async fn add_aux_pow( state: CupratedRpcHandler, request: AddAuxPowRequest, ) -> Result { + // This method can be a bit heavy, so rate-limit restricted use. + if state.is_restricted() { + tokio::time::sleep(Duration::from_millis(100)).await; + } + tokio::task::spawn_blocking(|| add_aux_pow_inner(state, request)).await? } @@ -1261,16 +1265,25 @@ fn add_aux_pow_inner( }) } +//---------------------------------------------------------------------------------------------------- Unsupported RPC calls (for now) + /// async fn get_tx_ids_loose( state: CupratedRpcHandler, request: GetTxIdsLooseRequest, ) -> Result { - // TODO: this RPC call is not yet in the v0.18 branch. - return Err(anyhow!("Not implemented")); - Ok(GetTxIdsLooseResponse { base: helper::response_base(false), - txids: todo!(), + txids: todo!("this RPC call is not yet in the v0.18 branch."), }) } + +//---------------------------------------------------------------------------------------------------- Unsupported RPC calls (forever) + +/// +async fn flush_cache( + state: CupratedRpcHandler, + request: FlushCacheRequest, +) -> Result { + unreachable!() +} diff --git a/binaries/cuprated/src/rpc/service/txpool.rs b/binaries/cuprated/src/rpc/service/txpool.rs index 7f9b673..88a75d4 100644 --- a/binaries/cuprated/src/rpc/service/txpool.rs +++ b/binaries/cuprated/src/rpc/service/txpool.rs @@ -59,7 +59,7 @@ pub async fn size( Ok(usize_to_u64(size)) } -/// TODO +/// [`TxpoolReadRequest::PoolInfo`] pub async fn pool_info( txpool_read: &mut TxpoolReadHandle, include_sensitive_txs: bool, @@ -84,7 +84,7 @@ pub async fn pool_info( Ok(pool_info) } -/// TODO +/// [`TxpoolReadRequest::TxsByHash`] pub async fn txs_by_hash( txpool_read: &mut TxpoolReadHandle, tx_hashes: Vec<[u8; 32]>, @@ -107,7 +107,7 @@ pub async fn txs_by_hash( Ok(txs_in_pool) } -/// TODO +/// [`TxpoolReadRequest::KeyImagesSpent`] pub async fn key_images_spent( txpool_read: &mut TxpoolReadHandle, key_images: Vec<[u8; 32]>, @@ -130,7 +130,7 @@ pub async fn key_images_spent( Ok(status) } -/// TODO +/// [`TxpoolReadRequest::Pool`] pub async fn pool( txpool_read: &mut TxpoolReadHandle, include_sensitive_txs: bool, @@ -157,7 +157,7 @@ pub async fn pool( Ok((txs, spent_key_images)) } -/// TODO +/// [`TxpoolReadRequest::PoolStats`] pub async fn pool_stats( txpool_read: &mut TxpoolReadHandle, include_sensitive_txs: bool, @@ -178,7 +178,7 @@ pub async fn pool_stats( Ok(txpool_stats) } -/// TODO +/// [`TxpoolReadRequest::AllHashes`] pub async fn all_hashes( txpool_read: &mut TxpoolReadHandle, include_sensitive_txs: bool, @@ -199,19 +199,19 @@ pub async fn all_hashes( Ok(hashes) } -/// TODO +/// TODO: impl txpool manager. pub async fn flush(txpool_manager: &mut Infallible, tx_hashes: Vec<[u8; 32]>) -> Result<(), Error> { todo!(); Ok(()) } -/// TODO +/// TODO: impl txpool manager. pub async fn relay(txpool_manager: &mut Infallible, tx_hashes: Vec<[u8; 32]>) -> Result<(), Error> { todo!(); Ok(()) } -/// TODO +/// TODO: impl txpool manager. pub async fn check_maybe_relay_local( txpool_manager: &mut Infallible, tx: Transaction, diff --git a/rpc/types/src/json.rs b/rpc/types/src/json.rs index 228dcc3..d5b7c7b 100644 --- a/rpc/types/src/json.rs +++ b/rpc/types/src/json.rs @@ -7,7 +7,9 @@ use serde::{Deserialize, Serialize}; use cuprate_hex::{Hex, HexVec}; -use cuprate_types::rpc::{AuxPow, GetMinerDataTxBacklogEntry, HardForkEntry, TxBacklogEntry}; +use cuprate_types::rpc::{ + AuxPow, GetMinerDataTxBacklogEntry, HardForkEntry, HardForkInfo, TxBacklogEntry, +}; use crate::{ base::{AccessResponseBase, ResponseBase}, @@ -385,14 +387,9 @@ define_request_and_response! { }, AccessResponseBase { - earliest_height: u64, - enabled: bool, - state: u32, - threshold: u32, - version: u8, - votes: u32, - voting: u8, - window: u32, + /// This field is [flattened](https://serde.rs/field-attrs.html#flatten). + #[cfg_attr(feature = "serde", serde(flatten))] + hard_fork_info: HardForkInfo, } } @@ -1464,14 +1461,16 @@ mod test { json::HARD_FORK_INFO_RESPONSE, HardForkInfoResponse { base: AccessResponseBase::OK, - earliest_height: 2689608, - enabled: true, - state: 0, - threshold: 0, - version: 16, - votes: 10080, - voting: 16, - window: 10080, + hard_fork_info: HardForkInfo { + earliest_height: 2689608, + enabled: true, + state: 0, + threshold: 0, + version: 16, + votes: 10080, + voting: 16, + window: 10080, + }, }, ); }