docs: document kernelless simulations in the PXE#23399
Conversation
Adds a concept page (foundational-topics/pxe/kernelless_simulations.md) and a how-to (aztec-js/how_to_simulate_without_signing.md) that explain what the PXE skips when skipKernels is true, how SimulationOverrides work with contract overrides and the stub account contracts, and how to harvest CallAuthorizationRequest offchain effects. Also: - removes a stale "Until simulated simulations are implemented (#9133)" line from the PXE concept page - adds a tip from how_to_read_data.md pointing at the new how-to - links from foundational-topics/wallets.md to the new concept page - adds docs:start/docs:end markers in test_wallet.ts and e2e_kernelless_simulation.test.ts so the how-to can #include_code the canonical implementation and the authwit-harvesting flow - adds 'kernelless' to docs-words.txt
| - The wallet prompts the user for a signature on every `.simulate()` call, including reads of view-style functions. | ||
| - `.simulate()` fails with `Circuit execution failed: min_revertible_side_effect_counter must not be 0 for tail_to_public` when you pass `from: AztecAddress.ZERO` and no fee block. | ||
| - A custom fee payment method breaks during simulation because `from` is `AztecAddress.ZERO`. | ||
| - Simulations take long enough that you want to skip the kernel proving step. |
There was a problem hiding this comment.
Proving doesn't happen during simulation
|
|
||
| ## The wrong fix | ||
|
|
||
| Do not use `from: AztecAddress.ZERO` as a workaround for the signing prompt. That value has a specific meaning in the Private eXecution Environment (PXE): it tells the wallet to execute the payload through the default entrypoint with no account contract mediation. Combined with no fee payment method, it skips the setup phase that ends with `end_setup()`, which is what produces the `min_revertible_side_effect_counter must not be 0` error. |
There was a problem hiding this comment.
AztecAddress.ZERO should not be used anymore for different reasons. NO_FROM is the replacement
|
|
||
| ## The right fix | ||
|
|
||
| Run the simulation with a **stub account contract override**. The PXE swaps your account contract for one whose `is_valid` always returns true, so authwit validity checks pass without a signature. The wallet then collects any `CallAuthorizationRequest` offchain effects from the simulation and turns them into real authentication witnesses for the actual `.send()`. |
There was a problem hiding this comment.
The wallet provides the stub
| For a normal `.simulate()` you do not need to pass overrides yourself. The default simulation path is already kernelless, and wallets such as `EmbeddedWallet` install the stub-account override internally for you. Three things to remember: | ||
|
|
||
| - Pass a real account address as `from`, not `AztecAddress.ZERO`. | ||
| - Omit the `fee` block; this is a simulation, not a real transaction. |
There was a problem hiding this comment.
If the payment would use an FPC with side effects (generates notes) you should provide it to get accurate gas numbers
|
|
||
| ### As a wallet implementer | ||
|
|
||
| The canonical implementation lives in `yarn-project/end-to-end/src/test-wallet/test_wallet.ts`. It is an end-to-end test fixture, not a production wallet, but the simulation override mechanics are the cleanest in-tree example. |
There was a problem hiding this comment.
Never point public docs to TestWallet, our public-facing wallet package is @aztec/wallets and the canonical example is EmbeddedWallet
|
|
||
| A simulation with the stub override active will reach `#[authorize_once]` call sites in the app and token contracts without prompting for signatures. Each such site emits a `CallAuthorizationRequest` as an offchain effect, which the wallet can collect and turn into a real authentication witness for the eventual `.send()`. | ||
|
|
||
| The pattern is in `yarn-project/end-to-end/src/e2e_kernelless_simulation.test.ts`. Switch the wallet into the override mode, simulate, and read the offchain effects: |
There was a problem hiding this comment.
Point them to EmbeddedWallet instead
| ## Things to watch out for | ||
|
|
||
| - **`AztecAddress.ZERO` is not "no sender".** Use a real account address with overrides instead. Reserve `AztecAddress.ZERO` (or `NO_FROM`) for calls that genuinely have no account context. | ||
| - **Private fee payment contracts can skew gas estimates.** Kernelless simulation matches full simulation on gas in the common case, but a private FPC that holds notes is a known edge case. If you need exact gas for a tx that pays through a private FPC, run a full simulation as a sanity check. |
There was a problem hiding this comment.
You can run kernelless with an FPC to get the speed benefits AND accurate gas
| - **Private fee payment contracts can skew gas estimates.** Kernelless simulation matches full simulation on gas in the common case, but a private FPC that holds notes is a known edge case. If you need exact gas for a tx that pays through a private FPC, run a full simulation as a sanity check. | ||
| - **`profile()` is not kernelless.** If you call `.profile()` to count circuit gates, the kernels run regardless. Use `.simulate()` if you only need return values, offchain effects, or gas estimates. | ||
| - **Utility functions ignore overrides.** `FunctionType.UTILITY` calls go through a different code path and reject `SimulationOverrides`. They do not need an override anyway, since they do not run through an account contract. | ||
| - **Wallet-wide simulation toggles can race.** If your wallet exposes a single mode flag (the way `TestWallet.setSimulationMode` does), concurrent `.simulate()` calls from different parts of the UI can see each other's state. Prefer per-call overrides via `SimulationOverrides` for production wallets. |
There was a problem hiding this comment.
This is an implementation detail of TestWallet, completely irrelevant
|
|
||
| - the private kernel init, inner, reset, and tail circuits | ||
| - the proof generation associated with those kernels | ||
| - the kernel-level authwit validity check (the account contract's `is_valid` is still invoked, but its result is not checked by a kernel) |
There was a problem hiding this comment.
Kernels do not check authwits
The previous commit added docs:start/docs:end markers in e2e test files so the docs could #include_code from them. This commit moves those snippets into a new runnable example at docs/examples/ts/aztecjs_kernelless_simulation/ and reverts the markers in test_wallet.ts and e2e_kernelless_simulation.test.ts. The new example connects to a sandbox, deploys a token, and demonstrates: - Reading a private view function (private_get_decimals) via .simulate() with no signing prompt (relies on EmbeddedWallet's default kernelless + stub-account override). - Bob initiating transfer_in_private from Alice via additionalScopes, collecting the resulting CallAuthorizationRequest offchain effects, decoding them, building real authwits from each inner hash, and sending. The how-to page now #include_codes from the new example instead of from the e2e test, and the wallet-implementer section points readers at EmbeddedWallet/TestWallet sources by path rather than #include_code-ing test-fixture methods. The concept page's multi-account-scopes section likewise describes the override-builder pattern in prose. Also: - Corrects the SimulationOverrides import path to '@aztec/aztec.js/wallet'. - Notes that EmbeddedWallet.sendTx auto-injects authwits, so the explicit authWitnesses pass in the example is redundant for EmbeddedWallet but is the pattern non-auto-injecting wallets must follow. - Drops 'overrides' from a code sample that was targeting a utility function, and notes that utility functions reject SimulationOverrides.
- "Skip the kernel proving step" -> "skip the kernel circuits entirely". Proving doesn't happen during simulation, regardless of skipKernels. - Rewrite the "wrong fix" section to point users at NO_FROM (the current "no account context" sentinel) instead of describing AztecAddress.ZERO semantics that no longer hold. - Reframe "The right fix" to credit the wallet for providing the stub override; the PXE is the consumer that applies it. - Add the private-FPC-with-side-effects case: include the FPC in fee options to get accurate gas; kernelless still applies and remains the supported path. - Remove all TestWallet references from public docs; EmbeddedWallet (@aztec/wallets) is the canonical example. - Drop the "Wallet-wide simulation toggles can race" bullet; it was a TestWallet implementation detail with no public-API relevance. - Correct the "kernels do not check authwits" claim: authwit validity is checked by user-contract code (is_valid invoked by #[authorize_once]), not by the kernels. The stub override is what makes simulation pass without a signature.
|
|
||
| ## Overview | ||
|
|
||
| A "full" simulation in the PXE runs the user's private function bytecode, then runs every private kernel circuit (init, inner, reset, tail) over the resulting execution trace. The kernels enforce protocol rules such as side-effect counter sequencing and authentication witness validity. |
There was a problem hiding this comment.
Kernels do not enforce authentication witness validity
…ording
Two corrections from an empirical run against v4.2.0:
- The 'Utility functions ignore overrides' bullet had 'reject' in the
body and 'ignore' in the heading. Confirmed in next:
ContractFunctionInteraction.simulate throws 'overrides are not supported
for utility function simulation' for FunctionType.UTILITY calls with
non-empty overrides. Updated the bullet to match.
- The 'Circuit execution failed: min_revertible_side_effect_counter must
not be 0' error is only reachable from a custom wallet that does not
intercept the zero address. EmbeddedWallet and BaseWallet-derived
wallets surface a wallet-level message first ('Account "0x0000…0000"
does not exist on this wallet.' or 'Account not found in wallet for
address: 0x...'). Symptoms list now mentions all three so users can
find the page regardless of which message they hit.
Summary
Adds two docs pages explaining kernelless simulation in the Private eXecution Environment (PXE):
foundational-topics/pxe/kernelless_simulations.mdcovers what the PXE skips whenskipKernelsis true, the shape ofSimulationOverrides, the stub account contracts, where kernelless does not apply (profileTx,simulateViaNodefor public static calls, utility functions), and the gas-parity guarantees the e2e test actually proves.aztec-js/how_to_simulate_without_signing.mdis the recipe-oriented page for the symptoms in #20028: signing prompts on.simulate(), themin_revertible_side_effect_countererror fromfrom: AztecAddress.ZERO, and the urge to deploy a no-op fee payment contract. It covers both dApp-side usage and the wallet-implementer pattern, usingTestWallet.{initStubClasses,buildAccountOverrides,simulateViaEntrypoint}and the authwit-harvesting flow ine2e_kernelless_simulation.test.tsas the canonical snippets via#include_code.Also:
pxe/index.mdand replaces it with a paragraph linking to the new concept page.pxe/index.mdand the wallets-page paragraph that already mentioned the stub-account simulation.how_to_read_data.mdpointing readers with the signing-prompt symptom at the new how-to.// docs:start:/// docs:end:markers inyarn-project/end-to-end/src/test-wallet/test_wallet.tsandyarn-project/end-to-end/src/e2e_kernelless_simulation.test.tsso the how-to can pull the implementation as#include_coderather than inlining stale snippets.kernellesstodocs-words.txt.Test plan
yarn preprocesspasses; every#include_codereference resolves.yarn spellcheckpasses.Notes for reviewers
min_revertible_side_effect_counter must not be 0on.simulate(). Grego pointed out that the PXE already supports the use case via kernelless simulation with a stub account override; this PR documents that.docs:start:markers in the test files are inert comments, but they sit insideyarn-project/. If the merge-train routing for this kind of mixed PR has a preferred home, happy to retarget.