Skip to content

Conversation

@manuelmauro
Copy link
Contributor

@manuelmauro manuelmauro commented Nov 27, 2025

Description

Runtimes that distribute transaction fees to block authors (like Moonbeam) fail on the charge_transaction_payment benchmark because no author is set when the benchmark runs. The fee distribution logic panics when trying to credit a non-existent author.

This PR introduces a benchmarking::Config trait with a setup_benchmark_environment() hook that runtimes can implement to set up required state before the benchmark executes.

Additionally, amount_to_endow is now calculated using compute_fee() to determine the actual fee (with a 10x buffer), ensuring it is at least the existential deposit.

Integration

Runtimes that need to set up state before running the charge_transaction_payment benchmark should implement pallet_transaction_payment::benchmarking::Config:

+ impl pallet_transaction_payment::benchmarking::Config for Runtime {
+     fn setup_benchmark_environment() {
+         // Set up any required state, e.g., block author for fee distribution
+         let author: AccountId = frame_benchmarking::whitelisted_caller();
+         pallet_author_inherent::Author::<Runtime>::put(author);
+     }
+ }

And update the benchmark list to use the wrapper type:

- [pallet_transaction_payment, TransactionPayment]
+ [pallet_transaction_payment, TransactionPaymentBenchmark::<Runtime>]

Runtimes that don't need custom setup can use the default implementation (no-op).

Review Notes

  • A new benchmarking::Config trait extends crate::Config with a setup_benchmark_environment() method (default no-op)
  • A wrapper Pallet<T> struct is introduced in the benchmarking module to use this extended trait
  • The benchmark calls T::setup_benchmark_environment() at the start
  • amount_to_endow is calculated as compute_fee(...).saturating_mul(10).max(existential_deposit) to ensure the account can exist and has sufficient funds

Checklist

  • My PR includes a detailed description as outlined in the "Description" and its two subsections above.
  • My PR follows the labeling requirements of this project (at minimum one label for T required)
    • External contributors: Use /cmd label <label-name> to add labels
    • Maintainers can also add labels manually
  • I have made corresponding changes to the documentation (if applicable)
  • I have added tests that prove my fix is effective or that my feature works (if applicable)

@manuelmauro manuelmauro requested a review from a team as a code owner November 27, 2025 11:16
@manuelmauro
Copy link
Contributor Author

/cmd label T1-FRAME

@manuelmauro
Copy link
Contributor Author

/cmd prdoc

@github-actions
Copy link
Contributor

Command "label T1-FRAME" has failed ❌! See logs here

@github-actions
Copy link
Contributor

Command "prdoc" has failed ❌! See logs here

manuelmauro added a commit to moonbeam-foundation/polkadot-sdk that referenced this pull request Nov 27, 2025
@manuelmauro manuelmauro force-pushed the fix-charge-transaction-payment-benchmark branch from 3be0033 to f825eab Compare November 27, 2025 11:24
@bkchr bkchr added the T2-pallets This PR/Issue is related to a particular pallet. label Nov 27, 2025
Comment on lines +28 to +29
/// Re-export the pallet for benchmarking with custom Config trait.
pub struct Pallet<T: Config>(crate::Pallet<T>);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need this?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can see and example use case here: moonbeam-foundation/moonbeam#3558

/// required state before running benchmarks. For example, runtimes that
/// distribute fees to block authors may need to set the author before
/// the benchmark runs.
pub trait Config: crate::Config {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we not just name this BenchmarkConfig? and then also put this below into the where_bound?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah could you try to just implement the benchmarking trait for the "normal Pallet"? Maybe it doesn't work, but right now I don't see the need for declaring this extra pallet struct.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The downside of this approach is that everyone would need to implement the new Config trait ext even if they do not need any special setup.

For instance we'd need to add the following to /substrate/frame/transaction-payment/src/mock.rs

#[cfg(feature = "runtime-benchmarks")]
impl crate::benchmarking::Config for Runtime {}


#[cfg(feature = "runtime-benchmarks")]
mod benchmarking;
pub mod benchmarking;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably enough to just export the config, instead of making the entire module public

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment on lines -51 to -53
let (amount_to_endow, tip) = if existential_deposit.is_zero() {
let min_tip: <<T as pallet::Config>::OnChargeTransaction as payment::OnChargeTransaction<T>>::Balance = 1_000_000_000u32.into();
(min_tip * 1000u32.into(), min_tip)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why did you change this?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The amount to endow is computed dynamically later on, while the tip logic stays the same.

let len: u32 = 10;
let expected_fee = crate::Pallet::<T>::compute_fee(len, &info, tip);
let amount_to_endow = expected_fee
.saturating_mul(10u32.into())
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should multiply by ED if not zero.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am sorry, I am not sure I understand why we need to do this. Could you please expand on the rationale? Happy to make this change.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed a problem with the computation here: 204b3e0

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

T2-pallets This PR/Issue is related to a particular pallet.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants