Skip to content

[scratch] e2e: aztec-benchmark block-duration-ms (do not merge) - #45

Closed
alejoamiras wants to merge 3 commits into
mainfrom
worktree-e2e-bench-block-duration
Closed

alejoamiras wants to merge 3 commits into
mainfrom
worktree-e2e-bench-block-duration

Conversation

@alejoamiras

Copy link
Copy Markdown
Collaborator

Scratch PR, do not merge. It will be closed once the checks report.

This is a live e2e of the new block-duration-ms input in AztecProtocol/aztec-benchmark at d623993 (branch worktree-aztec-v6-update). It runs on top of #38, whose Vault class publish needs 63,776 DA gas; the local network's default 3s blocks cap a transaction at 55,836.

  • J1 ("6000"): expected to pass, with the Vault class published.
  • J2 (input omitted): expected to fail at the 55,836 cap, showing the default behaviour is unchanged.
  • J3 ("6s"): expected to fail in "Configure block duration".
  • J4: update-baseline.yml pinned to the same commit with "6000", dispatched on this branch. Its artifact is keyed by this branch, so main's baseline is untouched.

🤖 Generated with Claude Code

alejoamiras and others added 3 commits September 30, 2026 13:04
The conversions compute `a * b / denominator`. Done natively in u128 the
intermediate product overflows for large-but-legitimate inputs; Noir
range-checks u128, so the transaction reverts rather than wrapping. That
is an availability bug (audit F-004): a vault whose totals reach the
range can no longer be deposited to or withdrawn from, permanently
locking every participant's funds. Both sites carried TODOs.

New `conversion.nr` module with a `mul_div` primitive that widens both
operands to noir-bignum's U256 (the same library the escrow's key
derivation already uses), multiplies and divides there, then narrows the
quotient back to u128, asserting it fits. U256 arithmetic is modulo
2^256 and the largest possible product, (2^128-1)^2, is 2^129-1 short of
that modulus, so the product is always exact — no modular wraparound.

Rounding is unchanged: both old and new return
floor(p/d) + (round_up && p%d != 0). For every input the old code
accepted the results are identical; the widening only extends the domain
that succeeds. This matters because the vault's economic safety depends
on rounding always favouring the vault — a shift in either direction
would leak value between the vault and its depositors.

`mul_div` also rejects a zero denominator explicitly: noir-bignum's
constrained udiv_mod fails on it, but its unconstrained path assumes
non-zero and would return a meaningless witness. Unreachable from the
vault (denominators are total_assets+1 and total_supply+vault_offset
with vault_offset >= 1) but the helper is now safe in isolation.

Extracting mul_div into its own module is what makes the overflow
boundary testable at all: a real vault cannot be driven to a 2^128
supply in a test, but the primitive can be called directly at its edges.

Also adds `ensureVaultContractClassPublished` to the JS test utils.
Publishing just the class is what the vault tests actually need and is
substantially cheaper in DA gas than deploying a throwaway Vault to get
the class published as a side effect. It is idempotent — publication
emits a nullifier keyed on the class id, so a second publish of the same
class is rejected with "Existing nullifier"; the helper checks
registration state first, the same way DeployMethod does.

Validated: vault_contract 195 Noir tests (188 pre-existing all still
green — the strongest evidence rounding did not shift — plus 7 new
covering limb round-trips, rounding direction both ways, products that
previously overflowed, max operands, and the two revert guards).
aztec compile OK. Codex adversarial review: correct, rounding invariance
proven algebraically, no value-leak path; its zero-denominator hardening
is applied.

Note: the vault README still describes the overflow as a known issue.
That warning block is rewritten in PR #24 (unmerged); leaving it there
avoids a three-way conflict.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
`aztec start --local-network` defaults to 3s blocks. With 72s slots that
packs 21 blocks into a checkpoint, and the per-tx DA admission limit is
`ceil(daBudget / blocks * 1.5)` = 55,882 DA gas. Mainnet runs 6s blocks
(10 blocks per checkpoint) and admits 117,668.

The protocol picks that 1.5 multiplier deliberately, and says so in
aztec stdlib `gas/tx_gas_limits.ts`: it is set "so the largest tx we want
to support — a maximal contract class registration (~97k DA gas) — fits
a single block under v5 mainnet geometry (72s slots, 6s blocks -> 10
blocks per checkpoint)". The default local geometry therefore advertises
a limit well below the ~97k the protocol guarantees for exactly this
kind of transaction, and rejects contracts that are valid on the network
we ship to.

Publishing the Vault contract class costs ~64k DA gas. That is fine on
mainnet and inside the protocol's stated envelope, but over the local
55,882 cap. Note this is not specific to any one change: main's Vault
already sits at ~54k, i.e. 97% of the local ceiling, so essentially any
growth in that contract trips it.

Reusable workflows do not inherit the caller's `env`, and
aztec-ci-actions' run-tests.yml exposes no knob for this, so the JS job
is inlined here (`run-js-tests: false` on the reusable call) purely to
own the environment. It still calls the same pinned `setup-aztec` and
`js-tests` composite actions, so behaviour is otherwise unchanged.

NOTE FOR REVIEWERS: this renames the check from "checks / JS Tests" to
"JS Tests". Any branch protection rule naming the old check needs
updating, or it will block merges waiting on a check that no longer runs.

The better long-term fix is upstream — either the local network should
default to mainnet geometry, or run-tests.yml should expose the knob.
Worth raising with the Aztec CI folks.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… merge)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown

Benchmark Comparison

CPU Cores RAM Arch
AMD EPYC 7763 64-Core Processor 4 16 GiB x64

Contract: escrow

🚦 Function Gates DA Gas L2 Gas Proving Time (ms)
Base PR Diff Base PR Diff Base PR Diff Base PR Diff
⚪ (partial) withdraw 354,645 354,645 3,744 3,744 587,800 587,800 7,140 8,426 +1,286 (+18.0%)
⚪ withdraw 270,326 270,326 832 832 499,700 499,700 6,012 7,074 +1,062 (+17.7%)
⚪ withdraw_nft 266,680 266,680 1,440 1,440 527,400 527,400 6,155 7,251 +1,096 (+17.8%)

Contract: logic

🚦 Function Gates DA Gas L2 Gas Proving Time (ms)
Base PR Diff Base PR Diff Base PR Diff Base PR Diff
⚪ get_escrow 298,866 298,866 192 192 456,000 456,000 6,549 7,733 +1,184 (+18.1%)
⚪ secret_key_to_public_keys 296,331 296,331 192 192 456,000 456,000 6,520 7,678 +1,158 (+17.8%)
⚪ share_escrow 245,424 245,424 1,952 1,952 520,700 520,700 5,729 6,893 +1,164 (+20.3%)

Contract: multitoken

🚦 Function Gates DA Gas L2 Gas Proving Time (ms)
Base PR Diff Base PR Diff Base PR Diff Base PR Diff
⚪ burn_private 222,468 222,468 832 832 499,700 499,700 5,336 6,331 +995 (+18.6%)
⚪ burn_public 185,539 185,539 416 416 638,286 638,286 5,019 5,955 +936 (+18.6%)
⚪ initialize_transfer_commitment 188,484 188,484 768 768 474,500 474,500 5,054 5,998 +944 (+18.7%)
⚪ mint_to_private 239,217 239,217 1,408 1,408 511,400 511,400 5,716 6,714 +998 (+17.5%)
⚪ mint_to_public 185,539 185,539 416 416 637,794 637,794 5,031 5,958 +927 (+18.4%)
⚪ transfer_private_to_commitment 225,577 225,577 1,024 1,024 511,400 511,400 5,417 6,398 +981 (+18.1%)
⚪ transfer_private_to_private 278,917 278,917 2,048 2,048 555,100 555,100 6,029 7,138 +1,109 (+18.4%)
⚪ transfer_private_to_public 254,364 254,364 1,056 1,056 714,671 714,671 5,853 6,928 +1,075 (+18.4%)
⚪ transfer_public_to_commitment 185,539 185,539 640 640 663,507 663,507 5,028 5,980 +952 (+18.9%)
⚪ transfer_public_to_private 246,797 246,797 1,024 1,024 683,952 683,952 5,746 6,810 +1,064 (+18.5%)
⚪ transfer_public_to_public 185,539 185,539 480 480 673,499 673,499 5,056 5,986 +930 (+18.4%)

Contract: nft

🚦 Function Gates DA Gas L2 Gas Proving Time (ms)
Base PR Diff Base PR Diff Base PR Diff Base PR Diff
⚪ burn_private 223,523 223,523 416 416 661,046 661,046 5,553 6,589 +1,036 (+18.7%)
⚪ burn_public 185,539 185,539 448 448 670,028 670,028 5,025 5,964 +939 (+18.7%)
⚪ mint_to_private 268,061 268,061 1,600 1,600 735,336 735,336 6,076 7,211 +1,135 (+18.7%)
⚪ mint_to_public 185,539 185,539 448 448 670,712 670,712 5,021 5,964 +943 (+18.8%)
⚪ transfer_private_to_private 211,981 211,981 832 832 499,700 499,700 5,253 6,241 +988 (+18.8%)
⚪ transfer_private_to_public 223,550 223,550 416 416 659,252 659,252 5,541 6,582 +1,041 (+18.8%)
⚪ transfer_public_to_private 240,913 240,913 992 992 683,262 683,262 5,697 6,834 +1,137 (+20.0%)
⚪ transfer_public_to_public 185,539 185,539 384 384 633,279 633,279 5,041 6,021 +980 (+19.4%)

Contract: token

🚦 Function Gates DA Gas L2 Gas Proving Time (ms)
Base PR Diff Base PR Diff Base PR Diff Base PR Diff
⚪ burn_private 250,779 250,779 1,024 1,024 713,951 713,951 5,845 6,906 +1,061 (+18.2%)
⚪ burn_public 185,539 185,539 448 448 672,626 672,626 5,056 6,012 +956 (+18.9%)
⚪ initialize_transfer_commitment 188,484 188,484 768 768 474,500 474,500 5,029 5,953 +924 (+18.4%)
⚪ mint_to_private 281,953 281,953 2,144 2,144 738,064 738,064 6,223 7,358 +1,135 (+18.2%)
⚪ mint_to_public 185,539 185,539 448 448 672,221 672,221 5,069 5,985 +916 (+18.1%)
⚪ transfer_private_to_commitment 222,045 222,045 992 992 511,400 511,400 5,388 6,370 +982 (+18.2%)
⚪ transfer_private_to_private 286,325 286,325 2,592 2,592 557,600 557,600 6,121 7,208 +1,087 (+17.8%)
⚪ transfer_private_to_public 250,831 250,831 1,024 1,024 714,017 714,017 5,837 6,915 +1,078 (+18.5%)
⚪ transfer_private_to_public_with_commitment 254,753 254,753 1,600 1,600 747,317 747,317 5,872 6,940 +1,068 (+18.2%)
⚪ transfer_public_to_commitment 185,539 185,539 576 576 662,520 662,520 5,022 5,975 +953 (+19.0%)
⚪ transfer_public_to_private 244,762 244,762 992 992 683,298 683,298 5,767 6,835 +1,068 (+18.5%)
⚪ transfer_public_to_public 185,539 185,539 448 448 672,542 672,542 5,078 5,980 +902 (+17.8%)

Contract: vault

🚦 Function Gates DA Gas L2 Gas Proving Time (ms)
Base PR Diff Base PR Diff Base PR Diff Base PR Diff
🔴 deposit_private_to_private 362,767 362,767 1,312 1,312 878,227 1,080,775 +202,548 (+23.1%) 7,254 8,582 +1,328 (+18.3%)
🔴 deposit_private_to_private_exact 471,194 471,194 1,888 1,888 915,295 1,117,843 +202,548 (+22.1%) 9,116 10,725 +1,609 (+17.7%)
🔴 deposit_private_to_public 308,983 308,983 768 768 862,252 1,064,800 +202,548 (+23.5%) 6,583 7,770 +1,187 (+18.0%)
🔴 deposit_public_to_private 298,960 298,960 1,984 1,984 965,024 1,167,572 +202,548 (+21.0%) 6,488 7,702 +1,214 (+18.7%)
🔴 deposit_public_to_private_exact 301,691 301,691 1,952 1,952 949,448 1,151,996 +202,548 (+21.3%) 6,491 7,698 +1,207 (+18.6%)
🔴 deposit_public_to_public 185,539 185,539 832 832 897,542 1,100,090 +202,548 (+22.6%) 5,035 5,980 +945 (+18.8%)
🔴 issue_private_to_private_exact 471,194 471,194 1,888 1,888 915,955 1,118,119 +202,164 (+22.1%) 8,682 10,265 +1,583 (+18.2%)
🔴 issue_private_to_public_exact 339,595 339,595 1,344 1,344 899,977 1,102,141 +202,164 (+22.5%) 7,059 8,375 +1,316 (+18.6%)
🔴 issue_public_to_private 275,652 275,652 1,376 1,376 921,640 1,123,804 +202,164 (+21.9%) 6,136 7,279 +1,143 (+18.6%)
🔴 issue_public_to_public 185,539 185,539 832 832 898,232 1,100,396 +202,164 (+22.5%) 5,040 5,969 +929 (+18.4%)
🔴 redeem_private_to_private_exact 474,161 474,161 1,888 1,888 915,802 1,118,350 +202,548 (+22.1%) 8,782 10,304 +1,522 (+17.3%)
🔴 redeem_private_to_public 308,930 308,930 768 768 862,696 1,065,244 +202,548 (+23.5%) 6,614 7,772 +1,158 (+17.5%)
🔴 redeem_public_to_private_exact 304,711 304,711 1,952 1,952 949,769 1,152,317 +202,548 (+21.3%) 6,553 7,731 +1,178 (+18.0%)
🔴 redeem_public_to_public 185,539 185,539 832 832 898,064 1,100,612 +202,548 (+22.6%) 5,033 5,997 +964 (+19.2%)
🔴 withdraw_private_to_private 365,734 365,734 1,312 1,312 878,437 1,080,601 +202,164 (+23.0%) 7,299 8,607 +1,308 (+17.9%)
🔴 withdraw_private_to_private_exact 474,161 474,161 1,888 1,888 915,748 1,117,912 +202,164 (+22.1%) 8,741 10,308 +1,567 (+17.9%)
🔴 withdraw_private_to_public_exact 339,542 339,542 1,344 1,344 900,211 1,102,375 +202,164 (+22.5%) 7,005 8,339 +1,334 (+19.0%)
🔴 withdraw_public_to_private 314,923 314,923 2,528 2,528 967,335 1,169,499 +202,164 (+20.9%) 6,664 7,858 +1,194 (+17.9%)
🔴 withdraw_public_to_public 185,539 185,539 832 832 898,367 1,100,531 +202,164 (+22.5%) 5,048 5,992 +944 (+18.7%)

@alejoamiras

Copy link
Copy Markdown
Collaborator Author

E2E done: J1 (6000) passed with the Vault class published, J2 (default) failed at the 55,836 DA cap, J3 ("6s") was rejected in Configure without echoing the value, and J4 (update-baseline) passed and uploaded a branch-keyed artifact. The branch stays until aztec-benchmark releases, in case a re-run is needed.

@alejoamiras
alejoamiras deleted the worktree-e2e-bench-block-duration branch October 1, 2026 20:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant