Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ Thumbs.db

### Development planning (not part of the source) ###
plan.md
!/bindings/circuit/PLAN.md
/.playwright-mcp/

### Durable local benchmark datasets (not removed by Gradle clean) ###
Expand Down
30 changes: 24 additions & 6 deletions bindings/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,22 @@ Experimental Java-to-Wasm bindings under [ADR-0057](../docs/adr/0057-webassembly
implemented in PR #92. See the [measured report](REPORT.md), [progress ledger](PROGRESS.md),
[reviews](reviews/W4-W5.md) and [distribution status](DISTRIBUTION.md).

The separate circuit-compiler stack adds [typed TS authoring and portable graph import](circuit/README.md)
under [ADR-0064](../docs/adr/0064-portable-circuits-and-typescript-frontend.md).
Its [plan](circuit/PLAN.md) tracks completed slices and remaining gates.
[Measured Merkle support](circuit/M3-MERKLE.md) includes depth-1/2 browser proofs and a
[local Cardano claim](cardano/README.md); larger trees remain compile/witness-only in the default profile.

The adapters reuse ZeroJ's Java implementation. Typed ESM packages expose `Uint8Array`, `bigint`,
Promises and worker-local handles. Browsers use Web Workers; Node uses worker threads. Importing
an API or bundle descriptor does not fetch Wasm. An application explicitly selects its images.
No JVM is needed at runtime. These images require a JavaScript host and are not generic WASI modules.

The opt-in [larger-Merkle experiment](circuit/M3-LARGE-MERKLE.md) also measures depths 8/16/32
in Node, Chromium, Firefox and the JVM at 6g/8g. It uses a separate research image outside npm
packages; normal SDK limits are unchanged. Key import takes minutes, so these results establish
desktop research feasibility rather than supported large-circuit browser performance.

## Build and try it

Prerequisites: Java 25; **Oracle GraalVM 25.3.4.1+1.1**, **Binaryen 132** on PATH, Python 3 and
Expand All @@ -26,9 +37,9 @@ cd ../..
python3 bindings/wasm/check.py
```

This opt-in check builds eight images (six consumer choices and two private entropy probes),
This opt-in check builds nine images (seven consumer choices and two private entropy probes),
generates development-only proof fixtures, runs adapter and SDK/browser tests, and installs
15 local npm tarballs outside the workspace. It never publishes packages. The default Java
local npm tarballs outside the workspace, including a separate compiler consumer. It never publishes packages. The default Java
build/BOM/publication does not depend on Node, Binaryen or Web Image.

After the check, from `bindings/js`:
Expand Down Expand Up @@ -79,8 +90,11 @@ To rerun the affected core regressions, see [REPORT.md](REPORT.md).
| `@zeroj/elgamal` | `createElGamal` | `elgamal` / `bundle-confidential` |
| `@zeroj/threshold` | `createThreshold` | `threshold` / `bundle-confidential` |
| `@zeroj/confidential-notes` | `createConfidentialNotes` | `confidential-notes` / `bundle-confidential` |
| `@zeroj/circuit` | `defineCircuit`, `createCircuitCompiler` | `circuit` / `bundle-circuit`; selected experimental graph/recipe compiler |
| `@zeroj/circuit-lint` | `zeroj-circuit-lint` CLI | Type-aware authoring checks; no Wasm |

`@zeroj/bundle-all` is optional: it supplies every capability through one image. Do not register
`@zeroj/bundle-all` is optional: it combines the original cryptographic capabilities. The new
compiler uses the separately selected `bundle-circuit`, which also supplies verification. Do not register
it together with a second provider for the same capability. Several wrappers can share a single
runtime/image; they do not dynamically link Java heaps across images. Default package imports
never select or download all images for you. The two probe packages are test-only.
Expand Down Expand Up @@ -122,12 +136,15 @@ Fr profile (t=3, alpha=5, RF=8, RP=57), rejects non-canonical scalars and requir
secret-path opt-in because its Java arithmetic is variable-time.

None of these ordinary calls builds ZK constraints. SHA-2/Poseidon circuit gadgets remain Java
circuit components. The shipped prover demonstrates `y = x³ + x + 5` with private x. Custom
circuit components. The standalone prover package demonstrates `y = x³ + x + 5` with private x. Custom
small circuits implement [CompiledCircuit](wasm/adapter-groth16/src/main/java/org/zeroj/bindings/groth16/CompiledCircuit.java),
register in a Java entry point, and compile into an explicit image. Import a ceremony key pinned
by trusted hash and exact fingerprint; do not implement setup in the consumer image. The existing
Java witness/circuit APIs can be used by a custom adapter. The current typed convenience method
is `proveCubic`; the transport supports only the compiled adapter's allowlisted input contract.
is `proveCubic`; that transport supports only the compiled adapter's allowlisted input contract.
The separate `@zeroj/circuit` / `bundle-circuit` path also accepts authenticated portable graphs
and typed recipes at runtime, including selected Poseidon/Merkle/SHA-256 gadgets. See the
[circuit guide](circuit/README.md) for measured limits and artifact authentication.

## Deployment and bundlers

Expand Down Expand Up @@ -178,7 +195,8 @@ shows the policy without enabling general JavaScript `unsafe-eval`.
| Boundary | Initial limit |
|---|---|
| Request JSON | 1,048,576 characters; depth 24; duplicate fields/trailing tokens rejected |
| Byte array / proving key | 262,144 bytes |
| Direct byte array / standalone proving key | 262,144 bytes |
| Compiler proving key (`circuit-key-v1`) | 2 MiB total, 64 KiB chunks, one upload / one successful key per compiled circuit |
| Key import allocations | <=4,096 wires, <=4,096 domain cells, <=16,384 coefficients; compiled relation must also match |
| Groth16 public inputs | 64 |
| BBS messages | 64, each <=4,096 bytes |
Expand Down
80 changes: 80 additions & 0 deletions bindings/cardano/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
# Browser membership proof → local Cardano claim

An opt-in, disposable Yaci DevKit example for [ADR-0064](../../docs/adr/0064-portable-circuits-and-typescript-frontend.md).
The browser compiles a TS-authored Merkle relation with the existing Java gadget and generates
a Groth16 BLS12-381 proof. This JVM runner uses ZeroJ's existing checked Cardano point codec
and JuLC Groth16 library to submit that exact Chromium proof. It implements no new cryptography.

The [M3 report](../circuit/M3-MERKLE.md) records costs, limits and security boundaries.
This is a two-leaf tree with published test inputs and disposable setup, **not a production allowlist**.
The runner creates an ephemeral signing account in memory and requests 100 local test ADA.
It writes public evidence only under ignored `bindings/cardano/build/evidence`.

## Prerequisites and run

1. Run `GRAALVM_HOME=... python3 bindings/wasm/circuit-check.py` from the repository root.
This creates the matching development key/VK, a fresh `chromium-proof.json` and an independently
verified valid `unapproved-root-proof.json`. No browser wallet or upload endpoint is needed.
2. Run a **local** Yaci DevKit with protocol major 11, store API at `http://localhost:8080/api/v1/`,
faucet at `http://localhost:10000/local-cluster/api/addresses/topup`, testnet magic 42, and
node container `node1-yaci-cli-1`. The tested node socket is
`/clusters/nodes/default/node/node.sock`. Other node/socket profiles are not silently inferred.
3. Install the pinned JuLC experimental dependencies into Maven Local. This isolated runner uses
`0.1.0-pre20-c3a31c2-SNAPSHOT`, source commit
`c3a31c2a1a74b5de8d3f604f17b597c76395b664` of `https://github.com/bloxbean/julc`.
This commit-labelled snapshot was not available in the public repositories during validation.
In a separate clean checkout of that commit, with Java 25, run `./gradlew publishToMavenLocal`.
The build derives the snapshot suffix from the checked-out commit. Do not publish remotely.
4. From this ZeroJ repository root:

```sh
export JAVA_HOME="$GRAALVM_HOME"
./gradlew -p bindings/cardano run --args=--local-devnet --max-workers=2 --console=plain
```

The composite build uses the current ZeroJ source for `zeroj-onchain-julc` and its checked codec.
The runner alone pins JuLC pre20 / Cardano Client Lib 0.8.0-pre7; it does not change the root
project's dependency profile or claim that the root profile was tested against this node.
The JVM evaluator can use native support for ledger builtins; browser proving remains pure Java
compiled to Wasm, with WebCrypto-backed entropy and no native blst dependency.

`YACI_STORE_URL` and `YACI_FAUCET_URL` may select another loopback port/path; non-loopback URLs,
userinfo, query strings and fragments reject. `CARDANO_CONTAINER` selects the local Docker
container. The CLI flag is deliberately required, and this example is not a mainnet/preprod runner.
The browser proof/VK/manifest are trusted local development artifacts; this harness is not a
service for admitting arbitrary uploaded cryptographic files. Never reuse its development keys
as production ceremony artifacts.

## What the validator actually authorizes

The script parameters pin the approved root, verification key and one payment-key hash.
The inline datum must contain exactly that one root. The redeemer carries action `1` and the
compressed proof. The validator requires the pinned key in transaction signatories, the exact
action, the approved root, and a valid proof against the pinned VK. Ledger signature validation
then authorizes the transaction's contents. The authorized signer chooses the recipient/output;
this is not an issuer-restricted payout or a private state transition.

Membership is reusable. A published proof can be reused for other deposits protected by the
same root/signer policy; it is not tied to a nullifier or a unique user. The signer is public.
Cardano prevents consuming **the same UTxO** twice; that does not provide global one-use
membership or anonymous spending. The test values (leaf 42, sibling 100, direction true) are
public fixtures, so there is no privacy claim about this particular witness.

## Checks and evidence

- Independent snarkjs and JVM checks cover fresh browser proofs before this runner is used.
- The valid signed claim is evaluated by JuLC Java, the remote evaluator, and Haskell
`cardano-cli calculate-plutus-script-cost`; their execution budgets must be identical.
- Signed submissions with a valid proof for an **unapproved root**, wrong action, missing required
signer, and negated proof A must all fail phase 2 with `ValidationTagMismatch`.
- The valid transaction must be submitted and confirmed in the store.
- A different transaction ID attempting to consume the same UTxO is constructed before that
confirmation and submitted afterwards. It must fail for spent/missing inputs. The observed
node error was `ConwayMempoolFailure: All inputs are spent`; `BadInputsUTxO` is also admitted.
Resubmitting identical bytes alone would not be a useful replay test.

The runner records live protocol parameters, script/transaction sizes, consumed and declared
execution units, execution/total fees, artifact hashes, transaction hashes and all verdicts.
Fees exclude the 5 ADA locked output, change and returned collateral; these are values moved
through the example, not verification fees. The valid claim fee and separate funding/locking
fee are reported independently. Raw transaction/vector/log files remain untracked.
25 changes: 25 additions & 0 deletions bindings/cardano/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
plugins { id 'java'; id 'application' }
repositories {
// Commit-pinned JuLC experiment is installed from source; not a public release.
mavenLocal { content { includeGroup "org.julclang" } }
mavenCentral()
maven { url 'https://central.sonatype.com/repository/maven-snapshots' }
maven { url 'https://repo.bloxbean.org/maven/snapshots' }
}
java { toolchain { languageVersion = JavaLanguageVersion.of(25) } }
// Isolated experimental runner: no change to the core module's pinned compiler profile.
def julc = '0.1.0-pre20-c3a31c2-SNAPSHOT'
dependencies {
implementation 'org.zeroj:zeroj-onchain-julc:0.1.0'
implementation "org.julclang:julc-compiler:${julc}"
implementation "org.julclang:julc-cardano-client-lib:${julc}"
implementation "org.julclang:julc-vm-java:${julc}"
implementation 'com.bloxbean.cardano:cardano-client-lib:0.8.0-pre7'
implementation 'com.bloxbean.cardano:cardano-client-backend-blockfrost:0.8.0-pre7'
implementation 'com.fasterxml.jackson.core:jackson-databind:2.18.3'
}
application { mainClass = 'org.zeroj.bindings.cardano.AllowlistDevnet' }
tasks.named('run') {
workingDir rootProject.file('../..')
maxHeapSize = '2g'
}
2 changes: 2 additions & 0 deletions bindings/cardano/settings.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
rootProject.name = 'zeroj-bindings-cardano-example'
includeBuild('../..')
Loading