Skip to main content

plumb-xrpl

XRP Ledger transactions and payments proven without trusting any server. Servers are only a source of bytes; every link of the chain of trust is checked on your machine:

  1. Ripple's validator list (vl.ripple.com): the publisher's manifest must be signed by the pinned publisher master key, the list by the publisher's signing key, and every validator's manifest by its own master key. An expired list is refused. Validators that rotated their signing key are refreshed with a newer manifest signed by the same master key.
  2. A ledger signed by a quorum (80%) of those validators: each validation's signature is checked against the validator's current signing key, over the validation's own bytes.
  3. Hash links from that ledger back to the transaction's ledger: every header carries its parent's hash, and each header's hash is recomputed here (SHA-512Half over LWR\0 + header).
  4. The ledger's transaction tree, rebuilt from every transaction and its metadata (SHAMap) and compared with the header's transaction root.
  5. The payment itself, from those proven bytes: a successful Payment, to the payee, of the right currency (issuer included), the delivered amount (never the requested Amount) at least the minimum, optionally from one payer and carrying one InvoiceID.

A lie at any link is caught by the tests (tests/test_prover.py): wrong publisher key, forged validation, broken hash link, tampered transaction, wrong recipient, underpayment, partial payment, wrong invoice.

This is the XRPL prover of the Plumb Data Exchange, where it has proven live RLUSD, XRP, payment-channel, escrow and batch payments on mainnet since October 2026. It is published so any seller, agent or wallet can hold a payment to the same standard.

Install

pip install plumb-xrpl            # library + CLI
pip install "plumb-xrpl[mcp]"     # + the MCP server for agents

Python 3.11+. Dependencies: xrpl-py (byte codecs only; every hash and signature is checked here), coincurve (secp256k1), cryptography (ed25519), websockets.

Command line

plumb-xrpl tx  71100808DD3DF3EA693471FA...                       # any transaction, proven: fields + metadata
plumb-xrpl pay 71100808... r9Y5...P2d --token RLUSD --min 50000   # was at least 0.05 RLUSD delivered to this account?
plumb-xrpl pay 3A3915B2... rXXX --invoice C08EC114...             # ...and does the payment carry this InvoiceID?
plumb-xrpl record                                                 # keep quorum proofs (see "How old can a payment be?")
plumb-xrpl serve                                                  # MCP server over stdio (extra: plumb-xrpl[mcp])

Output is JSON in one envelope shape:

{"verified": true,
 "value": {"verdict": "paid", "paid_raw": "50000", "reason": "...",
           "tx": {"tx_hash": "...", "block_number": 100000000, "block_hash": "...", "final": true, "...": "..."},
           "xrpl": {"level": "unl_quorum", "validators": 28, "of": 35, "quorum": 28, "proof_ledger": 100000120,
                    "walked": 120, "from_archive": false, "delivered": "...", "invoice_id": "..."}},
 "anchor": {"root": "<hash of the quorum-signed ledger>", "source": "XRPL validator list (vl.ripple.com seq N, pinned publisher ...): 28/35 validator signatures on ledger ..., 120 hash links back"}}

verified: false always comes with a reason and never a guess. A transaction the servers do not yet report in a validated ledger is verdict: pending_validation with retry_after_s.

Amounts are raw integers: XRP in drops, issued currencies (RLUSD, ...) in millionths (value x 10^6, rounded down). Tokens: XRP (default), RLUSD, or CURRENCY.rIssuer.

Library

from plumb_xrpl import XrplHistory, envelope

h = XrplHistory()
proof = h.verify_payment(tx_hash, recipient="r9Y5...P2d", token="RLUSD", min_raw=50_000, invoice=None)
# proof["verdict"] in {"paid", "not_paid", "underpaid", "failed_transaction", ...}; proof["xrpl"]["validators"] etc.

tx = h.transaction(tx_hash)          # any transaction type, decoded fields + metadata, proven
out = envelope(lambda: h.transaction(tx_hash))   # the CLI's {"verified": ..., "value": ..., "anchor": ...} shape

What a proven transaction gives you beyond payments: the metadata's AffectedNodes carry the final fields of every ledger object the transaction created or changed, so the state as of that transaction is proven too: account balances after it, a trust line, an escrow or payment channel it created, a credential it issued.

MCP server

plumb-xrpl serve exposes two tools over stdio: get_transaction(tx_hash) and verify_payment(tx_hash, recipient, token="", min_amount_raw="0", payer="", invoice=""). Same envelope as the CLI.

How old can a payment be?

Validator signatures exist only live; rippled servers do not keep them. So a quorum proof comes from one of two places:

  • the live stream: for a fresh payment, walk back from a ledger being validated now (up to MAX_WALK ledgers);
  • the proof archive: plumb-xrpl record keeps one quorum-signed ledger per 256 ledgers (about every 16 minutes) in ~/.verified-reads/xrpl-proofs.jsonl (override with VR_XRPL_ARCHIVE). Any older payment is then proven by hash links from the nearest archived proof. Run it as a service if you need to prove payments older than a few hours.

A payment older than both is reported unverifiable, never guessed. The validator list is cached at ~/.verified-reads/xrpl-unl.json (VR_XRPL_UNL_CACHE) and refreshed hourly.

What this does not do

  • It proves transactions, not the current state tree. "Current balance of account X" needs the full account-state SHAMap, which no public server proves. Balances are proven as of a transaction (see Library).
  • It does not submit or sign anything. Keys never enter this package.
  • It trusts Ripple's validator list publisher key, pinned in the source, and the 80% quorum rule of the XRP Ledger. That is the same trust assumption as a rippled node you run yourself, without running one.

Development

pip install -e ".[test,mcp]"
python -m pytest -q

Fixtures are real mainnet bytes recorded with tests/record_fixtures.py; the tests replay them through a fake source and then tamper with each link.

License

Apache-2.0. See LICENSE and NOTICE.

Metadata

Release files for plumb-xrpl 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for plumb-xrpl 0.1.0
File Size Uploaded Provenance Actions
plumb_xrpl-0.1.0.tar.gz 26.7 kB Without provenance Details for plumb_xrpl-0.1.0.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for plumb-xrpl 0.1.0
File Interpreter ABI Platform Actions
plumb_xrpl-0.1.0-py3-none-any.whl Python 3 none any Details for plumb_xrpl-0.1.0-py3-none-any.whl

Total release size: 48.9 kB

Release files / plumb_xrpl-0.1.0.tar.gz

Download URL plumb_xrpl-0.1.0.tar.gz
Size 26.7 kB
Tags Source
SHA-256 checksum
How to use checksums
cb9b1aa4bfe6282de50ddedf96f0e9291db1817c9906742ea2f93728124304eb
BLAKE2b-256 checksum
How to use checksums
f5147a94a0717fc151738623a16088f030f71d42679cb54ba08b5510f9a1570d
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.12.6

Release files / plumb_xrpl-0.1.0-py3-none-any.whl

Download URL plumb_xrpl-0.1.0-py3-none-any.whl
Size 22.2 kB
Tags Python 3
SHA-256 checksum
How to use checksums
4331e2fda63cb257e4b0df09099dab5e9b955cee8272c63d72e0932104f1eac6
BLAKE2b-256 checksum
How to use checksums
952f50b8a0a4a1888450e9a0658fbf8b1406307907904c2dbb89e1d3b555bfe2
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.12.6

Security for plumb-xrpl 0.1.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files No provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page