There’s a key difference between P2PKH and P2SH (and SegWit, Taproot).
P2PKH is still using the “bare” Script. It is user-implemented commit-reveal, not a modification of the Script VM system. P2PKH was there right from the start (first P2PKH UTXO appeared on Jan 16 2009): How did pay-to-pubkey hash come about? What is its history?
Looks like intention was just to reduce the size of addresses, quoting Satoshi:
To make Bitcoin Addresses short, they are a hash of the public key, not the public key itself. An attacker would only have to break the hash function, not ECDSA.
It’s interesting that nobody in that correspondence mentioned post-quantum security, and they seemed to be more concerned about breaking 160-bit hash with classical attacks. Turns out, even 160-bit P2PKH addresses are probably safe at rest: Post-quantum preimage resistance of HASH160 addresses and collision is not really a problem when sole owner of an address would be holding both pubkeys. Collision resistance matters for multi-party P2SH addresses:
If you are agreeing to lock up funds with somebody else, and they control what public key to use, you are susceptible to collision attacks.
P2SH and others are consensus-implemented commit-reveals which modify Script evaluation. P2SH execution has 2 stages: script authentication (1st VM run: it’s just a hashlock in that context) and execution (2nd VM run: consensus “hacks” the VM state to set up the redeem script execution context). More on this here.
Why did things go this way? Reading P2SH proposal (BIP-0016) we can see that main motivation was simplifying UX when funding contracts.
The purpose of pay-to-script-hash is to move the responsibility for supplying the conditions to redeem a transaction from the sender of the funds to the redeemer.
The benefit is allowing a sender to fund any arbitrary transaction, no matter how complicated, using a fixed-length 20-byte hash that is short enough to scan from a QR code or easily copied and pasted.
…
The author believes that this BIP will minimize the changes needed to all of the supporting infrastructure that has already been created to send funds to a base58-encoded-20-byte bitcoin addresses, allowing merchants and exchanges and other software to start supporting multisignature transactions sooner.
Since then, Bitcoin (BTC) locked in the “thou shall not hard fork” upgrade philosophy, which heavily influenced the design of later upgrades – where they all had to be designed as soft forks, leading to VM-ception created by SegWit and Taproot.
It’s interesting to compare Script evolution with its forked sibling (Bitcoin Cash, BCH), which upgrades through hard forks and has since introduced “check signature from stack”, TX introspection opcodes, persistent contract state (through native tokens), VM loops, functions, etc. which enabled public “anyonecanspend” contracts: instead of requiring a signature, the contract requires the spender to construct the TX in a particular way that satisfies the contract requirements.
Turns out, P2SH complicates these, because P2SH contracts are secret until spent, which conflicts with the “public use” nature of contracts like DEX AMM UTXOs etc. because they become harder to discover. They need to either use a constant address, or they need to announce contract parameters in some OP_RETURN, so spenders can reconstruct the redeem script.
Because of this, the May 2026 upgrade will be removing “standardness” relay rules, so such contracts can be made as “bare” pay-to-script instead of P2SH.
P2S can work well today because the UX for DeFi applications is different from the old “send to address”. With dapps you don’t pay to an address. Typically, you access some frontend which finds correct UTXOs and constructs correct outputs for the user, and the user signs just his input through WalletConnect or similar.