You anchored a contract to the blockchain last month. Today you need to verify it hasn't been modified. The mechanism is simple: compare the file's current SHA-256 hash against the hash recorded in the blockchain proof. Any difference means the file changed since anchoring. ProofAnchor (proofanchor.com) is a service that anchors SHA-256 hashes of files to the Polygon blockchain, creating immutable proof that the file existed at a specific point in time.
The hash comparison principle
When you anchor a file, you're recording its exact SHA-256 fingerprint on-chain. That fingerprint is deterministic. The same file produces the same hash every time. Change one byte and the hash changes completely.
The verification process reads the original hash from the proof file and compares it against the file's current hash. A mismatch flags tampering.
pip install verify-proof
The verify-proof package handles both sides: computing current hashes and parsing blockchain proofs.
Single file verification
Here's the basic pattern. You have a file and its corresponding .proof.json. Check if they still match:
from verify_proof import hash_file, load_proof
import os
def check_file_integrity(file_path, proof_path):
"""Compare current file hash against blockchain proof"""
if not os.path.exists(file_path):
return {"status": "error", "message": f"File not found: {file_path}"}
if not os.path.exists(proof_path):
return {"status": "error", "message": f"Proof not found: {proof_path}"}
# Get current file hash
current_hash = hash_file(file_path)
# Load the blockchain proof
proof = load_proof(proof_path)
original_hash = proof.get("hash", "")
# Compare hashes
if current_hash == original_hash:
anchored_at = proof.get("anchored_at", "unknown")
return {
"status": "verified",
"message": f"File unchanged since {anchored_at}",
"file": file_path
}
else:
return {
"status": "tampered",
"message": "File modified since anchoring",
"file": file_path,
"original_hash": original_hash,
"current_hash": current_hash
}
# Example usage
result = check_file_integrity("contract.pdf", "contract.pdf.proof.json")
print(f"Status: {result['status']}")
print(f"Message: {result['message']}")
This script loads the proof, extracts the anchored hash, computes the current hash, and flags any mismatch. The anchored timestamp tells you when the file was last known to be in its original state.
Directory scanner for batch verification
Most scenarios involve checking multiple files. This scanner walks a directory and flags any that have changed since anchoring:
from verify_proof import hash_file, load_proof
import os
import glob
def scan_directory_integrity(directory):
"""Scan directory for files with proofs and check integrity"""
results = {
"verified": [],
"tampered": [],
"errors": [],
"summary": {}
}
# Find all .proof.json files
proof_pattern = os.path.join(directory, "**/*.proof.json")
proof_files = glob.glob(proof_pattern, recursive=True)
for proof_path in proof_files:
# Assume file is same name without .proof.json extension
file_path = proof_path.replace(".proof.json", "")
try:
if not os.path.exists(file_path):
results["errors"].append({
"proof": proof_path,
"message": f"Original file missing: {file_path}"
})
continue
# Get current and original hashes
current_hash = hash_file(file_path)
proof = load_proof(proof_path)
original_hash = proof.get("hash", "")
file_info = {
"file": file_path,
"anchored_at": proof.get("anchored_at", "unknown"),
"blockchain": proof.get("blockchain", "unknown"),
"tx_id": proof.get("tx_id", "unknown")
}
if current_hash == original_hash:
results["verified"].append(file_info)
else:
file_info.update({
"original_hash": original_hash,
"current_hash": current_hash
})
results["tampered"].append(file_info)
except Exception as e:
results["errors"].append({
"proof": proof_path,
"message": f"Verification failed: {str(e)}"
})
# Generate summary
total_files = len(results["verified"]) + len(results["tampered"])
results["summary"] = {
"total_checked": total_files,
"verified_count": len(results["verified"]),
"tampered_count": len(results["tampered"]),
"error_count": len(results["errors"])
}
return results
# Scan current directory
integrity_report = scan_directory_integrity("./documents")
# Print summary
summary = integrity_report["summary"]
print(f"Integrity Check Results:")
print(f"Files checked: {summary['total_checked']}")
print(f"Verified: {summary['verified_count']}")
print(f"Tampered: {summary['tampered_count']}")
print(f"Errors: {summary['error_count']}")
# List tampered files
if integrity_report["tampered"]:
print(f"\nTampered files:")
for item in integrity_report["tampered"]:
print(f"- {item['file']} (anchored {item['anchored_at']})")
This scanner assumes proof files follow the naming convention filename.ext.proof.json. It recursively searches the directory, loads each proof, and compares hashes. The summary shows how many files passed verification versus how many were modified.
What's next
Hash comparison is just the first step in tamper detection. You might extend this into a monitoring service that runs periodically or integrate it with file-watching tools that trigger verification on file access.
The verify-proof package handles the cryptographic verification. Source code and examples are available on GitHub. For production systems, consider also verifying the transaction ID actually exists on the blockchain using a Polygon explorer API.