Skip to content

CompactProof: Deduplicate values - #233

Merged
skunert merged 20 commits into
masterfrom
compact-proof
Aug 19, 2026
Merged

CompactProof: Deduplicate values#233
skunert merged 20 commits into
masterfrom
compact-proof

Conversation

@skunert

@skunert skunert commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

Summary

encode_compact re-emits a shared detached value once per referencing node, causing proof size to grow with reference count instead of value count. Adds opt-in encode_compact_skip_duplicate_values to dedupe on encode. decode_compact_from_iter (and decode_compact) now always handle both old-style and deduplicated proofs correctly. Also fixes an off-by-one in decode_compact_from_iter's returned item count when the last node has an attached value.

Compatibility

  • encode_compact / decode_compact_from_iter behavior unchanged — new functions are additive.
  • Deduplicated proofs decode correctly with this crate's decoder, and with an unpatched old decoder into a hash-keyed MemoryDB (verified against 0.31.0) — but not into a PrefixedKey DB, which needs the value re-inserted per prefix.
  • Old decoder + hash-keyed DB: refcount for the shared value is undercounted (1 instead of N). Reads still work (get/contains only check rc > 0); This matters if you later do balanced insert/remove against that decoded DB.
  • decode_compact's item count is now correct when the last node has an attached value — previously it was off by one in that case.

skunert and others added 5 commits July 7, 2026 16:41
encode_compact emits a detached value node once per referencing trie
node, so a value shared by N keys is sent N times (paritytech/polkadot-sdk#12565).
Add encode_compact_skip_duplicate_values to emit each distinct value
once; repeats are emitted unmodified and stay decodable by existing
hash-keyed decoders. For prefixed databases,
decode_compact_from_iter_with_known_values re-inserts deduplicated
values at every referencing position.
A trailing attached value node was not counted in the returned
used-item count, so decoding concatenated encodings at that offset
re-read the value bytes as a node.
trie_codec_proof only ran ExtensionLayout (inline values), so value
detachment and deduplication were never fuzzed. Round-trip the
deduplicating encoding into hash-keyed and prefixed databases, add a
hashed-value target with heavily shared values, and a deterministic
smoke test so the assertions run in CI without libFuzzer. Also fix the
fuzz crate's stale memory-db path dependency version.
An old decoder inserts a deduplicated value once, so its refcount in a
hash-keyed database understates the referencing nodes; a consumer
consolidating removals could drop a still-referenced value. The
re-inserting decoder restores exact parity. Assert full database
equality (entries and refcounts, hash-keyed and prefixed) between the
plain and deduplicated encodings.
@skunert
skunert requested a review from bkchr July 7, 2026 15:12

@bkchr bkchr left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can do the de-duplication on the node level and not just on the value level.

Comment thread trie-db/src/trie_codec.rs Outdated
Comment thread trie-db/src/trie_codec.rs Outdated
Comment thread trie-db/src/trie_codec.rs Outdated
skunert added 3 commits July 13, 2026 12:25
Vendor the compact-proof decoder from the released trie-db 0.31.0
verbatim into the test crate as a frozen snapshot of deployed decoder
behavior, and assert that encodings produced by
encode_compact_skip_duplicate_values decode with it into a hash-keyed
database with every entry readable. This pins the backward
compatibility the deduplicating encoder relies on, instead of leaving
it as an argument in documentation.
encode_compact_skip_duplicates now emits each distinct trie node once, not
just each detached value. A later occurrence of an already-emitted subtree
keeps a plain hash reference (like any reference outside the partial trie),
so the encoding never grows. The decoder threads a known-items map and
re-inserts skipped subtrees at every position, reconstructing the same
database as an un-deduplicated encoding.
@skunert

skunert commented Jul 13, 2026

Copy link
Copy Markdown
Contributor Author

You can do the de-duplication on the node level and not just on the value level.

We now also deduplicate nodes

Comment thread trie-db/src/iterator.rs Outdated
Comment thread trie-db/src/trie_codec.rs Outdated
Comment thread trie-db/src/trie_codec.rs Outdated
Comment thread trie-db/src/trie_codec.rs Outdated
Comment thread trie-db/src/trie_codec.rs Outdated
Comment thread trie-db/src/trie_codec.rs Outdated
Comment thread trie-db/src/trie_codec.rs Outdated
Comment thread trie-db/src/trie_codec.rs Outdated
Comment thread trie-db/src/trie_codec.rs Outdated
Comment thread trie-db/src/trie_codec.rs Outdated
Comment thread trie-db/src/trie_codec.rs Outdated
@skunert
skunert requested a review from cheme July 15, 2026 12:42
@skunert

skunert commented Jul 15, 2026

Copy link
Copy Markdown
Contributor Author

@cheme Would be super nice if you could support us with a review here :)

@cheme

cheme commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

@cheme Would be super nice if you could support us with a review here :)

Sure, will look at it (may not be able to do tomorrow, but will try to do before next week).

@cheme cheme left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using seen_hashes make sense to me, I think it is good (should even be extended node hashes).
What I am not too sure, is why we try to use prefixedmemorydb or have proper rc count.

Comment thread trie-db/test/src/trie_codec.rs Outdated
Comment thread trie-db/src/trie_codec.rs
Comment thread trie-db/src/trie_codec.rs
Comment thread trie-db/src/trie_codec.rs Outdated
Comment thread trie-db/src/trie_codec.rs Outdated
Comment thread trie-db/src/trie_codec.rs
Comment thread trie-db/src/trie_codec.rs Outdated
Comment thread trie-db/src/trie_codec.rs Outdated

@lexnv lexnv left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM! Had a look over comments and addresses them in #234

Maybe one extra thing before merging would be 855ad48

@skunert

skunert commented Aug 5, 2026

Copy link
Copy Markdown
Contributor Author

@cheme @bkchr Sorry for the delay (was off for some time), did another pass and removed the RC reconstruction and subtree reinsertion during decoding. Substrate does not need it at all, and it was complicating the code.

The reinsertion had another problem, a deduplicated proof emits each shared subtree once, but the reinserting decoder replayed it at every referencing position. Nested duplication makes that unfold exponentially costing exponential decode time. To fix this we would have needed to introduce some kind of decoding budget on the decoder side. Not worth it IMO.

And if needed, it is still possible to arrive at a correct PrefixedDB from the HashDB.

@cheme cheme left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me.
Note that I did not go through all testing and fuzzing code path, from a quick glance, maybe there is a bit of code that is not so needed anymore (regarding latest simplification).

Comment thread trie-db/src/trie_codec.rs
last_entry.child_index += 1;
} else {
return Ok((node_hash, i + 1))
return Ok((node_hash, i + 1 + attached_node))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess this change means major version update.

@lexnv lexnv left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Had a brief look, this also incorporates the hashing optimization via Copy (ie TrieHash<L>)

One tiny thing that can be a followup would be to turn Clippy/Fmt unused/wrongly formatting warnings into errors at compile time such the CI would fail instead of the bot leaving comments on the PR, worth considering later 🙏

Comment thread trie-db/src/trie_codec.rs Outdated
@skunert
skunert merged commit 1a3a6a3 into master Aug 19, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants