A language-agnostic collection of test vectors for the XRP Ledger protocol, extracted from the rippled reference implementation.
The XRP Ledger has historically been a single-client network, with rippled as the only production implementation. This repository is a step toward a multi-client future: any implementation that wants to participate in the network must produce identical results to the reference node.
xrpl-fixtures provides a shared, versioned corpus of test vectors that any XRPL client — regardless of language or architecture — can use to verify its correctness. Passing these fixtures is a prerequisite for a client to be considered safe to run on the network.
These fixtures cover the transaction execution layer only: given a ledger state and a set of transactions, verify that the resulting state and TER codes match the reference implementation. This is equivalent to Ethereum's execution-spec-tests.
The following are explicitly out of scope for this fixture format:
- RPC/API conformance — testing JSON-RPC methods requires a live node; a separate fixture format and runner are needed.
- Consensus — multi-node message sequencing cannot be expressed as a state-transition fixture.
- Networking/P2P — rippled-internal infrastructure not relevant to alternative implementations.
Fixtures are extracted from rippled's own unit test suite using an instrumented recorder (FixtureRecorder) that captures, for each test case:
- The ledger environment (enabled amendments, fee schedule)
- The sequence of operations performed (funding accounts, trust lines, ledger closes, transaction submissions)
- The expected transaction result code (
TER) - The expected post-state (account balances, owner counts)
- Both the raw transaction binary (
tx_blob) and its JSON representation (tx_json)
This means every fixture is ground truth: it reflects what the reference implementation actually does, not what the specification says it should do.
To regenerate fixtures from a rippled build:
XRPL_FIXTURE_PATH=/path/to/output ./rippled --unittestxrpl-fixtures/
└── rippled-<version>/ # One directory per rippled release
├── app/ # Transaction engine tests (80+ suites)
│ ├── AccountDelete/
│ ├── AMM/
│ ├── Batch/
│ ├── Escrow/
│ ├── MPToken/
│ ├── NFTokenAllFeatures/
│ ├── OfferAllFeatures/
│ ├── PayChan/
│ └── ...
└── ledger/ # Ledger object and directory tests
├── Directory/
└── ...
Each test suite directory contains one JSON file per test case. The filename is the test case name with spaces replaced by underscores.
Each rippled release gets its own top-level directory (e.g., rippled-2.6.2). Multiple versions can coexist in the repository, allowing implementations to track specific releases or validate behaviour across version upgrades.
Each fixture is a JSON file with the following top-level fields:
| Field | Description |
|---|---|
rippled_version |
The rippled version these vectors were extracted from |
suite |
The fully-qualified C++ test suite name (e.g., ripple.app.PayChan) |
testcase |
The test case name within the suite |
env |
The ledger environment for the first scope (see below) |
steps |
Ordered list of operations to replay (see below) |
{
"env": {
"amendments_enabled": ["NonFungibleTokensV1_1", "AMM", "..."],
"base_fee": "10",
"reserve_base": "200000000",
"reserve_increment": "50000000"
}
}amendments_enabled: The exact set of amendments active at the start of the test. An implementation must enable exactly these amendments and no others.base_fee: The base transaction fee in drops.reserve_base/reserve_increment: Account reserve settings in drops.
The env field describes the initial scope. When a test contains multiple independent ledger scopes (see env_reset below), each subsequent scope's config is carried inline in the step.
Steps are executed sequentially. There are six operation types:
{
"op": "fund",
"account": "alice",
"address": "rG1QQv2nh2gr7RCZ1P8YYcBUKCCN633jCn",
"amount": "1000000000",
"set_default_ripple": true
}Creates the named account with the given XRP balance (in drops), funded from the genesis/master account. account is a human-readable label used to identify the account in subsequent steps.
set_default_ripple indicates whether the asfDefaultRipple flag was set on the account after creation. When false, the account was funded without enabling DefaultRipple — the account's Sequence after funding is ledger_seq (not ledger_seq + 1), and trust lines created on this account default to NoRipple.
{
"op": "trust",
"account": "alice",
"address": "rG1QQv2nh2gr7RCZ1P8YYcBUKCCN633jCn",
"limit_amount": { "currency": "USD", "issuer": "rHb9...", "value": "1000" }
}Establishes a trust line from account to the issuer in limit_amount. rippled's test framework reimburses the transaction fee to the account after the TrustSet, so the account's XRP balance is unchanged by this step.
{ "op": "close" }Advances the ledger, applying all pending transactions. Corresponds to env.close() in rippled's test framework. Any enable_amendment step that precedes a close takes effect at this close.
{
"op": "tx",
"tx_blob": "1200192400000004...",
"tx_json": {
"Account": "rG1QQv2nh2gr7RCZ1P8YYcBUKCCN633jCn",
"TransactionType": "NFTokenMint",
"Fee": "10",
"Sequence": 4,
"NFTokenTaxon": 0,
"SigningPubKey": "...",
"TxnSignature": "..."
},
"expect_ter": "tesSUCCESS",
"post_state": {
"accounts": [
{
"name": "alice",
"address": "rG1QQv2nh2gr7RCZ1P8YYcBUKCCN633jCn",
"xrp_balance": "999999990",
"owner_count": 1
}
]
}
}Submits the transaction to the current open ledger. The implementation must:
- Apply
tx_blob(the canonical serialised transaction) to the current ledger state. - Assert that the resulting TER code matches
expect_ter. - Assert that the post-ledger state matches
post_statefor every account listed.
tx_json is provided for human readability and debugging; tx_blob is the authoritative input.
{
"op": "env_reset",
"env": {
"amendments_enabled": ["..."],
"base_fee": "10",
"reserve_base": "200000000",
"reserve_increment": "50000000"
}
}Resets the ledger to a clean state and applies the new environment config. This corresponds to the construction of a new Env object within the same C++ test function (a new { } scope). The runner must:
- Discard all current ledger state and accounts.
- Create a fresh genesis ledger with the provided
envconfig. - Advance the ledger once (matching rippled's Env constructor behaviour).
All subsequent fund/trust/tx steps operate on this fresh ledger until the next env_reset or end of file.
{ "op": "enable_amendment", "amendment": "TicketBatch" }Signals that the named amendment should be activated. It takes effect on the next close step, matching rippled's semantics (Env::enableFeature() requires a subsequent close() to take effect). Steps between enable_amendment and the next close execute without the amendment active.
expect_ter uses rippled's standard transaction result codes:
| Prefix | Meaning |
|---|---|
tes |
Success (tesSUCCESS) |
tec |
Fee claimed, transaction failed (e.g., tecINSUFFICIENT_RESERVE) |
tem |
Malformed transaction (e.g., temMALFORMED) |
ter |
Retry (e.g., terQUEUED) |
tef |
Failure, fee not claimed |
The rippled-2.6.2 corpus contains:
- 1,518 fixture files across
app/andledger/ - 80+ application-level test suites covering: AMM, Batch, Clawback, Credentials, DID, Escrow, MPToken, NFToken, Offers, PayChan, PermissionedDEX, PermissionedDomains, SetTrust, Tickets, XChain, and more
- 1,285
env_resetsteps (previously untestable multi-scope tests, now fully replayable) - 37
enable_amendmentsteps (previously untestable amendment-activation tests, now fully replayable) - 114
fundsteps withset_default_ripple: false(noripple-funded accounts, previously causing sequence mismatches) - All major TER result codes exercised across both success and failure paths
To validate your XRPL implementation against these vectors:
- Pick a rippled version directory (e.g.,
rippled-2.6.2). - For each fixture file:
a. Read the top-level
envand configure a fresh genesis ledger (amendments, fees, reserves). b. Advance the ledger once (rippled's Env constructor does this on init). c. Replaystepsin order, handling all six op types. - For each
txstep, assertexpect_terand validatepost_state.
An implementation that passes all fixtures for a given version can be considered behaviourally equivalent to rippled at that version for the transaction execution layer.
New fixtures should be generated by running the rippled extractor against a specific tagged release and placing the output under a new versioned directory. Do not hand-author fixture files — they must come from the reference implementation to be authoritative.
When a new rippled version is released:
- Apply the
FixtureRecorderpatch from this repository to the new rippled source. - Build rippled and run:
XRPL_FIXTURE_PATH=/path/to/rippled-<version> ./rippled --unittest - Create a
rippled-<new-version>/directory with the output. - Keep older version directories intact for implementations tracking specific releases.