Skip to content

Instantly share code, notes, and snippets.

@Steake
Last active November 22, 2025 20:37
Show Gist options
  • Select an option

  • Save Steake/5d857371756d1e708035bf592ef97e0c to your computer and use it in GitHub Desktop.

Select an option

Save Steake/5d857371756d1e708035bf592ef97e0c to your computer and use it in GitHub Desktop.

Cellular Automaton Tournament Blockchain

with Protocol-local EBSL and Zero-Knowledge Smart Contracts

Version: 1.1 Date: November 2025 Status: Architectural Specification (Buildable v0.1) (c) Oliver Hirst 2025


Executive Summary

This document specifies a blockchain where:

  • Consensus is achieved via deterministic tournaments between miners in a cellular automaton (CA) arena.
  • Miner eligibility and long-term behaviour are governed by protocol-local EBSL (Evidence-Based Subjective Logic) using only on-chain events.
  • Transactions and smart contracts are privacy-preserving, enforced by modular ZK-SNARK circuits.
  • The chain uses a longest-chain / heaviest-chain fork choice with deterministic work per block, not hash lottery PoW.

Core design choices:

  1. Tournament PoW: Each block height has a CA tournament. Eligible miners commit to glider patterns, then reveal and battle in a deterministic bracket. The winner proposes the block.

  2. Anti-cartel properties: Random pairings (derived from VRF-based per-epoch randomness), ring signatures, and pairwise battles make cartel coordination unstable and economically irrational.

  3. Protocol-local EBSL gating: Miners must have:

    • A bond ≥ B_MIN (economic Sybil barrier), and
    • A protocol-local EBSL trust score T ≥ T_MIN derived from their on-chain behaviour. Negative events hit fast, forgiveness is slow.
  4. Full verification mandatory: Every validator must verify all battle proofs and contract proofs before a block is valid. No sampling in the consensus rule.

  5. Optional sampling mode with explicit finality: Implementations may use sampling as a fast pre-check for gossip / local UX, with quantified detection probability and a recommended safe finality depth (e.g. 100+ blocks).

  6. Privacy-native smart contracts: A ZKVM runs private contract logic, with only commitments and proofs on-chain. State is never decrypted in consensus.

This is a deliberately L1-focussed, security-first design targeting high-value, privacy-sensitive use cases. Throughput is acceptable (Bitcoin-ish, ~100 TPS), but not the primary goal.


Table of Contents

  1. System Architecture
  2. Participants, Identity, and Protocol-local EBSL
  3. Consensus Mechanism and Fork Choice
  4. Tournament Protocol (Commit–Reveal, Randomness, Liveness)
  5. Cellular Automaton Engine
  6. Zero-Knowledge Proving Architecture
  7. Smart Contract Execution (ZKVM)
  8. Cryptographic Primitives
  9. Network Protocol
  10. Economic Model
  11. Light Clients
  12. Governance and Upgradability
  13. Security Analysis
  14. Performance Characteristics
  15. Implementation Roadmap

1. System Architecture

At a high level:

  • Consensus layer: CA tournament + heaviest-chain rule.
  • State layer: Privacy-preserving state commitments (balances, contract storage, nullifiers).
  • Execution layer: ZKVM for contract logic; ZK-SNARK proofs for execution validity.
  • Reputation layer: Protocol-local EBSL for miner trust and eligibility.
  • Network layer: P2P gossip, compact blocks, standard inventory/headers.

Rough stack:

Application Layer
  ├─ Wallets / dApps / DeFi protocols
  └─ Explorer & monitoring

Smart Contract Layer
  ├─ ZKVM (RISC-like instruction set)
  ├─ Private state commitments + nullifiers
  └─ Contract libraries / standards

Consensus Layer
  ├─ Protocol-local EBSL + bonded miner set
  ├─ CA tournament per block height
  └─ Heaviest-chain fork choice (sum of per-block tournament work)

CA Layer
  ├─ 2D grid, Conway-style rules (tuned for cryptographic determinism)
  └─ Glider patterns, collisions, battle outcomes

ZK Layer
  ├─ Battle verification circuit
  ├─ ZKVM execution circuit
  └─ State transition / Merkle / nullifier circuit

Network Layer
  ├─ P2P gossip
  ├─ Block / tx / proof propagation
  └─ Light-client support (headers + proofs)

2. Participants, Identity, and Protocol-local EBSL

2.1 Miner Identity

Each miner is identified by:

  • MinerID = hash(pubkey)
  • pubkey for ECDSA/BLS signatures
  • Associated bond account that locks tokens as a Sybil barrier

2.2 Bonding

To join the candidate miner set, an address must:

  • Lock at least B_MIN tokens into a bond contract.
  • Agree to slashing conditions (equivocation, invalid blocks, etc).

Bond information is stored on-chain and is part of the global state.

2.3 Protocol-local EBSL

We define per-miner evidence counters:

  • r_m = positive evidence (honest behaviour)
  • s_m = negative evidence (dishonest behaviour)

Evidence updates:

Positive:
  GOOD_BLOCK(m)            → r_m += 1.0
  HONEST_PARTICIPATION(m)  → r_m += 0.25

Negative:
  INVALID_BLOCK(m)         → s_m += 6.0
  INVALID_TOURNAMENT(m)    → s_m += 10.0
  PROOF_FAILURE(m)         → s_m += 12.0
  EQUIVOCATION(m)          → s_m += 20.0

Per epoch we apply slow decay:

r_m *= POS_DECAY    (e.g. 0.99)
s_m *= NEG_DECAY    (e.g. 0.999)

Mapping to subjective logic opinion:

  • R = r_m + s_m
  • K = 2 (binary: honest/dishonest)
b_m = r_m / (R + K)
d_m = s_m / (R + K)
u_m = K   / (R + K)
T_m = b_m + ALPHA · u_m   (trust score)

Typical parameters:

  • ALPHA = 0.4
  • T_MIN ≈ 0.75 eligibility threshold
  • T_KILL ≈ 0.2 “you’re basically done here”

2.4 Miner Eligibility

At epoch/block height h, the active miner set is:

def get_active_miners(state, epoch):
    candidates = [m for m in state.miners if m.bond >= B_MIN]

    active = []
    for m in candidates:
        T = compute_trust(m)  # as above
        if T >= T_MIN and recently_active(m, epoch):
            active.append(m)

    return active

recently_active enforces liveness (e.g. must have participated in some of the last L tournaments).

2.5 Hard Punitive Logic

For severe offences (especially equivocation), we add:

def handle_negative_event(m, event):
    apply_negative_weight(m, event)
    T = compute_trust(m)

    if event == EQUIVOCATION:
        slash(m.bond, 1.0)                 # full slash
        m.banned_until = epoch +# effectively permanent
    elif T < T_KILL:
        slash(m.bond, SLASH_RATIO)         # e.g. 0.5–1.0
        m.banned_until = epoch + BAN_EPOCHS

Protocol-local EBSL and bonds together give a fast-punish, slow-forgive regime with an absolute guillotine for certain attacks.


3. Consensus Mechanism and Fork Choice

3.1 Block Production Overview

Per block height h:

  1. Take snapshot of eligible miners M_h using EBSL + bond rules.
  2. Derive randomness R_h from prior blocks via VRF.
  3. Run tournament among M_h with commit–reveal gliders in CA.
  4. Winner w_h becomes block proposer.
  5. Proposer executes contracts, assembles block, generates all required proofs.
  6. Validators verify all proofs; if valid, block is appended.

3.2 Deterministic Work and Difficulty

Unlike Bitcoin, block generation is not a probabilistic hash lottery. Work per block is deterministic, governed by protocol parameters:

  • N_h = |M_h| active miners
  • RND_h = ceil(log2(N_h)) tournament rounds
  • BATTLE_STEPS = CA steps per battle
  • GRID_COST = fixed cost factor for grid size / update rule

We define block work:

work_h = (N_h - 1) * BATTLE_STEPS * GRID_COST

Every block at a given N_h has the same notional work. There’s no “difficulty adjustment” in the lottery sense; instead:

  • Chain weight is the sum of work_h along the chain.
  • Block time is set by tuning CA/ZK parameters during design.

3.3 Fork Choice

Nodes adopt the chain with highest cumulative work:

def chain_weight(chain):
    return sum(block.work for block in chain)

def select_best_chain(candidates):
    return max(candidates, key=chain_weight)

Since work_h is deterministic given the block contents (and the activity snapshot), this is objective and verifiable.


4. Tournament Protocol (Commit–Reveal, Randomness, Liveness)

This is where we fix the gaping liveness/fairness issues.

Each block height h has a tournament frame with phases:

  1. Eligibility snapshot
  2. Commit phase (glider commitments)
  3. Reveal + battle phase
  4. Block assembly

4.1 Randomness: VRF-Based Epoch Seed

We define a VRF output per block, vrf_h, derived by the previous block proposer:

  • vrf_h = VRF_sk(prev_proposer, message = prev_block_hash || h)

The VRF proof is in the header; verifiable by all.

For height h, the tournament seed seed_h is:

seed_h = H(vrf_{h-1} || vrf_{h-2} || ... || vrf_{h-k})

for some small k (e.g. 8–32). This means:

  • No single block proposer fully controls seed_h.
  • Grinding vrf to bias seed_h would require controlling multiple consecutive blocks.

4.2 Phase 1: Eligibility Snapshot

At the start of height h:

  • Nodes compute M_h = get_active_miners(state_{h-1}, epoch(h)).
  • This set is fixed for the tournament at h.

4.3 Phase 2: Commitment Phase

Each m ∈ M_h must broadcast a glider commitment within a commit window Δ_commit:

commit_m = H(glider_pattern_m || nonce_m)
ring_sig_m = RingSign(sk_m, ring=M_h_pubkeys, msg=commit_m || h)

Commitment object:

message GliderCommit {
  bytes ring_pubkeys_hash = 1; // hash of sorted pubkeys in M_h
  bytes commitment        = 2; // H(pattern || nonce)
  bytes ring_signature    = 3;
  uint64 height           = 4;
}

Rules:

  • If a miner fails to commit within Δ_commit, they are excluded from M_h for this height and receive a small negative evidence hit (HONESTNESS_MISS type).
  • Commitments are anonymous within the ring, but membership is provable.

4.4 Pairing

Once the commit phase closes:

def generate_pairings(commits, seed_h):
    # commits correspond 1-1 with M_h elements
    miners = [c.miner_id for c in commits]  # implicit mapping
    rng = PRNG(seed_h)
    shuffled = miners.copy()
    for i in range(len(shuffled) - 1, 0, -1):
        j = rng.randint(0, i)
        shuffled[i], shuffled[j] = shuffled[j], shuffled[i]
    pairs = [(shuffled[i], shuffled[i+1]) for i in range(0, len(shuffled)-1, 2)]
    # odd one out, if any, auto-advances
    return pairs

The pairing is fully determined by seed_h and the sorted list of committed miner IDs.

4.5 Phase 3: Reveal + Battles

Each committed miner reveals:

reveal_m = (glider_pattern_m, nonce_m)

The block proposer collects reveals, verifies they hash to commitments:

H(glider_pattern_m || nonce_m) == commit_m

For each battle pair (A, B):

  1. Place glider_A, glider_B in CA grid with fixed positions.

  2. Simulate BATTLE_STEPS iterations.

  3. Determine collision and winner by the CA outcome rules.

  4. Generate a battle SNARK proof that:

    • The CA evolution was correct.
    • Winner declared matches outcome.
    • Commitment consistency holds.

Non-revealing miners:

  • If a miner committed but fails to reveal, they forfeit that round and get a negative EBSL event.
  • The opponent auto-advances.
  • Repeated non-reveals can be escalated to slashing / ban via EBSL.

The tournament proceeds in log₂(N) rounds until a single winner remains.

4.6 Phase 4: Block Assembly

The final winner w_h:

  • Executes contract calls and state transitions locally.

  • Generates ZK proofs for:

    • all battles,
    • all contract executions,
    • state transitions.
  • Packages everything into block B_h.

All nodes independently recompute:

  • Eligibility M_h,
  • Expected commits count,
  • Pairings,
  • Bracket structure.

They verify:

  • Each battle proof corresponds to the right pair & commitments.
  • Tournament structure leads to the declared w_h.

If anything diverges from locally recomputed bracket: block invalid.

4.7 Liveness and Timeouts

Per-block timing budget is split roughly:

  • Δ_commit – commit window
  • Δ_reveal – reveal window
  • Δ_compute – CA + proving
  • Δ_propagate – propagation / validation

All of this is bounded by the target block time (e.g. 600 seconds).

Rules:

  • Non-commit → excluded for that height, mild negative EBSL.
  • Non-reveal → forfeit, stronger negative EBSL.
  • Serial non-participation → trust decays, eventually miner drops out of M_h.

5. Cellular Automaton Engine

Same as your original concept, but we formalise the parameters.

  • Grid: 1024 × 1024 toroidal grid.
  • Cell state: 8-bit (0–255).
  • Update rule: Conway-like, with energy.

Pseudocode (unchanged in spirit):

def evolve_cell(cell, neighbors):
    live_neighbors = count_live(neighbors)
    cell_energy = cell.state

    if cell.is_alive():
        if live_neighbors < 2 or live_neighbors > 3:
            return Cell(state=0)
        else:
            return Cell(state=cell_energy)
    else:
        if live_neighbors == 3:
            energy = sum(n.state for n in neighbors if n.is_alive()) // 3
            return Cell(state=energy)
        else:
            return Cell(state=0)

Battles:

  • Fixed spawn positions

  • BATTLE_STEPS iterations

  • Outcome based on:

    • Whether a recognisable glider pattern persists
    • Fallback to energy tie-break

This logic is embedded both:

  • in the execution of the CA engine, and
  • as arithmetic constraints inside the Battle SNARK circuit.

6. Zero-Knowledge Proving Architecture

We drop the “single mega-circuit” idea. Instead:

  1. Battle Circuit C_battle

    • Verifies: commitment consistency + CA evolution + declared winner.

    • Inputs:

      • Public: commitments, winner id, seed, positions.
      • Private: initial grid state, glider patterns, nonce.
  2. Execution Circuit C_exec

    • Verifies: ZKVM execution for a single contract invocation.

    • Inputs:

      • Public: old state root, new state root, contract address, function selector, gas used.
      • Private: witness (plaintext state, arguments, internal steps).
  3. State Transition Circuit C_state

    • Verifies: commitment/merkle updates, nullifier correctness.

    • Inputs:

      • Public: old global root, new global root, nullifiers, commitments.
      • Private: merkle paths, underlying cleartext.

Each block carries:

  • One C_battle proof per battle (N_h - 1 proofs).
  • One C_exec proof per executed contract call.
  • One or more C_state proofs for state root updates.

Future versions can add recursive/aggregated SNARKs to compress these; v0.1 keeps it simple but modular.

Consensus rule: A block is invalid unless every referenced proof verifies under the appropriate circuit.


7. Smart Contract Execution (ZKVM)

7.1 Execution Model

The ZKVM is a RISC-ish VM with:

  • 256-bit word size, field-friendly arithmetic.
  • Finite instruction set: arithmetic, logic, memory, control flow, crypto ops.

The critical point:

  • Only provers have access to plaintext state.
  • Validators never decrypt anything; they only verify proofs.

So the correct mental model is:

def private_state_transition(old_commitment, function, args, prover_secret):
    old_state = decrypt_with_user_key(old_commitment, prover_secret)    # OFF-CHAIN
    new_state = run_function(function, args, old_state)                 # OFF-CHAIN

    new_commitment = commit(new_state, random_nonce)                    # OFF-CHAIN
    proof_exec = prove_C_exec(old_state, new_state, function_code, args)
    proof_state = prove_C_state(old_commitment, new_commitment, ...)

    return (new_commitment, proof_exec, proof_state)

On-chain / in consensus:

verify_C_exec(proof_exec, public_inputs)
verify_C_state(proof_state, public_inputs)
update_global_state_root(new_root)

No decrypt_state() in consensus code, ever.


8. Cryptographic Primitives

Mostly as you wrote, with emphasis:

  • Hash: SHA-256 for general use, Poseidon/Rescue inside circuits if needed.

  • Commitments: Pedersen or similar EC commitments, circuit-friendly.

  • Merkle trees: binary, SHA-256 or Poseidon, depending on context.

  • Signatures:

    • ECDSA secp256k1 for normal signatures.
    • Linkable ring signatures for tournament anonymity.
    • Optional BLS for aggregate signatures.
  • ZK System: Groth16 initially, modular per circuit. Later: Plonk / STARK with aggregator.


9. Network Protocol

Standard Bitcoin-esque P2P:

  • inv, getdata, block, tx, plus:

    • glider_commit
    • glider_reveal
    • optional battle_proof / exec_proof gossip.

Compact blocks used for efficiency.

Nodes must:

  • Propagate candidate blocks.
  • Fully verify before marking as valid (consensus rule).
  • May use sampling as a local fast filter (see below).

10. Economic Model

10.1 Block Reward

Per block:

block_reward = base_subsidy(h) + tx_fees + contract_fees

Distribution:

  • 60% to w_h (tournament winner / proposer)
  • 30% to all participants, weighted by deepest round reached and optional trust factor
  • 10% to treasury / dev fund

10.2 Deterministic Payout

To avoid proposers cheating on rewards:

  • The payout schedule is deterministically computable from the tournament bracket and trust scores.
  • The coinbase transaction is checked as part of block validation:
def validate_coinbase(block, bracket, trust_scores):
    expected_outputs = compute_expected_payouts(bracket, trust_scores)
    return outputs_equal(block.coinbase.outputs, expected_outputs)

If coinbase doesn’t match the deterministic formula: block invalid.

No “winner discretion” on paying other participants.

10.3 Gas and Fees

Same structure as before:

  • Base fee + tip.
  • Privacy multiplier + ZK overhead baked into gas costs.
  • Optionally burn part of base fee.

11. Light Clients

Light clients:

  • Track only headers + chain weight.

  • Verify:

    • header structure,
    • VRF proofs,
    • work_h consistency,
    • cumulative chain weight.

For transaction inclusion / correctness:

  • They request:

    • Merkle inclusion proof for the tx / contract call.
    • Corresponding battle/exec/state proofs from full nodes.

They do not re-simulate CA or recompute tournaments; they trust the heaviest header chain and verify individual proofs on demand.


12. Governance and Upgradability

We separate:

  1. Hard consensus rules:

    • CA rule definition.
    • Tournament protocol (phases, randomness, timeouts).
    • ZK circuits’ semantics (though they can be versioned).
    • Fork-choice rule.

    Changes to these require hard forks and explicit client upgrades.

  2. Soft parameters:

    • EBSL weights / decay rates.
    • Reward split percentages.
    • Gas pricing.
    • Treasury allocation.

    Governed via on-chain voting, with parameter changes scheduled far in advance (activation_height = current_height + N).

For CA rule updates (e.g. if an exploit is found), a special governance process should be defined (e.g. higher quorum, longer delay, signalling via signed messages and on-chain votes).


13. Security Analysis (Adjusted for New Design)

Key points with your fixes integrated:

  1. Randomness / grinding:

    • Tournament seed depends on multiple prior VRF outputs → grinding requires capturing several consecutive blocks, not just one.
  2. Cartel resistance:

    • Commit–reveal + ring signatures + random pairings still give the anti-cartel game dynamics we discussed.
    • Protocol-local EBSL further suppresses chronic misbehaviour.
  3. Proof soundness:

    • Full verification is mandatory: every battle & contract proof is checked before block is valid.
    • This removes sampling-based consensus risk.
  4. Optional sampling mode & finality depth:

Implementations can optionally:

  • Sample K battles/contracts as a pre-filter before spending CPU on full verification.

If an attacker corrupts at least a fraction f of proofs in a block:

  • Probability a sample of size K misses all bad proofs:

[ P_\text{miss} = (1 - f)^K ]

Example:

  • If f ≥ 0.1 (≥10% proofs are bogus) and K=60:

[ P_\text{miss} = 0.9^{60} \approx 0.0015 \ (\sim 0.15%) ]

Crank K up to 100:

[ P_\text{miss} = 0.9^{100} \approx 2.7 \times 10^{-5} ]

So you can tune a client to “almost never relay invalid garbage” before investing in full verification.

But consensus acceptance still requires full check; sampling is a performance hack, not a rule.

For external users (bridges, exchanges), we can recommend a safe finality depth:

  • E.g. require 100+ confirmations before treating a transaction as economically final.

  • This covers:

    • normal reorg risk,
    • plus the worst-case where some nodes are slow to validate and a malicious chain gets pruned later.
  1. Re-org behaviour:

    • Since full verification is required before a block is considered valid, deep reorgs due to late verification failures are avoided at the consensus level.
    • Any node that cuts corners (sampling-only) is outside the spec and accepts the risk.

14. Performance Characteristics (With Full Verification)

You already did the back-of-envelope; the big change is:

  • Full verification is on the critical path.

Target regime:

  • CA steps / circuit design chosen such that:

    • Battle proof verification: ~5–10ms
    • Exec/state proof verification: ~5–10ms
  • For N_h ~ 1000 miners:

    • N_h - 1 ≈ 999 battle proofs → ~10 seconds of verification on a modest machine.
  • Contract proofs: smaller, optional.

So a realistic full validation time per block:

  • 10–15 seconds on commodity hardware.

This is still fine with a 10-minute block time, and you can tune N_h, BATTLE_STEPS, and circuit constraints to keep it there.


15. Implementation Roadmap (Updated)

Phase 1 now explicitly includes:

  • Protocol-local EBSL module.
  • Tournament phases with commit–reveal.
  • VRF-based randomness.

Phase 2:

  • Modular circuits (C_battle, C_exec, C_state) instead of god-circuit.

Phase 3:

  • Full verification path optimised; optional sampling path as a client setting, not consensus.

Phase 4:

  • Security audits must specifically check:

    • tournament orchestration,
    • VRF randomness,
    • deterministic payout checking,
    • EBSL slashing integration.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment