Skip to content

Commit c17917c

Browse files
committed
Unify branches in subtree consistency proof
See davidben/merkle-tree-certs#149 for details. As an added bonus, this removes a `clone()` of the proof.
1 parent f05c734 commit c17917c

File tree

1 file changed

+75
-86
lines changed

1 file changed

+75
-86
lines changed

crates/tlog_tiles/src/tlog.rs

Lines changed: 75 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -510,7 +510,9 @@ pub fn verify_inclusion_proof(
510510
f_n >>= 1;
511511
s_n >>= 1;
512512
}
513-
// 5. Compare sn to 0. Compare r against the root_hash. If sn is equal to 0 and r and the root_hash are equal, then the log has proven the inclusion of hash. Otherwise, fail the proof verification.
513+
// 5. Compare sn to 0. Compare r against the root_hash. If sn is equal to 0
514+
// and r and the root_hash are equal, then the log has proven the
515+
// inclusion of hash. Otherwise, fail the proof verification.
514516
if s_n == 0 && r == root_hash {
515517
Ok(())
516518
} else {
@@ -646,10 +648,6 @@ pub fn verify_consistency_proof(
646648
/// # Errors
647649
///
648650
/// Will return an error if proof verification fails.
649-
///
650-
/// # Panics
651-
///
652-
/// Will panic if there are internal math errors.
653651
pub fn verify_subtree_consistency_proof(
654652
proof: &Proof,
655653
n: u64,
@@ -658,101 +656,90 @@ pub fn verify_subtree_consistency_proof(
658656
subtree_hash: Hash,
659657
) -> Result<(), TlogError> {
660658
let Subtree { lo: start, hi: end } = *m;
661-
// 1. If end is n, run the following:
662-
if end == n {
663-
// 1. Set fn to start and sn to end - 1.
664-
let mut f_n = start;
665-
let mut s_n = end - 1;
666-
// 2. Set r to node_hash.
667-
let mut r = subtree_hash;
668-
// 3. Until LSB(fn) is set or sn is 0, right-shift fn and sn equally.
669-
while !lsb_set(f_n) && s_n != 0 {
659+
660+
// Check that `[start, end)` is a valid subtree, and that `end <= n`. If either do not hold, fail proof verification. These checks imply `0 <= start < end <= n`.
661+
if end > n {
662+
return Err(TlogError::InvalidProof);
663+
}
664+
665+
// Set `fn` to `start`, `sn` to `end - 1`, and `tn` to `n - 1`.
666+
let mut f_n = start;
667+
let mut s_n = end - 1;
668+
let mut t_n = n - 1;
669+
let mut f_r: Hash;
670+
let mut s_r: Hash;
671+
// If `sn` is `tn`, then:
672+
if s_n == t_n {
673+
// Until `fn` is `sn`, right-shift `fn`, `sn`, and `tn` equally.
674+
while f_n != s_n {
670675
f_n >>= 1;
671676
s_n >>= 1;
677+
t_n >>= 1;
672678
}
673-
// 4. For each value p in the proof array:
674-
for p in proof {
675-
// 1. If sn is 0, then stop iteration and fail the proof verification.
676-
if s_n == 0 {
677-
return Err(TlogError::InvalidProof);
678-
}
679-
// 2. Set r to HASH(0x01, || p || r).
680-
r = node_hash(*p, r);
681-
// 3. Until LSB(sn) is set, right-shift sn.
682-
while !lsb_set(s_n) {
683-
s_n >>= 1;
684-
}
685-
// 4. Right-shift sn once more.
679+
} else
680+
// Otherwise:
681+
{
682+
// Until `LSB(sn)` is not set or `fn` is `sn`, right-shift `fn`, `sn`, and `tn` equally.
683+
while lsb_set(s_n) && f_n != s_n {
684+
f_n >>= 1;
686685
s_n >>= 1;
687-
}
688-
// 5. Compare sn to 0 and r to root_hash. If either is not equal, fail the proof verification. If all are equal, accept the proof.
689-
if s_n == 0 && r == root_hash {
690-
Ok(())
691-
} else {
692-
Err(TlogError::InvalidProof)
686+
t_n >>= 1;
693687
}
694688
}
695-
// 2. Otherwise, run the following:
696-
else {
697-
// 1. If proof is an empty array, stop and fail verification.
689+
// If `fn` is `sn`, set `fr` and `sr` to `node_hash`.
690+
let proof_offset;
691+
if f_n == s_n {
692+
f_r = subtree_hash;
693+
s_r = subtree_hash;
694+
proof_offset = 0;
695+
} else
696+
// Otherwise:
697+
{
698+
// If `proof` is an empty array, stop and fail verification.
698699
if proof.is_empty() {
699700
return Err(TlogError::InvalidProof);
700701
}
701-
// 2. If end - start is an exact power of 2, prepend node_hash to the proof array.
702-
let mut proof = proof.clone();
703-
if (end - start).is_power_of_two() {
704-
proof.insert(0, subtree_hash);
705-
}
706-
// 3. Set fn to start, sn to end - 1, and tn to n - 1.
707-
let mut f_n = start;
708-
let mut s_n = end - 1;
709-
let mut t_n = n - 1;
710-
// 4. Until LSB(sn) is not set or fn is equal to sn, right-shift fn, sn, and tn equally.
711-
while lsb_set(s_n) && f_n != s_n {
712-
f_n >>= 1;
713-
s_n >>= 1;
714-
t_n >>= 1;
702+
// Remove the first value of the `proof` array and set `fr` and `sr` to the removed value.
703+
f_r = proof[0];
704+
s_r = proof[0];
705+
proof_offset = 1;
706+
}
707+
// For each value `c` in the `proof` array:
708+
for &c in proof.iter().skip(proof_offset) {
709+
// If `tn` is `0`, then stop the iteration and fail the proof verification.
710+
if t_n == 0 {
711+
return Err(TlogError::InvalidProof);
715712
}
716-
// 5. Set both fr and sr to the first value in the proof array.
717-
let mut f_r = proof[0];
718-
let mut s_r = proof[0];
719-
// 6. For each subsequent value c in the proof array:
720-
for c in proof.into_iter().skip(1) {
721-
// 1. If tn is 0, then stop the iteration and fail the proof verification.
722-
if t_n == 0 {
723-
return Err(TlogError::InvalidProof);
713+
// If `LSB(sn)` is set, or if `sn` is equal to `tn`, then:
714+
if lsb_set(s_n) || s_n == t_n {
715+
// If `fn < sn`, set `fr` to `HASH(0x01 || c || fr)`.
716+
if f_n < s_n {
717+
f_r = node_hash(c, f_r);
724718
}
725-
// 2. If LSB(sn) is set, or if sn is equal to tn, then:
726-
if lsb_set(s_n) || s_n == t_n {
727-
// 1. If fn < sn, set fr to HASH(0x01 || c || fr).
728-
if f_n < s_n {
729-
f_r = node_hash(c, f_r);
730-
}
731-
// 2. Set sr to HASH(0x01 || c || sr).
732-
s_r = node_hash(c, s_r);
733-
// 3. Until LSB(sn) is set, right-shift fn, sn, and tn equally.
734-
while !lsb_set(s_n) {
735-
f_n >>= 1;
736-
s_n >>= 1;
737-
t_n >>= 1;
738-
}
739-
}
740-
// 3. Otherwise:
741-
else {
742-
// 1. Set sr to HASH(0x01 || sr || c).
743-
s_r = node_hash(s_r, c);
719+
// Set `sr` to `HASH(0x01 || c || sr)`.
720+
s_r = node_hash(c, s_r);
721+
// Until `LSB(sn)` is set, right-shift `fn`, `sn`, and `tn` equally.
722+
while !lsb_set(s_n) {
723+
f_n >>= 1;
724+
s_n >>= 1;
725+
t_n >>= 1;
744726
}
745-
// 4. Right-shift fn, sn, and tn once more.
746-
f_n >>= 1;
747-
s_n >>= 1;
748-
t_n >>= 1;
749727
}
750-
// 7. Compare tn to 0, fr to node_hash, and sr to root_hash. If any are not equal, fail the proof verification. If all are equal, accept the proof.
751-
if t_n == 0 && f_r == subtree_hash && s_r == root_hash {
752-
Ok(())
753-
} else {
754-
Err(TlogError::InvalidProof)
728+
// Otherwise:
729+
else {
730+
// Set `sr` to `HASH(0x01 || sr || c)`.
731+
s_r = node_hash(s_r, c);
755732
}
733+
// Right-shift `fn`, `sn`, and `tn` once more.
734+
f_n >>= 1;
735+
s_n >>= 1;
736+
t_n >>= 1;
737+
}
738+
// Compare `tn` to `0`, `fr` to `node_hash`, and `sr` to `root_hash`. If any are not equal, fail the proof verification. If all are equal, accept the proof.
739+
if t_n == 0 && f_r == subtree_hash && s_r == root_hash {
740+
Ok(())
741+
} else {
742+
Err(TlogError::InvalidProof)
756743
}
757744
}
758745

@@ -1321,6 +1308,8 @@ mod tests {
13211308
verify_consistency_proof(&vec![], 0, EMPTY_HASH, 0, EMPTY_HASH).unwrap();
13221309
verify_consistency_proof(&vec![], 0, EMPTY_HASH, 1, EMPTY_HASH).unwrap_err();
13231310
verify_consistency_proof(&vec![], 0, Hash::default(), 0, EMPTY_HASH).unwrap_err();
1311+
verify_consistency_proof(&vec![Hash::default()], 0, Hash::default(), 1, EMPTY_HASH)
1312+
.unwrap_err();
13241313

13251314
// Tree with single leaf.
13261315
verify_inclusion_proof(&vec![], 1, Hash::default(), 0, Hash::default()).unwrap();

0 commit comments

Comments
 (0)