Beaconless Confidential Transactions on Bitcoin Cash: RPA Today, PQ Tomorrow, ZK-Ready From Day 1
I’m starting a dedicated BitcoinCashResearch thread for the work I’ve been building in public over the last few months: a path toward beaconless reusable payment addresses and confidential asset transfers on Bitcoin Cash, designed to remain coherent as we move deeper into a post-quantum (PQ) world and into the BCH 2026 VM era.
This post is intentionally written for BCH developers and cryptography experts. It’s the “foundation memo” I want to keep pointing back to as we iterate on implementation details.
Why this direction
I began from the ABLA “SRPA” discussion and quickly learned we needed to be extremely precise about terms. For this post:
-
RPA (Reusable Payment Addresses): the Electron Cash implementation pattern where a static paycode produces fresh, unlinkable child addresses, but requires wallet-side detection + indexing. In Electron Cash specifically, this includes signature grinding and Electrum server support (
blockchain.reusable.*) to avoid full-chain scanning. - SRPA (Silent Reusable Payment Addresses) (as I’m using it here): achieve the same static-identity → one-time address UX without requiring signature grinding or special server indexing, and with a migration path to covenant-enforced policy + ZK verification.
- Deterministic restore: the wallet must be recoverable from a mnemonic + a wallet “birthday” (start height) even if the local state file is lost — using bounded scanning and (optionally) on-chain pool/state-cell checkpoints to make restore faster and more reliable.
The core idea I’m building toward is:
Keep the UX of “business-card paycodes”, but move the long-term security and privacy guarantees into enforceable covenants and ZK-verifiable state transitions, while keeping everything compatible with plain Bitcoin Cash transactions.
1) RPA + Post-Quantum: current viability and future key swap
RPA today (secp256k1 era)
Right now, RPA is practical on BCH. It gives us:
- a static identifier (paycode),
- unlinkable one-time P2PKH recipients,
- a workflow that fits inside normal BCH transaction shapes.
But we should be explicit about the security model: classic RPA relies on secp256k1-era assumptions (ECDH for shared-secret derivation, and ECDSA/Schnorr for authorization). In a fully-realized Shor adversary world — i.e., a fault-tolerant quantum computer capable of running Shor’s algorithm at scale — discrete log breaks the elliptic-curve foundation that both ECDH and ECDSA/Schnorr depend on (so privacy/receiver unlinkability and spending security both become fragile).
- Shor’s original result (factoring + discrete log in polynomial time): https://epubs.siam.org/doi/10.1137/S0097539795293172
- NIST framing on the PQ transition problem space: IR 8105, Report on Post-Quantum Cryptography | CSRC
- NIST status discussion (PQC standardization context): IR 8309, Status Report on the Second Round of the NIST Post-Quantum Cryptography Standardization Process | CSRC
What “PQ-ready” means here
“PQ-ready” does not mean “already PQ-secure today.” It means:
- We treat P2PKH/ECDSA-era flows as transitional.
- We progressively migrate value into outputs governed by policy (P2SH/P2S/P2SH32/covenant scripts), so that spending requires satisfying explicit rules.
- We design the system so the “paycode → one-time address” interface can swap out its underlying key agreement/signature assumptions without rewriting the whole wallet.
In other words: the UX layer (paycodes, scanning) can remain stable, while the authorization layer (what proves spend authority) transitions from ECDSA-era proofs to PQ signatures and/or ZK proofs enforced by covenant policy.
Why not abandon RPA immediately?
Because ZK doesn’t solve inbound payment UX by itself. In a UTXO chain, someone still needs a receiver surface that works without coordination. RPA (or an SRPA-like successor) is still the best “receiving interface” we have today on BCH while we build the covenant+ZK infrastructure.
So: RPA stays as the front end, while the state cell pool is the policy back end, and ZK becomes the confidentiality engine.
2) Tracking and discovery: solving it with scanning (no beacons)
The current Electron Cash RPA approach works because the wallet scans and indexes. That’s fine, but the goal here is:
- No protocol-specific beacons
- No special address prefixes/suffixes that scream “this is stealth”
- No OP_RETURN markers required for discovery
So we embrace the reality: discovery must be done via scanning, but we make it tractable and deterministic.
Scanning requirements
At minimum, the receiver needs:
- a scan key and a spend key (or a unified key in early implementations),
- a bounded scan window (wallet birthdate / start height),
- a method to identify candidate outputs and test them.
Practical scanning flow (receiver side)
A high-level “RPA output detector” looks like:
- Pull transactions (address-based index, mempool, or block range).
- For each tx, for each output:
- if it’s P2PKH, extract the 20-byte hash160.
- derive candidate one-time keys for plausible
(sender, outpoint, index)contexts. - compare computed child hash160s to output hash160s.
Complexity may be constrained by:
- using known input outpoints as anchors,
- using small indices (e.g. index = 0…k),
- using bounded scan windows.
The important point: the receiver can discover funds with no sender beacon.
3) Recording scans securely: the local sharded state cell pool
This is the part that’s easiest to misunderstand if you only look at “RPA = stealth addresses”.
What the sharded pool is buying us today
Right now, the pool does three things:
- Local state management for a wallet: what have I received, imported, spent, etc.
- On-chain enforceable policy for funds that I migrate into the pool: “this UTXO is governed by rules.”
- A path to deterministic restore that doesn’t require “scan the entire chain forever”.
This is why I keep describing it as “bringing a ZEC-style global pool local to the wallet.” Zcash’s shielded design is organized around value pools (transparent + shielded pools tracked by consensus), and the mental model of “funds live in a pool and spends update pool state” is proven in production (Zcash value pools reference).
The difference is architectural: I’m not proposing a new global consensus pool on BCH. I’m implementing a local-first pool anchored to wallet keys, with a migration path to broader coordination later if it’s ever needed.
Concretely: our “local pool” is not a consensus object and not a custodial system. It’s a wallet-managed set of ordinary BCH UTXOs that are locked under covenant policies. The only authority is the chain: if a transaction doesn’t satisfy the covenant’s on-chain rules and required signatures, it is invalid and will not be mined. The state file is just an index of on-chain facts — losing it can degrade UX, but it cannot move funds or weaken custody. In that sense, the pool is as trustless as any BCH wallet: the chain enforces validity, and keys enforce authorization.
And we should be explicit about the post-quantum direction: post-quantum vault contracts already exist on BCH Chipnet (Quantumroot), and they demonstrate the practical “policy-UTXO” pivot — move value toward script-locked outputs where spend rules can evolve (Quantumroot vaults on Chipnet). Quantumroot is also explicitly based on conservative stateful hash-based signatures (LM-OTS), which is exactly the class of primitive we expect to remain viable deep into PQ timelines (NIST SP 800-208: Stateful Hash-Based Signatures).
Why sharding matters
Without sharding, every update becomes a single shared state thread — a bottleneck and a coordination hotspot. With sharding:
- imports map to shards deterministically,
- updates touch only one shard at a time,
- the wallet scales operations without global contention.
This is very aligned with the fundamental advantage of UTXO systems: parallelism by default (many independent coins, many independent spends). Quantumroot explicitly calls this out in the context of BCH’s “highly parallel” UTXO architecture (Quantumroot vaults on Chipnet) — sharding is simply leaning into that same property for pool state management.
Deterministic restore goal
If the user loses the device/state file, they should be able to restore by:
- starting from wallet birth height,
- scanning only bounded windows,
- reconstructing pool state from on-chain shard history,
- re-deriving one-time keys deterministically.
This is still being proven out end-to-end, but the intent is clear: local state is recoverable from chain history + wallet secrets, rather than being a fragile “single JSON file or you’re toast” situation.
Pseudocode: pool state and deterministic shard selection
type PoolState = {
network: "chipnet" | "mainnet";
poolIdHex: string; // 20 bytes hex
categoryHex: string; // CashTokens category id
redeemScriptHex: string; // covenant redeem script bytecode
shardCount: number;
shards: Array<{
index: number;
txid: string;
vout: number;
valueSats: string;
commitmentHex: string; // state commitment (e.g. hash fold)
}>;
// records discovered or created by wallet:
stealthUtxos: Array<StealthUtxoRecord>;
deposits: Array<DepositRecord>;
// explicit signer binding (see section 5)
covenantSigner: {
pubkeyHash160Hex: string;
};
};
Deterministic shard index:
function shardSelection(args: {
depositOutpoint: { txid: string; vout: number };
receiverHash160Hex: string;
categoryHex: string;
shardCount: number;
}): number {
const preimage = concat(
hexToBytes(args.categoryHex),
hexToBytes(args.depositOutpoint.txid),
uint32le(args.depositOutpoint.vout),
hexToBytes(args.receiverHash160Hex),
);
const h = sha256(preimage);
return (bytesToUint32(h.slice(0, 4)) % args.shardCount) >>> 0;
}
The exact selector can vary; the point is: no hidden randomness, and the mapping is stable.
4) Application Binary Interface (ABI) shape: useful now (no ZK), ZK-ready later (proofBlob32)
The covenant work I’m doing is not “ZK only.” The covenant is a stable Application Binary Interface (ABI) for spending a specific class of UTXOs — a fixed, consensus-enforced contract for what goes on the unlocking stack, what the script computes, and what outputs must look like.
When I say “ABI” here, I mean:
- a canonical witness/stack layout (what data is provided when spending),
- a canonical validation routine (what the covenant checks),
- a canonical output commitment rule (what the next state must be),
- and a forward-compatible slot for future proof verification, without changing the transaction shapes later.
ABI concept (high level)
At a high level:
-
Inputs provide:
- the prior state commitment (read via introspection from the input token commitment),
- the unlocking witness, e.g.
limbs...,noteHash32, andproofBlob32
-
The script:
- computes
nextCommitmentdeterministically from the witness and current state, - asserts that output[0] carries a token commitment equal to the computed
nextCommitment, - verifies
proofBlob32is exactly 32 bytes
- computes
Why proofBlob32 is 32 bytes now
Because 32 bytes is an extremely useful “reserved lane”:
- it can be a hash pointer to a larger off-chain proof bundle,
- or a commitment to a transcript (Fiat–Shamir / IOP transcript commitments, etc.),
- or even a compact proof itself if the chosen scheme allows it.
In other words: the covenant doesn’t need to commit to which ZK system today, but it can still reserve a consensus-validated interface slot for proofs, so we don’t paint ourselves into an ABI corner later.
5) Phase 2: stealth identity transactions with RPA + sharded pool
Phase 2’s privacy target is identity stealth, not full amount hiding.
That means:
- external observers should not be able to trivially link sender identity to receiver identity,
- paycode recipients look like normal P2PKH outputs,
- wallet discovery is beaconless (scan-based),
- wallet can import discovered UTXOs into covenant-controlled shards.
Phase 2 flow (high-level)
Pool init (Bob):
- Bob initializes a local pool of N shards.
- Stores
poolStatelocally (and can reconstruct later from chain history).
Alice → Bob (deposit):
- Alice pays to Bob’s paycode-derived one-time P2PKH.
- Alice records a deposit record (optional but helpful).
Bob observes + imports:
- Bob scans, detects the one-time output.
- Derives the spend key (one-time priv).
- Imports it into a chosen shard via a covenant spend that updates shard commitment.
Bob withdraws:
- Bob spends from the shard to pay to Alice’s paycode-derived one-time address.
- Fees are handled with a transparent input (for now).
Locking the pool to its initialization key (attack surface reduction)
A priority requirement is:
the pool must only be mutable by the key(s) it was initialized with.
In practice, Phase 2 does this by:
- binding covenant authorization to
covenantSigner.pubkeyHash160Hex, - requiring spends to produce a signature under that key (or a policy threshold),
- enforcing output commitments match computed transitions.
This is the “policy UTXO” pivot that matters for the PQ roadmap: we’re moving value toward outputs whose spending rules can evolve.
6) Phase 3: full stealth identity + hidden amounts (ZK + nullifiers)
Phase 3 is where we move from placeholder commitments and “trust the off-chain logic” assumptions to cryptographic enforcement: spends carry proofs that the transition is valid, double spends are prevented via nullifiers, and value conservation (including range constraints) is proven rather than inferred.
What changes in Phase 3
- The pool becomes a true privacy pool (even if local-first).
- Spending requires:
- a proof of membership / authorization,
- a nullifier to prevent double spends,
- and a proof about amounts (range proof or balance conservation).
The covenant ABI already accepts proofBlob32, so the transaction shapes don’t need to be reinvented.
“Nullifier-ish” commitments
Right now we have placeholder commitment updates (hash folds, etc.). In Phase 3, the shard commitment becomes a structure that supports:
- membership (e.g., Merkle-style accumulator or incremental hash fold),
- a nullifier set (or compact nullifier commitment),
- balance conservation rules.
The exact cryptographic construction is not finalized in this post, but the state transition plug is.
Summary: what the state cell sharded pool is really for
It’s not “just tracking stealth change.”
It’s the bridge between:
- RPA as a practical UX layer today, and
- covenant policy + ZK proofs as the enforcement layer tomorrow.
You can view it as:
- a local-first privacy pool,
- an on-chain policy vault,
- a deterministic restore anchor,
- a stable ABI interface for future proofs.
Next steps
I’m using this thread as the foundation for:
- Finalizing the public CLI interfaces and removing demo hardcoding
- Implementing beaconless scan+record for RPA outputs
- Tightening pool authorization (init key ownership enforcement)
- Defining Phase 3 proof semantics for
proofBlob32and nullifiers
As always: the goal is to keep everything compatible with plain BCH transaction flows while moving aggressively toward PQ-ready, covenant-enforced, ZK-verifiable confidentiality.
If you have feedback on:
- the shard selection function,
- the restore model,
- the proof ABI design,
- or the PQ migration story,
please jump in — this is exactly the kind of iteration that needs expert review early.