Deterministic address derivation for quantum-resistant payout hygiene
> Disclaimer: This is independent research and development. The views and work presented here are my own and do not represent the official position, strategy, or endorsement of my employer.
Testnet4 Validation
On January 28, 2026, we validated coinbase rotation on Bitcoin testnet4. Two blocks were mined using automatically rotated addresses derived from a single extended public key. Each block’s coinbase output went to a fresh, never-before-used address—an address whose public key has never been exposed on-chain.
2026-01-28T17:44:24.925747Z INFO pool_sv2::channel_manager::mining_message_handler: SubmitSharesExtended: 💰 Block Found!!! 💰 0000000000000000b2d658e434cb1383eb41853a1b8bb23df1993a6c9036f2f7
2026-01-28T19:10:00.262347Z INFO pool_sv2::channel_manager::mining_message_handler: SubmitSharesExtended: 💰 Block Found!!! 💰00000000000000005a65b2d963bc3117c3b0f92d5674483e7ab6af1e03d51267
Mined Blocks
Block 1
-
Time: 2026-01-28 17:44:24 UTC
-
Derivation Index: 4
-
Address:
tb1q5u4w9hwuepxfng9tusl5l0h353k32wuwv56s5x
Block 2
-
Time: 2026-01-28 19:10:00 UTC
-
Derivation Index: 5
-
Address:
tb1qlqym3pzn76qz5ecj7y36rwrgxrr83pnwxtc5rp
Descriptor Used
wpkh(tpubDDHYkDsJ8XB1LLjMNrk5gXsmze87LRkWoNqprdXPud9Yx3ZfsjZZJEqscUgSRLJ1EG77KSKygC9uNAeDtgHsLtvH93MnPF2M9Vq5WvGvcLw/0/*)
The wildcard /* at the end enables automatic rotation. Each block found increments the derivation index, producing a new unique address with an unexposed public key.
—
The Quantum Threat to Bitcoin
Bitcoin’s security model relies on the Elliptic Curve Digital Signature Algorithm (ECDSA) and the secp256k1 curve. The security assumption is that deriving a private key from a public key is computationally infeasible—a problem known as the Elliptic Curve Discrete Logarithm Problem (ECDLP).
Quantum computers change this calculus. Shor’s algorithm, running on a sufficiently powerful quantum computer, can solve ECDLP in polynomial time. While such computers don’t exist today, the cryptographic community takes this threat seriously. Bitcoin’s long-term security depends on proactive measures.
BIP-360: Pay to Quantum Resistant Hash (P2QRH)
The Bitcoin community is actively developing quantum-resistant solutions. [BIP-360](<https://github.com/bitcoin/bips/blob/master/bip-0360.mediawiki>) proposes a new output type using post-quantum cryptographic signatures. Key features include:
-
Lattice-based signatures (e.g., FALCON, SPHINCS+) resistant to Shor’s algorithm
-
Backward compatibility via soft fork activation
-
Migration path for existing UTXOs to quantum-safe addresses
Until P2QRH or similar solutions are deployed, minimizing public key exposure is the most effective defense-in-depth measure available today.
Understanding Public Key Exposure
Bitcoin addresses are derived from public keys through a one-way hash function:
Address = RIPEMD160(SHA256(PublicKey)) // P2PKH
Address = SHA256(PublicKey)[0:20] // P2WPKH (simplified)
This hash provides a layer of protection: even if an attacker can derive private keys from public keys (the quantum threat), they still cannot derive public keys from addresses (hash preimage resistance). The public key remains hidden until the first spend.
The ECDSA Exposure Window
┌───────────────────────────────┐
│ FRESH ADDRESS │
│ (never spent from) │
├───────────────────────────────┤
│ • Pubkey hidden behind hash │
│ • Quantum-safe │
│ • No known attack │
└───────────────────────────────┘
│
│ First spend
▼
┌───────────────────────────────┐
│ EXPOSED ADDRESS │
│ (spent from) │
├───────────────────────────────┤
│ • Transaction reveals pubkey │
│ • Quantum vulnerable │
│ • Future deposits at risk │
└───────────────────────────────┘
Why Address Reuse is Dangerous
When you spend from an address, the full ECDSA public key is revealed in the transaction’s witness data (for SegWit) or scriptSig (for legacy). A quantum attacker could then:
1. Extract the public key from any historical transaction spending from that address
2. Run Shor’s algorithm to derive the private key
3. Steal any funds subsequently deposited to that address
This is particularly dangerous for mining pools that reuse a single coinbase address. After the first consolidation transaction, every future block reward sent to that address is quantum-vulnerable from the moment it’s mined.
The “Harvest Now, Decrypt Later” Attack
Nation-state adversaries are already archiving encrypted data and blockchain transactions with the expectation that future quantum computers will enable decryption. This is known as a “harvest now, decrypt later” (HNDL) attack.
For Bitcoin, this means:
-
Public keys exposed today are recorded permanently on-chain
-
When quantum computers become viable, historical public keys can be attacked
-
Funds in addresses with exposed public keys become immediately vulnerable
> Mining Pool Coinbases: A High-Value Target > > Mining pools accumulate significant value in coinbase outputs. A pool using a single static address creates an attractive target: one public key exposure compromises all future deposits. Coinbase rotation eliminates this single point of failure.
Coinbase Rotation: Defense in Depth
By deriving a fresh address for each block, coinbase rotation ensures:
-
No public key reuse: Each coinbase output has a unique, unexposed public key
-
Isolated exposure: Spending from one address doesn’t compromise others
-
Time-limited vulnerability: Each address is only at risk after its first spend
-
Reduced attack surface: An attacker must crack each address individually
Quantum Timeline Considerations
| Era | Timeframe | Status |
|---|---|---|
| Current | 2024-2026 | NISQ devices (noisy, limited qubits). No threat to ECDSA.Time to implement defensive measures. |
| Near-Term | 2030-2035 | Early fault-tolerant systems.Potential threat to weak keys. BIP-360 activation target. |
| Long-Term | 2035+ | Cryptographically relevant quantum computers.ECDSA potentially broken. P2QRH protects new UTXOs. |
Timeline is speculative. Estimates vary widely among cryptographers. The prudent approach is to minimize exposure now rather than react after quantum capabilities emerge.
—
Additional Benefits
Beyond quantum resistance, coinbase rotation provides operational benefits:
-
Reduced chain analysis surface: Blocks aren’t trivially linkable by address
-
Simplified UTXO management: Smaller, distributed UTXOs vs. one large accumulator
-
Standard wallet compatibility: Uses BIP-32/44/84 HD derivation paths
—
The Solution: Wildcard Descriptor Rotation
Coinbase rotation leverages Bitcoin’s hierarchical deterministic (HD) wallet standard (BIP-32/44/84) combined with miniscript descriptors. By specifying a wildcard /*) in the descriptor, the pool automatically derives sequential addresses from a single extended public key—each with a unique, unexposed public key.
Configuration Example
# pool-config.toml
# Wildcard descriptor enables rotation
coinbase_reward_script = "wpkh(tpub.../0/*)"
# Persistence file (survives restarts)
coinbase_index_file = "/var/lib/pool/coinbase_index.dat"
# Starting derivation index (default: 0)
coinbase_start_index = 0
# Optional: use block height as index for easier wallet recovery
use_block_height_derivation = false
Supported Descriptor Types
| Type | Descriptor Format | Address Type |
|---|---|---|
| Native SegWit |
wpkh(xpub.../0/*) |
bc1q… (P2WPKH) |
| Taproot |
tr(xpub.../0/*) |
bc1p… (P2TR) |
| Legacy SegWit |
sh(wpkh(xpub.../0/*)) |
3… (P2SH-P2WPKH) |
How It Works
Miner ──> Pool: Block found!
│
Pool ──> Derivator:
rotate_coinbase()
│
Derivator: index++ (atomic)
derive(xpub, index)
→ new scriptPubKey
│
Derivator ──> Disk:
persist("seq:N")
│
Pool: Update outputs
│
Pool ──> Miner: Success
—
Security Comparison: Static vs. Rotating Addresses
Static Address (Vulnerable)
┌───────────────────────────────┐
│ Block 1 ─┐ │
│ Block 2 ─┼─> Single ─> ONE │
│ Block 3 ─┤ Address key │
│ Block N ─┘ breaks│
│ ALL │
└───────────────────────────────┘
Rotating Addresses (Defense in Depth)
┌───────────────────────────────┐
│ Block 1 ──> Addr 1 ─┐ │
│ Block 2 ──> Addr 2 ─┼─> Each │
│ Block 3 ──> Addr 3 ─┤ isolated│
│ Block N ──> Addr N ─┘ │
└───────────────────────────────┘
—
Implementation Details
The implementation uses Rust’s miniscript crate for descriptor parsing and derivation. The current index is stored atomically in memory and persisted to disk after each rotation, ensuring crash-safety and restart resilience.
Key Components
XpubDerivator — stratum-apps/src/config_helpers/xpub_derivation.rs
-
Parses wildcard descriptors
-
Derives scriptPubKey at arbitrary indices
-
Thread-safe index management via
AtomicU32 -
Persistent storage with format:
seq:Norheight:N
CoinbaseRewardScript — stratum-apps/src/config_helpers/coinbase_output/
-
Detects wildcard descriptors via
has_wildcard()method -
Returns raw descriptor string for derivator initialization
ChannelManager — pool-apps/pool/src/lib/channel_manager/
-
Calls
rotate_coinbase_address()after block found -
Updates
coinbase_outputsin channel manager data
Derivation Modes
| Mode | Index Source | Use Case |
|---|---|---|
| Sequential | 0, 1, 2, 3, … | Default, simple incrementing |
| Block Height | Block’s height | Easier wallet recovery (derive at height Nto find block N’s address) |
> Note: Block Height Derivation Not Yet Verified
>
> The block height derivation mode use_block_height_derivation = true) has not been validated in production. The testnet4 blocks shown above used sequential derivation. Use block height mode with caution and thorough testing.
Persistence Format
# Sequential mode
seq:42
# Block height mode
height:850000
# Legacy format (treated as sequential)
42
—
Verify It Yourself
Don’t trust, verify. You can independently confirm that the testnet4 blocks used the correct derived addresses by running the derivation yourself.
Rust
use miniscript::{Descriptor, descriptor::DescriptorPublicKey, bitcoin::{Network, Address}};
fn main() {
let descriptor_str = "wpkh(tpubDDHYkDsJ8XB1LLjMNrk5gXsmze87LRkWoNqprdXPud9Yx3ZfsjZZJEqscUgSRLJ1EG77KSKygC9uNAeDtgHsLtvH93MnPF2M9Vq5WvGvcLw/0/*)";
let desc: Descriptor<DescriptorPublicKey> = descriptor_str.parse().unwrap();
// Derive at indices 4 and 5 (the two testnet4 blocks)
for i in 4..=5 {
let derived = desc.at_derivation_index(i).unwrap();
let script = derived.script_pubkey();
let address = Address::from_script(&script, Network::Testnet).unwrap();
println!("Index {}: {}", i, address);
}
}
// Expected output:
// Index 4: tb1q5u4w9hwuepxfng9tusl5l0h353k32wuwv56s5x
// Index 5: tb1qlqym3pzn76qz5ecj7y36rwrgxrr83pnwxtc5rp
Python (with python-bitcoinlib)
from bitcoin.wallet import CBitcoinExtPubKey
from bitcoin.core import Hash160
from bitcoin.bech32 import encode_segwit_address
# tpub from the descriptor (BIP84 account key)
tpub = "tpubDDHYkDsJ8XB1LLjMNrk5gXsmze87LRkWoNqprdXPud9Yx3ZfsjZZJEqscUgSRLJ1EG77KSKygC9uNAeDtgHsLtvH93MnPF2M9Vq5WvGvcLw"
# Parse the extended public key
xpub = CBitcoinExtPubKey(tpub)
# Derive at m/0/4 and m/0/5 (relative to the tpub)
for i in [4, 5]:
child = xpub.derive(0).derive(i)
pubkey_hash = Hash160(child.pub.serialize())
address = encode_segwit_address("tb", 0, pubkey_hash)
print(f"Index {i}: {address}")
# Expected output:
# Index 4: tb1q5u4w9hwuepxfng9tusl5l0h353k32wuwv56s5x
# Index 5: tb1qlqym3pzn76qz5ecj7y36rwrgxrr83pnwxtc5rp
### Bitcoin Core (bitcoin-cli)
# Import the descriptor (watch-only)
bitcoin-cli -testnet4 importdescriptors '[{
"desc": "wpkh(tpubDDHYkDsJ8XB1LLjMNrk5gXsmze87LRkWoNqprdXPud9Yx3ZfsjZZJEqscUgSRLJ1EG77KSKygC9uNAeDtgHsLtvH93MnPF2M9Vq5WvGvcLw/0/*)#checksum",
"timestamp": "now",
"range": [0, 10],
"watchonly": true
}]'
# Derive addresses at indices 4 and 5
bitcoin-cli -testnet4 deriveaddresses \
"wpkh(tpubDDHYkDsJ8XB1LLjMNrk5gXsmze87LRkWoNqprdXPud9Yx3ZfsjZZJEqscUgSRLJ1EG77KSKygC9uNAeDtgHsLtvH93MnPF2M9Vq5WvGvcLw/0/*)#checksum" \
"[4,5]"
# Expected output:
# [
# "tb1q5u4w9hwuepxfng9tusl5l0h353k32wuwv56s5x",
# "tb1qlqym3pzn76qz5ecj7y36rwrgxrr83pnwxtc5rp"
# ]
JavaScript (bitcoinjs-lib)
const bitcoin = require('bitcoinjs-lib');
const { BIP32Factory } = require('bip32');
const ecc = require('tiny-secp256k1');
const bip32 = BIP32Factory(ecc);
const network = bitcoin.networks.testnet;
// Parse the tpub
const tpub = "tpubDDHYkDsJ8XB1LLjMNrk5gXsmze87LRkWoNqprdXPud9Yx3ZfsjZZJEqscUgSRLJ1EG77KSKygC9uNAeDtgHsLtvH93MnPF2M9Vq5WvGvcLw";
const node = bip32.fromBase58(tpub, network);
// Derive at m/0/4 and m/0/5
for (const i of [4, 5]) {
const child = node.derivePath0/${i});
const { address } = bitcoin.payments.p2wpkh({
pubkey: child.publicKey,
network
});
console.logIndex ${i}: ${address});
}
// Expected output:
// Index 4: tb1q5u4w9hwuepxfng9tusl5l0h353k32wuwv56s5x
// Index 5: tb1qlqym3pzn76qz5ecj7y36rwrgxrr83pnwxtc5rp
Expected Derivation Table
| Index | ScriptPubKey (hex) | Testnet4 Address |
|---|---|---|
| 0 |
0014798fb52bc77ba8e028dfad1b522505223c7e7ca0 |
tb1q0x8m22780w5wq2xl45d4yfg9yg78ul9qcgs54f |
| 1 |
00143acc8d6d349a24a198fb9eec0e27b822c589d407 |
tb1q8txg6mf5ngj2rx8mnmkqufacytzcn4q8v4yt8n |
| 2 |
0014dd4da77967b0a8c59ee3026af582de496abad124 |
tb1qm4x6w7t8kz5vt8hrqf40tqk7f94t45fy88fzal |
| 3 |
001401b85a64c3c8d8dcf46f49230d938ec1245fcd8e |
tb1qqxu95exrervdear0fy3smyuwcyj9lnvwqhc2cv |
| 4 |
0014a72ae2dddcc84c99a0abe43f4fbef1a46d153b8e |
tb1q5u4w9hwuepxfng9tusl5l0h353k32wuwv56s5x |
| 5 |
0014f809b88453f6802a6712f123a1b86830c678866e |
tb1qlqym3pzn76qz5ecj7y36rwrgxrr83pnwxtc5rp |
Indices 4 and 5 correspond to the testnet4 blocks mined.
—
Getting Started
Clone and Build
# Clone the feature branch
git clone -b feat/coinbase-rotation \
https://github.com/average-gary/sv2-apps.git
cd sv2-apps/pool-apps
# Build the pool
cargo build --release -p pool_sv2
Configure Rotation
# In your pool-config.toml
coinbase_reward_script = "wpkh(YOUR_TPUB_OR_XPUB/0/*)"
coinbase_index_file = "/path/to/coinbase_index.dat"
Descriptor Conversion
If you have a descriptor from Sparrow or Bitcoin Core in a different format, use:
sv2-descriptor-cli — Convert descriptors between formats
—
Further Reading
BIP-360: Pay to Quantum Resistant Hash (P2QRH)
Bitcoin Wiki: Quantum Computing and Bitcoin
Quantum Attacks on Bitcoin, and How to Protect Against Them (Aggarwal et al.)
BIP-32: Hierarchical Deterministic Wallets
—
Links:
GitHub: feat/coinbase-rotation
Stratum V2 Reference Implementation
License: MIT / Apache-2.0
Last updated: January 28, 2026