RPC subsystem
@page "rpc" RPC subsystem
The RPC subsystem is where the common framework for handling incoming JSON requests is implemented.
Request validation and deserialisation are not defined here. They live in the shared
xrpl-rpc-spec library, consumed as <rpcspec/...>, so that
one spec per RPC method can be shared verbatim between Clio and xrpld. Clio's job is to supply the
handler body and the response shape.
Components
See the common subfolder.
- AnyHandler: The type-erased wrapper that allows for storing different handlers in one map/vector.
- Concepts: The
SomeHandlerconcept and its parts, which define what a handler must provide. - HandlerRegistry: The table mapping a method name to its handler factory, plus whether the method is Clio-only (and therefore never forwarded to xrpld).
From the spec library:
rpc::spec::HandlerFor<Input>: Base class supplying the staticparseInputandspecentry points a handler needs. It resolves the versioned spec forInputthrough ADL, so a handler names only itsInputtype.rpcspec/handlers/<method>/Types.hpp: The strongly-typedInputstruct for a method (xrpl::AccountID,xrpl::uint256,LedgerSpecifier, ... rather thanstd::string).rpcspec/handlers/<method>/Spec.hpp: The consteval spec declaring that method's fields, their validators and their converters.
Implementing a handler
See the existing handlers in src/rpc/handlers
for examples; NFTInfo is a small one.
Handlers need to fulfil the requirements specified by the SomeHandler concept (see
rpc/common/Concepts.hpp):
-
Derive from
rpc::spec::HandlerFor<rpc::spec::handlers::<method>::Input>. This supplies:-
Input— the strongly-typed input struct, owned by the spec library rather than declared here -
static parseInput(boost::json::value const&, uint32_t apiVersion)— validates and deserialises in one pass, returningstd::expected<Input, Status> -
static spec(uint32_t apiVersion)— returns a type-erasedrpc::spec::RpcSpecView
If the method takes no input at all, skip the base class and expose only
process(Context const&). -
-
Expose an
OutputPOD struct which acts as output of a valid handler invocation. -
Have a
process(Input const&, Context const&)member function returningHandlerReturnType<Output>. Cross-field checks that cannot be expressed in the spec belong here. -
Implement
value_fromsupport forOutputusingtag_invokeas perboost::jsondocumentation. Avalue_toforInputis not needed — the spec deserialises it. -
Register the method in
rpc/common/impl/HandlerRegistry.cpp.
If the method has no spec yet, add Types.hpp and Spec.hpp for it in the spec library first.
Important
Do not hand-write
template struct rpc::spec::HandlerFor<...>;, and do not include<rpcspec/HandlerForDefs.hpp>or<rpcspec/handlers/*/Spec.hpp>from a handler. The explicit instantiations are generated byrpcspec_generate_instantiations()insrc/rpc/CMakeLists.txt, one translation unit per method that has aSpec.hpp. A useful side effect is that every spec is compiled by Clio's build, so spec-side mistakes surface here.
Error messages
A method's error codes and messages are part of its wire contract: clients parse them, so an
altered error, error_code or error_message is a breaking change. If a test disagrees with the
code, fix the spec rather than the expectation.
Clio and xrpld do not always word the same failure identically. The spec library expresses those
differences per server rather than unifying early — ifServerClio() / ifServerXrpld() for
whole validators, and the RPCSPEC_IS_CLIO / RPCSPEC_IS_XRPLD macros for anything finer.