Remove max_depth in native MerkleTree (#442)

This simplifies the MerkleTree (and container) API.
Defer the max depth check when assigning the witness (merkle proof siblings) to the merkle tree circuit.

In this implementation the native Merkle Tree branches grow as much as they needed.  There are no checks of max depth in the merkle tree.  All keys are 256 bits (I added a debug_assert for this); so in the worst case a path will have depth 256.  It can't have a longer depth because the `insert` method calls `prove_nonexistence` which errors if the key already exists; another one may exist which must be different and thus require a path <= 256 depth. 

Resolve #436
This commit is contained in:
Eduard S. 2025-12-16 13:18:49 +01:00 committed by GitHub
parent 32dc85471d
commit 813a86c670
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
18 changed files with 266 additions and 462 deletions

View file

@ -356,7 +356,7 @@ mod tests {
let mut mp_builder = MainPodBuilder::new(&params, vd_set);
let set_values: HashSet<Value> = [1, 2, 3].iter().map(|i| Value::from(*i)).collect();
let s1 = Set::new(params.max_depth_mt_containers, set_values)?;
let s1 = Set::new(set_values);
let s2 = 1;
let set_contains = mp_builder.pub_op(Operation::set_contains(s1, s2))?;

View file

@ -59,7 +59,7 @@ impl SignedDictBuilder {
pub fn sign<S: Signer>(&self, signer: &S) -> Result<SignedDict> {
// Sign committed KV store.
let dict = Dictionary::new(self.params.max_depth_mt_containers, self.kvs.clone())?;
let dict = Dictionary::new(self.kvs.clone());
// NOTE: This is the same way that `TypedValue::Dictionary` computes the `RawValue`
let msg_raw = RawValue::from(dict.commitment());
let signature = signer.sign(msg_raw);
@ -1026,11 +1026,11 @@ pub mod tests {
let vd_set = &*MOCK_VD_SET;
let mut builder = SignedDictBuilder::new(&params);
let dict = dict!(params.max_depth_mt_containers, {
let dict = dict!({
"a" => 1,
"b" => 2,
"c" => 3,
})?;
});
let dict_root = Value::from(dict.clone());
builder.insert("dict", dict_root);
@ -1042,7 +1042,7 @@ pub mod tests {
.pub_op(Operation::dict_signed_by(&signed_dict))
.unwrap();
let st0 = signed_dict.get_statement("dict").unwrap();
let local = dict!(32, {"key" => "a"})?;
let local = dict!({"key" => "a"});
let st1 = builder
.op(true, vec![], Operation::dict_contains(local, "key", "a"))
.unwrap();
@ -1115,7 +1115,7 @@ pub mod tests {
let vd_set = &*MOCK_VD_SET;
let mut builder = MainPodBuilder::new(&params, vd_set);
let empty_set = Set::new(params.max_depth_mt_containers, [].into())?;
let empty_set = Set::new([].into());
let mut set1 = empty_set.clone();
set1.insert(&1.into())?;
@ -1163,7 +1163,7 @@ pub mod tests {
let vd_set = &*MOCK_VD_SET;
let mut builder = MainPodBuilder::new(&params, vd_set);
let array1 = Array::new(params.max_depth_mt_containers, [1.into()].into())?;
let array1 = Array::new([1.into()].into());
let mut array2 = array1.clone();
array2.update(0, &5.into())?;
@ -1217,7 +1217,7 @@ pub mod tests {
"owner",
Value::from(pk),
))?;
let local = dict!(32, { "known_secret" => sk.clone() })?;
let local = dict!({ "known_secret" => sk.clone() });
let st1 = builder.priv_op(Operation::dict_contains(
local,
"known_secret",
@ -1262,7 +1262,7 @@ pub mod tests {
.pub_op(Operation::dict_signed_by(&signed_dict))
.unwrap();
let st0 = signed_dict.get_statement("owner").unwrap();
let local = dict!(32, {"known_secret" => SecretKey(BigUint::from(123u32))})?;
let local = dict!({"known_secret" => SecretKey(BigUint::from(123u32))});
let st1 = builder
.op(
true,
@ -1333,7 +1333,7 @@ pub mod tests {
let params = Params::default();
let vd_set = &*MOCK_VD_SET;
let mut builder = MainPodBuilder::new(&params, vd_set);
let local = dict!(32, {"a" => 3, "b" => 27}).unwrap();
let local = dict!({"a" => 3, "b" => 27});
let value_of_a = Statement::contains(local.clone(), "a", 3);
let value_of_b = Statement::contains(local.clone(), "b", 27);

View file

@ -89,19 +89,18 @@ mod tests {
#[test]
fn test_value_serialization() {
let params = &Params::default();
// Pairs of values and their expected serialized representations
let values = vec![
(TypedValue::String("hello".to_string()), "\"hello\""),
(TypedValue::Int(42), "{\"Int\":\"42\"}"),
(TypedValue::Bool(true), "true"),
(
TypedValue::Array(Array::new(params.max_depth_mt_containers, vec!["foo".into(), false.into()]).unwrap()),
"{\"max_depth\":32,\"array\":[\"foo\",false]}",
TypedValue::Array(Array::new(vec!["foo".into(), false.into()])),
"{\"array\":[\"foo\",false]}",
),
(
TypedValue::Dictionary(
Dictionary::new(params.max_depth_mt_containers, HashMap::from([
Dictionary::new(HashMap::from([
// The set of valid keys is equal to the set of valid JSON keys
("foo".into(), 123.into()),
// Empty strings are valid JSON keys
@ -115,13 +114,12 @@ mod tests {
// Keys can contain emojis
(("🥳".into()), "party time!".into()),
]))
.unwrap(),
),
"{\"max_depth\":32,\"kvs\":{\"\":\"baz\",\"\\u0000\":\"\",\" hi\":false,\"!@£$%^&&*()\":\"\",\"foo\":{\"Int\":\"123\"},\"🥳\":\"party time!\"}}",
"{\"kvs\":{\"\":\"baz\",\"\\u0000\":\"\",\" hi\":false,\"!@£$%^&&*()\":\"\",\"foo\":{\"Int\":\"123\"},\"🥳\":\"party time!\"}}",
),
(
TypedValue::Set(Set::new(params.max_depth_mt_containers, HashSet::from(["foo".into(), "bar".into()])).unwrap()),
"{\"max_depth\":32,\"set\":[\"bar\",\"foo\"]}",
TypedValue::Set(Set::new(HashSet::from(["foo".into(), "bar".into()]))),
"{\"set\":[\"bar\",\"foo\"]}",
),
];
@ -147,39 +145,21 @@ mod tests {
builder.insert("very_large_int", 1152921504606846976);
builder.insert(
"a_dict_containing_one_key",
Dictionary::new(
params.max_depth_mt_containers,
HashMap::from([
("foo".into(), 123.into()),
(
"an_array_containing_three_ints".into(),
Array::new(
params.max_depth_mt_containers,
vec![1.into(), 2.into(), 3.into()],
)
.unwrap()
.into(),
),
(
"a_set_containing_two_strings".into(),
Set::new(
params.max_depth_mt_containers,
HashSet::from([
Array::new(
params.max_depth_mt_containers,
vec!["foo".into(), "bar".into()],
)
.unwrap()
.into(),
"baz".into(),
]),
)
.unwrap()
.into(),
),
]),
)
.unwrap(),
Dictionary::new(HashMap::from([
("foo".into(), 123.into()),
(
"an_array_containing_three_ints".into(),
Array::new(vec![1.into(), 2.into(), 3.into()]).into(),
),
(
"a_set_containing_two_strings".into(),
Set::new(HashSet::from([
Array::new(vec!["foo".into(), "bar".into()]).into(),
"baz".into(),
]))
.into(),
),
])),
);
builder
}
@ -228,7 +208,7 @@ mod tests {
};
let mut vds = DEFAULT_VD_LIST.clone();
vds.push(rec_main_pod_circuit_data(&params).1.verifier_only.clone());
let vd_set = VDSet::new(params.max_depth_mt_vds, &vds).unwrap();
let vd_set = VDSet::new(&vds);
let (gov_id_builder, pay_stub_builder) = zu_kyc_sign_dict_builders(&params);
let signer = Signer(SecretKey(1u32.into()));