Skip to content

Conversation

@ManuelArto
Copy link

@ManuelArto ManuelArto commented Dec 24, 2025

Changes

This PR introduces an initial approach for a proof of concept (PoC) tool, EthProofValidator, located in src/tools. This tool is designed to validate Ethereum block proofs aggregated by https://ethproofs.org/

Key additions:

  • C# Application (EthProofValidator): Orchestrates the fetching of proofs from the Ethproofs API and acts as a wrapper for verification.
  • Rust Native Library (native-zk-verifier): A native library that implements the actual cryptographic verification logic using various zkVM SDKs (SP1, Pico, OpenVM, Zisk).
  • Interop: Implements P/Invoke to bridge the .NET application with the Rust-based verifiers.
  • Docker Support: Added a Dockerfile to build both the Rust dependencies and the .NET application.

Workflow:

  1. Fetches the latest block proofs from ethproofs.org.
  2. Downloads the specific ZK proof and Verification Key (VK).
  3. Routes the proof to the correct verifier implementation (via the native Rust lib).
  4. Validates the execution of the Ethereum block.

Types of changes

What types of changes does your code introduce?

  • Bugfix (a non-breaking change that fixes an issue)
  • New feature (a non-breaking change that adds functionality)
  • Breaking change (a change that causes existing functionality not to work as expected)
  • Optimization
  • Refactoring
  • Documentation update
  • Build-related changes
  • Other: Description

Testing

Requires testing

  • Yes
  • No

Documentation

Requires documentation update

  • Yes
  • No

A README.md has been included within the tools/EthProofValidator directory explaining the project structure and build/run instructions.

Requires explanation in Release Notes

  • Yes
  • No

Remarks

  • Dependencies: This tool introduces a build-time dependency on the Rust toolchain (nightly channel) to compile the native-zk-verifier library.
  • Platform: The build process in EthProofValidator.csproj automatically detects the OS (Linux/Windows/OSX) to load the correct shared library extension (.so, .dll, .dylib).

// The following include Single-GPU (1:100) zk proofs
// var blockIds = new List<long> { 24046700, 24046800, 24046900, 24047000, 24047100, 24047200, 24047300, 24047400, 24047500, 24047600, 24047700 };

const long LatestBlockId = 24080984;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why hardcode these?


const long LatestBlockId = 24080984;
const int BlockCount = 25;
var blockIds = Enumerable.Range((int)(LatestBlockId - BlockCount), BlockCount+1).Select(i => i).ToList();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: generally use explicit types instead of var for clarity

var results = await Task.WhenAll(tasks);

int validCount = results.Count(r => r == 1);
int totalCount = results.Count(r => r != -1); // Exclude skipped proofs
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no need to iterate twice here

int totalCount = results.Count(r => r != -1); // Exclude skipped proofs

Console.WriteLine(" -----------------------------");
Console.WriteLine((double)validCount / totalCount >= 0.5
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: could use int arithmetic


private async Task<int> ProcessProofAsync(ProofMetadata proof, ZkProofVerifier? verifier)
{
if (verifier == null)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if (verifier == null)
if (verifier is null)

Console.WriteLine($" Proof {proof.ProofId,-10} : {(isValid ? "✅ Valid" : "❌ Invalid")} ({verifier.ZkType}, {sw.ElapsedMilliseconds} ms)");
return isValid ? 1 : 0;
}
catch (Exception ex)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

catch specific exception type

: $"❌ BLOCK #{blockId} REJECTED ({validCount}/{totalCount})");
}

private async Task<int> ProcessProofAsync(ProofMetadata proof, ZkProofVerifier? verifier)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

returning enum type seems clearer than int here

{
return await _httpClient.GetFromJsonAsync<List<ClusterVerifier>>("/api/v0/verification-keys/active");
}
catch (Exception ex)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

catch specific exception


public ZkProofVerifier? GetVerifier(string clusterId)
{
_verifiers.TryGetValue(clusterId, out var verifier);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

handle case where not found in dictionary?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the method is intentionally designed to return null to indicate a cache miss. This allows the caller (i.e. BlockValidator) to handle the missing verifier by calling TryAddVerifierAsync, which performs the lazy-loading of verification keys from the API

@ManuelArto ManuelArto requested a review from Marchhill January 7, 2026 17:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants