1

When is it appropriate to use <Runtime as TaggedTransactionQueue<Block>>::validate_transaction() to check if a transaction is valid (and reject a transaction if it is invalid) and when is it appropriate to use frame_system::Config::BaseCallFilter to check if a transaction is valid (and reject a transaction if it is invalid)?

It seems as though they both do almost the same thing? Except that validate_transaction() returns ValidateTransaction (https://paritytech.github.io/substrate/master/sp_runtime/transaction_validity/type.TransactionValidity.html) while <BaseCallFilter as Contains::<RuntimeCall>>::contains() returns a boolean.

Thank you :)

@shawn-tabrizi :)

@bkchr :)

1 Answer 1

1

Both are serving different purposes:

  1. validate_transaction: This is being used by the transaction pool to validate transactions. This means checking that stuff like the nonce is correct and that the user has funds to pay for the fees. This api is also not required to be called by the node, the node could also just skip calling this.

  2. BaseCallFilter: This is being used when applying a transaction. It will filter all calls that are being dispatched and if they are not allowed the dispatch will fail. This is for example useful to prevent executing certain calls in your runtime.

Both are being used in different parts of your chain and also serve quite different purposes. As the node is not required to call validate_transaction, your runtime logic also needs to ensure that there is never a transaction being applied that violates certain assumptions. An assumption could for example be that the chain doesn't allow balance transfers in the early stage of the network. So, validate_transaction being an utility function the node side to get certain information about a transaction, but not being a tool for you to ensure that only valid state transitions are happening.

6
  • Thank you for your answer. You mentioned that the runtime-api validate_transaction() is "not required to be called by the node, the node could also just skip calling this." But I assume that a block-authoring node has to check validate_transaction() before it authors a block, correct?
    – aang114
    Commented Dec 7, 2022 at 10:07
  • Every node that follows the normal protocol will always call validate_transaction before a transaction enters the pool. A block producer will not call this function before pushing transactions to the block. As I already tried to explain above, the runtime needs to ensure that transactions that are being applied are valid. This means it will basically run the same checks as in validate_transaction.
    – bkchr
    Commented Dec 7, 2022 at 12:27
  • Thank you for your answer. When you say "the runtime needs to ensure that transactions that are being applied are valid" - is this supposed to be done in <Runtime as sp_block_builder::BlockBuilder<Block>>::apply_extrinsic()? (because I notice that in the node-template - it calls Executive::apply_extrinsic(extrinsic) (github.com/paritytech/substrate/blob/master/bin/node-template/…) - which calls the SignedExtension::pre_dispatch() function of all the signed extensions (i.e of CheckNonce, CheckWeight etc.)
    – aang114
    Commented Dec 7, 2022 at 14:21
  • Yes exactly. You can use SignedExtensions to extend these checks in your runtime.
    – bkchr
    Commented Dec 7, 2022 at 14:33
  • So then I am confused. What is the difference between <Runtime as sp_block_builder::BlockBuilder<Block>>::apply_extrinsic() and <BaseCallFilter as Contains::<RuntimeCall>>::contains()? Don't they both "ensure that transactions that are being applied are valid"? Thanks
    – aang114
    Commented Dec 7, 2022 at 14:34

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.