You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Platform is adding private document types: a data contract can declare a document type private — no public index structure; each document is stored as a hiding commitment (32B) plus a ciphertext of uniform, contract-declared size; recipients discover documents by scanning and trial-decrypting rather than querying indices. Uniqueness is enforced by a sibling tag set (existing ProvableCountTree, nullifier-style), and the state transitions are funded from the shielded pool — all Platform-side concerns.
Phase one covers non-modifiable private documents only: entries are write-once, nothing is ever updated, deleted, or proven-against later. That means the store needs no Sinsemilla frontier and no anchor — it is a pure append-only log with provable reads. (Modifiable private documents are a later phase built on the anchored DataCommitmentTree of #783 plus a nullifier set; explicitly out of scope here.)
This issue adds the element type for that store. Behaviorally it is fully generic — an append-only store of fixed-size opaque entries; GroveDB never interprets a "document" — the name simply describes its Platform use.
Design
ElementType::PrivateDocumentStore = 15 with counted semantics, plus NonCountedPrivateDocumentStore = 143 following the existing +128 convention (12–14 and 140–142 are taken by Mmr/BulkAppend/DenseAppendOnlyFixedSize trees; 15/143 are the next free pair). Matching TreeType variant carrying the chunk power.
Thin wrapper over BulkAppendTree — the same relationship CommitmentTree has to it, minus the Sinsemilla frontier.
Committed config: the element value carries {entry_size, chunk_power}, and the config is bound into the state root, e.g. blake3("pds_state" || config_hash || bulk_state_root) — so the declared entry size is consensus-visible and a proof can never be reinterpreted under a different config. Precomputed empty-root constant with a runtime-equivalence test (mirroring EMPTY_COMMITMENT_TREE_STATE_ROOT).
Insert validates exact entry size; there is no per-entry delete or update — immutability is enforced by the type, matching what the Platform grammar's default promises.
Reads: get-by-position, count (from the counted variant), and paginated position-range reads with proofs — the scanning hot path (every client walks "all entries since my cursor"). Range reads should be implemented at the BulkAppendTree layer so the anchored DataCommitmentTree (DataCommitmentTree: generic commitment tree for data (configurable hash domain, entry format, payload size) #783), which embeds a BulkAppendTree, inherits them for free.
Fail closed: new discriminants rejected outright by older versions; everything gated under the next GROVE_V* flag.
Work plan
1. Element + TreeType plumbing. New discriminants 15/143 in grovedb-element/src/element_type.rs, TreeType variant in merk/src/tree_type/mod.rs, config carrier + serialization in grovedb-element/src/element/{constructor,helpers,visualize}.rs.
3. Operations.private_document_store_insert (size-validated append), get_value, count in a new grovedb/src/operations/private_document_store.rs, sharing the BulkAppendTree storage adapter.
4. Range reads with proofs at the BulkAppendTree layer: paginated position-range fetch, chunk-aligned, with proof generation/verification in operations/proof/{generate,verify}.rs; absence falls out of the provable count (position ≥ count).
Note: grovedb/src/replication has no support for commitment-tree-family types today; PrivateDocumentStore inherits that state-sync gap and should be included when it is addressed (see the workstream-D2 note in #783).
Motivation
Platform is adding private document types: a data contract can declare a document type
private— no public index structure; each document is stored as a hidingcommitment (32B)plus aciphertextof uniform, contract-declared size; recipients discover documents by scanning and trial-decrypting rather than querying indices. Uniqueness is enforced by a sibling tag set (existingProvableCountTree, nullifier-style), and the state transitions are funded from the shielded pool — all Platform-side concerns.Phase one covers non-modifiable private documents only: entries are write-once, nothing is ever updated, deleted, or proven-against later. That means the store needs no Sinsemilla frontier and no anchor — it is a pure append-only log with provable reads. (Modifiable private documents are a later phase built on the anchored
DataCommitmentTreeof #783 plus a nullifier set; explicitly out of scope here.)This issue adds the element type for that store. Behaviorally it is fully generic — an append-only store of fixed-size opaque entries; GroveDB never interprets a "document" — the name simply describes its Platform use.
Design
ElementType::PrivateDocumentStore = 15with counted semantics, plusNonCountedPrivateDocumentStore = 143following the existing +128 convention (12–14 and 140–142 are taken by Mmr/BulkAppend/DenseAppendOnlyFixedSize trees; 15/143 are the next free pair). MatchingTreeTypevariant carrying the chunk power.BulkAppendTree— the same relationshipCommitmentTreehas to it, minus the Sinsemilla frontier.{entry_size, chunk_power}, and the config is bound into the state root, e.g.blake3("pds_state" || config_hash || bulk_state_root)— so the declared entry size is consensus-visible and a proof can never be reinterpreted under a different config. Precomputed empty-root constant with a runtime-equivalence test (mirroringEMPTY_COMMITMENT_TREE_STATE_ROOT).BulkAppendTreelayer so the anchoredDataCommitmentTree(DataCommitmentTree: generic commitment tree for data (configurable hash domain, entry format, payload size) #783), which embeds aBulkAppendTree, inherits them for free.GROVE_V*flag.Work plan
grovedb-element/src/element_type.rs,TreeTypevariant inmerk/src/tree_type/mod.rs, config carrier + serialization ingrovedb-element/src/element/{constructor,helpers,visualize}.rs.pds_statecomposite root with config hash; empty-root constant + equivalence test.private_document_store_insert(size-validated append),get_value,countin a newgrovedb/src/operations/private_document_store.rs, sharing theBulkAppendTreestorage adapter.BulkAppendTreelayer: paginated position-range fetch, chunk-aligned, with proof generation/verification inoperations/proof/{generate,verify}.rs; absence falls out of the provable count (position ≥ count).is_tree(),TryFrom<u8>, display names,merk/src/element/{costs,delete,get,reconstruct}.rs,replace_subtree_root,is_empty_tree, batch policy (batch/mod.rs,batch_structure.rs). Mechanical but consensus-critical.verify_grovedbintegrity walk (bulk-tree ↔ config consistency, entry-size check).batch/estimated_costs/{average_case,worst_case}_costs.rs,merk/src/tree_type/costs.rs.GROVE_V*gating on discriminants and ops; rejection tests against the previous version.Note:
grovedb/src/replicationhas no support for commitment-tree-family types today;PrivateDocumentStoreinherits that state-sync gap and should be included when it is addressed (see the workstream-D2 note in #783).Non-goals
DataCommitmentTree(DataCommitmentTree: generic commitment tree for data (configurable hash domain, entry format, payload size) #783).ProvableCountTree).