Independently verify Qalypto market data exports. Offline, no account, no dependency on any Qalypto system.
Qalypto seals every UTC day of market data into a single Merkle super-root, timestamps it via RFC 3161 and anchors it in Bitcoin through OpenTimestamps. This tool recomputes those proofs from the data you hold and compares them against the published roots. If even one field of one row was changed after sealing, the recomputed root will not match the anchored one.
Two implementations, identical results:
| File | Runtime | Dependencies |
|---|---|---|
qalypto_verify.py |
Python 3.8+ | standard library only |
qalypto-verify.js |
Node.js 16+ | standard library only |
The tst and ots modes additionally shell out to openssl and the
OpenTimestamps client
(pip install opentimestamps-client). These are only needed to check the
timestamp anchors, not the data itself.
- Registry entries: one JSON document per sealed day (super-root, all cell roots with row counts, anchor references). Published append-only.
- Anchors: the RFC 3161 token and the OpenTimestamps proof of a day,
available per day via
GET /proof/anchorson the Qalypto compliance API and inside every case dossier (proofs/anchors-<day>-r<rev>.json). - Inclusion proofs: a Merkle path for one single row, from
POST /proof/inclusion.
Verify a data export against a published cell root:
python3 qalypto_verify.py cell --rows export.tsv --root <hex>
Verify a day's super-root against its registry entry:
python3 qalypto_verify.py day --registry 2026-07-05-r0.json
Verify a single row's membership, leaf to cell root to super-root:
python3 qalypto_verify.py inclusion --proof proof.json
python3 qalypto_verify.py inclusion --proof proof.json --registry 2026-07-05-r0.json
Verify the RFC 3161 timestamp token (requires the TSA's CA chain):
python3 qalypto_verify.py tst --registry anchors.json --cafile tsa-chain.pem
Verify the Bitcoin anchor:
python3 qalypto_verify.py ots --registry anchors.json
With a local Bitcoin node this check is fully trustless. Without one,
ots info names the attested Bitcoin block so any block explorer can
confirm the header.
Verify raw archive files against a day's raw cell (wire-to-row chain):
python3 qalypto_verify.py raw --dir ./raw/binance/2026-07-07 --root <hex>
Exit codes: 0 proof valid, 1 mismatch, 2 usage or input error.
Everything below is deliberately simple enough to reimplement from this description alone.
- Canonical rows. Each cell (one venue, one channel, one UTC day) is
rendered as TSV in a fixed column order. Lines are compared and sorted
bytewise (
LC_ALL=C), with no trailing newline per line. - Merkle tree (RFC 6962 construction). Leaf hash is
SHA-256(0x00 || line), interior node hash isSHA-256(0x01 || left || right). The tree over the sorted lines yields the 32-byte cell root. An empty cell hashes toSHA-256(""). - Super-root. For every cell of the day, one line
venue|channel|cell_root_hex|row_countis built. These lines are bytewise-sorted and folded with the same tree into the day's super-root. Raw-archive cells use the channel namerawand leaves of the form<sha256hex>\t<filename>. - Anchors. The super-root is timestamped via RFC 3161 and submitted to
OpenTimestamps calendar servers, which commit it into a Bitcoin
transaction. The
.otsproof carries the path from the super-root to the attested block header. - Inclusion proofs. A sibling path (
sideisLorR,hashis the sibling) folds a single leaf up to the cell root; a second path folds the cell'svenue|channel|root|row_countline up to the super-root.
Sealed days are append-only. A correction produces a new revision with a recorded reason; the original entry remains.
MIT. See LICENSE.