cardano-bean logo cardano-bean zw3rk · Java 25
ZW3RK · JAVA 25 · SPEC-CLEAN-ROOM · APACHE-2.0

cardano-bean

zw3rk's Cardano full node implementation in Java, built with LLM tools without ever looking at its source code.

NODE DIVERSITY CONFORMANCE SUITE

99.74% overall across 20,536 conformance vectors & benchmarks

Measured on pragma-org/node-diversity-conformance across all four functional and non-functional scenarios. Only 7 rows out of 20,536 do not pass — and every single one is a documented dataset or harness vocabulary artifact where Bean matches upstream Haskell cardano-ledger / plutus semantics.

CBOR DECODING
99.98%

9,198 / 9,200 Conway CDDL samples decoded, strictly re-encoded, and verified against reference bytes.

↓ cbor-decoding.json
LEDGER RULES
99.28%

275 / 277 raw-CBOR transaction fixtures across Phase-1 and Phase-2 validation rules.

↓ ledger-rules.json
UPLC CONFORMANCE
99.72%

1,056 / 1,059 evaluation vectors and 752 / 753 exact CPU/memory execution budgets.

↓ uplc-conformance.json
UPLC TURBO
100.00%

10,000 / 10,000 mainnet scripts within the 1.5× CPU & memory ceiling on both bases.

↓ .json.zst ↓ .json
Implementation Language CBOR Decoding (9,200) Ledger Rules (277) UPLC Conformance (1,059) UPLC Turbo (10,000) Overall (Mean)
Bean (cardano-bean) Java 25 99.98% (9,198) 99.28% (275) 99.72% (1,056) 100.00% (10,000) 99.74%
Amaru (suite preview / live) Rust 96.46% / 93.46% 97.11% (269) / — 99.91% (1,058) / — 83.39% (8,339) / — 94.21% / 23.36%
Dingo (live site) Go 98.35% (9,048) 98.92% (274) 98.77% (1,046) 2.61% (261 / 500 run) 74.66%
Cardano node (reference) Haskell 100.00% (9,200) 41.16% (114)* 100.00% (1,059) 53.60% (5,360) 73.69%
UPLC TURBO BENCHMARK QUANTILES (10,000 SCRIPTS × 50 RUNS)
10,000 / 10,000 on both production as-shipped and current-thread bases

A benchmark passes uplc-turbo only when (cpu_us + flat_us) ≤ 1.5 × budget_cpu_us (or within 100 μs), mem_bytes ≤ 1.5 × budget_mem, and the consumed CPU/memory budgets agree with the cross-node majority vote. Bean achieves 0 CPU failures, 0 memory failures, and 0 budget outliers across all 10,000 scripts:

Bean (as-shipped, production)
Pass: 10,000 / 10,000 (100%)
CPU ratio p50: 0.878× · p99: 1.086× · max: 1.281×
Mem ratio p50: 0.250× · p99: 0.295× · max: 0.395×
Flat decode median: 42.2 µs · CEK median: 328.9 µs
Amaru (Rust preview)
Pass: 8,339 / 10,000 (83.39%)
CPU ratio p50: 0.804× · p99: 1.086× · max: 1.376×
Mem ratio p50: 1.349× · p90: 1.505× · max: 1.522×
Flat decode median: 75.0 µs · CEK median: 242.0 µs
Cardano node (Haskell)
Pass: 5,360 / 10,000 (53.60%)
CPU ratio p50: 1.235× · p75: 2.131× · max: 3.564×
Mem ratio p50: 0.162× · p99: 0.759× · max: 0.822×
Flat decode median: 345.4 µs · CEK median: 218.5 µs
HONEST CONFORMANCE ACCOUNTING

The 7 non-passing cases out of 20,536 — explained simply

Why doesn't Bean score 100.00% everywhere? Because in 7 edge cases out of 20,536, the test vector's expected label differs from what a real Cardano node actually does on mainnet — and we refuse to hardcode test IDs or weaken production rules just to turn a badge green.

Cases 1 & 2 · Legacy Auxiliary Data in an Indefinite-Length CBOR Array
cbor-decoding (2 of 9,200)

Cardano has two CBOR decoders in its Haskell codebase: the strict wire decoder used by real nodes on the network, and a looser test-library decoder. For legacy [metadata, scripts] pairs encoded as an indefinite-length CBOR array (0x9f ... 0xff), the real node's wire decoder accidentally checks for the closing 0xff byte before reading the elements — meaning any non-empty indefinite array is rejected on mainnet. The test dataset was generated using the looser test-library decoder.

1. Concrete Example
A legacy 2-element auxiliary_data array encoded with indefinite length:
9f {..metadata..} [..scripts..] ff
2. Test Suite Expects
ACCEPT — because the dataset generator ran the non-wire test helper, which reads the elements before the closing ff.
3. Bean & Mainnet Do
REJECT — both Bean and the production Haskell node require 9fff (empty) when indefinite encoding is used in Conway.
Upstream source references & sample IDs

Samples: auxiliary_data/valid/00003-a457defa17a4ecca and auxiliary_data/valid/00025-756519256dadb3b0. Upstream production wire decoder: Alonzo/TxAuxData.hs:218-251 and Coders.hs:482 (RecD reads the break byte before the fields below decoder version 12); dataset generator: app/LedgerRules.hs:99 (uses non-Annotator DecCBOR).

Case 3 · Transaction Validity Interval Far in the Future (Past the Forecast Horizon)
ledger-rules (Fixture 00088)

When a Plutus transaction sets its upper validity bound to slot 500,000 (beyond the known epoch-forecast horizon of 432,000), the node cannot convert that future slot into a wall-clock POSIX time for the Plutus script context. Both Bean and the Haskell node reject the transaction at the exact same step — they just call the error by its real ledger name (BadTranslation), whereas the test harness rewrites that error message to OutsideForecast.

1. Concrete Example
Current slot is 0, forecast horizon ends at slot 432,000, and a PlutusV3 transaction sets ttl = 500,000.
2. Test Suite Expects
Reject with OutsideForecast — because the reference test runner intercepts "TimeTranslationPastHorizon" in the output string and renames it.
3. Bean & Mainnet Do
Reject with BadTranslation — Phase-1 linearly extends the slot, and Phase-2 fails building TxInfo with BadTranslation(TimeTranslationPastHorizon).
Upstream source references & fixture ID

Fixture: 00088. In cardano-ledger (Alonzo/Rules/Utxo.hs:379-397), Phase-1 validateOutsideForecast wraps EpochInfo in unsafeLinearExtendEpochInfo (Cardano/Slotting/EpochInfo/Extend.hs:54-66), so OutsideForecast never fires in Conway. Instead, Babbage/Rules/Utxos.hs:179-193 raises CollectErrors [BadTranslation (TimeTranslationPastHorizon ...)], which the test harness (Run.hs:443-445) string-matches and renames to OutsideForecast.

Case 4 · Output Paying to a Legacy Byron Address with Huge Attributes
ledger-rules (Fixture 00280)

Cardano has two separate size rules for transaction outputs: one checks whether the token value exceeds 5,000 bytes (OutputTooBigUTxO), and another checks whether a legacy Byron address's attributes exceed 64 bytes (BootstrapAddrAttrsTooBig). This test sends just 2 ADA (5 bytes) to a Byron address with 99 bytes of attributes. Bean and the Haskell ledger report the specific Byron attribute error, while the test fixture expects the generic output-size error name.

1. Concrete Example
An output carrying 2 ADA (5 bytes CBOR) to a legacy Byron bootstrap address with 99 bytes of address attributes (limit is 64 bytes).
2. Test Suite Expects
Reject with OutputTooBigUTxO — because the suite's error vocabulary folds Byron attribute size into the general output-size bucket.
3. Bean & Mainnet Do
Reject with BootstrapAddrAttrsTooBig — the specific Shelley/Conway rule for Byron address attributes over 64 bytes.
Upstream source references & fixture ID

Fixture: 00280. Upstream Alonzo.validateOutputTooBigUTxO (Alonzo/Rules/Utxo.hs:425-444) checks serSize (getValue txout) > maxValSize (5000) and does not fire on a 5-byte coin. Upstream Shelley.validateOutputBootAddrAttrsTooBig (Shelley/Rules/Utxo.hs:551-563) checks bootstrapAddressAttrsSize addr > 64 and fires OutputBootAddrAttrsTooBig (Conway UtxoPredicateFailure Sum 10).

Cases 5 & 6 · Calling dropList with a Count of ±264
uplc-conformance (2 of 1,059)

In Plutus, the CPU cost of dropList(n, list) is proportional to |n|, and the CEK machine charges the builtin's CPU cost before executing it. When a script asks to drop 264 elements, the cost calculation saturates to 263 − 1 CPU units (~9.22 quintillion), which immediately blows past the 10-billion transaction CPU limit. Bean, the Haskell Plutus evaluator, and Amaru's evaluator all stop with Out of Budget — but the small test wrapper that recorded these two regression files mapped every error to the string "evaluation failure".

1. Concrete Example
[(force (builtin dropList)) (con integer 18446744073709551616) [7, 8]]
under the standard 10,000,000,000 CPU ceiling.
2. Test Suite Expects
evaluation failure — because the secondary conformance_extra.rs test macro only has one failure string for any Err(_).
3. Bean & Mainnet Do
out of budget — spending 9,223,372,036,854,775,807 CPU before running dropList exhausts the 10,000,000,000 limit.
Upstream source references & vector IDs

Vectors: builtin_semantics_droplist_v3_count_beyond_u64_regression and builtin_semantics_droplist_v3_negative_count_beyond_u64_regression. Upstream Plutus 1.67.0.0: ExMemoryUsage.hs:306 (IntegerCostedLiterally narrows |±264| to maxBound :: SatInt = 263 - 1) and Cek/Internal.hs:1064-1065 (spendBudgets runs before the builtin denotation). Note that on the main corpus's identical huge-count tests (dropList-09/10/14/15/16), the suite itself expects out of budget.

Case 7 · Legacy Textual Builtin Name verifySignature
uplc-conformance (1 of 1,059)

On the blockchain, compiled Plutus scripts are binary ("flat" encoding) and refer to builtins by numeric ID (builtin #5). In human-readable text files, builtin #5 was originally called verifySignature in early Plutus drafts, and was renamed years ago to verifyEd25519Signature once ECDSA and Schnorr signatures were added. One regression text file still uses the old name, which the official Plutus text parser and Bean both reject as an unknown builtin name.

1. Concrete Example
A human-readable .uplc text file containing:
(builtin verifySignature) instead of (builtin verifyEd25519Signature)
2. Test Suite Expects
success — testing a custom parser alias that accepts the pre-1.0 name verifySignature in text mode.
3. Bean & Upstream Parser Do
parse error — upstream plutus-core's textual parser only accepts verifyEd25519Signature. (Binary on-chain scripts are unaffected.)
Upstream source references & vector ID

Vector: builtin_semantics_verifysignature_legacy_alias_test_vector_25_regression. On-chain flat-encoded scripts decode builtin tag 5 directly to VerifyEd25519Signature; only the textual UPLC parser checks the identifier string.

THE EXPERIMENT

How far can you get implementing a Cardano node without ever looking at its source code?

BUILT FROM
Specs, CDDL, genesis files

The published formal specification, CDDL wire grammars, and network genesis files.

TEST AGAINST
Stock node & cardano-cli

Used as black-box differential oracles at every milestone and across full mainnet history.

NEVER
Open the bean's source

LLM agents write the code; the human never reads it. Tests and diagnostics replace reading.

WHY JAVA

A mainstream runtime with tools operators already trust

Mature ecosystem

Decades of build tooling, testing and libraries, and a very large pool of engineers and agents who can work with the runtime.

High-quality diagnostics

Profilers, JDK Flight Recorder and heap analysis that work on a running node out of the box, without a special build.

Java 25 LTS virtual threads FFM native crypto off-heap mmap storage sealed types & records G1 GC · no GraalVM
FIRST COMMIT 7 JUNE 2026 → TODAY

From first commit to mainnet and 99.74% conformance

01
Talk & follow
Mux, handshake, ChainSync, BlockFetch
02
Plutus
101/101 builtins, 10,000/10,000 UPLC turbo
03
Validating relay
Full ledger validation of the chain it follows
04
Forging blocks
Praos leader; the stock node adopts our blocks
05
Local clients
Stock cardano-cli registers the BEAN pool
NOW
On mainnet
Plus Musashi (Leios); Mithril client & server
SCALE

More test code than node code

~8,500+
commits
built iteratively with oracle gates
~720k
lines of Java
more test code than production code
18
Gradle modules
from cbor and plutus-uplc to node-app
10,000/10,000
UPLC turbo
own CEK evaluator, real cost model, 100% pass
101/101
Plutus builtins
incl. BLS12-381, secp256k1, Ripemd-160, Array & Value
99.74%
NDC conformance
20,529 / 20,536 across CBOR, Ledger, UPLC & Turbo
FOR OPERATORS

Get on the chain fast, stay lean

FROM GENESIS
Fast full sync

Validates the whole chain from genesis, using off-heap LSM storage and ideas from Turbo Daedalus.

FROM A SNAPSHOT
Fast Mithril bootstrap

Mithril built directly into the node, as client and server: start from a certified snapshot in minutes.

ONCE RUNNING
History truncation

Drop old chain history the node no longer needs and keep disk footprint small.

ON THE NETWORK TODAY

Running on four networks, mainnet included

Preview Preprod Mainnet · ZW3RK Musashi · Leios testnet

Bean-forged blocks stamp 67 as the protocol-version minor in the header, the same way Dingo stamps 69.

67
protocol-version minor