Skip to content

Perna/solidity step - #390

Open
mpernambuco wants to merge 74 commits into
mainfrom
perna/solidity-step
Open

Perna/solidity step#390
mpernambuco wants to merge 74 commits into
mainfrom
perna/solidity-step

Conversation

@mpernambuco

@mpernambuco mpernambuco commented Jun 16, 2026

Copy link
Copy Markdown
Collaborator

One binary step log, verified by host, zkVM, and Solidity

Every provable state transition of the machine (N machine cycles, N uarch cycles, a
uarch reset, a CMIO response) is now recorded in one binary step-log format, and the
same log is verified by three independent implementations: the C++ host, the RISC0
zkVM guest, and a Solidity library that lives in this repo and is generated from the
emulator's own C++ sources. The log is a witness only (the pages touched plus the
hashes of the untouched subtrees); the caller supplies the claimed state hash before
and the cycle count, and every verifier returns the state hash after for the caller
to compare. Logs are passed around as in-memory buffers; only the command line writes
them to files. The per-access JSON log, its log_type options, and the separate
machine-solidity-step repo are gone.

Changes

  • Binary step-log format shared by log_step, log_step_uarch, log_reset_uarch
    and log_send_cmio_response; the JSON access log and ACCESS_LOG_TYPE_* are removed.
  • Verifiers take the claimed root hash before and the cycle count as arguments and
    return the obtained root hash after; the log itself carries no claims.
  • solidity-step/: on-chain verifier for uarch steps, uarch resets and CMIO responses.
    UArchStep.sol, UArchReset.sol and SendCmioResponse.sol are transpiled from the
    C++ algorithm sources and checked for drift in CI; fixtures are recorded by the
    same Lua recorders the emulator tests use.
  • RISC0 guest replays the new format (input: cycle count followed by the log bytes;
    journal: root hash before, cycle count, root hash after).
  • Rejected inputs: a uarch reset or a step that leaves the machine on a rejected
    yield verifies to the recorded revert root hash.
  • Log functions return the step log as a memory buffer (step_log_data; log_step_result
    and log_step_uarch_result pair it with the break reason) and verify functions take one;
    no filenames in the C++, C, Lua, or JSON-RPC APIs.
  • New dump_step_uarch prints a human-readable replay of a uarch step log.
  • Shared record and replay cores: step_log_recorder collects the witness for both
    recorders; step_log owns decoding, validation and lookups for both replayers.
  • Fixture manifests (_manifest.csv) carry the recorded claims for every fixture;
    step_log_manifest.lua claims <dir> <name> reads them from makefiles.

Building and testing the new pieces

Both stacks sit beside the emulator and record their fixtures with it, so they need the
emulator and the riscv test binaries built as usual. Toolchain installs (Foundry, RISC Zero)
are in the README's "From Sources" requirements.

Solidity verifier:

make -C solidity-step                 # forge build (installs forge-std on first run)
make -C solidity-step fixtures        # record the uarch step-log fixtures with the emulator
make -C solidity-step test            # forge test
make -C solidity-step check-gen-all   # generated .sol files match the C++ sources

RISC0 (RISC0_REPRODUCIBLE_BUILD=0 builds the guest natively instead of in Docker):

make -C risc0 fixtures                # record the machine step-log fixtures
make -C risc0                         # guest ELF + host CLI
RISC0_DEV_MODE=1 make -C risc0 test-dev-mode   # fast replay tests, fake receipts
make -C risc0 test                    # adds the real prove -> compress -> verify-seal pipeline

Neither is required for the core emulator: make, make test and make clean at the root
work without Foundry or the risc0 toolchain installed.

API changes

C++

before  interpreter_break_reason log_step(uint64_t mcycle_count, const std::string &filename)
        access_log log_step_uarch(const access_log::type &log_type)
        access_log log_reset_uarch(const access_log::type &log_type)
        access_log log_send_cmio_response(reason, data, length, revert_root_hash, const access_log::type &log_type)
        machine_hash verify_step(root_hash_before, const std::string &log_filename, mcycle_count)
        machine_hash verify_step_uarch(root_hash_before, const access_log &log)
        machine_hash verify_reset_uarch(root_hash_before, const access_log &log)
        machine_hash verify_send_cmio_response(reason, data, length, root_hash_before, const access_log &log, revert_root_hash)

after   log_step_result log_step(uint64_t mcycle_count)                  // {step_log_data log; interpreter_break_reason break_reason;}
        log_step_uarch_result log_step_uarch(uint64_t uarch_cycle_count)  // {step_log_data log; uarch_interpreter_break_reason break_reason;}
        step_log_data log_reset_uarch()
        step_log_data log_send_cmio_response(reason, data, length, revert_root_hash)
        machine_hash verify_step(root_hash_before, std::span<const unsigned char> log, mcycle_count)
        machine_hash verify_step_uarch(root_hash_before, std::span<const unsigned char> log, uarch_cycle_count)
        machine_hash verify_reset_uarch(root_hash_before, std::span<const unsigned char> log)
        machine_hash verify_send_cmio_response(reason, data, length, root_hash_before, std::span<const unsigned char> log, revert_root_hash)

step_log_data is std::vector<unsigned char>; the same signatures appear on i_machine (local and jsonrpc).

New, and deliberately not a machine method: cartesi::dump_step_uarch(std::span<const unsigned char> log,
uint64_t uarch_cycle_count) in src/step-dumper.hpp. It replays a uarch step log into a human-readable
dump and is kept out of the C API so its output format does not become part of the stable ABI; the Lua
binding calls it directly.

Lua

before  local access_log = machine:log_step_uarch(log_type)
        local access_log = machine:log_reset_uarch(log_type)
        local access_log = machine:log_send_cmio_response(reason, data, revert_root_hash, log_type)
        local break_reason = machine:log_step(mcycle_count, filename)
        cartesi.machine:verify_step_uarch(root_hash_before, access_log)
        cartesi.machine:verify_reset_uarch(root_hash_before, access_log)
        cartesi.machine:verify_send_cmio_response(reason, data, root_hash_before, access_log, revert_root_hash)
        cartesi.machine:verify_step(root_hash_before, filename, mcycle_count)

after   local log, break_reason = machine:log_step_uarch(uarch_cycle_count)
        local log = machine:log_reset_uarch()
        local log = machine:log_send_cmio_response(reason, data, revert_root_hash)
        local log, break_reason = machine:log_step(mcycle_count)
        cartesi.machine:verify_step_uarch(root_hash_before, log, uarch_cycle_count)
        cartesi.machine:verify_reset_uarch(root_hash_before, log)
        cartesi.machine:verify_send_cmio_response(reason, data, root_hash_before, log, revert_root_hash)
        cartesi.machine:verify_step(root_hash_before, log, mcycle_count)
        cartesi.machine:dump_step_uarch(log, uarch_cycle_count)          -- new

C API

before  cm_log_step_uarch(m, log_type, const char **log)
        cm_log_reset_uarch(m, log_type, const char **log)
        cm_log_send_cmio_response(m, reason, data, length, revert_root_hash, log_type, const char **log)
        cm_log_step(m, mcycle_count, const char *log_filename, break_reason)
        cm_verify_step_uarch(m, root_hash_before, const char *log, obtained_root_hash)
        cm_verify_reset_uarch(m, root_hash_before, const char *log, obtained_root_hash)
        cm_verify_send_cmio_response(m, reason, data, length, root_hash_before, const char *log, revert_root_hash, obtained_root_hash)
        cm_verify_step(root_hash_before, const char *log_filename, mcycle_count, obtained_root_hash)

after   cm_log_step_uarch(m, uarch_cycle_count, const uint8_t **log, uint64_t *log_length, uarch_break_reason)
        cm_log_reset_uarch(m, const uint8_t **log, uint64_t *log_length)
        cm_log_send_cmio_response(m, reason, data, length, revert_root_hash, const uint8_t **log, uint64_t *log_length)
        cm_log_step(m, mcycle_count, const uint8_t **log, uint64_t *log_length, break_reason)
        cm_verify_step_uarch(m, root_hash_before, const uint8_t *log, uint64_t log_length, uarch_cycle_count, obtained_root_hash)
        cm_verify_reset_uarch(m, root_hash_before, const uint8_t *log, uint64_t log_length, obtained_root_hash)
        cm_verify_send_cmio_response(m, reason, data, length, root_hash_before, const uint8_t *log, uint64_t log_length, revert_root_hash, obtained_root_hash)
        cm_verify_step(root_hash_before, const uint8_t *log, uint64_t log_length, mcycle_count, obtained_root_hash)

JSON-RPC

before  machine.log_step_uarch(log_type) -> AccessLog        machine.verify_step_uarch(root_hash_before, log)
        machine.log_reset_uarch(log_type) -> AccessLog       machine.verify_reset_uarch(root_hash_before, log)
        machine.log_send_cmio_response(reason, data, revert_root_hash, log_type) -> AccessLog
        machine.log_step(mcycle_count, filename) -> break reason
        machine.verify_step(root_hash_before, filename, mcycle_count)

after   machine.log_step_uarch(uarch_cycle_count) -> {log, break_reason}
        machine.log_reset_uarch() -> log
        machine.log_send_cmio_response(reason, data, revert_root_hash) -> log
        machine.log_step(mcycle_count) -> {log, break_reason}
        machine.verify_*(..., log, ...) with the log as a base64 string

Command-line changes

before  --log-step-uarch                     advance one uarch step and print the access log
        --log-reset-uarch                    reset the uarch and print the access log

after   --log-step-uarch=<filename>[,count:<uarch-cycle-count>][,dump]
                                             log <count> uarch cycles (default 1) to a binary
                                             step log file; ",dump" also prints a readable replay
        --log-reset-uarch=<filename>         reset the uarch and write the binary step log
        --log-send-cmio-response=<filename>,reason:<n>,data:<text>|data-file:<path>[,encoding:hex|base64|utf8]
                                             send a CMIO response and write the binary step log (new)

unchanged
        --log-step=<filename>,count:<mcycle-count>

@mpernambuco
mpernambuco force-pushed the perna/solidity-step branch 2 times, most recently from 2828860 to 2a5cbad Compare July 7, 2026 14:56
@edubart edubart added the enhancement New feature or request label Jul 7, 2026
@mpernambuco
mpernambuco force-pushed the perna/solidity-step branch 2 times, most recently from f55f233 to 07e7085 Compare July 11, 2026 17:10
@mpernambuco
mpernambuco marked this pull request as ready for review July 21, 2026 14:29
@mpernambuco
mpernambuco force-pushed the perna/solidity-step branch 2 times, most recently from f2ea138 to 68c3f5c Compare July 27, 2026 19:58

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔵 Needs a closer look

It spans low-level replay/verification semantics across C++/Lua/JSON-RPC/Solidity/RISC0, and it includes a correctness-sensitive guest-side buffer alignment change that needs fixes before the pipeline is reliably portable.

Pull request overview

This PR modernizes the step-log pipeline end-to-end by making step logs a “pure witness” byte blob (no embedded before/after claims), updating the emulator APIs to return/consume log bytes (instead of filenames/access-log objects), and adding an in-tree Solidity verifier (solidity-step/) that replays one uarch step/reset/send_cmio_response on-chain against those logs.

Changes:

  • Switch step logging/verifying APIs (C++/C/Lua/JSON-RPC) to return/accept binary log bytes and remove file-based/log-object plumbing.
  • Introduce solidity-step/: generators + Foundry tests + fixtures to validate on-chain replay of emulator-produced binary step logs.
  • Update RISC0 fixture generation/pipeline and add fuzzing coverage for the binary step-log decoder.
File summaries
File Description
uarch/uarch-run.cpp Terminology updates (“microarchitecture” → “uarch”).
uarch/uarch-ecall.h Move ECALL wrappers to inline, register-pinned asm for performance.
uarch/uarch-ecall.c Removed; ECALL wrappers now header-only.
uarch/README.md Update directory description to “uarch”.
uarch/Makefile Drop removed uarch-ecall.c and update comments.
tests/uarch/README.md Update wording to “uarch”.
tests/scripts/record-uarch-step-fixtures.sh New script to record uarch step-log fixtures for Solidity verifier tests.
tests/scripts/record-machine-step-fixtures.sh New script to record machine step-log fixtures for RISC0 pipeline/tests.
tests/scripts/collect-uarch-test-logs.sh Removed legacy uarch log collection helper.
tests/Makefile Remove legacy step-log targets; add uarch replay target and update parallel test list.
tests/machine/src/step_max_pages.S New worst-case page-footprint program for step-log sizing/coverage.
tests/machine/src/step_max_pages_flush.S New worst-case TLB-flush footprint program for step-log sizing/coverage.
tests/lua/spec-json.lua Update schema override test to use current machine dictionary types.
tests/lua/spec-collect-hashes.lua Adapt tests to new log_step/verify_step signatures and uarch collection args.
tests/lua/spec-cm-cli.lua Update CLI tests for new log argument formats and add cmio payload encoding coverage.
tests/lua/run-rv64i-arch-test.lua Update comments for “uarch” terminology.
tests/lua/record-uarch-multi-cycle.lua New generator for one- vs two-cycle uarch fixtures (pins “exactly one step” property).
tests/lua/record-one-mcycle.lua New standalone generator for the single 1-mcycle machine step log fixture.
tests/lua/record-adversarial-machine.lua New generator for structurally-invalid machine logs to assert rejection behavior.
tests/lua/mcycle-overflow.lua Adapt overflow tests to in-memory log bytes API.
tests/lua/machine-test.lua Update uarch stepping tests to explicit log_step_uarch(1) helper.
tests/lua/log-with-mtime-transition.lua Update to new verify_step_uarch(root, log, count) signature.
tests/lua/htif-yield.lua Update help text/comments from microarchitecture → uarch.
tests/lua/create-step-logs.lua Removed legacy file-based step log fixture generator.
tests/lua/cartesi/tests/step_log_manifest.lua New shared CSV manifest format for step-log fixture claims/recipes.
tests/fuzz/Makefile Add step-log fuzzer target + seed corpus wiring from recorded fixtures.
tests/fuzz/fuzz-step-log.cpp New libFuzzer harness for binary step-log decoder and root recomputation.
tests/fuzz/fuzz-interpret-step.cpp Update fuzz harness to new C API for log bytes and lifetimes.
src/variant-hasher.hpp Update keccak256 comment to reference uarch.
src/uarch-step.hpp Rename/expand replay/record state-access types and template instantiations.
src/uarch-step.cpp Update includes and explicit instantiations for new replay/record step accessors.
src/uarch-solidity-compat.hpp Improve Solidity-compat helpers (revert root hash write path; require throws).
src/uarch-reset-state.hpp Update forward declarations and explicit instantiations for new replay/record types.
src/uarch-reset-state.cpp Update includes and explicit instantiations for new replay/record types.
src/uarch-record-step-state-access.hpp New state-access adapter that records uarch steps/resets into a step_log_recorder.
src/uarch-processor-state.hpp Update terminology in file header comment.
src/uarch-interpret.hpp Update comment to “uarch interpreter”.
src/uarch-interpret.cpp Add explicit instantiations for record/replay uarch interpret paths.
src/uarch-defines.h Update comments for uarch terminology and ecall codes.
src/uarch-constants.hpp Update comment for uarch initial values.
src/step-dumper.hpp New host-only dumper API for producing human-readable uarch-step replays.
src/step-dumper.cpp Implement dump_step_uarch by replaying a step log with a printing state accessor.
src/state-access.hpp Remove revert-root-hash write hook from state_access (paired with interface changes).
src/soft-float.hpp Update comment to “uarch” (build-flag terminology).
src/send-cmio-response.hpp Switch record/replay access types to new step-log-based record/replay state accessors.
src/send-cmio-response.cpp Update explicit instantiations to new record/replay step state accessors.
src/poor-type-name.hpp Update comment to “uarch”.
src/Makefile Add step-dumper.o to libcartesi build.
src/machine-config.hpp Update comment to “Uarch config”.
src/machine-config.cpp Add <bit> include (supporting config logic changes).
src/local-machine.hpp Switch i_machine overrides to return log bytes + accept spans for verification.
src/local-machine.cpp Implement updated local-machine APIs around in-memory step logs.
src/jsonrpc-machine.hpp Update jsonrpc machine interface to log-bytes APIs.
src/jsonrpc-machine.cpp Implement new JSON-RPC request/response shapes for log bytes and verification.
src/jsonrpc-log-step-result.hpp New result types for JSON-RPC log_step/log_step_uarch returning bytes + break reason.
src/jsonrpc-cmio-request.hpp Replace access_data with std::vector<uint8_t> payload in JSON-RPC cmio request.
src/i-uarch-state-access.hpp Update terminology in interface comment.
src/i-state-access.hpp Update write_memory_with_padding docs; remove write_revert_root_hash from interface.
src/i-accept-dirty-pages.hpp Update comments from microarchitecture → uarch.
src/cm-internal.hpp Add helper to store binary temp data for C API lifetimes.
src/clua-cartesi.cpp Export hash-function constants, address-range constants, and step-log signature to Lua.
src/assert-printf.hpp Update comment to “Uarch-dependent includes…”.
src/address-range-defines.h Update comments from microarchitecture → uarch for uarch ranges.
solidity-step/tools/gen-emulator-constants.lua New generator for Solidity constants from the emulator’s Lua module.
solidity-step/test/VerifyUarchTestsPerCycle.t.sol New Foundry test: replay per-cycle uarch corpus and measure gas.
solidity-step/test/VerifyUarchReset.t.sol New Foundry test: replay reset_uarch fixtures via Verify.verifyReset.
solidity-step/test/VerifyUarchMultiCycle.t.sol New Foundry test: assert verifier executes exactly one uarch step.
solidity-step/test/VerifySendCmioResponse.t.sol New Foundry test: replay send_cmio_response fixtures including payload encodings.
solidity-step/test/UarchFixedPoint.t.sol New Foundry test: fixed-point semantics (halt/overflow) for uarchStep.
solidity-step/test/StepLogDecode.t.sol New Foundry test: decode/structural validation + corruption rejection cases.
solidity-step/test/RejectsPagelessLog.t.sol New Foundry test: ensure decode rejects a zero-page forged-witness construction.
solidity-step/test/RejectsMutatedLog.t.sol New Foundry fuzz test: single-byte mutations must revert for logs/resets.
solidity-step/test/HashTree.t.sol New Foundry tests for Merkle hashing primitives vs independent oracles.
solidity-step/src/Verify.sol New Solidity library that verifies step/reset/send_cmio_response transitions.
solidity-step/src/UArchReset.sol Generated Solidity from C++ uarch reset handler.
solidity-step/src/SendCmioResponse.sol Generated Solidity from C++ send_cmio_response handler.
solidity-step/src/HashTree.sol New Solidity Merkle hashing utilities (incl. padded hashing).
solidity-step/README.md New documentation for the Solidity verifier library and generated sources.
solidity-step/Makefile New build/test/fixtures/gen/check targets for Solidity verifier.
solidity-step/foundry.toml New Foundry configuration (via_ir, prague, fixtures read perms, limits).
solidity-step/.gitignore Ignore Foundry outputs and recorded fixtures.
risc0/step-log-util.lua Removed legacy step-log header reader (replaced by shared manifest tooling).
risc0/solidity/test/Groth16Verification.t.sol Update SPDX to Apache-2.0 header.
risc0/solidity/src/CartesiStepVerifier.sol Update SPDX to Apache-2.0 header.
risc0/solidity/README.md Update instructions to reflect recorded step-log fixture flow.
risc0/solidity/Makefile Add coverage; make ImageID.sol generation stable; update fixture prerequisite checks.
risc0/solidity/foundry.toml Ignore warnings from vendored libs and configure error-code ignores.
risc0/rust/methods/guest/src/main.rs Change guest input format (cycle count prefix) and replay call signature.
risc0/rust/methods/guest/Cargo.toml Minor dependency comment cleanup.
risc0/rust/methods/guest/Cargo.lock Dependency bumps (rand, rustls-webpki).
risc0/rust/methods/guest/build.rs Whitespace cleanup.
risc0/rust/methods/build.rs Enforce hermetic guest build and rerun triggers for embedded methods.
risc0/rust/Makefile Use --locked; add coverage/report + guest formatting targets; update fixtures path.
risc0/rust/cartesi-risc0/tests/test_reject_fixtures.rs New tests asserting guest rejects structurally-invalid logs and host rejects bad beliefs.
risc0/rust/cartesi-risc0/tests/Makefile Removed obsolete fixture helper Makefile.
risc0/rust/Cargo.lock Dependency bumps (rand, rustls-webpki).
risc0/rust/.gitignore Ignore coverage artifacts.
risc0/README.md Expand prerequisites and document fixtures + coverage behavior.
risc0/Makefile Rework fixtures pipeline to use shared manifest recorders and add coverage targets.
risc0/cpp/risc0-replay-steps.cpp Take cycle count as input, saturate on overflow, and return replay-obtained roots.
risc0/cpp/Makefile Pin cpp toolchain version; add depfile generation; fix dump define typo; simplify includes.
README.md Document luaposix test dependency, pinned rzup toolchain versions, and solidity-step usage.
Makefile Update help text terminology and include solidity-step clean in top-level clean.
doc/recipes/vgu.lua Update docs to reflect binary step logs rather than access logs.
doc/recipes/verify-uarch-step.lua Update recipe to record binary log, mutate via simple modes, verify via new API.
doc/recipes/verification-game.lua Update game schema + commit/verify paths to use binary logs.
doc/recipes/rolling-verification-game.lua Update game schema + commit/verify paths to use binary logs.
doc/recipes/dump-uarch-step.lua Switch from access-log printing to dump_step_uarch(binary_log).
doc/recipes/dishonest.lua Make composite machine forward varargs for updated log_step_uarch/log_reset_uarch.
CHANGELOG.md Document API changes, CLI option changes, and step-log wire-format changes.
.gitignore Ignore built fuzz binaries and additional local artifacts.
Review details
  • Files reviewed: 129/161 changed files
  • Comments generated: 1
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread risc0/rust/methods/guest/src/main.rs Outdated
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

Status: Todo

Development

Successfully merging this pull request may close these issues.

3 participants