3139ffa25e
- snowbridge-pezpallet-* → pezsnowbridge-pezpallet-* (201 refs) - pallet/ directories → pezpallet/ (4 locations) - Fixed pezpallet.rs self-include recursion bug - Fixed sc-chain-spec hardcoded crate name in derive macro - Reverted .pezpallet_by_name() to .pallet_by_name() (subxt API) - Added BizinikiwiConfig type alias for zombienet tests - Deleted obsolete session state files Verified: pezsnowbridge-pezpallet-*, pezpallet-staking, pezpallet-staking-async, pezframe-benchmarking-cli all pass cargo check
254 lines
8.7 KiB
Rust
254 lines
8.7 KiB
Rust
// This file is part of Bizinikiwi.
|
|
|
|
// Copyright (C) Parity Technologies (UK) Ltd.
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
use crate::{mock::*, *};
|
|
use pezframe_support::{assert_noop, assert_ok};
|
|
use pezsp_runtime::{testing::UintAuthorityId, transaction_validity::InvalidTransaction};
|
|
|
|
/// Test that a non-restricted origin (`NON_RESTRICTED_ORIGIN`) is never tracked, i.e., no usage.
|
|
#[test]
|
|
fn non_restricted_origin_is_not_charged() {
|
|
new_test_ext().execute_with(|| {
|
|
advance_by(1);
|
|
|
|
assert_ok!(exec_signed_tx(NON_RESTRICTED_ORIGIN, MockPalletCall::do_something {}));
|
|
|
|
assert!(
|
|
Usages::<Test>::iter().next().is_none(),
|
|
"Non-restricted origin should have no tracked usage."
|
|
);
|
|
});
|
|
}
|
|
|
|
/// Test that restricted origins (`RESTRICTED_ORIGIN_1`, `RESTRICTED_ORIGIN_2`) have their usage
|
|
/// tracked, refunded on Pays::No, and also can exceed the limit one time if the call is
|
|
/// whitelisted.
|
|
#[test]
|
|
fn restricted_origin_works() {
|
|
new_test_ext().execute_with(|| {
|
|
// length of the extrinsic.
|
|
let len = {
|
|
let tx_ext = (RestrictOrigin::<Test>::new(true),);
|
|
let tx = UncheckedExtrinsic::new_signed(
|
|
MockPalletCall::do_something {}.into(),
|
|
RESTRICTED_ORIGIN_1,
|
|
UintAuthorityId(RESTRICTED_ORIGIN_1),
|
|
tx_ext,
|
|
);
|
|
tx.encoded_size() as u64
|
|
};
|
|
|
|
let mut previous_used = 0;
|
|
|
|
assert_eq!(ALLOWANCE_RECOVERY_PER_BLOCK, 5);
|
|
assert_eq!(CALL_WEIGHT, 15);
|
|
assert_eq!(MAX_ALLOWANCE, 100);
|
|
|
|
// Move beyond block 0 for events
|
|
advance_by(1);
|
|
|
|
// Normal call => usage increases
|
|
assert_ok!(exec_signed_tx(RESTRICTED_ORIGIN_1, MockPalletCall::do_something {}));
|
|
let usage = Usages::<Test>::get(RuntimeRestrictedEntity::A).unwrap();
|
|
assert_eq!(usage.used, previous_used + CALL_WEIGHT + len);
|
|
assert_eq!(usage.at_block, 1);
|
|
|
|
// A call with `Pays::No` => usage is refunded
|
|
previous_used = Usages::<Test>::get(RuntimeRestrictedEntity::A).unwrap().used;
|
|
assert_ok!(exec_signed_tx(RESTRICTED_ORIGIN_1, MockPalletCall::do_something_refunded {}));
|
|
let usage_after = Usages::<Test>::get(RuntimeRestrictedEntity::A).unwrap();
|
|
assert_eq!(usage_after.used, previous_used);
|
|
|
|
// Again a normal call => usage increases
|
|
assert_ok!(exec_signed_tx(RESTRICTED_ORIGIN_1, MockPalletCall::do_something {}));
|
|
let usage = Usages::<Test>::get(RuntimeRestrictedEntity::A).unwrap();
|
|
assert_eq!(usage.used, previous_used + CALL_WEIGHT + len);
|
|
|
|
// Now we have reached the limit
|
|
// Normal calls that push usage above the max should fail.
|
|
assert_noop!(
|
|
exec_signed_tx(RESTRICTED_ORIGIN_1, MockPalletCall::do_something {}),
|
|
InvalidTransaction::Payment
|
|
);
|
|
|
|
// Advance a few blocks to partially recover usage
|
|
advance_by(1);
|
|
// Still not enough to do another normal call if we haven't recovered enough.
|
|
assert_noop!(
|
|
exec_signed_tx(RESTRICTED_ORIGIN_1, MockPalletCall::do_something {}),
|
|
InvalidTransaction::Payment
|
|
);
|
|
|
|
// Advance one more block => total 5 blocks.
|
|
advance_by(1);
|
|
previous_used = Usages::<Test>::get(RuntimeRestrictedEntity::A).unwrap().used;
|
|
assert_ok!(exec_signed_tx(RESTRICTED_ORIGIN_1, MockPalletCall::do_something {}));
|
|
let current_usage = Usages::<Test>::get(RuntimeRestrictedEntity::A).unwrap();
|
|
let recovered_amount = 2 * ALLOWANCE_RECOVERY_PER_BLOCK;
|
|
|
|
// Usage = (previous_used - recovered_amount) + (CALL_WEIGHT + len).
|
|
assert_eq!(current_usage.used, previous_used + CALL_WEIGHT + len - recovered_amount);
|
|
assert_eq!(current_usage.at_block, 3);
|
|
});
|
|
}
|
|
|
|
#[test]
|
|
fn one_time_excess_works_and_works_only_one_time() {
|
|
new_test_ext().execute_with(|| {
|
|
advance_by(1);
|
|
|
|
// Given usage is 0, RESTRICTED_ORIGIN_1 can exceed once.
|
|
assert_ok!(exec_signed_tx(
|
|
RESTRICTED_ORIGIN_1,
|
|
MockPalletCall::do_something_allowed_excess {}
|
|
));
|
|
let current_usage = Usages::<Test>::get(RuntimeRestrictedEntity::A).unwrap();
|
|
assert!(current_usage.used > MAX_ALLOWANCE);
|
|
|
|
// Now that usage has exceeded the max, even the "allowed excess" call should fail.
|
|
assert_noop!(
|
|
exec_signed_tx(RESTRICTED_ORIGIN_1, MockPalletCall::do_something_allowed_excess {}),
|
|
InvalidTransaction::Payment
|
|
);
|
|
});
|
|
}
|
|
|
|
#[test]
|
|
fn one_time_excess_is_origin_specific() {
|
|
new_test_ext().execute_with(|| {
|
|
advance_by(1);
|
|
|
|
// try the "allowed_excess" call from RESTRICTED_ORIGIN_2:
|
|
// It should *fail*, because RESTRICTED_ORIGIN_2 is not in `OperationAllowedOneTimeExcess`.
|
|
assert_noop!(
|
|
exec_signed_tx(RESTRICTED_ORIGIN_2, MockPalletCall::do_something_allowed_excess {}),
|
|
InvalidTransaction::Payment
|
|
);
|
|
|
|
// Demonstrate that RESTRICTED_ORIGIN_1 *can* exceed (for completeness).
|
|
assert_ok!(exec_signed_tx(
|
|
RESTRICTED_ORIGIN_1,
|
|
MockPalletCall::do_something_allowed_excess {}
|
|
));
|
|
let current_usage = Usages::<Test>::get(RuntimeRestrictedEntity::A).unwrap();
|
|
assert!(current_usage.used > MAX_ALLOWANCE);
|
|
});
|
|
}
|
|
|
|
#[test]
|
|
fn one_time_excess_requires_usage_zero() {
|
|
new_test_ext().execute_with(|| {
|
|
advance_by(1);
|
|
|
|
// We use a bit of the allowance.
|
|
assert_ok!(exec_signed_tx(RESTRICTED_ORIGIN_1, MockPalletCall::do_something {}));
|
|
let usage = Usages::<Test>::get(RuntimeRestrictedEntity::A).unwrap();
|
|
assert!(usage.used < MAX_ALLOWANCE);
|
|
assert!(usage.used > 0);
|
|
|
|
// Now that usage is non-zero, we can call exceeding operations.
|
|
assert_noop!(
|
|
exec_signed_tx(RESTRICTED_ORIGIN_1, MockPalletCall::do_something_allowed_excess {}),
|
|
InvalidTransaction::Payment
|
|
);
|
|
});
|
|
}
|
|
|
|
#[test]
|
|
fn clean_usage_works() {
|
|
new_test_ext().execute_with(|| {
|
|
// Move beyond block 0 for clarity in block numbering.
|
|
advance_by(1);
|
|
|
|
// 1) Attempt to clean usage with no recorded usage => should fail with NoUsage.
|
|
assert_noop!(
|
|
OriginsRestriction::clean_usage(
|
|
pezframe_system::RawOrigin::Root.into(),
|
|
RuntimeRestrictedEntity::A
|
|
),
|
|
Error::<Test>::NoUsage
|
|
);
|
|
|
|
// Create some usage for RESTRICTED_ORIGIN_1 (which maps to RuntimeRestrictedEntity::A).
|
|
assert_ok!(exec_signed_tx(RESTRICTED_ORIGIN_1, MockPalletCall::do_something {}));
|
|
let usage = Usages::<Test>::get(RuntimeRestrictedEntity::A).expect("Usage must be present");
|
|
assert!(usage.used > 0, "Usage should have increased after the call");
|
|
|
|
// 2) Try cleaning while usage is non-zero => should fail with NotZero.
|
|
assert_noop!(
|
|
OriginsRestriction::clean_usage(
|
|
pezframe_system::RawOrigin::Root.into(),
|
|
RuntimeRestrictedEntity::A
|
|
),
|
|
Error::<Test>::NotZero
|
|
);
|
|
|
|
// Figure out how many blocks to advance so that usage recovers fully back to zero.
|
|
// The usage recovers ALLOWANCE_RECOVERY_PER_BLOCK every block, so compute the needed
|
|
// blocks.
|
|
let used_amount = usage.used;
|
|
let blocks_needed = used_amount.div_ceil(ALLOWANCE_RECOVERY_PER_BLOCK); // Ceiling division
|
|
|
|
advance_by(blocks_needed);
|
|
|
|
// 3) Now that enough blocks have passed, usage should be zero => clean_usage should
|
|
// succeed.
|
|
assert_ok!(OriginsRestriction::clean_usage(
|
|
pezframe_system::RawOrigin::Root.into(),
|
|
RuntimeRestrictedEntity::A
|
|
));
|
|
|
|
// We expect the storage to be removed and the UsageCleaned event to be emitted.
|
|
assert!(Usages::<Test>::get(RuntimeRestrictedEntity::A).is_none());
|
|
System::assert_last_event(RuntimeEvent::OriginsRestriction(Event::UsageCleaned {
|
|
entity: RuntimeRestrictedEntity::A,
|
|
}));
|
|
|
|
// 4) Calling again when there is no usage => fail with NoUsage.
|
|
assert_noop!(
|
|
OriginsRestriction::clean_usage(
|
|
pezframe_system::RawOrigin::Root.into(),
|
|
RuntimeRestrictedEntity::A
|
|
),
|
|
Error::<Test>::NoUsage
|
|
);
|
|
});
|
|
}
|
|
|
|
#[test]
|
|
fn restrict_origin_extension_disabled_behavior() {
|
|
new_test_ext().execute_with(|| {
|
|
// Move to a non-zero block number for clarity.
|
|
advance_by(1);
|
|
|
|
// 1) Attempt from restricted origin => Expect InvalidTransaction::Call
|
|
// because the pezpallet explicitly forbids restricted origins if the extension is off.
|
|
assert_noop!(
|
|
exec_signed_tx_disabled(RESTRICTED_ORIGIN_1, MockPalletCall::do_something {}),
|
|
pezsp_runtime::transaction_validity::InvalidTransaction::Call
|
|
);
|
|
|
|
// 2) Attempt from non-restricted origin => Should succeed and also
|
|
// should not track any usage since usage is only tracked for restricted origins.
|
|
assert_ok!(exec_signed_tx_disabled(NON_RESTRICTED_ORIGIN, MockPalletCall::do_something {}));
|
|
assert!(
|
|
Usages::<Test>::iter().next().is_none(),
|
|
"Extension is disabled, so no usage should be tracked for non-restricted origins."
|
|
);
|
|
});
|
|
}
|