Validation Ledger & Transaction Store 
To generate xPOPs
Watcher that connects to multiple nodes & listens for validation messages, closed ledgers & transactions, and stores all of it in an organised file system data structure for xPOP generation. Why? Pretty important: XRPL validation messages are ephemeral, and if no one has them the burn can't be turned into a mint.
Based on the work by @RichardAH: https://github.com/RichardAH/xpop-generator
Consuming data from this service
You can easily fetch ready to use xPOP, or even generate them from source data possibly scattered across instances like this instance using the https://www.npmjs.com/package/xpop npm package.
Run (Docker)
Docker Compose
To run this service & nginx in two separate preconfigured containers:
- Webserver (nginx)
- Cleaner (cleans up xPOPs)
- xPOP collector for XRPL Mainnet (listens on
PORTandSSLPORT) - xPOP collector for XRPL Testnet (listens on
PORT_TESTNETandSSLPORT_TESTNET)
Simply run:
PORT=80 SSLPORT=443 PORT_TESTNET=81 SSLPORT_TESTNET=444 TELEMETRY=YES URL_PREFIX=https://localhost docker-compose up --build
Run with -d flag to run 'detached', in the background.
Unless specified otherwise (with environment variables) a connection to XRPL Testnet will be made.
The above command explained:
TELEMETRY=YESsends theURL_PREFIXand request hostname to XRPL Labs to build an xPOP serving directory. Default:NO, change toYESto enable (much appreciated) if you want to run this service publicly for others to fetch xPOPs from (really really appreciate it 💕!)URL_PREFIXspecifies a public URL (if applicable) you are serving your xPOPs on (mapped to this service)--buildat the end makes sure you rebuild your service container, to make sure you're running the latest version of this code
Warning (in case of docker-compose errors)
If you're running an older version of docker-compose, you may need to update your compose binary:
curl -SL https://github.com/docker/compose/releases/download/v2.23.0/docker-compose-linux-x86_64 -o $(which docker-compose)
Updates
To install & run an updated version, update the repository (git pull), take the existing containers down (docker-compose down) & then run the last docker-compose up command (with your environment variables, etc.) with the --build flag at the end. This rebuilds the containers and replaces the existing ones with the new version.
Cleanup
The docker-compose machines also contain a clean up machine.
The clean up will clear the pre-generated xPOP HEX files from the /xpop folder when
older than 60 minutes [default=60] - which can be changed with the TTL_MINUTES_PREGEN_XPOP environment variable.
The clean up will clear folders with all source files for xPOP generation from the /store/{networkid}
subfolders older than 30 days [default=30] (one month) - which can be changed with the TTL_DAYS_XPOP_SOURCE_FILES
environment variable.
Expect a significant IO impact during cleanup if a lot of existing history is stored. Clean up will run
on docker-compose up and every 60 minutes [default=60] thereafter - which can be changed with the
TTL_MINUTES_CLEANUP_INTERVAL environment variable.
Endpoints
You will get a container running at port 3000 (unless configured differently), with the following routes:
http://{host}:3000» Web Browser: homepage with some stats and linkshttp://{host}:3000» WebSocket: live events on xPOP generatedhttp://{host}:3000/blob» WebSocket: live events on xPOP generated + HEX XPOPhttp://{host}:3000/blob/{account}» WebSocket: live events on xPOP generated + HEX XPOP for specific accounthttp://{host}:3000/xpop/{hash}» HEX encoded xPOPhttp://{host}:3000/{networkid}/{...}» Web Browser Dirlisting & xPOP source fileshttp://{host}:3000/{networkid}/{...}» Called withContent-Type: application/json? JSON dirlisting
Single Docker Container
Run a container with HTTP exposed, for XRPL testnet, auto-remove container after running & interactive (allow for CTRL+C to kill).
Docker Hub: https://hub.docker.com/r/wietsewind/xpop
docker rmi wietsewind/xpop:latest # Clean existing image, or build locally
docker run \
--name xpop \
--rm -i \
-v $(pwd)/store:/usr/src/app/store
-p 3000:3000 \
-e EVENT_SOCKET_PORT=3000 \
-e URL_PREFIX=http://localhost:3000 \
-e NETWORKID=1 \
-e UNLURL=https://vl.altnet.rippletest.net \
-e UNLKEY=ED264807102805220DA0F312E71FC2C69E1552C9C5790F6C25E3729DEB573D5860 \
-e NODES=wss://testnet.xrpl-labs.com,wss://s.altnet.rippletest.net:51233 \
-e FIELDSREQUIRED=Fee,Account,OperationLimit \
-e NOVALIDATIONLOG=true \
-e NOELIGIBLEFULLTXLOG=true \
wietsewind/xpop:latest
Output
Folder
This tool creates a folder structore in the ./store directory, where it creates sub-directories like this:
store / {networkid} / {ledgerpath///} /
The ledgerpath is the ledger index chunked from right to left in sections of three digits, making sure there
are max. 1000 subfolders per level. This allows for easy dir listing & cleaning.
So e.g. for NetworkId 21338, ledger index 82906790, the path would be:
store/0/82/906/790
This way entire chunks of stored ledger scan be easily fetched or pruned by removing a directory recursively.
Folder contents
Every folder will contain the following files:
ledger_binary_transactions.jsonwith the binary info for aledgercommand, including transactions. The transactions contain themeta,tx_bloband a computedtx_idledger_info.jsonwith the regularledgercommand outputvl.jsonwith the UNL validator list (signature checked)validation_{signing pubkey}.json, e.g.validation_n9McDrz9tPujrQK3vMXJXzuEJv1B8UG3opfZEsFA8t6QxdZh1H6m.jsontx_{tx hash}.json, e.g.tx_FFDEADBEEF64F423CB4B317370F9B40645BA9D5646B47837FDC74B8DCAFEBABE.jsonxpop_{tx hash}.jsonfor the generated xPOP (in JSON format)
Helper scripts
npm run serve to launch a webserver for the store dir
npm run dev to launch (verbose)
npm run xpopgen to launch, less verbose
Webserver
This script also runs a webserver is the env. var is provided for the TCP port & URL Prefix where the app will run:
EVENT_SOCKET_PORT="3000"
URL_PREFIX="https://4849bf891e06.ngrok.app"
WebSocket
You can listen for xPOP publish events (live, so you don't hve to poll).
By default you will get all xPOP events. If you want to filter on a specific address, provide
the r-address in the URL path. If you also want to receive the xPOP Blob, also provide /blob in the URL path.
E.g. /blob/rwietsevLFg8XSmG3bEZzFein1g8RBqWDZ would listen for xPOPs for account rwietsevLFg8XSmG3bEZzFein1g8RBqWDZ
and serve the (hex encoded) xPOP in the xpop.blob property.
HTTP File Browser
On the HTTP port a file listing is also provided & xPOPs can be downloaded at /xpop/{tx hash}.
Original source files to reconstruct the xPOP locally can be downloaded at /{networkid}/.
When visiting the /{networkid}/ route, you'll be presented a dirlisting. When visiting with the HTTP
header Accept: application/json you will be presented a dirlisting & file browser in JSON format
for automation.
This file browser is for development and test purposes only, for production, put a static webserver in front of this application & reverse proxy only the WebSocket (HTTP Upgrade) server.
Monitoring
A health check endpoint lives on /health, and returns e.g.:
{
"uptime": 44023,
"lastLedger": 41800244,
"lastLedgerTx": 41800238,
"txCount": 276
}
Tools (Utils)
This package provides some internal helper functions:
- NPM (backend): https://www.npmjs.com/package/xpopgen
- CDN (browser): https://cdn.jsdelivr.net/npm/xpopgen/npm/browser.min.js
JS (backend)
import { ledgerIndexToFolders } from 'xpop-utils/npm/utils.mjs'
console.log(ledgerIndexToFolders(123456789))
JS (browser)
<script src="https://cdn.jsdelivr.net/npm/xpopgen/npm/browser.min.js"></script>
<script>
const { ledgerIndexToFolders } = require('xpop-utils')
console.log(ledgerIndexToFolders(123456789))
</script>
Triggering transactions
Sample to generate & submit a B2M transaction on Testnet, resulting in an xPOP:
Consuming this backend from the browser
Sample to use this script in the browser: