# Homework 1: Building a Custom RPC Handler

[← Back to Building and Integrating Custom RPC Handlers](/core-dev-bootcamp/module07.md)

***

### Overview

In this comprehensive workshop, you will implement a **complete RPC handler** from scratch. This exercise will consolidate everything you've learned by having you build a production-quality `GetAccountBalance` command for Rippled.

**Estimated Time**: 4-6 hours

**Difficulty Level**: Intermediate to Advanced

***

### Learning Objectives

By completing this workshop, you will:

* Understand how handlers are registered in the central handler table
* Implement proper input validation and error handling
* Access ledger data through the JsonContext API
* Calculate XRP reserves and available balances
* Build well-structured JSON responses
* Write comprehensive unit tests for your handler
* Follow Rippled coding conventions and best practices

***

### Prerequisites

Before starting this workshop, ensure you have:

* [ ] Completed Rippled compilation and setup
* [ ] Completed Understanding Rippled architecture
* [ ] Read all RPC Handler Architecture through Best Practices
* [ ] Working Rippled development environment with build tools
* [ ] Familiarity with C++20 features
* [ ] Google Test framework installed and configured

***

### The Challenge

You will implement a new RPC command called `get_account_balance` that provides detailed balance information for an XRP Ledger account, including:

* **Current XRP balance**
* **Base reserve requirement**
* **Owner reserve requirement** (based on owned objects)
* **Available balance** (balance minus reserves)
* **Owner count** (number of owned ledger objects)
* **Optional**: Trust line balances (if requested)

***

### Part 1: Understanding the RPC System

Before writing code, answer these architectural questions to demonstrate your understanding:

#### Questions

1. **Handler Discovery**
   * How does Rippled locate and invoke the correct handler when a request arrives?
   * What data structure stores the mapping between command names and handler functions?
   * Where in the codebase is this mapping defined?
2. **Request Lifecycle**
   * Trace the complete journey of an RPC request from reception to response
   * What stages does a request pass through?
   * At what point is permission checking performed?
3. **Role-Based Access Control**
   * What are the five role levels in Rippled's RBAC system?
   * How is a client's role determined?
   * Can you explain the difference between GUEST, USER, and ADMIN roles?

**Write your answers** in [answers.md](https://github.com/XRPL-Commons/xrpl-trainings/blob/main/core-dev-bootcamp/module07/homeworks/answers.md)

***

### Part 2: Handler Implementation

#### Task 2.1: Create the Handler File

Create a new file:

**File**: `src/xrpld/rpc/handlers/GetAccountBalance.cpp\`

Follow the standard file header format used in other handlers.

#### Task 2.2: Implement the Handler Function

Your handler must:

1. **Validate the `account` parameter**
   * Check if the field exists
   * Validate the account address format
   * Return appropriate errors for invalid input
2. **Handle optional `ledger_index` parameter**
   * Support `"current"`, `"validated"`, `"closed"`
   * Support specific ledger numbers
   * Default to current ledger if not specified
3. **Query the ledger for account data**
   * Use `RPC::lookupLedger()` to get the ledger
   * Read the account object using `keylet::account()`
   * Handle account not found scenario
4. **Calculate balance information**
   * Get current XRP balance
   * Calculate base reserve (from fees)
   * Calculate owner reserve (based on owner count)
   * Compute available balance (balance - total reserve)
5. **Build the response**
   * Include all required fields
   * Use proper JSON structure
   * Mark response as validated when appropriate

#### Task 2.3: Register the Handler

Add your handler to the central handler table:

**File**: `src/xrpld/rpc/handlers/Handlers.cpp`

Choose appropriate values for:

* Minimum required role
* Execution conditions
* API version support

#### Task 2.4: Expected Request Format

```json
{
    "method": "get_account_balance",
    "params": [{
        "account": "rN7n7otQDd6FczFgLdlqtyMVrn3NnrcVXs",
        "ledger_index": "validated"
    }]
}
```

#### Task 2.5: Expected Response Format

```json
{
    "result": {
        "account": "rN7n7otQDd6FczFgLdlqtyMVrn3NnrcVXs",
        "balance": "1000000000",
        "available_balance": "990000000",
        "base_reserve": "10000000",
        "owner_reserve": "0",
        "total_reserve": "10000000",
        "owner_count": 0,
        "ledger_index": 12345,
        "validated": true
    }
}
```

***

### Part 3: Error Handling

Your handler must properly handle these error scenarios:

#### Required Error Cases

1. **Missing `account` field**
   * Error: `rpcINVALID_PARAMS`
   * Message: "Missing 'account' field"
2. **Invalid account format**
   * Error: `rpcACT_MALFORMED`
   * Message: "Invalid account address"
3. **Account not found**
   * Error: `rpcACT_NOT_FOUND`
   * Message: "Account not found"
4. **Ledger not available**
   * Error: `rpcLGR_NOT_FOUND`
   * Message: "Ledger not found"
5. **Invalid ledger index**
   * Error: `rpcINVALID_PARAMS`
   * Message: "Invalid ledger\_index"

#### Task 3.1: Implement Error Handling

Ensure each error case returns the proper error code and a descriptive message.

***

### Part 4: Input Validation

Implement comprehensive validation for:

#### Task 4.1: Account Parameter

```cpp
// Validate account exists
if (!context.params.isMember(jss::account)) {
    // Return error
}

// Validate account is a string
if (!context.params[jss::account].isString()) {
    // Return error
}

// Parse and validate account address
auto const account = parseBase58<AccountID>(...);
if (!account) {
    // Return error
}
```

#### Task 4.2: Ledger Parameter

```cpp
// Handle optional ledger_index
if (context.params.isMember(jss::ledger_index)) {
    // Validate format (string or number)
    // Handle special values ("current", "validated", "closed")
    // Validate numeric ranges
}
```

#### Task 4.3: Edge Cases

Handle these special scenarios:

* Empty account string
* Account with no owner count field (shouldn't happen, but be defensive)
* Ledger with missing fee information
* Balance exceeding maximum XRP supply

***

### Part 5: Testing

Create comprehensive unit tests for your handler.

#### Task 5.1: Create Test File

**File**: `src/test/rpc/GetAccountBalance_test.cpp`

#### Task 5.2: Implement Test Cases

**Test 5.2.1: Valid Request**

```cpp
TEST_F(GetAccountBalance_test, validRequest)
{
    // Create account with known balance
    // Call handler with valid parameters
    // Verify response contains correct values
    // Verify all required fields present
}
```

**Test 5.2.2: Invalid Account Address**

```cpp
TEST_F(GetAccountBalance_test, invalidAccount)
{
    // Call handler with malformed account
    // Verify rpcACT_MALFORMED error returned
    // Verify error message is descriptive
}
```

**Test 5.2.3: Account Not Found**

```cpp
TEST_F(GetAccountBalance_test, accountNotFound)
{
    // Call handler with non-existent account
    // Verify rpcACT_NOT_FOUND error returned
}
```

**Test 5.2.4: Ledger Selection**

```cpp
TEST_F(GetAccountBalance_test, ledgerSelection)
{
    // Test with ledger_index = "current"
    // Test with ledger_index = "validated"
    // Test with specific ledger number
    // Verify correct ledger is queried
}
```

**Test 5.2.5: Permission Levels**

```cpp
TEST_F(GetAccountBalance_test, permissions)
{
    // Test with GUEST role
    // Test with USER role
    // Test with ADMIN role
    // Verify appropriate access granted
}
```

**Test 5.2.6: Missing Parameters**

```cpp
TEST_F(GetAccountBalance_test, missingAccount)
{
    // Call handler without account parameter
    // Verify rpcINVALID_PARAMS error returned
}
```

**Test 5.2.7: Reserve Calculations**

```cpp
TEST_F(GetAccountBalance_test, reserveCalculation)
{
    // Create account with owner count = 5
    // Verify base_reserve is correct
    // Verify owner_reserve = base_reserve * owner_count
    // Verify available_balance = balance - total_reserve
}
```

#### Task 5.3: Optional Advanced Tests

* **Concurrent access**: Test handler with multiple simultaneous requests
* **Performance**: Measure handler execution time
* **Ledger transitions**: Test behavior during ledger close
* **Large owner counts**: Test with accounts owning many objects

***

### Part 6: Optional Enhancements

After completing the core implementation, consider these enhancements:

#### Enhancement 6.1: Trust Line Balances

Add optional `include_lines` parameter:

```json
{
    "method": "get_account_balance",
    "params": [{
        "account": "rN7n7otQDd6FczFgLdlqtyMVrn3NnrcVXs",
        "include_lines": true
    }]
}
```

Response includes trust lines:

```json
{
    "result": {
        ...
        "lines": [
            {
                "currency": "USD",
                "issuer": "rN7n7otQDd6FczFgLdlqtyMVrn3NnrcVXs",
                "balance": "100.00"
            }
        ]
    }
}
```

#### Enhancement 6.2: Currency Filtering

Add `currency` filter for trust lines:

```json
{
    "include_lines": true,
    "currency": "USD"
}
```

#### Enhancement 6.3: Pagination

For accounts with many trust lines, add pagination:

```json
{
    "include_lines": true,
    "limit": 50,
    "marker": "..."
}
```

***

### Reference Implementation Files

Study these existing handlers as reference:

1. **AccountInfo.cpp**
   * Location: `src/xrpld/rpc/handlers/AccountInfo.cpp`
   * Similar structure to your handler
   * Shows account querying and response building
2. **AccountLines.cpp**
   * Location: `src/xrpld/rpc/handlers/AccountLines.cpp`
   * Trust line iteration and filtering
   * Pagination implementation
3. **LedgerData.cpp**
   * Location: `src/xrpld/rpc/handlers/LedgerData.cpp`
   * Ledger object iteration
   * Large result set handling
4. **RPCHelpers.h**
   * Location: `src/xrpld/rpc/detail/RPCHelpers.h`
   * Utility functions for common tasks
   * Account parsing, ledger lookup, etc.

***

### Submission Checklist

Before considering your implementation complete:

* [ ] Handler function implemented with proper signature
* [ ] Handler registered in Handlers.cpp
* [ ] All required parameters validated
* [ ] All error cases handled with appropriate codes
* [ ] Response format matches specification
* [ ] Reserve calculations are correct
* [ ] At least 7 unit tests implemented
* [ ] All tests pass
* [ ] Code follows Rippled style guidelines
* [ ] Code compiles without warnings
* [ ] Documentation comments added
* [ ] Edge cases considered and handled

***

### Evaluation Criteria

Your implementation will be evaluated on:

| Criterion          | Weight | Details                                |
| ------------------ | ------ | -------------------------------------- |
| **Correctness**    | 40%    | Handler produces correct results       |
| **Error Handling** | 20%    | All error cases properly handled       |
| **Code Quality**   | 20%    | Follows best practices and conventions |
| **Testing**        | 15%    | Comprehensive test coverage            |
| **Documentation**  | 5%     | Clear comments and documentation       |

***

### Getting Help

If you encounter difficulties:

1. **Review the module topics**
   * Re-read relevant sections
   * Study code examples carefully
2. **Examine existing handlers**
   * AccountInfo.cpp is the best reference
   * Look for similar patterns
3. **Check Rippled documentation**
   * [XRPL RPC Reference](https://xrpl.org/public-api-methods.html)
   * [Rippled Source Code](https://github.com/XRPLF/rippled)
4. **Ask for help**
   * [Contact us via feedback form](https://docs.google.com/forms/d/e/1FAIpQLSe7qIbqepUKGVJxmWFHv0rs1zsBAzk8G6y5-bfz0Yd5rqW_5A/viewform)
   * Share specific error messages or issues

***

### Solution Resources

After attempting the workshop independently:

* **Hints**: For guidance [see hints →](/core-dev-bootcamp/module07/homeworks/hints.md)
* **Code snippets**: For common patterns [see snippets →](/core-dev-bootcamp/module07/homeworks/snippets.md)
* **Full solution**: Available upon request after submission

***

### What You'll Build

By the end of this workshop, you'll have:

* A fully functional RPC handler integrated into Rippled
* Comprehensive error handling and input validation
* Production-quality unit tests
* Understanding of the complete RPC development lifecycle
* Skills to build any custom handler you need

**Good luck! Enjoy building your first RPC handler!**


---

# 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/module07/homeworks/homework1.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.
