bugfix: TSMT failed to verify empty word for depth 64.
When a prefix is pushed to the depth 64, the entry list includes only the values different than ZERO. This is required, since each block represents a 2^192 values. The bug was in the proof membership code, that failed to handle the case of a key that was not in the list, because the depth is 64 and the value was not set.
This commit is contained in:
parent
18310a89f0
commit
df2650eb1f
2 changed files with 52 additions and 12 deletions
|
@ -85,18 +85,26 @@ impl TieredSmtProof {
|
||||||
/// Note: this method cannot be used to assert non-membership. That is, if false is returned,
|
/// Note: this method cannot be used to assert non-membership. That is, if false is returned,
|
||||||
/// it does not mean that the provided key-value pair is not in the tree.
|
/// it does not mean that the provided key-value pair is not in the tree.
|
||||||
pub fn verify_membership(&self, key: &RpoDigest, value: &Word, root: &RpoDigest) -> bool {
|
pub fn verify_membership(&self, key: &RpoDigest, value: &Word, root: &RpoDigest) -> bool {
|
||||||
if self.is_value_empty() {
|
// Handles the following scenarios:
|
||||||
if value != &EMPTY_VALUE {
|
// - the value is set
|
||||||
return false;
|
// - empty leaf, there is an explicit entry for the key with the empty value
|
||||||
}
|
// - shared 64-bit prefix, the target key is not included in the entries list, the value is implicitly the empty word
|
||||||
// if the proof is for an empty value, we can verify it against any key which has a
|
let v = match self.entries.iter().find(|(k, _)| k == key) {
|
||||||
// common prefix with the key storied in entries, but the prefix must be greater than
|
Some((_, v)) => v,
|
||||||
// the path length
|
None => &EMPTY_VALUE,
|
||||||
let common_prefix_tier = get_common_prefix_tier_depth(key, &self.entries[0].0);
|
};
|
||||||
if common_prefix_tier < self.path.depth() {
|
|
||||||
return false;
|
// The value must match for the proof to be valid
|
||||||
}
|
if v != value {
|
||||||
} else if !self.entries.contains(&(*key, *value)) {
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the proof is for an empty value, we can verify it against any key which has a common
|
||||||
|
// prefix with the key storied in entries, but the prefix must be greater than the path
|
||||||
|
// length
|
||||||
|
if self.is_value_empty()
|
||||||
|
&& get_common_prefix_tier_depth(key, &self.entries[0].0) < self.path.depth()
|
||||||
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -715,6 +715,38 @@ fn tsmt_bottom_tier_two() {
|
||||||
// GET PROOF TESTS
|
// GET PROOF TESTS
|
||||||
// ================================================================================================
|
// ================================================================================================
|
||||||
|
|
||||||
|
/// Tests the membership and non-membership proof for a single at depth 64
|
||||||
|
#[test]
|
||||||
|
fn tsmt_get_proof_single_element_64() {
|
||||||
|
let mut smt = TieredSmt::default();
|
||||||
|
|
||||||
|
let raw_a = 0b_00000000_00000001_00000000_00000001_00000000_00000001_00000000_00000001_u64;
|
||||||
|
let key_a = [ONE, ONE, ONE, raw_a.into()].into();
|
||||||
|
let value_a = [ONE, ONE, ONE, ONE];
|
||||||
|
smt.insert(key_a, value_a);
|
||||||
|
|
||||||
|
// push element `a` to depth 64, by inserting another value that shares the 48-bit prefix
|
||||||
|
let raw_b = 0b_00000000_00000001_00000000_00000001_00000000_00000001_00000000_00000000_u64;
|
||||||
|
let key_b = [ONE, ONE, ONE, raw_b.into()].into();
|
||||||
|
smt.insert(key_b, [ONE, ONE, ONE, ONE]);
|
||||||
|
|
||||||
|
// verify the proof for element `a`
|
||||||
|
let proof = smt.prove(key_a);
|
||||||
|
assert!(proof.verify_membership(&key_a, &value_a, &smt.root()));
|
||||||
|
|
||||||
|
// check that a value that is not inserted in the tree produces a valid membership proof for the
|
||||||
|
// empty word
|
||||||
|
let key = [ZERO, ZERO, ZERO, ZERO].into();
|
||||||
|
let proof = smt.prove(key);
|
||||||
|
assert!(proof.verify_membership(&key, &EMPTY_WORD, &smt.root()));
|
||||||
|
|
||||||
|
// check that a key that shared the 64-bit prefix with `a`, but is not inserted, also has a
|
||||||
|
// valid membership proof for the empty word
|
||||||
|
let key = [ONE, ONE, ZERO, raw_a.into()].into();
|
||||||
|
let proof = smt.prove(key);
|
||||||
|
assert!(proof.verify_membership(&key, &EMPTY_WORD, &smt.root()));
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn tsmt_get_proof() {
|
fn tsmt_get_proof() {
|
||||||
let mut smt = TieredSmt::default();
|
let mut smt = TieredSmt::default();
|
||||||
|
|
Loading…
Add table
Reference in a new issue