mirror of https://github.com/zcash/zips.git
216 lines
9.5 KiB
ReStructuredText
216 lines
9.5 KiB
ReStructuredText
::
|
||
|
||
ZIP: 401
|
||
Title: Addressing Mempool Denial-of-Service
|
||
Owners: Daira Hopwood <daira@electriccoin.co>
|
||
Status: Final
|
||
Category: Network
|
||
Created: 2019-09-09
|
||
License: MIT
|
||
|
||
|
||
Terminology
|
||
===========
|
||
|
||
The key words "MUST", "SHOULD", and "MAY" in this document are to be interpreted
|
||
as described in RFC 2119. [#RFC2119]_
|
||
|
||
|
||
Abstract
|
||
========
|
||
|
||
This proposal specifies a change to the behaviour of ``zcashd`` nodes intended to
|
||
mitigate denial-of-service from transaction flooding.
|
||
|
||
|
||
Motivation
|
||
==========
|
||
|
||
Adoption of this proposal would increase robustness of Zcash nodes against
|
||
denial-of-service attack, in particular attacks that attempt to exhaust node
|
||
memory.
|
||
|
||
Bitcoin Core added size limitation for the mempool in version 0.12
|
||
[#BitcoinCore-PR6722]_, defaulting to 300 MB. This was after Zcash forked from
|
||
Bitcoin Core.
|
||
|
||
|
||
Requirements
|
||
============
|
||
|
||
The memory usage of a node’s mempool should be bounded.
|
||
|
||
The eviction policy should as far as possible not be “gameable” by an adversary,
|
||
i.e. an adversary should not be able to cause legitimate transactions (that do not
|
||
themselves present any denial-of-service problem) to be preferentially evicted
|
||
relative to its own transactions.
|
||
|
||
Any configuration options should have reasonable defaults, i.e. without changing
|
||
relevant configuration, a node should be adequately protected from denial-of-service
|
||
via mempool memory exhaustion.
|
||
|
||
|
||
Non-requirements
|
||
================
|
||
|
||
The current architecture of Zcash imposes fundamental limits on scaling of
|
||
transaction throughput. This proposal does not increase the aggregate transaction
|
||
capacity of the network. (The Blossom upgrade does increase transaction capacity,
|
||
by a factor of two [#zip-0208]_.)
|
||
|
||
Denial-of-service issues in the messaging layer of the peer-to-peer protocol are
|
||
out of scope for this proposal.
|
||
|
||
This proposal is focused primarily on memory exhaustion attacks. It does not
|
||
attempt to use fees to make denial-of-service economically prohibitive, since that
|
||
is unlikely to be feasible while maintaining low fees for legitimate users. It
|
||
does not preclude changes in fee policy.
|
||
|
||
|
||
Specification
|
||
=============
|
||
|
||
This specification describes the intended behaviour of ``zcashd`` nodes. Other node
|
||
implementations MAY implement the same or similar behaviour, but this is not a
|
||
requirement of the network protocol. Thus, RFC 2119 conformance keywords below are
|
||
to be interpreted only as placing requirements on the ``zcashd`` implementation (and
|
||
potentially other implementations that have adopted this specification in full).
|
||
|
||
The mempool of a node holds a set of transactions. Each transaction has a *cost*,
|
||
which is an integer defined as:
|
||
|
||
max(serialized transaction size in bytes, 4000)
|
||
|
||
Each transaction also has an *eviction weight*, which is *cost* + *low_fee_penalty*,
|
||
where *low_fee_penalty* is 16000 if the transaction pays a fee less than the
|
||
conventional fee, otherwise 0. The conventional fee is currently defined as
|
||
1000 zatoshis [#zip-0313]_.
|
||
|
||
Each node also MUST hold a FIFO queue RecentlyEvicted of pairs (txid, time), where
|
||
the time indicates when the transaction with the given txid was evicted. The txid
|
||
(rather than the wtxid as defined in [#zip-0239]_) is used even for version 5
|
||
transactions after activation of NU5 [#zip-0252]_.
|
||
|
||
The RecentlyEvicted queue SHOULD be empty on node startup. The size of RecentlyEvicted
|
||
SHOULD never exceed ``eviction_memory_entries`` entries, which is the constant 40000.
|
||
|
||
There MUST be a configuration option ``mempooltxcostlimit``, which SHOULD default
|
||
to 80000000.
|
||
|
||
There MUST be a configuration option ``mempoolevictionmemoryminutes``, which
|
||
SHOULD default to 60.
|
||
|
||
On receiving a transaction:
|
||
|
||
* If it is in RecentlyEvicted, the transaction MUST be dropped.
|
||
* Calculate its cost. If the total cost of transactions in the mempool including
|
||
this one would exceed ``mempooltxcostlimit``, then the node MUST repeatedly
|
||
call EvictTransaction (with the new transaction included as a candidate to evict)
|
||
until the total cost does not exceed ``mempooltxcostlimit``.
|
||
|
||
EvictTransaction MUST do the following:
|
||
|
||
* Select a random transaction to evict, with probability in direct proportion to
|
||
eviction weight.
|
||
* Add the txid and the current time to RecentlyEvicted, dropping the oldest entry
|
||
in RecentlyEvicted if necessary to keep it to at most ``eviction_memory_entries``
|
||
entries.
|
||
* Remove it from the mempool.
|
||
|
||
Nodes SHOULD remove transactions from RecentlyEvicted that were evicted more than
|
||
``mempoolevictionmemoryminutes`` minutes ago. This MAY be done periodically,
|
||
and/or just before RecentlyEvicted is accessed when receiving a transaction.
|
||
|
||
|
||
Rationale
|
||
=========
|
||
|
||
The accounting for transaction size should include some overhead per transaction,
|
||
to reflect the cost to the network of processing them (proof and signature
|
||
verification; networking overheads; size of in-memory data structures). The
|
||
implication of not including overhead is that a denial-of-service attacker would
|
||
be likely to use minimum-size transactions so that more of them would fit in a
|
||
block, increasing the unaccounted-for overhead. A possible counterargument would
|
||
be that the complexity of accounting for this overhead is unwarranted given that
|
||
the format of a transaction already imposes a minimum size. However, the proposed
|
||
cost function is almost as simple as using transaction size directly.
|
||
|
||
The threshold 4000 for the cost function is chosen so that the size in bytes of a
|
||
typical fully shielded Sapling transaction (with, say, 2 shielded outputs and up
|
||
to 5 shielded inputs) will fall below the threshold. This has the effect of
|
||
ensuring that such transactions are not evicted preferentially to typical
|
||
transparent transactions because of their size.
|
||
|
||
The proposed eviction policy differs significantly from that of Bitcoin Core
|
||
[#BitcoinCore-PR6722]_, which is primarily fee-based. This reflects differing
|
||
philosophies about the motivation for fees and the level of fee that legitimate
|
||
users can reasonably be expected to pay. The proposed eviction weight function
|
||
does involve a penalty for transactions with a fee lower than the standard
|
||
(0.0001 ZEC) value, but since there is no further benefit to increasing the fee
|
||
above the standard value, it creates no pressure toward escalating fees. For
|
||
transactions up to 4000 bytes, this penalty makes a transaction that pays less
|
||
than the standard fee value five times as likely to be chosen for eviction
|
||
(because 4000 + 16000 = 20000 = 4000 \* 5).
|
||
|
||
The fee penalty is not included in the cost that determines whether the mempool
|
||
is considered full. This ensures that a DoS attacker does not have an incentive
|
||
to pay less than the standard fee in order to cause the mempool to be considered
|
||
full sooner.
|
||
|
||
The default value of 80000000 for ``mempooltxcostlimit`` represents no more
|
||
than 40 blocks’ worth of transactions in the worst case, which is the default
|
||
expiration height after the Blossom network upgrade [#zip-0208]_. It would serve
|
||
no purpose to make it larger.
|
||
|
||
The ``mempooltxcostlimit`` is a per-node configurable parameter in order to
|
||
provide flexibility for node operators to change it either in response to
|
||
attempted denial-of-service attacks, or if needed to handle spikes in transaction
|
||
demand. It may also be useful for nodes running in memory-constrained environments
|
||
to reduce this parameter.
|
||
|
||
The limit of ``eviction_memory_entries`` = 40000 entries in RecentlyEvicted bounds
|
||
the memory needed for this data structure. Since a txid is 32 bytes and a
|
||
timestamp 8 bytes, 40000 entries can be stored in ~1.6 MB, which is small compared
|
||
to other node memory usage (in particular, small compared to the maximum memory
|
||
usage of the mempool itself under the default ``mempooltxcostlimit``).
|
||
``eviction_memory_entries`` entries should be sufficient to mitigate any
|
||
performance loss caused by re-accepting transactions that were previously evicted.
|
||
In particular, since a transaction has a minimum cost of 4000, and the default
|
||
``mempooltxcostlimit`` is 80000000, at most 20000 transactions can be in the
|
||
mempool of a node using the default parameters. While the number of transactions
|
||
“in flight” or across the mempools of all nodes in the network could exceed this
|
||
number, we believe that is unlikely to be a problem in practice.
|
||
|
||
Note that the RecentlyEvicted queue is intended as a performance optimization
|
||
under certain conditions, rather than as a DoS-mitigation measure in itself.
|
||
|
||
The default expiry of 40 blocks after Blossom activation represents an expected
|
||
time of 50 minutes. Therefore (even if some blocks are slow), most legitimate
|
||
transactions are expected to expire within 60 minutes. Note however that an
|
||
attacker’s transactions cannot be relied on to expire.
|
||
|
||
|
||
Deployment
|
||
==========
|
||
|
||
This specification is proposed to be implemented in ``zcashd`` v2.1.0. It is
|
||
independent of the Blossom network upgrade.
|
||
|
||
|
||
Reference implementation
|
||
========================
|
||
|
||
* `PR 4145: Implementation <https://github.com/zcash/zcash/pull/4145>`_
|
||
* `PR 4166: macOS compliation fix <https://github.com/zcash/zcash/pull/4166>`_
|
||
|
||
|
||
References
|
||
==========
|
||
|
||
.. [#RFC2119] `RFC 2119: Key words for use in RFCs to Indicate Requirement Levels <https://www.rfc-editor.org/rfc/rfc2119.html>`_
|
||
.. [#zip-0208] `ZIP 208: Shorter Block Target Spacing <zip-0208.rst>`_
|
||
.. [#zip-0239] `ZIP 239: Relay of Version 5 Transactions <zip-0239.rst>`_
|
||
.. [#zip-0252] `ZIP 252: Deployment of the NU5 Network Upgrade <zip-0252.rst>`_
|
||
.. [#zip-0313] `ZIP 313: Reduce Conventional Transaction Fee to 1000 zatoshis <zip-0313.rst>`_
|
||
.. [#BitcoinCore-PR6722] `Bitcoin Core PR 6722: Limit mempool by throwing away the cheapest txn and setting min relay fee to it <https://github.com/bitcoin/bitcoin/pull/6722>`_
|