Low Cost DoS Attack on Tendermint

The Tendermint and ABCI protocol allow invalid transactions in a block. The invalid transaction may pass CheckTx, but when it is included in a block and executed with other transactions, the transaction becomes invalid.

E.g., suppose an account A has 100 Token and the owner creates two transactions:
Tx0: transfer 99 Token to B with 1 Token as tx fee
Tx1: transfer 99 Token to C with 1 Token as tx fee

Both Tx0 and Tx1 could pass CheckTx and they are valid. However, if both of them are included in the same block, the later Tx will be invalid due to insufficient funds.

So here is the attack: the attacker creates a list of Tx0, Tx1, …, TxN with sufficient high tx fee, where each of them is valid, but will be invalid if any tx in the list is executed in the blockchain. The attacker submits all Txs to the network, and if the validator packs all of them in a block, then only one is valid, and the rest will be invalid. This could be repeated for each block, and thus the attack essentially congests the network with the fee of one tx per block while excluding other transactions with a lower fee to be included in the network.

Could anyone tell me whether it is true, or if so any way to prevent it?

Cosmos mempool doesn’t get sorted by the fee. It is addressed in simple FIFO order

Ok. I assume that because ETH and BTC sorts mpool by fee by default. In this case, could we still perform a dust attack such as filling a full block with one valid tx and the rest invalid txs?

ABCI apps should ensure that if a sequence of txs passes CheckTx in order, then they will also pass DeliverTx in order. Since correct validators propose blocks using txs in the same order that they’re found in the mempool, the guarantees should hold all the way through. Of course malicious validators can always propose blocks with invalid txs, but we assume theres <1/3 of them, and ABCI apps can be written to punish them for doing so!

Thanks for pointing this out. This sounds reasonable to me. I do check the CosmosSDK implementation, where checkState is reset for each commit, and checkState is modified after each CheckTx and reused for the next CheckTx, in order to ensure the same sequence of both CheckTx and DeliverTx, a.k.a., the same-sequence property, right?

A further correctness concern is about RecheckTx: if the number of txs in mempool exceeds that of a block, my understanding is that, to ensure the same-sequence property, after the block is committed and the txs in the blocks are reaped from mempool, all the remaining txs in mempool will be passed to CheckTx with recheck flag again, right? This makes sure that the new user-submitted tx passed to CheckTx will follow the state that all txs in mempool are applied, i.e., all rechecks are done.

Code for reference:

  • Reset checkState after commit
  • Use checkState for each DeliverTx

Also using FIFO rule will incentivize user to submit tx with minimal tx fee as higher fee cannot prioritize the tx, where ETH allows users to speed up a tx by submitting a tx with the same seq. no and higher fee.

Validators set up minimum gas fee that they’ll accept for a transaction to be included in the block to prevent spam attacks and maximize profits. Transactions with lower fee have to wait till it’s time for validator with that low fee or no fee to propose a block.

1 Like

Yes, I know each validator could setup a minimal gas fee, and a user could figure out the number and always submit a tx with the fee. In addition, no user is able to speed up its tx if the gas fee equals to max(all validator minimal fee).

The reason I came up with the question is to see if there is a value to add “speculative block execution” to TM when a validator creates a block and other validators vote a proposed block:

  • When creating a block, instead of reaping all mempool txs, the validator will speculatively execute the txs and if a tx is invalid, it will be not included the block to be created; and
  • When receiving a block from a proposer, the validator will speculatively execute the block and reject the block if an invalid tx is found.

This will make sure that a block with invalid tx should never be voted on chain, remove the assumption that the mempool is FIFO, and be able to prioritize a tx.

The cons are:

  • The ABCI needs to support speculative block execution. This may be done by adding a flag in BeginBlock/DeliverTx/EndBlock. In addition, with the cached store design in Cosmos SDK, implementing the speculative block execution may not be difficult.
  • Performance may be degraded since a validator must execute the block before voting. However, this can speed up the following processing of committing a block because all the execution/results are well cached (especially IO read part, which may be one of the most expensive part for a block execution).

Any thoughts?