What Is TxRouter? Transaction Propagation in the NEO P2P Network

The TxRouter is a lightweight Akka.NET actor that performs state-independent pre-verification of incoming transactions before forwarding them to the Blockchain actor for full validation and network relay.

In the neo-project/neo repository, the TxRouter (Neo.Ledger.TransactionRouter) serves as the first line of defense for transaction propagation across the peer-to-peer network. This specialized actor filters incoming transactions through lightweight checks before they reach the heavy-duty Blockchain actor, ensuring that only structurally valid transactions consume resources in the memory pool.

How TxRouter Fits Into the Transaction Propagation Pipeline

Entry Point from RemoteNode

When a remote peer sends a transaction inventory, the RemoteNode actor receives it through OnInventory in RemoteNode.ProtocolHandler.cs. Before forwarding, it checks for duplicates or conflicts using _system.ContainsTransaction and _system.ContainsConflictHash. If the transaction passes these checks, it is sent to the TxRouter:

_system.TxRouter.Tell(new TransactionRouter.Preverify(transaction, true));

(see [RemoteNode.ProtocolHandler.cs](https://github.com/neo-project/neo/blob/master-n3/src/Neo/Network/P2P/RemoteNode.ProtocolHandler.cs#L126-L130))

State-Independent Pre-Verification

The TxRouter receives the Preverify message in TransactionRouter.cs and immediately executes VerifyStateIndependent. This lightweight validation checks transaction structure, signatures, and basic rules without accessing the current blockchain state. The router then packages the result into a PreverifyCompleted message and forwards it to the Blockchain actor:

protected override void OnReceive(object message)
{
    if (message is not Preverify preverify) return;
    var send = new PreverifyCompleted(
        preverify.Transaction,
        preverify.Relay,
        preverify.Transaction.VerifyStateIndependent(_system.Settings));
    _system.Blockchain.Tell(send, Sender);
}

(see [TransactionRouter.cs](https://github.com/neo-project/neo/blob/master-n3/src/Neo/Ledger/TransactionRouter.cs#L26-L33))

Integration with the Blockchain Actor

Upon receiving PreverifyCompleted, the Blockchain actor in Blockchain.cs evaluates the verification result. If VerifyResult.Succeed is returned, the transaction proceeds to OnInventory for memory pool insertion and potential network relay. If pre-verification fails, the system immediately sends a relay failure result without wasting resources on full validation:

private void OnPreverifyCompleted(TransactionRouter.PreverifyCompleted task)
{
    if (task.Result == VerifyResult.Succeed)
        OnInventory(task.Transaction, task.Relay);
    else
        SendRelayResult(task.Transaction, task.Result);
}

(see [Blockchain.cs](https://github.com/neo-project/neo/blob/master-n3/src/Neo/Ledger/Blockchain.cs#L36-L44))

Direct RPC Entry Points and Internal Forwarding

The TxRouter also handles transactions submitted through RPC or other local sources. When the Blockchain actor receives a transaction directly (e.g., via sendrawtransaction), it checks for conflicts and forwards the transaction to the TxRouter using Forward instead of Tell, preserving the original sender reference:

else
{
    if (_system.ContainsConflictHash(hash, tx.Signers.Select(s => s.Account)))
        SendRelayResult(tx, VerifyResult.HasConflicts);
    else
        _system.TxRouter.Forward(new TransactionRouter.Preverify(tx, true));
}

(see [Blockchain.cs](https://github.com/neo-project/neo/blob/master-n3/src/Neo/Ledger/Blockchain.cs#L100-L107))

Why NEO Uses a Dedicated TxRouter Actor

Parallel Processing with SmallestMailboxPool

The TxRouter is instantiated using a SmallestMailboxPool router configuration, creating one actor instance per CPU core. This design allows NEO to process multiple transaction pre-verifications in parallel, preventing network bottlenecks during high-throughput periods. The SmallestMailboxPool strategy routes new messages to the actor with the smallest mailbox, ensuring load balancing across cores:

return Props.Create(() => new TransactionRouter(system))
            .WithRouter(new SmallestMailboxPool(Environment.ProcessorCount));

(see [TransactionRouter.cs](https://github.com/neo-project/neo/blob/master-n3/src/Neo/Ledger/TransactionRouter.cs#L34-L38))

Separation of Concerns

By isolating state-independent verification in the TxRouter, the Blockchain actor remains focused on state-dependent validation, memory pool management, and block processing. This architectural boundary ensures that expensive blockchain state operations are never blocked by lightweight signature checks or format validations, improving overall node responsiveness.

Summary

  • The TxRouter (TransactionRouter) acts as a lightweight Akka.NET actor that performs state-independent pre-verification of transactions before they reach the Blockchain actor.
  • It receives transactions from RemoteNode (P2P network) and directly from Blockchain (RPC/local sources), running VerifyStateIndependent to check signatures and structural validity.
  • Valid transactions are forwarded to Blockchain as PreverifyCompleted messages for memory pool insertion and network relay; invalid transactions are rejected immediately.
  • The actor uses a SmallestMailboxPool router with one instance per CPU core, enabling parallel pre-verification and preventing bottlenecks in the P2P transaction propagation pipeline.

Frequently Asked Questions

What is the difference between TxRouter and the Blockchain actor?

The TxRouter performs lightweight, state-independent verification (signature checks, format validation) using only the transaction data itself. The Blockchain actor handles state-dependent validation (checking balances, nonces against the current ledger state), manages the memory pool, and coordinates block processing. This separation allows the TxRouter to filter out invalid transactions before they consume resources in the Blockchain actor.

How does TxRouter improve transaction throughput?

TxRouter improves throughput through parallelism and load balancing. It is configured as a SmallestMailboxPool with one actor instance per CPU core, allowing multiple transactions to be pre-verified simultaneously. By offloading state-independent checks from the main Blockchain actor, it prevents the validation pipeline from stalling during high-volume network traffic.

What happens if a transaction fails pre-verification in TxRouter?

If a transaction fails the VerifyStateIndependent check in the TxRouter, it is immediately rejected without being forwarded to the Blockchain actor or entering the memory pool. The TxRouter sends a PreverifyCompleted message with a failure result (e.g., VerifyResult.InvalidSignature), and the system sends a relay failure response to the originating peer or RPC client.

Is TxRouter used for all transaction types in NEO?

Yes, the TxRouter handles all transaction types that enter the node through the P2P network or RPC interfaces. Whether the transaction is a standard transfer, smart contract deployment, or complex invocation, it must first pass through the TxRouter's state-independent verification before the Blockchain actor processes it for state-dependent validation and inclusion in the memory pool.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →