CHIP-2022-05 Pay-to-Script-Hash-32 (P2SH32) for Bitcoin Cash

Continuing what @bitjson and @johoe have started (recent P2SH32 discussion and 2020 mention), and knowing that Jason is busy with other projects, I went ahead and created a CHIP:

that aims to solve a particular and well known problem with the current P2SH feature: feasible collision against 160-bit hashes in near future; and that in a mimimum-cost way by simply enabling a longer version of the BIP-0016 template:

Existing, P2SH20:

  • Locking script:
    OP_HASH160 OP_DATA_20 hash160(redeem_script) OP_EQUAL
    (raw: a9 14 1122334455667788990011223344556677889900 87).

Proposed, P2SH32:

  • Locking script:
    OP_HASH256 OP_DATA_32 hash256(redeem_script) OP_EQUAL
    (raw: aa 20 1122334455667788990011223344556677889900112233445566778899001122 87).

Both output types will be unlocked the same way:

  • Unlocking script:
    [push data_N] ... [push data_1] [push data_0] <push redeem_script>.

There are other interesting upgrades related to P2SH and our Script VM that we could consider such as making it independent of the stack item limit, as already laid out by Jason, and those may still be proposed in parallel as separate CHIP(s) building on top of this one which secures the critical building block in all future smart UTXO applications.


Thank you for putting this together!

The P2SH vulnerability is indeed something that needs to be fixed now that smart contracts are becoming more important on Bitcoin Cash! P2SH32 is especially important for sidechain covenants like SHA-gate which are likely to be the first contracts to manage a ton of funds.

I support this effort & hope to see the ideas of fixing boolean malleability and the redeem script size limit as extension CHIPs to this one!


Thanks for putting this together! Just wanted to make a comment in explicit support here. :+1:

I already implemented this in Libauth, and I even included integration tests for it in the CashTokens test vectors. :rocket:

I’ll note that was slightly different than I had in mind when I first posted P2SH32: a long-term solution for 80-bit P2SH collision attacks – I had been thinking the locking bytecode “template” should begin with OP_SHA256 rather than OP_HASH256 because length extension attacks are not relevant to P2SH.

That remains true, but I was convinced (months ago) that it’s still wiser to opt for OP_HASH256 (as this CHIP specifies) because so much of the protocol already utilizes double sha256. Diverging for this one case would have practically no impact on performance, but it might make some implementations slightly more complex.

I also have some fuzzy notions about how OP_HASH256 might make some future cryptography upgrades possible/easier due to that consistency (it would be a bummer if we went with OP_SHA256 and that became the one part of the protocol requiring a different code path or workaround in a future upgrade). In general, OP_SHA256 strikes me as “too clever by half”, while OP_HASH256 is boring and safe.

Nit: it seems like this CHIP could be a lot more concise – maybe consider breaking out the non-critical stuff into appendixes? I did that with CHIP CashTokens here, and I think it made it a bit more approachable. I should review the rest of the CHIP more, but the technical change itself looks great. :100:


Thanks, and nice work on the CT update! I could do some similar restructuring with this CHIP, also I should update it with link to your and bchn’s test vectors, and add acknowledgements section.

1 Like

@Rucknium saw your comment about CashTokens privacy implications, maybe you want to weigh-in here, too:

Let’s start from the beginning: the white paper’s privacy section

As an additional firewall, a new key pair should be used for each transaction to keep them from being linked to a common owner. Some linking is still unavoidable with multi-input transactions, which necessarily reveal that their inputs were owned by the same owner. The risk is that if the owner of a key is revealed, linking could reveal other transactions that belonged to the same owner.

We now know that Satoshi was too optimistic about bitcoin’s privacy protections. A clear flaw in his reasoning was that an observer could not determine which transaction output returned back to the input owner’s wallet in the form of change. The looseness of bitcoin’s protocol in transaction construction allows some fingerprinting of different wallet software. Chain analysis companies can analyze the fingerprints to guess with high probability when coins have changed custody. Satoshi’s “new key pair” suggestion does not protect privacy very well when public keys can be linked to each other using statistical techniques.

Möser and Narayanan (2022) “Resurrecting Address Clustering in Bitcoin” analyzes various fingerprinting techniques. P2SH32 would be a potential privacy risk within their “Address type” category. Nikita Zhavoronkov’s presentation on the privacy risks of BTC Taproot is also worth a look. IMHO, fixing a moderate security risk is probably more important than avoiding a minor privacy risk. Still, we would want to consider how implementation can be done to minimize the privacy risk.

Probably the best way to minimize privacy risk is to limit P2SH32 addresses to the transactions that actually need them for the required scripting. The Privacy Analysis section of the CHIP expects that most users of P2SH32 addresses would be in this category. What we want to prevent is ordinary wallet software making P2SH32 its default change address type.

The risk of any wallet developers implementing P2SH32 as default is low, but nonzero. Wallet developers seem to take the path of least resistance. Even with the fee incentive and political pressure, it took 4 years to bring BTC SegWit adoption to 80 percent. Monero’s August 2022 hard fork was a bloodbath for multi-coin wallets. I counted 8 wallets (WooKey, Exodus, Edge, MyMonero, Atomic, Coinomi, Guarda, Zelcore) that ceased to function for some time after the hard fork because Monero reduces transaction fingerprintability by prohibiting old transaction versions as a consensus rule.

We want clear messaging to wallet developers that P2SH32 is not “SegWit for BCH” despite P2SH32 solving something that SegWit solves (if I am reading the CHIP correctly).

1 Like

Hey thanks for having a look!

For existing wallets, there’s no incentive to upgrade to P2SH32 - they don’t have to do anything about this CHIP, they can safely ignore it. Existing P2SH is used mostly for multisig and it’s established itself pretty well, and for multisig it’s possible to work around the collision problem and continue benefiting from privacy pool and smaller UTXOs.

Yeah, I expect first P2SH32 addresses to be those of public-facing contracts. So you’ll be sending to those by either your wallet enabling sending directly, or there will be a web app that runs a temporary wallet in your browser and you send to its normal P2PKH and the web app forwards it to the P2SH32 contract, similar to how AnyHedge works. In either case, your wallet would be returning the change to normal P2PKH UTXOs (or P2SH in case of multisig wallets) - even when sending to some new app that requires payment to P2SH32 address.

You can’t really prevent it - but it would be a conscious effort to make P2SH32 the change address type, and many wallets are not multisig but pure P2PKH (even though they know how to send to P2SH when shown such an address belonging to some other wallet, but they’ll return the change to P2PKH). For existing users there’s no benefit/incentive to going with P2SH32 because it costs manhours to implement it + sending to P2SH32 addresses is more expensive.

What I’m saying here - doing nothing is the path of least resistance, and we can expect most to do safely do the nothing :slight_smile:

Sure I’ll see how I can improve on that, although I hope that it’s already clear that other than upgrading the node as part of orderly HF upgrade - nobody has to do a thing about this CHIP. It solves only the particular collision problem which was not even the main objective of SegWit (but was a well known problem, which is why SegWit shipped with both 20b and 32b addresses and not just the 20b). The collision problem part has nothing to do with the main objectives of segwit: malleability and blocksize, and there’s no benefit like reduced fees to be reaped by wallets here. And P2SH32 solves the collision problem in a different way than SegWit: there will be no 3rd VM run, but anyway the inner workings are not really relevant for privacy consideration, just for info.