* 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> |
||
---|---|---|
.. | ||
src | ||
Cargo.toml | ||
README.md |
cuprate-rpc-interface
This crate provides Cuprate's RPC interface.
This crate is not a standalone RPC server, it is just the interface.
cuprate-rpc-interface provides these parts
│ │
┌───────────────────────────┤ ├───────────────────┐
▼ ▼ ▼ ▼
CLIENT ─► ROUTE ─► REQUEST ─► HANDLER ─► RESPONSE ─► CLIENT
▲ ▲
└───┬───┘
│
You provide this part
Everything coming in from a client is handled by this crate.
This is where your [RpcHandler
] turns this Request
into a Response
.
You hand this Response
back to cuprate-rpc-interface
and it will take care of sending it back to the client.
The main handler used by Cuprate is implemented in the cuprate-rpc-handler
crate;
it implements the standard RPC handlers modeled after monerod
.
Purpose
cuprate-rpc-interface
is built on-top of [axum
],
which is the crate actually handling everything.
This crate simply handles:
- Registering endpoint routes (e.g.
/get_block.bin
) - Defining handler function signatures
- (De)serialization of requests/responses (JSON-RPC, binary, JSON)
The actual server details are all handled by the [axum
] and [tower
] ecosystem.
The proper usage of this crate is to:
- Implement a [
RpcHandler
] - Use it with [
RouterBuilder
] to generate an [axum::Router
] with all Monero RPC routes set - Do whatever with it
The [RpcHandler
]
This is your [tower::Service
] that converts Request
s into Response
s,
i.e. the "inner handler".
Said concretely, RpcHandler
is 3 tower::Service
s where the
request/response types are the 3 endpoint enums from [cuprate_rpc_types
]:
JsonRpcRequest
&JsonRpcResponse
BinRequest
&BinResponse
OtherRequest
&OtherResponse
RpcHandler
's Future
is generic, although,
it must output Result<$RESPONSE, anyhow::Error>
.
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::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
TODO: decide what this crate should return (per different endpoint) when a request is received to an unknown endpoint, including HTTP stuff, e.g. status code.
Unknown JSON-RPC method behavior
TODO: decide what this crate returns when a /json_rpc
request is received with an unknown method, including HTTP stuff, e.g. status code.
Example
Example usage of this crate + starting an RPC server.
This uses RpcHandlerDummy
as the handler; it always responds with the
correct response type, but set to a default value regardless of the request.
use std::sync::Arc;
use tokio::{net::TcpListener, sync::Barrier};
use cuprate_json_rpc::{Request, Response, Id};
use cuprate_rpc_types::{
json::{JsonRpcRequest, JsonRpcResponse, GetBlockCountResponse},
other::{OtherRequest, OtherResponse},
};
use cuprate_rpc_interface::{RouterBuilder, RpcHandlerDummy};
// Send a `/get_height` request. This endpoint has no inputs.
async fn get_height(port: u16) -> OtherResponse {
let url = format!("http://127.0.0.1:{port}/get_height");
ureq::get(&url)
.set("Content-Type", "application/json")
.call()
.unwrap()
.into_json()
.unwrap()
}
// Send a JSON-RPC request with the `get_block_count` method.
//
// The returned [`String`] is JSON.
async fn get_block_count(port: u16) -> String {
let url = format!("http://127.0.0.1:{port}/json_rpc");
let method = JsonRpcRequest::GetBlockCount(Default::default());
let request = Request::new(method);
ureq::get(&url)
.set("Content-Type", "application/json")
.send_json(request)
.unwrap()
.into_string()
.unwrap()
}
#[tokio::main]
async fn main() {
// Start a local RPC server.
let port = {
// Create the router.
let state = RpcHandlerDummy { restricted: false };
let router = RouterBuilder::new().all().build().with_state(state);
// Start a server.
let listener = TcpListener::bind("127.0.0.1:0")
.await
.unwrap();
let port = listener.local_addr().unwrap().port();
// Run the server with `axum`.
tokio::task::spawn(async move {
axum::serve(listener, router).await.unwrap();
});
port
};
// Assert the response is the default.
let response = get_height(port).await;
let expected = OtherResponse::GetHeight(Default::default());
assert_eq!(response, expected);
// Assert the response JSON is correct.
let response = get_block_count(port).await;
let expected = r#"{"jsonrpc":"2.0","id":null,"result":{"status":"OK","untrusted":false,"count":0}}"#;
assert_eq!(response, expected);
// Assert that (de)serialization works.
let expected = Response::ok(Id::Null, Default::default());
let response: Response<GetBlockCountResponse> = serde_json::from_str(&response).unwrap();
assert_eq!(response, expected);
}
Feature flags
List of feature flags for cuprate-rpc-interface
.
All are enabled by default.
Feature flag | Does what |
---|---|
serde |
Enables serde on applicable types |
dummy |
Enables the RpcHandlerDummy type |