go-ethereum icon indicating copy to clipboard operation
go-ethereum copied to clipboard

Spec announce type-hint inconsistency for blob transactions: geth accepts non-standard Types (e.g., 0x09), reth/besu reject

Open zhouCode opened this issue 4 days ago • 1 comments

System information

  • Geth version: 1.16.2, reth: v1.6.0, besu: v25.8.0
  • CL client & version: [email protected]
  • OS & Version: Linux
  • Commit hash: N/A
  • Network: Ethereum Package devnet (Cancun enabled)

Expected behaviour

  • When announcing a valid blob (Type=3) transaction hash over DevP2P (eth/68) using NewPooledTransactionHashes, the Types and Sizes fields should function as hints. Even if Types contains a non-standard value (e.g., 0x09 or 0xff), the node should still request the transaction by hash and process it if the returned transaction is valid. Ideally, all client implementations should behave consistently.
  • Alternatively, if Types is intended as a strict constraint (i.e., must accurately reflect the real transaction type), then geth should reject or ignore announcements with mismatched or unknown Types, matching the behaviour of other clients.

Actual behaviour

  • Geth accepts an announcement using Types=0x09, sends GetPooledTransactions for the hash, receives the full blob transaction (including sidecar), and successfully inserts it into the mempool. The transaction can be mined.
  • Reth and Besu do not request or process transactions whose announcement includes a non-standard type hint such as 0x09. The transaction never enters the mempool.

This leads to inconsistent DevP2P behaviour for blob transaction propagation across client implementations.

Steps to reproduce

  1. Prepare a valid blob transaction (Type=3), signed with the Cancun signer and including full sidecar data (blobs/commitments/proofs).

  2. Establish a DevP2P (eth/68) session with a client node.

  3. During the announce phase, send a NewPooledTransactionHashes message containing:

    • Types: []byte{0x09} (non-standard type hint; also reproducible with 0xff)
    • Sizes: []uint32{uint32(tx.Size())} (also reproducible with 0 or math.MaxUint32)
    • Hashes: hashes (where tx is the valid blob transaction)
  4. Wait for a GetPooledTransactions request and respond with a PooledTransactions message containing the full blob transaction and sidecar.

  5. Observe:

    • Geth requests the hash, accepts the transaction, and may mine it.
    • Reth and Besu do not request the hash or do not accept the transaction.

Minimal announce example (Go pseudocode using go-ethereum types)

ann := eth.NewPooledTransactionHashesPacket{
    Types:  []byte{0x09},                  // non-standard type hint for experiment
    Sizes:  []uint32{uint32(tx.Size())},
    Hashes: hashes,      // tx is a valid Type=3 blob with sidecar
}
conn.Write(protoID, eth.NewPooledTransactionHashesMsg, ann)

req := new(eth.GetPooledTransactionsPacket)
conn.ReadMsg(protoID, eth.GetPooledTransactionsMsg, req)

resp := eth.PooledTransactionsPacket{
    RequestId:                  req.RequestId,
    PooledTransactionsResponse: eth.PooledTransactionsResponse(types.Transactions{tx}),
}
conn.Write(protoID, eth.PooledTransactionsMsg, resp)

Backtrace

No crash or error trace. Behaviour is visible in DevP2P logs:

  • Geth shows GetPooledTransactions after an announce with Types=0x09, and the PooledTransactions response is accepted.
  • Reth and Besu show no request for the hash when the announce uses a non-standard type hint.

Additional context

  • The core issue appears to be whether the Types and Sizes fields in NewPooledTransactionHashes are interpreted as strict validation filters or non-binding propagation hints.
  • Geth treats them as hints (still fetches by hash).
  • Reth and Besu treat them as filters (ignore unknown or mismatched types).
  • This misalignment results in inconsistent blob-transaction propagation across clients.

zhouCode avatar Dec 07 '25 17:12 zhouCode