ContentsBlock capacity and bounds

Block capacity and bounds

On this page
  1. Block totals
  2. Declared versus actual
  3. Active capacity
  4. Bounds that follow from the counters
  5. Fixed bounds

Block totals#

A block's C, S and G are the sums of what its transactions actually used. Its D also includes the header, the producer data and the transaction count, so for a block whose only output is its transactions' top-level results and the events their receipts keep, D equals the block's encoded size in bytes; any other output makes it larger (counters). All four totals must fit the active capacity at that height.

block_used[C, S, G] = sum(transaction_used[C, S, G])
block_used[D]       = 302 + len(producer_data) + sum(transaction_used[D])

Each transaction's used vector is what its receipt records, including everything consumed before a failure; the 302 bytes are broken down under The block's own bytes. Each of the four totals must be at most the active capacity, derived independently from the block's ancestry, and must equal the resources the header declares.

A block that exceeds a capacity is invalid as a whole. It does not change any transaction's outcome to fit the remaining space, and since counters are never restored by rollback, a revert cannot bring a total back under the limit. A validator may stop executing as soon as a debit would push the block over a limit, checking the transaction's own budgets first, so a debit the transaction could not afford never counts toward the block. That early stop is only a way of rejecting the block; it never produces an alternative receipt.

Declared versus actual#

A transaction's declared budget may exceed the current block capacity, because only what it actually uses has to fit. Any value is a valid declaration. A transaction can therefore carry generous headroom without its author knowing how full the block will be, and simulating a transaction needs no assumption about how much block space remains.

Active capacity#

Capacity starts at the genesis initial values and moves only through capacity voting, upward without limit and downward to the genesis floor. Your node's current capacities appear on the explorer's overview page and in /v1/status.

Bounds that follow from the counters#

Nothing a program does is capped by a fixed per-transaction number. Everything that could be capped follows from the budgets instead:

QuantityBounded by
Live guest memoryCompute: every 64 KiB page costs 40,000 C and is never refunded, about 1.6 bytes per C
Local stackCompute: every function entry pays its parameters, locals and operand slots
Nesting depthState access: every nested invocation reads two records before it runs
Record sizeThe memory that holds the value, and the compute to copy and hash it
Module size2^30 bytes, and the code price, about 60 C per byte, paid to validate it
Pending output and state journalsD, S and G: every byte and record they hold was charged

A node therefore knows, from the active capacities alone, how much memory a block can need. It may store journals however it likes, but local memory pressure can never turn into a different consensus result.

Fixed bounds#

Only two kinds of fixed bound remain: the 32-bit address space of a single instance, and the shape of a module, which exists so that validating a module takes work, and validator memory, proportional to its bytes. The shape bounds are set far above what compilers produce, and none of them is a capacity.

ItemAt most
Linear memory of one instance65,536 pages of 64 KiB (4 GiB)
Module2^30 bytes
Parameters of one function type1,000
Parameters plus declared locals of one function16,384
Reserved slots of one function, including its maximum operand height32,768
Functions per module, imports included1,000,000
Types per module1,000,000
Globals per module1,000,000
Data segments per module100,000
Element segments per module100,000
Table entriesThe module's function count

How a module is checked against them is on Validation. The reasoning is in Prices, not caps.