Skip to content

docs: document kernelless simulations in the PXE#23399

Draft
critesjosh wants to merge 4 commits into
nextfrom
docs/kernelless-simulations
Draft

docs: document kernelless simulations in the PXE#23399
critesjosh wants to merge 4 commits into
nextfrom
docs/kernelless-simulations

Conversation

@critesjosh
Copy link
Copy Markdown
Contributor

Summary

Adds two docs pages explaining kernelless simulation in the Private eXecution Environment (PXE):

  • Concept: foundational-topics/pxe/kernelless_simulations.md covers what the PXE skips when skipKernels is true, the shape of SimulationOverrides, the stub account contracts, where kernelless does not apply (profileTx, simulateViaNode for public static calls, utility functions), and the gas-parity guarantees the e2e test actually proves.
  • How-to: aztec-js/how_to_simulate_without_signing.md is the recipe-oriented page for the symptoms in #20028: signing prompts on .simulate(), the min_revertible_side_effect_counter error from from: AztecAddress.ZERO, and the urge to deploy a no-op fee payment contract. It covers both dApp-side usage and the wallet-implementer pattern, using TestWallet.{initStubClasses,buildAccountOverrides,simulateViaEntrypoint} and the authwit-harvesting flow in e2e_kernelless_simulation.test.ts as the canonical snippets via #include_code.

Also:

  • Removes the stale "Until simulated simulations are implemented ([epic] Simulating Simulations #9133)" line from pxe/index.md and replaces it with a paragraph linking to the new concept page.
  • Cleans up two em-dashes elsewhere in pxe/index.md and the wallets-page paragraph that already mentioned the stub-account simulation.
  • Adds a tip box in how_to_read_data.md pointing readers with the signing-prompt symptom at the new how-to.
  • Adds // docs:start: / // docs:end: markers in yarn-project/end-to-end/src/test-wallet/test_wallet.ts and yarn-project/end-to-end/src/e2e_kernelless_simulation.test.ts so the how-to can pull the implementation as #include_code rather than inlining stale snippets.
  • Adds kernelless to docs-words.txt.

Test plan

  • yarn preprocess passes; every #include_code reference resolves.
  • yarn spellcheck passes.
  • Reviewed twice by codex (second opinion) against the actual source code; technical claims and snippet ranges verified.

Notes for reviewers

  • Inspired by the Slack thread following #20028, where a community integrator built a custom no-op fee payment contract to work around min_revertible_side_effect_counter must not be 0 on .simulate(). Grego pointed out that the PXE already supports the use case via kernelless simulation with a stub account override; this PR documents that.
  • The docs:start: markers in the test files are inert comments, but they sit inside yarn-project/. If the merge-train routing for this kind of mixed PR has a preferred home, happy to retarget.

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.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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()`.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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:
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.
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.

2 participants