cuprated: CupratedRpcHandler, enable certain endpoints (#450)

* add rpc server

* add init fn

* add layers

* docs

* comments

* move

* warn

* split config

* split

* fix toml

* impl p2p port

* fix tests

* docs

* doc

* remove (de)compression

* init rpc servers with handlers

* add `rpc.md`

* typo

* sort

* revert cargo.lock diff

* 🟣

* not_available()

* `advertise`

* Update binaries/cuprated/src/config/rpc.rs

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

* update tracing

* `tracing::field::display`

* fix

* typo

* docs

* clippy

* remove comment_out

* add test for `cuprate_helper::net::ip_is_local`

* fix args

* `/get_outs`, `/get_height`

* enable methods

* apply

* block fn

* fix

* clippy

* book

* add warning

* update

* typos

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

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

* fmt

---------

Co-authored-by: Boog900 <boog900@tutanota.com>
This commit is contained in:
hinto-janai
2025-06-04 12:28:40 -04:00
committed by GitHub
parent cb41f2057e
commit 4c2c749a4c
16 changed files with 309 additions and 161 deletions

View File

@@ -133,7 +133,7 @@ fn main() {
let tx_handler = IncomingTxHandler::init(
network_interfaces.clearnet_network_interface.clone(),
txpool_write_handle.clone(),
txpool_read_handle,
txpool_read_handle.clone(),
context_svc.clone(),
blockchain_read_handle.clone(),
);
@@ -149,15 +149,20 @@ fn main() {
blockchain::init_blockchain_manager(
network_interfaces.clearnet_network_interface,
blockchain_write_handle,
blockchain_read_handle,
txpool_write_handle,
blockchain_read_handle.clone(),
txpool_write_handle.clone(),
context_svc.clone(),
config.block_downloader_config(),
)
.await;
// Initialize the RPC server(s).
rpc::init_rpc_servers(config.rpc);
rpc::init_rpc_servers(
config.rpc,
blockchain_read_handle,
context_svc.clone(),
txpool_read_handle,
);
// Start the command listener.
if std::io::IsTerminal::is_terminal(&std::io::stdin()) {

View File

@@ -30,7 +30,7 @@ use cuprate_types::{
};
use crate::rpc::{
handlers::{helper, shared},
handlers::{helper, shared, shared::not_available},
service::{blockchain, txpool},
CupratedRpcHandler,
};
@@ -44,17 +44,13 @@ pub async fn map_request(
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?)
}
Req::GetBlocks(r) => Resp::GetBlocks(not_available()?),
Req::GetBlocksByHeight(r) => Resp::GetBlocksByHeight(not_available()?),
Req::GetHashes(r) => Resp::GetHashes(not_available()?),
Req::GetOutputIndexes(r) => Resp::GetOutputIndexes(not_available()?),
Req::GetOuts(r) => Resp::GetOuts(not_available()?),
Req::GetTransactionPoolHashes(r) => Resp::GetTransactionPoolHashes(not_available()?),
Req::GetOutputDistribution(r) => Resp::GetOutputDistribution(not_available()?),
})
}

View File

@@ -15,7 +15,7 @@ use cuprate_rpc_types::{
base::{AccessResponseBase, ResponseBase},
misc::BlockHeader,
};
use cuprate_types::HardFork;
use cuprate_types::{Chain, HardFork};
use monero_serai::transaction::Timelock;
use crate::rpc::{
@@ -55,18 +55,15 @@ pub(super) async fn block_header(
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 seed_hash =
blockchain::block_hash(&mut state.blockchain_read, height, Chain::Main).await?;
Some(
blockchain_context::calculate_pow(
&mut state.blockchain_context,
hardfork,
block,
// TODO: expensive clone
block.clone(),
seed_hash,
)
.await?,

View File

@@ -61,7 +61,7 @@ use crate::{
constants::VERSION_BUILD,
rpc::{
constants::{FIELD_NOT_SUPPORTED, UNSUPPORTED_RPC_CALL},
handlers::{helper, shared},
handlers::{helper, shared, shared::not_available},
service::{address_book, blockchain, blockchain_context, blockchain_manager, txpool},
CupratedRpcHandler,
},
@@ -77,11 +77,11 @@ pub async fn map_request(
use JsonRpcResponse as Resp;
Ok(match request {
Req::GetBlockTemplate(r) => Resp::GetBlockTemplate(get_block_template(state, r).await?),
Req::GetBlockTemplate(r) => Resp::GetBlockTemplate(not_available()?),
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::SubmitBlock(r) => Resp::SubmitBlock(not_available()?),
Req::GenerateBlocks(r) => Resp::GenerateBlocks(not_available()?),
Req::GetLastBlockHeader(r) => {
Resp::GetLastBlockHeader(get_last_block_header(state, r).await?)
}
@@ -95,33 +95,25 @@ pub async fn map_request(
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::AddAuxPow(r) => Resp::AddAuxPow(add_aux_pow(state, r).await?),
Req::GetConnections(r) => Resp::GetConnections(not_available()?),
Req::GetInfo(r) => Resp::GetInfo(not_available()?),
Req::HardForkInfo(r) => Resp::HardForkInfo(not_available()?),
Req::SetBans(r) => Resp::SetBans(not_available()?),
Req::GetBans(r) => Resp::GetBans(not_available()?),
Req::Banned(r) => Resp::Banned(not_available()?),
Req::FlushTransactionPool(r) => Resp::FlushTransactionPool(not_available()?),
Req::GetOutputHistogram(r) => Resp::GetOutputHistogram(not_available()?),
Req::GetCoinbaseTxSum(r) => Resp::GetCoinbaseTxSum(not_available()?),
Req::GetVersion(r) => Resp::GetVersion(not_available()?),
Req::GetFeeEstimate(r) => Resp::GetFeeEstimate(not_available()?),
Req::GetAlternateChains(r) => Resp::GetAlternateChains(not_available()?),
Req::RelayTx(r) => Resp::RelayTx(not_available()?),
Req::SyncInfo(r) => Resp::SyncInfo(not_available()?),
Req::GetTransactionPoolBacklog(r) => Resp::GetTransactionPoolBacklog(not_available()?),
Req::GetMinerData(r) => Resp::GetMinerData(not_available()?),
Req::PruneBlockchain(r) => Resp::PruneBlockchain(not_available()?),
Req::CalcPow(r) => Resp::CalcPow(not_available()?),
Req::AddAuxPow(r) => Resp::AddAuxPow(not_available()?),
// Unsupported RPC calls.
Req::GetTxIdsLoose(_) | Req::FlushCache(_) => return Err(anyhow!(UNSUPPORTED_RPC_CALL)),
@@ -172,7 +164,7 @@ async fn get_block_template(
seed_hash,
next_seed_hash,
} = *blockchain_manager::create_block_template(
&mut state.blockchain_manager,
todo!(),
prev_block,
request.wallet_address,
request.extra_nonce.0,
@@ -242,7 +234,7 @@ async fn submit_block(
let block_id = Hex(block.hash());
// Attempt to relay the block.
blockchain_manager::relay_block(&mut state.blockchain_manager, Box::new(block)).await?;
blockchain_manager::relay_block(todo!(), Box::new(block)).await?;
Ok(SubmitBlockResponse {
base: helper::response_base(false),
@@ -269,7 +261,7 @@ async fn generate_blocks(
};
let (blocks, height) = blockchain_manager::generate_blocks(
&mut state.blockchain_manager,
todo!(),
request.amount_of_blocks,
prev_block,
request.starting_nonce,
@@ -479,7 +471,7 @@ async fn get_info(
(String::new(), false)
};
let busy_syncing = blockchain_manager::syncing(&mut state.blockchain_manager).await?;
let busy_syncing = blockchain_manager::syncing(todo!()).await?;
let (cumulative_difficulty, cumulative_difficulty_top64) =
split_u128_into_low_high_bits(cumulative_difficulty);
@@ -524,12 +516,10 @@ async fn get_info(
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 synchronized = blockchain_manager::synced(todo!()).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 target_height = blockchain_manager::target_height(todo!()).await?;
let target = blockchain_manager::target(todo!()).await?.as_secs();
let top_block_hash = Hex(c.top_hash);
let tx_count = blockchain::total_tx_count(&mut state.blockchain_read).await?;
@@ -738,7 +728,7 @@ async fn flush_transaction_pool(
.map(|h| h.0)
.collect::<Vec<[u8; 32]>>();
txpool::flush(&mut state.txpool_manager, tx_hashes).await?;
txpool::flush(todo!(), tx_hashes).await?;
Ok(FlushTransactionPoolResponse { status: Status::Ok })
}
@@ -807,7 +797,7 @@ async fn get_version(
_: GetVersionRequest,
) -> Result<GetVersionResponse, Error> {
let current_height = helper::top_height(&mut state).await?.0;
let target_height = blockchain_manager::target_height(&mut state.blockchain_manager).await?;
let target_height = blockchain_manager::target_height(todo!()).await?;
let mut hard_forks = Vec::with_capacity(HardFork::COUNT);
@@ -880,7 +870,7 @@ async fn relay_tx(
.map(|h| h.0)
.collect::<Vec<[u8; 32]>>();
txpool::relay(&mut state.txpool_manager, tx_hashes).await?;
txpool::relay(todo!(), tx_hashes).await?;
Ok(RelayTxResponse { status: Status::Ok })
}
@@ -892,7 +882,7 @@ async fn sync_info(
) -> Result<SyncInfoResponse, Error> {
let height = usize_to_u64(state.blockchain_context.blockchain_context().chain_height);
let target_height = blockchain_manager::target_height(&mut state.blockchain_manager).await?;
let target_height = blockchain_manager::target_height(todo!()).await?;
let peers = address_book::connection_info::<ClearNet>(&mut DummyAddressBook)
.await?
@@ -900,12 +890,11 @@ async fn sync_info(
.map(|info| SyncInfoPeer { info })
.collect();
let next_needed_pruning_seed =
blockchain_manager::next_needed_pruning_seed(&mut state.blockchain_manager)
.await?
.compress();
let next_needed_pruning_seed = blockchain_manager::next_needed_pruning_seed(todo!())
.await?
.compress();
let spans = blockchain_manager::spans::<ClearNet>(&mut state.blockchain_manager).await?;
let spans = blockchain_manager::spans::<ClearNet>(todo!()).await?;
// <https://github.com/Cuprate/cuprate/pull/320#discussion_r1811063772>
let overview = String::from(FIELD_NOT_SUPPORTED);
@@ -994,10 +983,8 @@ async fn prune_blockchain(
mut state: CupratedRpcHandler,
request: PruneBlockchainRequest,
) -> Result<PruneBlockchainResponse, Error> {
let pruned = blockchain_manager::pruned(&mut state.blockchain_manager).await?;
let pruning_seed = blockchain_manager::prune(&mut state.blockchain_manager)
.await?
.compress();
let pruned = blockchain_manager::pruned(todo!()).await?;
let pruning_seed = blockchain_manager::prune(todo!()).await?.compress();
Ok(PruneBlockchainResponse {
base: helper::response_base(false),

View File

@@ -49,7 +49,7 @@ use cuprate_types::{
use crate::{
rpc::{
constants::UNSUPPORTED_RPC_CALL,
handlers::{helper, shared},
handlers::{helper, shared, shared::not_available},
service::{address_book, blockchain, blockchain_context, blockchain_manager, txpool},
CupratedRpcHandler,
},
@@ -66,36 +66,26 @@ pub async fn map_request(
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?),
Req::GetTransactions(r) => Resp::GetTransactions(not_available()?),
Req::GetAltBlocksHashes(r) => Resp::GetAltBlocksHashes(not_available()?),
Req::IsKeyImageSpent(r) => Resp::IsKeyImageSpent(not_available()?),
Req::SendRawTransaction(r) => Resp::SendRawTransaction(not_available()?),
Req::SaveBc(r) => Resp::SaveBc(not_available()?),
Req::GetPeerList(r) => Resp::GetPeerList(not_available()?),
Req::SetLogLevel(r) => Resp::SetLogLevel(not_available()?),
Req::SetLogCategories(r) => Resp::SetLogCategories(not_available()?),
Req::GetTransactionPool(r) => Resp::GetTransactionPool(not_available()?),
Req::GetTransactionPoolStats(r) => Resp::GetTransactionPoolStats(not_available()?),
Req::StopDaemon(r) => Resp::StopDaemon(not_available()?),
Req::GetLimit(r) => Resp::GetLimit(not_available()?),
Req::SetLimit(r) => Resp::SetLimit(not_available()?),
Req::OutPeers(r) => Resp::OutPeers(not_available()?),
Req::InPeers(r) => Resp::InPeers(not_available()?),
Req::GetNetStats(r) => Resp::GetNetStats(not_available()?),
Req::GetOuts(r) => Resp::GetOuts(not_available()?),
Req::PopBlocks(r) => Resp::PopBlocks(not_available()?),
Req::GetTransactionPoolHashes(r) => Resp::GetTransactionPoolHashes(not_available()?),
Req::GetPublicNodes(r) => Resp::GetPublicNodes(not_available()?),
// Unsupported requests.
Req::SetBootstrapDaemon(_)
@@ -452,9 +442,9 @@ async fn send_raw_transaction(
}
}
// TODO: handle to txpool service.
let tx_relay_checks =
txpool::check_maybe_relay_local(&mut state.txpool_manager, tx, !request.do_not_relay)
.await?;
txpool::check_maybe_relay_local(todo!(), tx, !request.do_not_relay).await?;
if tx_relay_checks.is_empty() {
return Ok(resp);
@@ -496,7 +486,7 @@ async fn send_raw_transaction(
/// <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?;
blockchain_manager::sync(todo!()).await?;
Ok(SaveBcResponse {
base: ResponseBase::OK,
@@ -554,7 +544,7 @@ async fn stop_daemon(
mut state: CupratedRpcHandler,
_: StopDaemonRequest,
) -> Result<StopDaemonResponse, Error> {
blockchain_manager::stop(&mut state.blockchain_manager).await?;
blockchain_manager::stop(todo!()).await?;
Ok(StopDaemonResponse { status: Status::Ok })
}
@@ -658,8 +648,7 @@ 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?;
let height = blockchain_manager::pop_blocks(todo!(), request.nblocks).await?;
Ok(PopBlocksResponse {
base: helper::response_base(false),

View File

@@ -128,3 +128,11 @@ pub(super) async fn get_output_distribution(
),
})
}
/// Always returns an [`Error`].
///
/// This is a temporary function used for RPC method/endpoints
/// that are not yet ready - it should be removed when all are ready.
pub(super) fn not_available<T>() -> Result<T, Error> {
Err(anyhow!("Not available"))
}

View File

@@ -153,7 +153,7 @@ pub type BlockchainManagerHandle = cuprate_database_service::DatabaseReadService
BlockchainManagerResponse,
>;
/// TODO
/// cuprated's RPC handler service.
#[derive(Clone)]
pub struct CupratedRpcHandler {
/// Should this RPC server be [restricted](RpcHandler::is_restricted)?
@@ -167,14 +167,9 @@ pub struct CupratedRpcHandler {
/// Handle to the blockchain context service.
pub blockchain_context: BlockchainContextService,
/// Handle to the blockchain manager.
pub blockchain_manager: BlockchainManagerHandle,
/// Read handle to the transaction pool database.
pub txpool_read: TxpoolReadHandle,
/// TODO: handle to txpool service.
pub txpool_manager: std::convert::Infallible,
// TODO: handle to txpool service.
}
impl CupratedRpcHandler {
@@ -183,17 +178,13 @@ impl CupratedRpcHandler {
restricted: bool,
blockchain_read: BlockchainReadHandle,
blockchain_context: BlockchainContextService,
blockchain_manager: BlockchainManagerHandle,
txpool_read: TxpoolReadHandle,
txpool_manager: std::convert::Infallible,
) -> Self {
Self {
restricted,
blockchain_read,
blockchain_context,
blockchain_manager,
txpool_read,
txpool_manager,
}
}
}

View File

@@ -9,11 +9,17 @@ use anyhow::Error;
use tokio::net::TcpListener;
use tower::limit::rate::RateLimitLayer;
use tower_http::limit::RequestBodyLimitLayer;
use tracing::{field::display, info, warn};
use tracing::{info, warn};
use cuprate_rpc_interface::{RouterBuilder, RpcHandlerDummy};
use cuprate_blockchain::service::BlockchainReadHandle;
use cuprate_consensus::BlockchainContextService;
use cuprate_rpc_interface::{RouterBuilder, RpcHandler};
use cuprate_txpool::service::TxpoolReadHandle;
use crate::{config::RpcConfig, rpc::CupratedRpcHandler};
use crate::{
config::RpcConfig,
rpc::{rpc_handler::BlockchainManagerHandle, CupratedRpcHandler},
};
/// Initialize the RPC server(s).
///
@@ -22,7 +28,12 @@ use crate::{config::RpcConfig, rpc::CupratedRpcHandler};
/// - the server(s) could not be started
/// - unrestricted RPC is started on non-local
/// address without override option
pub fn init_rpc_servers(config: RpcConfig) {
pub fn init_rpc_servers(
config: RpcConfig,
blockchain_read: BlockchainReadHandle,
blockchain_context: BlockchainContextService,
txpool_read: TxpoolReadHandle,
) {
for ((enable, addr, request_byte_limit), restricted) in [
(
(
@@ -52,7 +63,7 @@ pub fn init_rpc_servers(config: RpcConfig) {
.i_know_what_im_doing_allow_public_unrestricted_rpc
{
warn!(
address = display(addr),
address = %addr,
"Starting unrestricted RPC on non-local address, this is dangerous!"
);
} else {
@@ -60,8 +71,15 @@ pub fn init_rpc_servers(config: RpcConfig) {
}
}
let rpc_handler = CupratedRpcHandler::new(
restricted,
blockchain_read.clone(),
blockchain_context.clone(),
txpool_read.clone(),
);
tokio::task::spawn(async move {
run_rpc_server(restricted, addr, request_byte_limit)
run_rpc_server(rpc_handler, restricted, addr, request_byte_limit)
.await
.unwrap();
});
@@ -72,28 +90,26 @@ pub fn init_rpc_servers(config: RpcConfig) {
///
/// The function will only return when the server itself returns or an error occurs.
async fn run_rpc_server(
rpc_handler: CupratedRpcHandler,
restricted: bool,
address: SocketAddr,
request_byte_limit: usize,
) -> Result<(), Error> {
info!(
restricted,
address = display(&address),
address = %address,
"Starting RPC server"
);
// Create the router.
//
// TODO: impl more layers, rate-limiting, configuration, etc.
let state = RpcHandlerDummy { restricted };
// TODO:
// - add functions that are `all()` but for restricted RPC
// - enable aliases automatically `other_get_height` + `other_getheight`?
//
// FIXME:
// - `json_rpc` is 1 endpoint; `RouterBuilder` operates at the
// level endpoint; we can't selectively enable certain `json_rpc` methods
let router = RouterBuilder::new().fallback().build().with_state(state);
let router = RouterBuilder::new()
.json_rpc()
.other_get_height()
.fallback()
.build()
.with_state(rpc_handler);
// Add restrictive layers if restricted RPC.
//

View File

@@ -15,14 +15,16 @@
- [Command line](cli.md)
- [Resources](resources/intro.md)
- [Address](resources/address.md)
- [Disk](resources/disk.md)
- [Ports](resources/ports.md)
- [IP](resources/ip.md)
- [Deployment](deployment/intro.md)
- [systemd](deployment/systemd.md)
- [RPC](rpc.md)
- [Platform support](platform.md)
- [License](license.md)
<!-- TODO: - [Glossary](glossary/intro.md) or maybe a wiki? -->

View File

@@ -52,6 +52,7 @@ Cuprate's node (`cuprated`) can currently:
- Sync the blockchain and transaction pool
- Broadcast and relay blocks and transactions
- Help other peers sync their blockchain
- Respond to certain [daemon RPC](https://www.getmonero.org/resources/developer-guides/daemon-rpc.html) requests
## How fast does `cuprated` sync?
The current full verification sync timings are around ~7.5x faster than `monerod`.
@@ -91,7 +92,7 @@ The database `cuprated` generates and uses cannot directly be used by `monerod`
## Can I connect a wallet to `cuprated`?
Not yet.
Wallets require the [daemon RPC API](https://docs.getmonero.org/rpc-library/monerod-rpc). This is actively being worked on to be backwards compatible with `monerod`, although this is not yet available.
Wallets require the [daemon RPC API](https://docs.getmonero.org/rpc-library/monerod-rpc). This is actively being worked on to be backwards compatible with `monerod`, see the [RPC section](rpc.md) for more information.
## Can `cuprated` be used with an anonymity network like Tor?
Not yet (directly).

View File

@@ -0,0 +1,19 @@
# Address
IP addresses and ports used by `cuprated`.
### P2P
`cuprated` can bind to a [IPv4](https://en.wikipedia.org/wiki/IPv4) or [IPv6](https://en.wikipedia.org/wiki/IPv6) address for P2P connections.
By default, this address is `0.0.0.0:18080`, which will bind to all available interfaces.
See the [`listen_on` and `p2p_port` option in the config file](../config.md) to manually set this address.
Setting the port to `0` will disable incoming P2P connections.
### RPC
By default, the:
- unrestricted RPC server is enabled and binds to `127.0.0.1:18081`
- restricted RPC server is disabled and binds to `0.0.0.0:18089`
See the [`address` option in the config file](../config.md) to manually set the addresses.

View File

@@ -1,5 +0,0 @@
# IP
`cuprated` currently binds to a single [IPv4 address](https://en.wikipedia.org/wiki/IPv4) for P2P connections.
By default, this IP address is `0.0.0.0`, which will bind to all available interfaces.
See the [`listen_on` option in the config file](../config.md) to manually set this IP address.

View File

@@ -1,7 +0,0 @@
# Ports
`cuprated` currently uses a single port to accept incoming P2P connections.
By default, this port is `18080`.
See the [`p2p_port` option in the config file](../config.md) to manually set this port.
Setting the port to `0` will disable incoming P2P connections.

106
books/user/src/rpc.md Normal file
View File

@@ -0,0 +1,106 @@
# RPC
> **⚠️ Warning ⚠️**
>
> Cuprate is still experimental software.
>
> Consider sandboxing `cuprated` before publicly exposing restricted RPC.
`monerod`'s daemon RPC has 3 kinds of interfaces:
1. [JSON-RPC 2.0](https://www.jsonrpc.org) methods called at the `/json_rpc` endpoint, e.g. [`get_block`](https://www.getmonero.org/resources/developer-guides/daemon-rpc.html#get_block)
1. JSON endpoints, e.g. [`/get_height`](https://www.getmonero.org/resources/developer-guides/daemon-rpc.html#get_height)
1. Binary endpoints, e.g. [`/get_blocks.bin`](https://www.getmonero.org/resources/developer-guides/daemon-rpc.html#get_blocksbin)
<!-- TODO: explain the binary format -->
`cuprated`'s RPC aims to mirror `monerod`'s as much as it can. The end-goal is compatibility with common use-cases such as wallet software.
This section contains the development status of endpoints/methods in `cuprated`.
| Status | Meaning |
|--------|---------|
| 🟢 | Enabled and tested
| 🟣 | Enabled but has differences waiting to be resolved
| 🟠 | Enabled but not fully tested
| ⚪ | Not enabled yet
| ⚫ | Not planned to be supported
<!-- NOTE: Sort methods/endpoints A-Z -->
## JSON-RPC methods
| Method | Status | Notes |
|--------------------------------|--------|---------|
| `add_aux_pow` | ⚪ |
| `banned` | ⚪ |
| `calc_pow` | ⚪ |
| `flush_cache` | ⚫ | `cuprated` does not require this method
| `flush_txpool` | ⚪ |
| `generateblocks` | ⚪ |
| `get_alternate_chains` | ⚪ |
| `get_bans` | ⚪ |
| `get_block` | 🟠 |
| `get_block_count` | 🟠 |
| `get_block_header_by_hash` | 🟠 |
| `get_block_header_by_height` | 🟠 |
| `get_block_headers_range` | 🟠 |
| `get_block_template` | ⚪ |
| `get_coinbase_tx_sum` | ⚪ |
| `get_connections` | ⚪ |
| `get_fee_estimate` | ⚪ |
| `get_info` | ⚪ |
| `get_last_block_header` | ⚪ |
| `get_miner_data` | ⚪ |
| `get_output_distribution` | ⚪ |
| `get_output_histogram` | ⚪ |
| `get_tx_ids_loose` | ⚪ | Not implemented in `monerod` release branch yet
| `get_txpool_backlog` | ⚪ |
| `get_version` | ⚪ |
| `hard_fork_info` | ⚪ |
| `on_get_block_hash` | 🟠 |
| `prune_blockchain` | ⚫ |
| `relay_tx` | ⚪ |
| `set_bans` | ⚪ |
| `submit_block` | ⚪ |
| `sync_info` | ⚪ |
## JSON endpoints
| Endpoint | Status | Notes |
|--------------------------------|--------|---------|
| `/get_alt_blocks_hashes` | ⚪ |
| `/get_height` | 🟠 |
| `/get_limit` | ⚪ |
| `/get_net_stats` | ⚪ |
| `/get_outs` | ⚪ |
| `/get_peer_list` | ⚪ |
| `/get_public_nodes` | ⚪ |
| `/get_transaction_pool` | ⚪ |
| `/get_transaction_pool_hashes` | ⚪ |
| `/get_transaction_pool_stats` | ⚪ |
| `/get_transactions` | ⚪ |
| `/in_peers` | ⚪ |
| `/is_key_image_spent` | ⚪ |
| `/mining_status` | ⚫ | `cuprated` does not mine
| `/out_peers` | ⚪ |
| `/pop_blocks` | ⚪ |
| `/save_bc` | ⚪ |
| `/send_raw_transaction` | ⚪ |
| `/set_bootstrap_daemon` | ⚪ | Requires bootstrap implementation
| `/set_limit` | ⚪ |
| `/set_log_categories` | ⚪ | Could be re-purposed to use `tracing` filters
| `/set_log_hash_rate` | ⚫ | `cuprated` does not mine
| `/set_log_level` | ⚪ | Will use `tracing` levels
| `/start_mining` | ⚫ | `cuprated` does not mine
| `/stop_daemon` | ⚪ |
| `/stop_mining` | ⚫ | `cuprated` does not mine
| `/update` | ⚫ |
## Binary endpoints
| Endpoint | Status | Notes |
|------------------------------------|--------|---------|
| `/get_blocks.bin` | ⚪ |
| `/get_blocks_by_height.bin` | ⚪ |
| `/get_hashes.bin` | ⚪ |
| `/get_output_distribution.bin` | ⚪ |
| `/get_output_indexes.bin` | ⚪ |
| `/get_outs.bin` | ⚪ |
| `/get_transaction_pool_hashes.bin` | ⚪ |

View File

@@ -354,6 +354,33 @@ pub fn get_block_extended_header_top(
Ok((header, height))
}
//---------------------------------------------------------------------------------------------------- Block
/// Retrieve a [`Block`] via its [`BlockHeight`].
#[doc = doc_error!()]
#[inline]
pub fn get_block(tables: &impl Tables, block_height: &BlockHeight) -> DbResult<Block> {
let header_blob = tables.block_header_blobs().get(block_height)?.0;
let header = BlockHeader::read(&mut header_blob.as_slice())?;
let transactions = tables.block_txs_hashes().get(block_height)?.0;
let miner_tx_id = tables.block_infos().get(block_height)?.mining_tx_index;
let miner_transaction = crate::ops::tx::get_tx_from_id(&miner_tx_id, tables.tx_blobs())?;
Ok(Block {
header,
miner_transaction,
transactions,
})
}
/// Retrieve a [`Block`] via its [`BlockHash`].
#[doc = doc_error!()]
#[inline]
pub fn get_block_by_hash(tables: &impl Tables, block_hash: &BlockHash) -> DbResult<Block> {
let block_height = tables.block_heights().get(block_hash)?;
get_block(tables, &block_height)
}
//---------------------------------------------------------------------------------------------------- Misc
/// Retrieve a [`BlockInfo`] via its [`BlockHeight`].
#[doc = doc_error!()]

View File

@@ -41,9 +41,9 @@ use crate::{
get_alt_chain_history_ranges,
},
block::{
block_exists, get_block_blob_with_tx_indexes, get_block_complete_entry,
get_block_complete_entry_from_height, get_block_extended_header_from_height,
get_block_height, get_block_info,
block_exists, get_block, get_block_blob_with_tx_indexes, get_block_by_hash,
get_block_complete_entry, 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,
@@ -888,12 +888,28 @@ fn alt_blocks_in_chain(env: &ConcreteEnv, chain_id: ChainId) -> ResponseResult {
/// [`BlockchainReadRequest::Block`]
fn block(env: &ConcreteEnv, block_height: BlockHeight) -> ResponseResult {
Ok(BlockchainResponse::Block(todo!()))
// Single-threaded, no `ThreadLocal` required.
let env_inner = env.env_inner();
let tx_ro = env_inner.tx_ro()?;
let tables = env_inner.open_tables(&tx_ro)?;
Ok(BlockchainResponse::Block(get_block(
&tables,
&block_height,
)?))
}
/// [`BlockchainReadRequest::BlockByHash`]
fn block_by_hash(env: &ConcreteEnv, block_hash: BlockHash) -> ResponseResult {
Ok(BlockchainResponse::Block(todo!()))
// Single-threaded, no `ThreadLocal` required.
let env_inner = env.env_inner();
let tx_ro = env_inner.tx_ro()?;
let tables = env_inner.open_tables(&tx_ro)?;
Ok(BlockchainResponse::Block(get_block_by_hash(
&tables,
&block_hash,
)?))
}
/// [`BlockchainReadRequest::TotalTxCount`]