Modular Transaction Pipeline

Replace 40+ hardcoded type switches with a registry of transaction type handlers

circle-info

Status: Design phase. No implementation dependencies — this is a foundational component. Required by: EIP-8141, Interop Bridge (Phase 3+), Shutter refactor, Lucid integration.

The Modular Transaction Pipeline replaces Erigon's current approach of inserting code at every pipeline stage whenever a new transaction type is added. Today, each new type requires editing 40+ locations across ~15 files — a type switch or if txn.Type == X check at every stage from parsing through serialization. With EIP-8141 frames, Shutter, and Lucid all in scope simultaneously, this model becomes unmanageable.

The pipeline introduces a TypeHandler registry: each transaction type registers its behavior once, and every pipeline stage calls through the registry instead of branching on types. Adding a new transaction type means writing one handler — not touching 40 files.


Key Capabilities

Registry-based dispatch: Each transaction type implements TypeHandler — a set of interfaces covering parsing, validation, pool policy, execution, and serialization. The pipeline calls through the registry; it never checks types directly. Types register via init() and components.cfg, following the same pattern as other Cocoon components.

Incremental migration: Existing transaction types (legacy, EIP-1559, EIP-4844, EIP-7702, RIP-7560) are wrapped as TypeHandler implementations with zero behavior change. The scattered type switches are then removed one pipeline stage at a time — each removal is a small, independently-reviewable PR.

Middleware layer: Encrypted mempool integrations (Shutter, Lucid) wrap the registry without touching type-specific code. Shutter decrypts transactions at the slot boundary before the registry handler runs; Lucid reassembles commitment and payload before the handler processes them. Neither needs to know anything about individual transaction types.


Design: TypeHandler Registry

// txtype/registry.go

type TypeHandler interface {
    TypeByte() byte
    Name() string

    Parser      // RLP → TxnSlot (txpool) or Transaction (execution)
    Validator   // mempool acceptance rules
    PoolPolicy  // replacement, eviction, pool limits
    Executor    // how this tx type is processed in a block
    Serializer  // RPC response fields, receipt format
}

Handlers embed DefaultHandler for any interfaces they don't need to override — a new type that is identical to DynamicFee except for one field only needs to implement Parser and Serializer.

Registration follows the components.cfg + init() pattern:

chevron-rightCurrent touch points per new transaction typehashtag
Layer
Touch points
What changes

Type system

5 files

RLP decode switch, JSON unmarshal, receipt encode, hash computation

Txpool parsing

3 functions

ParseTransaction, type-specific body parser, hash/sender recovery

Txpool validation

4 functions

validateTx, replacement rules, pool limits

Txpool state

3 functions

Auth tracking, eviction, fee checks

Block builder

2 functions

block_assembler.go, builderstages/exec.go

State processor

2 functions

applyTransaction, dispatch

Parallel execution

3 functions

txtask.go, trace workers

RPC

2 functions

Field extraction, receipt generation


Middleware Layer

The middleware layer wraps the registry without touching type-specific code — encrypted mempool integrations don't need to know anything about individual transaction types.

Shutter (threshold decryption): wraps the validation and execution stages. Encrypted transactions enter the mempool as opaque blobs; the Shutter middleware decrypts them at the appropriate slot boundary before the registry handler processes them normally.

Lucid (distributed payload propagation): wraps the pool policy and serialization stages. The commitment (hash) and payload (data) are propagated separately; the Lucid middleware reassembles them before the registry handler runs.

Migration Strategy

Existing transaction types register themselves using the new interface. No pipeline code changes:

  1. LegacyHandler — wraps existing legacy/access-list type code

  2. DynamicFeeHandler — wraps existing EIP-1559 code

  3. BlobHandler — wraps existing EIP-4844 code (KZG, pool limits, replacement rules)

  4. SetCodeHandler — wraps existing EIP-7702 code (authorization tracking)

  5. AAHandler — wraps existing RIP-7560 code

Once all types are registered, the type switches are removed one pipeline stage at a time. Each removal is a small, independently-reviewable PR.

Last updated