The State Root: It Moves When State Moves and Holds When It Does Not, and You Cannot Open It
What it is for
A chain's state is a large amount of data. Balances, identifiers, credentials, validator records.
A state root is one value that commits to all of it. So two participants can compare thirty-two bytes instead of comparing databases, and a block can promise what the world looked like after it without carrying the world.
How ours is built
Four separate trees, one per kind of thing: accounts, identifiers, credentials, validators.
Each is a sparse Merkle tree, and their four roots are hashed together into the single value that appears in every block header.
And here is the two-sided control, which is what makes it a reading
A field that never changes tells you nothing. A field that changes constantly tells you nothing either.
Twelve consecutive blocks were fetched by canonical sequence. All twelve are empty, and all twelve carry a byte-identical root. Eleven transitions, zero movement.
And a root sampled earlier in this estate's measurements differs from the current one.
So it holds when nothing commits and moves when something does. That is a much stronger statement than either half alone, and it is the check worth running against any chain's state root before believing the field means anything.
What it costs to produce
The trees are rebuilt from scratch in memory on every computation and then discarded. The node scans every entry in each of the four collections, computes the roots, combines them, and throws the trees away.
The reason is written next to the code and it is a good one: the stored version wrote hundreds of tree nodes per insert that nothing ever read back, and it was the bulk of roughly a gigabyte a day of disk growth.
The consequence on an idle chain is mildly funny and worth stating: every ten minutes this chain produces an empty block, and to do so it rescans its entire state to recompute a value that is identical to the last one.
At this size that costs nothing. It is work proportional to all of state rather than to what changed, which is the first thing that stops being free.
And you cannot open the commitment
A commitment is useful because you can later prove something was inside it.
No endpoint returns such a proof. The functions to produce and verify one exist in the code, and a contract-level primitive already consumes one, but nothing serves them.
So a reader can see the root and cannot show that any particular balance, identifier or credential is under it. The value is a promise with no way to redeem it, from outside.
And there is nothing to compare it against
The first purpose listed above was comparing two participants.
This deployment has one process and one database, so there is no second copy whose root could agree or disagree with this one.
A matching state root between two nodes is evidence. A state root from one node is a claim, and this network can only offer the second.
The two chains expose it differently
The other network this project runs answers a direct call for its state root. This one does not, and offers the value only inside a block header.
Neither is wrong. It is one more place where the two chains' interfaces do not line up, and a tool written against one will not work against the other.
What you can check yourself
solidus_canonHead, thensolidus_getBlockBySeqdownward, comparingstate_rootandtx_count
Across empty blocks the root should not move. Then look for a way to prove any single account is underneath it, and note there is none.
Keep reading
- Finality Here Is Deterministic, and the Wait Is Not Two Rounds. It Is Two Rounds Whenever They Happen
- Fork Choice: The Rule Is the Highest Certificate, and On This Deployment There Is One Database Behind All Four Validators
- The Genesis Block: You Cannot Obtain Ours, and the Hash That Claims to Identify It Is a Hash of Its Name
- The Nonce: What a Failed Transaction Does to It, and How This Deployment Had to Be Defended From Itself