Skip to content

NEEDS TEAM DECISION — sandbox: events/storage from a caught failed allow_write_ops sandbox persist (EVM divergence); bless-or-change + pinning golden #5

Description

@dohernandez

Re-file of archived genlayerlabs/genvm#329 (stranded read-only by the 2026-07-22 migration). Filed here per repo-split: this is executor/py-std domain.

⚠️ This needs an explicit team decision, not just a code change — the behavior is consensus-relevant, currently unpinned by any test, and diverges from EVM intuition. Whichever way the decision goes, a golden must lock it.

Behavior

When a contract runs a sandbox with allow_write_ops=True and the sandboxed callable fails, but the failure is caught by the parent, every effect the sandboxed code produced before failing — emitted events AND storage writes — persists into the parent execution.

Source: src/wasi/genlayer_sdk.rs, sandbox result handling — after rt::spawn_apply_run returns, the parent unconditionally adopts the sub-VM's state:

self.context.data.accumulator = my_res.vm_data.accumulator;  // events ride the accumulator
self.context.data.storage = my_res.vm_data.storage;

No branch on the sub-VM's outcome, no rollback path. The adoption is symmetric (storage and events together), so the model is self-consistent — it just may not be what anyone consciously chose.

Why it needs a decision

  1. EVM divergence: a reverted sub-call's logs are dropped in EVM along with its state changes. Cross-ecosystem developers will carry the "caught revert ⇒ no logs" intuition into GenVM and be wrong. If persist-on-caught-failure is intended (the symmetric adopt suggests it is), it should be documented behavior in the python-sdk events docs, not incidental.
  2. Unpinned: no test covers emit-then-raise-in-sandbox; a refactor of the sandbox result path could silently flip the semantics either way.

Downstream verification: genlayer-node hit this building gen_getLogs e2e (NOD-322/324) and verified persist-on-caught-failure empirically on the v0.6.0-rc1 bundle (executor v0.3.0-rc7); their analysis concluded intended-by-architecture (their plan doc: .claude/plans/nod-322-gen-events-rpc.md §8 OQ10).

Proposed pinning test

tests/cases/stable/py/events/post_event_sandbox_caught.py: method that, inside an allow_write_ops=True sandbox, emits gl.chain.Event + writes storage, then raises; parent catches; method returns normally. Golden asserts the emission IS in the receipt and the storage write IS visible — pinning today's semantics. If the team instead chooses EVM-style rollback: same fixture flips to asserting absence, plus a changelog entry and major-version consideration (consensus-relevant change).

Ask

  1. Team decision: bless (document + pin) or change (rollback semantics + changelog + version bump).
  2. Pinning fixture per the decision (needs Linux CI to validate; I can write it once the decision lands).

Related: genvm-executor#4 (docstring re-port from the same downstream e2e work), genvm-manager#7 (LLM provider-exhaustion VmError, same requester ecosystem).

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions