Skip to content

Fold revert-on-reject into the generated step code#98

Open
diegonehab wants to merge 5 commits into
mainfrom
feature/revert-root-hash
Open

Fold revert-on-reject into the generated step code#98
diegonehab wants to merge 5 commits into
mainfrom
feature/revert-root-hash

Conversation

@diegonehab

@diegonehab diegonehab commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

Part of cartesi/machine-emulator#388. Companion to cartesi/machine-emulator#389, which must
be referenced for regenerating the constants and test fixtures.

Problem

The EVM verifiers had no way to accept the canonical state transition of a rejected
input. The revert hash was written by a separate setCheckpointHash-era composition,
and the substitution after a rejection was left to the caller (Dave's
revertIfNeeded, built on the in-repo AdvanceStatus.sol).

Solution

  • SendCmioResponse.sol is regenerated from the emulator. sendCmioResponse takes a
    bytes32 revertRootHash first argument and commits it into the revert root hash leaf
    via EmulatorCompat.writeRevertRootHash as part of the proof
    (EmulatorCompat.getRevertRootHash/setRevertRootHash are renamed to
    readRevertRootHash/writeRevertRootHash to match the emulator naming).
  • UArchReset.sol is regenerated with the reversion folded in. After resetting the
    uarch state, reset() reads iflags.Y, conditionally reads htif.tohost, decodes
    dev/cmd/reason, and on a rejected manual yield calls the new
    EmulatorCompat.revertState, which replaces currentRootHash with the value read
    from the revert leaf. Callers need no substitution logic, Dave's boundary transition
    becomes just step and reset, and its input boundary becomes a single sendCmio
    call.
  • AdvanceStatus.sol is deleted, its only consumer was revertIfNeeded, which the
    fold makes obsolete.
  • EmulatorConstants.sol is regenerated. The yield reason trio is renamed from
    CMIO_YIELD_MANUAL_REASON_* to HTIF_YIELD_MANUAL_REASON_*, and the HTIF field
    shift/mask constants plus HTIF_DEV_YIELD and HTIF_YIELD_CMD_MANUAL are added,
    all queried from the emulator (CMIO_YIELD_REASON_ADVANCE_STATE keeps its name,
    Dave references it).
  • The generator scripts are updated for the new signatures and for prefixing
    EmulatorConstants names in UArchReset.sol.

Tests

The json log parser is generalized to derive per-access sibling counts and to emit the
value followed by the leaf hash for leaf read accesses, so mixed-shape logs replay.
SendCmioResponse.t.sol passes the catalog's initial root hash as the revert root
hash, and UArchReset.t.sol additionally replays the new rejected-reset fixture, whose
final root hash is the recorded revert root hash. All 56 Foundry suites pass against
fixtures regenerated from the emulator PR (Foundry v1.4.3, as pinned by CI).

The emulator now records the revert root hash as part of send_cmio_response,
so the generated SendCmioResponse.sol takes a bytes32 revertRootHash argument
and writes it via EmulatorCompat.writeRevertRootHash. Rename
EmulatorCompat.getRevertRootHash and setRevertRootHash to readRevertRootHash
and writeRevertRootHash to match the emulator naming.

Update generate_SendCmioResponse.sh for the new C++ signature and
explicit-instantiation layout. Fix generate_EmulatorConstants.lua to query
the renamed cartesi.HTIF_YIELD_* Lua constants and regenerate
EmulatorConstants.sol, which also picks up the new UARCH_PRISTINE_STATE_HASH.
The SendCmioResponse test passes the catalog's initialRootHash as the revert
root hash, matching the regenerated test logs.
Regenerate UArchReset.sol from the emulator. After resetting the uarch
state, reset() now reads iflags.Y and conditionally htif.tohost, decodes
the dev/cmd/reason fields, and on a rejected manual yield calls the new
EmulatorCompat.revertState, which replaces currentRootHash with the value
read from the revert root hash leaf. Callers need no substitution logic,
the boundary transition in the dispute becomes just step and reset.

Delete AdvanceStatus.sol. Its only consumer was Dave's revertIfNeeded,
which the fold makes obsolete.

Rename the EmulatorConstants yield reason trio from
CMIO_YIELD_MANUAL_REASON_* to HTIF_YIELD_MANUAL_REASON_*, matching the
emulator naming now that AdvanceStatus is gone, and add the HTIF field
shift and mask constants plus HTIF_DEV_YIELD and HTIF_YIELD_CMD_MANUAL,
all queried from the emulator by generate_EmulatorConstants.lua.
CMIO_YIELD_REASON_ADVANCE_STATE keeps its name, Dave references it.

Update generate_UArchReset.sh to prefix EmulatorConstants names like the
other generators. Generalize the test json parser to derive per-access
sibling counts and to emit the value followed by the leaf hash for leaf
read accesses, so mixed-shape logs replay. UArchReset.t.sol now also
replays the rejected-reset fixture, whose final root hash is the recorded
revert root hash.
@diegonehab diegonehab self-assigned this Jun 10, 2026
@diegonehab diegonehab added the enhancement New feature or request label Jun 10, 2026
@diegonehab diegonehab moved this from Todo to Waiting Review in Machine Unit Jun 10, 2026
@diegonehab diegonehab marked this pull request as draft June 10, 2026 19:56
@diegonehab diegonehab requested a review from mpernambuco June 10, 2026 21:25
@diegonehab diegonehab marked this pull request as ready for review June 10, 2026 21:31
mpernambuco
mpernambuco previously approved these changes Jun 10, 2026

@mpernambuco mpernambuco left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

looks great

@github-project-automation github-project-automation Bot moved this from Waiting Review to Waiting Merge in Machine Unit Jun 10, 2026
The core send_cmio_response can no longer fail. If verification could
fail for a reachable machine state and response, there would be states
for which the honest party cannot produce a log that proves the
resulting state transition. The iflags.Y and response-length checks now
return without touching the state, like the advance-state yield check
already did. The length check also moves ahead of the revert root hash
write, where it can still prevent all state changes. iflags.Y remains
the first access, so a no-op log is never empty, which replay requires.

The host-facing machine::send_cmio_response still refuses misuse
upfront. check_pending_cmio_request takes over the dropped checks,
requiring a manual yield for every response reason and a response that
fits in the rx buffer, with the same error messages as before.
machine::log_send_cmio_response no longer fails for any machine state
or response argument and logs a no-op instead.
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: Waiting Merge

Development

Successfully merging this pull request may close these issues.

2 participants