Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

19 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

zolcrypt

The pure-Zig, FFI-free cryptography that a live Solana validator runs, extracted with its byte-exactness proof carried alongside it.

zolcrypt is the cryptographic leaf of Vexor, an independent, Zig-native Solana validator, carved out as a standalone, dependency-free Zig package. Every module in this repository is std-only: zero C FFI, zero vendored .a/.so archives, zero external build graph. It implements the primitives a Solana validator needs to verify transaction signatures, run alt_bn128/Poseidon syscalls, and compute the account lattice hash that feeds bank_hash. It also ships the Known-Answer-Test (KAT) corpus that proves each one byte-exact against independent reference implementations.

This is not a from-scratch, never-before-seen cryptography library, and it does not claim to be. It is a byte-compatibility artifact: code written in Zig, informed by reading Agave, Firedancer Ballet, and Syndica's Sig as reference implementations and differential oracles, reimplementing their behavior, and gating every output against published/independent vectors. See NOTICE for the full, file-by-file attribution ledger. It is the authoritative source for "who wrote what," and this README summarizes it but does not replace it.

Status: pre-1.0, testnet-grade, not independently security-audited. Read Honest limits before depending on this for anything that moves value.


Table of contents

  1. What it is
  2. Why it exists / who it's for
  3. Feature matrix
  4. Repository layout
  5. Install
  6. Usage
  7. Verification & correctness
  8. The @prov: provenance-anchor convention
  9. Honest limits / what this is not
  10. License & attribution
  11. Roadmap
  12. Contributing

What it is

zolcrypt packages the exact set of cryptographic routines a Solana validator's execution path needs, with no dependency outside the Zig standard library:

  • Ed25519, consensus-path signature verification (verifyStrict, matching Agave's actual Signature::verify()), the same strict verifier with an AVX-512 IFMA fast path for the shred path, and a pure-Zig RFC 8032 signer.
  • BN254 / alt_bn128: G1/G2 point addition and scalar multiplication, compression/decompression, and the optimal-ate pairing check, implementing the same byte contract as the Solana sol_alt_bn128_* syscalls.
  • Poseidon-BN254: the permutation behind the sol_poseidon syscall.
  • LtHash: the 2048-byte / 1024-lane u16 lattice hash used to accumulate account state into bank_hash, @Vector-accelerated.
  • BLAKE3: a thin wrapper over std.crypto.hash.Blake3 (the accountLtHash hot path).
  • SHA-256-backed core types: Hash, Pubkey, Signature.
  • Precompiles: parsers/verifiers for the three Solana signature-verification precompile programs: Ed25519SigVerify111..., KeccakSecp256k1..., and Secp256r1SigVerify... (SIMD-0075).

Provenance, in one sentence: this code was extracted from Vexor's src/vex_crypto/ leaf, the stack that ships in Vexor's production binary and votes on Solana testnet today, and is republished here unmodified as a standalone Zig package, not copied at runtime from any other project. Some numeric floors (a field-arithmetic backend, a fiat-crypto artifact, a KAT corpus) are carried verbatim from Apache-2.0/MIT-licensed upstream projects and are credited as such; the orchestration, API surface, group law, pairing, Poseidon parameter conversion, and precompile logic are Vexor-authored. The full breakdown is in NOTICE.

Why it exists / who it's for

Pure-Zig, FFI-free Solana cryptography is not something you can zig fetch today as a standalone unit. It typically lives buried inside a validator's or client's source tree, coupled to that project's internal types, allocator conventions, and build graph. zolcrypt exists to close that gap:

  • Validator and client authors who want a Solana-crypto leaf they can vendor into a Zig project without pulling in a C toolchain, blst, libsecp256k1, or any other native dependency. That matters for reproducible builds, sandboxed/BPF-adjacent build environments, and trivial cross-compilation (the whole point of zig build -Dtarget=... is broken the moment you link a prebuilt .a).
  • Solana tooling authors (indexers, explorers, fuzzers, replay tools) who need byte-exact alt_bn128/Poseidon/Ed25519/precompile semantics in Zig without re-deriving them from Rust source.
  • ZK / applied-crypto Zig developers who want a BN254 tower (Fp/Fp2/Fp6/Fp12), pairing, and Poseidon implementation they can read, audit, and build on, with an existing byte-exact KAT harness as a starting point.
  • Anyone auditing Vexor who wants the crypto leaf isolated from the rest of the validator (networking, banking stage, gossip, ...) so it can be read and tested as a self-contained ~15.9k-line unit.

The unifying theme is FFI-free by construction: no blst, no libsecp256k1, no OpenSSL, no build-time C compiler requirement anywhere in this package.

Feature matrix

Primitive File(s) Status Verified against Notable perf
Ed25519 consensus verify ed25519.zig (verify) → ed25519/root.zig (verifyStrict) Shipping matches Agave's Signature::verify(), which resolves to verify_strict; differential-tested against dalek/ed25519-speccheck vectors AVX-512 IFMA (vpmadd52) on znver4; generic @Vector fallback on other targets
Ed25519 strict verify (shred path) ed25519.zig (verifyShred) → ed25519/root.zig (verifyStrict) Shipping 3-way stdlib-cofactored/strict/lenient matrix, dalek verify_strict semantics AVX-512 IFMA (vpmadd52) on znver4; generic @Vector fallback on other targets
Ed25519 batch strict verify ed25519/root.zig (verifyBatchStrict) Shipping (internal API) same 3-way matrix double-base scalar-mul (doubleBaseMul) + w-NAF
Ed25519 sign ed25519.zig (sign) → ed25519/root.zig (signPureZig) Shipping byte-identical to std.crypto RFC 8032 signing pure-Zig, FFI-free
Ed25519 KAT corpus ed25519/wycheproof.zig, ed25519/kat.zig Shipping Google Project Wycheproof EDDSA vectors
Ed25519SigVerify precompile ed25519_precompile.zig Shipping Agave verify_strict acceptance semantics
KeccakSecp256k1 precompile secp256k1.zig Shipping Agave ECDSA-recover + Ethereum-address acceptance semantics
Secp256r1SigVerify precompile (SIMD-0075) secp256r1.zig Shipping Agave low-S / P-256 acceptance semantics
BN254 G1/G2 add, scalar-mul bn254/curve.zig, bn254/root.zig Shipping Firedancer test_bn254.c, go-ethereum EIP-196/197, py_ecc
BN254 optimal-ate pairing bn254/pairing.zig Shipping Firedancer test_bn254.c, go-ethereum EIP-196/197 pairing fixtures batched Miller loop (BATCH_MAX)
BN254 compress/decompress bn254/curve.zig (G1.compress/decompress, G2.*) Shipping solana-bn254 v3.2.1 published constants (flag=0, flag=1 negated, point-at-infinity)
BN254 base-field reduction bn254/fiat_fp.zig Shipping fiat-crypto machine-generated proof (verbatim artifact) Montgomery form
Poseidon-BN254 bn254/poseidon.zig, bn254/poseidon_params.zig Shipping Firedancer test_poseidon.c (single-input LE/BE, N-ones N=1..12, FLIST chain) width up to 12 inputs (state width 13)
LtHash (lattice hash) lthash.zig Shipping Agave lattice-hash account-accumulator semantics @Vector(32, u16)vpaddw/vpsubw on AVX-512
BLAKE3 blake3.zig Shipping std.crypto.hash.Blake3 (pure-Zig; no FFI backend remains)
SHA-256 core types hash.zig Shipping std.crypto.hash.sha2.Sha256
BLS12-381 Not included deliberately excluded; see Honest limits

Repository layout

zolcrypt/
├── build.zig               # module "zolcrypt" (src/root.zig) + one zig build test-* step per KAT root
├── build.zig.zon            # package manifest — name, version, minimum_zig_version, fingerprint
├── LICENSE                  # Apache-2.0
├── NOTICE                   # full, file-by-file attribution ledger (authoritative)
├── README.md                 # this file
├── BUILDING.md              # toolchain, build targets, optimize modes, cross-compilation
├── TESTING.md               # test architecture, coverage categories, per-gate guide
└── src/
    ├── root.zig                   # public module surface: re-exports + aggregate `test { ... }` root
    ├── hash.zig                   # Hash / Pubkey / Signature (32/64-byte extern structs, SHA-256) + KATs
    ├── lthash.zig                 # LtHash — 2048-byte / 1024-lane u16 lattice hash + algebraic-law tests
    ├── blake3.zig                 # std.crypto.hash.Blake3 wrapper + official BLAKE3-team vectors
    ├── ed25519.zig                # verify() / verifyShred() / sign() / generateKeypair() — Vexor's call-site API
    ├── ed25519/
    │   ├── root.zig               # Vexor-authored: doubleBaseMul, w-NAF tables, verifyStrict,
    │   │                          # verifyLenientCofactorless, verifyBatchStrict, signPureZig
    │   ├── avx512.zig             # AVX-512 IFMA (vpmadd52) field backend  [credited floor, via Sig]
    │   ├── generic.zig            # portable @Vector fallback             [credited floor, via Sig]
    │   ├── crosscheck.zig         # generic-vs-AVX512-IFMA backend cross-consistency + std oracle
    │   ├── fuzz.zig               # adversarial/edge/fuzz: malformed inputs never panic, garbage → reject
    │   ├── wycheproof.zig         # Google Wycheproof EDDSA KAT corpus (150-vector, machine-generated)
    │   └── kat.zig                # wycheproof strict-verdict matrix + ACCEPT round-trips + 3-way matrix
    ├── ed25519_precompile.zig     # Ed25519SigVerify111... precompile parser/verifier
    ├── secp256k1.zig               # KeccakSecp256k1... ECDSA-recover + eth-address precompile
    ├── secp256r1.zig               # Secp256r1SigVerify... (SIMD-0075) P-256 precompile
    ├── bn254.zig                  # backend-dispatch leaf: pure_zig g1/g2 add·mul, pairing, compress, poseidon
    └── bn254/
        ├── root.zig               # alt_bn128 syscall surface (Firedancer Ballet fd_bn254_* byte contract)
        ├── field.zig               # Fp / Fp2 / Fp6 / Fp12 tower
        ├── curve.zig               # G1 / G2 group law, compress/decompress
        ├── pairing.zig             # optimal-ate Miller loop + final exponentiation
        ├── fiat_fp.zig             # BN254 base-field Fp Montgomery reduction  [verbatim fiat-crypto artifact]
        ├── poseidon.zig            # Poseidon-BN254 permutation + poseidonHash()
        ├── poseidon_params.zig     # round constants (ark) + MDS, de-Montgomery'd to normal Fr
        ├── fuzz.zig                # adversarial/edge/fuzz + algebraic group laws: not-on-curve reject, never-panic
        └── kat.zig                 # absolute + independent-oracle byte-exact vectors

Line counts (via wc -l, this revision):

Group Files Lines
Core types / hashes (hash.zig, lthash.zig, blake3.zig) 3 453
Ed25519 (ed25519.zig + ed25519/*.zig, incl. crosscheck.zig + fuzz.zig) 8 3,470
Precompiles (ed25519_precompile.zig, secp256k1.zig, secp256r1.zig) 3 1,434
BN254 + Poseidon (bn254.zig + bn254/*.zig, incl. fuzz.zig) 10 11,544
src/ total (incl. root.zig) 25 16,955
Build + docs (build.zig, build.zig.zon, LICENSE, NOTICE, README.md, BUILDING.md, TESTING.md) 7

(bn254/poseidon_params.zig alone is 7,226 lines — it is machine-derived round-constant/MDS-matrix data, not hand-written logic; bn254/fiat_fp.zig at 2,032 lines is the fiat-crypto-generated field reduction, also machine output. The hand-authored orchestration code is a small fraction of the raw line count.)

Install

Requires Zig 0.15.2 (pinned in build.zig.zon via minimum_zig_version; this package is not tested against other Zig versions).

Add as a dependency

From your project root, using Zig's package manager:

zig fetch --save git+https://github.com/DavidB-77/zolcrypt.git

This adds an entry to your build.zig.zon:

.dependencies = .{
    .zolcrypt = .{
        .url = "git+https://github.com/DavidB-77/zolcrypt.git#<commit>",
        .hash = "...", // filled in by `zig fetch`
    },
},

For local/monorepo development, a plain path dependency also works:

.dependencies = .{
    .zolcrypt = .{ .path = "../zolcrypt" },
},

Then wire it into your build.zig:

const zolcrypt_dep = b.dependency("zolcrypt", .{ .target = target, .optimize = optimize });
exe.root_module.addImport("zolcrypt", zolcrypt_dep.module("zolcrypt"));

Clone and run the test suite directly

git clone https://github.com/DavidB-77/zolcrypt.git
cd zolcrypt
zig build test --summary all

See BUILDING.md for toolchain, build targets, optimize modes, and cross-compilation, and TESTING.md for the test architecture, coverage categories, and per-gate guide.

Usage

All examples below use the actual public surface exposed by src/root.zig — nothing here is aspirational.

const zc = @import("zolcrypt");

Ed25519 — consensus-semantics verify

// Strict verification, matching Agave's actual Signature::verify(). This is
// the verifier Vexor's transaction/gossip/keypair paths use.
const ok: bool = zc.verify(&sig_64_bytes, &pubkey_32_bytes, message_bytes);
// equivalently: zc.ed25519.verify(...)

Ed25519 — sign / generate a keypair

const kp = zc.ed25519.generateKeypair(); // { public: [32]u8, secret: [64]u8 }
const sig64 = zc.ed25519.sign(kp.secret, "hello");
try std.testing.expect(zc.verify(&sig64, &kp.public, "hello"));

BN254 — G1 point addition (alt_bn128_add syscall contract)

// 128-byte input: two 64-byte (x,y) G1 points, packed per the endianness flag.
var out: [64]u8 = undefined;
const wrote: bool = zc.bn254.g1Add(&out, input_128_bytes, true /* big_endian */);
// wrote == false means a soft failure (bad length / not on curve) —
// the syscall layer above this package maps that to `return 1`, not a panic.

BN254 — optimal-ate pairing check

// `in` is N * 192-byte (G1||G2) pairs. out[0] (or out[31] if big_endian) is
// 1 iff the product of pairings equals the identity in GT, else 0.
var out: [32]u8 = undefined;
const wrote = zc.bn254.pairing(&out, pairing_input_bytes, true);

Poseidon-BN254

var digest: [32]u8 = undefined;
const inputs: []const []const u8 = &.{ input_a_32_bytes, input_b_32_bytes };
const wrote = zc.bn254.poseidonHash(&digest, inputs, true /* big_endian */, false /* enforce_padding */);

LtHash — the account lattice-hash accumulator

var lt = zc.LtHash.init();          // 1024 × u16, zeroed
lt.wrappingAdd(&other_lthash);      // per-lane wrapping add (vpaddw on AVX-512)
lt.wrappingSub(&removed_lthash);    // per-lane wrapping sub (vpsubw on AVX-512)
const bytes: *const [2048]u8 = lt.asBytes();

Precompiles — parsing/verifying a Solana precompile instruction

// Each precompile module exposes the same shape: verify(data, all_instr_datas) PrecompileError!void
try zc.ed25519_precompile.verify(this_instruction_data, all_instruction_datas_in_tx);
try zc.secp256k1.verify(this_instruction_data, all_instruction_datas_in_tx);
try zc.secp256r1.verify(this_instruction_data, all_instruction_datas_in_tx);

Core types

const h = zc.Hash.compute(some_bytes);      // SHA-256
const pk = zc.Pubkey.fromBytes(pubkey_bytes);
const sig = zc.Signature.fromBytes(sig_bytes);

Note on the Ed25519 "strict" API surface: verifyStrict, verifyLenientCofactorless, verifyBatchStrict, doubleBaseMul, and signPureZig live in src/ed25519/root.zig and are used internally by ed25519.zig (verify, verifyShred, and sign), but src/root.zig does not currently re-export the ed25519/root.zig submodule itself, so they are not part of the package's external public API in this revision, only zc.ed25519.verify, .verifyShred, .sign, and .generateKeypair are reachable from outside the module. This is a real gap if you need the strict/batch verifier standalone; see Roadmap.

Verification & correctness

The KATs are the point of this package. A Solana validator cannot afford a single wrong bit in these routines — one differing byte in alt_bn128, Poseidon, ed25519 verification, or the account lattice hash forks the node off consensus. So zolcrypt ships the vectors that prove parity, not just the code, and every primitive above is exercised by zig build test.

What "byte-exact" means here

For every KAT, the test asserts that the pure-Zig output is bit-for-bit identical to a published reference output — not "close," not "same result modulo encoding," but the exact same byte sequence a reference implementation (Firedancer, go-ethereum, py_ecc, solana-bn254/ark-bn254, Google Wycheproof) produces for the same input. For BN254 in particular, three independent-lineage oracles (go-ethereum in Go, py_ecc in Python from the Ethereum Foundation, and solana-bn254/ark-bn254 in Rust — no shared code, authorship, or ancestry between them or with Vexor's Zig) are checked against the same vectors, which is materially stronger evidence than self-consistency against a single reference.

The corpus, as actually run

Confirmed by running the suite in this revision (zig build test --summary all, zig-0.15.2, cores 28-31, nice -n 19 / taskset):

Build Summary: 21/21 steps succeeded; 214/215 tests passed; 1 skipped   (Debug)
Build Summary: 21/21 steps succeeded; 270/270 tests passed              (ReleaseSafe, -Doptimize=ReleaseSafe)

The single Debug "skipped" is the AVX-512 IFMA generic-vs-IFMA cross-check arm, correctly skipped on the self-hosted backend (which does not select the vpmadd52 kernels); under ReleaseSafe it runs and passes. The gap between 215 and 270 is the avx512.zig IFMA kernels' own inline declarations being pulled into the compiled graph when the backend is selected, plus std.testing.refAllDecls-style declaration coverage that expands under LLVM, the same source-level tests, exercised more thoroughly at the higher optimize level. Both Debug and ReleaseSafe must be green; only ReleaseSafe exercises the production shred-verify AVX-512 backend (see TESTING.md).

Per-module breakdown (Debug run):

Test artifact zig build step Tests What it proves
test-root zig build test-root 88 Aggregate: SHA-256/Hash/Pubkey/Signature KATs + LtHash algebra + BLAKE3 vectors + all re-exports
test-ed25519 zig build test-ed25519 13 ed25519.zig sign/verify round-trip
test-ed25519-core zig build test-ed25519-core 14 Pure-Zig ed25519 core: Wycheproof + ACCEPT + 3-way strictness matrix
test-ed25519-crosscheck zig build test-ed25519-crosscheck 13 (+1 skipped) Generic-vs-AVX512-IFMA backend cross-consistency + std.crypto oracle
test-ed25519-fuzz zig build test-ed25519-fuzz 18 Adversarial/edge/fuzz: malformed inputs never panic, garbage → reject
test-ed25519-precompile zig build test-ed25519-precompile 19 Ed25519SigVerify precompile parser + batch verify
test-secp256k1 zig build test-secp256k1 12 secp256k1 ECDSA-Keccak recover + eth-address precompile
test-secp256r1 zig build test-secp256r1 10 secp256r1 (P-256) SIMD-0075 precompile + low-S
test-bn254 zig build test-bn254 14 BN254/alt_bn128 group ops, pairing, compression, Poseidon byte-exact vectors
test-bn254-fuzz zig build test-bn254-fuzz 13 BN254 adversarial/edge/fuzz + algebraic group laws: not-on-curve reject, round-trips, never-panic
Total zig build test 214 (+1 skipped)

See TESTING.md for the full test architecture and the coverage categories (KATs, curve/structural edge cases, negative/adversarial parser inputs, seeded fuzz harnesses, algebraic group laws, round-trip consistency, and the generic-vs-AVX512-IFMA cross-check), and BUILDING.md for toolchain and optimize-mode details.

Live output from the Ed25519 and BN254 KAT gates (test-root / test-bn254, Debug run), reproduced verbatim:

[KAT a] wycheproof strict: 138/138 vectors matched
[KAT b] ACCEPT round-trip: 64 keypairs signed+verified
[KAT b/lenient] stdlib-cofactored ACCEPTs 2 cofactored cases (local disagreement documented)
[KAT c] 3-way semantic matrix: 9 vectors pinned (stdlib-cofactored/strict/lenient)
[ABS g1_add] 2 FD vectors byte-exact
[ABS g1_mul] 2 FD vectors byte-exact
[ABS pairing] generator-pair=not-one, empty=one OK
[IND g1_add] 4 go-ethereum EIP-197 vectors byte-exact
[IND g1_mul] 6 go-ethereum EIP-197 vectors byte-exact
[IND pairing] 5 go-ethereum EIP-197 vectors byte-exact
[IND g2_add] 4 py_ecc-computed vectors byte-exact
[IND g2_mul] 5 py_ecc-computed vectors byte-exact
[IND compress] solana-bn254 v3.2.1 g1/g2 (flag=0 + flag=1 negated + infinity) round-trips byte-exact
[ABS poseidon] single-input LE+BE byte-exact
[ABS poseidon] N-ones N=2..12 byte-exact
[ABS poseidon] FLIST 12-step chain byte-exact

On the "138/138" line: ed25519/wycheproof.zig carries the full, machine-generated Google Project Wycheproof EDDSA corpus (numberOfTests: 150 per its header comment). The strict-verdict KAT loop in ed25519/kat.zig iterates every group and skips (via a decode-guarded continue) groups whose public-key encoding cannot be parsed to a fixed 32-byte key — this is a property of a handful of the 150 upstream Wycheproof groups (which include deliberately malformed-encoding negative-test groups at the group level, not the signature level), not a gap in vector coverage of parseable vectors. 138 vectors are decoded and asserted byte-for-byte; 0 are silently ignored past the decode stage.

Sources cited by the corpus, per NOTICE and the KAT file headers:

  • Google Project Wycheproof (Apache-2.0) — EDDSA test vectors, ed25519/wycheproof.zig (machine-generated, "DO NOT EDIT" banner intact).
  • Firedancer Ballet (firedancer-io/firedancer, Apache-2.0) — test_bn254.c / test_poseidon.c absolute vectors; the fd_bn254_* / fd_ed25519_verify byte contracts as differential oracles.
  • go-ethereum (Apache-2.0/LGPL) — EIP-196/197 bn256Add/bn256ScalarMul/pairing precompile fixtures, as an independent-lineage oracle.
  • py_ecc (Ethereum Foundation, MIT) — Python-computed G2 vectors, independent-lineage oracle.
  • solana-bn254 v3.2.1 / ark-bn254 (Apache-2.0) — authoritative Agave wire/serialization semantics (endianness, flag bytes, length) and compression-constant vectors.
  • light-poseidon v0.2.0 (Apache-2.0) / circomlib v2.0.5 (GPL-3.0, parameters/spec reference only) — Poseidon round constants and MDS matrix provenance, converted out of Montgomery form for byte-identical output.
  • Agave (anza-xyz/agave, Apache-2.0) — the consensus acceptance ground truth for precompile behavior (SIMD-0075 for secp256r1).

Running the tests yourself

# Pin to cores 28-31, low CPU/IO priority, exactly as this README's own numbers were produced:
nice -n 19 ionice -c3 taskset -c 28-31 zig build test --summary all

# Individual gates:
nice -n 19 ionice -c3 taskset -c 28-31 zig build test-ed25519-core
nice -n 19 ionice -c3 taskset -c 28-31 zig build test-ed25519-crosscheck   # generic-vs-IFMA cross-check
nice -n 19 ionice -c3 taskset -c 28-31 zig build test-ed25519-fuzz
nice -n 19 ionice -c3 taskset -c 28-31 zig build test-bn254
nice -n 19 ionice -c3 taskset -c 28-31 zig build test-bn254-fuzz
nice -n 19 ionice -c3 taskset -c 28-31 zig build test-secp256k1
nice -n 19 ionice -c3 taskset -c 28-31 zig build test-secp256r1
nice -n 19 ionice -c3 taskset -c 28-31 zig build test-ed25519-precompile
nice -n 19 ionice -c3 taskset -c 28-31 zig build test-ed25519

# ReleaseSafe (matches the 270/270 figure above; the ONLY mode that runs the
# AVX-512 IFMA generic-vs-IFMA cross-check on an IFMA-capable host):
nice -n 19 ionice -c3 taskset -c 28-31 zig build test -Doptimize=ReleaseSafe --summary all

# Formatting gate:
zig fmt --check src build.zig

Each zig build test-* step maps 1:1 to a KAT root file/artifact, so a regression in one primitive fails loudly and specifically rather than as an opaque aggregate failure.

The @prov: provenance-anchor convention

Several modules carry inline @prov:crypto.<domain> comment tags at the top of security-relevant blocks (@prov:crypto.bn254, @prov:crypto.ed25519-precompile, @prov:crypto.lthash, @prov:crypto.secp256k1, @prov:crypto.secp256r1 — 89 occurrences across bn254.zig, ed25519_precompile.zig, lthash.zig, secp256k1.zig, and secp256r1.zig in this revision). These are provenance/behavior-spec anchors used inside the Vexor validator tree to tie a specific code region back to the acceptance-semantics rule it implements (e.g. "n_sigs == 0 is rejected" or "G2 mul requires a subgroup check, G2 add does not"), so that a future edit to that region is reviewed against the rule it's implementing, not just against the diff. They are documentation/traceability markers, not executable code — there is no build-time enforcement of @prov: tags in this standalone package (that tooling lives in the Vexor validator tree this was extracted from). Treat them as "why this line is shaped this way" breadcrumbs when reading the source.

Honest limits / what this is not

  • No BLS12-381. The BLS12-381 syscall module in Vexor's source tree (bls12_381.zig / bls12_381_syscall.zig) links the vendored blst C library via FFI. It was deliberately not extracted into this package — zolcrypt is FFI-free by construction, and a pure-Zig BLS12-381 implementation does not exist here yet. See Roadmap.
  • Zig 0.15.2 pinned, not portable across Zig versions. build.zig.zon sets minimum_zig_version = "0.15.2" and the package is developed/tested only against that exact toolchain. It has not been validated on other 0.15.x point releases, 0.14.x, or nightly Zig.
  • Testnet-grade, pre-1.0 (version = "0.1.0"). This is the code a validator runs on Solana testnet. It has not been run, or specifically hardened, for mainnet-beta value-at-risk conditions as a standalone library outside that validator's operational envelope.
  • Not independently security-audited. The correctness evidence here is KAT-based (byte-exact match against published/independent vectors) and "lives in a validator that holds consensus parity" — that is strong differential and operational evidence, but it is not a substitute for a professional, independent cryptographic security audit. None has been performed on this package as a standalone artifact.
  • The Ed25519 "strict"/batch API is not externally exposed. verifyStrict, verifyBatchStrict, verifyLenientCofactorless, doubleBaseMul, and signPureZig exist in src/ed25519/root.zig but are not re-exported by src/root.zig in this revision — see the note in Usage.
  • The consensus Ed25519 path is strict verification, matching Agave. ed25519.zig's verify() routes to verifyStrict. An earlier revision of this code routed the consensus path through the cofactored std.crypto verifier instead, on the claim that Agave accepts small-order-R signatures strict verification rejects. That claim was wrong: Agave's actual Signature::verify() resolves to verify_strict. The incident that motivated the earlier, mistaken choice (a live bank_hash divergence in Vexor, documented in ed25519.zig's source comments) came from a separate FFI verify_strict implementation's bug, not from strictness as a policy. verify() was corrected on 2026-07-29 and is now strict by default, matching the cluster.
  • Machine-generated components are carried, not hand-verified line-by-line. bn254/poseidon_params.zig (7,226 lines of round constants/MDS matrices) and bn254/fiat_fp.zig (2,032 lines, fiat-crypto's generated Montgomery reduction) are data/proof artifacts pinned by the KAT corpus, not independently re-derived by hand for this package.
  • No published performance benchmarks yet. The AVX-512 IFMA Ed25519 path and the @Vector-accelerated LtHash are architecturally fast (the point of using intrinsics/vectorization at all), but no formal benchmark suite or numbers are published in this repository as of this revision.

License & attribution

zolcrypt is licensed under the Apache License, Version 2.0 — see LICENSE.

Copyright (c) 2026 the Vexor authors.

This package reimplements behavior from, and in some components carries verbatim floors from, the following Apache-2.0 (and in one parameters-only case, GPL-3.0-referenced) upstream projects:

  • Agave (anza-xyz/agave) — consensus acceptance ground truth
  • Firedancer Ballet (firedancer-io/firedancer) — syscall byte contracts, differential oracle, parameter sources
  • Syndica's Sig (syndica/sig) — credited verbatim floor for ed25519/avx512.zig, ed25519/generic.zig (dalek curve25519-dalek IFMA backend, reached via Sig), and ed25519/wycheproof.zig
  • fiat-crypto (mit-plv/fiat-crypto) — verbatim, machine-generated, formally-verified BN254 base-field Montgomery reduction (bn254/fiat_fp.zig), tri-licensed MIT/Apache-2.0/BSD-1-Clause
  • Google Project Wycheproof — EDDSA KAT corpus
  • go-ethereum and py_ecc — independent-lineage BN254 oracles
  • solana-bn254 v3.2.1 / ark-bn254 — wire/serialization semantics and compression-constant vectors
  • light-poseidon v0.2.0 and circomlib v2.0.5 (GPL-3.0, parameters/spec reference only, no code linked) — Poseidon round-constant/MDS provenance

NOTICE is the authoritative attribution record. It documents, per module, exactly what is Vexor-authored orchestration versus what is a credited verbatim floor, why each floor is carried rather than rewritten, and which upstream project each differential oracle/KAT vector traces back to. Read it before redistributing, forking, or relying on this package's provenance claims — this README summarizes it but NOTICE governs.

"Agave", "Firedancer", "Sig", and "Solana" are trademarks of their respective owners; this project's use of their names is for accurate technical attribution only.

Roadmap

  • Pure-Zig BLS12-381. Replace the FFI/blst-linked BLS module that was excluded from this extraction with a from-scratch pure-Zig implementation, KAT-gated the same way as the BN254 tower.
  • More curves / precompiles as Solana SIMDs add them (this package tracks Agave 4.2-era syscall/precompile surface as of this revision).
  • Independent, professional security audit of the package as a standalone artifact (distinct from Vexor's own internal review process).
  • Published performance benchmarks for the AVX-512 IFMA Ed25519 path, the BN254 tower, Poseidon, and the LtHash accumulator, across representative hardware (currently only validated functionally, not benchmarked, in this repository).
  • Externally expose the Ed25519 strict/batch API (verifyStrict, verifyBatchStrict, verifyLenientCofactorless, signPureZig, doubleBaseMul) from src/root.zig so callers outside Vexor can use the strict/batch verifier without reaching into src/ed25519/root.zig directly.
  • 1.0.0 once the above (BLS coverage, an external audit, and benchmark numbers) land — 0.1.0 is intentionally conservative about what "done" means for a package whose entire purpose is not being byte-wrong.

Contributing

This package tracks Vexor's live, in-production crypto leaf. Correctness changes here are expected to be gated the same way the existing code is: KAT-first, byte-exact against an independent published reference, with zig build test passing under both Debug and ReleaseSafe before a change is proposed. If you're adding a new primitive, follow the existing per-module zig build test-<name> pattern in build.zig so it gets its own named, individually runnable gate rather than being folded silently into test-root.


Apache-2.0. Part of the Vexor ecosystem — vexornode.xyz.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages