← Duc V. Le
PoPETs 2020

A Tale of Two Trees: One Writes, and Other Reads

Optimized Oblivious Accesses to Bitcoin and other UTXO-based Blockchains

$T^3$ is a Bitcoin full node that answers light (SPV) clients from inside a trusted execution environment, over ORAM, so the node that hosts it never learns which addresses a client asked about. The trick: split every ORAM access in two — reads on one tree, evictions on another — and swap the trees at every new block.

Duc V. Le1, Lizzy Tengana Hurtado2, Adil Ahmad1, Mohsen Minaei1, Byoungyoung Lee3, Aniket Kate1

1Purdue University  ·  2National University of Colombia  ·  3Seoul National University
(2,3 most of this work was done while the author was at Purdue University)

In three numbers

What the split buys

All three come straight from the paper's evaluation: a tree of $N=2^{24}$ ORAM blocks of 544 bytes, over the 58,156,895 unspent outputs of Bitcoin blocks 0 to 551,731.

Read-once access · Circuit ORAM
0.64 ms

versus 1.70 ms for a standard ORAM access on the same tree. Path ORAM: 2.43 ms vs 5.89 ms. (Table 1)

Same read, four threads
0.35 ms

Read-once accesses never write, so threads share the tree without locks. Path ORAM drops from 2.43 ms to 0.73 ms. (Table 2)

Storage, both trees
≈26 GB

with Circuit ORAM ($Z=2$); ≈51 GB with Path ORAM ($Z=4$). Integrity needs only a ≈44 MB header chain. (§5.2)

All latencies were measured in SGX simulation mode on an Intel Xeon Silver 4116 (not SGX-enabled). The paper reports that a smaller tree run in hardware mode on an SGX-enabled Core i7 showed no noticeable difference from simulation.

The problem

Light clients leak their wallets

A phone cannot store the Bitcoin blockchain (230 GB unindexed as of October 2018), so it runs a simplified payment verification (SPV) client. Under BIP37 it loads its addresses into a Bloom filter and hands the filter to a full node, which returns matching transactions with Merkle proofs. Gervais et al. showed that a malicious node can recover several of the client's addresses from that filter with high probability, and many more once it sees two filters from the same client.

Cryptographic private information retrieval hides the query but does not scale to millions of users. ORAM inside a TEE is fast enough, but plain tree-based ORAM is sequential: every access reads a path and rewrites it (eviction), so two clients cannot be served at once. And a TEE like Intel SGX has little protected memory, so the position map has to be recursive.

$T^3$ starts from two observations. First, each tree-based ORAM access is a read-path followed by an eviction, and the two can happen on different copies of the tree. Second, Bitcoin produces a block roughly every 10 minutes, and a rational SPV client has no reason to ask for the same outputs twice before the next block. So one tree only serves reads, in parallel and without write-back. A second tree absorbs every eviction and every block update. At each new block the updated tree becomes the new read tree.

Interactive · Figures 1, 4 and 5 of the paper

Follow one query through both trees

A client asks for the outputs of one address, A. Step through the read protocol (blue) and then a new Bitcoin block arriving (amber). Turn on the irrational client to see the one case the design gives up, and how long it lasts.

T3 components: client, managing, reading and writing enclaves, read-once tree and original tree
A's leaf · read tree
5
A's leaf · original tree
5
Client reads
serving
Paths seen this interval
—
Access-pattern leakage
✓ none

The trees here are toy trees of 8 leaves so the paths are visible; the evaluated trees hold $2^{20}$ to $2^{24}$ blocks. Leaf numbers are illustrative. In the prototype all enclaves are threads of one enclave (§5.2).

Interactive · Table 1 of the paper

Read-once versus a standard ORAM access

Pick a tree size. Growing $N$ shrinks each block (fewer outputs per block), and smaller blocks mean a smaller stash to scan with cmov, so every access gets cheaper. The read-once access skips eviction entirely. Average of 10,000 accesses.

Tree size $N$  
ORAM access latency in milliseconds versus tree size, four series
    Show the numbers as a table

    Only the five measured tree sizes are selectable. Ratios in the readouts are computed here from Table 1; they do not appear in the paper.

    Interactive · Table 2 of the paper

    Reads in parallel

    A read-once access writes nothing back to the tree. Each thread keeps its own stash and only reads the shared position map, so there is no memory region two threads can write at once (Claim 5). Slide to add read threads on the $N=2^{24}$, 544-byte tree.

    1234
    Read threads sharing one read-once tree and one read-only position map, each with its own stash
    Read-once access time by thread count, Path and Circuit ORAM
    • Path ORAM
    • Circuit ORAM
    • 1 thread
    Show the numbers as a table
    Threads$T^3$ (Path ORAM)$T^3$ (Circuit ORAM)
    12.43 ms0.64 ms
    21.40 ms0.58 ms
    30.90 ms0.43 ms
    40.73 ms0.35 ms
    Provenance. The introduction says read time "decreases linearly with the number of the threads used". The bars show Table 2's measured values as-is. The speed-up ratios are computed here from that table.

    Interactive · Figures 2, 3 and 6 of the paper

    From an address to an ORAM block

    The client only knows its addresses (public-key hashes, $pkh$). Inside the enclave a PRF with a secret key $k_b$ maps each $pkh$ to a block ID, $\mathsf{bid} \leftarrow \mathsf{OBlockMap}(pkh, k_b)$. Only the enclave knows the mapping. Each block has room for a fixed number of outputs per address. How many is enough?

    12345678
    Percentage of addresses by number of unspent outputs, 1 to 8
    Addresses fully served
    92.05%

    One address, one block — or $\delta$ blocks

    Type any string as a stand-in for $pkh$. This page draws a random key $k_b$ (your "enclave") and uses HMAC-SHA-256 as the PRF into a toy tree of $N=16$ blocks.

    Mapping
    What the full node can count
    Provenance. Bar values are the labels printed on Figure 6. Cumulative shares are sums computed here. Two wordings in the paper differ. §5.1 and Figure 6's annotation count addresses ("more than 7%" / "≈8% of all addresses have more than 2 UTXOs"). Figure 6's caption says two slots cover "approximate 92% of the UTXO set", and §5.1 says "more than 92% of all the UTXOs per wallet ID". The Completeness goal (§6.2) gives "92–96% of all clients", depending on the mapping.

    Figures 7 and 8 of the paper

    Against Bloom-filter SPV and BITE

    For the Bloom-filter baseline, a "request" is the full node scanning one block and building Merkle proofs. For $T^3$ and BITE it is one ORAM access. BITE was re-implemented from its description with non-recursive Path ORAM and 32 kB blocks ($N=2^{17}$). An "improved BITE" uses recursive Path ORAM with $T^3$'s parameters.

    Figure 7: running time in milliseconds (log scale) against number of requests (0 to 160) for T3 with recursive Path ORAM, T3 with recursive Circuit ORAM, BITE with Path ORAM, improved BITE with recursive Path ORAM, and SPV with Bloom filter at 1% and 5% false-positive rate.
    Figure 7. Performance of $T^3$ using Path/Circuit ORAM with block of size 544 B, the current SPV with Bloom filter, original BITE oblivious database with block of size 32 kB, and improved BITE with block of size 544 B. For the SPV client with Bloom filter, false-positive rates of 1% and 5%.
    Figure 8: communication cost in kilobytes against number of requests (0 to 80) for T3 and SPV with Bloom filter at 1% and 5% false-positive rate.
    Figure 8. Communication cost of $T^3$ and the current SPV solution. Since both systems return the information of unspent outputs to the client, the communication overhead of BITE equals that of $T^3$. The SPV estimate uses block 551731 (1149 KB, 3017 transactions) and is described in the paper as pessimistic.

    Reproduced from the paper's figure files. The underlying data series are not in the paper's source repository, so this page does not read values off these plots.

    Guarantees

    What $T^3$ promises, and why

    GoalWhat it meansHow it is achieved
    PrivacyThe host learns nothing about which addresses a client queries.Attested TLS-style channel into the enclave. ORAM hides access patterns to untrusted memory. cmov-based oblivious code inside the enclave.
    ValidityA malicious host cannot feed the enclave invalid outputs.The enclave checks proof of work and the Merkle root against its own header chain. A Merkle hash tree authenticates every ORAM block fetched from untrusted memory.
    CompletenessClients get (most of) their unspent outputs.Address-to-block mappings that serve 92–96% of clients, trading storage for performance.
    EfficiencyBursty concurrent requests, little downtime.Concurrent read-once accesses on the read tree. Updates on the other tree. Reads queue only while queued evictions are replayed.

    The six security claims (§6.1)

    #ClaimArgument
    1The managing enclave leaks nothing user-related.Secure channel after attestation. Each address maps to a fixed number of ORAM blocks. The only thing revealed is the number of blocks updated, which is public.
    2Read-once accesses on the read tree leak nothing.Each path should be read once per block interval and is reshuffled before the next. A repeat query by the same client is the only leak (see the walkthrough above).
    3Writes on the original tree leak nothing.They are standard ORAM accesses, implemented side-channel-resistant as in ZeroTrace and Obliviate.
    4Data brought into the TEE is correct.Blocks are verified by proof of work and the header chain. ORAM data is verified with a Merkle hash tree.
    5Multiple threads cause no synchronisation issues.Threads only read the shared position map. Each writes only to its own stash.
    6In-enclave memory interactions are side-channel-resistant.ORAM hides accesses to untrusted memory. Oblivious operations inside. Every operation takes the same branch sequence.

    Out of scope: speculative-execution attacks, which the paper leaves to Intel's patches. Denial of service by a client that floods requests is also out of scope; the paper suggests fees or a cuckoo filter of unspent addresses.

    Table 3 · Capabilities of oblivious systems

    SystemConcurrencyRecursive constructionSide-channel protection
    ConcurORAM✓✗— (not a goal)
    Obliviate✗✗✓
    ZeroTrace✗✓✓
    BITE Oblivious Database✗✗✓
    $T^3$✓✓✓

    Measured

    Setup and results

    C++ on the Intel SGX SDK v2.1.3. The ORAM controller is built on ZeroTrace, and libjson-rpc-cpp talks to bitcoind from inside the enclave. Recursive Path ORAM ($Z=4$) and recursive Circuit ORAM ($Z=2$), single address into single block, up to 2 outputs per address. Intel Xeon Silver 4116 @ 2.10 GHz, 128 GB RAM, simulation mode.

    Table 1 · Read-once vs standard ORAM access

    $N$Block sizePath · read-oncePath · standardCircuit · read-onceCircuit · standard

    Dataset and storage

    UTXO snapshotblocks 0 – 551,731
    Unspent transaction outputs58,156,895
    ORAM storage blow-up≈4× Circuit · 6–8× Path
    Both ORAM trees, Circuit ORAM $Z=2$≈26 GB
    Both ORAM trees, Path ORAM $Z=4$≈51 GB
    Bitcoin header chain (untrusted, integrity-checked)≈44 MB
    Provenance. The snapshot date differs between sections. §5.1 says the UTXO set was extracted in February 2019 (blocks 0 to 551,731). The introduction says the unspent outputs set was extracted in October 2018. Both are reproduced here as written.

    Reference

    Cite this work

    @article{le2020tale,
      title   = {A Tale of Two Trees: One Writes, and Other Reads.
                 {O}ptimized Oblivious Accesses to {B}itcoin and other
                 {UTXO}-based Blockchains},
      author  = {Le, Duc V. and Tengana Hurtado, Lizzy and Ahmad, Adil and
                 Minaei, Mohsen and Lee, Byoungyoung and Kate, Aniket},
      journal = {Proceedings on Privacy Enhancing Technologies},
      year    = {2020}
    }

    Volume, issue and pages are omitted because the source PDF leaves them as placeholders. The year is from the author's publication list.

    Source code. The paper states that the prototype implementation is available at github.com/TEE-3/T3. It builds on the ZeroTrace source code, which its authors shared with the paper's authors.