feat: add support for EEST blockchain test fixtures#286
Conversation
There was a problem hiding this comment.
The previous crates/benchmark-runner/src/stateless_validator.rs file was getting a bit loaded. I moved to a module with a bit more organized structure.
I'll simply comment on the new stuff.
| #[derive(Debug, Clone)] | ||
| pub(crate) struct EestStatelessFixture { |
There was a problem hiding this comment.
Now we have an enum for BenchmarkFixture:
#[derive(Debug, Clone)]
pub(crate) enum BenchmarkFixture {
Legacy(StatelessValidationFixture),
Eest(EestStatelessFixture),
}
StatelessValidationFixture is the usual format. This new EestStatelssFixture is the one coming from EEST variant. It mainly contains basic data extracted from parsing EEST fixture that will be also used as Metadata for zkevm-metrics outputs.
Example of output:
{
"name": "eest__tests_amsterdam_eip8025_optional_proofs_test_witness_state_writes_py_test_witness_state_reverted_sstore_still_contains_storage_proof_fork_Amsterdam-blockchain_test__block0",
"timestamp_completed": "2026-05-21T17:48:02.645237188Z",
"metadata": {
"block_index": 0,
"block_number": 1,
"block_used_gas": 26012,
"chain_id": 1,
"fixture_format": "eest",
"network": "Amsterdam",
"original_test_name": "tests/amsterdam/eip8025_optional_proofs/test_witness_state_writes.py::test_witness_state_reverted_sstore_still_contains_storage_proof[fork_Amsterdam-blockchain_test]",
"source_path": "blockchain_tests/for_amsterdam/amsterdam/eip8025_optional_proofs/witness_state_writes/witness_state_reverted_sstore_still_contains_storage_proof.json"
},
"execution": {
"crashed": {
"reason": "zkVM method error: Emulator panicked: Mem::read() section not found for addr: 0=0 with width: 8"
}
}
}
| pub(crate) stateless_output_bytes: Vec<u8>, | ||
| } | ||
|
|
||
| #[derive(Debug, Deserialize)] |
There was a problem hiding this comment.
EEST fixture parsing structs.
| #[derive(Debug, Clone)] | ||
| pub(crate) enum BenchmarkFixture { | ||
| Legacy(Box<StatelessValidationFixture>), | ||
| Eest(EestStatelessFixture), | ||
| } |
There was a problem hiding this comment.
Note now BenchmarkFixture is an enum.
| fn load_benchmark_fixtures(path: &Path, input_root: &Path) -> Result<Vec<BenchmarkFixture>> { | ||
| let content = std::fs::read(path)?; | ||
| let value: serde_json::Value = serde_json::from_slice(&content) | ||
| .with_context(|| format!("Failed to parse {}", path.display()))?; | ||
|
|
||
| if value.get("stateless_input").is_some() { | ||
| let fixture = serde_json::from_value(value) | ||
| .with_context(|| format!("Failed to parse legacy fixture {}", path.display()))?; | ||
| return Ok(vec![BenchmarkFixture::Legacy(Box::new(fixture))]); | ||
| } | ||
|
|
||
| load_eest_benchmark_fixtures(value, path, input_root) | ||
| .map(|fixtures| fixtures.into_iter().map(BenchmarkFixture::Eest).collect()) | ||
| } |
There was a problem hiding this comment.
The automatic detection if we are in the legacy or eest format.
There was a problem hiding this comment.
As said, mostly refactoring to make it more modular.
| pub(crate) fn stateless_validator_input_from_fixture( | ||
| fixture: BenchmarkFixture, | ||
| el: ExecutionClient, | ||
| ) -> Result<Box<dyn GuestFixture>> { | ||
| match fixture { | ||
| BenchmarkFixture::Legacy(fixture) => match el { | ||
| ExecutionClient::Reth => reth_input_from_fixture(*fixture), | ||
| ExecutionClient::Ethrex => ethrex_input_from_fixture(*fixture), | ||
| }, | ||
| BenchmarkFixture::Eest(fixture) => raw_eest_input_from_fixture(fixture), | ||
| } | ||
| } |
There was a problem hiding this comment.
Gist-ish of the PR: depending on the BenchmarkFixture variant, we do the usual for Legacy, or call new raw_eest-input_from_fixture(...).
| block_number: fixture.block_number, | ||
| block_used_gas: fixture.block_used_gas, | ||
| }; | ||
| let expected_public_values = Sha256::digest(fixture.stateless_output_bytes).to_vec(); |
There was a problem hiding this comment.
Nit: here we explicitly Sha256 for now. But I think in theory, zkVMs are expected to output raw bytes that the guest program wrote to output.
@han0110, do you know if this is something we could assume in new Zisk v0.18.0? Or maybe we should still keep this Sha256-ing for a while.
There was a problem hiding this comment.
But I think in theory, zkVMs are expected to output raw bytes that the guest program wrote to output.
Yes but currently only SP1 and RISC Zero support that, others have restricted size:
- Airbender - 32 bytes
- OpenVM - 32 bytes (configurable)
- ZisK - 256 bytes
So I think we have to stick with the sha256 output for a while, or we drop the support of Airbender in ere-guests and configure OpenVM to support 256 bytes as well (assuming the public values will not exceed 256 bytes, where currently it's 41 bytes)
There was a problem hiding this comment.
Cool, let's keep the Sha256, and we can see how things evolve.
@marcinbugaj, is this something Zisk is planning to support? Since I think from zkEVM standards POV it should be required the return exactly what the guest program wrote as output?
han0110
left a comment
There was a problem hiding this comment.
LGTM! (looks like the fixture name is a bit tricky to handle when needing to use it for file name)
| block_number: fixture.block_number, | ||
| block_used_gas: fixture.block_used_gas, | ||
| }; | ||
| let expected_public_values = Sha256::digest(fixture.stateless_output_bytes).to_vec(); |
There was a problem hiding this comment.
But I think in theory, zkVMs are expected to output raw bytes that the guest program wrote to output.
Yes but currently only SP1 and RISC Zero support that, others have restricted size:
- Airbender - 32 bytes
- OpenVM - 32 bytes (configurable)
- ZisK - 256 bytes
So I think we have to stick with the sha256 output for a while, or we drop the support of Airbender in ere-guests and configure OpenVM to support 256 bytes as well (assuming the public values will not exceed 256 bytes, where currently it's 41 bytes)
Yeah, it is a bit tricky not to pass 256 in length. Before, I used a shorter style but had to add a hash to the name to avoid potential collisions, which was ugly. I think it should be fine. If we discover it violates the length max, we should prob switch to that idea too. |
|
Might leave this open for some extra mins/hours -- I'm using this branch to test a dashboard automation reg EEST fixtures direct run. |
This PR:
ere-hoststo directly consume EEST tests new format fromexecution-specs@projects/zkevm. It is the usual fixture format, but for each block containsstatelessInputBytesandstatelessOutputBytes.Legacy, and new oneEest). i.e. this is not a breaking change.Eestfixtures are detected, we directly usestateless{Input|Output}Bytesfor the guest program run (i.e. running EEST fixtures can be done directly without a previously requiredwitness-generator-cli).zkevm-metricsmetadata for an EEST run contains more details to easily map to original EEST file.Example run output in
zkevm-metrics: