# RPC Handler Architecture

### Understanding the Foundation of Rippled's RPC System

[← Back to Understanding XRPL(d) RPC Architecture](/core-dev-bootcamp/module06.md)

***

## 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`

```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:

```cpp
struct HandlerInfo {
    handler_type handler;           // Function pointer
    Role role;                      // Minimum role required
    RPC::Condition condition;       // Execution conditions
    unsigned int version_min = 1;   // Minimum API version
    unsigned int version_max = UINT_MAX; // Maximum API version
};
```

**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**:

```cpp
Json::Value handlerName(RPC::JsonContext& context);
```

**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:

```cpp
struct JsonContext {
    Json::Value params;              // Request parameters
    Application& app;                // Access to application services
    Resource::Consumer& consumer;    // Resource tracking
    Role role;                       // Caller's permission level
    std::shared_ptr<ReadView const> ledger; // Ledger view
    NetworkOPs& netOps;              // Network operations
    LedgerMaster& ledgerMaster;      // Ledger management
    // ... additional context
};
```

**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

```cpp
// src/xrpld/rpc/handlers/MyCustomHandler.cpp
namespace ripple {

Json::Value doMyCustomCommand(RPC::JsonContext& context)
{
    Json::Value result;
    // Implementation here
    return result;
}

} // namespace ripple
```

### Step 2: Add to Handler Table

```cpp
// src/xrpld/rpc/handlers/Handlers.cpp
{
    "my_custom_command",
    {
        &doMyCustomCommand,       // Function pointer
        Role::USER,               // Minimum role
        RPC::NEEDS_CURRENT_LEDGER // Conditions
    }
}
```

### Step 3: Declare in Header (Optional)

```cpp
// src/xrpld/rpc/handlers/Handlers.h
Json::Value doMyCustomCommand(RPC::JsonContext&);
```

***

## 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:

```json
{
    "method": "account_info",
    "params": [{
        "account": "rN7n7otQDd6FczFgLdlqtyMVrn3NnrcVXs"
    }]
}
```

### 2. Command Lookup

The dispatcher searches the handler table:

```cpp
auto it = handlerTable.find(request["method"].asString());
if (it == handlerTable.end()) {
    return rpcError(rpcUNKNOWN_COMMAND);
}
```

### 3. Permission Check

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

```cpp
if (context.role < handlerInfo.role) {
    return rpcError(rpcNO_PERMISSION);
}
```

### 4. Condition Validation

The system ensures required conditions are met:

```cpp
if (handlerInfo.condition & RPC::NEEDS_CURRENT_LEDGER) {
    if (!context.ledgerMaster.haveLedger()) {
        return rpcError(rpcNO_CURRENT);
    }
}
```

### 5. Handler Invocation

Finally, the handler is executed:

```cpp
Json::Value response = handlerInfo.handler(context);
```

### 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**:

```cpp
{"submit", {&doSubmit, Role::USER, RPC::NEEDS_CURRENT_LEDGER | RPC::NEEDS_NETWORK_CONNECTION}}
```

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)

```bash
curl -X POST http://localhost:5005/ \
  -H "Content-Type: application/json" \
  -d '{
    "method": "account_info",
    "params": [{"account": "rN7n7otQDd6FczFgLdlqtyMVrn3NnrcVXs"}]
  }'
```

### WebSocket (JSON-RPC)

```javascript
const ws = new WebSocket('ws://localhost:6006');
ws.send(JSON.stringify({
    command: 'account_info',
    account: 'rN7n7otQDd6FczFgLdlqtyMVrn3NnrcVXs'
}));
```

### gRPC (Protocol Buffers)

```proto
service XRPLedgerAPIService {
    rpc GetAccountInfo(GetAccountInfoRequest) returns (GetAccountInfoResponse);
}
```

**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:

```cpp
{
    "account_info",
    {
        &doAccountInfo_v2,  // New implementation
        Role::USER,
        RPC::NEEDS_CURRENT_LEDGER,
        2,  // Minimum API version
        UINT_MAX
    }
}
```

Clients can specify the API version in their requests:

```json
{
    "method": "account_info",
    "api_version": 2,
    "params": [...]
}
```

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`):

```cpp
{
    "account_info",
    {
        &doAccountInfo,
        Role::USER,
        RPC::NEEDS_CURRENT_LEDGER
    }
}
```

**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.

***


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.xrpl-commons.org/core-dev-bootcamp/module06/rpc-handler-architecture.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
