Skip to content

Commit

Permalink
fix(rpc): fix the implementation of get_block_proof for light client (
Browse files Browse the repository at this point in the history
#4585)

The implementation of `get_block_proof` has the following issue that caused it to have O(n) complexity sometimes:
```
        root
         / \ 
       /     \ 
      7       \
   /    \       \
  5       6    \
  / \    / \       \
0   1   2   3    4
```
In the above example, when we try to prove block 0, the current implementation, when trying to get the sibling of node 7, will do a useless traversal of a full binary tree with 4 leaves, whereas it does not need to recurse when it is known that the position is already outside of the number of leaves we have. More specifically, getting the left child (call to `get_merkle_tree_node`) should almost always be O(1) except when we are close to the right most leaf of the tree. For example
```
         / \ 
       /     \
      / \      \       
...   8   9   10  
```
if we have 11 leaves in the above example (only the rightmost part drawn), then we must get the parent of 8 and 9, and 10, respectively, to prove 0. This is because the parent of 10 will be different once 11 is inserted and the shape of the tree also changes slightly. Therefore, when we are at the parent of 10, we need to recursive in both directions and cannot just go to the left only. This condition is captured by a comparison between `index * counter` and `tree_size`.


Test plan
----------
`test_block_merkle_proof` still works and manually check that the hashmap constructed when we call `test_block_merkle_proof_with_len(10000)` is roughly `log2(10000)^2`
  • Loading branch information
bowenwang1996 authored Jul 27, 2021
1 parent 8b8fbb5 commit 6390d68
Showing 1 changed file with 21 additions and 17 deletions.
38 changes: 21 additions & 17 deletions chain/chain/src/chain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2115,21 +2115,25 @@ impl Chain {
} else {
let cur_tree_size = (index + 1) * counter;
let maybe_hash = if cur_tree_size > tree_size {
let left_hash = self.get_merkle_tree_node(
index * 2,
level - 1,
counter / 2,
tree_size,
tree_nodes,
)?;
let right_hash = self.reconstruct_merkle_tree_node(
index * 2 + 1,
level - 1,
counter / 2,
tree_size,
tree_nodes,
)?;
Self::combine_maybe_hashes(left_hash, right_hash)
if index * counter <= tree_size {
let left_hash = self.get_merkle_tree_node(
index * 2,
level - 1,
counter / 2,
tree_size,
tree_nodes,
)?;
let right_hash = self.reconstruct_merkle_tree_node(
index * 2 + 1,
level - 1,
counter / 2,
tree_size,
tree_nodes,
)?;
Self::combine_maybe_hashes(left_hash, right_hash)
} else {
None
}
} else {
Some(
*self
Expand Down Expand Up @@ -2216,7 +2220,7 @@ impl Chain {
let mut path = vec![];
let mut tree_nodes = HashMap::new();
let mut iter = tree_size;
while iter >= 1 {
while iter > 1 {
if cur_index % 2 == 0 {
cur_index += 1
} else {
Expand All @@ -2239,7 +2243,7 @@ impl Chain {
path.push(MerklePathItem { hash, direction });
}
cur_index /= 2;
iter /= 2;
iter = (iter + 1) / 2;
level += 1;
counter *= 2;
}
Expand Down

0 comments on commit 6390d68

Please sign in to comment.