Coinbase Rotation for SV2 Mining Pools

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

  • View on mempool.space

Block 2

  • Time: 2026-01-28 19:10:00 UTC

  • Derivation Index: 5

  • Address: tb1qlqym3pzn76qz5ecj7y36rwrgxrr83pnwxtc5rp

  • View on mempool.space

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](<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

EraTimeframeStatus
Current2024-2026NISQ devices (noisy, limited qubits). No threat to ECDSA.Time to implement defensive measures.
Near-Term2030-2035Early fault-tolerant systems.Potential threat to weak keys. BIP-360 activation target.
Long-Term2035+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

TypeDescriptor FormatAddress Type
Native SegWitwpkh(xpub.../0/*)bc1q... (P2WPKH)
Taproottr(xpub.../0/*)bc1p... (P2TR)
Legacy SegWitsh(wpkh(xpub.../0/*))3... (P2SH-P2WPKH)

How It Works

Miner ──&gt; Pool: Block found!
Β Β  Β  Β  Β  Β  Β  Β  β”‚
Pool ──&gt; Derivator:
Β Β  Β  Β  Β  Β  Β  Β  rotate_coinbase()
Β Β  Β  Β  Β  Β  Β  Β  β”‚
Derivator: Β  Β  index++ (atomic)
Β Β  Β  Β  Β  Β  Β  Β  derive(xpub, index)
Β Β  Β  Β  Β  Β  Β  Β  β†’ new scriptPubKey
Β Β  Β  Β  Β  Β  Β  Β  β”‚
Derivator ──&gt; Disk:
Β Β  Β  Β  Β  Β  Β  Β  persist("seq:N")
Β Β  Β  Β  Β  Β  Β  Β  β”‚
Pool:Β  Β  Β  Β  Β  Update outputs
Β Β  Β  Β  Β  Β  Β  Β  β”‚
Pool ──&gt; Miner: Success

---

Security Comparison: Static vs. Rotating Addresses

Static Address (Vulnerable)

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Block 1 ─┐  Β  Β  Β  Β  Β  Β  Β  Β  Β  β”‚
β”‚ Block 2 ─┼─&gt; SingleΒ  ─&gt; ONE Β  β”‚
β”‚ Block 3 ── Β  AddressΒ  Β  key Β  β”‚
β”‚ Block N β”€β”˜Β  Β  Β  Β  Β  Β  Β  breaksβ”‚
β”‚ Β  Β  Β  Β  Β  Β  Β  Β  Β  Β  Β  Β  ALL Β  β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Rotating Addresses (Defense in Depth)

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Block 1 ──&gt; Addr 1 ─┐ Β  Β  Β  Β  β”‚
β”‚ Block 2 ──&gt; Addr 2 ─┼─&gt; EachΒ  β”‚
β”‚ Block 3 ──&gt; Addr 3 ──  isolatedβ”‚
β”‚ Block N ──&gt; 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:N or height: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_outputs in channel manager data

Derivation Modes

ModeIndex SourceUse Case
Sequential0, 1, 2, 3, ...Default, simple incrementing
Block HeightBlock's heightEasier 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&lt;DescriptorPublicKey&gt; = 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

IndexScriptPubKey (hex)Testnet4 Address
00014798fb52bc77ba8e028dfad1b522505223c7e7ca0tb1q0x8m22780w5wq2xl45d4yfg9yg78ul9qcgs54f
100143acc8d6d349a24a198fb9eec0e27b822c589d407tb1q8txg6mf5ngj2rx8mnmkqufacytzcn4q8v4yt8n
20014dd4da77967b0a8c59ee3026af582de496abad124tb1qm4x6w7t8kz5vt8hrqf40tqk7f94t45fy88fzal
3001401b85a64c3c8d8dcf46f49230d938ec1245fcd8etb1qqxu95exrervdear0fy3smyuwcyj9lnvwqhc2cv
40014a72ae2dddcc84c99a0abe43f4fbef1a46d153b8etb1q5u4w9hwuepxfng9tusl5l0h353k32wuwv56s5x
50014f809b88453f6802a6712f123a1b86830c678866etb1qlqym3pzn76qz5ecj7y36rwrgxrr83pnwxtc5rp

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

SRI Documentation

License: MIT / Apache-2.0

Last updated: January 28, 2026