<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Nik</title>
    <description>The latest articles on DEV Community by Nik (@zxczxc).</description>
    <link>https://dev.to/zxczxc</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3782599%2F1b78dae4-e5da-437e-942f-597380a41fa7.png</url>
      <title>DEV Community: Nik</title>
      <link>https://dev.to/zxczxc</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/zxczxc"/>
    <language>en</language>
    <item>
      <title>AI agents are bad at code refactoring</title>
      <dc:creator>Nik</dc:creator>
      <pubDate>Thu, 23 Jul 2026 17:53:22 +0000</pubDate>
      <link>https://dev.to/zxczxc/why-ai-agents-are-bad-at-code-refactoring-1jkb</link>
      <guid>https://dev.to/zxczxc/why-ai-agents-are-bad-at-code-refactoring-1jkb</guid>
      <description>&lt;p&gt;Give an AI agent an empty directory and a page of requirements, and it will impress you. Give the same agent an existing codebase and a one-line feature request, and there's a good chance it will leave the project slightly worse while fully satisfying the request.&lt;/p&gt;

&lt;p&gt;And "existing" no longer means old. In the agentic era a codebase can reach that state — drifted design, blurred patterns, obsolete docs — in a matter of weeks, sometimes days, often with an agent as the original author. The archaeology now has to happen on sites that are still under construction.&lt;/p&gt;

&lt;p&gt;I want to walk through why I think this happens, and why I suspect it's not a temporary weakness.&lt;/p&gt;

&lt;h2&gt;
  
  
  Two forward passes
&lt;/h2&gt;

&lt;p&gt;When an agent builds a project from scratch, the pipeline is:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;requirements → design → code&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The user states what is needed. The agent converts requirements into a software design, picking patterns that fit — it has seen thousands. Then it converts the design into code, step by step.&lt;/p&gt;

&lt;p&gt;Both arrows point the same way, and both are &lt;em&gt;forward passes&lt;/em&gt; — and humanity has documented them exhaustively. Every architecture book, every pattern catalog, every tutorial, every open-source project is a record of someone moving in this direction. A model trained on public text has effectively watched this pipeline run millions of times. It's not surprising that agents are good at it. It would be surprising if they weren't.&lt;/p&gt;

&lt;h2&gt;
  
  
  Refactoring runs the pipeline backwards
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqp35qljumioael3xa8cq.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqp35qljumioael3xa8cq.jpg" alt="Mental Gymnastics meme: " width="500" height="427"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now the opposite situation. The agent is dropped into an existing project. What it holds is the pipeline's &lt;em&gt;output&lt;/em&gt; — the code. The inputs are exactly what's missing:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The design has drifted; the code no longer matches any design that was ever drawn.&lt;/li&gt;
&lt;li&gt;The patterns are blurred by local, not-so-clean fixes and workarounds.&lt;/li&gt;
&lt;li&gt;The documentation and comments describe a system that no longer exists.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The engineer's task — human or AI — becomes:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Recover the &lt;em&gt;actual&lt;/em&gt; current requirements (they've drifted from whatever the docs claim).&lt;/li&gt;
&lt;li&gt;Discern the real patterns inside the noisy code.&lt;/li&gt;
&lt;li&gt;For each anomaly, figure out &lt;em&gt;why&lt;/em&gt; it exists: legacy? An unfinished plan? A past mistake? A requirement that moved?&lt;/li&gt;
&lt;li&gt;Correct the requirements picture based on what the code investigation revealed.&lt;/li&gt;
&lt;li&gt;Decide what design would be ideal for the corrected requirements.&lt;/li&gt;
&lt;li&gt;Plan a path from the current code state to the target state.&lt;/li&gt;
&lt;li&gt;Write the code.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Look at the direction of these steps. Steps 1–4 run backwards: from code to design, from design to requirements. Steps 5 and 7 are the familiar forward passes — the easy, well-trained part. Step 6 is neither: it's plotting a route through a live system that has to keep working at every intermediate state — changing the tires while the car is moving.&lt;/p&gt;

&lt;p&gt;And the backward direction has a problem the forward one doesn't: it is &lt;em&gt;underdetermined&lt;/em&gt;. Many different pairs of (requirements, design) could have produced the code in front of you. The forward pass loses information; running it backwards means guessing what was lost. The only thing that disambiguates the guesses is history. And while git preserves the &lt;em&gt;what&lt;/em&gt; perfectly — it's all there in the repo — the &lt;em&gt;why&lt;/em&gt; mostly isn't: it lived in review comments, chat threads, and the heads of people who have moved on.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step zero: the smell
&lt;/h2&gt;

&lt;p&gt;Worse, you rarely know in advance that refactoring is needed. It is almost never a ticket. You sit down to add a feature or fix a bug, and every implementation you sketch &lt;em&gt;smells&lt;/em&gt;: each one fights the current design, each needs two workarounds to fit. The decision to refactor is the recognition of that smell — noticing a mismatch between the design you have and the requirement that just arrived.&lt;/p&gt;

&lt;p&gt;This is a background process, not a step. A human engineer runs it constantly, motivated by their own future suffering: the person who will maintain this mess is them. An agent's episode ends when the diff lands. Nothing in its loop rewards stopping to question the design; if anything, the training signal runs the other way — "sure, here's your feature" reads as helpful, "we should restructure first" reads as evasive. The agent never has to live in the house it builds.&lt;/p&gt;

&lt;h2&gt;
  
  
  Refactoring is a compression task
&lt;/h2&gt;

&lt;p&gt;Strip it down and refactoring is, first of all, &lt;em&gt;compression&lt;/em&gt;. Given tens of thousands of lines of code, extract the internal structure. And not only the current structure — also the historical one, because step 3 (&lt;em&gt;why is this here?&lt;/em&gt;) is a question about time, not about code.&lt;/p&gt;

&lt;p&gt;Two compressions, then. The first — what the design currently is — is hard, but at least the input fits in a context window. The second is harder in principle: the rationale was never written down, or was written down and has rotted along with the docs. If the code was written by an agent, the problem inverts rather than disappears: the &lt;em&gt;why&lt;/em&gt; is on record in exhausting detail — but uncompressed, too voluminous to load, and impossible to tell which of it is still true. The agent is asked to decompress a lossy archive with the dictionary thrown away.&lt;/p&gt;

&lt;p&gt;What a good human engineer brings here is a &lt;em&gt;prior&lt;/em&gt; — and note what the prior is over. Not over patterns, but over &lt;em&gt;decay&lt;/em&gt;. Having watched hundreds of codebases evolve, they can look at a warped shape and reconstruct the erosion: this was once a clean separation, then a deadline happened, then a hotfix happened. Their mental library includes the degraded versions of every pattern, because they have personally watched the degradation happen.&lt;/p&gt;

&lt;p&gt;Models are trained mostly on snapshots — final states, clean tutorials, curated open source. Their library holds the pristine versions. So faced with drifted code, a model does what it was trained to do: it matches the mess to the nearest clean shape and confidently describes the codebase as the textbook version of whatever it most resembles. But &lt;em&gt;which&lt;/em&gt; clean shape, if any, this mess descended from is precisely the question that was supposed to be investigated, not assumed away. Pattern-matching the noise to a pristine archetype is guessing at the story instead of recovering it.&lt;/p&gt;

&lt;h2&gt;
  
  
  What agents do instead
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ffxhzsbrpcpgysq9hytnm.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ffxhzsbrpcpgysq9hytnm.jpg" alt="Drake meme: disapproving at " width="500" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Deprived of the story, agents default to greenfield style: take the existing code as ground truth, assume everything in it is intentional, and build the new thing on top. A new abstraction here, a new entity there. Nothing is ever deleted; each session adds a stratum. The result is geological: layers on layers, each written as if everything below it were load-bearing.&lt;/p&gt;

&lt;p&gt;If you've watched an agent work on a mature project, you've seen this: it doesn't refactor the workaround — it &lt;em&gt;respects&lt;/em&gt; it. Wraps it, generalizes it, builds on it. The workaround becomes architecture.&lt;/p&gt;

&lt;p&gt;One clarification, because it causes a lot of confusion: agents are quite good at the &lt;em&gt;mechanics&lt;/em&gt; of refactoring. Tell one "extract this logic into a module, keep behavior identical, update all call sites" and it will do a decent job. This fools people. Fowler's catalog — extract method, move field, rename — is the last mile of refactoring. The hard part was never executing the moves; it's deciding that moves are needed, which ones, and toward what design. Judgment, not mechanics. Agents pass the mechanics test, and it tells us nothing about the judgment.&lt;/p&gt;

&lt;h2&gt;
  
  
  The economics flip
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fp9yvzyew2fr95vox8nf8.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fp9yvzyew2fr95vox8nf8.jpg" alt="Vince McMahon reaction meme, escalating excitement: ship a hotfix / fix it properly / refactor the codebase / rewrite from scratch" width="500" height="841"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So here is my suspicion: maybe with AI it is simply cheaper to throw the code away and regenerate it. Look at what a rewrite actually involves:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Extract the requirements — explicit and implicit.&lt;/li&gt;
&lt;li&gt;Design against them.&lt;/li&gt;
&lt;li&gt;Write the code.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That's the greenfield pipeline with one pre-step attached. No smell detection, no archaeology, no step-6 migration plan — most of the seven-step backward pipeline simply disappears. Mass production killed the repair shop for exactly this reason: when manufacturing a new unit costs less than diagnosing the old one, nobody fixes anything. Code generation is heading toward free. Code &lt;em&gt;diagnosis&lt;/em&gt; is not.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>refactoring</category>
      <category>programming</category>
      <category>softwareengineering</category>
    </item>
    <item>
      <title>The Kilobyte UTXO Set: Utreexo, SwiftSync, and Bitcoin's state problem</title>
      <dc:creator>Nik</dc:creator>
      <pubDate>Thu, 16 Jul 2026 21:14:15 +0000</pubDate>
      <link>https://dev.to/zxczxc/the-kilobyte-utxo-set-utreexo-swiftsync-and-bitcoins-state-problem-9i7</link>
      <guid>https://dev.to/zxczxc/the-kilobyte-utxo-set-utreexo-swiftsync-and-bitcoins-state-problem-9i7</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Interactive version:&lt;/strong&gt; this article has a &lt;a href="https://nikicat.github.io/kilobyte-utxo-set/" rel="noopener noreferrer"&gt;live playground edition&lt;/a&gt; where you can add coins, inspect Merkle proofs, and spend them yourself. The GIF below is a recording of it.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Every Bitcoin node drags a &lt;strong&gt;12 GB database&lt;/strong&gt; through its whole life just to answer one question: &lt;em&gt;does this coin exist?&lt;/em&gt; Cryptographic accumulators — Utreexo above all — compress that answer into &lt;strong&gt;about thirty hashes&lt;/strong&gt;. Here's why that matters, how it works, and what it costs.&lt;/p&gt;

&lt;h2&gt;
  
  
  1 · Validation is cheap. State is not.
&lt;/h2&gt;

&lt;p&gt;To validate a transaction, a node must check each input against a record of every coin that exists and hasn't been spent — the &lt;em&gt;UTXO set&lt;/em&gt; (unspent transaction outputs). Each check needs a handful of facts: the coin &lt;strong&gt;exists&lt;/strong&gt;, its &lt;strong&gt;amount&lt;/strong&gt;, its &lt;strong&gt;spending condition&lt;/strong&gt; (script), plus its &lt;strong&gt;creation height and coinbase flag&lt;/strong&gt; (for coinbase maturity and BIP 68 relative timelocks). Then the spent coins are deleted and the new outputs inserted.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;~170 M&lt;/th&gt;
&lt;th&gt;~12 GB&lt;/th&gt;
&lt;th&gt;~3 B&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;~0.9 kB&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;unspent coins tracked by every node&lt;/td&gt;
&lt;td&gt;UTXO database on disk&lt;/td&gt;
&lt;td&gt;random lookups during one full sync&lt;/td&gt;
&lt;td&gt;the same state, as a Utreexo accumulator&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Signatures can be skipped below a reviewable checkpoint (&lt;code&gt;assumevalid&lt;/code&gt;), bandwidth can be bought, but the UTXO set is different: it's &lt;strong&gt;random-access state touched by every single input&lt;/strong&gt;. If it fits in RAM, lookups are ~100 ns; if it doesn't, they become disk seeks and a full sync degrades from hours to days. State is the term that decides whether a Raspberry Pi and a workstation live in the same universe.&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;
  How Bitcoin Core actually stores it — LevelDB, dbcache, and the flush spiral
  &lt;p&gt;Core keeps the UTXO set in a LevelDB database (&lt;code&gt;chainstate/&lt;/code&gt;, ~48 bytes per coin plus index overhead) fronted by an in-memory cache (&lt;code&gt;-dbcache&lt;/code&gt;, default 450 MiB). While the working set fits in the cache, coin lookups and updates are memory-speed. When the cache fills during sync, Core writes it out and &lt;em&gt;empties it&lt;/em&gt; — so the next few million lookups all miss and go to disk.&lt;/p&gt;

&lt;p&gt;With ~170M coins needing north of 10 GB of cache, a default-sized cache overflows constantly. Every miss is a random read; every flush rewrites overlapping LevelDB files (compaction). On a machine with 2–4 GB of RAM this — not hashing, not signatures — is where initial sync goes to die. The standard advice "raise your dbcache" is a workaround for exactly the cost accumulators remove.&lt;/p&gt;



&lt;p&gt;&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;
  Why snapshots don't solve this — assumeutxo still ships the state
  &lt;p&gt;&lt;code&gt;assumeutxo&lt;/code&gt; lets a node start from a UTXO snapshot and validate history in the background — a big usability win. But the snapshot &lt;em&gt;is&lt;/em&gt; the 12 GB state: you still have to download it, hash it, store it, and random-access it forever after. It moves the state problem around the timeline; it doesn't shrink the state. An accumulator shrinks the thing itself, which is why the two compose nicely: an accumulator checkpoint is a kilobyte you can read in a code review.&lt;/p&gt;



&lt;p&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  2 · Flip who carries the data
&lt;/h2&gt;

&lt;p&gt;An &lt;em&gt;accumulator&lt;/em&gt; is a compact commitment to a set. It supports four operations: &lt;strong&gt;add&lt;/strong&gt; an element, &lt;strong&gt;delete&lt;/strong&gt; one, &lt;strong&gt;prove&lt;/strong&gt; membership, and &lt;strong&gt;verify&lt;/strong&gt; a proof. The trick is a change of responsibility: the verifier keeps only the tiny commitment, and whoever wants to &lt;em&gt;spend&lt;/em&gt; a coin must present a proof that it's in the set.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Today — the node carries the set
  tx input “spend coin X”
    → look up X in a 12 GB database   (random read, per input, forever)
    → exists? get amount + script → validate

Accumulator — the spender carries a proof
  input + ~1 kB proof (“X is in the set, here's the path”)
    → check proof against ~28 hashes  (pure hashing, no database)
    → amount + script are inside the proven leaf → validate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The database becomes a receipt check. Nothing about Bitcoin's consensus rules changes — only &lt;em&gt;how a node represents what it already verifies&lt;/em&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  3 · Utreexo: a forest that counts in binary
&lt;/h2&gt;

&lt;p&gt;Utreexo (Dryja, 2019) arranges the UTXO set as a &lt;strong&gt;forest of perfect Merkle trees&lt;/strong&gt; — every leaf is the hash of one coin (its outpoint, amount, script, creation height, and coinbase flag). The forest's shape is forced by one rule: &lt;em&gt;tree sizes are the binary digits of the coin count&lt;/em&gt;. 6 coins = &lt;code&gt;110₂&lt;/code&gt; = one 4-tree and one 2-tree. The node stores only the tree &lt;strong&gt;roots&lt;/strong&gt; and the count. That's the entire database.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fdipau643moc3nq8rp8pw.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fdipau643moc3nq8rp8pw.gif" alt="Playground recording: two coins are added (watch equal-height trees carry-merge like a binary counter, 6 → 8 coins collapse into one 8-tree), then coin c's Merkle proof is highlighted and it is spent — the proof hashes become the new roots in place." width="780" height="449"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;(Recording of the &lt;a href="https://nikicat.github.io/kilobyte-utxo-set/" rel="noopener noreferrer"&gt;interactive playground&lt;/a&gt; — try it yourself.)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Three things to notice:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Adding is a carry.&lt;/strong&gt; A new coin starts as a lone 1-tree. If a 1-tree already exists, the two combine under a new parent; if that collides with a 2-tree, they combine again — exactly like incrementing a binary counter. Adds need &lt;em&gt;no data at all&lt;/em&gt; beyond the roots.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A proof is a path.&lt;/strong&gt; To spend a coin, you present the coin plus its sibling hashes up to a root — about &lt;code&gt;log₂(n)&lt;/code&gt; hashes, ~28 for the real UTXO set. The amount and script are inside the leaf hash, so a proof can't lie about them: the hashes wouldn't land on the node's stored root.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The proof is also the update.&lt;/strong&gt; This is Utreexo's quiet elegance: when a coin is deleted, the new roots of the leftover pieces are &lt;em&gt;literally the hashes from the proof you just verified&lt;/em&gt;. Verification and the state update consume the same bytes — the node never needs data it doesn't already hold in its hand.&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;
  The delete, step by step — worked example with 4 coins
  &lt;p&gt;Say the forest holds one 4-tree over coins &lt;code&gt;a b c d&lt;/code&gt;: root &lt;code&gt;R = H(H(a,b), H(c,d))&lt;/code&gt;. A transaction spends &lt;code&gt;c&lt;/code&gt;, attaching the proof &lt;code&gt;{d, H(a,b)}&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Verify:&lt;/strong&gt; the node computes &lt;code&gt;H(c,d)&lt;/code&gt; from the claimed coin and its sibling, then &lt;code&gt;H(H(a,b), H(c,d))&lt;/code&gt;, and checks the result equals its stored root &lt;code&gt;R&lt;/code&gt;. It does — the coin exists, and its amount/script (inside &lt;code&gt;c&lt;/code&gt;'s leaf hash) are authentic.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Delete:&lt;/strong&gt; remove the 4-tree's root from state. Three coins remain, and 3 = &lt;code&gt;11₂&lt;/code&gt;, so the forest must become a 2-tree plus a 1-tree. Their roots are &lt;code&gt;H(a,b)&lt;/code&gt; and &lt;code&gt;d&lt;/code&gt; — &lt;em&gt;both are items from the proof&lt;/em&gt;. No lookup, no recomputation from leaves the node doesn't have.&lt;/p&gt;

&lt;p&gt;In general, deleting a leaf makes its sibling "move up" one level, and the proof always contains exactly the hashes needed to rebuild every affected root.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;A footnote for the precise:&lt;/em&gt; this tree-splitting is how the 2019 paper presents deletion. The implementations that shipped (utreexod, Floresta's rustreexo) use a newer &lt;em&gt;swapless&lt;/em&gt; scheme — the leaf counter never decreases, the deleted slot simply goes empty, and &lt;code&gt;d&lt;/code&gt; moves up into its parent's position, so the example would end as the single root &lt;code&gt;H(H(a,b), d)&lt;/code&gt; rather than two roots. The property that matters is identical in both designs: the verified proof contains exactly the bytes needed to write the new roots.&lt;/p&gt;



&lt;p&gt;&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;
  What does the node trust? — nothing but its own hashes
  &lt;p&gt;Where do the roots come from? &lt;strong&gt;The node computes them itself, from genesis&lt;/strong&gt; — it starts with an empty forest and applies every add and delete with its own hands as it validates each block. The accumulator state at any height is a pure function of the chain, exactly like today's UTXO set, only smaller. Nobody hands you roots.&lt;/p&gt;

&lt;p&gt;Given that, proofs are self-authenticating: a proof either hashes up to &lt;em&gt;your own&lt;/em&gt; stored roots or it doesn't, and forging one — wrong amount, wrong script, a coin that never existed — requires a SHA-2 collision (implementations hash with SHA-512/256). Whoever supplies proofs (a peer, a bridge) is exactly as untrusted as a peer supplying blocks today: garbage fails validation and the block is rejected.&lt;/p&gt;

&lt;p&gt;Bridges are therefore a &lt;strong&gt;liveness&lt;/strong&gt; dependency, never a safety one — with no proof you can't validate a spend, so you can be starved, but you cannot be fooled. The one optional trust-flavored piece is a hardcoded checkpoint (starting from baked-in roots instead of genesis), and that sits in the same trust class as &lt;code&gt;assumevalid&lt;/code&gt;: a reviewable constant in the source, auditable by anyone who syncs without it.&lt;/p&gt;



&lt;p&gt;&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;
  Where do proofs come from? — bridge nodes and stale proofs
  &lt;p&gt;Somebody has to generate proofs for spenders, and today's wallets don't track them. &lt;strong&gt;Bridge nodes&lt;/strong&gt; fill the gap: they maintain the &lt;em&gt;entire&lt;/em&gt; forest (every internal hash — larger than a plain UTXO database) and attach proofs to transactions and blocks on behalf of the legacy ecosystem.&lt;/p&gt;

&lt;p&gt;There's a subtler cost: &lt;strong&gt;proofs go stale&lt;/strong&gt;. Every block adds and deletes leaves, which restructures the forest, so a proof valid at height &lt;em&gt;H&lt;/em&gt; may be wrong at &lt;em&gt;H+1&lt;/em&gt;. Wallets that hold their own proofs must refresh them as blocks arrive (cheap per block, but a standing obligation), or lean on a bridge to prove on demand. This dynamic-proof property is the main engineering difference from textbook Merkle trees, and the main reason adoption needs infrastructure, not just code.&lt;/p&gt;



&lt;p&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  4 · What Utreexo trades away
&lt;/h2&gt;

&lt;p&gt;Accumulators don't create efficiency from nothing — they move costs to where they're cheaper to pay. The honest ledger:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Deleted: state &amp;amp; random I/O.&lt;/strong&gt; Node state: 12 GB → ~1 kB. No database, no cache tuning, no flush stalls. Validation speed becomes independent of RAM — a Pi validates like a workstation.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Added: bandwidth.&lt;/strong&gt; Blocks travel with proofs: about ×1.7 the bytes as utreexod ships today (worst case ×4 for per-transaction relay). Caching cuts most of it — most coins are spent soon after creation, proofs for cached coins are omitted, and the paper's simulation with a 500 MB leaf cache lands near ×1.25.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Added: prover burden.&lt;/strong&gt; Spenders (or bridge nodes on their behalf) must generate and refresh proofs. Bridges store &lt;em&gt;more&lt;/em&gt; than a normal node — the asymmetry is the point, but someone must run them.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Added: CPU, slightly.&lt;/strong&gt; ~log n hashes per input is &lt;em&gt;more&lt;/em&gt; raw compute than a RAM hash-map hit. On a big-RAM machine Utreexo doesn't win time — it wins footprint. The speedup is real only where state was the bottleneck.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;/p&gt;
  Why the bandwidth hit is smaller than it looks — coins die young
  &lt;p&gt;Empirically, a large share of outputs are spent within hours or days of creation (change outputs, exchange churn, batching flows). Utreexo exploits this: nodes keep recently added leaves cached, and peers skip sending proofs for anything the receiver is known to cache. Since most spends hit young coins, most proofs shrink to nearly nothing. The published numbers span exactly this effect: proofs roughly double a block uncached, utreexod ships at about ×1.7, and the paper's simulated 500 MB leaf cache brings the whole download down to about ×1.25. The long tail — old coins waking up — pays full ~28-hash fare.&lt;/p&gt;



&lt;p&gt;&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;
  What it means for initial sync — which bottleneck it removes, in numbers
  &lt;p&gt;A full sync is bounded below by &lt;code&gt;max(network, disk, cpu)&lt;/code&gt;. On mid-2026 mainnet (~753 GB of blocks, ~3B inputs): a 1 Gbps line needs ~1h40m for the download no matter what; hashing and parsing cost tens of minutes of CPU; signatures below &lt;code&gt;assumevalid&lt;/code&gt; are skipped. On big-RAM hardware the UTXO term is small — so Utreexo barely moves the bound there, and the extra proof bytes actually &lt;em&gt;raise&lt;/em&gt; the network term.&lt;/p&gt;

&lt;p&gt;The transformation is on &lt;em&gt;constrained&lt;/em&gt; hardware: with 2 GB of RAM, a stock node's UTXO term explodes into billions of disk seeks (days), while a Utreexo node's stays exactly where the big-RAM one is. Accumulators don't lower the floor — they make the floor reachable on hardware that used to sit 10× above it.&lt;/p&gt;



&lt;p&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  5 · SwiftSync: never build the database at all
&lt;/h2&gt;

&lt;p&gt;Utreexo shrinks the UTXO set by making spenders carry proofs. &lt;a href="https://gist.github.com/RubenSomsen/a61a37d14182ccd78760e477c78133cd" rel="noopener noreferrer"&gt;SwiftSync&lt;/a&gt; (Somsen, 2025) goes further for the special case of syncing: it needs &lt;strong&gt;no proofs, no forest, no lookups&lt;/strong&gt;. You download a &lt;em&gt;hint file&lt;/em&gt; — literally one bit per output ever created, saying whether that output will still be unspent when you reach the tip (~100 MB compressed for all of Bitcoin's history). Then validation becomes bookkeeping:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;output created, hint = 1  →  append to the final UTXO set    (write-once, never read during sync)
output created, hint = 0  →  aggregate += H(outpoint)        (never stored anywhere)
input spends a coin       →  aggregate -= H(outpoint)        (no existence check — just subtract)
───────────────────────────────────────────────────────────
end of sync:   aggregate == 0  →  set is consistent ✓
               aggregate != 0  →  bad hints or invalid spend → abort, fail closed ✗
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The fail-closed property is why the hint file needs no trust in the safety sense: anyone can publish hints, and lying is self-defeating — wrong hints can waste your time, never corrupt your state.&lt;/p&gt;

&lt;p&gt;The second consequence is the deeper one. Adding and subtracting from an aggregate &lt;strong&gt;commutes&lt;/strong&gt; — order doesn't matter. So blocks no longer have to be validated one after another:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;today:      b₁ → b₂ → b₃ → b₄        each block needs the state the previous one left
SwiftSync:  b₃, b₁, b₄, b₂  →  Σ     any order, every core at once
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;This attacks the &lt;em&gt;serial&lt;/em&gt; bottleneck of sync — the one cost no amount of bandwidth or disk can buy back. The trade: spent coins are never materialized, so their scripts can't be individually checked (nor coinbase maturity, which needs the same per-coin metadata) — SwiftSync inherits the same &lt;code&gt;assumevalid&lt;/code&gt; stance a default node already takes for old signatures. It's a sync-time trick only: when it finishes you're a stock node with a stock database. A proof-of-concept measured a &lt;a href="https://delvingbitcoin.org/t/swiftsync-speeding-up-ibd-with-pre-generated-hints-poc/1562" rel="noopener noreferrer"&gt;5.28× sync speedup&lt;/a&gt; before parallel validation is even exploited.&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;
  Why a sum can prove set-consistency — the accountant's argument
  &lt;p&gt;Think of the aggregate as a ledger that must balance. Every hint-0 output deposits &lt;code&gt;H(outpoint)&lt;/code&gt; exactly once when it's created; every input withdraws &lt;code&gt;H(outpoint)&lt;/code&gt; exactly once when it spends. If history is honest, deposits and withdrawals pair off perfectly and the balance is zero.&lt;/p&gt;

&lt;p&gt;Now try to cheat. Spend a coin that never existed: a withdrawal with no matching deposit — non-zero. Spend the same coin twice: two withdrawals, one deposit — non-zero. Mark a doomed output as "unspent" in the hints: its deposit never happens but its withdrawal does — non-zero. Mark a surviving output as "doomed": a deposit nobody withdraws — non-zero. The hash makes collisions infeasible, so entries can't be forged to cancel accidentally — and each node &lt;strong&gt;salts the hash with its own random secret&lt;/strong&gt;, so an adversary who authors both hints and transactions can't precompute entries engineered to cancel. One equality at the end certifies billions of set operations that were never performed individually.&lt;/p&gt;

&lt;p&gt;Hints are also &lt;strong&gt;deterministic&lt;/strong&gt; given the chain and target height — anyone can regenerate the file and compare, so a published hint file is reproducible, not bespoke. Fuller designs fold amounts into the aggregated data to tighten the money-supply check; the &lt;a href="https://gist.github.com/RubenSomsen/a61a37d14182ccd78760e477c78133cd" rel="noopener noreferrer"&gt;gist&lt;/a&gt; walks the exact accounting.&lt;/p&gt;



&lt;p&gt;&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;
  SwiftSync vs Utreexo, head to head — they solve different problems
  &lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Utreexo&lt;/th&gt;
&lt;th&gt;SwiftSync&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Scope&lt;/td&gt;
&lt;td&gt;node architecture, forever&lt;/td&gt;
&lt;td&gt;initial sync only&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;State while running&lt;/td&gt;
&lt;td&gt;~1 kB of roots&lt;/td&gt;
&lt;td&gt;hint bits + one aggregate&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Network cost&lt;/td&gt;
&lt;td&gt;×1.2–1.7 blocks, ongoing&lt;/td&gt;
&lt;td&gt;~100 MB hints, once&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Parallel validation&lt;/td&gt;
&lt;td&gt;no — forest updates are ordered&lt;/td&gt;
&lt;td&gt;yes — fully commutative&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Trust added&lt;/td&gt;
&lt;td&gt;none&lt;/td&gt;
&lt;td&gt;none for safety; pairs with assumevalid&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Needs ecosystem&lt;/td&gt;
&lt;td&gt;bridges, proof relay, wallet support&lt;/td&gt;
&lt;td&gt;nothing — one hint file&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;After sync&lt;/td&gt;
&lt;td&gt;stays a kilobyte node&lt;/td&gt;
&lt;td&gt;becomes a stock node&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;They compose, too: a Utreexo node can use SwiftSync-style hints to skip proof verification for coins that die during sync — the ideas attack orthogonal costs (state vs. seriality).&lt;/p&gt;



&lt;br&gt;
&lt;p&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  6 · Utreexo isn't the only answer
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Approach&lt;/th&gt;
&lt;th&gt;Deletes which cost&lt;/th&gt;
&lt;th&gt;Pays with&lt;/th&gt;
&lt;th&gt;Trust change&lt;/th&gt;
&lt;th&gt;Status&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Utreexo&lt;/td&gt;
&lt;td&gt;UTXO state, forever (~1 kB node)&lt;/td&gt;
&lt;td&gt;×1.2–1.7 block bandwidth; bridge infra; dynamic proofs&lt;/td&gt;
&lt;td&gt;none&lt;/td&gt;
&lt;td&gt;utreexod, Floresta&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SwiftSync&lt;/td&gt;
&lt;td&gt;UTXO state &lt;em&gt;during sync&lt;/em&gt; + parallel block validation&lt;/td&gt;
&lt;td&gt;~100 MB hint file (fail-closed if wrong)&lt;/td&gt;
&lt;td&gt;pairs with assumevalid&lt;/td&gt;
&lt;td&gt;PoC, ~5× sync&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;assumeutxo&lt;/td&gt;
&lt;td&gt;waiting: start at a snapshot, validate history behind&lt;/td&gt;
&lt;td&gt;obtain + verify a 12 GB snapshot; state unchanged&lt;/td&gt;
&lt;td&gt;reviewable snapshot hash in code&lt;/td&gt;
&lt;td&gt;shipped in Core&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;UHS (Fields)&lt;/td&gt;
&lt;td&gt;~half of state: store coin &lt;em&gt;hashes&lt;/em&gt; only&lt;/td&gt;
&lt;td&gt;peers attach coin data to relayed txs&lt;/td&gt;
&lt;td&gt;none&lt;/td&gt;
&lt;td&gt;proposal&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;TXO bitfield (Cohen)&lt;/td&gt;
&lt;td&gt;most of state: one spentness bit per historical output&lt;/td&gt;
&lt;td&gt;separate index to locate coin data&lt;/td&gt;
&lt;td&gt;none&lt;/td&gt;
&lt;td&gt;proposal&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;UTXO commitments&lt;/td&gt;
&lt;td&gt;trust in any snapshot: miners commit the set's hash&lt;/td&gt;
&lt;td&gt;a consensus change — the hard part&lt;/td&gt;
&lt;td&gt;reduces trust&lt;/td&gt;
&lt;td&gt;never activated&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;RSA / class-group accumulators&lt;/td&gt;
&lt;td&gt;proof staleness &amp;amp; log-size proofs (constant-size instead)&lt;/td&gt;
&lt;td&gt;trusted setup (RSA) or slow group math&lt;/td&gt;
&lt;td&gt;setup ceremony (RSA)&lt;/td&gt;
&lt;td&gt;research&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ZeroSync&lt;/td&gt;
&lt;td&gt;sync itself: verify a succinct proof of the whole chain&lt;/td&gt;
&lt;td&gt;enormous proving cost; young cryptography&lt;/td&gt;
&lt;td&gt;soundness of the proof system&lt;/td&gt;
&lt;td&gt;research&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;/p&gt;
  Why not RSA accumulators? — constant-size proofs exist, with a catch
  &lt;p&gt;RSA accumulators (Boneh–Bünz–Fisch, 2019) commit a set into a single group element; membership proofs are constant-size and don't go stale the way Merkle paths do, and thousands of proofs aggregate into one. The catch: you need a modulus nobody can factor — a &lt;em&gt;trusted setup ceremony&lt;/em&gt;, anathema to Bitcoin's assumptions. Class groups avoid the ceremony but make every operation orders of magnitude slower. Merkle forests won in practice because they're plain SHA-2 hashing (SHA-512/256 in the shipped code): no new assumptions, fast on any CPU, auditable by anyone who can read forty lines of code.&lt;/p&gt;



&lt;p&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  7 · Applicability: pick by deployment, not by elegance
&lt;/h2&gt;

&lt;p&gt;None of these designs dominates — they spend different budgets (RAM, bandwidth, latency, sync time), so the right one depends on what the machine is &lt;em&gt;for&lt;/em&gt;:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Deployment&lt;/th&gt;
&lt;th&gt;Pain today&lt;/th&gt;
&lt;th&gt;Best fit&lt;/th&gt;
&lt;th&gt;Why&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Desktop / workstation node&lt;/td&gt;
&lt;td&gt;little — a few hours of sync&lt;/td&gt;
&lt;td&gt;assumeutxo now; SwiftSync when it lands&lt;/td&gt;
&lt;td&gt;the UTXO set already fits in RAM; Utreexo would only add bandwidth&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Home server / Raspberry Pi&lt;/td&gt;
&lt;td&gt;cache thrashing: days of sync, dbcache tuning&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Utreexo&lt;/strong&gt; (utreexod, Floresta) + SwiftSync&lt;/td&gt;
&lt;td&gt;validation becomes RAM-independent — the death-spiral mode disappears&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Phone / mobile wallet&lt;/td&gt;
&lt;td&gt;light wallets verify PoW + inclusion but can't check rule-validity; Electrum-style ones also reveal addresses&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Utreexo&lt;/strong&gt; from a checkpoint (embedded Floresta)&lt;/td&gt;
&lt;td&gt;~1 kB state and forward-only validation are phone-sized; a wallet that enforces the rules itself&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Exchange / explorer / heavy infra&lt;/td&gt;
&lt;td&gt;none of the above — they need rich indexes anyway&lt;/td&gt;
&lt;td&gt;stock node, big dbcache; &lt;strong&gt;run bridges&lt;/strong&gt; as a service&lt;/td&gt;
&lt;td&gt;address/history indexes dwarf the UTXO set&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Miner / pool&lt;/td&gt;
&lt;td&gt;validation latency is money&lt;/td&gt;
&lt;td&gt;stock node, everything in RAM&lt;/td&gt;
&lt;td&gt;proof bytes in the hot path are pure cost&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ephemeral nodes — CI, fleet spin-up&lt;/td&gt;
&lt;td&gt;every fresh instance pays a full IBD&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;SwiftSync&lt;/strong&gt; (+ assumeutxo today)&lt;/td&gt;
&lt;td&gt;reproducible hints turn provisioning into a parallel batch job&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;/p&gt;
  What a phone full node actually looks like — the Utreexo endgame
  &lt;p&gt;The pieces: an embeddable compact-state node (Floresta ships as a library with bindings for exactly this), a &lt;strong&gt;hardcoded accumulator checkpoint&lt;/strong&gt; — a kilobyte of roots, reviewable in the source like &lt;code&gt;assumevalid&lt;/code&gt; — and forward-only validation from that checkpoint. Wallet discovery uses compact block filters (BIP 158) checked locally, so no server ever learns your addresses.&lt;/p&gt;

&lt;p&gt;The honest costs: proof-carrying blocks are ~×1.7 bytes on a possibly metered connection, and sustained hashing costs battery — so the realistic mode is "validate on Wi-Fi and charge, serve the wallet locally all day."&lt;/p&gt;

&lt;p&gt;To be precise about what this improves on: today's light wallets are &lt;em&gt;not&lt;/em&gt; "no validation." An SPV client verifies the header chain (proof-of-work, difficulty) and Merkle inclusion of its transactions, so a server cannot forge a payment to it — and Neutrino (BIP 157/158) already fixed the privacy leak that Electrum-style and bloom-filter wallets have. What no light client can do is check &lt;strong&gt;rule-validity&lt;/strong&gt;: scripts, signatures, inflation, double-spends elsewhere. It follows the most-work chain unconditionally, trusting the hashrate majority to enforce the rules, and it can be lied to by omission. That specific gap — enforcing consensus rules yourself — is the one a checkpointed Utreexo node closes; privacy it merely matches.&lt;/p&gt;



&lt;p&gt;&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;
  Why the heavyweights keep fat nodes — and why that's the design working
  &lt;p&gt;&lt;strong&gt;Miners&lt;/strong&gt; race the network: every millisecond spent validating a fresh block delays mining on top of it, which costs real revenue in stale and empty blocks. They keep the whole UTXO set pinned in RAM and want the block-relay hot path as thin as possible.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Exchanges, explorers, and API providers&lt;/strong&gt; need address indexes, transaction history, and mempool analytics — orders of magnitude more state than the UTXO set — so shrinking it saves them approximately nothing. But they own the beefy hardware anyway, which makes them the natural &lt;strong&gt;bridge operators&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This split isn't a failure of adoption — it's the intended shape: &lt;em&gt;few fat provers, many thin verifiers&lt;/em&gt;. The asymmetry between a data-center bridge and a phone verifying against 28 hashes is precisely what the accumulator was designed to create.&lt;/p&gt;



&lt;p&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  8 · Further reading
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://eprint.iacr.org/2019/611.pdf" rel="noopener noreferrer"&gt;Utreexo: a dynamic hash-based accumulator&lt;/a&gt; — Dryja's original paper; sections 1–4 are unusually readable, and the coin-lifetime data behind proof caching lives here.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://lukechampine.com/utreexo-talk.html" rel="noopener noreferrer"&gt;Utreexo in Context&lt;/a&gt; — where accumulators sit among stateless-client designs.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://getfloresta.github.io/floresta-docs/ch00-00-introduction.html" rel="noopener noreferrer"&gt;Floresta docs&lt;/a&gt; — a working Rust compact-state node, documented as a book.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/utreexo/utreexod" rel="noopener noreferrer"&gt;utreexod&lt;/a&gt; — full node with Utreexo support (beta), including kilobyte-checkpoint bootstrap.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://gist.github.com/RubenSomsen/a61a37d14182ccd78760e477c78133cd" rel="noopener noreferrer"&gt;SwiftSync gist&lt;/a&gt; and the &lt;a href="https://delvingbitcoin.org/t/swiftsync-speeding-up-ibd-with-pre-generated-hints-poc/1562" rel="noopener noreferrer"&gt;Delving Bitcoin PoC thread&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://bitcoinops.org/en/topics/utreexo/" rel="noopener noreferrer"&gt;Bitcoin Optech: Utreexo topic&lt;/a&gt; — the maintained index of everything since the paper.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;Figures are mid-2026 mainnet approximations. The &lt;a href="https://nikicat.github.io/kilobyte-utxo-set/" rel="noopener noreferrer"&gt;interactive edition&lt;/a&gt; simplifies Utreexo's position bookkeeping (noted inline there); the root arithmetic is faithful. Corrections welcome via &lt;a href="https://github.com/nikicat/kilobyte-utxo-set" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>bitcoin</category>
      <category>cryptography</category>
      <category>datastructures</category>
    </item>
  </channel>
</rss>
