Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

don't confuse with different numbering bases #3660

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 15 additions & 41 deletions doc/mmr.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,54 +65,28 @@ so we need a method to compute one (otherwise it would defeat the purpose of
using a hash tree). This process is called "bagging the peaks" for reasons
described in [1].

First, we identify the peaks of the MMR; we will define one method of doing so
here. We first write another small example MMR but with the indexes written as
binary (instead of decimal), starting from 1:

```
Height

2 111
/ \
1 11 110 1010
/ \ / \ / \
0 1 10 100 101 1000 1001 1011
```

This MMR has 11 nodes and its peaks are at position 111 (7), 1010 (10) and
1011 (11). We first notice how the first leftmost peak is always going to be
the highest and always "all ones" when expressed in binary. Therefore that
peak will have a position of the form `2^n - 1` and will always be the
largest such position that is inside the MMR (its position is lesser than the
total size). We process iteratively for a MMR of size 11:

```
2^0 - 1 = 0, and 0 < 11
2^1 - 1 = 1, and 1 < 11
2^2 - 1 = 3, and 3 < 11
2^3 - 1 = 7, and 7 < 11
2^4 - 1 = 15, and 15 is not < 11
```

(This can also be calculated non-iteratively as `2^(binary logarithm of size + 1) - 1`

Therefore the first peak is 7. To find the next peak, we then need to "jump" to
its right sibling. If that node is not in the MMR (and it won't), take its left
child. If that child is not in the MMR either, keep taking its left child
until we have a node that exists in our MMR. Once we find that next peak,
keep repeating the process until we're at the last node.

All these operations are very simple. Jumping to the right sibling of a node at
height `h` is adding `2^(h+1) - 1` to its position. Taking its left child is
subtracting `2^h`.
First, we identify the peaks of the MMR.

The MMR above has 19 nodes and 3 peaks, each of which
is the root of a subtree of size a 2-power minus one.
If the word-size were 8-bits, then 19 is 00010011 in binary, with 3 leading zeros.
Shifting the all 1-bit word 11111111 right by that number 3 gives us 00011111, or 31,
the first candidate peak size.
Since 19 < 31, we have no 31-peak.
The next candidate peak size is 31 >> 1 = 15.
Since 19 >= 15, we have a 15-peak, and the relative position beyond identified
peaks is 19-15=4.
After 2 more right shifts to peak size 3, we find 4 >= 3 and identify the 2nd peak,
reducing relative position to 4-3 = 1.
A final right shift gives a peak size of 1, and with 1 >= 1, we identified the 3rd and final peak.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is definitely more correct in terms of keeping consistent with a 0 based index and the code, but I have to say I did like having the diagram in place and the 'all ones on the left' to assist with the process conceptually (even thought it was 1-index based).


Finally, once all the positions of the peaks are known, "bagging" the peaks
consists of hashing them iteratively from the right, using the total size of
the MMR as prefix. For a MMR of size N with 3 peaks p1, p2 and p3 we get the
final top peak:

```
P = Blake2b(N | Blake2b(N | Node(p3) | Node(p2)) | Node(p1))
P = Blake2b(N | Node(p1) | Blake2b(N | Node(p2) | Node(p3)))
```

## Pruning
Expand Down