kadena-io/chainweb-node

DoS Attack Potential Using gasLimit

Luzzotica opened this issue · 6 comments

As of right now, I can request a transaction that requires ~250 gas by just committing "hello" as pact code.

In that request, I can set the gasLimit to 150000 (The max gasLimit set by api.chainweb.com node).

Which reserves 150000 gas on the block my transaction gets committed to.
This blocks out 150000 gas on the block for a ~250 gas transaction.
Which means there's only 30000 gas left. It'd be easy to do this twice.

Running some quick math on all of the above:

24 * 60 * 60 / 30 = 2880 blocks per day
Submitting one 150k gasLimit transaction per block is
2880 * 250 * 0.0001 = 72 KDA per for one chain
0.0001 is so that my transaction gets chosen first.

Which means I can effectively DoS a single chain for a day for 72KDA.

Or ~4k to slow down all of Kadena for 3 days (72320).

Another thing to note is that developers don't want their transactions to fail, so new devs will generally set the gas limit REALLY high.
Which means they are inadvertently causing a DoS attack, and slowing down the entire chain.
I know I've already done it to myself when working with DadBod.

This must be fixed.

To Fix
Plain and simple: the gasLimit shouldn't reserve that amount on the block.

Hi @Luzzotica, thanks for the report. You mention that you have done this attack by accident while working with DadBod. I notice that Luzzotica/DadBod has its only public commit in June of 2022. #1399 was released in April of 2022, in chainweb-node 2.14. It contains our fix for this issue. Is it possible that you encountered this issue with an earlier version of chainweb-node?

I tested it this morning.
It appears as though it only has an impact if that gasLimit is above 140000.

If your gas limit is over 140000 then only one transaction will be fit into each block.

If the gas limit is less than that, then it appears to somewhat ignored. Or at least exponentially reduced.

For example.
I was able to fit one 150k gasLimit transaction into a block, and only 4-5 130k transaction ones. Which means the gasLimit was still being used in some way to govern block size.

As you've noticed, you are not able to "reserve" gas, at least not after #1399.

What's likely happening here at gas limit ~140,000 is that you are not able to include a subsequent high gas limit tx in a block if there is not enough gas remaining in the block to satisfy the high gas limit; the default whole-block gas limit is 150,000. A lower gas limit tx could have made it into the block after your high gas limit tx; again there is no "reservation" of block gas, and the node is not willing to "speculatively" execute your high gas limit tx to find out if it actually uses little enough gas to fit into the block.

A tx with a gas limit that doesn't fit in the rest of the block will be skipped over in favor of txs with smaller gas limits, which means that there is no denial of service attack here, but it does reveal that there is a strong incentive, if you want your transactions to get into blocks quickly, to have the smallest gas limit that is feasible for your transaction. The node's programming reflects that miners like to have a good idea of how expensive a transaction will be before executing it.

I don't understand how what you're saying is different from a gas reservation in the block.
My high gas limit transaction fails because it can't fit.

Here's what I think you're trying to say: The "current block size" is calculated as transactions are added to the block by actually running the transaction and determining the "true" gas cost of a transaction.
Then it will try to add additional transactions.

If the gasLimit can't fit (150k - "current block size" < gasLimit), then the transaction will be ignored, and it will find better fits.

Is this what you're saying?

If so, then perfect. And you're right there is no DOS attack vector.
It only seems like it when you submit transactions that are 150k gas.

I think we're on the same page here, yes. Your high gas limit tx fails because it can't fit, because the true gas cost of previous transactions in the block has been subtracted from the block's total gas limit.

If the gasLimit can't fit (150k - "current block size" < gasLimit), then the transaction will be ignored, and it will find better fits.

Exactly correct.

Got it.

Thank you for the description.