Verify Blockchain File Proofs with Python's verify-proof Package
Your application receives a file and a JSON proof claiming the file was timestamped on a blockchain before a critical event occurred. How do you verify that claim programmatically? The verify-proof Python package handles this exact scenario, providing offline verification of blockchain-anchored file hashes without network dependencies.

Installing and Basic Usage
The verify-proof package is available on PyPI and works entirely offline. It doesn't make network calls to validate blockchain transactions — instead, it performs cryptographic verification of the proof structure itself.
pip install verify-proof
The package provides two main functions: hash_file() for generating SHA-256 hashes and verify_proof() for validating proof documents.
from verify_proof import hash_file, verify_proof
# Generate SHA-256 hash of a file
file_hash = hash_file("document.pdf")
print(f"SHA-256: {file_hash}")
# Output: SHA-256: a1b2c3d4e5f6789...
The hash_file() function returns a 64-character hexadecimal string representing the SHA-256 digest. This is the same hash that gets anchored to blockchain networks.
Hash and Proof Verification Workflow
The verification process involves two steps: confirming the file matches its claimed hash, then validating the proof document structure.
import json
from verify_proof import hash_file, verify_proof
def verify_file_and_proof(file_path, proof_path):
# Step 1: Hash the file
calculated_hash = hash_file(file_path)
# Step 2: Load the proof document
with open(proof_path, 'r') as f:
proof_data = json.load(f)
# Step 3: Verify the proof
result = verify_proof(calculated_hash, proof_data)
return calculated_hash, result
# Example usage
file_hash, verification_result = verify_file_and_proof(
"contract.pdf",
"contract_proof.json"
)
print(f"File hash: {file_hash}")
print(f"Verification result: {verification_result}")
The verify_proof() function returns a dictionary with several fields:
verified: Boolean indicating whether verification passedblockchain: The blockchain network used (e.g., "polygon", "bitcoin")tx_id: Transaction ID on the blockchainanchored_at: Timestamp when the hash was anchoredservice: The anchoring service that created the proof
Complete Verification Script
Here's a production-ready script that handles multiple files and includes proper error handling:
#!/usr/bin/env python3
import json
import sys
import os
from pathlib import Path
from verify_proof import hash_file, verify_proof
def verify_file_proof(file_path, proof_path):
"""
Verify a file against its blockchain proof.
Returns tuple: (success: bool, details: dict)
"""
try:
# Check if files exist
if not os.path.exists(file_path):
return False, {"error": f"File not found: {file_path}"}
if not os.path.exists(proof_path):
return False, {"error": f"Proof not found: {proof_path}"}
# Generate file hash
file_hash = hash_file(file_path)
# Load proof document
with open(proof_path, 'r') as f:
proof_data = json.load(f)
# Verify proof
result = verify_proof(file_hash, proof_data)
if result['verified']:
return True, {
"file_hash": file_hash,
"blockchain": result['blockchain'],
"tx_id": result['tx_id'],
"anchored_at": result['anchored_at'],
"service": result['service']
}
else:
return False, {
"file_hash": file_hash,
"error": "Proof verification failed",
"proof_data": result
}
except json.JSONDecodeError:
return False, {"error": "Invalid JSON in proof file"}
except Exception as e:
return False, {"error": f"Verification failed: {str(e)}"}
def main():
if len(sys.argv) != 3:
print("Usage: python verify_script.py <file_path> <proof_path>")
sys.exit(1)
file_path = sys.argv[1]
proof_path = sys.argv[2]
success, details = verify_file_proof(file_path, proof_path)
if success:
print("✅ VERIFICATION PASSED")
print(f"File: {file_path}")
print(f"Hash: {details['file_hash']}")
print(f"Blockchain: {details['blockchain']}")
print(f"Transaction: {details['tx_id']}")
print(f"Anchored: {details['anchored_at']}")
print(f"Service: {details['service']}")
else:
print("❌ VERIFICATION FAILED")
print(f"File: {file_path}")
print(f"Error: {details['error']}")
sys.exit(1)
if __name__ == "__main__":
main()
Run this script with any file and its corresponding proof:
python verify_script.py document.pdf document_proof.json
Batch Processing and Error Handling
For applications processing multiple files, you can extend the verification logic to handle batches:
import os
from concurrent.futures import ThreadPoolExecutor
from verify_proof import hash_file, verify_proof
import json
def verify_single_item(item):
"""Worker function for parallel verification."""
file_path, proof_path = item
try:
file_hash = hash_file(file_path)
with open(proof_path, 'r') as f:
proof_data = json.load(f)
result = verify_proof(file_hash, proof_data)
return {
"file": file_path,
"verified": result['verified'],
"hash": file_hash,
"details": result
}
except Exception as e:
return {
"file": file_path,
"verified": False,
"error": str(e)
}
def batch_verify(file_proof_pairs, max_workers=4):
"""Verify multiple file/proof pairs in parallel."""
with ThreadPoolExecutor(max_workers=max_workers) as executor:
results = list(executor.map(verify_single_item, file_proof_pairs))
return results
# Example usage
pairs = [
("file1.pdf", "file1_proof.json"),
("file2.jpg", "file2_proof.json"),
("file3.docx", "file3_proof.json")
]
verification_results = batch_verify(pairs)
for result in verification_results:
status = "✅" if result['verified'] else "❌"
print(f"{status} {result['file']}: {result.get('hash', 'ERROR')}")
Note that the ThreadPoolExecutor approach works because we're using a proper function, not a lambda. The verify-proof package performs only local cryptographic operations, so parallel processing is safe and efficient.
Understanding What Gets Verified
It's important to understand exactly what the verify-proof package validates. The package performs structural verification of the proof document — it confirms that required fields are present and the proof format is correct. However, it doesn't make network calls to validate that the transaction actually exists on the blockchain.
# This is what verify_proof() checks:
# 1. Does the calculated hash match the hash in the proof?
# 2. Is the proof structure valid JSON with required fields?
# 3. Is the tx_id field non-empty?
# 4. Are timestamps properly formatted?
# This is what it does NOT check:
# - Whether the transaction exists on the blockchain
# - Whether the transaction is confirmed
# - Whether the anchoring service is legitimate
For complete verification in a production system, you would combine verify-proof with blockchain explorer APIs to confirm the transaction exists on-chain.
Integration Patterns
The verify-proof package fits well into existing Python applications. Here are common integration patterns:
# Flask API endpoint
from flask import Flask, request, jsonify
from verify_proof import hash_file, verify_proof
import tempfile
import os
app = Flask(__name__)
@app.route('/verify', methods=['POST'])
def verify_endpoint():
uploaded_file = request.files['file']
proof_data = request.json.get('proof')
# Save uploaded file temporarily
with tempfile.NamedTemporaryFile(delete=False) as tmp:
uploaded_file.save(tmp.name)
file_hash = hash_file(tmp.name)
os.unlink(tmp.name)
# Verify against proof
result = verify_proof(file_hash, proof_data)
return jsonify({
"verified": result['verified'],
"hash": file_hash,
"blockchain": result.get('blockchain'),
"tx_id": result.get('tx_id')
})
Next Steps
The verify-proof package provides the foundation for blockchain timestamp verification in Python applications. For complete verification workflows, you'll want to:
Combine it with blockchain explorer APIs for on-chain validation
Implement proper logging and audit trails for verification attempts
Add database storage for verification results and file metadata
Consider caching hash calculations for large files
The source code is available on GitHub at https://github.com/Fulcrum-Enterprises/verify-proof, and the package supports all major Python versions. For CLI usage, the package also provides command-line tools for quick verification tasks.


