Skip to content

feat(network): add local-tx-monitor server agent, align codec with the wire protocol#792

Open
Javieracost wants to merge 3 commits into
txpipe:mainfrom
Javieracost:feat/txmonitor-server
Open

feat(network): add local-tx-monitor server agent, align codec with the wire protocol#792
Javieracost wants to merge 3 commits into
txpipe:mainfrom
Javieracost:feat/txmonitor-server

Conversation

@Javieracost

@Javieracost Javieracost commented Jul 20, 2026

Copy link
Copy Markdown

What

Server-side support for the local-tx-monitor miniprotocol (N2C protocol 9), plus the codec fixes required for it to interoperate with real peers. Three commits, each self-contained:

  1. chore: fix clippy lints for Rust 1.97 — unblocks the workspace clippy job on current stable; no functional changes. Happy to split this into its own PR if you'd rather merge it independently.

  2. fix(network): align txmonitor codec with the on-the-wire protocol — the existing local-tx-monitor codec diverged from the authoritative ouroboros-network codec in three ways that made interop with cardano-node and ogmios impossible:

    • MsgAwaitAcquire was guessed as wire label 4, which doesn't exist in the spec. The wire shares label 1 with MsgAcquire; peers disambiguate by protocol state (Idle = acquire, Acquired = await re-acquire). It now encodes as 1, and a stateless decode always yields Acquire, which agents map back based on their state.
    • The protocol-local txmonitor::TxId (the type used only by local-tx-monitor messages — not the chain-level transaction id types elsewhere in pallas) was modeled as a text string. The wire format is the hard-fork-combinator GenTxId: an era-wrapped [era, hash-bytes] pair. Clients such as ogmios treat the era wrapper as significant and probe the same hash under every plausible era.
    • MsgGetMeasures (11) / MsgReplyGetMeasures (12), part of the protocol since node-to-client v20, weren't modeled, so receiving them killed the decode.

    The client agent is repaired along the way: release() was rejected by its own outbound-state assertion, Release/AwaitAcquire/Done were missing from the assert tables, and await_acquire(), query_measures() and done() are added. The error type is renamed ErrorClientError (matching the chainsync ClientError/ServerError convention) to make room for the server agent.

  3. feat(network): add txmonitor server agent and wire it into NodeServer — mirrors the localstate server pattern: recv_while_idle / recv_while_acquired drive the session and typed send_* replies cover NextTx, HasTx, GetSizes and GetMeasures. Wire label 1 received in the Acquired state surfaces as ClientQueryRequest::AwaitAcquire. NodeServer now subscribes PROTOCOL_N2C_TX_MONITOR and exposes the agent via txmonitor().

Why

Any pallas-based N2C server is currently unusable behind ogmios: ogmios opens local-tx-monitor eagerly on every session, and since accepting the N2C handshake claims support for the full protocol set, the unsubscribed protocol makes the multiplexer kill the whole connection. The immediate beneficiary is dolos, whose ouroboros N2C endpoint gains a local-tx-monitor session handler built on this agent — see the companion PR txpipe/dolos#1120.

Breaking changes

  • The protocol-local txmonitor::TxId changes shape (text string → era-wrapped GenTxId). No working code can regress on the wire format: the previous encoding was never accepted by real peers.
  • localtxmonitor client error type renamed ErrorClientError.

Testing

  • Full client/server pair test over a unix socket exercising acquire, iteration, has-tx, sizes, measures, blocking re-acquire, release and termination.
  • Interop-validated on mainnet with ogmios v7.0.0 as the client (through the dolos companion PR): mempool RPCs round-trip correctly and sessions survive the eager protocol-9 open. That deployment also fed a mainnet indexing pipeline whose derived state was verified byte-identical against a cardano-node reference, so the codec changes are exercised well beyond the unit suite.

One deliberate leniency: the server accepts GetMeasures regardless of the negotiated handshake version (the spec gates it at v20+). Pallas miniprotocol agents are version-agnostic by design, so the server errs on the lenient side.

Summary by CodeRabbit

  • New Features

    • Added transaction-monitor server support for node-to-client connections.
    • Added mempool measures querying, including transaction counts and size/capacity data.
    • Expanded transaction-monitor client and server interactions for snapshot management and transaction queries.
    • Improved support for newer transaction-monitor protocol behavior and message handling.
  • Tests

    • Added integration coverage for transaction-monitor workflows, including acquisition, transaction queries, measures, release, and completion.

Javieracost and others added 3 commits July 19, 2026 13:42
Rust 1.97 stable introduces lints that fail the workspace clippy job:

- for_kv_map in pallas-validate phase1 (alonzo, babbage, conway,
  shelley_ma): iterate map keys directly instead of destructuring
  unused values
- useless_borrows_in_formatting in pallas-math tests and
  pallas-hardano haskell_display: drop redundant references in
  format arguments

Mechanical fixes only, no behavior change. Verified clean under both
1.93 and 1.97 clippy.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The local-tx-monitor codec diverged from the authoritative
ouroboros-network codec in three ways that made interop with real
peers (cardano-node, ogmios) impossible:

- MsgAwaitAcquire was guessed as label 4, which does not exist in the
  spec. The wire shares label 1 with MsgAcquire and peers disambiguate
  by protocol state (Idle = acquire, Acquired = await re-acquire). The
  AwaitAcquire variant now encodes as 1; a stateless decode always
  yields Acquire and agents map it back based on their state.
- TxId was modeled as a text string. The wire format is the
  hard-fork-combinator GenTxId: an era-wrapped [era, hash-bytes] pair.
  Clients such as ogmios treat the era wrapper as significant and
  probe every plausible era for the same hash.
- MsgGetMeasures (11) / MsgReplyGetMeasures (12), part of the protocol
  since node-to-client v20, were not modeled at all, so receiving them
  killed the decode.

The client agent is repaired along the way: release() was rejected by
its own outbound-state assertion, Release/AwaitAcquire/Done were
missing from the assert tables, and await_acquire(), query_measures()
and done() are added. The error type is renamed Error -> ClientError,
matching the chainsync ClientError/ServerError convention, to make
room for the server agent.

BREAKING: TxId changes shape and the client error type is renamed.
No working code can regress on the wire format: the previous encoding
was never accepted by real peers.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Adds the server side of the local-tx-monitor miniprotocol, mirroring
the localstate server pattern: recv_while_idle / recv_while_acquired
drive the session and typed send_* replies cover NextTx, HasTx,
GetSizes and GetMeasures. Wire label 1 received in the Acquired state
surfaces as ClientQueryRequest::AwaitAcquire.

NodeServer now subscribes PROTOCOL_N2C_TX_MONITOR and exposes the
agent via txmonitor(). Without the subscription, the multiplexer
kills the whole session as soon as a client opens protocol 9 - which
ogmios does on every connection, making pallas-based N2C servers
(e.g. dolos) unusable behind it.

The server accepts GetMeasures regardless of the negotiated
handshake version (the spec gates it at v20+); pallas miniprotocol
agents are version-agnostic by design, so the server errs on the
lenient side.

Includes a full client/server pair test over a unix socket exercising
acquire, iteration, has-tx, sizes, measures, blocking re-acquire,
release and termination.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jul 20, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Changes

The tx-monitor protocol now supports typed transaction IDs, mempool measures, CBOR request/response messages, client queries, server-side state handling, NodeServer wiring, and an integration test. Separate cleanup changes remove unnecessary formatting references and iterate directly over minted policy keys.

Tx-monitor protocol support

Layer / File(s) Summary
Protocol types and CBOR messages
pallas-network/src/miniprotocols/txmonitor/protocol.rs, pallas-network/src/miniprotocols/txmonitor/codec.rs
Adds measure payload types and request/response variants, changes transaction ID representation, and updates CBOR encoding, decoding, and tests.
Client and server state flow
pallas-network/src/miniprotocols/txmonitor/{client.rs,server.rs,mod.rs}
Adds the tx-monitor server state machine, measure querying, expanded validation, client error naming, and public server re-exports.
NodeServer wiring and integration flow
pallas-network/src/facades.rs, pallas-network/tests/protocols.rs
Connects NodeServer to the tx-monitor server and adds an ignored Unix client-server happy-path test.

Formatting and iteration cleanups

Layer / File(s) Summary
Formatting and policy iteration cleanup
pallas-hardano/src/display/haskell_display.rs, pallas-math/src/math.rs, pallas-validate/src/phase1/{alonzo,babbage,conway,shelley_ma}.rs
Passes formatting values directly and iterates over minted policy keys without changing validation logic.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant NodeClient
  participant NodeServer
  participant TxMonitorServer
  NodeClient->>NodeServer: Connect and open tx-monitor protocol
  NodeServer->>TxMonitorServer: Route protocol channel
  NodeClient->>TxMonitorServer: Request acquire and mempool queries
  TxMonitorServer->>NodeClient: Return transactions, sizes, and measures
  NodeClient->>TxMonitorServer: Release and done
Loading

Possibly related PRs

  • txpipe/pallas#686: Modifies minting-policy iteration and validation in related pallas-validate phase files.
  • txpipe/pallas#748: Modifies formatting arguments in the related golden_tests logic.

Suggested reviewers: scarmuega

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 73.91% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main changes: adding the local-tx-monitor server agent and updating the codec to match the wire protocol.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

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.

1 participant