fix: version-gate add_element_on_transaction (v0=Op::Put, v1=layered) - #759
Conversation
…layered) PR #752 broadened the non-batch insert tree arm so CountSumTree / ProvableCountTree / ProvableCountSumTree are written as layered subtrees (Op::PutLayeredReference). grovedb <= v4.1.0 wrote them as plain values (Op::Put), and that is the behaviour frozen into the live protocol-v11 activation chain (testnet block 245,344, transition_to_version_11). The layered op computes a different parent value_hash — combine_hash(value_hash(serialized), NULL_HASH) instead of value_hash(serialized) — and therefore a different grovedb root, a consensus divergence on replay. Rather than revert unconditionally (cf. #757), split add_element_on_transaction into a versioned dispatch, mirroring the proof v0/v1 pattern: - v0: grovedb v4.1.0 behaviour — those three types take the Op::Put arm. Selected by GROVE_V1 / GROVE_V2, preserving the protocol-v11 root. - v1: current behaviour — those three types are layered, consistent with the batch insert path (both root hash and fee). Selected by GROVE_V3. v0/v1 live in their own files as frozen snapshots; they differ only in which match arm those three element types fall into. GROVE_V3's add_element_on_transaction version slot is bumped 0 -> 1; v1/v2 stay 0. Adds a consensus-guard test that replays the transition_to_version_11 shape and pins both roots: the v0 root is byte-identical to PR #757's protocol-v11 golden, and the v1 (layered) root differs. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
📝 WalkthroughWalkthroughThis PR refactors the non-batch ChangesVersioned non-batch insert implementation
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## develop #759 +/- ##
===========================================
+ Coverage 91.44% 91.47% +0.02%
===========================================
Files 237 240 +3
Lines 67298 67570 +272
===========================================
+ Hits 61540 61807 +267
- Misses 5758 5763 +5
🚀 New features to boost your workflow:
|
Adds exhaustive non-batch-insert coverage for both frozen snapshots (v0.rs / v1.rs): the layered-tree arm (every tree type), the commitment-tree arm, the append-tree arm (MMR / bulk-append / dense), the item arm, the reference arm, both override guards, and the empty-tree-only (value.is_some) guard — driven under GROVE_V1 (v0 / Op::Put) and GROVE_V3 (v1 / layered). Also asserts the dispatcher rejects an unknown version slot. Closes the codecov/patch gap on the new add_element_on_transaction module. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Resolves a conflict in grovedb/src/operations/insert/mod.rs where develop PR #759 moved `add_element_on_transaction` out of the inline function into a versioned dispatch submodule (insert/add_element_on_transaction/{mod,v0,v1}.rs), version-gated to preserve the grovedb v4.1.0 / protocol-v11 consensus root for the three grandfathered tree types. Resolution: accept develop's `insert/mod.rs` skeleton (the function is gone from this file - it lives in the submodule), and port my ProvableCountIndexedTree / ProvableSumIndexedTree / ProvableCountProvableSumIndexedTree match arms identically into both v0.rs and v1.rs. The v0/v1 divergence applies only to the three grandfathered types (CountSumTree / ProvableCountTree / ProvableCountSumTree); the new indexed-tree variants are v12-only, were never live on the v11 chain, and have identical layered-subtree behavior in both versions. 4060 workspace lib tests pass, clippy clean. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
What
Splits the non-batch insert helper
GroveDb::add_element_on_transactioninto a versioned dispatch (Platform-style: dispatcher + frozenv0/v1snapshots, each in its own file undergrovedb/src/operations/insert/add_element_on_transaction/):add_element_on_transactionslotCountSumTree/ProvableCountTree/ProvableCountSumTreeGROVE_V1,GROVE_V20→ v0Op::Put) — matches grovedb v4.1.0 / the live protocol-v11 chainGROVE_V31→ v1Op::PutLayeredReference) — current behaviour, consistent with the batch pathOnly
GROVE_V3's slot is bumped0 → 1;v1/v2stay0.Why — protocol-v11 consensus
add_element_on_transactiondecides whether an element is written as a layered subtree (Op::PutLayeredReference) or a plain value (Op::Put). The two ops compute a different parent-nodevalue_hash:In grovedb v4.1.0,
CountSumTree/ProvableCountTree/ProvableCountSumTreefell through_ => element.insert()→Op::Put. #752 (subtree dump/restore primitives) broadened the tree arm to include them →Op::PutLayeredReference, changing the parent node hash and therefore the grovedb root.These types are created on the protocol-v11 activation (
transition_to_version_11inserts anempty_provable_count_sum_treeand anempty_count_sum_treevia this non-batch path — testnet block 245,344), so the broadened dispatch makes a v11 node compute a different app-hash than the v4.1.0-era binaries that produced the canonical chain — a consensus divergence on replay.Why versioned instead of an unconditional revert (cf. #757)
#757 reverts the three types to
Op::Putfor all versions. This PR instead preserves both behaviours and gates them, so:GROVE_V1/GROVE_V2replay the protocol-v11 chain correctly (Op::Put), andGROVE_V3+ keep the layered behaviour, which makes the non-batch path agree with the batch path on both root hash and fee (the batch path has always written these three types as layered, viainsert_subtree_into_batch_operations+LayeredValueDefinedCost). That removes the long-standing batch/non-batch divergence going forward instead of freezing it.v0/v1are full frozen snapshots; they differ only in which match arm those three element types fall into.v0is intentionally "current body with those three arms moved toOp::Put" rather than a literal v4.1.0 copy, so it still handles the newer element types (ProvableSumTree,CommitmentTree, the append-trees,NonCountedwrappers) that v4.1.0 predates, while reproducing the v4.1.0 consensus root exactly for every type that existed then.Test
add_element_on_transaction_version_gate_provable_count_sum_tree_rootreplays thetransition_to_version_11shape (empty_sum_treecontrol at[56], thenempty_provable_count_sum_treeat[56,'c']) under both versions and pins both roots:GROVE_V1 → v0 → Op::Putreproduces the exact canonical root.GROVE_V3) root differs, and the gate is asserted to switch behaviour so it cannot silently collapse.Verification
cargo build -p grovedb— clean, no warningscargo clippy -p grovedb --lib— cleancargo test -p grovedb --lib— 1853 passed, 0 failed, 1 ignoredReviewer notes
latest() = GROVE_V3 = v1 = layered, which is today's behaviour, so alllatest()-based tests are unaffected. The behaviour change is limited toGROVE_V1/GROVE_V2, which now correctly useOp::Putfor these three types (the regressed layered behaviour was never on the canonical chain for those versions).ProvableSumTree/ProvableCountProvableSumTreekeep the layered behaviour in both v0 and v1 — they were never created via this path on the v11 chain.orchard0.14.0 pin) so it builds.🤖 Generated with Claude Code
Summary by CodeRabbit
Improvements
Tests