Skip to content

feat(runtime): configurable per-block transaction capacity - #2026

Draft
chrispalaskas wants to merge 1 commit into
mainfrom
christos-tx-weight-factor
Draft

feat(runtime): configurable per-block transaction capacity#2026
chrispalaskas wants to merge 1 commit into
mainfrom
christos-tx-weight-factor

Conversation

@chrispalaskas

@chrispalaskas chrispalaskas commented Aug 14, 2026

Copy link
Copy Markdown
Contributor

Overview - DO NOT MERGE - Only for testing

Adds one config value, tx_weight_factor_permille in res/<network>/system-parameters-config.json, that decides how many ledger transactions a block holds relative to the ledger's own block limits. 1000 (the default, and what configs without the field deserialize to) is unscaled; 500 means "half the weight per transaction", i.e. roughly twice as many per block.

This is for perf/test networks, where we want to pack more transactions into a block than the shipped limits allow. Today that needs an update-ledger-parameters governance transaction against a running chain; with this change the ledger genesis is prebaked with the same value the node uses.

Why the factor is applied in two different ways. A transaction's weight has two parts:

  • The ledger-derived part is the transaction's cost normalised against LedgerParameters::limits::block_limits (Bridge::get_transaction_costcost.normalize(limits)scale_normalized_cost(..., max_block)). generate-genesis now divides those block limits by the factor, which widens the ledger's own per-block capacity and narrows this part of the weight by exactly the same ratio — so FRAME's view of a full block and the ledger's stay in step.
  • The flat ConfigurableTransactionSizeWeight term (20 ms today) is independent of the block limits, so nothing else would rescale it. pallet_midnight applies the factor to that term, seeded from the same config file via the chain spec.

Deliberately not applying the factor to the ledger-derived part in the pallet: that would count it twice, letting FRAME pack ~4x while the ledger admits 2x, and the surplus transactions would be included in the block but fail with BlockLimitExceededError. doubling_block_limits_halves_the_scaled_cost pins the property this rests on.

Genesis verification takes --system-parameters-config too and applies the same scaling to the config file before comparing against the genesis state — otherwise it would flag the scaled limits as a mismatch. Generator and verifier share one implementation, midnight_node_ledger_helpers::block_capacity::scale_block_limits.

Changing the factor requires regenerating the network's genesis state and chain spec. All networks default to 1000, so this is a no-op until a network sets it.

🗹 TODO before merging

  • /bot rebuild-metadata — new set_tx_weight_factor call and TxWeightFactorPermille storage item change runtime metadata
  • Ready

📌 Submission Checklist

  • All commits are signed off (git commit -s) for the DCO
  • Changes are backward-compatible (or flagged if breaking)
  • Pull request description explains why the change is needed
  • Self-reviewed the diff
  • I have included a change file, or skipped for this reason:
  • If the changes introduce a new feature, I have bumped the node minor version
  • Update documentation (if relevant)
  • Updated AGENTS.md if build commands, architecture, or workflows changed
  • No new todos introduced

Backward compatibility: tx_weight_factor_permille has a serde default, so network configs that predate it parse unchanged; the pallet's genesis field is Option<u32> and None leaves the storage default; raw chain specs without the storage item fall back to the same default. --system-parameters-config is optional on both generate-genesis and verify-ledger-state-genesis.

🧪 Testing Evidence

Workspace cargo check --all-targets and cargo clippy --workspace --all-targets clean; cargo fmt --all applied.

pallet-midnight   27 passed  (3 new)
midnight-node-ledger-helpers  53 passed  (4 new)
midnight-node-ledger           3 passed  (1 new, one per ledger version)
midnight-node                138 passed

New tests:

  • pallet_midnight::tests::tx_weight_factor_rescales_only_the_flat_term — the factor moves ConfigurableTransactionSizeWeight and leaves the ledger-derived term alone.
  • pallet_midnight::tests::tx_weight_factor_comes_from_genesis_config — the chain-spec path seeds the storage value.
  • pallet_midnight::tests::tx_weight_factor_requires_root.
  • block_capacity::tests::* — 500 doubles every block-limit dimension, 2000 halves them, 1000 is a no-op, 0 is rejected.
  • ledger::...::doubling_block_limits_halves_the_scaled_cost — the coupling the design rests on: doubling the block limits halves the scaled cost.

Measured weight split on the undeployed fixtures, for context on what the factor buys (normal-class limit 1.5e12):

fixture ledger term flat term txs/block at 1000 at 500
contract call (CHECK_TX) 15.9 G 20.0 G 41 ~83
deploy (DEPLOY_TX) 295.6 G 20.0 G 4 ~8

Not run: the toolkit's end-to-end generate-genesis test (needs a proof server). An actual perfnet genesis rebuild at 500 is the remaining validation.

  • Additional tests are provided (if possible)

🔱 Fork Strategy

  • Node Runtime Update
  • Node Client Update
  • Other:
  • N/A

No migration needed: the new storage item is ValueQuery with a default of 1000, so an existing chain reads unscaled until root calls set_tx_weight_factor. Changing the factor for a network means regenerating its genesis state and chain spec, i.e. a new network rather than an upgrade.

Links

Adds `tx_weight_factor_permille` to `res/<network>/system-parameters-config.json`:
one value deciding how many ledger transactions a block holds relative to the
ledger's own block limits. `1000` (the default, and what configs without the
field deserialize to) is unscaled; `500` means "half the weight per transaction",
i.e. roughly twice as many per block.

A transaction's weight has two parts, and the factor reaches them by two routes:

- The ledger-derived part is the transaction's cost normalised against
  `LedgerParameters::limits::block_limits`. `generate-genesis` divides those
  limits by the factor, which widens the ledger's own per-block capacity and
  narrows this part of the weight by the same ratio, so FRAME's view of a full
  block and the ledger's stay in step without a governance transaction.
- The flat `ConfigurableTransactionSizeWeight` term does not follow the block
  limits, so `pallet_midnight` applies the factor to it directly. The chain spec
  seeds the new `TxWeightFactorPermille` storage from the same config file;
  `set_tx_weight_factor` (root) can change it on a running chain.

Genesis verification takes `--system-parameters-config` and applies the same
scaling to the config file before comparing it against the genesis state, so the
generator and the verifier share one implementation
(`midnight_node_ledger_helpers::block_capacity::scale_block_limits`).

Assisted-by: Claude:claude-opus-5
Signed-off-by: chrispalaskas <chris.palaskas@gmail.com>
@chrispalaskas chrispalaskas added skip-changes-check-issue bot:ai-assisted Authored or substantially edited by an AI agent labels Aug 14, 2026
@datadog-official

Copy link
Copy Markdown

Pipelines

⚠️ Warnings

🚦 2 Pipeline jobs failed

CI + E2E | Metadata Check   View in Datadog   GitHub Actions

See error Binary files image_metadata.scale and repo_metadata.scale differ. Error in Earthfile:1104:4

Changes Check | check-changes   View in Datadog   GitHub Actions

See error No changes found for PR 2026 causing process to exit with code 1.

Useful? React with 👍 / 👎

This comment will be updated automatically if new data arrives.
🔗 Commit SHA: 29dc7b5 | Docs | Datadog PR Page | Give us feedback!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bot:ai-assisted Authored or substantially edited by an AI agent skip-changes-check-issue

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant