mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-09 20:11:09 +00:00
Implement batch_all and update Utility pallet for weight refunds (#7188)
* implement batch_all * bump version * updates * Better weight story for utility * small fixes * weights * assert_noop_ignore_postinfo doesnt make sense * Apply suggestions from code review Co-authored-by: Bastian Köcher <bkchr@users.noreply.github.com> Co-authored-by: Shawn Tabrizi <shawntabrizi@gmail.com> Co-authored-by: Bastian Köcher <bkchr@users.noreply.github.com>
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Copyright (C) 2017-2020 Parity Technologies (UK) Ltd.
|
||||
// Copyright (C) 2020 Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@@ -15,7 +15,9 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 2.0.0-rc5
|
||||
//! Weights for pallet_utility
|
||||
//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 2.0.0
|
||||
//! DATE: 2020-10-02, STEPS: [50], REPEAT: 20, LOW RANGE: [], HIGH RANGE: []
|
||||
|
||||
#![allow(unused_parens)]
|
||||
#![allow(unused_imports)]
|
||||
@@ -26,10 +28,14 @@ use sp_std::marker::PhantomData;
|
||||
pub struct WeightInfo<T>(PhantomData<T>);
|
||||
impl<T: frame_system::Trait> pallet_utility::WeightInfo for WeightInfo<T> {
|
||||
fn batch(c: u32, ) -> Weight {
|
||||
(16461000 as Weight)
|
||||
.saturating_add((1982000 as Weight).saturating_mul(c as Weight))
|
||||
(20_803_000 as Weight)
|
||||
.saturating_add((1_984_000 as Weight).saturating_mul(c as Weight))
|
||||
}
|
||||
fn as_derivative() -> Weight {
|
||||
(4086000 as Weight)
|
||||
(5_853_000 as Weight)
|
||||
}
|
||||
fn batch_all(c: u32, ) -> Weight {
|
||||
(21_104_000 as Weight)
|
||||
.saturating_add((1_509_000 as Weight).saturating_mul(c as Weight))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,6 +56,19 @@ benchmarks! {
|
||||
let caller_key = frame_system::Account::<T>::hashed_key_for(&caller);
|
||||
frame_benchmarking::benchmarking::add_to_whitelist(caller_key.into());
|
||||
}: _(RawOrigin::Signed(caller), SEED as u16, call)
|
||||
|
||||
batch_all {
|
||||
let c in 0 .. 1000;
|
||||
let mut calls: Vec<<T as Trait>::Call> = Vec::new();
|
||||
for i in 0 .. c {
|
||||
let call = frame_system::Call::remark(vec![]).into();
|
||||
calls.push(call);
|
||||
}
|
||||
let caller = whitelisted_caller();
|
||||
}: _(RawOrigin::Signed(caller), calls)
|
||||
verify {
|
||||
assert_last_event::<T>(Event::BatchCompleted.into())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -69,6 +82,7 @@ mod tests {
|
||||
new_test_ext().execute_with(|| {
|
||||
assert_ok!(test_benchmark_batch::<Test>());
|
||||
assert_ok!(test_benchmark_as_derivative::<Test>());
|
||||
assert_ok!(test_benchmark_batch_all::<Test>());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Copyright (C) 2019-2020 Parity Technologies (UK) Ltd.
|
||||
// Copyright (C) 2020 Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@@ -15,7 +15,9 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 2.0.0-rc5
|
||||
//! Weights for pallet_utility
|
||||
//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 2.0.0
|
||||
//! DATE: 2020-10-02, STEPS: [50], REPEAT: 20, LOW RANGE: [], HIGH RANGE: []
|
||||
|
||||
#![allow(unused_parens)]
|
||||
#![allow(unused_imports)]
|
||||
@@ -24,10 +26,14 @@ use frame_support::weights::{Weight, constants::RocksDbWeight as DbWeight};
|
||||
|
||||
impl crate::WeightInfo for () {
|
||||
fn batch(c: u32, ) -> Weight {
|
||||
(16461000 as Weight)
|
||||
.saturating_add((1982000 as Weight).saturating_mul(c as Weight))
|
||||
(20_803_000 as Weight)
|
||||
.saturating_add((1_984_000 as Weight).saturating_mul(c as Weight))
|
||||
}
|
||||
fn as_derivative() -> Weight {
|
||||
(4086000 as Weight)
|
||||
(5_853_000 as Weight)
|
||||
}
|
||||
fn batch_all(c: u32, ) -> Weight {
|
||||
(21_104_000 as Weight)
|
||||
.saturating_add((1_509_000 as Weight).saturating_mul(c as Weight))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,13 +59,14 @@ use sp_std::prelude::*;
|
||||
use codec::{Encode, Decode};
|
||||
use sp_core::TypeId;
|
||||
use sp_io::hashing::blake2_256;
|
||||
use frame_support::{decl_module, decl_event, decl_storage, Parameter};
|
||||
use frame_support::{decl_module, decl_event, decl_storage, Parameter, transactional};
|
||||
use frame_support::{
|
||||
traits::{OriginTrait, UnfilteredDispatchable, Get},
|
||||
weights::{Weight, GetDispatchInfo, DispatchClass}, dispatch::PostDispatchInfo,
|
||||
weights::{Weight, GetDispatchInfo, DispatchClass, extract_actual_weight},
|
||||
dispatch::{PostDispatchInfo, DispatchResultWithPostInfo},
|
||||
};
|
||||
use frame_system::{ensure_signed, ensure_root};
|
||||
use sp_runtime::{DispatchError, DispatchResult, traits::Dispatchable};
|
||||
use sp_runtime::{DispatchError, traits::Dispatchable};
|
||||
|
||||
mod tests;
|
||||
mod benchmarking;
|
||||
@@ -74,6 +75,7 @@ mod default_weights;
|
||||
pub trait WeightInfo {
|
||||
fn batch(c: u32, ) -> Weight;
|
||||
fn as_derivative() -> Weight;
|
||||
fn batch_all(c: u32, ) -> Weight;
|
||||
}
|
||||
|
||||
/// Configuration trait.
|
||||
@@ -128,9 +130,7 @@ decl_module! {
|
||||
/// bypassing `frame_system::Trait::BaseCallFilter`).
|
||||
///
|
||||
/// # <weight>
|
||||
/// - Base weight: 14.39 + .987 * c µs
|
||||
/// - Plus the sum of the weights of the `calls`.
|
||||
/// - Plus one additional event. (repeat read/write)
|
||||
/// - Complexity: O(C) where C is the number of calls to be batched.
|
||||
/// # </weight>
|
||||
///
|
||||
/// This will return `Ok` in all circumstances. To determine the success of the batch, an
|
||||
@@ -154,20 +154,32 @@ decl_module! {
|
||||
}
|
||||
},
|
||||
)]
|
||||
fn batch(origin, calls: Vec<<T as Trait>::Call>) {
|
||||
fn batch(origin, calls: Vec<<T as Trait>::Call>) -> DispatchResultWithPostInfo {
|
||||
let is_root = ensure_root(origin.clone()).is_ok();
|
||||
let calls_len = calls.len();
|
||||
// Track the actual weight of each of the batch calls.
|
||||
let mut weight: Weight = 0;
|
||||
for (index, call) in calls.into_iter().enumerate() {
|
||||
let info = call.get_dispatch_info();
|
||||
// If origin is root, don't apply any dispatch filters; root can call anything.
|
||||
let result = if is_root {
|
||||
call.dispatch_bypass_filter(origin.clone())
|
||||
} else {
|
||||
call.dispatch(origin.clone())
|
||||
};
|
||||
// Add the weight of this call.
|
||||
weight = weight.saturating_add(extract_actual_weight(&result, &info));
|
||||
if let Err(e) = result {
|
||||
Self::deposit_event(Event::BatchInterrupted(index as u32, e.error));
|
||||
return Ok(());
|
||||
// Take the weight of this function itself into account.
|
||||
let base_weight = T::WeightInfo::batch(index.saturating_add(1) as u32);
|
||||
// Return the actual used weight + base_weight of this call.
|
||||
return Ok(Some(base_weight + weight).into());
|
||||
}
|
||||
}
|
||||
Self::deposit_event(Event::BatchCompleted);
|
||||
let base_weight = T::WeightInfo::batch(calls_len as u32);
|
||||
Ok(Some(base_weight + weight).into())
|
||||
}
|
||||
|
||||
/// Send a call through an indexed pseudonym of the sender.
|
||||
@@ -190,12 +202,79 @@ decl_module! {
|
||||
.saturating_add(T::DbWeight::get().reads_writes(1, 1)),
|
||||
call.get_dispatch_info().class,
|
||||
)]
|
||||
fn as_derivative(origin, index: u16, call: Box<<T as Trait>::Call>) -> DispatchResult {
|
||||
fn as_derivative(origin, index: u16, call: Box<<T as Trait>::Call>) -> DispatchResultWithPostInfo {
|
||||
let mut origin = origin;
|
||||
let who = ensure_signed(origin.clone())?;
|
||||
let pseudonym = Self::derivative_account_id(who, index);
|
||||
origin.set_caller_from(frame_system::RawOrigin::Signed(pseudonym));
|
||||
call.dispatch(origin).map(|_| ()).map_err(|e| e.error)
|
||||
let info = call.get_dispatch_info();
|
||||
let result = call.dispatch(origin);
|
||||
// Always take into account the base weight of this call.
|
||||
let mut weight = T::WeightInfo::as_derivative().saturating_add(T::DbWeight::get().reads_writes(1, 1));
|
||||
// Add the real weight of the dispatch.
|
||||
weight = weight.saturating_add(extract_actual_weight(&result, &info));
|
||||
result.map_err(|mut err| {
|
||||
err.post_info = Some(weight).into();
|
||||
err
|
||||
}).map(|_| Some(weight).into())
|
||||
}
|
||||
|
||||
/// Send a batch of dispatch calls and atomically execute them.
|
||||
/// The whole transaction will rollback and fail if any of the calls failed.
|
||||
///
|
||||
/// May be called from any origin.
|
||||
///
|
||||
/// - `calls`: The calls to be dispatched from the same origin.
|
||||
///
|
||||
/// If origin is root then call are dispatch without checking origin filter. (This includes
|
||||
/// bypassing `frame_system::Trait::BaseCallFilter`).
|
||||
///
|
||||
/// # <weight>
|
||||
/// - Complexity: O(C) where C is the number of calls to be batched.
|
||||
/// # </weight>
|
||||
#[weight = (
|
||||
calls.iter()
|
||||
.map(|call| call.get_dispatch_info().weight)
|
||||
.fold(0, |total: Weight, weight: Weight| total.saturating_add(weight))
|
||||
.saturating_add(T::WeightInfo::batch_all(calls.len() as u32)),
|
||||
{
|
||||
let all_operational = calls.iter()
|
||||
.map(|call| call.get_dispatch_info().class)
|
||||
.all(|class| class == DispatchClass::Operational);
|
||||
if all_operational {
|
||||
DispatchClass::Operational
|
||||
} else {
|
||||
DispatchClass::Normal
|
||||
}
|
||||
},
|
||||
)]
|
||||
#[transactional]
|
||||
fn batch_all(origin, calls: Vec<<T as Trait>::Call>) -> DispatchResultWithPostInfo {
|
||||
let is_root = ensure_root(origin.clone()).is_ok();
|
||||
let calls_len = calls.len();
|
||||
// Track the actual weight of each of the batch calls.
|
||||
let mut weight: Weight = 0;
|
||||
for (index, call) in calls.into_iter().enumerate() {
|
||||
let info = call.get_dispatch_info();
|
||||
// If origin is root, bypass any dispatch filter; root can call anything.
|
||||
let result = if is_root {
|
||||
call.dispatch_bypass_filter(origin.clone())
|
||||
} else {
|
||||
call.dispatch(origin.clone())
|
||||
};
|
||||
// Add the weight of this call.
|
||||
weight = weight.saturating_add(extract_actual_weight(&result, &info));
|
||||
result.map_err(|mut err| {
|
||||
// Take the weight of this function itself into account.
|
||||
let base_weight = T::WeightInfo::batch_all(index.saturating_add(1) as u32);
|
||||
// Return the actual used weight + base_weight of this call.
|
||||
err.post_info = Some(base_weight + weight).into();
|
||||
err
|
||||
})?;
|
||||
}
|
||||
Self::deposit_event(Event::BatchCompleted);
|
||||
let base_weight = T::WeightInfo::batch_all(calls_len as u32);
|
||||
Ok(Some(base_weight + weight).into())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,13 +22,51 @@
|
||||
use super::*;
|
||||
|
||||
use frame_support::{
|
||||
assert_ok, assert_noop, impl_outer_origin, parameter_types, impl_outer_dispatch,
|
||||
weights::Weight, impl_outer_event, dispatch::DispatchError, traits::Filter, storage,
|
||||
assert_ok, assert_noop, impl_outer_origin, parameter_types, impl_outer_dispatch, impl_outer_event,
|
||||
assert_err_ignore_postinfo,
|
||||
weights::{Weight, Pays},
|
||||
dispatch::{DispatchError, DispatchErrorWithPostInfo, Dispatchable},
|
||||
traits::Filter,
|
||||
storage,
|
||||
};
|
||||
use sp_core::H256;
|
||||
use sp_runtime::{Perbill, traits::{BlakeTwo256, IdentityLookup}, testing::Header};
|
||||
use crate as utility;
|
||||
|
||||
// example module to test behaviors.
|
||||
pub mod example {
|
||||
use super::*;
|
||||
use frame_support::dispatch::WithPostDispatchInfo;
|
||||
pub trait Trait: frame_system::Trait { }
|
||||
|
||||
decl_module! {
|
||||
pub struct Module<T: Trait> for enum Call where origin: <T as frame_system::Trait>::Origin {
|
||||
#[weight = *weight]
|
||||
fn noop(_origin, weight: Weight) { }
|
||||
|
||||
#[weight = *start_weight]
|
||||
fn foobar(
|
||||
origin,
|
||||
err: bool,
|
||||
start_weight: Weight,
|
||||
end_weight: Option<Weight>,
|
||||
) -> DispatchResultWithPostInfo {
|
||||
let _ = ensure_signed(origin)?;
|
||||
if err {
|
||||
let error: DispatchError = "The cake is a lie.".into();
|
||||
if let Some(weight) = end_weight {
|
||||
Err(error.with_weight(weight))
|
||||
} else {
|
||||
Err(error)?
|
||||
}
|
||||
} else {
|
||||
Ok(end_weight.into())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl_outer_origin! {
|
||||
pub enum Origin for Test where system = frame_system {}
|
||||
}
|
||||
@@ -44,6 +82,7 @@ impl_outer_dispatch! {
|
||||
frame_system::System,
|
||||
pallet_balances::Balances,
|
||||
utility::Utility,
|
||||
example::Example,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -102,13 +141,19 @@ parameter_types! {
|
||||
pub const MultisigDepositFactor: u64 = 1;
|
||||
pub const MaxSignatories: u16 = 3;
|
||||
}
|
||||
|
||||
impl example::Trait for Test {}
|
||||
|
||||
pub struct TestBaseCallFilter;
|
||||
impl Filter<Call> for TestBaseCallFilter {
|
||||
fn filter(c: &Call) -> bool {
|
||||
match *c {
|
||||
Call::Balances(_) => true,
|
||||
Call::Utility(_) => true,
|
||||
// For benchmarking, this acts as a noop call
|
||||
Call::System(frame_system::Call::remark(..)) => true,
|
||||
// For tests
|
||||
Call::Example(_) => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
@@ -120,8 +165,12 @@ impl Trait for Test {
|
||||
}
|
||||
type System = frame_system::Module<Test>;
|
||||
type Balances = pallet_balances::Module<Test>;
|
||||
type Example = example::Module<Test>;
|
||||
type Utility = Module<Test>;
|
||||
|
||||
type ExampleCall = example::Call<Test>;
|
||||
type UtilityCall = crate::Call<Test>;
|
||||
|
||||
use frame_system::Call as SystemCall;
|
||||
use pallet_balances::Call as BalancesCall;
|
||||
use pallet_balances::Error as BalancesError;
|
||||
@@ -149,7 +198,7 @@ fn as_derivative_works() {
|
||||
new_test_ext().execute_with(|| {
|
||||
let sub_1_0 = Utility::derivative_account_id(1, 0);
|
||||
assert_ok!(Balances::transfer(Origin::signed(1), sub_1_0, 5));
|
||||
assert_noop!(Utility::as_derivative(
|
||||
assert_err_ignore_postinfo!(Utility::as_derivative(
|
||||
Origin::signed(1),
|
||||
1,
|
||||
Box::new(Call::Balances(BalancesCall::transfer(6, 3))),
|
||||
@@ -164,10 +213,70 @@ fn as_derivative_works() {
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn as_derivative_handles_weight_refund() {
|
||||
new_test_ext().execute_with(|| {
|
||||
let start_weight = 100;
|
||||
let end_weight = 75;
|
||||
let diff = start_weight - end_weight;
|
||||
|
||||
// Full weight when ok
|
||||
let inner_call = Call::Example(ExampleCall::foobar(false, start_weight, None));
|
||||
let call = Call::Utility(UtilityCall::as_derivative(0, Box::new(inner_call)));
|
||||
let info = call.get_dispatch_info();
|
||||
let result = call.dispatch(Origin::signed(1));
|
||||
assert_ok!(result);
|
||||
assert_eq!(extract_actual_weight(&result, &info), info.weight);
|
||||
|
||||
// Refund weight when ok
|
||||
let inner_call = Call::Example(ExampleCall::foobar(false, start_weight, Some(end_weight)));
|
||||
let call = Call::Utility(UtilityCall::as_derivative(0, Box::new(inner_call)));
|
||||
let info = call.get_dispatch_info();
|
||||
let result = call.dispatch(Origin::signed(1));
|
||||
assert_ok!(result);
|
||||
// Diff is refunded
|
||||
assert_eq!(extract_actual_weight(&result, &info), info.weight - diff);
|
||||
|
||||
// Full weight when err
|
||||
let inner_call = Call::Example(ExampleCall::foobar(true, start_weight, None));
|
||||
let call = Call::Utility(UtilityCall::as_derivative(0, Box::new(inner_call)));
|
||||
let info = call.get_dispatch_info();
|
||||
let result = call.dispatch(Origin::signed(1));
|
||||
assert_noop!(
|
||||
result,
|
||||
DispatchErrorWithPostInfo {
|
||||
post_info: PostDispatchInfo {
|
||||
// No weight is refunded
|
||||
actual_weight: Some(info.weight),
|
||||
pays_fee: Pays::Yes,
|
||||
},
|
||||
error: DispatchError::Other("The cake is a lie."),
|
||||
}
|
||||
);
|
||||
|
||||
// Refund weight when err
|
||||
let inner_call = Call::Example(ExampleCall::foobar(true, start_weight, Some(end_weight)));
|
||||
let call = Call::Utility(UtilityCall::as_derivative(0, Box::new(inner_call)));
|
||||
let info = call.get_dispatch_info();
|
||||
let result = call.dispatch(Origin::signed(1));
|
||||
assert_noop!(
|
||||
result,
|
||||
DispatchErrorWithPostInfo {
|
||||
post_info: PostDispatchInfo {
|
||||
// Diff is refunded
|
||||
actual_weight: Some(info.weight - diff),
|
||||
pays_fee: Pays::Yes,
|
||||
},
|
||||
error: DispatchError::Other("The cake is a lie."),
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn as_derivative_filters() {
|
||||
new_test_ext().execute_with(|| {
|
||||
assert_noop!(Utility::as_derivative(
|
||||
assert_err_ignore_postinfo!(Utility::as_derivative(
|
||||
Origin::signed(1),
|
||||
1,
|
||||
Box::new(Call::System(frame_system::Call::suicide())),
|
||||
@@ -255,3 +364,179 @@ fn batch_weight_calculation_doesnt_overflow() {
|
||||
assert_eq!(batch_call.get_dispatch_info().weight, Weight::max_value());
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn batch_handles_weight_refund() {
|
||||
new_test_ext().execute_with(|| {
|
||||
let start_weight = 100;
|
||||
let end_weight = 75;
|
||||
let diff = start_weight - end_weight;
|
||||
let batch_len: Weight = 4;
|
||||
|
||||
// Full weight when ok
|
||||
let inner_call = Call::Example(ExampleCall::foobar(false, start_weight, None));
|
||||
let batch_calls = vec![inner_call; batch_len as usize];
|
||||
let call = Call::Utility(UtilityCall::batch(batch_calls));
|
||||
let info = call.get_dispatch_info();
|
||||
let result = call.dispatch(Origin::signed(1));
|
||||
assert_ok!(result);
|
||||
assert_eq!(extract_actual_weight(&result, &info), info.weight);
|
||||
|
||||
// Refund weight when ok
|
||||
let inner_call = Call::Example(ExampleCall::foobar(false, start_weight, Some(end_weight)));
|
||||
let batch_calls = vec![inner_call; batch_len as usize];
|
||||
let call = Call::Utility(UtilityCall::batch(batch_calls));
|
||||
let info = call.get_dispatch_info();
|
||||
let result = call.dispatch(Origin::signed(1));
|
||||
assert_ok!(result);
|
||||
// Diff is refunded
|
||||
assert_eq!(extract_actual_weight(&result, &info), info.weight - diff * batch_len);
|
||||
|
||||
// Full weight when err
|
||||
let good_call = Call::Example(ExampleCall::foobar(false, start_weight, None));
|
||||
let bad_call = Call::Example(ExampleCall::foobar(true, start_weight, None));
|
||||
let batch_calls = vec![good_call, bad_call];
|
||||
let call = Call::Utility(UtilityCall::batch(batch_calls));
|
||||
let info = call.get_dispatch_info();
|
||||
let result = call.dispatch(Origin::signed(1));
|
||||
assert_ok!(result);
|
||||
expect_event(Event::BatchInterrupted(1, DispatchError::Other("")));
|
||||
// No weight is refunded
|
||||
assert_eq!(extract_actual_weight(&result, &info), info.weight);
|
||||
|
||||
// Refund weight when err
|
||||
let good_call = Call::Example(ExampleCall::foobar(false, start_weight, Some(end_weight)));
|
||||
let bad_call = Call::Example(ExampleCall::foobar(true, start_weight, Some(end_weight)));
|
||||
let batch_calls = vec![good_call, bad_call];
|
||||
let batch_len = batch_calls.len() as Weight;
|
||||
let call = Call::Utility(UtilityCall::batch(batch_calls));
|
||||
let info = call.get_dispatch_info();
|
||||
let result = call.dispatch(Origin::signed(1));
|
||||
assert_ok!(result);
|
||||
expect_event(Event::BatchInterrupted(1, DispatchError::Other("")));
|
||||
assert_eq!(extract_actual_weight(&result, &info), info.weight - diff * batch_len);
|
||||
|
||||
// Partial batch completion
|
||||
let good_call = Call::Example(ExampleCall::foobar(false, start_weight, Some(end_weight)));
|
||||
let bad_call = Call::Example(ExampleCall::foobar(true, start_weight, Some(end_weight)));
|
||||
let batch_calls = vec![good_call, bad_call.clone(), bad_call];
|
||||
let call = Call::Utility(UtilityCall::batch(batch_calls));
|
||||
let info = call.get_dispatch_info();
|
||||
let result = call.dispatch(Origin::signed(1));
|
||||
assert_ok!(result);
|
||||
expect_event(Event::BatchInterrupted(1, DispatchError::Other("")));
|
||||
assert_eq!(
|
||||
extract_actual_weight(&result, &info),
|
||||
// Real weight is 2 calls at end_weight
|
||||
<Test as Trait>::WeightInfo::batch(2) + end_weight * 2,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn batch_all_works() {
|
||||
new_test_ext().execute_with(|| {
|
||||
assert_eq!(Balances::free_balance(1), 10);
|
||||
assert_eq!(Balances::free_balance(2), 10);
|
||||
assert_ok!(
|
||||
Utility::batch_all(Origin::signed(1), vec![
|
||||
Call::Balances(BalancesCall::transfer(2, 5)),
|
||||
Call::Balances(BalancesCall::transfer(2, 5))
|
||||
]),
|
||||
);
|
||||
assert_eq!(Balances::free_balance(1), 0);
|
||||
assert_eq!(Balances::free_balance(2), 20);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn batch_all_revert() {
|
||||
new_test_ext().execute_with(|| {
|
||||
let call = Call::Balances(BalancesCall::transfer(2, 5));
|
||||
let info = call.get_dispatch_info();
|
||||
|
||||
assert_eq!(Balances::free_balance(1), 10);
|
||||
assert_eq!(Balances::free_balance(2), 10);
|
||||
assert_noop!(
|
||||
Utility::batch_all(Origin::signed(1), vec![
|
||||
Call::Balances(BalancesCall::transfer(2, 5)),
|
||||
Call::Balances(BalancesCall::transfer(2, 10)),
|
||||
Call::Balances(BalancesCall::transfer(2, 5)),
|
||||
]),
|
||||
DispatchErrorWithPostInfo {
|
||||
post_info: PostDispatchInfo {
|
||||
actual_weight: Some(<Test as Trait>::WeightInfo::batch_all(2) + info.weight * 2),
|
||||
pays_fee: Pays::Yes
|
||||
},
|
||||
error: pallet_balances::Error::<Test, _>::InsufficientBalance.into()
|
||||
}
|
||||
);
|
||||
assert_eq!(Balances::free_balance(1), 10);
|
||||
assert_eq!(Balances::free_balance(2), 10);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn batch_all_handles_weight_refund() {
|
||||
new_test_ext().execute_with(|| {
|
||||
let start_weight = 100;
|
||||
let end_weight = 75;
|
||||
let diff = start_weight - end_weight;
|
||||
let batch_len: Weight = 4;
|
||||
|
||||
// Full weight when ok
|
||||
let inner_call = Call::Example(ExampleCall::foobar(false, start_weight, None));
|
||||
let batch_calls = vec![inner_call; batch_len as usize];
|
||||
let call = Call::Utility(UtilityCall::batch_all(batch_calls));
|
||||
let info = call.get_dispatch_info();
|
||||
let result = call.dispatch(Origin::signed(1));
|
||||
assert_ok!(result);
|
||||
assert_eq!(extract_actual_weight(&result, &info), info.weight);
|
||||
|
||||
// Refund weight when ok
|
||||
let inner_call = Call::Example(ExampleCall::foobar(false, start_weight, Some(end_weight)));
|
||||
let batch_calls = vec![inner_call; batch_len as usize];
|
||||
let call = Call::Utility(UtilityCall::batch_all(batch_calls));
|
||||
let info = call.get_dispatch_info();
|
||||
let result = call.dispatch(Origin::signed(1));
|
||||
assert_ok!(result);
|
||||
// Diff is refunded
|
||||
assert_eq!(extract_actual_weight(&result, &info), info.weight - diff * batch_len);
|
||||
|
||||
// Full weight when err
|
||||
let good_call = Call::Example(ExampleCall::foobar(false, start_weight, None));
|
||||
let bad_call = Call::Example(ExampleCall::foobar(true, start_weight, None));
|
||||
let batch_calls = vec![good_call, bad_call];
|
||||
let call = Call::Utility(UtilityCall::batch_all(batch_calls));
|
||||
let info = call.get_dispatch_info();
|
||||
let result = call.dispatch(Origin::signed(1));
|
||||
assert_err_ignore_postinfo!(result, "The cake is a lie.");
|
||||
// No weight is refunded
|
||||
assert_eq!(extract_actual_weight(&result, &info), info.weight);
|
||||
|
||||
// Refund weight when err
|
||||
let good_call = Call::Example(ExampleCall::foobar(false, start_weight, Some(end_weight)));
|
||||
let bad_call = Call::Example(ExampleCall::foobar(true, start_weight, Some(end_weight)));
|
||||
let batch_calls = vec![good_call, bad_call];
|
||||
let batch_len = batch_calls.len() as Weight;
|
||||
let call = Call::Utility(UtilityCall::batch_all(batch_calls));
|
||||
let info = call.get_dispatch_info();
|
||||
let result = call.dispatch(Origin::signed(1));
|
||||
assert_err_ignore_postinfo!(result, "The cake is a lie.");
|
||||
assert_eq!(extract_actual_weight(&result, &info), info.weight - diff * batch_len);
|
||||
|
||||
// Partial batch completion
|
||||
let good_call = Call::Example(ExampleCall::foobar(false, start_weight, Some(end_weight)));
|
||||
let bad_call = Call::Example(ExampleCall::foobar(true, start_weight, Some(end_weight)));
|
||||
let batch_calls = vec![good_call, bad_call.clone(), bad_call];
|
||||
let call = Call::Utility(UtilityCall::batch_all(batch_calls));
|
||||
let info = call.get_dispatch_info();
|
||||
let result = call.dispatch(Origin::signed(1));
|
||||
assert_err_ignore_postinfo!(result, "The cake is a lie.");
|
||||
assert_eq!(
|
||||
extract_actual_weight(&result, &info),
|
||||
// Real weight is 2 calls at end_weight
|
||||
<Test as Trait>::WeightInfo::batch_all(2) + end_weight * 2,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user