Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions grovedb-version/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use crate::version::merk_versions::*;
use crate::version::v1::GROVE_V1;
use crate::version::v2::GROVE_V2;
use crate::version::v3::GROVE_V3;
use crate::version::v4::GROVE_V4;
use crate::version::{GroveVersion, GROVE_VERSIONS};
use crate::{TryFromVersioned, TryIntoVersioned};

Expand Down Expand Up @@ -64,14 +65,14 @@ fn grove_version_first_returns_v1() {
}

#[test]
fn grove_version_latest_returns_v3() {
fn grove_version_latest_returns_v4() {
let latest = GroveVersion::latest();
assert_eq!(latest.protocol_version, GROVE_V3.protocol_version);
assert_eq!(latest.protocol_version, GROVE_V4.protocol_version);
}

#[test]
fn grove_versions_count() {
assert_eq!(GROVE_VERSIONS.len(), 3);
assert_eq!(GROVE_VERSIONS.len(), 4);
}

#[test]
Expand Down
4 changes: 3 additions & 1 deletion grovedb-version/src/version/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@ pub mod merk_versions;
pub mod v1;
pub mod v2;
pub mod v3;
pub mod v4;

pub use versioned_feature_core::*;

use crate::version::v3::GROVE_V3;
use crate::version::v4::GROVE_V4;
use crate::version::{
grovedb_versions::GroveDBVersions, merk_versions::MerkVersions, v1::GROVE_V1, v2::GROVE_V2,
};
Expand All @@ -32,4 +34,4 @@ impl GroveVersion {
}
}

pub const GROVE_VERSIONS: &[GroveVersion] = &[GROVE_V1, GROVE_V2, GROVE_V3];
pub const GROVE_VERSIONS: &[GroveVersion] = &[GROVE_V1, GROVE_V2, GROVE_V3, GROVE_V4];
265 changes: 265 additions & 0 deletions grovedb-version/src/version/v4.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,265 @@
//! Grove protocol version 4.
//!
//! Behaviourally **identical to [`GROVE_V3`](super::v3::GROVE_V3)** at the
//! moment it is introduced: every method-version slot carries the same value,
//! so activating protocol version 4 without gating anything on it is a no-op.
//!
//! It exists so that fixes which change released behaviour have somewhere to
//! land. GROVE_V3 is live, so a fix that alters an accepted/rejected outcome,
//! a committed root hash, or a tracked cost cannot be applied unconditionally
//! — nodes carrying it would diverge from nodes that do not. Such a fix bumps
//! the relevant slot here and branches on it, leaving V1..V3 untouched.
//!
//! When adding a gate, say what changes in the slot comment, the way
//! `add_element_on_transaction: 1` documents the layered-subtree switch in
//! v3.rs. `GroveVersion::latest()` returns the last entry of
//! `GROVE_VERSIONS`, so anything defaulting to "latest" starts exercising this
//! version as soon as it is registered.

use crate::version::grovedb_versions::GroveDBAggregateSumPathQueryMethodVersions;
use crate::version::{
grovedb_versions::{
GroveDBApplyBatchVersions, GroveDBElementMethodVersions,
GroveDBOperationsAverageCaseVersions, GroveDBOperationsDeleteUpTreeVersions,
GroveDBOperationsDeleteVersions, GroveDBOperationsGetVersions,
GroveDBOperationsInsertVersions, GroveDBOperationsProofVersions,
GroveDBOperationsQueryVersions, GroveDBOperationsVersions,
GroveDBOperationsWorstCaseVersions, GroveDBPathQueryMethodVersions, GroveDBQueryLimits,
GroveDBReplicationVersions, GroveDBVersions,
},
merk_versions::{
MerkAverageCaseCostsVersions, MerkBatchVersions, MerkProofVersions, MerkVersions,
},
GroveVersion,
};

pub const GROVE_V4: GroveVersion = GroveVersion {
protocol_version: 4,
grovedb_versions: GroveDBVersions {
apply_batch: GroveDBApplyBatchVersions {
apply_batch_structure: 0,
apply_body: 0,
continue_partial_apply_body: 0,
apply_operations_without_batching: 0,
apply_batch: 0,
apply_partial_batch: 0,
open_batch_transactional_merk_at_path: 0,
open_batch_merk_at_path: 0,
apply_batch_with_element_flags_update: 0,
apply_partial_batch_with_element_flags_update: 0,
estimated_case_operations_for_batch: 0,
},
element: GroveDBElementMethodVersions {
delete: 0,
delete_with_sectioned_removal_bytes: 0,
delete_into_batch_operations: 0,
element_at_key_already_exists: 0,
get: 0,
get_optional: 0,
get_from_storage: 0,
get_optional_from_storage: 1,
get_with_absolute_refs: 0,
get_value_hash: 0,
get_specialized_cost: 0,
value_defined_cost: 0,
value_defined_cost_for_serialized_value: 0,
specialized_costs_for_key_value: 0,
required_item_space: 0,
required_item_with_sum_item_space: 0,
required_reference_with_sum_item_space: 0,
insert: 0,
insert_into_batch_operations: 0,
insert_if_not_exists: 0,
insert_if_not_exists_into_batch_operations: 0,
insert_if_changed_value: 0,
insert_if_changed_value_into_batch_operations: 0,
insert_reference: 0,
insert_reference_into_batch_operations: 0,
insert_subtree: 0,
insert_subtree_into_batch_operations: 0,
get_query: 0,
get_aggregate_sum_query: 0,
get_query_values: 0,
get_query_apply_function: 0,
get_path_query: 0,
get_sized_query: 0,
get_aggregate_sum_query_apply_function: 0,
path_query_push: 0,
aggregate_sum_path_query_push: 0,
query_item: 0,
basic_push: 0,
basic_aggregate_sum_push: 0,
serialize: 0,
serialized_size: 0,
deserialize: 0,
get_with_value_hash: 0,
insert_reference_if_changed_value: 0,
aggregate_sum_query_item: 0,
},
operations: GroveDBOperationsVersions {
get: GroveDBOperationsGetVersions {
get: 0,
get_caching_optional: 0,
follow_reference: 0,
get_raw: 0,
get_raw_caching_optional: 0,
get_raw_optional: 0,
get_raw_optional_caching_optional: 0,
has_raw: 0,
check_subtree_exists_invalid_path: 0,
average_case_for_has_raw: 0,
average_case_for_has_raw_tree: 0,
average_case_for_get_raw: 0,
average_case_for_get: 0,
average_case_for_get_tree: 0,
worst_case_for_has_raw: 0,
worst_case_for_get_raw: 0,
worst_case_for_get: 0,
is_empty_tree: 0,
follow_reference_once: 0,
},
insert: GroveDBOperationsInsertVersions {
insert: 0,
insert_on_transaction: 0,
insert_without_transaction: 0,
// v1: non-batch insert writes CountSumTree / ProvableCountTree /
// ProvableCountSumTree as layered subtrees, consistent with the
// batch path. GROVE_V1 / GROVE_V2 keep v0 (Op::Put) to preserve
// the protocol-v11 consensus root (testnet block 245,344).
add_element_on_transaction: 1,
add_element_without_transaction: 0,
insert_if_not_exists: 0,
insert_if_not_exists_return_existing_element: 0,
insert_if_changed_value: 0,
},
delete: GroveDBOperationsDeleteVersions {
delete: 0,
clear_subtree: 0,
delete_with_sectional_storage_function: 0,
delete_if_empty_tree: 0,
delete_if_empty_tree_with_sectional_storage_function: 0,
delete_operation_for_delete_internal: 0,
delete_internal_on_transaction: 0,
delete_internal_without_transaction: 0,
average_case_delete_operation_for_delete: 0,
worst_case_delete_operation_for_delete: 0,
},
delete_up_tree: GroveDBOperationsDeleteUpTreeVersions {
delete_up_tree_while_empty: 0,
delete_up_tree_while_empty_with_sectional_storage: 0,
delete_operations_for_delete_up_tree_while_empty: 0,
add_delete_operations_for_delete_up_tree_while_empty: 0,
average_case_delete_operations_for_delete_up_tree_while_empty: 0,
worst_case_delete_operations_for_delete_up_tree_while_empty: 0,
},
query: GroveDBOperationsQueryVersions {
query_encoded_many: 0,
query_many_raw: 0,
get_proved_path_query: 0,
query: 0,
query_item_value: 0,
query_item_value_or_sum: 0,
query_aggregate_sums: 0,
query_aggregate_count_on_range: 0,
query_aggregate_sum_on_range: 0,
query_aggregate_count_and_sum_on_range: 0,
query_sums: 0,
query_raw: 0,
query_keys_optional: 0,
query_raw_keys_optional: 0,
follow_element: 0,
},
proof: GroveDBOperationsProofVersions {
prove_query: 0,
prove_query_many: 0,
prove_query_non_serialized: 1, // v1 supports MmrTree/BulkAppendTree proof generation
prove_trunk_chunk: 1,
prove_trunk_chunk_non_serialized: 1,
prove_branch_chunk: 0,
prove_branch_chunk_non_serialized: 0,
verify_query_with_options: 0,
verify_query_raw: 0,
verify_layer_proof: 0,
verify_query: 0,
verify_subset_query: 0,
verify_query_with_absence_proof: 0,
verify_subset_query_with_absence_proof: 0,
verify_query_with_chained_path_queries: 0,
verify_query_get_parent_tree_info_with_options: 0,
},
average_case: GroveDBOperationsAverageCaseVersions {
add_average_case_get_merk_at_path: 0,
average_case_merk_replace_tree: 1,
average_case_merk_insert_tree: 0,
average_case_merk_delete_tree: 0,
average_case_merk_insert_element: 0,
average_case_merk_replace_element: 0,
average_case_merk_patch_element: 0,
average_case_merk_delete_element: 0,
add_average_case_has_raw_cost: 0,
add_average_case_has_raw_tree_cost: 0,
add_average_case_get_raw_cost: 0,
add_average_case_get_raw_tree_cost: 0,
add_average_case_get_cost: 0,
},
worst_case: GroveDBOperationsWorstCaseVersions {
add_worst_case_get_merk_at_path: 0,
worst_case_merk_replace_tree: 0,
worst_case_merk_insert_tree: 0,
worst_case_merk_delete_tree: 0,
worst_case_merk_insert_element: 0,
worst_case_merk_replace_element: 0,
worst_case_merk_patch_element: 0,
worst_case_merk_delete_element: 0,
add_worst_case_has_raw_cost: 0,
add_worst_case_get_raw_tree_cost: 0,
add_worst_case_get_raw_cost: 0,
add_worst_case_get_cost: 0,
},
},
aggregate_sum_path_query_methods: GroveDBAggregateSumPathQueryMethodVersions { merge: 0 },
path_query_methods: GroveDBPathQueryMethodVersions {
terminal_keys: 0,
merge: 0,
query_items_at_path: 0,
should_add_parent_tree_at_path: 0,
},
replication: GroveDBReplicationVersions {
get_subtrees_metadata: 0,
fetch_chunk: 0,
start_snapshot_syncing: 0,
apply_chunk: 0,
},
query_limits: GroveDBQueryLimits {
max_aggregate_sum_query_elements_scanned: 1024,
},
},
merk_versions: MerkVersions {
batch: MerkBatchVersions { commit: 1 }, // return accumulated batch costs
average_case_costs: MerkAverageCaseCostsVersions {
// Bumped from 1 → 2: v2 fixes the Mix-arm divisor on both
// `replaced_bytes` and `storage_loaded_bytes`. v1 computed
// `Σ(weight_i · cost_i) / (nodes_updated · total_weight)`
// (off by a factor of `nodes_updated²` vs the equivalent
// homogeneous-layer cost); v2 computes
// `nodes_updated · Σ(weight_i · cost_i) / total_weight`.
add_average_case_merk_propagate: 2,
// Bumped from 1 → 2: v2 folds the four `Provable*` tree-type
// weight fields on `EstimatedSumTrees::SomeSumTrees` into the
// weighted-average formula. v0/v1 outputs are preserved for
// already-shipped grove versions (v1 → v0 formula, v2 → v1
// formula).
sum_tree_estimated_size: 2,
// Bumped from 0 → 1: v1 fixes the Mix-arm "average" to a
// proper weighted average. v0 returned
// `Σ size_i / Σ weight_i` (low-biased and ignored weights);
// v1 returns `Σ (size_i · weight_i) / Σ weight_i`.
value_with_feature_and_flags_size: 1,
},
proof: MerkProofVersions {
// Initial implementation; introduced alongside the V1
// proof envelope.
prove_count_offset_on_range: 0,
},
},
};
Loading