Files

7.2 KiB

Solution: LP-0012 — Structured Event System for LEZ Programs

Submitted by: bristinWild

Summary

This submission implements a complete structured event system for the Logos Execution Zone (LEZ). Programs can emit typed, Borsh-encoded events during execution via emit_event(). Events are preserved in transaction receipts on both success and failure paths, retrievable by clients via a new getTransactionReceipt RPC method. A CLI decoder renders events in human-readable form for debugging and indexing.

Repository

  • Repo: https://github.com/bristinWild/logos-execution-zone
  • Commit: 6dca25ba (branch: main)
  • Key files:
    • lez-events/src/lib.rs — guest SDK (emit_event, EventRecord, drain_events)
    • nssa/core/src/program.rsProgramOutput.events, write_nssa_outputs_on_failure
    • nssa/src/program.rs — host-side event extraction, ExitCode handling
    • nssa/src/error.rsNssaError::ProgramExecutionFailed with partial_output
    • sequencer/core/src/block_store.rsRejectedTx, RejectedTxStore
    • sequencer/core/src/lib.rs — event preservation on tx failure
    • sequencer/service/rpc/src/lib.rsTxReceipt, TxStatus, getTransactionReceipt
    • sequencer/service/src/service.rs — RPC implementation
    • lez-events-decoder/src/main.rs — CLI decoder tool
    • examples/emit_event_demo/ — success + failure path example programs
    • docs/event-format.md — schema specification

Approach

Event Model

Each emitted event is an EventRecord containing a caller-defined discriminant (u32), a monotonically increasing sequence number for ordering, and a Borsh-encoded payload:

pub struct EventRecord {
    pub discriminant: u32,  // program-defined event type
    pub sequence: u32,       // emission order within transaction
    pub payload: Vec<u8>,    // Borsh-encoded event data
}

Borsh was chosen per the team's own direction (issues #131, #260) for determinism and compactness. program_id attribution is injected by the sequencer host at receipt time (programs cannot read their own ID — issue #347).

Guest SDK

Programs call emit_event(discriminant, &payload) from the lez-events crate. Events are buffered in a thread-local and flushed into ProgramOutput.events when write_nssa_outputs() is called.

Failure-Path Persistence

For failure cases, programs call write_nssa_outputs_on_failure() before panic!(). This commits (FAILURE_SENTINEL, Vec<EventRecord>) to the Risc0 journal using a sentinel value (0xDEAD_FA11) so the host can distinguish it from a success ProgramOutput. The sequencer detects the non-zero exit code, extracts events from the partial journal, and stores them in RejectedTxStore.

Dev-mode note: In RISC0_DEV_MODE=1, the Risc0 executor does not expose the journal after a guest panic. Failure-path recovery works in production ZK mode.

Sequencer Integration

  • RejectedTxStore: in-memory store keyed by tx hash, preserving error message + events
  • getTransactionReceipt RPC: returns TxStatus (Included/Rejected/Pending/Unknown) + events
  • Events from included transactions come from ProgramOutput.events in the block
  • Events from rejected transactions come from RejectedTxStore

Decoder CLI

lez-events-decoder --receipt receipt.json

Outputs human-readable transaction receipt with event discriminants, sequence numbers, and hex/UTF-8 decoded payloads.

Success Criteria Checklist

  • Event API for programsemit_event(discriminant, &MyEvent { ... }) in lez-events crate, usable from any LEZ guest program. See examples/emit_event_demo/methods/guest/src/bin/withdraw.rs.
  • Machine-readable format — Borsh encoding, stable field ordering, sequence numbers for ordering, program_id injected by sequencer host, documented schema in docs/event-format.md.
  • Human-friendly renderinglez-events-decoder CLI renders receipts as structured text with discriminants, sequence numbers, and decoded payloads.
  • Available post-executiongetTransactionReceipt RPC method returns events for both included and rejected transactions.
  • Emittable on failurewrite_nssa_outputs_on_failure() + FAILURE_SENTINEL pattern preserves events before panic. Works in production ZK mode; dev-mode limitation documented.
  • Testing — 167 tests passing across all crates:
    • Deterministic encoding/decoding: lez-events tests
    • Event ordering: sequence counter tests
    • Failure-path persistence: emit_event_demo::failure_path_emits_insufficient_funds_event
    • Full nssa suite: 118 tests including all existing program execution tests

FURPS Self-Assessment

Functionality

  • Programs emit typed events on success and failure paths
  • Events retrievable via getTransactionReceipt RPC
  • Decoder CLI renders events in human-readable form
  • program_id attribution via sequencer host injection
  • Privacy: public execution emits by default; private execution requires explicit opt-in (documented)
  • Limitation: failure-path event recovery requires production ZK mode (not RISC0_DEV_MODE)

Usability

  • Single function call: emit_event(discriminant, &event_struct)
  • Pattern for failure path: emit_event(...); write_nssa_outputs_on_failure(); panic!(...)
  • CLI decoder: lez-events-decoder --receipt file.json
  • Full schema documented in docs/event-format.md

Reliability

  • Events in ProgramOutput.events are committed atomically with program output — consistent with existing tx semantics
  • RejectedTxStore is in-memory (cleared on restart) — sufficient for testnet; persistent storage is a natural next step
  • 167 tests passing; all 118 existing nssa tests continue to pass

Performance

  • Borsh encoding is zero-copy and allocation-minimal
  • Thread-local event buffer adds negligible overhead to guest execution
  • RejectedTxStore uses a HashMap — O(1) lookup per tx hash

Supportability

  • New crates (lez-events, lez-events-decoder) are self-contained and independently testable
  • docs/event-format.md documents the full schema, encoding, privacy considerations, and size guidelines
  • artifacts/program_methods/ update process documented (critical for future schema changes)
  • Build scripts updated with rerun-if-changed for nssa_core and lez-events

Supporting Materials

Terms & Conditions

By submitting this solution, I confirm that I have read and agree to the Terms & Conditions.