Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

cuprated: RPC handlers (json-rpc) #308

Open
wants to merge 59 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
59 commits
Select commit Hold shift + click to select a range
d170b46
import diffs
hinto-janai Oct 10, 2024
02e950b
small fixes, hardfork changes
hinto-janai Oct 10, 2024
c18ba6e
lints
hinto-janai Oct 10, 2024
93be3be
hard_fork
hinto-janai Oct 11, 2024
c590bbb
apply diffs
hinto-janai Oct 11, 2024
cec746b
review fixes
hinto-janai Oct 11, 2024
e57c887
Merge branch 'hf' into json
hinto-janai Oct 11, 2024
ac6360a
Merge branch 'main' into json
hinto-janai Oct 11, 2024
2522bb8
binaries/cuprated/src/rpc/request: `pub(super)` -> `pub(crate)`
hinto-janai Oct 11, 2024
fd136b2
add `BlockChainContextService`, `on_get_block_hash`
hinto-janai Oct 11, 2024
9459d59
Merge branch 'main' into json
hinto-janai Oct 12, 2024
b82ff27
map `tower::BoxError` to `anyhow::Error`
hinto-janai Oct 12, 2024
8a3229b
get_block
hinto-janai Oct 12, 2024
9422c07
connection_info
hinto-janai Oct 14, 2024
50bd576
hard_fork_info
hinto-janai Oct 14, 2024
f677b1a
set_bans
hinto-janai Oct 14, 2024
37bcbb5
get_bans
hinto-janai Oct 14, 2024
c7ae795
banned
hinto-janai Oct 14, 2024
6af977f
flush_transaction_pool
hinto-janai Oct 14, 2024
1a76934
get_output_histogram
hinto-janai Oct 14, 2024
1d435cc
get_coinbase_tx_sum
hinto-janai Oct 15, 2024
d7fef15
get_version
hinto-janai Oct 15, 2024
1e0ab56
get_fee_estimate
hinto-janai Oct 15, 2024
294df3f
get_alternate_chains
hinto-janai Oct 15, 2024
08b5ff2
relay_tx
hinto-janai Oct 15, 2024
2daf255
response_base: `fn` -> `const`
hinto-janai Oct 15, 2024
a10ed59
get_transaction_pool_backlog
hinto-janai Oct 15, 2024
d7d8b35
prune
hinto-janai Oct 15, 2024
5926f24
calc_pow
hinto-janai Oct 15, 2024
7148ba1
add_aux_pow
hinto-janai Oct 15, 2024
d56477c
get_tx_ids_loose
hinto-janai Oct 15, 2024
548639b
generate_blocks
hinto-janai Oct 16, 2024
e2f25bd
get_info
hinto-janai Oct 16, 2024
4b1d7bc
sync_info
hinto-janai Oct 16, 2024
bd3a844
get_miner_data
hinto-janai Oct 16, 2024
bf07f2c
`BlockchainManagerRequest` docs
hinto-janai Oct 17, 2024
98dfaa4
docs, `ConnectionInfo`, `AddressType`
hinto-janai Oct 17, 2024
13eedc6
sig docs, remove `HardForks` request
hinto-janai Oct 17, 2024
79b03e3
Merge branch 'main' into json
hinto-janai Nov 2, 2024
544287d
Merge branch 'main' into json
hinto-janai Nov 13, 2024
0cb6a76
clean imports
hinto-janai Nov 13, 2024
e30eeb8
fix `on_get_block_hash`, `generate_blocks`, `get_block_headers_range`
hinto-janai Nov 13, 2024
228a718
fix `get_info`, `banned`
hinto-janai Nov 13, 2024
9374d08
fix `sync_info`
hinto-janai Nov 13, 2024
fd84940
fix `get_miner_data`
hinto-janai Nov 13, 2024
46bf198
initial `add_aux_pow` impl
hinto-janai Nov 14, 2024
1f3b5ed
fix `calculate_pow`
hinto-janai Nov 14, 2024
e975c42
add_aux_pow
hinto-janai Nov 15, 2024
d634bea
`get_output_distribution`
hinto-janai Nov 15, 2024
854b175
checkup
hinto-janai Nov 16, 2024
2d2d493
`find_nonce()` + `add_aux_pow` async wrapper
hinto-janai Nov 16, 2024
86f2dd1
fixes
hinto-janai Nov 18, 2024
de7abfc
`helper::block_header`
hinto-janai Nov 26, 2024
1ffa86c
review fixes
hinto-janai Nov 27, 2024
916d593
fixes
hinto-janai Nov 27, 2024
ff48fb4
doc fix
hinto-janai Nov 27, 2024
8d5f779
p2p: remove tmp `AddressBookRequest::NextNeededPruningSeed`
hinto-janai Nov 27, 2024
1a079c8
lint/todo fixes
hinto-janai Nov 27, 2024
a3fa638
fix bans
hinto-janai Nov 27, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
313 changes: 155 additions & 158 deletions Cargo.lock

Large diffs are not rendered by default.

5 changes: 3 additions & 2 deletions binaries/cuprated/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ cuprate-consensus = { workspace = true }
cuprate-fast-sync = { workspace = true }
cuprate-consensus-context = { workspace = true }
cuprate-consensus-rules = { workspace = true }
cuprate-constants = { workspace = true }
cuprate-constants = { workspace = true, features = ["build", "rpc"] }
cuprate-cryptonight = { workspace = true }
cuprate-helper = { workspace = true }
cuprate-epee-encoding = { workspace = true }
Expand All @@ -31,7 +31,7 @@ cuprate-txpool = { workspace = true }
cuprate-database = { workspace = true }
cuprate-pruning = { workspace = true }
cuprate-test-utils = { workspace = true }
cuprate-types = { workspace = true }
cuprate-types = { workspace = true, features = ["json"] }
cuprate-json-rpc = { workspace = true }
cuprate-rpc-interface = { workspace = true }
cuprate-rpc-types = { workspace = true }
Expand Down Expand Up @@ -66,6 +66,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 }
Expand Down
1 change: 1 addition & 0 deletions binaries/cuprated/src/rpc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
mod bin;
mod constants;
mod handler;
mod helper;
mod json;
mod other;
mod request;
Expand Down
2 changes: 2 additions & 0 deletions binaries/cuprated/src/rpc/bin.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
//! RPC request handler functions (binary endpoints).

use anyhow::Error;

use cuprate_rpc_types::{
Expand Down
17 changes: 9 additions & 8 deletions binaries/cuprated/src/rpc/handler.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//! Dummy implementation of [`RpcHandler`].
//! `cuprated`'s implementation of [`RpcHandler`].

use std::task::{Context, Poll};

Expand All @@ -7,7 +7,7 @@ use futures::future::BoxFuture;
use monero_serai::block::Block;
use tower::Service;

use cuprate_blockchain::service::{BlockchainReadHandle, BlockchainWriteHandle};
use cuprate_blockchain::service::BlockchainReadHandle;
use cuprate_consensus::BlockChainContextService;
use cuprate_pruning::PruningSeed;
use cuprate_rpc_interface::RpcHandler;
Expand All @@ -16,14 +16,12 @@ 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 crate::rpc::{bin, json, other};

/// TODO: use real type when public.
#[derive(Clone)]
#[expect(clippy::large_enum_variant)]
pub enum BlockchainManagerRequest {
/// Pop blocks off the top of the blockchain.
///
Expand All @@ -37,7 +35,10 @@ 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>,
),

/// Is the blockchain in the middle of syncing?
///
Expand Down Expand Up @@ -139,7 +140,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,
Expand Down Expand Up @@ -182,7 +183,7 @@ impl CupratedRpcHandler {
}

impl RpcHandler for CupratedRpcHandler {
fn restricted(&self) -> bool {
fn is_restricted(&self) -> bool {
self.restricted
}
}
Expand Down
186 changes: 186 additions & 0 deletions binaries/cuprated/src/rpc/helper.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
//! 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::request`] 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::misc::{BlockHeader, KeyImageSpentStatus};
use cuprate_types::HardFork;

use crate::{
rpc::request::{blockchain, blockchain_context},
rpc::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;
Comment on lines +36 to +38
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Alt blocks need to be handled here, is there a cheap way this could be done?


// FIXME: is there a cheaper way to get this?
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?;

let pow_hash = blockchain_context::calculate_pow(
&mut state.blockchain_context,
hardfork,
block,
seed_hash,
)
.await?;

hex::encode(pow_hash)
} else {
String::new()
};

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 wide_difficulty = hex::encode(difficulty.to_ne_bytes());

let reward = block
.miner_transaction
.prefix()
.outputs
.iter()
.map(|o| o.amount.expect("coinbase is transparent"))
.sum::<u64>();

Ok(BlockHeader {
block_size: block_weight,
block_weight,
cumulative_difficulty_top64,
cumulative_difficulty,
depth,
difficulty_top64,
difficulty,
hash: hex::encode(block.hash()),
height,
long_term_weight: usize_to_u64(header.long_term_weight),
major_version: header.version.as_u8(),
miner_tx_hash: hex::encode(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: hex::encode(block.header.previous),
reward,
timestamp: block.header.timestamp,
wide_cumulative_difficulty: hex::encode(u128::to_le_bytes(header.cumulative_difficulty)),
wide_difficulty,
})
}

/// 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.saturating_sub(1);
Ok((height, hash))
}

/// Check if a key image is spent.
pub(super) async fn key_image_spent(
state: &mut CupratedRpcHandler,
key_image: [u8; 32],
) -> Result<KeyImageSpentStatus, Error> {
todo!("impl key image vec check responding KeyImageSpentStatus")
// if blockchain::key_image_spent(state, key_image).await? {
// Ok(KeyImageSpentStatus::SpentInBlockchain)
// } else if todo!("key image is spent in tx pool") {
// Ok(KeyImageSpentStatus::SpentInPool)
// } else {
// Ok(KeyImageSpentStatus::Unspent)
// }
}
Comment on lines +174 to +186
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TODO: not JSON-RPC but /is_key_image_spent requires something like BlockchainReadRequest::KeyImagesSpent(Vec<[u8; 32]>) -> BlockchainResponse::KeyImagesSpent(Vec<KeyImageSpentStatus>)

Loading