XChain Platform Decoder — Operations
Prerequisites
- Node.js >= 18
- MariaDB server
- A running coin node (bitcoind, litecoind, or dogecoind) with JSON-RPC enabled
Running the Decoder
npm run api
# or directly:
node ./src/api.js
On startup, the decoder:
- Loads environment variables from
.env - Starts the Express JSON-RPC API server on
DECODER_API_PORT - Validates the database name (alphanumeric + underscores only)
- Creates the database if it doesn’t exist
- Creates all 8 tables if they don’t exist
- Waits for the coin node to reach 99% verification progress
- Begins parsing from the configured start block (or last parsed block + 1)
Docker
The decoder includes a docker-compose.yml for containerized deployment:
docker-compose up --build
The Dockerfile copies the source into the container and runs npm run api. Environment variables can be passed via docker-compose.yml or a mounted .env file.
Stopping
The decoder handles graceful shutdown via SIGTERM and SIGINT signals:
- Sets the
stopFlagto true - The main polling loop exits after the current iteration completes
- Mempool updates are cancelled
- All database connections are released
In Docker, docker stop sends SIGTERM, triggering the graceful shutdown path.
API
The decoder exposes a minimal JSON-RPC API for health monitoring:
ping
Basic health check.
Request:
{
"jsonrpc": "2.0",
"method": "ping",
"id": 1
}
Response:
{
"jsonrpc": "2.0",
"result": { "status": "success" },
"id": 1
}
health
Detailed health status including decoder state.
Request:
{
"jsonrpc": "2.0",
"method": "health",
"id": 1
}
Response:
{
"jsonrpc": "2.0",
"result": {
"status": "ok",
"decoderRunning": true,
"synced": true,
"error": null
},
"id": 1
}
| Field | Type | Description |
|---|---|---|
status |
string |
"ok" when decoder is running, "error" otherwise |
decoderRunning |
boolean |
Whether the decoder process is alive |
synced |
boolean |
Whether the decoder is within 3 blocks of the chain tip |
error |
string|null |
Error message if the decoder crashed |
Security
The API includes:
- Helmet — sets secure HTTP headers
- CORS — enabled for cross-origin requests
- Rate limiting — 100 requests per minute per IP
- Body size limit — 100kb maximum request body
Reorg Handling
Chain reorganizations are detected automatically during the block polling loop:
- Before writing a new block, the decoder compares the
previous_block_hashfrom the coin node with the hash stored in the database for the previous block - If they don’t match, a reorganization has occurred
- The decoder deletes the invalid block (and all its transactions) from the database
- A REORG event is recorded in the
eventstable with the affected block height - The polling loop resumes, re-parsing from the corrected chain
The indexer monitors the decoder’s blocks table and independently handles reorg rollback of its own state.
Mempool Tracking
Mempool tracking activates when the decoder is synced (within 3 blocks of the tip):
- Poll interval: every 60 seconds
- Batch size: 1000 transactions per RPC batch
- Comparison method: binary search against sorted txid lists
- Cleanup: stale mempool entries (no longer in node’s mempool) are deleted each cycle
Mempool tracking pauses if the decoder falls more than 3 blocks behind the tip, and resumes automatically when caught up.
Connection Resilience
Coin Node (JSON-RPC)
- All RPC calls have a 5-second HTTP timeout
- Failed calls are retried up to 10 times with 500ms backoff
- HTTP 429 (rate limited) triggers a longer 5-second backoff
- Connection aborts (ECONNABORTED) are retried with timeout warnings logged
MariaDB
- Connection pool of 10 concurrent connections
- 30-second timeout for acquiring a connection from the pool
- Transaction locking via a queue-based mutex prevents concurrent block commits and mempool updates from interleaving
- On connection failure, errors are logged with
e.code(not full error object, to avoid credential leakage)
Troubleshooting
Decoder won’t start parsing
- Verify the coin node is accessible at
NODE_URL:NODE_PORT - Check that
verificationprogressis >= 0.99 (runbitcoin-cli getblockchaininfo) - If using Dogecoin, ensure
AUX_POWis set in the environment
Decoder is stuck / not advancing
- Check coin node connectivity (the decoder logs RPC timeout warnings)
- Verify MariaDB is accessible and the connection pool isn’t exhausted
- Check for reorg loops — if the chain is continuously reorganizing, the decoder may repeatedly delete and re-parse the same block
Database name rejected
- Database names must match
/^[A-Za-z0-9_]+$/— no spaces, backticks, or special characters - Follow the naming convention:
XChain_{CHAIN}_{NETWORK}_Decoder
Mempool not updating
- Mempool tracking only runs when the decoder is synced (within 3 blocks of tip)
- Check that the 60-second interval hasn’t been interrupted by a long block parse
- Verify
getRawMempoolRPC is accessible
High memory usage
- Large mempool batches (>10,000 unconfirmed txs) can cause temporary memory spikes during the batch-fetch phase
- The 1000-tx chunk size limits peak memory per batch
Monitoring
The health API endpoint provides the key monitoring signals:
| Condition | status |
decoderRunning |
synced |
|---|---|---|---|
| Normal operation, caught up | ok |
true |
true |
| Normal operation, catching up | ok |
true |
false |
| Decoder crashed | error |
false |
false |
Monitor the events table for REORG events, which indicate chain instability.
Copyright © 2025–2026 Dankest, LLC
Based on XChain Platform by Dankest, LLC – https://dankest.llc
Licensed under the GNU Affero General Public License v3.0 (AGPL-3.0-or-later) with a commercial license available for proprietary use.
You may use, modify, and distribute this material under the terms of the License. See LICENSE and NOTICE for full terms. See the licensing overview.