Skip to main content

Nethereum.EVM.Zisk

NuGet: Nethereum.EVM.Zisk | Source: src/Nethereum.EVM.Zisk/

Nethereum.EVM.Zisk

PREVIEW — This package is in preview. APIs may change between releases.

Bridge between Nethereum.EVM.Core and the Zisk zkVM. Compiled as the guest ELF, it reads a witness from Zisk's input channel, executes the EVM, and writes state-root / block-hash commitments to Zisk's output channel.

Overview​

The guest pipeline:

  1. Input — ZiskInput.Read() returns the witness byte stream (serialised BinaryBlockWitness v4).
  2. Deserialise — BinaryBlockWitness.Deserialize(bytes) produces a BlockWitnessData whose Features.Fork drives registry lookup.
  3. Registry — the guest builds its own registry with BuildMinimalRegistry(block.Features): a single HardforkRegistry holding one fork whose precompiles are wired from ZiskPrecompileBackends.Instance (see Backends section) via PrecompileRegistries.PragueBase for Prague, or OsakaBase (which adds the P256Verify backend) for Osaka and the blob-parameter forks. MainnetHardforkRegistry.Build is never called. Only Prague, Osaka, OsakaBpo1, OsakaBpo2 are accepted; any other fork stops the run with output code 5.
  4. Execute — BlockExecutor.Execute (the #if EVM_SYNC sync build) walks transactions, computes state roots via PatriciaStateRootCalculator or BinaryStateRootCalculator (selected by witness Features.StateTree), and populates receipts / block commitments.
  5. Output — ZiskIO.SetOutput(slot, value) emits: result flag, cumulative gas (2 × 32-bit), block hash (8 slots), state root (8 slots), transactions root (8 slots), receipts root (8 slots), transaction count, pre-state root (8 slots), block number (2 × 32-bit), chain id (2 × 32-bit), and parent hash (8 slots). When the witness carries AnchorInputs, the guest first writes keccak256(AnchorInputs) to slots 0-7 (AnchorBinding.CommitmentSlots) and every field above moves up by eight slots; a witness without anchor inputs keeps the layout above starting at slot 0. ZiskIO.Write / ZiskIO.WriteLong and the private WriteHex helper log human-readable diagnostics to the emulator stream.

Installation​

This package is referenced by the Zisk guest binary at build time. The build produces a RISC-V ELF consumed by ziskemu (emulator) or cargo-zisk prove (proof generator).

Dependencies​

Backends​

ZiskPrecompileBackends.Instance is a singleton PrecompileBackends bundle whose seven fields route to witness-backed native Zisk operations. The first seven rows below are those fields; ZiskBls12381Operations and ZiskKzgOperations are separate singletons (see the note after the table):

BackendRoutes toCSRs
ZiskEcRecoverBackendzkvm_secp256k1_ecrecover → keccak address derivationsecp256k1_add/dbl
ZiskSha256Backendzkvm_sha256sha256_f (0x805)
ZiskRipemd160BackendManaged Ripemd160 (no native CSR)—
ZiskModExpBackendzkvm_modexparith256_mod
ZiskBn128Backendzkvm_bn254_g1_add, zkvm_bn254_g1_mul, zkvm_bn254_pairingbn254_curve_add/dbl, bn254_complex_*
ZiskBlake2fBackendzkvm_blake2fblake2b_round (0x819)
ZiskP256VerifyBackendzkvm_secp256r1_verifysecp256r1_add/dbl
ZiskBls12381Operationszkvm_bls12_g1_add/msm, zkvm_bls12_g2_add/msm, zkvm_bls12_pairing, zkvm_bls12_map_fp_to_g1/fp2_to_g2bls12_381_curve_add/dbl, bls12_381_complex_*
ZiskKzgOperationszkvm_kzg_point_eval, zkvm_sha256 (versioned hash)BLS12-381 CSRs + sha256_f

ZiskBls12381Operations and ZiskKzgOperations are not fields of the PrecompileBackends bundle (which holds the seven backends above). They are separate singletons that ZiskBinaryWitness.LayerKzgAndBlsBackends wires onto the registry when the fork's precompile set declares KZG point-evaluation (0x0A) or BLS (0x0B) — there is no ZiskPrecompileBackends.Instance.Bls12381/.Kzg field.

BN254 pairing and BLS12-381 pairing call zkvm_bn254_pairing and zkvm_bls12_pairing. A non-zero status (an input that is not a valid point) is returned as false from the backend's Try* operation, and the handler's TryExecute reports a failed precompile call; the guest has no exception handling, so nothing on the precompile input path throws.

State Tree Selection​

The witness BlockFeatureConfig.StateTree field selects the trie:

StateTreeHashFunctionCalculator
Patricia (default)KeccakPatriciaStateRootCalculator
Binary (EIP-7864)Blake3BinaryStateRootCalculator(Blake3HashProvider)
BinaryPoseidonBinaryStateRootCalculator(ZiskPoseidonHashProvider)
BinarySha256BinaryStateRootCalculator(ZiskSha256HashProvider)
BinaryKeccakBinaryStateRootCalculator(ZiskKeccakHashProvider)

Poseidon uses ZiskPoseidonHashProvider — Poseidon2 over Goldilocks field via CSR 0x812 (width=16, rate=12, x^7 S-box). The managed equivalent is GoldilocksPoseidon2HashProvider in Nethereum.Util, validated to produce identical state roots.

Guest entry point​

ZiskBinaryWitness.Main is the entry point the Zisk ELF links against. It reads the witness, validates the version byte (must equal BinaryBlockWitness.VERSION = 4; any other version stops the run with output code 3), resolves the fork's rules, executes the block, and writes the output slots.

Only forks this guest has rules AND witness fields for are accepted: Prague, Osaka, OsakaBpo1, OsakaBpo2. Any other real fork — pre-Prague forks and Amsterdam — is refused with output code 5 and a non-zero exit rather than executed under a neighbouring fork's rules. (Unspecified is rejected even earlier: BinaryBlockWitness.Deserialize throws because the producer never stamped a fork, so it never reaches the code-5 path.) Proving a block under rules it was not produced under yields a valid proof of a state transition that never happened, and nothing downstream can distinguish that from a correct one.

Amsterdam is refused despite its rules existing, for one reason now rather than two: the witness format carries the EIP-7843 slot number, so SLOTNUM no longer pushes a fabricated zero — but it still carries no EIP-7928 block access-list hash, so the header would be built a field short. Accepting it requires extending the format first.

Build Pipeline​

The build orchestration lives in zisk/ at the repo root. It uses the nethereum/bflat-riscv64:zisk-1.3.1 Docker image built from the Nethereum/bflat-riscv64 fork on the zisk-1.3.1 branch, or the nethereum-zisk image (ci/Dockerfile.zisk), which carries that toolchain together with ziskemu and cargo-zisk.

Two modes:

  • Source mode (default) — bflat compiles all linked .cs files directly from the EVM.Core, Zisk.Core, and EVM.Zisk source trees. Smaller ELF; use for production proofs.
  • DLL mode (--dll) — dotnet build produces the managed DLL first and bflat links against it. ~25% larger ELF but faster iteration when only the guest entry changes.

First-time setup, after building nethereum/bflat-riscv64:zisk-1.3.1 from the fork (the image builds libziskos.a itself):

docker build -f ci/Dockerfile.zisk -t nethereum-zisk zisk/scripts

Build:

bash zisk/scripts/build.sh           # source mode
bash zisk/scripts/build.sh --dll # DLL mode

Output lands in zisk/output/nethereum_evm_elf. Run it:

ziskemu -e zisk/output/nethereum_evm_elf \
--legacy-inputs zisk/output/witnesses/my_witness.bin \
-n 500000000

See zisk/README.md for the STARK proving flow, the PLONK wrap and the Solidity ZiskVerifier, the verification checklist, and environment overrides (ZISK_IMAGE, LIBZISKOS_DIR).

See Also​