Good point, OTS + stateless hybrid is safe against key reuse as long as the stateless leg is not broken, but I worry about people screwing up key management and forgetting about the issue and later causing a mess if q-day ever comes.
If wallets have to worry about key reuse then it is not a drop-in replacement. IMO only practically stateless schemes should qualify for OP_CHECKSIG overload since that is the established norm with secp256k1. One or few-time schemes can either get their own opcode or just be done via Script.
Falcon is small but risky, SPHINCS+ is safe but big, so I wouldn’t expect many P2PKH users now. But think about other uses:
- for oracles, an OP_CHECKDATASIG oracle attestation needs to happen only once and small proofs can be emitted as NFTs so you “pay” the 8kB only once.
- for vaults, the SPHINCS+ OP_CHECKSIG can be a conditional which the contract would carry but would flip to only post q-day, and in the meantime the vault would use secp256k1.
Also, the P2PKH:SPHINCS+ creates a reliable target for which to prepare migration plans. If we work out the commit-delay-reveal-challenge scheme then we must have a reliable migration target for it, so people can pre-commit and save their commitments knowing that they’ll be good indefinitely. If q-day never comes, they just keep using same old P2PKH:secp256k1. If q-day comes, they have their aged commitment which they can use to safely migrate P2PKH:secp256k1 to P2PKH:SPHINCS+, and then from there maybe migrate to some lighter future scheme.
But OTS (one-time-signature) / FTS (few-time-signature) schemes can be used too, maybe we should propose OP_CHECKFTS and OP_CHECKDATAFTS instead of this overload now.
PS if wallets are ready to prevent key reuse, then this contract is a generic way to allow yourself safe migration in case of q-day:
OP_DEPTH <2> OP_EQUAL
OP_IF
OP_DUP
OP_HASH160
<$(<key.public_key> OP_HASH160)>
OP_EQUALVERIFY
OP_CHECKSIG
OP_ELSE
OP_DUP
OP_HASH256
<{migration function body hash}>
OP_EQUALVERIFY
<0> OP_DEFINE
<0> OP_INVOKE
OP_ENDIF
The function body is a simple forward covenant that moves the UTXO’s contents to a pre-committed locking script and without revealing the P2PKH key from the other IF branch:
// pre-committed migration function
// migrates without exposing the p2pkh key
<
// set the pre-commited locking script on the output
<{migration target locking script}>
OP_INPUTINDEX OP_OUTPUTBYTECODE OP_EQUAL
// pass on UTXO's state to the output
OP_INPUTINDEX OP_UTXOVALUE <300> OP_SUB
OP_INPUTINDEX OP_OUTPUTVALUE OP_LESSTHANOREQUAL OP_VERIFY
OP_INPUTINDEX OP_UTXOTOKENCATEGORY
OP_INPUTINDEX OP_OUTPUTTOKENCATEGORY OP_EQUALVERIFY
OP_INPUTINDEX OP_UTXOTOKENCOMMITMENT
OP_INPUTINDEX OP_OUTPUTTOKENCOMMITMENT OP_EQUALVERIFY
OP_INPUTINDEX OP_UTXOTOKENAMOUNT
OP_INPUTINDEX OP_OUTPUTTOKENAMOUNT OP_EQUAL
>
But for this to work, you need to have a good migration target so you can write that function now and start using such wallet now.
This is compact: only 71 bytes redeem script (BitauthIDE link).
