-
Notifications
You must be signed in to change notification settings - Fork 694
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
pallet-utility: if_else
#6321
Open
rainbow-promise
wants to merge
11
commits into
paritytech:master
Choose a base branch
from
rainbow-promise:if-else-utility
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+326
−0
Open
pallet-utility: if_else
#6321
Changes from 1 commit
Commits
Show all changes
11 commits
Select commit
Hold shift + click to select a range
b83a8e8
extrinsic outline
rainbow-promise 1b01dc9
new: outline utility_if_else
rainbow-promise 430a791
do not clone calls
rainbow-promise 2caf95e
compiler needs to know the size, put in Box
rainbow-promise 4183c6b
siimple test cases
rainbow-promise e70a607
if_else::benchmarking
rainbow-promise a93b142
add weights substrate
rainbow-promise cc79306
add weight to all runtimes
rainbow-promise 007d1c3
add prdoc
rainbow-promise 7f2c600
Merge branch 'master' into if-else-utility
rainbow-promise f2bd937
check CI event not emitted
rainbow-promise File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -72,12 +72,6 @@ pub use weights::WeightInfo; | |||||
|
||||||
pub use pallet::*; | ||||||
|
||||||
#[derive(PartialEq, Eq, Clone, Copy, Encode, Decode, Debug, TypeInfo)] | ||||||
pub enum Which { | ||||||
Main, | ||||||
Fallback, | ||||||
} | ||||||
|
||||||
#[frame_support::pallet] | ||||||
pub mod pallet { | ||||||
use super::*; | ||||||
|
@@ -127,8 +121,12 @@ pub mod pallet { | |||||
ItemFailed { error: DispatchError }, | ||||||
/// A call was dispatched. | ||||||
DispatchedAs { result: DispatchResult }, | ||||||
/// if_else completed. | ||||||
IfElseCompleted { call: Which }, | ||||||
/// Main call was dispatched. | ||||||
IfElseMainSuccess, | ||||||
/// The fallback call was dispatched. | ||||||
IfElseFallbackSuccess { main_error: DispatchError }, | ||||||
/// Both calls failed. | ||||||
IfElseBothFailure { main_error: DispatchError, fallback_error: DispatchError }, | ||||||
} | ||||||
|
||||||
// Align the call size to 1KB. As we are currently compiling the runtime for native/wasm | ||||||
|
@@ -466,6 +464,32 @@ pub mod pallet { | |||||
res.map(|_| ()).map_err(|e| e.error) | ||||||
} | ||||||
|
||||||
/// Dispatch a fallback call in the event the main call fails to execute. | ||||||
/// | ||||||
/// This function first attempts to dispatch the `main` call. If the `main` call fails, the `fallback` call | ||||||
/// is dispatched instead. Both calls are executed with the same origin, and the weight of both calls | ||||||
/// is accumulated and returned. The success or failure of the main and fallback calls is tracked and | ||||||
/// appropriate events are deposited. | ||||||
/// | ||||||
/// May be called from any origin except `None`. | ||||||
/// | ||||||
/// - `main`: The main call to be dispatched. This is the primary action to execute. | ||||||
/// - `fallback`: The fallback call to be dispatched in case the `main` call fails. | ||||||
/// | ||||||
/// ## Dispatch Logic | ||||||
/// - If the origin is `root`, both the main and fallback calls are executed without applying any origin filters. | ||||||
/// - If the origin is not `root`, the origin filter is applied to both the `main` and `fallback` calls. | ||||||
/// | ||||||
/// ## Complexity | ||||||
/// - O(1) for the origin check and dispatching the main call. | ||||||
/// - O(1) for the origin check and dispatching the fallback call (if needed). | ||||||
/// - Overall complexity is O(1) since we only dispatch at most two calls. | ||||||
/// | ||||||
/// ## Weight | ||||||
/// The weight of this call is calculated as the sum of: | ||||||
/// - The weight of the `main` call (if it is executed), | ||||||
/// - The weight of the `fallback` call (if the `main` call fails and the `fallback` is executed), | ||||||
/// - A base weight (`WeightInfo::if_else()`), which accounts for the logic involved in dispatching and handling both calls. | ||||||
#[pallet::call_index(6)] | ||||||
rainbow-promise marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
#[pallet::weight({ | ||||||
let main_dispatch_info = main.get_dispatch_info(); | ||||||
|
@@ -490,7 +514,7 @@ pub mod pallet { | |||||
let is_root = ensure_root(origin.clone()).is_ok(); | ||||||
|
||||||
// Track the weights | ||||||
let mut weight = Weight::zero(); | ||||||
let mut weight = T::WeightInfo::if_else(); | ||||||
|
||||||
let info = main.get_dispatch_info(); | ||||||
|
||||||
|
@@ -504,30 +528,31 @@ pub mod pallet { | |||||
// Add weight of the main call | ||||||
weight = weight.saturating_add(extract_actual_weight(&main_result, &info)); | ||||||
|
||||||
if let Err(_main_call_error) = main_result { | ||||||
if let Err(main_error) = main_result { | ||||||
// If the main call failed, execute the fallback call | ||||||
let fallback_info = fallback.get_dispatch_info(); | ||||||
|
||||||
let fallback_result = if is_root { | ||||||
fallback.dispatch_bypass_filter(origin.clone()) | ||||||
} else { | ||||||
fallback.dispatch(origin.clone()) | ||||||
fallback.dispatch(origin) | ||||||
}; | ||||||
|
||||||
// Add weight of the fallback call | ||||||
weight = | ||||||
weight.saturating_add(extract_actual_weight(&fallback_result, &fallback_info)); | ||||||
|
||||||
if let Err(_fallback_error) = fallback_result { | ||||||
if let Err(fallback_error) = fallback_result { | ||||||
// Both calls have faild. | ||||||
Self::deposit_event(Event::IfElseBothFailure { main_error: main_error.error, fallback_error: fallback_error.error }); | ||||||
return Err(Error::<T>::InvalidCalls.into()) | ||||||
rainbow-promise marked this conversation as resolved.
Show resolved
Hide resolved
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We can also do the refund in case of failure.
Suggested change
|
||||||
} | ||||||
// Fallback succeeded. | ||||||
Self::deposit_event(Event::IfElseCompleted { call: Which::Fallback }); | ||||||
Self::deposit_event(Event::IfElseFallbackSuccess { main_error: main_error.error }); | ||||||
return Ok(Some(weight).into()); | ||||||
} | ||||||
// Main call succeeded. | ||||||
Self::deposit_event(Event::IfElseCompleted { call: Which::Main }); | ||||||
Self::deposit_event(Event::IfElseMainSuccess); | ||||||
Ok(Some(weight).into()) | ||||||
} | ||||||
} | ||||||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -952,5 +952,41 @@ fn if_else_with_signed_works() { | |
)); | ||
assert_eq!(Balances::free_balance(1), 5); | ||
assert_eq!(Balances::free_balance(2), 15); | ||
|
||
System::assert_last_event(utility::Event::IfElseFallbackSuccess { main_error: TokenError::FundsUnavailable.into() }.into()); | ||
}); | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe 2 more tests:
|
||
|
||
#[test] | ||
fn if_else_successful_main_call() { | ||
new_test_ext().execute_with(|| { | ||
assert_eq!(Balances::free_balance(1), 10); | ||
assert_eq!(Balances::free_balance(2), 10); | ||
assert_ok!(Utility::if_else( | ||
RuntimeOrigin::signed(1), | ||
Box::new(call_transfer(2, 9)), | ||
Box::new(call_transfer(2, 1)) | ||
)); | ||
assert_eq!(Balances::free_balance(1), 1); | ||
assert_eq!(Balances::free_balance(2), 19); | ||
|
||
System::assert_last_event(utility::Event::IfElseMainSuccess.into()); | ||
}) | ||
} | ||
|
||
#[test] | ||
fn if_else_failing_else_call() { | ||
new_test_ext().execute_with(|| { | ||
assert_eq!(Balances::free_balance(1), 10); | ||
assert_eq!(Balances::free_balance(2), 10); | ||
assert_noop!(Utility::if_else( | ||
RuntimeOrigin::signed(1), | ||
Box::new(call_transfer(2, 11)), | ||
Box::new(call_transfer(2, 11)) | ||
), utility::Error::<Test>::InvalidCalls); | ||
assert_eq!(Balances::free_balance(1), 10); | ||
assert_eq!(Balances::free_balance(2), 10); | ||
|
||
System::assert_last_event(utility::Event::IfElseBothFailure { main_error: TokenError::FundsUnavailable.into(), fallback_error: TokenError::FundsUnavailable.into()}.into()); | ||
}) | ||
} |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back 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.
The err branch should be slightly more costly, but difference should be negligible so it is ok.
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 adjusting with a failed main call will give the proper weights as it enters the else branch? will update this.
Thanks for the review