Skip to content

Architecture

Anvil256 — Architecture

Three components. Zero servers. No coordinator.

┌──────────────────────┐ ┌───────────────────────────┐ ┌──────────────────────────────┐
│ Static website │ │ CLI (Rust) │ │ Anvil256.sol │
│ Astro Starlight │ │ γ-counter read │ │ Base L2 │
│ IPFS + Cloudflare │ │ epoch_entropy fetch │ │ immutable ERC-20 + PoW │
│ no backend │ │ nonce search dispatch │ │ PI + NCT controller │
│ │ │ tx construction + submit │ │ Cascade verifier │
└──────────▲───────────┘ └────────────▲──────────────┘ └──────────────▲───────────────┘
│ │ │
│ browser, viem read-only │ operator RPC (HTTPS, EIP-1559) │ same RPC
└─────────────────────────────┴───────────────────────────────────┘
no Anvil256 infrastructure in the loop

On-Chain / Off-Chain Split

What Lives On-Chain

ComponentStateFormal Invariant
ERC-20balanceOf, totalSupply, transfertotalSupply <= 21,000,000e18 (I1)
Mining stateepoch, difficulty D[n]D[n], currentReward(), integralErrorWad I[n]I[n], lastMineBlock, epochEntropy ϵ[n]\epsilon[n], period start timestampD[n]1D[n] \geq 1 (I5); $
Miner identitygamma(m) = minerEpochCount[m] — monotonic per-address counterγ\gamma strictly non-decreasing (I13)
NCT windowminerWindow[256] packed circular buffer, windowHead, uniqueCount CuC_u, windowFreq[m]Cu[1,256]C_u \in [1,256] (I11)
Cascade verifiermine(ν) recomputes τ,ι,κ\tau,\iota,\kappa on-chain; asserts κ<D[n]\kappa < D[n]I12
DistributionValid mine(): mint R(n)R(n) to msg.sender, mint up to 0.1R(n)0.1R(n) to POL reserve, increment γ(m)\gamma(m), update NCT windowSseed+(Rpaid+MLP,paid)SmaxS_{seed}+\sum(R_{paid}+M_{LP,paid})\leq S_{max}
FeeSplit currentFeeWei(): 50% to feeRecipient, 50% to lpReserveEthWei; failed dev transfer parks in stuckFeesWeiI7: dev + stuck + lpReserve = fee exactly
AdjustmentEvery 2,0162{,}016 epochs: PIController.step(...) updates (D[n+1],I[n+1])(D[n+1], I[n+1]) before entropy is written
Entropy`epochEntropy = H(entropySource
HalvingR(n)=R0n/210,000R(n) = R_0 \gg \lfloor n/210{,}000\rfloor, clamped to 0 at epoch 13,440,000\geq 13{,}440{,}000

What Lives Off-Chain

ComponentDescriptionMathematical Role
γ\gamma readCLI reads minerEpochCount[m]\texttt{minerEpochCount}[m] before each job dispatch — one eth_call per epochγ(m)\gamma(m) required to compute τ(m,n)=H(mγg)\tau(m,n) = H(m \,\|\, \gamma \,\|\, g)
ϵ[n]\epsilon[n] fetchCLI reads epochEntropy from contract — mandatory round-trip, cannot be skippedϵ[n]\epsilon[n] is the outer-pass input; withholding it makes nonce computation impossible (Theorem 4.1, SECURITY.md). EntropyFallback event indicates prevrandao-only path was used (PATCH-1).
Nonce searchCUDA/OpenCL kernel iterates: find ν\nu s.t. κ(m,ν,n)<D[n]\kappa(m,\nu,n) < D[n]Brute-force search over ν{0,,2321}\nu \in \{0,\ldots,2^{32}-1\}; expected iterations =2256/D[n]= 2^{256}/D[n]
Tx constructionCLI reads currentFeeWei(), attaches as msg.value, signs EIP-1559 txEnsures I7 is satisfiable
Stats siteBrowser reads on-chain state via viem — no server, no cache

Cascade PoW — Off-Chain Computation

The mining kernel receives a pre-computed job from the CLI:

JOB <inner_hex64> <epoch_entropy_hex64> <difficulty_hex64> <job_id>

where inner_hex64 =ι(m,n)=H(τ(m,n)n)= \iota(m,n) = H(\tau(m,n) \,\|\, n) is precomputed by the CLI (requiring both γ(m)\gamma(m) from chain and the current epoch nn).

Kernel computation. For each ν\nu in the search space:

mid  =  H(ινbe32),result  =  H(midϵ[n])\text{mid} \;=\; H(\iota \,\|\, \nu_{\text{be32}}), \qquad \text{result} \;=\; H(\text{mid} \,\|\, \epsilon[n])

If result<D[n]\text{result} < D[n]: FOUND. Expected iterations before first success:

E[iterations]  =  2256D[n]\mathbb{E}[\text{iterations}] \;=\; \frac{2^{256}}{D[n]}

ϵ[n]\epsilon[n] is held in GPU constant memory for the duration of the epoch — no per-batch network call needed. The mandatory network round-trips (for γ\gamma and ϵ[n]\epsilon[n]) occur once per epoch transition, not once per batch.

Security note. The kernel never receives the raw miner address mm or the private key — only the derived ι\iota value. Compromise of the kernel process exposes only ι\iota (a one-way derivative of mm, γ\gamma, gg), not the signer key.


CLI ↔ Kernel Protocol

host → kernel (line-oriented, plain text):
JOB <inner_hex64> <epoch_entropy_hex64> <difficulty_hex64> <job_id>
STOP
EXIT
kernel → host (JSON, one object per line):
{"type":"ready", "devices":[...]}
{"type":"progress", "job":J, "device":"…", "hashes":H, "hashrate":R, "elapsed_ms":T}
{"type":"found", "job":J, "nonce":"<decimal>", "hash":"0x…"}
{"type":"error", "message":"…"}

The CLI precomputes ι\iota (requiring γ\gamma from chain and miner address) before dispatching the job. The kernel never sees the raw miner address — it only operates on the derived ι\iota value, ensuring:

ι  =  H(H(mγ(m)g)n)\iota \;=\; H(H(m \,\|\, \gamma(m) \,\|\, g) \,\|\, n)

is a one-way commitment to the miner’s identity, verifiable on-chain without exposing mm to the kernel process.


On-Chain State Machine

┌─────────────────────┐
│ epoch n, D[n] │ ◄──── mine(ν) called
└──────────┬──────────┘
κ(m,ν,n) < D[n]?
┌────────┴────────┐
no yes
│ │
revert ┌────▼───────────────────────────────────┐
│ γ(m)++ │
│ win[h] ← m; h ← (h+1) mod 256 │
│ uniqueCount ← updated C_u │
│ mint R(n) to msg.sender │
│ mint 0.1R(n) to POL reserve if cap allows │
│ split currentFeeWei(): dev + LP reserve │
│ │
│ if (currentEpoch % 2016 == 0): │
│ e[n] ← timing error │
│ I[n] ← sat(I[n−1]+e, ±I_MAX) │
│ u_nct ← |nctSignal()| (≥ 0) │
│ u ← sat(u_pi + u_nct, ±0.5) │
│ D[n+1] ← sat(D[n]·exp4(u), D/4,4D) │
│ │
│ ε[n+1] ← H(entropySource ∥ D_cur) │
│ entropySource=H(blockhash ∥ prevrandao ∥ ts) │
│ (PATCH-1 fallback: H(prevrandao ∥ ts) if bh=0) │
│ (D_cur = D[n+1] at boundary, else D[n])│
└─────────────────────────────────────────┘
epoch n+1 begins

Operator Trust Model

Formal trust assumptions. The protocol requires no trust in any party except the operator’s own infrastructure:

Trust RequiredRationale
Own machineKernel runs locally; key signs locally; no remote attestation
Own RPC endpointMalicious RPC can censor but cannot steal: CLI verifies eth_getTransactionReceipt
Chainlink oracleCompromise \Rightarrow fee spike only; client-side MAX_FEE_USD guard

Formal trust exclusions. These parties have zero protocol-level authority:

PartyWhy No Trust Needed
Anvil256 maintainersContract is immutable and verified; \nexists admin key
Pool operatorsNo pool protocol exists or is necessary
Centralised gatewaysNone in the architecture
Other minersNo coordination primitive; each mine() is fully independent
Post-deployment deployerConstructor ensures feeRecipientdeployer\texttt{feeRecipient} \neq \texttt{deployer} (I3); \nexists privileged function post-deploy

The operator’s private key is the unique secret in the system. All other values — γ(m)\gamma(m), ϵ[n]\epsilon[n], D[n]D[n], ι(m,n)\iota(m,n) — are either public on-chain state or deterministic functions thereof.