Docs
Verify a Proof Record yourself
A Pudding Proof Record is verifiable end-to-end without trusting Pudding. Every record's fingerprint is placed as a leaf in a Merkle tree; only the tree's root is timestamped by OpenTimestamps and, ultimately, the Bitcoin blockchain. This page tells you exactly how to walk that chain of hashes for any record.
What you need from the record page
- The original file (or the source URL).
- The record's SHA-256 fingerprint.
- The record's inclusion path (list of sibling hashes).
- The batch Merkle root.
- The batch
.otsproof file.
The three steps
- Hash the file. Compute
SHA-256of the file bytes and confirm it equals the record's fingerprint. - Walk the inclusion path. Start with the 32-byte fingerprint. For each sibling in the path, concatenate the raw bytes in the recorded order — sibling on the left means
sha256(sibling ‖ running), sibling on the right meanssha256(running ‖ sibling). The final result must equal the batch Merkle root. - Verify the batch
.otsproof. Download the batch's.otsfile from the record page and runots verify <file>.otsagainst a Bitcoin node (or a trusted block explorer). A successful verification proves the Merkle root existed at the anchored Bitcoin block height — and, combined with step 2, that your file existed then too.
Single-record batches
When a batch contains only one record, that record's fingerprint is the batch's Merkle root — there are no siblings to walk. The record page will say so explicitly and won't show an inclusion path. In that case, skip step 2: after hashing the file (step 1), confirm the fingerprint equals the Merkle root shown on the record page, then go straight to step 3 and ots verify the batch .ots against Bitcoin. The proof timestamps the fingerprint directly.
Reference walker (Python)
import hashlib, json
# 1. paste the record's inclusion path JSON
proof = json.loads("""{
"leaf": "<record fingerprint hex>",
"path": [
{ "hash": "<sibling hex>", "position": "left" },
{ "hash": "<sibling hex>", "position": "right" }
]
}""")
merkle_root_hex = "<batch merkle root hex>"
h = bytes.fromhex(proof["leaf"])
for step in proof["path"]:
s = bytes.fromhex(step["hash"])
pair = s + h if step["position"] == "left" else h + s
h = hashlib.sha256(pair).digest()
assert h.hex() == merkle_root_hex, "inclusion path does not reach the root"
print("root matches — now run: ots verify <batch>.ots")What each step proves
- Step 1 proves the file you have is the exact bytes that were registered.
- Step 2 proves that fingerprint was included in the batch whose root was timestamped.
- Step 3 proves that root existed no later than a specific Bitcoin block.
Questions? Get in touch.
