EIP-2464 eth/65: transaction announcement and retrieval ("eth/65: transaction announcements and retrievals")
Final · Networking & nodes · EIP status Final · created 2020-01-13
Introduces a new protocol version, eth/65, which adds three message types that first announce only hashes for transaction propagation between nodes and then selectively retrieve only the ones not already known. This reduces the waste of repeatedly sending the same transaction in full to every peer.
Why it is needed
Ethereum nodes use two methods when propagating a new block. They send the full block to a small number of peers, and announce only the hash to the rest, who can fetch it if needed. Transactions, however, lack this dual mechanism, so a node must send the full transaction as is to every connected peer. With N peers, the same transaction travels N times, when in a perfect world one transmission would suffice. Also, when two nodes newly connect, they need to reconcile their transaction pools, but since there is no way to check what the other side already knows, they had to send their entire pool, which can run from tens to hundreds of MB. The original text points out that transaction propagation was, at the time, the single largest consumer of network resources, and that most of it was wasted on redundant transmission, aiming to first agree on which items to exchange using 32-byte hashes before the expensive exchange, thereby cutting overall operating bandwidth by at least an order of magnitude.
What changes
Today
- Transactions are redundantly sent in full, N times, to every connected peer
- When two nodes newly connect, they exchange their entire transaction pool (tens to hundreds of MB) in full
- The same data is repeatedly transmitted across every connection, wasting network resources
After EIP-2464
- Propagation bandwidth, which had scaled linearly with the number of peers, is reduced to roughly the square root of that
- A newly connected node exchanges only a list of hashes (pool size times 32 bytes, about 128KB) and requests only the transactions it does not know
- Only the data that is strictly necessary is exchanged, through three stages: hash announcement, selective request, and response
Who is affected
- Node operators: The bandwidth used for transaction propagation is reduced, easing the burden of operating costs. The original text estimates this can cut the entire network's operating bandwidth by at least an order of magnitude.
- Client teams: eth/65, containing the three new message types, must be implemented. However, since devp2p supports running multiple versions of the same protocol side by side, there is no need to coordinate deployment timing with other client teams.
- Ordinary users: Since neither the consensus rules nor the transaction format change, there is nothing different about how wallets or apps are used.
In depth
NewPooledTransactionHashes (0x08): hash announcement
A message announcing a list of hashes [hash_0: B_32, hash_1: B_32, ...] for transactions not yet included in a block. A node should ideally announce as many transactions as possible that the other side might not know about. There is no protocol-violating hard cap on the number of hashes that can be announced, only the 10MB devp2p packet limit, but 4096 hashes (128KB) is suggested as an appropriate batch size so that a single packet does not monopolize the connection. The principle is to announce only hashes the other side is likely not to know, but it is stated explicitly that over-announcing is better than creating a nonce gap in the pool.
GetPooledTransactions (0x09): selective request
A request message that specifies, as a list of hashes, the transactions to fetch from the peer's transaction pool. There is no hard cap on the number of requests that constitutes a protocol violation (other than the 10MB devp2p packet limit), but the recipient may impose arbitrary limits on response size or processing time, and this must not be treated as a protocol violation. A cap of 256 is suggested as a reasonable limit to reduce bandwidth wasted on hashes that will not be answered.
PooledTransactions (0x0a): response and ordering rules
A message that responds to a GetPooledTransactions request with transactions from the local transaction pool. Each entry follows the main Ethereum specification's transaction format. The response must preserve the same order as the request, and missing transactions may be skipped. This rule lets the requester, when a response size limit is hit, re-request the hashes after the last transaction received and treat the gap before it as transactions the peer does not have. An empty response is only allowed when none of the requested hashes are in the pool. It is also acceptable for an announced transaction to have since been included in a block and therefore not be provided later.
Why only pool transactions are covered
Restricting lookups to the transaction pool is deliberate. Transactions outside the pool are stored in batches of hundreds inside block bodies, and existing network lookups already follow this structure; allowing individual transactions to be pulled directly from disk would expose expensive database reads to the network with no useful purpose. Transactions already finalized on disk are propagated as part of blocks anyway, so in the worst case this only adds a delay of a few hundred milliseconds. Hash announcement deduplication follows the same logic and is done only against the pool, ignoring disk. In a healthy network, transactions propagate much faster than block inclusion, so a newly announced transaction is effectively never already on disk, and skipping disk-based deduplication also prevents DoS harassment via remote announcements. For stricter handling, keeping an in-memory set of transactions from recent blocks to filter out late announcements is suggested.
Why Transactions (0x02) is not reused
The initial proposal reused the existing Transactions (0x02) message as the response to GetPooledTransactions. However, since nodes constantly exchange Transactions (0x02) as gossip broadcasts anyway, it becomes hard to tell which of the flood of messages is the actual response to a request, complicating client code. Splitting it out as a separate PooledTransactions (0x0a) removes this problem and also leaves room for future optimizations, such as adding a request ID that would be meaningless for gossip broadcasts.
Deployment approach: a new version, no hard fork
Because this EIP extends the eth protocol in a backward-incompatible way, a new version, eth/65, must be released. However, since devp2p supports running multiple versions of the same wire protocol side by side, clients that have not updated can simply keep using eth/64, and no coordination of rollout across clients is needed. Since the consensus engine is unchanged, no hard fork is needed either. The original document's status is finalized as Final.