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

PMMR Backend Support for append_pruned_root (Continued) #3659

Merged
merged 16 commits into from
Nov 9, 2021

Conversation

yeastplume
Copy link
Member

@yeastplume yeastplume commented Nov 4, 2021

This is a continuation/completion of #3476

All of the comments and discussion from the previous PR still apply. I've merged this with with the most recent code, and reviewed/tested, particularly with respect to the simplification of the prune list cache in #3573.

As of now, this all appears to be working, and at least doesn't appear to break the basic existing case of expanding the pmmr by adding individual leaf nodes. Fuller testing of appending pruned segments to the pmmr will be performed in later tests/PRs.

pub fn bintree_leaf_pos_iter(pos: u64) -> impl Iterator<Item = u64> {
let leaf_start = max(1, bintree_leftmost(pos as u64));
let leaf_end = bintree_rightmost(pos as u64);
(leaf_start..=leaf_end).filter(|x| is_leaf(*x))
Copy link
Contributor

@tromp tromp Nov 4, 2021

Choose a reason for hiding this comment

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

It would be more efficient to iterate over leaf_indices, and map those into node_indices with the very cheap
map( \leaf_index -> 2 * leaf_index - count_ones(leaf_index) )
probably should add that as a method leaf_to_node_index()...

Copy link
Member Author

Choose a reason for hiding this comment

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

Is there a way to iterate over the leaf indices of a subtree without reading them from an already-stored bitmap, (which is implementation-specific and not available in these helpers which I assume are supposed to be 'pure' MMR operations)?

Copy link
Contributor

Choose a reason for hiding this comment

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

i don't see what a bitmap has to do with this routine which is only about indices.
i'm suggesting to convert both leaf_start and leaf_end into leaf indices (which is as expensive as an is_leaf test), using a node_to_leaf_index method, then construct a range from those, and map each element back to a node index.

Copy link
Member Author

Choose a reason for hiding this comment

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

thanks, that makes it clearer, will implement

Copy link
Contributor

Choose a reason for hiding this comment

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

the max(1, part is also redundant here.

PS: I just made a PR rewriting that MMR doc to stick with 0-based positions.


/// Iterator over all pos beneath the provided subtree root (including the root itself).
pub fn bintree_pos_iter(pos: u64) -> impl Iterator<Item = u64> {
let leaf_start = max(1, bintree_leftmost(pos as u64));
Copy link
Contributor

@tromp tromp Nov 4, 2021

Choose a reason for hiding this comment

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

the max(1, part is redundant.

bintree_leftmost, like bintree_postorder_height uses 1-based positions
(which BTW I don't like. one of these days i will try to make everything 0-based)
and can never return 0.
bintree_leftmost also has a wrong description, copy-pasted from bintree_rightmost.

Copy link
Member Author

Choose a reason for hiding this comment

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

it's redundant in theory however the tests are constructed with the assumption that bintree_pos_iter(0) returns an empty iterator which is produced by this, so I don't want to break this assumption ATM.

shift_cache: vec![],
leaf_shift_cache: vec![],
};

for pos in bitmap.iter().filter(|x| *x > 0) {
Copy link
Contributor

Choose a reason for hiding this comment

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

the non-zero test is either wrong (for 0-based pos) or redundant (for 1-based pos).

Copy link
Member Author

Choose a reason for hiding this comment

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

agreed in theory, but removing this test breaks existing assumptions about the underlying data

@@ -179,6 +180,18 @@ impl PruneList {
}
}

// Calculate the next shift based on provided pos and the previous shift.
fn calculate_next_shift(&self, pos: u64) -> u64 {
let prev_shift = self.get_shift(pos.saturating_sub(1));
Copy link
Contributor

Choose a reason for hiding this comment

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

pos.saturating_sub(1) should be simply pos-1 since pos is 1-based and cannot be 0.

Copy link
Contributor

Choose a reason for hiding this comment

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

I suggest we change all occurrences of pos to either pos0 or pos1 depending on whether they're 0-based or 1-based.

Copy link
Member Author

Choose a reason for hiding this comment

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

In this case yes, we can just change the saturating sub since it's only called in one place at the moment that will fail with assertion beforehand is pos is 0

Copy link
Member Author

Choose a reason for hiding this comment

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

I'm going to do a bit of a 'clean as you go' on changing pos to pos0 or pos1 at the moment, but if we do a complete pass on all instances it should be within a separate PR.

Copy link
Contributor

Choose a reason for hiding this comment

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

Let me know when you're done making pmmr changes so I can do my own cleanup...

let prev_shift = self.get_shift(pos.saturating_sub(1));
let shift = if self.is_pruned_root(pos) {
let height = bintree_postorder_height(pos);
2 * ((1 << height) - 1)
Copy link
Contributor

@tromp tromp Nov 4, 2021

Choose a reason for hiding this comment

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

Why is shift 2 less than a 2-power (1 less than a subtree size)?

…_iter to use it. Note there's still an unwrap that needs to be dealt with sanely
Copy link
Contributor

@tromp tromp left a comment

Choose a reason for hiding this comment

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

pmmr_leaf_to_insertion_index is way too complicated.
let (insertidx, height) = peak_map_height(pos1 - 1);
if (height == 0) then Some (insertidx) else None;
should do it...

…rsion between 1 and 0 based in bintree_leaf_pos_iter
Copy link
Contributor

@tromp tromp left a comment

Choose a reason for hiding this comment

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

So far so good.
Needs more work on 1-based indices but will work on that in separate PR...

@yeastplume yeastplume merged commit 3f4f165 into mimblewimble:master Nov 9, 2021
@yeastplume yeastplume deleted the append_pruned_root branch January 20, 2022 10:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants