RPC Handler Architecture

Understanding the Foundation of Rippled's RPC System

← Back to Understanding XRPL(d) RPC Architecture


Introduction

The RPC (Remote Procedure Call) system is the primary interface through which external applications, wallets, and services interact with a Rippled node. Understanding its architecture is fundamental to building custom handlers that integrate seamlessly with the XRP Ledger.

Rippled's RPC architecture supports multiple transport protocols—JSON-RPC over HTTP, JSON-RPC over WebSocket, and gRPC—all converging on a unified handler dispatch system. This design ensures consistency, maintainability, and extensibility across different client types.

In this section, you'll learn how handlers are registered, discovered, and invoked within the Rippled codebase.


Core Architecture Components

The RPC system consists of several key components that work together to process requests:

1. Central Handler Table

The handler table is a centralized registry that maps RPC command names to their corresponding handler functions.

Location: src/xrpld/rpc/detail/Handlers.cpp

// Handler table structure (simplified)
std::map<std::string, HandlerInfo> handlerTable = {
    {"account_info", {&doAccountInfo, Role::USER, RPC::NEEDS_CURRENT_LEDGER}},
    {"ledger", {&doLedger, Role::USER, RPC::NEEDS_NETWORK_CONNECTION}},
    {"submit", {&doSubmit, Role::USER, RPC::NEEDS_CURRENT_LEDGER}},
    // ... hundreds of other handlers
};

Key characteristics:

  • Command Name: Case-sensitive string identifier (e.g., "account_info")

  • Handler Function: Pointer to the actual implementation function

  • Required Role: Minimum permission level needed to execute the command

  • Capability Flags: Additional requirements (ledger access, network connectivity, etc.)

2. Handler Information Structure

Each handler is described by a HandlerInfo structure containing metadata:

Purpose:

  • Enables versioning for backward compatibility

  • Specifies permission requirements before execution

  • Defines runtime conditions (e.g., must have synced ledger)

3. Handler Function Signature

All RPC handlers follow a standardized function signature:

Components:

  • Return Type: Json::Value — The JSON response object

  • Parameter: RPC::JsonContext& — Contains request data, ledger access, and configuration

This consistency allows the dispatcher to invoke any handler uniformly.

4. JsonContext Object

The JsonContext provides handlers with everything needed to process a request:

Key capabilities:

  • Ledger Access: Query account states, transactions, and metadata

  • Network Information: Node status, peer connections, consensus state

  • Resource Management: Track API usage and enforce limits

  • Authentication: Know the caller's permission level


Handler Registration Process

Handlers are registered at compile time through static initialization:

Step 1: Define the Handler Function

Step 2: Add to Handler Table

Step 3: Declare in Header (Optional)


Handler Discovery and Dispatch

When a client sends an RPC request, the system follows this flow:

1. Request Reception

The server receives a JSON-RPC request via HTTP, WebSocket, or gRPC:

2. Command Lookup

The dispatcher searches the handler table:

3. Permission Check

Before invoking the handler, the system verifies the caller's role:

4. Condition Validation

The system ensures required conditions are met:

5. Handler Invocation

Finally, the handler is executed:

6. Response Serialization

The result is wrapped in a JSON-RPC response envelope and returned to the client.


Handler Capability Flags

Handlers can declare various capability requirements:

Flag
Meaning
Example Use Case

NEEDS_CURRENT_LEDGER

Requires an open (current) ledger

Real-time account queries

NEEDS_CLOSED_LEDGER

Requires a validated ledger

Historical transaction lookups

NEEDS_NETWORK_CONNECTION

Requires peer connectivity

Transaction submission

NO_CONDITION

No special requirements

Server info, ping

Example:

This ensures the handler cannot execute unless both conditions are satisfied.


Multi-Transport Support

Rippled's RPC system abstracts away transport details, allowing handlers to work across:

HTTP (JSON-RPC)

WebSocket (JSON-RPC)

gRPC (Protocol Buffers)

Handler Transparency: The same handler function serves all three transports—the dispatcher handles protocol-specific details.


Versioning and Compatibility

Rippled supports API versioning to maintain backward compatibility:

Clients can specify the API version in their requests:

If no version is specified, the system uses the default (version 1) implementation.


Real-World Example: AccountInfo Handler

Let's examine the registration of the widely-used account_info handler:

Registration (Handlers.cpp):

Analysis:

  • Command: "account_info"

  • Function: doAccountInfo (defined in src/xrpld/rpc/handlers/AccountInfo.cpp)

  • Role: USER — Available to authenticated users (not just admins)

  • Condition: NEEDS_CURRENT_LEDGER — Requires access to the current open ledger

This registration tells Rippled:

  1. Accept requests with method: "account_info"

  2. Ensure the caller has at least USER-level permissions

  3. Verify a current ledger is available

  4. Invoke doAccountInfo() with the request context


Conclusion

The RPC handler architecture provides a clean, extensible foundation for Rippled's API layer. Through centralized registration in a handler table, uniform function signatures, and automatic permission enforcement, the system ensures consistency across hundreds of commands while remaining easy to extend. The separation between transport protocols and handler logic means the same implementation serves HTTP, WebSocket, and gRPC clients transparently. Understanding this architecture is essential for navigating the codebase, debugging RPC issues, and preparing to implement custom handlers.


Last updated