Skip to content

Tracking PR for v0.16.0 release - #2976

Merged
bobbinth merged 310 commits into
mainfrom
next
Aug 7, 2026
Merged

Tracking PR for v0.16.0 release#2976
bobbinth merged 310 commits into
mainfrom
next

Conversation

@bobbinth

Copy link
Copy Markdown
Contributor

This is a tracking PR for v0.16.0 release.

onurinanc and others added 30 commits June 23, 2026 00:32
#3116)

* change assert_sender_has_role from call to exec

* changelog

---------

Co-authored-by: Bobbin Threadbare <43513081+bobbinth@users.noreply.github.com>
…3121)

* add zero root check for active mint and burn policy

* changelog

---------

Co-authored-by: Bobbin Threadbare <43513081+bobbinth@users.noreply.github.com>
* fix policy getters

* changelog

---------

Co-authored-by: Bobbin Threadbare <43513081+bobbinth@users.noreply.github.com>
* chore: remove `AccountDelta::merge`

* chore: remove `AccountVaultDelta::merge`

* chore: remove tests using `Account::apply_delta`

* chore: remove `Account::apply_delta`

* chore: replace `apply_delta` in delta -> account conversion

* chore: Remove AssetVault::apply_delta

* chore: add changelog

* chore: make added_assets and removed_assets non-testing

---------

Co-authored-by: Bobbin Threadbare <43513081+bobbinth@users.noreply.github.com>
…3118)

* add FUNGIBLE_ASSET_MAX_AMOUNT to set_max_supply

* changelog

---------

Co-authored-by: Bobbin Threadbare <43513081+bobbinth@users.noreply.github.com>
* feat(testing): introduce TestTransactionBuilder

* Simplify TestTransactionBuilder doc comment

* fix: lint check
* feat(standards): add P2idNote bon builder, require at least one asset

Replace the `P2idNote` marker type and its `P2idNote::create` factory with a
`P2idNote` struct built via a `bon` typestate builder (`P2idNote::builder()`):

- `.sender()`, `.target()`, and a serial number (`.serial_number()` or
  `.generate_serial_number(rng)`) are required; `.note_type()` defaults to
  private and attachments are optional.
- `.asset()`/`.assets()` and `.attachment()`/`.attachments()` append items.
- P2ID notes must now carry at least one asset, enforced in `new()` via the
  new `NoteError::MissingAsset`.
- `From<P2idNote> for Note` converts infallibly.

Migrate all `P2idNote::create` call sites to the builder. Incidental 0-asset
P2ID notes in kernel tests move to P2ANY (which legitimately carries no
assets), preserving the 0-asset coverage.

Part of #2283

* docs(standards): address P2ID builder review comments

- Drop the builder usage description from `P2idNote::new` docs; the typestate
  builder is self-documenting.
- Trim redundant tails from the builder extension method docs.
- Remove the "Use P2ANY ..." rationalization comments across the kernel tests
  and the P2ANY reference in the `setup_test` doc comment.
- Assert the note type against `NoteType::default()` instead of a hardcoded
  `NoteType::Private` in the minimal-builder test.
- Add a `compile_fail` doctest showing `.serial_number()` and
  `.generate_serial_number()` are mutually exclusive at compile time.

* docs(standards): drop the dual-serial compile_fail doctest

Per review feedback, remove the `compile_fail` doctest (and its explanation) on
`generate_serial_number`; the typestate already enforces that the serial number
cannot be set twice.

* fix(standards): address #3126 review feedback

- Use `NoteError::other` instead of adding a dedicated `MissingAsset` variant to
  the protocol crate; the "at least one asset" rule is a standards concern.
- Remove the hand-rolled `create_p2id_note_exact` helper and inline the builder
  (with `.serial_number()`) at all 9 call sites.
- Drop the low-value `builder_generates_serial_number` and
  `into_note_preserves_assets` tests; exercise `generate_serial_number` inside
  `builder_accumulates_assets` instead.
- Remove the 5 duplicate CHANGELOG entries the rebase re-inserted.

* fix(testing): remove unused `Word` import

Left over from removing `create_p2id_note_exact`; clippy (-D warnings) flagged it.
* feat(standards): add P2ideNote bon builder, require at least one asset

Replace the `P2ideNote` marker type and its `P2ideNote::create` factory with a
`P2ideNote` struct built via a `bon` typestate builder (`P2ideNote::builder()`):

- `.sender()`, `.target()`, and a serial number (`.serial_number()` or
  `.generate_serial_number(rng)`) are required; `.note_type()` defaults to
  private, and `.reclaim_height()`, `.timelock_height()`, and attachments are
  optional.
- `.asset()`/`.assets()` and `.attachment()`/`.attachments()` append items.
- P2IDE notes must now carry at least one asset, enforced in `new()` via the
  new `NoteError::MissingAsset`.
- `From<P2ideNote> for Note` converts infallibly.

Migrate the `P2ideNote::create` call sites to the builder.

Part of #2283

* docs(standards): address P2IDE builder review comments

- Drop the builder usage description from `P2ideNote::new` docs; the typestate
  builder is self-documenting.
- Trim redundant tails from the builder extension method docs.
- Assert the note type against `NoteType::default()` instead of a hardcoded
  `NoteType::Private` in the minimal-builder test.
- Add a `compile_fail` doctest showing `.serial_number()` and
  `.generate_serial_number()` are mutually exclusive at compile time.

* docs(standards): drop dual-serial doctest, restore P2ideNote doc

Per review feedback:
- Remove the `compile_fail` doctest (and its explanation) on
  `generate_serial_number`; the typestate already enforces that the serial
  number cannot be set twice.
- Roll back the `P2ideNote` struct documentation to its original wording.

* fix(standards): address P2IDE builder review feedback

- Use `NoteError::other` instead of adding a dedicated `MissingAsset` variant to
  the protocol crate; the "at least one asset" rule is a standards concern.
- Drop the low-value `builder_generates_serial_number` and
  `into_note_preserves_assets` tests; exercise `generate_serial_number` inside
  `builder_accumulates_assets` instead.
- Remove the 5 duplicate CHANGELOG entries the rebase re-inserted.

* test(standards): tighten the empty-assets assertions for P2ID and P2IDE

Use `assert_matches!` to also check the error message in the empty-assets
builder test, applied to both the P2ID and P2IDE modules, per review feedback.
* Use TestTransactionBuilder in miden-testing

* Deleting methods from tx context builder as per comments
…3065)

* flip AuthSingleSigAcl to exempt-list semantics

Replace the previous trigger-list ACL with an exempt-list ACL: every called
account procedure requires a signature by default, and only procedures
listed in the exempt map are allowed to execute without authentication.

This removes a footgun where forgetting to register a new setter in the
trigger list silently left it permissionless, and lets us drop the
`allow_unauthorized_output_notes` and `allow_unauthorized_input_notes`
configuration flags. Under the new model the input/output note checks
become implicit: any side-effecting note consumption goes through an
account procedure call, so the procedure check covers it.

- MASM: rewrite `auth_tx_acl` to iterate account procedures (skipping
  index 0, the auth proc itself), check `was_procedure_called`, and look
  up each called proc in the exempt map. Mirrors the structure of
  `multisig::compute_transaction_threshold`.
- Storage layout: rename `trigger_procedure_roots` slot to
  `exempt_procedure_roots`, keyed by procedure root with `[1, 0, 0, 0]`
  presence marker. Drop the now-unused `config` slot.
- Faucet factory: `AuthControlled + SingleSig` now uses an empty exempt
  list, so every authority-gated setter requires a signature including
  the burn/receive path that previously rode in unsigned.
- Test helper `Auth::Acl` and integration tests flipped to the new
  semantics, with added invariant coverage for the empty-exempt-list
  default and for transactions that mix exempt and non-exempt calls.

Part of #2964.

* docs: changelog entry for AuthSingleSigAcl exempt-list flip

Part of #2964.

* address pre-push review findings

- Validate exempt_procedures uniqueness in AuthSingleSigAcl::new so a
  caller passing duplicate procedure roots receives an AccountError
  instead of panicking inside StorageMap::with_entries. Add a unit test
  that exercises the rejection path.
- Refresh the stale "Safe to unwrap because we know that the map keys
  are unique" comment to reference the validation source. Correct the
  pre-existing # Panics doc on `new` (it returns Result for the length
  check, never panics).
- Reword the integration-test assertion message that read as
  contradictory next to nonce_delta == 0.
- Note in the CHANGELOG that the no-signature hot path now iterates
  every account procedure (two kernel calls each), so it costs more than
  the old trigger-list iteration that was bounded by the trigger count.

* restore explicit input/output note auth checks

Security review caught a real auth bypass: deleting the explicit
`tx::get_num_output_notes` / `tx::get_num_input_notes` gates relied on
`was_procedure_called` being set for every side-effecting procedure, but
the kernel only flags procedures that touch account-restricted APIs.
`output_note_create` and `output_note_add_asset` do *not* trip that
flag (only `assert_native_account`), so any account procedure that
emits notes without also moving assets through the vault would have
executed unsigned. Asset-bearing flows stay safe because vault
add/remove is tracked, but the "note checks are implicit" claim was
false for asset-less note emission.

This restores the issue's original three-invariant design:

1. Any kernel-detected called procedure not on the exempt list forces
   `auth_required`.
2. Any output note creation forces `auth_required` unconditionally
   (this is the gap that `was_procedure_called` cannot close).
3. Any input note consumption forces `auth_required` unless an exempt
   procedure was detected as called during the transaction (the
   consumption is then vouched for by the exempt call).

The MASM now tracks `any_exempt_called` via a local across the
procedure loop so the input-note gate can distinguish vouched
consumption from raw consumption. The Rust module doc, the on-type
"procedure detection" callout, and the CHANGELOG entry are updated to
state precisely which gaps `was_procedure_called` leaves and why the
explicit note checks remain. A new integration test
`test_acl_exempt_detected_procedure_succeeds_without_auth` exercises
the positive exempt-map lookup with a kernel-detected procedure
(`get_item`) so a regression that inverted the marker would be caught.

* test: cover the unconditional output-note auth gate

Code review flagged that the new condition 2 ("any output note created
→ auth_required, unconditionally") had no regression coverage. Adding a
test where the procedure that emits the note (BasicWallet's
move_asset_to_note) is on the exempt list, so condition 1 is satisfied
and the only thing forcing authentication is the output-note check
itself. Without an authenticator the tx must fail with
MissingAuthenticator.

* clarify transaction-wide input-note vouching and harden mixed test

Security review caught a documentation mismatch: the prior wording
("vouched for by an exempt procedure") read as per-note vouching, but
`ANY_EXEMPT_CALLED_LOC` is a transaction-wide flag — a single detected
exempt call lifts the input-note signature requirement for every input
note in the same transaction, not only the notes that procedure
handled. Asset exfil is still blocked by the unconditional output-note
gate, but exempting any detected procedure relaxes input-note auth
transaction-wide, which integrators should know.

- Rewrite condition 3 in the Rust doc to state the global scope
  explicitly, with a usage guidance line.
- Mirror the clarification in the MASM `ANY_EXEMPT_CALLED_LOC` rationale.
- Add a SECURITY-CRITICAL invariant comment on the unconditional
  output-note `or` (the line is the only barrier against an exempt
  procedure emitting notes via `output_note_create`).
- Update the CHANGELOG bullet with the same global-scope wording.

Also addressing a code review nit:
`test_acl_mixed_exempt_and_protected_requires_auth` previously exempted
`account_procedure_1` (undetected), so the test never actually
exercised the "detected-exempt alongside detected-non-exempt" branch.
Switch to exempting `get_item` and calling both `get_item` + `set_item`
so a regression that let a detected exempt call suppress the non-exempt
auth requirement would be caught.

* isolate input-note auth gate and surface vouching warning at config site

- Add `test_acl_input_note_consumption_requires_auth_without_exempt`:
  empty exempt list, no tx script, consume the mock note → expect
  MissingAuthenticator. Without this, the input-note branch of the
  MASM `and or` could be deleted and no test would fail (every other
  no-auth note test also calls a non-exempt detected procedure that
  trips condition 1 first).
- Sharpen the cost note in the CHANGELOG to mention the per-detected-
  call map lookup (was undercounted as "two kernel calls each").
- Add a pointer on `AuthSingleSigAclConfig::with_exempt_procedures`
  warning about the transaction-wide scope of condition 3, so callers
  see it at the configuration site, not only on the type.

* review nits: expand faucet exempt-map probe and fix doc comment

- auth_scheme_slot_schema: switch `//` to `///` so the doc renders
  consistently with the sibling slot-schema accessors.
- faucet_contract_creation: probe the full former trigger set
  (mint_and_send + 4 metadata setters + 4 policy setters + pause +
  unpause) for absence from the exempt map, rather than only three
  representative roots. A regression that put any of these back into
  the exempt map will now surface here.

* review nits: cover MAX_NUM_PROCEDURES path and inline BTreeSet import

- Add `test_singlesig_acl_rejects_exempt_list_above_account_limit` so
  the two error paths in `AuthSingleSigAcl::new` (duplicates and
  over-limit) have symmetric coverage.
- Move `alloc::collections::BTreeSet` to a top-of-file `use` to match
  the surrounding import style (`alloc::vec::Vec` is already imported
  this way).

* style: replace em dashes with simple dashes per project convention

* docs: consolidate auth-logic into two conditions

Restructure the three-condition auth description into two: condition 1
combines the per-procedure check and the input-note vouching clause
(they are logically coupled and condition 1 already handles the
non-exempt-called half through the exempt-list lookup), and explicitly
spells out that input-note consumption with no procedures called at all
still requires authentication. Condition 2 (output notes) stays
unconditional and is unchanged. Apply the same restructuring to the
MASM docstring, the CHANGELOG entry, and the cross-references in the
`with_exempt_procedures` setter and the procedure-detection note.

The implementation is unchanged - this is purely a docs revision. The
transaction-wide vouching behavior remains as-is pending further
discussion on whether to introduce a separate per-note-vouching opt-in.

* docs: roll back to three conditions, sharpen input-note check

Restructure the auth-logic description into three conditions, matching
the issue's original spec but with a sharper wording for the input-note
check:

  1. A kernel-detected non-exempt procedure was called.
  2. An input note was consumed AND no procedure was detected as called
     at all.
  3. Any output note was created (unconditional).

Conditions 1 and 2 are logically equivalent in outcome to the previous
formulation ("input consumed AND no exempt called"), because condition
1 already catches the non-exempt-called case. Stating condition 2 as
"no procedure called at all" makes the easy-to-miss case (input
consumed but no account procedure invoked) explicit.

Update the MASM to literally match the new wording: track
ANY_PROC_CALLED_LOC (set to 1 whenever was_procedure_called returns
true) instead of ANY_EXEMPT_CALLED_LOC. The flag-update site simplifies
from a load-or-store to an idempotent `push.1 loc_store`. All 16
integration tests and 4 unit tests still pass without modification,
confirming the outcome equivalence.

Apply the same restructuring to the Rust type doc, the
with_exempt_procedures setter, the procedure-detection note, and the
CHANGELOG entry. Update two test docstring cross-references to use the
new condition numbering. The footgun warning about transaction-wide
vouching remains in place.

* optimize auth_tx_acl loop and drop "input-note" hyphenation

Tighten the procedure-iteration loop: instead of iterating
num_procedures-1 down to 0 and gating the body on `dup neq.0` to skip
the auth procedure at index 0, iterate down to 1 directly. The initial
guard becomes `dup neq.1` (don't enter the loop if num_procedures == 1,
i.e. only the auth procedure is installed) and the continuation check
also becomes `dup neq.1`. This removes the inner `if.true ... end`
block that was gating every iteration, saving a few cycles per
procedure on the no-signature hot path while preserving the index-0
skip property (the auth procedure is guaranteed at index 0 by the
account-code builder, so by never decrementing below 1 we never touch
it).

Replace "input-note" with "input note" in the comments and docs of
files this branch introduced or modified.

* docs: shorten ANY_PROC_CALLED_LOC comment and use named rule references

Shrink the doc comment on `ANY_PROC_CALLED_LOC` to four lines explaining
the flag's role: it tracks "any procedure detected as called" and gates
the input note check on whether at least one procedure ran, with a note
that non-exempt detected calls already force authentication via the
per-procedure check inside the loop.

Replace every numbered authentication-condition reference ("condition 1",
"condition 2", "condition 3") in the type doc, MASM docstring,
CHANGELOG entry, `with_exempt_procedures` setter, the procedure-
detection note, and the integration test docstrings with named rules:
"non-exempt proc was called", "input note was consumed, but no proc was
called", and "output note was created". Numbered references couple
prose to a particular ordering and are fragile under future doc
reshuffles; named references are stable.

* address PR review comments

- MASM: trim the slot comment on EXEMPT_PROCEDURE_ROOTS_SLOT to two
  lines; drop the "most easily missed" trailing sentence and the
  output-note explanation from the auth_tx_acl docstring; remove the
  redundant multi-line explanation above the input note check (the
  docstring already covers the same ground).
- Faucet factory: shorten the AuthControlled + SingleSig comment by
  dropping the historical reference to allow_unauthorized_input_notes.
- Errors: add a typed `AccountError::DuplicateExemptProcedure(_)`
  variant carrying the offending procedure root, and replace the
  duplicate-check `AccountError::other` call with it. The check now
  short-circuits in a single forward pass that identifies which root
  was duplicated.
- Tests: hoist the per-function `use` block in
  `test_acl_output_note_creation_requires_auth_even_when_caller_exempt`
  up to the top of the file, alongside the other module-level imports;
  rewrite the explicit `TransactionScript::from(...)` cast to a typed
  binding plus `.into()`.
- CHANGELOG: rewrite the entry to fit in under 400 characters by
  deferring the full auth-logic description to the type doc.

* docs: drop quoted-phrase rule references in favor of short descriptions

Convert "non-exempt proc was called", "input note was consumed, but no
proc was called", and "output note was created" from literal quoted
labels (used everywhere the rules are referenced) into natural prose
descriptions: "the non-exempt proc check", "the input note check", and
"the output note check". The enumeration in the type doc and MASM
docstring is now numbered 1./2./3. with descriptive bullet bodies, and
all references in the surrounding prose, the `with_exempt_procedures`
setter, the procedure-detection note, and the integration test
docstrings use the short forms.

* address remaining PR review comments

- MASM: drop the SECURITY-CRITICAL comment block above the unconditional
  output-note `or`. The section header on the line above already names
  the invariant and the reviewer considered the explanation redundant.
- singlesig_acl.rs: remove the per-setter warning on
  `with_exempt_procedures` (already covered by the type doc), shorten
  the transaction-wide-vouching paragraph to its first sentence, drop
  the "auth runs after the rest of the transaction" paragraph, and
  compress the "Important Note on Procedure Detection" section into
  eight lines while preserving the practical guidance.
- CHANGELOG: shorten the entry to a single sentence and switch the
  reference from #2964 (issue) to #3065 (this PR), per project
  convention.

* address remaining PR review comments

- MASM: apply the requested commentary edits in `auth_tx_acl` (add a
  Locals section header on the procedure, reword the section header
  for the iteration block, clarify the initialization comments for the
  any_proc_called flag and auth_required, restate the loop comment as
  a single line, rewrite the post-was_called marker comment to mention
  the input note check it gates, shorten the exempt-map lookup comment,
  and tighten the post-loop stack annotation).
- AuthSingleSigAclConfig: switch `exempt_procedures` from
  `Vec<AccountProcedureRoot>` to `BTreeSet<AccountProcedureRoot>`.
  Uniqueness is now enforced by the type, so the runtime duplicate
  check in `AuthSingleSigAcl::new` and the `DuplicateExemptProcedure`
  variant on `AccountError` (along with the corresponding unit test)
  are dropped. The "safety" comment on the now-trivially-infallible
  `StorageMap::with_entries(...).unwrap()` is removed, and the
  surrounding storage-slot comment is reworded per the reviewer's
  suggestion.
- Update the `Auth::Acl` mock-chain helper variant and every
  integration test call site to use `BTreeSet::from([...])`.
- Trim the doc comment on the procedure-detection note and rewrite
  the `# Panics`-style block on `AuthSingleSigAcl::new` as a bullet
  list under "Returns an error if".
- Faucet construction test: apply the wording suggestion on the
  empty-exempt-map probe comment.

* reflow inline comments to <=100 cols and remove stray breaks

- Faucet construction test: rejoin the "Probe the full" / "former
  trigger set" fragments back into a single sentence; the PR-suggested
  comment had been pasted with a stray mid-clause newline.
- MASM auth_tx_acl:
  - shorten the procedure-iteration section header so it fits in 100
    columns,
  - trim the "no authentication required by default" comment by one
    word so it fits,
  - wrap the loop-direction comment over two lines,
  - reflow the `was_called` marker comment block onto sentence
    boundaries (the previous break landed mid-clause after "Setting to
    1"), and
  - wrap the exempt-map lookup comment so the marker value sits on its
    own line.

* simplify auth_tx_acl: drop input/output note checks

Per Philipp's review (#3065 (comment)),
the input note vouching rule and the unconditional output note rule
are redundant for the funds-out concern. Any procedure that moves
assets out of the account vault must call `account_remove_asset` (or
equivalent), which is kernel-tracked, so an exfiltrating procedure
always shows up in the per-procedure check unless the author has
explicitly exempted it. The transaction kernel epilogue separately
enforces asset conservation across the whole transaction
(input_vault == output_vault, see ERR_EPILOGUE_TOTAL_NUMBER_OF_ASSETS_MUST_STAY_THE_SAME
at epilogue.masm:439), so an output note cannot smuggle in assets
that weren't taken from the vault or an input note.

This collapses the auth logic to a single condition: "a non-exempt
detected procedure was called". The benefits:

- The MASM drops `ANY_PROC_CALLED_LOC`, the `@locals(1)` annotation,
  the flag-set inside the loop, and the entire post-loop input/output
  note block.
- `use miden::protocol::tx` is no longer needed.
- The type doc collapses from a three-condition list with a
  transaction-wide-vouching footgun warning to a two-paragraph
  description plus the unchanged procedure-detection caveat.
- Two integration tests
  (test_acl_output_note_creation_requires_auth_even_when_caller_exempt
  and test_acl_input_note_consumption_requires_auth_without_exempt)
  are removed because they exercise rules that no longer exist; the
  remaining seven tests still cover the procedure-based path
  (empty/exempt/mixed/protected, key rotation, initial-state read).

The faucet's `AuthControlled + SingleSig` factory ships an empty
exempt list, so its behavior is unchanged: every authority-gated
setter still requires a signature.

* address remaining PR review comments

- MASM auth_tx_acl docstring: shorten the security argument per the
  reviewer's wording. The first sentence states the auth rule
  directly; the second explains that asset removal goes through a
  kernel-tracked procedure, so funds-out is gated unless every
  procedure in that path is explicitly exempted.
- MASM post-loop stack annotation: label the literal `1` as `i = 1`
  so a reader does not have to derive that this is the loop counter
  at its final value.
- Rust type doc: drop the trailing sentence about the epilogue's
  asset conservation check; the kernel-tracked `account_remove_asset`
  explanation in the preceding sentence carries the same argument
  more directly.

* address Philipp's remaining PR review comments

- testing/faucet.rs: per Philipp's note that exempting `receive_and_burn`
  preserves the old "BURN note runs unsigned" behavior, seed
  `user_faucet_single_sig_acl` with an exempt set containing
  `FungibleFaucet::receive_and_burn_root()`. Every other authority-gated
  procedure (mint_and_send, the metadata setters, the policy setters,
  pause/unpause) still requires a signature.

- tests/auth/singlesig_acl.rs: address the three test cleanups Philipp
  flagged. Inline the temporary `tx_context_*_with_auth` bindings into a
  single build + execute + await chain. Factor the canonical
  `mock::account::get_item` and `mock::account::set_item` tx scripts
  into `compile_call_get_item_script` / `compile_call_set_item_script`
  helpers (the get_item script was duplicated across three tests).
  Replace `.expect(...)` on test results with `?` so the failure
  message preserves the source error. Also finishes the `TestSetup`
  refactor the user had started: rewrites every test body to destructure
  the helper output, and drops the unused proc-root fields from the
  struct.

- New `test_acl_burn_note_against_user_faucet_runs_without_signature`:
  builds a public fungible faucet account using `user_faucet_single_sig_acl`
  (which now carries `receive_and_burn` in its exempt set), creates a BURN
  note targeted at the faucet, and verifies the consumption transaction
  executes without an authenticator. Direct evidence that the exempt-set
  choice in `user_faucet_single_sig_acl` matches Philipp's intent.

* docs: fix doc lint

---------

Co-authored-by: Claude (Opus) <noreply@anthropic.com>
Co-authored-by: Bobbin Threadbare <43513081+bobbinth@users.noreply.github.com>
* feat(standards): replace BurnNote::create with a bon builder

Convert the `BurnNote` marker type into a struct built via a `bon` typestate
builder (`BurnNote::builder()`), mirroring `P2idNote`. A `BurnNote` converts
into a `Note` via `Note::from`. Migrate the faucet burn-note test sites.

Part of #2283.

* refactor(standards): model BurnNote as carrying a single Asset

Address review: store the burned asset as Asset instead of NoteAssets, take #[builder(into)] asset, expose an asset() getter, and build the NoteAssets in the Note conversion (one asset can't exceed the limit). Use NoteStorage::default() for the empty storage.

* test(testing): migrate singlesig_acl burn note to the BurnNote builder

The next merge brought in tests/auth/singlesig_acl.rs, which still called the removed BurnNote::create. Port it to BurnNote::builder() and drop the now-unused NoteAttachments import.
* rework SWAP to support public payback notes

Public payback notes were unrecoverable from on-chain data: the
previous SWAP precomputed the payback recipient off-line and embedded
only the resulting hash, so the consuming script had no preimage to
register with the advice provider.

Build the payback P2ID recipient at consume time from data available
in SWAP storage so the on-chain script can call p2id::new (which also
registers the recipient preimage in the advice map):

- Embed the creator account ID in storage (hybrid embed, mirroring
  PSWAP) so the consumer reads it directly instead of going through
  active_note::get_sender.
- Derive the payback serial as swap_serial[0] + 1.
- Derive the payback tag from the creator account ID prefix via
  note_tag::create_account_target.

Storage shrinks from 14 to 11 items: the precomputed recipient and tag
are no longer stored. The Rust constructor sets creator_id = sender by
convention.

* simplify swap.masm comments

* refactor(standards): hide private SWAP payback recipient

Branch SWAP MASM on payback note type so private paybacks store an opaque
precomputed recipient digest (and tag) instead of the creator id. Public
paybacks keep the creator id in plaintext since the consumer needs it to
reconstruct the recipient via p2id::new. The unified 16-felt storage asserts
zero on the slots unused by each branch, making the privacy guarantee
structural rather than convention-based.

* fix(standards): clippy and rustfmt in swap.rs

* docs: refresh SWAP storage description in note.md

* docs(swap): explain why creator id is stored explicitly

* rename SWAP creator id field to payback target id

* docs(swap): consolidate per-mode payback docs

Centralize the rationale for the SwapPayback::Private vs Public storage
shape on the enum itself and drop the repeated explanations from
SwapNote::create, SwapNoteStorage, and the constructors. Also remove a
stale comment from swap.masm.

* style(standards): rustfmt swap.rs

* test(standards): adapt dummy_target_id to new AccountId::dummy signature

* feat(standards): re-export SwapPayback from note module

* Apply suggestion from @partylikeits1983

Co-authored-by: Alexander John Lee <77119221+partylikeits1983@users.noreply.github.com>

* docs(swap): clarify payback target defaults to sender

* refactor(swap): store payback target account ID as [suffix, prefix]

Swap the public payback target limbs so the suffix occupies slot [14] and
the prefix slot [15], matching the [suffix, prefix] account ID convention
used elsewhere (e.g. P2ID storage). Updates the MASM storage constants and
layout comment, the Rust From/TryFrom conversions, and the storage doc
table. Purely a layout convention change; the on-chain payback recipient is
unaffected, as verified by the public-payback integration tests.

* refactor(swap): avoid single-letter closure bindings in TryFrom

Rename the |e| and |f| closure parameters in the SwapNoteStorage TryFrom impl
to |err| and |felt| for readability.

* test(swap): fold try_from round-trip into the storage round-trip tests

Merge the standalone swap_note_storage_try_from_round_trip_public/private
tests into the two round-trip tests, which now also parse the serialized
NoteStorage back via TryFrom and assert equality with the original.

* test(swap): assert exact error in dirty-slot rejection tests

Replace the loose is_err() checks in the SwapNoteStorage dirty-slot tests
with assert_matches! on NoteError::Other guarded by the exact error message,
so the tests keep verifying the intended validation after refactors.

* docs(swap): clarify payback target load-order comment

Reword the public-payback comment to explain that loading prefix then
suffix leaves the stack as [suffix, prefix], matching p2id::new's
[target_id_suffix, target_id_prefix, ...] inputs (the previous wording
read as if the signature were prefix-first).

* replace apply_delta with apply_patch

---------

Co-authored-by: Marti <marti@miden.team>
Co-authored-by: Alexander John Lee <77119221+partylikeits1983@users.noreply.github.com>
* chore: replace delta error for patch error

* docs: add changelog entry
* extend wallet APIs with multisig and guarded multisig

* changelog

* add Approver struct

* add documentation for withholding issue

* add Approver struct

* apply suggestions

---------

Co-authored-by: Bobbin Threadbare <43513081+bobbinth@users.noreply.github.com>
* feat(standards): replace MintNote::create with a bon builder

Convert the `MintNote` marker type into a struct built via a `bon` typestate
builder (`MintNote::builder()`), mirroring `P2idNote`. A `MintNote` converts
into a `Note` via `Note::from` and keeps the faucet-id/asset-faucet match check
via `NoteError::other`. Migrate the faucet mint-note test sites.

Part of #2283.

* Update crates/miden-standards/src/note/mint.rs

Co-authored-by: Philipp Gackstatter <PhilippGackstatter@users.noreply.github.com>

* refactor(standards): derive MintNote faucet_id from storage

Address review: drop the faucet_id builder param and derive it from the embedded MintNoteStorage asset, removing the mismatch error condition. Remove the now-dead builder_rejects_mismatched_faucet test and inline its storage helper into the remaining test. Update the mint call sites in faucet.rs.

* style(standards): collapse Note::with_attachments to satisfy nightly rustfmt

---------

Co-authored-by: Philipp Gackstatter <PhilippGackstatter@users.noreply.github.com>
* add multisig options to fungible faucet

* changelog

* chore: minor fixes

---------

Co-authored-by: Bobbin Threadbare <43513081+bobbinth@users.noreply.github.com>
Co-authored-by: Bobbin Threadbare <bobbinth@protonmail.com>
…protocol` (#3122)

* refactor: Add attachments to NoteFile

* chore: comments

* chore: move to standards

* chore: comments

* refactor: remove attachments

---------

Co-authored-by: Bobbin Threadbare <43513081+bobbinth@users.noreply.github.com>
…#3073)

* perf: avoid double-hashing the asset vault key in fungible add/remove

Hash the raw asset vault key once at the start of each asset vault
modifier and have peek_asset/get_asset accept the already-hashed key,
removing one poseidon2 hash per fungible asset add/remove (including
the fee-removing path in the epilogue). The set_asset helper becomes a
bare smt::set wrapper and is removed.

Worst-case post-compute_fee cycles drop from 863 to 843; VAULT_KEY_HASH_CYCLES
is re-measured from 50 to 30 (same 45-cycle margin).

Closes #3059

* chore: add changelog entry for #3073

* refactor: keep asset vault key hashing internal to the vault

Address review feedback: hashing stays inside the asset_vault module so
callers don't have to think about it.

- get_asset is unchanged: it takes a raw ASSET_KEY and hashes internally.
- hash_asset_key stays private.
- peek_asset takes the pre-hashed ASSET_KEY_HASH; add_fungible_asset and
  remove_fungible_asset hash the key once at the start and reuse it for
  both the peek and the set, eliminating the double hash.
- the now-redundant set_asset wrapper is replaced by a direct smt::set.

---------

Co-authored-by: Bobbin Threadbare <43513081+bobbinth@users.noreply.github.com>
)

The note doc's Inputs section was renamed to Storage (### Storage), but
three reference links still pointed at note#inputs, which docs.miden.xyz
flags as a broken anchor on ingest. Repoint them to note#storage:
- protocol_library.md: get_storage / get_storage_info
- transaction.md: P2ID note storage link

Closes #3158.
…3157)

* refactor: update AuthRequest event to carry either signature or tx summary

* chore: update changelog
…ts (#3123)

* feat: Introduce create, update, remove storage patches

* chore: add `AccountStoragePatchBuilder`

* chore: replace mutators on storage patch with builder

* chore: test all slot merge permutations

* chore: normalize empty words in created map

* chore: add changelog

* chore: optimize StorageMapPatchEntries serialization

* chore: compact empty words to `None` in value patch serialization

* chore: reject duplicate entries in map patch

* chore: add note on no-op-ness of map update variant

* chore: split storage patch into modules

* feat: allow re-creating existing slot

* feat: allow removing non-existent slot

* feat: document merge behavior

* feat: ensure num storage patches is within limits

* feat: remove no-op storage patches after merge

* chore: revert allowing removing non-existent slot

* chore: remove outdated error condition

* chore: remove `set_init_map_item`

* chore: use `expect` in patch construction
* feat: update kernel to commit to patch operation

* chore: add changelog

* chore: make map trailer description clearer

* chore: remove unnecessary delta computation in epilogue
* implement nft faucet

* changelog

* apply suggestions

* apply suggestions

* lint

* remove max supply from NFTs

* remove redundant comments

* add nft modify assertion

* apply suggestions
* feat: reject full state deltas with storage updates

* chore: replace storage patch test helpers with more targeted ones

* feat: define full state as only create ops

* chore: add changelog
* feat(standards): treat PSWAP requested amount as a minimum

Previously the PSWAP note reverted when the total fill (account_fill +
note_fill) exceeded the requested amount. This makes `requested` a minimum
rather than an exact cap: an over-fill is accepted and treated as a full take.

- Payout divisor is `denom = max(total_fill, requested)`, so an over-fill
  divides by the fill itself and pays out the whole offered side.
- No remainder note is created on a full or over-fill: the partial-fill
  check is `total_in < total_requested` (was `!=`).
- The creator banks the surplus requested tokens via the P2ID payback,
  which carries the full fill amount.
- Removes the `ERR_PSWAP_FILL_EXCEEDS_REQUESTED` cap.

MASM and Rust mirror each other (denom selection, no-remainder-on-overfill,
payout read from on-chain attachments). Removing the cap also removes
accidental-overpay protection: a filler who over-pays gets no refund and no
extra offered tokens, so callers must compute the intended fill amount.

Tests: single-sided over-fill payback round-trip, mixed account+note
over-fill with rounding-dust attribution, a calculate_offered_for_requested
over-fill unit case; the fill-exceeds-requested error case is removed.

* refactor(standards): clarify PSWAP over-fill, address review comments

- Extract a `max` helper proc; `execute_pswap` calls `exec.max` instead of the
  inline lt/if-else to pick `denominator = max(fill, requested)`.
- Rename `CALC_DENOM`/`EXEC_AMT_DENOM` to `*_DENOMINATOR`, matching the Rust mirror
  and spelling out the abbreviation.
- Rewrite the `execute_pswap` doc with the two fill cases stated explicitly, and
  note that two-sided over-fill rounding dust (<=1 token) is absorbed by the
  cross-swap counterparty, never minted or stranded.
- Drop the redundant inline `is_partial` annotation.
- Test: assert the consumer takes the whole offered side on a single-sided
  over-fill (per CodeRabbit).

No behaviour change: the max extraction is behaviour-equivalent. 10 standards
unit + 69 pswap integration tests pass; fmt clean.

* refactor(standards): rename divisor to fill_reference, tidy comments

Address review feedback on PR #9:
- Rename the payout divisor `full_fill_amount` -> `fill_reference` across the MASM
  (CALC_/EXEC_AMT_ constants) and the Rust mirror.
- MASM: drop single/two-sided wording in the execute_pswap doc (use account_fill/
  note_fill); trim the inline `fill_reference = max(...)` comment that duplicated
  the proc doc.
- Tests: shrink the combined-fill rstest doc and de-hardcode the per-case numbers
  in its body comments.

* docs: add CHANGELOG entry for PSWAP requested-as-minimum

* Update crates/miden-testing/tests/scripts/pswap.rs

Co-authored-by: Philipp Gackstatter <PhilippGackstatter@users.noreply.github.com>

* refactor(standards): address review feedback on PR #3148

- MASM `max`: branchless `cdrop` instead of an if/else branch.
- Rename the requested-amount locals to `min_requested`/`min_requested_amount` and
  align the divisor comments to make clear it is a minimum, not an exact target.
- Combined-fill test: Alice/Bob are now bare AccountIds (not funded wallets), and the
  rstest fill params are named after their source (`charlie_fill`/`bob_fill`).

* refactor(standards): use min_requested consistently for the runtime minimum

Follow-up to the review-feedback commit: rename the runtime requested-minimum
references (the MASM `EXEC_AMT_REQUESTED` local + its comments, `execute_full_fill`'s
local, and the remaining doc lines) to `min_requested`. Storage field/accessors,
raw storage slots, the account/note fill legs, and `remaining_requested` keep the
`requested` name. Pure rename — recipients/digests unchanged (tests pass).

* refactor(standards)!: rename PSWAP requested -> min_requested everywhere

Rename every `requested` reference in the PSWAP module to `min_requested` to make
clear the amount is a minimum: the public `PswapNoteStorage` field/accessors/builder
(`requested_asset` -> `min_requested_asset`, `requested_asset_amount` ->
`min_requested_asset_amount`, `requested_faucet_id` -> `min_requested_faucet_id`),
`calculate_offered_for_requested` -> `calculate_offered_for_min_requested`, the MASM
storage/exec/P2ID constants, and all comments/docs.

Behaviour-preserving: names do not affect the assembled MAST or the positional storage
serialization (recipients/digests unchanged; 9 standards unit + 69 pswap integration tests
pass). BREAKING for downstream callers of the renamed public API (miden-client, web-sdk).

* Narrow PSWAP rename to the public asset/amount accessors

Revert the over-broad min_requested sweep and keep the rename to exactly
the public-facing amount: PswapNoteStorage::requested_asset ->
min_requested_asset and requested_asset_amount -> min_requested_amount.
requested_faucet_id is left unchanged, and the MASM/internal naming
returns to its prior form.

* Use bare account IDs for creator-only accounts in PSWAP tests

Accounts that only supply a PSWAP creator/sender AccountId were built as
full wallets via add_existing_wallet_with_assets, carrying an unused vault
balance that implied the offered asset came from the creator's vault (it
comes from the note). Replace them with AccountIdBuilder::new()
.build_with_seed(..), matching the existing combined-fill test. Accounts
that consume a transaction, source fill assets from their vault, or have
their vault asserted stay full wallets.

* Rename PSWAP storage amount slot to min_requested_amount

The storage-layout doc and the slot const used requested_amount while the
amount is the requested minimum (the Rust accessor is min_requested_amount).
Rename the doc entry and REQUESTED_AMOUNT_ITEM -> MIN_REQUESTED_AMOUNT_ITEM;
the faucet/callbacks slots stay requested_* (matching requested_faucet_id).

---------

Co-authored-by: Philipp Gackstatter <PhilippGackstatter@users.noreply.github.com>
* add code inspection account component

* changelog

* namespace
* feat: migrate protocol crate to miden-project

* chore: comments

* review: rename `miden::protocol_utils`

* review: address comments on MASM imports and constants

* review: declare miden-core dep in the manifest

* chore: format toml

* review: rename projects

* review: provide miden-core through `InMemoryPackageRegistry`

* review: use `package.write_masp_file` convenience

* review: do not use Result on `build_assembler`

* review: expose Package for `ProtocolLib`

* review: expose Package for `TransactionKernel`

* feat: comments

* feat: explicit miden-core versioning

* feat: explicit linkage on project file

* feat: remove `shared_modules` and move `account_id` to `protocol-utils`

* feat: split `miden-tx-kernel` and `miden-tx-kernel-core` (#3149)

* feat: split miden-kernel-tx and miden-kernel-tx-core

* chore: remove miden-project dep; lint

* fix: test

* review: small changes on comments

* review: miden-core version and comment

* review: rename `protocol_utils`

* review: move masm source code to `src` dir

* review: changelog breaking marker
* read allowlist via get_initial_map_item

* changelog

* read tx script allowlist via get_initial_map_item

---------

Co-authored-by: Bobbin Threadbare <43513081+bobbinth@users.noreply.github.com>
…key and signature (#3178)

* add documentation for ecdsa

* changelog

---------

Co-authored-by: Bobbin Threadbare <43513081+bobbinth@users.noreply.github.com>
…SigAcl` refactor (#3180)

* remove all_authority_gated_setter_roots after AuthSingleSigAcl refactor

* changelog

---------

Co-authored-by: Bobbin Threadbare <43513081+bobbinth@users.noreply.github.com>
mmagician and others added 28 commits August 2, 2026 11:04
…notes (#3447)

* feat(agglayer): enable on-chain bridge role rotation via RBAC action notes

Wraps up the role-transfer mechanism for the bridge's administrative
roles. PR #3130 moved the bridge to the miden-standards RBAC stack but
left rotation unexercisable on-chain: the bridge's AuthNetworkAccount
allowlist rejected the role-management note.

- Add RbacActionNote::script_root() to AggLayerBridge::allowed_notes(),
  making grant_role / revoke_role / set_role_admin / renounce_role
  reachable on the bridge. The fee-schedule entry derives from the
  allowlist automatically; the allowlist is component storage, so
  BRIDGE_CODE_COMMITMENT is unaffected.
- Add a create_existing_bridge_account_with_admin_and_roles fixture and
  an agglayer rbac_rotation test suite covering: grant-then-use,
  revoke-then-fail, unauthorized grant, ADMIN rotation, the
  last-admin-renounce lockout hazard, and an allowlist pin test.
- Document role rotation in SPEC.md section 2.5 (with the rotation
  ordering, last-admin, and target-binding caveats), add the RBAC_ACTION
  note as section 4.7, and resolve the stale rotation TODOs.

Closes #2706

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

* refactor(agglayer): single bridge test fixture with explicit admin

Merge create_existing_bridge_account_with_admin_and_roles into
create_existing_bridge_account_with_roles: the fixture now always takes
the ADMIN member explicitly, and callers that do not exercise rotation
pass the dummy admin themselves.

Addresses review feedback on the duplicate constructors.

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

* docs(agglayer): drop historical framing and fix the ADMIN-decommission recipe

Document only present behavior in SPEC section 2.5, per review feedback.
Also correct the recipe for bounding ADMIN authority: each delegate
admin role must be populated and made self-administering, in committed
steps, before ADMIN is emptied - otherwise the delegate roles freeze
under the then-empty ADMIN role. Pinned by the new
self_administered_delegate_survives_admin_removal test.

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

* docs: move generic RBAC rotation guidance from the agglayer SPEC to the component docs

SPEC section 2.5 now keeps only the bridge-specific consequences of
on-chain role management and points to the miden-standards docs for the
generic hazards: the RoleBasedAccessControl rustdoc gains the corrected,
strictly ordered ADMIN-decommission recipe (with its no-quorum residual
risk), and the RbacActionNote rustdoc gains the note-model security
considerations (ordering, unexpiring pending notes, no target binding).

Addresses review feedback that most of this content belongs in the RBAC
documentation rather than the agglayer spec.

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

* docs: shorten the RBAC rotation guidance

Cut the RoleBasedAccessControl decommission paragraph, the
RbacConfigNote security considerations, and the SPEC 2.5 caveat list
down to their essentials. While shortening, correct two claims for the
post-#3434 reality: the builder now attaches NetworkAccountTarget by
default, and the auto-allowlisted NETWORK_ACCOUNT_CONFIG note reaches
ADMIN-defaulted procedures - so the bridge's ADMIN role must never be
emptied (it would forfeit the post-deployment configuration channel),
and the SPEC now says so instead of describing decommissioning as
viable.

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

* docs(agglayer): trim the bridge fixture doc comment

Addresses review feedback.

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

* docs(standards): drop the target-binding caveat from the RbacConfigNote docs

Target binding will be enforced by the note script itself (tracked
separately), so the caveat is not worth documenting.

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

* docs(agglayer): drop the target-binding caveat from SPEC section 2.5

Note target binding is tracked in a separate issue; the section 4.7
permissions table keeps the factual consumer row.

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

* test(agglayer): drop rotation tests already covered by the generic RBAC suite

Keep only the bridge-specific wiring tests: grant-then-use and
revoke-then-fail (RBAC_CONFIG through the bridge allowlist, fee
schedule, and update_ger gating) plus the allowlist pin. Admin
rotation, delegation exclusivity, and last-admin removal are component
semantics covered in tests/scripts/rbac/.

Three properties the generic suite did not yet cover move there instead
of being dropped: an unauthorized-sender RBAC_CONFIG note is rejected,
a note targeting another account is consumable (the assertion that
inverts once target binding lands), and a self-administered role stays
manageable after ADMIN renounces (set_role_admin(X, X)).

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

* refactor(agglayer): share the bridge setup helper across test suites

Extract the duplicated setup_bridge helpers from remove_ger.rs and
rbac_rotation.rs into test_utils (BridgeSetup + setup_bridge), seeding
a chain-resident admin wallet so both suites - and future ones - can
use the same fixture.

Addresses review feedback on re-using test helpers.

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

* test(agglayer): pin paused-bridge rotation and the effective note allowlist

Post-merge follow-ups: a test pinning that RBAC_CONFIG notes stay
consumable while the bridge is paused (the emergency-recovery path SPEC
section 2.5 documents), an extension of the allowlist pin to the
effective on-account set including the AuthNetworkAccount auto-added
entries, and two wording fixes (NETWORK_ACCOUNT_CONFIG also dispatches
fee-policy updates; the bench dummy admin covers all ADMIN-gated
operations).

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

* test(agglayer): assert the pause actually took effect during paused rotation

Promote is_bridge_paused into test_utils, assert the pause state before
and after the paused-bridge rotation, and pin the bridge's tx-script
allowlist alongside the note allowlist.

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

* Apply suggestions from code review

Co-authored-by: Marti <marcin.gorny.94@protonmail.com>

---------

Co-authored-by: Claude (Opus) <noreply@anthropic.com>
* refactor(standards): remove the AuthSingleSigAcl auth component

Its exempt-procedure-list mechanism let fee-charging accounts be
drained: anyone could call an exempt procedure without a signature,
but the account still paid the transaction fee. The team decided to
remove the component rather than fix it now (see issue discussion).

The plain AuthSingleSig component (every call requires a signature)
remains available and now backs the "singlesig user faucet" factories.

Closes #3360

* fix: revert unnecessary comment change in BurnNote conversion

Per review feedback: the struct-level doc comment already covers the
network-execution caveat; the internal conversion comment didn't need
the same rewording.

---------

Co-authored-by: Claude (Opus) <noreply@anthropic.com>
* fix: restrict indexed input note asset removal

* refactor: share indexed input note removal guard

* test: make malicious input note theft regression explicit

* fix: require native account context for indexed input note asset removal

The account-origin check resolves the caller against the *active* account,
so the gate alone was bypassable: a malicious note script could route the
indexed removal through an attacker-controlled foreign account via FPI,
where that foreign account is active and vouches for its own procedures.
The full #3445 drain still worked end to end against a standard wallet.

Add exec.memory::assert_native_account to the guard, which closes that
path while leaving the one legitimate by-index caller intact (the fee
manager collects sponsorship note assets from the native account's auth
procedure).

Tests: a note-script-via-FPI regression test, and an explicit test that
transaction scripts are rejected too - the latter pins a behavior change
that was previously only implied by a deleted test.

---------

Co-authored-by: Claude (Opus) <noreply@anthropic.com>
Co-authored-by: Bobbin Threadbare <43513081+bobbinth@users.noreply.github.com>
* feat: add miden-build-utils crate

* feat: remove copy_directory helper

* feat: more build-utils

* feat: remove TransactionScript and NoteScript constructor from Program

* feat: remove unused helpers

* feat: refactors

* revert: remove Program constructors

* chore: changelog

* chore: remove leftover changes

* refactor: assemble utils

* review: move regex into LazyLock

* fix: cargo shear

* review: address comments

* review: address remaining comments

* fix: changelog merge

* feat: add publish logic to read masp file from OUT_DIR

* refactor: inline code

---------

Co-authored-by: Bobbin Threadbare <43513081+bobbinth@users.noreply.github.com>
* refactor min burn amount comment

* changelog

* Update CHANGELOG.md

Co-authored-by: zeapoz <zeapo@pm.me>

---------

Co-authored-by: Philipp Gackstatter <PhilippGackstatter@users.noreply.github.com>
Co-authored-by: zeapoz <zeapo@pm.me>
* feat: assemble `ExpirationTransactionScript` on build-time

* feat: move script to library procedure inside the standards package

* chore: changelog

* feat: add `SendNotesTransactionScript` to standards package

* chore: changelog

* feat: add tx_script() accessor

* chore: comments

* chore: tests

* chore: fmt

* feat: factor out common masm code

* feat: comments; masm signatures

* feat: add tests

* review: address comments

* review: address nits and tests

* feat: address more comments

* review: address comments

* chore: update comments

* review: make common module private

* review: improve comments

* review: validate asset composition

* review: add `add_existing_non_fungible_faucet` to mockchain builder helper

* review: remove `validate_note_records` costly validation

* review: keep note idx and read ptr on stack

* review: add cross-check with asset composition to pick script

---------

Co-authored-by: Bobbin Threadbare <43513081+bobbinth@users.noreply.github.com>
* chore: fix fresh build

* change version to rc1

* regenerate cost tables

---------

Co-authored-by: Marti <marti@miden.team>
…3471)

* fix(tx): gate only signature production outside the auth procedure

* changelog

* fix in_auth_procedure: bool comment

* remove auth request probe component
* chore: reject unbounded signatures in tx event

* chore: add changelog

* chore: use multiple assertions

* fix: changelog entry
… note (#3469)

* fix pswap.masm

* add regression tests

* test: cover a pswap note-fill payback created behind a remainder note

* rename tests

* changelog

* clean tests

* simplify tests

* Update crates/miden-testing/tests/scripts/pswap.rs

---------

Co-authored-by: Philipp Gackstatter <PhilippGackstatter@users.noreply.github.com>
The v0.16.0-rc.1 release aborted mid-publish with:

  403 Forbidden: Trusted Publishing tokens do not support creating new
  crates. Publish the crate manually, first

`miden-protocol-build-utils` was added to the workspace and had never been
published, and crates.io does not let an OIDC token create a new crate name.
`cargo publish --dry-run` never contacts the registry to reserve a name, so
nothing caught this until the tagged release had already been cut.

Add scripts/check-crates-published.sh, which enumerates publishable workspace
members via `cargo metadata` and checks each name against the crates.io sparse
index. It runs in the publish job, ahead of the token mint - the check needs no
credential, and a slow registry would otherwise eat into the token's short
lifetime. A release that would abort partway now stops before publishing
anything, with an error naming the manual-claim and Trusted Publishing setup
needed to fix it.

The remediation is explicit that the claim must happen at a version strictly
below the upcoming release, from a placeholder outside this workspace. Claiming
the name at the version about to be released is what turned this incident's
retry into "crate miden-protocol-build-utils@0.16.0-rc.1 already exists on
crates.io index", and publishing from the workspace would need --allow-dirty,
which bakes untracked files into a permanently public artifact.

Scope is narrow and stated in the header: a hit proves the name is registered
by someone, not that we own it, that Trusted Publishing is configured, or that
the release version is free. Those still surface only at publish time, and the
success message says so rather than reading as "the release will succeed".

`cargo metadata` and the jq filter are captured into a variable rather than
read through a process substitution, so a failed or truncated read cannot
yield a partial crate list that silently passes. An unverifiable crate refuses
to guess rather than passing.

All eight publishable members currently resolve on the index, so this is safe
to land as-is.

Co-authored-by: Claude (Opus) <noreply@anthropic.com>
Co-authored-by: Marti <marti@miden.team>
`cargo publish --workspace` verifies each member against a temporary local
registry under target/package/tmp-registry, and cargo unpacks that registry's
crates into ~/.cargo/registry/src keyed only by name and version. Extraction
is skipped whenever the target directory already has a `.cargo-ok` marker, so
a stale unpack is reused without ever being compared to the tarball.

The registry cache key hashes only Cargo.lock, so a release branch and `next`
sitting at the same version share it. The v0.16.0-rc.2 publish restored a
cache saved minutes earlier by a `next` dry-run and verified the release
branch's miden-tx against `next`'s miden-protocol, which failed with a
missing-method error on a rename that had not been cherry-picked over.

The clearing step already existed but was gated to dry-run mode. Drop the
gate.

Co-authored-by: Claude (Opus) <noreply@anthropic.com>
* fix missing invocation comments

* changelog
* feat: unify account-origin authenticators in api.masm

Merge the tracking and non-tracking `authenticate_*` procedures in the
transaction kernel into a single `authenticate_account_origin` /
`account::authenticate_procedure`, and gate `account_upgrade` with it
(closing the follow-up noted in the issue).

A kernel procedure call is tracked (via the was_called flag) only when the
active account is native and the account's authentication procedure is not
currently executing. The "auth in progress" condition is keyed on a new
epilogue-controlled kernel-memory flag (set around the auth dyncall in
`execute_auth_procedure`), NOT on the caller being the auth procedure
(index 0).

Keying on the flag rather than the caller index is required for safety: the
epilogue's replay guard (ERR_EPILOGUE_AUTH_PROCEDURE_CALLED_FROM_WRONG_CONTEXT)
detects an auth procedure invoked from user code by checking that
was_called[0] is still 0 before it runs the auth procedure. That detection
relies on the auth procedure's own kernel calls being tracked. An
index-based exemption would suppress that tracking whenever the auth
procedure runs - including when a note/tx script invokes it during the main
phase - defeating the guard for any auth component that does not
unconditionally increment the nonce (e.g. no_auth, network_account). The
flag is only set during the epilogue's auth window, so a main-phase
invocation is still tracked and the guard still fires.

Since read-only introspection procedures are now tracked in the native
context, this changes their was_procedure_called results; `account_upgrade`
now panics when not invoked from the account context.

Tests:
- test_authenticate_procedure_conditional_tracking: tracking is suppressed
  while the auth-in-progress flag is set and applied otherwise.
- test_non_incrementing_auth_procedure_called_from_wrong_context: an auth
  procedure that makes a gated call but never increments the nonce, invoked
  from a tx script, still trips the epilogue replay guard (fails under an
  index-based exemption, passes with the flag).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix: address review comments

- add a positive kernel test that successfully calls `native_account::upgrade`
  through an account procedure (mock account now exposes `upgrade`), alongside
  the negative wrong-context test
- trim the verbose doc comments on the epilogue auth-in-progress flag,
  `authenticate_procedure`, `assert_auth_procedure`, and the epilogue guard back
  to concise versions
- reword the `was_procedure_called` docs (kernel and protocol wrapper)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* refactor: update doc comments and CHANGELOG, add a new skill file

* test: address review comments on auth tests

- merge `test_non_incrementing_auth_procedure_called_from_wrong_context` into
  `test_auth_procedure_called_from_wrong_context` via `rstest`, parameterizing
  the auth-proc body (incrementing vs non-incrementing)
- replace the flag-toggling `test_authenticate_procedure_conditional_tracking`
  with a realistic end-to-end test: a procedure called from a tx script is
  tracked, one called from the auth procedure is not, both asserted in the auth
  procedure

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* chore: small doc comment fix

---------

Co-authored-by: Claude (Opus) <noreply@anthropic.com>
* state the NFT asset value model as a convention, not a guarantee

* changelog

---------

Co-authored-by: Philipp Gackstatter <PhilippGackstatter@users.noreply.github.com>
* fix: enforce asset-value preservation at the callback boundary

* docs: link the asset-callback changelog entry to the issue

* test: document what actually enforces callback value preservation

Order the moved amounts as 200 then 100 to match the scenario described in
issue #3442, and record that the executor's host-side note reconciliation
already rejects this transaction today: `NOTE_BEFORE_ADD_ASSET_EVENT` is
emitted before the callback runs, so the host keeps the pre-callback amounts
and its output-notes commitment disagrees with the kernel's.

That reconciliation is not proof-enforced, which is why the test asserts on
the kernel error rather than on the commitment mismatch.

* test: cover the account path of the callback value-preservation check

Add a negative test where on_before_asset_added_to_account rewrites the
added amount so that offsetting rewrites keep the aggregate vault totals
intact. On this path there is no host-side backstop at all:
ACCOUNT_VAULT_BEFORE_ADD_ASSET_EVENT is emitted after the callback with
the processed value, so without the callback-boundary assertion the
transaction would succeed end-to-end.

Also note in the asset docs that the kernel enforces the existing
requirement that the processed value equals the input value.

* refactor: make callback stack overwrite explicit

* fix: address callback value preservation review

---------

Co-authored-by: Bobbin Threadbare <43513081+bobbinth@users.noreply.github.com>
* chore: update miden-vm dependencies to v0.29

Bumps the `miden-vm` and `miden-crypto` workspace dependencies from v0.25 /
v0.28.1 to v0.29, along with the `miden-core` version declared in the
`miden-project.toml` MASM manifests, and adapts to the upstream API changes:

- `CoreLibrary` now bundles a separate `miden-precompiles` package. Both the
  build-time package registries and the `TransactionMastStore` seed the pair via
  `CoreLibrary::packages()`; without the precompiles forest, dynamic calls into
  Falcon/Keccak wrappers fail to resolve at execution time.
- The advice stack moved behind the typed `AdviceStack` API: `AdviceInputs`'
  stack field is private, `AdviceInputs::into_parts` returns three components,
  and `AdviceMutation::extend_stack` became `extend_advice_stack`.
- `Kernel` was renamed to `KernelDescriptor`, `Package::to_kernel` to
  `to_kernel_descriptor`, and `Package::module_infos` to `module_descriptors`.
- `miden_verifier::verify` now takes an `ExecutionClaim` and verifies bundled
  precompile proofs itself, replacing `verify_with_precompiles`.
- `miden_core_lib::handlers::keccak256::KeccakPreimage` is gone; the agglayer
  tests compute digests through a local `keccak256_felts` helper.

The ECDSA k256 encoded signature is now 32 felts (was 33). Falcon verification
moved into a precompile, which cuts hasher rows dramatically, so the note
consumption cost tables, `bench-tx.json`, and the committed trace-shape
brackets are regenerated.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* chore: revert claude settings changes

* chore: clean up advice provider instantiations

* chore: minor rename

* chore: address review comments

* chore: fix core library loading

---------

Co-authored-by: Claude (Opus) <noreply@anthropic.com>
…3495)

* refactor(standards): drop `components` prefix from `NAME` constants

* chore: add changelog

---------

Co-authored-by: Bobbin Threadbare <43513081+bobbinth@users.noreply.github.com>
… depth (#3470)

* fix(standards): return the call-invoked getters at the required stack depth

* changelog

* apply suggestions

---------

Co-authored-by: Bobbin Threadbare <43513081+bobbinth@users.noreply.github.com>
* feat(standards): add the MinBurnAmountConfig note

* test(testing): cover the MinBurnAmountConfig note

* changelog

* address review comments
* feat(standards): seed RBAC delegated admins at construction

Replace `RoleBasedAccessControl::new` with a `bon` builder over `RoleSeed`s.
A seed carries a role's members and its delegated admin, so the role config
word is seeded as `[member_count, admin_role_symbol, 0, 0]` instead of always
leaving the admin unset. Exclusive delegation is therefore established at
account creation rather than through a sequence of on-chain `set_role_admin`
calls during which `ADMIN` still administers the role.

The builder's `build()` rejects duplicate role seeds, seeds that carry neither
members nor a delegated admin, member counts exceeding `u32::MAX`, and seeds
whose effective admin chain never reaches a populated role, which would leave
the role permanently unmanageable. The last check covers the default admin too:
seeding an operational role without seeding `ADMIN` freezes it just as a dead
explicit delegation does, since `ADMIN` administers itself.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* chore: rename RoleSeed to RoleConfig

* chore: simplified BridgeRoles

* chore: minor renaming

---------

Co-authored-by: Claude (Opus) <noreply@anthropic.com>
Co-authored-by: Bobbin Threadbare <bobbinth@protonmail.com>
Co-authored-by: Bobbin Threadbare <43513081+bobbinth@users.noreply.github.com>
…3508)

* feat(protocol): add native_account::is_issuer_of

* fix(standards): exempt the issuing faucet from its own transfer list

* test(standards): cover the issuer exemption in the transfer list suites

* changelog

* refactor(standards): inline the issuer check into the transfer policies

* test(standards): drive the transfer-policy mint tests through MintNote

* docs: move the changelog entry into the v0.16.0 section
…3510)

* refactor(standards): dispatch mint and burn policies via dyncall

* changelog

* remove redundant comments

* drop redundant tests and fix the input/output
@bobbinth
bobbinth marked this pull request as ready for review August 7, 2026 00:41
@bobbinth
bobbinth merged commit 75d05d9 into main Aug 7, 2026
72 checks passed
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.