0g-storage-node/common/append_merkle/src/proof.rs
peilun-conflux 19d9d31b0c
Fix proof data update in corner cases. (#13)
* Fix proof data update in corner cases.

* Fix fmt.
2024-01-28 16:52:28 +08:00

232 lines
7.2 KiB
Rust

use crate::{ensure_eq, Algorithm, HashElement};
use anyhow::{bail, ensure, Result};
use serde::{Deserialize, Serialize};
use ssz_derive::{Decode as DeriveDecode, Encode as DeriveEncode};
#[derive(Clone, Debug, Eq, PartialEq, DeriveEncode, DeriveDecode, Deserialize, Serialize)]
pub struct Proof<T: HashElement> {
lemma: Vec<T>,
path: Vec<bool>,
}
impl<T: HashElement> Proof<T> {
/// Creates new MT inclusion proof
pub fn new(hash: Vec<T>, path: Vec<bool>) -> Proof<T> {
assert_eq!(hash.len() - 2, path.len());
Proof { lemma: hash, path }
}
pub fn new_empty() -> Proof<T> {
Proof {
lemma: vec![],
path: vec![],
}
}
/// Return proof target leaf
pub fn item(&self) -> T {
self.lemma.first().unwrap().clone()
}
/// Return tree root
pub fn root(&self) -> T {
self.lemma.last().unwrap().clone()
}
/// Verifies MT inclusion proof
fn validate_integrity<A: Algorithm<T>>(&self) -> bool {
let size = self.lemma.len();
if size < 2 {
return false;
}
let mut h = self.item();
for i in 1..size - 1 {
h = if self.path[i - 1] {
A::parent(&h, &self.lemma[i])
} else {
A::parent(&self.lemma[i], &h)
};
}
h == self.root()
}
pub fn validate<A: Algorithm<T>>(&self, item: &T, position: usize) -> Result<()> {
if !self.validate_integrity::<A>() {
bail!("Invalid proof");
}
if *item != self.item() {
bail!("Proof item unmatch");
}
if position != self.position() {
bail!("Proof position unmatch");
}
Ok(())
}
/// Returns the path of this proof.
pub fn path(&self) -> &[bool] {
&self.path
}
/// Returns the lemma of this proof.
pub fn lemma(&self) -> &[T] {
&self.lemma
}
pub fn position(&self) -> usize {
let mut pos = 0;
for (i, is_left) in self.path.iter().enumerate() {
if !is_left {
pos += 1 << i;
}
}
pos
}
/// Return `Vec<(index_in_layer, data)>`.
pub fn proof_nodes_in_tree(&self) -> Vec<(usize, T)> {
let mut r = Vec::with_capacity(self.lemma.len());
let mut pos = 0;
r.push((0, self.root()));
for (i, is_left) in self.path.iter().rev().enumerate() {
pos <<= 1;
if !*is_left {
pos += 1;
}
let lemma_pos = if *is_left { pos + 1 } else { pos - 1 };
r.push((lemma_pos, self.lemma[self.lemma.len() - 2 - i].clone()));
}
r.reverse();
r
}
pub fn file_proof_nodes_in_tree(
&self,
tx_merkle_nodes: Vec<(usize, T)>,
tx_merkle_nodes_size: usize,
) -> Vec<(usize, T)> {
let mut r = Vec::with_capacity(self.lemma.len());
let mut subtree_pos = 0;
let mut root_pos = 0;
let mut in_subtree = tx_merkle_nodes_size == 1;
for (i, is_left) in self.path.iter().rev().enumerate() {
if !in_subtree {
if *is_left {
in_subtree = true;
root_pos >>= tx_merkle_nodes[i].0;
} else if i < tx_merkle_nodes.len() {
root_pos += 1 << tx_merkle_nodes[i].0;
// This is the last node, so there is no more intermediate node.
if i == tx_merkle_nodes_size - 2 {
if i + 1 >= tx_merkle_nodes.len() {
break;
} else {
in_subtree = true;
root_pos >>= tx_merkle_nodes[i + 1].0;
}
}
} else {
break;
}
} else {
subtree_pos <<= 1;
root_pos <<= 1;
if !*is_left {
subtree_pos += 1;
}
let lemma_pos = if *is_left {
root_pos + subtree_pos + 1
} else {
root_pos + subtree_pos - 1
};
r.push((lemma_pos, self.lemma[self.lemma.len() - 2 - i].clone()));
}
}
r.reverse();
r
}
}
#[derive(Clone, Debug, Eq, PartialEq, DeriveEncode, DeriveDecode, Deserialize, Serialize)]
pub struct RangeProof<E: HashElement> {
pub left_proof: Proof<E>,
pub right_proof: Proof<E>,
}
impl<E: HashElement> RangeProof<E> {
pub fn new_empty() -> Self {
Self {
left_proof: Proof::new_empty(),
right_proof: Proof::new_empty(),
}
}
fn validate_integrity<A: Algorithm<E>>(&self) -> bool {
self.left_proof.validate_integrity::<A>()
&& self.right_proof.validate_integrity::<A>()
&& self.left_proof.root() == self.right_proof.root()
&& self.left_proof.path().len() == self.right_proof.path().len()
}
pub fn root(&self) -> E {
self.left_proof.root()
}
pub fn validate<A: Algorithm<E>>(
&self,
range_leaves: &[E],
start_position: usize,
) -> Result<()> {
if !self.validate_integrity::<A>() {
bail!("Invalid range proof");
}
if range_leaves.is_empty() {
bail!("Empty range");
}
let end_position = start_position + range_leaves.len() - 1;
ensure_eq!(self.left_proof.item(), range_leaves[0]);
ensure_eq!(
self.right_proof.item(),
*range_leaves.last().expect("not empty")
);
ensure_eq!(self.left_proof.position(), start_position);
ensure_eq!(self.right_proof.position(), end_position);
let tree_depth = self.left_proof.path().len() + 1;
// TODO: We can avoid copying the first layer.
let mut children_layer = range_leaves.to_vec();
for height in 0..(tree_depth - 1) {
let mut parent_layer = Vec::new();
let start_index = if !self.left_proof.path()[height] {
// If the left-most node is the right child, its sibling is not within the data range and should be retrieved from the proof.
let parent = A::parent(&self.left_proof.lemma()[height + 1], &children_layer[0]);
parent_layer.push(parent);
1
} else {
// The left-most node is the left child, its sibling is just the next child.
0
};
let mut iter = children_layer[start_index..].chunks_exact(2);
while let Some([left, right]) = iter.next() {
parent_layer.push(A::parent(left, right))
}
if let [right_most] = iter.remainder() {
if self.right_proof.path()[height] {
parent_layer.push(A::parent(right_most, &self.right_proof.lemma()[height + 1]));
} else {
bail!("Unexpected error");
}
}
children_layer = parent_layer;
}
assert_eq!(children_layer.len(), 1);
let computed_root = children_layer.pop().unwrap();
ensure_eq!(computed_root, self.root());
Ok(())
}
}