Skip to content

Implement WaitForTx RPC method - #1351

Open
carbolymer wants to merge 3 commits into
masterfrom
mgalazyn/feature/grpc-waitfortx
Open

carbolymer wants to merge 3 commits into
masterfrom
mgalazyn/feature/grpc-waitfortx

Conversation

@carbolymer

@carbolymer carbolymer commented Sep 21, 2026 •

Copy link
Copy Markdown
Contributor

Context

Adds the WaitForTx method of the UTxO RPC submit service, the last unsupported method in that service. The cardano-rpc README table now marks it as supported. This change also generalises ChainChange and ChainFollower over their per-block payload: withFollower retains raw block bytes and the parsed block, while withBlockFollower provides only the parsed block. Callers that name the old unparameterised types must add the payload type.

The client sends a list of transaction ids. The server reports STAGE_MEMPOOL when it observes a requested transaction in the mempool, and STAGE_CONFIRMED when it appears in a block applied after the stream opens. It reports another STAGE_MEMPOOL after an observed removal and re-entry. The stream closes when every requested transaction is confirmed.

It uses node kernel access, with no N2C connection. A chain follower is positioned at the current tip when the stream opens. The handler waits for chain-tip and pending-transaction mempool-membership changes through STM, then drains the follower when the tip moves. A TraceRpcWaitForTxSpan event wraps the call. The WatchMempool loop wakes only when a new transaction enters the mempool; removals and slot-only changes do not wake it. This breaks MempoolWatchSnapshot (only mempoolWatchTxsAfter remains), nextMempoolWatchSnapshot and the watchMempoolStream callback, which now take the last seen TicketNo.

Known limits:

  • A transaction confirmed before the stream opens is never reported. If no update arrives, the stream ends after 10 minutes with DEADLINE_EXCEEDED. A change that is ready after the deadline is still handled; if it sends nothing, the stream ends. Reopening does not recover past confirmations, so clients should open WaitForTx before or while they submit.
  • A transaction that leaves the mempool without being included produces no message. The stream keeps waiting until the transaction is re-added or confirmed, or the 10-minute idle timeout expires.
  • A transaction that is evicted and re-added gets another STAGE_MEMPOOL if the handler observes the removal before the re-entry. A removal and re-entry between membership snapshots can be missed.
  • A block rolled back within the same follower drain is not reported as confirmed. A transaction confirmed in one drain and rolled back in a later one is not announced again. FollowTip covers that case.
  • An empty request closes the stream at once. A malformed transaction id or a request with more than 500 references fails with INVALID_ARGUMENT. Duplicate ids count once, but they count toward the 500-reference limit.
  • Byron blocks and Byron special payloads carry no transaction ids here.

Related:

How to trust this PR

The core logic is in cardano-rpc/src/Cardano/Rpc/Server/Internal/UtxoRpc/WaitForTx.hs. stepWaitForTx is the pure state machine: it takes a state and event, so its behaviour can be tested without a live node. waitForTxMethod connects it to node kernel access and a chain follower. The remaining changes register the method, add its tracing constructor, and list the new modules in the cabal file.

The unit tests in cardano-rpc/test/cardano-rpc-test/Test/Cardano/Rpc/WaitForTxStream.hs feed stepWaitForTx scripted events. They cover:

  • a mempool stage followed by confirmation, including another stage after an observed eviction and re-entry
  • confirmation with no earlier mempool sighting
  • several transactions, with the stream closing only when all are confirmed
  • an empty request closing without waiting
  • the 500-reference limit and duplicate counting
  • idle-timeout calculations and resetting the deadline after an update
  • dropping block confirmations undone by a rollback during one follower drain

To run them, use the cardano-rpc-test suite of cardano-rpc.

The end-to-end tests run against a real testnet and live in cardano-node. They check that a transaction submitted after the stream opens reaches STAGE_CONFIRMED, and that a malformed id gives INVALID_ARGUMENT. They are in the commit "Add WatchMempool and WaitForTx RPC E2E tests" on the branch mgalazyn/feature/rpc-mempool-support, which is the draft PR above. That PR also adds the tracer entries that the new span needs on the node side.

Checklist

  • Commit sequence broadly makes sense and commits have useful messages
  • New tests are added if needed and existing tests are updated. See Running tests for more details
  • Self-reviewed the diff
  • Changelog fragment added in .changes/

@carbolymer
carbolymer added this pull request to stack #1352 September 21, 2026 15:23
@carbolymer
carbolymer force-pushed the mgalazyn/feature/grpc-waitfortx branch from f7a44d1 to 8e3bb0d Compare September 22, 2026 14:34
@carbolymer
carbolymer force-pushed the mgalazyn/feature/grpc-waitfortx branch 2 times, most recently from 3021b1f to f941cb5 Compare September 22, 2026 15:14
@carbolymer
carbolymer force-pushed the mgalazyn/feature/grpc-waitfortx branch from f941cb5 to decead2 Compare September 22, 2026 15:16
@carbolymer
carbolymer force-pushed the mgalazyn/feature/grpc-waitfortx branch 2 times, most recently from 567ebae to af949db Compare September 22, 2026 16:32
@carbolymer
carbolymer force-pushed the mgalazyn/feature/grpc-waitfortx branch 3 times, most recently from 7b22103 to a4c73f2 Compare September 24, 2026 13:54
@carbolymer
carbolymer force-pushed the mgalazyn/feature/grpc-waitfortx branch from a4c73f2 to 8d092ed Compare September 24, 2026 14:14
@carbolymer
carbolymer force-pushed the mgalazyn/feature/grpc-waitfortx branch from 8d092ed to d5e519f Compare September 24, 2026 14:21
Base automatically changed from mgalazyn/feature/grpc-watchmempool to master September 24, 2026 14:21
@carbolymer
carbolymer force-pushed the mgalazyn/feature/grpc-waitfortx branch from d5e519f to 3be64ea Compare September 24, 2026 14:22
@carbolymer carbolymer self-assigned this Oct 2, 2026
@carbolymer
carbolymer force-pushed the mgalazyn/feature/grpc-waitfortx branch from 3be64ea to 5895421 Compare October 2, 2026 11:56
@carbolymer
carbolymer force-pushed the mgalazyn/feature/grpc-waitfortx branch 4 times, most recently from 4347a36 to 051e7ed Compare October 21, 2026 09:47
@carbolymer
carbolymer force-pushed the mgalazyn/feature/grpc-waitfortx branch 2 times, most recently from 0874d6e to b0d97de Compare October 21, 2026 10:56
@carbolymer
carbolymer marked this pull request as ready for review October 21, 2026 11:03
Copilot AI balanced review requested due to automatic review settings October 21, 2026 11:03
@carbolymer
carbolymer force-pushed the mgalazyn/feature/grpc-waitfortx branch from b0d97de to c1dee61 Compare October 21, 2026 11:05

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Idle-timeout enforcement, empty-request handling before kernel initialization, and public mempool API migration documentation need correction.

3 open issues
What changed in this PR

Implements WaitForTx, completing the UTxO RPC submit service’s supported methods.

Changes:

  • Adds transaction-stage streaming with request limits, idle timeout, and unit tests.
  • Generalizes chain followers and switches mempool watching to ticket cursors.
  • Registers the handler and tracing, updates support documentation, and adds changelog fragments.
File Description
cardano-rpc/​test/​cardano-rpc-test/​Test/​Cardano/​Rpc/​WatchMempoolStream.hs Adapts snapshot fixtures and removal tests.
cardano-rpc/​test/​cardano-rpc-test/​Test/​Cardano/​Rpc/​WaitForTxStream.hs Tests streaming state transitions and helpers.
cardano-rpc/​test/​cardano-rpc-test/​Test/​Cardano/​Rpc/​FollowTipStream.hs Updates follower fixtures.
cardano-rpc/​src/​Cardano/​Rpc/​Server/​NodeKernelAccess/​Internal/​Follower.hs Extracts and generalizes chain followers.
cardano-rpc/​src/​Cardano/​Rpc/​Server/​NodeKernelAccess.hs Adds node reads and ticket-based mempool waits.
cardano-rpc/​src/​Cardano/​Rpc/​Server/​Internal/​UtxoRpc/​WaitForTx.hs Implements transaction-stage streaming.
cardano-rpc/​src/​Cardano/​Rpc/​Server/​Internal/​UtxoRpc/​Sync.hs Updates the follower payload type.
cardano-rpc/​src/​Cardano/​Rpc/​Server/​Internal/​UtxoRpc/​Mempool.hs Uses ticket cursors for watching.
cardano-rpc/​src/​Cardano/​Rpc/​Server/​Internal/​Tracing.hs Adds the wait-method trace span.
cardano-rpc/​src/​Cardano/​Rpc/​Server.hs Registers WaitForTx.
cardano-rpc/​README.md Marks WaitForTx supported.
cardano-rpc/​cardano-rpc.cabal Registers implementation and test modules.
.changes/​20260921_cardano_rpc_wait_for_tx.yml Documents the feature and limits.
.changes/​20260921_cardano_rpc_chain_follower_payload.yml Documents follower API breaks.

Review effort: Balanced

Comment thread cardano-rpc/src/Cardano/Rpc/Server/Internal/UtxoRpc/WaitForTx.hs Outdated
Comment on lines +137 to +138
Just state' ->
waitLoop nodeKernelAccess follower state' tip $ nextIdleDeadline sentAt deadline responses

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in aad56e1. After the deadline the loop still takes one change that is already waiting, but if handling it sends nothing, the stream ends with DEADLINE_EXCEEDED. A change that sends a response restarts the deadline as before. A new property covers the expired-and-silent, expired-with-response and not-expired cases.

Comment thread .changes/20260921_cardano_rpc_chain_follower_payload.yml Outdated
@carbolymer
carbolymer force-pushed the mgalazyn/feature/grpc-waitfortx branch 2 times, most recently from eaa897a to 3834c3f Compare October 21, 2026 13:13
ChainFollower and ChainChange take the payload type, so a caller reads only the block components it uses, and ChainFollower gains a non-blocking tryNextChange.
NodeKernelAccess gains STM reads of the chain tip point, the mempool snapshot and the current era.
WatchMempool now waits for a transaction newer than the last seen ticket, found in O(log n) with snapshotTxsAfter, instead of comparing every ticket number on each mempool change.
Removals and slot changes no longer wake it, as they never produced a message.
@carbolymer
carbolymer force-pushed the mgalazyn/feature/grpc-waitfortx branch from 3834c3f to bb26b5a Compare October 21, 2026 13:20

@carbolymer carbolymer left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

.

Comment thread cardano-rpc/src/Cardano/Rpc/Server/Internal/UtxoRpc/WaitForTx.hs Outdated
Comment on lines +137 to +138
Just state' ->
waitLoop nodeKernelAccess follower state' tip $ nextIdleDeadline sentAt deadline responses

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in aad56e1. After the deadline the loop still takes one change that is already waiting, but if handling it sends nothing, the stream ends with DEADLINE_EXCEEDED. A change that sends a response restarts the deadline as before. A new property covers the expired-and-silent, expired-with-response and not-expired cases.

Comment thread .changes/20260921_cardano_rpc_chain_follower_payload.yml Outdated

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants