Make checkpoint_storage_at use plain loop instead of recursion (#9734)

* Make checkpoint_storage_at use plain loop instead of recursion

* Add target: "state" to warn!

* Use iterator::Skip
This commit is contained in:
Wei Tang 2018-10-15 22:06:56 +08:00 committed by GitHub
parent 5319d33bc6
commit 7434026f5f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -553,62 +553,70 @@ impl<B: Backend> State<B> {
} }
/// Get the value of storage at a specific checkpoint. /// Get the value of storage at a specific checkpoint.
pub fn checkpoint_storage_at(&self, checkpoint_index: usize, address: &Address, key: &H256) -> TrieResult<Option<H256>> { pub fn checkpoint_storage_at(&self, start_checkpoint_index: usize, address: &Address, key: &H256) -> TrieResult<Option<H256>> {
#[must_use]
enum ReturnKind { enum ReturnKind {
/// Use original storage at value at this address. /// Use original storage at value at this address.
OriginalAt, OriginalAt,
/// Use the downward checkpoint value. /// The checkpoint storage value is the same as the checkpoint storage value at the next checkpoint.
Downward, SameAsNext,
} }
let (checkpoints_len, kind) = { let kind = {
let checkpoints = self.checkpoints.borrow(); let checkpoints = self.checkpoints.borrow();
let checkpoints_len = checkpoints.len();
let checkpoint = match checkpoints.get(checkpoint_index) { if start_checkpoint_index >= checkpoints.len() {
Some(checkpoint) => checkpoint,
// The checkpoint was not found. Return None. // The checkpoint was not found. Return None.
None => return Ok(None), return Ok(None);
}; }
let kind = match checkpoint.get(address) { let mut kind = None;
// The account exists at this checkpoint.
Some(Some(AccountEntry { account: Some(ref account), .. })) => { for checkpoint in checkpoints.iter().skip(start_checkpoint_index) {
if let Some(value) = account.cached_storage_at(key) { match checkpoint.get(address) {
return Ok(Some(value)); // The account exists at this checkpoint.
} else { Some(Some(AccountEntry { account: Some(ref account), .. })) => {
// This account has checkpoint entry, but the key is not in the entry's cache. We can use if let Some(value) = account.cached_storage_at(key) {
// original_storage_at if current account's original storage root is the same as checkpoint return Ok(Some(value));
// account's original storage root. Otherwise, the account must be a newly created contract.
if account.base_storage_root() == self.original_storage_root(address)? {
ReturnKind::OriginalAt
} else { } else {
// If account base storage root is different from the original storage root since last // This account has checkpoint entry, but the key is not in the entry's cache. We can use
// commit, then it can only be created from a new contract, where the base storage root // original_storage_at if current account's original storage root is the same as checkpoint
// would always be empty. Note that this branch is actually never called, because // account's original storage root. Otherwise, the account must be a newly created contract.
// `cached_storage_at` handled this case. if account.base_storage_root() == self.original_storage_root(address)? {
warn!("Trying to get an account's cached storage value, but base storage root does not equal to original storage root! Assuming the value is empty."); kind = Some(ReturnKind::OriginalAt);
return Ok(Some(H256::new())); break
} else {
// If account base storage root is different from the original storage root since last
// commit, then it can only be created from a new contract, where the base storage root
// would always be empty. Note that this branch is actually never called, because
// `cached_storage_at` handled this case.
warn!(target: "state", "Trying to get an account's cached storage value, but base storage root does not equal to original storage root! Assuming the value is empty.");
return Ok(Some(H256::new()));
}
} }
} },
}, // The account didn't exist at that point. Return empty value.
// The account didn't exist at that point. Return empty value. Some(Some(AccountEntry { account: None, .. })) => return Ok(Some(H256::new())),
Some(Some(AccountEntry { account: None, .. })) => return Ok(Some(H256::new())), // The value was not cached at that checkpoint, meaning it was not modified at all.
// The value was not cached at that checkpoint, meaning it was not modified at all. Some(None) => {
Some(None) => ReturnKind::OriginalAt, kind = Some(ReturnKind::OriginalAt);
// This key does not have a checkpoint entry. break
None => ReturnKind::Downward, },
}; // This key does not have a checkpoint entry.
None => {
kind = Some(ReturnKind::SameAsNext);
},
}
}
(checkpoints_len, kind) kind.expect("start_checkpoint_index is checked to be below checkpoints_len; for loop above must have been executed at least once; it will either early return, or set the kind value to Some; qed")
}; };
match kind { match kind {
ReturnKind::Downward => { ReturnKind::SameAsNext => {
if checkpoint_index >= checkpoints_len - 1 { // If we reached here, all previous SameAsNext failed to early return. It means that the value we want
Ok(Some(self.storage_at(address, key)?)) // to fetch is the same as current.
} else { Ok(Some(self.storage_at(address, key)?))
self.checkpoint_storage_at(checkpoint_index + 1, address, key)
}
}, },
ReturnKind::OriginalAt => Ok(Some(self.original_storage_at(address, key)?)), ReturnKind::OriginalAt => Ok(Some(self.original_storage_at(address, key)?)),
} }