Just want to highlight what I think is next here:
I’d love to see node implementation developers experiment with how the VM can most simply and efficiently limit stack memory usage.
The current proposal is essentially: sum_all_stack_items + stack_depth <= 130,000
. That limit is rounded up from a max of 125,320 + 242
in what we believe is the most memory-expensive script currently possible.
The issue here is that we need to check memory usage after every operation in which memory usage might have increased.
Instead of the simple, incrementing counter that is currently used (++maxOperationsPerEvaluation < 201
), we have to accumulate the sizes of all stack memory items, then add the length of the stack.
The calculation is not terribly expensive, and it can be optimized in many ways – e.g. many operations can skip the check, stack memory usage at >6 depth can be cached across operations, etc., but the current formulation still strikes me as less elegant than past improvements to the VM.
It’s possible that this is the best we can do, but I’d like to see some other people review the issue. Some questions:
-
Stack depth: Do we actually need to limit stack depth?
MAX_SCRIPT_SIZE
is 10,000 bytes (and for standardness,MAX_TX_IN_SCRIPT_SIG_SIZE
is only 1,650 bytes), so withOP_3DUP
, we’re looking at a maximum depth of ~30k for transactions first heard in blocks (and only ~5k for transactions first heard over the network). Can we save cycles and complexity by just skipping that? (Are we ok with the protocol requiring stack +OP_ROLL
implementations to be reasonably optimized?) And if not, do we need to multiply depth by some number >1 to represent the realistic overhead of a stack item? - Memory usage: Is there a more elegant way to limit stack memory usage? Rather than checking total usage after every operation in which it may have increased, is there a more efficient, less direct strategy?
If other developers with an interest in low-level optimization want to take a look at this, I’d love to hear your thoughts!