Skip to content

feat(sequencer): add persistent transaction receipt system#442

Open
paschal533 wants to merge 9 commits intologos-blockchain:mainfrom
paschal533:feat/tx-receipts
Open

feat(sequencer): add persistent transaction receipt system#442
paschal533 wants to merge 9 commits intologos-blockchain:mainfrom
paschal533:feat/tx-receipts

Conversation

@paschal533
Copy link
Copy Markdown

@paschal533 paschal533 commented Apr 11, 2026

Summary

Adds a get_transaction_receipt RPC method that answers the question "what happened to the transaction I submitted?" with a structured, typed response rather than the current null-returning get_transaction.

The four-tier lookup gives callers the exact status of any hash they have ever submitted, even across sequencer restarts:

  1. Rejected, checked first from a new RocksDB column family that stores rejection records durably. Includes the human-readable reason and a millisecond timestamp.
  2. Included the block store is searched for the transaction hash; if found, the block ID and the block's timestamp are returned.
  3. Pending, an in-memory set tracks hashes inserted by send_transaction that have not yet reached a terminal state.
  4. Unknown, hash was never seen, is invalid, or the pending set entry was evicted.

Receipt entries in the pending set are lazily evicted when the receipt is
first queried for a terminal-state transaction, bounding memory growth for
any transaction that callers actually follow up on.

Changes

  • common/src/receipt.rs (new), TxStatus, TxReceipt, RejectedTxRecord types with Borsh and serde derivations.
  • common/src/lib.rs, expose pub mod receipt.
  • storage/src/sequencer/mod.rs, new cf_rejected_tx column family, put_rejected_tx and get_rejected_tx methods.
  • sequencer/core/src/block_store.rs, store_rejected_tx, get_rejected_tx, and get_block_id_for_tx methods.
  • sequencer/core/src/lib.rs, when a transaction fails validation during block production the rejection is now persisted to RocksDB instead of being silently dropped.
  • sequencer/service/src/service.rs, pending_txs tracking field, get_transaction_receipt implementation, lazy pending set eviction.
  • sequencer/service/rpc/src/lib.rs and sequencer/service/protocol/src/lib.rs, RPC trait method and protocol type exports.

Stacking note

This PR builds on top of #441 (crash fix). The diff against main will include those commits too; the meaningful change in this PR starts at the feat(common): add TxStatus commit.

Test plan

  • cargo test -p common, Borsh and serde roundtrip tests for receipt types
  • cargo test -p sequencer_service get_receipt, receipt lifecycle tests: pending after submit, unknown for unseen hash
  • Manual: submit a transaction, query receipt (expect Pending), wait for block production, query again (expect Included with block ID and timestamp)
  • Manual: submit an invalid transaction, query receipt (expect Rejected with reason string)
  • Restart sequencer, query receipt for a previously rejected hash (expect Rejected, verifies RocksDB persistence)

…ationError

Replace panicking .expect() calls in send_transaction with proper error
propagation using TransactionMalformationError variants. Unify the size
error to use TransactionMalformationError::TransactionTooLarge instead
of a raw format string. Add unit tests for too-large and valid transaction
paths.
Registers the new rejected-transaction column family in RocksDB,
and exposes rejected_tx_column, put_rejected_tx, and get_rejected_tx
on RocksDBIO for storing and retrieving RejectedTxRecord by hash.
…ulate block timestamp

Restructure get_transaction_receipt to scope the sequencer lock to a
block expression, collect a terminal receipt (Rejected/Included) under
the lock, release the lock, then lazily remove the hash from pending_txs
before returning.

This fixes two related issues:
- pending_txs grew without bound because insert() in send_transaction
  had no corresponding remove() when TXs were confirmed or rejected
- a TX that had been included could still appear as Pending if the
  caller never queried its receipt

Also populate timestamp_ms for Included receipts by fetching the block
from the store (an additional DB read already cached by RocksDB's
block cache), replacing the previous None placeholder.

Lock ordering is preserved: sequencer lock is always released before
pending_txs is acquired, matching the order in send_transaction.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants