At the lowest level of the protocol transactions are always processed, meaning they collect the fee and update the sequence number.
The layer above that, still done by validators, does the same checks done during consensus but actually removes the bad transactions that it will send out to consensus to reduce the chance of collecting fees for failed transactions.
The checks done by a validator are sanity checks and very simple:
- it checks if the parameters provided in the operations are valid (like no negative amounts, assets referenced are not garbage). Those checks are independent of the ledger state (so it doesn't need to walk the order book, etc).
- it also checks signatures and that the transaction source account can process the fee and has the proper sequence number.
The next layer above that (Horizon, Wallets, etc) can perform additional checks before signing transactions, for example, enforce some limits on payments (amount, rate per day, etc).
Reason for only doing those simple checks is two fold:
- it's very cheap (so very fast)
- it keeps the concern of tracking dependencies between transactions outside of the protocol layer (other than sequence number tracking). If more validations were done at that layer, it would require complex reordering of transactions when merging transaction sets, and that complexity increases the attack surface of the protocol.
With this explained - a payment can fail with an "underfunded" error because the payment amount is not part of sanity checks that validators perform. This responsibility falls onto the upper layers of the stack.