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.rs—ProgramOutput.events,write_nssa_outputs_on_failurenssa/src/program.rs— host-side event extraction,ExitCodehandlingnssa/src/error.rs—NssaError::ProgramExecutionFailedwithpartial_outputsequencer/core/src/block_store.rs—RejectedTx,RejectedTxStoresequencer/core/src/lib.rs— event preservation on tx failuresequencer/service/rpc/src/lib.rs—TxReceipt,TxStatus,getTransactionReceiptsequencer/service/src/service.rs— RPC implementationlez-events-decoder/src/main.rs— CLI decoder toolexamples/emit_event_demo/— success + failure path example programsdocs/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 + eventsgetTransactionReceiptRPC: returnsTxStatus(Included/Rejected/Pending/Unknown) + events- Events from included transactions come from
ProgramOutput.eventsin 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 programs —
emit_event(discriminant, &MyEvent { ... })inlez-eventscrate, usable from any LEZ guest program. Seeexamples/emit_event_demo/methods/guest/src/bin/withdraw.rs. - Machine-readable format — Borsh encoding, stable field ordering, sequence numbers for ordering,
program_idinjected by sequencer host, documented schema indocs/event-format.md. - Human-friendly rendering —
lez-events-decoderCLI renders receipts as structured text with discriminants, sequence numbers, and decoded payloads. - Available post-execution —
getTransactionReceiptRPC method returns events for both included and rejected transactions. - Emittable on failure —
write_nssa_outputs_on_failure()+FAILURE_SENTINELpattern preserves events before panic. Works in production ZK mode; dev-mode limitation documented. - Testing — 167 tests passing across all crates:
- Deterministic encoding/decoding:
lez-eventstests - 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
- Deterministic encoding/decoding:
FURPS Self-Assessment
Functionality
- Programs emit typed events on success and failure paths
- Events retrievable via
getTransactionReceiptRPC - Decoder CLI renders events in human-readable form
program_idattribution 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.eventsare committed atomically with program output — consistent with existing tx semantics RejectedTxStoreis 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
RejectedTxStoreuses aHashMap— O(1) lookup per tx hash
Supportability
- New crates (
lez-events,lez-events-decoder) are self-contained and independently testable docs/event-format.mddocuments the full schema, encoding, privacy considerations, and size guidelinesartifacts/program_methods/update process documented (critical for future schema changes)- Build scripts updated with
rerun-if-changedfornssa_coreandlez-events
Supporting Materials
- Event format spec:
docs/event-format.md - Example program (success + failure):
examples/emit_event_demo/ - Guest SDK:
lez-events/src/lib.rs - Decoder CLI:
lez-events-decoder/src/main.rs - RPC interface:
sequencer/service/rpc/src/lib.rs - Related issues addressed: #170 (Risc0 error handling), #379 (TX visibility), #347 (program_id), #131/#260 (Borsh encoding)
Terms & Conditions
By submitting this solution, I confirm that I have read and agree to the Terms & Conditions.