Skip to content

feat(sequencer): add simulate_transaction RPC endpoint#443

Open
paschal533 wants to merge 13 commits intologos-blockchain:mainfrom
paschal533:feat/simulate-transaction
Open

feat(sequencer): add simulate_transaction RPC endpoint#443
paschal533 wants to merge 13 commits intologos-blockchain:mainfrom
paschal533:feat/simulate-transaction

Conversation

@paschal533
Copy link
Copy Markdown

@paschal533 paschal533 commented Apr 11, 2026

Summary

Adds a simulate_transaction RPC method that executes a transaction against a snapshot of the current state and returns a structured result without ever committing the changes.

Callers can use this to:

  • Check whether a transaction will succeed before broadcasting it
  • Inspect which accounts will be modified, what nullifiers will be created, and what commitments will be added
  • Surface human-readable error messages without wasting a real submission

Design

The implementation follows a clone-and-discard pattern:

  1. Stateless signature/structure checks run without acquiring any lock.
  2. The sequencer lock is acquired, V03State is cloned, the lock is released immediately.
  3. validate_on_state runs on the clone, never on the real state, so block production continues concurrently without contention.
  4. The resulting diff (or error) is returned without being applied.

The simulation result includes:

Field Contents
success whether the transaction would succeed
error failure reason if success == false
accounts_modified Vec<(AccountId, Account)> post-state for each touched account
nullifiers_created nullifiers that would be spent
commitments_created commitments that would be added

Changes

  • common/src/simulation.rs (new), SimulationResult type.
  • common/src/lib.rs, expose pub mod simulation.
  • nssa/src/validated_state_diff.rs, expose new_nullifiers() and new_commitments() accessors on ValidatedStateDiff (previously only public_diff() was public).
  • sequencer/service/src/service.rs, simulate_transaction implementation with lock-release-before-execute pattern.
  • 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 #442 (receipt system) and #441 (crash fix). The
meaningful change in this PR starts at the feat(common): add SimulationResult
commit.

Test plan

  • cargo test -p sequencer_service simulate, two tests: simulate_valid_transaction_returns_success and simulate_does_not_modify_state
  • Manual: simulate a token transfer; confirm accounts_modified shows the expected balance delta and the real account state is unchanged after the call
  • Manual: simulate a transaction that should fail (e.g., insufficient balance); confirm success == false and error contains a readable message
  • Load test: run N concurrent simulate_transaction calls while block production is active; confirm no lock contention and no state corruption

…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.
@paschal533 paschal533 force-pushed the feat/simulate-transaction branch from 6eede91 to 222cbae Compare April 11, 2026 23:53
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