-
Notifications
You must be signed in to change notification settings - Fork 623
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
(fix) Fix storage usage bug with migration #4274
Conversation
Co-authored-by: Aleksey Kladov <aleksey.kladov@gmail.com>
…o migration-fix-storage-usage
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.
I don't see what prevents this migration from being applied on every single epoch start. Do I miss something?
Another corner case I have in mind is: what if the very first block on the epoch won't be produced? Will this still work fine? I am not very familiar with the block production code.
#[derive(Default)] | ||
pub struct MigrationData { | ||
#[cfg(feature = "protocol_feature_fix_storage_usage")] | ||
pub storage_usage_delta: Vec<(AccountId, u64)>, |
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.
I would prefer to have a struct with named arguments as u64
is not really descriptive
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 we use named structure in json this will lead to additional 70kb of data included executable
migration_data in ApplyState in only Some when protocol version changes, not just epoch |
It follows same logic as changes to validators at the start of an epoch |
Co-authored-by: Vlad Frolov <frolvlad@gmail.com>
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.
All my concerns are clarified.
@EgorKulikov This implementation is much cleaner, thank you for re-implementing it to this maintainable state! 🚀
@@ -152,6 +152,9 @@ pub enum StateChangeCause { | |||
/// State change that happens when we update validator accounts. Not associated with with any | |||
/// specific transaction or receipt. | |||
ValidatorAccountsUpdate, | |||
/// State change that is happens due to migration that happens in first block of an epoch |
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.
nit:
/// State change that is happens due to migration that happens in first block of an epoch | |
/// State change that happens due to state-correction migration that happens in the first block of an epoch |
Ideally, reference this PR as an example for the reader, so they don't need to do git-blame to look up what kind of "state-corrections" happen.
const GAS_USED_FOR_STORAGE_USAGE_DELTA_MIGRATION: Gas = 490_000_000_000_000; | ||
|
||
pub fn load_migration_data(chain_id: &String) -> MigrationData { | ||
#[cfg(not(feature = "protocol_feature_fix_storage_usage"))] |
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.
Separate blocks under #[cfg(feature = "protocol_feature_fix_storage_usage")]
and #[cfg(not(feature = "protocol_feature_fix_storage_usage"))]
could be more readable.
Some(mut account) => { | ||
// Storage usage is saved in state, hence it is nowhere close to max value | ||
// of u64, and maximal delta is 4196, se we can add here without checking | ||
// for overflow |
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.
Maybe checked add would be just easier than long comment why it's not needed :)?
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.
LGTM, just few nits.
chain/client/tests/process_blocks.rs
Outdated
@@ -2267,6 +2275,135 @@ fn test_epoch_protocol_version_change() { | |||
assert_eq!(protocol_version, PROTOCOL_VERSION + 1); | |||
} | |||
|
|||
#[test] | |||
#[cfg(feature = "protocol_feature_fix_storage_usage")] |
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.
It is better to group the tests for some protocol feature in one mod so that the imports are cleaner, e.g.
#[cfg(test)]
mod protocol_feature_fix_storage_usage_tests {
....
}
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.
Will do, though I have to notice that there is a lot of different kinds of tests in this file and just one mod
chain/client/tests/process_blocks.rs
Outdated
assert_eq!(account_test0.storage_usage(), 182); | ||
} | ||
} | ||
{ |
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 it is a different test, please actually make it separate.
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.
They are kinda the same, just with different chain ids
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.
Then it makes sense to refactor the test to avoid code duplication. Something like
fn test_with_chain_id(chain_id: &str) {
....
}
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.
I did something like that, not sure it is more understandable though
chain/client/tests/process_blocks.rs
Outdated
let (encoded_chunk, merkle_paths, receipts) = | ||
create_chunk_on_height(&mut env.clients[0], i); | ||
|
||
let mut chain_store = | ||
ChainStore::new(env.clients[0].chain.store().owned_store(), genesis_height); | ||
env.clients[0] | ||
.shards_mgr | ||
.distribute_encoded_chunk( | ||
encoded_chunk.clone(), | ||
merkle_paths.clone(), | ||
receipts.clone(), | ||
&mut chain_store, | ||
) | ||
.unwrap(); | ||
|
||
let mut block = env.clients[0].produce_block(i).unwrap().unwrap(); | ||
|
||
let validator_signer = InMemoryValidatorSigner::from_seed( | ||
&"test0".to_string(), | ||
KeyType::ED25519, | ||
&"test0".to_string(), | ||
); | ||
|
||
block.mut_header().get_mut().inner_rest.latest_protocol_version = | ||
ProtocolFeature::FixStorageUsage.protocol_version(); | ||
block.mut_header().resign(&validator_signer); |
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.
Could you explain what is done here? This looks the same if you were to call TestEnv::produce_block
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.
Maybe. I started with test_epoch_protocol_version_change as a template for my test. I'll look if this can be refactored
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.
I need to update protocol version, so no, I cannot just use TestEnv::produce_block
Co-authored-by: Bowen Wang <bowenwang1996@users.noreply.github.com>
On some further though we are actually able to migrate this inside a single block
Test plan
Sanity test in migrations.rs
Integration test in process_blocks