mirror of
https://github.com/Cuprate/cuprate.git
synced 2024-12-26 21:50:09 +00:00
27767690ca
Some checks are pending
Audit / audit (push) Waiting to run
CI / fmt (push) Waiting to run
CI / typo (push) Waiting to run
CI / ci (macos-latest, stable, bash) (push) Waiting to run
CI / ci (ubuntu-latest, stable, bash) (push) Waiting to run
CI / ci (windows-latest, stable-x86_64-pc-windows-gnu, msys2 {0}) (push) Waiting to run
Deny / audit (push) Waiting to run
Doc / build (push) Waiting to run
Doc / deploy (push) Blocked by required conditions
* fixed-bytes: add `serde`, document feature flags * fixed-bytes: add derives * rpc: add `as _` syntax to macro * rpc: use `ByteArrayVec` and `ContainerAsBlob` for binary types * fixed-bytes: re-add derives * rpc-types: dedup default value within macro * readme: fixed bytes section * types: custom epee - `BlockCompleteEntry` * types: custom epee - `KeyImageSpentStatus` * types: custom epee - `PoolInfoExtent` * types: add `Status::Other(String)` variant * types: custom epee - `TxEntry`, add `read_epee_field` macro * bin: custom epee - `GetBlocks` * types: add `serde.rs` * misc: make `TxEntry` an `enum`, impl serde * misc: `unimplemented!()` for `TxEntry`'s epee * types: add `BlockCompleteEntry` * rpc: replace `BlockCompleteEntry` with `cuprate-types` * types: document `BlockCompleteEntry` * bin: fix `number_of_fields` for `GetBlocksResponse` * misc: add `Distribution` * distribution: add todo * misc fixes * readme: add `(De)serialization invariants` * distribution: compress variants * types: add `block_complete_entry.rs` * net: fix imports * p2p: fix imports * turn off default-features * p2p: fix imports * misc fixes * Update net/wire/Cargo.toml Co-authored-by: Boog900 <boog900@tutanota.com> * distribution: module doc * wire: re-export types * test-utils: add `crate::rpc::types` module * test-utils: conditional json doc-tests * bin: use enum for `GetBlocksResponse` * misc: use lowercase for stringify * json: add test data, fix macro doc tests * json: add all data * other: add all data * bin: add skeleton * docs * move type to correct file * remove duplicated fields for custom epee * rpc: `client/{client,constants}.rs` -> `client.rs` * lib.rs: remove `clippy::module_inception` * macros: add json doc test macro * json: add some tests * json: add doc-test for all types * add all other JSON doc-tests * move doc-test macros to files * base: add doc-tests * rpc: add `cuprate-rpc-interface` skeleton files * traits * json_rpc_method: add `.is_restricted()` * add route fn signatures * types: add rpc enums * interface: routes, types * interface: simplify routes * rewrite interface fns * types: remove `()` type alias, add `(restricted)` * types: add `other::InPeers` * interface: routes * types: fix `is_restricted()` * interface: reorder short-circuit bool * clean up traits/bounds * types: remove `axum` feature * interface: cleanup unused imports * interface: call handler in routes * json: TODO distribution test * interface: readme intro * combine `RpcHandler` + `RpcService`, add `RpcDummyHandler` * interface: readme docs + test * `IsRestricted` -> `RpcCall` * fix no input route problem * interface: `RpcHandlerDummy` docs * interface: crate docs * replace `create_router` with `RouterBuilder` * types: docs * types: doc `JsonRpc{Request,Response}` * types: readme docs * interface: doc `route/` * interface: fix `todo!()` * interface: allow customizing HTTP method on route functions * interface: fix tests * fix derives * Update rpc/interface/README.md Co-authored-by: Boog900 <boog900@tutanota.com> * Update rpc/interface/README.md Co-authored-by: Boog900 <boog900@tutanota.com> * interface: make `RpcHandler`'s `Future` generic * interface: add JSON-RPC notification todo * formatting * interface: use associated type bound for `RpcHandler`'s `Future` --------- Co-authored-by: Boog900 <boog900@tutanota.com>
161 lines
No EOL
5.5 KiB
Markdown
161 lines
No EOL
5.5 KiB
Markdown
# `cuprate-rpc-interface`
|
|
This crate provides Cuprate's RPC _interface_.
|
|
|
|
This crate is _not_ a standalone RPC server, it is just the interface.
|
|
|
|
```text
|
|
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 [`RpcRequest`] into a [`RpcResponse`].
|
|
|
|
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:
|
|
1. Implement a [`RpcHandler`]
|
|
2. Use it with [`RouterBuilder`] to generate an
|
|
[`axum::Router`] with all Monero RPC routes set
|
|
3. Do whatever with it
|
|
|
|
# The [`RpcHandler`]
|
|
This is your [`tower::Service`] that converts [`RpcRequest`]s into [`RpcResponse`]s,
|
|
i.e. the "inner handler".
|
|
|
|
Said concretely, `RpcHandler` is a `tower::Service` where the associated types are from this crate:
|
|
- [`RpcRequest`]
|
|
- [`RpcResponse`]
|
|
- [`RpcError`]
|
|
|
|
`RpcHandler`'s [`Future`](std::future::Future) is generic, _although_,
|
|
it must output `Result<RpcResponse, RpcError>`.
|
|
|
|
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
|
|
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.
|
|
|
|
```rust
|
|
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, RpcRequest};
|
|
|
|
// 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 |