Skip to content
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

Adds syntax for marking calls feeless #1926

Merged
merged 49 commits into from
Nov 13, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
61695cf
Initial setup
gupnik Sep 27, 2023
200abe3
Merge branch 'master' of github.com:paritytech/polkadot-sdk into gupn…
Oct 11, 2023
d199795
Fixes end-to-end
gupnik Oct 18, 2023
b0390bf
Adds pallet-skip-feeless-payment
gupnik Oct 20, 2023
aac1933
Merge branch 'master' into gupnik/feeless
gupnik Oct 20, 2023
7e0450b
Adds AccountIdFor in test
gupnik Oct 20, 2023
6802f4d
Fixes node build
gupnik Oct 20, 2023
093fa63
Fixes prelude in tests
gupnik Oct 20, 2023
9cf7401
Minor fixes
gupnik Oct 20, 2023
25f57f5
Fixes feature propagation
gupnik Oct 20, 2023
de68ccc
Fixes feature propagation
gupnik Oct 20, 2023
9907ca5
Merge branch 'master' into gupnik/feeless
gupnik Oct 20, 2023
fac68c1
Formats features
gupnik Oct 21, 2023
b0fdbc1
Updates UI test
gupnik Oct 21, 2023
32e9bd1
Merge branch 'master' into gupnik/feeless
gupnik Oct 21, 2023
73c7bca
Adds tests for skip fee payment pallet
gupnik Oct 23, 2023
84bb8fa
Adds UI Tests for feeless args
gupnik Oct 24, 2023
948207a
Adds UI Tests for feeless input type
gupnik Oct 24, 2023
be7a0fc
Adds UI Tests for feeless return type
gupnik Oct 24, 2023
23cfd91
Adds UI Test for passing case
gupnik Oct 24, 2023
e87a139
Moves feeless extension to kitchensink
gupnik Oct 25, 2023
1c95fdf
Fixes node-testing build
gupnik Oct 25, 2023
049ed24
Merge branch 'master' into gupnik/feeless
gupnik Oct 25, 2023
ff860e6
Fixes node-cli build
gupnik Oct 25, 2023
a9074a5
Adds feeless macro in kitchensink example
gupnik Oct 25, 2023
6706bc2
Fmt
gupnik Oct 25, 2023
edaf143
fixes build
gupnik Oct 25, 2023
9987b88
Adds docs
gupnik Oct 25, 2023
864b50c
Adds prdoc
gupnik Oct 25, 2023
646fe0f
Minor fix
gupnik Oct 25, 2023
5049e94
Update substrate/frame/transaction-payment/skip-feeless-payment/Cargo…
gupnik Nov 3, 2023
649708a
Update substrate/frame/transaction-payment/skip-feeless-payment/Cargo…
gupnik Nov 3, 2023
b12fa28
Update substrate/frame/transaction-payment/skip-feeless-payment/Cargo…
gupnik Nov 3, 2023
c8f99a6
Addresses review comments
gupnik Nov 7, 2023
615558e
Merge branch 'master' of github.com:paritytech/polkadot-sdk into gupn…
gupnik Nov 7, 2023
c11a5e8
Uses Origin as the first arg
gupnik Nov 7, 2023
ff51a94
Update substrate/frame/support/src/dispatch.rs
gupnik Nov 11, 2023
a1e2ea9
Addresses review comments
gupnik Nov 11, 2023
c6f1091
Update substrate/frame/support/procedural/src/lib.rs
gupnik Nov 11, 2023
69193c6
Update substrate/frame/support/procedural/src/pallet/parse/call.rs
gupnik Nov 11, 2023
5a44ab4
Update prdoc/pr_1926.prdoc
gupnik Nov 11, 2023
90f2cf8
Update substrate/frame/support/procedural/src/lib.rs
gupnik Nov 11, 2023
f031785
Update substrate/frame/support/procedural/src/lib.rs
gupnik Nov 11, 2023
e210941
Addresses review comments
gupnik Nov 11, 2023
d79cc22
Fixes error description for invalid attr type
gupnik Nov 11, 2023
0ab5492
Fixes error description and span for closure args
gupnik Nov 11, 2023
e8e2f70
Merge branch 'master' into gupnik/feeless
gupnik Nov 11, 2023
3f9faad
Updates error output in UI test
gupnik Nov 11, 2023
9351d4b
Merge branch 'master' into gupnik/feeless
gupnik Nov 13, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions Cargo.lock

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

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -382,6 +382,7 @@ members = [
"substrate/frame/transaction-payment/asset-tx-payment",
"substrate/frame/transaction-payment/rpc",
"substrate/frame/transaction-payment/rpc/runtime-api",
"substrate/frame/transaction-payment/skip-feeless-payment",
"substrate/frame/transaction-storage",
"substrate/frame/treasury",
"substrate/frame/try-runtime",
Expand Down
30 changes: 30 additions & 0 deletions prdoc/pr_1926.prdoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
title: Adds syntax for marking calls feeless

doc:
- audience: Core Dev
description: |
1. Adds an attribute `#[pallet::feeless_if]` that can be optionally attached to a `pallet::call`.
2. Adds a signed extension SkipCheckIfFeeless<T: SignedExtension> that wraps a transaction
payment processor to potentially skip payment fees for such calls.
Note that both the attribute and the signed extension are needed to make the call feeless.

migrations:
db: []

runtime: []

crates:
- name: "frame-support-procedural"
semver: minor
- name: "pallet-skip-feeless-payment"
semver: major
- pallet-example-kitchensink
semver: patch
- kitchensink-runtime
semver: major
- node-testing
semver: patch
- node-cli
semver: patch

host_functions: []
3 changes: 3 additions & 0 deletions substrate/bin/node/cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ pallet-assets = { path = "../../../frame/assets" }
pallet-asset-conversion-tx-payment = { path = "../../../frame/transaction-payment/asset-conversion-tx-payment" }
pallet-asset-tx-payment = { path = "../../../frame/transaction-payment/asset-tx-payment" }
pallet-im-online = { path = "../../../frame/im-online", default-features = false}
pallet-skip-feeless-payment = { path = "../../../frame/transaction-payment/skip-feeless-payment", default-features = false}

# node-specific dependencies
kitchensink-runtime = { path = "../runtime" }
Expand Down Expand Up @@ -168,6 +169,7 @@ runtime-benchmarks = [
"pallet-assets/runtime-benchmarks",
"pallet-balances/runtime-benchmarks",
"pallet-im-online/runtime-benchmarks",
"pallet-skip-feeless-payment/runtime-benchmarks",
"pallet-timestamp/runtime-benchmarks",
"sc-client-db/runtime-benchmarks",
"sc-service/runtime-benchmarks",
Expand All @@ -183,6 +185,7 @@ try-runtime = [
"pallet-assets/try-runtime",
"pallet-balances/try-runtime",
"pallet-im-online/try-runtime",
"pallet-skip-feeless-payment/try-runtime",
"pallet-timestamp/try-runtime",
"sp-runtime/try-runtime",
"substrate-cli-test-utils/try-runtime",
Expand Down
38 changes: 21 additions & 17 deletions substrate/bin/node/cli/src/service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,21 +91,24 @@ pub fn create_extrinsic(
.map(|c| c / 2)
.unwrap_or(2) as u64;
let tip = 0;
let extra: kitchensink_runtime::SignedExtra = (
frame_system::CheckNonZeroSender::<kitchensink_runtime::Runtime>::new(),
frame_system::CheckSpecVersion::<kitchensink_runtime::Runtime>::new(),
frame_system::CheckTxVersion::<kitchensink_runtime::Runtime>::new(),
frame_system::CheckGenesis::<kitchensink_runtime::Runtime>::new(),
frame_system::CheckEra::<kitchensink_runtime::Runtime>::from(generic::Era::mortal(
period,
best_block.saturated_into(),
)),
frame_system::CheckNonce::<kitchensink_runtime::Runtime>::from(nonce),
frame_system::CheckWeight::<kitchensink_runtime::Runtime>::new(),
pallet_asset_conversion_tx_payment::ChargeAssetTxPayment::<kitchensink_runtime::Runtime>::from(
tip, None,
),
);
let extra: kitchensink_runtime::SignedExtra =
(
frame_system::CheckNonZeroSender::<kitchensink_runtime::Runtime>::new(),
frame_system::CheckSpecVersion::<kitchensink_runtime::Runtime>::new(),
frame_system::CheckTxVersion::<kitchensink_runtime::Runtime>::new(),
frame_system::CheckGenesis::<kitchensink_runtime::Runtime>::new(),
frame_system::CheckEra::<kitchensink_runtime::Runtime>::from(generic::Era::mortal(
period,
best_block.saturated_into(),
)),
frame_system::CheckNonce::<kitchensink_runtime::Runtime>::from(nonce),
frame_system::CheckWeight::<kitchensink_runtime::Runtime>::new(),
pallet_skip_feeless_payment::SkipCheckIfFeeless::from(
pallet_asset_conversion_tx_payment::ChargeAssetTxPayment::<
kitchensink_runtime::Runtime,
>::from(tip, None),
),
);

let raw_payload = kitchensink_runtime::SignedPayload::from_raw(
function.clone(),
Expand Down Expand Up @@ -879,8 +882,9 @@ mod tests {
let check_era = frame_system::CheckEra::from(Era::Immortal);
let check_nonce = frame_system::CheckNonce::from(index);
let check_weight = frame_system::CheckWeight::new();
let tx_payment =
pallet_asset_conversion_tx_payment::ChargeAssetTxPayment::from(0, None);
let tx_payment = pallet_skip_feeless_payment::SkipCheckIfFeeless::from(
pallet_asset_conversion_tx_payment::ChargeAssetTxPayment::from(0, None),
);
let extra = (
check_non_zero_sender,
check_spec_version,
Expand Down
4 changes: 4 additions & 0 deletions substrate/bin/node/runtime/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@ pallet-transaction-payment = { path = "../../../frame/transaction-payment", defa
pallet-transaction-payment-rpc-runtime-api = { path = "../../../frame/transaction-payment/rpc/runtime-api", default-features = false}
pallet-asset-conversion-tx-payment = { path = "../../../frame/transaction-payment/asset-conversion-tx-payment", default-features = false}
pallet-asset-tx-payment = { path = "../../../frame/transaction-payment/asset-tx-payment", default-features = false}
pallet-skip-feeless-payment = { path = "../../../frame/transaction-payment/skip-feeless-payment", default-features = false}
pallet-transaction-storage = { path = "../../../frame/transaction-storage", default-features = false}
pallet-uniques = { path = "../../../frame/uniques", default-features = false}
pallet-vesting = { path = "../../../frame/vesting", default-features = false}
Expand Down Expand Up @@ -212,6 +213,7 @@ std = [
"pallet-scheduler/std",
"pallet-session-benchmarking?/std",
"pallet-session/std",
"pallet-skip-feeless-payment/std",
"pallet-society/std",
"pallet-staking-runtime-api/std",
"pallet-staking/std",
Expand Down Expand Up @@ -308,6 +310,7 @@ runtime-benchmarks = [
"pallet-salary/runtime-benchmarks",
"pallet-scheduler/runtime-benchmarks",
"pallet-session-benchmarking/runtime-benchmarks",
"pallet-skip-feeless-payment/runtime-benchmarks",
"pallet-society/runtime-benchmarks",
"pallet-staking/runtime-benchmarks",
"pallet-state-trie-migration/runtime-benchmarks",
Expand Down Expand Up @@ -381,6 +384,7 @@ try-runtime = [
"pallet-salary/try-runtime",
"pallet-scheduler/try-runtime",
"pallet-session/try-runtime",
"pallet-skip-feeless-payment/try-runtime",
"pallet-society/try-runtime",
"pallet-staking/try-runtime",
"pallet-state-trie-migration/try-runtime",
Expand Down
16 changes: 14 additions & 2 deletions substrate/bin/node/runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -569,6 +569,10 @@ impl pallet_asset_conversion_tx_payment::Config for Runtime {
pallet_asset_conversion_tx_payment::AssetConversionAdapter<Balances, AssetConversion>;
}

impl pallet_skip_feeless_payment::Config for Runtime {
type RuntimeEvent = RuntimeEvent;
}

parameter_types! {
pub const MinimumPeriod: Moment = SLOT_DURATION / 2;
}
Expand Down Expand Up @@ -1394,7 +1398,11 @@ where
frame_system::CheckEra::<Runtime>::from(era),
frame_system::CheckNonce::<Runtime>::from(nonce),
frame_system::CheckWeight::<Runtime>::new(),
pallet_asset_conversion_tx_payment::ChargeAssetTxPayment::<Runtime>::from(tip, None),
pallet_skip_feeless_payment::SkipCheckIfFeeless::from(
pallet_asset_conversion_tx_payment::ChargeAssetTxPayment::<Runtime>::from(
tip, None,
),
),
);
let raw_payload = SignedPayload::new(call, extra)
.map_err(|e| {
Expand Down Expand Up @@ -2134,6 +2142,7 @@ construct_runtime!(
Statement: pallet_statement,
Broker: pallet_broker,
Mixnet: pallet_mixnet,
SkipFeelessPayment: pallet_skip_feeless_payment,
}
);

Expand All @@ -2160,7 +2169,10 @@ pub type SignedExtra = (
frame_system::CheckEra<Runtime>,
frame_system::CheckNonce<Runtime>,
frame_system::CheckWeight<Runtime>,
pallet_asset_conversion_tx_payment::ChargeAssetTxPayment<Runtime>,
pallet_skip_feeless_payment::SkipCheckIfFeeless<
Runtime,
pallet_asset_conversion_tx_payment::ChargeAssetTxPayment<Runtime>,
gupnik marked this conversation as resolved.
Show resolved Hide resolved
>,
);

/// Unchecked extrinsic type as expected by this runtime.
Expand Down
1 change: 1 addition & 0 deletions substrate/bin/node/testing/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ pallet-asset-conversion = { path = "../../../frame/asset-conversion" }
pallet-assets = { path = "../../../frame/assets" }
pallet-asset-conversion-tx-payment = { path = "../../../frame/transaction-payment/asset-conversion-tx-payment" }
pallet-asset-tx-payment = { path = "../../../frame/transaction-payment/asset-tx-payment" }
pallet-skip-feeless-payment = { path = "../../../frame/transaction-payment/skip-feeless-payment" }
sc-block-builder = { path = "../../../client/block-builder" }
sc-client-api = { path = "../../../client/api" }
sc-client-db = { path = "../../../client/db", features = ["rocksdb"]}
Expand Down
4 changes: 3 additions & 1 deletion substrate/bin/node/testing/src/keyring.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,9 @@ pub fn signed_extra(nonce: Nonce, extra_fee: Balance) -> SignedExtra {
frame_system::CheckEra::from(Era::mortal(256, 0)),
frame_system::CheckNonce::from(nonce),
frame_system::CheckWeight::new(),
pallet_asset_conversion_tx_payment::ChargeAssetTxPayment::from(extra_fee, None),
pallet_skip_feeless_payment::SkipCheckIfFeeless::from(
pallet_asset_conversion_tx_payment::ChargeAssetTxPayment::from(extra_fee, None),
),
)
}

Expand Down
4 changes: 4 additions & 0 deletions substrate/frame/examples/kitchensink/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,10 @@ pub mod pallet {
impl<T: Config> Pallet<T> {
#[pallet::call_index(0)]
#[pallet::weight(T::WeightInfo::set_foo_benchmark())]
/// Marks this call as feeless if `new_foo` is zero.
#[pallet::feeless_if(|_origin: &OriginFor<T>, new_foo: &u32, _other_compact: &u128| -> bool {
*new_foo == 0
})]
pub fn set_foo(
_: OriginFor<T>,
new_foo: u32,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,18 @@ pub fn expand_outer_dispatch(
}
}

impl #scrate::dispatch::CheckIfFeeless for RuntimeCall {
type Origin = #system_path::pallet_prelude::OriginFor<#runtime>;
fn is_feeless(&self, origin: &Self::Origin) -> bool {
match self {
#(
#pallet_attrs
#variant_patterns => call.is_feeless(origin),
)*
}
}
}

impl #scrate::traits::GetCallMetadata for RuntimeCall {
fn get_call_metadata(&self) -> #scrate::traits::CallMetadata {
use #scrate::traits::GetCallName;
Expand Down
30 changes: 30 additions & 0 deletions substrate/frame/support/procedural/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1157,6 +1157,36 @@ pub fn call_index(_: TokenStream, _: TokenStream) -> TokenStream {
pallet_macro_stub()
}

/// Each dispatchable may be annotated with the `#[pallet::feeless_if($closure)]` attribute,
/// which explicitly defines the condition for the dispatchable to be feeless.
///
/// The arguments for the closure must be the referenced arguments of the dispatchable function.
///
/// The closure must return `bool`.
///
/// ### Example
/// ```ignore
/// #[pallet::feeless_if(|_origin: &OriginFor<T>, something: &u32| -> bool {
/// *something == 0
/// })]
/// pub fn do_something(origin: OriginFor<T>, something: u32) -> DispatchResult {
/// ....
/// }
/// ```
///
/// Please note that this only works for signed dispatchables and requires a signed extension
/// such as `SkipCheckIfFeeless` as defined in `pallet-skip-feeless-payment` to wrap the existing
/// payment extension. Else, this is completely ignored and the dispatchable is still charged.
gupnik marked this conversation as resolved.
Show resolved Hide resolved
///
/// ### Macro expansion
///
/// The macro implements the `CheckIfFeeless` trait on the dispatchable and calls the corresponding
/// closure in the implementation.
#[proc_macro_attribute]
pub fn feeless_if(_: TokenStream, _: TokenStream) -> TokenStream {
pallet_macro_stub()
}

/// Allows you to define some extra constants to be added into constant metadata.
///
/// Item must be defined as:
Expand Down
27 changes: 27 additions & 0 deletions substrate/frame/support/procedural/src/pallet/expand/call.rs
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,16 @@ pub fn expand_call(def: &mut Def) -> proc_macro2::TokenStream {
})
.collect::<Vec<_>>();

let feeless_check = methods.iter().map(|method| &method.feeless_check).collect::<Vec<_>>();
let feeless_check_result =
feeless_check.iter().zip(args_name.iter()).map(|(feeless_check, arg_name)| {
if let Some(feeless_check) = feeless_check {
quote::quote!(#feeless_check(origin, #( #arg_name, )*))
} else {
quote::quote!(false)
}
});

quote::quote_spanned!(span =>
mod warnings {
#(
Expand Down Expand Up @@ -347,6 +357,23 @@ pub fn expand_call(def: &mut Def) -> proc_macro2::TokenStream {
}
}

impl<#type_impl_gen> #frame_support::dispatch::CheckIfFeeless for #call_ident<#type_use_gen>
#where_clause
{
type Origin = #frame_system::pallet_prelude::OriginFor<T>;
#[allow(unused_variables)]
fn is_feeless(&self, origin: &Self::Origin) -> bool {
match *self {
#(
Self::#fn_name { #( #args_name_pattern_ref, )* } => {
#feeless_check_result
},
)*
Self::__Ignore(_, _) => unreachable!("__Ignore cannot be used"),
}
}
}

impl<#type_impl_gen> #frame_support::traits::GetCallName for #call_ident<#type_use_gen>
#where_clause
{
Expand Down
Loading
Loading