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:
- Input —
ZiskInput.Read()returns the witness byte stream (serialisedBinaryBlockWitnessv4). - Deserialise —
BinaryBlockWitness.Deserialize(bytes)produces aBlockWitnessDatawhoseFeatures.Forkdrives registry lookup. - Registry — the guest builds its own registry with
BuildMinimalRegistry(block.Features): a singleHardforkRegistryholding one fork whose precompiles are wired fromZiskPrecompileBackends.Instance(see Backends section) viaPrecompileRegistries.PragueBasefor Prague, orOsakaBase(which adds the P256Verify backend) for Osaka and the blob-parameter forks.MainnetHardforkRegistry.Buildis never called. Only Prague, Osaka, OsakaBpo1, OsakaBpo2 are accepted; any other fork stops the run with output code 5. - Execute —
BlockExecutor.Execute(the#if EVM_SYNCsync build) walks transactions, computes state roots viaPatriciaStateRootCalculatororBinaryStateRootCalculator(selected by witnessFeatures.StateTree), and populates receipts / block commitments. - 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 carriesAnchorInputs, the guest first writeskeccak256(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.WriteLongand the privateWriteHexhelper 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
Nethereum.Zisk.Core— Zisk runtime bindings (IO, crypto P/Invokes, memory map).Nethereum.EVM.Core— EVM engine source-shared into the guest.
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):
| Backend | Routes to | CSRs |
|---|---|---|
ZiskEcRecoverBackend | zkvm_secp256k1_ecrecover → keccak address derivation | secp256k1_add/dbl |
ZiskSha256Backend | zkvm_sha256 | sha256_f (0x805) |
ZiskRipemd160Backend | Managed Ripemd160 (no native CSR) | — |
ZiskModExpBackend | zkvm_modexp | arith256_mod |
ZiskBn128Backend | zkvm_bn254_g1_add, zkvm_bn254_g1_mul, zkvm_bn254_pairing | bn254_curve_add/dbl, bn254_complex_* |
ZiskBlake2fBackend | zkvm_blake2f | blake2b_round (0x819) |
ZiskP256VerifyBackend | zkvm_secp256r1_verify | secp256r1_add/dbl |
ZiskBls12381Operations | zkvm_bls12_g1_add/msm, zkvm_bls12_g2_add/msm, zkvm_bls12_pairing, zkvm_bls12_map_fp_to_g1/fp2_to_g2 | bls12_381_curve_add/dbl, bls12_381_complex_* |
ZiskKzgOperations | zkvm_kzg_point_eval, zkvm_sha256 (versioned hash) | BLS12-381 CSRs + sha256_f |
ZiskBls12381OperationsandZiskKzgOperationsare not fields of thePrecompileBackendsbundle (which holds the seven backends above). They are separate singletons thatZiskBinaryWitness.LayerKzgAndBlsBackendswires onto the registry when the fork's precompile set declares KZG point-evaluation (0x0A) or BLS (0x0B) — there is noZiskPrecompileBackends.Instance.Bls12381/.Kzgfield.
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:
| StateTree | HashFunction | Calculator |
|---|---|---|
| Patricia (default) | Keccak | PatriciaStateRootCalculator |
| Binary (EIP-7864) | Blake3 | BinaryStateRootCalculator(Blake3HashProvider) |
| Binary | Poseidon | BinaryStateRootCalculator(ZiskPoseidonHashProvider) |
| Binary | Sha256 | BinaryStateRootCalculator(ZiskSha256HashProvider) |
| Binary | Keccak | BinaryStateRootCalculator(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) —
bflatcompiles all linked.csfiles directly from the EVM.Core, Zisk.Core, and EVM.Zisk source trees. Smaller ELF; use for production proofs. - DLL mode (
--dll) —dotnet buildproduces the managed DLL first andbflatlinks 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
Nethereum.EVM.Core— EVM engine andBinaryBlockWitnessformat.Nethereum.Zisk.Core— Zisk runtime bindings.Nethereum.EVM.Precompiles— the non-zkVM backend bundle this mirrors in shape.