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

DO NOT MERGE Valset: add pagination to active validators query #35

Closed
wants to merge 2 commits into from

Conversation

ueco-jb
Copy link

@ueco-jb ueco-jb commented Jan 24, 2022

closes #33

It won't be merged, as Ethan suggested better solution for that problem - but stays as reference for #42

@ueco-jb ueco-jb self-assigned this Jan 24, 2022
@ueco-jb ueco-jb changed the title Valset: replace vector of active validators with map Valset: add pagination to active validators query Jan 24, 2022
Comment on lines +554 to +568
// First iteration - load all validators, second - remove all, third - add all new
// Not very efficient...
let old_validators = VALIDATORS
.range(deps.storage, None, None, Order::Descending)
.map(|validator_info| {
let (_, info) = validator_info?;
Ok(info)
})
.collect::<StdResult<Vec<ValidatorInfo>>>()?;
for validator in &old_validators {
VALIDATORS.remove(deps.storage, &validator.operator);
}
for validator in &validators {
VALIDATORS.save(deps.storage, &validator.operator, validator)?;
}
Copy link
Author

@ueco-jb ueco-jb Jan 25, 2022

Choose a reason for hiding this comment

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

Both new_ and old_validators vecs are necessary to calulate diff (line 571).
I had idea about looping through all validators, if address is not found in new_ then it would be removed from VALIDATORS... but that would require 1) in-place remove 2) completely different approach to calculate diff.

I'm still trying to figure out something more optimal.

Copy link
Contributor

Choose a reason for hiding this comment

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

@maurolacy worked on something similar with HashMap in some other PR

Copy link
Contributor

Choose a reason for hiding this comment

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

I think BTree map was important if we want deterministic sorting order.

Can you link the other contract that did this?

Copy link
Contributor

@maurolacy maurolacy Jan 26, 2022

Choose a reason for hiding this comment

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

There are two things here:

  1. Pagination. This is (relatively) minor, and can be solved in different ways.
  2. Item value size limitations. This is something that came up during discussion, and it's important, because we're storing ValidatorMetadata (which has a lot of String fields) inside ValidatorInfo. These fields are also in control of the operator, so, this can be clearly abused, and can lead to performance issues, or even, denial of service.

We'll tackle 2. in #42.

Regarding 1., and without changing the underlying structure, a simple approach would be to populate a BtreeSet with the data, then range over it using BTreeSet::range, and paginate using take over the iterator as usual.

A similar / equivalent way would be to sort the vec, and then bisect, but I think the btree approach is simpler / better. BTreeSet is already being used in this contract to compute the difference of the validators sets.

Copy link
Contributor

@maurolacy maurolacy Jan 26, 2022

Choose a reason for hiding this comment

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

Another option could be to turn VALIDATORS into an Item<BTreeSet<ValidatorInfo>> structure.

Need to research this a bit, but at first glance it looks overkill. We will also have to be very careful with the size requirements of BTreeSet itself. And, reducing the ValidatorInfo footprint as well, like mentioned in #42.

Update: BTreeSet footprint is similar to that of Vec (24 bytes). Will take a look at the code but in principle a BTreeSet can be treated like a vector, so, this looks feasible, and is perhaps the best option.

Update 2: Will do more analysis, as part of #42, to confirm these footprints / limits.

Copy link
Contributor

@maurolacy maurolacy Jan 26, 2022

Choose a reason for hiding this comment

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

Finally, implementing the Map approach for VALIDATORS as you're doing here doesn't look like a bad idea either. It will solve both the pagination, and the size / limits issue. At the expense of some required code changes / adaptations.

In that case, the way to make the code above efficient would be to process only the updates and removals over the differences, i.a. as part of (or after) calculate_diff.

Anyway, the hybrid approach looks better in that it will require less reading / writing to storage. Particularly and if we reduce the ValidatorInfo footprint as suggested in #42.

@ueco-jb ueco-jb changed the title Valset: add pagination to active validators query DON'T MERGE Valset: add pagination to active validators query Jan 26, 2022
@ueco-jb ueco-jb changed the title DON'T MERGE Valset: add pagination to active validators query DO NOT MERGE Valset: add pagination to active validators query Jan 26, 2022
@ueco-jb
Copy link
Author

ueco-jb commented Jan 27, 2022

Can be now closed.

@ueco-jb ueco-jb closed this Jan 27, 2022
@ueco-jb ueco-jb deleted the 33-valset-limit-active-valset-query branch August 30, 2022 13:47
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.

valset: limit active_valset query and add pagination
3 participants