Skip to content

Commit

Permalink
Merge pull request #7 from Davidson-Souza/new-deletion-alg
Browse files Browse the repository at this point in the history
Implement calculate_roots
  • Loading branch information
kcalvinalvin authored Sep 16, 2022
2 parents b97133d + 8e5f797 commit 64ce26e
Show file tree
Hide file tree
Showing 3 changed files with 75 additions and 188 deletions.
224 changes: 39 additions & 185 deletions src/accumulator/proof.rs
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ impl Proof {
}

let mut calculated_roots = self
.create_root_candidates(del_hashes, stump)?
.calculate_roots(del_hashes, stump)?
.into_iter()
.peekable();

Expand All @@ -137,115 +137,18 @@ impl Proof {
}
Ok(true)
}
pub fn proof_after_deletion(
&self,
num_leafs: u64,
) -> Result<(Vec<bitcoin_hashes::sha256::Hash>, Proof), String> {
let total_rows = util::tree_rows(num_leafs);
let mut targets = self.targets.to_owned();
targets.sort();

let proof_positions = util::get_proof_positions(&targets, num_leafs, total_rows);

let mut proof_hashes: Vec<_> = proof_positions
.to_owned()
.into_iter()
.zip(self.hashes.to_owned())
.collect();

let mut targets: Vec<_> = util::detwin(targets.to_owned(), total_rows)
.into_iter()
.rev()
.collect();

// Here is where our modified proof will be constructed
let mut target_hashes: Vec<(u64, bitcoin_hashes::sha256::Hash)> = vec![];

// For each of the targets, we'll try to find the sibling in the proof hashes
// and promote it as the parent. If it's not in the proof hashes, we'll move
// the descendants of the existing targets and proofs of the sibling's parent
// up by one row.

while let Some(target) = targets.pop() {
if util::is_root_position(target, num_leafs, total_rows) {
target_hashes.push((target, bitcoin_hashes::sha256::Hash::default()));
continue;
}

let sib = target ^ 1;
// Is the node sibling on hash proofs?
if let Some(idx) = proof_hashes.iter().position(|(pos, _)| sib == *pos) {
let parent_pos = util::parent(sib as u64, total_rows);

target_hashes.push((parent_pos, proof_hashes[idx].1));

// Delete the sibling from proof_hashes as this sibling is a target now, not a proof.
proof_hashes.remove(idx);
} else {
// If the sibling is not in the proof hashes or the targets,
// the descendants of the sibling will be moving up.
//
// 14
// |---------------\
// 12 13
// |-------\ |-------\
// 08 09 10 11
// |---\ |---\ |---\ |---\
// 00 01 04 05 06 07

let to_update: Vec<_> = target_hashes
.iter_mut()
.filter(|(node, _)| {
let parent = util::parent(sib, total_rows);
util::is_ancestor(parent, *node, total_rows).unwrap_or(false)
})
.collect();

for to_update_node in to_update {
let new_pos = util::calc_next_pos(to_update_node.0, sib, total_rows)?;
to_update_node.0 = new_pos;
}

let to_update: Vec<_> = proof_hashes
.iter_mut()
.filter(|(node, _)| {
let parent = util::parent(sib, total_rows);
util::is_ancestor(parent, *node, total_rows).unwrap_or(false)
})
.collect();

for node_to_update in to_update {
let next_pos = util::calc_next_pos(node_to_update.0, sib, total_rows)?;
node_to_update.0 = next_pos;
}
}
}
let mut new_proof_hashes = vec![];
let mut new_target_hashes = vec![];

proof_hashes.sort();
target_hashes.sort();

proof_hashes
.iter()
.for_each(|(_, hash)| new_proof_hashes.push(hash.to_owned()));
let mut prove_targets = vec![];
target_hashes.into_iter().for_each(|(pos, hash)| {
prove_targets.push(pos);
new_target_hashes.push(hash)
});

prove_targets.dedup();

Ok((
new_target_hashes,
Proof {
targets: prove_targets,
hashes: new_proof_hashes,
},
))
/// Returns how many targets this proof has
pub fn targets(&self) -> usize {
self.targets.len()
}
pub fn create_root_candidates(
/// This function computes a set of roots from a proof.
/// If some target's hashes are null, then it computes the roots after
/// those targets are deleted. In this context null means [sha256::Hash::default].
///
/// It's the caller's responsibility to null out the targets if desired by
/// passing a `bitcoin_hashes::sha256::Hash::default()` instead of the actual hash.
pub(crate) fn calculate_roots(
&self,
del_hashes: &Vec<sha256::Hash>,
stump: &Stump,
Expand Down Expand Up @@ -280,6 +183,7 @@ impl Proof {
.peekable();

while let Some((pos, hash)) = row_nodes.next() {
let next_to_prove = util::parent(pos, total_rows);
// If the current position is a root, we add that to our result and don't go any further
if util::is_root_position(pos, stump.leafs, total_rows) {
calculated_root_hashes.push(hash);
Expand All @@ -289,10 +193,20 @@ impl Proof {
if let Some((next_pos, next_hash)) = row_nodes.peek() {
// Is the next node our sibling? If so, we should be hashed together
if util::is_right_sibling(pos, *next_pos) {
let hash = types::parent_hash(&hash, &next_hash);
let next_to_prove = util::parent(pos, total_rows);

Proof::sorted_push(&mut nodes, (next_to_prove, hash));
// There are three possible cases: the current hash is null,
// and the sibling is present, we push the sibling to targets.
// If The sibling is null, we push the current node.
// If none of them is null, we compute the parent hash of both siblings
// and push this to the next target.
if hash == sha256::Hash::default() {
Proof::sorted_push(&mut nodes, (next_to_prove, *next_hash));
} else if *next_hash == sha256::Hash::default() {
Proof::sorted_push(&mut nodes, (next_to_prove, hash));
} else {
let hash = types::parent_hash(&hash, &next_hash);

Proof::sorted_push(&mut nodes, (next_to_prove, hash));
}

// Since we consumed 2 elements from nodes, skip one more here
// We need make this explicitly because peek, by definition
Expand All @@ -305,21 +219,24 @@ impl Proof {

// If the next node is not my sibling, the hash must be passed inside the proof
if let Some(next_proof_hash) = hashes_iter.next() {
let hash = if util::is_left_niece(pos) {
types::parent_hash(&hash, next_proof_hash)
} else {
types::parent_hash(next_proof_hash, &hash)
};
if hash != sha256::Hash::default() {
let hash = if util::is_left_niece(pos) {
types::parent_hash(&hash, next_proof_hash)
} else {
types::parent_hash(next_proof_hash, &hash)
};

let next_to_prove = util::parent(pos, total_rows);

Proof::sorted_push(&mut nodes, (next_to_prove, hash));
Proof::sorted_push(&mut nodes, (next_to_prove, hash));
continue;
} else {
// If none of the above, push a null hash upwards
Proof::sorted_push(&mut nodes, (next_to_prove, *next_proof_hash));
}
} else {
return Err(String::from("Proof too short"));
}
}
}

Ok(calculated_root_hashes)
}
fn sorted_push(
Expand Down Expand Up @@ -406,70 +323,7 @@ mod tests {
assert!(expected == res);
}
}
#[test]
fn test_proof_after_deletion() {
let mut hashes = vec![];
for i in 0..20 {
hashes.push(hash_from_u8(i as u8));
}
let s = Stump::new()
.modify(&hashes, &vec![], &Proof::new(vec![], vec![]))
.expect("Modify should not fail");

let proof = Proof::new(
vec![1, 16, 10],
vec![
bitcoin_hashes::sha256::Hash::from_str(
"6e340b9cffb37a989ca544e6bb780a2c78901d3fb33738768511a30617afa01d",
)
.unwrap(),
bitcoin_hashes::sha256::Hash::from_str(
"e7cf46a078fed4fafd0b5e3aff144802b853f8ae459a4f0c14add3314b7cc3a6",
)
.unwrap(),
bitcoin_hashes::sha256::Hash::from_str(
"4a64a107f0cb32536e5bce6c98c393db21cca7f4ea187ba8c4dca8b51d4ea80a",
)
.unwrap(),
bitcoin_hashes::sha256::Hash::from_str(
"9576f4ade6e9bc3a6458b506ce3e4e890df29cb14cb5d3d887672aef55647a2b",
)
.unwrap(),
bitcoin_hashes::sha256::Hash::from_str(
"cd9c77062a338e63a63ca623db438cb8676f15466641079ee61ec2dda98de796",
)
.unwrap(),
bitcoin_hashes::sha256::Hash::from_str(
"96d56447466674521007145ed72f8757517c72f7737dc4a0dcd3ecb996968971",
)
.unwrap(),
bitcoin_hashes::sha256::Hash::from_str(
"29590a14c1b09384b94a2c0e94bf821ca75b62eacebc47893397ca88e3bbcbd7",
)
.unwrap(),
bitcoin_hashes::sha256::Hash::from_str(
"e799acb98a071c4884707e4bc8c093ba22571c8d84cc0223ab0c2c9327313a5b",
)
.unwrap(),
],
);
let proof_after = proof.proof_after_deletion(s.leafs).unwrap();
let (del_hashes, proof) = proof_after;
let roots = proof.create_root_candidates(&del_hashes, &s).unwrap();

// They are swapped, because create_root_candidates builds the forest bottom-up
let expected = vec![
bitcoin_hashes::sha256::Hash::from_str(
"21326d8aebeb6ef7bc02f40bdf778a02ba1c836b257f946ae21cab2a6f95fa18",
)
.unwrap(),
bitcoin_hashes::sha256::Hash::from_str(
"37968ef73d30dda38ede8357d66593c72acd4f0eb9f7a1a9acfeb7de850c05b4",
)
.unwrap(),
];
assert_eq!(expected, roots);
}

#[test]
fn test_proof_verify() {
let contents = std::fs::read_to_string("test_values/test_cases.json")
Expand Down
8 changes: 5 additions & 3 deletions src/accumulator/stump.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
use super::{proof::Proof, types};
use bitcoin_hashes::sha256;
use std::vec;

#[derive(Debug, Clone, PartialEq)]
pub struct Stump {
Expand Down Expand Up @@ -33,7 +35,7 @@ impl Stump {
proof: &Proof,
) -> Result<Stump, String> {
let mut root_candidates = proof
.create_root_candidates(del_hashes, self)?
.calculate_roots(del_hashes, self)?
.into_iter()
.rev()
.peekable();
Expand Down Expand Up @@ -93,8 +95,8 @@ impl Stump {
return Ok(self.roots.clone());
}

let (new_hashes, new_proof) = proof.proof_after_deletion(self.leafs)?;
let new_roots = new_proof.create_root_candidates(&new_hashes, self)?;
let del_hashes = vec![sha256::Hash::default(); proof.targets()];
let new_roots = proof.calculate_roots(&del_hashes, self)?;

Ok(new_roots)
}
Expand Down
31 changes: 31 additions & 0 deletions test_values/test_cases.json
Original file line number Diff line number Diff line change
Expand Up @@ -429,6 +429,37 @@
"9576f4ade6e9bc3a6458b506ce3e4e890df29cb14cb5d3d887672aef55647a2b",
"c413035120e8c9b0ca3e40c93d06fe60a0d056866138300bb1f1dd172b4923c3"
]
},
{
"leaf_values": [0, 1, 2, 3, 4, 5, 6, 7],
"target_values":[0, 1, 2, 3, 4, 5, 6, 7],
"roots":[
"0000000000000000000000000000000000000000000000000000000000000000"
],
"proof_hashes": []
},
{
"leaf_values": [0, 1, 2, 3, 4, 5, 6, 7],
"target_values": [0, 1, 2, 3],
"roots": [
"29590a14c1b09384b94a2c0e94bf821ca75b62eacebc47893397ca88e3bbcbd7"
],
"proof_hashes": [
"29590a14c1b09384b94a2c0e94bf821ca75b62eacebc47893397ca88e3bbcbd7"
]
},
{
"leaf_values": [0, 1, 2, 3, 4, 5, 6, 7],
"target_values": [0, 2, 4, 6],
"roots": [
"54128834807e7f8763ff00fef2e7aea740c1d19977f95ee138ee6eecd0b9c702"
],
"proof_hashes": [
"4bf5122f344554c53bde2ebb8cd2b7e3d1600ad631c385a5d7cce23c7785459a",
"084fed08b978af4d7d196a7446a86b58009e636b611db16211b65a9aadff29c5",
"e77b9a9ae9e30b0dbdb6f510a264ef9de781501d7b6b92ae89eb059c5ab743db",
"ca358758f6d27e6cf45272937977a748fd88391db679ceda7dc7bf1f005ee879"
]
}
]
}

0 comments on commit 64ce26e

Please sign in to comment.