Implement nested storage transactions (#6269)

* Add transactional storage functionality to OverlayChanges

A collection already has a natural None state. No need to
wrap it with an option.

* Add storage transactions runtime interface

* Add frame support for transactions

* Fix committed typo

* Rename 'changes' variable to 'overlay'

* Fix renaming change

* Fixed strange line break

* Rename clear to clear_where

* Add comment regarding delete value on mutation

* Add comment which changes are covered by a transaction

* Do force the arg to with_transaction return a Result

* Use rust doc comments on every documentable place

* Fix wording of insert_diry doc

* Improve doc on start_transaction

* Rename value to overlayed in close_transaction

* Inline negation

* Improve wording of close_transaction comments

* Get rid of an expect by using get_or_insert_with

* Remove trailing whitespace

* Rename should to expected in tests

* Rolling back a transaction must mark the overlay as dirty

* Protect client initiated storage tx from being droped by runtime

* Review nits

* Return Err when entering or exiting runtime fails

* Documentation fixup

* Remove close type

* Move enter/exit runtime to excute_aux in the state-machine

* Rename Discard -> Rollback

* Move child changeset creation to constructor

* Move child spawning into the closure

* Apply suggestions from code review

Co-authored-by: Bastian Köcher <bkchr@users.noreply.github.com>

* Fixup for code suggestion

* Unify re-exports

* Rename overlay_changes to mod.rs and move into subdir

* Change proof wording

* Adapt a new test from master to storage-tx

* Suggestions from the latest round of review

* Fix warning message

Co-authored-by: Bastian Köcher <bkchr@users.noreply.github.com>
This commit is contained in:
Alexander Theißen
2020-06-23 11:17:53 +02:00
committed by GitHub
parent 6aa8965f33
commit bb2df2122e
16 changed files with 1383 additions and 532 deletions
+27 -14
View File
@@ -79,6 +79,10 @@ pub use in_memory_backend::new_in_mem;
pub use stats::{UsageInfo, UsageUnit, StateMachineStats};
pub use sp_core::traits::CloneableSpawn;
const PROOF_CLOSE_TRANSACTION: &str = "\
Closing a transaction that was started in this function. Client initiated transactions
are protected from being closed by the runtime. qed";
type CallResult<R, E> = Result<NativeOrEncoded<R>, E>;
/// Default handler of the execution manager.
@@ -297,6 +301,8 @@ impl<'a, B, H, N, Exec> StateMachine<'a, B, H, N, Exec> where
None => &mut cache,
};
self.overlay.enter_runtime().expect("StateMachine is never called from the runtime; qed");
let mut ext = Ext::new(
self.overlay,
self.offchain_overlay,
@@ -324,6 +330,9 @@ impl<'a, B, H, N, Exec> StateMachine<'a, B, H, N, Exec> where
native_call,
);
self.overlay.exit_runtime()
.expect("Runtime is not able to call this function in the overlay; qed");
trace!(
target: "state", "{:04x}: Return. Native={:?}, Result={:?}",
id,
@@ -347,11 +356,11 @@ impl<'a, B, H, N, Exec> StateMachine<'a, B, H, N, Exec> where
CallResult<R, Exec::Error>,
) -> CallResult<R, Exec::Error>
{
let pending_changes = self.overlay.clone_pending();
self.overlay.start_transaction();
let (result, was_native) = self.execute_aux(true, native_call.take());
if was_native {
self.overlay.replace_pending(pending_changes);
self.overlay.rollback_transaction().expect(PROOF_CLOSE_TRANSACTION);
let (wasm_result, _) = self.execute_aux(
false,
native_call,
@@ -366,6 +375,7 @@ impl<'a, B, H, N, Exec> StateMachine<'a, B, H, N, Exec> where
on_consensus_failure(wasm_result, result)
}
} else {
self.overlay.commit_transaction().expect(PROOF_CLOSE_TRANSACTION);
result
}
}
@@ -378,16 +388,17 @@ impl<'a, B, H, N, Exec> StateMachine<'a, B, H, N, Exec> where
R: Decode + Encode + PartialEq,
NC: FnOnce() -> result::Result<R, String> + UnwindSafe,
{
let pending_changes = self.overlay.clone_pending();
self.overlay.start_transaction();
let (result, was_native) = self.execute_aux(
true,
native_call.take(),
);
if !was_native || result.is_ok() {
self.overlay.commit_transaction().expect(PROOF_CLOSE_TRANSACTION);
result
} else {
self.overlay.replace_pending(pending_changes);
self.overlay.rollback_transaction().expect(PROOF_CLOSE_TRANSACTION);
let (wasm_result, _) = self.execute_aux(
false,
native_call,
@@ -977,7 +988,7 @@ mod tests {
let mut overlay = OverlayedChanges::default();
overlay.set_storage(b"aba".to_vec(), Some(b"1312".to_vec()));
overlay.set_storage(b"bab".to_vec(), Some(b"228".to_vec()));
overlay.commit_prospective();
overlay.start_transaction();
overlay.set_storage(b"abd".to_vec(), Some(b"69".to_vec()));
overlay.set_storage(b"bbd".to_vec(), Some(b"42".to_vec()));
@@ -994,10 +1005,10 @@ mod tests {
);
ext.clear_prefix(b"ab");
}
overlay.commit_prospective();
overlay.commit_transaction().unwrap();
assert_eq!(
overlay.changes(None).map(|(k, v)| (k.clone(), v.value().cloned()))
overlay.changes().map(|(k, v)| (k.clone(), v.value().cloned()))
.collect::<HashMap<_, _>>(),
map![
b"abc".to_vec() => None.into(),
@@ -1083,7 +1094,7 @@ mod tests {
Some(vec![reference_data[0].clone()].encode()),
);
}
overlay.commit_prospective();
overlay.start_transaction();
{
let mut ext = Ext::new(
&mut overlay,
@@ -1102,7 +1113,7 @@ mod tests {
Some(reference_data.encode()),
);
}
overlay.discard_prospective();
overlay.rollback_transaction().unwrap();
{
let ext = Ext::new(
&mut overlay,
@@ -1145,7 +1156,7 @@ mod tests {
ext.clear_storage(key.as_slice());
ext.storage_append(key.clone(), Item::InitializationItem.encode());
}
overlay.commit_prospective();
overlay.start_transaction();
// For example, first transaction resulted in panic during block building
{
@@ -1170,7 +1181,7 @@ mod tests {
Some(vec![Item::InitializationItem, Item::DiscardedItem].encode()),
);
}
overlay.discard_prospective();
overlay.rollback_transaction().unwrap();
// Then we apply next transaction which is valid this time.
{
@@ -1196,7 +1207,7 @@ mod tests {
);
}
overlay.commit_prospective();
overlay.start_transaction();
// Then only initlaization item and second (commited) item should persist.
{
@@ -1317,9 +1328,11 @@ mod tests {
let backend = state.as_trie_backend().unwrap();
let mut overlay = OverlayedChanges::default();
overlay.start_transaction();
overlay.set_storage(b"ccc".to_vec(), Some(b"".to_vec()));
assert_eq!(overlay.storage(b"ccc"), Some(Some(&[][..])));
overlay.commit_prospective();
overlay.commit_transaction().unwrap();
overlay.start_transaction();
assert_eq!(overlay.storage(b"ccc"), Some(Some(&[][..])));
assert_eq!(overlay.storage(b"bbb"), None);
@@ -1339,7 +1352,7 @@ mod tests {
ext.clear_storage(b"ccc");
assert_eq!(ext.storage(b"ccc"), None);
}
overlay.commit_prospective();
overlay.commit_transaction().unwrap();
assert_eq!(overlay.storage(b"ccc"), Some(None));
}
}