Quantum-resistant One-time-use Lock

I present a proof of concept for a quantum-resistant BCH contract.

This is similar to Stewart, I., et. al. (2018). “Committing to quantum resistance: A slow defence for Bitcoin against a fast quantum computing attack”, but it is implemented using smart contract capabilities specific to Bitcoin Cash (TX introspection opcodes, OP_CAT & OP_SPLIT). The outline of the contract was given at end of section here:

We can outline the contract here: require a valid signature from a key, and also require spending another input alongside - one that spends a prevout which reveals a pre-commitment to the signature and such that it is time-locked.
This way, even if the key is revealed, the adversary could not immediately produce an alternative transaction since he would not have a matured pre-commitment output in his possession.
He could produce one and wait, but by the time it matures, the real owner’s transaction will be buried under some blocks, and stealing the funds would require a chain re-org.

We don’t even need a signature! The contract is essentially a hash-lock contract (labeled “lock”) with an additional requirement in that an aged commitment to the particular “lock” UTXO + desired outputs must be revealed in the same transaction.
The commitment is done by using another contract (labeled “commit”) to which only BCH dust is sent when the user wants to spend from the “lock”.

The “lock” only commits to the secret + invariant part of the “commit” redeem script (sha256(one_time_secret + commit_script_tail)), meaning the owner can later decide to which outputs he wants to send the funds locked.

Once decided, he generates the commitment for the “commit” script, which commits to prevout ref. of the particular “lock” UTXO, the secret, and the outputs: sha256(associated_outpoint + one_time_secret + {first 3 outputs}) and sends some dust to the “commit” script.

To spend, both “lock” and matching “commit” must be spent together.
The “lock” contract will verify the contents of the “commit” reveal script, and the “commit” contract will verify the outputs of the TX and that it’s being spent with the correct “lock” UTXO.

Example transactions:

Notes:

  • Main trade-off is that we lose the 0-conf UX, because each spend requires waiting for the commitment UTXO to age.
  • If you give the “lock” address to some sender, he could later send you once more, not knowing that the secret is revealed and anyone can race to spend from it. We can work around this with a more complex scheme - one that would use a pay-to-token address so senders are never given the quantum-lock address. The owner would keep only the NFT in the quantum-lock address, and use it to collect BCH sent to associated pay-to-token address. This would hide the quantum-lock address from senders.
  • The 2 contracts use relative +1 & -1 offsets from “this” input index to “look” at each other, meaning any number of the pairs can be spent in the same TX.
  • The pair of contracts is limited to up to 3 outputs (more can be added but it would increase size of the contract).
  • So, N-to-1, N-to-2, and N-to-3 spends are possible.
  • Once a secret is revealed, anyone can learn it and start watching the “lock” address for deposits and race to steal the funds. Users should take great care in managing their secrets.
  • The commitment UTXO could be commiting to invalid outputs (like an output with 22M BCH), meaning user would be unable to spend not because scripts would fail evaluation but because the TX would fail to validate against general consensus rules (I made that mistake, then I had to generate a correct commitment and clean up the unusable one). This means that users could accidentally reveal the secret by trying to broadcast an invalid TX, and then some attacker could front-run their later, valid, spend by generating a commitment as soon as he learns the secret.
  • Adversarial hash-rate could in theory collude to delay confiriming the spending TX, while allowing their own commit UTXO to age and then they’d allow their own spend through, while censoring the original. Unless they could execute a 51%, users could just set a longer age_spend.
  • If blocks would get clogged, it could happen that the spending TX is not confirmed for a long time, allowing a miner to age an alternative commitment and post an alternative spending TX.

Contract’s BitauthIDE Template

License: MIT No Attribution.

6 Likes

Neat way of locking coins without requiring a cryptographic signature to safely get them out again :exploding_head:.

I can imagine this also used in an application where you’d want to send someone BCH without them having an address yet – by sharing the secret in some way (and the secret is not a private key).

2 Likes

Yeah, although security is reduced because there’s a race condition, which can be gamed by hash-rate.

ScriptVM can theoretically implement Lamport signatures, but it needs like 1000 opcodes. Their trade-off is that they’d be fully secure and 0-conf would still work, but they’re big and verification is heavy on hashing. Keys are still one-time-use and users would have to be very careful not to mess up. Lamport, too, could make good use of pay-to-NFT to hide the signature from the payee (so you can receive multiple payments to same address).