From: Amarildo <amarildocaka01@gmail.com>
To: Bitcoin Development Mailing List <bitcoindev@googlegroups.com>
Subject: [bitcoindev] Q-Lock: Quantum-Resistant Spending via ECDSA + Hash-Based Secrets
Date: Fri, 28 Nov 2025 07:00:04 -0800 (PST) [thread overview]
Message-ID: <1c6c4afc-0fe4-4b72-b70f-3f6ba4c19315n@googlegroups.com> (raw)
[-- Attachment #1.1: Type: text/plain, Size: 14499 bytes --]
Hi everyone,
I'd like to propose an alternative approach to quantum resistance
for Bitcoin that I believe is simpler than BIP-360 P2QRH.
**Q-Lock: Quantum-Resistant Spending Protocol**
SUMMARY:
- Keeps ECDSA unchanged (no new signature algorithms!)
- Adds hash-based secret layer on top
- Uses only SHA256 + Merkle trees (proven crypto)
- ~3 KB transactions (comparable to FALCON)
- Two-phase commit-reveal scheme
- Soft fork compatible
- BIP-32 HD wallets work normally
KEY INSIGHT:
Instead of replacing ECDSA with new post-quantum algorithms
(FALCON, SPHINCS+, Dilithium), Q-Lock adds a quantum-safe
secret layer. Attacker must break BOTH ECDSA AND know the
hash preimages - quantum computers can't reverse SHA256.
COMPARISON TO BIP-360:
- BIP-360: New lattice-based crypto, 1.3-50 KB sigs, breaks BIP-32
- Q-Lock: Proven SHA256 crypto, ~3 KB sigs, BIP-32 works
HOW IT WORKS:
1. Setup: Generate 64 random secrets, commit via Merkle root
2. Commit phase: Lock outputs WITHOUT exposing pubkey
3. Reveal phase: Expose pubkey + secrets at block-hash-determined positions
4. Quantum attacker sees pubkey too late - outputs already locked!
```
Q-Lock is a quantum-resistant spending protocol for Bitcoin
that adds a hash-based secret layer on top of existing ECDSA
signatures. It uses a two-phase commit-reveal scheme where
spending positions are determined by the block hash, making
it secure against quantum attackers who can break ECDSA.
Q-Lock does NOT replace ECDSA. It adds quantum protection
while preserving Bitcoin's existing cryptographic foundation.
Transaction size: ~3 KB
Requires: Soft fork (1-2 new opcodes)
```
-----
## MOTIVATION
```
THE QUANTUM THREAT:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Shor's algorithm can break ECDSA by extracting private keys
from public keys. When a Bitcoin transaction is broadcast,
the public key is exposed in the mempool. A quantum attacker
could:
1. See public key in mempool
2. Run Shor's algorithm
3. Extract private key
4. Create competing transaction
5. Steal funds
Current solutions like Lamport signatures:
- Replace ECDSA entirely (24 KB signatures!)
- Politically difficult (changing Bitcoin's core crypto)
- Unlikely to be adopted before emergency
Q-LOCK ADVANTAGES:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
- Keeps ECDSA unchanged ✓
- Small size (~3 KB vs 24 KB) ✓
- Soft fork deployment ✓
- Opt-in adoption ✓
- Quantum resistant ✓
```
-----
## SPECIFICATION
### 1. SETUP
```
USER GENERATES:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
1. Standard ECDSA keypair:
- privkey (256 bits)
- pubkey = privkey × G
- pubkey_hash = HASH160(pubkey)
2. Secret array (64 secrets):
- secret[0] = random(256 bits)
- secret[1] = random(256 bits)
- ...
- secret[63] = random(256 bits)
3. Commitment array:
- commitment[i] = SHA256(secret[i])
4. Merkle tree from commitments:
- merkle_root = MerkleRoot(commitment[0..63])
STORED IN WALLET:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
- privkey (32 bytes)
- secret[0..63] (2048 bytes)
- merkle_tree (for generating proofs)
STORED ON CHAIN (UTXO):
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
- pubkey_hash (20 bytes)
- merkle_root (32 bytes)
- num_secrets (1 byte) = 64
Total UTXO: ~53 bytes
```
-----
### 2. ADDRESS FORMAT
```
Q-LOCK ADDRESS:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
witness version: 2 (new)
witness program: <pubkey_hash> || <merkle_root>
Bech32m encoding: bc1q...
Example: bc1qlock1abc123...
```
-----
### 3. LOCKING SCRIPT
```
scriptPubKey:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
OP_2 <pubkey_hash> <merkle_root> <num_secrets>
```
-----
### 4. SPENDING: PHASE 1 (COMMIT)
```
PURPOSE:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Lock the transaction outputs WITHOUT exposing public key.
Prevents quantum attacker from knowing which key to attack.
COMMIT TRANSACTION:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
inputs:
- txid: <utxo_txid>
- vout: <utxo_index>
outputs:
- value: <amount>
- scriptPubKey: <destination>
witness:
- intent_hash = SHA256(pubkey || merkle_root || outputs_hash || nonce)
- nonce (for uniqueness)
- phase_flag = 0x00 (commit)
WHAT'S EXPOSED:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
✓ Which UTXO being spent
✓ Where funds are going
✓ Intent hash (proves knowledge)
✗ NO public key exposed!
✗ NO signature exposed!
✗ Quantum attacker can't run Shor's!
VALIDATION RULES:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
1. Intent hash is 32 bytes
2. Phase flag is 0x00
3. Creates "pending" state
4. Must be completed by reveal within 144 blocks (~24 hours)
```
-----
### 5. SPENDING: PHASE 2 (REVEAL)
```
PURPOSE:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Complete the spend by revealing:
- ECDSA signature
- Public key
- Secrets at block-determined positions
POSITION CALCULATION:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
commit_block_hash = GetBlockHash(commit_block_height)
position_seed = SHA256(commit_block_hash || commit_txid)
For i = 0 to 31:
position[i] = position_seed[i*2 : i*2+2] mod 64
Result: 32 positions from 0-63
REVEAL TRANSACTION:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
inputs:
- txid: <commit_txid>
- vout: 0
witness:
- signature (ECDSA)
- pubkey (compressed, 33 bytes)
- commit_block_height (4 bytes)
- num_reveals = 32
- For each position:
- secret[position[i]] (32 bytes)
- merkle_proof[i] (6 × 32 = 192 bytes)
- phase_flag = 0x01 (reveal)
REVEAL SIZE:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
- Signature: ~72 bytes
- Pubkey: 33 bytes
- Block height: 4 bytes
- 32 secrets: 1024 bytes
- 32 merkle proofs: ~2048 bytes (optimized)
- Overhead: ~50 bytes
TOTAL: ~3200 bytes (~3 KB)
```
-----
### 6. VALIDATION RULES
```
COMMIT VALIDATION:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
1. phase_flag == 0x00
2. intent_hash is valid 32-byte hash
3. Output creates pending Q-Lock state
4. Mark UTXO as "commit pending"
REVEAL VALIDATION:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
1. phase_flag == 0x01
2. Referenced commit exists and is pending
3. Reveal within 144 blocks of commit
4. ECDSA signature valid for pubkey
5. HASH160(pubkey) == pubkey_hash in UTXO
6. Recompute: position_seed = SHA256(block_hash || commit_txid)
7. Extract 32 positions from position_seed
8. For each position i:
a. commitment = SHA256(revealed_secret[i])
b. Verify merkle_proof[i] proves commitment is in merkle_root
9. intent_hash matches SHA256(pubkey || merkle_root || outputs_hash ||
nonce)
10. All checks pass → spend complete!
```
-----
### 7. NEW OPCODES
```
OPTION A: Single Opcode
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
OP_QLOCK (0xXX)
Handles both commit and reveal based on phase_flag.
Stack input:
<phase_flag> <witness_data...> <pubkey_hash> <merkle_root>
If phase_flag == 0x00: Validate commit
If phase_flag == 0x01: Validate reveal
OPTION B: Two Opcodes
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
OP_QLOCK_COMMIT (0xXX)
- Validates commit phase
- Creates pending state
OP_QLOCK_REVEAL (0xXY)
- Validates reveal phase
- Completes spend
```
-----
## SECURITY ANALYSIS
```
ATTACK 1: QUANTUM BREAKS ECDSA
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Attacker sees pubkey in REVEAL phase.
Runs Shor's algorithm, gets privkey.
RESULT: Too late!
- Commit already confirmed
- Outputs already locked
- tx_id bound to YOUR outputs
- Attacker's tx would have different tx_id
- Different tx_id = different positions
- Attacker doesn't have secrets at those positions!
ATTACK FAILS ✓
ATTACK 2: QUANTUM FRONT-RUNNING
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Attacker tries to attack during COMMIT phase.
RESULT: Can't!
- Public key not exposed in commit!
- Only intent_hash visible
- Can't run Shor's on a hash!
ATTACK FAILS ✓
ATTACK 3: GROVER'S ON SECRETS
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Attacker tries Grover's algorithm on SHA256.
RESULT: Impractical!
- SHA256: 2^256 → 2^128 with Grover
- 2^128 still astronomically large
- Would take longer than age of universe
ATTACK FAILS ✓
ATTACK 4: PREDICT BLOCK HASH
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Attacker tries to predict future block hash.
RESULT: Impossible!
- Block hash depends on mining nonce
- Nonce found by brute force
- Unpredictable until block mined
ATTACK FAILS ✓
ATTACK 5: INTERCEPT REVEAL
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Attacker sees secrets in reveal broadcast.
RESULT: Useless!
- Commit already locked outputs
- Secrets bound to YOUR tx_id
- Can't redirect funds
ATTACK FAILS ✓
SECURITY PROPERTIES:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
✓ Quantum resistant (hash-based secrets)
✓ Forward secure (one-time use)
✓ Output binding (tx_id commits to destination)
✓ Unpredictable positions (block hash randomness)
```
-----
## RATIONALE
```
WHY NOT PURE LAMPORT?
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
- 24 KB signatures (too big!)
- Replaces ECDSA (political nightmare!)
- Hard to deploy
Q-Lock: ~3 KB, keeps ECDSA, soft fork!
WHY TWO PHASES?
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
- Phase 1 hides pubkey (prevents Shor's attack)
- Phase 2 reveals after outputs locked
- Only way to prevent mempool quantum attack!
WHY 64 SECRETS, REVEAL 32?
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
- 64 secrets = 2^64 possible combinations
- Reveal 32 = attacker needs to guess which 32
- Probability of guessing: 1 / C(64,32) ≈ 1 / 10^18
- Astronomically unlikely!
WHY MERKLE TREE?
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
- 64 commitments = 2048 bytes on chain (too big!)
- Merkle root = 32 bytes on chain (small!)
- Proofs in witness (~2 KB, acceptable)
```
-----
## BACKWARDS COMPATIBILITY
```
SOFT FORK:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
- Old nodes see Q-Lock outputs as "anyone can spend"
- New nodes enforce Q-Lock rules
- No chain split
- Gradual adoption
EXISTING ADDRESSES:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
- bc1q... (SegWit) continues to work
- bc1p... (Taproot) continues to work
- bc1qlock... (Q-Lock) is new option
- Users migrate when ready
```
-----
## TEST VECTORS
```
TEST VECTOR 1: SETUP
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
privkey: 0x0123456789abcdef...
pubkey: 0x02abc123...
pubkey_hash: 0x89ab...
secret[0]: 0xaaaa...
secret[1]: 0xbbbb...
...
secret[63]: 0xffff...
commitment[0]: SHA256(secret[0]) = 0x1111...
commitment[1]: SHA256(secret[1]) = 0x2222...
...
merkle_root: 0x9876...
TEST VECTOR 2: POSITION CALCULATION
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
commit_block_hash: 0x00000000000000000002f4a1...
commit_txid: 0x7a3f2b1c...
position_seed = SHA256(commit_block_hash || commit_txid)
= 0xe9c2a1b3...
positions = [3, 7, 12, 19, 24, 31, 33, 41, ...]
```
Full technical specification available. Looking for feedback
before formal BIP submission.
Does this approach have merit as an alternative/complement to BIP-360?
--
You received this message because you are subscribed to the Google Groups "Bitcoin Development Mailing List" group.
To unsubscribe from this group and stop receiving emails from it, send an email to bitcoindev+unsubscribe@googlegroups.com.
To view this discussion visit https://groups.google.com/d/msgid/bitcoindev/1c6c4afc-0fe4-4b72-b70f-3f6ba4c19315n%40googlegroups.com.
[-- Attachment #1.2: Type: text/html, Size: 16970 bytes --]
reply other threads:[~2025-12-10 18:12 UTC|newest]
Thread overview: [no followups] expand[flat|nested] mbox.gz Atom feed
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1c6c4afc-0fe4-4b72-b70f-3f6ba4c19315n@googlegroups.com \
--to=amarildocaka01@gmail.com \
--cc=bitcoindev@googlegroups.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox