From fafa20c20fcc40fb709db49eaf42afddf01f4ed6 Mon Sep 17 00:00:00 2001 From: hinto-janai Date: Wed, 7 Aug 2024 19:15:22 -0400 Subject: [PATCH] architecture-book: fill `RPC` section (#243) * books: add `rpc` skeleton * json-rpc * types section * differences * interface * typos * differences: add `json-formatting.md` * rpc: small fixes * appendix: add `cuprate-rpc-handler` to crate list * differences: remove extra fields section, add more info * differences: add `id:0` section --- books/architecture/src/SUMMARY.md | 30 ++-- books/architecture/src/appendix/crates.md | 1 + .../src/rpc/differences/custom-strings.md | 7 + .../src/rpc/differences/http-methods.md | 17 +++ .../architecture/src/rpc/differences/intro.md | 9 ++ .../rpc/differences/json-field-ordering.md | 51 +++++++ .../src/rpc/differences/json-formatting.md | 118 +++++++++++++++ .../rpc/differences/json-rpc-strictness.md | 137 ++++++++++++++++++ .../src/rpc/differences/json-strictness.md | 49 +++++++ .../rpc-calls-with-different-behavior.md | 2 + .../src/rpc/differences/rpc-payment.md | 9 ++ .../rpc/differences/unsupported-rpc-calls.md | 2 + books/architecture/src/rpc/handler.md | 1 - books/architecture/src/rpc/handler/intro.md | 2 + books/architecture/src/rpc/interface.md | 37 ++++- books/architecture/src/rpc/intro.md | 31 +++- books/architecture/src/rpc/json-rpc.md | 10 ++ books/architecture/src/rpc/methods/intro.md | 1 - books/architecture/src/rpc/router.md | 1 - books/architecture/src/rpc/server/intro.md | 2 + .../architecture/src/rpc/types/base-types.md | 39 +++++ books/architecture/src/rpc/types/binary.md | 1 - .../src/rpc/types/deserialization.md | 38 +++++ books/architecture/src/rpc/types/intro.md | 32 +++- books/architecture/src/rpc/types/json.md | 1 - books/architecture/src/rpc/types/macro.md | 16 ++ books/architecture/src/rpc/types/metadata.md | 13 ++ .../architecture/src/rpc/types/misc-types.md | 11 ++ books/architecture/src/rpc/types/other.md | 1 - 29 files changed, 650 insertions(+), 19 deletions(-) create mode 100644 books/architecture/src/rpc/differences/custom-strings.md create mode 100644 books/architecture/src/rpc/differences/http-methods.md create mode 100644 books/architecture/src/rpc/differences/intro.md create mode 100644 books/architecture/src/rpc/differences/json-field-ordering.md create mode 100644 books/architecture/src/rpc/differences/json-formatting.md create mode 100644 books/architecture/src/rpc/differences/json-rpc-strictness.md create mode 100644 books/architecture/src/rpc/differences/json-strictness.md create mode 100644 books/architecture/src/rpc/differences/rpc-calls-with-different-behavior.md create mode 100644 books/architecture/src/rpc/differences/rpc-payment.md create mode 100644 books/architecture/src/rpc/differences/unsupported-rpc-calls.md delete mode 100644 books/architecture/src/rpc/handler.md create mode 100644 books/architecture/src/rpc/handler/intro.md create mode 100644 books/architecture/src/rpc/json-rpc.md delete mode 100644 books/architecture/src/rpc/methods/intro.md delete mode 100644 books/architecture/src/rpc/router.md create mode 100644 books/architecture/src/rpc/server/intro.md create mode 100644 books/architecture/src/rpc/types/base-types.md delete mode 100644 books/architecture/src/rpc/types/binary.md create mode 100644 books/architecture/src/rpc/types/deserialization.md delete mode 100644 books/architecture/src/rpc/types/json.md create mode 100644 books/architecture/src/rpc/types/macro.md create mode 100644 books/architecture/src/rpc/types/metadata.md create mode 100644 books/architecture/src/rpc/types/misc-types.md delete mode 100644 books/architecture/src/rpc/types/other.md diff --git a/books/architecture/src/SUMMARY.md b/books/architecture/src/SUMMARY.md index 3a8b351..ad521df 100644 --- a/books/architecture/src/SUMMARY.md +++ b/books/architecture/src/SUMMARY.md @@ -35,15 +35,27 @@ --- -- [🔴 RPC](rpc/intro.md) - - [⚪️ Types](rpc/types/intro.md) - - [⚪️ JSON](rpc/types/json.md) - - [⚪️ Binary](rpc/types/binary.md) - - [⚪️ Other](rpc/types/other.md) - - [⚪️ Interface](rpc/interface.md) - - [⚪️ Router](rpc/router.md) - - [⚪️ Handler](rpc/handler.md) - - [⚪️ Methods](rpc/methods/intro.md) +- [🟢 RPC](rpc/intro.md) + - [🟡 JSON-RPC 2.0](rpc/json-rpc.md) + - [🟢 The types](rpc/types/intro.md) + - [🟢 Misc types](rpc/types/misc-types.md) + - [🟢 Base RPC types](rpc/types/base-types.md) + - [🟢 The type generator macro](rpc/types/macro.md) + - [🟢 Metadata](rpc/types/metadata.md) + - [🟡 (De)serialization](rpc/types/deserialization.md) + - [🟢 The interface](rpc/interface.md) + - [🔴 The handler](rpc/handler/intro.md) + - [🔴 The server](rpc/server/intro.md) + - [🟢 Differences with `monerod`](rpc/differences/intro.md) + - [🟢 JSON field ordering](rpc/differences/json-field-ordering.md) + - [🟢 JSON formatting](rpc/differences/json-formatting.md) + - [🟢 JSON strictness](rpc/differences/json-strictness.md) + - [🟡 JSON-RPC strictness](rpc/differences/json-rpc-strictness.md) + - [🟡 HTTP methods](rpc/differences/http-methods.md) + - [🟡 RPC payment](rpc/differences/rpc-payment.md) + - [🟢 Custom strings](rpc/differences/custom-strings.md) + - [🔴 Unsupported RPC calls](rpc/differences/unsupported-rpc-calls.md) + - [🔴 RPC calls with different behavior](rpc/differences/rpc-calls-with-different-behavior.md) --- diff --git a/books/architecture/src/appendix/crates.md b/books/architecture/src/appendix/crates.md index 224e678..e5311a8 100644 --- a/books/architecture/src/appendix/crates.md +++ b/books/architecture/src/appendix/crates.md @@ -50,6 +50,7 @@ cargo doc --open --package cuprate-blockchain | [`cuprate-json-rpc`](https://doc.cuprate.org/cuprate_json_rpc) | [`rpc/json-rpc/`](https://github.com/Cuprate/cuprate/tree/main/rpc/json-rpc) | JSON-RPC 2.0 implementation | [`cuprate-rpc-types`](https://doc.cuprate.org/cuprate_rpc_types) | [`rpc/types/`](https://github.com/Cuprate/cuprate/tree/main/rpc/types) | Monero RPC types and traits | [`cuprate-rpc-interface`](https://doc.cuprate.org/cuprate_rpc_interface) | [`rpc/interface/`](https://github.com/Cuprate/cuprate/tree/main/rpc/interface) | RPC interface & routing +| [`cuprate-rpc-handler`](https://doc.cuprate.org/cuprate_rpc_handler) | [`rpc/handler/`](https://github.com/Cuprate/cuprate/tree/main/rpc/handler) | RPC inner handlers ## 1-off crates | Crate | In-tree path | Purpose | diff --git a/books/architecture/src/rpc/differences/custom-strings.md b/books/architecture/src/rpc/differences/custom-strings.md new file mode 100644 index 0000000..736c481 --- /dev/null +++ b/books/architecture/src/rpc/differences/custom-strings.md @@ -0,0 +1,7 @@ +# Custom strings +Many JSON response fields contain strings with custom messages. + +This may be error messages, status, etc. + +Although the field + string type will be followed, Cuprate will not always +have the exact same message, particularly when it comes to error messages. \ No newline at end of file diff --git a/books/architecture/src/rpc/differences/http-methods.md b/books/architecture/src/rpc/differences/http-methods.md new file mode 100644 index 0000000..238e2e3 --- /dev/null +++ b/books/architecture/src/rpc/differences/http-methods.md @@ -0,0 +1,17 @@ +# HTTP methods +`monerod` endpoints supports multiple [HTTP methods](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods) +that do not necessarily make sense. + +For example: +```bash +curl \ + http://127.0.0.1:18081/get_limit \ + -H 'Content-Type: application/json' \ + --request DELETE +``` +This is sending an HTTP `DELETE` request, which should be a `GET`. + +`monerod` will respond to this the same as `GET`, `POST`, `PUT`, and `TRACE`. + +## Cuprate's behavior +> TODO: decide allowed HTTP methods for Cuprate . \ No newline at end of file diff --git a/books/architecture/src/rpc/differences/intro.md b/books/architecture/src/rpc/differences/intro.md new file mode 100644 index 0000000..54027e2 --- /dev/null +++ b/books/architecture/src/rpc/differences/intro.md @@ -0,0 +1,9 @@ +# Differences with `monerod` +As noted in the [introduction](../intro.md), `monerod`'s RPC behavior cannot always be perfectly followed by Cuprate. + +The reasoning for the differences can vary from: +- Technical limitations +- Behavior being `monerod`-specific +- Purposeful decision to not support behavior + +This section lays out the details of the differences between `monerod`'s and Cuprate's RPC system. diff --git a/books/architecture/src/rpc/differences/json-field-ordering.md b/books/architecture/src/rpc/differences/json-field-ordering.md new file mode 100644 index 0000000..ad4c1f9 --- /dev/null +++ b/books/architecture/src/rpc/differences/json-field-ordering.md @@ -0,0 +1,51 @@ +# JSON field ordering +When serializing JSON, `monerod` has the behavior to order key fields within a scope alphabetically. + +For example: +```json +{ + "id": "0", + "jsonrpc": "2.0", + "result": { + "blockhashing_blob": "...", + "blocktemplate_blob": "...", + "difficulty": 283305047039, + "difficulty_top64": 0, + "expected_reward": 600000000000, + "height": 3195018, + "next_seed_hash": "", + "prev_hash": "9d648e741d85ca0e7acb4501f051b27e9b107d3cd7a3f03aa7f776089117c81a", + "reserved_offset": 131, + "seed_hash": "e2aa0b7b55042cd48b02e395d78fa66a29815ccc1584e38db2d1f0e8485cd44f", + "seed_height": 3194880, + "status": "OK", + "untrusted": false, + "wide_difficulty": "0x41f64bf3ff" + } +} +``` +In the main `{}`, `id` comes before `jsonrpc`, which comes before `result`. + +The same alphabetical ordering is applied to the fields within `result`. + +Cuprate uses [`serde`](https://docs.rs/serde) for JSON serialization, +which serializes fields based on the _definition_ order, i.e. whatever +order the fields are defined in the code, is the order they will appear +in JSON. + +Some `struct` fields within Cuprate's RPC types happen to be alphabetical, but this is not a guarantee. + +As these are JSON maps, the ordering of fields should not matter, +although this is something to note as the output will technically differ. + +## Example incompatibility +An example of where this leads to incompatibility is if specific +line numbers are depended on to contain specific fields. + +For example, this will print the 10th line: +```bash +curl http://127.0.0.1:18081/json_rpc -d '{"jsonrpc":"2.0","id":"0","method":"get_block_template","params":{"wallet_address":"44GBHzv6ZyQdJkjqZje6KLZ3xSyN1hBSFAnLP6EAqJtCRVzMzZmeXTC2AHKDS9aEDTRKmo6a6o9r9j86pYfhCWDkKjbtcns","reserve_size":60}' -H 'Content-Type: application/json' | sed -n 10p +``` +It will be `"height": 3195018` in `monerod`'s case, but may not necessarily be for Cuprate. + +By all means, this should not be relied upon in the first place, although it is shown as an example. diff --git a/books/architecture/src/rpc/differences/json-formatting.md b/books/architecture/src/rpc/differences/json-formatting.md new file mode 100644 index 0000000..2d29d48 --- /dev/null +++ b/books/architecture/src/rpc/differences/json-formatting.md @@ -0,0 +1,118 @@ +# JSON formatting +In general, Cuprate's JSON formatting is very similar to `monerod`, but there are some differences. + +This is a list of those differences. + +## Pretty vs compact +> TODO: decide when handlers are created if we should allow custom formatting. + +Cuprate's RPC (really, [`serde_json`](https://docs.rs/serde_json)) can be configured to use either: +- [Pretty formatting](https://docs.rs/serde_json/latest/serde_json/ser/struct.PrettyFormatter.html) +- [Compact formatting](https://docs.rs/serde_json/latest/serde_json/ser/struct.CompactFormatter.html) + +`monerod` uses something _similar_ to pretty formatting. + +As an example, pretty formatting: +```json +{ + "number": 1, + "array": [ + 0, + 1 + ], + "string": "", + "array_of_objects": [ + { + "x": 1.0, + "y": -1.0 + }, + { + "x": 2.0, + "y": -2.0 + } + ] +} +``` +compact formatting: +```json +{"number":1,"array":[0,1],"string":"","array_of_objects":[{"x":1.0,"y":-1.0},{"x":2.0,"y":-2.0}]} +``` + +## Array of objects +`monerod` will format an array of objects like such: +```json +{ + "array_of_objects": [{ + "x": 0.0, + "y": 0.0, + },{ + "x": 0.0, + "y": 0.0, + },{ + "x": 0.0, + "y": 0.0 + }] +} +``` + +Cuprate will format the above like such: +```json +{ + "array_of_objects": [ + { + "x": 0.0, + "y": 0.0, + }, + { + "x": 0.0, + "y": 0.0, + }, + { + "x": 0.0, + "y": 0.0 + } + ] +} +``` + +## Array of maps containing named objects +An method that contains outputs like this is the `peers` field in the `sync_info` method: +```bash +curl \ + http://127.0.0.1:18081/json_rpc \ + -d '{"jsonrpc":"2.0","id":"0","method":"sync_info"}' \ + -H 'Content-Type: application/json' +``` + +`monerod` will format an array of maps that contains named objects like such: +```json +{ + "array": [{ + "named_object": { + "field": "" + } + },{ + "named_object": { + "field": "" + } + }] +} +``` + +Cuprate will format the above like such: +```json +{ + "array": [ + { + "named_object": { + "field": "" + } + }, + { + "named_object": { + "field": "" + } + } + ] +} +``` \ No newline at end of file diff --git a/books/architecture/src/rpc/differences/json-rpc-strictness.md b/books/architecture/src/rpc/differences/json-rpc-strictness.md new file mode 100644 index 0000000..a30d79d --- /dev/null +++ b/books/architecture/src/rpc/differences/json-rpc-strictness.md @@ -0,0 +1,137 @@ +# JSON-RPC strictness +This is a list of behavior that `monerod`'s JSON-RPC implementation allows, that Cuprate's JSON-RPC implementation does not. + +In general, `monerod`'s JSON-RPC is quite lenient, going against the specification in many cases. +Cuprate's JSON-RPC implementation is slightly more strict. + +Cuprate also makes some decisions that are _different_ than `monerod`, but are not necessarily more or less strict. + +## Allowing an incorrect `jsonrpc` field +[The JSON-RPC 2.0 specification states that the `jsonrpc` field must be exactly `"2.0"`](https://www.jsonrpc.org/specification#request_object). + +`monerod` allows `jsonrpc` to: +- Be any string +- Be an empty array +- Be `null` +- Not exist at all + +Examples: +```bash +curl \ + http://127.0.0.1:18081/json_rpc \ + -d '{"jsonrpc":"???","method":"get_block_count"}' \ + -H 'Content-Type: application/json' +``` + +```bash +curl \ + http://127.0.0.1:18081/json_rpc \ + -d '{"jsonrpc":[],"method":"get_block_count"}' \ + -H 'Content-Type: application/json' +``` + +```bash +curl \ + http://127.0.0.1:18081/json_rpc \ + -d '{"jsonrpc":null,"method":"get_block_count"}' \ + -H 'Content-Type: application/json' +``` + +```bash +curl \ + http://127.0.0.1:18081/json_rpc \ + -d '{"method":"get_block_count"}' \ + -H 'Content-Type: application/json' +``` + +## Allowing `id` to be any type +JSON-RPC 2.0 responses must contain the same `id` as the original request. + +However, the [specification states](https://www.jsonrpc.org/specification#request_object): + +> An identifier established by the Client that MUST contain a String, Number, or NULL value if included + +`monerod` does not check this and allows `id` to be any JSON type, for example, a map: +```bash +curl \ + http://127.0.0.1:18081/json_rpc \ + -d '{"jsonrpc":"2.0","id":{"THIS":{"IS":"ALLOWED"}},"method":"get_block_count"}' \ + -H 'Content-Type: application/json' +``` + +The response: +```json +{ + "id": { + "THIS": { + "IS": "ALLOWED" + } + }, + "jsonrpc": "2.0", + "result": { + "count": 3210225, + "status": "OK", + "untrusted": false + } +} +``` + +## Responding with `id:0` on error +The JSON-RPC [specification states](https://www.jsonrpc.org/specification#response_object): + +> If there was an error in detecting the id in the Request object (e.g. Parse error/Invalid Request), it MUST be Null. + +Although, `monerod` will respond with `id:0` in these cases. + +```bash +curl \ + http://127.0.0.1:18081/json_rpc \ + -d '{"jsonrpc":"2.0","id":asdf,"method":"get_block_count"}' \ + -H 'Content-Type: application/json' +``` +Response: +```bash +{ + "error": { + "code": -32700, + "message": "Parse error" + }, + "id": 0, + "jsonrpc": "2.0" +} +``` + +## Responding to notifications +> TODO: decide on Cuprate behavior + +Requests that have no `id` field are "notifications". + +[The JSON-RPC 2.0 specification states that requests without +an `id` field must _not_ be responded to](https://www.jsonrpc.org/specification#notification). + +Example: +```bash +curl \ + http://127.0.0.1:18081/json_rpc \ + -d '{"jsonrpc":"2.0","method":"get_block_count"}' \ + -H 'Content-Type: application/json' +``` + +## Upper/mixed case fields +`monerod` will accept upper/mixed case fields on: +- `jsonrpc` +- `id` + +`method` however, is checked. + +The JSON-RPC 2.0 specification does not outright state what case to support, +although, Cuprate only supports lowercase as supporting upper/mixed case +is more code to add as `serde` by default is case-sensitive on `struct` fields. + +Example: +```bash +curl \ + http://127.0.0.1:18081/json_rpc \ + -d '{"jsONrPc":"2.0","iD":0,"method":"get_block_count"}' \ + -H 'Content-Type: application/json' +``` \ No newline at end of file diff --git a/books/architecture/src/rpc/differences/json-strictness.md b/books/architecture/src/rpc/differences/json-strictness.md new file mode 100644 index 0000000..92b6fc2 --- /dev/null +++ b/books/architecture/src/rpc/differences/json-strictness.md @@ -0,0 +1,49 @@ +# JSON strictness +This is a list of behavior that `monerod`'s JSON parser allows, that Cuprate's JSON parser ([`serde_json`](https://docs.rs/serde_json)) does not. + +In general, `monerod`'s parser is quite lenient, allowing invalid JSON in many cases. +Cuprate's (really, `serde_json`) JSON parser is quite strict, essentially sticking to +the [JSON specification](https://datatracker.ietf.org/doc/html/rfc8259). + +Cuprate also makes some decisions that are _different_ than `monerod`, but are not necessarily more or less strict. + +## Missing closing bracket +`monerod` will accept JSON missing a final closing `}`. + +Example: +```bash +curl \ + http://127.0.0.1:18081/json_rpc \ + -d '{"jsonrpc":"2.0","id":"0","method":"get_block_count"' \ + -H 'Content-Type: application/json' +``` + +## Trailing ending comma +`monerod` will accept JSON containing a final trailing `,`. + +Example: +```bash +curl \ + http://127.0.0.1:18081/json_rpc \ + -d '{"jsonrpc":"2.0","id":"0","method":"get_block_count",}' \ + -H 'Content-Type: application/json' +``` + +## Allowing `-` in fields +`monerod` allows `-` as a valid value in certain fields, **not a string `"-"`, but the character `-`**. + +The fields where this is allowed seems to be any field `monerod` does not explicitly look for, examples include: +- `jsonrpc` +- `id` +- `params` (where parameters are not expected) +- Any ignored field + +The [JSON-RPC 2.0 specification does state that the response `id` should be `null` upon errors in detecting the request `id`](https://wwwjsonrpc.org/specification#response_object), although in this case, this is invalid JSON and should not make it this far. The response will contain the default `id: 0` in this case. + +Example: +```bash +curl \ + http://127.0.0.1:18081/json_rpc \ + -d '{"jsonrpc":-,"id":-,"params":-,"IGNORED_FIELD":-,"method":"get_block_count"}' \ + -H 'Content-Type: application/json' +``` \ No newline at end of file diff --git a/books/architecture/src/rpc/differences/rpc-calls-with-different-behavior.md b/books/architecture/src/rpc/differences/rpc-calls-with-different-behavior.md new file mode 100644 index 0000000..6c1cb69 --- /dev/null +++ b/books/architecture/src/rpc/differences/rpc-calls-with-different-behavior.md @@ -0,0 +1,2 @@ +# RPC calls with different behavior +> TODO: compile RPC calls with different behavior after handlers are created. \ No newline at end of file diff --git a/books/architecture/src/rpc/differences/rpc-payment.md b/books/architecture/src/rpc/differences/rpc-payment.md new file mode 100644 index 0000000..ae38c0d --- /dev/null +++ b/books/architecture/src/rpc/differences/rpc-payment.md @@ -0,0 +1,9 @@ +# RPC payment +The RPC payment system in `monerod` is a [pseudo-deprecated](https://github.com/monero-project/monero/issues/8722) +system that allows node operators to be compensated for RPC usage. + +Although this system is pseudo-deprecated, `monerod` still generates related fields in responses. [Cuprate follows this behavior](https://doc.cuprate.org/cuprate_rpc_types/base/struct.AccessResponseBase.html). + +However, the [associated endpoints](https://github.com/monero-project/monero/blob/cc73fe71162d564ffda8e549b79a350bca53c454/src/rpc/core_rpc_server.h#L182-L187) and [actual functionality](https://github.com/monero-project/monero/blob/cc73fe71162d564ffda8e549b79a350bca53c454/src/rpc/core_rpc_server.h#L260-L265) are not supported by Cuprate. The associated endpoints will return an error upon invocation. + +> TODO: decide on behavior and document . \ No newline at end of file diff --git a/books/architecture/src/rpc/differences/unsupported-rpc-calls.md b/books/architecture/src/rpc/differences/unsupported-rpc-calls.md new file mode 100644 index 0000000..ac0c2df --- /dev/null +++ b/books/architecture/src/rpc/differences/unsupported-rpc-calls.md @@ -0,0 +1,2 @@ +# Unsupported RPC calls +> TODO: compile unsupported RPC calls after handlers are created. \ No newline at end of file diff --git a/books/architecture/src/rpc/handler.md b/books/architecture/src/rpc/handler.md deleted file mode 100644 index fffa45f..0000000 --- a/books/architecture/src/rpc/handler.md +++ /dev/null @@ -1 +0,0 @@ -# ⚪️ Handler diff --git a/books/architecture/src/rpc/handler/intro.md b/books/architecture/src/rpc/handler/intro.md new file mode 100644 index 0000000..e664f5b --- /dev/null +++ b/books/architecture/src/rpc/handler/intro.md @@ -0,0 +1,2 @@ +# The handler +> TODO: fill after `cuprate-rpc-handler` is created. \ No newline at end of file diff --git a/books/architecture/src/rpc/interface.md b/books/architecture/src/rpc/interface.md index 541b744..3557ffb 100644 --- a/books/architecture/src/rpc/interface.md +++ b/books/architecture/src/rpc/interface.md @@ -1 +1,36 @@ -# ⚪️ Interface +# The interface +> This section is short as [`cuprate-rpc-interface`](https://doc.cuprate.org/cuprate_rpc_interface) contains detailed documentation. + +The RPC interface, which includes: + +- Endpoint routing (`/json_rpc`, `/get_blocks.bin`, etc) +- Route function signatures (`async fn json_rpc(...) -> Response`) +- Type (de)serialization +- Any miscellaneous handling (denying `restricted` RPC calls) + +is handled by the [`cuprate-rpc-interface`](https://doc.cuprate.org/cuprate_rpc_interface) crate. + +Essentially, this crate provides the API for the RPC. + +`cuprate-rpc-interface` is built on-top of [`axum`](https://docs.rs/axum) and [`tower`](https://docs.rs/tower), +which are the crates doing the bulk majority of the work. + +## Request -> Response +The functions that map requests to responses are not implemented by `cuprate-rpc-interface` itself, they must be provided by the user, i.e. it can be _customized_. + +In Rust terms, this crate provides you with: +```rust +async fn json_rpc( + state: State, + request: Request, +) -> Response { + /* your handler here */ +} +``` +and you provide the function body. + +The main handler crate is [`cuprate-rpc-handler`](https://doc.cuprate.org/cuprate_rpc_handler). +This crate implements the standard RPC behavior, i.e. it mostly mirrors `monerod`. + +Although, it's worth noting that other implementations are possible, such as an RPC handler that caches blocks, +or an RPC handler that only accepts certain endpoints, or any combination. \ No newline at end of file diff --git a/books/architecture/src/rpc/intro.md b/books/architecture/src/rpc/intro.md index dcfc82b..acfc604 100644 --- a/books/architecture/src/rpc/intro.md +++ b/books/architecture/src/rpc/intro.md @@ -1,3 +1,30 @@ # RPC -- -- \ No newline at end of file +`monerod`'s daemon RPC has three kinds of RPC calls: +1. [JSON-RPC 2.0](https://www.jsonrpc.org/specification) methods, called at the `/json_rpc` endpoint +1. JSON (but not JSON-RPC 2.0) methods called at their own endpoints, e.g. [`/get_height`](https://www.getmonero.org/resources/developer-guides/daemon-rpc.html#get_height) +1. Binary ([epee](../../formats-protocols-types/epee.html)) RPC methods called at their own endpoints ending in `.bin`, e.g. [`/get_blocks.bin`](https://www.getmonero.org/resources/developer-guides/daemon-rpc.html#get_blocksbin) + +Cuprate's RPC aims to mirror `monerod`'s as much as it practically can. + +This includes, but is not limited to: +- Using the same endpoints +- Receiving the same request data +- Sending the same response data +- Responding with the same HTTP status codes +- Following internal behavior (e.g. [`/pop_blocks`](https://www.getmonero.org/resources/developer-guides/daemon-rpc.html#pop_blocks)) + +Not all `monerod` behavior can always be followed, however. + +Some are not followed on purpose, some cannot be followed due to technical limitations, and some cannot be due to the behavior being `monerod` specific such as the [`/set_log_categories`](https://www.getmonero.org/resources/developer-guides/daemon-rpc.html#set_log_categories) endpoint which uses `monerod`'s logging categories. + +Both subtle and large differences between Cuprate's RPC and `monerod`'s RPC are documented in the [Differences with `monerod`](differences/intro.md) section. + +## Main RPC components +The main components that make up Cuprate's RPC are noted below, alongside the equivalent `monerod` code and other notes. + +| Cuprate crate | `monerod` (rough) equivalent | Purpose | Notes | +|---------------|------------------------------|---------|-------| +| [`cuprate-json-rpc`](https://doc.cuprate.org/cuprate_json_rpc) | [`jsonrpc_structs.h`](https://github.com/monero-project/monero/blob/caa62bc9ea1c5f2ffe3ffa440ad230e1de509bfd/contrib/epee/include/net/jsonrpc_structs.h), [`http_server_handlers_map2.h`](https://github.com/monero-project/monero/blob/caa62bc9ea1c5f2ffe3ffa440ad230e1de509bfd/contrib/epee/include/net/http_server_handlers_map2.h) | JSON-RPC 2.0 implementation | `monerod`'s JSON-RPC 2.0 handling is spread across a few files. The first defines some data structures, the second contains macros that (essentially) implement JSON-RPC 2.0. +| [`cuprate-rpc-types`](https://doc.cuprate.org/cuprate_rpc_types) | [`core_rpc_server_commands_defs.h`](https://github.com/monero-project/monero/blob/caa62bc9ea1c5f2ffe3ffa440ad230e1de509bfd/src/rpc/core_rpc_server_commands_defs.h) | RPC request/response type definitions and (de)serialization | | +| [`cuprate-rpc-interface`](https://doc.cuprate.org/cuprate_rpc_interface) | [`core_rpc_server.h`](https://github.com/monero-project/monero/blob/caa62bc9ea1c5f2ffe3ffa440ad230e1de509bfd/src/rpc/core_rpc_server.h) | RPC interface, routing, endpoints | | +| [`cuprate-rpc-handler`](https://doc.cuprate.org/cuprate_rpc_handler) | [`core_rpc_server.cpp`](https://github.com/monero-project/monero/blob/caa62bc9ea1c5f2ffe3ffa440ad230e1de509bfd/src/rpc/core_rpc_server.cpp) | RPC request/response handling | These are the "inner handler" functions that turn requests into responses | \ No newline at end of file diff --git a/books/architecture/src/rpc/json-rpc.md b/books/architecture/src/rpc/json-rpc.md new file mode 100644 index 0000000..ac52cd1 --- /dev/null +++ b/books/architecture/src/rpc/json-rpc.md @@ -0,0 +1,10 @@ +# JSON-RPC 2.0 +Cuprate has a standalone crate that implements the [JSON-RPC 2.0](https://www.jsonrpc.org/specification) specification, [`cuprate-json-rpc`](https://doc.cuprate.org/cuprate_json_rpc). The RPC methods at the `/json_rpc` endpoint use this crate's types, functions, and (de)serialization. + +There is nothing too special about Cuprate's implementation. +Any small notes and differences are noted in the crate documentation. + +As such, there is not much to document here, instead, consider reading the very +brief JSON-RPC 2.0 specification, and the `cuprate-json-rpc` crate documentation. + +> TODO: document `method/params` vs flattened `base` when figured out. diff --git a/books/architecture/src/rpc/methods/intro.md b/books/architecture/src/rpc/methods/intro.md deleted file mode 100644 index d4a3a15..0000000 --- a/books/architecture/src/rpc/methods/intro.md +++ /dev/null @@ -1 +0,0 @@ -# ⚪️ Methods diff --git a/books/architecture/src/rpc/router.md b/books/architecture/src/rpc/router.md deleted file mode 100644 index 1827dd3..0000000 --- a/books/architecture/src/rpc/router.md +++ /dev/null @@ -1 +0,0 @@ -# ⚪️ Router diff --git a/books/architecture/src/rpc/server/intro.md b/books/architecture/src/rpc/server/intro.md new file mode 100644 index 0000000..0178ce2 --- /dev/null +++ b/books/architecture/src/rpc/server/intro.md @@ -0,0 +1,2 @@ +# 🔴 The server +> TODO: fill after `cuprate-rpc-server` or binary impl is created. \ No newline at end of file diff --git a/books/architecture/src/rpc/types/base-types.md b/books/architecture/src/rpc/types/base-types.md new file mode 100644 index 0000000..feda38d --- /dev/null +++ b/books/architecture/src/rpc/types/base-types.md @@ -0,0 +1,39 @@ +# Base RPC types +There exists a few "base" types that many types are built on-top of in `monerod`. +These are also implemented in [`cuprate-rpc-types`](https://doc.cuprate.org/cuprate_rpc_types/base/index.html). + +For example, many requests include these 2 fields: +```json +{ + "status": "OK", + "untrusted": false, +} +``` +This is [`rpc_response_base`](https://github.com/monero-project/monero/blob/cc73fe71162d564ffda8e549b79a350bca53c454/src/rpc/core_rpc_server_commands_defs.h#L101-L112) in `monerod`, and [`ResponseBase`](https://doc.cuprate.org/cuprate_rpc_types/base/struct.ResponseBase.html) in Cuprate. + +These types are [flattened](https://serde.rs/field-attrs.html#flatten) into other types, i.e. the fields +from these base types are injected into the given type. For example, [`get_block_count`](https://www.getmonero.org/resources/developer-guides/daemon-rpc.html#get_block_count)'s response type is defined [like such in Cuprate](https://doc.cuprate.org/cuprate_rpc_types/json/struct.GetBlockCountResponse.html): +```rust +struct GetBlockCountResponse { + // The fields of this `base` type are directly + // injected into `GetBlockCountResponse` during + // (de)serialization. + // + // I.e. it is as if this `base` field were actually these 2 fields: + // status: Status, + // untrusted: bool, + base: ResponseBase, + count: u64, +} +``` +The JSON output of this type would look something like: +```json +{ + "status": "OK", + "untrusted": "false", + "count": 993163 +} +``` + +## RPC payment +`monerod` also contains RPC base types for the [RPC payment](https://doc.cuprate.org/cuprate_rpc_types/base/struct.AccessResponseBase.html) system. Although the RPC payment system [is](https://github.com/monero-project/monero/issues/8722) [pseudo](https://github.com/monero-project/monero/pull/8724) [deprecated](https://github.com/monero-project/monero/pull/8843), `monerod` still generates these fields in responses, and thus, [so does Cuprate](https://doc.cuprate.org/cuprate_rpc_types/base/struct.AccessResponseBase.html). \ No newline at end of file diff --git a/books/architecture/src/rpc/types/binary.md b/books/architecture/src/rpc/types/binary.md deleted file mode 100644 index dea12fa..0000000 --- a/books/architecture/src/rpc/types/binary.md +++ /dev/null @@ -1 +0,0 @@ -# ⚪️ Binary diff --git a/books/architecture/src/rpc/types/deserialization.md b/books/architecture/src/rpc/types/deserialization.md new file mode 100644 index 0000000..736ef24 --- /dev/null +++ b/books/architecture/src/rpc/types/deserialization.md @@ -0,0 +1,38 @@ +# (De)serialization +A crucial responsibility of [`cuprate-rpc-types`](https://doc.cuprate.org/cuprate_rpc_types) +is to provide the _correct_ (de)serialization of types. + +The input/output of Cuprate's RPC should match `monerod` (as much as practically possible). + +A simple example of this is that [`/get_height`](https://www.getmonero.org/resources/developer-guides/daemon-rpc.html#get_height) +should respond with the exact same data for both `monerod` and Cuprate: +```json +{ + "hash": "7e23a28cfa6df925d5b63940baf60b83c0cbb65da95f49b19e7cf0ce7dd709ce", + "height": 2287217, + "status": "OK", + "untrusted": false +} +``` +Behavior would be considered incompatible if any of the following were true: +- Fields are missing +- Extra fields exist +- Field types are incorrect (`string` instead of `number`, etc) + +## JSON +(De)serialization for JSON is implemented using [`serde`](https://docs.rs/serde) and [`serde_json`](https://docs.rs/serde_json). + +[`cuprate-rpc-interface`](https://doc.cuprate.org/cuprate_rpc_interface) (the main crate responsible +for the actual output) uses `serde_json` for JSON formatting. It is _mostly_ the same formatting as `monerod`, [although there are slight differences](../differences/json-formatting.md). + +Technically, the formatting of the JSON output is not handled by `cuprate-rpc-types`, users are free to choose whatever formatting they desire. + +## Epee +(De)serialization for the [epee binary format](../../formats-protocols-types/epee.md) is +handled by Cuprate's in-house [cuprate-epee-encoding](https://doc.cuprate.org/cuprate_epee_encoding) library. + +## Bitcasted `struct`s +> TODO: + +## Compressed data +> TODO: \ No newline at end of file diff --git a/books/architecture/src/rpc/types/intro.md b/books/architecture/src/rpc/types/intro.md index 22e430c..87b2f6e 100644 --- a/books/architecture/src/rpc/types/intro.md +++ b/books/architecture/src/rpc/types/intro.md @@ -1 +1,31 @@ -# ⚪️ Types +# The types +Cuprate has a crate that defines all the types related to RPC: [`cuprate-rpc-types`](https://doc.cuprate.org/cuprate_rpc_types). + +The main purpose of this crate is to port the types used in `monerod`'s RPC and to re-implement +(de)serialization for those types, whether that be JSON, `epee`, or a custom mix. + +The bulk majority of these types are [request & response types](macro.md), i.e. the inputs +Cuprate's RPC is expecting from users, and the output it will respond with. + +## Example +To showcase an example of the kinds of types defined in this crate, here is a request type: +```rust +#[serde(transparent)] +#[repr(transparent)] +struct OnGetBlockHashRequest { + block_height: [u64; 1], +} +``` +This is the input (`params`) expected in the [`on_get_block_hash`](https://www.getmonero.org/resources/developer-guides/daemon-rpc.html#on_get_block_hash) method. + +As seen above, the type itself encodes some properties, such as being (de)serialized [transparently](https://serde.rs/container-attrs.html#transparent), and the input being an array with 1 length, rather than a single `u64`. [This is to match the behavior of `monerod`](https://github.com/monero-project/monero/blob/caa62bc9ea1c5f2ffe3ffa440ad230e1de509bfd/src/rpc/core_rpc_server.cpp#L1826). + +An example JSON form of this type would be: +```json +{ + "jsonrpc": "2.0", + "id": "0", + "method": "on_get_block_hash", + "params": [912345] // <- This can (de)serialize as a `OnGetBlockHashRequest` +} +``` \ No newline at end of file diff --git a/books/architecture/src/rpc/types/json.md b/books/architecture/src/rpc/types/json.md deleted file mode 100644 index 0bf9351..0000000 --- a/books/architecture/src/rpc/types/json.md +++ /dev/null @@ -1 +0,0 @@ -# ⚪️ JSON diff --git a/books/architecture/src/rpc/types/macro.md b/books/architecture/src/rpc/types/macro.md new file mode 100644 index 0000000..49fb8bc --- /dev/null +++ b/books/architecture/src/rpc/types/macro.md @@ -0,0 +1,16 @@ +# The type generator macro +Request and response types make up the majority of [`cuprate-rpc-types`](https://doc.cuprate.org/cuprate_rpc_types). + +- Request types are the inputs expected _from_ users +- Response types are what will be outputted _to_ users + +Regardless of being meant for JSON-RPC, binary, or a standalone JSON endpoint, +all request/response types are defined using the ["type generator macro"](https://github.com/Cuprate/cuprate/blob/bd375eae40acfad7c8d0205bb10afd0b78e424d2/rpc/types/src/macros.rs#L46). This macro is important because it defines _all_ request/response types. + +This macro: +- Defines a matching pair of request & response types +- Implements many `derive` traits, e.g. `Clone` on those types +- Implements both `serde` and `epee` on those types +- Automates documentation, tests, etc. + +See [here](https://github.com/Cuprate/cuprate/blob/bd375eae40acfad7c8d0205bb10afd0b78e424d2/rpc/types/src/macros.rs#L46) for example usage of this macro. \ No newline at end of file diff --git a/books/architecture/src/rpc/types/metadata.md b/books/architecture/src/rpc/types/metadata.md new file mode 100644 index 0000000..a9c8c73 --- /dev/null +++ b/books/architecture/src/rpc/types/metadata.md @@ -0,0 +1,13 @@ +# Metadata +[`cuprate-rpc-types`](https://doc.cuprate.org/cuprate_rpc_types) also provides +some `trait`s to access some metadata surrounding RPC data types. + +For example, [`trait RpcCall`](https://doc.cuprate.org/cuprate_rpc_types/trait.RpcCall.html) +allows accessing whether an RPC request is [`restricted`](https://doc.cuprate.org/cuprate_rpc_types/trait.RpcCall.html#associatedconstant.IS_RESTRICTED) or not. + +`monerod` has a boolean permission system. RPC calls can be restricted or not. +If an RPC call is restricted, it will only be allowed on un-restricted RPC servers (`18081`). +If an RPC call is _not_ restricted, it will be allowed on all RPC server types (`18081` & `18089`). + +This metadata is used in crates that build upon `cuprate-rpc-types`, e.g. +to know if an RPC call should be allowed through or not. \ No newline at end of file diff --git a/books/architecture/src/rpc/types/misc-types.md b/books/architecture/src/rpc/types/misc-types.md new file mode 100644 index 0000000..07bd516 --- /dev/null +++ b/books/architecture/src/rpc/types/misc-types.md @@ -0,0 +1,11 @@ +# Misc types +Other than the main request/response types, this crate is also responsible +for any [miscellaneous types](https://doc.cuprate.org/cuprate_rpc_types/misc) used within `monerod`'s RPC. + +For example, the `status` field within many RPC responses is defined within +[`cuprate-rpc-types`](https://doc.cuprate.org/cuprate_rpc_types/misc/enum.Status.html). + +Types that aren't requests/responses but exist _within_ request/response +types are also defined in this crate, such as the +[`Distribution`](https://doc.cuprate.org/cuprate_rpc_types/misc/enum.Distribution.html) +structure returned from the [`get_output_distribution`](https://www.getmonero.org/resources/developer-guides/daemon-rpc.html#get_output_distribution) method. \ No newline at end of file diff --git a/books/architecture/src/rpc/types/other.md b/books/architecture/src/rpc/types/other.md deleted file mode 100644 index 49a36cc..0000000 --- a/books/architecture/src/rpc/types/other.md +++ /dev/null @@ -1 +0,0 @@ -# ⚪️ Other