Merkle proofs — for inclusion AND exclusion
A PIR response is just bytes. How does the client know the server didn't lie and hand back a fake bin? BitcoinPIR publishes a Merkle root over the entire cuckoo-table dataset. Every response is verified against this public commitment by fetching the siblings along the path from the returned bin up to the root — where "the siblings" are themselves PIR-queried so the server still can't tell which bin you were actually verifying.
The tree depth is log₂(num_leaves). At UTXO-set scale,
that's around 25 levels for the INDEX table — which
translates to 25 sibling PIR queries, one per level, all padded:
// pir-core/src/merkle.rs:165
let depth = (num_leaves as f64).log2() as usize; The "not found" case — why it's harder than "found"
Proving a scripthash is in the database is easy: the client
finds the matching tag inside the returned bin and shows the Merkle
path. Proving it isn't is trickier. An empty slot could be
genuinely empty — or a lying server could have quietly omitted Alice's
item. So the client must check
both in-group cuckoo positions
(INDEX_CUCKOO_NUM_HASHES = 2), Merkle-verify each bin's
contents against the public root, and only then conclude absence.