Skip to content

Conversation

athei
Copy link
Member

@athei athei commented Sep 18, 2025

Replaces #9590.

The audit of #9590 showed that holding the txfee as held balance and especially playing around with providers causes a lot of troubles.

This PR is a much lighter change. It keeps the original withdraw/deposit pattern. It simply stores the withdrawn Credit and allows other pallets to withdraw from it.

It is also better in terms of performance since all tx signers share a single storage item (instead of a named hold per account).

Copy link
Contributor

@kianenigma kianenigma left a comment

Choose a reason for hiding this comment

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

First glance note: This can render trait EstimateCallFee useless, as we can inspect the new storage item introduced here.

Comment on lines 705 to 706
/// overestimated. Drawing too much balance will cause the signer to underpay for the
/// transaction. Too much means that not enough is left to pay for the fee with regard
Copy link
Contributor

Choose a reason for hiding this comment

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

?

Suggested change
/// overestimated. Drawing too much balance will cause the signer to underpay for the
/// transaction. Too much means that not enough is left to pay for the fee with regard
/// overestimated. Drawing too much balance will cause the signer to overpay for the
/// transaction. Too little means that not enough is left to pay for the fee with regard

Copy link
Contributor

@pgherveou pgherveou Sep 19, 2025

Choose a reason for hiding this comment

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

no I think it's correct, if you draw too much then there is not enough left to pay for what was meant to be used for the tx fee

Copy link
Member Author

Choose a reason for hiding this comment

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

I think it is correct. The signer underpays for the tx because the balance was taken away to pay for something else.

@athei
Copy link
Member Author

athei commented Sep 19, 2025

It had a nasty bug which I fixed the last commt: Putting a Imbalance in storage will call its Drop implementation. Which reduces the issuance. Which we don't want. It is not really dropped logically. It will be restored later. So I worked around it by adding a new NoDrop<T> type that we can use to put T into storage without calling its Drop impl.

Copy link

@TorstenStueber TorstenStueber left a comment

Choose a reason for hiding this comment

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

Maybe not for now: the logic to fail the transaction when too much of the credit has been drawn could be implemented in pallet-transaction-pallet instead of the pallet implementing the transaction logic: it could be a new function in the TransactionExtension trait with a no-op blanket implementation that is called before post_dispatch here and where dispatch_transaction changes res to an Error depending on the return value of that function. The code change would be minimal (apart from requiring new benchmarks).

NextFeeMultiplier::<T>::mutate(|fm| {
*fm = T::FeeMultiplierUpdate::convert(*fm);
});
TxPaymentCredit::<T>::kill();
Copy link

@TorstenStueber TorstenStueber Sep 22, 2025

Choose a reason for hiding this comment

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

Why is this required? At this point the value should always be None, correct? If not, then we would also need to drop the imbalance properly here.

Or is it just not to use any storage in between blocks?

Copy link
Member Author

Choose a reason for hiding this comment

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

Why is this required? At this point the value should always be None, correct?

Yes, the expectation is that it is always None here. We just kill to make sure the value is never carried between blocks.

If not, then we would also need to drop the imbalance properly here.

We cannot know for sure as the behavior is defined by the OnChargeTransaction. So yes, we should make sure to call the drop impl here. Will fix.

///
/// # Warning
///
/// This is only useful if a pallet knows that the pre-dispatch weight was vastly

Choose a reason for hiding this comment

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

I think that the condition here is more subtle: the pallet is allowed to draw too much balance but would need to fail the transaction if that is the case (this more relaxed condition is required for pallet-revive).

It is important that in this case any effects that the balance was drawn for during execution (e.g., storage deposits) are reverted – in order to protect against DoS.

Copy link
Member Author

@athei athei Sep 23, 2025

Choose a reason for hiding this comment

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

You are right. This is what I meant by that but it should be expressed explicitly. Will change the text.

Copy link
Member Author

Choose a reason for hiding this comment

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

Improved the docs.

@athei
Copy link
Member Author

athei commented Sep 23, 2025

Maybe not for now: the logic to fail the transaction when too much of the credit has been drawn could be implemented in pallet-transaction-pallet instead of the pallet implementing the transaction logic: it could be a new function in the TransactionExtension trait with a no-op blanket implementation that is called before post_dispatch here and where dispatch_transaction changes res to an Error depending on the return value of that function. The code change would be minimal (apart from requiring new benchmarks).

Yes will check if possible. The runtime impact will consist of an additional storage transaction in order to be able to roll back the changes done by the tx.

But should be done in a follow up. Let's keep this PR minimal as it is already touching a lot of critical stuff.

@athei
Copy link
Member Author

athei commented Sep 23, 2025

@joepetrowski @TorstenStueber I improved the docs on withdraw_txfee. Please check if it makes sense to you now.

@rachsrl rachsrl moved this from Scheduled to In progress in Security Audit (PRs) - SRLabs Sep 23, 2025
@athei
Copy link
Member Author

athei commented Sep 23, 2025

cc @jakoblell

Applied the variable renaming suggestions you sent me. I applied them as-is with one exception: I renamed Pre::Charge::tip_credit to Pre::Charge::liquidity_info. Reasoning is that this type is opaque and the impl can pass whatever they want there. For example, the CurrencyAdapter does not pass the tip_credit there.

@paritytech-workflow-stopper
Copy link

All GitHub workflows were cancelled due to failure one of the required jobs.
Failed workflow url: https://github.com/paritytech/polkadot-sdk/actions/runs/17952887852
Failed job name: fmt

@athei
Copy link
Member Author

athei commented Sep 23, 2025

/cmd fmt

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
Status: In progress
Development

Successfully merging this pull request may close these issues.

5 participants