EVMPARTY: Gaslimit per block

Me and Robby had a lengthy conversation about the gas limit per block and felt that based on a summary of that we could have a good community discussion about this, so I’m posting a summary of what we discussed here and hope we get some input ;);

so it’s a given that we’ll need a gaslimit per block to prevent a DoS attack where 1 block takes (way) too long to process.

this block gaslimit will be so that it’s doable to catch up with a fresh node,
so you should be able to parse the limit of block gaslimit amount of executions in probably < 1min on mediocre hardware so that you can catchup 1 day of blocks in 2.4hrs.
it should probably be even lower, but we’ll benchmark to see what is feasible (1min can contain A LOT of executions).

one thing in case of a DoS attack that is hard to deal with is;
if the block gas limit is reached before the end of parsing all smart contract executions in that block it will be very awkward for the ‘users’ of the later executions, because their transactions be ignored / dropped.

there’s 2 options to deal with it:

  1. they need to do another bitcoin TX to repeat the execution
  2. we need to queue all the executions and on the next block parse the queue before any new executions from that block

the latter seems like the only solution that wont make users go insane during a DoS on the block gaslimit.

to draw out how this would work:

gaslimit = 100 XCP

block 1
-------
 - TX1 executes for 20 XCP
 - TX2 executes for 100 XCP
 - TX3 executes for 20 XCP

parsing block:
 - TX1 : parsed, gaslimit = 80 XCP
 - TX2 : parsed, hits block gaslimit, halted and placed in queue
 - TX3 : placed in queue, block gaslimit already hit

block 2
-------
 - TX4 executes for 40 XCP
 - TX5 executes for 60 XCP

parsing queue:
 - TX2 : parsed, gaslimit = 0 XCP
 - TX3 : left in queue, block gaslimit already hit

parsing block:
 - TX4 : left in queue, block gaslimit already hit
 - TX5 : left in queue, block gaslimit already hit

block 3
-------
 - TX6 executes 20 XCP
 - TX7 executes 20 XCP

parsing queue:
 - TX3 : parsed, gaslimit = 80 XCP
 - TX4 : parsed, gaslimit = 60 XCP
 - TX5 : parsed, gaslimit = 0 XCP

parsing block:
 - TX6 : placed in queue, block gaslimit already hit
 - TX7 : placed in queue, block gaslimit already hit

block 4
-------
 - TX8 executes 40 XCP

parsing queue:
 - TX6 : parsed, gaslimit = 80 XCP
 - TX7 : parsed, gaslimit = 60 XCP

parsing block:
 - TX8 : parsed, gaslimit = 20 XCP

an attacker could queue up a week of executions, however, it’s a lot more transparent than the first option and an attacker willing to spend enough XCP to mount a DoS attack that last hours or even days should be able to do so with option 1 as well, he’d just have to do a bunch of extra bitcoin TXs.
from our perspective we can consider the order of TXs in the block to be random, so in case of a DoS a few lucky other people might get a couple of executions higher on the list of TXs in the block,
but the impact of the DoS will still be very high.

we think the queue is worth it, reducing the awkwardness of having to rebroadcast your executions when they didn’t get executed.

we discussed letting the user set a gasprice and order the queue by the gasprice to get bumped up in the queue,
essentially how ethereum and bitcoin work with the mempool and the assumption that miners are rational and pick the TXs that pay them the most.
(but for us the queue would be deterministic instead of the (almost) complete unknown that the bitcoin / eth mempool is.)

but that would also mess with double spends,
a simple example is a provible fair ponzie smart contract (this is an actual thing on ethereum atm!):

if I’d see you buy in to the ponzie I’d create a TX and outbid you on the gasprice you’ve set, that way I’ll be before you in the queue and I’d guarantee myself a profit.
which sort-of is already possible in ethereum, except the mempool is more unknown and harder to game then if it would be deterministic and with 10min blocks.

this could be mitigated a bit by only applying this to when the queue is actually used, but if there’s enough money at stake an attacker could be willing to keep a DoS rolling to keep the queue in effect.
it would at least mean to game that system you’d have to spend enough XCP to fill the blocks continuesly.

the problem without this mechanism would be that no matter how much it is worth to you to get your execution off you have to wait for the queue to empty.
while in bitcoin if I transfer $1mil I can easily pay $10 in fees to ensure I get around any DoS of an attacker paying $0.50 fees.

the biggest / only real weak point with the queue I see is that if you’d queue up a bunch of TXs in 1 blocks which all use 51% of the block gaslimit we’d continuously waste the remaining 49% because the 2nd get’s pushed back into the queue all the time.
essentially the cost to DoS the network with this would be 51% of the block gaslimit, not 100%.

again to illustrate:

gaslimit = 100 XCP

block 1
-------
 - [attacker] TX1 executes for 51 XCP
 - [attacker] TX2 executes for 51 XCP
 - [attacker] TX3 executes for 51 XCP
 - [attacker] TX4 executes for 51 XCP
 - [attacker] TX5 executes for 51 XCP
 - [attacker] TX6 executes for 51 XCP
 - [attacker] TX7 executes for 51 XCP

parsing block:
 - TX1 : parsed, gaslimit = 49 XCP
 - TX2 : parsed, hits block gaslimit, halted and placed in queue

block 2
-------
 - [normal user] TX8 executes for 20 XCP
 - [normal user] TX9 executes for 30 XCP

parsing queue:
 - TX2 : parsed, gaslimit = 49 XCP
 - TX3 : parsed, hits block gaslimit, halted and placed back in queue
 - TX4, TX5, TX6, TX7: left in queue, block gaslimit already hit

parsing block:
 - TX8 : placed in queue, block gaslimit already hit
 - TX9 : placed in queue, block gaslimit already hit

block 3
-------
parsing queue:
 - TX3 : parsed, gaslimit = 49 XCP
 - TX4 : parsed, hits block gaslimit, halted and placed back in queue
 - TX5, TX6, TX7, TX8, TX9: left in queue, block gaslimit already hit

block 4
-------
parsing queue:
 - TX4 : parsed, gaslimit = 49 XCP
 - TX5 : parsed, hits block gaslimit, halted and placed back in queue
 - TX6, TX7, TX8, TX9: left in queue, block gaslimit already hit

block 5
-------
parsing queue:
 - TX5 : parsed, gaslimit = 49 XCP
 - TX6 : parsed, hits block gaslimit, halted and placed back in queue
 - TX7, TX8, TX9: left in queue, block gaslimit already hit

... etc ... 

another question is if we should age out TXs in the queue after some period if not “parsed”, but again this would result in awkwardness, I don’t think there’s a reason to do so, or maybe it should be opt-in for users who are doing something that is time sensitive and they’d rather have it not execute after X blocks have gone by.

1 Like

I like option #2 with gaslimit, queueing up executions and executing queue before new executions.

Also in favor of expiring executions from the queue after a set period if not “parsed”.

I think the expiration should at least be configurable / with a high default

1 Like

Counterparty transactions are parsed in the order that the transactions are mined, correct? I think that should stay the same for smart contract executions. The first transaction in the block is executed first, regardless of gas price or fees paid.

So, in the fair ponzi example, for me to beat you I would need to get my transaction mined before you in the block. This is theoretically possible, but you have to have a relationship with the miner who mined that block. And since you never know who will mine the next block, this attack seems difficult to mount.

If pseudo-random order is important, we could determine our own deterministic order for processing transactions in a block. Perhaps you could take the last 64 bits of the transaction ID and XOR that with the last 64 bits of the block hash. Then process the transactions in this order.

I think the simplest solution will work fine here:

  1. process the queued transactions from previous blocks in order
  2. process new transactions in the order they appear in the block
  3. once the gas limit is exceeded, add all remaining transaction to the end of the queue

I don’t think a timeout is necessary or a good idea. If a spam attack becomes trivial to mount, then the gas price should adjust dynamically to account for it (another discussion!).

1 Like

the XOR thing is interesting, though if a miner is in on an attack then he can probably orchestrate that too

the problem with a dynamic gas price would be that we’d fall back to the old problem, you won’t be 100% sure your broadcasted and mined TX will get processed because the gas price might skyrocket and push you out.