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 (sandbox catch), every effect the sandboxed code produced before failing — emitted events AND storage writes — persists into the parent execution.
Source: executor/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;
There is no branch on the sub-VM's outcome and no rollback path. The adoption is symmetric (storage and events together), so the model is self-consistent.
Why raise it
- It diverges from EVM, where a reverted sub-call's logs are dropped along with its state changes. Cross-ecosystem developers will carry the EVM intuition ("caught revert ⇒ no logs") into GenVM and be wrong. If the divergence is intended — and the symmetric adopt suggests it is — it should be documented behavior, not incidental.
- No test pins it.
tests/cases/stable/py/events/ has no emit-then-raise-in-sandbox fixture, so a future refactor of the sandbox result path could silently flip this semantics either way. Whichever way the team decides, a test should lock it.
Downstream verification: the genlayer-node side hit this while building gen_getLogs e2e (NOD-322/324) and verified the persist-on-caught-failure behavior empirically on the v0.6.0-rc1 bundle (executor v0.3.0-rc7); their analysis concluded intended-by-architecture. Their open question doc: node repo plan .claude/plans/nod-322-gen-events-rpc.md §8 OQ10.
Proposed test (sketch)
tests/cases/stable/py/events/post_event_sandbox_caught.py: contract method that, inside allow_write_ops=True sandbox, emits gl.chain.Event + writes storage, then raises; parent catches, method returns normally. Golden asserts: the emission IS present in the receipt and the storage write IS visible — pinning today's semantics. If the team instead decides to adopt EVM-style rollback, the same fixture flips to asserting absence (and the change needs a changelog entry + major consideration, since it's consensus-relevant behavior).
Ask
- Team bless-or-change the semantics (decision, then doc note in the events section of the python-sdk docs either way).
- Add the pinning fixture (needs a Linux build env or CI to validate; happy to write it once the decision lands).
Related: #327 / #328 (same downstream e2e work surfaced both).
Behavior
When a contract runs a sandbox with
allow_write_ops=Trueand the sandboxed callable fails, but the failure is caught by the parent (sandbox catch), every effect the sandboxed code produced before failing — emitted events AND storage writes — persists into the parent execution.Source:
executor/src/wasi/genlayer_sdk.rs, sandbox result handling — afterrt::spawn_apply_runreturns, the parent unconditionally adopts the sub-VM's state:There is no branch on the sub-VM's outcome and no rollback path. The adoption is symmetric (storage and events together), so the model is self-consistent.
Why raise it
tests/cases/stable/py/events/has no emit-then-raise-in-sandbox fixture, so a future refactor of the sandbox result path could silently flip this semantics either way. Whichever way the team decides, a test should lock it.Downstream verification: the genlayer-node side hit this while building
gen_getLogse2e (NOD-322/324) and verified the persist-on-caught-failure behavior empirically on the v0.6.0-rc1 bundle (executor v0.3.0-rc7); their analysis concluded intended-by-architecture. Their open question doc: node repo plan.claude/plans/nod-322-gen-events-rpc.md§8 OQ10.Proposed test (sketch)
tests/cases/stable/py/events/post_event_sandbox_caught.py: contract method that, insideallow_write_ops=Truesandbox, emitsgl.chain.Event+ writes storage, then raises; parent catches, method returns normally. Golden asserts: the emission IS present in the receipt and the storage write IS visible — pinning today's semantics. If the team instead decides to adopt EVM-style rollback, the same fixture flips to asserting absence (and the change needs a changelog entry + major consideration, since it's consensus-relevant behavior).Ask
Related: #327 / #328 (same downstream e2e work surfaced both).