Consensus & proof of work
CoinCync’s consensus layer is proof of work — single algorithm, memory-hard, CPU-biased, ASIC-resistant. Article V of the Constitution commits to RandomX and only RandomX. No rotation, no fallback, no hybrid, no PoS transition path. Single knob, thoroughly studied, verifier and miner call the same function.
RandomX
RandomX is a CPU-friendly, ASIC-resistant, memory-hard proof-of-work algorithm originally designed for Monero and in production there since November 2019. It uses a 256 MB scratchpad and a virtual machine that interprets randomly-generated bytecode — both of which favor general-purpose CPUs over fixed-function hardware. An ASIC for RandomX would essentially be a CPU, which offers no economic advantage over commodity consumer hardware. This is the longest-running, most-battle-tested CPU-biased PoW design in production on any live chain.
CoinCync wraps the upstream randomx-rs crate. The implementation lives in src/consensus/pow.rs. The randomx Cargo feature is on by default — building without it makes compute_pow_hash return an error at runtime with "RandomX feature not enabled". This is intentional: there is no non-RandomX fallback, so an accidental misbuild produces a binary that refuses to mine, not one that silently uses a weaker hash.
PowAlgorithm in src/consensus/pow.rs is a single-variant enum. The enum exists (rather than being removed entirely) so that any code that matches on algorithm kind continues to compile cleanly, and so any future attempt to add a second variant immediately trips code review. Adding a second PoW algorithm is a constitutional violation (Article V) and would also require refreshing critical_files.lock, so the change cannot happen silently.
Why RandomX-only and not multi-algo rotation
This is worth explaining because it’s the opposite of the “diversify across algorithms” intuition a lot of people start with:
- A rotation is as ASIC-resistant as its weakest algorithm, not its strongest. Any rotation that includes a fast algorithm like Blake3 is trivially ASIC-mineable every N blocks. The weakest algorithm defines the upper bound on an adversary’s ability to concentrate hashrate.
- Multi-algo expands attack surface. Per-algorithm difficulty tracking, cross-algorithm reorg rules, algorithm-selection enforcement — every one adds lines to the verifier, and every line is a potential bug. Chains that tried multi-algo rotation (Verge, Bitcoin Gold) have a documented history of consensus-level exploits that cleaner single-algo designs did not have.
- RandomX alone has a five-year production record as of 2024 on Monero. Zero consensus exploits attributable to algorithm choice. Zero hashrate concentration events attributable to algorithm choice. That’s an outcome you cannot replicate by inventing a new rotation.
See the Constitution Article V rationale for the full argument. It is load-bearing and worth reading.
The PoW preimage
input = anchor || nonce || tx_root
height = (block height — used for RandomX VM key derivation)
output = RandomX(input, height) // 256-bit hash
A block satisfies the difficulty target if output ≤ target, where target is the per-block difficulty target stored in header.target.
The anchor is a deterministic chain-history value computed by compute_full_anchor(prev_hash, height, timestamp). It binds every PoW solution to its specific chain context — you cannot pre-compute solutions for a hypothetical future chain, and a solution computed for block h is invalid at block h+1 because the anchor differs.
The RandomX VM key derives from the block’s seed epoch. CoinCync rotates the VM seed every RANDOMX_KEY_EPOCH = 64 blocks, which is ~2 hours at 120-second blocks. This matches Monero’s epoch cadence and amortizes the ~1-second VM-key setup cost across ~60 blocks of hashing.
Implementation:
src/consensus/pow.rs::compute_pow_hash— what the miner callssrc/consensus/pow.rs::verify_pow— what the verifier calls insidevalidate_block- They are the same function. The miner does not have a “fast path” or a “loose mode.” If the miner produces a hash, the verifier produces the same bytes.
Difficulty adjustment
CoinCync uses LWMA (Linearly Weighted Moving Average) difficulty adjustment, the same algorithm Monero adopted in 2018. LWMA is responsive to short-term hashrate changes (better than Bitcoin’s 2-week retarget), resistant to time-warp attacks, and produces stable block intervals.
The target block time is fixed at 120 seconds (TARGET_BLOCK_TIME in src/constants.rs). 2-minute blocks align with the mountain emission curve and give LWMA enough samples to damp hashrate volatility without overcorrecting. The window over which LWMA averages is 60 blocks (~2 hours, one full VM-key epoch).
Emergency difficulty adjustment triggers if block production stalls beyond EMERGENCY_DIFFICULTY_BLOCKS * EMERGENCY_TIME_MULTIPLIER * TARGET_BLOCK_TIME — this is the anti-wedge rule that prevents a 95% hashrate drop from leaving the chain stuck producing one block per day until the next LWMA window completes.
Implementation: src/consensus/difficulty.rs::calculate_difficulty. Called on every block accepted into the chain.
Header structure
#![allow(unused)]
fn main() {
pub struct BlockHeader {
pub network_magic: [u8; 4], // testnet/mainnet/regtest discriminator
pub version: u8, // protocol version
pub height: u64,
pub timestamp: u64, // unix seconds
pub prev_hash: Hash, // parent block
pub tx_root: Hash, // merkle root of transactions
pub anchor: Hash, // pow input anchor
pub algorithm: u8, // always 0 (RandomX) — Article V
pub nonce: u64,
pub target: Hash, // difficulty target this block met
pub miner_pubkey: PublicKey, // stealth address that gets the reward
pub supply_commitment: [u8; 32], // running supply commitment (Article IV)
pub checkpoint_vote: Option<(u64, Hash)>,
pub spark_set_root: [u8; 32], // Phase 2 — Lelantus Spark accumulator root
pub mw_kernel_root: [u8; 32], // Phase 2 — MimbleWimble kernel set root
}
}
Every field is included in the header hash preimage. BlockHeader::hash() prefixes the preimage with the domain-separation tag coincync/header/v1 so the header hash can never collide with the transaction signing hash, the PoW input, or any other hash in the protocol. See Privacy model → Hash domain separation.
The algorithm field is always 0 (RandomX). It is kept in the header for format stability — if a future constitutional amendment ever justified adding a second algorithm, the header schema would not need to change. Validation rejects any non-zero value.
The Phase 2 roots (spark_set_root, mw_kernel_root) are hashed unconditionally — zero bytes pre-activation, real accumulator roots post-activation. This means the upgrade is a soft fork rather than a hard fork: pre-activation blocks already commit to the (zero) roots, so there’s no header schema change at activation time, just a new validation rule that starts checking the field is non-zero and well-formed.
Current live status: public testnet is still pre-activation, so both roots are expected to remain zero in valid blocks.
The supply_commitment field is what lets Article IV (Auditable Integrity) work. Each block commits to the running Pedersen-accumulator supply, so any node can verify the total in-circulation CYNC matches the emission schedule without seeing individual transaction amounts.
Validation rules
src/consensus/validation.rs::validate_block applies these checks in order:
- Network magic — first check, before any crypto. Rejects testnet blocks reaching a mainnet node and vice versa. The expected magic is selected at compile time via the
testnet/mainnetCargo feature. - Header sanity — version, height monotonic, prev_hash matches parent, timestamp monotonic, timestamp not far in the future (with
MAX_TIMESTAMP_DRIFT = 600seconds, except for height 0 — see below). - Algorithm slot —
header.algorithm == 0. Any other value is an instant reject — Article V permits RandomX only. - PoW — RandomX hash must meet
header.target. Genesis (height 0) is exempt because the genesis block is a hardcoded constant, not mined. - Difficulty target —
header.targetmatches what LWMA computes for this height. - Mandatory privacy —
enforce_privacy_policychecks every non-coinbase tx has Pedersen-committed amounts, a stealth address, and at least one privacy-preserving input. Structural fast-fail before the expensive crypto verification at stage 8. - Coinbase — first transaction must be a
Coinbasetype, must have zero inputs, must pay the correct mountain-curve reward toheader.miner_pubkey. - Per-transaction validation — every non-coinbase tx is run through
validate_transaction, which calls into the same crypto verifiers the mempool uses (verify_ring_signature,verify_output_range_proofs,verify_balance_proof). - No double-spend — every key image in the block must be unique within the block AND not already spent in chain history.
- Supply commitment —
header.supply_commitmentmatches the running Pedersen accumulator after applying this block’s coinbase + fee-burn deltas. This is what enforces Article I (Fixed Supply) — a block that claims to mint more than the emission schedule allows fails the accumulator check. - Block size cap — total serialized size ≤
MAX_BLOCK_SIZE(2 MB).
Failing any check returns BlockValidation { valid: false, errors: [...] } and the block is rejected.
Genesis timestamp exemption
Mainnet genesis has a hardcoded timestamp of 1790812800 (October 1, 2026 00:00:00 UTC). If you run a mainnet node before that date, the genesis block’s timestamp is in the future by wall-clock time. Without an exemption, validate_header would reject the genesis block as “too far in future.”
The fix in validate_header:
#![allow(unused)]
fn main() {
if header.height > 0 && header.timestamp > current_time + MAX_TIMESTAMP_DRIFT {
result.add_error("Block timestamp too far in future");
}
}
The header.height > 0 guard exempts genesis (which is hardcoded and trusted by hash-match in init_genesis, not by timestamp validation). Non-genesis blocks are still bounded by MAX_TIMESTAMP_DRIFT.
Finality
CoinCync uses probabilistic finality, consistent with Constitution Article V’s commitment to pure PoW (no PoS finality gadget). Rough exchange-grade confirmation depths with 120-second blocks:
- 1 confirmation (~2 min): tip block
- 6 confirmations (~12 min): standard “safe” depth for low-value txs
- 12 confirmations (~24 min): standard “deep” depth for high-value txs
- 180 confirmations (~6 h): considered finalized for exchange-grade workflows
There’s no Casper-style finality gadget. There’s no checkpoint enforcement except at the consensus level for hard-coded historic checkpoints (which exist primarily to defend against long-range attacks during initial sync).
Reorgs
The chain follows the heaviest chain rule: the chain with the most accumulated work wins. If the node sees a competing fork that has more total work than its current tip, it reorganizes:
- Roll back blocks on the old tip until the fork point
- Apply blocks from the new chain
- Move orphaned txs back to the mempool
- Fire
ChainEventType::Reorgfor the explorer’s convergence timeline
Implementation: src/chain.rs. The reorg path is the source of most chain-storage complexity.
What’s not in the consensus layer
Things that exist in other privacy coins but are deliberately out of scope for CoinCync:
- No PoS, no hybrid PoS/PoW, no finality gadget. Article V permanently prohibits any transition to Proof of Stake. Stake-weighted consensus reintroduces the “already-wealthy get richer” problem the chain was built to reject.
- No multi-algorithm rotation. Single-algorithm PoW (RandomX). See the “Why RandomX-only” section above.
- No GPU-friendly fallback. RandomX is CPU-optimized by design. GPUs can run it but are uneconomic compared to CPUs at typical power costs.
- No merged mining with other coins. Each block must commit to exactly one chain. Merged mining adds attack surface for marginal hashrate gain.
- No checkpoint server. The consensus rules are self-sufficient; new nodes verify from genesis.
- No instant finality. Probabilistic PoW finality model, same as Bitcoin and Monero.
Next reading
- Constitution Article V — the full RandomX-only rationale
- Emission curve — the mountain curve, tail emission, 250M cap
- Transaction format — wire layout and signing hash construction
- Privacy model — what the cryptographic primitives actually protect