The Scaling Problem
Blockchain transactions aren’t free. Even on efficient networks like Polygon, each transaction has a gas cost. If every file required its own individual blockchain transaction, the economics break:
- 1 file = 1 transaction = manageable
- 100 files = 100 transactions = expensive
- 10,000 files = 10,000 transactions = infeasible
For a timestamping service used by thousands of users, writing individual transactions per file would make the service unaffordable. The cost structure has to be fundamentally different.
The Merkle Tree Solution
Merkle trees solve this by compressing any number of file hashes into a single 32-byte root that can be stored in one blockchain transaction.
How the tree is built
Starting with a set of file hashes (the “leaves”):
File A hash: a1b2c3...
File B hash: d4e5f6...
File C hash: 7a8b9c...
File D hash: 0d1e2f...
The tree is constructed bottom-up:
Root: hash(AB + CD)
/ \
AB: hash(A + B) CD: hash(C + D)
/ \ / \
A B C D
Each non-leaf node is the hash of its two children concatenated. The root is a single 32-byte value that uniquely represents the entire set of files.
Why this works
Collision resistance: Finding two different sets of files that produce the same Merkle root is computationally infeasible (it would require breaking SHA-256). If the root matches, the files are the exact same set.
Individual verifiability: To prove File C is in the tree, you only need:
- File C’s hash
- File D’s hash (C’s sibling)
- The AB hash (the other branch)
Combine: hash(C + D) = CD, then hash(AB + CD) = Root. If this matches the root on the blockchain, File C is proven.
This “Merkle proof” is only 2 hashes for a 4-file tree. For a 1,000-file tree, it’s just 10 hashes. For a million-file tree, only 20 hashes. The proof size grows logarithmically — absurdly efficient.
TimeProof’s Anchoring Architecture
Instant timestamps
When you request an instant timestamp, your file hash is included in the next available Merkle batch. On a busy system, this batch may include other users’ files. On a quiet system, it might be just yours. Either way:
- Your file hash becomes a leaf in the Merkle tree
- The tree is constructed and the root computed
- The root is written to the TimeProofAnchor smart contract on Polygon
- Your certificate includes your file hash, Merkle proof, and transaction ID
The entire process completes in seconds.
Scheduled timestamps
Scheduled timestamps accumulate in a batch over a defined window. When the window closes:
- All accumulated file hashes are assembled
- A Merkle tree is constructed from all hashes
- The root is anchored to Polygon
- Each user receives their individual Merkle proof
Scheduled batching is why the lower-cost path only uses 1 credit per file instead of the 2-credit instant path — more files per transaction means lower per-file cost.
Verification Paths
There are three ways to verify a timestamped file:
Path 1: TimeProof Verification Page
Upload your file (or paste its hash) on TimeProof’s verification page. The system computes the hash, looks up the corresponding Merkle proof, and verifies it against the on-chain root. Simplest method — requires no technical knowledge.
Path 2: Certificate + Polygonscan
Your timestamp certificate contains all the information needed:
- Open the transaction hash on Polygonscan
- Decode the smart contract call data to extract the Merkle root
- Use your Merkle proof to verify your hash connects to that root Requires some technical knowledge but is fully independent of TimeProof.
Path 3: Programmatic Verification
Using the Merkle proof from your certificate, write a simple verification script:
// Pseudocode
const fileHash = sha256(yourFile);
let current = fileHash;
for (const {hash, position} of merkleProof) {
current = position === 'left'
? sha256(hash + current)
: sha256(current + hash);
}
assert(current === onChainMerkleRoot);
This is the strongest verification — you trust nothing except the cryptographic math and the blockchain state.
Batch Privacy
A critical property: files in the same batch learn nothing about each other.
Your Merkle proof contains sibling hashes (intermediate nodes you need for verification), but these are hashes of hashes — they reveal nothing about the underlying files. You can’t reverse a SHA-256 hash to determine its input, so the intermediate hashes in your proof are completely opaque.
This means:
- User A can’t determine what files User B timestamped
- The batch structure doesn’t reveal the number of files (padding can be used)
- Your Merkle proof is a self-contained piece of evidence about your file alone
Why Not Just Hash All Files Together?
A simpler approach: concatenate all file hashes and hash the result. One hash, one transaction. Why use a tree?
Because concatenated hashing destroys individual verifiability. To verify any single file, you’d need all other file hashes in the batch — which compromises privacy and requires storing the entire batch indefinitely.
Merkle trees preserve both efficiency (one transaction) and individual verifiability (compact, per-file proofs).
Performance Characteristics
| Metric | Value |
|---|---|
| Proof size | O(log n) — ~10 hashes for 1,000 files |
| Verification time | Under 1ms for any batch size |
| Tree construction | O(n log n) — milliseconds for typical batches |
| On-chain storage | 32 bytes per batch (one Merkle root) |
The system scales gracefully. Whether a batch contains 10 files or 10,000 files, the on-chain cost is identical (one transaction with one 32-byte root), and individual verification remains nearly instant.