-
Notifications
You must be signed in to change notification settings - Fork 796
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
[FRAME] State Diff Guard #4204
base: master
Are you sure you want to change the base?
[FRAME] State Diff Guard #4204
Conversation
StateDiffGuard
initial implf3cc891
to
730d9a9
Compare
8e68898
to
ddf1711
Compare
#[derive(Default, Debug)] | ||
pub struct StateDiffGuard { | ||
// Storage prefixes that are expected to change. | ||
whitelisted_prefixes: BTreeSet<StoragePrefix>, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
whitelisted_prefixes: BTreeSet<StoragePrefix>, | |
must_change_prefixes: BTreeSet<StoragePrefix>, |
The name here is not really expressing that they must change. It is also possible to make this more abstract by having a Map<Prefix, Change>
where Change
can be { Can | Must } x { Change | NotChange }
(PS: not sure if these enum values are correct - hopefully you get the idea), but i think its fine for the beginning.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
made some improvements here, let me know if it's in the right direction, thanks)
e93ecb2
to
d1e0384
Compare
The CI pipeline was cancelled due to failure one of the required jobs. |
d1e0384
to
12e6f44
Compare
This looks like a very good PR. @dastansam sorry for the delay, are you still around for finishing? @ggwpez @liamaharon and I can provide reviews. |
What I see missing is integrating this into |
thanks, for sure. Looking forward for your reviews) |
Signed-off-by: Oliver Tale-Yazdi <[email protected]>
Signed-off-by: Oliver Tale-Yazdi <[email protected]>
Signed-off-by: Oliver Tale-Yazdi <[email protected]>
hey @ggwpez, thanks for the updates. I am willing to finish this off if you have any other comments? |
Signed-off-by: Oliver Tale-Yazdi <[email protected]>
Signed-off-by: Oliver Tale-Yazdi <[email protected]>
Signed-off-by: Oliver Tale-Yazdi <[email protected]>
Signed-off-by: Oliver Tale-Yazdi <[email protected]>
Just wanted to improve the API a bit but I think it should be good now. PS: Maybe still update the MR description to show a small copy&paste example, possibly one of the tests to make it understandable what is being added. |
let check_passed = self.apply_mutation_policy(); | ||
|
||
// No need to double panic, eg. inside a test assertion failure. | ||
if sp_std::thread::panicking() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if sp_std::thread::panicking() { | |
#[cfg(feature = "std")] | |
if std::thread::panicking() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You did not apply this.
let mut state = BTreeMap::new(); | ||
|
||
let mut previous_key = Vec::new(); | ||
while let Some(next) = sp_io::storage::next_key(&previous_key) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why are we reading the entire state? We should only read the state based on the prefixes we will compare later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
so that we can get the keys that were not meant to be changed (i.e maybe accidentally removed). see this line
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not from the runtime, but from the node side we could probably do this more elegently by using the ProofRecording types that are used for state proofs.
I think from the runtime there is no way other than (naively) reading the entire state pre and post operation, and compare them.
@bkchr one more review, please |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Given the current way it is implemented, we can never use this with any production chain. The time it would take to run the migrations would grow by a lot if every migration would read the entire state.
IMO if we really want to have this, we need to change the internals. The user needs to declare which storage entries are allowed to change. At the start we would read these keys from the state. (Could still hit a big map, but we can not really go better with what we have at hand)
After the operation is finished, we could do the checks that the keys that should have changed, indeed have changed and also record the current keys/values. Now we could do a trick and run the migration
and the checking in a storage transaction. After the migration and the checking succeeded, we would record the storage root and revert the transaction. Then we would apply the changed keys again and check that the storage root matches. If the storage root doesn't match, something else was touched. Problem would be that we would not know which key changed.
//! In the example above, the guard will panic if any storage entry that doesn't match `SomeMap` or | ||
//! `SomeDoubleMap` prefixes is changed. | ||
|
||
use alloc::{collections::btree_map::BTreeMap, vec::Vec}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
use alloc::{collections::btree_map::BTreeMap, vec::Vec}; | |
use alloc::{collections::btree_map::BTreeMap, vec::Vec, vec}; |
//! | ||
//! # Example | ||
//! | ||
//! Use the `StateDiffGuard` in a migration: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IMO this can not be used in any migration with a reasonable state, because of the behavior of reading the entire state.
This would take ages.
let check_passed = self.apply_mutation_policy(); | ||
|
||
// No need to double panic, eg. inside a test assertion failure. | ||
if sp_std::thread::panicking() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You did not apply this.
Motivation
Adds a guard that prevents unexpected storage changes.
StateDiffGuard
:AnythingElse
if present.Example
Code below panics since migration is changing
SomeDoubleMap
part of #240
polkadot address: 16FqwPZ8GRC5U5D4Fu7W33nA55ZXzXGWHwmbnE1eT6pxuqcT