End-to-end, one scripthash
Here's the complete path a single query takes, stitched together. The happy path has up to three PIR rounds — INDEX, CHUNK, MERKLE — each padded to fixed size. Every round is padded even when there's nothing to return.
- Tag & groups. The client hashes the scripthash into a short 8-byte tag and derives its 3 candidate PBC groups; a deterministic rule picks one as the assigned group.
- INDEX round. The client generates 75 × 2 DPF keys — one real (keyed to the item's 2 in-group cuckoo bin positions) and 74 random dummies — and ships them to the two servers.
- Decode. Client XORs the two servers' responses per
group, scans the returned bin for the matching tag, and reads
(start_chunk_id, num_chunks). - CHUNK round(s). If
num_chunks > 0, the client issues CHUNK queries atK_CHUNK = 80per round, potentially spreading many chunks across multiple rounds. - MERKLE round(s). For each bin returned (found or
empty), the client issues padded sibling-PIR queries — one per tree
level — to verify the bin against the published Merkle root. For a
"not found" answer, this must cover both
INDEX_CUCKOO_NUM_HASHES = 2in-group bins. - Result. Only now does the client report UTXOs to the rest of the wallet. Every PIR-returned byte has been Merkle-verified; every round looked the same on the wire regardless of whether the address had one UTXO or zero.