Skip to content
This repository has been archived by the owner on Nov 15, 2023. It is now read-only.

Commit

Permalink
Revert "checkpoint of what SADLY DOES NOT WORK, have to move the ::wi…
Browse files Browse the repository at this point in the history
…th wrapper up the stack, probably to executive"

This reverts commit 5db84f8.
  • Loading branch information
kianenigma committed Mar 31, 2022
1 parent 4914985 commit 30d1e5c
Show file tree
Hide file tree
Showing 4 changed files with 74 additions and 65 deletions.
1 change: 0 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion frame/support/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@ impl-trait-for-tuples = "0.2.1"
smallvec = "1.8.0"
log = { version = "0.4.14", default-features = false }
sp-core-hashing-proc-macro = { version = "5.0.0", path = "../../primitives/core/hashing/proc-macro" }
environmental = "1.1.3"

[dev-dependencies]
assert_matches = "1.3.0"
Expand Down
3 changes: 1 addition & 2 deletions frame/support/procedural/src/transactional.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,12 @@ pub fn transactional(_attr: TokenStream, input: TokenStream) -> Result<TokenStre
let ItemFn { attrs, vis, sig, block } = syn::parse(input)?;

let crate_ = generate_crate_access_2018("frame-support")?;
let fn_body = quote!(|| { #block });
let output = quote! {
#(#attrs)*
#vis #sig {
use #crate_::storage::{with_transaction, TransactionOutcome};
with_transaction(|| {
let r = #fn_body();
let r = (|| { #block })();
if r.is_ok() {
TransactionOutcome::Commit(r)
} else {
Expand Down
134 changes: 73 additions & 61 deletions frame/support/src/storage/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ use crate::{
},
};
use codec::{Decode, Encode, EncodeLike, FullCodec, FullEncode};
use sp_arithmetic::traits::Zero;
use sp_core::storage::ChildInfo;
pub use sp_runtime::TransactionOutcome;
use sp_runtime::{
Expand All @@ -48,33 +47,67 @@ pub mod types;
pub mod unhashed;
pub mod weak_bounded_vec;

type Layer = u32;
environmental::environmental!(current_level: Layer);
const TRANSACTIONAL_LIMIT: Layer = 255;
mod transaction_level_tracker {
const TRANSACTION_LEVEL_KEY: &'static [u8] = b":transaction_level:";
const TRANSACTIONAL_LIMIT: u32 = 255;
type Layer = u32;

/// Increments the transaction level. Returns an error if levels go past the limit.
pub fn inc_transaction_level(existing_levels: &mut Layer) -> Result<(), ()> {
if *existing_levels >= TRANSACTIONAL_LIMIT || *existing_levels == Layer::MAX {
return Err(())
pub fn get_transaction_level() -> Layer {
crate::storage::unhashed::get_or_default::<Layer>(TRANSACTION_LEVEL_KEY)
}
// Cannot overflow because of check above.
*existing_levels = *existing_levels + 1;
Ok(())
}

fn dec_transaction_level(existing_levels: &mut Layer) {
if *existing_levels == 0 {
crate::defensive!(
"We are underflowing with calculating transactional levels. Not great, but let's not panic..."
);
} else {
*existing_levels = *existing_levels - 1;
fn set_transaction_level(level: &Layer) {
crate::storage::unhashed::put::<Layer>(TRANSACTION_LEVEL_KEY, level);
}

fn kill_transaction_level() {
crate::storage::unhashed::kill(TRANSACTION_LEVEL_KEY);
}

/// Increments the transaction level. Returns an error if levels go past the limit.
///
/// Returns a guard that when dropped decrements the transaction level automatically.
pub fn inc_transaction_level() -> Result<StorageLayerGuard, ()> {
let existing_levels = get_transaction_level();
if existing_levels >= TRANSACTIONAL_LIMIT || existing_levels == Layer::MAX {
return Err(())
}
// Cannot overflow because of check above.
set_transaction_level(&(existing_levels + 1));
Ok(StorageLayerGuard)
}

fn dec_transaction_level() {
let existing_levels = get_transaction_level();
if existing_levels == 0 {
log::warn!(
"We are underflowing with calculating transactional levels. Not great, but let's not panic...",
);
} else if existing_levels == 1 {
// Don't leave any trace of this storage item.
kill_transaction_level();
} else {
// Cannot underflow because of checks above.
set_transaction_level(&(existing_levels - 1));
}
}

pub fn is_transactional() -> bool {
get_transaction_level() > 0
}

pub struct StorageLayerGuard;

impl Drop for StorageLayerGuard {
fn drop(&mut self) {
dec_transaction_level()
}
}
}

/// Check if the current call is within a transactional layer.
pub fn is_transactional() -> bool {
current_level::with(|_| {}).is_some()
transaction_level_tracker::is_transactional()
}

/// Execute the supplied function in a new storage transaction.
Expand All @@ -85,40 +118,27 @@ pub fn is_transactional() -> bool {
/// Transactions can be nested up to 10 times; more than that will result in an error.
///
/// Commits happen to the parent transaction.
pub fn with_transaction<T, E>(
f: impl FnOnce() -> TransactionOutcome<Result<T, E>>,
existing_levels: &mut Layer,
) -> Result<T, E>
pub fn with_transaction<T, E>(f: impl FnOnce() -> TransactionOutcome<Result<T, E>>) -> Result<T, E>
where
E: From<DispatchError>,
{
use sp_io::storage::{commit_transaction, rollback_transaction, start_transaction};
use TransactionOutcome::*;
let execute_with_transaction_levels = || {
current_level::with(|existing_levels| {
let _ = inc_transaction_level(existing_levels)
.map_err(|()| DispatchError::TransactionalLimit.into())?;
start_transaction();
let res = match f() {
Commit(res) => {
commit_transaction();
res
},
Rollback(res) => {
rollback_transaction();
res
},
};
dec_transaction_level(existing_levels);

let _guard = transaction_level_tracker::inc_transaction_level()
.map_err(|()| DispatchError::TransactionalLimit.into())?;

start_transaction();

match f() {
Commit(res) => {
commit_transaction();
res
})
.unwrap()
};
if is_transactional() {
execute_with_transaction_levels()
} else {
let mut init = Zero::zero();
current_level::using(&mut init, || execute_with_transaction_levels())
},
Rollback(res) => {
rollback_transaction();
res
},
}
}

Expand Down Expand Up @@ -1574,28 +1594,20 @@ mod test {
})
}

fn get_transaction_level_or_default() -> Layer {
if is_transactional() {
current_level::with(|x| *x).unwrap()
} else {
0
}
}

#[test]
fn transaction_limit_should_work() {
TestExternalities::default().execute_with(|| {
assert_eq!(get_transaction_level_or_default(), 0);
assert_eq!(transaction_level_tracker::get_transaction_level(), 0);

assert_ok!(with_transaction(|| -> TransactionOutcome<DispatchResult> {
assert_eq!(get_transaction_level_or_default(), 1);
assert_eq!(transaction_level_tracker::get_transaction_level(), 1);
TransactionOutcome::Commit(Ok(()))
}));

assert_ok!(with_transaction(|| -> TransactionOutcome<DispatchResult> {
assert_eq!(get_transaction_level_or_default(), 1);
assert_eq!(transaction_level_tracker::get_transaction_level(), 1);
let res = with_transaction(|| -> TransactionOutcome<DispatchResult> {
assert_eq!(get_transaction_level_or_default(), 2);
assert_eq!(transaction_level_tracker::get_transaction_level(), 2);
TransactionOutcome::Commit(Ok(()))
});
TransactionOutcome::Commit(res)
Expand All @@ -1607,7 +1619,7 @@ mod test {
sp_runtime::DispatchError::TransactionalLimit
);

assert_eq!(get_transaction_level_or_default(), 0);
assert_eq!(transaction_level_tracker::get_transaction_level(), 0);
});
}

Expand Down

0 comments on commit 30d1e5c

Please sign in to comment.