The transaction pool regularly calls the validate_unsigned function of the SignedExtension trait to check transaction validity against current state. This trait then gets used to implement custom extensions in frame_system, which get passed into the runtime as a single type, for example in a typical runtime/src/lib.rs file we have:
pub type SignedExtra = ( frame_system::CheckNonZeroSender<Runtime>, frame_system::CheckSpecVersion<Runtime>, frame_system::CheckTxVersion<Runtime>, frame_system::CheckGenesis<Runtime>, frame_system::CheckEra<Runtime>, frame_system::CheckNonce<Runtime>, frame_system::CheckWeight<Runtime>, pallet_transaction_payment::ChargeTransactionPayment<Runtime>, ); So, IIUC this then is used to construct all parts of extra data that a signed call submitted to the runtime expects:
/// Unchecked extrinsic type as expected by this runtime. pub type UncheckedExtrinsic = generic::UncheckedExtrinsic<Address, Call, Signature, SignedExtra>; Then, the TaggedTransactionQueue runtime API provides a way to check that a submitted signed transaction is valid, giving the pool to order transaction by priority etc.
From what I understand, this runtime API verifies all the data in SignedExtra. My question is how?
How does this extra data gets passed into the signed extension and how does the transaction pool know what and where to check in the encoded call?